[Cmake-commits] CMake branch, master, updated. v3.9.0-378-g6bef326

Kitware Robot kwrobot at kitware.com
Wed Aug 2 11:55:03 EDT 2017


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "CMake".

The branch, master has been updated
       via  6bef326eb4140444ed70a202bc37af11e0ba158b (commit)
       via  c80d8cb293155ef9be76079c90cb159990ecb43f (commit)
       via  6df1bda1c57193b289866389a994ea79a2f5e1d8 (commit)
       via  11f3dcb04887fccf38a8eda19d498bf7a5060486 (commit)
       via  b6674431af4790ba5deea9e5e6bd7b2620beb69e (commit)
      from  83c86639103d9cfc009b9e998009d81e579df25f (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=6bef326eb4140444ed70a202bc37af11e0ba158b
commit 6bef326eb4140444ed70a202bc37af11e0ba158b
Merge: 83c8663 c80d8cb
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Wed Aug 2 15:45:19 2017 +0000
Commit:     Kitware Robot <kwrobot at kitware.com>
CommitDate: Wed Aug 2 11:45:35 2017 -0400

    Merge topic 'update-libarchive'
    
    c80d8cb2 libarchive: Fix inclusion of zlib, bzlib, and lzma for build within CMake
    6df1bda1 Merge branch 'upstream-LibArchive' into update-libarchive
    11f3dcb0 LibArchive 2017-07-09 (98a69539)
    b6674431 libarchive: Update script to get 3.3.2
    
    Acked-by: Kitware Robot <kwrobot at kitware.com>
    Merge-request: !1071


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=c80d8cb293155ef9be76079c90cb159990ecb43f
commit c80d8cb293155ef9be76079c90cb159990ecb43f
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Jul 20 12:51:15 2017 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Thu Jul 20 12:52:54 2017 -0400

    libarchive: Fix inclusion of zlib, bzlib, and lzma for build within CMake
    
    Update a new source file imported from libarchive upstream to include
    the headers for compression libraries using the CMake wrappers.

diff --git a/Utilities/cmlibarchive/libarchive/archive_version_details.c b/Utilities/cmlibarchive/libarchive/archive_version_details.c
index 813f0f3..9289bf1 100644
--- a/Utilities/cmlibarchive/libarchive/archive_version_details.c
+++ b/Utilities/cmlibarchive/libarchive/archive_version_details.c
@@ -34,13 +34,13 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:1
 #include <string.h>
 #endif
 #ifdef HAVE_ZLIB_H
-#include <zlib.h>
+#include <cm_zlib.h>
 #endif
 #ifdef HAVE_LZMA_H
-#include <lzma.h>
+#include <cm_lzma.h>
 #endif
 #ifdef HAVE_BZLIB_H
-#include <bzlib.h>
+#include <cm_bzlib.h>
 #endif
 #ifdef HAVE_LZ4_H
 #include <lz4.h>

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=6df1bda1c57193b289866389a994ea79a2f5e1d8
commit 6df1bda1c57193b289866389a994ea79a2f5e1d8
Merge: b667443 11f3dcb
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Jul 20 11:36:18 2017 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Thu Jul 20 11:36:18 2017 -0400

    Merge branch 'upstream-LibArchive' into update-libarchive
    
    * upstream-LibArchive:
      LibArchive 2017-07-09 (98a69539)

diff --cc Utilities/cmlibarchive/CMakeLists.txt
index bfe6b13,0000000..206f3c6
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/CMakeLists.txt
+++ b/Utilities/cmlibarchive/CMakeLists.txt
@@@ -1,1452 -1,0 +1,1641 @@@
 +PROJECT(libarchive C)
 +#
 +SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake")
 +if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
 +  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${libarchive_BINARY_DIR}/bin)
 +endif()
 +
 +# On MacOS, prefer MacPorts libraries to system libraries.
 +# I haven't come up with a compelling argument for this to be conditional.
 +list(APPEND CMAKE_PREFIX_PATH /opt/local)
 +# Enable @rpath in the install name.
 +# detail in "cmake  --help-policy CMP0042"
 +SET(CMAKE_MACOSX_RPATH ON)
 +
 +#
 +# Version - read from 'version' file.
 +#
 +FILE(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/build/version _version)
 +STRING(REGEX REPLACE
 + "^([0-9])[0-9][0-9][0-9][0-9][0-9][0-9][a-z]*$" "\\1" _major ${_version})
 +STRING(REGEX REPLACE
 + "^[0-9]([0-9][0-9][0-9])[0-9][0-9][0-9][a-z]*$" "\\1" _minor ${_version})
 +STRING(REGEX REPLACE
 + "^[0-9][0-9][0-9][0-9]([0-9][0-9][0-9])[a-z]*$" "\\1" _revision ${_version})
 +STRING(REGEX REPLACE
 + "^[0-9][0-9][0-9][0-9][0-9][0-9][0-9]([a-z]*)$" "\\1" _quality ${_version})
 +SET(_version_number ${_major}${_minor}${_revision})
 +STRING(REGEX REPLACE "[0]*([^0]*[0-9])$" "\\1" _trimmed_minor ${_minor})
 +STRING(REGEX REPLACE "[0]*([^0]*[0-9])$" "\\1" _trimmed_revision ${_revision})
 +#
 +SET(VERSION                    "${_major}.${_trimmed_minor}.${_trimmed_revision}${_quality}")
 +SET(BSDCPIO_VERSION_STRING     "${VERSION}")
 +SET(BSDTAR_VERSION_STRING      "${VERSION}")
 +SET(BSDCAT_VERSION_STRING      "${VERSION}")
 +SET(LIBARCHIVE_VERSION_NUMBER  "${_version_number}")
 +SET(LIBARCHIVE_VERSION_STRING  "${VERSION}")
 +
 +# INTERFACE_VERSION increments with every release
 +# libarchive 2.7 == interface version 9 = 2 + 7
 +# libarchive 2.8 == interface version 10 = 2 + 8
 +# libarchive 2.9 == interface version 11 = 2 + 9
 +# libarchive 3.0 == interface version 12
 +# libarchive 3.1 == interface version 13
 +math(EXPR INTERFACE_VERSION  "13 + ${_minor}")
 +
 +# Set SOVERSION == Interface version
 +# ?? Should there be more here ??
 +SET(SOVERSION "${INTERFACE_VERSION}")
 +
 +# Enalbe CMAKE_PUSH_CHECK_STATE() and CMAKE_POP_CHECK_STATE() macros
 +# saving and restoring the state of the variables.
 +INCLUDE(${CMake_SOURCE_DIR}/Modules/CMakePushCheckState.cmake)
 +
 +# Initialize the state of the variables. This initialization is not
 +# necessary but this shows you what value the variables initially have.
 +SET(CMAKE_REQUIRED_DEFINITIONS)
 +SET(CMAKE_REQUIRED_INCLUDES)
 +SET(CMAKE_REQUIRED_LIBRARIES)
 +SET(CMAKE_REQUIRED_FLAGS)
 +
 +# Disable warnings to avoid changing 3rd party code.
 +IF(CMAKE_C_COMPILER_ID MATCHES
 +    "^(GNU|Clang|AppleClang|XL|VisualAge|SunPro|MIPSpro|HP|Intel)$")
 +  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w")
 +ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
 +  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
 +ENDIF()
 +
 +# Enable CTest/CDash support
 +include(CTest)
 +
 +OPTION(ENABLE_NETTLE "Enable use of Nettle" ON)
 +OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON)
 +OPTION(ENABLE_LZO "Enable the use of the system LZO library if found" OFF)
 +OPTION(ENABLE_LZMA "Enable the use of the system LZMA library if found" ON)
 +
 +OPTION(ENABLE_ZLIB "Enable the use of the system ZLIB library if found" ON)
 +OPTION(ENABLE_BZip2 "Enable the use of the system BZip2 library if found" ON)
 +OPTION(ENABLE_LIBXML2 "Enable the use of the system libxml2 library if found" ON)
 +OPTION(ENABLE_EXPAT "Enable the use of the system EXPAT library if found" ON)
 +OPTION(ENABLE_PCREPOSIX "Enable the use of the system PCREPOSIX library if found" ON)
 +OPTION(ENABLE_LibGCC "Enable the use of the system LibGCC library if found" ON)
 +# CNG is used for encrypt/decrypt Zip archives on Windows.
 +OPTION(ENABLE_CNG "Enable the use of CNG(Crypto Next Generation)" ON)
 +
 +OPTION(ENABLE_XATTR "Enable extended attribute support" ON)
 +OPTION(ENABLE_ACL "Enable ACL support" ON)
 +OPTION(ENABLE_ICONV "Enable iconv support" ON)
 +
 +IF(WIN32)
 +  #ELSEIF(WINDOWS_VERSION STREQUAL "WINXP")
 +  SET(NTDDI_VERSION 0x05010000)
 +  SET(_WIN32_WINNT 0x0501)
 +  SET(WINVER 0x0501)
 +ENDIF(WIN32)
 +
 +set(HAVE_PTHREAD_H 0) # no threads in CMake
 +
 +IF("${CMAKE_C_PLATFORM_ID}" MATCHES "^(HP-UX)$")
 +  ADD_DEFINITIONS(-D_XOPEN_SOURCE=500) # Ask wchar.h for mbstate_t
 +ENDIF()
 +
 +#
 +INCLUDE(CheckCSourceCompiles)
 +INCLUDE(CheckCSourceRuns)
 +INCLUDE(CheckFileOffsetBits)
 +INCLUDE(CheckFuncs)
 +INCLUDE(CheckHeaderDirent)
 +INCLUDE(CheckIncludeFile)
 +INCLUDE(CheckIncludeFiles)
 +INCLUDE(CheckLibraryExists)
 +INCLUDE(CheckStructHasMember)
 +INCLUDE(CheckSymbolExists)
 +INCLUDE(CheckTypeExists)
 +INCLUDE(CheckTypeSize)
 +
 +#
 +# Generate list.h
 +#
 +MACRO (GENERATE_LIST_H _listfile _cmlist __list_sources)
 +  SET(_argv ${ARGV})
 +  # Remove _listfile and _cmlist from _argv
 +  LIST(REMOVE_AT _argv 0 1)
 +  IF (NOT EXISTS "${_listfile}" OR
 +     ${_cmlist} IS_NEWER_THAN "${_listfile}")
 +
 +    MESSAGE(STATUS "Generating ${_listfile}")
 +    FILE(WRITE ${_listfile} "")
 +    FOREACH (testfile ${_argv})
 +      IF (testfile MATCHES "^test_[^/]+[.]c$")
 +        FILE(STRINGS ${testfile} testvar REGEX "^DEFINE_TEST")
 +        FOREACH (deftest ${testvar})
 +          FILE(APPEND ${_listfile} "${deftest}\n")
 +        ENDFOREACH (deftest)
 +      ENDIF (testfile MATCHES "^test_[^/]+[.]c$")
 +    ENDFOREACH (testfile)
 +
 +  ENDIF (NOT EXISTS "${_listfile}" OR
 +     ${_cmlist} IS_NEWER_THAN "${_listfile}")
 +ENDMACRO (GENERATE_LIST_H)
 +#
 +# Generate installation rules for man pages.
 +#
 +MACRO (INSTALL_MAN __mans)
 +  FOREACH (_man ${ARGV})
 +    STRING(REGEX REPLACE "^.+[.]([1-9])" "\\1" _mansect ${_man})
 +    INSTALL(FILES ${_man} DESTINATION "share/man/man${_mansect}")
 +  ENDFOREACH (_man)
 +ENDMACRO (INSTALL_MAN __mans)
 +#
 +# Find out what macro is needed to use libraries on Windows.
 +#
 +MACRO (TRY_MACRO_FOR_LIBRARY INCLUDES LIBRARIES
 +       TRY_TYPE SAMPLE_SOURCE MACRO_LIST)
 +  IF(WIN32 AND NOT CYGWIN)
 +    CMAKE_PUSH_CHECK_STATE()	# Save the state of the variables
 +    SET(CMAKE_REQUIRED_INCLUDES ${INCLUDES})
 +    SET(CMAKE_REQUIRED_LIBRARIES ${LIBRARIES})
 +    FOREACH(VAR ${MACRO_LIST})
 +      # Clear ${VAR} from CACHE If the libraries which ${VAR} was
 +      # checked with are changed.
 +      SET(VAR_WITH_LIB "${VAR}_WITH_LIB")
 +      GET_PROPERTY(PREV_VAR_WITH_LIB VARIABLE PROPERTY ${VAR_WITH_LIB})
 +      IF(NOT "${PREV_VAR_WITH_LIB}" STREQUAL "${LIBRARIES}")
 +        UNSET(${VAR} CACHE)
 +      ENDIF(NOT "${PREV_VAR_WITH_LIB}" STREQUAL "${LIBRARIES}")
 +      # Check if the library can be used with the macro.
 +      IF("${TRY_TYPE}" MATCHES "COMPILES")
 +        CHECK_C_SOURCE_COMPILES("${SAMPLE_SOURCE}" ${VAR})
 +      ELSEIF("${TRY_TYPE}" MATCHES "RUNS")
 +        CHECK_C_SOURCE_RUNS("${SAMPLE_SOURCE}" ${VAR})
 +      ELSE("${TRY_TYPE}" MATCHES "COMPILES")
 +        MESSAGE(FATAL_ERROR "UNKNOWN KEYWORD \"${TRY_TYPE}\" FOR TRY_TYPE")
 +      ENDIF("${TRY_TYPE}" MATCHES "COMPILES")
 +      # Save the libraries which ${VAR} is checked with.
 +      SET(${VAR_WITH_LIB} "${LIBRARIES}" CACHE INTERNAL
 +          "Macro ${VAR} is checked with")
 +    ENDFOREACH(VAR)
 +    CMAKE_POP_CHECK_STATE()	# Restore the state of the variables
 +  ENDIF(WIN32 AND NOT CYGWIN)
 +ENDMACRO (TRY_MACRO_FOR_LIBRARY)
 +#
 +# Check compress/decompress libraries
 +#
 +IF(WIN32 AND NOT CMAKE_CL_64 AND NOT CYGWIN)
 +  # GnuWin32 is only for Win32, not Win64.
 +  SET(__GNUWIN32PATH "C:/Program Files/GnuWin32")
 +ENDIF(WIN32 AND NOT CMAKE_CL_64 AND NOT CYGWIN)
 +IF(DEFINED __GNUWIN32PATH AND EXISTS "${__GNUWIN32PATH}")
 +  # You have to add a path availabel DLL file into PATH environment variable.
 +  # Maybe DLL path is "C:/Program Files/GnuWin32/bin".
 +  # The zlib and the bzip2 Setup program have installed programs and DLLs into
 +  # "C:/Program Files/GnuWin32" by default.
 +  # This is convenience setting for Windows.
 +  SET(CMAKE_PREFIX_PATH ${__GNUWIN32PATH} $(CMAKE_PREFIX_PATH))
 +  #
 +  # If you didn't use Setup program or installed into nonstandard path,
 +  # cmake cannot find out your zlib or bzip2 libraries and include files,
 +  # you should execute cmake with  -DCMAKE_PREFIX_PATH option.
 +  #   e.g.
 +  #     cmake -DCMAKE_PREFIX_PATH=<your-GnuWin32-path> <path-to-source>
 +  #
 +  # If compiling error occurred in zconf.h, You may need patch to zconf.h.
 +  #--- zconf.h.orig	2005-07-21 00:40:26.000000000
 +  #+++ zconf.h	2009-01-19 11:39:10.093750000
 +  #@@ -286,7 +286,7 @@
 +  #
 +  # #if 1           /* HAVE_UNISTD_H -- this line is updated by ./configure */
 +  # #  include <sys/types.h> /* for off_t */
 +  #-#  include <unistd.h>    /* for SEEK_* and off_t */
 +  #+#  include <stdio.h>    /* for SEEK_* and off_t */
 +  # #  ifdef VMS
 +  # #    include <unixio.h>   /* for off_t */
 +  # #  endif
 +ENDIF(DEFINED __GNUWIN32PATH AND EXISTS "${__GNUWIN32PATH}")
 +
 +SET(ADDITIONAL_LIBS "")
 +#
 +# Find ZLIB
 +#
 +IF(ENABLE_ZLIB)
 +  FIND_PACKAGE(ZLIB)
 +ELSE()
 +  SET(ZLIB_FOUND FALSE) # Override cached value
 +ENDIF()
 +IF(ZLIB_FOUND)
 +  SET(HAVE_LIBZ 1)
 +  SET(HAVE_ZLIB_H 1)
 +  INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
 +  LIST(APPEND ADDITIONAL_LIBS ${ZLIB_LIBRARIES})
 +  IF(WIN32 AND NOT CYGWIN)
 +    #
 +    # Test if ZLIB_WINAPI macro is needed to use.
 +    #
 +    TRY_MACRO_FOR_LIBRARY(
 +      "${ZLIB_INCLUDE_DIR}" "${ZLIB_LIBRARIES}"
 +      RUNS
 +      "#include <zlib.h>\nint main() {uLong f = zlibCompileFlags(); return (f&(1U<<10))?0:-1; }"
 +      ZLIB_WINAPI)
 +    IF(ZLIB_WINAPI)
 +      ADD_DEFINITIONS(-DZLIB_WINAPI)
 +    ELSE(ZLIB_WINAPI)
 +      # Test if a macro is needed for the library.
 +      TRY_MACRO_FOR_LIBRARY(
 +        "${ZLIB_INCLUDE_DIR}" "${ZLIB_LIBRARIES}"
 +        COMPILES
 +        "#include <zlib.h>\nint main() {return zlibVersion()?1:0; }"
 +        "ZLIB_DLL;WITHOUT_ZLIB_DLL")
 +      IF(ZLIB_DLL)
 +        ADD_DEFINITIONS(-DZLIB_DLL)
 +      ENDIF(ZLIB_DLL)
 +    ENDIF(ZLIB_WINAPI)
 +  ENDIF(WIN32 AND NOT CYGWIN)
 +ELSE(ZLIB_FOUND)
 +  MESSAGE(FATAL_ERROR "CMake requires zlib to be available to libarchive")
 +ENDIF(ZLIB_FOUND)
 +#
 +# Find BZip2
 +#
 +IF(ENABLE_BZip2)
 +  FIND_PACKAGE(BZip2)
 +ELSE()
 +  SET(BZIP2_FOUND FALSE) # Override cached value
 +ENDIF()
 +IF(BZIP2_FOUND)
 +  SET(HAVE_LIBBZ2 1)
 +  SET(HAVE_BZLIB_H 1)
 +  INCLUDE_DIRECTORIES(${BZIP2_INCLUDE_DIR})
 +  LIST(APPEND ADDITIONAL_LIBS ${BZIP2_LIBRARIES})
 +  # Test if a macro is needed for the library.
 +  TRY_MACRO_FOR_LIBRARY(
 +    "${BZIP2_INCLUDE_DIR}" "${BZIP2_LIBRARIES}"
 +    COMPILES
 +    "#include <bzlib.h>\nint main() {return BZ2_bzlibVersion()?1:0; }"
 +    "USE_BZIP2_DLL;USE_BZIP2_STATIC")
 +  IF(USE_BZIP2_DLL)
 +    ADD_DEFINITIONS(-DUSE_BZIP2_DLL)
 +  ELSEIF(USE_BZIP2_STATIC)
 +    ADD_DEFINITIONS(-DUSE_BZIP2_STATIC)
 +  ENDIF(USE_BZIP2_DLL)
 +ENDIF(BZIP2_FOUND)
 +MARK_AS_ADVANCED(CLEAR BZIP2_INCLUDE_DIR)
 +MARK_AS_ADVANCED(CLEAR BZIP2_LIBRARIES)
 +
 +
 +#
 +# Find LZMA
 +#
 +IF(ENABLE_LZMA)
 +  FIND_PACKAGE(LibLZMA)
 +ELSE()
 +  SET(LIBZMA_FOUND FALSE) # Override cached value
 +ENDIF()
 +
 +IF(LIBLZMA_FOUND)
 +  SET(HAVE_LIBLZMA 1)
 +  SET(HAVE_LZMA_H 1)
 +  INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIRS})
 +  LIST(APPEND ADDITIONAL_LIBS ${LIBLZMA_LIBRARIES})
 +  IF(CMAKE_USE_SYSTEM_LIBLZMA)
 +    # Test if a macro is needed for the library.
 +    TRY_MACRO_FOR_LIBRARY(
 +      "${LIBLZMA_INCLUDE_DIRS}" "${LIBLZMA_LIBRARIES}"
 +      COMPILES
 +      "#include <lzma.h>\nint main() {return (int)lzma_version_number(); }"
 +      "WITHOUT_LZMA_API_STATIC;LZMA_API_STATIC")
 +    IF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC)
 +      ADD_DEFINITIONS(-DLZMA_API_STATIC)
 +    ENDIF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC)
 +  ELSE()
 +    ADD_DEFINITIONS(-DLZMA_API_STATIC)
 +  ENDIF()
 +ELSE(LIBLZMA_FOUND)
 +# LZMA not found and will not be used.
 +ENDIF(LIBLZMA_FOUND)
 +IF(0) # CMake does not need LZO2 support in libarchive
 +#
 +# Find LZO2
 +#
 +IF(ENABLE_LZO)
 +  IF (LZO2_INCLUDE_DIR)
 +    # Already in cache, be silent
 +    SET(LZO2_FIND_QUIETLY TRUE)
 +  ENDIF (LZO2_INCLUDE_DIR)
 +
 +  FIND_PATH(LZO2_INCLUDE_DIR lzo/lzoconf.h)
 +  FIND_LIBRARY(LZO2_LIBRARY NAMES lzo2 liblzo2)
 +  INCLUDE(FindPackageHandleStandardArgs)
 +  FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZO2 DEFAULT_MSG LZO2_LIBRARY LZO2_INCLUDE_DIR)
 +ELSE(ENABLE_LZO)
 +  SET(LIBZMA_FOUND FALSE) # Override cached value
 +ENDIF(ENABLE_LZO)
 +IF(LZO2_FOUND)
 +  SET(HAVE_LIBLZO2 1)
 +  SET(HAVE_LZO_LZOCONF_H 1)
 +  SET(HAVE_LZO_LZO1X_H 1)
 +  INCLUDE_DIRECTORIES(${LZO2_INCLUDE_DIR})
 +  LIST(APPEND ADDITIONAL_LIBS ${LZO2_LIBRARY})
 +  #
 +  # TODO: test for static library.
 +  #
 +ENDIF(LZO2_FOUND)
 +MARK_AS_ADVANCED(CLEAR LZO2_INCLUDE_DIR)
 +MARK_AS_ADVANCED(CLEAR LZO2_LIBRARY)
 +ENDIF()
 +IF(0) # CMake does not need LZ4 support in libarchive
 +#
 +# Find LZ4
 +#
 +IF (LZ4_INCLUDE_DIR)
 +  # Already in cache, be silent
 +  SET(LZ4_FIND_QUIETLY TRUE)
 +ENDIF (LZ4_INCLUDE_DIR)
 +
 +FIND_PATH(LZ4_INCLUDE_DIR lz4.h)
 +FIND_LIBRARY(LZ4_LIBRARY NAMES lz4 liblz4)
 +INCLUDE(FindPackageHandleStandardArgs)
 +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZ4 DEFAULT_MSG LZ4_LIBRARY LZ4_INCLUDE_DIR)
 +IF(LZ4_FOUND)
 +  SET(HAVE_LIBLZ4 1)
 +  SET(HAVE_LZ4_H 1)
 +  CMAKE_PUSH_CHECK_STATE()	# Save the state of the variables
 +  SET(CMAKE_REQUIRED_INCLUDES ${LZ4_INCLUDE_DIR})
 +  CHECK_INCLUDE_FILES("lz4hc.h" HAVE_LZ4HC_H)
 +  CMAKE_POP_CHECK_STATE()	# Restore the state of the variables
 +  INCLUDE_DIRECTORIES(${LZ4_INCLUDE_DIR})
 +  LIST(APPEND ADDITIONAL_LIBS ${LZ4_LIBRARY})
 +  #
 +  # TODO: test for static library.
 +  #
 +ENDIF(LZ4_FOUND)
 +MARK_AS_ADVANCED(CLEAR LZ4_INCLUDE_DIR)
 +MARK_AS_ADVANCED(CLEAR LZ4_LIBRARY)
 +ENDIF()
 +
 +#
 +# Check headers
 +#
 +CHECK_HEADER_DIRENT()
 +
 +SET(INCLUDES "")
 +MACRO (LA_CHECK_INCLUDE_FILE header var)
 +      CHECK_INCLUDE_FILES("${INCLUDES};${header}" ${var})
 +      IF (${var})
 +      	 SET(INCLUDES ${INCLUDES} ${header})
 +      ENDIF (${var})
 +ENDMACRO (LA_CHECK_INCLUDE_FILE)
 +
 +# Some FreeBSD headers assume sys/types.h was already included.
 +LA_CHECK_INCLUDE_FILE("sys/types.h" HAVE_SYS_TYPES_H)
 +
 +# Alphabetize the rest unless there's a compelling reason
 +IF(ENABLE_ACL)
 +  LA_CHECK_INCLUDE_FILE("acl/libacl.h" HAVE_ACL_LIBACL_H)
 +ELSE(ENABLE_ACL)
 +  SET(HAVE_ACL_LIBACL_H FALSE)
 +ENDIF(ENABLE_ACL)
 +LA_CHECK_INCLUDE_FILE("ctype.h" HAVE_CTYPE_H)
 +LA_CHECK_INCLUDE_FILE("copyfile.h" HAVE_COPYFILE_H)
 +LA_CHECK_INCLUDE_FILE("direct.h" HAVE_DIRECT_H)
 +LA_CHECK_INCLUDE_FILE("dlfcn.h" HAVE_DLFCN_H)
 +LA_CHECK_INCLUDE_FILE("errno.h" HAVE_ERRNO_H)
 +LA_CHECK_INCLUDE_FILE("ext2fs/ext2_fs.h" HAVE_EXT2FS_EXT2_FS_H)
 +
 +CHECK_C_SOURCE_COMPILES("#include <sys/ioctl.h>
 +#include <ext2fs/ext2_fs.h>
 +int main(void) { return EXT2_IOC_GETFLAGS; }" HAVE_WORKING_EXT2_IOC_GETFLAGS)
 +
 +LA_CHECK_INCLUDE_FILE("fcntl.h" HAVE_FCNTL_H)
 +LA_CHECK_INCLUDE_FILE("grp.h" HAVE_GRP_H)
 +LA_CHECK_INCLUDE_FILE("inttypes.h" HAVE_INTTYPES_H)
 +LA_CHECK_INCLUDE_FILE("io.h" HAVE_IO_H)
 +LA_CHECK_INCLUDE_FILE("langinfo.h" HAVE_LANGINFO_H)
 +LA_CHECK_INCLUDE_FILE("limits.h" HAVE_LIMITS_H)
 +LA_CHECK_INCLUDE_FILE("linux/types.h" HAVE_LINUX_TYPES_H)
 +LA_CHECK_INCLUDE_FILE("linux/fiemap.h" HAVE_LINUX_FIEMAP_H)
 +LA_CHECK_INCLUDE_FILE("linux/fs.h" HAVE_LINUX_FS_H)
 +
 +CHECK_C_SOURCE_COMPILES("#include <sys/ioctl.h>
 +#include <linux/fs.h>
 +int main(void) { return FS_IOC_GETFLAGS; }" HAVE_WORKING_FS_IOC_GETFLAGS)
 +
 +LA_CHECK_INCLUDE_FILE("linux/magic.h" HAVE_LINUX_MAGIC_H)
 +LA_CHECK_INCLUDE_FILE("locale.h" HAVE_LOCALE_H)
++LA_CHECK_INCLUDE_FILE("membership.h" HAVE_MEMBERSHIP_H)
 +LA_CHECK_INCLUDE_FILE("memory.h" HAVE_MEMORY_H)
 +LA_CHECK_INCLUDE_FILE("paths.h" HAVE_PATHS_H)
 +LA_CHECK_INCLUDE_FILE("poll.h" HAVE_POLL_H)
 +LA_CHECK_INCLUDE_FILE("process.h" HAVE_PROCESS_H)
 +LA_CHECK_INCLUDE_FILE("pthread.h" HAVE_PTHREAD_H)
 +LA_CHECK_INCLUDE_FILE("pwd.h" HAVE_PWD_H)
 +LA_CHECK_INCLUDE_FILE("readpassphrase.h" HAVE_READPASSPHRASE_H)
 +LA_CHECK_INCLUDE_FILE("regex.h" HAVE_REGEX_H)
 +LA_CHECK_INCLUDE_FILE("signal.h" HAVE_SIGNAL_H)
 +LA_CHECK_INCLUDE_FILE("spawn.h" HAVE_SPAWN_H)
 +LA_CHECK_INCLUDE_FILE("stdarg.h" HAVE_STDARG_H)
 +LA_CHECK_INCLUDE_FILE("stdint.h" HAVE_STDINT_H)
 +LA_CHECK_INCLUDE_FILE("stdlib.h" HAVE_STDLIB_H)
 +LA_CHECK_INCLUDE_FILE("string.h" HAVE_STRING_H)
 +LA_CHECK_INCLUDE_FILE("strings.h" HAVE_STRINGS_H)
 +LA_CHECK_INCLUDE_FILE("sys/acl.h" HAVE_SYS_ACL_H)
 +LA_CHECK_INCLUDE_FILE("sys/cdefs.h" HAVE_SYS_CDEFS_H)
++LA_CHECK_INCLUDE_FILE("sys/extattr.h" HAVE_SYS_EXTATTR_H)
 +LA_CHECK_INCLUDE_FILE("sys/ioctl.h" HAVE_SYS_IOCTL_H)
 +LA_CHECK_INCLUDE_FILE("sys/mkdev.h" HAVE_SYS_MKDEV_H)
 +LA_CHECK_INCLUDE_FILE("sys/mount.h" HAVE_SYS_MOUNT_H)
 +LA_CHECK_INCLUDE_FILE("sys/param.h" HAVE_SYS_PARAM_H)
 +LA_CHECK_INCLUDE_FILE("sys/poll.h" HAVE_SYS_POLL_H)
++LA_CHECK_INCLUDE_FILE("sys/richacl.h" HAVE_SYS_RICHACL_H)
 +LA_CHECK_INCLUDE_FILE("sys/select.h" HAVE_SYS_SELECT_H)
 +LA_CHECK_INCLUDE_FILE("sys/stat.h" HAVE_SYS_STAT_H)
 +LA_CHECK_INCLUDE_FILE("sys/statfs.h" HAVE_SYS_STATFS_H)
 +LA_CHECK_INCLUDE_FILE("sys/statvfs.h" HAVE_SYS_STATVFS_H)
 +LA_CHECK_INCLUDE_FILE("sys/time.h" HAVE_SYS_TIME_H)
 +LA_CHECK_INCLUDE_FILE("sys/utime.h" HAVE_SYS_UTIME_H)
 +LA_CHECK_INCLUDE_FILE("sys/utsname.h" HAVE_SYS_UTSNAME_H)
 +LA_CHECK_INCLUDE_FILE("sys/vfs.h" HAVE_SYS_VFS_H)
 +LA_CHECK_INCLUDE_FILE("sys/wait.h" HAVE_SYS_WAIT_H)
++LA_CHECK_INCLUDE_FILE("sys/xattr.h" HAVE_SYS_XATTR_H)
 +LA_CHECK_INCLUDE_FILE("time.h" HAVE_TIME_H)
 +LA_CHECK_INCLUDE_FILE("unistd.h" HAVE_UNISTD_H)
 +LA_CHECK_INCLUDE_FILE("utime.h" HAVE_UTIME_H)
 +LA_CHECK_INCLUDE_FILE("wchar.h" HAVE_WCHAR_H)
 +LA_CHECK_INCLUDE_FILE("wctype.h" HAVE_WCTYPE_H)
 +LA_CHECK_INCLUDE_FILE("windows.h" HAVE_WINDOWS_H)
 +IF(ENABLE_CNG)
 +  LA_CHECK_INCLUDE_FILE("Bcrypt.h" HAVE_BCRYPT_H)
++  IF(HAVE_BCRYPT_H)
++    LIST(APPEND ADDITIONAL_LIBS "Bcrypt")
++  ENDIF(HAVE_BCRYPT_H)
 +ELSE(ENABLE_CNG)
 +  UNSET(HAVE_BCRYPT_H CACHE)
 +ENDIF(ENABLE_CNG)
 +# Following files need windows.h, so we should test it after windows.h test.
 +LA_CHECK_INCLUDE_FILE("wincrypt.h" HAVE_WINCRYPT_H)
 +LA_CHECK_INCLUDE_FILE("winioctl.h" HAVE_WINIOCTL_H)
 +
 +#
 +# Check whether use of __EXTENSIONS__ is safe.
 +# We need some macro such as _GNU_SOURCE to use extension functions.
 +#
 +SET(_INCLUDE_FILES)
 +FOREACH (it ${_HEADER})
 +   SET(_INCLUDE_FILES "${_INCLUDE_FILES}#include <${it}>\n")
 +ENDFOREACH (it)
 +
 +CHECK_C_SOURCE_COMPILES(
 +  "#define __EXTENSIONS__ 1
 +   ${_INCLUDE_FILES}
 +   int main() { return 0;}"
 + SAFE_TO_DEFINE_EXTENSIONS)
 +
 +#
 +# Find Nettle
 +#
 +IF(ENABLE_NETTLE)
 +  FIND_PACKAGE(Nettle)
 +  IF(NETTLE_FOUND)
 +    SET(HAVE_LIBNETTLE 1)
 +    LIST(APPEND ADDITIONAL_LIBS ${NETTLE_LIBRARIES})
 +    INCLUDE_DIRECTORIES(${NETTLE_INCLUDE_DIR})
 +
 +    LIST(APPEND CMAKE_REQUIRED_INCLUDES ${NETTLE_INCLUDE_DIR})
 +    LA_CHECK_INCLUDE_FILE("nettle/aes.h" HAVE_NETTLE_AES_H)
 +    LA_CHECK_INCLUDE_FILE("nettle/hmac.h" HAVE_NETTLE_HMAC_H)
 +    LA_CHECK_INCLUDE_FILE("nettle/md5.h" HAVE_NETTLE_MD5_H)
 +    LA_CHECK_INCLUDE_FILE("nettle/pbkdf2.h" HAVE_NETTLE_PBKDF2_H)
 +    LA_CHECK_INCLUDE_FILE("nettle/ripemd160.h" HAVE_NETTLE_RIPEMD160_H)
 +    LA_CHECK_INCLUDE_FILE("nettle/sha.h" HAVE_NETTLE_SHA_H)
 +
 +  ENDIF(NETTLE_FOUND)
 +  MARK_AS_ADVANCED(CLEAR NETTLE_INCLUDE_DIR)
 +  MARK_AS_ADVANCED(CLEAR NETTLE_LIBRARIES)
 +ENDIF(ENABLE_NETTLE)
 +
 +#
 +# Find OpenSSL
 +# (Except on Mac, where OpenSSL is deprecated.)
 +#
 +IF(ENABLE_OPENSSL AND NOT CMAKE_SYSTEM_NAME MATCHES "Darwin")
 +  FIND_PACKAGE(OpenSSL)
 +  IF(OPENSSL_FOUND)
 +    SET(HAVE_LIBCRYPTO 1)
 +    INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
 +  ENDIF(OPENSSL_FOUND)
 +ELSE()
 +  SET(OPENSSL_FOUND FALSE) # Override cached value
 +ENDIF()
 +
 +# FreeBSD libmd
 +IF(NOT OPENSSL_FOUND)
 +  CHECK_LIBRARY_EXISTS(md "MD5Init" "" LIBMD_FOUND)
 +  IF(LIBMD_FOUND)
 +    CMAKE_PUSH_CHECK_STATE()	# Save the state of the variables
 +    SET(CMAKE_REQUIRED_LIBRARIES "md")
 +    FIND_LIBRARY(LIBMD_LIBRARY NAMES md)
 +    LIST(APPEND ADDITIONAL_LIBS ${LIBMD_LIBRARY})
 +    CMAKE_POP_CHECK_STATE()	# Restore the state of the variables
 +  ENDIF(LIBMD_FOUND)
 +ENDIF(NOT OPENSSL_FOUND)
 +
 +#
 +# How to prove that CRYPTO functions, which have several names on various
 +# platforms, just see if archive_digest.c can compile and link against
 +# required libraries.
 +#
 +MACRO(CHECK_CRYPTO ALGORITHMS IMPLEMENTATION)
 +    FOREACH(ALGORITHM ${ALGORITHMS})
 +      IF(NOT ARCHIVE_CRYPTO_${ALGORITHM})
 +      STRING(TOLOWER "${ALGORITHM}" lower_algorithm)
 +      STRING(TOUPPER "${ALGORITHM}" algorithm)
 +      IF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND NOT OPENSSL_FOUND)
 +        SET(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} FALSE)
 +      ELSEIF("${IMPLEMENTATION}" MATCHES "^NETTLE$" AND NOT NETTLE_FOUND)
 +        SET(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} FALSE)
 +      ENDIF("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND NOT OPENSSL_FOUND)
 +
 +      IF(NOT DEFINED ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION})
 +        # Probe the local implementation for whether this
 +	# crypto implementation is available on this platform.
 +	SET(TRY_CRYPTO_REQUIRED_INCLUDES
 +	  "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_BINARY_DIR};${CMAKE_CURRENT_SOURCE_DIR}/libarchive;${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp")
 +	SET(TRY_CRYPTO_REQUIRED_LIBS)
 +	IF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND OPENSSL_FOUND)
 +	    SET(TRY_CRYPTO_REQUIRED_INCLUDES
 +	      "${TRY_CRYPTO_REQUIRED_INCLUDES};${OPENSSL_INCLUDE_DIR}")
 +	    SET(TRY_CRYPTO_REQUIRED_LIBS
 +	        "-DLINK_LIBRARIES:STRING=${OPENSSL_LIBRARIES}")
 +	ELSEIF("${IMPLEMENTATION}" MATCHES "^NETTLE$" AND NETTLE_FOUND)
 +	    SET(TRY_CRYPTO_REQUIRED_INCLUDES
 +	      "${TRY_CRYPTO_REQUIRED_INCLUDES};${NETTLE_INCLUDE_DIR}")
 +	    SET(TRY_CRYPTO_REQUIRED_LIBS
 +	        "-DLINK_LIBRARIES:STRING=${NETTLE_LIBRARY}")
 +	ELSEIF("${IMPLEMENTATION}" MATCHES "^LIBMD$" AND LIBMD_FOUND)
 +	    SET(TRY_CRYPTO_REQUIRED_LIBS
 +	        "-DLINK_LIBRARIES:STRING=${LIBMD_LIBRARY}")
 +	ENDIF("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND OPENSSL_FOUND)
 +
 +    CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in
 +      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/confdefs.h)
 +	FILE(READ "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/confdefs.h"
 +	     CONFDEFS_H)
 +	FILE(READ "${CMAKE_CURRENT_SOURCE_DIR}/libarchive/archive_digest.c"
 +	     ARCHIVE_CRYPTO_C)
 +
 +	SET(SOURCE "${CONFDEFS_H}
 +
 +#define ARCHIVE_${algorithm}_COMPILE_TEST
 +#define ARCHIVE_CRYPTO_${algorithm}_${IMPLEMENTATION}
 +#define PLATFORM_CONFIG_H \"check_crypto_md.h\"
 +
 +${ARCHIVE_CRYPTO_C}
 +
 +int
 +main(int argc, char **argv)
 +{
 +  archive_${lower_algorithm}_ctx ctx;
 +  archive_${lower_algorithm}_init(&ctx);
 +  archive_${lower_algorithm}_update(&ctx, *argv, argc);
 +  archive_${lower_algorithm}_final(&ctx, NULL);
 +  return 0;
 +}
 +")
 +
 +  FILE(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check_crypto_md.h" "")
 +	FILE(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check_crypto_md.c" "${SOURCE}")
 +	MESSAGE(STATUS "Checking support for ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION}")
 +
 +	TRY_COMPILE(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION}
 +	  ${CMAKE_BINARY_DIR}
 +	  ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check_crypto_md.c
 +	  CMAKE_FLAGS
 +	   "${TRY_CRYPTO_REQUIRED_LIBS}"
 +	   "${TRY_CRYPTO_REQUIRED_INCLUDES}"
 +	  OUTPUT_VARIABLE OUTPUT)
 +
 +	# Inform user whether or not we found it; if not, log why we didn't.
 +        IF (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION})
 +          MESSAGE(STATUS "Checking support for ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} -- found")
 +		  SET(ARCHIVE_CRYPTO_${ALGORITHM} 1)
 +        ELSE (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION})
 +          MESSAGE(STATUS "Checking support for ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} -- not found")
 +          FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
 +    	    "Checking support for ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} failed with the following output:\n"
 +    	    "${OUTPUT}\n"
 +    	    "Source file was:\n${SOURCE}\n")
 +        ENDIF (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION})
 +      ENDIF(NOT DEFINED ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION})
 +
 +      # Add appropriate libs/includes depending on whether the implementation
 +      # was found on this platform.
 +      IF (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION})
 +        IF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND OPENSSL_FOUND)
 +          INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
 +	  LIST(APPEND ADDITIONAL_LIBS ${OPENSSL_LIBRARIES})
 +	   LIST(REMOVE_DUPLICATES ADDITIONAL_LIBS)
 +        ENDIF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND OPENSSL_FOUND)
 +      ENDIF (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION})
 +      ENDIF(NOT ARCHIVE_CRYPTO_${ALGORITHM})
 +    ENDFOREACH(ALGORITHM ${ALGORITHMS})
 +ENDMACRO(CHECK_CRYPTO ALGORITHMS IMPLEMENTATION)
 +
 +#
 +# CRYPTO functions on Windows is defined at archive_windows.c, thus we do not
 +# need the test what the functions can be mapped to archive_{crypto name}_init,
 +# archive_{crypto name}_update and archive_{crypto name}_final.
 +# The functions on Windows use CALG_{crypto name} macro to create a crypt object
 +# and then we need to know what CALG_{crypto name} macros is available to show
 +# ARCHIVE_CRYPTO_{crypto name}_WIN macros because Windows 2000 and earlier version
 +# of Windows XP do not support SHA256, SHA384 and SHA512.
 +#
 +MACRO(CHECK_CRYPTO_WIN CRYPTO_LIST)
 +  IF(WIN32 AND NOT CYGWIN)
 +    FOREACH(CRYPTO ${CRYPTO_LIST})
 +      IF(NOT ARCHIVE_CRYPTO_${CRYPTO})
 +      IF(NOT DEFINED ARCHIVE_CRYPTO_${CRYPTO}_WIN)
 +	STRING(TOUPPER "${CRYPTO}" crypto)
 +	SET(ALGID "")
 +	IF ("${CRYPTO}" MATCHES "^MD5$")
 +	    SET(ALGID "CALG_MD5")
 +	ENDIF ("${CRYPTO}" MATCHES "^MD5$")
 +	IF ("${CRYPTO}" MATCHES "^SHA1$")
 +	    SET(ALGID "CALG_SHA1")
 +	ENDIF ("${CRYPTO}" MATCHES "^SHA1$")
 +	IF ("${CRYPTO}" MATCHES "^SHA256$")
 +	    SET(ALGID "CALG_SHA_256")
 +	ENDIF ("${CRYPTO}" MATCHES "^SHA256$")
 +	IF ("${CRYPTO}" MATCHES "^SHA384$")
 +	    SET(ALGID "CALG_SHA_384")
 +	ENDIF ("${CRYPTO}" MATCHES "^SHA384$")
 +	IF ("${CRYPTO}" MATCHES "^SHA512$")
 +	    SET(ALGID "CALG_SHA_512")
 +	ENDIF ("${CRYPTO}" MATCHES "^SHA512$")
 +
 +    CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in
 +      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/confdefs.h)
 +	FILE(READ "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/confdefs.h"
 +	     CONFDEFS_H)
 +
 +	SET(SOURCE "${CONFDEFS_H}
 +
 +#define ${crypto}_COMPILE_TEST
 +#include <windows.h>
 +#include <wincrypt.h>
 +
 +int
 +main(int argc, char **argv)
 +{
 +	return ${ALGID};
 +}
 +")
 +	SET(SOURCE_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check_crypto_win.c")
 +
 +	FILE(WRITE "${SOURCE_FILE}" "${SOURCE}")
 +	MESSAGE(STATUS "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN")
 +
 +	TRY_COMPILE(ARCHIVE_CRYPTO_${CRYPTO}_WIN
 +	  ${CMAKE_BINARY_DIR}
 +	  ${SOURCE_FILE}
 +	  CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_BINARY_DIR};${CMAKE_CURRENT_SOURCE_DIR}/libarchive"
 +	  OUTPUT_VARIABLE OUTPUT)
 +
 +	IF (ARCHIVE_CRYPTO_${CRYPTO}_WIN)
 +	    MESSAGE(STATUS
 +	        "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN -- found")
 +		SET(ARCHIVE_CRYPTO_${CRYPTO} 1)
 +	ELSE (ARCHIVE_CRYPTO_${CRYPTO}_WIN)
 +	    MESSAGE(STATUS
 +	         "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN -- not found")
 +    	    FILE(APPEND
 +	        ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
 +                "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN failed with the following output:\n"
 +        	"${OUTPUT}\n"
 +        	"Source file was:\n${SOURCE}\n")
 +	ENDIF (ARCHIVE_CRYPTO_${CRYPTO}_WIN)
 +
 +      ENDIF(NOT DEFINED ARCHIVE_CRYPTO_${CRYPTO}_WIN)
 +      ENDIF(NOT ARCHIVE_CRYPTO_${CRYPTO})
 +    ENDFOREACH(CRYPTO)
 +  ENDIF(WIN32 AND NOT CYGWIN)
 +ENDMACRO(CHECK_CRYPTO_WIN CRYPTO_LIST)
 +
 +#
 +# Find iconv
 +# POSIX defines the second arg as const char **
 +# and requires it to be in libc.  But we can accept
 +# a non-const argument here and can support iconv()
 +# being in libiconv.
 +#
 +MACRO(CHECK_ICONV LIB TRY_ICONV_CONST)
 +  IF(NOT HAVE_ICONV)
 +    CMAKE_PUSH_CHECK_STATE()	# Save the state of the variables
 +    IF (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR
 +        CMAKE_C_COMPILER_ID STREQUAL "Clang")
 +      #
 +      # During checking iconv proto type, we should use -Werror to avoid the
 +      # success of iconv detection with a warnig which success is a miss
 +      # detection. So this needs for all build mode(even it's a release mode).
 +      #
 +      SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror")
 +    ENDIF ()
 +    IF (CMAKE_C_COMPILER_ID STREQUAL "XL")
 +      SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -qhalt=w -qflag=w:w")
 +    ENDIF ()
 +    IF (MSVC)
 +      # NOTE: /WX option is the same as gcc's -Werror option.
 +      SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} /WX")
 +    ENDIF (MSVC)
 +    #
 +    CHECK_C_SOURCE_COMPILES(
 +      "#include <stdlib.h>
 +       #include <iconv.h>
 +       int main() {
 +          ${TRY_ICONV_CONST} char *ccp;
 +          iconv_t cd = iconv_open(\"\", \"\");
 +          iconv(cd, &ccp, (size_t *)0, (char **)0, (size_t *)0);
 +          iconv_close(cd);
 +          return 0;
 +       }"
 +     HAVE_ICONV_${LIB}_${TRY_ICONV_CONST})
 +    IF(HAVE_ICONV_${LIB}_${TRY_ICONV_CONST})
 +      SET(HAVE_ICONV true)
 +      SET(ICONV_CONST ${TRY_ICONV_CONST})
 +    ENDIF(HAVE_ICONV_${LIB}_${TRY_ICONV_CONST})
 +    CMAKE_POP_CHECK_STATE()	# Restore the state of the variables
 +  ENDIF(NOT HAVE_ICONV)
 +ENDMACRO(CHECK_ICONV TRY_ICONV_CONST)
 +
 +IF(ENABLE_ICONV)
 +  CMAKE_PUSH_CHECK_STATE()	# Save the state of the variables
 +  FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
 +  MARK_AS_ADVANCED(ICONV_INCLUDE_DIR)
 +  IF(ICONV_INCLUDE_DIR)
 +    #SET(INCLUDES ${INCLUDES} "iconv.h")
 +    SET(HAVE_ICONV_H 1)
 +    INCLUDE_DIRECTORIES(${ICONV_INCLUDE_DIR})
 +    SET(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR})
 +    CHECK_ICONV("libc" "const")
 +    CHECK_ICONV("libc" "")
 +
 +    # If iconv isn't in libc and we have a libiconv, try that.
 +    FIND_LIBRARY(LIBICONV_PATH NAMES iconv libiconv)
 +    IF(NOT HAVE_ICONV AND LIBICONV_PATH)
 +      LIST(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBICONV_PATH})
 +      # Test if a macro is needed for the library.
 +      TRY_MACRO_FOR_LIBRARY(
 +        "${ICONV_INCLUDE_DIR}" "${LIBICONV_PATH}"
 +        COMPILES
 +        "#include <iconv.h>\nint main() {return iconv_close((iconv_t)0);}"
 +        "WITHOUT_LIBICONV_STATIC;LIBICONV_STATIC")
 +      IF(NOT WITHOUT_LIBICONV_STATIC AND LIBICONV_STATIC)
 +        ADD_DEFINITIONS(-DLIBICONV_STATIC)
 +      ENDIF(NOT WITHOUT_LIBICONV_STATIC AND LIBICONV_STATIC)
 +      #
 +      # Set up CMAKE_REQUIRED_* for CHECK_ICONV
 +      #
 +      SET(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR})
 +      SET(CMAKE_REQUIRED_LIBRARIES ${LIBICONV_PATH})
 +      IF(LIBICONV_STATIC)
 +        # LIBICONV_STATIC is necessary for the success of CHECK_ICONV
 +        # on Windows.
 +        SET(CMAKE_REQUIRED_DEFINITIONS "-DLIBICONV_STATIC")
 +      ELSE(LIBICONV_STATIC)
 +        SET(CMAKE_REQUIRED_DEFINITIONS)
 +      ENDIF(LIBICONV_STATIC)
 +      CHECK_ICONV("libiconv" "const")
 +      CHECK_ICONV("libiconv" "")
 +      IF (HAVE_ICONV)
 +        LIST(APPEND ADDITIONAL_LIBS ${LIBICONV_PATH})
 +      ENDIF(HAVE_ICONV)
 +    ENDIF(NOT HAVE_ICONV AND LIBICONV_PATH)
 +  ENDIF(ICONV_INCLUDE_DIR)
 +  #
 +  # Find locale_charset() for libiconv.
 +  #
 +  IF(LIBICONV_PATH)
 +    SET(CMAKE_REQUIRED_DEFINITIONS)
 +    SET(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR})
 +    SET(CMAKE_REQUIRED_LIBRARIES)
 +    CHECK_INCLUDE_FILES("localcharset.h" HAVE_LOCALCHARSET_H)
 +    FIND_LIBRARY(LIBCHARSET_PATH NAMES charset libcharset)
 +    IF(LIBCHARSET_PATH)
 +      SET(CMAKE_REQUIRED_LIBRARIES ${LIBCHARSET_PATH})
 +      IF(WIN32 AND NOT CYGWIN)
 +        # Test if a macro is needed for the library.
 +        TRY_MACRO_FOR_LIBRARY(
 +          "${ICONV_INCLUDE_DIR}" "${LIBCHARSET_PATH}"
 +          COMPILES
 +          "#include <localcharset.h>\nint main() {return locale_charset()?1:0;}"
 +          "WITHOUT_LIBCHARSET_STATIC;LIBCHARSET_STATIC")
 +        IF(NOT WITHOUT_LIBCHARSET_STATIC AND LIBCHARSET_STATIC)
 +          ADD_DEFINITIONS(-DLIBCHARSET_STATIC)
 +        ENDIF(NOT WITHOUT_LIBCHARSET_STATIC AND LIBCHARSET_STATIC)
 +        IF(WITHOUT_LIBCHARSET_STATIC OR LIBCHARSET_STATIC)
 +          SET(HAVE_LOCALE_CHARSET ON CACHE INTERNAL
 +              "Have function locale_charset")
 +        ENDIF(WITHOUT_LIBCHARSET_STATIC OR LIBCHARSET_STATIC)
 +      ELSE(WIN32 AND NOT CYGWIN)
 +        CHECK_FUNCTION_EXISTS_GLIBC(locale_charset HAVE_LOCALE_CHARSET)
 +      ENDIF(WIN32 AND NOT CYGWIN)
 +      IF(HAVE_LOCALE_CHARSET)
 +        LIST(APPEND ADDITIONAL_LIBS ${LIBCHARSET_PATH})
 +      ENDIF(HAVE_LOCALE_CHARSET)
 +    ENDIF(LIBCHARSET_PATH)
 +  ENDIF(LIBICONV_PATH)
 +  CMAKE_POP_CHECK_STATE()	# Restore the state of the variables
 +ELSE(ENABLE_ICONV)
 +  # Make sure ICONV variables are not in CACHE after ENABLE_ICONV disabled
 +  # (once enabled).
 +  UNSET(HAVE_LOCALE_CHARSET CACHE)
 +  UNSET(HAVE_ICONV CACHE)
 +  UNSET(HAVE_ICONV_libc_ CACHE)
 +  UNSET(HAVE_ICONV_libc_const CACHE)
 +  UNSET(HAVE_ICONV_libiconv_ CACHE)
 +  UNSET(HAVE_ICONV_libiconv_const CACHE)
 +  UNSET(ICONV_INCLUDE_DIR CACHE)
 +  UNSET(LIBICONV_PATH CACHE)
 +  UNSET(LIBICONV_DLL CACHE)
 +  UNSET(LIBICONV_STATIC CACHE)
 +  UNSET(LIBCHARSET_DLL CACHE)
 +  UNSET(LIBCHARSET_STATIC CACHE)
 +ENDIF(ENABLE_ICONV)
 +
 +IF(0) # CMake does not need XML support in libarchive
 +#
 +# Find Libxml2
 +#
 +IF(ENABLE_LIBXML2)
 +  FIND_PACKAGE(LibXml2)
 +ELSE()
 +  SET(LIBXML2_FOUND FALSE)
 +ENDIF()
 +IF(LIBXML2_FOUND)
 +  CMAKE_PUSH_CHECK_STATE()	# Save the state of the variables
 +  INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR})
 +  LIST(APPEND ADDITIONAL_LIBS ${LIBXML2_LIBRARIES})
 +  SET(HAVE_LIBXML2 1)
 +  # libxml2's include files use iconv.h
 +  SET(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR})
 +  CHECK_INCLUDE_FILES("libxml/xmlreader.h" HAVE_LIBXML_XMLREADER_H)
 +  CHECK_INCLUDE_FILES("libxml/xmlwriter.h" HAVE_LIBXML_XMLWRITER_H)
 +  # Test if a macro is needed for the library.
 +  TRY_MACRO_FOR_LIBRARY(
 +    "${ICONV_INCLUDE_DIR};${LIBXML2_INCLUDE_DIR}"
 +    "ws2_32.lib;${ZLIB_LIBRARIES};${LIBICONV_PATH};${LIBXML2_LIBRARIES}"
 +    COMPILES
 +    "#include <stddef.h>\n#include <libxml/xmlreader.h>\nint main() {return xmlTextReaderRead((xmlTextReaderPtr)(void *)0);}"
 +    "WITHOUT_LIBXML_STATIC;LIBXML_STATIC")
 +  IF(NOT WITHOUT_LIBXML_STATIC AND LIBXML_STATIC)
 +    ADD_DEFINITIONS(-DLIBXML_STATIC)
 +  ENDIF(NOT WITHOUT_LIBXML_STATIC AND LIBXML_STATIC)
 +  CMAKE_POP_CHECK_STATE()	# Restore the state of the variables
 +ELSE(LIBXML2_FOUND)
 +  #
 +  # Find Expat
 +  #
 +  IF(ENABLE_EXPAT)
 +    FIND_PACKAGE(EXPAT)
 +  ELSE()
 +    SET(EXPAT_FOUND FALSE)
 +  ENDIF()
 +  IF(EXPAT_FOUND)
 +    CMAKE_PUSH_CHECK_STATE()	# Save the state of the variables
 +    INCLUDE_DIRECTORIES(${EXPAT_INCLUDE_DIR})
 +    LIST(APPEND ADDITIONAL_LIBS ${EXPAT_LIBRARIES})
 +    SET(HAVE_LIBEXPAT 1)
 +    LA_CHECK_INCLUDE_FILE("expat.h" HAVE_EXPAT_H)
 +    CMAKE_POP_CHECK_STATE()	# Restore the state of the variables
 +  ENDIF(EXPAT_FOUND)
 +ENDIF(LIBXML2_FOUND)
 +MARK_AS_ADVANCED(CLEAR LIBXML2_INCLUDE_DIR)
 +MARK_AS_ADVANCED(CLEAR LIBXML2_LIBRARIES)
 +ENDIF()
 +
 +#
 +# Check functions
 +#
 +CMAKE_PUSH_CHECK_STATE()	# Save the state of the variables
 +IF (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR
 +    CMAKE_C_COMPILER_ID STREQUAL "Clang")
 +  #
 +  # During checking functions, we should use -fno-builtin to avoid the
 +  # failure of function detection which failure is an error "conflicting
 +  # types for built-in function" caused by using -Werror option.
 +  #
 +  SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-builtin")
 +ENDIF ()
 +CHECK_SYMBOL_EXISTS(_CrtSetReportMode "crtdbg.h" HAVE__CrtSetReportMode)
 +CHECK_FUNCTION_EXISTS_GLIBC(arc4random_buf HAVE_ARC4RANDOM_BUF)
 +CHECK_FUNCTION_EXISTS_GLIBC(chflags HAVE_CHFLAGS)
 +CHECK_FUNCTION_EXISTS_GLIBC(chown HAVE_CHOWN)
 +CHECK_FUNCTION_EXISTS_GLIBC(chroot HAVE_CHROOT)
 +CHECK_FUNCTION_EXISTS_GLIBC(ctime_r HAVE_CTIME_R)
- CHECK_FUNCTION_EXISTS_GLIBC(dirfd HAVE_DIRFD)
 +CHECK_FUNCTION_EXISTS_GLIBC(fchdir HAVE_FCHDIR)
 +CHECK_FUNCTION_EXISTS_GLIBC(fchflags HAVE_FCHFLAGS)
 +CHECK_FUNCTION_EXISTS_GLIBC(fchmod HAVE_FCHMOD)
 +CHECK_FUNCTION_EXISTS_GLIBC(fchown HAVE_FCHOWN)
 +CHECK_FUNCTION_EXISTS_GLIBC(fcntl HAVE_FCNTL)
 +CHECK_FUNCTION_EXISTS_GLIBC(fdopendir HAVE_FDOPENDIR)
 +CHECK_FUNCTION_EXISTS_GLIBC(fork HAVE_FORK)
 +CHECK_FUNCTION_EXISTS_GLIBC(fstat HAVE_FSTAT)
 +CHECK_FUNCTION_EXISTS_GLIBC(fstatat HAVE_FSTATAT)
 +CHECK_FUNCTION_EXISTS_GLIBC(fstatfs HAVE_FSTATFS)
 +CHECK_FUNCTION_EXISTS_GLIBC(fstatvfs HAVE_FSTATVFS)
 +CHECK_FUNCTION_EXISTS_GLIBC(ftruncate HAVE_FTRUNCATE)
 +CHECK_FUNCTION_EXISTS_GLIBC(futimens HAVE_FUTIMENS)
 +CHECK_FUNCTION_EXISTS_GLIBC(futimes HAVE_FUTIMES)
 +CHECK_FUNCTION_EXISTS_GLIBC(futimesat HAVE_FUTIMESAT)
 +CHECK_FUNCTION_EXISTS_GLIBC(geteuid HAVE_GETEUID)
 +CHECK_FUNCTION_EXISTS_GLIBC(getgrgid_r HAVE_GETGRGID_R)
 +CHECK_FUNCTION_EXISTS_GLIBC(getgrnam_r HAVE_GETGRNAM_R)
 +CHECK_FUNCTION_EXISTS_GLIBC(getpwnam_r HAVE_GETPWNAM_R)
 +CHECK_FUNCTION_EXISTS_GLIBC(getpwuid_r HAVE_GETPWUID_R)
 +CHECK_FUNCTION_EXISTS_GLIBC(getpid HAVE_GETPID)
 +CHECK_FUNCTION_EXISTS_GLIBC(getvfsbyname HAVE_GETVFSBYNAME)
 +CHECK_FUNCTION_EXISTS_GLIBC(gmtime_r HAVE_GMTIME_R)
 +CHECK_FUNCTION_EXISTS_GLIBC(lchflags HAVE_LCHFLAGS)
 +CHECK_FUNCTION_EXISTS_GLIBC(lchmod HAVE_LCHMOD)
 +CHECK_FUNCTION_EXISTS_GLIBC(lchown HAVE_LCHOWN)
 +CHECK_FUNCTION_EXISTS_GLIBC(link HAVE_LINK)
 +CHECK_FUNCTION_EXISTS_GLIBC(localtime_r HAVE_LOCALTIME_R)
 +CHECK_FUNCTION_EXISTS_GLIBC(lstat HAVE_LSTAT)
 +CHECK_FUNCTION_EXISTS_GLIBC(lutimes HAVE_LUTIMES)
 +CHECK_FUNCTION_EXISTS_GLIBC(mbrtowc HAVE_MBRTOWC)
 +CHECK_FUNCTION_EXISTS_GLIBC(memmove HAVE_MEMMOVE)
 +CHECK_FUNCTION_EXISTS_GLIBC(mkdir HAVE_MKDIR)
 +CHECK_FUNCTION_EXISTS_GLIBC(mkfifo HAVE_MKFIFO)
 +CHECK_FUNCTION_EXISTS_GLIBC(mknod HAVE_MKNOD)
 +CHECK_FUNCTION_EXISTS_GLIBC(mkstemp HAVE_MKSTEMP)
 +CHECK_FUNCTION_EXISTS_GLIBC(nl_langinfo HAVE_NL_LANGINFO)
 +CHECK_FUNCTION_EXISTS_GLIBC(openat HAVE_OPENAT)
 +CHECK_FUNCTION_EXISTS_GLIBC(pipe HAVE_PIPE)
 +CHECK_FUNCTION_EXISTS_GLIBC(poll HAVE_POLL)
 +CHECK_FUNCTION_EXISTS_GLIBC(posix_spawnp HAVE_POSIX_SPAWNP)
 +CHECK_FUNCTION_EXISTS_GLIBC(readlink HAVE_READLINK)
 +CHECK_FUNCTION_EXISTS_GLIBC(readpassphrase HAVE_READPASSPHRASE)
 +CHECK_FUNCTION_EXISTS_GLIBC(select HAVE_SELECT)
 +CHECK_FUNCTION_EXISTS_GLIBC(setenv HAVE_SETENV)
 +CHECK_FUNCTION_EXISTS_GLIBC(setlocale HAVE_SETLOCALE)
 +CHECK_FUNCTION_EXISTS_GLIBC(sigaction HAVE_SIGACTION)
 +CHECK_FUNCTION_EXISTS_GLIBC(statfs HAVE_STATFS)
 +CHECK_FUNCTION_EXISTS_GLIBC(statvfs HAVE_STATVFS)
 +CHECK_FUNCTION_EXISTS_GLIBC(strchr HAVE_STRCHR)
 +CHECK_FUNCTION_EXISTS_GLIBC(strdup HAVE_STRDUP)
 +CHECK_FUNCTION_EXISTS_GLIBC(strerror HAVE_STRERROR)
 +CHECK_FUNCTION_EXISTS_GLIBC(strncpy_s HAVE_STRNCPY_S)
 +CHECK_FUNCTION_EXISTS_GLIBC(strrchr HAVE_STRRCHR)
 +CHECK_FUNCTION_EXISTS_GLIBC(symlink HAVE_SYMLINK)
 +CHECK_FUNCTION_EXISTS_GLIBC(timegm HAVE_TIMEGM)
 +CHECK_FUNCTION_EXISTS_GLIBC(tzset HAVE_TZSET)
 +CHECK_FUNCTION_EXISTS_GLIBC(unsetenv HAVE_UNSETENV)
 +CHECK_FUNCTION_EXISTS_GLIBC(utime HAVE_UTIME)
 +CHECK_FUNCTION_EXISTS_GLIBC(utimes HAVE_UTIMES)
 +CHECK_FUNCTION_EXISTS_GLIBC(utimensat HAVE_UTIMENSAT)
 +CHECK_FUNCTION_EXISTS_GLIBC(vfork HAVE_VFORK)
 +CHECK_FUNCTION_EXISTS_GLIBC(wcrtomb HAVE_WCRTOMB)
 +CHECK_FUNCTION_EXISTS_GLIBC(wcscmp HAVE_WCSCMP)
 +CHECK_FUNCTION_EXISTS_GLIBC(wcscpy HAVE_WCSCPY)
 +CHECK_FUNCTION_EXISTS_GLIBC(wcslen HAVE_WCSLEN)
 +CHECK_FUNCTION_EXISTS_GLIBC(wctomb HAVE_WCTOMB)
 +CHECK_FUNCTION_EXISTS_GLIBC(_ctime64_s HAVE__CTIME64_S)
 +CHECK_FUNCTION_EXISTS_GLIBC(_fseeki64 HAVE__FSEEKI64)
 +CHECK_FUNCTION_EXISTS_GLIBC(_get_timezone HAVE__GET_TIMEZONE)
 +CHECK_FUNCTION_EXISTS_GLIBC(_localtime64_s HAVE__LOCALTIME64_S)
 +CHECK_FUNCTION_EXISTS_GLIBC(_mkgmtime64 HAVE__MKGMTIME64)
 +
 +SET(CMAKE_REQUIRED_LIBRARIES "")
 +CHECK_FUNCTION_EXISTS(cygwin_conv_path HAVE_CYGWIN_CONV_PATH)
 +CHECK_FUNCTION_EXISTS(fseeko HAVE_FSEEKO)
 +CHECK_FUNCTION_EXISTS(strerror_r HAVE_STRERROR_R)
 +CHECK_FUNCTION_EXISTS(strftime HAVE_STRFTIME)
 +CHECK_FUNCTION_EXISTS(vprintf HAVE_VPRINTF)
 +CHECK_FUNCTION_EXISTS(wmemcmp HAVE_WMEMCMP)
 +CHECK_FUNCTION_EXISTS(wmemcpy HAVE_WMEMCPY)
 +CHECK_FUNCTION_EXISTS(wmemmove HAVE_WMEMMOVE)
 +
 +CMAKE_POP_CHECK_STATE()	# Restore the state of the variables
 +
 +CHECK_C_SOURCE_COMPILES(
 +  "#include <sys/types.h>\n#include <sys/mount.h>\nint main(void) { struct vfsconf v; return sizeof(v);}"
 +  HAVE_STRUCT_VFSCONF)
 +
 +CHECK_C_SOURCE_COMPILES(
 +  "#include <sys/types.h>\n#include <sys/mount.h>\nint main(void) { struct xvfsconf v; return sizeof(v);}"
 +  HAVE_STRUCT_XVFSCONF)
 +
 +# Make sure we have the POSIX version of readdir_r, not the
 +# older 2-argument version.
 +CHECK_C_SOURCE_COMPILES(
 +  "#include <dirent.h>\nint main() {DIR *d = opendir(\".\"); struct dirent e,*r; return readdir_r(d,&e,&r);}"
 +  HAVE_READDIR_R)
 +
++# dirfd can be either a function or a macro.
++CHECK_C_SOURCE_COMPILES(
++  "#include <dirent.h>\nint main() {DIR *d = opendir(\".\"); return dirfd(d);}"
++  HAVE_DIRFD)
 +
 +# Only detect readlinkat() if we also have AT_FDCWD in unistd.h.
 +# NOTE: linux requires fcntl.h for AT_FDCWD.
 +CHECK_C_SOURCE_COMPILES(
 +  "#include <fcntl.h>\n#include <unistd.h>\nint main() {char buf[10]; return readlinkat(AT_FDCWD, \"\", buf, 0);}"
 +  HAVE_READLINKAT)
 +
 +
 +# To verify major(), we need to both include the header
 +# of interest and verify that the result can be linked.
 +# CHECK_FUNCTION_EXISTS doesn't accept a header argument,
 +# CHECK_SYMBOL_EXISTS doesn't test linkage.
 +CHECK_C_SOURCE_COMPILES(
 +  "#include <sys/mkdev.h>\nint main() { return major(256); }"
 +  MAJOR_IN_MKDEV)
 +CHECK_C_SOURCE_COMPILES(
 +  "#include <sys/sysmacros.h>\nint main() { return major(256); }"
 +  MAJOR_IN_SYSMACROS)
 +
 +CHECK_C_SOURCE_COMPILES(
 +  "#include <lzma.h>\n#if LZMA_VERSION < 50020000\n#error unsupported\n#endif\nint main(void){lzma_stream_encoder_mt(0, 0); return 0;}"
 +  HAVE_LZMA_STREAM_ENCODER_MT)
 +
 +IF(HAVE_STRERROR_R)
 +  SET(HAVE_DECL_STRERROR_R 1)
 +ENDIF(HAVE_STRERROR_R)
 +
 +#
 +# Check defines
 +#
 +SET(headers "limits.h")
 +IF(HAVE_STDINT_H)
 +  LIST(APPEND headers "stdint.h")
 +ENDIF(HAVE_STDINT_H)
 +IF(HAVE_INTTYPES_H)
 +  LIST(APPEND headers "inttypes.h")
 +ENDIF(HAVE_INTTYPES_H)
 +CHECK_SYMBOL_EXISTS(EFTYPE           "errno.h"    HAVE_EFTYPE)
 +CHECK_SYMBOL_EXISTS(EILSEQ           "errno.h"    HAVE_EILSEQ)
 +CHECK_SYMBOL_EXISTS(D_MD_ORDER       "langinfo.h" HAVE_D_MD_ORDER)
 +CHECK_SYMBOL_EXISTS(INT32_MAX        "${headers}" HAVE_DECL_INT32_MAX)
 +CHECK_SYMBOL_EXISTS(INT32_MIN        "${headers}" HAVE_DECL_INT32_MIN)
 +CHECK_SYMBOL_EXISTS(INT64_MAX        "${headers}" HAVE_DECL_INT64_MAX)
 +CHECK_SYMBOL_EXISTS(INT64_MIN        "${headers}" HAVE_DECL_INT64_MIN)
 +CHECK_SYMBOL_EXISTS(INTMAX_MAX       "${headers}" HAVE_DECL_INTMAX_MAX)
 +CHECK_SYMBOL_EXISTS(INTMAX_MIN       "${headers}" HAVE_DECL_INTMAX_MIN)
 +CHECK_SYMBOL_EXISTS(UINT32_MAX       "${headers}" HAVE_DECL_UINT32_MAX)
 +CHECK_SYMBOL_EXISTS(UINT64_MAX       "${headers}" HAVE_DECL_UINT64_MAX)
 +CHECK_SYMBOL_EXISTS(UINTMAX_MAX      "${headers}" HAVE_DECL_UINTMAX_MAX)
 +CHECK_SYMBOL_EXISTS(SIZE_MAX         "${headers}" HAVE_DECL_SIZE_MAX)
 +CHECK_SYMBOL_EXISTS(SSIZE_MAX        "limits.h"   HAVE_DECL_SSIZE_MAX)
 +
 +#
 +# Check struct members
 +#
 +# Check for tm_gmtoff in struct tm
 +CHECK_STRUCT_HAS_MEMBER("struct tm" tm_gmtoff
 +    "time.h" HAVE_STRUCT_TM_TM_GMTOFF)
 +CHECK_STRUCT_HAS_MEMBER("struct tm" __tm_gmtoff
 +    "time.h" HAVE_STRUCT_TM___TM_GMTOFF)
 +
 +# Check for f_namemax in struct statfs
 +CHECK_STRUCT_HAS_MEMBER("struct statfs" f_namemax
 +    "sys/param.h;sys/mount.h" HAVE_STRUCT_STATFS_F_NAMEMAX)
 +
 +# Check for birthtime in struct stat
 +CHECK_STRUCT_HAS_MEMBER("struct stat" st_birthtime
 +    "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_BIRTHTIME)
 +
 +# Check for high-resolution timestamps in struct stat
 +CHECK_STRUCT_HAS_MEMBER("struct stat" st_birthtimespec.tv_nsec
 +    "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC)
 +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtimespec.tv_nsec
 +    "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC)
 +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec
 +    "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
 +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtime_n
 +    "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIME_N)
 +CHECK_STRUCT_HAS_MEMBER("struct stat" st_umtime
 +    "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_UMTIME)
 +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtime_usec
 +    "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_MTIME_USEC)
 +# Check for block size support in struct stat
 +CHECK_STRUCT_HAS_MEMBER("struct stat" st_blksize
 +    "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_BLKSIZE)
 +# Check for st_flags in struct stat (BSD fflags)
 +CHECK_STRUCT_HAS_MEMBER("struct stat" st_flags
 +    "sys/types.h;sys/stat.h" HAVE_STRUCT_STAT_ST_FLAGS)
 +
 +IF(HAVE_SYS_STATVFS_H)
 +  CHECK_STRUCT_HAS_MEMBER("struct statvfs" f_iosize
 +    "sys/types.h;sys/statvfs.h" HAVE_STRUCT_STATVFS_F_IOSIZE)
 +ENDIF()
 +
 +#
 +#
 +CHECK_STRUCT_HAS_MEMBER("struct tm" tm_sec
 +    "sys/types.h;sys/time.h;time.h" TIME_WITH_SYS_TIME)
 +
 +#
 +# Check for integer types
 +#
 +#
 +CHECK_TYPE_SIZE("short" SIZE_OF_SHORT)
 +CHECK_TYPE_SIZE("int" SIZE_OF_INT)
 +CHECK_TYPE_SIZE("long" SIZE_OF_LONG)
 +CHECK_TYPE_SIZE("long long"     SIZE_OF_LONG_LONG)
 +
 +CHECK_TYPE_SIZE("unsigned short" SIZE_OF_UNSIGNED_SHORT)
 +CHECK_TYPE_SIZE("unsigned" SIZE_OF_UNSIGNED)
 +CHECK_TYPE_SIZE("unsigned long" SIZE_OF_UNSIGNED_LONG)
 +CHECK_TYPE_SIZE("unsigned long long" SIZE_OF_UNSIGNED_LONG_LONG)
 +
 +CHECK_TYPE_SIZE("__int64" __INT64)
 +CHECK_TYPE_SIZE("unsigned __int64" UNSIGNED___INT64)
 +
 +CHECK_TYPE_SIZE(int16_t INT16_T)
 +CHECK_TYPE_SIZE(int32_t INT32_T)
 +CHECK_TYPE_SIZE(int64_t INT64_T)
 +CHECK_TYPE_SIZE(intmax_t INTMAX_T)
 +CHECK_TYPE_SIZE(uint8_t UINT8_T)
 +CHECK_TYPE_SIZE(uint16_t UINT16_T)
 +CHECK_TYPE_SIZE(uint32_t UINT32_T)
 +CHECK_TYPE_SIZE(uint64_t UINT64_T)
 +CHECK_TYPE_SIZE(uintmax_t UINTMAX_T)
 +
 +CHECK_TYPE_SIZE(dev_t       DEV_T)
 +IF(NOT HAVE_DEV_T)
 +  IF(MSVC)
 +    SET(dev_t "unsigned int")
 +  ENDIF(MSVC)
 +ENDIF(NOT HAVE_DEV_T)
 +#
 +CHECK_TYPE_SIZE(gid_t       GID_T)
 +IF(NOT HAVE_GID_T)
 +  IF(WIN32)
 +    SET(gid_t "short")
 +  ELSE(WIN32)
 +    SET(gid_t "unsigned int")
 +  ENDIF(WIN32)
 +ENDIF(NOT HAVE_GID_T)
 +#
 +CHECK_TYPE_SIZE(id_t        ID_T)
 +IF(NOT HAVE_ID_T)
 +  IF(WIN32)
 +    SET(id_t "short")
 +  ELSE(WIN32)
 +    SET(id_t "unsigned int")
 +  ENDIF(WIN32)
 +ENDIF(NOT HAVE_ID_T)
 +#
 +CHECK_TYPE_SIZE(mode_t      MODE_T)
 +IF(NOT HAVE_MODE_T)
 +  IF(WIN32)
 +    SET(mode_t "unsigned short")
 +  ELSE(WIN32)
 +    SET(mode_t "int")
 +  ENDIF(WIN32)
 +ENDIF(NOT HAVE_MODE_T)
 +#
 +CHECK_TYPE_SIZE(off_t       OFF_T)
 +IF(NOT HAVE_OFF_T)
 +  SET(off_t "__int64")
 +ENDIF(NOT HAVE_OFF_T)
 +#
 +CHECK_TYPE_SIZE(size_t      SIZE_T)
 +IF(NOT HAVE_SIZE_T)
 +  IF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8)
 +    SET(size_t "uint64_t")
 +  ELSE("${CMAKE_SIZEOF_VOID_P}" EQUAL 8)
 +    SET(size_t   "uint32_t")
 +  ENDIF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8)
 +ENDIF(NOT HAVE_SIZE_T)
 +#
 +CHECK_TYPE_SIZE(ssize_t     SSIZE_T)
 +IF(NOT HAVE_SSIZE_T)
 +  IF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8)
 +    SET(ssize_t "int64_t")
 +  ELSE("${CMAKE_SIZEOF_VOID_P}" EQUAL 8)
 +    SET(ssize_t "long")
 +  ENDIF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8)
 +ENDIF(NOT HAVE_SSIZE_T)
 +#
 +CHECK_TYPE_SIZE(uid_t       UID_T)
 +IF(NOT HAVE_UID_T)
 +  IF(WIN32)
 +    SET(uid_t "short")
 +  ELSE(WIN32)
 +    SET(uid_t "unsigned int")
 +  ENDIF(WIN32)
 +ENDIF(NOT HAVE_UID_T)
 +#
 +CHECK_TYPE_SIZE(pid_t       PID_T)
 +IF(NOT HAVE_PID_T)
 +  IF(WIN32)
 +    SET(pid_t "int")
 +  ELSE(WIN32)
 +    MESSAGE(FATAL_ERROR "pid_t doesn't exist on this platform?")
 +  ENDIF(WIN32)
 +ENDIF(NOT HAVE_PID_T)
 +#
 +CHECK_TYPE_SIZE(intptr_t   INTPTR_T)
 +IF(NOT HAVE_INTPTR_T)
 +  IF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8)
 +    SET(intptr_t "int64_t")
 +  ELSE()
 +    SET(intptr_t "int32_t")
 +  ENDIF()
 +ENDIF(NOT HAVE_INTPTR_T)
 +#
 +CHECK_TYPE_SIZE(uintptr_t   UINTPTR_T)
 +IF(NOT HAVE_UINTPTR_T)
 +  IF("${CMAKE_SIZEOF_VOID_P}" EQUAL 8)
 +    SET(uintptr_t "uint64_t")
 +  ELSE()
 +    SET(uintptr_t "uint32_t")
 +  ENDIF()
 +ENDIF(NOT HAVE_UINTPTR_T)
 +#
 +CHECK_TYPE_SIZE(wchar_t     SIZEOF_WCHAR_T)
 +IF(HAVE_SIZEOF_WCHAR_T)
 +  SET(HAVE_WCHAR_T 1)
 +ENDIF(HAVE_SIZEOF_WCHAR_T)
 +#
 +# Check if _FILE_OFFSET_BITS macro needed for large files
 +#
 +CHECK_FILE_OFFSET_BITS()
 +
 +#
 +# Check for Extended Attribute libraries, headers, and functions
 +#
 +IF(ENABLE_XATTR)
-   LA_CHECK_INCLUDE_FILE(attr/xattr.h     HAVE_ATTR_XATTR_H)
-   LA_CHECK_INCLUDE_FILE(sys/xattr.h      HAVE_SYS_XATTR_H)
-   LA_CHECK_INCLUDE_FILE(sys/extattr.h      HAVE_SYS_EXTATTR_H)
 +  CHECK_LIBRARY_EXISTS(attr "setxattr" "" HAVE_LIBATTR)
 +  IF(HAVE_LIBATTR)
 +    SET(CMAKE_REQUIRED_LIBRARIES "attr")
 +  ENDIF(HAVE_LIBATTR)
 +  CHECK_SYMBOL_EXISTS(EXTATTR_NAMESPACE_USER "sys/types.h;sys/extattr.h" HAVE_DECL_EXTATTR_NAMESPACE_USER)
-   CHECK_FUNCTION_EXISTS_GLIBC(extattr_get_file HAVE_EXTATTR_GET_FILE)
-   CHECK_FUNCTION_EXISTS_GLIBC(extattr_list_file HAVE_EXTATTR_LIST_FILE)
-   CHECK_FUNCTION_EXISTS_GLIBC(extattr_set_fd HAVE_EXTATTR_SET_FD)
-   CHECK_FUNCTION_EXISTS_GLIBC(extattr_set_file HAVE_EXTATTR_SET_FILE)
-   CHECK_FUNCTION_EXISTS_GLIBC(fgetxattr HAVE_FGETXATTR)
-   CHECK_FUNCTION_EXISTS_GLIBC(flistxattr HAVE_FLISTXATTR)
-   CHECK_FUNCTION_EXISTS_GLIBC(fsetxattr HAVE_FSETXATTR)
-   CHECK_FUNCTION_EXISTS_GLIBC(getxattr HAVE_GETXATTR)
-   CHECK_FUNCTION_EXISTS_GLIBC(lgetxattr HAVE_LGETXATTR)
-   CHECK_FUNCTION_EXISTS_GLIBC(listxattr HAVE_LISTXATTR)
-   CHECK_FUNCTION_EXISTS_GLIBC(llistxattr HAVE_LLISTXATTR)
-   CHECK_FUNCTION_EXISTS_GLIBC(lsetxattr HAVE_LSETXATTR)
-   CHECK_FUNCTION_EXISTS_GLIBC(fgetea HAVE_FGETEA)
-   CHECK_FUNCTION_EXISTS_GLIBC(flistea HAVE_FLISTEA)
-   CHECK_FUNCTION_EXISTS_GLIBC(fsetea HAVE_FSETEA)
-   CHECK_FUNCTION_EXISTS_GLIBC(getea HAVE_GETEA)
-   CHECK_FUNCTION_EXISTS_GLIBC(lgetea HAVE_LGETEA)
-   CHECK_FUNCTION_EXISTS_GLIBC(listea HAVE_LISTEA)
-   CHECK_FUNCTION_EXISTS_GLIBC(llistea HAVE_LLISTEA)
-   CHECK_FUNCTION_EXISTS_GLIBC(lsetea HAVE_LSETEA)
++  CHECK_SYMBOL_EXISTS(XATTR_NOFOLLOW "sys/xattr.h" HAVE_DECL_XATTR_NOFOLLOW)
++  IF(HAVE_SYS_XATTR_H AND HAVE_DECL_XATTR_NOFOLLOW)
++    CHECK_FUNCTION_EXISTS(fgetxattr HAVE_FGETXATTR)
++    CHECK_FUNCTION_EXISTS(flistxattr HAVE_FLISTXATTR)
++    CHECK_FUNCTION_EXISTS(fsetxattr HAVE_FSETXATTR)
++    CHECK_FUNCTION_EXISTS(getxattr HAVE_GETXATTR)
++    CHECK_FUNCTION_EXISTS(listxattr HAVE_LISTXATTR)
++    CHECK_FUNCTION_EXISTS(setxattr HAVE_SETXATTR)
++    IF(HAVE_FGETXATTR AND
++       HAVE_FLISTXATTR AND
++       HAVE_FSETXATTR AND
++       HAVE_GETXATTR AND
++       HAVE_LISTXATTR AND
++       HAVE_SETXATTR)
++      SET(ARCHIVE_XATTR_DARWIN TRUE)
++    ENDIF()
++  ELSEIF(HAVE_SYS_EXTATTR_H AND HAVE_DECL_EXTATTR_NAMESPACE_USER)
++    # FreeBSD xattr support
++    CHECK_FUNCTION_EXISTS(extattr_get_fd HAVE_EXTATTR_GET_FD)
++    CHECK_FUNCTION_EXISTS(extattr_get_file HAVE_EXTATTR_GET_FILE)
++    CHECK_FUNCTION_EXISTS(extattr_get_link HAVE_EXTATTR_GET_LINK)
++    CHECK_FUNCTION_EXISTS(extattr_list_fd HAVE_EXTATTR_LIST_FD)
++    CHECK_FUNCTION_EXISTS(extattr_list_file HAVE_EXTATTR_LIST_FILE)
++    CHECK_FUNCTION_EXISTS(extattr_list_link HAVE_EXTATTR_LIST_LINK)
++    CHECK_FUNCTION_EXISTS(extattr_set_fd HAVE_EXTATTR_SET_FD)
++    CHECK_FUNCTION_EXISTS(extattr_set_link HAVE_EXTATTR_SET_LINK)
++    IF(HAVE_EXTATTR_GET_FD AND
++       HAVE_EXTATTR_GET_FILE AND
++       HAVE_EXTATTR_GET_LINK AND
++       HAVE_EXTATTR_LIST_FD AND
++       HAVE_EXTATTR_LIST_FILE AND
++       HAVE_EXTATTR_LIST_LINK AND
++       HAVE_EXTATTR_SET_FD AND
++       HAVE_EXTATTR_SET_LINK)
++      SET(ARCHIVE_XATTR_FREEBSD TRUE)
++    ENDIF()
++  ELSEIF(HAVE_SYS_XATTR_H OR HAVE_ATTR_XATTR_H)
++    # Linux xattr support
++    CHECK_FUNCTION_EXISTS_GLIBC(fgetxattr HAVE_FGETXATTR)
++    CHECK_FUNCTION_EXISTS_GLIBC(flistxattr HAVE_FLISTXATTR)
++    CHECK_FUNCTION_EXISTS_GLIBC(fsetxattr HAVE_FSETXATTR)
++    CHECK_FUNCTION_EXISTS_GLIBC(getxattr HAVE_GETXATTR)
++    CHECK_FUNCTION_EXISTS_GLIBC(lgetxattr HAVE_LGETXATTR)
++    CHECK_FUNCTION_EXISTS_GLIBC(listxattr HAVE_LISTXATTR)
++    CHECK_FUNCTION_EXISTS_GLIBC(llistxattr HAVE_LLISTXATTR)
++    CHECK_FUNCTION_EXISTS_GLIBC(lsetxattr HAVE_LSETXATTR)
++    IF(HAVE_FGETXATTR AND
++       HAVE_FLISTXATTR AND
++       HAVE_FSETXATTR AND
++       HAVE_GETXATTR AND
++       HAVE_LGETXATTR AND
++       HAVE_LISTXATTR AND
++       HAVE_LLISTXATTR AND
++       HAVE_LSETXATTR)
++      SET(ARCHIVE_XATTR_LINUX TRUE)
++    ENDIF()
++  ELSEIF(HAVE_SYS_EA_H)
++    # AIX xattr support
++    CHECK_FUNCTION_EXISTS(fgetea HAVE_FGETEA)
++    CHECK_FUNCTION_EXISTS(flistea HAVE_FLISTEA)
++    CHECK_FUNCTION_EXISTS(fsetea HAVE_FSETEA)
++    CHECK_FUNCTION_EXISTS(getea HAVE_GETEA)
++    CHECK_FUNCTION_EXISTS(lgetea HAVE_LGETEA)
++    CHECK_FUNCTION_EXISTS(listea HAVE_LISTEA)
++    CHECK_FUNCTION_EXISTS(llistea HAVE_LLISTEA)
++    CHECK_FUNCTION_EXISTS(lsetea HAVE_LSETEA)
++    IF(HAVE_FGETEA AND
++       HAVE_FLISTEA AND
++       HAVE_FSETEA AND
++       HAVE_GETEA AND
++       HAVE_LGETEA AND
++       HAVE_LISTEA AND
++       HAVE_LLISTEA AND
++       HAVE_LSETEA)
++      SET(ARCHIVE_XATTR_AIX TRUE)
++    ENDIF()
++  ENDIF()
++
++  IF(ARCHIVE_XATTR_DARWIN)
++    MESSAGE(STATUS "Extended attributes support: Darwin")
++  ELSEIF(ARCHIVE_XATTR_FREEBSD)
++    MESSAGE(STATUS "Extended attributes support: FreeBSD")
++  ELSEIF(ARCHIVE_XATTR_LINUX)
++    MESSAGE(STATUS "Extended attributes support: Linux")
++  ELSEIF(ARCHIVE_XATTR_AIX)
++    MESSAGE(STATUS "Extended attributes support: AIX")
++  ELSE()
++    MESSAGE(STATUS "Extended attributes support: none")
++  ENDIF()
 +ELSE(ENABLE_XATTR)
-   SET(HAVE_ATTR_LIB FALSE)
-   SET(HAVE_ATTR_XATTR_H FALSE)
-   SET(HAVE_DECL_EXTATTR_NAMESPACE_USER FALSE)
-   SET(HAVE_EXTATTR_GET_FILE FALSE)
-   SET(HAVE_EXTATTR_LIST_FILE FALSE)
-   SET(HAVE_EXTATTR_SET_FD FALSE)
-   SET(HAVE_EXTATTR_SET_FILE FALSE)
-   SET(HAVE_FGETEA FALSE)
-   SET(HAVE_FGETXATTR FALSE)
-   SET(HAVE_FLISTEA FALSE)
-   SET(HAVE_FLISTXATTR FALSE)
-   SET(HAVE_FSETEA FALSE)
-   SET(HAVE_FSETXATTR FALSE)
-   SET(HAVE_GETEA FALSE)
-   SET(HAVE_GETXATTR FALSE)
-   SET(HAVE_LGETEA FALSE)
-   SET(HAVE_LGETXATTR FALSE)
-   SET(HAVE_LISTEA FALSE)
-   SET(HAVE_LISTXATTR FALSE)
-   SET(HAVE_LLISTEA FALSE)
-   SET(HAVE_LLISTXATTR FALSE)
-   SET(HAVE_LSETEA FALSE)
-   SET(HAVE_LSETXATTR FALSE)
-   SET(HAVE_SYS_EXTATTR_H FALSE)
-   SET(HAVE_SYS_XATTR_H FALSE)
++  SET(ARCHIVE_XATTR_DARWIN FALSE)
++  SET(ARCHIVE_XATTR_FREEBSD FALSE)
++  SET(ARCHIVE_XATTR_LINUX FALSE)
++  SET(ARCHIVE_XATTR_AIX FALSE)
 +ENDIF(ENABLE_XATTR)
 +
 +#
 +# Check for ACL libraries, headers, and functions
 +#
 +# The ACL support in libarchive is written against the POSIX1e draft,
 +# which was never officially approved and varies quite a bit across
 +# platforms.  Worse, some systems have completely non-POSIX acl functions,
 +# which makes the following checks rather more complex than I would like.
 +#
 +IF(ENABLE_ACL)
++  # Solaris and derivates ACLs
++  CHECK_FUNCTION_EXISTS(acl HAVE_ACL)
++  CHECK_FUNCTION_EXISTS(facl HAVE_FACL)
++
++  # Libacl
 +  CHECK_LIBRARY_EXISTS(acl "acl_get_file" "" HAVE_LIBACL)
 +  IF(HAVE_LIBACL)
 +    SET(CMAKE_REQUIRED_LIBRARIES "acl")
 +    FIND_LIBRARY(ACL_LIBRARY NAMES acl)
 +    LIST(APPEND ADDITIONAL_LIBS ${ACL_LIBRARY})
 +  ENDIF(HAVE_LIBACL)
-   #
-   CHECK_FUNCTION_EXISTS_GLIBC(acl_create_entry HAVE_ACL_CREATE_ENTRY)
-   CHECK_FUNCTION_EXISTS_GLIBC(acl_init HAVE_ACL_INIT)
-   CHECK_FUNCTION_EXISTS_GLIBC(acl_set_fd HAVE_ACL_SET_FD)
-   CHECK_FUNCTION_EXISTS_GLIBC(acl_set_fd_np HAVE_ACL_SET_FD_NP)
-   CHECK_FUNCTION_EXISTS_GLIBC(acl_set_file HAVE_ACL_SET_FILE)
-   CHECK_TYPE_EXISTS(acl_permset_t "${INCLUDES}"    HAVE_ACL_PERMSET_T)
- 
-   # The "acl_get_perm()" function was omitted from the POSIX draft.
-   # (It's a pretty obvious oversight; otherwise, there's no way to
-   # test for specific permissions in a permset.)  Linux uses the obvious
-   # name, FreeBSD adds _np to mark it as "non-Posix extension."
-   # Test for both as a double-check that we really have POSIX-style ACL support.
-   CHECK_FUNCTION_EXISTS(acl_get_fd_np HAVE_ACL_GET_FD_NP)
-   CHECK_FUNCTION_EXISTS(acl_get_perm HAVE_ACL_GET_PERM)
-   CHECK_FUNCTION_EXISTS(acl_get_perm_np HAVE_ACL_GET_PERM_NP)
-   CHECK_FUNCTION_EXISTS(acl_get_link HAVE_ACL_GET_LINK)
-   CHECK_FUNCTION_EXISTS(acl_get_link_np HAVE_ACL_GET_LINK_NP)
-   CHECK_FUNCTION_EXISTS(acl_is_trivial_np HAVE_ACL_IS_TRIVIAL_NP)
-   CHECK_FUNCTION_EXISTS(acl_set_link_np HAVE_ACL_SET_LINK_NP)
-   CHECK_SYMBOL_EXISTS(ACL_TYPE_NFS4 "${INCLUDES}" HAVE_ACL_TYPE_NFS4)
- 
-   # MacOS has an acl.h that isn't POSIX.  It can be detected by
-   # checking for ACL_USER
-   CHECK_SYMBOL_EXISTS(ACL_USER "${INCLUDES}" HAVE_ACL_USER)
-   CHECK_C_SOURCE_COMPILES("#include <sys/types.h>
++
++  CHECK_TYPE_EXISTS(acl_t "sys/types.h;sys/acl.h" HAVE_ACL_T)
++  CHECK_TYPE_EXISTS(acl_entry_t "sys/types.h;sys/acl.h" HAVE_ACL_ENTRY_T)
++  CHECK_TYPE_EXISTS(acl_permset_t "sys/types.h;sys/acl.h" HAVE_ACL_PERMSET_T)
++  CHECK_TYPE_EXISTS(acl_tag_t "sys/types.h;sys/acl.h" HAVE_ACL_TAG_T)
++
++  IF(HAVE_ACL AND HAVE_FACL)
++    CHECK_TYPE_EXISTS(aclent_t "sys/acl.h" HAVE_ACLENT_T)
++    IF(HAVE_ACLENT_T)
++      CHECK_SYMBOL_EXISTS(GETACL "sys/acl.h" HAVE_DECL_GETACL)
++      CHECK_SYMBOL_EXISTS(GETACLCNT "sys/acl.h" HAVE_DECL_GETACLCNT)
++      CHECK_SYMBOL_EXISTS(SETACL "sys/acl.h" HAVE_DECL_SETACL)
++      IF(HAVE_DECL_GETACL AND
++         HAVE_DECL_GETACLCNT AND
++         HAVE_DECL_SETACL)
++        SET(ARCHIVE_ACL_SUNOS TRUE)
++      ENDIF()
++      CHECK_TYPE_EXISTS(ace_t "sys/acl.h" HAVE_ACE_T)
++      IF(HAVE_ACE_T)
++        CHECK_SYMBOL_EXISTS(ACE_GETACL "sys/acl.h" HAVE_DECL_ACE_GETACL)
++        CHECK_SYMBOL_EXISTS(ACE_GETACLCNT "sys/acl.h" HAVE_DECL_ACE_GETACLCNT)
++        CHECK_SYMBOL_EXISTS(ACE_SETACL "sys/acl.h" HAVE_DECL_ACE_SETACL)
++        IF(HAVE_DECL_ACE_GETACL AND
++           HAVE_DECL_ACE_GETACLCNT AND
++           HAVE_DECL_ACE_SETACL)
++          SET(ARCHIVE_ACL_SUNOS_NFS4 TRUE)
++        ENDIF()
++      ENDIF(HAVE_ACE_T)
++    ENDIF(HAVE_ACLENT_T)
++  ENDIF(HAVE_ACL AND HAVE_FACL)
++
++  IF(HAVE_ACL_T AND HAVE_ACL_ENTRY_T AND HAVE_ACL_PERMSET_T AND HAVE_ACL_TAG_T)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_add_perm HAVE_ACL_ADD_PERM)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_clear_perms HAVE_ACL_CLEAR_PERMS)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_create_entry HAVE_ACL_CREATE_ENTRY)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_delete_def_file HAVE_ACL_DELETE_DEF_FILE)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_free HAVE_ACL_FREE)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_entry HAVE_ACL_GET_ENTRY)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_fd HAVE_ACL_GET_FD)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_file HAVE_ACL_GET_FILE)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_permset HAVE_ACL_GET_PERMSET)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_qualifier HAVE_ACL_GET_QUALIFIER)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_tag_type HAVE_ACL_GET_TAG_TYPE)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_init HAVE_ACL_INIT)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_set_fd HAVE_ACL_SET_FD)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_set_file HAVE_ACL_SET_FILE)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_set_qualifier HAVE_ACL_SET_QUALIFIER)
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_set_tag_type HAVE_ACL_SET_TAG_TYPE)
++    IF(HAVE_ACL_ADD_PERM AND
++       HAVE_ACL_CLEAR_PERMS AND
++       HAVE_ACL_CREATE_ENTRY AND
++       HAVE_ACL_DELETE_DEF_FILE AND
++       HAVE_ACL_FREE AND
++       HAVE_ACL_GET_ENTRY AND
++       HAVE_ACL_GET_FD AND
++       HAVE_ACL_GET_FILE AND
++       HAVE_ACL_GET_PERMSET AND
++       HAVE_ACL_GET_QUALIFIER AND
++       HAVE_ACL_GET_TAG_TYPE AND
++       HAVE_ACL_INIT AND
++       HAVE_ACL_SET_FD AND
++       HAVE_ACL_SET_FILE AND
++       HAVE_ACL_SET_QUALIFIER AND
++       HAVE_ACL_SET_TAG_TYPE)
++         SET(HAVE_POSIX_ACL_FUNCS 1)
++    ENDIF()
++
++    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_perm HAVE_ACL_GET_PERM)
++
++    IF(HAVE_POSIX_ACL_FUNCS AND HAVE_ACL_LIBACL_H AND HAVE_LIBACL AND
++       HAVE_ACL_GET_PERM)
++      SET(ARCHIVE_ACL_LIBACL TRUE)
++    ELSE()
++      CHECK_FUNCTION_EXISTS(acl_add_flag_np HAVE_ACL_ADD_FLAG_NP)
++      CHECK_FUNCTION_EXISTS(acl_clear_flags_np HAVE_ACL_CLEAR_FLAGS_NP)
++      CHECK_FUNCTION_EXISTS(acl_get_brand_np HAVE_ACL_GET_BRAND_NP)
++      CHECK_FUNCTION_EXISTS(acl_get_entry_type_np HAVE_ACL_GET_ENTRY_TYPE_NP)
++      CHECK_FUNCTION_EXISTS(acl_get_flag_np HAVE_ACL_GET_FLAG_NP)
++      CHECK_FUNCTION_EXISTS(acl_get_flagset_np HAVE_ACL_GET_FLAGSET_NP)
++      CHECK_FUNCTION_EXISTS(acl_get_fd_np HAVE_ACL_GET_FD_NP)
++      CHECK_FUNCTION_EXISTS(acl_get_link_np HAVE_ACL_GET_LINK_NP)
++      CHECK_FUNCTION_EXISTS(acl_get_perm_np HAVE_ACL_GET_PERM_NP)
++      CHECK_FUNCTION_EXISTS(acl_is_trivial_np HAVE_ACL_IS_TRIVIAL_NP)
++      CHECK_FUNCTION_EXISTS(acl_set_entry_type_np HAVE_ACL_SET_ENTRY_TYPE_NP)
++      CHECK_FUNCTION_EXISTS(acl_set_fd_np HAVE_ACL_SET_FD_NP)
++      CHECK_FUNCTION_EXISTS(acl_set_link_np HAVE_ACL_SET_LINK_NP)
++      CHECK_FUNCTION_EXISTS(mbr_gid_to_uuid HAVE_MBR_GID_TO_UUID)
++      CHECK_FUNCTION_EXISTS(mbr_uid_to_uuid HAVE_MBR_UID_TO_UUID)
++      CHECK_FUNCTION_EXISTS(mbr_uuid_to_id HAVE_MBR_UUID_TO_ID)
++
++      CHECK_C_SOURCE_COMPILES("#include <sys/types.h>
 +#include <sys/acl.h>
- int main(void) { return ACL_TYPE_EXTENDED; }" HAVE_ACL_TYPE_EXTENDED)
++int main(void) { return ACL_TYPE_EXTENDED; }" HAVE_DECL_ACL_TYPE_EXTENDED)
++      CHECK_C_SOURCE_COMPILES("#include <sys/types.h>
++#include <sys/acl.h>
++int main(void) { return ACL_SYNCHRONIZE; }" HAVE_DECL_ACL_SYNCHRONIZE)
++      CHECK_SYMBOL_EXISTS(ACL_TYPE_NFS4 "sys/acl.h" HAVE_DECL_ACL_TYPE_NFS4)
++      CHECK_SYMBOL_EXISTS(ACL_USER "sys/acl.h" HAVE_DECL_ACL_USER)
++
++      IF(HAVE_POSIX_ACL_FUNCS AND
++         HAVE_ACL_GET_FD_NP AND
++         HAVE_ACL_GET_PERM_NP AND
++         NOT HAVE_ACL_GET_PERM AND
++         HAVE_ACL_SET_FD_NP)
++        IF(HAVE_DECL_ACL_USER)
++          SET(ARCHIVE_ACL_FREEBSD TRUE)
++          IF(HAVE_DECL_ACL_TYPE_NFS4 AND
++             HAVE_ACL_ADD_FLAG_NP AND
++             HAVE_ACL_CLEAR_FLAGS_NP AND
++             HAVE_ACL_GET_BRAND_NP AND
++             HAVE_ACL_GET_ENTRY_TYPE_NP AND
++             HAVE_ACL_GET_FLAGSET_NP AND
++             HAVE_ACL_SET_ENTRY_TYPE_NP)
++            SET(ARCHIVE_ACL_FREEBSD_NFS4 TRUE)
++          ENDIF()
++        ELSEIF(HAVE_DECL_ACL_TYPE_EXTENDED AND
++               HAVE_MEMBERSHIP_H AND
++               HAVE_ACL_ADD_FLAG_NP AND
++               HAVE_ACL_CLEAR_FLAGS_NP AND
++               HAVE_ACL_GET_FLAGSET_NP AND
++               HAVE_ACL_GET_LINK_NP AND
++               HAVE_ACL_SET_LINK_NP AND
++               HAVE_MBR_UID_TO_UUID AND
++               HAVE_MBR_GID_TO_UUID AND
++               HAVE_MBR_UUID_TO_ID)
++          SET(ARCHIVE_ACL_DARWIN TRUE)
++        ENDIF()
++      ENDIF()
++    ENDIF()
++  ENDIF(HAVE_ACL_T AND HAVE_ACL_ENTRY_T AND HAVE_ACL_PERMSET_T AND
++        HAVE_ACL_TAG_T)
++
++  # Richacl
++  CHECK_LIBRARY_EXISTS(richacl "richacl_get_file" "" HAVE_LIBRICHACL)
++  IF(HAVE_LIBRICHACL)
++    SET(CMAKE_REQUIRED_LIBRARIES "richacl")
++    FIND_LIBRARY(RICHACL_LIBRARY NAMES richacl)
++    LIST(APPEND ADDITIONAL_LIBS ${RICHACL_LIBRARY})
++  ENDIF(HAVE_LIBRICHACL)
++
++  CHECK_STRUCT_HAS_MEMBER("struct richace" e_type "sys/richacl.h"
++    HAVE_STRUCT_RICHACE)
++  CHECK_STRUCT_HAS_MEMBER("struct richacl" a_flags "sys/richacl.h"
++    HAVE_STRUCT_RICHACL)
++
++  IF(HAVE_LIBRICHACL AND HAVE_STRUCT_RICHACL AND HAVE_STRUCT_RICHACE)
++    CHECK_FUNCTION_EXISTS_GLIBC(richacl_alloc HAVE_RICHACL_ALLOC)
++    CHECK_FUNCTION_EXISTS_GLIBC(richacl_equiv_mode HAVE_RICHACL_EQUIV_MODE)
++    CHECK_FUNCTION_EXISTS_GLIBC(richacl_free HAVE_RICHACL_FREE)
++    CHECK_FUNCTION_EXISTS_GLIBC(richacl_get_fd HAVE_RICHACL_GET_FD)
++    CHECK_FUNCTION_EXISTS_GLIBC(richacl_get_file HAVE_RICHACL_GET_FILE)
++    CHECK_FUNCTION_EXISTS_GLIBC(richacl_set_fd HAVE_RICHACL_SET_FD)
++    CHECK_FUNCTION_EXISTS_GLIBC(richacl_set_file HAVE_RICHACL_SET_FILE)
++    IF(HAVE_RICHACL_ALLOC AND
++       HAVE_RICHACL_EQUIV_MODE AND
++       HAVE_RICHACL_FREE AND
++       HAVE_RICHACL_GET_FD AND
++       HAVE_RICHACL_GET_FILE AND
++       HAVE_RICHACL_SET_FD AND
++       HAVE_RICHACL_SET_FILE)
++      SET(ARCHIVE_ACL_LIBRICHACL TRUE)
++    ENDIF()
++  ENDIF(HAVE_LIBRICHACL AND HAVE_STRUCT_RICHACL AND HAVE_STRUCT_RICHACE)
++
++  IF(ARCHIVE_ACL_DARWIN)
++    MESSAGE(STATUS "ACL support: Darwin (limited NFSv4)")
++  ELSEIF(ARCHIVE_ACL_FREEBSD_NFS4)
++    MESSAGE(STATUS "ACL support: FreeBSD (POSIX.1e and NFSv4)")
++  ELSEIF(ARCHIVE_ACL_FREEBSD)
++    MESSAGE(STATUS "ACL support: FreeBSD (POSIX.1e)")
++  ELSEIF(ARCHIVE_ACL_LIBACL OR ARCHIVE_ACL_LIBRICHACL)
++    IF(ARCHIVE_ACL_LIBACL AND ARCHIVE_ACL_LIBRICHACL)
++      MESSAGE(STATUS "ACL support: libacl (POSIX.1e) + librichacl (NFSv4)")
++    ELSEIF(ARCHIVE_ACL_LIBRICHACL)
++      MESSAGE(STATUS "ACL support: librichacl (NFSv4)")
++    ELSE()
++      MESSAGE(STATUS "ACL support: libacl (POSIX.1e)")
++    ENDIF()
++  ELSEIF(ARCHIVE_ACL_SUNOS_NFS4)
++    MESSAGE(STATUS "ACL support: Solaris (POSIX.1e and NFSv4)")
++  ELSEIF(ARCHIVE_ACL_SUNOS)
++    MESSAGE(STATUS "ACL support: Solaris (POSIX.1e)")
++  ELSE()
++    MESSAGE(STATUS "ACL support: none")
++  ENDIF()
 +
-   # Solaris and derivates ACLs
-   CHECK_LIBRARY_EXISTS(sec "acl_get" "" HAVE_LIBSEC)
-   IF(HAVE_LIBSEC)
-     SET(CMAKE_REQUIRED_LIBRARIES "sec")
-     FIND_LIBRARY(SEC_LIBRARY NAMES sec)
-     LIST(APPEND ADDITIONAL_LIBS ${SEC_LIBRARY})
-   ENDIF(HAVE_LIBSEC)
-   #
-   CHECK_TYPE_EXISTS(aclent_t "${INCLUDES}" HAVE_ACLENT_T)
-   CHECK_TYPE_EXISTS(ace_t "${INCLUDES}" HAVE_ACE_T)
-   CHECK_FUNCTION_EXISTS(acl_get HAVE_FACL_GET)
-   CHECK_FUNCTION_EXISTS(facl_get HAVE_FACL_GET)
-   CHECK_FUNCTION_EXISTS(acl_set HAVE_FACL_SET)
-   CHECK_FUNCTION_EXISTS(facl_set HAVE_FACL_SET)
 +ELSE(ENABLE_ACL)
 +  # If someone runs cmake, then disables ACL support, we need
 +  # to forcibly override the cached values for these.
-   SET(HAVE_ACL_CREATE_ENTRY FALSE)
-   SET(HAVE_ACL_GET_LINK FALSE)
-   SET(HAVE_ACL_GET_LINK_NP FALSE)
-   SET(HAVE_ACL_GET_PERM FALSE)
-   SET(HAVE_ACL_GET_PERM_NP FALSE)
-   SET(HAVE_ACL_INIT FALSE)
-   SET(HAVE_ACL_LIB FALSE)
-   SET(HAVE_ACL_PERMSET_T FALSE)
-   SET(HAVE_ACL_SET_FD FALSE)
-   SET(HAVE_ACL_SET_FD_NP FALSE)
-   SET(HAVE_ACL_SET_FILE FALSE)
-   SET(HAVE_ACL_TYPE_NFS4 FALSE)
-   SET(HAVE_ACL_USER FALSE)
-   SET(HAVE_ACL_TYPE_EXTENDED FALSE)
-   SET(HAVE_ACL_GET FALSE)
-   SET(HAVE_ACLENT_T FALSE)
-   SET(HAVE_ACE_T FALSE)
-   SET(HAVE_FACL_GET FALSE)
-   SET(HAVE_ACL_SET FALSE)
-   SET(HAVE_FACL_SET FALSE)
++  SET(ARCHIVE_ACL_DARWIN FALSE)
++  SET(ARCHIVE_ACL_FREEBSD FALSE)
++  SET(ARCHIVE_ACL_FREEBSD_NFS4 FALSE)
++  SET(ARCHIVE_ACL_LIBACL FALSE)
++  SET(ARCHIVE_ACL_SUNOS FALSE)
++  SET(ARCHIVE_ACL_SUNOS_NFS4 FALSE)
 +ENDIF(ENABLE_ACL)
 +
 +#
 +# Check MD5/RMD160/SHA support
 +# NOTE: Crypto checks must be run last before generating config.h
 +#
 +CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" LIBC)
 +CHECK_CRYPTO("SHA256;SHA384;SHA512" LIBC2)
 +CHECK_CRYPTO("SHA256;SHA384;SHA512" LIBC3)
 +CHECK_CRYPTO("MD5;SHA1;SHA256;SHA384;SHA512" LIBSYSTEM)
 +CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" NETTLE)
 +CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" OPENSSL)
 +
 +# Libmd has to be probed after OpenSSL.
 +CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA512" LIBMD)
 +
 +CHECK_CRYPTO_WIN("MD5;SHA1;SHA256;SHA384;SHA512")
 +
 +# Generate "config.h" from "build/cmake/config.h.in"
 +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in
 +	${CMAKE_CURRENT_BINARY_DIR}/config.h)
 +INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
 +ADD_DEFINITIONS(-DHAVE_CONFIG_H)
 +
 +#
 +# Register installation of PDF documents.
 +#
 +IF(WIN32 AND NOT CYGWIN)
 +  #
 +  # On Windows platform, It's better that we install PDF documents
 +  # on one's computer.
 +  # These PDF documents are available in the release package.
 +  #
 +  IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/doc/pdf)
 +    INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc/pdf
 +            DESTINATION share/man
 +            FILES_MATCHING PATTERN "*.pdf"
 +    )
 +  ENDIF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/doc/pdf)
 +ENDIF(WIN32 AND NOT CYGWIN)
 +#
 +#
 +#
 +INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/libarchive)
 +#
 +IF(MSVC)
 +  ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
 +ENDIF(MSVC)
 +
 +# We need CoreServices on Mac OS.
 +IF(APPLE)
 +  LIST(APPEND ADDITIONAL_LIBS "-framework CoreServices")
 +ENDIF(APPLE)
 +
 +add_subdirectory(libarchive)
 +
 +install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmlibarchive)
diff --cc Utilities/cmlibarchive/build/cmake/config.h.in
index 55e04b9,0000000..368a451
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/build/cmake/config.h.in
+++ b/Utilities/cmlibarchive/build/cmake/config.h.in
@@@ -1,1245 -1,0 +1,1319 @@@
 +/* config.h.  Generated from build/cmake/config.h.in by cmake configure */
 +#if defined(__osf__)
 +# define _OSF_SOURCE
 +#endif
 +
 +/*
 + * Ensure we have C99-style int64_t, etc, all defined.
 + */
 +
 +/* First, we need to know if the system has already defined them. */
 +#cmakedefine HAVE_INT16_T
 +#cmakedefine HAVE_INT32_T
 +#cmakedefine HAVE_INT64_T
 +#cmakedefine HAVE_INTMAX_T
 +
 +#cmakedefine HAVE_UINT8_T
 +#cmakedefine HAVE_UINT16_T
 +#cmakedefine HAVE_UINT32_T
 +#cmakedefine HAVE_UINT64_T
 +#cmakedefine HAVE_UINTMAX_T
 +
 +/* We might have the types we want under other spellings. */
 +#cmakedefine HAVE___INT64
 +#cmakedefine HAVE_U_INT64_T
 +#cmakedefine HAVE_UNSIGNED___INT64
 +
 +/* The sizes of various standard integer types. */
 + at SIZE_OF_SHORT_CODE@
 + at SIZE_OF_INT_CODE@
 + at SIZE_OF_LONG_CODE@
 + at SIZE_OF_LONG_LONG_CODE@
 + at SIZE_OF_UNSIGNED_SHORT_CODE@
 + at SIZE_OF_UNSIGNED_CODE@
 + at SIZE_OF_UNSIGNED_LONG_CODE@
 + at SIZE_OF_UNSIGNED_LONG_LONG_CODE@
 +
 +/*
 + * If we lack int64_t, define it to the first of __int64, int, long, and long long
 + * that exists and is the right size.
 + */
 +#if !defined(HAVE_INT64_T) && defined(HAVE___INT64)
 +typedef __int64 int64_t;
 +#define HAVE_INT64_T
 +#endif
 +
 +#if !defined(HAVE_INT64_T) && SIZE_OF_INT == 8
 +typedef int int64_t;
 +#define HAVE_INT64_T
 +#endif
 +
 +#if !defined(HAVE_INT64_T) && SIZE_OF_LONG == 8
 +typedef long int64_t;
 +#define HAVE_INT64_T
 +#endif
 +
 +#if !defined(HAVE_INT64_T) && SIZE_OF_LONG_LONG == 8
 +typedef long long int64_t;
 +#define HAVE_INT64_T
 +#endif
 +
 +#if !defined(HAVE_INT64_T)
 +#error No 64-bit integer type was found.
 +#endif
 +
 +/*
 + * Similarly for int32_t
 + */
 +#if !defined(HAVE_INT32_T) && SIZE_OF_INT == 4
 +typedef int int32_t;
 +#define HAVE_INT32_T
 +#endif
 +
 +#if !defined(HAVE_INT32_T) && SIZE_OF_LONG == 4
 +typedef long int32_t;
 +#define HAVE_INT32_T
 +#endif
 +
 +#if !defined(HAVE_INT32_T)
 +#error No 32-bit integer type was found.
 +#endif
 +
 +/*
 + * Similarly for int16_t
 + */
 +#if !defined(HAVE_INT16_T) && SIZE_OF_INT == 2
 +typedef int int16_t;
 +#define HAVE_INT16_T
 +#endif
 +
 +#if !defined(HAVE_INT16_T) && SIZE_OF_SHORT == 2
 +typedef short int16_t;
 +#define HAVE_INT16_T
 +#endif
 +
 +#if !defined(HAVE_INT16_T)
 +#error No 16-bit integer type was found.
 +#endif
 +
 +/*
 + * Similarly for uint64_t
 + */
 +#if !defined(HAVE_UINT64_T) && defined(HAVE_UNSIGNED___INT64)
 +typedef unsigned __int64 uint64_t;
 +#define HAVE_UINT64_T
 +#endif
 +
 +#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED == 8
 +typedef unsigned uint64_t;
 +#define HAVE_UINT64_T
 +#endif
 +
 +#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED_LONG == 8
 +typedef unsigned long uint64_t;
 +#define HAVE_UINT64_T
 +#endif
 +
 +#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED_LONG_LONG == 8
 +typedef unsigned long long uint64_t;
 +#define HAVE_UINT64_T
 +#endif
 +
 +#if !defined(HAVE_UINT64_T)
 +#error No 64-bit unsigned integer type was found.
 +#endif
 +
 +
 +/*
 + * Similarly for uint32_t
 + */
 +#if !defined(HAVE_UINT32_T) && SIZE_OF_UNSIGNED == 4
 +typedef unsigned uint32_t;
 +#define HAVE_UINT32_T
 +#endif
 +
 +#if !defined(HAVE_UINT32_T) && SIZE_OF_UNSIGNED_LONG == 4
 +typedef unsigned long uint32_t;
 +#define HAVE_UINT32_T
 +#endif
 +
 +#if !defined(HAVE_UINT32_T)
 +#error No 32-bit unsigned integer type was found.
 +#endif
 +
 +/*
 + * Similarly for uint16_t
 + */
 +#if !defined(HAVE_UINT16_T) && SIZE_OF_UNSIGNED == 2
 +typedef unsigned uint16_t;
 +#define HAVE_UINT16_T
 +#endif
 +
 +#if !defined(HAVE_UINT16_T) && SIZE_OF_UNSIGNED_SHORT == 2
 +typedef unsigned short uint16_t;
 +#define HAVE_UINT16_T
 +#endif
 +
 +#if !defined(HAVE_UINT16_T)
 +#error No 16-bit unsigned integer type was found.
 +#endif
 +
 +/*
 + * Similarly for uint8_t
 + */
 +#if !defined(HAVE_UINT8_T)
 +typedef unsigned char uint8_t;
 +#define HAVE_UINT8_T
 +#endif
 +
 +#if !defined(HAVE_UINT16_T)
 +#error No 8-bit unsigned integer type was found.
 +#endif
 +
 +/* Define intmax_t and uintmax_t if they are not already defined. */
 +#if !defined(HAVE_INTMAX_T)
 +typedef int64_t intmax_t;
 +#endif
 +
 +#if !defined(HAVE_UINTMAX_T)
 +typedef uint64_t uintmax_t;
 +#endif
 +
 +/* Define ZLIB_WINAPI if zlib was built on Visual Studio. */
 +#cmakedefine ZLIB_WINAPI 1
 +
++/* Darwin ACL support */
++#cmakedefine ARCHIVE_ACL_DARWIN 1
++
++/* FreeBSD ACL support */
++#cmakedefine ARCHIVE_ACL_FREEBSD 1
++
++/* FreeBSD NFSv4 ACL support */
++#cmakedefine ARCHIVE_ACL_FREEBSD_NFS4 1
++
++/* Linux POSIX.1e ACL support via libacl */
++#cmakedefine ARCHIVE_ACL_LIBACL 1
++
++/* Linux NFSv4 ACL support via librichacl */
++#cmakedefine ARCHIVE_ACL_LIBRICHACL 1
++
++/* Solaris ACL support */
++#cmakedefine ARCHIVE_ACL_SUNOS 1
++
++/* Solaris NFSv4 ACL support */
++#cmakedefine ARCHIVE_ACL_SUNOS_NFS4 1
++
 +/* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */
 +#cmakedefine ARCHIVE_CRYPTO_MD5_LIBC 1
 +
 +/* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */
 +#cmakedefine ARCHIVE_CRYPTO_MD5_LIBSYSTEM 1
 +
 +/* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */
 +#cmakedefine ARCHIVE_CRYPTO_MD5_NETTLE 1
 +
 +/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */
 +#cmakedefine ARCHIVE_CRYPTO_MD5_OPENSSL 1
 +
 +/* MD5 via ARCHIVE_CRYPTO_MD5_WIN supported. */
 +#cmakedefine ARCHIVE_CRYPTO_MD5_WIN 1
 +
 +/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */
 +#cmakedefine ARCHIVE_CRYPTO_RMD160_LIBC 1
 +
 +/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */
 +#cmakedefine ARCHIVE_CRYPTO_RMD160_NETTLE 1
 +
 +/* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */
 +#cmakedefine ARCHIVE_CRYPTO_RMD160_OPENSSL 1
 +
 +/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA1_LIBC 1
 +
 +/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA1_LIBSYSTEM 1
 +
 +/* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA1_NETTLE 1
 +
 +/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA1_OPENSSL 1
 +
 +/* SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA1_WIN 1
 +
 +/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA256_LIBC 1
 +
 +/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC2 supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA256_LIBC2 1
 +
 +/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA256_LIBC3 1
 +
 +/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA256_LIBSYSTEM 1
 +
 +/* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA256_NETTLE 1
 +
 +/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA256_OPENSSL 1
 +
 +/* SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA256_WIN 1
 +
 +/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA384_LIBC 1
 +
 +/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC2 supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA384_LIBC2 1
 +
 +/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC3 supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA384_LIBC3 1
 +
 +/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA384_LIBSYSTEM 1
 +
 +/* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA384_NETTLE 1
 +
 +/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA384_OPENSSL 1
 +
 +/* SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA384_WIN 1
 +
 +/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA512_LIBC 1
 +
 +/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC2 supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA512_LIBC2 1
 +
 +/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA512_LIBC3 1
 +
 +/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA512_LIBSYSTEM 1
 +
 +/* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA512_NETTLE 1
 +
 +/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA512_OPENSSL 1
 +
 +/* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */
 +#cmakedefine ARCHIVE_CRYPTO_SHA512_WIN 1
 +
++/* AIX xattr support */
++#cmakedefine ARCHIVE_XATTR_AIX 1
++
++/* Darwin xattr support */
++#cmakedefine ARCHIVE_XATTR_DARWIN 1
++
++/* FreeBSD xattr support */
++#cmakedefine ARCHIVE_XATTR_FREEBSD 1
++
++/* Linux xattr support */
++#cmakedefine ARCHIVE_XATTR_LINUX 1
++
 +/* Version number of bsdcpio */
 +#cmakedefine BSDCPIO_VERSION_STRING "${BSDCPIO_VERSION_STRING}"
 +
 +/* Version number of bsdtar */
 +#cmakedefine BSDTAR_VERSION_STRING "${BSDTAR_VERSION_STRING}"
 +
 +/* Version number of bsdcat */
 +#cmakedefine BSDCAT_VERSION_STRING "${BSDCAT_VERSION_STRING}"
 +
 +/* Define to 1 if you have the `acl_create_entry' function. */
 +#cmakedefine HAVE_ACL_CREATE_ENTRY 1
 +
 +/* Define to 1 if you have the `acl_get_fd_np' function. */
 +#cmakedefine HAVE_ACL_GET_FD_NP 1
 +
 +/* Define to 1 if you have the `acl_get_link' function. */
 +#cmakedefine HAVE_ACL_GET_LINK 1
 +
 +/* Define to 1 if you have the `acl_get_link_np' function. */
 +#cmakedefine HAVE_ACL_GET_LINK_NP 1
 +
 +/* Define to 1 if you have the `acl_get_perm' function. */
 +#cmakedefine HAVE_ACL_GET_PERM 1
 +
 +/* Define to 1 if you have the `acl_get_perm_np' function. */
 +#cmakedefine HAVE_ACL_GET_PERM_NP 1
 +
 +/* Define to 1 if you have the `acl_init' function. */
 +#cmakedefine HAVE_ACL_INIT 1
 +
 +/* Define to 1 if you have the <acl/libacl.h> header file. */
 +#cmakedefine HAVE_ACL_LIBACL_H 1
 +
 +/* Define to 1 if the system has the type `acl_permset_t'. */
 +#cmakedefine HAVE_ACL_PERMSET_T 1
 +
 +/* Define to 1 if you have the `acl_set_fd' function. */
 +#cmakedefine HAVE_ACL_SET_FD 1
 +
 +/* Define to 1 if you have the `acl_set_fd_np' function. */
 +#cmakedefine HAVE_ACL_SET_FD_NP 1
 +
 +/* Define to 1 if you have the `acl_set_file' function. */
 +#cmakedefine HAVE_ACL_SET_FILE 1
 +
- /* True for FreeBSD with NFSv4 ACL support */
- #cmakedefine HAVE_ACL_TYPE_NFS4 1
- 
- /* True for MacOS ACL support */
- #cmakedefine HAVE_ACL_TYPE_EXTENDED 1
- 
- /* True for systems with POSIX ACL support */
- #cmakedefine HAVE_ACL_USER 1
- 
 +/* Define to 1 if you have the `arc4random_buf' function. */
 +#cmakedefine HAVE_ARC4RANDOM_BUF 1
 +
 +/* Define to 1 if you have the <attr/xattr.h> header file. */
 +#cmakedefine HAVE_ATTR_XATTR_H 1
 +
 +/* Define to 1 if you have the <Bcrypt.h> header file. */
 +#cmakedefine HAVE_BCRYPT_H 1
 +
 +/* Define to 1 if you have the <bsdxml.h> header file. */
 +#cmakedefine HAVE_BSDXML_H 1
 +
 +/* Define to 1 if you have the <bzlib.h> header file. */
 +#cmakedefine HAVE_BZLIB_H 1
 +
 +/* Define to 1 if you have the `chflags' function. */
 +#cmakedefine HAVE_CHFLAGS 1
 +
 +/* Define to 1 if you have the `chown' function. */
 +#cmakedefine HAVE_CHOWN 1
 +
 +/* Define to 1 if you have the `chroot' function. */
 +#cmakedefine HAVE_CHROOT 1
 +
 +/* Define to 1 if you have the <copyfile.h> header file. */
 +#cmakedefine HAVE_COPYFILE_H 1
 +
 +/* Define to 1 if you have the `ctime_r' function. */
 +#cmakedefine HAVE_CTIME_R 1
 +
 +/* Define to 1 if you have the <ctype.h> header file. */
 +#cmakedefine HAVE_CTYPE_H 1
 +
 +/* Define to 1 if you have the `cygwin_conv_path' function. */
 +#cmakedefine HAVE_CYGWIN_CONV_PATH 1
 +
++/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you
++   don't. */
++#cmakedefine HAVE_DECL_ACE_GETACL 1
++
++/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you
++   don't. */
++#cmakedefine HAVE_DECL_ACE_GETACLCNT 1
++
++/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you
++   don't. */
++#cmakedefine HAVE_DECL_ACE_SETACL 1
++
++/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if
++   you don't. */
++#cmakedefine HAVE_DECL_ACL_SYNCHRONIZE 1
++
++/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if
++   you don't. */
++#cmakedefine HAVE_DECL_ACL_TYPE_EXTENDED 1
++
++/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you
++   don't. */
++#cmakedefine HAVE_DECL_ACL_TYPE_NFS4 1
++
++/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you
++   don't. */
++#cmakedefine HAVE_DECL_ACL_USER 1
++
 +/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_INT32_MAX 1
 +
 +/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_INT32_MIN 1
 +
 +/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_INT64_MAX 1
 +
 +/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_INT64_MIN 1
 +
 +/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_INTMAX_MAX 1
 +
 +/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_INTMAX_MIN 1
 +
++/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.
++   */
++#cmakedefine HAVE_DECL_SETACL 1
++
 +/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_SIZE_MAX 1
 +
 +/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_SSIZE_MAX 1
 +
 +/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_STRERROR_R 1
 +
 +/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_UINT32_MAX 1
 +
 +/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_UINT64_MAX 1
 +
 +/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you
 +   don't. */
 +#cmakedefine HAVE_DECL_UINTMAX_MAX 1
 +
++/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if
++   you don't. */
++#cmakedefine HAVE_DECL_XATTR_NOFOLLOW 1
++
 +/* Define to 1 if you have the <direct.h> header file. */
 +#cmakedefine HAVE_DIRECT_H 1
 +
 +/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
 +   */
 +#cmakedefine HAVE_DIRENT_H 1
 +
 +/* Define to 1 if you have the `dirfd' function. */
 +#cmakedefine HAVE_DIRFD 1
 +
 +/* Define to 1 if you have the <dlfcn.h> header file. */
 +#cmakedefine HAVE_DLFCN_H 1
 +
 +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
 +#cmakedefine HAVE_DOPRNT 1
 +
 +/* Define to 1 if nl_langinfo supports D_MD_ORDER */
 +#cmakedefine HAVE_D_MD_ORDER 1
 +
 +/* A possible errno value for invalid file format errors */
 +#cmakedefine HAVE_EFTYPE 1
 +
 +/* A possible errno value for invalid file format errors */
 +#cmakedefine HAVE_EILSEQ 1
 +
 +/* Define to 1 if you have the <errno.h> header file. */
 +#cmakedefine HAVE_ERRNO_H 1
 +
 +/* Define to 1 if you have the <expat.h> header file. */
 +#cmakedefine HAVE_EXPAT_H 1
 +
 +/* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */
 +#cmakedefine HAVE_EXT2FS_EXT2_FS_H 1
 +
 +/* Define to 1 if you have the `extattr_get_file' function. */
 +#cmakedefine HAVE_EXTATTR_GET_FILE 1
 +
 +/* Define to 1 if you have the `extattr_list_file' function. */
 +#cmakedefine HAVE_EXTATTR_LIST_FILE 1
 +
 +/* Define to 1 if you have the `extattr_set_fd' function. */
 +#cmakedefine HAVE_EXTATTR_SET_FD 1
 +
 +/* Define to 1 if you have the `extattr_set_file' function. */
 +#cmakedefine HAVE_EXTATTR_SET_FILE 1
 +
 +/* Define to 1 if EXTATTR_NAMESPACE_USER is defined in sys/extattr.h. */
 +#cmakedefine HAVE_DECL_EXTATTR_NAMESPACE_USER 1
 +
++/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.
++   */
++#cmakedefine HAVE_DECL_GETACL 1
++
++/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you
++   don't. */
++#cmakedefine HAVE_DECL_GETACLCNT 1
++
 +/* Define to 1 if you have the `fchdir' function. */
 +#cmakedefine HAVE_FCHDIR 1
 +
 +/* Define to 1 if you have the `fchflags' function. */
 +#cmakedefine HAVE_FCHFLAGS 1
 +
 +/* Define to 1 if you have the `fchmod' function. */
 +#cmakedefine HAVE_FCHMOD 1
 +
 +/* Define to 1 if you have the `fchown' function. */
 +#cmakedefine HAVE_FCHOWN 1
 +
 +/* Define to 1 if you have the `fcntl' function. */
 +#cmakedefine HAVE_FCNTL 1
 +
 +/* Define to 1 if you have the <fcntl.h> header file. */
 +#cmakedefine HAVE_FCNTL_H 1
 +
 +/* Define to 1 if you have the `fdopendir' function. */
 +#cmakedefine HAVE_FDOPENDIR 1
 +
 +/* Define to 1 if you have the `fgetea' function. */
 +#cmakedefine HAVE_FGETEA 1
 +
 +/* Define to 1 if you have the `fgetxattr' function. */
 +#cmakedefine HAVE_FGETXATTR 1
 +
 +/* Define to 1 if you have the `flistea' function. */
 +#cmakedefine HAVE_FLISTEA 1
 +
 +/* Define to 1 if you have the `flistxattr' function. */
 +#cmakedefine HAVE_FLISTXATTR 1
 +
 +/* Define to 1 if you have the `fork' function. */
 +#cmakedefine HAVE_FORK 1
 +
 +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
 +#cmakedefine HAVE_FSEEKO 1
 +
 +/* Define to 1 if you have the `fsetea' function. */
 +#cmakedefine HAVE_FSETEA 1
 +
 +/* Define to 1 if you have the `fsetxattr' function. */
 +#cmakedefine HAVE_FSETXATTR 1
 +
 +/* Define to 1 if you have the `fstat' function. */
 +#cmakedefine HAVE_FSTAT 1
 +
 +/* Define to 1 if you have the `fstatat' function. */
 +#cmakedefine HAVE_FSTATAT 1
 +
 +/* Define to 1 if you have the `fstatfs' function. */
 +#cmakedefine HAVE_FSTATFS 1
 +
 +/* Define to 1 if you have the `fstatvfs' function. */
 +#cmakedefine HAVE_FSTATVFS 1
 +
 +/* Define to 1 if you have the `ftruncate' function. */
 +#cmakedefine HAVE_FTRUNCATE 1
 +
 +/* Define to 1 if you have the `futimens' function. */
 +#cmakedefine HAVE_FUTIMENS 1
 +
 +/* Define to 1 if you have the `futimes' function. */
 +#cmakedefine HAVE_FUTIMES 1
 +
 +/* Define to 1 if you have the `futimesat' function. */
 +#cmakedefine HAVE_FUTIMESAT 1
 +
 +/* Define to 1 if you have the `getea' function. */
 +#cmakedefine HAVE_GETEA 1
 +
 +/* Define to 1 if you have the `geteuid' function. */
 +#cmakedefine HAVE_GETEUID 1
 +
 +/* Define to 1 if you have the `getgrgid_r' function. */
 +#cmakedefine HAVE_GETGRGID_R 1
 +
 +/* Define to 1 if you have the `getgrnam_r' function. */
 +#cmakedefine HAVE_GETGRNAM_R 1
 +
 +/* Define to 1 if you have the `getpid' function. */
 +#cmakedefine HAVE_GETPID 1
 +
 +/* Define to 1 if you have the `getpwnam_r' function. */
 +#cmakedefine HAVE_GETPWNAM_R 1
 +
 +/* Define to 1 if you have the `getpwuid_r' function. */
 +#cmakedefine HAVE_GETPWUID_R 1
 +
 +/* Define to 1 if you have the `getvfsbyname' function. */
 +#cmakedefine HAVE_GETVFSBYNAME 1
 +
 +/* Define to 1 if you have the `getxattr' function. */
 +#cmakedefine HAVE_GETXATTR 1
 +
 +/* Define to 1 if you have the `gmtime_r' function. */
 +#cmakedefine HAVE_GMTIME_R 1
 +
 +/* Define to 1 if you have the <grp.h> header file. */
 +#cmakedefine HAVE_GRP_H 1
 +
 +/* Define to 1 if you have the `iconv' function. */
 +#cmakedefine HAVE_ICONV 1
 +
 +/* Define to 1 if you have the <iconv.h> header file. */
 +#cmakedefine HAVE_ICONV_H 1
 +
 +/* Define to 1 if you have the <inttypes.h> header file. */
 +#cmakedefine HAVE_INTTYPES_H 1
 +
 +/* Define to 1 if you have the <io.h> header file. */
 +#cmakedefine HAVE_IO_H 1
 +
 +/* Define to 1 if you have the <langinfo.h> header file. */
 +#cmakedefine HAVE_LANGINFO_H 1
 +
 +/* Define to 1 if you have the `lchflags' function. */
 +#cmakedefine HAVE_LCHFLAGS 1
 +
 +/* Define to 1 if you have the `lchmod' function. */
 +#cmakedefine HAVE_LCHMOD 1
 +
 +/* Define to 1 if you have the `lchown' function. */
 +#cmakedefine HAVE_LCHOWN 1
 +
 +/* Define to 1 if you have the `lgetea' function. */
 +#cmakedefine HAVE_LGETEA 1
 +
 +/* Define to 1 if you have the `lgetxattr' function. */
 +#cmakedefine HAVE_LGETXATTR 1
 +
 +/* Define to 1 if you have the `acl' library (-lacl). */
 +#cmakedefine HAVE_LIBACL 1
 +
 +/* Define to 1 if you have the `attr' library (-lattr). */
 +#cmakedefine HAVE_LIBATTR 1
 +
 +/* Define to 1 if you have the `bsdxml' library (-lbsdxml). */
 +#cmakedefine HAVE_LIBBSDXML 1
 +
 +/* Define to 1 if you have the `bz2' library (-lbz2). */
 +#cmakedefine HAVE_LIBBZ2 1
 +
 +/* Define to 1 if you have the `charset' library (-lcharset). */
 +#cmakedefine HAVE_LIBCHARSET 1
 +
 +/* Define to 1 if you have the `crypto' library (-lcrypto). */
 +#cmakedefine HAVE_LIBCRYPTO 1
 +
 +/* Define to 1 if you have the `expat' library (-lexpat). */
 +#cmakedefine HAVE_LIBEXPAT 1
 +
 +/* Define to 1 if you have the `gcc' library (-lgcc). */
 +#cmakedefine HAVE_LIBGCC 1
 +
 +/* Define to 1 if you have the `lz4' library (-llz4). */
 +#cmakedefine HAVE_LIBLZ4 1
 +
 +/* Define to 1 if you have the `lzma' library (-llzma). */
 +#cmakedefine HAVE_LIBLZMA 1
 +
 +/* Define to 1 if you have the `lzmadec' library (-llzmadec). */
 +#cmakedefine HAVE_LIBLZMADEC 1
 +
 +/* Define to 1 if you have the `lzo2' library (-llzo2). */
 +#cmakedefine HAVE_LIBLZO2 1
 +
 +/* Define to 1 if you have the `nettle' library (-lnettle). */
 +#cmakedefine HAVE_LIBNETTLE 1
 +
 +/* Define to 1 if you have the `pcre' library (-lpcre). */
 +#cmakedefine HAVE_LIBPCRE 1
 +
 +/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */
 +#cmakedefine HAVE_LIBPCREPOSIX 1
 +
 +/* Define to 1 if you have the `xml2' library (-lxml2). */
 +#cmakedefine HAVE_LIBXML2 1
 +
 +/* Define to 1 if you have the <libxml/xmlreader.h> header file. */
 +#cmakedefine HAVE_LIBXML_XMLREADER_H 1
 +
 +/* Define to 1 if you have the <libxml/xmlwriter.h> header file. */
 +#cmakedefine HAVE_LIBXML_XMLWRITER_H 1
 +
 +/* Define to 1 if you have the `z' library (-lz). */
 +#cmakedefine HAVE_LIBZ 1
 +
 +/* Define to 1 if you have the <limits.h> header file. */
 +#cmakedefine HAVE_LIMITS_H 1
 +
 +/* Define to 1 if you have the `link' function. */
 +#cmakedefine HAVE_LINK 1
 +
 +/* Define to 1 if you have the <linux/types.h> header file. */
 +#cmakedefine HAVE_LINUX_TYPES_H 1
 +
 +/* Define to 1 if you have the <linux/fiemap.h> header file. */
 +#cmakedefine HAVE_LINUX_FIEMAP_H 1
 +
 +/* Define to 1 if you have the <linux/fs.h> header file. */
 +#cmakedefine HAVE_LINUX_FS_H 1
 +
 +/* Define to 1 if you have the <linux/magic.h> header file. */
 +#cmakedefine HAVE_LINUX_MAGIC_H 1
 +
 +/* Define to 1 if you have the <linux/types.h> header file. */
 +#cmakedefine HAVE_LINUX_TYPES_H 1
 +
 +/* Define to 1 if you have the `listea' function. */
 +#cmakedefine HAVE_LISTEA 1
 +
 +/* Define to 1 if you have the `listxattr' function. */
 +#cmakedefine HAVE_LISTXATTR 1
 +
 +/* Define to 1 if you have the `llistea' function. */
 +#cmakedefine HAVE_LLISTEA 1
 +
 +/* Define to 1 if you have the `llistxattr' function. */
 +#cmakedefine HAVE_LLISTXATTR 1
 +
 +/* Define to 1 if you have the <localcharset.h> header file. */
 +#cmakedefine HAVE_LOCALCHARSET_H 1
 +
 +/* Define to 1 if you have the `locale_charset' function. */
 +#cmakedefine HAVE_LOCALE_CHARSET 1
 +
 +/* Define to 1 if you have the <locale.h> header file. */
 +#cmakedefine HAVE_LOCALE_H 1
 +
 +/* Define to 1 if you have the `localtime_r' function. */
 +#cmakedefine HAVE_LOCALTIME_R 1
 +
 +/* Define to 1 if the system has the type `long long int'. */
 +#cmakedefine HAVE_LONG_LONG_INT 1
 +
 +/* Define to 1 if you have the `lsetea' function. */
 +#cmakedefine HAVE_LSETEA 1
 +
 +/* Define to 1 if you have the `lsetxattr' function. */
 +#cmakedefine HAVE_LSETXATTR 1
 +
 +/* Define to 1 if you have the `lstat' function. */
 +#cmakedefine HAVE_LSTAT 1
 +
 +/* Define to 1 if `lstat' has the bug that it succeeds when given the
 +   zero-length file name argument. */
 +#cmakedefine HAVE_LSTAT_EMPTY_STRING_BUG 1
 +
 +/* Define to 1 if you have the `lutimes' function. */
 +#cmakedefine HAVE_LUTIMES 1
 +
 +/* Define to 1 if you have the <lz4hc.h> header file. */
 +#cmakedefine HAVE_LZ4HC_H 1
 +
 +/* Define to 1 if you have the <lz4.h> header file. */
 +#cmakedefine HAVE_LZ4_H 1
 +
 +/* Define to 1 if you have the <lzmadec.h> header file. */
 +#cmakedefine HAVE_LZMADEC_H 1
 +
 +/* Define to 1 if you have the <lzma.h> header file. */
 +#cmakedefine HAVE_LZMA_H 1
 +
 +/* Define to 1 if you have a working `lzma_stream_encoder_mt' function. */
 +#cmakedefine HAVE_LZMA_STREAM_ENCODER_MT 1
 +
 +/* Define to 1 if you have the <lzo/lzo1x.h> header file. */
 +#cmakedefine HAVE_LZO_LZO1X_H 1
 +
 +/* Define to 1 if you have the <lzo/lzoconf.h> header file. */
 +#cmakedefine HAVE_LZO_LZOCONF_H 1
 +
 +/* Define to 1 if you have the `mbrtowc' function. */
 +#cmakedefine HAVE_MBRTOWC 1
 +
++/* Define to 1 if you have the <membership.h> header file. */
++#cmakedefine HAVE_MEMBERSHIP_H 1
++
 +/* Define to 1 if you have the `memmove' function. */
 +#cmakedefine HAVE_MEMMOVE 1
 +
 +/* Define to 1 if you have the <memory.h> header file. */
 +#cmakedefine HAVE_MEMORY_H 1
 +
 +/* Define to 1 if you have the `mkdir' function. */
 +#cmakedefine HAVE_MKDIR 1
 +
 +/* Define to 1 if you have the `mkfifo' function. */
 +#cmakedefine HAVE_MKFIFO 1
 +
 +/* Define to 1 if you have the `mknod' function. */
 +#cmakedefine HAVE_MKNOD 1
 +
 +/* Define to 1 if you have the `mkstemp' function. */
 +#cmakedefine HAVE_MKSTEMP 1
 +
 +/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
 +#cmakedefine HAVE_NDIR_H 1
 +
 +/* Define to 1 if you have the <nettle/aes.h> header file. */
 +#cmakedefine HAVE_NETTLE_AES_H 1
 +
 +/* Define to 1 if you have the <nettle/hmac.h> header file. */
 +#cmakedefine HAVE_NETTLE_HMAC_H 1
 +
 +/* Define to 1 if you have the <nettle/md5.h> header file. */
 +#cmakedefine HAVE_NETTLE_MD5_H 1
 +
 +/* Define to 1 if you have the <nettle/pbkdf2.h> header file. */
 +#cmakedefine HAVE_NETTLE_PBKDF2_H 1
 +
 +/* Define to 1 if you have the <nettle/ripemd160.h> header file. */
 +#cmakedefine HAVE_NETTLE_RIPEMD160_H 1
 +
 +/* Define to 1 if you have the <nettle/sha.h> header file. */
 +#cmakedefine HAVE_NETTLE_SHA_H 1
 +
 +/* Define to 1 if you have the `nl_langinfo' function. */
 +#cmakedefine HAVE_NL_LANGINFO 1
 +
 +/* Define to 1 if you have the `openat' function. */
 +#cmakedefine HAVE_OPENAT 1
 +
 +/* Define to 1 if you have the <paths.h> header file. */
 +#cmakedefine HAVE_PATHS_H 1
 +
 +/* Define to 1 if you have the <pcreposix.h> header file. */
 +#cmakedefine HAVE_PCREPOSIX_H 1
 +
 +/* Define to 1 if you have the `pipe' function. */
 +#cmakedefine HAVE_PIPE 1
 +
 +/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */
 +#cmakedefine HAVE_PKCS5_PBKDF2_HMAC_SHA1 1
 +
 +/* Define to 1 if you have the `poll' function. */
 +#cmakedefine HAVE_POLL 1
 +
 +/* Define to 1 if you have the <poll.h> header file. */
 +#cmakedefine HAVE_POLL_H 1
 +
 +/* Define to 1 if you have the `posix_spawnp' function. */
 +#cmakedefine HAVE_POSIX_SPAWNP 1
 +
 +/* Define to 1 if you have the <process.h> header file. */
 +#cmakedefine HAVE_PROCESS_H 1
 +
 +/* Define to 1 if you have the <pthread.h> header file. */
 +#cmakedefine HAVE_PTHREAD_H 1
 +
 +/* Define to 1 if you have the <pwd.h> header file. */
 +#cmakedefine HAVE_PWD_H 1
 +
 +/* Define to 1 if you have the `readdir_r' function. */
 +#cmakedefine HAVE_READDIR_R 1
 +
 +/* Define to 1 if you have the `readlink' function. */
 +#cmakedefine HAVE_READLINK 1
 +
 +/* Define to 1 if you have the `readlinkat' function. */
 +#cmakedefine HAVE_READLINKAT 1
 +
 +/* Define to 1 if you have the `readpassphrase' function. */
 +#cmakedefine HAVE_READPASSPHRASE 1
 +
 +/* Define to 1 if you have the <readpassphrase.h> header file. */
 +#cmakedefine HAVE_READPASSPHRASE_H 1
 +
 +/* Define to 1 if you have the <regex.h> header file. */
 +#cmakedefine HAVE_REGEX_H 1
 +
 +/* Define to 1 if you have the `select' function. */
 +#cmakedefine HAVE_SELECT 1
 +
 +/* Define to 1 if you have the `setenv' function. */
 +#cmakedefine HAVE_SETENV 1
 +
 +/* Define to 1 if you have the `setlocale' function. */
 +#cmakedefine HAVE_SETLOCALE 1
 +
 +/* Define to 1 if you have the `sigaction' function. */
 +#cmakedefine HAVE_SIGACTION 1
 +
 +/* Define to 1 if you have the <signal.h> header file. */
 +#cmakedefine HAVE_SIGNAL_H 1
 +
 +/* Define to 1 if you have the <spawn.h> header file. */
 +#cmakedefine HAVE_SPAWN_H 1
 +
 +/* Define to 1 if you have the `statfs' function. */
 +#cmakedefine HAVE_STATFS 1
 +
 +/* Define to 1 if you have the `statvfs' function. */
 +#cmakedefine HAVE_STATVFS 1
 +
 +/* Define to 1 if `stat' has the bug that it succeeds when given the
 +   zero-length file name argument. */
 +#cmakedefine HAVE_STAT_EMPTY_STRING_BUG 1
 +
 +/* Define to 1 if you have the <stdarg.h> header file. */
 +#cmakedefine HAVE_STDARG_H 1
 +
 +/* Define to 1 if you have the <stdint.h> header file. */
 +#cmakedefine HAVE_STDINT_H 1
 +
 +/* Define to 1 if you have the <stdlib.h> header file. */
 +#cmakedefine HAVE_STDLIB_H 1
 +
 +/* Define to 1 if you have the `strchr' function. */
 +#cmakedefine HAVE_STRCHR 1
 +
 +/* Define to 1 if you have the `strdup' function. */
 +#cmakedefine HAVE_STRDUP 1
 +
 +/* Define to 1 if you have the `strerror' function. */
 +#cmakedefine HAVE_STRERROR 1
 +
 +/* Define to 1 if you have the `strerror_r' function. */
 +#cmakedefine HAVE_STRERROR_R 1
 +
 +/* Define to 1 if you have the `strftime' function. */
 +#cmakedefine HAVE_STRFTIME 1
 +
 +/* Define to 1 if you have the <strings.h> header file. */
 +#cmakedefine HAVE_STRINGS_H 1
 +
 +/* Define to 1 if you have the <string.h> header file. */
 +#cmakedefine HAVE_STRING_H 1
 +
 +/* Define to 1 if you have the `strrchr' function. */
 +#cmakedefine HAVE_STRRCHR 1
 +
 +/* Define to 1 if `f_namemax' is a member of `struct statfs'. */
 +#cmakedefine HAVE_STRUCT_STATFS_F_NAMEMAX 1
 +
 +/* Define to 1 if `f_iosize' is a member of `struct statvfs'. */
 +#cmakedefine HAVE_STRUCT_STATVFS_F_IOSIZE 1
 +
 +/* Define to 1 if `st_birthtime' is a member of `struct stat'. */
 +#cmakedefine HAVE_STRUCT_STAT_ST_BIRTHTIME 1
 +
 +/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */
 +#cmakedefine HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
 +
 +/* Define to 1 if `st_blksize' is a member of `struct stat'. */
 +#cmakedefine HAVE_STRUCT_STAT_ST_BLKSIZE 1
 +
 +/* Define to 1 if `st_flags' is a member of `struct stat'. */
 +#cmakedefine HAVE_STRUCT_STAT_ST_FLAGS 1
 +
 +/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */
 +#cmakedefine HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
 +
 +/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */
 +#cmakedefine HAVE_STRUCT_STAT_ST_MTIME_N 1
 +
 +/* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */
 +#cmakedefine HAVE_STRUCT_STAT_ST_MTIME_USEC 1
 +
 +/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
 +#cmakedefine HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
 +
 +/* Define to 1 if `st_umtime' is a member of `struct stat'. */
 +#cmakedefine HAVE_STRUCT_STAT_ST_UMTIME 1
 +
 +/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */
 +#cmakedefine HAVE_STRUCT_TM_TM_GMTOFF 1
 +
 +/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */
 +#cmakedefine HAVE_STRUCT_TM___TM_GMTOFF 1
 +
 +/* Define to 1 if you have `struct vfsconf'. */
 +#cmakedefine HAVE_STRUCT_VFSCONF 1
 +
 +/* Define to 1 if you have `struct xvfsconf'. */
 +#cmakedefine HAVE_STRUCT_XVFSCONF 1
 +
 +/* Define to 1 if you have the `symlink' function. */
 +#cmakedefine HAVE_SYMLINK 1
 +
 +/* Define to 1 if you have the <sys/acl.h> header file. */
 +#cmakedefine HAVE_SYS_ACL_H 1
 +
 +/* Define to 1 if you have the <sys/cdefs.h> header file. */
 +#cmakedefine HAVE_SYS_CDEFS_H 1
 +
 +/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
 +   */
 +#cmakedefine HAVE_SYS_DIR_H 1
 +
 +/* Define to 1 if you have the <sys/ea.h> header file. */
 +#cmakedefine HAVE_SYS_EA_H 1
 +
 +/* Define to 1 if you have the <sys/extattr.h> header file. */
 +#cmakedefine HAVE_SYS_EXTATTR_H 1
 +
 +/* Define to 1 if you have the <sys/ioctl.h> header file. */
 +#cmakedefine HAVE_SYS_IOCTL_H 1
 +
 +/* Define to 1 if you have the <sys/mkdev.h> header file. */
 +#cmakedefine HAVE_SYS_MKDEV_H 1
 +
 +/* Define to 1 if you have the <sys/mount.h> header file. */
 +#cmakedefine HAVE_SYS_MOUNT_H 1
 +
 +/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
 +   */
 +#cmakedefine HAVE_SYS_NDIR_H 1
 +
 +/* Define to 1 if you have the <sys/param.h> header file. */
 +#cmakedefine HAVE_SYS_PARAM_H 1
 +
 +/* Define to 1 if you have the <sys/poll.h> header file. */
 +#cmakedefine HAVE_SYS_POLL_H 1
 +
++/* Define to 1 if you have the <sys/richacl.h> header file. */
++#cmakedefine HAVE_SYS_RICHACL_H 1
++
 +/* Define to 1 if you have the <sys/select.h> header file. */
 +#cmakedefine HAVE_SYS_SELECT_H 1
 +
 +/* Define to 1 if you have the <sys/statfs.h> header file. */
 +#cmakedefine HAVE_SYS_STATFS_H 1
 +
 +/* Define to 1 if you have the <sys/statvfs.h> header file. */
 +#cmakedefine HAVE_SYS_STATVFS_H 1
 +
 +/* Define to 1 if you have the <sys/stat.h> header file. */
 +#cmakedefine HAVE_SYS_STAT_H 1
 +
 +/* Define to 1 if you have the <sys/time.h> header file. */
 +#cmakedefine HAVE_SYS_TIME_H 1
 +
 +/* Define to 1 if you have the <sys/types.h> header file. */
 +#cmakedefine HAVE_SYS_TYPES_H 1
 +
 +/* Define to 1 if you have the <sys/utime.h> header file. */
 +#cmakedefine HAVE_SYS_UTIME_H 1
 +
 +/* Define to 1 if you have the <sys/utsname.h> header file. */
 +#cmakedefine HAVE_SYS_UTSNAME_H 1
 +
 +/* Define to 1 if you have the <sys/vfs.h> header file. */
 +#cmakedefine HAVE_SYS_VFS_H 1
 +
 +/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
 +#cmakedefine HAVE_SYS_WAIT_H 1
 +
 +/* Define to 1 if you have the <sys/xattr.h> header file. */
 +#cmakedefine HAVE_SYS_XATTR_H 1
 +
 +/* Define to 1 if you have the `timegm' function. */
 +#cmakedefine HAVE_TIMEGM 1
 +
 +/* Define to 1 if you have the <time.h> header file. */
 +#cmakedefine HAVE_TIME_H 1
 +
 +/* Define to 1 if you have the `tzset' function. */
 +#cmakedefine HAVE_TZSET 1
 +
 +/* Define to 1 if you have the <unistd.h> header file. */
 +#cmakedefine HAVE_UNISTD_H 1
 +
 +/* Define to 1 if you have the `unsetenv' function. */
 +#cmakedefine HAVE_UNSETENV 1
 +
 +/* Define to 1 if the system has the type `unsigned long long'. */
 +#cmakedefine HAVE_UNSIGNED_LONG_LONG 1
 +
 +/* Define to 1 if the system has the type `unsigned long long int'. */
 +#cmakedefine HAVE_UNSIGNED_LONG_LONG_INT 1
 +
 +/* Define to 1 if you have the `utime' function. */
 +#cmakedefine HAVE_UTIME 1
 +
 +/* Define to 1 if you have the `utimensat' function. */
 +#cmakedefine HAVE_UTIMENSAT 1
 +
 +/* Define to 1 if you have the `utimes' function. */
 +#cmakedefine HAVE_UTIMES 1
 +
 +/* Define to 1 if you have the <utime.h> header file. */
 +#cmakedefine HAVE_UTIME_H 1
 +
 +/* Define to 1 if you have the `vfork' function. */
 +#cmakedefine HAVE_VFORK 1
 +
 +/* Define to 1 if you have the `vprintf' function. */
 +#cmakedefine HAVE_VPRINTF 1
 +
 +/* Define to 1 if you have the <wchar.h> header file. */
 +#cmakedefine HAVE_WCHAR_H 1
 +
 +/* Define to 1 if the system has the type `wchar_t'. */
 +#cmakedefine HAVE_WCHAR_T 1
 +
 +/* Define to 1 if you have the `wcrtomb' function. */
 +#cmakedefine HAVE_WCRTOMB 1
 +
 +/* Define to 1 if you have the `wcscmp' function. */
 +#cmakedefine HAVE_WCSCMP 1
 +
 +/* Define to 1 if you have the `wcscpy' function. */
 +#cmakedefine HAVE_WCSCPY 1
 +
 +/* Define to 1 if you have the `wcslen' function. */
 +#cmakedefine HAVE_WCSLEN 1
 +
 +/* Define to 1 if you have the `wctomb' function. */
 +#cmakedefine HAVE_WCTOMB 1
 +
 +/* Define to 1 if you have the <wctype.h> header file. */
 +#cmakedefine HAVE_WCTYPE_H 1
 +
 +/* Define to 1 if you have the <wincrypt.h> header file. */
 +#cmakedefine HAVE_WINCRYPT_H 1
 +
 +/* Define to 1 if you have the <windows.h> header file. */
 +#cmakedefine HAVE_WINDOWS_H 1
 +
 +/* Define to 1 if you have the <winioctl.h> header file. */
 +#cmakedefine HAVE_WINIOCTL_H 1
 +
 +/* Define to 1 if you have _CrtSetReportMode in <crtdbg.h>  */
 +#cmakedefine HAVE__CrtSetReportMode 1
 +
 +/* Define to 1 if you have the `wmemcmp' function. */
 +#cmakedefine HAVE_WMEMCMP 1
 +
 +/* Define to 1 if you have the `wmemcpy' function. */
 +#cmakedefine HAVE_WMEMCPY 1
 +
 +/* Define to 1 if you have the `wmemmove' function. */
 +#cmakedefine HAVE_WMEMMOVE 1
 +
 +/* Define to 1 if you have a working EXT2_IOC_GETFLAGS */
 +#cmakedefine HAVE_WORKING_EXT2_IOC_GETFLAGS 1
 +
 +/* Define to 1 if you have a working FS_IOC_GETFLAGS */
 +#cmakedefine HAVE_WORKING_FS_IOC_GETFLAGS 1
 +
 +/* Define to 1 if you have the <zlib.h> header file. */
 +#cmakedefine HAVE_ZLIB_H 1
 +
 +/* Define to 1 if you have the `_ctime64_s' function. */
 +#cmakedefine HAVE__CTIME64_S 1
 +
 +/* Define to 1 if you have the `_fseeki64' function. */
 +#cmakedefine HAVE__FSEEKI64 1
 +
 +/* Define to 1 if you have the `_get_timezone' function. */
 +#cmakedefine HAVE__GET_TIMEZONE 1
 +
 +/* Define to 1 if you have the `_localtime64_s' function. */
 +#cmakedefine HAVE__LOCALTIME64_S 1
 +
 +/* Define to 1 if you have the `_mkgmtime64' function. */
 +#cmakedefine HAVE__MKGMTIME64 1
 +
 +/* Define as const if the declaration of iconv() needs const. */
 +#define ICONV_CONST ${ICONV_CONST}
 +
 +/* Version number of libarchive as a single integer */
 +#cmakedefine LIBARCHIVE_VERSION_NUMBER "${LIBARCHIVE_VERSION_NUMBER}"
 +
 +/* Version number of libarchive */
 +#cmakedefine LIBARCHIVE_VERSION_STRING "${LIBARCHIVE_VERSION_STRING}"
 +
 +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
 +   slash. */
 +#cmakedefine LSTAT_FOLLOWS_SLASHED_SYMLINK 1
 +
 +/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
 +   */
 +#cmakedefine MAJOR_IN_MKDEV 1
 +
 +/* Define to 1 if `major', `minor', and `makedev' are declared in
 +   <sysmacros.h>. */
 +#cmakedefine MAJOR_IN_SYSMACROS 1
 +
 +/* Define to 1 if your C compiler doesn't accept -c and -o together. */
 +#cmakedefine NO_MINUS_C_MINUS_O 1
 +
 +/* The size of `wchar_t', as computed by sizeof. */
 +#cmakedefine SIZEOF_WCHAR_T ${SIZEOF_WCHAR_T}
 +
 +/* Define to 1 if strerror_r returns char *. */
 +#cmakedefine STRERROR_R_CHAR_P 1
 +
 +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
 +#cmakedefine TIME_WITH_SYS_TIME 1
 +
 +/*
 + * Some platform requires a macro to use extension functions.
 + */
 +#cmakedefine SAFE_TO_DEFINE_EXTENSIONS 1
 +#ifdef SAFE_TO_DEFINE_EXTENSIONS
 +/* Enable extensions on AIX 3, Interix.  */
 +#ifndef _ALL_SOURCE
 +# define _ALL_SOURCE 1
 +#endif
 +/* Enable GNU extensions on systems that have them.  */
 +#ifndef _GNU_SOURCE
 +# define _GNU_SOURCE 1
 +#endif
 +/* Enable threading extensions on Solaris.  */
 +#ifndef _POSIX_PTHREAD_SEMANTICS
 +# define _POSIX_PTHREAD_SEMANTICS 1
 +#endif
 +/* Enable extensions on HP NonStop.  */
 +#ifndef _TANDEM_SOURCE
 +# define _TANDEM_SOURCE 1
 +#endif
 +/* Enable general extensions on Solaris.  */
 +#ifndef __EXTENSIONS__
 +# define __EXTENSIONS__ 1
 +#endif
 +#endif /* SAFE_TO_DEFINE_EXTENSIONS */
 +
 +/* Version number of package */
 +#cmakedefine VERSION "${VERSION}"
 +
 +/* Number of bits in a file offset, on hosts where this is settable. */
 +#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
 +
 +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
 +#cmakedefine _LARGEFILE_SOURCE 1
 +
 +/* Define for large files, on AIX-style hosts. */
 +#cmakedefine _LARGE_FILES ${_LARGE_FILES}
 +
 +/* Define to control Windows SDK version */
 +#ifndef NTDDI_VERSION
 +#cmakedefine NTDDI_VERSION ${NTDDI_VERSION}
 +#endif // NTDDI_VERSION
 +
 +#ifndef _WIN32_WINNT
 +#cmakedefine _WIN32_WINNT ${_WIN32_WINNT}
 +#endif // _WIN32_WINNT
 +
 +#ifndef WINVER
 +#cmakedefine WINVER ${WINVER}
 +#endif // WINVER
 +
 +/* Define to empty if `const' does not conform to ANSI C. */
 +#cmakedefine const ${const}
 +
 +/* Define to `int' if <sys/types.h> doesn't define. */
 +#cmakedefine gid_t ${gid_t}
 +
 +/* Define to `unsigned long' if <sys/types.h> does not define. */
 +#cmakedefine id_t ${id_t}
 +
 +/* Define to `int' if <sys/types.h> does not define. */
 +#cmakedefine mode_t ${mode_t}
 +
 +/* Define to `long long' if <sys/types.h> does not define. */
 +#cmakedefine off_t ${off_t}
 +
 +/* Define to `int' if <sys/types.h> doesn't define. */
 +#cmakedefine pid_t ${pid_t}
 +
 +/* Define to `unsigned int' if <sys/types.h> does not define. */
 +#cmakedefine size_t ${size_t}
 +
 +/* Define to `int' if <sys/types.h> does not define. */
 +#cmakedefine ssize_t ${ssize_t}
 +
 +/* Define to `int' if <sys/types.h> doesn't define. */
 +#cmakedefine uid_t ${uid_t}
 +
 +/* Define to `int' if <sys/types.h> does not define. */
 +#cmakedefine intptr_t ${intptr_t}
 +
 +/* Define to `unsigned int' if <sys/types.h> does not define. */
 +#cmakedefine uintptr_t ${uintptr_t}
diff --cc Utilities/cmlibarchive/libarchive/CMakeLists.txt
index d412c80,0000000..b02ae82
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/CMakeLists.txt
+++ b/Utilities/cmlibarchive/libarchive/CMakeLists.txt
@@@ -1,215 -1,0 +1,228 @@@
 +
 +############################################
 +#
 +# How to build libarchive
 +#
 +############################################
 +
 +# Public headers
 +SET(include_HEADERS
 +  archive.h
 +  archive_entry.h
 +)
 +
 +# Sources and private headers
 +SET(libarchive_SOURCES
 +  archive_acl.c
++  archive_acl_private.h
 +  archive_check_magic.c
 +  archive_cmdline.c
 +  archive_cmdline_private.h
 +  archive_crc32.h
 +  archive_cryptor.c
 +  archive_cryptor_private.h
 +  archive_digest.c
 +  archive_digest_private.h
 +  archive_endian.h
 +  archive_entry.c
 +  archive_entry.h
 +  archive_entry_copy_stat.c
 +  archive_entry_link_resolver.c
 +  archive_entry_locale.h
 +  archive_entry_private.h
 +  archive_entry_sparse.c
 +  archive_entry_stat.c
 +  archive_entry_strmode.c
 +  archive_entry_xattr.c
 +  archive_getdate.c
 +  archive_getdate.h
 +  archive_hmac.c
 +  archive_hmac_private.h
 +  archive_match.c
 +  archive_openssl_evp_private.h
 +  archive_openssl_hmac_private.h
 +  archive_options.c
 +  archive_options_private.h
 +  archive_pack_dev.h
 +  archive_pack_dev.c
 +  archive_pathmatch.c
 +  archive_pathmatch.h
 +  archive_platform.h
++  archive_platform_acl.h
++  archive_platform_xattr.h
 +  archive_ppmd_private.h
 +  archive_ppmd7.c
 +  archive_ppmd7_private.h
 +  archive_private.h
 +  archive_random.c
 +  archive_random_private.h
 +  archive_rb.c
 +  archive_rb.h
 +  archive_read.c
 +  archive_read_add_passphrase.c
 +  archive_read_append_filter.c
 +  archive_read_data_into_fd.c
 +  archive_read_disk_entry_from_file.c
 +  archive_read_disk_posix.c
 +  archive_read_disk_private.h
 +  archive_read_disk_set_standard_lookup.c
 +  archive_read_extract.c
 +  archive_read_extract2.c
 +  archive_read_open_fd.c
 +  archive_read_open_file.c
 +  archive_read_open_filename.c
 +  archive_read_open_memory.c
 +  archive_read_private.h
 +  archive_read_set_format.c
 +  archive_read_set_options.c
 +  archive_read_support_filter_all.c
 +  archive_read_support_filter_bzip2.c
 +  archive_read_support_filter_compress.c
 +  archive_read_support_filter_gzip.c
 +  archive_read_support_filter_grzip.c
 +  archive_read_support_filter_lrzip.c
 +  archive_read_support_filter_lz4.c
 +  archive_read_support_filter_lzop.c
 +  archive_read_support_filter_none.c
 +  archive_read_support_filter_program.c
 +  archive_read_support_filter_rpm.c
 +  archive_read_support_filter_uu.c
 +  archive_read_support_filter_xz.c
 +  archive_read_support_format_7zip.c
 +  archive_read_support_format_all.c
 +  archive_read_support_format_ar.c
 +  archive_read_support_format_by_code.c
 +  archive_read_support_format_cab.c
 +  archive_read_support_format_cpio.c
 +  archive_read_support_format_empty.c
 +  archive_read_support_format_iso9660.c
 +  archive_read_support_format_lha.c
 +  archive_read_support_format_mtree.c
 +  archive_read_support_format_rar.c
 +  archive_read_support_format_raw.c
 +  archive_read_support_format_tar.c
 +  archive_read_support_format_warc.c
 +  archive_read_support_format_xar.c
 +  archive_read_support_format_zip.c
 +  archive_string.c
 +  archive_string.h
 +  archive_string_composition.h
 +  archive_string_sprintf.c
 +  archive_util.c
++  archive_version_details.c
 +  archive_virtual.c
 +  archive_write.c
-   archive_write_disk_acl.c
 +  archive_write_disk_posix.c
 +  archive_write_disk_private.h
 +  archive_write_disk_set_standard_lookup.c
 +  archive_write_private.h
 +  archive_write_open_fd.c
 +  archive_write_open_file.c
 +  archive_write_open_filename.c
 +  archive_write_open_memory.c
 +  archive_write_add_filter.c
 +  archive_write_add_filter_b64encode.c
 +  archive_write_add_filter_by_name.c
 +  archive_write_add_filter_bzip2.c
 +  archive_write_add_filter_compress.c
 +  archive_write_add_filter_grzip.c
 +  archive_write_add_filter_gzip.c
 +  archive_write_add_filter_lrzip.c
 +  archive_write_add_filter_lz4.c
 +  archive_write_add_filter_lzop.c
 +  archive_write_add_filter_none.c
 +  archive_write_add_filter_program.c
 +  archive_write_add_filter_uuencode.c
 +  archive_write_add_filter_xz.c
 +  archive_write_set_format.c
 +  archive_write_set_format_7zip.c
 +  archive_write_set_format_ar.c
 +  archive_write_set_format_by_name.c
 +  archive_write_set_format_cpio.c
 +  archive_write_set_format_cpio_newc.c
 +  archive_write_set_format_filter_by_ext.c
 +  archive_write_set_format_gnutar.c
 +  archive_write_set_format_iso9660.c
 +  archive_write_set_format_mtree.c
 +  archive_write_set_format_pax.c
 +  archive_write_set_format_raw.c
 +  archive_write_set_format_shar.c
 +  archive_write_set_format_ustar.c
 +  archive_write_set_format_v7tar.c
 +  archive_write_set_format_warc.c
 +  archive_write_set_format_xar.c
 +  archive_write_set_format_zip.c
 +  archive_write_set_options.c
 +  archive_write_set_passphrase.c
 +  archive_xxhash.h
 +  filter_fork_posix.c
 +  filter_fork.h
 +  xxhash.c
 +)
 +
 +# Man pages
 +SET(libarchive_MANS
 +  archive_entry.3
 +  archive_entry_acl.3
 +  archive_entry_linkify.3
 +  archive_entry_paths.3
 +  archive_entry_perms.3
 +  archive_entry_stat.3
 +  archive_entry_time.3
 +  archive_read.3
 +  archive_read_add_passphrase.3
 +  archive_read_data.3
 +  archive_read_disk.3
 +  archive_read_extract.3
 +  archive_read_filter.3
 +  archive_read_format.3
 +  archive_read_free.3
 +  archive_read_header.3
 +  archive_read_new.3
 +  archive_read_open.3
 +  archive_read_set_options.3
 +  archive_util.3
 +  archive_write.3
 +  archive_write_blocksize.3
 +  archive_write_data.3
 +  archive_write_disk.3
 +  archive_write_filter.3
 +  archive_write_finish_entry.3
 +  archive_write_format.3
 +  archive_write_free.3
 +  archive_write_header.3
 +  archive_write_new.3
 +  archive_write_open.3
 +  archive_write_set_options.3
 +  archive_write_set_passphrase.3
 +  cpio.5
 +  libarchive.3
 +  libarchive_changes.3
 +  libarchive_internals.3
 +  libarchive-formats.5
 +  mtree.5
 +  tar.5
 +)
 +
 +IF(WIN32 AND NOT CYGWIN)
 +  LIST(APPEND libarchive_SOURCES archive_entry_copy_bhfi.c)
 +  LIST(APPEND libarchive_SOURCES archive_read_disk_windows.c)
 +  LIST(APPEND libarchive_SOURCES archive_windows.c)
 +  LIST(APPEND libarchive_SOURCES archive_windows.h)
 +  LIST(APPEND libarchive_SOURCES archive_write_disk_windows.c)
 +  LIST(APPEND libarchive_SOURCES filter_fork_windows.c)
 +ENDIF(WIN32 AND NOT CYGWIN)
 +
++IF(ARCHIVE_ACL_DARWIN)
++  LIST(APPEND libarchive_SOURCES archive_disk_acl_darwin.c)
++ELSEIF(ARCHIVE_ACL_FREEBSD)
++  LIST(APPEND libarchive_SOURCES archive_disk_acl_freebsd.c)
++ELSEIF(ARCHIVE_ACL_LIBACL)
++  LIST(APPEND libarchive_SOURCES archive_disk_acl_linux.c)
++ELSEIF(ARCHIVE_ACL_SUNOS)
++  LIST(APPEND libarchive_SOURCES archive_disk_acl_sunos.c)
++ENDIF()
++
 +# CMake needs just one static "cmlibarchive" library.
 +ADD_LIBRARY(cmlibarchive STATIC ${libarchive_SOURCES} ${include_HEADERS})
 +TARGET_LINK_LIBRARIES(cmlibarchive ${ADDITIONAL_LIBS})
diff --cc Utilities/cmlibarchive/libarchive/archive.h
index cfb5c48,0000000..0f94d2e
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive.h
+++ b/Utilities/cmlibarchive/libarchive/archive.h
@@@ -1,1184 -1,0 +1,1184 @@@
 +/*-
 + * Copyright (c) 2003-2010 Tim Kientzle
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + *
 + * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.50 2008/05/26 17:00:22 kientzle Exp $
 + */
 +
 +#ifndef ARCHIVE_H_INCLUDED
 +#define	ARCHIVE_H_INCLUDED
 +
 +/*
 + * The version number is expressed as a single integer that makes it
 + * easy to compare versions at build time: for version a.b.c, the
 + * version number is printf("%d%03d%03d",a,b,c).  For example, if you
 + * know your application requires version 2.12.108 or later, you can
 + * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
 + */
 +/* Note: Compiler will complain if this does not match archive_entry.h! */
- #define	ARCHIVE_VERSION_NUMBER 3003001
++#define	ARCHIVE_VERSION_NUMBER 3003002
 +
 +#include <sys/stat.h>
 +#include <stddef.h>  /* for wchar_t */
 +#include <stdio.h> /* For FILE * */
 +#include <time.h> /* For time_t */
 +
 +/*
 + * Note: archive.h is for use outside of libarchive; the configuration
 + * headers (config.h, archive_platform.h, etc.) are purely internal.
 + * Do NOT use HAVE_XXX configuration macros to control the behavior of
 + * this header!  If you must conditionalize, use predefined compiler and/or
 + * platform macros.
 + */
 +#if defined(__BORLANDC__) && __BORLANDC__ >= 0x560
 +# include <stdint.h>
 +#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__)
 +# include <inttypes.h>
 +#endif
 +
 +/* Get appropriate definitions of 64-bit integer */
 +#if !defined(__LA_INT64_T_DEFINED)
 +/* Older code relied on the __LA_INT64_T macro; after 4.0 we'll switch to the typedef exclusively. */
 +# if ARCHIVE_VERSION_NUMBER < 4000000
 +#define __LA_INT64_T la_int64_t
 +# endif
 +#define __LA_INT64_T_DEFINED
 +# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
 +typedef __int64 la_int64_t;
 +# else
 +# include <unistd.h>  /* ssize_t */
 +#  if defined(_SCO_DS) || defined(__osf__)
 +typedef long long la_int64_t;
 +#  else
 +typedef int64_t la_int64_t;
 +#  endif
 +# endif
 +#endif
 +
 +/* The la_ssize_t should match the type used in 'struct stat' */
 +#if !defined(__LA_SSIZE_T_DEFINED)
 +/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */
 +# if ARCHIVE_VERSION_NUMBER < 4000000
 +#define __LA_SSIZE_T la_ssize_t
 +# endif
 +#define __LA_SSIZE_T_DEFINED
 +# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
 +#  if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
 +typedef ssize_t la_ssize_t;
 +#  elif defined(_WIN64)
 +typedef __int64 la_ssize_t;
 +#  else
 +typedef long la_ssize_t;
 +#  endif
 +# else
 +# include <unistd.h>  /* ssize_t */
 +typedef ssize_t la_ssize_t;
 +# endif
 +#endif
 +
 +/* Large file support for Android */
 +#ifdef __ANDROID__
 +#include "android_lf.h"
 +#endif
 +
 +/*
 + * On Windows, define LIBARCHIVE_STATIC if you're building or using a
 + * .lib.  The default here assumes you're building a DLL.  Only
 + * libarchive source should ever define __LIBARCHIVE_BUILD.
 + */
 +#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)
 +# ifdef __LIBARCHIVE_BUILD
 +#  ifdef __GNUC__
 +#   define __LA_DECL	__attribute__((dllexport)) extern
 +#  else
 +#   define __LA_DECL	__declspec(dllexport)
 +#  endif
 +# else
 +#  ifdef __GNUC__
 +#   define __LA_DECL
 +#  else
 +#   define __LA_DECL	__declspec(dllimport)
 +#  endif
 +# endif
 +#else
 +/* Static libraries or non-Windows needs no special declaration. */
 +# define __LA_DECL
 +#endif
 +
 +#if defined(__GNUC__) && __GNUC__ >= 3 && !defined(__MINGW32__)
 +#define	__LA_PRINTF(fmtarg, firstvararg) \
 +	__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
 +#else
 +#define	__LA_PRINTF(fmtarg, firstvararg)	/* nothing */
 +#endif
 +
 +/* CMake uses some deprecated APIs to build with old libarchive versions.  */
 +#define __LA_DEPRECATED
 +
 +#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.3.1"
++#define	ARCHIVE_VERSION_ONLY_STRING "3.3.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);
 +
 +/* Declare our basic types. */
 +struct archive;
 +struct archive_entry;
 +
 +/*
 + * Error codes: Use archive_errno() and archive_error_string()
 + * to retrieve details.  Unless specified otherwise, all functions
 + * that return 'int' use these codes.
 + */
 +#define	ARCHIVE_EOF	  1	/* Found end of archive. */
 +#define	ARCHIVE_OK	  0	/* Operation was successful. */
 +#define	ARCHIVE_RETRY	(-10)	/* Retry might succeed. */
 +#define	ARCHIVE_WARN	(-20)	/* Partial success. */
 +/* For example, if write_header "fails", then you can't push data. */
 +#define	ARCHIVE_FAILED	(-25)	/* Current operation cannot complete. */
 +/* But if write_header is "fatal," then this archive is dead and useless. */
 +#define	ARCHIVE_FATAL	(-30)	/* No more operations are possible. */
 +
 +/*
 + * As far as possible, archive_errno returns standard platform errno codes.
 + * Of course, the details vary by platform, so the actual definitions
 + * here are stored in "archive_platform.h".  The symbols are listed here
 + * for reference; as a rule, clients should not need to know the exact
 + * platform-dependent error code.
 + */
 +/* Unrecognized or invalid file format. */
 +/* #define	ARCHIVE_ERRNO_FILE_FORMAT */
 +/* Illegal usage of the library. */
 +/* #define	ARCHIVE_ERRNO_PROGRAMMER_ERROR */
 +/* Unknown or unclassified error. */
 +/* #define	ARCHIVE_ERRNO_MISC */
 +
 +/*
 + * Callbacks are invoked to automatically read/skip/write/open/close the
 + * archive. You can provide your own for complex tasks (like breaking
 + * archives across multiple tapes) or use standard ones built into the
 + * library.
 + */
 +
 +/* Returns pointer and size of next block of data from archive. */
 +typedef la_ssize_t	archive_read_callback(struct archive *,
 +			    void *_client_data, const void **_buffer);
 +
 +/* Skips at most request bytes from archive and returns the skipped amount.
 + * This may skip fewer bytes than requested; it may even skip zero bytes.
 + * If you do skip fewer bytes than requested, libarchive will invoke your
 + * read callback and discard data as necessary to make up the full skip.
 + */
 +typedef la_int64_t	archive_skip_callback(struct archive *,
 +			    void *_client_data, la_int64_t request);
 +
 +/* Seeks to specified location in the file and returns the position.
 + * Whence values are SEEK_SET, SEEK_CUR, SEEK_END from stdio.h.
 + * Return ARCHIVE_FATAL if the seek fails for any reason.
 + */
 +typedef la_int64_t	archive_seek_callback(struct archive *,
 +    void *_client_data, la_int64_t offset, int whence);
 +
 +/* Returns size actually written, zero on EOF, -1 on error. */
 +typedef la_ssize_t	archive_write_callback(struct archive *,
 +			    void *_client_data,
 +			    const void *_buffer, size_t _length);
 +
 +typedef int	archive_open_callback(struct archive *, void *_client_data);
 +
 +typedef int	archive_close_callback(struct archive *, void *_client_data);
 +
 +/* Switches from one client data object to the next/prev client data object.
 + * This is useful for reading from different data blocks such as a set of files
 + * that make up one large file.
 + */
 +typedef int archive_switch_callback(struct archive *, void *_client_data1,
 +			    void *_client_data2);
 +
 +/*
 + * Returns a passphrase used for encryption or decryption, NULL on nothing
 + * to do and give it up.
 + */
 +typedef const char *archive_passphrase_callback(struct archive *,
 +			    void *_client_data);
 +
 +/*
 + * Codes to identify various stream filters.
 + */
 +#define	ARCHIVE_FILTER_NONE	0
 +#define	ARCHIVE_FILTER_GZIP	1
 +#define	ARCHIVE_FILTER_BZIP2	2
 +#define	ARCHIVE_FILTER_COMPRESS	3
 +#define	ARCHIVE_FILTER_PROGRAM	4
 +#define	ARCHIVE_FILTER_LZMA	5
 +#define	ARCHIVE_FILTER_XZ	6
 +#define	ARCHIVE_FILTER_UU	7
 +#define	ARCHIVE_FILTER_RPM	8
 +#define	ARCHIVE_FILTER_LZIP	9
 +#define	ARCHIVE_FILTER_LRZIP	10
 +#define	ARCHIVE_FILTER_LZOP	11
 +#define	ARCHIVE_FILTER_GRZIP	12
 +#define	ARCHIVE_FILTER_LZ4	13
 +
 +#if ARCHIVE_VERSION_NUMBER < 4000000
 +#define	ARCHIVE_COMPRESSION_NONE	ARCHIVE_FILTER_NONE
 +#define	ARCHIVE_COMPRESSION_GZIP	ARCHIVE_FILTER_GZIP
 +#define	ARCHIVE_COMPRESSION_BZIP2	ARCHIVE_FILTER_BZIP2
 +#define	ARCHIVE_COMPRESSION_COMPRESS	ARCHIVE_FILTER_COMPRESS
 +#define	ARCHIVE_COMPRESSION_PROGRAM	ARCHIVE_FILTER_PROGRAM
 +#define	ARCHIVE_COMPRESSION_LZMA	ARCHIVE_FILTER_LZMA
 +#define	ARCHIVE_COMPRESSION_XZ		ARCHIVE_FILTER_XZ
 +#define	ARCHIVE_COMPRESSION_UU		ARCHIVE_FILTER_UU
 +#define	ARCHIVE_COMPRESSION_RPM		ARCHIVE_FILTER_RPM
 +#define	ARCHIVE_COMPRESSION_LZIP	ARCHIVE_FILTER_LZIP
 +#define	ARCHIVE_COMPRESSION_LRZIP	ARCHIVE_FILTER_LRZIP
 +#endif
 +
 +/*
 + * Codes returned by archive_format.
 + *
 + * Top 16 bits identifies the format family (e.g., "tar"); lower
 + * 16 bits indicate the variant.  This is updated by read_next_header.
 + * Note that the lower 16 bits will often vary from entry to entry.
 + * In some cases, this variation occurs as libarchive learns more about
 + * the archive (for example, later entries might utilize extensions that
 + * weren't necessary earlier in the archive; in this case, libarchive
 + * will change the format code to indicate the extended format that
 + * was used).  In other cases, it's because different tools have
 + * modified the archive and so different parts of the archive
 + * actually have slightly different formats.  (Both tar and cpio store
 + * format codes in each entry, so it is quite possible for each
 + * entry to be in a different format.)
 + */
 +#define	ARCHIVE_FORMAT_BASE_MASK		0xff0000
 +#define	ARCHIVE_FORMAT_CPIO			0x10000
 +#define	ARCHIVE_FORMAT_CPIO_POSIX		(ARCHIVE_FORMAT_CPIO | 1)
 +#define	ARCHIVE_FORMAT_CPIO_BIN_LE		(ARCHIVE_FORMAT_CPIO | 2)
 +#define	ARCHIVE_FORMAT_CPIO_BIN_BE		(ARCHIVE_FORMAT_CPIO | 3)
 +#define	ARCHIVE_FORMAT_CPIO_SVR4_NOCRC		(ARCHIVE_FORMAT_CPIO | 4)
 +#define	ARCHIVE_FORMAT_CPIO_SVR4_CRC		(ARCHIVE_FORMAT_CPIO | 5)
 +#define	ARCHIVE_FORMAT_CPIO_AFIO_LARGE		(ARCHIVE_FORMAT_CPIO | 6)
 +#define	ARCHIVE_FORMAT_SHAR			0x20000
 +#define	ARCHIVE_FORMAT_SHAR_BASE		(ARCHIVE_FORMAT_SHAR | 1)
 +#define	ARCHIVE_FORMAT_SHAR_DUMP		(ARCHIVE_FORMAT_SHAR | 2)
 +#define	ARCHIVE_FORMAT_TAR			0x30000
 +#define	ARCHIVE_FORMAT_TAR_USTAR		(ARCHIVE_FORMAT_TAR | 1)
 +#define	ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE	(ARCHIVE_FORMAT_TAR | 2)
 +#define	ARCHIVE_FORMAT_TAR_PAX_RESTRICTED	(ARCHIVE_FORMAT_TAR | 3)
 +#define	ARCHIVE_FORMAT_TAR_GNUTAR		(ARCHIVE_FORMAT_TAR | 4)
 +#define	ARCHIVE_FORMAT_ISO9660			0x40000
 +#define	ARCHIVE_FORMAT_ISO9660_ROCKRIDGE	(ARCHIVE_FORMAT_ISO9660 | 1)
 +#define	ARCHIVE_FORMAT_ZIP			0x50000
 +#define	ARCHIVE_FORMAT_EMPTY			0x60000
 +#define	ARCHIVE_FORMAT_AR			0x70000
 +#define	ARCHIVE_FORMAT_AR_GNU			(ARCHIVE_FORMAT_AR | 1)
 +#define	ARCHIVE_FORMAT_AR_BSD			(ARCHIVE_FORMAT_AR | 2)
 +#define	ARCHIVE_FORMAT_MTREE			0x80000
 +#define	ARCHIVE_FORMAT_RAW			0x90000
 +#define	ARCHIVE_FORMAT_XAR			0xA0000
 +#define	ARCHIVE_FORMAT_LHA			0xB0000
 +#define	ARCHIVE_FORMAT_CAB			0xC0000
 +#define	ARCHIVE_FORMAT_RAR			0xD0000
 +#define	ARCHIVE_FORMAT_7ZIP			0xE0000
 +#define	ARCHIVE_FORMAT_WARC			0xF0000
 +
 +/*
 + * Codes returned by archive_read_format_capabilities().
 + *
 + * This list can be extended with values between 0 and 0xffff.
 + * The original purpose of this list was to let different archive
 + * format readers expose their general capabilities in terms of
 + * encryption.
 + */
 +#define ARCHIVE_READ_FORMAT_CAPS_NONE (0) /* no special capabilities */
 +#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA (1<<0)  /* reader can detect encrypted data */
 +#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA (1<<1)  /* reader can detect encryptable metadata (pathname, mtime, etc.) */
 +
 +/*
 + * Codes returned by archive_read_has_encrypted_entries().
 + *
 + * In case the archive does not support encryption detection at all
 + * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. If the reader
 + * for some other reason (e.g. not enough bytes read) cannot say if
 + * there are encrypted entries, ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW
 + * is returned.
 + */
 +#define ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED -2
 +#define ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW -1
 +
 +/*-
 + * Basic outline for reading an archive:
 + *   1) Ask archive_read_new for an archive reader object.
 + *   2) Update any global properties as appropriate.
 + *      In particular, you'll certainly want to call appropriate
 + *      archive_read_support_XXX functions.
 + *   3) Call archive_read_open_XXX to open the archive
 + *   4) Repeatedly call archive_read_next_header to get information about
 + *      successive archive entries.  Call archive_read_data to extract
 + *      data for entries of interest.
 + *   5) Call archive_read_free to end processing.
 + */
 +__LA_DECL struct archive	*archive_read_new(void);
 +
 +/*
 + * The archive_read_support_XXX calls enable auto-detect for this
 + * archive handle.  They also link in the necessary support code.
 + * For example, if you don't want bzlib linked in, don't invoke
 + * support_compression_bzip2().  The "all" functions provide the
 + * obvious shorthand.
 + */
 +
 +#if ARCHIVE_VERSION_NUMBER < 4000000
 +__LA_DECL int archive_read_support_compression_all(struct archive *)
 +		__LA_DEPRECATED;
 +__LA_DECL int archive_read_support_compression_bzip2(struct archive *)
 +		__LA_DEPRECATED;
 +__LA_DECL int archive_read_support_compression_compress(struct archive *)
 +		__LA_DEPRECATED;
 +__LA_DECL int archive_read_support_compression_gzip(struct archive *)
 +		__LA_DEPRECATED;
 +__LA_DECL int archive_read_support_compression_lzip(struct archive *)
 +		__LA_DEPRECATED;
 +__LA_DECL int archive_read_support_compression_lzma(struct archive *)
 +		__LA_DEPRECATED;
 +__LA_DECL int archive_read_support_compression_none(struct archive *)
 +		__LA_DEPRECATED;
 +__LA_DECL int archive_read_support_compression_program(struct archive *,
 +		     const char *command) __LA_DEPRECATED;
 +__LA_DECL int archive_read_support_compression_program_signature
 +		(struct archive *, const char *,
 +		 const void * /* match */, size_t) __LA_DEPRECATED;
 +
 +__LA_DECL int archive_read_support_compression_rpm(struct archive *)
 +		__LA_DEPRECATED;
 +__LA_DECL int archive_read_support_compression_uu(struct archive *)
 +		__LA_DEPRECATED;
 +__LA_DECL int archive_read_support_compression_xz(struct archive *)
 +		__LA_DEPRECATED;
 +#endif
 +
 +__LA_DECL int archive_read_support_filter_all(struct archive *);
 +__LA_DECL int archive_read_support_filter_bzip2(struct archive *);
 +__LA_DECL int archive_read_support_filter_compress(struct archive *);
 +__LA_DECL int archive_read_support_filter_gzip(struct archive *);
 +__LA_DECL int archive_read_support_filter_grzip(struct archive *);
 +__LA_DECL int archive_read_support_filter_lrzip(struct archive *);
 +__LA_DECL int archive_read_support_filter_lz4(struct archive *);
 +__LA_DECL int archive_read_support_filter_lzip(struct archive *);
 +__LA_DECL int archive_read_support_filter_lzma(struct archive *);
 +__LA_DECL int archive_read_support_filter_lzop(struct archive *);
 +__LA_DECL int archive_read_support_filter_none(struct archive *);
 +__LA_DECL int archive_read_support_filter_program(struct archive *,
 +		     const char *command);
 +__LA_DECL int archive_read_support_filter_program_signature
 +		(struct archive *, const char * /* cmd */,
 +				    const void * /* match */, size_t);
 +__LA_DECL int archive_read_support_filter_rpm(struct archive *);
 +__LA_DECL int archive_read_support_filter_uu(struct archive *);
 +__LA_DECL int archive_read_support_filter_xz(struct archive *);
 +
 +__LA_DECL int archive_read_support_format_7zip(struct archive *);
 +__LA_DECL int archive_read_support_format_all(struct archive *);
 +__LA_DECL int archive_read_support_format_ar(struct archive *);
 +__LA_DECL int archive_read_support_format_by_code(struct archive *, int);
 +__LA_DECL int archive_read_support_format_cab(struct archive *);
 +__LA_DECL int archive_read_support_format_cpio(struct archive *);
 +__LA_DECL int archive_read_support_format_empty(struct archive *);
 +__LA_DECL int archive_read_support_format_gnutar(struct archive *);
 +__LA_DECL int archive_read_support_format_iso9660(struct archive *);
 +__LA_DECL int archive_read_support_format_lha(struct archive *);
 +__LA_DECL int archive_read_support_format_mtree(struct archive *);
 +__LA_DECL int archive_read_support_format_rar(struct archive *);
 +__LA_DECL int archive_read_support_format_raw(struct archive *);
 +__LA_DECL int archive_read_support_format_tar(struct archive *);
 +__LA_DECL int archive_read_support_format_warc(struct archive *);
 +__LA_DECL int archive_read_support_format_xar(struct archive *);
 +/* archive_read_support_format_zip() enables both streamable and seekable
 + * zip readers. */
 +__LA_DECL int archive_read_support_format_zip(struct archive *);
 +/* Reads Zip archives as stream from beginning to end.  Doesn't
 + * correctly handle SFX ZIP files or ZIP archives that have been modified
 + * in-place. */
 +__LA_DECL int archive_read_support_format_zip_streamable(struct archive *);
 +/* Reads starting from central directory; requires seekable input. */
 +__LA_DECL int archive_read_support_format_zip_seekable(struct archive *);
 +
 +/* Functions to manually set the format and filters to be used. This is
 + * useful to bypass the bidding process when the format and filters to use
 + * is known in advance.
 + */
 +__LA_DECL int archive_read_set_format(struct archive *, int);
 +__LA_DECL int archive_read_append_filter(struct archive *, int);
 +__LA_DECL int archive_read_append_filter_program(struct archive *,
 +    const char *);
 +__LA_DECL int archive_read_append_filter_program_signature
 +    (struct archive *, const char *, const void * /* match */, size_t);
 +
 +/* Set various callbacks. */
 +__LA_DECL int archive_read_set_open_callback(struct archive *,
 +    archive_open_callback *);
 +__LA_DECL int archive_read_set_read_callback(struct archive *,
 +    archive_read_callback *);
 +__LA_DECL int archive_read_set_seek_callback(struct archive *,
 +    archive_seek_callback *);
 +__LA_DECL int archive_read_set_skip_callback(struct archive *,
 +    archive_skip_callback *);
 +__LA_DECL int archive_read_set_close_callback(struct archive *,
 +    archive_close_callback *);
 +/* Callback used to switch between one data object to the next */
 +__LA_DECL int archive_read_set_switch_callback(struct archive *,
 +    archive_switch_callback *);
 +
 +/* This sets the first data object. */
 +__LA_DECL int archive_read_set_callback_data(struct archive *, void *);
 +/* This sets data object at specified index */
 +__LA_DECL int archive_read_set_callback_data2(struct archive *, void *,
 +    unsigned int);
 +/* This adds a data object at the specified index. */
 +__LA_DECL int archive_read_add_callback_data(struct archive *, void *,
 +    unsigned int);
 +/* This appends a data object to the end of list */
 +__LA_DECL int archive_read_append_callback_data(struct archive *, void *);
 +/* This prepends a data object to the beginning of list */
 +__LA_DECL int archive_read_prepend_callback_data(struct archive *, void *);
 +
 +/* Opening freezes the callbacks. */
 +__LA_DECL int archive_read_open1(struct archive *);
 +
 +/* Convenience wrappers around the above. */
 +__LA_DECL int archive_read_open(struct archive *, void *_client_data,
 +		     archive_open_callback *, archive_read_callback *,
 +		     archive_close_callback *);
 +__LA_DECL int archive_read_open2(struct archive *, void *_client_data,
 +		     archive_open_callback *, archive_read_callback *,
 +		     archive_skip_callback *, archive_close_callback *);
 +
 +/*
 + * A variety of shortcuts that invoke archive_read_open() with
 + * canned callbacks suitable for common situations.  The ones that
 + * accept a block size handle tape blocking correctly.
 + */
 +/* Use this if you know the filename.  Note: NULL indicates stdin. */
 +__LA_DECL int archive_read_open_filename(struct archive *,
 +		     const char *_filename, size_t _block_size);
 +/* Use this for reading multivolume files by filenames.
 + * NOTE: Must be NULL terminated. Sorting is NOT done. */
 +__LA_DECL int archive_read_open_filenames(struct archive *,
 +		     const char **_filenames, size_t _block_size);
 +__LA_DECL int archive_read_open_filename_w(struct archive *,
 +		     const wchar_t *_filename, size_t _block_size);
 +/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */
 +__LA_DECL int archive_read_open_file(struct archive *,
 +		     const char *_filename, size_t _block_size) __LA_DEPRECATED;
 +/* Read an archive that's stored in memory. */
 +__LA_DECL int archive_read_open_memory(struct archive *,
 +		     const void * buff, size_t size);
 +/* A more involved version that is only used for internal testing. */
 +__LA_DECL int archive_read_open_memory2(struct archive *a, const void *buff,
 +		     size_t size, size_t read_size);
 +/* Read an archive that's already open, using the file descriptor. */
 +__LA_DECL int archive_read_open_fd(struct archive *, int _fd,
 +		     size_t _block_size);
 +/* Read an archive that's already open, using a FILE *. */
 +/* Note: DO NOT use this with tape drives. */
 +__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file);
 +
 +/* Parses and returns next entry header. */
 +__LA_DECL int archive_read_next_header(struct archive *,
 +		     struct archive_entry **);
 +
 +/* Parses and returns next entry header using the archive_entry passed in */
 +__LA_DECL int archive_read_next_header2(struct archive *,
 +		     struct archive_entry *);
 +
 +/*
 + * Retrieve the byte offset in UNCOMPRESSED data where last-read
 + * header started.
 + */
 +__LA_DECL la_int64_t		 archive_read_header_position(struct archive *);
 +
 +/*
 + * Returns 1 if the archive contains at least one encrypted entry.
 + * If the archive format not support encryption at all
 + * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.
 + * If for any other reason (e.g. not enough data read so far)
 + * we cannot say whether there are encrypted entries, then
 + * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.
 + * In general, this function will return values below zero when the
 + * reader is uncertain or totally incapable of encryption support.
 + * When this function returns 0 you can be sure that the reader
 + * supports encryption detection but no encrypted entries have
 + * been found yet.
 + *
 + * NOTE: If the metadata/header of an archive is also encrypted, you
 + * cannot rely on the number of encrypted entries. That is why this
 + * function does not return the number of encrypted entries but#
 + * just shows that there are some.
 + */
 +__LA_DECL int	archive_read_has_encrypted_entries(struct archive *);
 +
 +/*
 + * Returns a bitmask of capabilities that are supported by the archive format reader.
 + * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned.
 + */
 +__LA_DECL int		 archive_read_format_capabilities(struct archive *);
 +
 +/* Read data from the body of an entry.  Similar to read(2). */
 +__LA_DECL la_ssize_t		 archive_read_data(struct archive *,
 +				    void *, size_t);
 +
 +/* Seek within the body of an entry.  Similar to lseek(2). */
 +__LA_DECL la_int64_t archive_seek_data(struct archive *, la_int64_t, int);
 +
 +/*
 + * A zero-copy version of archive_read_data that also exposes the file offset
 + * of each returned block.  Note that the client has no way to specify
 + * the desired size of the block.  The API does guarantee that offsets will
 + * be strictly increasing and that returned blocks will not overlap.
 + */
 +__LA_DECL int archive_read_data_block(struct archive *a,
 +		    const void **buff, size_t *size, la_int64_t *offset);
 +
 +/*-
 + * Some convenience functions that are built on archive_read_data:
 + *  'skip': skips entire entry
 + *  'into_buffer': writes data into memory buffer that you provide
 + *  'into_fd': writes data to specified filedes
 + */
 +__LA_DECL int archive_read_data_skip(struct archive *);
 +__LA_DECL int archive_read_data_into_fd(struct archive *, int fd);
 +
 +/*
 + * Set read options.
 + */
 +/* Apply option to the format only. */
 +__LA_DECL int archive_read_set_format_option(struct archive *_a,
 +			    const char *m, const char *o,
 +			    const char *v);
 +/* Apply option to the filter only. */
 +__LA_DECL int archive_read_set_filter_option(struct archive *_a,
 +			    const char *m, const char *o,
 +			    const char *v);
 +/* Apply option to both the format and the filter. */
 +__LA_DECL int archive_read_set_option(struct archive *_a,
 +			    const char *m, const char *o,
 +			    const char *v);
 +/* Apply option string to both the format and the filter. */
 +__LA_DECL int archive_read_set_options(struct archive *_a,
 +			    const char *opts);
 +
 +/*
 + * Add a decryption passphrase.
 + */
 +__LA_DECL int archive_read_add_passphrase(struct archive *, const char *);
 +__LA_DECL int archive_read_set_passphrase_callback(struct archive *,
 +			    void *client_data, archive_passphrase_callback *);
 +
 +
 +/*-
 + * Convenience function to recreate the current entry (whose header
 + * has just been read) on disk.
 + *
 + * This does quite a bit more than just copy data to disk. It also:
 + *  - Creates intermediate directories as required.
 + *  - Manages directory permissions:  non-writable directories will
 + *    be initially created with write permission enabled; when the
 + *    archive is closed, dir permissions are edited to the values specified
 + *    in the archive.
 + *  - Checks hardlinks:  hardlinks will not be extracted unless the
 + *    linked-to file was also extracted within the same session. (TODO)
 + */
 +
 +/* The "flags" argument selects optional behavior, 'OR' the flags you want. */
 +
 +/* Default: Do not try to set owner/group. */
 +#define	ARCHIVE_EXTRACT_OWNER			(0x0001)
 +/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */
 +#define	ARCHIVE_EXTRACT_PERM			(0x0002)
 +/* Default: Do not restore mtime/atime. */
 +#define	ARCHIVE_EXTRACT_TIME			(0x0004)
 +/* Default: Replace existing files. */
 +#define	ARCHIVE_EXTRACT_NO_OVERWRITE 		(0x0008)
 +/* Default: Try create first, unlink only if create fails with EEXIST. */
 +#define	ARCHIVE_EXTRACT_UNLINK			(0x0010)
 +/* Default: Do not restore ACLs. */
 +#define	ARCHIVE_EXTRACT_ACL			(0x0020)
 +/* Default: Do not restore fflags. */
 +#define	ARCHIVE_EXTRACT_FFLAGS			(0x0040)
 +/* Default: Do not restore xattrs. */
 +#define	ARCHIVE_EXTRACT_XATTR 			(0x0080)
 +/* Default: Do not try to guard against extracts redirected by symlinks. */
 +/* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */
 +#define	ARCHIVE_EXTRACT_SECURE_SYMLINKS		(0x0100)
 +/* Default: Do not reject entries with '..' as path elements. */
 +#define	ARCHIVE_EXTRACT_SECURE_NODOTDOT		(0x0200)
 +/* Default: Create parent directories as needed. */
 +#define	ARCHIVE_EXTRACT_NO_AUTODIR		(0x0400)
 +/* Default: Overwrite files, even if one on disk is newer. */
 +#define	ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER	(0x0800)
 +/* Detect blocks of 0 and write holes instead. */
 +#define	ARCHIVE_EXTRACT_SPARSE			(0x1000)
 +/* Default: Do not restore Mac extended metadata. */
 +/* This has no effect except on Mac OS. */
 +#define	ARCHIVE_EXTRACT_MAC_METADATA		(0x2000)
 +/* Default: Use HFS+ compression if it was compressed. */
 +/* This has no effect except on Mac OS v10.6 or later. */
 +#define	ARCHIVE_EXTRACT_NO_HFS_COMPRESSION	(0x4000)
 +/* Default: Do not use HFS+ compression if it was not compressed. */
 +/* This has no effect except on Mac OS v10.6 or later. */
 +#define	ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED	(0x8000)
 +/* Default: Do not reject entries with absolute paths */
 +#define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000)
 +/* Default: Do not clear no-change flags when unlinking object */
 +#define	ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS	(0x20000)
 +
 +__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 *);
 +
 +
 +/* A convenience function to set the format based on the code or name. */
 +__LA_DECL int archive_write_set_format(struct archive *, int format_code);
 +__LA_DECL int archive_write_set_format_by_name(struct archive *,
 +		     const char *name);
 +/* To minimize link pollution, use one or more of the following. */
 +__LA_DECL int archive_write_set_format_7zip(struct archive *);
 +__LA_DECL int archive_write_set_format_ar_bsd(struct archive *);
 +__LA_DECL int archive_write_set_format_ar_svr4(struct archive *);
 +__LA_DECL int archive_write_set_format_cpio(struct archive *);
 +__LA_DECL int archive_write_set_format_cpio_newc(struct archive *);
 +__LA_DECL int archive_write_set_format_gnutar(struct archive *);
 +__LA_DECL int archive_write_set_format_iso9660(struct archive *);
 +__LA_DECL int archive_write_set_format_mtree(struct archive *);
 +__LA_DECL int archive_write_set_format_mtree_classic(struct archive *);
 +/* TODO: int archive_write_set_format_old_tar(struct archive *); */
 +__LA_DECL int archive_write_set_format_pax(struct archive *);
 +__LA_DECL int archive_write_set_format_pax_restricted(struct archive *);
 +__LA_DECL int archive_write_set_format_raw(struct archive *);
 +__LA_DECL int archive_write_set_format_shar(struct archive *);
 +__LA_DECL int archive_write_set_format_shar_dump(struct archive *);
 +__LA_DECL int archive_write_set_format_ustar(struct archive *);
 +__LA_DECL int archive_write_set_format_v7tar(struct archive *);
 +__LA_DECL int archive_write_set_format_warc(struct archive *);
 +__LA_DECL int archive_write_set_format_xar(struct archive *);
 +__LA_DECL int archive_write_set_format_zip(struct archive *);
 +__LA_DECL int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename);
 +__LA_DECL int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext);
 +__LA_DECL int archive_write_zip_set_compression_deflate(struct archive *);
 +__LA_DECL int archive_write_zip_set_compression_store(struct archive *);
 +__LA_DECL int archive_write_open(struct archive *, void *,
 +		     archive_open_callback *, archive_write_callback *,
 +		     archive_close_callback *);
 +__LA_DECL int archive_write_open_fd(struct archive *, int _fd);
 +__LA_DECL int archive_write_open_filename(struct archive *, const char *_file);
 +__LA_DECL int archive_write_open_filename_w(struct archive *,
 +		     const wchar_t *_file);
 +/* A deprecated synonym for archive_write_open_filename() */
 +__LA_DECL int archive_write_open_file(struct archive *, const char *_file)
 +		__LA_DEPRECATED;
 +__LA_DECL int archive_write_open_FILE(struct archive *, FILE *);
 +/* _buffSize is the size of the buffer, _used refers to a variable that
 + * will be updated after each write into the buffer. */
 +__LA_DECL int archive_write_open_memory(struct archive *,
 +			void *_buffer, size_t _buffSize, size_t *_used);
 +
 +/*
 + * Note that the library will truncate writes beyond the size provided
 + * to archive_write_header or pad if the provided data is short.
 + */
 +__LA_DECL int archive_write_header(struct archive *,
 +		     struct archive_entry *);
 +__LA_DECL la_ssize_t	archive_write_data(struct archive *,
 +			    const void *, size_t);
 +
 +/* This interface is currently only available for archive_write_disk handles.  */
 +__LA_DECL la_ssize_t	 archive_write_data_block(struct archive *,
 +				    const void *, size_t, la_int64_t);
 +
 +__LA_DECL int		 archive_write_finish_entry(struct archive *);
 +__LA_DECL int		 archive_write_close(struct archive *);
 +/* Marks the archive as FATAL so that a subsequent free() operation
 + * won't try to close() cleanly.  Provides a fast abort capability
 + * when the client discovers that things have gone wrong. */
 +__LA_DECL int            archive_write_fail(struct archive *);
 +/* This can fail if the archive wasn't already closed, in which case
 + * archive_write_free() will implicitly call archive_write_close(). */
 +__LA_DECL int		 archive_write_free(struct archive *);
 +#if ARCHIVE_VERSION_NUMBER < 4000000
 +/* Synonym for archive_write_free() for backwards compatibility. */
 +__LA_DECL int		 archive_write_finish(struct archive *) __LA_DEPRECATED;
 +#endif
 +
 +/*
 + * Set write options.
 + */
 +/* Apply option to the format only. */
 +__LA_DECL int archive_write_set_format_option(struct archive *_a,
 +			    const char *m, const char *o,
 +			    const char *v);
 +/* Apply option to the filter only. */
 +__LA_DECL int archive_write_set_filter_option(struct archive *_a,
 +			    const char *m, const char *o,
 +			    const char *v);
 +/* Apply option to both the format and the filter. */
 +__LA_DECL int archive_write_set_option(struct archive *_a,
 +			    const char *m, const char *o,
 +			    const char *v);
 +/* Apply option string to both the format and the filter. */
 +__LA_DECL int archive_write_set_options(struct archive *_a,
 +			    const char *opts);
 +
 +/*
 + * Set a encryption passphrase.
 + */
 +__LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p);
 +__LA_DECL int archive_write_set_passphrase_callback(struct archive *,
 +			    void *client_data, archive_passphrase_callback *);
 +
 +/*-
 + * ARCHIVE_WRITE_DISK API
 + *
 + * To create objects on disk:
 + *   1) Ask archive_write_disk_new for a new archive_write_disk object.
 + *   2) Set any global properties.  In particular, you probably
 + *      want to set the options.
 + *   3) For each entry:
 + *      - construct an appropriate struct archive_entry structure
 + *      - archive_write_header to create the file/dir/etc on disk
 + *      - archive_write_data to write the entry data
 + *   4) archive_write_free to cleanup the writer and release resources
 + *
 + * In particular, you can use this in conjunction with archive_read()
 + * to pull entries out of an archive and create them on disk.
 + */
 +__LA_DECL struct archive	*archive_write_disk_new(void);
 +/* This file will not be overwritten. */
 +__LA_DECL int archive_write_disk_set_skip_file(struct archive *,
 +    la_int64_t, la_int64_t);
 +/* Set flags to control how the next item gets created.
 + * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */
 +__LA_DECL int		 archive_write_disk_set_options(struct archive *,
 +		     int flags);
 +/*
 + * The lookup functions are given uname/uid (or gname/gid) pairs and
 + * return a uid (gid) suitable for this system.  These are used for
 + * restoring ownership and for setting ACLs.  The default functions
 + * are naive, they just return the uid/gid.  These are small, so reasonable
 + * for applications that don't need to preserve ownership; they
 + * are probably also appropriate for applications that are doing
 + * same-system backup and restore.
 + */
 +/*
 + * The "standard" lookup functions use common system calls to lookup
 + * the uname/gname, falling back to the uid/gid if the names can't be
 + * found.  They cache lookups and are reasonably fast, but can be very
 + * large, so they are not used unless you ask for them.  In
 + * particular, these match the specifications of POSIX "pax" and old
 + * POSIX "tar".
 + */
 +__LA_DECL int	 archive_write_disk_set_standard_lookup(struct archive *);
 +/*
 + * If neither the default (naive) nor the standard (big) functions suit
 + * your needs, you can write your own and register them.  Be sure to
 + * include a cleanup function if you have allocated private data.
 + */
 +__LA_DECL int archive_write_disk_set_group_lookup(struct archive *,
 +    void * /* private_data */,
 +    la_int64_t (*)(void *, const char *, la_int64_t),
 +    void (* /* cleanup */)(void *));
 +__LA_DECL int archive_write_disk_set_user_lookup(struct archive *,
 +    void * /* private_data */,
 +    la_int64_t (*)(void *, const char *, la_int64_t),
 +    void (* /* cleanup */)(void *));
 +__LA_DECL la_int64_t archive_write_disk_gid(struct archive *, const char *, la_int64_t);
 +__LA_DECL la_int64_t archive_write_disk_uid(struct archive *, const char *, la_int64_t);
 +
 +/*
 + * ARCHIVE_READ_DISK API
 + *
 + * This is still evolving and somewhat experimental.
 + */
 +__LA_DECL struct archive *archive_read_disk_new(void);
 +/* The names for symlink modes here correspond to an old BSD
 + * command-line argument convention: -L, -P, -H */
 +/* Follow all symlinks. */
 +__LA_DECL int archive_read_disk_set_symlink_logical(struct archive *);
 +/* Follow no symlinks. */
 +__LA_DECL int archive_read_disk_set_symlink_physical(struct archive *);
 +/* Follow symlink initially, then not. */
 +__LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *);
 +/* TODO: Handle Linux stat32/stat64 ugliness. <sigh> */
 +__LA_DECL int archive_read_disk_entry_from_file(struct archive *,
 +    struct archive_entry *, int /* fd */, const struct stat *);
 +/* Look up gname for gid or uname for uid. */
 +/* Default implementations are very, very stupid. */
 +__LA_DECL const char *archive_read_disk_gname(struct archive *, la_int64_t);
 +__LA_DECL const char *archive_read_disk_uname(struct archive *, la_int64_t);
 +/* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the
 + * results for performance. */
 +__LA_DECL int	archive_read_disk_set_standard_lookup(struct archive *);
 +/* You can install your own lookups if you like. */
 +__LA_DECL int	archive_read_disk_set_gname_lookup(struct archive *,
 +    void * /* private_data */,
 +    const char *(* /* lookup_fn */)(void *, la_int64_t),
 +    void (* /* cleanup_fn */)(void *));
 +__LA_DECL int	archive_read_disk_set_uname_lookup(struct archive *,
 +    void * /* private_data */,
 +    const char *(* /* lookup_fn */)(void *, la_int64_t),
 +    void (* /* cleanup_fn */)(void *));
 +/* Start traversal. */
 +__LA_DECL int	archive_read_disk_open(struct archive *, const char *);
 +__LA_DECL int	archive_read_disk_open_w(struct archive *, const wchar_t *);
 +/*
 + * Request that current entry be visited.  If you invoke it on every
 + * directory, you'll get a physical traversal.  This is ignored if the
 + * current entry isn't a directory or a link to a directory.  So, if
 + * you invoke this on every returned path, you'll get a full logical
 + * traversal.
 + */
 +__LA_DECL int	archive_read_disk_descend(struct archive *);
 +__LA_DECL int	archive_read_disk_can_descend(struct archive *);
 +__LA_DECL int	archive_read_disk_current_filesystem(struct archive *);
 +__LA_DECL int	archive_read_disk_current_filesystem_is_synthetic(struct archive *);
 +__LA_DECL int	archive_read_disk_current_filesystem_is_remote(struct archive *);
 +/* Request that the access time of the entry visited by traversal be restored. */
 +__LA_DECL int  archive_read_disk_set_atime_restored(struct archive *);
 +/*
 + * Set behavior. The "flags" argument selects optional behavior.
 + */
 +/* Request that the access time of the entry visited by traversal be restored.
 + * This is the same as archive_read_disk_set_atime_restored. */
 +#define	ARCHIVE_READDISK_RESTORE_ATIME		(0x0001)
 +/* Default: Do not skip an entry which has nodump flags. */
 +#define	ARCHIVE_READDISK_HONOR_NODUMP		(0x0002)
 +/* Default: Skip a mac resource fork file whose prefix is "._" because of
 + * using copyfile. */
 +#define	ARCHIVE_READDISK_MAC_COPYFILE		(0x0004)
 +/* Default: Traverse mount points. */
 +#define	ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS	(0x0008)
 +/* Default: Xattrs are read from disk. */
 +#define	ARCHIVE_READDISK_NO_XATTR		(0x0010)
 +/* Default: ACLs are read from disk. */
 +#define	ARCHIVE_READDISK_NO_ACL			(0x0020)
 +/* Default: File flags are read from disk. */
 +#define	ARCHIVE_READDISK_NO_FFLAGS		(0x0040)
 +
 +__LA_DECL int  archive_read_disk_set_behavior(struct archive *,
 +		    int flags);
 +
 +/*
 + * Set archive_match object that will be used in archive_read_disk to
 + * know whether an entry should be skipped. The callback function
 + * _excluded_func will be invoked when an entry is skipped by the result
 + * of archive_match.
 + */
 +__LA_DECL int	archive_read_disk_set_matching(struct archive *,
 +		    struct archive *_matching, void (*_excluded_func)
 +		    (struct archive *, void *, struct archive_entry *),
 +		    void *_client_data);
 +__LA_DECL int	archive_read_disk_set_metadata_filter_callback(struct archive *,
 +		    int (*_metadata_filter_func)(struct archive *, void *,
 +		    	struct archive_entry *), void *_client_data);
 +
 +/* Simplified cleanup interface;
 + * This calls archive_read_free() or archive_write_free() as needed. */
 +__LA_DECL int	archive_free(struct archive *);
 +
 +/*
 + * Accessor functions to read/set various information in
 + * the struct archive object:
 + */
 +
 +/* Number of filters in the current filter pipeline. */
 +/* Filter #0 is the one closest to the format, -1 is a synonym for the
 + * last filter, which is always the pseudo-filter that wraps the
 + * client callbacks. */
 +__LA_DECL int		 archive_filter_count(struct archive *);
 +__LA_DECL la_int64_t	 archive_filter_bytes(struct archive *, int);
 +__LA_DECL int		 archive_filter_code(struct archive *, int);
 +__LA_DECL const char *	 archive_filter_name(struct archive *, int);
 +
 +#if ARCHIVE_VERSION_NUMBER < 4000000
 +/* These don't properly handle multiple filters, so are deprecated and
 + * will eventually be removed. */
 +/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */
 +__LA_DECL la_int64_t	 archive_position_compressed(struct archive *)
 +				__LA_DEPRECATED;
 +/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */
 +__LA_DECL la_int64_t	 archive_position_uncompressed(struct archive *)
 +				__LA_DEPRECATED;
 +/* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */
 +__LA_DECL const char	*archive_compression_name(struct archive *)
 +				__LA_DEPRECATED;
 +/* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */
 +__LA_DECL int		 archive_compression(struct archive *)
 +				__LA_DEPRECATED;
 +#endif
 +
 +__LA_DECL int		 archive_errno(struct archive *);
 +__LA_DECL const char	*archive_error_string(struct archive *);
 +__LA_DECL const char	*archive_format_name(struct archive *);
 +__LA_DECL int		 archive_format(struct archive *);
 +__LA_DECL void		 archive_clear_error(struct archive *);
 +__LA_DECL void		 archive_set_error(struct archive *, int _err,
 +			    const char *fmt, ...) __LA_PRINTF(3, 4);
 +__LA_DECL void		 archive_copy_error(struct archive *dest,
 +			    struct archive *src);
 +__LA_DECL int		 archive_file_count(struct archive *);
 +
 +/*
 + * ARCHIVE_MATCH API
 + */
 +__LA_DECL struct archive *archive_match_new(void);
 +__LA_DECL int	archive_match_free(struct archive *);
 +
 +/*
 + * Test if archive_entry is excluded.
 + * This is a convenience function. This is the same as calling all
 + * archive_match_path_excluded, archive_match_time_excluded
 + * and archive_match_owner_excluded.
 + */
 +__LA_DECL int	archive_match_excluded(struct archive *,
 +		    struct archive_entry *);
 +
 +/*
 + * Test if pathname is excluded. The conditions are set by following functions.
 + */
 +__LA_DECL int	archive_match_path_excluded(struct archive *,
 +		    struct archive_entry *);
 +/* Add exclusion pathname pattern. */
 +__LA_DECL int	archive_match_exclude_pattern(struct archive *, const char *);
 +__LA_DECL int	archive_match_exclude_pattern_w(struct archive *,
 +		    const wchar_t *);
 +/* Add exclusion pathname pattern from file. */
 +__LA_DECL int	archive_match_exclude_pattern_from_file(struct archive *,
 +		    const char *, int _nullSeparator);
 +__LA_DECL int	archive_match_exclude_pattern_from_file_w(struct archive *,
 +		    const wchar_t *, int _nullSeparator);
 +/* Add inclusion pathname pattern. */
 +__LA_DECL int	archive_match_include_pattern(struct archive *, const char *);
 +__LA_DECL int	archive_match_include_pattern_w(struct archive *,
 +		    const wchar_t *);
 +/* Add inclusion pathname pattern from file. */
 +__LA_DECL int	archive_match_include_pattern_from_file(struct archive *,
 +		    const char *, int _nullSeparator);
 +__LA_DECL int	archive_match_include_pattern_from_file_w(struct archive *,
 +		    const wchar_t *, int _nullSeparator);
 +/*
 + * How to get statistic information for inclusion patterns.
 + */
 +/* Return the amount number of unmatched inclusion patterns. */
 +__LA_DECL int	archive_match_path_unmatched_inclusions(struct archive *);
 +/* Return the pattern of unmatched inclusion with ARCHIVE_OK.
 + * Return ARCHIVE_EOF if there is no inclusion pattern. */
 +__LA_DECL int	archive_match_path_unmatched_inclusions_next(
 +		    struct archive *, const char **);
 +__LA_DECL int	archive_match_path_unmatched_inclusions_next_w(
 +		    struct archive *, const wchar_t **);
 +
 +/*
 + * Test if a file is excluded by its time stamp.
 + * The conditions are set by following functions.
 + */
 +__LA_DECL int	archive_match_time_excluded(struct archive *,
 +		    struct archive_entry *);
 +
 +/*
 + * Flags to tell a matching type of time stamps. These are used for
 + * following functions.
 + */
 +/* Time flag: mtime to be tested. */
 +#define ARCHIVE_MATCH_MTIME	(0x0100)
 +/* Time flag: ctime to be tested. */
 +#define ARCHIVE_MATCH_CTIME	(0x0200)
 +/* Comparison flag: Match the time if it is newer than. */
 +#define ARCHIVE_MATCH_NEWER	(0x0001)
 +/* Comparison flag: Match the time if it is older than. */
 +#define ARCHIVE_MATCH_OLDER	(0x0002)
 +/* Comparison flag: Match the time if it is equal to. */
 +#define ARCHIVE_MATCH_EQUAL	(0x0010)
 +/* Set inclusion time. */
 +__LA_DECL int	archive_match_include_time(struct archive *, int _flag,
 +		    time_t _sec, long _nsec);
 +/* Set inclusion time by a date string. */
 +__LA_DECL int	archive_match_include_date(struct archive *, int _flag,
 +		    const char *_datestr);
 +__LA_DECL int	archive_match_include_date_w(struct archive *, int _flag,
 +		    const wchar_t *_datestr);
 +/* Set inclusion time by a particular file. */
 +__LA_DECL int	archive_match_include_file_time(struct archive *,
 +		    int _flag, const char *_pathname);
 +__LA_DECL int	archive_match_include_file_time_w(struct archive *,
 +		    int _flag, const wchar_t *_pathname);
 +/* Add exclusion entry. */
 +__LA_DECL int	archive_match_exclude_entry(struct archive *,
 +		    int _flag, struct archive_entry *);
 +
 +/*
 + * Test if a file is excluded by its uid ,gid, uname or gname.
 + * The conditions are set by following functions.
 + */
 +__LA_DECL int	archive_match_owner_excluded(struct archive *,
 +		    struct archive_entry *);
 +/* Add inclusion uid, gid, uname and gname. */
 +__LA_DECL int	archive_match_include_uid(struct archive *, la_int64_t);
 +__LA_DECL int	archive_match_include_gid(struct archive *, la_int64_t);
 +__LA_DECL int	archive_match_include_uname(struct archive *, const char *);
 +__LA_DECL int	archive_match_include_uname_w(struct archive *,
 +		    const wchar_t *);
 +__LA_DECL int	archive_match_include_gname(struct archive *, const char *);
 +__LA_DECL int	archive_match_include_gname_w(struct archive *,
 +		    const wchar_t *);
 +
 +/* Utility functions */
 +/* Convenience function to sort a NULL terminated list of strings */
 +__LA_DECL int archive_utility_string_sort(char **);
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +/* These are meaningless outside of this header. */
 +#undef __LA_DECL
 +
 +#endif /* !ARCHIVE_H_INCLUDED */
diff --cc Utilities/cmlibarchive/libarchive/archive_disk_acl_darwin.c
index 0000000,48ad016..48ad016
mode 000000,100644..100644
--- a/Utilities/cmlibarchive/libarchive/archive_disk_acl_darwin.c
+++ b/Utilities/cmlibarchive/libarchive/archive_disk_acl_darwin.c
diff --cc Utilities/cmlibarchive/libarchive/archive_disk_acl_freebsd.c
index 0000000,07d08ff..07d08ff
mode 000000,100644..100644
--- a/Utilities/cmlibarchive/libarchive/archive_disk_acl_freebsd.c
+++ b/Utilities/cmlibarchive/libarchive/archive_disk_acl_freebsd.c
diff --cc Utilities/cmlibarchive/libarchive/archive_disk_acl_linux.c
index 0000000,3928f3d..3928f3d
mode 000000,100644..100644
--- a/Utilities/cmlibarchive/libarchive/archive_disk_acl_linux.c
+++ b/Utilities/cmlibarchive/libarchive/archive_disk_acl_linux.c
diff --cc Utilities/cmlibarchive/libarchive/archive_disk_acl_sunos.c
index 0000000,bc84fd6..bc84fd6
mode 000000,100644..100644
--- a/Utilities/cmlibarchive/libarchive/archive_disk_acl_sunos.c
+++ b/Utilities/cmlibarchive/libarchive/archive_disk_acl_sunos.c
diff --cc Utilities/cmlibarchive/libarchive/archive_entry.h
index 6e0225b,0000000..8c8e75a
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_entry.h
+++ b/Utilities/cmlibarchive/libarchive/archive_entry.h
@@@ -1,699 -1,0 +1,699 @@@
 +/*-
 + * Copyright (c) 2003-2008 Tim Kientzle
 + * Copyright (c) 2016 Martin Matuska
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + *
 + * $FreeBSD: head/lib/libarchive/archive_entry.h 201096 2009-12-28 02:41:27Z kientzle $
 + */
 +
 +#ifndef ARCHIVE_ENTRY_H_INCLUDED
 +#define	ARCHIVE_ENTRY_H_INCLUDED
 +
 +/* Note: Compiler will complain if this does not match archive.h! */
- #define	ARCHIVE_VERSION_NUMBER 3003001
++#define	ARCHIVE_VERSION_NUMBER 3003002
 +
 +/*
 + * 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 <time.h>
 +
 +#if defined(_WIN32) && !defined(__CYGWIN__)
 +#include <windows.h>
 +#endif
 +
 +/* Get a suitable 64-bit integer type. */
 +#if !defined(__LA_INT64_T_DEFINED)
 +# if ARCHIVE_VERSION_NUMBER < 4000000
 +#define __LA_INT64_T la_int64_t
 +# endif
 +#define __LA_INT64_T_DEFINED
 +# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
 +typedef __int64 la_int64_t;
 +# else
 +#include <unistd.h>
 +#  if defined(_SCO_DS) || defined(__osf__)
 +typedef long long la_int64_t;
 +#  else
 +typedef int64_t la_int64_t;
 +#  endif
 +# endif
 +#endif
 +
 +/* The la_ssize_t should match the type used in 'struct stat' */
 +#if !defined(__LA_SSIZE_T_DEFINED)
 +/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */
 +# if ARCHIVE_VERSION_NUMBER < 4000000
 +#define __LA_SSIZE_T la_ssize_t
 +# endif
 +#define __LA_SSIZE_T_DEFINED
 +# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
 +#  if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
 +typedef ssize_t la_ssize_t;
 +#  elif defined(_WIN64)
 +typedef __int64 la_ssize_t;
 +#  else
 +typedef long la_ssize_t;
 +#  endif
 +# else
 +# include <unistd.h>  /* ssize_t */
 +typedef ssize_t la_ssize_t;
 +# endif
 +#endif
 +
 +/* Get a suitable definition for mode_t */
 +#if ARCHIVE_VERSION_NUMBER >= 3999000
 +/* Switch to plain 'int' for libarchive 4.0.  It's less broken than 'mode_t' */
 +# define	__LA_MODE_T	int
 +#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) && !defined(__WATCOMC__)
 +# define	__LA_MODE_T	unsigned short
 +#else
 +# define	__LA_MODE_T	mode_t
 +#endif
 +
 +/* Large file support for Android */
 +#ifdef __ANDROID__
 +#include "android_lf.h"
 +#endif
 +
 +/*
 + * On Windows, define LIBARCHIVE_STATIC if you're building or using a
 + * .lib.  The default here assumes you're building a DLL.  Only
 + * libarchive source should ever define __LIBARCHIVE_BUILD.
 + */
 +#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)
 +# ifdef __LIBARCHIVE_BUILD
 +#  ifdef __GNUC__
 +#   define __LA_DECL	__attribute__((dllexport)) extern
 +#  else
 +#   define __LA_DECL	__declspec(dllexport)
 +#  endif
 +# else
 +#  ifdef __GNUC__
 +#   define __LA_DECL
 +#  else
 +#   define __LA_DECL	__declspec(dllimport)
 +#  endif
 +# endif
 +#else
 +/* Static libraries on all platforms and shared libraries on non-Windows. */
 +# define __LA_DECL
 +#endif
 +
 +/* CMake uses some deprecated APIs to build with old libarchive versions.  */
 +#define __LA_DEPRECATED
 +
 +#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)
 +
 +/*
 + * Basic object manipulation
 + */
 +
 +__LA_DECL struct archive_entry	*archive_entry_clear(struct archive_entry *);
 +/* The 'clone' function does a deep copy; all of the strings are copied too. */
 +__LA_DECL struct archive_entry	*archive_entry_clone(struct archive_entry *);
 +__LA_DECL void			 archive_entry_free(struct archive_entry *);
 +__LA_DECL struct archive_entry	*archive_entry_new(void);
 +
 +/*
 + * This form of archive_entry_new2() will pull character-set
 + * conversion information from the specified archive handle.  The
 + * older archive_entry_new(void) form is equivalent to calling
 + * archive_entry_new2(NULL) and will result in the use of an internal
 + * default character-set conversion.
 + */
 +__LA_DECL struct archive_entry	*archive_entry_new2(struct archive *);
 +
 +/*
 + * Retrieve fields from an archive_entry.
 + *
 + * There are a number of implicit conversions among these fields.  For
 + * example, if a regular string field is set and you read the _w wide
 + * character field, the entry will implicitly convert narrow-to-wide
 + * using the current locale.  Similarly, dev values are automatically
 + * updated when you write devmajor or devminor and vice versa.
 + *
 + * In addition, fields can be "set" or "unset."  Unset string fields
 + * return NULL, non-string fields have _is_set() functions to test
 + * whether they've been set.  You can "unset" a string field by
 + * assigning NULL; non-string fields have _unset() functions to
 + * unset them.
 + *
 + * Note: There is one ambiguity in the above; string fields will
 + * also return NULL when implicit character set conversions fail.
 + * This is usually what you want.
 + */
 +__LA_DECL time_t	 archive_entry_atime(struct archive_entry *);
 +__LA_DECL long		 archive_entry_atime_nsec(struct archive_entry *);
 +__LA_DECL int		 archive_entry_atime_is_set(struct archive_entry *);
 +__LA_DECL time_t	 archive_entry_birthtime(struct archive_entry *);
 +__LA_DECL long		 archive_entry_birthtime_nsec(struct archive_entry *);
 +__LA_DECL int		 archive_entry_birthtime_is_set(struct archive_entry *);
 +__LA_DECL time_t	 archive_entry_ctime(struct archive_entry *);
 +__LA_DECL long		 archive_entry_ctime_nsec(struct archive_entry *);
 +__LA_DECL int		 archive_entry_ctime_is_set(struct archive_entry *);
 +__LA_DECL dev_t		 archive_entry_dev(struct archive_entry *);
 +__LA_DECL int		 archive_entry_dev_is_set(struct archive_entry *);
 +__LA_DECL dev_t		 archive_entry_devmajor(struct archive_entry *);
 +__LA_DECL dev_t		 archive_entry_devminor(struct archive_entry *);
 +__LA_DECL __LA_MODE_T	 archive_entry_filetype(struct archive_entry *);
 +__LA_DECL void		 archive_entry_fflags(struct archive_entry *,
 +			    unsigned long * /* set */,
 +			    unsigned long * /* clear */);
 +__LA_DECL const char	*archive_entry_fflags_text(struct archive_entry *);
 +__LA_DECL la_int64_t	 archive_entry_gid(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_gname(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_gname_utf8(struct archive_entry *);
 +__LA_DECL const wchar_t	*archive_entry_gname_w(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_hardlink(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_hardlink_utf8(struct archive_entry *);
 +__LA_DECL const wchar_t	*archive_entry_hardlink_w(struct archive_entry *);
 +__LA_DECL la_int64_t	 archive_entry_ino(struct archive_entry *);
 +__LA_DECL la_int64_t	 archive_entry_ino64(struct archive_entry *);
 +__LA_DECL int		 archive_entry_ino_is_set(struct archive_entry *);
 +__LA_DECL __LA_MODE_T	 archive_entry_mode(struct archive_entry *);
 +__LA_DECL time_t	 archive_entry_mtime(struct archive_entry *);
 +__LA_DECL long		 archive_entry_mtime_nsec(struct archive_entry *);
 +__LA_DECL int		 archive_entry_mtime_is_set(struct archive_entry *);
 +__LA_DECL unsigned int	 archive_entry_nlink(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_pathname(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_pathname_utf8(struct archive_entry *);
 +__LA_DECL const wchar_t	*archive_entry_pathname_w(struct archive_entry *);
 +__LA_DECL __LA_MODE_T	 archive_entry_perm(struct archive_entry *);
 +__LA_DECL dev_t		 archive_entry_rdev(struct archive_entry *);
 +__LA_DECL dev_t		 archive_entry_rdevmajor(struct archive_entry *);
 +__LA_DECL dev_t		 archive_entry_rdevminor(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_sourcepath(struct archive_entry *);
 +__LA_DECL const wchar_t	*archive_entry_sourcepath_w(struct archive_entry *);
 +__LA_DECL la_int64_t	 archive_entry_size(struct archive_entry *);
 +__LA_DECL int		 archive_entry_size_is_set(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_strmode(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_symlink(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_symlink_utf8(struct archive_entry *);
 +__LA_DECL const wchar_t	*archive_entry_symlink_w(struct archive_entry *);
 +__LA_DECL la_int64_t	 archive_entry_uid(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_uname(struct archive_entry *);
 +__LA_DECL const char	*archive_entry_uname_utf8(struct archive_entry *);
 +__LA_DECL const wchar_t	*archive_entry_uname_w(struct archive_entry *);
 +__LA_DECL int archive_entry_is_data_encrypted(struct archive_entry *);
 +__LA_DECL int archive_entry_is_metadata_encrypted(struct archive_entry *);
 +__LA_DECL int archive_entry_is_encrypted(struct archive_entry *);
 +
 +/*
 + * Set fields in an archive_entry.
 + *
 + * Note: Before libarchive 2.4, there were 'set' and 'copy' versions
 + * of the string setters.  'copy' copied the actual string, 'set' just
 + * stored the pointer.  In libarchive 2.4 and later, strings are
 + * always copied.
 + */
 +
 +__LA_DECL void	archive_entry_set_atime(struct archive_entry *, time_t, long);
 +__LA_DECL void  archive_entry_unset_atime(struct archive_entry *);
 +#if defined(_WIN32) && !defined(__CYGWIN__)
 +__LA_DECL void archive_entry_copy_bhfi(struct archive_entry *, struct _BY_HANDLE_FILE_INFORMATION *);
 +#endif
 +__LA_DECL void	archive_entry_set_birthtime(struct archive_entry *, time_t, long);
 +__LA_DECL void  archive_entry_unset_birthtime(struct archive_entry *);
 +__LA_DECL void	archive_entry_set_ctime(struct archive_entry *, time_t, long);
 +__LA_DECL void  archive_entry_unset_ctime(struct archive_entry *);
 +__LA_DECL void	archive_entry_set_dev(struct archive_entry *, dev_t);
 +__LA_DECL void	archive_entry_set_devmajor(struct archive_entry *, dev_t);
 +__LA_DECL void	archive_entry_set_devminor(struct archive_entry *, dev_t);
 +__LA_DECL void	archive_entry_set_filetype(struct archive_entry *, unsigned int);
 +__LA_DECL void	archive_entry_set_fflags(struct archive_entry *,
 +	    unsigned long /* set */, unsigned long /* clear */);
 +/* Returns pointer to start of first invalid token, or NULL if none. */
 +/* Note that all recognized tokens are processed, regardless. */
 +__LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *,
 +	    const char *);
 +__LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
 +	    const wchar_t *);
 +__LA_DECL void	archive_entry_set_gid(struct archive_entry *, la_int64_t);
 +__LA_DECL void	archive_entry_set_gname(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_gname_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_gname(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
 +__LA_DECL int	archive_entry_update_gname_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_hardlink(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_hardlink_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_hardlink(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
 +__LA_DECL int	archive_entry_update_hardlink_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_ino(struct archive_entry *, la_int64_t);
 +__LA_DECL void	archive_entry_set_ino64(struct archive_entry *, la_int64_t);
 +__LA_DECL void	archive_entry_set_link(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_link_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_link(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);
 +__LA_DECL int	archive_entry_update_link_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_mode(struct archive_entry *, __LA_MODE_T);
 +__LA_DECL void	archive_entry_set_mtime(struct archive_entry *, time_t, long);
 +__LA_DECL void  archive_entry_unset_mtime(struct archive_entry *);
 +__LA_DECL void	archive_entry_set_nlink(struct archive_entry *, unsigned int);
 +__LA_DECL void	archive_entry_set_pathname(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_pathname_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_pathname(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
 +__LA_DECL int	archive_entry_update_pathname_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_perm(struct archive_entry *, __LA_MODE_T);
 +__LA_DECL void	archive_entry_set_rdev(struct archive_entry *, dev_t);
 +__LA_DECL void	archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
 +__LA_DECL void	archive_entry_set_rdevminor(struct archive_entry *, dev_t);
 +__LA_DECL void	archive_entry_set_size(struct archive_entry *, la_int64_t);
 +__LA_DECL void	archive_entry_unset_size(struct archive_entry *);
 +__LA_DECL void	archive_entry_copy_sourcepath(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *);
 +__LA_DECL void	archive_entry_set_symlink(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_symlink_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_symlink(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
 +__LA_DECL int	archive_entry_update_symlink_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_uid(struct archive_entry *, la_int64_t);
 +__LA_DECL void	archive_entry_set_uname(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_uname_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_uname(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
 +__LA_DECL int	archive_entry_update_uname_utf8(struct archive_entry *, const char *);
 +__LA_DECL void	archive_entry_set_is_data_encrypted(struct archive_entry *, char is_encrypted);
 +__LA_DECL void	archive_entry_set_is_metadata_encrypted(struct archive_entry *, char is_encrypted);
 +/*
 + * Routines to bulk copy fields to/from a platform-native "struct
 + * stat."  Libarchive used to just store a struct stat inside of each
 + * archive_entry object, but this created issues when trying to
 + * manipulate archives on systems different than the ones they were
 + * created on.
 + *
 + * TODO: On Linux and other LFS systems, provide both stat32 and
 + * stat64 versions of these functions and all of the macro glue so
 + * that archive_entry_stat is magically defined to
 + * archive_entry_stat32 or archive_entry_stat64 as appropriate.
 + */
 +__LA_DECL const struct stat	*archive_entry_stat(struct archive_entry *);
 +__LA_DECL void	archive_entry_copy_stat(struct archive_entry *, const struct stat *);
 +
 +/*
 + * Storage for Mac OS-specific AppleDouble metadata information.
 + * Apple-format tar files store a separate binary blob containing
 + * encoded metadata with ACL, extended attributes, etc.
 + * This provides a place to store that blob.
 + */
 +
 +__LA_DECL const void * archive_entry_mac_metadata(struct archive_entry *, size_t *);
 +__LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_t);
 +
 +/*
 + * ACL routines.  This used to simply store and return text-format ACL
 + * strings, but that proved insufficient for a number of reasons:
 + *   = clients need control over uname/uid and gname/gid mappings
 + *   = there are many different ACL text formats
 + *   = would like to be able to read/convert archives containing ACLs
 + *     on platforms that lack ACL libraries
 + *
 + *  This last point, in particular, forces me to implement a reasonably
 + *  complete set of ACL support routines.
 + */
 +
 +/*
 + * Permission bits.
 + */
 +#define	ARCHIVE_ENTRY_ACL_EXECUTE             0x00000001
 +#define	ARCHIVE_ENTRY_ACL_WRITE               0x00000002
 +#define	ARCHIVE_ENTRY_ACL_READ                0x00000004
 +#define	ARCHIVE_ENTRY_ACL_READ_DATA           0x00000008
 +#define	ARCHIVE_ENTRY_ACL_LIST_DIRECTORY      0x00000008
 +#define	ARCHIVE_ENTRY_ACL_WRITE_DATA          0x00000010
 +#define	ARCHIVE_ENTRY_ACL_ADD_FILE            0x00000010
 +#define	ARCHIVE_ENTRY_ACL_APPEND_DATA         0x00000020
 +#define	ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY    0x00000020
 +#define	ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS    0x00000040
 +#define	ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS   0x00000080
 +#define	ARCHIVE_ENTRY_ACL_DELETE_CHILD        0x00000100
 +#define	ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES     0x00000200
 +#define	ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES    0x00000400
 +#define	ARCHIVE_ENTRY_ACL_DELETE              0x00000800
 +#define	ARCHIVE_ENTRY_ACL_READ_ACL            0x00001000
 +#define	ARCHIVE_ENTRY_ACL_WRITE_ACL           0x00002000
 +#define	ARCHIVE_ENTRY_ACL_WRITE_OWNER         0x00004000
 +#define	ARCHIVE_ENTRY_ACL_SYNCHRONIZE         0x00008000
 +
 +#define	ARCHIVE_ENTRY_ACL_PERMS_POSIX1E			\
 +	(ARCHIVE_ENTRY_ACL_EXECUTE			\
 +	    | ARCHIVE_ENTRY_ACL_WRITE			\
 +	    | ARCHIVE_ENTRY_ACL_READ)
 +
 +#define ARCHIVE_ENTRY_ACL_PERMS_NFS4			\
 +	(ARCHIVE_ENTRY_ACL_EXECUTE			\
 +	    | ARCHIVE_ENTRY_ACL_READ_DATA		\
 +	    | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY 		\
 +	    | ARCHIVE_ENTRY_ACL_WRITE_DATA		\
 +	    | ARCHIVE_ENTRY_ACL_ADD_FILE		\
 +	    | ARCHIVE_ENTRY_ACL_APPEND_DATA		\
 +	    | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY	\
 +	    | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS	\
 +	    | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS	\
 +	    | ARCHIVE_ENTRY_ACL_DELETE_CHILD		\
 +	    | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES		\
 +	    | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES	\
 +	    | ARCHIVE_ENTRY_ACL_DELETE			\
 +	    | ARCHIVE_ENTRY_ACL_READ_ACL		\
 +	    | ARCHIVE_ENTRY_ACL_WRITE_ACL		\
 +	    | ARCHIVE_ENTRY_ACL_WRITE_OWNER		\
 +	    | ARCHIVE_ENTRY_ACL_SYNCHRONIZE)
 +
 +/*
 + * Inheritance values (NFS4 ACLs only); included in permset.
 + */
 +#define	ARCHIVE_ENTRY_ACL_ENTRY_INHERITED                   0x01000000
 +#define	ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT                0x02000000
 +#define	ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT           0x04000000
 +#define	ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT        0x08000000
 +#define	ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY                0x10000000
 +#define	ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS           0x20000000
 +#define	ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS               0x40000000
 +
 +#define	ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4			\
 +	(ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT			\
 +	    | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT		\
 +	    | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT	\
 +	    | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY		\
 +	    | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS		\
 +	    | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS		\
 +	    | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED)
 +
 +/* We need to be able to specify combinations of these. */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_ACCESS	0x00000100  /* POSIX.1e only */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_DEFAULT	0x00000200  /* POSIX.1e only */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_ALLOW	0x00000400 /* NFS4 only */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_DENY	0x00000800 /* NFS4 only */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_AUDIT	0x00001000 /* NFS4 only */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_ALARM	0x00002000 /* NFS4 only */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_POSIX1E	(ARCHIVE_ENTRY_ACL_TYPE_ACCESS \
 +	    | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
 +#define	ARCHIVE_ENTRY_ACL_TYPE_NFS4	(ARCHIVE_ENTRY_ACL_TYPE_ALLOW \
 +	    | ARCHIVE_ENTRY_ACL_TYPE_DENY \
 +	    | ARCHIVE_ENTRY_ACL_TYPE_AUDIT \
 +	    | ARCHIVE_ENTRY_ACL_TYPE_ALARM)
 +
 +/* Tag values mimic POSIX.1e */
 +#define	ARCHIVE_ENTRY_ACL_USER		10001	/* Specified user. */
 +#define	ARCHIVE_ENTRY_ACL_USER_OBJ 	10002	/* User who owns the file. */
 +#define	ARCHIVE_ENTRY_ACL_GROUP		10003	/* Specified group. */
 +#define	ARCHIVE_ENTRY_ACL_GROUP_OBJ	10004	/* Group who owns the file. */
 +#define	ARCHIVE_ENTRY_ACL_MASK		10005	/* Modify group access (POSIX.1e only) */
 +#define	ARCHIVE_ENTRY_ACL_OTHER		10006	/* Public (POSIX.1e only) */
 +#define	ARCHIVE_ENTRY_ACL_EVERYONE	10107   /* Everyone (NFS4 only) */
 +
 +/*
 + * Set the ACL by clearing it and adding entries one at a time.
 + * Unlike the POSIX.1e ACL routines, you must specify the type
 + * (access/default) for each entry.  Internally, the ACL data is just
 + * a soup of entries.  API calls here allow you to retrieve just the
 + * entries of interest.  This design (which goes against the spirit of
 + * POSIX.1e) is useful for handling archive formats that combine
 + * default and access information in a single ACL list.
 + */
 +__LA_DECL void	 archive_entry_acl_clear(struct archive_entry *);
 +__LA_DECL int	 archive_entry_acl_add_entry(struct archive_entry *,
 +	    int /* type */, int /* permset */, int /* tag */,
 +	    int /* qual */, const char * /* name */);
 +__LA_DECL int	 archive_entry_acl_add_entry_w(struct archive_entry *,
 +	    int /* type */, int /* permset */, int /* tag */,
 +	    int /* qual */, const wchar_t * /* name */);
 +
 +/*
 + * To retrieve the ACL, first "reset", then repeatedly ask for the
 + * "next" entry.  The want_type parameter allows you to request only
 + * certain types of entries.
 + */
 +__LA_DECL int	 archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
 +__LA_DECL int	 archive_entry_acl_next(struct archive_entry *, int /* want_type */,
 +	    int * /* type */, int * /* permset */, int * /* tag */,
 +	    int * /* qual */, const char ** /* name */);
 +__LA_DECL int	 archive_entry_acl_next_w(struct archive_entry *, int /* want_type */,
 +	    int * /* type */, int * /* permset */, int * /* tag */,
 +	    int * /* qual */, const wchar_t ** /* name */);
 +
 +/*
 + * Construct a text-format ACL.  The flags argument is a bitmask that
 + * can include any of the following:
 + *
 + * Flags only for archive entries with POSIX.1e ACL:
 + * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries.
 + * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries.
 + * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
 + *    default ACL entry.
 + * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS - Output only one colon after "other" and
 + *    "mask" entries.
 + *
 + * Flags only for archive entries with NFSv4 ACL:
 + * ARCHIVE_ENTRY_ACL_STYLE_COMPACT - Do not output the minus character for
 + *    unset permissions and flags in NFSv4 ACL permission and flag fields
 + *
 + * Flags for for archive entries with POSIX.1e ACL or NFSv4 ACL:
 + * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
 + *    each ACL entry.
 + * ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA - Separate entries with comma
 + *    instead of newline.
 + */
 +#define	ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID	0x00000001
 +#define	ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	0x00000002
 +#define	ARCHIVE_ENTRY_ACL_STYLE_SOLARIS		0x00000004
 +#define	ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA	0x00000008
 +#define	ARCHIVE_ENTRY_ACL_STYLE_COMPACT		0x00000010
 +
 +__LA_DECL wchar_t *archive_entry_acl_to_text_w(struct archive_entry *,
 +	    la_ssize_t * /* len */, int /* flags */);
 +__LA_DECL char *archive_entry_acl_to_text(struct archive_entry *,
 +	    la_ssize_t * /* len */, int /* flags */);
 +__LA_DECL int archive_entry_acl_from_text_w(struct archive_entry *,
 +	    const wchar_t * /* wtext */, int /* type */);
 +__LA_DECL int archive_entry_acl_from_text(struct archive_entry *,
 +	    const char * /* text */, int /* type */);
 +
 +/* Deprecated constants */
 +#define	OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID		1024
 +#define	OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	2048
 +
 +/* Deprecated functions */
 +__LA_DECL const wchar_t	*archive_entry_acl_text_w(struct archive_entry *,
 +		    int /* flags */) __LA_DEPRECATED;
 +__LA_DECL const char *archive_entry_acl_text(struct archive_entry *,
 +		    int /* flags */) __LA_DEPRECATED;
 +
 +/* Return bitmask of ACL types in an archive entry */
 +__LA_DECL int	 archive_entry_acl_types(struct archive_entry *);
 +
 +/* Return a count of entries matching 'want_type' */
 +__LA_DECL int	 archive_entry_acl_count(struct archive_entry *, int /* want_type */);
 +
 +/* Return an opaque ACL object. */
 +/* There's not yet anything clients can actually do with this... */
 +struct archive_acl;
 +__LA_DECL struct archive_acl *archive_entry_acl(struct archive_entry *);
 +
 +/*
 + * extended attributes
 + */
 +
 +__LA_DECL void	 archive_entry_xattr_clear(struct archive_entry *);
 +__LA_DECL void	 archive_entry_xattr_add_entry(struct archive_entry *,
 +	    const char * /* name */, const void * /* value */,
 +	    size_t /* size */);
 +
 +/*
 + * To retrieve the xattr list, first "reset", then repeatedly ask for the
 + * "next" entry.
 + */
 +
 +__LA_DECL int	archive_entry_xattr_count(struct archive_entry *);
 +__LA_DECL int	archive_entry_xattr_reset(struct archive_entry *);
 +__LA_DECL int	archive_entry_xattr_next(struct archive_entry *,
 +	    const char ** /* name */, const void ** /* value */, size_t *);
 +
 +/*
 + * sparse
 + */
 +
 +__LA_DECL void	 archive_entry_sparse_clear(struct archive_entry *);
 +__LA_DECL void	 archive_entry_sparse_add_entry(struct archive_entry *,
 +	    la_int64_t /* offset */, la_int64_t /* length */);
 +
 +/*
 + * To retrieve the xattr list, first "reset", then repeatedly ask for the
 + * "next" entry.
 + */
 +
 +__LA_DECL int	archive_entry_sparse_count(struct archive_entry *);
 +__LA_DECL int	archive_entry_sparse_reset(struct archive_entry *);
 +__LA_DECL int	archive_entry_sparse_next(struct archive_entry *,
 +	    la_int64_t * /* offset */, la_int64_t * /* length */);
 +
 +/*
 + * Utility to match up hardlinks.
 + *
 + * The 'struct archive_entry_linkresolver' is a cache of archive entries
 + * for files with multiple links.  Here's how to use it:
 + *   1. Create a lookup object with archive_entry_linkresolver_new()
 + *   2. Tell it the archive format you're using.
 + *   3. Hand each archive_entry to archive_entry_linkify().
 + *      That function will return 0, 1, or 2 entries that should
 + *      be written.
 + *   4. Call archive_entry_linkify(resolver, NULL) until
 + *      no more entries are returned.
 + *   5. Call archive_entry_linkresolver_free(resolver) to free resources.
 + *
 + * The entries returned have their hardlink and size fields updated
 + * appropriately.  If an entry is passed in that does not refer to
 + * a file with multiple links, it is returned unchanged.  The intention
 + * is that you should be able to simply filter all entries through
 + * this machine.
 + *
 + * To make things more efficient, be sure that each entry has a valid
 + * nlinks value.  The hardlink cache uses this to track when all links
 + * have been found.  If the nlinks value is zero, it will keep every
 + * name in the cache indefinitely, which can use a lot of memory.
 + *
 + * Note that archive_entry_size() is reset to zero if the file
 + * body should not be written to the archive.  Pay attention!
 + */
 +struct archive_entry_linkresolver;
 +
 +/*
 + * There are three different strategies for marking hardlinks.
 + * The descriptions below name them after the best-known
 + * formats that rely on each strategy:
 + *
 + * "Old cpio" is the simplest, it always returns any entry unmodified.
 + *    As far as I know, only cpio formats use this.  Old cpio archives
 + *    store every link with the full body; the onus is on the dearchiver
 + *    to detect and properly link the files as they are restored.
 + * "tar" is also pretty simple; it caches a copy the first time it sees
 + *    any link.  Subsequent appearances are modified to be hardlink
 + *    references to the first one without any body.  Used by all tar
 + *    formats, although the newest tar formats permit the "old cpio" strategy
 + *    as well.  This strategy is very simple for the dearchiver,
 + *    and reasonably straightforward for the archiver.
 + * "new cpio" is trickier.  It stores the body only with the last
 + *    occurrence.  The complication is that we might not
 + *    see every link to a particular file in a single session, so
 + *    there's no easy way to know when we've seen the last occurrence.
 + *    The solution here is to queue one link until we see the next.
 + *    At the end of the session, you can enumerate any remaining
 + *    entries by calling archive_entry_linkify(NULL) and store those
 + *    bodies.  If you have a file with three links l1, l2, and l3,
 + *    you'll get the following behavior if you see all three links:
 + *           linkify(l1) => NULL   (the resolver stores l1 internally)
 + *           linkify(l2) => l1     (resolver stores l2, you write l1)
 + *           linkify(l3) => l2, l3 (all links seen, you can write both).
 + *    If you only see l1 and l2, you'll get this behavior:
 + *           linkify(l1) => NULL
 + *           linkify(l2) => l1
 + *           linkify(NULL) => l2   (at end, you retrieve remaining links)
 + *    As the name suggests, this strategy is used by newer cpio variants.
 + *    It's noticeably more complex for the archiver, slightly more complex
 + *    for the dearchiver than the tar strategy, but makes it straightforward
 + *    to restore a file using any link by simply continuing to scan until
 + *    you see a link that is stored with a body.  In contrast, the tar
 + *    strategy requires you to rescan the archive from the beginning to
 + *    correctly extract an arbitrary link.
 + */
 +
 +__LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);
 +__LA_DECL void archive_entry_linkresolver_set_strategy(
 +	struct archive_entry_linkresolver *, int /* format_code */);
 +__LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);
 +__LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,
 +    struct archive_entry **, struct archive_entry **);
 +__LA_DECL struct archive_entry *archive_entry_partial_links(
 +    struct archive_entry_linkresolver *res, unsigned int *links);
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +/* This is meaningless outside of this header. */
 +#undef __LA_DECL
 +
 +#endif /* !ARCHIVE_ENTRY_H_INCLUDED */
diff --cc Utilities/cmlibarchive/libarchive/archive_platform.h
index 47d144b,0000000..4cb8f81
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_platform.h
+++ b/Utilities/cmlibarchive/libarchive/archive_platform.h
@@@ -1,214 -1,0 +1,188 @@@
 +/*-
 + * Copyright (c) 2003-2007 Tim Kientzle
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + *
 + * $FreeBSD: head/lib/libarchive/archive_platform.h 201090 2009-12-28 02:22:04Z kientzle $
 + */
 +
 +/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
 +
 +/*
 + * This header is the first thing included in any of the libarchive
 + * source files.  As far as possible, platform-specific issues should
 + * be dealt with here and not within individual source files.  I'm
 + * actively trying to minimize #if blocks within the main source,
 + * since they obfuscate the code.
 + */
 +
 +#ifndef ARCHIVE_PLATFORM_H_INCLUDED
 +#define	ARCHIVE_PLATFORM_H_INCLUDED
 +
 +/* archive.h and archive_entry.h require this. */
 +#define	__LIBARCHIVE_BUILD 1
 +
 +#if defined(PLATFORM_CONFIG_H)
 +/* Use hand-built config.h in environments that need it. */
 +#include PLATFORM_CONFIG_H
 +#elif defined(HAVE_CONFIG_H)
 +/* Most POSIX platforms use the 'configure' script to build config.h */
 +#include "config.h"
 +#else
 +/* Warn if the library hasn't been (automatically or manually) configured. */
 +#error Oops: No config.h and no pre-built configuration in archive_platform.h.
 +#endif
 +
 +/* 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"
 +#endif
 +
 +/*
 + * The config files define a lot of feature macros.  The following
 + * uses those macros to select/define replacements and include key
 + * headers as required.
 + */
 +
 +/* Get a real definition for __FBSDID or __RCSID if we can */
 +#if HAVE_SYS_CDEFS_H
 +#include <sys/cdefs.h>
 +#endif
 +
 +/* If not, define them so as to avoid dangling semicolons. */
 +#ifndef __FBSDID
 +#define	__FBSDID(a)     struct _undefined_hack
 +#endif
 +#ifndef __RCSID
 +#define	__RCSID(a)     struct _undefined_hack
 +#endif
 +
 +/* Old glibc mbsnrtowcs fails assertions in our use case.  */
 +#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ <= 1
 +# undef HAVE_MBSNRTOWCS
 +#endif
 +
 +/* Try to get standard C99-style integer type definitions. */
 +#if HAVE_INTTYPES_H
 +#include <inttypes.h>
 +#endif
 +#if HAVE_STDINT_H
 +#include <stdint.h>
 +#endif
 +
 +/* Borland warns about its own constants!  */
 +#if defined(__BORLANDC__)
 +# if HAVE_DECL_UINT64_MAX
 +#  undef	UINT64_MAX
 +#  undef	HAVE_DECL_UINT64_MAX
 +# endif
 +# if HAVE_DECL_UINT64_MIN
 +#  undef	UINT64_MIN
 +#  undef	HAVE_DECL_UINT64_MIN
 +# endif
 +# if HAVE_DECL_INT64_MAX
 +#  undef	INT64_MAX
 +#  undef	HAVE_DECL_INT64_MAX
 +# endif
 +# if HAVE_DECL_INT64_MIN
 +#  undef	INT64_MIN
 +#  undef	HAVE_DECL_INT64_MIN
 +# endif
 +#endif
 +
 +/* Some platforms lack the standard *_MAX definitions. */
 +#if !HAVE_DECL_SIZE_MAX
 +#define	SIZE_MAX (~(size_t)0)
 +#endif
 +#if !HAVE_DECL_SSIZE_MAX
 +#define	SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1))
 +#endif
 +#if !HAVE_DECL_UINT32_MAX
 +#define	UINT32_MAX (~(uint32_t)0)
 +#endif
 +#if !HAVE_DECL_INT32_MAX
 +#define	INT32_MAX ((int32_t)(UINT32_MAX >> 1))
 +#endif
 +#if !HAVE_DECL_INT32_MIN
 +#define	INT32_MIN ((int32_t)(~INT32_MAX))
 +#endif
 +#if !HAVE_DECL_UINT64_MAX
 +#define	UINT64_MAX (~(uint64_t)0)
 +#endif
 +#if !HAVE_DECL_INT64_MAX
 +#define	INT64_MAX ((int64_t)(UINT64_MAX >> 1))
 +#endif
 +#if !HAVE_DECL_INT64_MIN
 +#define	INT64_MIN ((int64_t)(~INT64_MAX))
 +#endif
 +#if !HAVE_DECL_UINTMAX_MAX
 +#define	UINTMAX_MAX (~(uintmax_t)0)
 +#endif
 +#if !HAVE_DECL_INTMAX_MAX
 +#define	INTMAX_MAX ((intmax_t)(UINTMAX_MAX >> 1))
 +#endif
 +#if !HAVE_DECL_INTMAX_MIN
 +#define	INTMAX_MIN ((intmax_t)(~INTMAX_MAX))
 +#endif
 +
- /*
-  * If this platform has <sys/acl.h>, acl_create(), acl_init(),
-  * acl_set_file(), and ACL_USER, we assume it has the rest of the
-  * POSIX.1e draft functions used in archive_read_extract.c.
-  */
- #if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE
- #if HAVE_ACL_USER
- #define	HAVE_POSIX_ACL	1
- #elif HAVE_ACL_TYPE_EXTENDED
- #define HAVE_DARWIN_ACL 1
- #endif
- #endif
- 
- /*
-  * If this platform has <sys/acl.h>, acl_get(), facl_get(), acl_set(),
-  * facl_set() and types aclent_t and ace_t it uses Solaris-style ACL functions
-  */
- #if HAVE_SYS_ACL_H && HAVE_ACL_GET && HAVE_FACL_GET && HAVE_ACL_SET && HAVE_FACL_SET && HAVE_ACLENT_T && HAVE_ACE_T
- #define	HAVE_SUN_ACL	1
- #endif
- 
- /* Define if platform supports NFSv4 ACLs */
- #if (HAVE_POSIX_ACL && HAVE_ACL_TYPE_NFS4) || HAVE_SUN_ACL || HAVE_DARWIN_ACL
- #define HAVE_NFS4_ACL	1
- #endif
- 
 +/*
 + * If we can't restore metadata using a file descriptor, then
 + * for compatibility's sake, close files before trying to restore metadata.
 + */
 +#if defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(HAVE_ACL_SET_FD) || defined(HAVE_ACL_SET_FD_NP) || defined(HAVE_FCHOWN)
 +#define	CAN_RESTORE_METADATA_FD
 +#endif
 +
 +/*
 + * glibc 2.24 deprecates readdir_r
 + */
 +#if defined(HAVE_READDIR_R) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24))
 +#define	USE_READDIR_R	1
 +#else
 +#undef	USE_READDIR_R
 +#endif
 +
 +/* Set up defaults for internal error codes. */
 +#ifndef ARCHIVE_ERRNO_FILE_FORMAT
 +#if HAVE_EFTYPE
 +#define	ARCHIVE_ERRNO_FILE_FORMAT EFTYPE
 +#else
 +#if HAVE_EILSEQ
 +#define	ARCHIVE_ERRNO_FILE_FORMAT EILSEQ
 +#else
 +#define	ARCHIVE_ERRNO_FILE_FORMAT EINVAL
 +#endif
 +#endif
 +#endif
 +
 +#ifndef ARCHIVE_ERRNO_PROGRAMMER
 +#define	ARCHIVE_ERRNO_PROGRAMMER EINVAL
 +#endif
 +
 +#ifndef ARCHIVE_ERRNO_MISC
 +#define	ARCHIVE_ERRNO_MISC (-1)
 +#endif
 +
 +#endif /* !ARCHIVE_PLATFORM_H_INCLUDED */
diff --cc Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
index 20eb157,0000000..51f79fa
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
@@@ -1,3353 -1,0 +1,3227 @@@
 +/*-
 + * Copyright (c) 2010-2012 Michihiro NAKAJIMA
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + */
 +
 +#include "archive_platform.h"
 +
 +#ifdef HAVE_ERRNO_H
 +#include <errno.h>
 +#endif
 +#ifdef HAVE_LIMITS_H
 +#include <limits.h>
 +#endif
 +#ifdef HAVE_STDLIB_H
 +#include <stdlib.h>
 +#endif
 +#ifdef HAVE_STRING_H
 +#include <string.h>
 +#endif
 +#ifdef HAVE_ZLIB_H
 +#include <cm_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.
 +		 */
- #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;
 +	}			 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 *compression_name[] = {
++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);
- static int	lzx_decode_huffman_tree(struct huffman *, unsigned, int);
 +
 +
 +int
 +archive_read_support_format_cab(struct archive *_a)
 +{
 +	struct archive_read *a = (struct archive_read *)_a;
 +	struct cab *cab;
 +	int r;
 +
 +	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
 +	    ARCHIVE_STATE_NEW, "archive_read_support_format_cab");
 +
 +	cab = (struct cab *)calloc(1, sizeof(*cab));
 +	if (cab == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate CAB data");
 +		return (ARCHIVE_FATAL);
 +	}
 +	archive_string_init(&cab->ws);
 +	archive_wstring_ensure(&cab->ws, 256);
 +
 +	r = __archive_read_register_format(a,
 +	    cab,
 +	    "cab",
 +	    archive_read_format_cab_bid,
 +	    archive_read_format_cab_options,
 +	    archive_read_format_cab_read_header,
 +	    archive_read_format_cab_read_data,
 +	    archive_read_format_cab_read_data_skip,
 +	    NULL,
 +	    archive_read_format_cab_cleanup,
 +	    NULL,
 +	    NULL);
 +
 +	if (r != ARCHIVE_OK)
 +		free(cab);
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +find_cab_magic(const char *p)
 +{
 +	switch (p[4]) {
 +	case 0:
 +		/*
 +		 * Note: Self-Extraction program has 'MSCF' string in their
 +		 * program. If we were finding 'MSCF' string only, we got
 +		 * wrong place for Cabinet header, thus, we have to check
 +		 * following four bytes which are reserved and must be set
 +		 * to zero.
 +		 */
 +		if (memcmp(p, "MSCF\0\0\0\0", 8) == 0)
 +			return 0;
 +		return 5;
 +	case 'F': return 1;
 +	case 'C': return 2;
 +	case 'S': return 3;
 +	case 'M': return 4;
 +	default:  return 5;
 +	}
 +}
 +
 +static int
 +archive_read_format_cab_bid(struct archive_read *a, int best_bid)
 +{
 +	const char *p;
 +	ssize_t bytes_avail, offset, window;
 +
 +	/* If there's already a better bid than we can ever
 +	   make, don't bother testing. */
 +	if (best_bid > 64)
 +		return (-1);
 +
 +	if ((p = __archive_read_ahead(a, 8, NULL)) == NULL)
 +		return (-1);
 +
 +	if (memcmp(p, "MSCF\0\0\0\0", 8) == 0)
 +		return (64);
 +
 +	/*
 +	 * Attempt to handle self-extracting archives
 +	 * by noting a PE header and searching forward
 +	 * up to 128k for a 'MSCF' marker.
 +	 */
 +	if (p[0] == 'M' && p[1] == 'Z') {
 +		offset = 0;
 +		window = 4096;
 +		while (offset < (1024 * 128)) {
 +			const char *h = __archive_read_ahead(a, offset + window,
 +			    &bytes_avail);
 +			if (h == NULL) {
 +				/* Remaining bytes are less than window. */
 +				window >>= 1;
 +				if (window < 128)
 +					return (0);
 +				continue;
 +			}
 +			p = h + offset;
 +			while (p + 8 < h + bytes_avail) {
 +				int next;
 +				if ((next = find_cab_magic(p)) == 0)
 +					return (64);
 +				p += next;
 +			}
 +			offset = p - h;
 +		}
 +	}
 +	return (0);
 +}
 +
 +static int
 +archive_read_format_cab_options(struct archive_read *a,
 +    const char *key, const char *val)
 +{
 +	struct cab *cab;
 +	int ret = ARCHIVE_FAILED;
 +
 +	cab = (struct cab *)(a->format->data);
 +	if (strcmp(key, "hdrcharset")  == 0) {
 +		if (val == NULL || val[0] == 0)
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "cab: hdrcharset option needs a character-set name");
 +		else {
 +			cab->sconv = archive_string_conversion_from_charset(
 +			    &a->archive, val, 0);
 +			if (cab->sconv != NULL)
 +				ret = ARCHIVE_OK;
 +			else
 +				ret = ARCHIVE_FATAL;
 +		}
 +		return (ret);
 +	}
 +
 +	/* Note: The "warn" return is just to inform the options
 +	 * supervisor that we didn't handle it.  It will generate
 +	 * a suitable error if no one used this option. */
 +	return (ARCHIVE_WARN);
 +}
 +
 +static int
 +cab_skip_sfx(struct archive_read *a)
 +{
 +	const char *p, *q;
 +	size_t skip;
 +	ssize_t bytes, window;
 +
 +	window = 4096;
 +	for (;;) {
 +		const char *h = __archive_read_ahead(a, window, &bytes);
 +		if (h == NULL) {
 +			/* Remaining size are less than window. */
 +			window >>= 1;
 +			if (window < 128) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_FILE_FORMAT,
 +				    "Couldn't find out CAB header");
 +				return (ARCHIVE_FATAL);
 +			}
 +			continue;
 +		}
 +		p = h;
 +		q = p + bytes;
 +
 +		/*
 +		 * Scan ahead until we find something that looks
 +		 * like the cab header.
 +		 */
 +		while (p + 8 < q) {
 +			int next;
 +			if ((next = find_cab_magic(p)) == 0) {
 +				skip = p - h;
 +				__archive_read_consume(a, skip);
 +				return (ARCHIVE_OK);
 +			}
 +			p += next;
 +		}
 +		skip = p - h;
 +		__archive_read_consume(a, skip);
 +	}
 +}
 +
 +static int
 +truncated_error(struct archive_read *a)
 +{
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Truncated CAB header");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +static ssize_t
 +cab_strnlen(const unsigned char *p, size_t maxlen)
 +{
 +	size_t i;
 +
 +	for (i = 0; i <= maxlen; i++) {
 +		if (p[i] == 0)
 +			break;
 +	}
 +	if (i > maxlen)
 +		return (-1);/* invalid */
 +	return ((ssize_t)i);
 +}
 +
 +/* Read bytes as much as remaining. */
 +static const void *
 +cab_read_ahead_remaining(struct archive_read *a, size_t min, ssize_t *avail)
 +{
 +	const void *p;
 +
 +	while (min > 0) {
 +		p = __archive_read_ahead(a, min, avail);
 +		if (p != NULL)
 +			return (p);
 +		min--;
 +	}
 +	return (NULL);
 +}
 +
 +/* Convert a path separator '\' -> '/' */
 +static int
 +cab_convert_path_separator_1(struct archive_string *fn, unsigned char attr)
 +{
 +	size_t i;
 +	int mb;
 +
 +	/* Easy check if we have '\' in multi-byte string. */
 +	mb = 0;
 +	for (i = 0; i < archive_strlen(fn); i++) {
 +		if (fn->s[i] == '\\') {
 +			if (mb) {
 +				/* This may be second byte of multi-byte
 +				 * character. */
 +				break;
 +			}
 +			fn->s[i] = '/';
 +			mb = 0;
 +		} else if ((fn->s[i] & 0x80) && !(attr & ATTR_NAME_IS_UTF))
 +			mb = 1;
 +		else
 +			mb = 0;
 +	}
 +	if (i == archive_strlen(fn))
 +		return (0);
 +	return (-1);
 +}
 +
 +/*
 + * Replace a character '\' with '/' in wide character.
 + */
 +static void
 +cab_convert_path_separator_2(struct cab *cab, struct archive_entry *entry)
 +{
 +	const wchar_t *wp;
 +	size_t i;
 +
 +	/* If a conversion to wide character failed, force the replacement. */
 +	if ((wp = archive_entry_pathname_w(entry)) != NULL) {
 +		archive_wstrcpy(&(cab->ws), wp);
 +		for (i = 0; i < archive_strlen(&(cab->ws)); i++) {
 +			if (cab->ws.s[i] == L'\\')
 +				cab->ws.s[i] = L'/';
 +		}
 +		archive_entry_copy_pathname_w(entry, cab->ws.s);
 +	}
 +}
 +
 +/*
 + * Read CFHEADER, CFFOLDER and CFFILE.
 + */
 +static int
 +cab_read_header(struct archive_read *a)
 +{
 +	const unsigned char *p;
 +	struct cab *cab;
 +	struct cfheader *hd;
 +	size_t bytes, used;
 +	ssize_t len;
 +	int64_t skip;
 +	int err, i;
 +	int cur_folder, prev_folder;
 +	uint32_t offset32;
 +	
 +	a->archive.archive_format = ARCHIVE_FORMAT_CAB;
 +	if (a->archive.archive_format_name == NULL)
 +		a->archive.archive_format_name = "CAB";
 +
 +	if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)
 +		return (truncated_error(a));
 +
 +	cab = (struct cab *)(a->format->data);
 +	if (cab->found_header == 0 &&
 +	    p[0] == 'M' && p[1] == 'Z') {
 +		/* This is an executable?  Must be self-extracting... */
 +		err = cab_skip_sfx(a);
 +		if (err < ARCHIVE_WARN)
 +			return (err);
 +
 +		/* Re-read header after processing the SFX. */
 +		if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)
 +			return (truncated_error(a));
 +	}
 +
 +	cab->cab_offset = 0;
 +	/*
 +	 * Read CFHEADER.
 +	 */
 +	hd = &cab->cfheader;
 +	if (p[CFHEADER_signature+0] != 'M' || p[CFHEADER_signature+1] != 'S' ||
 +	    p[CFHEADER_signature+2] != 'C' || p[CFHEADER_signature+3] != 'F') {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Couldn't find out CAB header");
 +		return (ARCHIVE_FATAL);
 +	}
 +	hd->total_bytes = archive_le32dec(p + CFHEADER_cbCabinet);
 +	hd->files_offset = archive_le32dec(p + CFHEADER_coffFiles);
 +	hd->minor = p[CFHEADER_versionMinor];
 +	hd->major = p[CFHEADER_versionMajor];
 +	hd->folder_count = archive_le16dec(p + CFHEADER_cFolders);
 +	if (hd->folder_count == 0)
 +		goto invalid;
 +	hd->file_count = archive_le16dec(p + CFHEADER_cFiles);
 +	if (hd->file_count == 0)
 +		goto invalid;
 +	hd->flags = archive_le16dec(p + CFHEADER_flags);
 +	hd->setid = archive_le16dec(p + CFHEADER_setID);
 +	hd->cabinet = archive_le16dec(p + CFHEADER_iCabinet);
 +	used = CFHEADER_iCabinet + 2;
 +	if (hd->flags & RESERVE_PRESENT) {
 +		uint16_t cfheader;
 +		cfheader = archive_le16dec(p + CFHEADER_cbCFHeader);
 +		if (cfheader > 60000U)
 +			goto invalid;
 +		hd->cffolder = p[CFHEADER_cbCFFolder];
 +		hd->cfdata = p[CFHEADER_cbCFData];
 +		used += 4;/* cbCFHeader, cbCFFolder and cbCFData */
 +		used += cfheader;/* abReserve */
 +	} else
 +		hd->cffolder = 0;/* Avoid compiling warning. */
 +	if (hd->flags & PREV_CABINET) {
 +		/* How many bytes are used for szCabinetPrev. */
 +		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
 +			return (truncated_error(a));
 +		if ((len = cab_strnlen(p + used, 255)) <= 0)
 +			goto invalid;
 +		used += len + 1;
 +		/* How many bytes are used for szDiskPrev. */
 +		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
 +			return (truncated_error(a));
 +		if ((len = cab_strnlen(p + used, 255)) <= 0)
 +			goto invalid;
 +		used += len + 1;
 +	}
 +	if (hd->flags & NEXT_CABINET) {
 +		/* How many bytes are used for szCabinetNext. */
 +		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
 +			return (truncated_error(a));
 +		if ((len = cab_strnlen(p + used, 255)) <= 0)
 +			goto invalid;
 +		used += len + 1;
 +		/* How many bytes are used for szDiskNext. */
 +		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
 +			return (truncated_error(a));
 +		if ((len = cab_strnlen(p + used, 255)) <= 0)
 +			goto invalid;
 +		used += len + 1;
 +	}
 +	__archive_read_consume(a, used);
 +	cab->cab_offset += used;
 +	used = 0;
 +
 +	/*
 +	 * Read CFFOLDER.
 +	 */
 +	hd->folder_array = (struct cffolder *)calloc(
 +	    hd->folder_count, sizeof(struct cffolder));
 +	if (hd->folder_array == NULL)
 +		goto nomem;
 +	
 +	bytes = 8;
 +	if (hd->flags & RESERVE_PRESENT)
 +		bytes += hd->cffolder;
 +	bytes *= hd->folder_count;
 +	if ((p = __archive_read_ahead(a, bytes, NULL)) == NULL)
 +		return (truncated_error(a));
 +	offset32 = 0;
 +	for (i = 0; i < hd->folder_count; i++) {
 +		struct cffolder *folder = &(hd->folder_array[i]);
 +		folder->cfdata_offset_in_cab =
 +		    archive_le32dec(p + CFFOLDER_coffCabStart);
 +		folder->cfdata_count = archive_le16dec(p+CFFOLDER_cCFData);
 +		folder->comptype =
 +		    archive_le16dec(p+CFFOLDER_typeCompress) & 0x0F;
 +		folder->compdata =
 +		    archive_le16dec(p+CFFOLDER_typeCompress) >> 8;
 +		/* Get a compression name. */
 +		if (folder->comptype <
 +		    sizeof(compression_name) / sizeof(compression_name[0]))
 +			folder->compname = compression_name[folder->comptype];
 +		else
 +			folder->compname = "UNKNOWN";
 +		p += 8;
 +		used += 8;
 +		if (hd->flags & RESERVE_PRESENT) {
 +			p += hd->cffolder;/* abReserve */
 +			used += hd->cffolder;
 +		}
 +		/*
 +		 * Sanity check if each data is acceptable.
 +		 */
 +		if (offset32 >= folder->cfdata_offset_in_cab)
 +			goto invalid;
 +		offset32 = folder->cfdata_offset_in_cab;
 +
 +		/* Set a request to initialize zlib for the CFDATA of
 +		 * this folder. */
 +		folder->decompress_init = 0;
 +	}
 +	__archive_read_consume(a, used);
 +	cab->cab_offset += used;
 +
 +	/*
 +	 * Read CFFILE.
 +	 */
 +	/* Seek read pointer to the offset of CFFILE if needed. */
 +	skip = (int64_t)hd->files_offset - cab->cab_offset;
 +	if (skip <  0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Invalid offset of CFFILE %jd < %jd",
 +		    (intmax_t)hd->files_offset, (intmax_t)cab->cab_offset);
 +		return (ARCHIVE_FATAL);
 +	}
 +	if (skip) {
 +		__archive_read_consume(a, skip);
 +		cab->cab_offset += skip;
 +	}
 +	/* Allocate memory for CFDATA */
 +	hd->file_array = (struct cffile *)calloc(
 +	    hd->file_count, sizeof(struct cffile));
 +	if (hd->file_array == NULL)
 +		goto nomem;
 +
 +	prev_folder = -1;
 +	for (i = 0; i < hd->file_count; i++) {
 +		struct cffile *file = &(hd->file_array[i]);
 +		ssize_t avail;
 +
 +		if ((p = __archive_read_ahead(a, 16, NULL)) == NULL)
 +			return (truncated_error(a));
 +		file->uncompressed_size = archive_le32dec(p + CFFILE_cbFile);
 +		file->offset = archive_le32dec(p + CFFILE_uoffFolderStart);
 +		file->folder = archive_le16dec(p + CFFILE_iFolder);
 +		file->mtime = cab_dos_time(p + CFFILE_date_time);
 +		file->attr = (uint8_t)archive_le16dec(p + CFFILE_attribs);
 +		__archive_read_consume(a, 16);
 +
 +		cab->cab_offset += 16;
 +		if ((p = cab_read_ahead_remaining(a, 256, &avail)) == NULL)
 +			return (truncated_error(a));
 +		if ((len = cab_strnlen(p, avail-1)) <= 0)
 +			goto invalid;
 +
 +		/* Copy a pathname.  */
 +		archive_string_init(&(file->pathname));
 +		archive_strncpy(&(file->pathname), p, len);
 +		__archive_read_consume(a, len + 1);
 +		cab->cab_offset += len + 1;
 +
 +		/*
 +		 * Sanity check if each data is acceptable.
 +		 */
 +		if (file->uncompressed_size > 0x7FFF8000)
 +			goto invalid;/* Too large */
 +		if ((int64_t)file->offset + (int64_t)file->uncompressed_size
 +		    > ARCHIVE_LITERAL_LL(0x7FFF8000))
 +			goto invalid;/* Too large */
 +		switch (file->folder) {
 +		case iFoldCONTINUED_TO_NEXT:
 +			/* This must be last file in a folder. */
 +			if (i != hd->file_count -1)
 +				goto invalid;
 +			cur_folder = hd->folder_count -1;
 +			break;
 +		case iFoldCONTINUED_PREV_AND_NEXT:
 +			/* This must be only one file in a folder. */
 +			if (hd->file_count != 1)
 +				goto invalid;
 +			/* FALL THROUGH */
 +		case iFoldCONTINUED_FROM_PREV:
 +			/* This must be first file in a folder. */
 +			if (i != 0)
 +				goto invalid;
 +			prev_folder = cur_folder = 0;
 +			offset32 = file->offset;
 +			break;
 +		default:
 +			if (file->folder >= hd->folder_count)
 +				goto invalid;
 +			cur_folder = file->folder;
 +			break;
 +		}
 +		/* Dot not back track. */
 +		if (cur_folder < prev_folder)
 +			goto invalid;
 +		if (cur_folder != prev_folder)
 +			offset32 = 0;
 +		prev_folder = cur_folder;
 +
 +		/* Make sure there are not any blanks from last file
 +		 * contents. */
 +		if (offset32 != file->offset)
 +			goto invalid;
 +		offset32 += file->uncompressed_size;
 +
 +		/* CFDATA is available for file contents. */
 +		if (file->uncompressed_size > 0 &&
 +		    hd->folder_array[cur_folder].cfdata_count == 0)
 +			goto invalid;
 +	}
 +
 +	if (hd->cabinet != 0 || hd->flags & (PREV_CABINET | NEXT_CABINET)) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Multivolume cabinet file is unsupported");
 +		return (ARCHIVE_WARN);
 +	}
 +	return (ARCHIVE_OK);
 +invalid:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Invalid CAB header");
 +	return (ARCHIVE_FATAL);
 +nomem:
 +	archive_set_error(&a->archive, ENOMEM,
 +	    "Can't allocate memory for CAB data");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +static int
 +archive_read_format_cab_read_header(struct archive_read *a,
 +    struct archive_entry *entry)
 +{
 +	struct cab *cab;
 +	struct cfheader *hd;
 +	struct cffolder *prev_folder;
 +	struct cffile *file;
 +	struct archive_string_conv *sconv;
 +	int err = ARCHIVE_OK, r;
 +	
 +	cab = (struct cab *)(a->format->data);
 +	if (cab->found_header == 0) {
 +		err = cab_read_header(a); 
 +		if (err < ARCHIVE_WARN)
 +			return (err);
 +		/* We've found the header. */
 +		cab->found_header = 1;
 +	}
 +	hd = &cab->cfheader;
 +
 +	if (hd->file_index >= hd->file_count) {
 +		cab->end_of_archive = 1;
 +		return (ARCHIVE_EOF);
 +	}
 +	file = &hd->file_array[hd->file_index++];
 +
 +	cab->end_of_entry = 0;
 +	cab->end_of_entry_cleanup = 0;
 +	cab->entry_compressed_bytes_read = 0;
 +	cab->entry_uncompressed_bytes_read = 0;
 +	cab->entry_unconsumed = 0;
 +	cab->entry_cffile = file;
 +
 +	/*
 +	 * Choose a proper folder.
 +	 */
 +	prev_folder = cab->entry_cffolder;
 +	switch (file->folder) {
 +	case iFoldCONTINUED_FROM_PREV:
 +	case iFoldCONTINUED_PREV_AND_NEXT:
 +		cab->entry_cffolder = &hd->folder_array[0];
 +		break;
 +	case iFoldCONTINUED_TO_NEXT:
 +		cab->entry_cffolder = &hd->folder_array[hd->folder_count-1];
 +		break;
 +	default:
 +		cab->entry_cffolder = &hd->folder_array[file->folder];
 +		break;
 +	}
 +	/* If a cffolder of this file is changed, reset a cfdata to read
 +	 * file contents from next cfdata. */
 +	if (prev_folder != cab->entry_cffolder)
 +		cab->entry_cfdata = NULL;
 +
 +	/* If a pathname is UTF-8, prepare a string conversion object
 +	 * for UTF-8 and use it. */
 +	if (file->attr & ATTR_NAME_IS_UTF) {
 +		if (cab->sconv_utf8 == NULL) {
 +			cab->sconv_utf8 =
 +			    archive_string_conversion_from_charset(
 +				&(a->archive), "UTF-8", 1);
 +			if (cab->sconv_utf8 == NULL)
 +				return (ARCHIVE_FATAL);
 +		}
 +		sconv = cab->sconv_utf8;
 +	} else if (cab->sconv != NULL) {
 +		/* Choose the conversion specified by the option. */
 +		sconv = cab->sconv;
 +	} else {
 +		/* Choose the default conversion. */
 +		if (!cab->init_default_conversion) {
 +			cab->sconv_default =
 +			    archive_string_default_conversion_for_read(
 +			      &(a->archive));
 +			cab->init_default_conversion = 1;
 +		}
 +		sconv = cab->sconv_default;
 +	}
 +
 +	/*
 +	 * Set a default value and common data
 +	 */
 +	r = cab_convert_path_separator_1(&(file->pathname), file->attr);
 +	if (archive_entry_copy_pathname_l(entry, file->pathname.s,
 +	    archive_strlen(&(file->pathname)), sconv) != 0) {
 +		if (errno == ENOMEM) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory for Pathname");
 +			return (ARCHIVE_FATAL);
 +		}
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Pathname cannot be converted "
 +		    "from %s to current locale.",
 +		    archive_string_conversion_charset_name(sconv));
 +		err = ARCHIVE_WARN;
 +	}
 +	if (r < 0) {
 +		/* Convert a path separator '\' -> '/' */
 +		cab_convert_path_separator_2(cab, entry);
 +	}
 +
 +	archive_entry_set_size(entry, file->uncompressed_size);
 +	if (file->attr & ATTR_RDONLY)
 +		archive_entry_set_mode(entry, AE_IFREG | 0555);
 +	else
 +		archive_entry_set_mode(entry, AE_IFREG | 0666);
 +	archive_entry_set_mtime(entry, file->mtime, 0);
 +
 +	cab->entry_bytes_remaining = file->uncompressed_size;
 +	cab->entry_offset = 0;
 +	/* We don't need compress data. */
 +	if (file->uncompressed_size == 0)
 +		cab->end_of_entry_cleanup = cab->end_of_entry = 1;
 +
 +	/* Set up a more descriptive format name. */
 +	sprintf(cab->format_name, "CAB %d.%d (%s)",
 +	    hd->major, hd->minor, cab->entry_cffolder->compname);
 +	a->archive.archive_format_name = cab->format_name;
 +
 +	return (err);
 +}
 +
 +static int
 +archive_read_format_cab_read_data(struct archive_read *a,
 +    const void **buff, size_t *size, int64_t *offset)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	int r;
 +
 +	switch (cab->entry_cffile->folder) {
 +	case iFoldCONTINUED_FROM_PREV:
 +	case iFoldCONTINUED_TO_NEXT:
 +	case iFoldCONTINUED_PREV_AND_NEXT:
 +		*buff = NULL;
 +		*size = 0;
 +		*offset = 0;
 +		archive_clear_error(&a->archive);
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Cannot restore this file split in multivolume.");
 +		return (ARCHIVE_FAILED);
 +	default:
 +		break;
 +	}
 +	if (cab->read_data_invoked == 0) {
 +		if (cab->bytes_skipped) {
 +			if (cab->entry_cfdata == NULL) {
 +				r = cab_next_cfdata(a);
 +				if (r < 0)
 +					return (r);
 +			}
 +			if (cab_consume_cfdata(a, cab->bytes_skipped) < 0)
 +				return (ARCHIVE_FATAL);
 +			cab->bytes_skipped = 0;
 +		}
 +		cab->read_data_invoked = 1;
 +	}
 +	if (cab->entry_unconsumed) {
 +		/* Consume as much as the compressor actually used. */
 +		r = (int)cab_consume_cfdata(a, cab->entry_unconsumed);
 +		cab->entry_unconsumed = 0;
 +		if (r < 0)
 +			return (r);
 +	}
 +	if (cab->end_of_archive || cab->end_of_entry) {
 +		if (!cab->end_of_entry_cleanup) {
 +			/* End-of-entry cleanup done. */
 +			cab->end_of_entry_cleanup = 1;
 +		}
 +		*offset = cab->entry_offset;
 +		*size = 0;
 +		*buff = NULL;
 +		return (ARCHIVE_EOF);
 +	}
 +
 +	return (cab_read_data(a, buff, size, offset));
 +}
 +
 +static uint32_t
 +cab_checksum_cfdata_4(const void *p, size_t bytes, uint32_t seed)
 +{
 +	const unsigned char *b;
 +	unsigned u32num;
 +	uint32_t sum;
 +
 +	u32num = (unsigned)bytes / 4;
 +	sum = seed;
 +	b = p;
 +	for (;u32num > 0; --u32num) {
 +		sum ^= archive_le32dec(b);
 +		b += 4;
 +	}
 +	return (sum);
 +}
 +
 +static uint32_t
 +cab_checksum_cfdata(const void *p, size_t bytes, uint32_t seed)
 +{
 +	const unsigned char *b;
 +	uint32_t sum;
 +	uint32_t t;
 +
 +	sum = cab_checksum_cfdata_4(p, bytes, seed);
 +	b = p;
 +	b += bytes & ~3;
 +	t = 0;
 +	switch (bytes & 3) {
 +	case 3:
 +		t |= ((uint32_t)(*b++)) << 16;
 +		/* FALL THROUGH */
 +	case 2:
 +		t |= ((uint32_t)(*b++)) << 8;
 +		/* FALL THROUGH */
 +	case 1:
 +		t |= *b;
 +		/* FALL THROUGH */
 +	default:
 +		break;
 +	}
 +	sum ^= t;
 +
 +	return (sum);
 +}
 +
 +static void
 +cab_checksum_update(struct archive_read *a, size_t bytes)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	struct cfdata *cfdata = cab->entry_cfdata;
 +	const unsigned char *p;
 +	size_t sumbytes;
 +
 +	if (cfdata->sum == 0 || cfdata->sum_ptr == NULL)
 +		return;
 +	/*
 +	 * Calculate the sum of this CFDATA.
 +	 * Make sure CFDATA must be calculated in four bytes.
 +	 */
 +	p = cfdata->sum_ptr;
 +	sumbytes = bytes;
 +	if (cfdata->sum_extra_avail) {
 +		while (cfdata->sum_extra_avail < 4 && sumbytes > 0) {
 +			cfdata->sum_extra[
 +			    cfdata->sum_extra_avail++] = *p++;
 +			sumbytes--;
 +		}
 +		if (cfdata->sum_extra_avail == 4) {
 +			cfdata->sum_calculated = cab_checksum_cfdata_4(
 +			    cfdata->sum_extra, 4, cfdata->sum_calculated);
 +			cfdata->sum_extra_avail = 0;
 +		}
 +	}
 +	if (sumbytes) {
 +		int odd = sumbytes & 3;
 +		if (sumbytes - odd > 0)
 +			cfdata->sum_calculated = cab_checksum_cfdata_4(
 +			    p, sumbytes - odd, cfdata->sum_calculated);
 +		if (odd)
 +			memcpy(cfdata->sum_extra, p + sumbytes - odd, odd);
 +		cfdata->sum_extra_avail = odd;
 +	}
 +	cfdata->sum_ptr = NULL;
 +}
 +
 +static int
 +cab_checksum_finish(struct archive_read *a)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	struct cfdata *cfdata = cab->entry_cfdata;
 +	int l;
 +
 +	/* Do not need to compute a sum. */
 +	if (cfdata->sum == 0)
 +		return (ARCHIVE_OK);
 +
 +	/*
 +	 * Calculate the sum of remaining CFDATA.
 +	 */
 +	if (cfdata->sum_extra_avail) {
 +		cfdata->sum_calculated =
 +		    cab_checksum_cfdata(cfdata->sum_extra,
 +		       cfdata->sum_extra_avail, cfdata->sum_calculated);
 +		cfdata->sum_extra_avail = 0;
 +	}
 +
 +	l = 4;
 +	if (cab->cfheader.flags & RESERVE_PRESENT)
 +		l += cab->cfheader.cfdata;
 +	cfdata->sum_calculated = cab_checksum_cfdata(
 +	    cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated);
 +	if (cfdata->sum_calculated != cfdata->sum) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Checksum error CFDATA[%d] %x:%x in %d bytes",
 +		    cab->entry_cffolder->cfdata_index -1,
 +		    cfdata->sum, cfdata->sum_calculated,
 +		    cfdata->compressed_size);
 +		return (ARCHIVE_FAILED);
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Read CFDATA if needed.
 + */
 +static int
 +cab_next_cfdata(struct archive_read *a)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	struct cfdata *cfdata = cab->entry_cfdata;
 +
 +	/* There are remaining bytes in current CFDATA, use it first. */
 +	if (cfdata != NULL && cfdata->uncompressed_bytes_remaining > 0)
 +		return (ARCHIVE_OK);
 +
 +	if (cfdata == NULL) {
 +		int64_t skip;
 +
 +		cab->entry_cffolder->cfdata_index = 0;
 +
 +		/* Seek read pointer to the offset of CFDATA if needed. */
 +		skip = cab->entry_cffolder->cfdata_offset_in_cab
 +			- cab->cab_offset;
 +		if (skip < 0) {
 +			int folder_index;
 +			switch (cab->entry_cffile->folder) {
 +			case iFoldCONTINUED_FROM_PREV:
 +			case iFoldCONTINUED_PREV_AND_NEXT:
 +				folder_index = 0;
 +				break;
 +			case iFoldCONTINUED_TO_NEXT:
 +				folder_index = cab->cfheader.folder_count-1;
 +				break;
 +			default:
 +				folder_index = cab->entry_cffile->folder;
 +				break;
 +			}
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Invalid offset of CFDATA in folder(%d) %jd < %jd",
 +			    folder_index,
 +			    (intmax_t)cab->entry_cffolder->cfdata_offset_in_cab,
 +			    (intmax_t)cab->cab_offset);
 +			return (ARCHIVE_FATAL);
 +		}
 +		if (skip > 0) {
 +			if (__archive_read_consume(a, skip) < 0)
 +				return (ARCHIVE_FATAL);
 +			cab->cab_offset =
 +			    cab->entry_cffolder->cfdata_offset_in_cab;
 +		}
 +	}
 +
 +	/*
 +	 * Read a CFDATA.
 +	 */
 +	if (cab->entry_cffolder->cfdata_index <
 +	    cab->entry_cffolder->cfdata_count) {
 +		const unsigned char *p;
 +		int l;
 +
 +		cfdata = &(cab->entry_cffolder->cfdata);
 +		cab->entry_cffolder->cfdata_index++;
 +		cab->entry_cfdata = cfdata;
 +		cfdata->sum_calculated = 0;
 +		cfdata->sum_extra_avail = 0;
 +		cfdata->sum_ptr = NULL;
 +		l = 8;
 +		if (cab->cfheader.flags & RESERVE_PRESENT)
 +			l += cab->cfheader.cfdata;
 +		if ((p = __archive_read_ahead(a, l, NULL)) == NULL)
 +			return (truncated_error(a));
 +		cfdata->sum = archive_le32dec(p + CFDATA_csum);
 +		cfdata->compressed_size = archive_le16dec(p + CFDATA_cbData);
 +		cfdata->compressed_bytes_remaining = cfdata->compressed_size;
 +		cfdata->uncompressed_size =
 +		    archive_le16dec(p + CFDATA_cbUncomp);
 +		cfdata->uncompressed_bytes_remaining =
 +		    cfdata->uncompressed_size;
 +		cfdata->uncompressed_avail = 0;
 +		cfdata->read_offset = 0;
 +		cfdata->unconsumed = 0;
 +
 +		/*
 +		 * Sanity check if data size is acceptable.
 +		 */
 +		if (cfdata->compressed_size == 0 ||
 +		    cfdata->compressed_size > (0x8000+6144))
 +			goto invalid;
 +		if (cfdata->uncompressed_size > 0x8000)
 +			goto invalid;
 +		if (cfdata->uncompressed_size == 0) {
 +			switch (cab->entry_cffile->folder) {
 +			case iFoldCONTINUED_PREV_AND_NEXT:
 +			case iFoldCONTINUED_TO_NEXT:
 +				break;
 +			case iFoldCONTINUED_FROM_PREV:
 +			default:
 +				goto invalid;
 +			}
 +		}
 +		/* If CFDATA is not last in a folder, an uncompressed
 +		 * size must be 0x8000(32KBi) */
 +		if ((cab->entry_cffolder->cfdata_index <
 +		     cab->entry_cffolder->cfdata_count) &&
 +		       cfdata->uncompressed_size != 0x8000)
 +			goto invalid;
 +
 +		/* A compressed data size and an uncompressed data size must
 +		 * be the same in no compression mode. */
 +		if (cab->entry_cffolder->comptype == COMPTYPE_NONE &&
 +		    cfdata->compressed_size != cfdata->uncompressed_size)
 +			goto invalid;
 +
 +		/*
 +		 * Save CFDATA image for sum check.
 +		 */
 +		if (cfdata->memimage_size < (size_t)l) {
 +			free(cfdata->memimage);
 +			cfdata->memimage = malloc(l);
 +			if (cfdata->memimage == NULL) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "Can't allocate memory for CAB data");
 +				return (ARCHIVE_FATAL);
 +			}
 +			cfdata->memimage_size = l;
 +		}
 +		memcpy(cfdata->memimage, p, l);
 +
 +		/* Consume bytes as much as we used. */
 +		__archive_read_consume(a, l);
 +		cab->cab_offset += l;
 +	} else if (cab->entry_cffolder->cfdata_count > 0) {
 +		/* Run out of all CFDATA in a folder. */
 +		cfdata->compressed_size = 0;
 +		cfdata->uncompressed_size = 0;
 +		cfdata->compressed_bytes_remaining = 0;
 +		cfdata->uncompressed_bytes_remaining = 0;
 +	} else {
 +		/* Current folder does not have any CFDATA. */
 +		cfdata = &(cab->entry_cffolder->cfdata);
 +		cab->entry_cfdata = cfdata;
 +		memset(cfdata, 0, sizeof(*cfdata));
 +	}
 +	return (ARCHIVE_OK);
 +invalid:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Invalid CFDATA");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +/*
 + * Read ahead CFDATA.
 + */
 +static const void *
 +cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	int err;
 +
 +	err = cab_next_cfdata(a);
 +	if (err < ARCHIVE_OK) {
 +		*avail = err;
 +		return (NULL);
 +	}
 +
 +	switch (cab->entry_cffolder->comptype) {
 +	case COMPTYPE_NONE:
 +		return (cab_read_ahead_cfdata_none(a, avail));
 +	case COMPTYPE_MSZIP:
 +		return (cab_read_ahead_cfdata_deflate(a, avail));
 +	case COMPTYPE_LZX:
 +		return (cab_read_ahead_cfdata_lzx(a, avail));
 +	default: /* Unsupported compression. */
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Unsupported CAB compression : %s",
 +		    cab->entry_cffolder->compname);
 +		*avail = ARCHIVE_FAILED;
 +		return (NULL);
 +	}
 +}
 +
 +/*
 + * Read ahead CFDATA as uncompressed data.
 + */
 +static const void *
 +cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	struct cfdata *cfdata;
 +	const void *d;
 +
 +	cfdata = cab->entry_cfdata;
 +
 +	/*
 +	 * Note: '1' here is a performance optimization.
 +	 * Recall that the decompression layer returns a count of
 +	 * available bytes; asking for more than that forces the
 +	 * decompressor to combine reads by copying data.
 +	 */
 +	d = __archive_read_ahead(a, 1, avail);
 +	if (*avail <= 0) {
 +		*avail = truncated_error(a);
 +		return (NULL);
 +	}
 +	if (*avail > cfdata->uncompressed_bytes_remaining)
 +		*avail = cfdata->uncompressed_bytes_remaining;
 +	cfdata->uncompressed_avail = cfdata->uncompressed_size;
 +	cfdata->unconsumed = *avail;
 +	cfdata->sum_ptr = d;
 +	return (d);
 +}
 +
 +/*
 + * Read ahead CFDATA as deflate data.
 + */
 +#ifdef HAVE_ZLIB_H
 +static const void *
 +cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	struct cfdata *cfdata;
 +	const void *d;
 +	int r, mszip;
 +	uint16_t uavail;
 +	char eod = 0;
 +
 +	cfdata = cab->entry_cfdata;
 +	/* If the buffer hasn't been allocated, allocate it now. */
 +	if (cab->uncompressed_buffer == NULL) {
 +		cab->uncompressed_buffer_size = 0x8000;
 +		cab->uncompressed_buffer
 +		    = (unsigned char *)malloc(cab->uncompressed_buffer_size);
 +		if (cab->uncompressed_buffer == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "No memory for CAB reader");
 +			*avail = ARCHIVE_FATAL;
 +			return (NULL);
 +		}
 +	}
 +
 +	uavail = cfdata->uncompressed_avail;
 +	if (uavail == cfdata->uncompressed_size) {
 +		d = cab->uncompressed_buffer + cfdata->read_offset;
 +		*avail = uavail - cfdata->read_offset;
 +		return (d);
 +	}
 +
 +	if (!cab->entry_cffolder->decompress_init) {
 +		cab->stream.next_in = NULL;
 +		cab->stream.avail_in = 0;
 +		cab->stream.total_in = 0;
 +		cab->stream.next_out = NULL;
 +		cab->stream.avail_out = 0;
 +		cab->stream.total_out = 0;
 +		if (cab->stream_valid)
 +			r = inflateReset(&cab->stream);
 +		else
 +			r = inflateInit2(&cab->stream,
 +			    -15 /* Don't check for zlib header */);
 +		if (r != Z_OK) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Can't initialize deflate decompression.");
 +			*avail = ARCHIVE_FATAL;
 +			return (NULL);
 +		}
 +		/* Stream structure has been set up. */
 +		cab->stream_valid = 1;
 +		/* We've initialized decompression for this stream. */
 +		cab->entry_cffolder->decompress_init = 1;
 +	}
 +
 +	if (cfdata->compressed_bytes_remaining == cfdata->compressed_size)
 +		mszip = 2;
 +	else
 +		mszip = 0;
 +	eod = 0;
 +	cab->stream.total_out = uavail;
 +	/*
 +	 * We always uncompress all data in current CFDATA.
 +	 */
 +	while (!eod && cab->stream.total_out < cfdata->uncompressed_size) {
 +		ssize_t bytes_avail;
 +
 +		cab->stream.next_out =
 +		    cab->uncompressed_buffer + cab->stream.total_out;
 +		cab->stream.avail_out =
 +		    cfdata->uncompressed_size - cab->stream.total_out;
 +
 +		d = __archive_read_ahead(a, 1, &bytes_avail);
 +		if (bytes_avail <= 0) {
 +			*avail = truncated_error(a);
 +			return (NULL);
 +		}
 +		if (bytes_avail > cfdata->compressed_bytes_remaining)
 +			bytes_avail = cfdata->compressed_bytes_remaining;
 +		/*
 +		 * A bug in zlib.h: stream.next_in should be marked 'const'
 +		 * but isn't (the library never alters data through the
 +		 * next_in pointer, only reads it).  The result: this ugly
 +		 * cast to remove 'const'.
 +		 */
 +		cab->stream.next_in = (Bytef *)(uintptr_t)d;
 +		cab->stream.avail_in = (uInt)bytes_avail;
 +		cab->stream.total_in = 0;
 +
 +		/* Cut out a tow-byte MSZIP signature(0x43, 0x4b). */
 +		if (mszip > 0) {
 +			if (bytes_avail <= 0)
 +				goto nomszip;
 +			if (bytes_avail <= mszip) {
 +				if (mszip == 2) {
 +					if (cab->stream.next_in[0] != 0x43)
 +						goto nomszip;
 +					if (bytes_avail > 1 &&
 +					    cab->stream.next_in[1] != 0x4b)
 +						goto nomszip;
 +				} else if (cab->stream.next_in[0] != 0x4b)
 +					goto nomszip;
 +				cfdata->unconsumed = bytes_avail;
 +				cfdata->sum_ptr = d;
 +				if (cab_minimum_consume_cfdata(
 +				    a, cfdata->unconsumed) < 0) {
 +					*avail = ARCHIVE_FATAL;
 +					return (NULL);
 +				}
 +				mszip -= (int)bytes_avail;
 +				continue;
 +			}
 +			if (mszip == 1 && cab->stream.next_in[0] != 0x4b)
 +				goto nomszip;
 +			else if (cab->stream.next_in[0] != 0x43 ||
 +			    cab->stream.next_in[1] != 0x4b)
 +				goto nomszip;
 +			cab->stream.next_in += mszip;
 +			cab->stream.avail_in -= mszip;
 +			cab->stream.total_in += mszip;
 +			mszip = 0;
 +		}
 +
 +		r = inflate(&cab->stream, 0);
 +		switch (r) {
 +		case Z_OK:
 +			break;
 +		case Z_STREAM_END:
 +			eod = 1;
 +			break;
 +		default:
 +			goto zlibfailed;
 +		}
 +		cfdata->unconsumed = cab->stream.total_in;
 +		cfdata->sum_ptr = d;
 +		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
 +			*avail = ARCHIVE_FATAL;
 +			return (NULL);
 +		}
 +	}
 +	uavail = (uint16_t)cab->stream.total_out;
 +
 +	if (uavail < cfdata->uncompressed_size) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Invalid uncompressed size (%d < %d)",
 +		    uavail, cfdata->uncompressed_size);
 +		*avail = ARCHIVE_FATAL;
 +		return (NULL);
 +	}
 +
 +	/*
 +	 * Note: I suspect there is a bug in makecab.exe because, in rare
 +	 * case, compressed bytes are still remaining regardless we have
 +	 * gotten all uncompressed bytes, which size is recorded in CFDATA,
 +	 * as much as we need, and we have to use the garbage so as to
 +	 * correctly compute the sum of CFDATA accordingly.
 +	 */
 +	if (cfdata->compressed_bytes_remaining > 0) {
 +		ssize_t bytes_avail;
 +
 +		d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining,
 +		    &bytes_avail);
 +		if (bytes_avail <= 0) {
 +			*avail = truncated_error(a);
 +			return (NULL);
 +		}
 +		cfdata->unconsumed = cfdata->compressed_bytes_remaining;
 +		cfdata->sum_ptr = d;
 +		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
 +			*avail = ARCHIVE_FATAL;
 +			return (NULL);
 +		}
 +	}
 +
 +	/*
 +	 * Set dictionary data for decompressing of next CFDATA, which
 +	 * in the same folder. This is why we always do decompress CFDATA
 +	 * even if beginning CFDATA or some of CFDATA are not used in
 +	 * skipping file data.
 +	 */
 +	if (cab->entry_cffolder->cfdata_index <
 +	    cab->entry_cffolder->cfdata_count) {
 +		r = inflateReset(&cab->stream);
 +		if (r != Z_OK)
 +			goto zlibfailed;
 +		r = inflateSetDictionary(&cab->stream,
 +		    cab->uncompressed_buffer, cfdata->uncompressed_size);
 +		if (r != Z_OK)
 +			goto zlibfailed;
 +	}
 +
 +	d = cab->uncompressed_buffer + cfdata->read_offset;
 +	*avail = uavail - cfdata->read_offset;
 +	cfdata->uncompressed_avail = uavail;
 +
 +	return (d);
 +
 +zlibfailed:
 +	switch (r) {
 +	case Z_MEM_ERROR:
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Out of memory for deflate decompression");
 +		break;
 +	default:
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Deflate decompression failed (%d)", r);
 +		break;
 +	}
 +	*avail = ARCHIVE_FATAL;
 +	return (NULL);
 +nomszip:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +	    "CFDATA incorrect(no MSZIP signature)");
 +	*avail = ARCHIVE_FATAL;
 +	return (NULL);
 +}
 +
 +#else /* HAVE_ZLIB_H */
 +
 +static const void *
 +cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
 +{
 +	*avail = ARCHIVE_FATAL;
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +	    "libarchive compiled without deflate support (no libz)");
 +	return (NULL);
 +}
 +
 +#endif /* HAVE_ZLIB_H */
 +
 +static const void *
 +cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	struct cfdata *cfdata;
 +	const void *d;
 +	int r;
 +	uint16_t uavail;
 +
 +	cfdata = cab->entry_cfdata;
 +	/* If the buffer hasn't been allocated, allocate it now. */
 +	if (cab->uncompressed_buffer == NULL) {
 +		cab->uncompressed_buffer_size = 0x8000;
 +		cab->uncompressed_buffer
 +		    = (unsigned char *)malloc(cab->uncompressed_buffer_size);
 +		if (cab->uncompressed_buffer == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "No memory for CAB reader");
 +			*avail = ARCHIVE_FATAL;
 +			return (NULL);
 +		}
 +	}
 +
 +	uavail = cfdata->uncompressed_avail;
 +	if (uavail == cfdata->uncompressed_size) {
 +		d = cab->uncompressed_buffer + cfdata->read_offset;
 +		*avail = uavail - cfdata->read_offset;
 +		return (d);
 +	}
 +
 +	if (!cab->entry_cffolder->decompress_init) {
 +		r = lzx_decode_init(&cab->xstrm,
 +		    cab->entry_cffolder->compdata);
 +		if (r != ARCHIVE_OK) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Can't initialize LZX decompression.");
 +			*avail = ARCHIVE_FATAL;
 +			return (NULL);
 +		}
 +		/* We've initialized decompression for this stream. */
 +		cab->entry_cffolder->decompress_init = 1;
 +	}
 +
 +	/* Clean up remaining bits of previous CFDATA. */
 +	lzx_cleanup_bitstream(&cab->xstrm);
 +	cab->xstrm.total_out = uavail;
 +	while (cab->xstrm.total_out < cfdata->uncompressed_size) {
 +		ssize_t bytes_avail;
 +
 +		cab->xstrm.next_out =
 +		    cab->uncompressed_buffer + cab->xstrm.total_out;
 +		cab->xstrm.avail_out =
 +		    cfdata->uncompressed_size - cab->xstrm.total_out;
 +
 +		d = __archive_read_ahead(a, 1, &bytes_avail);
 +		if (bytes_avail <= 0) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Truncated CAB file data");
 +			*avail = ARCHIVE_FATAL;
 +			return (NULL);
 +		}
 +		if (bytes_avail > cfdata->compressed_bytes_remaining)
 +			bytes_avail = cfdata->compressed_bytes_remaining;
 +
 +		cab->xstrm.next_in = d;
 +		cab->xstrm.avail_in = bytes_avail;
 +		cab->xstrm.total_in = 0;
 +		r = lzx_decode(&cab->xstrm,
 +		    cfdata->compressed_bytes_remaining == bytes_avail);
 +		switch (r) {
 +		case ARCHIVE_OK:
 +		case ARCHIVE_EOF:
 +			break;
 +		default:
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "LZX decompression failed (%d)", r);
 +			*avail = ARCHIVE_FATAL;
 +			return (NULL);
 +		}
 +		cfdata->unconsumed = cab->xstrm.total_in;
 +		cfdata->sum_ptr = d;
 +		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
 +			*avail = ARCHIVE_FATAL;
 +			return (NULL);
 +		}
 +	}
 +
 +	uavail = (uint16_t)cab->xstrm.total_out;
 +	/*
 +	 * Make sure a read pointer advances to next CFDATA.
 +	 */
 +	if (cfdata->compressed_bytes_remaining > 0) {
 +		ssize_t bytes_avail;
 +
 +		d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining,
 +		    &bytes_avail);
 +		if (bytes_avail <= 0) {
 +			*avail = truncated_error(a);
 +			return (NULL);
 +		}
 +		cfdata->unconsumed = cfdata->compressed_bytes_remaining;
 +		cfdata->sum_ptr = d;
 +		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
 +			*avail = ARCHIVE_FATAL;
 +			return (NULL);
 +		}
 +	}
 +
 +	/*
 +	 * Translation reversal of x86 processor CALL byte sequence(E8).
 +	 */
 +	lzx_translation(&cab->xstrm, cab->uncompressed_buffer,
 +	    cfdata->uncompressed_size,
 +	    (cab->entry_cffolder->cfdata_index-1) * 0x8000);
 +
 +	d = cab->uncompressed_buffer + cfdata->read_offset;
 +	*avail = uavail - cfdata->read_offset;
 +	cfdata->uncompressed_avail = uavail;
 +
 +	return (d);
 +}
 +
 +/*
 + * Consume CFDATA.
 + * We always decompress CFDATA to consume CFDATA as much as we need
 + * in uncompressed bytes because all CFDATA in a folder are related
 + * so we do not skip any CFDATA without decompressing.
 + * Note: If the folder of a CFFILE is iFoldCONTINUED_PREV_AND_NEXT or
 + * iFoldCONTINUED_FROM_PREV, we won't decompress because a CFDATA for
 + * the CFFILE is remaining bytes of previous Multivolume CAB file.
 + */
 +static int64_t
 +cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	struct cfdata *cfdata;
 +	int64_t cbytes, rbytes;
 +	int err;
 +
 +	rbytes = cab_minimum_consume_cfdata(a, consumed_bytes);
 +	if (rbytes < 0)
 +		return (ARCHIVE_FATAL);
 +
 +	cfdata = cab->entry_cfdata;
 +	while (rbytes > 0) {
 +		ssize_t avail;
 +
 +		if (cfdata->compressed_size == 0) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Invalid CFDATA");
 +			return (ARCHIVE_FATAL);
 +		}
 +		cbytes = cfdata->uncompressed_bytes_remaining;
 +		if (cbytes > rbytes)
 +			cbytes = rbytes;
 +		rbytes -= cbytes;
 +
 +		if (cfdata->uncompressed_avail == 0 &&
 +		   (cab->entry_cffile->folder == iFoldCONTINUED_PREV_AND_NEXT ||
 +		    cab->entry_cffile->folder == iFoldCONTINUED_FROM_PREV)) {
 +			/* We have not read any data yet. */
 +			if (cbytes == cfdata->uncompressed_bytes_remaining) {
 +				/* Skip whole current CFDATA. */
 +				__archive_read_consume(a,
 +				    cfdata->compressed_size);
 +				cab->cab_offset += cfdata->compressed_size;
 +				cfdata->compressed_bytes_remaining = 0;
 +				cfdata->uncompressed_bytes_remaining = 0;
 +				err = cab_next_cfdata(a);
 +				if (err < 0)
 +					return (err);
 +				cfdata = cab->entry_cfdata;
 +				if (cfdata->uncompressed_size == 0) {
 +					switch (cab->entry_cffile->folder) {
 +					case iFoldCONTINUED_PREV_AND_NEXT:
 +					case iFoldCONTINUED_TO_NEXT:
 +					case iFoldCONTINUED_FROM_PREV:
 +						rbytes = 0;
 +						break;
 +					default:
 +						break;
 +					}
 +				}
 +				continue;
 +			}
 +			cfdata->read_offset += (uint16_t)cbytes;
 +			cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
 +			break;
 +		} else if (cbytes == 0) {
 +			err = cab_next_cfdata(a);
 +			if (err < 0)
 +				return (err);
 +			cfdata = cab->entry_cfdata;
 +			if (cfdata->uncompressed_size == 0) {
 +				switch (cab->entry_cffile->folder) {
 +				case iFoldCONTINUED_PREV_AND_NEXT:
 +				case iFoldCONTINUED_TO_NEXT:
 +				case iFoldCONTINUED_FROM_PREV:
 +					return (ARCHIVE_FATAL);
 +				default:
 +					break;
 +				}
 +			}
 +			continue;
 +		}
 +		while (cbytes > 0) {
 +			(void)cab_read_ahead_cfdata(a, &avail);
 +			if (avail <= 0)
 +				return (ARCHIVE_FATAL);
 +			if (avail > cbytes)
 +				avail = (ssize_t)cbytes;
 +			if (cab_minimum_consume_cfdata(a, avail) < 0)
 +				return (ARCHIVE_FATAL);
 +			cbytes -= avail;
 +		}
 +	}
 +	return (consumed_bytes);
 +}
 +
 +/*
 + * Consume CFDATA as much as we have already gotten and
 + * compute the sum of CFDATA.
 + */
 +static int64_t
 +cab_minimum_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	struct cfdata *cfdata;
 +	int64_t cbytes, rbytes;
 +	int err;
 +
 +	cfdata = cab->entry_cfdata;
 +	rbytes = consumed_bytes;
 +	if (cab->entry_cffolder->comptype == COMPTYPE_NONE) {
 +		if (consumed_bytes < cfdata->unconsumed)
 +			cbytes = consumed_bytes;
 +		else
 +			cbytes = cfdata->unconsumed;
 +		rbytes -= cbytes; 
 +		cfdata->read_offset += (uint16_t)cbytes;
 +		cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
 +		cfdata->unconsumed -= cbytes;
 +	} else {
 +		cbytes = cfdata->uncompressed_avail - cfdata->read_offset;
 +		if (cbytes > 0) {
 +			if (consumed_bytes < cbytes)
 +				cbytes = consumed_bytes;
 +			rbytes -= cbytes;
 +			cfdata->read_offset += (uint16_t)cbytes;
 +			cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
 +		}
 +
 +		if (cfdata->unconsumed) {
 +			cbytes = cfdata->unconsumed;
 +			cfdata->unconsumed = 0;
 +		} else
 +			cbytes = 0;
 +	}
 +	if (cbytes) {
 +		/* Compute the sum. */
 +		cab_checksum_update(a, (size_t)cbytes);
 +
 +		/* Consume as much as the compressor actually used. */
 +		__archive_read_consume(a, cbytes);
 +		cab->cab_offset += cbytes;
 +		cfdata->compressed_bytes_remaining -= (uint16_t)cbytes;
 +		if (cfdata->compressed_bytes_remaining == 0) {
 +			err = cab_checksum_finish(a);
 +			if (err < 0)
 +				return (err);
 +		}
 +	}
 +	return (rbytes);
 +}
 +
 +/*
 + * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
 + * cab->end_of_entry if it consumes all of the data.
 + */
 +static int
 +cab_read_data(struct archive_read *a, const void **buff,
 +    size_t *size, int64_t *offset)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	ssize_t bytes_avail;
 +
 +	if (cab->entry_bytes_remaining == 0) {
 +		*buff = NULL;
 +		*size = 0;
 +		*offset = cab->entry_offset;
 +		cab->end_of_entry = 1;
 +		return (ARCHIVE_OK);
 +	}
 +
 +	*buff = cab_read_ahead_cfdata(a, &bytes_avail);
 +	if (bytes_avail <= 0) {
 +		*buff = NULL;
 +		*size = 0;
 +		*offset = 0;
 +		if (bytes_avail == 0 &&
 +		    cab->entry_cfdata->uncompressed_size == 0) {
 +			/* All of CFDATA in a folder has been handled. */
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA");
 +			return (ARCHIVE_FATAL);
 +		} else
 +			return ((int)bytes_avail);
 +	}
 +	if (bytes_avail > cab->entry_bytes_remaining)
 +		bytes_avail = (ssize_t)cab->entry_bytes_remaining;
 +
 +	*size = bytes_avail;
 +	*offset = cab->entry_offset;
 +	cab->entry_offset += bytes_avail;
 +	cab->entry_bytes_remaining -= bytes_avail;
 +	if (cab->entry_bytes_remaining == 0)
 +		cab->end_of_entry = 1;
 +	cab->entry_unconsumed = bytes_avail;
 +	if (cab->entry_cffolder->comptype == COMPTYPE_NONE) {
 +		/* Don't consume more than current entry used. */
 +		if (cab->entry_cfdata->unconsumed > cab->entry_unconsumed)
 +			cab->entry_cfdata->unconsumed = cab->entry_unconsumed;
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +archive_read_format_cab_read_data_skip(struct archive_read *a)
 +{
 +	struct cab *cab;
 +	int64_t bytes_skipped;
 +	int r;
 +
 +	cab = (struct cab *)(a->format->data);
 +
 +	if (cab->end_of_archive)
 +		return (ARCHIVE_EOF);
 +
 +	if (!cab->read_data_invoked) {
 +		cab->bytes_skipped += cab->entry_bytes_remaining;
 +		cab->entry_bytes_remaining = 0;
 +		/* This entry is finished and done. */
 +		cab->end_of_entry_cleanup = cab->end_of_entry = 1;
 +		return (ARCHIVE_OK);
 +	}
 +
 +	if (cab->entry_unconsumed) {
 +		/* Consume as much as the compressor actually used. */
 +		r = (int)cab_consume_cfdata(a, cab->entry_unconsumed);
 +		cab->entry_unconsumed = 0;
 +		if (r < 0)
 +			return (r);
 +	} else if (cab->entry_cfdata == NULL) {
 +		r = cab_next_cfdata(a);
 +		if (r < 0)
 +			return (r);
 +	}
 +
 +	/* if we've already read to end of data, we're done. */
 +	if (cab->end_of_entry_cleanup)
 +		return (ARCHIVE_OK);
 +
 +	/*
 +	 * If the length is at the beginning, we can skip the
 +	 * compressed data much more quickly.
 +	 */
 +	bytes_skipped = cab_consume_cfdata(a, cab->entry_bytes_remaining);
 +	if (bytes_skipped < 0)
 +		return (ARCHIVE_FATAL);
 +
 +	/* If the compression type is none(uncompressed), we've already
 +	 * consumed data as much as the current entry size. */
 +	if (cab->entry_cffolder->comptype == COMPTYPE_NONE &&
 +	    cab->entry_cfdata != NULL)
 +		cab->entry_cfdata->unconsumed = 0;
 +
 +	/* This entry is finished and done. */
 +	cab->end_of_entry_cleanup = cab->end_of_entry = 1;
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +archive_read_format_cab_cleanup(struct archive_read *a)
 +{
 +	struct cab *cab = (struct cab *)(a->format->data);
 +	struct cfheader *hd = &cab->cfheader;
 +	int i;
 +
 +	if (hd->folder_array != NULL) {
 +		for (i = 0; i < hd->folder_count; i++)
 +			free(hd->folder_array[i].cfdata.memimage);
 +		free(hd->folder_array);
 +	}
 +	if (hd->file_array != NULL) {
 +		for (i = 0; i < cab->cfheader.file_count; i++)
 +			archive_string_free(&(hd->file_array[i].pathname));
 +		free(hd->file_array);
 +	}
 +#ifdef HAVE_ZLIB_H
 +	if (cab->stream_valid)
 +		inflateEnd(&cab->stream);
 +#endif
 +	lzx_decode_free(&cab->xstrm);
 +	archive_wstring_free(&cab->ws);
 +	free(cab->uncompressed_buffer);
 +	free(cab);
 +	(a->format->data) = NULL;
 +	return (ARCHIVE_OK);
 +}
 +
 +/* Convert an MSDOS-style date/time into Unix-style time. */
 +static time_t
 +cab_dos_time(const unsigned char *p)
 +{
 +	int msTime, msDate;
 +	struct tm ts;
 +
 +	msDate = archive_le16dec(p);
 +	msTime = archive_le16dec(p+2);
 +
 +	memset(&ts, 0, sizeof(ts));
 +	ts.tm_year = ((msDate >> 9) & 0x7f) + 80;   /* Years since 1900. */
 +	ts.tm_mon = ((msDate >> 5) & 0x0f) - 1;     /* Month number.     */
 +	ts.tm_mday = msDate & 0x1f;		    /* Day of month.     */
 +	ts.tm_hour = (msTime >> 11) & 0x1f;
 +	ts.tm_min = (msTime >> 5) & 0x3f;
 +	ts.tm_sec = (msTime << 1) & 0x3e;
 +	ts.tm_isdst = -1;
 +	return (mktime(&ts));
 +}
 +
 +/*****************************************************************
 + *
 + * LZX decompression code.
 + *
 + *****************************************************************/
 +
 +/*
 + * Initialize LZX decoder.
 + *
 + * Returns ARCHIVE_OK if initialization was successful.
 + * Returns ARCHIVE_FAILED if w_bits has unsupported value.
 + * Returns ARCHIVE_FATAL if initialization failed; memory allocation
 + * error occurred.
 + */
 +static int
 +lzx_decode_init(struct lzx_stream *strm, int w_bits)
 +{
 +	struct lzx_dec *ds;
 +	int slot, w_size, w_slot;
 +	int base, footer;
 +	int base_inc[18];
 +
 +	if (strm->ds == NULL) {
 +		strm->ds = calloc(1, sizeof(*strm->ds));
 +		if (strm->ds == NULL)
 +			return (ARCHIVE_FATAL);
 +	}
 +	ds = strm->ds;
 +	ds->error = ARCHIVE_FAILED;
 +
 +	/* Allow bits from 15(32KBi) up to 21(2MBi) */
 +	if (w_bits < SLOT_BASE || w_bits > SLOT_MAX)
 +		return (ARCHIVE_FAILED);
 +
 +	ds->error = ARCHIVE_FATAL;
 +
 +	/*
 +	 * Alloc window
 +	 */
 +	w_size = ds->w_size;
 +	w_slot = slots[w_bits - SLOT_BASE];
 +	ds->w_size = 1U << w_bits;
 +	ds->w_mask = ds->w_size -1;
 +	if (ds->w_buff == NULL || w_size != ds->w_size) {
 +		free(ds->w_buff);
 +		ds->w_buff = malloc(ds->w_size);
 +		if (ds->w_buff == NULL)
 +			return (ARCHIVE_FATAL);
 +		free(ds->pos_tbl);
 +		ds->pos_tbl = malloc(sizeof(ds->pos_tbl[0]) * w_slot);
 +		if (ds->pos_tbl == NULL)
 +			return (ARCHIVE_FATAL);
 +		lzx_huffman_free(&(ds->mt));
 +	}
 +
 +	for (footer = 0; footer < 18; footer++)
 +		base_inc[footer] = 1 << footer;
 +	base = footer = 0;
 +	for (slot = 0; slot < w_slot; slot++) {
 +		int n;
 +		if (footer == 0)
 +			base = slot;
 +		else
 +			base += base_inc[footer];
 +		if (footer < 17) {
 +			footer = -2;
 +			for (n = base; n; n >>= 1)
 +				footer++;
 +			if (footer <= 0)
 +				footer = 0;
 +		}
 +		ds->pos_tbl[slot].base = base;
 +		ds->pos_tbl[slot].footer_bits = footer;
 +	}
 +
 +	ds->w_pos = 0;
 +	ds->state = 0;
 +	ds->br.cache_buffer = 0;
 +	ds->br.cache_avail = 0;
 +	ds->r0 = ds->r1 = ds->r2 = 1;
 +
 +	/* Initialize aligned offset tree. */
 +	if (lzx_huffman_init(&(ds->at), 8, 8) != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Initialize pre-tree. */
 +	if (lzx_huffman_init(&(ds->pt), 20, 10) != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Initialize Main tree. */
 +	if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16)
 +	    != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Initialize Length tree. */
 +	if (lzx_huffman_init(&(ds->lt), 249, 16) != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	ds->error = 0;
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Release LZX decoder.
 + */
 +static void
 +lzx_decode_free(struct lzx_stream *strm)
 +{
 +
 +	if (strm->ds == NULL)
 +		return;
 +	free(strm->ds->w_buff);
 +	free(strm->ds->pos_tbl);
 +	lzx_huffman_free(&(strm->ds->at));
 +	lzx_huffman_free(&(strm->ds->pt));
 +	lzx_huffman_free(&(strm->ds->mt));
 +	lzx_huffman_free(&(strm->ds->lt));
 +	free(strm->ds);
 +	strm->ds = NULL;
 +}
 +
 +/*
 + * E8 Call Translation reversal.
 + */
 +static void
 +lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset)
 +{
 +	struct lzx_dec *ds = strm->ds;
 +	unsigned char *b, *end;
 +
 +	if (!ds->translation || size <= 10)
 +		return;
 +	b = p;
 +	end = b + size - 10;
 +	while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) {
 +		size_t i = b - (unsigned char *)p;
 +		int32_t cp, displacement, value;
 +
 +		cp = (int32_t)(offset + (uint32_t)i);
 +		value = archive_le32dec(&b[1]);
 +		if (value >= -cp && value < (int32_t)ds->translation_size) {
 +			if (value >= 0)
 +				displacement = value - cp;
 +			else
 +				displacement = value + ds->translation_size;
 +			archive_le32enc(&b[1], (uint32_t)displacement);
 +		}
 +		b += 5;
 +	}
 +}
 +
 +/*
 + * Bit stream reader.
 + */
 +/* Check that the cache buffer has enough bits. */
 +#define lzx_br_has(br, n)	((br)->cache_avail >= n)
 +/* Get compressed data by bit. */
 +#define lzx_br_bits(br, n)				\
 +	(((uint32_t)((br)->cache_buffer >>		\
 +		((br)->cache_avail - (n)))) & cache_masks[n])
 +#define lzx_br_bits_forced(br, n)			\
 +	(((uint32_t)((br)->cache_buffer <<		\
 +		((n) - (br)->cache_avail))) & cache_masks[n])
 +/* Read ahead to make sure the cache buffer has enough compressed data we
 + * will use.
 + *  True  : completed, there is enough data in the cache buffer.
 + *  False : we met that strm->next_in is empty, we have to get following
 + *          bytes. */
 +#define lzx_br_read_ahead_0(strm, br, n)	\
 +	(lzx_br_has((br), (n)) || lzx_br_fillup(strm, br))
 +/*  True  : the cache buffer has some bits as much as we need.
 + *  False : there are no enough bits in the cache buffer to be used,
 + *          we have to get following bytes if we could. */
 +#define lzx_br_read_ahead(strm, br, n)	\
 +	(lzx_br_read_ahead_0((strm), (br), (n)) || lzx_br_has((br), (n)))
 +
 +/* Notify how many bits we consumed. */
 +#define lzx_br_consume(br, n)	((br)->cache_avail -= (n))
 +#define lzx_br_consume_unaligned_bits(br) ((br)->cache_avail &= ~0x0f)
 +
 +#define lzx_br_is_unaligned(br)	((br)->cache_avail & 0x0f)
 +
 +static const uint32_t cache_masks[] = {
 +	0x00000000, 0x00000001, 0x00000003, 0x00000007,
 +	0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
 +	0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
 +	0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
 +	0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
 +	0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
 +	0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
 +	0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
 +	0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
 +};
 +
 +/*
 + * Shift away used bits in the cache data and fill it up with following bits.
 + * Call this when cache buffer does not have enough bits you need.
 + *
 + * Returns 1 if the cache buffer is full.
 + * Returns 0 if the cache buffer is not full; input buffer is empty.
 + */
 +static int
 +lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
 +{
 +/*
 + * x86 processor family can read misaligned data without an access error.
 + */
 +	int n = CACHE_BITS - br->cache_avail;
 +
 +	for (;;) {
 +		switch (n >> 4) {
 +		case 4:
 +			if (strm->avail_in >= 8) {
 +				br->cache_buffer =
 +				    ((uint64_t)strm->next_in[1]) << 56 |
 +				    ((uint64_t)strm->next_in[0]) << 48 |
 +				    ((uint64_t)strm->next_in[3]) << 40 |
 +				    ((uint64_t)strm->next_in[2]) << 32 |
 +				    ((uint32_t)strm->next_in[5]) << 24 |
 +				    ((uint32_t)strm->next_in[4]) << 16 |
 +				    ((uint32_t)strm->next_in[7]) << 8 |
 +				     (uint32_t)strm->next_in[6];
 +				strm->next_in += 8;
 +				strm->avail_in -= 8;
 +				br->cache_avail += 8 * 8;
 +				return (1);
 +			}
 +			break;
 +		case 3:
 +			if (strm->avail_in >= 6) {
 +				br->cache_buffer =
 +		 		   (br->cache_buffer << 48) |
 +				    ((uint64_t)strm->next_in[1]) << 40 |
 +				    ((uint64_t)strm->next_in[0]) << 32 |
 +				    ((uint32_t)strm->next_in[3]) << 24 |
 +				    ((uint32_t)strm->next_in[2]) << 16 |
 +				    ((uint32_t)strm->next_in[5]) << 8 |
 +				     (uint32_t)strm->next_in[4];
 +				strm->next_in += 6;
 +				strm->avail_in -= 6;
 +				br->cache_avail += 6 * 8;
 +				return (1);
 +			}
 +			break;
 +		case 0:
 +			/* We have enough compressed data in
 +			 * the cache buffer.*/
 +			return (1);
 +		default:
 +			break;
 +		}
 +		if (strm->avail_in < 2) {
 +			/* There is not enough compressed data to
 +			 * fill up the cache buffer. */
 +			if (strm->avail_in == 1) {
 +				br->odd = *strm->next_in++;
 +				strm->avail_in--;
 +				br->have_odd = 1;
 +			}
 +			return (0);
 +		}
 +		br->cache_buffer =
 +		   (br->cache_buffer << 16) |
 +		    archive_le16dec(strm->next_in);
 +		strm->next_in += 2;
 +		strm->avail_in -= 2;
 +		br->cache_avail += 16;
 +		n -= 16;
 +	}
 +}
 +
 +static void
 +lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br)
 +{
 +	int n = CACHE_BITS - br->cache_avail;
 +
 +	if (br->have_odd && n >= 16 && strm->avail_in > 0) {
 +		br->cache_buffer =
 +		   (br->cache_buffer << 16) |
 +		   ((uint16_t)(*strm->next_in)) << 8 | br->odd;
 +		strm->next_in++;
 +		strm->avail_in--;
 +		br->cache_avail += 16;
 +		br->have_odd = 0;
 +	}
 +}
 +
 +static void
 +lzx_cleanup_bitstream(struct lzx_stream *strm)
 +{
 +	strm->ds->br.cache_avail = 0;
 +	strm->ds->br.have_odd = 0;
 +}
 +
 +/*
 + * Decode LZX.
 + *
 + * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty.
 + *    Please set available buffer and call this function again.
 + * 2. Returns ARCHIVE_EOF if decompression has been completed.
 + * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
 + *    is broken or you do not set 'last' flag properly.
 + */
 +#define ST_RD_TRANSLATION	0
 +#define ST_RD_TRANSLATION_SIZE	1
 +#define ST_RD_BLOCK_TYPE	2
 +#define ST_RD_BLOCK_SIZE	3
 +#define ST_RD_ALIGNMENT		4
 +#define ST_RD_R0		5
 +#define ST_RD_R1		6
 +#define ST_RD_R2		7
 +#define ST_COPY_UNCOMP1		8
 +#define ST_COPY_UNCOMP2		9
 +#define ST_RD_ALIGNED_OFFSET	10
 +#define ST_RD_VERBATIM		11
 +#define ST_RD_PRE_MAIN_TREE_256	12
 +#define ST_MAIN_TREE_256	13
 +#define ST_RD_PRE_MAIN_TREE_REM	14
 +#define ST_MAIN_TREE_REM	15
 +#define ST_RD_PRE_LENGTH_TREE	16
 +#define ST_LENGTH_TREE		17
 +#define ST_MAIN			18
 +#define ST_LENGTH		19
 +#define ST_OFFSET		20
 +#define ST_REAL_POS		21
 +#define ST_COPY			22
 +
 +static int
 +lzx_decode(struct lzx_stream *strm, int last)
 +{
 +	struct lzx_dec *ds = strm->ds;
 +	int64_t avail_in;
 +	int r;
 +
 +	if (ds->error)
 +		return (ds->error);
 +
 +	avail_in = strm->avail_in;
 +	lzx_br_fixup(strm, &(ds->br));
 +	do {
 +		if (ds->state < ST_MAIN)
 +			r = lzx_read_blocks(strm, last);
 +		else {
 +			int64_t bytes_written = strm->avail_out;
 +			r = lzx_decode_blocks(strm, last);
 +			bytes_written -= strm->avail_out;
 +			strm->next_out += bytes_written;
 +			strm->total_out += bytes_written;
 +		}
 +	} while (r == 100);
 +	strm->total_in += avail_in - strm->avail_in;
 +	return (r);
 +}
 +
 +static int
 +lzx_read_blocks(struct lzx_stream *strm, int last)
 +{
 +	struct lzx_dec *ds = strm->ds;
 +	struct lzx_br *br = &(ds->br);
 +	int i, r;
 +
 +	for (;;) {
 +		switch (ds->state) {
 +		case ST_RD_TRANSLATION:
 +			if (!lzx_br_read_ahead(strm, br, 1)) {
 +				ds->state = ST_RD_TRANSLATION;
 +				if (last)
 +					goto failed;
 +				return (ARCHIVE_OK);
 +			}
 +			ds->translation = lzx_br_bits(br, 1);
 +			lzx_br_consume(br, 1);
 +			/* FALL THROUGH */
 +		case ST_RD_TRANSLATION_SIZE:
 +			if (ds->translation) {
 +				if (!lzx_br_read_ahead(strm, br, 32)) {
 +					ds->state = ST_RD_TRANSLATION_SIZE;
 +					if (last)
 +						goto failed;
 +					return (ARCHIVE_OK);
 +				}
 +				ds->translation_size = lzx_br_bits(br, 16);
 +				lzx_br_consume(br, 16);
 +				ds->translation_size <<= 16;
 +				ds->translation_size |= lzx_br_bits(br, 16);
 +				lzx_br_consume(br, 16);
 +			}
 +			/* FALL THROUGH */
 +		case ST_RD_BLOCK_TYPE:
 +			if (!lzx_br_read_ahead(strm, br, 3)) {
 +				ds->state = ST_RD_BLOCK_TYPE;
 +				if (last)
 +					goto failed;
 +				return (ARCHIVE_OK);
 +			}
 +			ds->block_type = lzx_br_bits(br, 3);
 +			lzx_br_consume(br, 3);
 +			/* Check a block type. */
 +			switch (ds->block_type) {
 +			case VERBATIM_BLOCK:
 +			case ALIGNED_OFFSET_BLOCK:
 +			case UNCOMPRESSED_BLOCK:
 +				break;
 +			default:
 +				goto failed;/* Invalid */
 +			}
 +			/* FALL THROUGH */
 +		case ST_RD_BLOCK_SIZE:
 +			if (!lzx_br_read_ahead(strm, br, 24)) {
 +				ds->state = ST_RD_BLOCK_SIZE;
 +				if (last)
 +					goto failed;
 +				return (ARCHIVE_OK);
 +			}
 +			ds->block_size = lzx_br_bits(br, 8);
 +			lzx_br_consume(br, 8);
 +			ds->block_size <<= 16;
 +			ds->block_size |= lzx_br_bits(br, 16);
 +			lzx_br_consume(br, 16);
 +			if (ds->block_size == 0)
 +				goto failed;
 +			ds->block_bytes_avail = ds->block_size;
 +			if (ds->block_type != UNCOMPRESSED_BLOCK) {
 +				if (ds->block_type == VERBATIM_BLOCK)
 +					ds->state = ST_RD_VERBATIM;
 +				else
 +					ds->state = ST_RD_ALIGNED_OFFSET;
 +				break;
 +			}
 +			/* FALL THROUGH */
 +		case ST_RD_ALIGNMENT:
 +			/*
 +			 * Handle an Uncompressed Block.
 +			 */
 +			/* Skip padding to align following field on
 +			 * 16-bit boundary. */
 +			if (lzx_br_is_unaligned(br))
 +				lzx_br_consume_unaligned_bits(br);
 +			else {
 +				if (lzx_br_read_ahead(strm, br, 16))
 +					lzx_br_consume(br, 16);
 +				else {
 +					ds->state = ST_RD_ALIGNMENT;
 +					if (last)
 +						goto failed;
 +					return (ARCHIVE_OK);
 +				}
 +			}
 +			/* Preparation to read repeated offsets R0,R1 and R2. */
 +			ds->rbytes_avail = 0;
 +			ds->state = ST_RD_R0;
 +			/* FALL THROUGH */
 +		case ST_RD_R0:
 +		case ST_RD_R1:
 +		case ST_RD_R2:
 +			do {
 +				uint16_t u16;
 +				/* Drain bits in the cache buffer of
 +				 * bit-stream. */
 +				if (lzx_br_has(br, 32)) {
 +					u16 = lzx_br_bits(br, 16);
 +					lzx_br_consume(br, 16);
 +					archive_le16enc(ds->rbytes, u16);
 +					u16 = lzx_br_bits(br, 16);
 +					lzx_br_consume(br, 16);
 +					archive_le16enc(ds->rbytes+2, u16);
 +					ds->rbytes_avail = 4;
 +				} else if (lzx_br_has(br, 16)) {
 +					u16 = lzx_br_bits(br, 16);
 +					lzx_br_consume(br, 16);
 +					archive_le16enc(ds->rbytes, u16);
 +					ds->rbytes_avail = 2;
 +				}
 +				if (ds->rbytes_avail < 4 && ds->br.have_odd) {
 +					ds->rbytes[ds->rbytes_avail++] =
 +					    ds->br.odd;
 +					ds->br.have_odd = 0;
 +				}
 +				while (ds->rbytes_avail < 4) {
 +					if (strm->avail_in <= 0) {
 +						if (last)
 +							goto failed;
 +						return (ARCHIVE_OK);
 +					}
 +					ds->rbytes[ds->rbytes_avail++] =
 +					    *strm->next_in++;
 +					strm->avail_in--;
 +				}
 +				ds->rbytes_avail = 0;
 +				if (ds->state == ST_RD_R0) {
 +					ds->r0 = archive_le32dec(ds->rbytes);
 +					if (ds->r0 < 0)
 +						goto failed;
 +					ds->state = ST_RD_R1;
 +				} else if (ds->state == ST_RD_R1) {
 +					ds->r1 = archive_le32dec(ds->rbytes);
 +					if (ds->r1 < 0)
 +						goto failed;
 +					ds->state = ST_RD_R2;
 +				} else if (ds->state == ST_RD_R2) {
 +					ds->r2 = archive_le32dec(ds->rbytes);
 +					if (ds->r2 < 0)
 +						goto failed;
 +					/* We've gotten all repeated offsets. */
 +					ds->state = ST_COPY_UNCOMP1;
 +				}
 +			} while (ds->state != ST_COPY_UNCOMP1);
 +			/* FALL THROUGH */
 +		case ST_COPY_UNCOMP1:
 +			/*
 +			 * Copy bytes form next_in to next_out directly.
 +			 */
 +			while (ds->block_bytes_avail) {
 +				int l;
 +
 +				if (strm->avail_out <= 0)
 +					/* Output buffer is empty. */
 +					return (ARCHIVE_OK);
 +				if (strm->avail_in <= 0) {
 +					/* Input buffer is empty. */
 +					if (last)
 +						goto failed;
 +					return (ARCHIVE_OK);
 +				}
 +				l = (int)ds->block_bytes_avail;
 +				if (l > ds->w_size - ds->w_pos)
 +					l = ds->w_size - ds->w_pos;
 +				if (l > strm->avail_out)
 +					l = (int)strm->avail_out;
 +				if (l > strm->avail_in)
 +					l = (int)strm->avail_in;
 +				memcpy(strm->next_out, strm->next_in, l);
 +				memcpy(&(ds->w_buff[ds->w_pos]),
 +				    strm->next_in, l);
 +				strm->next_in += l;
 +				strm->avail_in -= l;
 +				strm->next_out += l;
 +				strm->avail_out -= l;
 +				strm->total_out += l;
 +				ds->w_pos = (ds->w_pos + l) & ds->w_mask;
 +				ds->block_bytes_avail -= l;
 +			}
 +			/* FALL THROUGH */
 +		case ST_COPY_UNCOMP2:
 +			/* Re-align; skip padding byte. */
 +			if (ds->block_size & 1) {
 +				if (strm->avail_in <= 0) {
 +					/* Input buffer is empty. */
 +					ds->state = ST_COPY_UNCOMP2;
 +					if (last)
 +						goto failed;
 +					return (ARCHIVE_OK);
 +				}
 +				strm->next_in++;
 +				strm->avail_in --;
 +			}
 +			/* This block ended. */
 +			ds->state = ST_RD_BLOCK_TYPE;
 +			return (ARCHIVE_EOF);
 +			/********************/
 +		case ST_RD_ALIGNED_OFFSET:
 +			/*
 +			 * Read Aligned offset tree.
 +			 */
 +			if (!lzx_br_read_ahead(strm, br, 3 * ds->at.len_size)) {
 +				ds->state = ST_RD_ALIGNED_OFFSET;
 +				if (last)
 +					goto failed;
 +				return (ARCHIVE_OK);
 +			}
 +			memset(ds->at.freq, 0, sizeof(ds->at.freq));
 +			for (i = 0; i < ds->at.len_size; i++) {
 +				ds->at.bitlen[i] = lzx_br_bits(br, 3);
 +				ds->at.freq[ds->at.bitlen[i]]++;
 +				lzx_br_consume(br, 3);
 +			}
 +			if (!lzx_make_huffman_table(&ds->at))
 +				goto failed;
 +			/* FALL THROUGH */
 +		case ST_RD_VERBATIM:
 +			ds->loop = 0;
 +			/* FALL THROUGH */
 +		case ST_RD_PRE_MAIN_TREE_256:
 +			/*
 +			 * Read Pre-tree for first 256 elements of main tree.
 +			 */
 +			if (!lzx_read_pre_tree(strm)) {
 +				ds->state = ST_RD_PRE_MAIN_TREE_256;
 +				if (last)
 +					goto failed;
 +				return (ARCHIVE_OK);
 +			}
 +			if (!lzx_make_huffman_table(&ds->pt))
 +				goto failed;
 +			ds->loop = 0;
 +			/* FALL THROUGH */
 +		case ST_MAIN_TREE_256:
 +			/*
 +			 * Get path lengths of first 256 elements of main tree.
 +			 */
 +			r = lzx_read_bitlen(strm, &ds->mt, 256);
 +			if (r < 0)
 +				goto failed;
 +			else if (!r) {
 +				ds->state = ST_MAIN_TREE_256;
 +				if (last)
 +					goto failed;
 +				return (ARCHIVE_OK);
 +			}
 +			ds->loop = 0;
 +			/* FALL THROUGH */
 +		case ST_RD_PRE_MAIN_TREE_REM:
 +			/*
 +			 * Read Pre-tree for remaining elements of main tree.
 +			 */
 +			if (!lzx_read_pre_tree(strm)) {
 +				ds->state = ST_RD_PRE_MAIN_TREE_REM;
 +				if (last)
 +					goto failed;
 +				return (ARCHIVE_OK);
 +			}
 +			if (!lzx_make_huffman_table(&ds->pt))
 +				goto failed;
 +			ds->loop = 256;
 +			/* FALL THROUGH */
 +		case ST_MAIN_TREE_REM:
 +			/*
 +			 * Get path lengths of remaining elements of main tree.
 +			 */
 +			r = lzx_read_bitlen(strm, &ds->mt, -1);
 +			if (r < 0)
 +				goto failed;
 +			else if (!r) {
 +				ds->state = ST_MAIN_TREE_REM;
 +				if (last)
 +					goto failed;
 +				return (ARCHIVE_OK);
 +			}
 +			if (!lzx_make_huffman_table(&ds->mt))
 +				goto failed;
 +			ds->loop = 0;
 +			/* FALL THROUGH */
 +		case ST_RD_PRE_LENGTH_TREE:
 +			/*
 +			 * Read Pre-tree for remaining elements of main tree.
 +			 */
 +			if (!lzx_read_pre_tree(strm)) {
 +				ds->state = ST_RD_PRE_LENGTH_TREE;
 +				if (last)
 +					goto failed;
 +				return (ARCHIVE_OK);
 +			}
 +			if (!lzx_make_huffman_table(&ds->pt))
 +				goto failed;
 +			ds->loop = 0;
 +			/* FALL THROUGH */
 +		case ST_LENGTH_TREE:
 +			/*
 +			 * Get path lengths of remaining elements of main tree.
 +			 */
 +			r = lzx_read_bitlen(strm, &ds->lt, -1);
 +			if (r < 0)
 +				goto failed;
 +			else if (!r) {
 +				ds->state = ST_LENGTH_TREE;
 +				if (last)
 +					goto failed;
 +				return (ARCHIVE_OK);
 +			}
 +			if (!lzx_make_huffman_table(&ds->lt))
 +				goto failed;
 +			ds->state = ST_MAIN;
 +			return (100);
 +		}
 +	}
 +failed:
 +	return (ds->error = ARCHIVE_FAILED);
 +}
 +
 +static int
 +lzx_decode_blocks(struct lzx_stream *strm, int last)
 +{
 +	struct lzx_dec *ds = strm->ds;
 +	struct lzx_br bre = ds->br;
 +	struct huffman *at = &(ds->at), *lt = &(ds->lt), *mt = &(ds->mt);
 +	const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl;
 +	unsigned char *noutp = strm->next_out;
 +	unsigned char *endp = noutp + strm->avail_out;
 +	unsigned char *w_buff = ds->w_buff;
 +	unsigned char *at_bitlen = at->bitlen;
 +	unsigned char *lt_bitlen = lt->bitlen;
 +	unsigned char *mt_bitlen = mt->bitlen;
 +	size_t block_bytes_avail = ds->block_bytes_avail;
 +	int at_max_bits = at->max_bits;
 +	int lt_max_bits = lt->max_bits;
 +	int mt_max_bits = mt->max_bits;
 +	int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos;
 +	int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
 +	int length_header = ds->length_header;
 +	int offset_bits = ds->offset_bits;
 +	int position_slot = ds->position_slot;
 +	int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2;
 +	int state = ds->state;
 +	char block_type = ds->block_type;
 +
 +	for (;;) {
 +		switch (state) {
 +		case ST_MAIN:
 +			for (;;) {
 +				if (block_bytes_avail == 0) {
 +					/* This block ended. */
 +					ds->state = ST_RD_BLOCK_TYPE;
 +					ds->br = bre;
 +					ds->block_bytes_avail =
 +					    block_bytes_avail;
 +					ds->copy_len = copy_len;
 +					ds->copy_pos = copy_pos;
 +					ds->length_header = length_header;
 +					ds->position_slot = position_slot;
 +					ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;
 +					ds->w_pos = w_pos;
 +					strm->avail_out = endp - noutp;
 +					return (ARCHIVE_EOF);
 +				}
 +				if (noutp >= endp)
 +					/* Output buffer is empty. */
 +					goto next_data;
 +
 +				if (!lzx_br_read_ahead(strm, &bre,
 +				    mt_max_bits)) {
 +					if (!last)
 +						goto next_data;
 +					/* Remaining bits are less than
 +					 * maximum bits(mt.max_bits) but maybe
 +					 * it still remains as much as we need,
 +					 * so we should try to use it with
 +					 * dummy bits. */
 +					c = lzx_decode_huffman(mt,
 +					      lzx_br_bits_forced(
 +				 	        &bre, mt_max_bits));
 +					lzx_br_consume(&bre, mt_bitlen[c]);
 +					if (!lzx_br_has(&bre, 0))
 +						goto failed;/* Over read. */
 +				} else {
 +					c = lzx_decode_huffman(mt,
 +					      lzx_br_bits(&bre, mt_max_bits));
 +					lzx_br_consume(&bre, mt_bitlen[c]);
 +				}
 +				if (c > UCHAR_MAX)
 +					break;
 +				/*
 +				 * 'c' is exactly literal code.
 +				 */
 +				/* Save a decoded code to reference it
 +				 * afterward. */
 +				w_buff[w_pos] = c;
 +				w_pos = (w_pos + 1) & w_mask;
 +				/* Store the decoded code to output buffer. */
 +				*noutp++ = c;
 +				block_bytes_avail--;
 +			}
 +			/*
 +			 * Get a match code, its length and offset.
 +			 */
 +			c -= UCHAR_MAX + 1;
 +			length_header = c & 7;
 +			position_slot = c >> 3;
 +			/* FALL THROUGH */
 +		case ST_LENGTH:
 +			/*
 +			 * Get a length.
 +			 */
 +			if (length_header == 7) {
 +				if (!lzx_br_read_ahead(strm, &bre,
 +				    lt_max_bits)) {
 +					if (!last) {
 +						state = ST_LENGTH;
 +						goto next_data;
 +					}
 +					c = lzx_decode_huffman(lt,
 +					      lzx_br_bits_forced(
 +					        &bre, lt_max_bits));
 +					lzx_br_consume(&bre, lt_bitlen[c]);
 +					if (!lzx_br_has(&bre, 0))
 +						goto failed;/* Over read. */
 +				} else {
 +					c = lzx_decode_huffman(lt,
 +					    lzx_br_bits(&bre, lt_max_bits));
 +					lzx_br_consume(&bre, lt_bitlen[c]);
 +				}
 +				copy_len = c + 7 + 2;
 +			} else
 +				copy_len = length_header + 2;
 +			if ((size_t)copy_len > block_bytes_avail)
 +				goto failed;
 +			/*
 +			 * Get an offset.
 +			 */
 +			switch (position_slot) {
 +			case 0: /* Use repeated offset 0. */
 +				copy_pos = r0;
 +				state = ST_REAL_POS;
 +				continue;
 +			case 1: /* Use repeated offset 1. */
 +				copy_pos = r1;
 +				/* Swap repeated offset. */
 +				r1 = r0;
 +				r0 = copy_pos;
 +				state = ST_REAL_POS;
 +				continue;
 +			case 2: /* Use repeated offset 2. */
 +				copy_pos = r2;
 +				/* Swap repeated offset. */
 +				r2 = r0;
 +				r0 = copy_pos;
 +				state = ST_REAL_POS;
 +				continue;
 +			default:
 +				offset_bits =
 +				    pos_tbl[position_slot].footer_bits;
 +				break;
 +			}
 +			/* FALL THROUGH */
 +		case ST_OFFSET:
 +			/*
 +			 * Get the offset, which is a distance from
 +			 * current window position.
 +			 */
 +			if (block_type == ALIGNED_OFFSET_BLOCK &&
 +			    offset_bits >= 3) {
 +				int offbits = offset_bits - 3;
 +
 +				if (!lzx_br_read_ahead(strm, &bre, offbits)) {
 +					state = ST_OFFSET;
 +					if (last)
 +						goto failed;
 +					goto next_data;
 +				}
 +				copy_pos = lzx_br_bits(&bre, offbits) << 3;
 +
 +				/* Get an aligned number. */
 +				if (!lzx_br_read_ahead(strm, &bre,
 +				    offbits + at_max_bits)) {
 +					if (!last) {
 +						state = ST_OFFSET;
 +						goto next_data;
 +					}
 +					lzx_br_consume(&bre, offbits);
 +					c = lzx_decode_huffman(at,
 +					      lzx_br_bits_forced(&bre,
 +					        at_max_bits));
 +					lzx_br_consume(&bre, at_bitlen[c]);
 +					if (!lzx_br_has(&bre, 0))
 +						goto failed;/* Over read. */
 +				} else {
 +					lzx_br_consume(&bre, offbits);
 +					c = lzx_decode_huffman(at,
 +					      lzx_br_bits(&bre, at_max_bits));
 +					lzx_br_consume(&bre, at_bitlen[c]);
 +				}
 +				/* Add an aligned number. */
 +				copy_pos += c;
 +			} else {
 +				if (!lzx_br_read_ahead(strm, &bre,
 +				    offset_bits)) {
 +					state = ST_OFFSET;
 +					if (last)
 +						goto failed;
 +					goto next_data;
 +				}
 +				copy_pos = lzx_br_bits(&bre, offset_bits);
 +				lzx_br_consume(&bre, offset_bits);
 +			}
 +			copy_pos += pos_tbl[position_slot].base -2;
 +
 +			/* Update repeated offset LRU queue. */
 +			r2 = r1;
 +			r1 = r0;
 +			r0 = copy_pos;
 +			/* FALL THROUGH */
 +		case ST_REAL_POS:
 +			/*
 +			 * Compute a real position in window.
 +			 */
 +			copy_pos = (w_pos - copy_pos) & w_mask;
 +			/* FALL THROUGH */
 +		case ST_COPY:
 +			/*
 +			 * Copy several bytes as extracted data from the window
 +			 * into the output buffer.
 +			 */
 +			for (;;) {
 +				const unsigned char *s;
 +				int l;
 +
 +				l = copy_len;
 +				if (copy_pos > w_pos) {
 +					if (l > w_size - copy_pos)
 +						l = w_size - copy_pos;
 +				} else {
 +					if (l > w_size - w_pos)
 +						l = w_size - w_pos;
 +				}
 +				if (noutp + l >= endp)
 +					l = (int)(endp - noutp);
 +				s = w_buff + copy_pos;
 +				if (l >= 8 && ((copy_pos + l < w_pos)
 +				  || (w_pos + l < copy_pos))) {
 +					memcpy(w_buff + w_pos, s, l);
 +					memcpy(noutp, s, l);
 +				} else {
 +					unsigned char *d;
 +					int li;
 +
 +					d = w_buff + w_pos;
 +					for (li = 0; li < l; li++)
 +						noutp[li] = d[li] = s[li];
 +				}
 +				noutp += l;
 +				copy_pos = (copy_pos + l) & w_mask;
 +				w_pos = (w_pos + l) & w_mask;
 +				block_bytes_avail -= l;
 +				if (copy_len <= l)
 +					/* A copy of current pattern ended. */
 +					break;
 +				copy_len -= l;
 +				if (noutp >= endp) {
 +					/* Output buffer is empty. */
 +					state = ST_COPY;
 +					goto next_data;
 +				}
 +			}
 +			state = ST_MAIN;
 +			break;
 +		}
 +	}
 +failed:
 +	return (ds->error = ARCHIVE_FAILED);
 +next_data:
 +	ds->br = bre;
 +	ds->block_bytes_avail = block_bytes_avail;
 +	ds->copy_len = copy_len;
 +	ds->copy_pos = copy_pos;
 +	ds->length_header = length_header;
 +	ds->offset_bits = offset_bits;
 +	ds->position_slot = position_slot;
 +	ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;
 +	ds->state = state;
 +	ds->w_pos = w_pos;
 +	strm->avail_out = endp - noutp;
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +lzx_read_pre_tree(struct lzx_stream *strm)
 +{
 +	struct lzx_dec *ds = strm->ds;
 +	struct lzx_br *br = &(ds->br);
 +	int i;
 +
 +	if (ds->loop == 0)
 +		memset(ds->pt.freq, 0, sizeof(ds->pt.freq));
 +	for (i = ds->loop; i < ds->pt.len_size; i++) {
 +		if (!lzx_br_read_ahead(strm, br, 4)) {
 +			ds->loop = i;
 +			return (0);
 +		}
 +		ds->pt.bitlen[i] = lzx_br_bits(br, 4);
 +		ds->pt.freq[ds->pt.bitlen[i]]++;
 +		lzx_br_consume(br, 4);
 +	}
 +	ds->loop = i;
 +	return (1);
 +}
 +
 +/*
 + * Read a bunch of bit-lengths from pre-tree.
 + */
 +static int
 +lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end)
 +{
 +	struct lzx_dec *ds = strm->ds;
 +	struct lzx_br *br = &(ds->br);
 +	int c, i, j, ret, same;
 +	unsigned rbits;
 +
 +	i = ds->loop;
 +	if (i == 0)
 +		memset(d->freq, 0, sizeof(d->freq));
 +	ret = 0;
 +	if (end < 0)
 +		end = d->len_size;
 +	while (i < end) {
 +		ds->loop = i;
 +		if (!lzx_br_read_ahead(strm, br, ds->pt.max_bits))
 +			goto getdata;
 +		rbits = lzx_br_bits(br, ds->pt.max_bits);
 +		c = lzx_decode_huffman(&(ds->pt), rbits);
 +		switch (c) {
 +		case 17:/* several zero lengths, from 4 to 19. */
 +			if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+4))
 +				goto getdata;
 +			lzx_br_consume(br, ds->pt.bitlen[c]);
 +			same = lzx_br_bits(br, 4) + 4;
 +			if (i + same > end)
 +				return (-1);/* Invalid */
 +			lzx_br_consume(br, 4);
 +			for (j = 0; j < same; j++)
 +				d->bitlen[i++] = 0;
 +			break;
 +		case 18:/* many zero lengths, from 20 to 51. */
 +			if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+5))
 +				goto getdata;
 +			lzx_br_consume(br, ds->pt.bitlen[c]);
 +			same = lzx_br_bits(br, 5) + 20;
 +			if (i + same > end)
 +				return (-1);/* Invalid */
 +			lzx_br_consume(br, 5);
 +			memset(d->bitlen + i, 0, same);
 +			i += same;
 +			break;
 +		case 19:/* a few same lengths. */
 +			if (!lzx_br_read_ahead(strm, br,
 +			    ds->pt.bitlen[c]+1+ds->pt.max_bits))
 +				goto getdata;
 +			lzx_br_consume(br, ds->pt.bitlen[c]);
 +			same = lzx_br_bits(br, 1) + 4;
 +			if (i + same > end)
 +				return (-1);
 +			lzx_br_consume(br, 1);
 +			rbits = lzx_br_bits(br, ds->pt.max_bits);
 +			c = lzx_decode_huffman(&(ds->pt), rbits);
 +			lzx_br_consume(br, ds->pt.bitlen[c]);
 +			c = (d->bitlen[i] - c + 17) % 17;
 +			if (c < 0)
 +				return (-1);/* Invalid */
 +			for (j = 0; j < same; j++)
 +				d->bitlen[i++] = c;
 +			d->freq[c] += same;
 +			break;
 +		default:
 +			lzx_br_consume(br, ds->pt.bitlen[c]);
 +			c = (d->bitlen[i] - c + 17) % 17;
 +			if (c < 0)
 +				return (-1);/* Invalid */
 +			d->freq[c]++;
 +			d->bitlen[i++] = c;
 +			break;
 +		}
 +	}
 +	ret = 1;
 +getdata:
 +	ds->loop = i;
 +	return (ret);
 +}
 +
 +static int
 +lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
 +{
- 	int 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) {
- 		if (tbl_bits < HTBL_BITS)
- 			bits = tbl_bits;
- 		else
- 			bits = HTBL_BITS;
- 		hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0]));
++		hf->tbl = malloc(((size_t)1 << tbl_bits) * sizeof(hf->tbl[0]));
 +		if (hf->tbl == NULL)
 +			return (ARCHIVE_FATAL);
 +		hf->tbl_bits = tbl_bits;
 +	}
- 	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);
- 	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static void
 +lzx_huffman_free(struct huffman *hf)
 +{
 +	free(hf->bitlen);
 +	free(hf->tbl);
- 	free(hf->tree);
 +}
 +
 +/*
 + * 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 diffbits, len_avail;
++	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;
 +		}
 +	}
- 	if (maxbits > HTBL_BITS) {
- 		int 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_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;
- 		uint16_t bit;
- 		int extlen;
- 		struct htree_t *ht;
 +
 +		if (bitlen[i] == 0)
 +			continue;
 +		/* Get a bit pattern */
 +		len = bitlen[i];
++		if (len > tbl_size)
++			return (0);
 +		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]);
- 			while (--cnt >= 0)
- 				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;
- 		}
++		/* 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 int
- lzx_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_size) {
- 		c -= hf->len_size;
- 		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
 +lzx_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];
++	c = hf->tbl[rbits];
 +	if (c < hf->len_size)
 +		return (c);
- 	/* This bit pattern needs to be found out at a huffman tree. */
- 	return (lzx_decode_huffman_tree(hf, rbits, c));
++	return (0);
 +}
- 
diff --cc Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c
index 3541330,0000000..4b436d1
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c
@@@ -1,3265 -1,0 +1,3266 @@@
 +/*-
 + * Copyright (c) 2003-2007 Tim Kientzle
 + * Copyright (c) 2009 Andreas Henriksson <andreas at fatal.se>
 + * Copyright (c) 2009-2012 Michihiro NAKAJIMA
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + */
 +
 +#include "archive_platform.h"
 +__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_iso9660.c 201246 2009-12-30 05:30:35Z kientzle $");
 +
 +#ifdef HAVE_ERRNO_H
 +#include <errno.h>
 +#endif
 +/* #include <stdint.h> */ /* See archive_platform.h */
 +#include <stdio.h>
 +#ifdef HAVE_STDLIB_H
 +#include <stdlib.h>
 +#endif
 +#ifdef HAVE_STRING_H
 +#include <string.h>
 +#endif
 +#include <time.h>
 +#ifdef HAVE_ZLIB_H
 +#include <cm_zlib.h>
 +#endif
 +
 +#include "archive.h"
 +#include "archive_endian.h"
 +#include "archive_entry.h"
 +#include "archive_entry_locale.h"
 +#include "archive_private.h"
 +#include "archive_read_private.h"
 +#include "archive_string.h"
 +
 +/*
 + * An overview of ISO 9660 format:
 + *
 + * Each disk is laid out as follows:
 + *   * 32k reserved for private use
 + *   * Volume descriptor table.  Each volume descriptor
 + *     is 2k and specifies basic format information.
 + *     The "Primary Volume Descriptor" (PVD) is defined by the
 + *     standard and should always be present; other volume
 + *     descriptors include various vendor-specific extensions.
 + *   * Files and directories.  Each file/dir is specified by
 + *     an "extent" (starting sector and length in bytes).
 + *     Dirs are just files with directory records packed one
 + *     after another.  The PVD contains a single dir entry
 + *     specifying the location of the root directory.  Everything
 + *     else follows from there.
 + *
 + * This module works by first reading the volume descriptors, then
 + * building a list of directory entries, sorted by starting
 + * sector.  At each step, I look for the earliest dir entry that
 + * hasn't yet been read, seek forward to that location and read
 + * that entry.  If it's a dir, I slurp in the new dir entries and
 + * add them to the heap; if it's a regular file, I return the
 + * corresponding archive_entry and wait for the client to request
 + * the file body.  This strategy allows us to read most compliant
 + * CDs with a single pass through the data, as required by libarchive.
 + */
 +#define	LOGICAL_BLOCK_SIZE	2048
 +#define	SYSTEM_AREA_BLOCK	16
 +
 +/* Structure of on-disk primary volume descriptor. */
 +#define PVD_type_offset 0
 +#define PVD_type_size 1
 +#define PVD_id_offset (PVD_type_offset + PVD_type_size)
 +#define PVD_id_size 5
 +#define PVD_version_offset (PVD_id_offset + PVD_id_size)
 +#define PVD_version_size 1
 +#define PVD_reserved1_offset (PVD_version_offset + PVD_version_size)
 +#define PVD_reserved1_size 1
 +#define PVD_system_id_offset (PVD_reserved1_offset + PVD_reserved1_size)
 +#define PVD_system_id_size 32
 +#define PVD_volume_id_offset (PVD_system_id_offset + PVD_system_id_size)
 +#define PVD_volume_id_size 32
 +#define PVD_reserved2_offset (PVD_volume_id_offset + PVD_volume_id_size)
 +#define PVD_reserved2_size 8
 +#define PVD_volume_space_size_offset (PVD_reserved2_offset + PVD_reserved2_size)
 +#define PVD_volume_space_size_size 8
 +#define PVD_reserved3_offset (PVD_volume_space_size_offset + PVD_volume_space_size_size)
 +#define PVD_reserved3_size 32
 +#define PVD_volume_set_size_offset (PVD_reserved3_offset + PVD_reserved3_size)
 +#define PVD_volume_set_size_size 4
 +#define PVD_volume_sequence_number_offset (PVD_volume_set_size_offset + PVD_volume_set_size_size)
 +#define PVD_volume_sequence_number_size 4
 +#define PVD_logical_block_size_offset (PVD_volume_sequence_number_offset + PVD_volume_sequence_number_size)
 +#define PVD_logical_block_size_size 4
 +#define PVD_path_table_size_offset (PVD_logical_block_size_offset + PVD_logical_block_size_size)
 +#define PVD_path_table_size_size 8
 +#define PVD_type_1_path_table_offset (PVD_path_table_size_offset + PVD_path_table_size_size)
 +#define PVD_type_1_path_table_size 4
 +#define PVD_opt_type_1_path_table_offset (PVD_type_1_path_table_offset + PVD_type_1_path_table_size)
 +#define PVD_opt_type_1_path_table_size 4
 +#define PVD_type_m_path_table_offset (PVD_opt_type_1_path_table_offset + PVD_opt_type_1_path_table_size)
 +#define PVD_type_m_path_table_size 4
 +#define PVD_opt_type_m_path_table_offset (PVD_type_m_path_table_offset + PVD_type_m_path_table_size)
 +#define PVD_opt_type_m_path_table_size 4
 +#define PVD_root_directory_record_offset (PVD_opt_type_m_path_table_offset + PVD_opt_type_m_path_table_size)
 +#define PVD_root_directory_record_size 34
 +#define PVD_volume_set_id_offset (PVD_root_directory_record_offset + PVD_root_directory_record_size)
 +#define PVD_volume_set_id_size 128
 +#define PVD_publisher_id_offset (PVD_volume_set_id_offset + PVD_volume_set_id_size)
 +#define PVD_publisher_id_size 128
 +#define PVD_preparer_id_offset (PVD_publisher_id_offset + PVD_publisher_id_size)
 +#define PVD_preparer_id_size 128
 +#define PVD_application_id_offset (PVD_preparer_id_offset + PVD_preparer_id_size)
 +#define PVD_application_id_size 128
 +#define PVD_copyright_file_id_offset (PVD_application_id_offset + PVD_application_id_size)
 +#define PVD_copyright_file_id_size 37
 +#define PVD_abstract_file_id_offset (PVD_copyright_file_id_offset + PVD_copyright_file_id_size)
 +#define PVD_abstract_file_id_size 37
 +#define PVD_bibliographic_file_id_offset (PVD_abstract_file_id_offset + PVD_abstract_file_id_size)
 +#define PVD_bibliographic_file_id_size 37
 +#define PVD_creation_date_offset (PVD_bibliographic_file_id_offset + PVD_bibliographic_file_id_size)
 +#define PVD_creation_date_size 17
 +#define PVD_modification_date_offset (PVD_creation_date_offset + PVD_creation_date_size)
 +#define PVD_modification_date_size 17
 +#define PVD_expiration_date_offset (PVD_modification_date_offset + PVD_modification_date_size)
 +#define PVD_expiration_date_size 17
 +#define PVD_effective_date_offset (PVD_expiration_date_offset + PVD_expiration_date_size)
 +#define PVD_effective_date_size 17
 +#define PVD_file_structure_version_offset (PVD_effective_date_offset + PVD_effective_date_size)
 +#define PVD_file_structure_version_size 1
 +#define PVD_reserved4_offset (PVD_file_structure_version_offset + PVD_file_structure_version_size)
 +#define PVD_reserved4_size 1
 +#define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size)
 +#define PVD_application_data_size 512
 +#define PVD_reserved5_offset (PVD_application_data_offset + PVD_application_data_size)
 +#define PVD_reserved5_size (2048 - PVD_reserved5_offset)
 +
 +/* TODO: It would make future maintenance easier to just hardcode the
 + * above values.  In particular, ECMA119 states the offsets as part of
 + * the standard.  That would eliminate the need for the following check.*/
 +#if PVD_reserved5_offset != 1395
 +#error PVD offset and size definitions are wrong.
 +#endif
 +
 +
 +/* Structure of optional on-disk supplementary volume descriptor. */
 +#define SVD_type_offset 0
 +#define SVD_type_size 1
 +#define SVD_id_offset (SVD_type_offset + SVD_type_size)
 +#define SVD_id_size 5
 +#define SVD_version_offset (SVD_id_offset + SVD_id_size)
 +#define SVD_version_size 1
 +/* ... */
 +#define SVD_reserved1_offset	72
 +#define SVD_reserved1_size	8
 +#define SVD_volume_space_size_offset 80
 +#define SVD_volume_space_size_size 8
 +#define SVD_escape_sequences_offset (SVD_volume_space_size_offset + SVD_volume_space_size_size)
 +#define SVD_escape_sequences_size 32
 +/* ... */
 +#define SVD_logical_block_size_offset 128
 +#define SVD_logical_block_size_size 4
 +#define SVD_type_L_path_table_offset 140
 +#define SVD_type_M_path_table_offset 148
 +/* ... */
 +#define SVD_root_directory_record_offset 156
 +#define SVD_root_directory_record_size 34
 +#define SVD_file_structure_version_offset 881
 +#define SVD_reserved2_offset	882
 +#define SVD_reserved2_size	1
 +#define SVD_reserved3_offset	1395
 +#define SVD_reserved3_size	653
 +/* ... */
 +/* FIXME: validate correctness of last SVD entry offset. */
 +
 +/* Structure of an on-disk directory record. */
 +/* Note:  ISO9660 stores each multi-byte integer twice, once in
 + * each byte order.  The sizes here are the size of just one
 + * of the two integers.  (This is why the offset of a field isn't
 + * the same as the offset+size of the previous field.) */
 +#define DR_length_offset 0
 +#define DR_length_size 1
 +#define DR_ext_attr_length_offset 1
 +#define DR_ext_attr_length_size 1
 +#define DR_extent_offset 2
 +#define DR_extent_size 4
 +#define DR_size_offset 10
 +#define DR_size_size 4
 +#define DR_date_offset 18
 +#define DR_date_size 7
 +#define DR_flags_offset 25
 +#define DR_flags_size 1
 +#define DR_file_unit_size_offset 26
 +#define DR_file_unit_size_size 1
 +#define DR_interleave_offset 27
 +#define DR_interleave_size 1
 +#define DR_volume_sequence_number_offset 28
 +#define DR_volume_sequence_number_size 2
 +#define DR_name_len_offset 32
 +#define DR_name_len_size 1
 +#define DR_name_offset 33
 +
 +#ifdef HAVE_ZLIB_H
 +static const unsigned char zisofs_magic[8] = {
 +	0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07
 +};
 +
 +struct zisofs {
 +	/* Set 1 if this file compressed by paged zlib */
 +	int		 pz;
 +	int		 pz_log2_bs; /* Log2 of block size */
 +	uint64_t	 pz_uncompressed_size;
 +
 +	int		 initialized;
 +	unsigned char	*uncompressed_buffer;
 +	size_t		 uncompressed_buffer_size;
 +
 +	uint32_t	 pz_offset;
 +	unsigned char	 header[16];
 +	size_t		 header_avail;
 +	int		 header_passed;
 +	unsigned char	*block_pointers;
 +	size_t		 block_pointers_alloc;
 +	size_t		 block_pointers_size;
 +	size_t		 block_pointers_avail;
 +	size_t		 block_off;
 +	uint32_t	 block_avail;
 +
 +	z_stream	 stream;
 +	int		 stream_valid;
 +};
 +#else
 +struct zisofs {
 +	/* Set 1 if this file compressed by paged zlib */
 +	int		 pz;
 +};
 +#endif
 +
 +struct content {
 +	uint64_t	 offset;/* Offset on disk.		*/
 +	uint64_t	 size;	/* File size in bytes.		*/
 +	struct content	*next;
 +};
 +
 +/* In-memory storage for a directory record. */
 +struct file_info {
 +	struct file_info	*use_next;
 +	struct file_info	*parent;
 +	struct file_info	*next;
 +	struct file_info	*re_next;
 +	int		 subdirs;
 +	uint64_t	 key;		/* Heap Key.			*/
 +	uint64_t	 offset;	/* Offset on disk.		*/
 +	uint64_t	 size;		/* File size in bytes.		*/
 +	uint32_t	 ce_offset;	/* Offset of CE.		*/
 +	uint32_t	 ce_size;	/* Size of CE.			*/
 +	char		 rr_moved;	/* Flag to rr_moved.		*/
 +	char		 rr_moved_has_re_only;
 +	char		 re;		/* Having RRIP "RE" extension.	*/
 +	char		 re_descendant;
 +	uint64_t	 cl_offset;	/* Having RRIP "CL" extension.	*/
 +	int		 birthtime_is_set;
 +	time_t		 birthtime;	/* File created time.		*/
 +	time_t		 mtime;		/* File last modified time.	*/
 +	time_t		 atime;		/* File last accessed time.	*/
 +	time_t		 ctime;		/* File attribute change time.	*/
 +	uint64_t	 rdev;		/* Device number.		*/
 +	mode_t		 mode;
 +	uid_t		 uid;
 +	gid_t		 gid;
 +	int64_t		 number;
 +	int		 nlinks;
 +	struct archive_string name; /* Pathname */
 +	unsigned char	*utf16be_name;
 +	size_t		 utf16be_bytes;
 +	char		 name_continues; /* Non-zero if name continues */
 +	struct archive_string symlink;
 +	char		 symlink_continues; /* Non-zero if link continues */
 +	/* Set 1 if this file compressed by paged zlib(zisofs) */
 +	int		 pz;
 +	int		 pz_log2_bs; /* Log2 of block size */
 +	uint64_t	 pz_uncompressed_size;
 +	/* Set 1 if this file is multi extent. */
 +	int		 multi_extent;
 +	struct {
 +		struct content	*first;
 +		struct content	**last;
 +	} contents;
 +	struct {
 +		struct file_info	*first;
 +		struct file_info	**last;
 +	} rede_files;
 +};
 +
 +struct heap_queue {
 +	struct file_info **files;
 +	int		 allocated;
 +	int		 used;
 +};
 +
 +struct iso9660 {
 +	int	magic;
 +#define ISO9660_MAGIC   0x96609660
 +
 +	int opt_support_joliet;
 +	int opt_support_rockridge;
 +
 +	struct archive_string pathname;
 +	char	seenRockridge;	/* Set true if RR extensions are used. */
 +	char	seenSUSP;	/* Set true if SUSP is being used. */
 +	char	seenJoliet;
 +
 +	unsigned char	suspOffset;
 +	struct file_info *rr_moved;
 +	struct read_ce_queue {
 +		struct read_ce_req {
 +			uint64_t	 offset;/* Offset of CE on disk. */
 +			struct file_info *file;
 +		}		*reqs;
 +		int		 cnt;
 +		int		 allocated;
 +	}	read_ce_req;
 +
 +	int64_t		previous_number;
 +	struct archive_string previous_pathname;
 +
 +	struct file_info		*use_files;
 +	struct heap_queue		 pending_files;
 +	struct {
 +		struct file_info	*first;
 +		struct file_info	**last;
 +	}	cache_files;
 +	struct {
 +		struct file_info	*first;
 +		struct file_info	**last;
 +	}	re_files;
 +
 +	uint64_t current_position;
 +	ssize_t	logical_block_size;
 +	uint64_t volume_size; /* Total size of volume in bytes. */
 +	int32_t  volume_block;/* Total size of volume in logical blocks. */
 +
 +	struct vd {
 +		int		location;	/* Location of Extent.	*/
 +		uint32_t	size;
 +	} primary, joliet;
 +
 +	int64_t	entry_sparse_offset;
 +	int64_t	entry_bytes_remaining;
 +	size_t  entry_bytes_unconsumed;
 +	struct zisofs	 entry_zisofs;
 +	struct content	*entry_content;
 +	struct archive_string_conv *sconv_utf16be;
 +	/*
 +	 * Buffers for a full pathname in UTF-16BE in Joliet extensions.
 +	 */
 +#define UTF16_NAME_MAX	1024
 +	unsigned char *utf16be_path;
 +	size_t		 utf16be_path_len;
 +	unsigned char *utf16be_previous_path;
 +	size_t		 utf16be_previous_path_len;
 +	/* Null buffer used in bidder to improve its performance. */
 +	unsigned char	 null[2048];
 +};
 +
 +static int	archive_read_format_iso9660_bid(struct archive_read *, int);
 +static int	archive_read_format_iso9660_options(struct archive_read *,
 +		    const char *, const char *);
 +static int	archive_read_format_iso9660_cleanup(struct archive_read *);
 +static int	archive_read_format_iso9660_read_data(struct archive_read *,
 +		    const void **, size_t *, int64_t *);
 +static int	archive_read_format_iso9660_read_data_skip(struct archive_read *);
 +static int	archive_read_format_iso9660_read_header(struct archive_read *,
 +		    struct archive_entry *);
 +static const char *build_pathname(struct archive_string *, struct file_info *, int);
 +static int	build_pathname_utf16be(unsigned char *, size_t, size_t *,
 +		    struct file_info *);
 +#if DEBUG
 +static void	dump_isodirrec(FILE *, const unsigned char *isodirrec);
 +#endif
 +static time_t	time_from_tm(struct tm *);
 +static time_t	isodate17(const unsigned char *);
 +static time_t	isodate7(const unsigned char *);
 +static int	isBootRecord(struct iso9660 *, const unsigned char *);
 +static int	isVolumePartition(struct iso9660 *, const unsigned char *);
 +static int	isVDSetTerminator(struct iso9660 *, const unsigned char *);
 +static int	isJolietSVD(struct iso9660 *, const unsigned char *);
 +static int	isSVD(struct iso9660 *, const unsigned char *);
 +static int	isEVD(struct iso9660 *, const unsigned char *);
 +static int	isPVD(struct iso9660 *, const unsigned char *);
 +static int	next_cache_entry(struct archive_read *, struct iso9660 *,
 +		    struct file_info **);
 +static int	next_entry_seek(struct archive_read *, struct iso9660 *,
 +		    struct file_info **);
 +static struct file_info *
 +		parse_file_info(struct archive_read *a,
 +		    struct file_info *parent, const unsigned char *isodirrec);
 +static int	parse_rockridge(struct archive_read *a,
 +		    struct file_info *file, const unsigned char *start,
 +		    const unsigned char *end);
 +static int	register_CE(struct archive_read *a, int32_t location,
 +		    struct file_info *file);
 +static int	read_CE(struct archive_read *a, struct iso9660 *iso9660);
 +static void	parse_rockridge_NM1(struct file_info *,
 +		    const unsigned char *, int);
 +static void	parse_rockridge_SL1(struct file_info *,
 +		    const unsigned char *, int);
 +static void	parse_rockridge_TF1(struct file_info *,
 +		    const unsigned char *, int);
 +static void	parse_rockridge_ZF1(struct file_info *,
 +		    const unsigned char *, int);
 +static void	register_file(struct iso9660 *, struct file_info *);
 +static void	release_files(struct iso9660 *);
 +static unsigned	toi(const void *p, int n);
 +static inline void re_add_entry(struct iso9660 *, struct file_info *);
 +static inline struct file_info * re_get_entry(struct iso9660 *);
 +static inline int rede_add_entry(struct file_info *);
 +static inline struct file_info * rede_get_entry(struct file_info *);
 +static inline void cache_add_entry(struct iso9660 *iso9660,
 +		    struct file_info *file);
 +static inline struct file_info *cache_get_entry(struct iso9660 *iso9660);
 +static int	heap_add_entry(struct archive_read *a, struct heap_queue *heap,
 +		    struct file_info *file, uint64_t key);
 +static struct file_info *heap_get_entry(struct heap_queue *heap);
 +
 +#define add_entry(arch, iso9660, file)	\
 +	heap_add_entry(arch, &((iso9660)->pending_files), file, file->offset)
 +#define next_entry(iso9660)		\
 +	heap_get_entry(&((iso9660)->pending_files))
 +
 +int
 +archive_read_support_format_iso9660(struct archive *_a)
 +{
 +	struct archive_read *a = (struct archive_read *)_a;
 +	struct iso9660 *iso9660;
 +	int r;
 +
 +	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
 +	    ARCHIVE_STATE_NEW, "archive_read_support_format_iso9660");
 +
 +	iso9660 = (struct iso9660 *)calloc(1, sizeof(*iso9660));
 +	if (iso9660 == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate iso9660 data");
 +		return (ARCHIVE_FATAL);
 +	}
 +	iso9660->magic = ISO9660_MAGIC;
 +	iso9660->cache_files.first = NULL;
 +	iso9660->cache_files.last = &(iso9660->cache_files.first);
 +	iso9660->re_files.first = NULL;
 +	iso9660->re_files.last = &(iso9660->re_files.first);
 +	/* Enable to support Joliet extensions by default.	*/
 +	iso9660->opt_support_joliet = 1;
 +	/* Enable to support Rock Ridge extensions by default.	*/
 +	iso9660->opt_support_rockridge = 1;
 +
 +	r = __archive_read_register_format(a,
 +	    iso9660,
 +	    "iso9660",
 +	    archive_read_format_iso9660_bid,
 +	    archive_read_format_iso9660_options,
 +	    archive_read_format_iso9660_read_header,
 +	    archive_read_format_iso9660_read_data,
 +	    archive_read_format_iso9660_read_data_skip,
 +	    NULL,
 +	    archive_read_format_iso9660_cleanup,
 +	    NULL,
 +	    NULL);
 +
 +	if (r != ARCHIVE_OK) {
 +		free(iso9660);
 +		return (r);
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +
 +static int
 +archive_read_format_iso9660_bid(struct archive_read *a, int best_bid)
 +{
 +	struct iso9660 *iso9660;
 +	ssize_t bytes_read;
 +	const unsigned char *p;
 +	int seenTerminator;
 +
 +	/* If there's already a better bid than we can ever
 +	   make, don't bother testing. */
 +	if (best_bid > 48)
 +		return (-1);
 +
 +	iso9660 = (struct iso9660 *)(a->format->data);
 +
 +	/*
 +	 * Skip the first 32k (reserved area) and get the first
 +	 * 8 sectors of the volume descriptor table.  Of course,
 +	 * if the I/O layer gives us more, we'll take it.
 +	 */
 +#define RESERVED_AREA	(SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE)
 +	p = __archive_read_ahead(a,
 +	    RESERVED_AREA + 8 * LOGICAL_BLOCK_SIZE,
 +	    &bytes_read);
 +	if (p == NULL)
 +	    return (-1);
 +
 +	/* Skip the reserved area. */
 +	bytes_read -= RESERVED_AREA;
 +	p += RESERVED_AREA;
 +
 +	/* Check each volume descriptor. */
 +	seenTerminator = 0;
 +	for (; bytes_read > LOGICAL_BLOCK_SIZE;
 +	    bytes_read -= LOGICAL_BLOCK_SIZE, p += LOGICAL_BLOCK_SIZE) {
 +		/* Do not handle undefined Volume Descriptor Type. */
 +		if (p[0] >= 4 && p[0] <= 254)
 +			return (0);
 +		/* Standard Identifier must be "CD001" */
 +		if (memcmp(p + 1, "CD001", 5) != 0)
 +			return (0);
 +		if (isPVD(iso9660, p))
 +			continue;
 +		if (!iso9660->joliet.location) {
 +			if (isJolietSVD(iso9660, p))
 +				continue;
 +		}
 +		if (isBootRecord(iso9660, p))
 +			continue;
 +		if (isEVD(iso9660, p))
 +			continue;
 +		if (isSVD(iso9660, p))
 +			continue;
 +		if (isVolumePartition(iso9660, p))
 +			continue;
 +		if (isVDSetTerminator(iso9660, p)) {
 +			seenTerminator = 1;
 +			break;
 +		}
 +		return (0);
 +	}
 +	/*
 +	 * ISO 9660 format must have Primary Volume Descriptor and
 +	 * Volume Descriptor Set Terminator.
 +	 */
 +	if (seenTerminator && iso9660->primary.location > 16)
 +		return (48);
 +
 +	/* We didn't find a valid PVD; return a bid of zero. */
 +	return (0);
 +}
 +
 +static int
 +archive_read_format_iso9660_options(struct archive_read *a,
 +		const char *key, const char *val)
 +{
 +	struct iso9660 *iso9660;
 +
 +	iso9660 = (struct iso9660 *)(a->format->data);
 +
 +	if (strcmp(key, "joliet") == 0) {
 +		if (val == NULL || strcmp(val, "off") == 0 ||
 +				strcmp(val, "ignore") == 0 ||
 +				strcmp(val, "disable") == 0 ||
 +				strcmp(val, "0") == 0)
 +			iso9660->opt_support_joliet = 0;
 +		else
 +			iso9660->opt_support_joliet = 1;
 +		return (ARCHIVE_OK);
 +	}
 +	if (strcmp(key, "rockridge") == 0 ||
 +	    strcmp(key, "Rockridge") == 0) {
 +		iso9660->opt_support_rockridge = val != NULL;
 +		return (ARCHIVE_OK);
 +	}
 +
 +	/* Note: The "warn" return is just to inform the options
 +	 * supervisor that we didn't handle it.  It will generate
 +	 * a suitable error if no one used this option. */
 +	return (ARCHIVE_WARN);
 +}
 +
 +static int
 +isNull(struct iso9660 *iso9660, const unsigned char *h, unsigned offset,
 +unsigned bytes)
 +{
 +
 +	while (bytes >= sizeof(iso9660->null)) {
 +		if (!memcmp(iso9660->null, h + offset, sizeof(iso9660->null)))
 +			return (0);
 +		offset += sizeof(iso9660->null);
 +		bytes -= sizeof(iso9660->null);
 +	}
 +	if (bytes)
 +		return memcmp(iso9660->null, h + offset, bytes) == 0;
 +	else
 +		return (1);
 +}
 +
 +static int
 +isBootRecord(struct iso9660 *iso9660, const unsigned char *h)
 +{
 +	(void)iso9660; /* UNUSED */
 +
 +	/* Type of the Volume Descriptor Boot Record must be 0. */
 +	if (h[0] != 0)
 +		return (0);
 +
 +	/* Volume Descriptor Version must be 1. */
 +	if (h[6] != 1)
 +		return (0);
 +
 +	return (1);
 +}
 +
 +static int
 +isVolumePartition(struct iso9660 *iso9660, const unsigned char *h)
 +{
 +	int32_t location;
 +
 +	/* Type of the Volume Partition Descriptor must be 3. */
 +	if (h[0] != 3)
 +		return (0);
 +
 +	/* Volume Descriptor Version must be 1. */
 +	if (h[6] != 1)
 +		return (0);
 +	/* Unused Field */
 +	if (h[7] != 0)
 +		return (0);
 +
 +	location = archive_le32dec(h + 72);
 +	if (location <= SYSTEM_AREA_BLOCK ||
 +	    location >= iso9660->volume_block)
 +		return (0);
 +	if ((uint32_t)location != archive_be32dec(h + 76))
 +		return (0);
 +
 +	return (1);
 +}
 +
 +static int
 +isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h)
 +{
 +	(void)iso9660; /* UNUSED */
 +
 +	/* Type of the Volume Descriptor Set Terminator must be 255. */
 +	if (h[0] != 255)
 +		return (0);
 +
 +	/* Volume Descriptor Version must be 1. */
 +	if (h[6] != 1)
 +		return (0);
 +
 +	/* Reserved field must be 0. */
 +	if (!isNull(iso9660, h, 7, 2048-7))
 +		return (0);
 +
 +	return (1);
 +}
 +
 +static int
 +isJolietSVD(struct iso9660 *iso9660, const unsigned char *h)
 +{
 +	const unsigned char *p;
 +	ssize_t logical_block_size;
 +	int32_t volume_block;
 +
 +	/* Check if current sector is a kind of Supplementary Volume
 +	 * Descriptor. */
 +	if (!isSVD(iso9660, h))
 +		return (0);
 +
 +	/* FIXME: do more validations according to joliet spec. */
 +
 +	/* check if this SVD contains joliet extension! */
 +	p = h + SVD_escape_sequences_offset;
 +	/* N.B. Joliet spec says p[1] == '\\', but.... */
 +	if (p[0] == '%' && p[1] == '/') {
 +		int level = 0;
 +
 +		if (p[2] == '@')
 +			level = 1;
 +		else if (p[2] == 'C')
 +			level = 2;
 +		else if (p[2] == 'E')
 +			level = 3;
 +		else /* not joliet */
 +			return (0);
 +
 +		iso9660->seenJoliet = level;
 +
 +	} else /* not joliet */
 +		return (0);
 +
 +	logical_block_size =
 +	    archive_le16dec(h + SVD_logical_block_size_offset);
 +	volume_block = archive_le32dec(h + SVD_volume_space_size_offset);
 +
 +	iso9660->logical_block_size = logical_block_size;
 +	iso9660->volume_block = volume_block;
 +	iso9660->volume_size = logical_block_size * (uint64_t)volume_block;
 +	/* Read Root Directory Record in Volume Descriptor. */
 +	p = h + SVD_root_directory_record_offset;
 +	iso9660->joliet.location = archive_le32dec(p + DR_extent_offset);
 +	iso9660->joliet.size = archive_le32dec(p + DR_size_offset);
 +
 +	return (48);
 +}
 +
 +static int
 +isSVD(struct iso9660 *iso9660, const unsigned char *h)
 +{
 +	const unsigned char *p;
 +	ssize_t logical_block_size;
 +	int32_t volume_block;
 +	int32_t location;
 +
 +	(void)iso9660; /* UNUSED */
 +
 +	/* Type 2 means it's a SVD. */
 +	if (h[SVD_type_offset] != 2)
 +		return (0);
 +
 +	/* Reserved field must be 0. */
 +	if (!isNull(iso9660, h, SVD_reserved1_offset, SVD_reserved1_size))
 +		return (0);
 +	if (!isNull(iso9660, h, SVD_reserved2_offset, SVD_reserved2_size))
 +		return (0);
 +	if (!isNull(iso9660, h, SVD_reserved3_offset, SVD_reserved3_size))
 +		return (0);
 +
 +	/* File structure version must be 1 for ISO9660/ECMA119. */
 +	if (h[SVD_file_structure_version_offset] != 1)
 +		return (0);
 +
 +	logical_block_size =
 +	    archive_le16dec(h + SVD_logical_block_size_offset);
 +	if (logical_block_size <= 0)
 +		return (0);
 +
 +	volume_block = archive_le32dec(h + SVD_volume_space_size_offset);
 +	if (volume_block <= SYSTEM_AREA_BLOCK+4)
 +		return (0);
 +
 +	/* Location of Occurrence of Type L Path Table must be
 +	 * available location,
 +	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
 +	location = archive_le32dec(h+SVD_type_L_path_table_offset);
 +	if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
 +		return (0);
 +
 +	/* The Type M Path Table must be at a valid location (WinISO
 +	 * and probably other programs omit this, so we allow zero)
 +	 *
 +	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
 +	location = archive_be32dec(h+SVD_type_M_path_table_offset);
 +	if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
 +	    || location >= volume_block)
 +		return (0);
 +
 +	/* Read Root Directory Record in Volume Descriptor. */
 +	p = h + SVD_root_directory_record_offset;
 +	if (p[DR_length_offset] != 34)
 +		return (0);
 +
 +	return (48);
 +}
 +
 +static int
 +isEVD(struct iso9660 *iso9660, const unsigned char *h)
 +{
 +	const unsigned char *p;
 +	ssize_t logical_block_size;
 +	int32_t volume_block;
 +	int32_t location;
 +
 +	(void)iso9660; /* UNUSED */
 +
 +	/* Type of the Enhanced Volume Descriptor must be 2. */
 +	if (h[PVD_type_offset] != 2)
 +		return (0);
 +
 +	/* EVD version must be 2. */
 +	if (h[PVD_version_offset] != 2)
 +		return (0);
 +
 +	/* Reserved field must be 0. */
 +	if (h[PVD_reserved1_offset] != 0)
 +		return (0);
 +
 +	/* Reserved field must be 0. */
 +	if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
 +		return (0);
 +
 +	/* Reserved field must be 0. */
 +	if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
 +		return (0);
 +
 +	/* Logical block size must be > 0. */
 +	/* I've looked at Ecma 119 and can't find any stronger
 +	 * restriction on this field. */
 +	logical_block_size =
 +	    archive_le16dec(h + PVD_logical_block_size_offset);
 +	if (logical_block_size <= 0)
 +		return (0);
 +
 +	volume_block =
 +	    archive_le32dec(h + PVD_volume_space_size_offset);
 +	if (volume_block <= SYSTEM_AREA_BLOCK+4)
 +		return (0);
 +
 +	/* File structure version must be 2 for ISO9660:1999. */
 +	if (h[PVD_file_structure_version_offset] != 2)
 +		return (0);
 +
 +	/* Location of Occurrence of Type L Path Table must be
 +	 * available location,
 +	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
 +	location = archive_le32dec(h+PVD_type_1_path_table_offset);
 +	if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
 +		return (0);
 +
 +	/* Location of Occurrence of Type M Path Table must be
 +	 * available location,
 +	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
 +	location = archive_be32dec(h+PVD_type_m_path_table_offset);
 +	if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
 +	    || location >= volume_block)
 +		return (0);
 +
 +	/* Reserved field must be 0. */
 +	if (!isNull(iso9660, h, PVD_reserved4_offset, PVD_reserved4_size))
 +		return (0);
 +
 +	/* Reserved field must be 0. */
 +	if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
 +		return (0);
 +
 +	/* Read Root Directory Record in Volume Descriptor. */
 +	p = h + PVD_root_directory_record_offset;
 +	if (p[DR_length_offset] != 34)
 +		return (0);
 +
 +	return (48);
 +}
 +
 +static int
 +isPVD(struct iso9660 *iso9660, const unsigned char *h)
 +{
 +	const unsigned char *p;
 +	ssize_t logical_block_size;
 +	int32_t volume_block;
 +	int32_t location;
 +	int i;
 +
 +	/* Type of the Primary Volume Descriptor must be 1. */
 +	if (h[PVD_type_offset] != 1)
 +		return (0);
 +
 +	/* PVD version must be 1. */
 +	if (h[PVD_version_offset] != 1)
 +		return (0);
 +
 +	/* Reserved field must be 0. */
 +	if (h[PVD_reserved1_offset] != 0)
 +		return (0);
 +
 +	/* Reserved field must be 0. */
 +	if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
 +		return (0);
 +
 +	/* Reserved field must be 0. */
 +	if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
 +		return (0);
 +
 +	/* Logical block size must be > 0. */
 +	/* I've looked at Ecma 119 and can't find any stronger
 +	 * restriction on this field. */
 +	logical_block_size =
 +	    archive_le16dec(h + PVD_logical_block_size_offset);
 +	if (logical_block_size <= 0)
 +		return (0);
 +
 +	volume_block = archive_le32dec(h + PVD_volume_space_size_offset);
 +	if (volume_block <= SYSTEM_AREA_BLOCK+4)
 +		return (0);
 +
 +	/* File structure version must be 1 for ISO9660/ECMA119. */
 +	if (h[PVD_file_structure_version_offset] != 1)
 +		return (0);
 +
 +	/* Location of Occurrence of Type L Path Table must be
 +	 * available location,
 +	 * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
 +	location = archive_le32dec(h+PVD_type_1_path_table_offset);
 +	if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
 +		return (0);
 +
 +	/* The Type M Path Table must also be at a valid location
 +	 * (although ECMA 119 requires a Type M Path Table, WinISO and
 +	 * probably other programs omit it, so we permit a zero here)
 +	 *
 +	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
 +	location = archive_be32dec(h+PVD_type_m_path_table_offset);
 +	if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
 +	    || location >= volume_block)
 +		return (0);
 +
 +	/* Reserved field must be 0. */
 +	/* But accept NetBSD/FreeBSD "makefs" images with 0x20 here. */
 +	for (i = 0; i < PVD_reserved4_size; ++i)
 +		if (h[PVD_reserved4_offset + i] != 0
 +		    && h[PVD_reserved4_offset + i] != 0x20)
 +			return (0);
 +
 +	/* Reserved field must be 0. */
 +	if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
 +		return (0);
 +
 +	/* XXX TODO: Check other values for sanity; reject more
 +	 * malformed PVDs. XXX */
 +
 +	/* Read Root Directory Record in Volume Descriptor. */
 +	p = h + PVD_root_directory_record_offset;
 +	if (p[DR_length_offset] != 34)
 +		return (0);
 +
 +	if (!iso9660->primary.location) {
 +		iso9660->logical_block_size = logical_block_size;
 +		iso9660->volume_block = volume_block;
 +		iso9660->volume_size =
 +		    logical_block_size * (uint64_t)volume_block;
 +		iso9660->primary.location =
 +		    archive_le32dec(p + DR_extent_offset);
 +		iso9660->primary.size = archive_le32dec(p + DR_size_offset);
 +	}
 +
 +	return (48);
 +}
 +
 +static int
 +read_children(struct archive_read *a, struct file_info *parent)
 +{
 +	struct iso9660 *iso9660;
 +	const unsigned char *b, *p;
 +	struct file_info *multi;
 +	size_t step, skip_size;
 +
 +	iso9660 = (struct iso9660 *)(a->format->data);
 +	/* flush any remaining bytes from the last round to ensure
 +	 * we're positioned */
 +	if (iso9660->entry_bytes_unconsumed) {
 +		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);
 +		iso9660->entry_bytes_unconsumed = 0;
 +	}
 +	if (iso9660->current_position > parent->offset) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Ignoring out-of-order directory (%s) %jd > %jd",
 +		    parent->name.s,
 +		    (intmax_t)iso9660->current_position,
 +		    (intmax_t)parent->offset);
 +		return (ARCHIVE_WARN);
 +	}
 +	if (parent->offset + parent->size > iso9660->volume_size) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Directory is beyond end-of-media: %s",
 +		    parent->name.s);
 +		return (ARCHIVE_WARN);
 +	}
 +	if (iso9660->current_position < parent->offset) {
 +		int64_t skipsize;
 +
 +		skipsize = parent->offset - iso9660->current_position;
 +		skipsize = __archive_read_consume(a, skipsize);
 +		if (skipsize < 0)
 +			return ((int)skipsize);
 +		iso9660->current_position = parent->offset;
 +	}
 +
 +	step = (size_t)(((parent->size + iso9660->logical_block_size -1) /
 +	    iso9660->logical_block_size) * iso9660->logical_block_size);
 +	b = __archive_read_ahead(a, step, NULL);
 +	if (b == NULL) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Failed to read full block when scanning "
 +		    "ISO9660 directory list");
 +		return (ARCHIVE_FATAL);
 +	}
 +	iso9660->current_position += step;
 +	multi = NULL;
 +	skip_size = step;
 +	while (step) {
 +		p = b;
 +		b += iso9660->logical_block_size;
 +		step -= iso9660->logical_block_size;
 +		for (; *p != 0 && p < b && p + *p <= b; p += *p) {
 +			struct file_info *child;
 +
 +			/* N.B.: these special directory identifiers
 +			 * are 8 bit "values" even on a
 +			 * Joliet CD with UCS-2 (16bit) encoding.
 +			 */
 +
 +			/* Skip '.' entry. */
 +			if (*(p + DR_name_len_offset) == 1
 +			    && *(p + DR_name_offset) == '\0')
 +				continue;
 +			/* Skip '..' entry. */
 +			if (*(p + DR_name_len_offset) == 1
 +			    && *(p + DR_name_offset) == '\001')
 +				continue;
 +			child = parse_file_info(a, parent, p);
 +			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);
 +	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);
 +		if (file == NULL)
 +			return (ARCHIVE_FATAL);
 +		iso9660->seenJoliet = seenJoliet;
 +	}
 +
 +	/* Store the root directory in the pending list. */
 +	if (add_entry(a, iso9660, file) != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +	if (iso9660->seenRockridge) {
 +		a->archive.archive_format = ARCHIVE_FORMAT_ISO9660_ROCKRIDGE;
 +		a->archive.archive_format_name =
 +		    "ISO9660 with Rockridge extensions";
 +	}
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +archive_read_format_iso9660_read_header(struct archive_read *a,
 +    struct archive_entry *entry)
 +{
 +	struct iso9660 *iso9660;
 +	struct file_info *file;
 +	int r, rd_r = ARCHIVE_OK;
 +
 +	iso9660 = (struct iso9660 *)(a->format->data);
 +
 +	if (!a->archive.archive_format) {
 +		a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
 +		a->archive.archive_format_name = "ISO9660";
 +	}
 +
 +	if (iso9660->current_position == 0) {
 +		r = choose_volume(a, iso9660);
 +		if (r != ARCHIVE_OK)
 +			return (r);
 +	}
 +
 +	file = NULL;/* Eliminate a warning. */
 +	/* Get the next entry that appears after the current offset. */
 +	r = next_entry_seek(a, iso9660, &file);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +
 +	if (iso9660->seenJoliet) {
 +		/*
 +		 * Convert UTF-16BE of a filename to local locale MBS
 +		 * and store the result into a filename field.
 +		 */
 +		if (iso9660->sconv_utf16be == NULL) {
 +			iso9660->sconv_utf16be =
 +			    archive_string_conversion_from_charset(
 +				&(a->archive), "UTF-16BE", 1);
 +			if (iso9660->sconv_utf16be == NULL)
 +				/* Couldn't allocate memory */
 +				return (ARCHIVE_FATAL);
 +		}
 +		if (iso9660->utf16be_path == NULL) {
 +			iso9660->utf16be_path = malloc(UTF16_NAME_MAX);
 +			if (iso9660->utf16be_path == NULL) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "No memory");
 +				return (ARCHIVE_FATAL);
 +			}
 +		}
 +		if (iso9660->utf16be_previous_path == NULL) {
 +			iso9660->utf16be_previous_path = malloc(UTF16_NAME_MAX);
 +			if (iso9660->utf16be_previous_path == NULL) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "No memory");
 +				return (ARCHIVE_FATAL);
 +			}
 +		}
 +
 +		iso9660->utf16be_path_len = 0;
 +		if (build_pathname_utf16be(iso9660->utf16be_path,
 +		    UTF16_NAME_MAX, &(iso9660->utf16be_path_len), file) != 0) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Pathname is too long");
 +			return (ARCHIVE_FATAL);
 +		}
 +
 +		r = archive_entry_copy_pathname_l(entry,
 +		    (const char *)iso9660->utf16be_path,
 +		    iso9660->utf16be_path_len,
 +		    iso9660->sconv_utf16be);
 +		if (r != 0) {
 +			if (errno == ENOMEM) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "No memory for Pathname");
 +				return (ARCHIVE_FATAL);
 +			}
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Pathname cannot be converted "
 +			    "from %s to current locale.",
 +			    archive_string_conversion_charset_name(
 +			      iso9660->sconv_utf16be));
 +
 +			rd_r = ARCHIVE_WARN;
 +		}
 +	} else {
 +		const char *path = build_pathname(&iso9660->pathname, file, 0);
 +		if (path == NULL) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Pathname is too long");
 +			return (ARCHIVE_FATAL);
 +		} else {
 +			archive_string_empty(&iso9660->pathname);
 +			archive_entry_set_pathname(entry, path);
 +		}
 +	}
 +
 +	iso9660->entry_bytes_remaining = file->size;
 +	/* Offset for sparse-file-aware clients. */
 +	iso9660->entry_sparse_offset = 0;
 +
 +	if (file->offset + file->size > iso9660->volume_size) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "File is beyond end-of-media: %s",
 +		    archive_entry_pathname(entry));
 +		iso9660->entry_bytes_remaining = 0;
 +		return (ARCHIVE_WARN);
 +	}
 +
 +	/* Set up the entry structure with information about this entry. */
 +	archive_entry_set_mode(entry, file->mode);
 +	archive_entry_set_uid(entry, file->uid);
 +	archive_entry_set_gid(entry, file->gid);
 +	archive_entry_set_nlink(entry, file->nlinks);
 +	if (file->birthtime_is_set)
 +		archive_entry_set_birthtime(entry, file->birthtime, 0);
 +	else
 +		archive_entry_unset_birthtime(entry);
 +	archive_entry_set_mtime(entry, file->mtime, 0);
 +	archive_entry_set_ctime(entry, file->ctime, 0);
 +	archive_entry_set_atime(entry, file->atime, 0);
 +	/* N.B.: Rock Ridge supports 64-bit device numbers. */
 +	archive_entry_set_rdev(entry, (dev_t)file->rdev);
 +	archive_entry_set_size(entry, iso9660->entry_bytes_remaining);
 +	if (file->symlink.s != NULL)
 +		archive_entry_copy_symlink(entry, file->symlink.s);
 +
 +	/* Note: If the input isn't seekable, we can't rewind to
 +	 * return the same body again, so if the next entry refers to
 +	 * the same data, we have to return it as a hardlink to the
 +	 * original entry. */
 +	if (file->number != -1 &&
 +	    file->number == iso9660->previous_number) {
 +		if (iso9660->seenJoliet) {
 +			r = archive_entry_copy_hardlink_l(entry,
 +			    (const char *)iso9660->utf16be_previous_path,
 +			    iso9660->utf16be_previous_path_len,
 +			    iso9660->sconv_utf16be);
 +			if (r != 0) {
 +				if (errno == ENOMEM) {
 +					archive_set_error(&a->archive, ENOMEM,
 +					    "No memory for Linkname");
 +					return (ARCHIVE_FATAL);
 +				}
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_FILE_FORMAT,
 +				    "Linkname cannot be converted "
 +				    "from %s to current locale.",
 +				    archive_string_conversion_charset_name(
 +				      iso9660->sconv_utf16be));
 +				rd_r = ARCHIVE_WARN;
 +			}
 +		} else
 +			archive_entry_set_hardlink(entry,
 +			    iso9660->previous_pathname.s);
 +		archive_entry_unset_size(entry);
 +		iso9660->entry_bytes_remaining = 0;
 +		return (rd_r);
 +	}
 +
 +	if ((file->mode & AE_IFMT) != AE_IFDIR &&
 +	    file->offset < iso9660->current_position) {
 +		int64_t r64;
 +
 +		r64 = __archive_read_seek(a, file->offset, SEEK_SET);
 +		if (r64 != (int64_t)file->offset) {
 +			/* We can't seek backwards to extract it, so issue
 +			 * a warning.  Note that this can only happen if
 +			 * this entry was added to the heap after we passed
 +			 * this offset, that is, only if the directory
 +			 * mentioning this entry is later than the body of
 +			 * the entry. Such layouts are very unusual; most
 +			 * ISO9660 writers lay out and record all directory
 +			 * information first, then store all file bodies. */
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Ignoring out-of-order file @%jx (%s) %jd < %jd",
 +			    (intmax_t)file->number,
 +			    iso9660->pathname.s,
 +			    (intmax_t)file->offset,
 +			    (intmax_t)iso9660->current_position);
 +			iso9660->entry_bytes_remaining = 0;
 +			return (ARCHIVE_WARN);
 +		}
 +		iso9660->current_position = (uint64_t)r64;
 +	}
 +
 +	/* Initialize zisofs variables. */
 +	iso9660->entry_zisofs.pz = file->pz;
 +	if (file->pz) {
 +#ifdef HAVE_ZLIB_H
 +		struct zisofs  *zisofs;
 +
 +		zisofs = &iso9660->entry_zisofs;
 +		zisofs->initialized = 0;
 +		zisofs->pz_log2_bs = file->pz_log2_bs;
 +		zisofs->pz_uncompressed_size = file->pz_uncompressed_size;
 +		zisofs->pz_offset = 0;
 +		zisofs->header_avail = 0;
 +		zisofs->header_passed = 0;
 +		zisofs->block_pointers_avail = 0;
 +#endif
 +		archive_entry_set_size(entry, file->pz_uncompressed_size);
 +	}
 +
 +	iso9660->previous_number = file->number;
 +	if (iso9660->seenJoliet) {
 +		memcpy(iso9660->utf16be_previous_path, iso9660->utf16be_path,
 +		    iso9660->utf16be_path_len);
 +		iso9660->utf16be_previous_path_len = iso9660->utf16be_path_len;
 +	} else
 +		archive_strcpy(
 +		    &iso9660->previous_pathname, iso9660->pathname.s);
 +
 +	/* Reset entry_bytes_remaining if the file is multi extent. */
 +	iso9660->entry_content = file->contents.first;
 +	if (iso9660->entry_content != NULL)
 +		iso9660->entry_bytes_remaining = iso9660->entry_content->size;
 +
 +	if (archive_entry_filetype(entry) == AE_IFDIR) {
 +		/* Overwrite nlinks by proper link number which is
 +		 * calculated from number of sub directories. */
 +		archive_entry_set_nlink(entry, 2 + file->subdirs);
 +		/* Directory data has been read completely. */
 +		iso9660->entry_bytes_remaining = 0;
 +	}
 +
 +	if (rd_r != ARCHIVE_OK)
 +		return (rd_r);
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +archive_read_format_iso9660_read_data_skip(struct archive_read *a)
 +{
 +	/* Because read_next_header always does an explicit skip
 +	 * to the next entry, we don't need to do anything here. */
 +	(void)a; /* UNUSED */
 +	return (ARCHIVE_OK);
 +}
 +
 +#ifdef HAVE_ZLIB_H
 +
 +static int
 +zisofs_read_data(struct archive_read *a,
 +    const void **buff, size_t *size, int64_t *offset)
 +{
 +	struct iso9660 *iso9660;
 +	struct zisofs  *zisofs;
 +	const unsigned char *p;
 +	size_t avail;
 +	ssize_t bytes_read;
 +	size_t uncompressed_size;
 +	int r;
 +
 +	iso9660 = (struct iso9660 *)(a->format->data);
 +	zisofs = &iso9660->entry_zisofs;
 +
 +	p = __archive_read_ahead(a, 1, &bytes_read);
 +	if (bytes_read <= 0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated zisofs file body");
 +		return (ARCHIVE_FATAL);
 +	}
 +	if (bytes_read > iso9660->entry_bytes_remaining)
 +		bytes_read = (ssize_t)iso9660->entry_bytes_remaining;
 +	avail = bytes_read;
 +	uncompressed_size = 0;
 +
 +	if (!zisofs->initialized) {
 +		size_t ceil, xsize;
 +
 +		/* Allocate block pointers buffer. */
 +		ceil = (size_t)((zisofs->pz_uncompressed_size +
 +			(((int64_t)1) << zisofs->pz_log2_bs) - 1)
 +			>> zisofs->pz_log2_bs);
 +		xsize = (ceil + 1) * 4;
 +		if (zisofs->block_pointers_alloc < xsize) {
 +			size_t alloc;
 +
 +			if (zisofs->block_pointers != NULL)
 +				free(zisofs->block_pointers);
 +			alloc = ((xsize >> 10) + 1) << 10;
 +			zisofs->block_pointers = malloc(alloc);
 +			if (zisofs->block_pointers == NULL) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "No memory for zisofs decompression");
 +				return (ARCHIVE_FATAL);
 +			}
 +			zisofs->block_pointers_alloc = alloc;
 +		}
 +		zisofs->block_pointers_size = xsize;
 +
 +		/* Allocate uncompressed data buffer. */
 +		xsize = (size_t)1UL << zisofs->pz_log2_bs;
 +		if (zisofs->uncompressed_buffer_size < xsize) {
 +			if (zisofs->uncompressed_buffer != NULL)
 +				free(zisofs->uncompressed_buffer);
 +			zisofs->uncompressed_buffer = malloc(xsize);
 +			if (zisofs->uncompressed_buffer == NULL) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "No memory for zisofs decompression");
 +				return (ARCHIVE_FATAL);
 +			}
 +		}
 +		zisofs->uncompressed_buffer_size = xsize;
 +
 +		/*
 +		 * Read the file header, and check the magic code of zisofs.
 +		 */
 +		if (zisofs->header_avail < sizeof(zisofs->header)) {
 +			xsize = sizeof(zisofs->header) - zisofs->header_avail;
 +			if (avail < xsize)
 +				xsize = avail;
 +			memcpy(zisofs->header + zisofs->header_avail, p, xsize);
 +			zisofs->header_avail += xsize;
 +			avail -= xsize;
 +			p += xsize;
 +		}
 +		if (!zisofs->header_passed &&
 +		    zisofs->header_avail == sizeof(zisofs->header)) {
 +			int err = 0;
 +
 +			if (memcmp(zisofs->header, zisofs_magic,
 +			    sizeof(zisofs_magic)) != 0)
 +				err = 1;
 +			if (archive_le32dec(zisofs->header + 8)
 +			    != zisofs->pz_uncompressed_size)
 +				err = 1;
 +			if (zisofs->header[12] != 4)
 +				err = 1;
 +			if (zisofs->header[13] != zisofs->pz_log2_bs)
 +				err = 1;
 +			if (err) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_FILE_FORMAT,
 +				    "Illegal zisofs file body");
 +				return (ARCHIVE_FATAL);
 +			}
 +			zisofs->header_passed = 1;
 +		}
 +		/*
 +		 * Read block pointers.
 +		 */
 +		if (zisofs->header_passed &&
 +		    zisofs->block_pointers_avail < zisofs->block_pointers_size) {
 +			xsize = zisofs->block_pointers_size
 +			    - zisofs->block_pointers_avail;
 +			if (avail < xsize)
 +				xsize = avail;
 +			memcpy(zisofs->block_pointers
 +			    + zisofs->block_pointers_avail, p, xsize);
 +			zisofs->block_pointers_avail += xsize;
 +			avail -= xsize;
 +			p += xsize;
 +		    	if (zisofs->block_pointers_avail
 +			    == zisofs->block_pointers_size) {
 +				/* We've got all block pointers and initialize
 +				 * related variables.	*/
 +				zisofs->block_off = 0;
 +				zisofs->block_avail = 0;
 +				/* Complete a initialization */
 +				zisofs->initialized = 1;
 +			}
 +		}
 +
 +		if (!zisofs->initialized)
 +			goto next_data; /* We need more data. */
 +	}
 +
 +	/*
 +	 * Get block offsets from block pointers.
 +	 */
 +	if (zisofs->block_avail == 0) {
 +		uint32_t bst, bed;
 +
 +		if (zisofs->block_off + 4 >= zisofs->block_pointers_size) {
 +			/* There isn't a pair of offsets. */
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Illegal zisofs block pointers");
 +			return (ARCHIVE_FATAL);
 +		}
 +		bst = archive_le32dec(
 +		    zisofs->block_pointers + zisofs->block_off);
 +		if (bst != zisofs->pz_offset + (bytes_read - avail)) {
 +			/* TODO: Should we seek offset of current file
 +			 * by bst ? */
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Illegal zisofs block pointers(cannot seek)");
 +			return (ARCHIVE_FATAL);
 +		}
 +		bed = archive_le32dec(
 +		    zisofs->block_pointers + zisofs->block_off + 4);
 +		if (bed < bst) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Illegal zisofs block pointers");
 +			return (ARCHIVE_FATAL);
 +		}
 +		zisofs->block_avail = bed - bst;
 +		zisofs->block_off += 4;
 +
 +		/* Initialize compression library for new block. */
 +		if (zisofs->stream_valid)
 +			r = inflateReset(&zisofs->stream);
 +		else
 +			r = inflateInit(&zisofs->stream);
 +		if (r != Z_OK) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Can't initialize zisofs decompression.");
 +			return (ARCHIVE_FATAL);
 +		}
 +		zisofs->stream_valid = 1;
 +		zisofs->stream.total_in = 0;
 +		zisofs->stream.total_out = 0;
 +	}
 +
 +	/*
 +	 * Make uncompressed data.
 +	 */
 +	if (zisofs->block_avail == 0) {
 +		memset(zisofs->uncompressed_buffer, 0,
 +		    zisofs->uncompressed_buffer_size);
 +		uncompressed_size = zisofs->uncompressed_buffer_size;
 +	} else {
 +		zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p;
 +		if (avail > zisofs->block_avail)
 +			zisofs->stream.avail_in = zisofs->block_avail;
 +		else
 +			zisofs->stream.avail_in = (uInt)avail;
 +		zisofs->stream.next_out = zisofs->uncompressed_buffer;
 +		zisofs->stream.avail_out =
 +		    (uInt)zisofs->uncompressed_buffer_size;
 +
 +		r = inflate(&zisofs->stream, 0);
 +		switch (r) {
 +		case Z_OK: /* Decompressor made some progress.*/
 +		case Z_STREAM_END: /* Found end of stream. */
 +			break;
 +		default:
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "zisofs decompression failed (%d)", r);
 +			return (ARCHIVE_FATAL);
 +		}
 +		uncompressed_size =
 +		    zisofs->uncompressed_buffer_size - zisofs->stream.avail_out;
 +		avail -= zisofs->stream.next_in - p;
 +		zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p);
 +	}
 +next_data:
 +	bytes_read -= avail;
 +	*buff = zisofs->uncompressed_buffer;
 +	*size = uncompressed_size;
 +	*offset = iso9660->entry_sparse_offset;
 +	iso9660->entry_sparse_offset += uncompressed_size;
 +	iso9660->entry_bytes_remaining -= bytes_read;
 +	iso9660->current_position += bytes_read;
 +	zisofs->pz_offset += (uint32_t)bytes_read;
 +	iso9660->entry_bytes_unconsumed += bytes_read;
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +#else /* HAVE_ZLIB_H */
 +
 +static int
 +zisofs_read_data(struct archive_read *a,
 +    const void **buff, size_t *size, int64_t *offset)
 +{
 +
 +	(void)buff;/* UNUSED */
 +	(void)size;/* UNUSED */
 +	(void)offset;/* UNUSED */
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "zisofs is not supported on this platform.");
 +	return (ARCHIVE_FAILED);
 +}
 +
 +#endif /* HAVE_ZLIB_H */
 +
 +static int
 +archive_read_format_iso9660_read_data(struct archive_read *a,
 +    const void **buff, size_t *size, int64_t *offset)
 +{
 +	ssize_t bytes_read;
 +	struct iso9660 *iso9660;
 +
 +	iso9660 = (struct iso9660 *)(a->format->data);
 +
 +	if (iso9660->entry_bytes_unconsumed) {
 +		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);
 +		iso9660->entry_bytes_unconsumed = 0;
 +	}
 +
 +	if (iso9660->entry_bytes_remaining <= 0) {
 +		if (iso9660->entry_content != NULL)
 +			iso9660->entry_content = iso9660->entry_content->next;
 +		if (iso9660->entry_content == NULL) {
 +			*buff = NULL;
 +			*size = 0;
 +			*offset = iso9660->entry_sparse_offset;
 +			return (ARCHIVE_EOF);
 +		}
 +		/* Seek forward to the start of the entry. */
 +		if (iso9660->current_position < iso9660->entry_content->offset) {
 +			int64_t step;
 +
 +			step = iso9660->entry_content->offset -
 +			    iso9660->current_position;
 +			step = __archive_read_consume(a, step);
 +			if (step < 0)
 +				return ((int)step);
 +			iso9660->current_position =
 +			    iso9660->entry_content->offset;
 +		}
 +		if (iso9660->entry_content->offset < iso9660->current_position) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Ignoring out-of-order file (%s) %jd < %jd",
 +			    iso9660->pathname.s,
 +			    (intmax_t)iso9660->entry_content->offset,
 +			    (intmax_t)iso9660->current_position);
 +			*buff = NULL;
 +			*size = 0;
 +			*offset = iso9660->entry_sparse_offset;
 +			return (ARCHIVE_WARN);
 +		}
 +		iso9660->entry_bytes_remaining = iso9660->entry_content->size;
 +	}
 +	if (iso9660->entry_zisofs.pz)
 +		return (zisofs_read_data(a, buff, size, offset));
 +
 +	*buff = __archive_read_ahead(a, 1, &bytes_read);
 +	if (bytes_read == 0)
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Truncated input file");
 +	if (*buff == NULL)
 +		return (ARCHIVE_FATAL);
 +	if (bytes_read > iso9660->entry_bytes_remaining)
 +		bytes_read = (ssize_t)iso9660->entry_bytes_remaining;
 +	*size = bytes_read;
 +	*offset = iso9660->entry_sparse_offset;
 +	iso9660->entry_sparse_offset += bytes_read;
 +	iso9660->entry_bytes_remaining -= bytes_read;
 +	iso9660->entry_bytes_unconsumed = bytes_read;
 +	iso9660->current_position += bytes_read;
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +archive_read_format_iso9660_cleanup(struct archive_read *a)
 +{
 +	struct iso9660 *iso9660;
 +	int r = ARCHIVE_OK;
 +
 +	iso9660 = (struct iso9660 *)(a->format->data);
 +	release_files(iso9660);
 +	free(iso9660->read_ce_req.reqs);
 +	archive_string_free(&iso9660->pathname);
 +	archive_string_free(&iso9660->previous_pathname);
 +	if (iso9660->pending_files.files)
 +		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)
 +{
 +	struct iso9660 *iso9660;
 +	struct file_info *file, *filep;
 +	size_t name_len;
 +	const unsigned char *rr_start, *rr_end;
 +	const unsigned char *p;
 +	size_t dr_len;
 +	uint64_t fsize, offset;
 +	int32_t location;
 +	int flags;
 +
 +	iso9660 = (struct iso9660 *)(a->format->data);
 +
 +	dr_len = (size_t)isodirrec[DR_length_offset];
 +	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 dr_len needs at least 34. */
 +	if (dr_len < 34) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Invalid length of directory record");
 +		return (NULL);
 +	}
 +	/* Sanity check that name_len doesn't exceed dr_len. */
 +	if (dr_len - 33 < name_len || name_len == 0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Invalid length of file identifier");
 +		return (NULL);
 +	}
 +	/* Sanity check that location doesn't exceed volume block.
 +	 * Don't check lower limit of location; it's possibility
 +	 * the location has negative value when file type is symbolic
 +	 * link or file size is zero. As far as I know latest mkisofs
 +	 * do that.
 +	 */
 +	if (location > 0 &&
 +	    (location + ((fsize + iso9660->logical_block_size -1)
 +	       / iso9660->logical_block_size))
 +			> (uint32_t)iso9660->volume_block) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Invalid location of extent of file");
 +		return (NULL);
 +	}
 +	/* Sanity check that location doesn't have a negative value
 +	 * when the file is not empty. it's too large. */
 +	if (fsize != 0 && location < 0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Invalid location of extent of file");
 +		return (NULL);
 +	}
 +
 +	/* Sanity check that this entry does not create a cycle. */
 +	offset = iso9660->logical_block_size * (uint64_t)location;
 +	for (filep = parent; filep != NULL; filep = filep->parent) {
 +		if (filep->offset == offset) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Directory structure contains loop");
 +			return (NULL);
 +		}
 +	}
 +
 +	/* Create a new file entry and copy data from the ISO dir record. */
 +	file = (struct file_info *)calloc(1, sizeof(*file));
 +	if (file == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "No memory for file entry");
 +		return (NULL);
 +	}
 +	file->parent = parent;
 +	file->offset = offset;
 +	file->size = fsize;
 +	file->mtime = isodate7(isodirrec + DR_date_offset);
 +	file->ctime = file->atime = file->mtime;
 +	file->rede_files.first = NULL;
 +	file->rede_files.last = &(file->rede_files.first);
 +
 +	p = isodirrec + DR_name_offset;
 +	/* Rockridge extensions (if any) follow name.  Compute this
 +	 * before fidgeting the name_len below. */
 +	rr_start = p + name_len + (name_len & 1 ? 0 : 1);
 +	rr_end = isodirrec + dr_len;
 +
 +	if (iso9660->seenJoliet) {
 +		/* Joliet names are max 64 chars (128 bytes) according to spec,
 +		 * but genisoimage/mkisofs allows recording longer Joliet
 +		 * names which are 103 UCS2 characters(206 bytes) by their
 +		 * option '-joliet-long'.
 +		 */
 +		if (name_len > 206)
 +			name_len = 206;
 +		name_len &= ~1;
 +
 +		/* trim trailing first version and dot from filename.
 +		 *
 +		 * Remember we were in UTF-16BE land!
 +		 * SEPARATOR 1 (.) and SEPARATOR 2 (;) are both
 +		 * 16 bits big endian characters on Joliet.
 +		 *
 +		 * TODO: sanitize filename?
 +		 *       Joliet allows any UCS-2 char except:
 +		 *       *, /, :, ;, ? and \.
 +		 */
 +		/* Chop off trailing ';1' from files. */
 +		if (name_len > 4 && p[name_len-4] == 0 && p[name_len-3] == ';'
 +		    && p[name_len-2] == 0 && p[name_len-1] == '1')
 +			name_len -= 4;
 +#if 0 /* XXX: this somehow manages to strip of single-character file extensions, like '.c'. */
 +		/* Chop off trailing '.' from filenames. */
 +		if (name_len > 2 && p[name_len-2] == 0 && p[name_len-1] == '.')
 +			name_len -= 2;
 +#endif
 +		if ((file->utf16be_name = malloc(name_len)) == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "No memory for file name");
 +			goto fail;
 +		}
 +		memcpy(file->utf16be_name, p, name_len);
 +		file->utf16be_bytes = name_len;
 +	} else {
 +		/* Chop off trailing ';1' from files. */
 +		if (name_len > 2 && p[name_len - 2] == ';' &&
 +				p[name_len - 1] == '1')
 +			name_len -= 2;
 +		/* Chop off trailing '.' from filenames. */
 +		if (name_len > 1 && p[name_len - 1] == '.')
 +			--name_len;
 +
 +		archive_strncpy(&file->name, (const char *)p, name_len);
 +	}
 +
 +	flags = isodirrec[DR_flags_offset];
 +	if (flags & 0x02)
 +		file->mode = AE_IFDIR | 0700;
 +	else
 +		file->mode = AE_IFREG | 0400;
 +	if (flags & 0x80)
 +		file->multi_extent = 1;
 +	else
 +		file->multi_extent = 0;
 +	/*
 +	 * Use a location for the file number, which is treated as an inode
 +	 * number to find out hardlink target. If Rockridge extensions is
 +	 * being used, the file number will be overwritten by FILE SERIAL
 +	 * NUMBER of RRIP "PX" extension.
 +	 * Note: Old mkisofs did not record that FILE SERIAL NUMBER
 +	 * in ISO images.
 +	 * Note2: xorriso set 0 to the location of a symlink file. 
 +	 */
 +	if (file->size == 0 && location >= 0) {
 +		/* If file->size is zero, its location points wrong place,
 +		 * and so we should not use it for the file number.
 +		 * When the location has negative value, it can be used
 +		 * for the file number.
 +		 */
 +		file->number = -1;
 +		/* Do not appear before any directory entries. */
 +		file->offset = -1;
 +	} else
 +		file->number = (int64_t)(uint32_t)location;
 +
 +	/* Rockridge extensions overwrite information from above. */
 +	if (iso9660->opt_support_rockridge) {
 +		if (parent == NULL && rr_end - rr_start >= 7) {
 +			p = rr_start;
 +			if (memcmp(p, "SP\x07\x01\xbe\xef", 6) == 0) {
 +				/*
 +				 * SP extension stores the suspOffset
 +				 * (Number of bytes to skip between
 +				 * filename and SUSP records.)
 +				 * It is mandatory by the SUSP standard
 +				 * (IEEE 1281).
 +				 *
 +				 * It allows SUSP to coexist with
 +				 * non-SUSP uses of the System
 +				 * Use Area by placing non-SUSP data
 +				 * before SUSP data.
 +				 *
 +				 * SP extension must be in the root
 +				 * directory entry, disable all SUSP
 +				 * processing if not found.
 +				 */
 +				iso9660->suspOffset = p[6];
 +				iso9660->seenSUSP = 1;
 +				rr_start += 7;
 +			}
 +		}
 +		if (iso9660->seenSUSP) {
 +			int r;
 +
 +			file->name_continues = 0;
 +			file->symlink_continues = 0;
 +			rr_start += iso9660->suspOffset;
 +			r = parse_rockridge(a, file, rr_start, rr_end);
 +			if (r != ARCHIVE_OK)
 +				goto fail;
 +			/*
 +			 * A file size of symbolic link files in ISO images
 +			 * made by makefs is not zero and its location is
 +			 * the same as those of next regular file. That is
 +			 * the same as hard like file and it causes unexpected
 +			 * error. 
 +			 */
 +			if (file->size > 0 &&
 +			    (file->mode & AE_IFMT) == AE_IFLNK) {
 +				file->size = 0;
 +				file->number = -1;
 +				file->offset = -1;
 +			}
 +		} else
 +			/* If there isn't SUSP, disable parsing
 +			 * rock ridge extensions. */
 +			iso9660->opt_support_rockridge = 0;
 +	}
 +
 +	file->nlinks = 1;/* Reset nlink. we'll calculate it later. */
 +	/* Tell file's parent how many children that parent has. */
 +	if (parent != NULL && (flags & 0x02))
 +		parent->subdirs++;
 +
 +	if (iso9660->seenRockridge) {
 +		if (parent != NULL && parent->parent == NULL &&
 +		    (flags & 0x02) && iso9660->rr_moved == NULL &&
 +		    file->name.s &&
 +		    (strcmp(file->name.s, "rr_moved") == 0 ||
 +		     strcmp(file->name.s, ".rr_moved") == 0)) {
 +			iso9660->rr_moved = file;
 +			file->rr_moved = 1;
 +			file->rr_moved_has_re_only = 1;
 +			file->re = 0;
 +			parent->subdirs--;
 +		} else if (file->re) {
 +			/*
 +			 * Sanity check: file's parent is rr_moved.
 +			 */
 +			if (parent == NULL || parent->rr_moved == 0) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Invalid Rockridge RE");
 +				goto fail;
 +			}
 +			/*
 +			 * Sanity check: file does not have "CL" extension.
 +			 */
 +			if (file->cl_offset) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Invalid Rockridge RE and CL");
 +				goto fail;
 +			}
 +			/*
 +			 * Sanity check: The file type must be a directory.
 +			 */
 +			if ((flags & 0x02) == 0) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Invalid Rockridge RE");
 +				goto fail;
 +			}
 +		} else if (parent != NULL && parent->rr_moved)
 +			file->rr_moved_has_re_only = 0;
 +		else if (parent != NULL && (flags & 0x02) &&
 +		    (parent->re || parent->re_descendant))
 +			file->re_descendant = 1;
 +		if (file->cl_offset) {
 +			struct file_info *r;
 +
 +			if (parent == NULL || parent->parent == NULL) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Invalid Rockridge CL");
 +				goto fail;
 +			}
 +			/*
 +			 * Sanity check: The file type must be a regular file.
 +			 */
 +			if ((flags & 0x02) != 0) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Invalid Rockridge CL");
 +				goto fail;
 +			}
 +			parent->subdirs++;
 +			/* Overwrite an offset and a number of this "CL" entry
 +			 * to appear before other dirs. "+1" to those is to
 +			 * make sure to appear after "RE" entry which this
 +			 * "CL" entry should be connected with. */
 +			file->offset = file->number = file->cl_offset + 1;
 +
 +			/*
 +			 * Sanity check: cl_offset does not point at its
 +			 * the parents or itself.
 +			 */
 +			for (r = parent; r; r = r->parent) {
 +				if (r->offset == file->cl_offset) {
 +					archive_set_error(&a->archive,
 +					    ARCHIVE_ERRNO_MISC,
 +					    "Invalid Rockridge CL");
 +					goto fail;
 +				}
 +			}
 +			if (file->cl_offset == file->offset ||
 +			    parent->rr_moved) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Invalid Rockridge CL");
 +				goto fail;
 +			}
 +		}
 +	}
 +
 +#if DEBUG
 +	/* DEBUGGING: Warn about attributes I don't yet fully support. */
 +	if ((flags & ~0x02) != 0) {
 +		fprintf(stderr, "\n ** Unrecognized flag: ");
 +		dump_isodirrec(stderr, isodirrec);
 +		fprintf(stderr, "\n");
 +	} else if (toi(isodirrec + DR_volume_sequence_number_offset, 2) != 1) {
 +		fprintf(stderr, "\n ** Unrecognized sequence number: ");
 +		dump_isodirrec(stderr, isodirrec);
 +		fprintf(stderr, "\n");
 +	} else if (*(isodirrec + DR_file_unit_size_offset) != 0) {
 +		fprintf(stderr, "\n ** Unexpected file unit size: ");
 +		dump_isodirrec(stderr, isodirrec);
 +		fprintf(stderr, "\n");
 +	} else if (*(isodirrec + DR_interleave_offset) != 0) {
 +		fprintf(stderr, "\n ** Unexpected interleave: ");
 +		dump_isodirrec(stderr, isodirrec);
 +		fprintf(stderr, "\n");
 +	} else if (*(isodirrec + DR_ext_attr_length_offset) != 0) {
 +		fprintf(stderr, "\n ** Unexpected extended attribute length: ");
 +		dump_isodirrec(stderr, isodirrec);
 +		fprintf(stderr, "\n");
 +	}
 +#endif
 +	register_file(iso9660, file);
 +	return (file);
 +fail:
 +	archive_string_free(&file->name);
 +	free(file);
 +	return (NULL);
 +}
 +
 +static int
 +parse_rockridge(struct archive_read *a, struct file_info *file,
 +    const unsigned char *p, const unsigned char *end)
 +{
 +	struct iso9660 *iso9660;
 +
 +	iso9660 = (struct iso9660 *)(a->format->data);
 +
 +	while (p + 4 <= end  /* Enough space for another entry. */
 +	    && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */
 +	    && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */
 +	    && p[2] >= 4 /* Sanity-check length. */
 +	    && p + p[2] <= end) { /* Sanity-check length. */
 +		const unsigned char *data = p + 4;
 +		int data_length = p[2] - 4;
 +		int version = p[3];
 +
 +		switch(p[0]) {
 +		case 'C':
 +			if (p[1] == 'E') {
 +				if (version == 1 && data_length == 24) {
 +					/*
 +					 * CE extension comprises:
 +					 *   8 byte sector containing extension
 +					 *   8 byte offset w/in above sector
 +					 *   8 byte length of continuation
 +					 */
 +					int32_t location =
 +					    archive_le32dec(data);
 +					file->ce_offset =
 +					    archive_le32dec(data+8);
 +					file->ce_size =
 +					    archive_le32dec(data+16);
 +					if (register_CE(a, location, file)
 +					    != ARCHIVE_OK)
 +						return (ARCHIVE_FATAL);
 +				}
 +			}
 +			else if (p[1] == 'L') {
 +				if (version == 1 && data_length == 8) {
 +					file->cl_offset = (uint64_t)
 +					    iso9660->logical_block_size *
 +					    (uint64_t)archive_le32dec(data);
 +					iso9660->seenRockridge = 1;
 +				}
 +			}
 +			break;
 +		case 'N':
 +			if (p[1] == 'M') {
 +				if (version == 1) {
 +					parse_rockridge_NM1(file,
 +					    data, data_length);
 +					iso9660->seenRockridge = 1;
 +				}
 +			}
 +			break;
 +		case 'P':
 +			/*
 +			 * PD extension is padding;
 +			 * contents are always ignored.
 +			 *
 +			 * PL extension won't appear;
 +			 * contents are always ignored.
 +			 */
 +			if (p[1] == 'N') {
 +				if (version == 1 && data_length == 16) {
 +					file->rdev = toi(data,4);
 +					file->rdev <<= 32;
 +					file->rdev |= toi(data + 8, 4);
 +					iso9660->seenRockridge = 1;
 +				}
 +			}
 +			else if (p[1] == 'X') {
 +				/*
 +				 * PX extension comprises:
 +				 *   8 bytes for mode,
 +				 *   8 bytes for nlinks,
 +				 *   8 bytes for uid,
 +				 *   8 bytes for gid,
 +				 *   8 bytes for inode.
 +				 */
 +				if (version == 1) {
 +					if (data_length >= 8)
 +						file->mode
 +						    = toi(data, 4);
 +					if (data_length >= 16)
 +						file->nlinks
 +						    = toi(data + 8, 4);
 +					if (data_length >= 24)
 +						file->uid
 +						    = toi(data + 16, 4);
 +					if (data_length >= 32)
 +						file->gid
 +						    = toi(data + 24, 4);
 +					if (data_length >= 40)
 +						file->number
 +						    = toi(data + 32, 4);
 +					iso9660->seenRockridge = 1;
 +				}
 +			}
 +			break;
 +		case 'R':
 +			if (p[1] == 'E' && version == 1) {
 +				file->re = 1;
 +				iso9660->seenRockridge = 1;
 +			}
 +			else if (p[1] == 'R' && version == 1) {
 +				/*
 +				 * RR extension comprises:
 +				 *    one byte flag value
 +				 * This extension is obsolete,
 +				 * so contents are always ignored.
 +				 */
 +			}
 +			break;
 +		case 'S':
 +			if (p[1] == 'L') {
 +				if (version == 1) {
 +					parse_rockridge_SL1(file,
 +					    data, data_length);
 +					iso9660->seenRockridge = 1;
 +				}
 +			}
 +			else if (p[1] == 'T'
 +			    && data_length == 0 && version == 1) {
 +				/*
 +				 * ST extension marks end of this
 +				 * block of SUSP entries.
 +				 *
 +				 * It allows SUSP to coexist with
 +				 * non-SUSP uses of the System
 +				 * Use Area by placing non-SUSP data
 +				 * after SUSP data.
 +				 */
 +				iso9660->seenSUSP = 0;
 +				iso9660->seenRockridge = 0;
 +				return (ARCHIVE_OK);
 +			}
 +			break;
 +		case 'T':
 +			if (p[1] == 'F') {
 +				if (version == 1) {
 +					parse_rockridge_TF1(file,
 +					    data, data_length);
 +					iso9660->seenRockridge = 1;
 +				}
 +			}
 +			break;
 +		case 'Z':
 +			if (p[1] == 'F') {
 +				if (version == 1)
 +					parse_rockridge_ZF1(file,
 +					    data, data_length);
 +			}
 +			break;
 +		default:
 +			break;
 +		}
 +
 +		p += p[2];
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +register_CE(struct archive_read *a, int32_t location,
 +    struct file_info *file)
 +{
 +	struct iso9660 *iso9660;
 +	struct read_ce_queue *heap;
 +	struct read_ce_req *p;
 +	uint64_t offset, parent_offset;
 +	int hole, parent;
 +
 +	iso9660 = (struct iso9660 *)(a->format->data);
 +	offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size;
 +	if (((file->mode & AE_IFMT) == AE_IFREG &&
 +	    offset >= file->offset) ||
 +	    offset < iso9660->current_position ||
 +	    (((uint64_t)file->ce_offset) + file->ce_size)
 +	      > (uint64_t)iso9660->logical_block_size ||
 +	    offset + file->ce_offset + file->ce_size
 +		  > iso9660->volume_size) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Invalid parameter in SUSP \"CE\" extension");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	/* Expand our CE list as necessary. */
 +	heap = &(iso9660->read_ce_req);
 +	if (heap->cnt >= heap->allocated) {
 +		int new_size;
 +
 +		if (heap->allocated < 16)
 +			new_size = 16;
 +		else
 +			new_size = heap->allocated * 2;
 +		/* Overflow might keep us from growing the list. */
 +		if (new_size <= heap->allocated) {
 +			archive_set_error(&a->archive, ENOMEM, "Out of memory");
 +			return (ARCHIVE_FATAL);
 +		}
 +		p = calloc(new_size, sizeof(p[0]));
 +		if (p == NULL) {
 +			archive_set_error(&a->archive, ENOMEM, "Out of memory");
 +			return (ARCHIVE_FATAL);
 +		}
 +		if (heap->reqs != NULL) {
 +			memcpy(p, heap->reqs, heap->cnt * sizeof(*p));
 +			free(heap->reqs);
 +		}
 +		heap->reqs = p;
 +		heap->allocated = new_size;
 +	}
 +
 +	/*
 +	 * Start with hole at end, walk it up tree to find insertion point.
 +	 */
 +	hole = heap->cnt++;
 +	while (hole > 0) {
 +		parent = (hole - 1)/2;
 +		parent_offset = heap->reqs[parent].offset;
 +		if (offset >= parent_offset) {
 +			heap->reqs[hole].offset = offset;
 +			heap->reqs[hole].file = file;
 +			return (ARCHIVE_OK);
 +		}
 +		/* Move parent into hole <==> move hole up tree. */
 +		heap->reqs[hole] = heap->reqs[parent];
 +		hole = parent;
 +	}
 +	heap->reqs[0].offset = offset;
 +	heap->reqs[0].file = file;
 +	return (ARCHIVE_OK);
 +}
 +
 +static void
 +next_CE(struct read_ce_queue *heap)
 +{
 +	uint64_t a_offset, b_offset, c_offset;
 +	int a, b, c;
 +	struct read_ce_req tmp;
 +
 +	if (heap->cnt < 1)
 +		return;
 +
 +	/*
 +	 * Move the last item in the heap to the root of the tree
 +	 */
 +	heap->reqs[0] = heap->reqs[--(heap->cnt)];
 +
 +	/*
 +	 * Rebalance the heap.
 +	 */
 +	a = 0; /* Starting element and its offset */
 +	a_offset = heap->reqs[a].offset;
 +	for (;;) {
 +		b = a + a + 1; /* First child */
 +		if (b >= heap->cnt)
 +			return;
 +		b_offset = heap->reqs[b].offset;
 +		c = b + 1; /* Use second child if it is smaller. */
 +		if (c < heap->cnt) {
 +			c_offset = heap->reqs[c].offset;
 +			if (c_offset < b_offset) {
 +				b = c;
 +				b_offset = c_offset;
 +			}
 +		}
 +		if (a_offset <= b_offset)
 +			return;
 +		tmp = heap->reqs[a];
 +		heap->reqs[a] = heap->reqs[b];
 +		heap->reqs[b] = tmp;
 +		a = b;
 +	}
 +}
 +
 +
 +static int
 +read_CE(struct archive_read *a, struct iso9660 *iso9660)
 +{
 +	struct read_ce_queue *heap;
 +	const unsigned char *b, *p, *end;
 +	struct file_info *file;
 +	size_t step;
 +	int r;
 +
 +	/* Read data which RRIP "CE" extension points. */
 +	heap = &(iso9660->read_ce_req);
 +	step = iso9660->logical_block_size;
 +	while (heap->cnt &&
 +	    heap->reqs[0].offset == iso9660->current_position) {
 +		b = __archive_read_ahead(a, step, NULL);
 +		if (b == NULL) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_MISC,
 +			    "Failed to read full block when scanning "
 +			    "ISO9660 directory list");
 +			return (ARCHIVE_FATAL);
 +		}
 +		do {
 +			file = heap->reqs[0].file;
 +			if (file->ce_offset + file->ce_size > step) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_FILE_FORMAT,
 +				    "Malformed CE information");
 +				return (ARCHIVE_FATAL);
 +			}
 +			p = b + file->ce_offset;
 +			end = p + file->ce_size;
 +			next_CE(heap);
 +			r = parse_rockridge(a, file, p, end);
 +			if (r != ARCHIVE_OK)
 +				return (ARCHIVE_FATAL);
 +		} while (heap->cnt &&
 +		    heap->reqs[0].offset == iso9660->current_position);
 +		/* NOTE: Do not move this consume's code to front of
 +		 * do-while loop. Registration of nested CE extension
 +		 * might cause error because of current position. */
 +		__archive_read_consume(a, step);
 +		iso9660->current_position += step;
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static void
 +parse_rockridge_NM1(struct file_info *file,
 +		    const unsigned char *data, int data_length)
 +{
 +	if (!file->name_continues)
 +		archive_string_empty(&file->name);
 +	file->name_continues = 0;
 +	if (data_length < 1)
 +		return;
 +	/*
 +	 * NM version 1 extension comprises:
 +	 *   1 byte flag, value is one of:
 +	 *     = 0: remainder is name
 +	 *     = 1: remainder is name, next NM entry continues name
 +	 *     = 2: "."
 +	 *     = 4: ".."
 +	 *     = 32: Implementation specific
 +	 *     All other values are reserved.
 +	 */
 +	switch(data[0]) {
 +	case 0:
 +		if (data_length < 2)
 +			return;
 +		archive_strncat(&file->name,
 +		    (const char *)data + 1, data_length - 1);
 +		break;
 +	case 1:
 +		if (data_length < 2)
 +			return;
 +		archive_strncat(&file->name,
 +		    (const char *)data + 1, data_length - 1);
 +		file->name_continues = 1;
 +		break;
 +	case 2:
 +		archive_strcat(&file->name, ".");
 +		break;
 +	case 4:
 +		archive_strcat(&file->name, "..");
 +		break;
 +	default:
 +		return;
 +	}
 +
 +}
 +
 +static void
 +parse_rockridge_TF1(struct file_info *file, const unsigned char *data,
 +    int data_length)
 +{
 +	char flag;
 +	/*
 +	 * TF extension comprises:
 +	 *   one byte flag
 +	 *   create time (optional)
 +	 *   modify time (optional)
 +	 *   access time (optional)
 +	 *   attribute time (optional)
 +	 *  Time format and presence of fields
 +	 *  is controlled by flag bits.
 +	 */
 +	if (data_length < 1)
 +		return;
 +	flag = data[0];
 +	++data;
 +	--data_length;
 +	if (flag & 0x80) {
 +		/* Use 17-byte time format. */
 +		if ((flag & 1) && data_length >= 17) {
 +			/* Create time. */
 +			file->birthtime_is_set = 1;
 +			file->birthtime = isodate17(data);
 +			data += 17;
 +			data_length -= 17;
 +		}
 +		if ((flag & 2) && data_length >= 17) {
 +			/* Modify time. */
 +			file->mtime = isodate17(data);
 +			data += 17;
 +			data_length -= 17;
 +		}
 +		if ((flag & 4) && data_length >= 17) {
 +			/* Access time. */
 +			file->atime = isodate17(data);
 +			data += 17;
 +			data_length -= 17;
 +		}
 +		if ((flag & 8) && data_length >= 17) {
 +			/* Attribute change time. */
 +			file->ctime = isodate17(data);
 +		}
 +	} else {
 +		/* Use 7-byte time format. */
 +		if ((flag & 1) && data_length >= 7) {
 +			/* Create time. */
 +			file->birthtime_is_set = 1;
 +			file->birthtime = isodate7(data);
 +			data += 7;
 +			data_length -= 7;
 +		}
 +		if ((flag & 2) && data_length >= 7) {
 +			/* Modify time. */
 +			file->mtime = isodate7(data);
 +			data += 7;
 +			data_length -= 7;
 +		}
 +		if ((flag & 4) && data_length >= 7) {
 +			/* Access time. */
 +			file->atime = isodate7(data);
 +			data += 7;
 +			data_length -= 7;
 +		}
 +		if ((flag & 8) && data_length >= 7) {
 +			/* Attribute change time. */
 +			file->ctime = isodate7(data);
 +		}
 +	}
 +}
 +
 +static void
 +parse_rockridge_SL1(struct file_info *file, const unsigned char *data,
 +    int data_length)
 +{
 +	const char *separator = "";
 +
 +	if (!file->symlink_continues || file->symlink.length < 1)
 +		archive_string_empty(&file->symlink);
 +	file->symlink_continues = 0;
 +
 +	/*
 +	 * Defined flag values:
 +	 *  0: This is the last SL record for this symbolic link
 +	 *  1: this symbolic link field continues in next SL entry
 +	 *  All other values are reserved.
 +	 */
 +	if (data_length < 1)
 +		return;
 +	switch(*data) {
 +	case 0:
 +		break;
 +	case 1:
 +		file->symlink_continues = 1;
 +		break;
 +	default:
 +		return;
 +	}
 +	++data;  /* Skip flag byte. */
 +	--data_length;
 +
 +	/*
 +	 * SL extension body stores "components".
 +	 * Basically, this is a complicated way of storing
 +	 * a POSIX path.  It also interferes with using
 +	 * symlinks for storing non-path data. <sigh>
 +	 *
 +	 * Each component is 2 bytes (flag and length)
 +	 * possibly followed by name data.
 +	 */
 +	while (data_length >= 2) {
 +		unsigned char flag = *data++;
 +		unsigned char nlen = *data++;
 +		data_length -= 2;
 +
 +		archive_strcat(&file->symlink, separator);
 +		separator = "/";
 +
 +		switch(flag) {
 +		case 0: /* Usual case, this is text. */
 +			if (data_length < nlen)
 +				return;
 +			archive_strncat(&file->symlink,
 +			    (const char *)data, nlen);
 +			break;
 +		case 0x01: /* Text continues in next component. */
 +			if (data_length < nlen)
 +				return;
 +			archive_strncat(&file->symlink,
 +			    (const char *)data, nlen);
 +			separator = "";
 +			break;
 +		case 0x02: /* Current dir. */
 +			archive_strcat(&file->symlink, ".");
 +			break;
 +		case 0x04: /* Parent dir. */
 +			archive_strcat(&file->symlink, "..");
 +			break;
 +		case 0x08: /* Root of filesystem. */
 +			archive_strcat(&file->symlink, "/");
 +			separator = "";
 +			break;
 +		case 0x10: /* Undefined (historically "volume root" */
 +			archive_string_empty(&file->symlink);
 +			archive_strcat(&file->symlink, "ROOT");
 +			break;
 +		case 0x20: /* Undefined (historically "hostname") */
 +			archive_strcat(&file->symlink, "hostname");
 +			break;
 +		default:
 +			/* TODO: issue a warning ? */
 +			return;
 +		}
 +		data += nlen;
 +		data_length -= nlen;
 +	}
 +}
 +
 +static void
 +parse_rockridge_ZF1(struct file_info *file, const unsigned char *data,
 +    int data_length)
 +{
 +
 +	if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) {
 +		/* paged zlib */
 +		file->pz = 1;
 +		file->pz_log2_bs = data[3];
 +		file->pz_uncompressed_size = archive_le32dec(&data[4]);
 +	}
 +}
 +
 +static void
 +register_file(struct iso9660 *iso9660, struct file_info *file)
 +{
 +
 +	file->use_next = iso9660->use_files;
 +	iso9660->use_files = file;
 +}
 +
 +static void
 +release_files(struct iso9660 *iso9660)
 +{
 +	struct content *con, *connext;
 +	struct file_info *file;
 +
 +	file = iso9660->use_files;
 +	while (file != NULL) {
 +		struct file_info *next = file->use_next;
 +
 +		archive_string_free(&file->name);
 +		archive_string_free(&file->symlink);
 +		free(file->utf16be_name);
 +		con = file->contents.first;
 +		while (con != NULL) {
 +			connext = con->next;
 +			free(con);
 +			con = connext;
 +		}
 +		free(file);
 +		file = next;
 +	}
 +}
 +
 +static int
 +next_entry_seek(struct archive_read *a, struct iso9660 *iso9660,
 +    struct file_info **pfile)
 +{
 +	struct file_info *file;
 +	int r;
 +
 +	r = next_cache_entry(a, iso9660, pfile);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	file = *pfile;
 +
 +	/* Don't waste time seeking for zero-length bodies. */
 +	if (file->size == 0)
 +		file->offset = iso9660->current_position;
 +
 +	/* flush any remaining bytes from the last round to ensure
 +	 * we're positioned */
 +	if (iso9660->entry_bytes_unconsumed) {
 +		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);
 +		iso9660->entry_bytes_unconsumed = 0;
 +	}
 +
 +	/* Seek forward to the start of the entry. */
 +	if (iso9660->current_position < file->offset) {
 +		int64_t step;
 +
 +		step = file->offset - iso9660->current_position;
 +		step = __archive_read_consume(a, step);
 +		if (step < 0)
 +			return ((int)step);
 +		iso9660->current_position = file->offset;
 +	}
 +
 +	/* We found body of file; handle it now. */
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +next_cache_entry(struct archive_read *a, struct iso9660 *iso9660,
 +    struct file_info **pfile)
 +{
 +	struct file_info *file;
 +	struct {
 +		struct file_info	*first;
 +		struct file_info	**last;
 +	}	empty_files;
 +	int64_t number;
 +	int count;
 +
 +	file = cache_get_entry(iso9660);
 +	if (file != NULL) {
 +		*pfile = file;
 +		return (ARCHIVE_OK);
 +	}
 +
 +	for (;;) {
 +		struct file_info *re, *d;
 +
 +		*pfile = file = next_entry(iso9660);
 +		if (file == NULL) {
 +			/*
 +			 * If directory entries all which are descendant of
 +			 * rr_moved are still remaining, expose their.
 +			 */
 +			if (iso9660->re_files.first != NULL && 
 +			    iso9660->rr_moved != NULL &&
 +			    iso9660->rr_moved->rr_moved_has_re_only)
 +				/* Expose "rr_moved" entry. */
 +				cache_add_entry(iso9660, iso9660->rr_moved);
 +			while ((re = re_get_entry(iso9660)) != NULL) {
 +				/* Expose its descendant dirs. */
 +				while ((d = rede_get_entry(re)) != NULL)
 +					cache_add_entry(iso9660, d);
 +			}
 +			if (iso9660->cache_files.first != NULL)
 +				return (next_cache_entry(a, iso9660, pfile));
 +			return (ARCHIVE_EOF);
 +		}
 +
 +		if (file->cl_offset) {
 +			struct file_info *first_re = NULL;
 +			int nexted_re = 0;
 +
 +			/*
 +			 * Find "RE" dir for the current file, which
 +			 * has "CL" flag.
 +			 */
 +			while ((re = re_get_entry(iso9660))
 +			    != first_re) {
 +				if (first_re == NULL)
 +					first_re = re;
 +				if (re->offset == file->cl_offset) {
 +					re->parent->subdirs--;
 +					re->parent = file->parent;
 +					re->re = 0;
 +					if (re->parent->re_descendant) {
 +						nexted_re = 1;
 +						re->re_descendant = 1;
 +						if (rede_add_entry(re) < 0)
 +							goto fatal_rr;
 +						/* Move a list of descendants
 +						 * to a new ancestor. */
 +						while ((d = rede_get_entry(
 +						    re)) != NULL)
 +							if (rede_add_entry(d)
 +							    < 0)
 +								goto fatal_rr;
 +						break;
 +					}
 +					/* Replace the current file
 +					 * with "RE" dir */
 +					*pfile = file = re;
 +					/* Expose its descendant */
 +					while ((d = rede_get_entry(
 +					    file)) != NULL)
 +						cache_add_entry(
 +						    iso9660, d);
 +					break;
 +				} else
 +					re_add_entry(iso9660, re);
 +			}
 +			if (nexted_re) {
 +				/*
 +				 * Do not expose this at this time
 +				 * because we have not gotten its full-path
 +				 * name yet.
 +				 */
 +				continue;
 +			}
 +		} else if ((file->mode & AE_IFMT) == AE_IFDIR) {
 +			int r;
 +
 +			/* Read file entries in this dir. */
 +			r = read_children(a, file);
 +			if (r != ARCHIVE_OK)
 +				return (r);
 +
 +			/*
 +			 * Handle a special dir of Rockridge extensions,
 +			 * "rr_moved".
 +			 */
 +			if (file->rr_moved) {
 +				/*
 +				 * If this has only the subdirectories which
 +				 * have "RE" flags, do not expose at this time.
 +				 */
 +				if (file->rr_moved_has_re_only)
 +					continue;
 +				/* Otherwise expose "rr_moved" entry. */
 +			} else if (file->re) {
 +				/*
 +				 * Do not expose this at this time
 +				 * because we have not gotten its full-path
 +				 * name yet.
 +				 */
 +				re_add_entry(iso9660, file);
 +				continue;
 +			} else if (file->re_descendant) {
 +				/*
 +				 * If the top level "RE" entry of this entry
 +				 * is not exposed, we, accordingly, should not
 +				 * expose this entry at this time because
 +				 * we cannot make its proper full-path name.
 +				 */
 +				if (rede_add_entry(file) == 0)
 +					continue;
 +				/* Otherwise we can expose this entry because
 +				 * it seems its top level "RE" has already been
 +				 * exposed. */
 +			}
 +		}
 +		break;
 +	}
 +
 +	if ((file->mode & AE_IFMT) != AE_IFREG || file->number == -1)
 +		return (ARCHIVE_OK);
 +
 +	count = 0;
 +	number = file->number;
 +	iso9660->cache_files.first = NULL;
 +	iso9660->cache_files.last = &(iso9660->cache_files.first);
 +	empty_files.first = NULL;
 +	empty_files.last = &empty_files.first;
 +	/* Collect files which has the same file serial number.
 +	 * Peek pending_files so that file which number is different
 +	 * is not put back. */
 +	while (iso9660->pending_files.used > 0 &&
 +	    (iso9660->pending_files.files[0]->number == -1 ||
 +	     iso9660->pending_files.files[0]->number == number)) {
 +		if (file->number == -1) {
 +			/* This file has the same offset
 +			 * but it's wrong offset which empty files
 +			 * and symlink files have.
 +			 * NOTE: This wrong offset was recorded by
 +			 * old mkisofs utility. If ISO images is
 +			 * created by latest mkisofs, this does not
 +			 * happen.
 +			 */
 +			file->next = NULL;
 +			*empty_files.last = file;
 +			empty_files.last = &(file->next);
 +		} else {
 +			count++;
 +			cache_add_entry(iso9660, file);
 +		}
 +		file = next_entry(iso9660);
 +	}
 +
 +	if (count == 0) {
 +		*pfile = file;
 +		return ((file == NULL)?ARCHIVE_EOF:ARCHIVE_OK);
 +	}
 +	if (file->number == -1) {
 +		file->next = NULL;
 +		*empty_files.last = file;
 +		empty_files.last = &(file->next);
 +	} else {
 +		count++;
 +		cache_add_entry(iso9660, file);
 +	}
 +
 +	if (count > 1) {
 +		/* The count is the same as number of hardlink,
 +		 * so much so that each nlinks of files in cache_file
 +		 * is overwritten by value of the count.
 +		 */
 +		for (file = iso9660->cache_files.first;
 +		    file != NULL; file = file->next)
 +			file->nlinks = count;
 +	}
 +	/* If there are empty files, that files are added
 +	 * to the tail of the cache_files. */
 +	if (empty_files.first != NULL) {
 +		*iso9660->cache_files.last = empty_files.first;
 +		iso9660->cache_files.last = empty_files.last;
 +	}
 +	*pfile = cache_get_entry(iso9660);
 +	return ((*pfile == NULL)?ARCHIVE_EOF:ARCHIVE_OK);
 +
 +fatal_rr:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +	    "Failed to connect 'CL' pointer to 'RE' rr_moved pointer of "
 +	    "Rockridge extensions: current position = %jd, CL offset = %jd",
 +	    (intmax_t)iso9660->current_position, (intmax_t)file->cl_offset);
 +	return (ARCHIVE_FATAL);
 +}
 +
 +static inline void
 +re_add_entry(struct iso9660 *iso9660, struct file_info *file)
 +{
 +	file->re_next = NULL;
 +	*iso9660->re_files.last = file;
 +	iso9660->re_files.last = &(file->re_next);
 +}
 +
 +static inline struct file_info *
 +re_get_entry(struct iso9660 *iso9660)
 +{
 +	struct file_info *file;
 +
 +	if ((file = iso9660->re_files.first) != NULL) {
 +		iso9660->re_files.first = file->re_next;
 +		if (iso9660->re_files.first == NULL)
 +			iso9660->re_files.last =
 +			    &(iso9660->re_files.first);
 +	}
 +	return (file);
 +}
 +
 +static inline int
 +rede_add_entry(struct file_info *file)
 +{
 +	struct file_info *re;
 +
 +	/*
 +	 * Find "RE" entry.
 +	 */
 +	re = file->parent;
 +	while (re != NULL && !re->re)
 +		re = re->parent;
 +	if (re == NULL)
 +		return (-1);
 +
 +	file->re_next = NULL;
 +	*re->rede_files.last = file;
 +	re->rede_files.last = &(file->re_next);
 +	return (0);
 +}
 +
 +static inline struct file_info *
 +rede_get_entry(struct file_info *re)
 +{
 +	struct file_info *file;
 +
 +	if ((file = re->rede_files.first) != NULL) {
 +		re->rede_files.first = file->re_next;
 +		if (re->rede_files.first == NULL)
 +			re->rede_files.last =
 +			    &(re->rede_files.first);
 +	}
 +	return (file);
 +}
 +
 +static inline void
 +cache_add_entry(struct iso9660 *iso9660, struct file_info *file)
 +{
 +	file->next = NULL;
 +	*iso9660->cache_files.last = file;
 +	iso9660->cache_files.last = &(file->next);
 +}
 +
 +static inline struct file_info *
 +cache_get_entry(struct iso9660 *iso9660)
 +{
 +	struct file_info *file;
 +
 +	if ((file = iso9660->cache_files.first) != NULL) {
 +		iso9660->cache_files.first = file->next;
 +		if (iso9660->cache_files.first == NULL)
 +			iso9660->cache_files.last =
 +			    &(iso9660->cache_files.first);
 +	}
 +	return (file);
 +}
 +
 +static int
 +heap_add_entry(struct archive_read *a, struct heap_queue *heap,
 +    struct file_info *file, uint64_t key)
 +{
 +	uint64_t file_key, parent_key;
 +	int hole, parent;
 +
 +	/* Expand our pending files list as necessary. */
 +	if (heap->used >= heap->allocated) {
 +		struct file_info **new_pending_files;
 +		int new_size = heap->allocated * 2;
 +
 +		if (heap->allocated < 1024)
 +			new_size = 1024;
 +		/* Overflow might keep us from growing the list. */
 +		if (new_size <= heap->allocated) {
 +			archive_set_error(&a->archive,
 +			    ENOMEM, "Out of memory");
 +			return (ARCHIVE_FATAL);
 +		}
 +		new_pending_files = (struct file_info **)
 +		    malloc(new_size * sizeof(new_pending_files[0]));
 +		if (new_pending_files == NULL) {
 +			archive_set_error(&a->archive,
 +			    ENOMEM, "Out of memory");
 +			return (ARCHIVE_FATAL);
 +		}
- 		memcpy(new_pending_files, heap->files,
- 		    heap->allocated * sizeof(new_pending_files[0]));
++		if (heap->allocated)
++			memcpy(new_pending_files, heap->files,
++			    heap->allocated * sizeof(new_pending_files[0]));
 +		if (heap->files != NULL)
 +			free(heap->files);
 +		heap->files = new_pending_files;
 +		heap->allocated = new_size;
 +	}
 +
 +	file_key = file->key = key;
 +
 +	/*
 +	 * Start with hole at end, walk it up tree to find insertion point.
 +	 */
 +	hole = heap->used++;
 +	while (hole > 0) {
 +		parent = (hole - 1)/2;
 +		parent_key = heap->files[parent]->key;
 +		if (file_key >= parent_key) {
 +			heap->files[hole] = file;
 +			return (ARCHIVE_OK);
 +		}
 +		/* Move parent into hole <==> move hole up tree. */
 +		heap->files[hole] = heap->files[parent];
 +		hole = parent;
 +	}
 +	heap->files[0] = file;
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static struct file_info *
 +heap_get_entry(struct heap_queue *heap)
 +{
 +	uint64_t a_key, b_key, c_key;
 +	int a, b, c;
 +	struct file_info *r, *tmp;
 +
 +	if (heap->used < 1)
 +		return (NULL);
 +
 +	/*
 +	 * The first file in the list is the earliest; we'll return this.
 +	 */
 +	r = heap->files[0];
 +
 +	/*
 +	 * Move the last item in the heap to the root of the tree
 +	 */
 +	heap->files[0] = heap->files[--(heap->used)];
 +
 +	/*
 +	 * Rebalance the heap.
 +	 */
 +	a = 0; /* Starting element and its heap key */
 +	a_key = heap->files[a]->key;
 +	for (;;) {
 +		b = a + a + 1; /* First child */
 +		if (b >= heap->used)
 +			return (r);
 +		b_key = heap->files[b]->key;
 +		c = b + 1; /* Use second child if it is smaller. */
 +		if (c < heap->used) {
 +			c_key = heap->files[c]->key;
 +			if (c_key < b_key) {
 +				b = c;
 +				b_key = c_key;
 +			}
 +		}
 +		if (a_key <= b_key)
 +			return (r);
 +		tmp = heap->files[a];
 +		heap->files[a] = heap->files[b];
 +		heap->files[b] = tmp;
 +		a = b;
 +	}
 +}
 +
 +static unsigned int
 +toi(const void *p, int n)
 +{
 +	const unsigned char *v = (const unsigned char *)p;
 +	if (n > 1)
 +		return v[0] + 256 * toi(v + 1, n - 1);
 +	if (n == 1)
 +		return v[0];
 +	return (0);
 +}
 +
 +static time_t
 +isodate7(const unsigned char *v)
 +{
 +	struct tm tm;
 +	int offset;
 +	time_t t;
 +
 +	memset(&tm, 0, sizeof(tm));
 +	tm.tm_year = v[0];
 +	tm.tm_mon = v[1] - 1;
 +	tm.tm_mday = v[2];
 +	tm.tm_hour = v[3];
 +	tm.tm_min = v[4];
 +	tm.tm_sec = v[5];
 +	/* v[6] is the signed timezone offset, in 1/4-hour increments. */
 +	offset = ((const signed char *)v)[6];
 +	if (offset > -48 && offset < 52) {
 +		tm.tm_hour -= offset / 4;
 +		tm.tm_min -= (offset % 4) * 15;
 +	}
 +	t = time_from_tm(&tm);
 +	if (t == (time_t)-1)
 +		return ((time_t)0);
 +	return (t);
 +}
 +
 +static time_t
 +isodate17(const unsigned char *v)
 +{
 +	struct tm tm;
 +	int offset;
 +	time_t t;
 +
 +	memset(&tm, 0, sizeof(tm));
 +	tm.tm_year = (v[0] - '0') * 1000 + (v[1] - '0') * 100
 +	    + (v[2] - '0') * 10 + (v[3] - '0')
 +	    - 1900;
 +	tm.tm_mon = (v[4] - '0') * 10 + (v[5] - '0');
 +	tm.tm_mday = (v[6] - '0') * 10 + (v[7] - '0');
 +	tm.tm_hour = (v[8] - '0') * 10 + (v[9] - '0');
 +	tm.tm_min = (v[10] - '0') * 10 + (v[11] - '0');
 +	tm.tm_sec = (v[12] - '0') * 10 + (v[13] - '0');
 +	/* v[16] is the signed timezone offset, in 1/4-hour increments. */
 +	offset = ((const signed char *)v)[16];
 +	if (offset > -48 && offset < 52) {
 +		tm.tm_hour -= offset / 4;
 +		tm.tm_min -= (offset % 4) * 15;
 +	}
 +	t = time_from_tm(&tm);
 +	if (t == (time_t)-1)
 +		return ((time_t)0);
 +	return (t);
 +}
 +
 +static time_t
 +time_from_tm(struct tm *t)
 +{
 +#if HAVE_TIMEGM
 +        /* Use platform timegm() if available. */
 +        return (timegm(t));
 +#elif HAVE__MKGMTIME64
 +        return (_mkgmtime64(t));
 +#else
 +        /* Else use direct calculation using POSIX assumptions. */
 +        /* First, fix up tm_yday based on the year/month/day. */
 +        if (mktime(t) == (time_t)-1)
 +                return ((time_t)-1);
 +        /* Then we can compute timegm() from first principles. */
 +        return (t->tm_sec
 +            + t->tm_min * 60
 +            + t->tm_hour * 3600
 +            + t->tm_yday * 86400
 +            + (t->tm_year - 70) * 31536000
 +            + ((t->tm_year - 69) / 4) * 86400
 +            - ((t->tm_year - 1) / 100) * 86400
 +            + ((t->tm_year + 299) / 400) * 86400);
 +#endif
 +}
 +
 +static const char *
 +build_pathname(struct archive_string *as, struct file_info *file, int depth)
 +{
 +	// Plain ISO9660 only allows 8 dir levels; if we get
 +	// to 1000, then something is very, very wrong.
 +	if (depth > 1000) {
 +		return NULL;
 +	}
 +	if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) {
 +		if (build_pathname(as, file->parent, depth + 1) == NULL) {
 +			return NULL;
 +		}
 +		archive_strcat(as, "/");
 +	}
 +	if (archive_strlen(&file->name) == 0)
 +		archive_strcat(as, ".");
 +	else
 +		archive_string_concat(as, &file->name);
 +	return (as->s);
 +}
 +
 +static int
 +build_pathname_utf16be(unsigned char *p, size_t max, size_t *len,
 +    struct file_info *file)
 +{
 +	if (file->parent != NULL && file->parent->utf16be_bytes > 0) {
 +		if (build_pathname_utf16be(p, max, len, file->parent) != 0)
 +			return (-1);
 +		p[*len] = 0;
 +		p[*len + 1] = '/';
 +		*len += 2;
 +	}
 +	if (file->utf16be_bytes == 0) {
 +		if (*len + 2 > max)
 +			return (-1);/* Path is too long! */
 +		p[*len] = 0;
 +		p[*len + 1] = '.';
 +		*len += 2;
 +	} else {
 +		if (*len + file->utf16be_bytes > max)
 +			return (-1);/* Path is too long! */
 +		memcpy(p + *len, file->utf16be_name, file->utf16be_bytes);
 +		*len += file->utf16be_bytes;
 +	}
 +	return (0);
 +}
 +
 +#if DEBUG
 +static void
 +dump_isodirrec(FILE *out, const unsigned char *isodirrec)
 +{
 +	fprintf(out, " l %d,",
 +	    toi(isodirrec + DR_length_offset, DR_length_size));
 +	fprintf(out, " a %d,",
 +	    toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size));
 +	fprintf(out, " ext 0x%x,",
 +	    toi(isodirrec + DR_extent_offset, DR_extent_size));
 +	fprintf(out, " s %d,",
 +	    toi(isodirrec + DR_size_offset, DR_extent_size));
 +	fprintf(out, " f 0x%x,",
 +	    toi(isodirrec + DR_flags_offset, DR_flags_size));
 +	fprintf(out, " u %d,",
 +	    toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size));
 +	fprintf(out, " ilv %d,",
 +	    toi(isodirrec + DR_interleave_offset, DR_interleave_size));
 +	fprintf(out, " seq %d,",
 +	    toi(isodirrec + DR_volume_sequence_number_offset,
 +		DR_volume_sequence_number_size));
 +	fprintf(out, " nl %d:",
 +	    toi(isodirrec + DR_name_len_offset, DR_name_len_size));
 +	fprintf(out, " `%.*s'",
 +	    toi(isodirrec + DR_name_len_offset, DR_name_len_size),
 +		isodirrec + DR_name_offset);
 +}
 +#endif
diff --cc Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c
index ebd7f2c,0000000..1995e9c
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c
@@@ -1,2817 -1,0 +1,2817 @@@
 +/*-
 + * Copyright (c) 2008-2014 Michihiro NAKAJIMA
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + */
 +
 +#include "archive_platform.h"
 +
 +#ifdef HAVE_ERRNO_H
 +#include <errno.h>
 +#endif
 +#ifdef HAVE_LIMITS_H
 +#include <limits.h>
 +#endif
 +#ifdef HAVE_STDLIB_H
 +#include <stdlib.h>
 +#endif
 +#ifdef HAVE_STRING_H
 +#include <string.h>
 +#endif
 +
 +#include "archive.h"
 +#include "archive_entry.h"
 +#include "archive_entry_locale.h"
 +#include "archive_private.h"
 +#include "archive_read_private.h"
 +#include "archive_endian.h"
 +
 +
 +#define MAXMATCH		256	/* Maximum match length. */
 +#define MINMATCH		3	/* Minimum match length. */
 +/*
 + * Literal table format:
 + * +0              +256                      +510
 + * +---------------+-------------------------+
 + * | literal code  |       match length      |
 + * |   0 ... 255   |  MINMATCH ... MAXMATCH  |
 + * +---------------+-------------------------+
 + *  <---          LT_BITLEN_SIZE         --->
 + */
 +/* Literal table size. */
 +#define LT_BITLEN_SIZE		(UCHAR_MAX + 1 + MAXMATCH - MINMATCH + 1)
 +/* Position table size.
 + * Note: this used for both position table and pre literal table.*/
 +#define PT_BITLEN_SIZE		(3 + 16)
 +
 +struct lzh_dec {
 +	/* Decoding status. */
 +	int     		 state;
 +
 +	/*
 +	 * Window to see last 8Ki(lh5),32Ki(lh6),64Ki(lh7) bytes of decoded
 +	 * data.
 +	 */
 +	int			 w_size;
 +	int			 w_mask;
 +	/* Window buffer, which is a loop buffer. */
 +	unsigned char		*w_buff;
 +	/* The insert position to the window. */
 +	int			 w_pos;
 +	/* The position where we can copy decoded code from the window. */
 +	int     		 copy_pos;
 +	/* The length how many bytes we can copy decoded code from
 +	 * the window. */
 +	int     		 copy_len;
 +
 +	/*
 +	 * Bit stream reader.
 +	 */
 +	struct lzh_br {
 +#define CACHE_TYPE		uint64_t
 +#define CACHE_BITS		(8 * sizeof(CACHE_TYPE))
 +	 	/* Cache buffer. */
 +		CACHE_TYPE	 cache_buffer;
 +		/* Indicates how many bits avail in cache_buffer. */
 +		int		 cache_avail;
 +	} br;
 +
 +	/*
 +	 * Huffman coding.
 +	 */
 +	struct huffman {
 +		int		 len_size;
 +		int		 len_avail;
 +		int		 len_bits;
 +		int		 freq[17];
 +		unsigned char	*bitlen;
 +
 +		/*
 +		 * Use a index table. It's faster than searching a huffman
 +		 * coding tree, which is a binary tree. But a use of a large
 +		 * index table causes L1 cache read miss many times.
 +		 */
 +#define HTBL_BITS	10
 +		int		 max_bits;
 +		int		 shift_bits;
 +		int		 tbl_bits;
 +		int		 tree_used;
 +		int		 tree_avail;
 +		/* Direct access table. */
 +		uint16_t	*tbl;
 +		/* Binary tree table for extra bits over the direct access. */
 +		struct htree_t {
 +			uint16_t left;
 +			uint16_t right;
 +		}		*tree;
 +	}			 lt, pt;
 +
 +	int			 blocks_avail;
 +	int			 pos_pt_len_size;
 +	int			 pos_pt_len_bits;
 +	int			 literal_pt_len_size;
 +	int			 literal_pt_len_bits;
 +	int			 reading_position;
 +	int			 loop;
 +	int			 error;
 +};
 +
 +struct lzh_stream {
 +	const unsigned char	*next_in;
 +	int			 avail_in;
 +	int64_t			 total_in;
 +	const unsigned char	*ref_ptr;
 +	int			 avail_out;
 +	int64_t			 total_out;
 +	struct lzh_dec		*ds;
 +};
 +
 +struct lha {
 +	/* entry_bytes_remaining is the number of bytes we expect.	    */
 +	int64_t                  entry_offset;
 +	int64_t                  entry_bytes_remaining;
 +	int64_t			 entry_unconsumed;
 +	uint16_t		 entry_crc_calculated;
 + 
 +	size_t			 header_size;	/* header size		    */
 +	unsigned char		 level;		/* header level		    */
 +	char			 method[3];	/* compress type	    */
 +	int64_t			 compsize;	/* compressed data size	    */
 +	int64_t			 origsize;	/* original file size	    */
 +	int			 setflag;
 +#define BIRTHTIME_IS_SET	1
 +#define ATIME_IS_SET		2
 +#define UNIX_MODE_IS_SET	4
 +#define CRC_IS_SET		8
 +	time_t			 birthtime;
 +	long			 birthtime_tv_nsec;
 +	time_t			 mtime;
 +	long			 mtime_tv_nsec;
 +	time_t			 atime;
 +	long			 atime_tv_nsec;
 +	mode_t			 mode;
 +	int64_t			 uid;
 +	int64_t			 gid;
 +	struct archive_string 	 uname;
 +	struct archive_string 	 gname;
 +	uint16_t		 header_crc;
 +	uint16_t		 crc;
 +	struct archive_string_conv *sconv;
 +	struct archive_string_conv *opt_sconv;
 +
 +	struct archive_string 	 dirname;
 +	struct archive_string 	 filename;
 +	struct archive_wstring	 ws;
 +
 +	unsigned char		 dos_attr;
 +
 +	/* Flag to mark progress that an archive was read their first header.*/
 +	char			 found_first_header;
 +	/* Flag to mark that indicates an empty directory. */
 +	char			 directory;
 +
 +	/* Flags to mark progress of decompression. */
 +	char			 decompress_init;
 +	char			 end_of_entry;
 +	char			 end_of_entry_cleanup;
 +	char			 entry_is_compressed;
 +
 +	char			 format_name[64];
 +
 +	struct lzh_stream	 strm;
 +};
 +
 +/*
 + * LHA header common member offset.
 + */
 +#define H_METHOD_OFFSET	2	/* Compress type. */
 +#define H_ATTR_OFFSET	19	/* DOS attribute. */
 +#define H_LEVEL_OFFSET	20	/* Header Level.  */
 +#define H_SIZE		22	/* Minimum header size. */
 +
 +static int      archive_read_format_lha_bid(struct archive_read *, int);
 +static int      archive_read_format_lha_options(struct archive_read *,
 +		    const char *, const char *);
 +static int	archive_read_format_lha_read_header(struct archive_read *,
 +		    struct archive_entry *);
 +static int	archive_read_format_lha_read_data(struct archive_read *,
 +		    const void **, size_t *, int64_t *);
 +static int	archive_read_format_lha_read_data_skip(struct archive_read *);
 +static int	archive_read_format_lha_cleanup(struct archive_read *);
 +
 +static void	lha_replace_path_separator(struct lha *,
 +		    struct archive_entry *);
 +static int	lha_read_file_header_0(struct archive_read *, struct lha *);
 +static int	lha_read_file_header_1(struct archive_read *, struct lha *);
 +static int	lha_read_file_header_2(struct archive_read *, struct lha *);
 +static int	lha_read_file_header_3(struct archive_read *, struct lha *);
 +static int	lha_read_file_extended_header(struct archive_read *,
 +		    struct lha *, uint16_t *, int, size_t, size_t *);
 +static size_t	lha_check_header_format(const void *);
 +static int	lha_skip_sfx(struct archive_read *);
 +static time_t	lha_dos_time(const unsigned char *);
 +static time_t	lha_win_time(uint64_t, long *);
 +static unsigned char	lha_calcsum(unsigned char, const void *,
 +		    int, size_t);
 +static int	lha_parse_linkname(struct archive_string *,
 +		    struct archive_string *);
 +static int	lha_read_data_none(struct archive_read *, const void **,
 +		    size_t *, int64_t *);
 +static int	lha_read_data_lzh(struct archive_read *, const void **,
 +		    size_t *, int64_t *);
 +static void	lha_crc16_init(void);
 +static uint16_t lha_crc16(uint16_t, const void *, size_t);
 +static int	lzh_decode_init(struct lzh_stream *, const char *);
 +static void	lzh_decode_free(struct lzh_stream *);
 +static int	lzh_decode(struct lzh_stream *, int);
 +static int	lzh_br_fillup(struct lzh_stream *, struct lzh_br *);
 +static int	lzh_huffman_init(struct huffman *, size_t, int);
 +static void	lzh_huffman_free(struct huffman *);
 +static int	lzh_read_pt_bitlen(struct lzh_stream *, int start, int end);
 +static int	lzh_make_fake_table(struct huffman *, uint16_t);
 +static int	lzh_make_huffman_table(struct huffman *);
 +static inline int lzh_decode_huffman(struct huffman *, unsigned);
 +static int	lzh_decode_huffman_tree(struct huffman *, unsigned, int);
 +
 +
 +int
 +archive_read_support_format_lha(struct archive *_a)
 +{
 +	struct archive_read *a = (struct archive_read *)_a;
 +	struct lha *lha;
 +	int r;
 +
 +	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
 +	    ARCHIVE_STATE_NEW, "archive_read_support_format_lha");
 +
 +	lha = (struct lha *)calloc(1, sizeof(*lha));
 +	if (lha == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate lha data");
 +		return (ARCHIVE_FATAL);
 +	}
 +	archive_string_init(&lha->ws);
 +
 +	r = __archive_read_register_format(a,
 +	    lha,
 +	    "lha",
 +	    archive_read_format_lha_bid,
 +	    archive_read_format_lha_options,
 +	    archive_read_format_lha_read_header,
 +	    archive_read_format_lha_read_data,
 +	    archive_read_format_lha_read_data_skip,
 +	    NULL,
 +	    archive_read_format_lha_cleanup,
 +	    NULL,
 +	    NULL);
 +
 +	if (r != ARCHIVE_OK)
 +		free(lha);
 +	return (ARCHIVE_OK);
 +}
 +
 +static size_t
 +lha_check_header_format(const void *h)
 +{
 +	const unsigned char *p = h;
 +	size_t next_skip_bytes;
 +
 +	switch (p[H_METHOD_OFFSET+3]) {
 +	/*
 +	 * "-lh0-" ... "-lh7-" "-lhd-"
 +	 * "-lzs-" "-lz5-"
 +	 */
 +	case '0': case '1': case '2': case '3':
 +	case '4': case '5': case '6': case '7':
 +	case 'd':
 +	case 's':
 +		next_skip_bytes = 4;
 +
 +		/* b0 == 0 means the end of an LHa archive file.	*/
 +		if (p[0] == 0)
 +			break;
 +		if (p[H_METHOD_OFFSET] != '-' || p[H_METHOD_OFFSET+1] != 'l'
 +		    ||  p[H_METHOD_OFFSET+4] != '-')
 +			break;
 +
 +		if (p[H_METHOD_OFFSET+2] == 'h') {
 +			/* "-lh?-" */
 +			if (p[H_METHOD_OFFSET+3] == 's')
 +				break;
 +			if (p[H_LEVEL_OFFSET] == 0)
 +				return (0);
 +			if (p[H_LEVEL_OFFSET] <= 3 && p[H_ATTR_OFFSET] == 0x20)
 +				return (0);
 +		}
 +		if (p[H_METHOD_OFFSET+2] == 'z') {
 +			/* LArc extensions: -lzs-,-lz4- and -lz5- */
 +			if (p[H_LEVEL_OFFSET] != 0)
 +				break;
 +			if (p[H_METHOD_OFFSET+3] == 's'
 +			    || p[H_METHOD_OFFSET+3] == '4'
 +			    || p[H_METHOD_OFFSET+3] == '5')
 +				return (0);
 +		}
 +		break;
 +	case 'h': next_skip_bytes = 1; break;
 +	case 'z': next_skip_bytes = 1; break;
 +	case 'l': next_skip_bytes = 2; break;
 +	case '-': next_skip_bytes = 3; break;
 +	default : next_skip_bytes = 4; break;
 +	}
 +
 +	return (next_skip_bytes);
 +}
 +
 +static int
 +archive_read_format_lha_bid(struct archive_read *a, int best_bid)
 +{
 +	const char *p;
 +	const void *buff;
 +	ssize_t bytes_avail, offset, window;
 +	size_t next;
 +
 +	/* If there's already a better bid than we can ever
 +	   make, don't bother testing. */
 +	if (best_bid > 30)
 +		return (-1);
 +
 +	if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL)
 +		return (-1);
 +
 +	if (lha_check_header_format(p) == 0)
 +		return (30);
 +
 +	if (p[0] == 'M' && p[1] == 'Z') {
 +		/* PE file */
 +		offset = 0;
 +		window = 4096;
 +		while (offset < (1024 * 20)) {
 +			buff = __archive_read_ahead(a, offset + window,
 +			    &bytes_avail);
 +			if (buff == NULL) {
 +				/* Remaining bytes are less than window. */
 +				window >>= 1;
 +				if (window < (H_SIZE + 3))
 +					return (0);
 +				continue;
 +			}
 +			p = (const char *)buff + offset;
 +			while (p + H_SIZE < (const char *)buff + bytes_avail) {
 +				if ((next = lha_check_header_format(p)) == 0)
 +					return (30);
 +				p += next;
 +			}
 +			offset = p - (const char *)buff;
 +		}
 +	}
 +	return (0);
 +}
 +
 +static int
 +archive_read_format_lha_options(struct archive_read *a,
 +    const char *key, const char *val)
 +{
 +	struct lha *lha;
 +	int ret = ARCHIVE_FAILED;
 +
 +	lha = (struct lha *)(a->format->data);
 +	if (strcmp(key, "hdrcharset")  == 0) {
 +		if (val == NULL || val[0] == 0)
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "lha: hdrcharset option needs a character-set name");
 +		else {
 +			lha->opt_sconv =
 +			    archive_string_conversion_from_charset(
 +				&a->archive, val, 0);
 +			if (lha->opt_sconv != NULL)
 +				ret = ARCHIVE_OK;
 +			else
 +				ret = ARCHIVE_FATAL;
 +		}
 +		return (ret);
 +	}
 +
 +	/* Note: The "warn" return is just to inform the options
 +	 * supervisor that we didn't handle it.  It will generate
 +	 * a suitable error if no one used this option. */
 +	return (ARCHIVE_WARN);
 +}
 +
 +static int
 +lha_skip_sfx(struct archive_read *a)
 +{
 +	const void *h;
 +	const char *p, *q;
 +	size_t next, skip;
 +	ssize_t bytes, window;
 +
 +	window = 4096;
 +	for (;;) {
 +		h = __archive_read_ahead(a, window, &bytes);
 +		if (h == NULL) {
 +			/* Remaining bytes are less than window. */
 +			window >>= 1;
 +			if (window < (H_SIZE + 3))
 +				goto fatal;
 +			continue;
 +		}
 +		if (bytes < H_SIZE)
 +			goto fatal;
 +		p = h;
 +		q = p + bytes;
 +
 +		/*
 +		 * Scan ahead until we find something that looks
 +		 * like the lha header.
 +		 */
 +		while (p + H_SIZE < q) {
 +			if ((next = lha_check_header_format(p)) == 0) {
 +				skip = p - (const char *)h;
 +				__archive_read_consume(a, skip);
 +				return (ARCHIVE_OK);
 +			}
 +			p += next;
 +		}
 +		skip = p - (const char *)h;
 +		__archive_read_consume(a, skip);
 +	}
 +fatal:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Couldn't find out LHa header");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +static int
 +truncated_error(struct archive_read *a)
 +{
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Truncated LHa header");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +static int
 +archive_read_format_lha_read_header(struct archive_read *a,
 +    struct archive_entry *entry)
 +{
 +	struct archive_string linkname;
 +	struct archive_string pathname;
 +	struct lha *lha;
 +	const unsigned char *p;
 +	const char *signature;
 +	int err;
 +	
 +	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 = lha->opt_sconv;
 +	else
 +		lha->sconv = 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.
 +	 */
 +	archive_string_concat(&lha->dirname, &lha->filename);
 +	archive_string_init(&pathname);
 +	archive_string_init(&linkname);
 +	archive_string_copy(&pathname, &lha->dirname);
 +
 +	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_string_free(&pathname);
 +			archive_string_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.
 +	 */
 +	if (archive_entry_copy_pathname_l(entry, pathname.s,
 +	    pathname.length, lha->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(lha->sconv));
 +		err = ARCHIVE_WARN;
 +	}
 +	archive_string_free(&pathname);
 +	if (archive_strlen(&linkname) > 0) {
 +		if (archive_entry_copy_symlink_l(entry, linkname.s,
 +		    linkname.length, lha->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(lha->sconv));
 +			err = ARCHIVE_WARN;
 +		}
 +	} else
 +		archive_entry_set_symlink(entry, NULL);
 +	archive_string_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;
 +	lha->entry_offset = 0;
 +	lha->entry_crc_calculated = 0;
 +
 +	/*
 +	 * This file does not have a content.
 +	 */
 +	if (lha->directory || lha->compsize == 0)
 +		lha->end_of_entry = 1;
 +
 +	sprintf(lha->format_name, "lha -%c%c%c-",
 +	    lha->method[0], lha->method[1], lha->method[2]);
 +	a->archive.archive_format_name = lha->format_name;
 +
 +	return (err);
 +}
 +
 +/*
 + * Replace a DOS path separator '\' by a character '/'.
 + * Some multi-byte character set have  a character '\' in its second byte.
 + */
 +static void
 +lha_replace_path_separator(struct lha *lha, struct archive_entry *entry)
 +{
 +	const wchar_t *wp;
 +	size_t i;
 +
 +	if ((wp = archive_entry_pathname_w(entry)) != NULL) {
 +		archive_wstrcpy(&(lha->ws), wp);
 +		for (i = 0; i < archive_strlen(&(lha->ws)); i++) {
 +			if (lha->ws.s[i] == L'\\')
 +				lha->ws.s[i] = L'/';
 +		}
 +		archive_entry_copy_pathname_w(entry, lha->ws.s);
 +	}
 +
 +	if ((wp = archive_entry_symlink_w(entry)) != NULL) {
 +		archive_wstrcpy(&(lha->ws), wp);
 +		for (i = 0; i < archive_strlen(&(lha->ws)); i++) {
 +			if (lha->ws.s[i] == L'\\')
 +				lha->ws.s[i] = L'/';
 +		}
 +		archive_entry_copy_symlink_w(entry, lha->ws.s);
 +	}
 +}
 +
 +/*
 + * Header 0 format
 + *
 + * +0              +1         +2               +7                  +11
 + * +---------------+----------+----------------+-------------------+
 + * |header size(*1)|header sum|compression type|compressed size(*2)|
 + * +---------------+----------+----------------+-------------------+
 + *                             <---------------------(*1)----------*
 + *
 + * +11               +15       +17       +19            +20              +21
 + * +-----------------+---------+---------+--------------+----------------+
 + * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=0)|
 + * +-----------------+---------+---------+--------------+----------------+
 + * *--------------------------------(*1)---------------------------------*
 + *
 + * +21             +22       +22+(*3)   +22+(*3)+2       +22+(*3)+2+(*4)
 + * +---------------+---------+----------+----------------+------------------+
 + * |name length(*3)|file name|file CRC16|extra header(*4)|  compressed data |
 + * +---------------+---------+----------+----------------+------------------+
 + *                  <--(*3)->                             <------(*2)------>
 + * *----------------------(*1)-------------------------->
 + *
 + */
 +#define H0_HEADER_SIZE_OFFSET	0
 +#define H0_HEADER_SUM_OFFSET	1
 +#define H0_COMP_SIZE_OFFSET	7
 +#define H0_ORIG_SIZE_OFFSET	11
 +#define H0_DOS_TIME_OFFSET	15
 +#define H0_NAME_LEN_OFFSET	21
 +#define H0_FILE_NAME_OFFSET	22
 +#define H0_FIXED_SIZE		24
 +static int
 +lha_read_file_header_0(struct archive_read *a, struct lha *lha)
 +{
 +	const unsigned char *p;
 +	int extdsize, namelen;
 +	unsigned char headersum, sum_calculated;
 +
 +	if ((p = __archive_read_ahead(a, H0_FIXED_SIZE, NULL)) == NULL)
 +		return (truncated_error(a));
 +	lha->header_size = p[H0_HEADER_SIZE_OFFSET] + 2;
 +	headersum = p[H0_HEADER_SUM_OFFSET];
 +	lha->compsize = archive_le32dec(p + H0_COMP_SIZE_OFFSET);
 +	lha->origsize = archive_le32dec(p + H0_ORIG_SIZE_OFFSET);
 +	lha->mtime = lha_dos_time(p + H0_DOS_TIME_OFFSET);
 +	namelen = p[H0_NAME_LEN_OFFSET];
 +	extdsize = (int)lha->header_size - H0_FIXED_SIZE - namelen;
 +	if ((namelen > 221 || extdsize < 0) && extdsize != -2) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Invalid LHa header");
 +		return (ARCHIVE_FATAL);
 +	}
 +	if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)
 +		return (truncated_error(a));
 +
 +	archive_strncpy(&lha->filename, p + H0_FILE_NAME_OFFSET, namelen);
 +	/* When extdsize == -2, A CRC16 value is not present in the header. */
 +	if (extdsize >= 0) {
 +		lha->crc = archive_le16dec(p + H0_FILE_NAME_OFFSET + namelen);
 +		lha->setflag |= CRC_IS_SET;
 +	}
 +	sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2);
 +
 +	/* Read an extended header */
 +	if (extdsize > 0) {
 +		/* This extended data is set by 'LHa for UNIX' only.
 +		 * Maybe fixed size.
 +		 */
 +		p += H0_FILE_NAME_OFFSET + namelen + 2;
 +		if (p[0] == 'U' && extdsize == 12) {
 +			/* p[1] is a minor version. */
 +			lha->mtime = archive_le32dec(&p[2]);
 +			lha->mode = archive_le16dec(&p[6]);
 +			lha->uid = archive_le16dec(&p[8]);
 +			lha->gid = archive_le16dec(&p[10]);
 +			lha->setflag |= UNIX_MODE_IS_SET;
 +		}
 +	}
 +	__archive_read_consume(a, lha->header_size);
 +
 +	if (sum_calculated != headersum) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "LHa header sum error");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Header 1 format
 + *
 + * +0              +1         +2               +7            +11
 + * +---------------+----------+----------------+-------------+
 + * |header size(*1)|header sum|compression type|skip size(*2)|
 + * +---------------+----------+----------------+-------------+
 + *                             <---------------(*1)----------*
 + *
 + * +11               +15       +17       +19            +20              +21
 + * +-----------------+---------+---------+--------------+----------------+
 + * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=1)|
 + * +-----------------+---------+---------+--------------+----------------+
 + * *-------------------------------(*1)----------------------------------*
 + *
 + * +21             +22       +22+(*3)   +22+(*3)+2  +22+(*3)+3  +22+(*3)+3+(*4)
 + * +---------------+---------+----------+-----------+-----------+
 + * |name length(*3)|file name|file CRC16|  creator  |padding(*4)|
 + * +---------------+---------+----------+-----------+-----------+
 + *                  <--(*3)->
 + * *----------------------------(*1)----------------------------*
 + *
 + * +22+(*3)+3+(*4)  +22+(*3)+3+(*4)+2     +22+(*3)+3+(*4)+2+(*5)
 + * +----------------+---------------------+------------------------+
 + * |next header size| extended header(*5) |     compressed data    |
 + * +----------------+---------------------+------------------------+
 + * *------(*1)-----> <--------------------(*2)-------------------->
 + */
 +#define H1_HEADER_SIZE_OFFSET	0
 +#define H1_HEADER_SUM_OFFSET	1
 +#define H1_COMP_SIZE_OFFSET	7
 +#define H1_ORIG_SIZE_OFFSET	11
 +#define H1_DOS_TIME_OFFSET	15
 +#define H1_NAME_LEN_OFFSET	21
 +#define H1_FILE_NAME_OFFSET	22
 +#define H1_FIXED_SIZE		27
 +static int
 +lha_read_file_header_1(struct archive_read *a, struct lha *lha)
 +{
 +	const unsigned char *p;
 +	size_t extdsize;
 +	int i, err, err2;
 +	int namelen, padding;
 +	unsigned char headersum, sum_calculated;
 +
 +	err = ARCHIVE_OK;
 +
 +	if ((p = __archive_read_ahead(a, H1_FIXED_SIZE, NULL)) == NULL)
 +		return (truncated_error(a));
 +
 +	lha->header_size = p[H1_HEADER_SIZE_OFFSET] + 2;
 +	headersum = p[H1_HEADER_SUM_OFFSET];
 +	/* Note: An extended header size is included in a compsize. */
 +	lha->compsize = archive_le32dec(p + H1_COMP_SIZE_OFFSET);
 +	lha->origsize = archive_le32dec(p + H1_ORIG_SIZE_OFFSET);
 +	lha->mtime = lha_dos_time(p + H1_DOS_TIME_OFFSET);
 +	namelen = p[H1_NAME_LEN_OFFSET];
 +	/* Calculate a padding size. The result will be normally 0 only(?) */
 +	padding = ((int)lha->header_size) - H1_FIXED_SIZE - namelen;
 +
 +	if (namelen > 230 || padding < 0)
 +		goto invalid;
 +
 +	if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)
 +		return (truncated_error(a));
 +
 +	for (i = 0; i < namelen; i++) {
 +		if (p[i + H1_FILE_NAME_OFFSET] == 0xff)
 +			goto invalid;/* Invalid filename. */
 +	}
 +	archive_strncpy(&lha->filename, p + H1_FILE_NAME_OFFSET, namelen);
 +	lha->crc = archive_le16dec(p + H1_FILE_NAME_OFFSET + namelen);
 +	lha->setflag |= CRC_IS_SET;
 +
 +	sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2);
 +	/* Consume used bytes but not include `next header size' data
 +	 * since it will be consumed in lha_read_file_extended_header(). */
 +	__archive_read_consume(a, lha->header_size - 2);
 +
 +	/* Read extended headers */
 +	err2 = lha_read_file_extended_header(a, lha, NULL, 2,
 +	    (size_t)(lha->compsize + 2), &extdsize);
 +	if (err2 < ARCHIVE_WARN)
 +		return (err2);
 +	if (err2 < err)
 +		err = err2;
 +	/* Get a real compressed file size. */
 +	lha->compsize -= extdsize - 2;
 +
 +	if (lha->compsize < 0)
 +		goto invalid;	/* Invalid compressed file size */
 +
 +	if (sum_calculated != headersum) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "LHa header sum error");
 +		return (ARCHIVE_FATAL);
 +	}
 +	return (err);
 +invalid:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Invalid LHa header");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +/*
 + * Header 2 format
 + *
 + * +0              +2               +7                  +11               +15
 + * +---------------+----------------+-------------------+-----------------+
 + * |header size(*1)|compression type|compressed size(*2)|uncompressed size|
 + * +---------------+----------------+-------------------+-----------------+
 + *  <--------------------------------(*1)---------------------------------*
 + *
 + * +15               +19          +20              +21        +23         +24
 + * +-----------------+------------+----------------+----------+-----------+
 + * |data/time(time_t)| 0x20 fixed |header level(=2)|file CRC16|  creator  |
 + * +-----------------+------------+----------------+----------+-----------+
 + * *---------------------------------(*1)---------------------------------*
 + *
 + * +24              +26                 +26+(*3)      +26+(*3)+(*4)
 + * +----------------+-------------------+-------------+-------------------+
 + * |next header size|extended header(*3)| padding(*4) |  compressed data  |
 + * +----------------+-------------------+-------------+-------------------+
 + * *--------------------------(*1)-------------------> <------(*2)------->
 + *
 + */
 +#define H2_HEADER_SIZE_OFFSET	0
 +#define H2_COMP_SIZE_OFFSET	7
 +#define H2_ORIG_SIZE_OFFSET	11
 +#define H2_TIME_OFFSET		15
 +#define H2_CRC_OFFSET		21
 +#define H2_FIXED_SIZE		24
 +static int
 +lha_read_file_header_2(struct archive_read *a, struct lha *lha)
 +{
 +	const unsigned char *p;
 +	size_t extdsize;
 +	int err, padding;
 +	uint16_t header_crc;
 +
 +	if ((p = __archive_read_ahead(a, H2_FIXED_SIZE, NULL)) == NULL)
 +		return (truncated_error(a));
 +
 +	lha->header_size =archive_le16dec(p + H2_HEADER_SIZE_OFFSET);
 +	lha->compsize = archive_le32dec(p + H2_COMP_SIZE_OFFSET);
 +	lha->origsize = archive_le32dec(p + H2_ORIG_SIZE_OFFSET);
 +	lha->mtime = archive_le32dec(p + H2_TIME_OFFSET);
 +	lha->crc = archive_le16dec(p + H2_CRC_OFFSET);
 +	lha->setflag |= CRC_IS_SET;
 +
 +	if (lha->header_size < H2_FIXED_SIZE) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Invalid LHa header size");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	header_crc = lha_crc16(0, p, H2_FIXED_SIZE);
 +	__archive_read_consume(a, H2_FIXED_SIZE);
 +
 +	/* Read extended headers */
 +	err = lha_read_file_extended_header(a, lha, &header_crc, 2,
 +		  lha->header_size - H2_FIXED_SIZE, &extdsize);
 +	if (err < ARCHIVE_WARN)
 +		return (err);
 +
 +	/* Calculate a padding size. The result will be normally 0 or 1. */
 +	padding = (int)lha->header_size - (int)(H2_FIXED_SIZE + extdsize);
 +	if (padding > 0) {
 +		if ((p = __archive_read_ahead(a, padding, NULL)) == NULL)
 +			return (truncated_error(a));
 +		header_crc = lha_crc16(header_crc, p, padding);
 +		__archive_read_consume(a, padding);
 +	}
 +
 +	if (header_crc != lha->header_crc) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "LHa header CRC error");
 +		return (ARCHIVE_FATAL);
 +	}
 +	return (err);
 +}
 +
 +/*
 + * Header 3 format
 + *
 + * +0           +2               +7                  +11               +15
 + * +------------+----------------+-------------------+-----------------+
 + * | 0x04 fixed |compression type|compressed size(*2)|uncompressed size|
 + * +------------+----------------+-------------------+-----------------+
 + *  <-------------------------------(*1)-------------------------------*
 + *
 + * +15               +19          +20              +21        +23         +24
 + * +-----------------+------------+----------------+----------+-----------+
 + * |date/time(time_t)| 0x20 fixed |header level(=3)|file CRC16|  creator  |
 + * +-----------------+------------+----------------+----------+-----------+
 + * *--------------------------------(*1)----------------------------------*
 + *
 + * +24             +28              +32                 +32+(*3)
 + * +---------------+----------------+-------------------+-----------------+
 + * |header size(*1)|next header size|extended header(*3)| compressed data |
 + * +---------------+----------------+-------------------+-----------------+
 + * *------------------------(*1)-----------------------> <------(*2)----->
 + *
 + */
 +#define H3_FIELD_LEN_OFFSET	0
 +#define H3_COMP_SIZE_OFFSET	7
 +#define H3_ORIG_SIZE_OFFSET	11
 +#define H3_TIME_OFFSET		15
 +#define H3_CRC_OFFSET		21
 +#define H3_HEADER_SIZE_OFFSET	24
 +#define H3_FIXED_SIZE		28
 +static int
 +lha_read_file_header_3(struct archive_read *a, struct lha *lha)
 +{
 +	const unsigned char *p;
 +	size_t extdsize;
 +	int err;
 +	uint16_t header_crc;
 +
 +	if ((p = __archive_read_ahead(a, H3_FIXED_SIZE, NULL)) == NULL)
 +		return (truncated_error(a));
 +
 +	if (archive_le16dec(p + H3_FIELD_LEN_OFFSET) != 4)
 +		goto invalid;
 +	lha->header_size =archive_le32dec(p + H3_HEADER_SIZE_OFFSET);
 +	lha->compsize = archive_le32dec(p + H3_COMP_SIZE_OFFSET);
 +	lha->origsize = archive_le32dec(p + H3_ORIG_SIZE_OFFSET);
 +	lha->mtime = archive_le32dec(p + H3_TIME_OFFSET);
 +	lha->crc = archive_le16dec(p + H3_CRC_OFFSET);
 +	lha->setflag |= CRC_IS_SET;
 +
 +	if (lha->header_size < H3_FIXED_SIZE + 4)
 +		goto invalid;
 +	header_crc = lha_crc16(0, p, H3_FIXED_SIZE);
 +	__archive_read_consume(a, H3_FIXED_SIZE);
 +
 +	/* Read extended headers */
 +	err = lha_read_file_extended_header(a, lha, &header_crc, 4,
 +		  lha->header_size - H3_FIXED_SIZE, &extdsize);
 +	if (err < ARCHIVE_WARN)
 +		return (err);
 +
 +	if (header_crc != lha->header_crc) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "LHa header CRC error");
 +		return (ARCHIVE_FATAL);
 +	}
 +	return (err);
 +invalid:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Invalid LHa header");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +/*
 + * Extended header format
 + *
 + * +0             +2        +3  -- used in header 1 and 2
 + * +0             +4        +5  -- used in header 3
 + * +--------------+---------+-------------------+--------------+--
 + * |ex-header size|header id|        data       |ex-header size| .......
 + * +--------------+---------+-------------------+--------------+--
 + *  <-------------( ex-header size)------------> <-- next extended header --*
 + *
 + * If the ex-header size is zero, it is the make of the end of extended
 + * headers.
 + *
 + */
 +static int
 +lha_read_file_extended_header(struct archive_read *a, struct lha *lha,
 +    uint16_t *crc, int sizefield_length, size_t limitsize, size_t *total_size)
 +{
 +	const void *h;
 +	const unsigned char *extdheader;
 +	size_t	extdsize;
 +	size_t	datasize;
 +	unsigned int i;
 +	unsigned char extdtype;
 +
 +#define EXT_HEADER_CRC		0x00		/* Header CRC and information*/
 +#define EXT_FILENAME		0x01		/* Filename 		    */
 +#define EXT_DIRECTORY		0x02		/* Directory name	    */
 +#define EXT_DOS_ATTR		0x40		/* MS-DOS attribute	    */
 +#define EXT_TIMESTAMP		0x41		/* Windows time stamp	    */
 +#define EXT_FILESIZE		0x42		/* Large file size	    */
 +#define EXT_TIMEZONE		0x43		/* Time zone		    */
 +#define EXT_UTF16_FILENAME	0x44		/* UTF-16 filename 	    */
 +#define EXT_UTF16_DIRECTORY	0x45		/* UTF-16 directory name    */
 +#define EXT_CODEPAGE		0x46		/* Codepage		    */
 +#define EXT_UNIX_MODE		0x50		/* File permission	    */
 +#define EXT_UNIX_GID_UID	0x51		/* gid,uid		    */
 +#define EXT_UNIX_GNAME		0x52		/* Group name		    */
 +#define EXT_UNIX_UNAME		0x53		/* User name		    */
 +#define EXT_UNIX_MTIME		0x54		/* Modified time	    */
 +#define EXT_OS2_NEW_ATTR	0x7f		/* new attribute(OS/2 only) */
 +#define EXT_NEW_ATTR		0xff		/* new attribute	    */
 +
 +	*total_size = sizefield_length;
 +
 +	for (;;) {
 +		/* Read an extended header size. */
 +		if ((h =
 +		    __archive_read_ahead(a, sizefield_length, NULL)) == NULL)
 +			return (truncated_error(a));
 +		/* Check if the size is the zero indicates the end of the
 +		 * extended header. */
 +		if (sizefield_length == sizeof(uint16_t))
 +			extdsize = archive_le16dec(h);
 +		else
 +			extdsize = archive_le32dec(h);
 +		if (extdsize == 0) {
 +			/* End of extended header */
 +			if (crc != NULL)
 +				*crc = lha_crc16(*crc, h, sizefield_length);
 +			__archive_read_consume(a, sizefield_length);
 +			return (ARCHIVE_OK);
 +		}
 +
 +		/* Sanity check to the extended header size. */
 +		if (((uint64_t)*total_size + extdsize) >
 +				    (uint64_t)limitsize ||
 +		    extdsize <= (size_t)sizefield_length)
 +			goto invalid;
 +
 +		/* Read the extended header. */
 +		if ((h = __archive_read_ahead(a, extdsize, NULL)) == NULL)
 +			return (truncated_error(a));
 +		*total_size += extdsize;
 +
 +		extdheader = (const unsigned char *)h;
 +		/* Get the extended header type. */
 +		extdtype = extdheader[sizefield_length];
 +		/* Calculate an extended data size. */
 +		datasize = extdsize - (1 + sizefield_length);
 +		/* Skip an extended header size field and type field. */
 +		extdheader += sizefield_length + 1;
 +
 +		if (crc != NULL && extdtype != EXT_HEADER_CRC)
 +			*crc = lha_crc16(*crc, h, extdsize);
 +		switch (extdtype) {
 +		case EXT_HEADER_CRC:
 +			/* We only use a header CRC. Following data will not
 +			 * be used. */
 +			if (datasize >= 2) {
 +				lha->header_crc = archive_le16dec(extdheader);
 +				if (crc != NULL) {
 +					static const char zeros[2] = {0, 0};
 +					*crc = lha_crc16(*crc, h,
 +					    extdsize - datasize);
 +					/* CRC value itself as zero */
 +					*crc = lha_crc16(*crc, zeros, 2);
 +					*crc = lha_crc16(*crc,
 +					    extdheader+2, datasize - 2);
 +				}
 +			}
 +			break;
 +		case EXT_FILENAME:
 +			if (datasize == 0) {
 +				/* maybe directory header */
 +				archive_string_empty(&lha->filename);
 +				break;
 +			}
 +			if (extdheader[0] == '\0')
 +				goto invalid;
 +			archive_strncpy(&lha->filename,
 +			    (const char *)extdheader, datasize);
 +			break;
 +		case EXT_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_DOS_ATTR:
 +			if (datasize == 2)
 +				lha->dos_attr = (unsigned char)
 +				    (archive_le16dec(extdheader) & 0xff);
 +			break;
 +		case EXT_TIMESTAMP:
 +			if (datasize == (sizeof(uint64_t) * 3)) {
 +				lha->birthtime = lha_win_time(
 +				    archive_le64dec(extdheader),
 +				    &lha->birthtime_tv_nsec);
 +				extdheader += sizeof(uint64_t);
 +				lha->mtime = lha_win_time(
 +				    archive_le64dec(extdheader),
 +				    &lha->mtime_tv_nsec);
 +				extdheader += sizeof(uint64_t);
 +				lha->atime = lha_win_time(
 +				    archive_le64dec(extdheader),
 +				    &lha->atime_tv_nsec);
 +				lha->setflag |= BIRTHTIME_IS_SET |
 +				    ATIME_IS_SET;
 +			}
 +			break;
 +		case EXT_FILESIZE:
 +			if (datasize == sizeof(uint64_t) * 2) {
 +				lha->compsize = archive_le64dec(extdheader);
 +				extdheader += sizeof(uint64_t);
 +				lha->origsize = archive_le64dec(extdheader);
 +			}
 +			break;
 +		case EXT_CODEPAGE:
 +			/* Get an archived filename charset from codepage.
 +			 * This overwrites the charset specified by
 +			 * hdrcharset option. */
 +			if (datasize == sizeof(uint32_t)) {
 +				struct archive_string cp;
 +				const char *charset;
 +
 +				archive_string_init(&cp);
 +				switch (archive_le32dec(extdheader)) {
 +				case 65001: /* UTF-8 */
 +					charset = "UTF-8";
 +					break;
 +				default:
 +					archive_string_sprintf(&cp, "CP%d",
 +					    (int)archive_le32dec(extdheader));
 +					charset = cp.s;
 +					break;
 +				}
 +				lha->sconv =
 +				    archive_string_conversion_from_charset(
 +					&(a->archive), charset, 1);
 +				archive_string_free(&cp);
 +				if (lha->sconv == 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 */
 +		case EXT_UTF16_FILENAME:	/* Not supported */
 +		case EXT_UTF16_DIRECTORY:	/* Not supported */
 +		default:
 +			break;
 +		}
 +
 +		__archive_read_consume(a, extdsize);
 +	}
 +invalid:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Invalid extended LHa header");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +static int
 +lha_end_of_entry(struct archive_read *a)
 +{
 +	struct lha *lha = (struct lha *)(a->format->data);
 +	int r = ARCHIVE_EOF;
 +
 +	if (!lha->end_of_entry_cleanup) {
 +		if ((lha->setflag & CRC_IS_SET) &&
 +		    lha->crc != lha->entry_crc_calculated) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "LHa data CRC error");
 +			r = ARCHIVE_WARN;
 +		}
 +
 +		/* End-of-entry cleanup done. */
 +		lha->end_of_entry_cleanup = 1;
 +	}
 +	return (r);
 +}
 +
 +static int
 +archive_read_format_lha_read_data(struct archive_read *a,
 +    const void **buff, size_t *size, int64_t *offset)
 +{
 +	struct lha *lha = (struct lha *)(a->format->data);
 +	int r;
 +
 +	if (lha->entry_unconsumed) {
 +		/* Consume as much as the decompressor actually used. */
 +		__archive_read_consume(a, lha->entry_unconsumed);
 +		lha->entry_unconsumed = 0;
 +	}
 +	if (lha->end_of_entry) {
 +		*offset = lha->entry_offset;
 +		*size = 0;
 +		*buff = NULL;
 +		return (lha_end_of_entry(a));
 +	}
 +
 +	if (lha->entry_is_compressed)
 +		r =  lha_read_data_lzh(a, buff, size, offset);
 +	else
 +		/* No compression. */
 +		r =  lha_read_data_none(a, buff, size, offset);
 +	return (r);
 +}
 +
 +/*
 + * Read a file content in no compression.
 + *
 + * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
 + * lha->end_of_entry if it consumes all of the data.
 + */
 +static int
 +lha_read_data_none(struct archive_read *a, const void **buff,
 +    size_t *size, int64_t *offset)
 +{
 +	struct lha *lha = (struct lha *)(a->format->data);
 +	ssize_t bytes_avail;
 +
 +	if (lha->entry_bytes_remaining == 0) {
 +		*buff = NULL;
 +		*size = 0;
 +		*offset = lha->entry_offset;
 +		lha->end_of_entry = 1;
 +		return (ARCHIVE_OK);
 +	}
 +	/*
 +	 * Note: '1' here is a performance optimization.
 +	 * Recall that the decompression layer returns a count of
 +	 * available bytes; asking for more than that forces the
 +	 * decompressor to combine reads by copying data.
 +	 */
 +	*buff = __archive_read_ahead(a, 1, &bytes_avail);
 +	if (bytes_avail <= 0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated LHa file data");
 +		return (ARCHIVE_FATAL);
 +	}
 +	if (bytes_avail > lha->entry_bytes_remaining)
 +		bytes_avail = (ssize_t)lha->entry_bytes_remaining;
 +	lha->entry_crc_calculated =
 +	    lha_crc16(lha->entry_crc_calculated, *buff, bytes_avail);
 +	*size = bytes_avail;
 +	*offset = lha->entry_offset;
 +	lha->entry_offset += bytes_avail;
 +	lha->entry_bytes_remaining -= bytes_avail;
 +	if (lha->entry_bytes_remaining == 0)
 +		lha->end_of_entry = 1;
 +	lha->entry_unconsumed = bytes_avail;
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Read a file content in LZHUFF encoding.
 + *
 + * Returns ARCHIVE_OK if successful, returns ARCHIVE_WARN if compression is
 + * unsupported, ARCHIVE_FATAL otherwise, sets lha->end_of_entry if it consumes
 + * all of the data.
 + */
 +static int
 +lha_read_data_lzh(struct archive_read *a, const void **buff,
 +    size_t *size, int64_t *offset)
 +{
 +	struct lha *lha = (struct lha *)(a->format->data);
 +	ssize_t bytes_avail;
 +	int r;
 +
 +	/* If we haven't yet read any data, initialize the decompressor. */
 +	if (!lha->decompress_init) {
 +		r = lzh_decode_init(&(lha->strm), lha->method);
 +		switch (r) {
 +		case ARCHIVE_OK:
 +			break;
 +		case ARCHIVE_FAILED:
 +        		/* Unsupported compression. */
 +			*buff = NULL;
 +			*size = 0;
 +			*offset = 0;
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Unsupported lzh compression method -%c%c%c-",
 +			    lha->method[0], lha->method[1], lha->method[2]);
 +			/* We know compressed size; just skip it. */
 +			archive_read_format_lha_read_data_skip(a);
 +			return (ARCHIVE_WARN);
 +		default:
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Couldn't allocate memory "
 +			    "for lzh decompression");
 +			return (ARCHIVE_FATAL);
 +		}
 +		/* We've initialized decompression for this stream. */
 +		lha->decompress_init = 1;
 +		lha->strm.avail_out = 0;
 +		lha->strm.total_out = 0;
 +	}
 +
 +	/*
 +	 * Note: '1' here is a performance optimization.
 +	 * Recall that the decompression layer returns a count of
 +	 * available bytes; asking for more than that forces the
 +	 * decompressor to combine reads by copying data.
 +	 */
 +	lha->strm.next_in = __archive_read_ahead(a, 1, &bytes_avail);
 +	if (bytes_avail <= 0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated LHa file body");
 +		return (ARCHIVE_FATAL);
 +	}
 +	if (bytes_avail > lha->entry_bytes_remaining)
 +		bytes_avail = (ssize_t)lha->entry_bytes_remaining;
 +
 +	lha->strm.avail_in = (int)bytes_avail;
 +	lha->strm.total_in = 0;
 +	lha->strm.avail_out = 0;
 +
 +	r = lzh_decode(&(lha->strm), bytes_avail == lha->entry_bytes_remaining);
 +	switch (r) {
 +	case ARCHIVE_OK:
 +		break;
 +	case ARCHIVE_EOF:
 +		lha->end_of_entry = 1;
 +		break;
 +	default:
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Bad lzh data");
 +		return (ARCHIVE_FAILED);
 +	}
 +	lha->entry_unconsumed = lha->strm.total_in;
 +	lha->entry_bytes_remaining -= lha->strm.total_in;
 +
 +	if (lha->strm.avail_out) {
 +		*offset = lha->entry_offset;
 +		*size = lha->strm.avail_out;
 +		*buff = lha->strm.ref_ptr;
 +		lha->entry_crc_calculated =
 +		    lha_crc16(lha->entry_crc_calculated, *buff, *size);
 +		lha->entry_offset += *size;
 +	} else {
 +		*offset = lha->entry_offset;
 +		*size = 0;
 +		*buff = NULL;
 +		if (lha->end_of_entry)
 +			return (lha_end_of_entry(a));
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Skip a file content.
 + */
 +static int
 +archive_read_format_lha_read_data_skip(struct archive_read *a)
 +{
 +	struct lha *lha;
 +	int64_t bytes_skipped;
 +
 +	lha = (struct lha *)(a->format->data);
 +
 +	if (lha->entry_unconsumed) {
 +		/* Consume as much as the decompressor actually used. */
 +		__archive_read_consume(a, lha->entry_unconsumed);
 +		lha->entry_unconsumed = 0;
 +	}
 +
 +	/* if we've already read to end of data, we're done. */
 +	if (lha->end_of_entry_cleanup)
 +		return (ARCHIVE_OK);
 +
 +	/*
 +	 * If the length is at the beginning, we can skip the
 +	 * compressed data much more quickly.
 +	 */
 +	bytes_skipped = __archive_read_consume(a, lha->entry_bytes_remaining);
 +	if (bytes_skipped < 0)
 +		return (ARCHIVE_FATAL);
 +
 +	/* This entry is finished and done. */
 +	lha->end_of_entry_cleanup = lha->end_of_entry = 1;
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +archive_read_format_lha_cleanup(struct archive_read *a)
 +{
 +	struct lha *lha = (struct lha *)(a->format->data);
 +
 +	lzh_decode_free(&(lha->strm));
 +	archive_string_free(&(lha->dirname));
 +	archive_string_free(&(lha->filename));
 +	archive_string_free(&(lha->uname));
 +	archive_string_free(&(lha->gname));
 +	archive_wstring_free(&(lha->ws));
 +	free(lha);
 +	(a->format->data) = NULL;
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * 'LHa for UNIX' utility has archived a symbolic-link name after
 + * a pathname with '|' character.
 + * This function extracts the symbolic-link name from the pathname.
 + *
 + * example.
 + *   1. a symbolic-name is 'aaa/bb/cc'
 + *   2. a filename is 'xxx/bbb'
 + *  then a archived pathname is 'xxx/bbb|aaa/bb/cc'
 + */
 +static int
 +lha_parse_linkname(struct archive_string *linkname,
 +    struct archive_string *pathname)
 +{
 +	char *	linkptr;
 +	size_t 	symlen;
 +
 +	linkptr = strchr(pathname->s, '|');
 +	if (linkptr != NULL) {
 +		symlen = strlen(linkptr + 1);
 +		archive_strncpy(linkname, linkptr+1, symlen);
 +
 +		*linkptr = 0;
 +		pathname->length = strlen(pathname->s);
 +
 +		return (1);
 +	}
 +	return (0);
 +}
 +
 +/* Convert an MSDOS-style date/time into Unix-style time. */
 +static time_t
 +lha_dos_time(const unsigned char *p)
 +{
 +	int msTime, msDate;
 +	struct tm ts;
 +
 +	msTime = archive_le16dec(p);
 +	msDate = archive_le16dec(p+2);
 +
 +	memset(&ts, 0, sizeof(ts));
 +	ts.tm_year = ((msDate >> 9) & 0x7f) + 80;   /* Years since 1900. */
 +	ts.tm_mon = ((msDate >> 5) & 0x0f) - 1;     /* Month number.     */
 +	ts.tm_mday = msDate & 0x1f;		    /* Day of month.     */
 +	ts.tm_hour = (msTime >> 11) & 0x1f;
 +	ts.tm_min = (msTime >> 5) & 0x3f;
 +	ts.tm_sec = (msTime << 1) & 0x3e;
 +	ts.tm_isdst = -1;
 +	return (mktime(&ts));
 +}
 +
 +/* Convert an MS-Windows-style date/time into Unix-style time. */
 +static time_t
 +lha_win_time(uint64_t wintime, long *ns)
 +{
 +#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
 +
 +	if (wintime >= EPOC_TIME) {
 +		wintime -= EPOC_TIME;	/* 1970-01-01 00:00:00 (UTC) */
 +		if (ns != NULL)
 +			*ns = (long)(wintime % 10000000) * 100;
 +		return (wintime / 10000000);
 +	} else {
 +		if (ns != NULL)
 +			*ns = 0;
 +		return (0);
 +	}
 +}
 +
 +static unsigned char
 +lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size)
 +{
 +	unsigned char const *p = (unsigned char const *)pp;
 +
 +	p += offset;
 +	for (;size > 0; --size)
 +		sum += *p++;
 +	return (sum);
 +}
 +
 +static uint16_t crc16tbl[2][256];
 +static void
 +lha_crc16_init(void)
 +{
 +	unsigned int i;
 +	static int crc16init = 0;
 +
 +	if (crc16init)
 +		return;
 +	crc16init = 1;
 +
 +	for (i = 0; i < 256; i++) {
 +		unsigned int j;
 +		uint16_t crc = (uint16_t)i;
 +		for (j = 8; j; j--)
 +			crc = (crc >> 1) ^ ((crc & 1) * 0xA001);
 +		crc16tbl[0][i] = crc;
 +	}
 +
 +	for (i = 0; i < 256; i++) {
 +		crc16tbl[1][i] = (crc16tbl[0][i] >> 8)
 +			^ crc16tbl[0][crc16tbl[0][i] & 0xff];
 +	}
 +}
 +
 +static uint16_t
 +lha_crc16(uint16_t crc, const void *pp, size_t len)
 +{
 +	const unsigned char *p = (const unsigned char *)pp;
 +	const uint16_t *buff;
 +	const union {
 +		uint32_t i;
 +		char c[4];
 +	} u = { 0x01020304 };
 +
 +	if (len == 0)
 +		return crc;
 +
 +	/* Process unaligned address. */
 +	if (((uintptr_t)p) & (uintptr_t)0x1) {
 +		crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff];
 +		len--;
 +	}
 +	buff = (const uint16_t *)p;
 +	/*
 +	 * Modern C compiler such as GCC does not unroll automatically yet
 +	 * without unrolling pragma, and Clang is so. So we should
 +	 * unroll this loop for its performance.
 +	 */
 +	for (;len >= 8; len -= 8) {
 +		/* This if statement expects compiler optimization will
 +		 * remove the statement which will not be executed. */
 +#undef bswap16
 +#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)
 +/* All clang versions have __builtin_bswap16() */
 +#  define bswap16(x) __builtin_bswap16(x)
 +#else
 +#  define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8))
 +#endif
 +#define CRC16W	do { 	\
 +		if(u.c[0] == 1) { /* Big endian */		\
 +			crc ^= bswap16(*buff); buff++;		\
 +		} else						\
 +			crc ^= *buff++;				\
 +		crc = crc16tbl[1][crc & 0xff] ^ crc16tbl[0][crc >> 8];\
 +} while (0)
 +		CRC16W;
 +		CRC16W;
 +		CRC16W;
 +		CRC16W;
 +#undef CRC16W
 +#undef bswap16
 +	}
 +
 +	p = (const unsigned char *)buff;
 +	for (;len; len--) {
 +		crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff];
 +	}
 +	return crc;
 +}
 +
 +/*
 + * Initialize LZHUF decoder.
 + *
 + * Returns ARCHIVE_OK if initialization was successful.
 + * Returns ARCHIVE_FAILED if method is unsupported.
 + * Returns ARCHIVE_FATAL if initialization failed; memory allocation
 + * error occurred.
 + */
 +static int
 +lzh_decode_init(struct lzh_stream *strm, const char *method)
 +{
 +	struct lzh_dec *ds;
 +	int w_bits, w_size;
 +
 +	if (strm->ds == NULL) {
 +		strm->ds = calloc(1, sizeof(*strm->ds));
 +		if (strm->ds == NULL)
 +			return (ARCHIVE_FATAL);
 +	}
 +	ds = strm->ds;
 +	ds->error = ARCHIVE_FAILED;
 +	if (method == NULL || method[0] != 'l' || method[1] != 'h')
 +		return (ARCHIVE_FAILED);
 +	switch (method[2]) {
 +	case '5':
 +		w_bits = 13;/* 8KiB for window */
 +		break;
 +	case '6':
 +		w_bits = 15;/* 32KiB for window */
 +		break;
 +	case '7':
 +		w_bits = 16;/* 64KiB for window */
 +		break;
 +	default:
 +		return (ARCHIVE_FAILED);/* Not supported. */
 +	}
 +	ds->error = ARCHIVE_FATAL;
 +	/* Expand a window size up to 128 KiB for decompressing process
 +	 * performance whatever its original window size is. */
 +	ds->w_size = 1U << 17;
 +	ds->w_mask = ds->w_size -1;
 +	if (ds->w_buff == NULL) {
 +		ds->w_buff = malloc(ds->w_size);
 +		if (ds->w_buff == NULL)
 +			return (ARCHIVE_FATAL);
 +	}
 +	w_size = 1U << w_bits;
 +	memset(ds->w_buff + ds->w_size - w_size, 0x20, w_size);
 +	ds->w_pos = 0;
 +	ds->state = 0;
 +	ds->pos_pt_len_size = w_bits + 1;
 +	ds->pos_pt_len_bits = (w_bits == 15 || w_bits == 16)? 5: 4;
 +	ds->literal_pt_len_size = PT_BITLEN_SIZE;
 +	ds->literal_pt_len_bits = 5;
 +	ds->br.cache_buffer = 0;
 +	ds->br.cache_avail = 0;
 +
 +	if (lzh_huffman_init(&(ds->lt), LT_BITLEN_SIZE, 16)
 +	    != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +	ds->lt.len_bits = 9;
 +	if (lzh_huffman_init(&(ds->pt), PT_BITLEN_SIZE, 16)
 +	    != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +	ds->error = 0;
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Release LZHUF decoder.
 + */
 +static void
 +lzh_decode_free(struct lzh_stream *strm)
 +{
 +
 +	if (strm->ds == NULL)
 +		return;
 +	free(strm->ds->w_buff);
 +	lzh_huffman_free(&(strm->ds->lt));
 +	lzh_huffman_free(&(strm->ds->pt));
 +	free(strm->ds);
 +	strm->ds = NULL;
 +}
 +
 +/*
 + * Bit stream reader.
 + */
 +/* Check that the cache buffer has enough bits. */
 +#define lzh_br_has(br, n)	((br)->cache_avail >= n)
 +/* Get compressed data by bit. */
 +#define lzh_br_bits(br, n)				\
 +	(((uint16_t)((br)->cache_buffer >>		\
 +		((br)->cache_avail - (n)))) & cache_masks[n])
 +#define lzh_br_bits_forced(br, n)			\
 +	(((uint16_t)((br)->cache_buffer <<		\
 +		((n) - (br)->cache_avail))) & cache_masks[n])
 +/* Read ahead to make sure the cache buffer has enough compressed data we
 + * will use.
 + *  True  : completed, there is enough data in the cache buffer.
 + *  False : we met that strm->next_in is empty, we have to get following
 + *          bytes. */
 +#define lzh_br_read_ahead_0(strm, br, n)	\
 +	(lzh_br_has(br, (n)) || lzh_br_fillup(strm, br))
 +/*  True  : the cache buffer has some bits as much as we need.
 + *  False : there are no enough bits in the cache buffer to be used,
 + *          we have to get following bytes if we could. */
 +#define lzh_br_read_ahead(strm, br, n)	\
 +	(lzh_br_read_ahead_0((strm), (br), (n)) || lzh_br_has((br), (n)))
 +
 +/* Notify how many bits we consumed. */
 +#define lzh_br_consume(br, n)	((br)->cache_avail -= (n))
 +#define lzh_br_unconsume(br, n)	((br)->cache_avail += (n))
 +
 +static const uint16_t cache_masks[] = {
 +	0x0000, 0x0001, 0x0003, 0x0007,
 +	0x000F, 0x001F, 0x003F, 0x007F,
 +	0x00FF, 0x01FF, 0x03FF, 0x07FF,
 +	0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,
 +	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF
 +};
 +
 +/*
 + * Shift away used bits in the cache data and fill it up with following bits.
 + * Call this when cache buffer does not have enough bits you need.
 + *
 + * Returns 1 if the cache buffer is full.
 + * Returns 0 if the cache buffer is not full; input buffer is empty.
 + */
 +static int
 +lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br)
 +{
 +	int n = CACHE_BITS - br->cache_avail;
 +
 +	for (;;) {
 +		const int x = n >> 3;
 +		if (strm->avail_in >= x) {
 +			switch (x) {
 +			case 8:
 +				br->cache_buffer =
 +				    ((uint64_t)strm->next_in[0]) << 56 |
 +				    ((uint64_t)strm->next_in[1]) << 48 |
 +				    ((uint64_t)strm->next_in[2]) << 40 |
 +				    ((uint64_t)strm->next_in[3]) << 32 |
 +				    ((uint32_t)strm->next_in[4]) << 24 |
 +				    ((uint32_t)strm->next_in[5]) << 16 |
 +				    ((uint32_t)strm->next_in[6]) << 8 |
 +				     (uint32_t)strm->next_in[7];
 +				strm->next_in += 8;
 +				strm->avail_in -= 8;
 +				br->cache_avail += 8 * 8;
 +				return (1);
 +			case 7:
 +				br->cache_buffer =
 +		 		   (br->cache_buffer << 56) |
 +				    ((uint64_t)strm->next_in[0]) << 48 |
 +				    ((uint64_t)strm->next_in[1]) << 40 |
 +				    ((uint64_t)strm->next_in[2]) << 32 |
 +				    ((uint32_t)strm->next_in[3]) << 24 |
 +				    ((uint32_t)strm->next_in[4]) << 16 |
 +				    ((uint32_t)strm->next_in[5]) << 8 |
 +				     (uint32_t)strm->next_in[6];
 +				strm->next_in += 7;
 +				strm->avail_in -= 7;
 +				br->cache_avail += 7 * 8;
 +				return (1);
 +			case 6:
 +				br->cache_buffer =
 +		 		   (br->cache_buffer << 48) |
 +				    ((uint64_t)strm->next_in[0]) << 40 |
 +				    ((uint64_t)strm->next_in[1]) << 32 |
 +				    ((uint32_t)strm->next_in[2]) << 24 |
 +				    ((uint32_t)strm->next_in[3]) << 16 |
 +				    ((uint32_t)strm->next_in[4]) << 8 |
 +				     (uint32_t)strm->next_in[5];
 +				strm->next_in += 6;
 +				strm->avail_in -= 6;
 +				br->cache_avail += 6 * 8;
 +				return (1);
 +			case 0:
 +				/* We have enough compressed data in
 +				 * the cache buffer.*/
 +				return (1);
 +			default:
 +				break;
 +			}
 +		}
 +		if (strm->avail_in == 0) {
 +			/* There is not enough compressed data to fill up the
 +			 * cache buffer. */
 +			return (0);
 +		}
 +		br->cache_buffer =
 +		   (br->cache_buffer << 8) | *strm->next_in++;
 +		strm->avail_in--;
 +		br->cache_avail += 8;
 +		n -= 8;
 +	}
 +}
 +
 +/*
 + * Decode LZHUF.
 + *
 + * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty.
 + *    Please set available buffer and call this function again.
 + * 2. Returns ARCHIVE_EOF if decompression has been completed.
 + * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
 + *    is broken or you do not set 'last' flag properly.
 + * 4. 'last' flag is very important, you must set 1 to the flag if there
 + *    is no input data. The lha compressed data format does not provide how
 + *    to know the compressed data is really finished.
 + *    Note: lha command utility check if the total size of output bytes is
 + *    reached the uncompressed size recorded in its header. it does not mind
 + *    that the decoding process is properly finished.
 + *    GNU ZIP can decompress another compressed file made by SCO LZH compress.
 + *    it handles EOF as null to fill read buffer with zero until the decoding
 + *    process meet 2 bytes of zeros at reading a size of a next chunk, so the
 + *    zeros are treated as the mark of the end of the data although the zeros
 + *    is dummy, not the file data.
 + */
 +static int	lzh_read_blocks(struct lzh_stream *, int);
 +static int	lzh_decode_blocks(struct lzh_stream *, int);
 +#define ST_RD_BLOCK		0
 +#define ST_RD_PT_1		1
 +#define ST_RD_PT_2		2
 +#define ST_RD_PT_3		3
 +#define ST_RD_PT_4		4
 +#define ST_RD_LITERAL_1		5
 +#define ST_RD_LITERAL_2		6
 +#define ST_RD_LITERAL_3		7
 +#define ST_RD_POS_DATA_1	8
 +#define ST_GET_LITERAL		9
 +#define ST_GET_POS_1		10
 +#define ST_GET_POS_2		11
 +#define ST_COPY_DATA		12
 +
 +static int
 +lzh_decode(struct lzh_stream *strm, int last)
 +{
 +	struct lzh_dec *ds = strm->ds;
 +	int avail_in;
 +	int r;
 +
 +	if (ds->error)
 +		return (ds->error);
 +
 +	avail_in = strm->avail_in;
 +	do {
 +		if (ds->state < ST_GET_LITERAL)
 +			r = lzh_read_blocks(strm, last);
 +		else
 +			r = lzh_decode_blocks(strm, last);
 +	} while (r == 100);
 +	strm->total_in += avail_in - strm->avail_in;
 +	return (r);
 +}
 +
 +static void
 +lzh_emit_window(struct lzh_stream *strm, size_t s)
 +{
 +	strm->ref_ptr = strm->ds->w_buff;
 +	strm->avail_out = (int)s;
 +	strm->total_out += s;
 +}
 +
 +static int
 +lzh_read_blocks(struct lzh_stream *strm, int last)
 +{
 +	struct lzh_dec *ds = strm->ds;
 +	struct lzh_br *br = &(ds->br);
 +	int c = 0, i;
 +	unsigned rbits;
 +
 +	for (;;) {
 +		switch (ds->state) {
 +		case ST_RD_BLOCK:
 +			/*
 +			 * Read a block number indicates how many blocks
 +			 * we will handle. The block is composed of a
 +			 * literal and a match, sometimes a literal only
 +			 * in particular, there are no reference data at
 +			 * the beginning of the decompression.
 +			 */
 +			if (!lzh_br_read_ahead_0(strm, br, 16)) {
 +				if (!last)
 +					/* We need following data. */
 +					return (ARCHIVE_OK);
 +				if (lzh_br_has(br, 8)) {
 +					/*
 +					 * It seems there are extra bits.
 +					 *  1. Compressed data is broken.
 +					 *  2. `last' flag does not properly
 +					 *     set.
 +					 */
 +					goto failed;
 +				}
 +				if (ds->w_pos > 0) {
 +					lzh_emit_window(strm, ds->w_pos);
 +					ds->w_pos = 0;
 +					return (ARCHIVE_OK);
 +				}
 +				/* End of compressed data; we have completely
 +				 * handled all compressed data. */
 +				return (ARCHIVE_EOF);
 +			}
 +			ds->blocks_avail = lzh_br_bits(br, 16);
 +			if (ds->blocks_avail == 0)
 +				goto failed;
 +			lzh_br_consume(br, 16);
 +			/*
 +			 * Read a literal table compressed in huffman
 +			 * coding.
 +			 */
 +			ds->pt.len_size = ds->literal_pt_len_size;
 +			ds->pt.len_bits = ds->literal_pt_len_bits;
 +			ds->reading_position = 0;
 +			/* FALL THROUGH */
 +		case ST_RD_PT_1:
 +			/* Note: ST_RD_PT_1, ST_RD_PT_2 and ST_RD_PT_4 are
 +			 * used in reading both a literal table and a
 +			 * position table. */
 +			if (!lzh_br_read_ahead(strm, br, ds->pt.len_bits)) {
 +				if (last)
 +					goto failed;/* Truncated data. */
 +				ds->state = ST_RD_PT_1;
 +				return (ARCHIVE_OK);
 +			}
 +			ds->pt.len_avail = lzh_br_bits(br, ds->pt.len_bits);
 +			lzh_br_consume(br, ds->pt.len_bits);
 +			/* FALL THROUGH */
 +		case ST_RD_PT_2:
 +			if (ds->pt.len_avail == 0) {
 +				/* There is no bitlen. */
 +				if (!lzh_br_read_ahead(strm, br,
 +				    ds->pt.len_bits)) {
 +					if (last)
 +						goto failed;/* Truncated data.*/
 +					ds->state = ST_RD_PT_2;
 +					return (ARCHIVE_OK);
 +				}
 +				if (!lzh_make_fake_table(&(ds->pt),
 +				    lzh_br_bits(br, ds->pt.len_bits)))
 +					goto failed;/* Invalid data. */
 +				lzh_br_consume(br, ds->pt.len_bits);
 +				if (ds->reading_position)
 +					ds->state = ST_GET_LITERAL;
 +				else
 +					ds->state = ST_RD_LITERAL_1;
 +				break;
 +			} else if (ds->pt.len_avail > ds->pt.len_size)
 +				goto failed;/* Invalid data. */
 +			ds->loop = 0;
 +			memset(ds->pt.freq, 0, sizeof(ds->pt.freq));
 +			if (ds->pt.len_avail < 3 ||
 +			    ds->pt.len_size == ds->pos_pt_len_size) {
 +				ds->state = ST_RD_PT_4;
 +				break;
 +			}
 +			/* FALL THROUGH */
 +		case ST_RD_PT_3:
 +			ds->loop = lzh_read_pt_bitlen(strm, ds->loop, 3);
 +			if (ds->loop < 3) {
 +				if (ds->loop < 0 || last)
 +					goto failed;/* Invalid data. */
 +				/* Not completed, get following data. */
 +				ds->state = ST_RD_PT_3;
 +				return (ARCHIVE_OK);
 +			}
 +			/* There are some null in bitlen of the literal. */
 +			if (!lzh_br_read_ahead(strm, br, 2)) {
 +				if (last)
 +					goto failed;/* Truncated data. */
 +				ds->state = ST_RD_PT_3;
 +				return (ARCHIVE_OK);
 +			}
 +			c = lzh_br_bits(br, 2);
 +			lzh_br_consume(br, 2);
 +			if (c > ds->pt.len_avail - 3)
 +				goto failed;/* Invalid data. */
 +			for (i = 3; c-- > 0 ;)
 +				ds->pt.bitlen[i++] = 0;
 +			ds->loop = i;
 +			/* FALL THROUGH */
 +		case ST_RD_PT_4:
 +			ds->loop = lzh_read_pt_bitlen(strm, ds->loop,
 +			    ds->pt.len_avail);
 +			if (ds->loop < ds->pt.len_avail) {
 +				if (ds->loop < 0 || last)
 +					goto failed;/* Invalid data. */
 +				/* Not completed, get following data. */
 +				ds->state = ST_RD_PT_4;
 +				return (ARCHIVE_OK);
 +			}
 +			if (!lzh_make_huffman_table(&(ds->pt)))
 +				goto failed;/* Invalid data */
 +			if (ds->reading_position) {
 +				ds->state = ST_GET_LITERAL;
 +				break;
 +			}
 +			/* FALL THROUGH */
 +		case ST_RD_LITERAL_1:
 +			if (!lzh_br_read_ahead(strm, br, ds->lt.len_bits)) {
 +				if (last)
 +					goto failed;/* Truncated data. */
 +				ds->state = ST_RD_LITERAL_1;
 +				return (ARCHIVE_OK);
 +			}
 +			ds->lt.len_avail = lzh_br_bits(br, ds->lt.len_bits);
 +			lzh_br_consume(br, ds->lt.len_bits);
 +			/* FALL THROUGH */
 +		case ST_RD_LITERAL_2:
 +			if (ds->lt.len_avail == 0) {
 +				/* There is no bitlen. */
 +				if (!lzh_br_read_ahead(strm, br,
 +				    ds->lt.len_bits)) {
 +					if (last)
 +						goto failed;/* Truncated data.*/
 +					ds->state = ST_RD_LITERAL_2;
 +					return (ARCHIVE_OK);
 +				}
 +				if (!lzh_make_fake_table(&(ds->lt),
 +				    lzh_br_bits(br, ds->lt.len_bits)))
 +					goto failed;/* Invalid data */
 +				lzh_br_consume(br, ds->lt.len_bits);
 +				ds->state = ST_RD_POS_DATA_1;
 +				break;
 +			} else if (ds->lt.len_avail > ds->lt.len_size)
 +				goto failed;/* Invalid data */
 +			ds->loop = 0;
 +			memset(ds->lt.freq, 0, sizeof(ds->lt.freq));
 +			/* FALL THROUGH */
 +		case ST_RD_LITERAL_3:
 +			i = ds->loop;
 +			while (i < ds->lt.len_avail) {
 +				if (!lzh_br_read_ahead(strm, br,
 +				    ds->pt.max_bits)) {
 +					if (last)
 +						goto failed;/* Truncated data.*/
 +					ds->loop = i;
 +					ds->state = ST_RD_LITERAL_3;
 +					return (ARCHIVE_OK);
 +				}
 +				rbits = lzh_br_bits(br, ds->pt.max_bits);
 +				c = lzh_decode_huffman(&(ds->pt), rbits);
 +				if (c > 2) {
 +					/* Note: 'c' will never be more than
 +					 * eighteen since it's limited by
 +					 * PT_BITLEN_SIZE, which is being set
 +					 * to ds->pt.len_size through
 +					 * ds->literal_pt_len_size. */
 +					lzh_br_consume(br, ds->pt.bitlen[c]);
 +					c -= 2;
 +					ds->lt.freq[c]++;
 +					ds->lt.bitlen[i++] = c;
 +				} else if (c == 0) {
 +					lzh_br_consume(br, ds->pt.bitlen[c]);
 +					ds->lt.bitlen[i++] = 0;
 +				} else {
 +					/* c == 1 or c == 2 */
 +					int n = (c == 1)?4:9;
 +					if (!lzh_br_read_ahead(strm, br,
 +					     ds->pt.bitlen[c] + n)) {
 +						if (last) /* Truncated data. */
 +							goto failed;
 +						ds->loop = i;
 +						ds->state = ST_RD_LITERAL_3;
 +						return (ARCHIVE_OK);
 +					}
 +					lzh_br_consume(br, ds->pt.bitlen[c]);
 +					c = lzh_br_bits(br, n);
 +					lzh_br_consume(br, n);
 +					c += (n == 4)?3:20;
 +					if (i + c > ds->lt.len_avail)
 +						goto failed;/* Invalid data */
 +					memset(&(ds->lt.bitlen[i]), 0, c);
 +					i += c;
 +				}
 +			}
 +			if (i > ds->lt.len_avail ||
 +			    !lzh_make_huffman_table(&(ds->lt)))
 +				goto failed;/* Invalid data */
 +			/* FALL THROUGH */
 +		case ST_RD_POS_DATA_1:
 +			/*
 +			 * Read a position table compressed in huffman
 +			 * coding.
 +			 */
 +			ds->pt.len_size = ds->pos_pt_len_size;
 +			ds->pt.len_bits = ds->pos_pt_len_bits;
 +			ds->reading_position = 1;
 +			ds->state = ST_RD_PT_1;
 +			break;
 +		case ST_GET_LITERAL:
 +			return (100);
 +		}
 +	}
 +failed:
 +	return (ds->error = ARCHIVE_FAILED);
 +}
 +
 +static int
 +lzh_decode_blocks(struct lzh_stream *strm, int last)
 +{
 +	struct lzh_dec *ds = strm->ds;
 +	struct lzh_br bre = ds->br;
 +	struct huffman *lt = &(ds->lt);
 +	struct huffman *pt = &(ds->pt);
 +	unsigned char *w_buff = ds->w_buff;
 +	unsigned char *lt_bitlen = lt->bitlen;
 +	unsigned char *pt_bitlen = pt->bitlen;
 +	int blocks_avail = ds->blocks_avail, c = 0;
 +	int copy_len = ds->copy_len, copy_pos = ds->copy_pos;
 +	int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
 +	int lt_max_bits = lt->max_bits, pt_max_bits = pt->max_bits;
 +	int state = ds->state;
 +
 +	for (;;) {
 +		switch (state) {
 +		case ST_GET_LITERAL:
 +			for (;;) {
 +				if (blocks_avail == 0) {
 +					/* We have decoded all blocks.
 +					 * Let's handle next blocks. */
 +					ds->state = ST_RD_BLOCK;
 +					ds->br = bre;
 +					ds->blocks_avail = 0;
 +					ds->w_pos = w_pos;
 +					ds->copy_pos = 0;
 +					return (100);
 +				}
 +
 +				/* lzh_br_read_ahead() always try to fill the
 +				 * cache buffer up. In specific situation we
 +				 * are close to the end of the data, the cache
 +				 * buffer will not be full and thus we have to
 +				 * determine if the cache buffer has some bits
 +				 * as much as we need after lzh_br_read_ahead()
 +				 * failed. */
 +				if (!lzh_br_read_ahead(strm, &bre,
 +				    lt_max_bits)) {
 +					if (!last)
 +						goto next_data;
 +					/* Remaining bits are less than
 +					 * maximum bits(lt.max_bits) but maybe
 +					 * it still remains as much as we need,
 +					 * so we should try to use it with
 +					 * dummy bits. */
 +					c = lzh_decode_huffman(lt,
 +					      lzh_br_bits_forced(&bre,
 +					        lt_max_bits));
 +					lzh_br_consume(&bre, lt_bitlen[c]);
 +					if (!lzh_br_has(&bre, 0))
 +						goto failed;/* Over read. */
 +				} else {
 +					c = lzh_decode_huffman(lt,
 +					      lzh_br_bits(&bre, lt_max_bits));
 +					lzh_br_consume(&bre, lt_bitlen[c]);
 +				}
 +				blocks_avail--;
 +				if (c > UCHAR_MAX)
 +					/* Current block is a match data. */
 +					break;
 +				/*
 +				 * 'c' is exactly a literal code.
 +				 */
 +				/* Save a decoded code to reference it
 +				 * afterward. */
 +				w_buff[w_pos] = c;
 +				if (++w_pos >= w_size) {
 +					w_pos = 0;
 +					lzh_emit_window(strm, w_size);
 +					goto next_data;
 +				}
 +			}
 +			/* 'c' is the length of a match pattern we have
 +			 * already extracted, which has be stored in
 +			 * window(ds->w_buff). */
 +			copy_len = c - (UCHAR_MAX + 1) + MINMATCH;
 +			/* FALL THROUGH */
 +		case ST_GET_POS_1:
 +			/*
 +			 * Get a reference position. 
 +			 */
 +			if (!lzh_br_read_ahead(strm, &bre, pt_max_bits)) {
 +				if (!last) {
 +					state = ST_GET_POS_1;
 +					ds->copy_len = copy_len;
 +					goto next_data;
 +				}
 +				copy_pos = lzh_decode_huffman(pt,
 +				    lzh_br_bits_forced(&bre, pt_max_bits));
 +				lzh_br_consume(&bre, pt_bitlen[copy_pos]);
 +				if (!lzh_br_has(&bre, 0))
 +					goto failed;/* Over read. */
 +			} else {
 +				copy_pos = lzh_decode_huffman(pt,
 +				    lzh_br_bits(&bre, pt_max_bits));
 +				lzh_br_consume(&bre, pt_bitlen[copy_pos]);
 +			}
 +			/* FALL THROUGH */
 +		case ST_GET_POS_2:
 +			if (copy_pos > 1) {
 +				/* We need an additional adjustment number to
 +				 * the position. */
 +				int p = copy_pos - 1;
 +				if (!lzh_br_read_ahead(strm, &bre, p)) {
 +					if (last)
 +						goto failed;/* Truncated data.*/
 +					state = ST_GET_POS_2;
 +					ds->copy_len = copy_len;
 +					ds->copy_pos = copy_pos;
 +					goto next_data;
 +				}
 +				copy_pos = (1 << p) + lzh_br_bits(&bre, p);
 +				lzh_br_consume(&bre, p);
 +			}
 +			/* The position is actually a distance from the last
 +			 * code we had extracted and thus we have to convert
 +			 * it to a position of the window. */
 +			copy_pos = (w_pos - copy_pos - 1) & w_mask;
 +			/* FALL THROUGH */
 +		case ST_COPY_DATA:
 +			/*
 +			 * Copy `copy_len' bytes as extracted data from
 +			 * the window into the output buffer.
 +			 */
 +			for (;;) {
 +				int l;
 +
 +				l = copy_len;
 +				if (copy_pos > w_pos) {
 +					if (l > w_size - copy_pos)
 +						l = w_size - copy_pos;
 +				} else {
 +					if (l > w_size - w_pos)
 +						l = w_size - w_pos;
 +				}
 +				if ((copy_pos + l < w_pos)
 +				    || (w_pos + l < copy_pos)) {
 +					/* No overlap. */
 +					memcpy(w_buff + w_pos,
 +					    w_buff + copy_pos, l);
 +				} else {
 +					const unsigned char *s;
 +					unsigned char *d;
 +					int li;
 +
 +					d = w_buff + w_pos;
 +					s = w_buff + copy_pos;
 +					for (li = 0; li < l-1;) {
 +						d[li] = s[li];li++;
 +						d[li] = s[li];li++;
 +					}
 +					if (li < l)
 +						d[li] = s[li];
 +				}
 +				w_pos += l;
 +				if (w_pos == w_size) {
 +					w_pos = 0;
 +					lzh_emit_window(strm, w_size);
 +					if (copy_len <= l)
 +						state = ST_GET_LITERAL;
 +					else {
 +						state = ST_COPY_DATA;
 +						ds->copy_len = copy_len - l;
 +						ds->copy_pos =
 +						    (copy_pos + l) & w_mask;
 +					}
 +					goto next_data;
 +				}
 +				if (copy_len <= l)
 +					/* A copy of current pattern ended. */
 +					break;
 +				copy_len -= l;
 +				copy_pos = (copy_pos + l) & w_mask;
 +			}
 +			state = ST_GET_LITERAL;
 +			break;
 +		}
 +	}
 +failed:
 +	return (ds->error = ARCHIVE_FAILED);
 +next_data:
 +	ds->br = bre;
 +	ds->blocks_avail = blocks_avail;
 +	ds->state = state;
 +	ds->w_pos = w_pos;
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +lzh_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
 +{
 +	int bits;
 +
 +	if (hf->bitlen == NULL) {
 +		hf->bitlen = malloc(len_size * sizeof(hf->bitlen[0]));
 +		if (hf->bitlen == NULL)
 +			return (ARCHIVE_FATAL);
 +	}
 +	if (hf->tbl == NULL) {
 +		if (tbl_bits < HTBL_BITS)
 +			bits = tbl_bits;
 +		else
 +			bits = HTBL_BITS;
 +		hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0]));
 +		if (hf->tbl == NULL)
 +			return (ARCHIVE_FATAL);
 +	}
 +	if (hf->tree == NULL && tbl_bits > HTBL_BITS) {
 +		hf->tree_avail = 1 << (tbl_bits - HTBL_BITS + 4);
 +		hf->tree = malloc(hf->tree_avail * sizeof(hf->tree[0]));
 +		if (hf->tree == NULL)
 +			return (ARCHIVE_FATAL);
 +	}
 +	hf->len_size = (int)len_size;
 +	hf->tbl_bits = tbl_bits;
 +	return (ARCHIVE_OK);
 +}
 +
 +static void
 +lzh_huffman_free(struct huffman *hf)
 +{
 +	free(hf->bitlen);
 +	free(hf->tbl);
 +	free(hf->tree);
 +}
 +
- static char bitlen_tbl[0x400] = {
++static const char bitlen_tbl[0x400] = {
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
 +	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
 +	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
 +	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
 +	11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
 +	11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
 +	12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
 +	13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 16,  0
 +};
 +static int
 +lzh_read_pt_bitlen(struct lzh_stream *strm, int start, int end)
 +{
 +	struct lzh_dec *ds = strm->ds;
 +	struct lzh_br *br = &(ds->br);
 +	int c, i;
 +
 +	for (i = start; i < end; ) {
 +		/*
 +		 *  bit pattern     the number we need
 +		 *     000           ->  0
 +		 *     001           ->  1
 +		 *     010           ->  2
 +		 *     ...
 +		 *     110           ->  6
 +		 *     1110          ->  7
 +		 *     11110         ->  8
 +		 *     ...
 +		 *     1111111111110 ->  16
 +		 */
 +		if (!lzh_br_read_ahead(strm, br, 3))
 +			return (i);
 +		if ((c = lzh_br_bits(br, 3)) == 7) {
 +			if (!lzh_br_read_ahead(strm, br, 13))
 +				return (i);
 +			c = bitlen_tbl[lzh_br_bits(br, 13) & 0x3FF];
 +			if (c)
 +				lzh_br_consume(br, c - 3);
 +			else
 +				return (-1);/* Invalid data. */
 +		} else
 +			lzh_br_consume(br, 3);
 +		ds->pt.bitlen[i++] = c;
 +		ds->pt.freq[c]++;
 +	}
 +	return (i);
 +}
 +
 +static int
 +lzh_make_fake_table(struct huffman *hf, uint16_t c)
 +{
 +	if (c >= hf->len_size)
 +		return (0);
 +	hf->tbl[0] = c;
 +	hf->max_bits = 0;
 +	hf->shift_bits = 0;
 +	hf->bitlen[hf->tbl[0]] = 0;
 +	return (1);
 +}
 +
 +/*
 + * Make a huffman coding table.
 + */
 +static int
 +lzh_make_huffman_table(struct huffman *hf)
 +{
 +	uint16_t *tbl;
 +	const unsigned char *bitlen;
 +	int bitptn[17], weight[17];
 +	int i, maxbits = 0, ptn, tbl_size, w;
 +	int diffbits, len_avail;
 +
 +	/*
 +	 * Initialize bit patterns.
 +	 */
 +	ptn = 0;
 +	for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {
 +		bitptn[i] = ptn;
 +		weight[i] = w;
 +		if (hf->freq[i]) {
 +			ptn += hf->freq[i] * w;
 +			maxbits = i;
 +		}
 +	}
 +	if (ptn != 0x10000 || maxbits > hf->tbl_bits)
 +		return (0);/* Invalid */
 +
 +	hf->max_bits = maxbits;
 +
 +	/*
 +	 * Cut out extra bits which we won't house in the table.
 +	 * This preparation reduces the same calculation in the for-loop
 +	 * making the table.
 +	 */
 +	if (maxbits < 16) {
 +		int ebits = 16 - maxbits;
 +		for (i = 1; i <= maxbits; i++) {
 +			bitptn[i] >>= ebits;
 +			weight[i] >>= ebits;
 +		}
 +	}
 +	if (maxbits > HTBL_BITS) {
 +		unsigned htbl_max;
 +		uint16_t *p;
 +
 +		diffbits = maxbits - HTBL_BITS;
 +		for (i = 1; i <= HTBL_BITS; i++) {
 +			bitptn[i] >>= diffbits;
 +			weight[i] >>= diffbits;
 +		}
 +		htbl_max = bitptn[HTBL_BITS] +
 +		    weight[HTBL_BITS] * hf->freq[HTBL_BITS];
 +		p = &(hf->tbl[htbl_max]);
 +		while (p < &hf->tbl[1U<<HTBL_BITS])
 +			*p++ = 0;
 +	} else
 +		diffbits = 0;
 +	hf->shift_bits = diffbits;
 +
 +	/*
 +	 * Make the table.
 +	 */
 +	tbl_size = 1 << HTBL_BITS;
 +	tbl = hf->tbl;
 +	bitlen = hf->bitlen;
 +	len_avail = hf->len_avail;
 +	hf->tree_used = 0;
 +	for (i = 0; i < len_avail; i++) {
 +		uint16_t *p;
 +		int len, cnt;
 +		uint16_t bit;
 +		int extlen;
 +		struct htree_t *ht;
 +
 +		if (bitlen[i] == 0)
 +			continue;
 +		/* Get a bit pattern */
 +		len = bitlen[i];
 +		ptn = bitptn[len];
 +		cnt = weight[len];
 +		if (len <= HTBL_BITS) {
 +			/* Calculate next bit pattern */
 +			if ((bitptn[len] = ptn + cnt) > tbl_size)
 +				return (0);/* Invalid */
 +			/* Update the table */
 +			p = &(tbl[ptn]);
 +			if (cnt > 7) {
 +				uint16_t *pc;
 +
 +				cnt -= 8;
 +				pc = &p[cnt];
 +				pc[0] = (uint16_t)i;
 +				pc[1] = (uint16_t)i;
 +				pc[2] = (uint16_t)i;
 +				pc[3] = (uint16_t)i;
 +				pc[4] = (uint16_t)i;
 +				pc[5] = (uint16_t)i;
 +				pc[6] = (uint16_t)i;
 +				pc[7] = (uint16_t)i;
 +				if (cnt > 7) {
 +					cnt -= 8;
 +					memcpy(&p[cnt], pc,
 +						8 * sizeof(uint16_t));
 +					pc = &p[cnt];
 +					while (cnt > 15) {
 +						cnt -= 16;
 +						memcpy(&p[cnt], pc,
 +							16 * sizeof(uint16_t));
 +					}
 +				}
 +				if (cnt)
 +					memcpy(p, pc, cnt * sizeof(uint16_t));
 +			} else {
 +				while (cnt > 1) {
 +					p[--cnt] = (uint16_t)i;
 +					p[--cnt] = (uint16_t)i;
 +				}
 +				if (cnt)
 +					p[--cnt] = (uint16_t)i;
 +			}
 +			continue;
 +		}
 +
 +		/*
 +		 * A bit length is too big to be housed to a direct table,
 +		 * so we use a tree model for its extra bits.
 +		 */
 +		bitptn[len] = ptn + cnt;
 +		bit = 1U << (diffbits -1);
 +		extlen = len - HTBL_BITS;
 +		
 +		p = &(tbl[ptn >> diffbits]);
 +		if (*p == 0) {
 +			*p = len_avail + hf->tree_used;
 +			ht = &(hf->tree[hf->tree_used++]);
 +			if (hf->tree_used > hf->tree_avail)
 +				return (0);/* Invalid */
 +			ht->left = 0;
 +			ht->right = 0;
 +		} else {
 +			if (*p < len_avail ||
 +			    *p >= (len_avail + hf->tree_used))
 +				return (0);/* Invalid */
 +			ht = &(hf->tree[*p - len_avail]);
 +		}
 +		while (--extlen > 0) {
 +			if (ptn & bit) {
 +				if (ht->left < len_avail) {
 +					ht->left = len_avail + hf->tree_used;
 +					ht = &(hf->tree[hf->tree_used++]);
 +					if (hf->tree_used > hf->tree_avail)
 +						return (0);/* Invalid */
 +					ht->left = 0;
 +					ht->right = 0;
 +				} else {
 +					ht = &(hf->tree[ht->left - len_avail]);
 +				}
 +			} else {
 +				if (ht->right < len_avail) {
 +					ht->right = len_avail + hf->tree_used;
 +					ht = &(hf->tree[hf->tree_used++]);
 +					if (hf->tree_used > hf->tree_avail)
 +						return (0);/* Invalid */
 +					ht->left = 0;
 +					ht->right = 0;
 +				} else {
 +					ht = &(hf->tree[ht->right - len_avail]);
 +				}
 +			}
 +			bit >>= 1;
 +		}
 +		if (ptn & bit) {
 +			if (ht->left != 0)
 +				return (0);/* Invalid */
 +			ht->left = (uint16_t)i;
 +		} else {
 +			if (ht->right != 0)
 +				return (0);/* Invalid */
 +			ht->right = (uint16_t)i;
 +		}
 +	}
 +	return (1);
 +}
 +
 +static int
 +lzh_decode_huffman_tree(struct huffman *hf, unsigned rbits, int c)
 +{
 +	struct htree_t *ht;
 +	int extlen;
 +
 +	ht = hf->tree;
 +	extlen = hf->shift_bits;
 +	while (c >= hf->len_avail) {
 +		c -= hf->len_avail;
 +		if (extlen-- <= 0 || c >= hf->tree_used)
 +			return (0);
 +		if (rbits & (1U << extlen))
 +			c = ht[c].left;
 +		else
 +			c = ht[c].right;
 +	}
 +	return (c);
 +}
 +
 +static inline int
 +lzh_decode_huffman(struct huffman *hf, unsigned rbits)
 +{
 +	int c;
 +	/*
 +	 * At first search an index table for a bit pattern.
 +	 * If it fails, search a huffman tree for.
 +	 */
 +	c = hf->tbl[rbits >> hf->shift_bits];
 +	if (c < hf->len_avail || hf->len_avail == 0)
 +		return (c);
 +	/* This bit pattern needs to be found out at a huffman tree. */
 +	return (lzh_decode_huffman_tree(hf, rbits, c));
 +}
 +
diff --cc Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c
index 658d49d,0000000..a0e641e
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c
@@@ -1,2953 -1,0 +1,2953 @@@
 +/*-
 +* 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 <cm_zlib.h> /* crc32 */
 +#endif
 +
 +#include "archive.h"
 +#ifndef HAVE_ZLIB_H
 +#include "archive_crc32.h"
 +#endif
 +#include "archive_endian.h"
 +#include "archive_entry.h"
 +#include "archive_entry_locale.h"
 +#include "archive_ppmd7_private.h"
 +#include "archive_private.h"
 +#include "archive_read_private.h"
 +
 +/* RAR signature, also known as the mark header */
 +#define RAR_SIGNATURE "\x52\x61\x72\x21\x1A\x07\x00"
 +
 +/* Header types */
 +#define MARK_HEAD    0x72
 +#define MAIN_HEAD    0x73
 +#define FILE_HEAD    0x74
 +#define COMM_HEAD    0x75
 +#define AV_HEAD      0x76
 +#define SUB_HEAD     0x77
 +#define PROTECT_HEAD 0x78
 +#define SIGN_HEAD    0x79
 +#define NEWSUB_HEAD  0x7a
 +#define ENDARC_HEAD  0x7b
 +
 +/* Main Header Flags */
 +#define MHD_VOLUME       0x0001
 +#define MHD_COMMENT      0x0002
 +#define MHD_LOCK         0x0004
 +#define MHD_SOLID        0x0008
 +#define MHD_NEWNUMBERING 0x0010
 +#define MHD_AV           0x0020
 +#define MHD_PROTECT      0x0040
 +#define MHD_PASSWORD     0x0080
 +#define MHD_FIRSTVOLUME  0x0100
 +#define MHD_ENCRYPTVER   0x0200
 +
 +/* Flags common to all headers */
 +#define HD_MARKDELETION     0x4000
 +#define HD_ADD_SIZE_PRESENT 0x8000
 +
 +/* File Header Flags */
 +#define FHD_SPLIT_BEFORE 0x0001
 +#define FHD_SPLIT_AFTER  0x0002
 +#define FHD_PASSWORD     0x0004
 +#define FHD_COMMENT      0x0008
 +#define FHD_SOLID        0x0010
 +#define FHD_LARGE        0x0100
 +#define FHD_UNICODE      0x0200
 +#define FHD_SALT         0x0400
 +#define FHD_VERSION      0x0800
 +#define FHD_EXTTIME      0x1000
 +#define FHD_EXTFLAGS     0x2000
 +
 +/* File dictionary sizes */
 +#define DICTIONARY_SIZE_64   0x00
 +#define DICTIONARY_SIZE_128  0x20
 +#define DICTIONARY_SIZE_256  0x40
 +#define DICTIONARY_SIZE_512  0x60
 +#define DICTIONARY_SIZE_1024 0x80
 +#define DICTIONARY_SIZE_2048 0xA0
 +#define DICTIONARY_SIZE_4096 0xC0
 +#define FILE_IS_DIRECTORY    0xE0
 +#define DICTIONARY_MASK      FILE_IS_DIRECTORY
 +
 +/* OS Flags */
 +#define OS_MSDOS  0
 +#define OS_OS2    1
 +#define OS_WIN32  2
 +#define OS_UNIX   3
 +#define OS_MAC_OS 4
 +#define OS_BEOS   5
 +
 +/* Compression Methods */
 +#define COMPRESS_METHOD_STORE   0x30
 +/* LZSS */
 +#define COMPRESS_METHOD_FASTEST 0x31
 +#define COMPRESS_METHOD_FAST    0x32
 +#define COMPRESS_METHOD_NORMAL  0x33
 +/* PPMd Variant H */
 +#define COMPRESS_METHOD_GOOD    0x34
 +#define COMPRESS_METHOD_BEST    0x35
 +
 +#define CRC_POLYNOMIAL 0xEDB88320
 +
 +#define NS_UNIT 10000000
 +
 +#define DICTIONARY_MAX_SIZE 0x400000
 +
 +#define MAINCODE_SIZE      299
 +#define OFFSETCODE_SIZE    60
 +#define LOWOFFSETCODE_SIZE 17
 +#define LENGTHCODE_SIZE    28
 +#define HUFFMAN_TABLE_SIZE \
 +  MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE + LENGTHCODE_SIZE
 +
 +#define MAX_SYMBOL_LENGTH 0xF
 +#define MAX_SYMBOLS       20
 +
 +/*
 + * Considering L1,L2 cache miss and a calling of write system-call,
 + * the best size of the output buffer(uncompressed buffer) is 128K.
 + * If the structure of extracting process is changed, this value
 + * might be researched again.
 + */
 +#define UNP_BUFFER_SIZE   (128 * 1024)
 +
 +/* Define this here for non-Windows platforms */
 +#if !((defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__))
 +#define FILE_ATTRIBUTE_DIRECTORY 0x10
 +#endif
 +
 +/* Fields common to all headers */
 +struct rar_header
 +{
 +  char crc[2];
 +  char type;
 +  char flags[2];
 +  char size[2];
 +};
 +
 +/* Fields common to all file headers */
 +struct rar_file_header
 +{
 +  char pack_size[4];
 +  char unp_size[4];
 +  char host_os;
 +  char file_crc[4];
 +  char file_time[4];
 +  char unp_ver;
 +  char method;
 +  char name_size[2];
 +  char file_attr[4];
 +};
 +
 +struct huffman_tree_node
 +{
 +  int branches[2];
 +};
 +
 +struct huffman_table_entry
 +{
 +  unsigned int length;
 +  int value;
 +};
 +
 +struct huffman_code
 +{
 +  struct huffman_tree_node *tree;
 +  int numentries;
 +  int numallocatedentries;
 +  int minlength;
 +  int maxlength;
 +  int tablesize;
 +  struct huffman_table_entry *table;
 +};
 +
 +struct lzss
 +{
 +  unsigned char *window;
 +  int mask;
 +  int64_t position;
 +};
 +
 +struct data_block_offsets
 +{
 +  int64_t header_size;
 +  int64_t start_offset;
 +  int64_t end_offset;
 +};
 +
 +struct rar
 +{
 +  /* Entries from main RAR header */
 +  unsigned main_flags;
 +  unsigned long file_crc;
 +  char reserved1[2];
 +  char reserved2[4];
 +  char encryptver;
 +
 +  /* File header entries */
 +  char compression_method;
 +  unsigned file_flags;
 +  int64_t packed_size;
 +  int64_t unp_size;
 +  time_t mtime;
 +  long mnsec;
 +  mode_t mode;
 +  char *filename;
 +  char *filename_save;
 +  size_t filename_save_size;
 +  size_t filename_allocated;
 +
 +  /* File header optional entries */
 +  char salt[8];
 +  time_t atime;
 +  long ansec;
 +  time_t ctime;
 +  long cnsec;
 +  time_t arctime;
 +  long arcnsec;
 +
 +  /* Fields to help with tracking decompression of files. */
 +  int64_t bytes_unconsumed;
 +  int64_t bytes_remaining;
 +  int64_t bytes_uncopied;
 +  int64_t offset;
 +  int64_t offset_outgoing;
 +  int64_t offset_seek;
 +  char valid;
 +  unsigned int unp_offset;
 +  unsigned int unp_buffer_size;
 +  unsigned char *unp_buffer;
 +  unsigned int dictionary_size;
 +  char start_new_block;
 +  char entry_eof;
 +  unsigned long crc_calculated;
 +  int found_first_header;
 +  char has_endarc_header;
 +  struct data_block_offsets *dbo;
 +  unsigned int cursor;
 +  unsigned int nodes;
 +
 +  /* LZSS members */
 +  struct huffman_code maincode;
 +  struct huffman_code offsetcode;
 +  struct huffman_code lowoffsetcode;
 +  struct huffman_code lengthcode;
 +  unsigned char lengthtable[HUFFMAN_TABLE_SIZE];
 +  struct lzss lzss;
 +  char output_last_match;
 +  unsigned int lastlength;
 +  unsigned int lastoffset;
 +  unsigned int oldoffset[4];
 +  unsigned int lastlowoffset;
 +  unsigned int numlowoffsetrepeats;
 +  int64_t filterstart;
 +  char start_new_table;
 +
 +  /* PPMd Variant H members */
 +  char ppmd_valid;
 +  char ppmd_eod;
 +  char is_ppmd_block;
 +  int ppmd_escape;
 +  CPpmd7 ppmd7_context;
 +  CPpmd7z_RangeDec range_dec;
 +  IByteIn bytein;
 +
 +  /*
 +   * String conversion object.
 +   */
 +  int init_default_conversion;
 +  struct archive_string_conv *sconv_default;
 +  struct archive_string_conv *opt_sconv;
 +  struct archive_string_conv *sconv_utf8;
 +  struct archive_string_conv *sconv_utf16be;
 +
 +  /*
 +   * Bit stream reader.
 +   */
 +  struct rar_br {
 +#define CACHE_TYPE	uint64_t
 +#define CACHE_BITS	(8 * sizeof(CACHE_TYPE))
 +    /* Cache buffer. */
 +    CACHE_TYPE		 cache_buffer;
 +    /* Indicates how many bits avail in cache_buffer. */
 +    int			 cache_avail;
 +    ssize_t		 avail_in;
 +    const unsigned char *next_in;
 +  } br;
 +
 +  /*
 +   * Custom field to denote that this archive contains encrypted entries
 +   */
 +  int has_encrypted_entries;
 +};
 +
 +static int archive_read_support_format_rar_capabilities(struct archive_read *);
 +static int archive_read_format_rar_has_encrypted_entries(struct archive_read *);
 +static int archive_read_format_rar_bid(struct archive_read *, int);
 +static int archive_read_format_rar_options(struct archive_read *,
 +    const char *, const char *);
 +static int archive_read_format_rar_read_header(struct archive_read *,
 +    struct archive_entry *);
 +static int archive_read_format_rar_read_data(struct archive_read *,
 +    const void **, size_t *, int64_t *);
 +static int archive_read_format_rar_read_data_skip(struct archive_read *a);
 +static int64_t archive_read_format_rar_seek_data(struct archive_read *, int64_t,
 +    int);
 +static int archive_read_format_rar_cleanup(struct archive_read *);
 +
 +/* Support functions */
 +static int read_header(struct archive_read *, struct archive_entry *, char);
 +static time_t get_time(int);
 +static int read_exttime(const char *, struct rar *, const char *);
 +static int read_symlink_stored(struct archive_read *, struct archive_entry *,
 +                               struct archive_string_conv *);
 +static int read_data_stored(struct archive_read *, const void **, size_t *,
 +                            int64_t *);
 +static int read_data_compressed(struct archive_read *, const void **, size_t *,
 +                          int64_t *);
 +static int rar_br_preparation(struct archive_read *, struct rar_br *);
 +static int parse_codes(struct archive_read *);
 +static void free_codes(struct archive_read *);
 +static int read_next_symbol(struct archive_read *, struct huffman_code *);
 +static int create_code(struct archive_read *, struct huffman_code *,
 +                        unsigned char *, int, char);
 +static int add_value(struct archive_read *, struct huffman_code *, int, int,
 +                     int);
 +static int new_node(struct huffman_code *);
 +static int make_table(struct archive_read *, struct huffman_code *);
 +static int make_table_recurse(struct archive_read *, struct huffman_code *, int,
 +                              struct huffman_table_entry *, int, int);
 +static int64_t expand(struct archive_read *, int64_t);
 +static int copy_from_lzss_window(struct archive_read *, const void **,
 +                                   int64_t, int);
 +static const void *rar_read_ahead(struct archive_read *, size_t, ssize_t *);
 +
 +/*
 + * Bit stream reader.
 + */
 +/* Check that the cache buffer has enough bits. */
 +#define rar_br_has(br, n) ((br)->cache_avail >= n)
 +/* Get compressed data by bit. */
 +#define rar_br_bits(br, n)        \
 +  (((uint32_t)((br)->cache_buffer >>    \
 +    ((br)->cache_avail - (n)))) & cache_masks[n])
 +#define rar_br_bits_forced(br, n)     \
 +  (((uint32_t)((br)->cache_buffer <<    \
 +    ((n) - (br)->cache_avail))) & cache_masks[n])
 +/* Read ahead to make sure the cache buffer has enough compressed data we
 + * will use.
 + *  True  : completed, there is enough data in the cache buffer.
 + *  False : there is no data in the stream. */
 +#define rar_br_read_ahead(a, br, n) \
 +  ((rar_br_has(br, (n)) || rar_br_fillup(a, br)) || rar_br_has(br, (n)))
 +/* Notify how many bits we consumed. */
 +#define rar_br_consume(br, n) ((br)->cache_avail -= (n))
 +#define rar_br_consume_unalined_bits(br) ((br)->cache_avail &= ~7)
 +
 +static const uint32_t cache_masks[] = {
 +  0x00000000, 0x00000001, 0x00000003, 0x00000007,
 +  0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
 +  0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
 +  0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
 +  0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
 +  0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
 +  0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
 +  0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
 +  0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
 +};
 +
 +/*
 + * Shift away used bits in the cache data and fill it up with following bits.
 + * Call this when cache buffer does not have enough bits you need.
 + *
 + * Returns 1 if the cache buffer is full.
 + * Returns 0 if the cache buffer is not full; input buffer is empty.
 + */
 +static int
 +rar_br_fillup(struct archive_read *a, struct rar_br *br)
 +{
 +  struct rar *rar = (struct rar *)(a->format->data);
 +  int n = CACHE_BITS - br->cache_avail;
 +
 +  for (;;) {
 +    switch (n >> 3) {
 +    case 8:
 +      if (br->avail_in >= 8) {
 +        br->cache_buffer =
 +            ((uint64_t)br->next_in[0]) << 56 |
 +            ((uint64_t)br->next_in[1]) << 48 |
 +            ((uint64_t)br->next_in[2]) << 40 |
 +            ((uint64_t)br->next_in[3]) << 32 |
 +            ((uint32_t)br->next_in[4]) << 24 |
 +            ((uint32_t)br->next_in[5]) << 16 |
 +            ((uint32_t)br->next_in[6]) << 8 |
 +             (uint32_t)br->next_in[7];
 +        br->next_in += 8;
 +        br->avail_in -= 8;
 +        br->cache_avail += 8 * 8;
 +        rar->bytes_unconsumed += 8;
 +        rar->bytes_remaining -= 8;
 +        return (1);
 +      }
 +      break;
 +    case 7:
 +      if (br->avail_in >= 7) {
 +        br->cache_buffer =
 +           (br->cache_buffer << 56) |
 +            ((uint64_t)br->next_in[0]) << 48 |
 +            ((uint64_t)br->next_in[1]) << 40 |
 +            ((uint64_t)br->next_in[2]) << 32 |
 +            ((uint32_t)br->next_in[3]) << 24 |
 +            ((uint32_t)br->next_in[4]) << 16 |
 +            ((uint32_t)br->next_in[5]) << 8 |
 +             (uint32_t)br->next_in[6];
 +        br->next_in += 7;
 +        br->avail_in -= 7;
 +        br->cache_avail += 7 * 8;
 +        rar->bytes_unconsumed += 7;
 +        rar->bytes_remaining -= 7;
 +        return (1);
 +      }
 +      break;
 +    case 6:
 +      if (br->avail_in >= 6) {
 +        br->cache_buffer =
 +           (br->cache_buffer << 48) |
 +            ((uint64_t)br->next_in[0]) << 40 |
 +            ((uint64_t)br->next_in[1]) << 32 |
 +            ((uint32_t)br->next_in[2]) << 24 |
 +            ((uint32_t)br->next_in[3]) << 16 |
 +            ((uint32_t)br->next_in[4]) << 8 |
 +             (uint32_t)br->next_in[5];
 +        br->next_in += 6;
 +        br->avail_in -= 6;
 +        br->cache_avail += 6 * 8;
 +        rar->bytes_unconsumed += 6;
 +        rar->bytes_remaining -= 6;
 +        return (1);
 +      }
 +      break;
 +    case 0:
 +      /* We have enough compressed data in
 +       * the cache buffer.*/
 +      return (1);
 +    default:
 +      break;
 +    }
 +    if (br->avail_in <= 0) {
 +
 +      if (rar->bytes_unconsumed > 0) {
 +        /* Consume as much as the decompressor
 +         * actually used. */
 +        __archive_read_consume(a, rar->bytes_unconsumed);
 +        rar->bytes_unconsumed = 0;
 +      }
 +      br->next_in = rar_read_ahead(a, 1, &(br->avail_in));
 +      if (br->next_in == NULL)
 +        return (0);
 +      if (br->avail_in == 0)
 +        return (0);
 +    }
 +    br->cache_buffer =
 +       (br->cache_buffer << 8) | *br->next_in++;
 +    br->avail_in--;
 +    br->cache_avail += 8;
 +    n -= 8;
 +    rar->bytes_unconsumed++;
 +    rar->bytes_remaining--;
 +  }
 +}
 +
 +static int
 +rar_br_preparation(struct archive_read *a, struct rar_br *br)
 +{
 +  struct rar *rar = (struct rar *)(a->format->data);
 +
 +  if (rar->bytes_remaining > 0) {
 +    br->next_in = rar_read_ahead(a, 1, &(br->avail_in));
 +    if (br->next_in == NULL) {
 +      archive_set_error(&a->archive,
 +          ARCHIVE_ERRNO_FILE_FORMAT,
 +          "Truncated RAR file data");
 +      return (ARCHIVE_FATAL);
 +    }
 +    if (br->cache_avail == 0)
 +      (void)rar_br_fillup(a, br);
 +  }
 +  return (ARCHIVE_OK);
 +}
 +
 +/* Find last bit set */
 +static inline int
 +rar_fls(unsigned int word)
 +{
 +  word |= (word >>  1);
 +  word |= (word >>  2);
 +  word |= (word >>  4);
 +  word |= (word >>  8);
 +  word |= (word >> 16);
 +  return word - (word >> 1);
 +}
 +
 +/* LZSS functions */
 +static inline int64_t
 +lzss_position(struct lzss *lzss)
 +{
 +  return lzss->position;
 +}
 +
 +static inline int
 +lzss_mask(struct lzss *lzss)
 +{
 +  return lzss->mask;
 +}
 +
 +static inline int
 +lzss_size(struct lzss *lzss)
 +{
 +  return lzss->mask + 1;
 +}
 +
 +static inline int
 +lzss_offset_for_position(struct lzss *lzss, int64_t pos)
 +{
 +  return (int)(pos & lzss->mask);
 +}
 +
 +static inline unsigned char *
 +lzss_pointer_for_position(struct lzss *lzss, int64_t pos)
 +{
 +  return &lzss->window[lzss_offset_for_position(lzss, pos)];
 +}
 +
 +static inline int
 +lzss_current_offset(struct lzss *lzss)
 +{
 +  return lzss_offset_for_position(lzss, lzss->position);
 +}
 +
 +static inline uint8_t *
 +lzss_current_pointer(struct lzss *lzss)
 +{
 +  return lzss_pointer_for_position(lzss, lzss->position);
 +}
 +
 +static inline void
 +lzss_emit_literal(struct rar *rar, uint8_t literal)
 +{
 +  *lzss_current_pointer(&rar->lzss) = literal;
 +  rar->lzss.position++;
 +}
 +
 +static inline void
 +lzss_emit_match(struct rar *rar, int offset, int length)
 +{
 +  int dstoffs = lzss_current_offset(&rar->lzss);
 +  int srcoffs = (dstoffs - offset) & lzss_mask(&rar->lzss);
 +  int l, li, remaining;
 +  unsigned char *d, *s;
 +
 +  remaining = length;
 +  while (remaining > 0) {
 +    l = remaining;
 +    if (dstoffs > srcoffs) {
 +      if (l > lzss_size(&rar->lzss) - dstoffs)
 +        l = lzss_size(&rar->lzss) - dstoffs;
 +    } else {
 +      if (l > lzss_size(&rar->lzss) - srcoffs)
 +        l = lzss_size(&rar->lzss) - srcoffs;
 +    }
 +    d = &(rar->lzss.window[dstoffs]);
 +    s = &(rar->lzss.window[srcoffs]);
 +    if ((dstoffs + l < srcoffs) || (srcoffs + l < dstoffs))
 +      memcpy(d, s, l);
 +    else {
 +      for (li = 0; li < l; li++)
 +        d[li] = s[li];
 +    }
 +    remaining -= l;
 +    dstoffs = (dstoffs + l) & lzss_mask(&(rar->lzss));
 +    srcoffs = (srcoffs + l) & lzss_mask(&(rar->lzss));
 +  }
 +  rar->lzss.position += length;
 +}
 +
 +static void *
 +ppmd_alloc(void *p, size_t size)
 +{
 +  (void)p;
 +  return malloc(size);
 +}
 +static void
 +ppmd_free(void *p, void *address)
 +{
 +  (void)p;
 +  free(address);
 +}
 +static ISzAlloc g_szalloc = { ppmd_alloc, ppmd_free };
 +
 +static Byte
 +ppmd_read(void *p)
 +{
 +  struct archive_read *a = ((IByteIn*)p)->a;
 +  struct rar *rar = (struct rar *)(a->format->data);
 +  struct rar_br *br = &(rar->br);
 +  Byte b;
 +  if (!rar_br_read_ahead(a, br, 8))
 +  {
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "Truncated RAR file data");
 +    rar->valid = 0;
 +    return 0;
 +  }
 +  b = rar_br_bits(br, 8);
 +  rar_br_consume(br, 8);
 +  return b;
 +}
 +
 +int
 +archive_read_support_format_rar(struct archive *_a)
 +{
 +  struct archive_read *a = (struct archive_read *)_a;
 +  struct rar *rar;
 +  int r;
 +
 +  archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
 +                      "archive_read_support_format_rar");
 +
 +  rar = (struct rar *)calloc(sizeof(*rar), 1);
 +  if (rar == NULL)
 +  {
 +    archive_set_error(&a->archive, ENOMEM, "Can't allocate rar data");
 +    return (ARCHIVE_FATAL);
 +  }
 +
 +	/*
 +	 * Until enough data has been read, we cannot tell about
 +	 * any encrypted entries yet.
 +	 */
 +	rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
 +
 +  r = __archive_read_register_format(a,
 +                                     rar,
 +                                     "rar",
 +                                     archive_read_format_rar_bid,
 +                                     archive_read_format_rar_options,
 +                                     archive_read_format_rar_read_header,
 +                                     archive_read_format_rar_read_data,
 +                                     archive_read_format_rar_read_data_skip,
 +                                     archive_read_format_rar_seek_data,
 +                                     archive_read_format_rar_cleanup,
 +                                     archive_read_support_format_rar_capabilities,
 +                                     archive_read_format_rar_has_encrypted_entries);
 +
 +  if (r != ARCHIVE_OK)
 +    free(rar);
 +  return (r);
 +}
 +
 +static int
 +archive_read_support_format_rar_capabilities(struct archive_read * a)
 +{
 +	(void)a; /* UNUSED */
 +	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA
 +			| ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
 +}
 +
 +static int
 +archive_read_format_rar_has_encrypted_entries(struct archive_read *_a)
 +{
 +	if (_a && _a->format) {
 +		struct rar * rar = (struct rar *)_a->format->data;
 +		if (rar) {
 +			return rar->has_encrypted_entries;
 +		}
 +	}
 +	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
 +}
 +
 +
 +static int
 +archive_read_format_rar_bid(struct archive_read *a, int best_bid)
 +{
 +  const char *p;
 +
 +  /* If there's already a bid > 30, we'll never win. */
 +  if (best_bid > 30)
 +	  return (-1);
 +
 +  if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
 +    return (-1);
 +
 +  if (memcmp(p, RAR_SIGNATURE, 7) == 0)
 +    return (30);
 +
 +  if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
 +    /* This is a PE file */
 +    ssize_t offset = 0x10000;
 +    ssize_t window = 4096;
 +    ssize_t bytes_avail;
 +    while (offset + window <= (1024 * 128)) {
 +      const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail);
 +      if (buff == NULL) {
 +        /* Remaining bytes are less than window. */
 +        window >>= 1;
 +        if (window < 0x40)
 +          return (0);
 +        continue;
 +      }
 +      p = buff + offset;
 +      while (p + 7 < buff + bytes_avail) {
 +        if (memcmp(p, RAR_SIGNATURE, 7) == 0)
 +          return (30);
 +        p += 0x10;
 +      }
 +      offset = p - buff;
 +    }
 +  }
 +  return (0);
 +}
 +
 +static int
 +skip_sfx(struct archive_read *a)
 +{
 +  const void *h;
 +  const char *p, *q;
 +  size_t skip, total;
 +  ssize_t bytes, window;
 +
 +  total = 0;
 +  window = 4096;
 +  while (total + window <= (1024 * 128)) {
 +    h = __archive_read_ahead(a, window, &bytes);
 +    if (h == NULL) {
 +      /* Remaining bytes are less than window. */
 +      window >>= 1;
 +      if (window < 0x40)
 +      	goto fatal;
 +      continue;
 +    }
 +    if (bytes < 0x40)
 +      goto fatal;
 +    p = h;
 +    q = p + bytes;
 +
 +    /*
 +     * Scan ahead until we find something that looks
 +     * like the RAR header.
 +     */
 +    while (p + 7 < q) {
 +      if (memcmp(p, RAR_SIGNATURE, 7) == 0) {
 +      	skip = p - (const char *)h;
 +      	__archive_read_consume(a, skip);
 +      	return (ARCHIVE_OK);
 +      }
 +      p += 0x10;
 +    }
 +    skip = p - (const char *)h;
 +    __archive_read_consume(a, skip);
 +	total += skip;
 +  }
 +fatal:
 +  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +      "Couldn't find out RAR header");
 +  return (ARCHIVE_FATAL);
 +}
 +
 +static int
 +archive_read_format_rar_options(struct archive_read *a,
 +    const char *key, const char *val)
 +{
 +  struct rar *rar;
 +  int ret = ARCHIVE_FAILED;
 +
 +  rar = (struct rar *)(a->format->data);
 +  if (strcmp(key, "hdrcharset")  == 0) {
 +    if (val == NULL || val[0] == 0)
 +      archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +          "rar: hdrcharset option needs a character-set name");
 +    else {
 +      rar->opt_sconv =
 +          archive_string_conversion_from_charset(
 +              &a->archive, val, 0);
 +      if (rar->opt_sconv != NULL)
 +        ret = ARCHIVE_OK;
 +      else
 +        ret = ARCHIVE_FATAL;
 +    }
 +    return (ret);
 +  }
 +
 +  /* Note: The "warn" return is just to inform the options
 +   * supervisor that we didn't handle it.  It will generate
 +   * a suitable error if no one used this option. */
 +  return (ARCHIVE_WARN);
 +}
 +
 +static int
 +archive_read_format_rar_read_header(struct archive_read *a,
 +                                    struct archive_entry *entry)
 +{
 +  const void *h;
 +  const char *p;
 +  struct rar *rar;
 +  size_t skip;
 +  char head_type;
 +  int ret;
 +  unsigned flags;
 +  unsigned long crc32_expected;
 +
 +  a->archive.archive_format = ARCHIVE_FORMAT_RAR;
 +  if (a->archive.archive_format_name == NULL)
 +    a->archive.archive_format_name = "RAR";
 +
 +  rar = (struct rar *)(a->format->data);
 +
 +  /*
 +   * It should be sufficient to call archive_read_next_header() for
 +   * a reader to determine if an entry is encrypted or not. If the
 +   * encryption of an entry is only detectable when calling
 +   * archive_read_data(), so be it. We'll do the same check there
 +   * as well.
 +   */
 +  if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
 +	  rar->has_encrypted_entries = 0;
 +  }
 +
 +  /* RAR files can be generated without EOF headers, so return ARCHIVE_EOF if
 +  * this fails.
 +  */
 +  if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
 +    return (ARCHIVE_EOF);
 +
 +  p = h;
 +  if (rar->found_first_header == 0 &&
 +     ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)) {
 +    /* This is an executable ? Must be self-extracting... */
 +    ret = skip_sfx(a);
 +    if (ret < ARCHIVE_WARN)
 +      return (ret);
 +  }
 +  rar->found_first_header = 1;
 +
 +  while (1)
 +  {
 +    unsigned long crc32_val;
 +
 +    if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
 +      return (ARCHIVE_FATAL);
 +    p = h;
 +
 +    head_type = p[2];
 +    switch(head_type)
 +    {
 +    case MARK_HEAD:
 +      if (memcmp(p, RAR_SIGNATURE, 7) != 0) {
 +        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +          "Invalid marker header");
 +        return (ARCHIVE_FATAL);
 +      }
 +      __archive_read_consume(a, 7);
 +      break;
 +
 +    case MAIN_HEAD:
 +      rar->main_flags = archive_le16dec(p + 3);
 +      skip = archive_le16dec(p + 5);
 +      if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)) {
 +        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +          "Invalid header size");
 +        return (ARCHIVE_FATAL);
 +      }
 +      if ((h = __archive_read_ahead(a, skip, NULL)) == NULL)
 +        return (ARCHIVE_FATAL);
 +      p = h;
 +      memcpy(rar->reserved1, p + 7, sizeof(rar->reserved1));
 +      memcpy(rar->reserved2, p + 7 + sizeof(rar->reserved1),
 +             sizeof(rar->reserved2));
 +      if (rar->main_flags & MHD_ENCRYPTVER) {
 +        if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)+1) {
 +          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +            "Invalid header size");
 +          return (ARCHIVE_FATAL);
 +        }
 +        rar->encryptver = *(p + 7 + sizeof(rar->reserved1) +
 +                            sizeof(rar->reserved2));
 +      }
 +
 +      /* Main header is password encrypted, so we cannot read any
 +         file names or any other info about files from the header. */
 +      if (rar->main_flags & MHD_PASSWORD)
 +      {
 +        archive_entry_set_is_metadata_encrypted(entry, 1);
 +        archive_entry_set_is_data_encrypted(entry, 1);
 +        rar->has_encrypted_entries = 1;
 +         archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                          "RAR encryption support unavailable.");
 +        return (ARCHIVE_FATAL);
 +      }
 +
 +      crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2);
 +      if ((crc32_val & 0xffff) != archive_le16dec(p)) {
 +        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +          "Header CRC error");
 +        return (ARCHIVE_FATAL);
 +      }
 +      __archive_read_consume(a, skip);
 +      break;
 +
 +    case FILE_HEAD:
 +      return read_header(a, entry, head_type);
 +
 +    case COMM_HEAD:
 +    case AV_HEAD:
 +    case SUB_HEAD:
 +    case PROTECT_HEAD:
 +    case SIGN_HEAD:
 +    case ENDARC_HEAD:
 +      flags = archive_le16dec(p + 3);
 +      skip = archive_le16dec(p + 5);
 +      if (skip < 7) {
 +        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +          "Invalid header size too small");
 +        return (ARCHIVE_FATAL);
 +      }
 +      if (flags & HD_ADD_SIZE_PRESENT)
 +      {
 +        if (skip < 7 + 4) {
 +          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +            "Invalid header size too small");
 +          return (ARCHIVE_FATAL);
 +        }
 +        if ((h = __archive_read_ahead(a, skip, NULL)) == NULL)
 +          return (ARCHIVE_FATAL);
 +        p = h;
 +        skip += archive_le32dec(p + 7);
 +      }
 +
 +      /* Skip over the 2-byte CRC at the beginning of the header. */
 +      crc32_expected = archive_le16dec(p);
 +      __archive_read_consume(a, 2);
 +      skip -= 2;
 +
 +      /* Skim the entire header and compute the CRC. */
 +      crc32_val = 0;
 +      while (skip > 0) {
 +	      size_t to_read = skip;
 +	      ssize_t did_read;
 +	      if (to_read > 32 * 1024) {
 +		      to_read = 32 * 1024;
 +	      }
 +	      if ((h = __archive_read_ahead(a, to_read, &did_read)) == NULL) {
 +		      return (ARCHIVE_FATAL);
 +	      }
 +	      p = h;
 +	      crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned)did_read);
 +	      __archive_read_consume(a, did_read);
 +	      skip -= did_read;
 +      }
 +      if ((crc32_val & 0xffff) != crc32_expected) {
 +	      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		  "Header CRC error");
 +	      return (ARCHIVE_FATAL);
 +      }
 +      if (head_type == ENDARC_HEAD)
 +	      return (ARCHIVE_EOF);
 +      break;
 +
 +    case NEWSUB_HEAD:
 +      if ((ret = read_header(a, entry, head_type)) < ARCHIVE_WARN)
 +        return ret;
 +      break;
 +
 +    default:
 +      archive_set_error(&a->archive,  ARCHIVE_ERRNO_FILE_FORMAT,
 +                        "Bad RAR file");
 +      return (ARCHIVE_FATAL);
 +    }
 +  }
 +}
 +
 +static int
 +archive_read_format_rar_read_data(struct archive_read *a, const void **buff,
 +                                  size_t *size, int64_t *offset)
 +{
 +  struct rar *rar = (struct rar *)(a->format->data);
 +  int ret;
 +
 +  if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
 +	  rar->has_encrypted_entries = 0;
 +  }
 +
 +  if (rar->bytes_unconsumed > 0) {
 +      /* Consume as much as the decompressor actually used. */
 +      __archive_read_consume(a, rar->bytes_unconsumed);
 +      rar->bytes_unconsumed = 0;
 +  }
 +
 +  *buff = NULL;
 +  if (rar->entry_eof || rar->offset_seek >= rar->unp_size) {
 +    *size = 0;
 +    *offset = rar->offset;
 +    if (*offset < rar->unp_size)
 +      *offset = rar->unp_size;
 +    return (ARCHIVE_EOF);
 +  }
 +
 +  switch (rar->compression_method)
 +  {
 +  case COMPRESS_METHOD_STORE:
 +    ret = read_data_stored(a, buff, size, offset);
 +    break;
 +
 +  case COMPRESS_METHOD_FASTEST:
 +  case COMPRESS_METHOD_FAST:
 +  case COMPRESS_METHOD_NORMAL:
 +  case COMPRESS_METHOD_GOOD:
 +  case COMPRESS_METHOD_BEST:
 +    ret = read_data_compressed(a, buff, size, offset);
 +    if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN)
 +      __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context, &g_szalloc);
 +    break;
 +
 +  default:
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "Unsupported compression method for RAR file.");
 +    ret = ARCHIVE_FATAL;
 +    break;
 +  }
 +  return (ret);
 +}
 +
 +static int
 +archive_read_format_rar_read_data_skip(struct archive_read *a)
 +{
 +  struct rar *rar;
 +  int64_t bytes_skipped;
 +  int ret;
 +
 +  rar = (struct rar *)(a->format->data);
 +
 +  if (rar->bytes_unconsumed > 0) {
 +      /* Consume as much as the decompressor actually used. */
 +      __archive_read_consume(a, rar->bytes_unconsumed);
 +      rar->bytes_unconsumed = 0;
 +  }
 +
 +  if (rar->bytes_remaining > 0) {
 +    bytes_skipped = __archive_read_consume(a, rar->bytes_remaining);
 +    if (bytes_skipped < 0)
 +      return (ARCHIVE_FATAL);
 +  }
 +
 +  /* Compressed data to skip must be read from each header in a multivolume
 +   * archive.
 +   */
 +  if (rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER)
 +  {
 +    ret = archive_read_format_rar_read_header(a, a->entry);
 +    if (ret == (ARCHIVE_EOF))
 +      ret = archive_read_format_rar_read_header(a, a->entry);
 +    if (ret != (ARCHIVE_OK))
 +      return ret;
 +    return archive_read_format_rar_read_data_skip(a);
 +  }
 +
 +  return (ARCHIVE_OK);
 +}
 +
 +static int64_t
 +archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset,
 +    int whence)
 +{
 +  int64_t client_offset, ret;
 +  unsigned int i;
 +  struct rar *rar = (struct rar *)(a->format->data);
 +
 +  if (rar->compression_method == COMPRESS_METHOD_STORE)
 +  {
 +    /* Modify the offset for use with SEEK_SET */
 +    switch (whence)
 +    {
 +      case SEEK_CUR:
 +        client_offset = rar->offset_seek;
 +        break;
 +      case SEEK_END:
 +        client_offset = rar->unp_size;
 +        break;
 +      case SEEK_SET:
 +      default:
 +        client_offset = 0;
 +    }
 +    client_offset += offset;
 +    if (client_offset < 0)
 +    {
 +      /* Can't seek past beginning of data block */
 +      return -1;
 +    }
 +    else if (client_offset > rar->unp_size)
 +    {
 +      /*
 +       * Set the returned offset but only seek to the end of
 +       * the data block.
 +       */
 +      rar->offset_seek = client_offset;
 +      client_offset = rar->unp_size;
 +    }
 +
 +    client_offset += rar->dbo[0].start_offset;
 +    i = 0;
 +    while (i < rar->cursor)
 +    {
 +      i++;
 +      client_offset += rar->dbo[i].start_offset - rar->dbo[i-1].end_offset;
 +    }
 +    if (rar->main_flags & MHD_VOLUME)
 +    {
 +      /* Find the appropriate offset among the multivolume archive */
 +      while (1)
 +      {
 +        if (client_offset < rar->dbo[rar->cursor].start_offset &&
 +          rar->file_flags & FHD_SPLIT_BEFORE)
 +        {
 +          /* Search backwards for the correct data block */
 +          if (rar->cursor == 0)
 +          {
 +            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +              "Attempt to seek past beginning of RAR data block");
 +            return (ARCHIVE_FAILED);
 +          }
 +          rar->cursor--;
 +          client_offset -= rar->dbo[rar->cursor+1].start_offset -
 +            rar->dbo[rar->cursor].end_offset;
 +          if (client_offset < rar->dbo[rar->cursor].start_offset)
 +            continue;
 +          ret = __archive_read_seek(a, rar->dbo[rar->cursor].start_offset -
 +            rar->dbo[rar->cursor].header_size, SEEK_SET);
 +          if (ret < (ARCHIVE_OK))
 +            return ret;
 +          ret = archive_read_format_rar_read_header(a, a->entry);
 +          if (ret != (ARCHIVE_OK))
 +          {
 +            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +              "Error during seek of RAR file");
 +            return (ARCHIVE_FAILED);
 +          }
 +          rar->cursor--;
 +          break;
 +        }
 +        else if (client_offset > rar->dbo[rar->cursor].end_offset &&
 +          rar->file_flags & FHD_SPLIT_AFTER)
 +        {
 +          /* Search forward for the correct data block */
 +          rar->cursor++;
 +          if (rar->cursor < rar->nodes &&
 +            client_offset > rar->dbo[rar->cursor].end_offset)
 +          {
 +            client_offset += rar->dbo[rar->cursor].start_offset -
 +              rar->dbo[rar->cursor-1].end_offset;
 +            continue;
 +          }
 +          rar->cursor--;
 +          ret = __archive_read_seek(a, rar->dbo[rar->cursor].end_offset,
 +                                    SEEK_SET);
 +          if (ret < (ARCHIVE_OK))
 +            return ret;
 +          ret = archive_read_format_rar_read_header(a, a->entry);
 +          if (ret == (ARCHIVE_EOF))
 +          {
 +            rar->has_endarc_header = 1;
 +            ret = archive_read_format_rar_read_header(a, a->entry);
 +          }
 +          if (ret != (ARCHIVE_OK))
 +          {
 +            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +              "Error during seek of RAR file");
 +            return (ARCHIVE_FAILED);
 +          }
 +          client_offset += rar->dbo[rar->cursor].start_offset -
 +            rar->dbo[rar->cursor-1].end_offset;
 +          continue;
 +        }
 +        break;
 +      }
 +    }
 +
 +    ret = __archive_read_seek(a, client_offset, SEEK_SET);
 +    if (ret < (ARCHIVE_OK))
 +      return ret;
 +    rar->bytes_remaining = rar->dbo[rar->cursor].end_offset - ret;
 +    i = rar->cursor;
 +    while (i > 0)
 +    {
 +      i--;
 +      ret -= rar->dbo[i+1].start_offset - rar->dbo[i].end_offset;
 +    }
 +    ret -= rar->dbo[0].start_offset;
 +
 +    /* Always restart reading the file after a seek */
 +    __archive_reset_read_data(&a->archive);
 +
 +    rar->bytes_unconsumed = 0;
 +    rar->offset = 0;
 +
 +    /*
 +     * If a seek past the end of file was requested, return the requested
 +     * offset.
 +     */
 +    if (ret == rar->unp_size && rar->offset_seek > rar->unp_size)
 +      return rar->offset_seek;
 +
 +    /* Return the new offset */
 +    rar->offset_seek = ret;
 +    return rar->offset_seek;
 +  }
 +  else
 +  {
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +      "Seeking of compressed RAR files is unsupported");
 +  }
 +  return (ARCHIVE_FAILED);
 +}
 +
 +static int
 +archive_read_format_rar_cleanup(struct archive_read *a)
 +{
 +  struct rar *rar;
 +
 +  rar = (struct rar *)(a->format->data);
 +  free_codes(a);
 +  free(rar->filename);
 +  free(rar->filename_save);
 +  free(rar->dbo);
 +  free(rar->unp_buffer);
 +  free(rar->lzss.window);
 +  __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context, &g_szalloc);
 +  free(rar);
 +  (a->format->data) = NULL;
 +  return (ARCHIVE_OK);
 +}
 +
 +static int
 +read_header(struct archive_read *a, struct archive_entry *entry,
 +            char head_type)
 +{
 +  const void *h;
 +  const char *p, *endp;
 +  struct rar *rar;
 +  struct rar_header rar_header;
 +  struct rar_file_header file_header;
 +  int64_t header_size;
 +  unsigned filename_size, end;
 +  char *filename;
 +  char *strp;
 +  char packed_size[8];
 +  char unp_size[8];
 +  int ttime;
 +  struct archive_string_conv *sconv, *fn_sconv;
 +  unsigned long crc32_val;
 +  int ret = (ARCHIVE_OK), ret2;
 +
 +  rar = (struct rar *)(a->format->data);
 +
 +  /* Setup a string conversion object for non-rar-unicode filenames. */
 +  sconv = rar->opt_sconv;
 +  if (sconv == NULL) {
 +    if (!rar->init_default_conversion) {
 +      rar->sconv_default =
 +          archive_string_default_conversion_for_read(
 +            &(a->archive));
 +      rar->init_default_conversion = 1;
 +    }
 +    sconv = rar->sconv_default;
 +  }
 +
 +
 +  if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
 +    return (ARCHIVE_FATAL);
 +  p = h;
 +  memcpy(&rar_header, p, sizeof(rar_header));
 +  rar->file_flags = archive_le16dec(rar_header.flags);
 +  header_size = archive_le16dec(rar_header.size);
 +  if (header_size < (int64_t)sizeof(file_header) + 7) {
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +      "Invalid header size");
 +    return (ARCHIVE_FATAL);
 +  }
 +  crc32_val = crc32(0, (const unsigned char *)p + 2, 7 - 2);
 +  __archive_read_consume(a, 7);
 +
 +  if (!(rar->file_flags & FHD_SOLID))
 +  {
 +    rar->compression_method = 0;
 +    rar->packed_size = 0;
 +    rar->unp_size = 0;
 +    rar->mtime = 0;
 +    rar->ctime = 0;
 +    rar->atime = 0;
 +    rar->arctime = 0;
 +    rar->mode = 0;
 +    memset(&rar->salt, 0, sizeof(rar->salt));
 +    rar->atime = 0;
 +    rar->ansec = 0;
 +    rar->ctime = 0;
 +    rar->cnsec = 0;
 +    rar->mtime = 0;
 +    rar->mnsec = 0;
 +    rar->arctime = 0;
 +    rar->arcnsec = 0;
 +  }
 +  else
 +  {
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "RAR solid archive support unavailable.");
 +    return (ARCHIVE_FATAL);
 +  }
 +
 +  if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)
 +    return (ARCHIVE_FATAL);
 +
 +  /* File Header CRC check. */
 +  crc32_val = crc32(crc32_val, h, (unsigned)(header_size - 7));
 +  if ((crc32_val & 0xffff) != archive_le16dec(rar_header.crc)) {
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +      "Header CRC error");
 +    return (ARCHIVE_FATAL);
 +  }
 +  /* If no CRC error, Go on parsing File Header. */
 +  p = h;
 +  endp = p + header_size - 7;
 +  memcpy(&file_header, p, sizeof(file_header));
 +  p += sizeof(file_header);
 +
 +  rar->compression_method = file_header.method;
 +
 +  ttime = archive_le32dec(file_header.file_time);
 +  rar->mtime = get_time(ttime);
 +
 +  rar->file_crc = archive_le32dec(file_header.file_crc);
 +
 +  if (rar->file_flags & FHD_PASSWORD)
 +  {
 +	archive_entry_set_is_data_encrypted(entry, 1);
 +	rar->has_encrypted_entries = 1;
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "RAR encryption support unavailable.");
 +    /* Since it is only the data part itself that is encrypted we can at least
 +       extract information about the currently processed entry and don't need
 +       to return ARCHIVE_FATAL here. */
 +    /*return (ARCHIVE_FATAL);*/
 +  }
 +
 +  if (rar->file_flags & FHD_LARGE)
 +  {
 +    memcpy(packed_size, file_header.pack_size, 4);
 +    memcpy(packed_size + 4, p, 4); /* High pack size */
 +    p += 4;
 +    memcpy(unp_size, file_header.unp_size, 4);
 +    memcpy(unp_size + 4, p, 4); /* High unpack size */
 +    p += 4;
 +    rar->packed_size = archive_le64dec(&packed_size);
 +    rar->unp_size = archive_le64dec(&unp_size);
 +  }
 +  else
 +  {
 +    rar->packed_size = archive_le32dec(file_header.pack_size);
 +    rar->unp_size = archive_le32dec(file_header.unp_size);
 +  }
 +
 +  if (rar->packed_size < 0 || rar->unp_size < 0)
 +  {
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "Invalid sizes specified.");
 +    return (ARCHIVE_FATAL);
 +  }
 +
 +  rar->bytes_remaining = rar->packed_size;
 +
 +  /* TODO: RARv3 subblocks contain comments. For now the complete block is
 +   * consumed at the end.
 +   */
 +  if (head_type == NEWSUB_HEAD) {
 +    size_t distance = p - (const char *)h;
 +    header_size += rar->packed_size;
 +    /* Make sure we have the extended data. */
 +    if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)
 +        return (ARCHIVE_FATAL);
 +    p = h;
 +    endp = p + header_size - 7;
 +    p += distance;
 +  }
 +
 +  filename_size = archive_le16dec(file_header.name_size);
 +  if (p + filename_size > endp) {
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +      "Invalid filename size");
 +    return (ARCHIVE_FATAL);
 +  }
 +  if (rar->filename_allocated < filename_size * 2 + 2) {
 +    char *newptr;
 +    size_t newsize = filename_size * 2 + 2;
 +    newptr = realloc(rar->filename, newsize);
 +    if (newptr == NULL) {
 +      archive_set_error(&a->archive, ENOMEM,
 +                        "Couldn't allocate memory.");
 +      return (ARCHIVE_FATAL);
 +    }
 +    rar->filename = newptr;
 +    rar->filename_allocated = newsize;
 +  }
 +  filename = rar->filename;
 +  memcpy(filename, p, filename_size);
 +  filename[filename_size] = '\0';
 +  if (rar->file_flags & FHD_UNICODE)
 +  {
 +    if (filename_size != strlen(filename))
 +    {
 +      unsigned char highbyte, flagbits, flagbyte;
 +      unsigned fn_end, offset;
 +
 +      end = filename_size;
 +      fn_end = filename_size * 2;
 +      filename_size = 0;
 +      offset = (unsigned)strlen(filename) + 1;
 +      highbyte = *(p + offset++);
 +      flagbits = 0;
 +      flagbyte = 0;
 +      while (offset < end && filename_size < fn_end)
 +      {
 +        if (!flagbits)
 +        {
 +          flagbyte = *(p + offset++);
 +          flagbits = 8;
 +        }
 +
 +        flagbits -= 2;
 +        switch((flagbyte >> flagbits) & 3)
 +        {
 +          case 0:
 +            filename[filename_size++] = '\0';
 +            filename[filename_size++] = *(p + offset++);
 +            break;
 +          case 1:
 +            filename[filename_size++] = highbyte;
 +            filename[filename_size++] = *(p + offset++);
 +            break;
 +          case 2:
 +            filename[filename_size++] = *(p + offset + 1);
 +            filename[filename_size++] = *(p + offset);
 +            offset += 2;
 +            break;
 +          case 3:
 +          {
 +            char extra, high;
 +            uint8_t length = *(p + offset++);
 +
 +            if (length & 0x80) {
 +              extra = *(p + offset++);
 +              high = (char)highbyte;
 +            } else
 +              extra = high = 0;
 +            length = (length & 0x7f) + 2;
 +            while (length && filename_size < fn_end) {
 +              unsigned cp = filename_size >> 1;
 +              filename[filename_size++] = high;
 +              filename[filename_size++] = p[cp] + extra;
 +              length--;
 +            }
 +          }
 +          break;
 +        }
 +      }
 +      if (filename_size > fn_end) {
 +        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +          "Invalid filename");
 +        return (ARCHIVE_FATAL);
 +      }
 +      filename[filename_size++] = '\0';
 +      filename[filename_size++] = '\0';
 +
 +      /* Decoded unicode form is UTF-16BE, so we have to update a string
 +       * conversion object for it. */
 +      if (rar->sconv_utf16be == NULL) {
 +        rar->sconv_utf16be = archive_string_conversion_from_charset(
 +           &a->archive, "UTF-16BE", 1);
 +        if (rar->sconv_utf16be == NULL)
 +          return (ARCHIVE_FATAL);
 +      }
 +      fn_sconv = rar->sconv_utf16be;
 +
 +      strp = filename;
 +      while (memcmp(strp, "\x00\x00", 2))
 +      {
 +        if (!memcmp(strp, "\x00\\", 2))
 +          *(strp + 1) = '/';
 +        strp += 2;
 +      }
 +      p += offset;
 +    } else {
 +      /*
 +       * If FHD_UNICODE is set but no unicode data, this file name form
 +       * is UTF-8, so we have to update a string conversion object for
 +       * it accordingly.
 +       */
 +      if (rar->sconv_utf8 == NULL) {
 +        rar->sconv_utf8 = archive_string_conversion_from_charset(
 +           &a->archive, "UTF-8", 1);
 +        if (rar->sconv_utf8 == NULL)
 +          return (ARCHIVE_FATAL);
 +      }
 +      fn_sconv = rar->sconv_utf8;
 +      while ((strp = strchr(filename, '\\')) != NULL)
 +        *strp = '/';
 +      p += filename_size;
 +    }
 +  }
 +  else
 +  {
 +    fn_sconv = sconv;
 +    while ((strp = strchr(filename, '\\')) != NULL)
 +      *strp = '/';
 +    p += filename_size;
 +  }
 +
 +  /* Split file in multivolume RAR. No more need to process header. */
 +  if (rar->filename_save &&
 +    filename_size == rar->filename_save_size &&
 +    !memcmp(rar->filename, rar->filename_save, filename_size + 1))
 +  {
 +    __archive_read_consume(a, header_size - 7);
 +    rar->cursor++;
 +    if (rar->cursor >= rar->nodes)
 +    {
 +      rar->nodes++;
 +      if ((rar->dbo =
 +        realloc(rar->dbo, sizeof(*rar->dbo) * rar->nodes)) == NULL)
 +      {
 +        archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
 +        return (ARCHIVE_FATAL);
 +      }
 +      rar->dbo[rar->cursor].header_size = header_size;
 +      rar->dbo[rar->cursor].start_offset = -1;
 +      rar->dbo[rar->cursor].end_offset = -1;
 +    }
 +    if (rar->dbo[rar->cursor].start_offset < 0)
 +    {
 +      rar->dbo[rar->cursor].start_offset = a->filter->position;
 +      rar->dbo[rar->cursor].end_offset = rar->dbo[rar->cursor].start_offset +
 +        rar->packed_size;
 +    }
 +    return ret;
 +  }
 +
 +  rar->filename_save = (char*)realloc(rar->filename_save,
 +                                      filename_size + 1);
 +  memcpy(rar->filename_save, rar->filename, filename_size + 1);
 +  rar->filename_save_size = filename_size;
 +
 +  /* Set info for seeking */
 +  free(rar->dbo);
 +  if ((rar->dbo = calloc(1, sizeof(*rar->dbo))) == NULL)
 +  {
 +    archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
 +    return (ARCHIVE_FATAL);
 +  }
 +  rar->dbo[0].header_size = header_size;
 +  rar->dbo[0].start_offset = -1;
 +  rar->dbo[0].end_offset = -1;
 +  rar->cursor = 0;
 +  rar->nodes = 1;
 +
 +  if (rar->file_flags & FHD_SALT)
 +  {
 +    if (p + 8 > endp) {
 +      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +        "Invalid header size");
 +      return (ARCHIVE_FATAL);
 +    }
 +    memcpy(rar->salt, p, 8);
 +    p += 8;
 +  }
 +
 +  if (rar->file_flags & FHD_EXTTIME) {
 +    if (read_exttime(p, rar, endp) < 0) {
 +      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +        "Invalid header size");
 +      return (ARCHIVE_FATAL);
 +    }
 +  }
 +
 +  __archive_read_consume(a, header_size - 7);
 +  rar->dbo[0].start_offset = a->filter->position;
 +  rar->dbo[0].end_offset = rar->dbo[0].start_offset + rar->packed_size;
 +
 +  switch(file_header.host_os)
 +  {
 +  case OS_MSDOS:
 +  case OS_OS2:
 +  case OS_WIN32:
 +    rar->mode = archive_le32dec(file_header.file_attr);
 +    if (rar->mode & FILE_ATTRIBUTE_DIRECTORY)
 +      rar->mode = AE_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
 +    else
 +      rar->mode = AE_IFREG;
 +    rar->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
 +    break;
 +
 +  case OS_UNIX:
 +  case OS_MAC_OS:
 +  case OS_BEOS:
 +    rar->mode = archive_le32dec(file_header.file_attr);
 +    break;
 +
 +  default:
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "Unknown file attributes from RAR file's host OS");
 +    return (ARCHIVE_FATAL);
 +  }
 +
 +  rar->bytes_uncopied = rar->bytes_unconsumed = 0;
 +  rar->lzss.position = rar->offset = 0;
 +  rar->offset_seek = 0;
 +  rar->dictionary_size = 0;
 +  rar->offset_outgoing = 0;
 +  rar->br.cache_avail = 0;
 +  rar->br.avail_in = 0;
 +  rar->crc_calculated = 0;
 +  rar->entry_eof = 0;
 +  rar->valid = 1;
 +  rar->is_ppmd_block = 0;
 +  rar->start_new_table = 1;
 +  free(rar->unp_buffer);
 +  rar->unp_buffer = NULL;
 +  rar->unp_offset = 0;
 +  rar->unp_buffer_size = UNP_BUFFER_SIZE;
 +  memset(rar->lengthtable, 0, sizeof(rar->lengthtable));
 +  __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context, &g_szalloc);
 +  rar->ppmd_valid = rar->ppmd_eod = 0;
 +
 +  /* Don't set any archive entries for non-file header types */
 +  if (head_type == NEWSUB_HEAD)
 +    return ret;
 +
 +  archive_entry_set_mtime(entry, rar->mtime, rar->mnsec);
 +  archive_entry_set_ctime(entry, rar->ctime, rar->cnsec);
 +  archive_entry_set_atime(entry, rar->atime, rar->ansec);
 +  archive_entry_set_size(entry, rar->unp_size);
 +  archive_entry_set_mode(entry, rar->mode);
 +
 +  if (archive_entry_copy_pathname_l(entry, filename, filename_size, fn_sconv))
 +  {
 +    if (errno == ENOMEM)
 +    {
 +      archive_set_error(&a->archive, ENOMEM,
 +                        "Can't allocate memory for Pathname");
 +      return (ARCHIVE_FATAL);
 +    }
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "Pathname cannot be converted from %s to current locale.",
 +                      archive_string_conversion_charset_name(fn_sconv));
 +    ret = (ARCHIVE_WARN);
 +  }
 +
 +  if (((rar->mode) & AE_IFMT) == AE_IFLNK)
 +  {
 +    /* Make sure a symbolic-link file does not have its body. */
 +    rar->bytes_remaining = 0;
 +    archive_entry_set_size(entry, 0);
 +
 +    /* Read a symbolic-link name. */
 +    if ((ret2 = read_symlink_stored(a, entry, sconv)) < (ARCHIVE_WARN))
 +      return ret2;
 +    if (ret > ret2)
 +      ret = ret2;
 +  }
 +
 +  if (rar->bytes_remaining == 0)
 +    rar->entry_eof = 1;
 +
 +  return ret;
 +}
 +
 +static time_t
 +get_time(int ttime)
 +{
 +  struct tm tm;
 +  tm.tm_sec = 2 * (ttime & 0x1f);
 +  tm.tm_min = (ttime >> 5) & 0x3f;
 +  tm.tm_hour = (ttime >> 11) & 0x1f;
 +  tm.tm_mday = (ttime >> 16) & 0x1f;
 +  tm.tm_mon = ((ttime >> 21) & 0x0f) - 1;
 +  tm.tm_year = ((ttime >> 25) & 0x7f) + 80;
 +  tm.tm_isdst = -1;
 +  return mktime(&tm);
 +}
 +
 +static int
 +read_exttime(const char *p, struct rar *rar, const char *endp)
 +{
 +  unsigned rmode, flags, rem, j, count;
 +  int ttime, i;
 +  struct tm *tm;
 +  time_t t;
 +  long nsec;
 +
 +  if (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 = ((*p) << 16) | (rem >> 8);
++        rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8);
 +        p++;
 +      }
 +      tm = localtime(&t);
 +      nsec = tm->tm_sec + rem / NS_UNIT;
 +      if (rmode & 4)
 +      {
 +        tm->tm_sec++;
 +        t = mktime(tm);
 +      }
 +      if (i == 3)
 +      {
 +        rar->mtime = t;
 +        rar->mnsec = nsec;
 +      }
 +      else if (i == 2)
 +      {
 +        rar->ctime = t;
 +        rar->cnsec = nsec;
 +      }
 +      else if (i == 1)
 +      {
 +        rar->atime = t;
 +        rar->ansec = nsec;
 +      }
 +      else
 +      {
 +        rar->arctime = t;
 +        rar->arcnsec = nsec;
 +      }
 +    }
 +  }
 +  return (0);
 +}
 +
 +static int
 +read_symlink_stored(struct archive_read *a, struct archive_entry *entry,
 +                    struct archive_string_conv *sconv)
 +{
 +  const void *h;
 +  const char *p;
 +  struct rar *rar;
 +  int ret = (ARCHIVE_OK);
 +
 +  rar = (struct rar *)(a->format->data);
 +  if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL)
 +    return (ARCHIVE_FATAL);
 +  p = h;
 +
 +  if (archive_entry_copy_symlink_l(entry,
 +      p, (size_t)rar->packed_size, sconv))
 +  {
 +    if (errno == ENOMEM)
 +    {
 +      archive_set_error(&a->archive, ENOMEM,
 +                        "Can't allocate memory for link");
 +      return (ARCHIVE_FATAL);
 +    }
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "link cannot be converted from %s to current locale.",
 +                      archive_string_conversion_charset_name(sconv));
 +    ret = (ARCHIVE_WARN);
 +  }
 +  __archive_read_consume(a, rar->packed_size);
 +  return ret;
 +}
 +
 +static int
 +read_data_stored(struct archive_read *a, const void **buff, size_t *size,
 +                 int64_t *offset)
 +{
 +  struct rar *rar;
 +  ssize_t bytes_avail;
 +
 +  rar = (struct rar *)(a->format->data);
 +  if (rar->bytes_remaining == 0 &&
 +    !(rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER))
 +  {
 +    *buff = NULL;
 +    *size = 0;
 +    *offset = rar->offset;
 +    if (rar->file_crc != rar->crc_calculated) {
 +      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                        "File CRC error");
 +      return (ARCHIVE_FATAL);
 +    }
 +    rar->entry_eof = 1;
 +    return (ARCHIVE_EOF);
 +  }
 +
 +  *buff = rar_read_ahead(a, 1, &bytes_avail);
 +  if (bytes_avail <= 0)
 +  {
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "Truncated RAR file data");
 +    return (ARCHIVE_FATAL);
 +  }
 +
 +  *size = bytes_avail;
 +  *offset = rar->offset;
 +  rar->offset += bytes_avail;
 +  rar->offset_seek += bytes_avail;
 +  rar->bytes_remaining -= bytes_avail;
 +  rar->bytes_unconsumed = bytes_avail;
 +  /* Calculate File CRC. */
 +  rar->crc_calculated = crc32(rar->crc_calculated, *buff,
 +    (unsigned)bytes_avail);
 +  return (ARCHIVE_OK);
 +}
 +
 +static int
 +read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
 +               int64_t *offset)
 +{
 +  struct rar *rar;
 +  int64_t start, end, actualend;
 +  size_t bs;
 +  int ret = (ARCHIVE_OK), sym, code, lzss_offset, length, i;
 +
 +  rar = (struct rar *)(a->format->data);
 +
 +  do {
 +    if (!rar->valid)
 +      return (ARCHIVE_FATAL);
 +    if (rar->ppmd_eod ||
 +       (rar->dictionary_size && rar->offset >= rar->unp_size))
 +    {
 +      if (rar->unp_offset > 0) {
 +        /*
 +         * We have unprocessed extracted data. write it out.
 +         */
 +        *buff = rar->unp_buffer;
 +        *size = rar->unp_offset;
 +        *offset = rar->offset_outgoing;
 +        rar->offset_outgoing += *size;
 +        /* Calculate File CRC. */
 +        rar->crc_calculated = crc32(rar->crc_calculated, *buff,
 +          (unsigned)*size);
 +        rar->unp_offset = 0;
 +        return (ARCHIVE_OK);
 +      }
 +      *buff = NULL;
 +      *size = 0;
 +      *offset = rar->offset;
 +      if (rar->file_crc != rar->crc_calculated) {
 +        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                          "File CRC error");
 +        return (ARCHIVE_FATAL);
 +      }
 +      rar->entry_eof = 1;
 +      return (ARCHIVE_EOF);
 +    }
 +
 +    if (!rar->is_ppmd_block && rar->dictionary_size && rar->bytes_uncopied > 0)
 +    {
 +      if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset))
 +        bs = rar->unp_buffer_size - rar->unp_offset;
 +      else
 +        bs = (size_t)rar->bytes_uncopied;
 +      ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs);
 +      if (ret != ARCHIVE_OK)
 +        return (ret);
 +      rar->offset += bs;
 +      rar->bytes_uncopied -= bs;
 +      if (*buff != NULL) {
 +        rar->unp_offset = 0;
 +        *size = rar->unp_buffer_size;
 +        *offset = rar->offset_outgoing;
 +        rar->offset_outgoing += *size;
 +        /* Calculate File CRC. */
 +        rar->crc_calculated = crc32(rar->crc_calculated, *buff,
 +          (unsigned)*size);
 +        return (ret);
 +      }
 +      continue;
 +    }
 +
 +    if (!rar->br.next_in &&
 +      (ret = rar_br_preparation(a, &(rar->br))) < ARCHIVE_WARN)
 +      return (ret);
 +    if (rar->start_new_table && ((ret = parse_codes(a)) < (ARCHIVE_WARN)))
 +      return (ret);
 +
 +    if (rar->is_ppmd_block)
 +    {
 +      if ((sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
 +        &rar->ppmd7_context, &rar->range_dec.p)) < 0)
 +      {
 +        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                          "Invalid symbol");
 +        return (ARCHIVE_FATAL);
 +      }
 +      if(sym != rar->ppmd_escape)
 +      {
 +        lzss_emit_literal(rar, sym);
 +        rar->bytes_uncopied++;
 +      }
 +      else
 +      {
 +        if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
 +          &rar->ppmd7_context, &rar->range_dec.p)) < 0)
 +        {
 +          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                            "Invalid symbol");
 +          return (ARCHIVE_FATAL);
 +        }
 +
 +        switch(code)
 +        {
 +          case 0:
 +            rar->start_new_table = 1;
 +            return read_data_compressed(a, buff, size, offset);
 +
 +          case 2:
 +            rar->ppmd_eod = 1;/* End Of ppmd Data. */
 +            continue;
 +
 +          case 3:
 +            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +                              "Parsing filters is unsupported.");
 +            return (ARCHIVE_FAILED);
 +
 +          case 4:
 +            lzss_offset = 0;
 +            for (i = 2; i >= 0; i--)
 +            {
 +              if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
 +                &rar->ppmd7_context, &rar->range_dec.p)) < 0)
 +              {
 +                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                                  "Invalid symbol");
 +                return (ARCHIVE_FATAL);
 +              }
 +              lzss_offset |= code << (i * 8);
 +            }
 +            if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
 +              &rar->ppmd7_context, &rar->range_dec.p)) < 0)
 +            {
 +              archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                                "Invalid symbol");
 +              return (ARCHIVE_FATAL);
 +            }
 +            lzss_emit_match(rar, lzss_offset + 2, length + 32);
 +            rar->bytes_uncopied += length + 32;
 +            break;
 +
 +          case 5:
 +            if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
 +              &rar->ppmd7_context, &rar->range_dec.p)) < 0)
 +            {
 +              archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                                "Invalid symbol");
 +              return (ARCHIVE_FATAL);
 +            }
 +            lzss_emit_match(rar, 1, length + 4);
 +            rar->bytes_uncopied += length + 4;
 +            break;
 +
 +         default:
 +           lzss_emit_literal(rar, sym);
 +           rar->bytes_uncopied++;
 +        }
 +      }
 +    }
 +    else
 +    {
 +      start = rar->offset;
 +      end = start + rar->dictionary_size;
 +      rar->filterstart = INT64_MAX;
 +
 +      if ((actualend = expand(a, end)) < 0)
 +        return ((int)actualend);
 +
 +      rar->bytes_uncopied = actualend - start;
 +      if (rar->bytes_uncopied == 0) {
 +          /* Broken RAR files cause this case.
 +          * NOTE: If this case were possible on a normal RAR file
 +          * we would find out where it was actually bad and
 +          * what we would do to solve it. */
 +          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                            "Internal error extracting RAR file");
 +          return (ARCHIVE_FATAL);
 +      }
 +    }
 +    if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset))
 +      bs = rar->unp_buffer_size - rar->unp_offset;
 +    else
 +      bs = (size_t)rar->bytes_uncopied;
 +    ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs);
 +    if (ret != ARCHIVE_OK)
 +      return (ret);
 +    rar->offset += bs;
 +    rar->bytes_uncopied -= bs;
 +    /*
 +     * If *buff is NULL, it means unp_buffer is not full.
 +     * So we have to continue extracting a RAR file.
 +     */
 +  } while (*buff == NULL);
 +
 +  rar->unp_offset = 0;
 +  *size = rar->unp_buffer_size;
 +  *offset = rar->offset_outgoing;
 +  rar->offset_outgoing += *size;
 +  /* Calculate File CRC. */
 +  rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size);
 +  return ret;
 +}
 +
 +static int
 +parse_codes(struct archive_read *a)
 +{
 +  int i, j, val, n, r;
 +  unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags;
 +  unsigned int maxorder;
 +  struct huffman_code precode;
 +  struct rar *rar = (struct rar *)(a->format->data);
 +  struct rar_br *br = &(rar->br);
 +
 +  free_codes(a);
 +
 +  /* Skip to the next byte */
 +  rar_br_consume_unalined_bits(br);
 +
 +  /* PPMd block flag */
 +  if (!rar_br_read_ahead(a, br, 1))
 +    goto truncated_data;
 +  if ((rar->is_ppmd_block = rar_br_bits(br, 1)) != 0)
 +  {
 +    rar_br_consume(br, 1);
 +    if (!rar_br_read_ahead(a, br, 7))
 +      goto truncated_data;
 +    ppmd_flags = rar_br_bits(br, 7);
 +    rar_br_consume(br, 7);
 +
 +    /* Memory is allocated in MB */
 +    if (ppmd_flags & 0x20)
 +    {
 +      if (!rar_br_read_ahead(a, br, 8))
 +        goto truncated_data;
 +      rar->dictionary_size = (rar_br_bits(br, 8) + 1) << 20;
 +      rar_br_consume(br, 8);
 +    }
 +
 +    if (ppmd_flags & 0x40)
 +    {
 +      if (!rar_br_read_ahead(a, br, 8))
 +        goto truncated_data;
 +      rar->ppmd_escape = rar->ppmd7_context.InitEsc = rar_br_bits(br, 8);
 +      rar_br_consume(br, 8);
 +    }
 +    else
 +      rar->ppmd_escape = 2;
 +
 +    if (ppmd_flags & 0x20)
 +    {
 +      maxorder = (ppmd_flags & 0x1F) + 1;
 +      if(maxorder > 16)
 +        maxorder = 16 + (maxorder - 16) * 3;
 +
 +      if (maxorder == 1)
 +      {
 +        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                          "Truncated RAR file data");
 +        return (ARCHIVE_FATAL);
 +      }
 +
 +      /* Make sure ppmd7_contest is freed before Ppmd7_Construct
 +       * because reading a broken file cause this abnormal sequence. */
 +      __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context, &g_szalloc);
 +
 +      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, &g_szalloc))
 +      {
 +        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;
 +    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; 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 repeatpos, lastnode, bitpos, bit, repeatnode, nextnode;
 +
 +  free(code->table);
 +  code->table = NULL;
 +
 +  if(length > code->maxlength)
 +    code->maxlength = length;
 +  if(length < code->minlength)
 +    code->minlength = length;
 +
 +  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);
 +    }
 +
 +    if (bitpos == repeatpos)
 +    {
 +      /* Open branch check */
 +      if (!(code->tree[lastnode].branches[bit] < 0))
 +      {
 +        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                          "Invalid repeating code");
 +        return (ARCHIVE_FATAL);
 +      }
 +
 +      if ((repeatnode = new_node(code)) < 0) {
 +        archive_set_error(&a->archive, ENOMEM,
 +                          "Unable to allocate memory for node data.");
 +        return (ARCHIVE_FATAL);
 +      }
 +      if ((nextnode = new_node(code)) < 0) {
 +        archive_set_error(&a->archive, ENOMEM,
 +                          "Unable to allocate memory for node data.");
 +        return (ARCHIVE_FATAL);
 +      }
 +
 +      /* Set branches */
 +      code->tree[lastnode].branches[bit] = repeatnode;
 +      code->tree[repeatnode].branches[bit] = repeatnode;
 +      code->tree[repeatnode].branches[bit^1] = nextnode;
 +      lastnode = nextnode;
 +
 +      bitpos++; /* terminating bit already handled, skip it */
 +    }
 +    else
 +    {
 +      /* Open branch check */
 +      if (code->tree[lastnode].branches[bit] < 0)
 +      {
 +        if (new_node(code) < 0) {
 +          archive_set_error(&a->archive, ENOMEM,
 +                            "Unable to allocate memory for node data.");
 +          return (ARCHIVE_FATAL);
 +        }
 +        code->tree[lastnode].branches[bit] = code->numentries++;
 +      }
 +
 +      /* set to branch */
 +      lastnode = code->tree[lastnode].branches[bit];
 +    }
 +  }
 +
 +  if (!(code->tree[lastnode].branches[0] == -1
 +    && code->tree[lastnode].branches[1] == -2))
 +  {
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "Prefix found");
 +    return (ARCHIVE_FATAL);
 +  }
 +
 +  /* Set leaf value */
 +  code->tree[lastnode].branches[0] = value;
 +  code->tree[lastnode].branches[1] = value;
 +
 +  return (ARCHIVE_OK);
 +}
 +
 +static int
 +new_node(struct huffman_code *code)
 +{
 +  void *new_tree;
 +  if (code->numallocatedentries == code->numentries) {
 +    int new_num_entries = 256;
 +    if (code->numentries > 0) {
 +        new_num_entries = code->numentries * 2;
 +    }
 +    new_tree = realloc(code->tree, new_num_entries * sizeof(*code->tree));
 +    if (new_tree == NULL)
 +        return (-1);
 +    code->tree = (struct huffman_tree_node *)new_tree;
 +    code->numallocatedentries = new_num_entries;
 +  }
 +  code->tree[code->numentries].branches[0] = -1;
 +  code->tree[code->numentries].branches[1] = -2;
 +  return 1;
 +}
 +
 +static int
 +make_table(struct archive_read *a, struct huffman_code *code)
 +{
 +  if (code->maxlength < code->minlength || code->maxlength > 10)
 +    code->tablesize = 10;
 +  else
 +    code->tablesize = code->maxlength;
 +
 +  code->table =
 +    (struct huffman_table_entry *)calloc(1, sizeof(*code->table)
 +    * ((size_t)1 << code->tablesize));
 +
 +  return make_table_recurse(a, code, 0, code->table, 0, code->tablesize);
 +}
 +
 +static int
 +make_table_recurse(struct archive_read *a, struct huffman_code *code, int node,
 +                   struct huffman_table_entry *table, int depth,
 +                   int maxdepth)
 +{
 +  int currtablesize, i, ret = (ARCHIVE_OK);
 +
 +  if (!code->tree)
 +  {
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "Huffman tree was not created.");
 +    return (ARCHIVE_FATAL);
 +  }
 +  if (node < 0 || node >= code->numentries)
 +  {
 +    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                      "Invalid location to Huffman tree specified.");
 +    return (ARCHIVE_FATAL);
 +  }
 +
 +  currtablesize = 1 << (maxdepth - depth);
 +
 +  if (code->tree[node].branches[0] ==
 +    code->tree[node].branches[1])
 +  {
 +    for(i = 0; i < currtablesize; i++)
 +    {
 +      table[i].length = depth;
 +      table[i].value = code->tree[node].branches[0];
 +    }
 +  }
 +  else if (node < 0)
 +  {
 +    for(i = 0; i < currtablesize; i++)
 +      table[i].length = -1;
 +  }
 +  else
 +  {
 +    if(depth == maxdepth)
 +    {
 +      table[0].length = maxdepth + 1;
 +      table[0].value = node;
 +    }
 +    else
 +    {
 +      ret |= make_table_recurse(a, code, code->tree[node].branches[0], table,
 +                                depth + 1, maxdepth);
 +      ret |= make_table_recurse(a, code, code->tree[node].branches[1],
 +                         table + currtablesize / 2, depth + 1, maxdepth);
 +    }
 +  }
 +  return ret;
 +}
 +
 +static int64_t
 +expand(struct archive_read *a, int64_t end)
 +{
 +  static const unsigned char lengthbases[] =
 +    {   0,   1,   2,   3,   4,   5,   6,
 +        7,   8,  10,  12,  14,  16,  20,
 +       24,  28,  32,  40,  48,  56,  64,
 +       80,  96, 112, 128, 160, 192, 224 };
 +  static const unsigned char lengthbits[] =
 +    { 0, 0, 0, 0, 0, 0, 0,
 +      0, 1, 1, 1, 1, 2, 2,
 +      2, 2, 3, 3, 3, 3, 4,
 +      4, 4, 4, 5, 5, 5, 5 };
 +  static const 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 unsigned char shortbases[] =
 +    { 0, 4, 8, 16, 32, 64, 128, 192 };
 +  static const unsigned char shortbits[] =
 +    { 2, 2, 3, 4, 5, 6, 6, 6 };
 +
 +  int symbol, offs, len, offsindex, lensymbol, i, offssymbol, lowoffsetsymbol;
 +  unsigned char newfile;
 +  struct rar *rar = (struct rar *)(a->format->data);
 +  struct rar_br *br = &(rar->br);
 +
 +  if (rar->filterstart < end)
 +    end = rar->filterstart;
 +
 +  while (1)
 +  {
 +    if (rar->output_last_match &&
 +      lzss_position(&rar->lzss) + rar->lastlength <= end)
 +    {
 +      lzss_emit_match(rar, rar->lastoffset, rar->lastlength);
 +      rar->output_last_match = 0;
 +    }
 +
 +    if(rar->is_ppmd_block || rar->output_last_match ||
 +      lzss_position(&rar->lzss) >= end)
 +      return lzss_position(&rar->lzss);
 +
 +    if ((symbol = read_next_symbol(a, &rar->maincode)) < 0)
 +      return (ARCHIVE_FATAL);
 +    rar->output_last_match = 0;
 +
 +    if (symbol < 256)
 +    {
 +      lzss_emit_literal(rar, symbol);
 +      continue;
 +    }
 +    else if (symbol == 256)
 +    {
 +      if (!rar_br_read_ahead(a, br, 1))
 +        goto truncated_data;
 +      newfile = !rar_br_bits(br, 1);
 +      rar_br_consume(br, 1);
 +
 +      if(newfile)
 +      {
 +        rar->start_new_block = 1;
 +        if (!rar_br_read_ahead(a, br, 1))
 +          goto truncated_data;
 +        rar->start_new_table = rar_br_bits(br, 1);
 +        rar_br_consume(br, 1);
 +        return lzss_position(&rar->lzss);
 +      }
 +      else
 +      {
 +        if (parse_codes(a) != ARCHIVE_OK)
 +          return (ARCHIVE_FATAL);
 +        continue;
 +      }
 +    }
 +    else if(symbol==257)
 +    {
 +      archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +                        "Parsing filters is unsupported.");
 +      return (ARCHIVE_FAILED);
 +    }
 +    else if(symbol==258)
 +    {
 +      if(rar->lastlength == 0)
 +        continue;
 +
 +      offs = rar->lastoffset;
 +      len = rar->lastlength;
 +    }
 +    else if (symbol <= 262)
 +    {
 +      offsindex = symbol - 259;
 +      offs = rar->oldoffset[offsindex];
 +
 +      if ((lensymbol = read_next_symbol(a, &rar->lengthcode)) < 0)
 +        goto bad_data;
 +      if (lensymbol > (int)(sizeof(lengthbases)/sizeof(lengthbases[0])))
 +        goto bad_data;
 +      if (lensymbol > (int)(sizeof(lengthbits)/sizeof(lengthbits[0])))
 +        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 > (int)(sizeof(lengthbases)/sizeof(lengthbases[0])))
 +        goto bad_data;
 +      if (symbol-271 > (int)(sizeof(lengthbits)/sizeof(lengthbits[0])))
 +        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 > (int)(sizeof(offsetbases)/sizeof(offsetbases[0])))
 +        goto bad_data;
 +      if (offssymbol > (int)(sizeof(offsetbits)/sizeof(offsetbits[0])))
 +        goto bad_data;
 +      offs = offsetbases[offssymbol]+1;
 +      if(offsetbits[offssymbol] > 0)
 +      {
 +        if(offssymbol > 9)
 +        {
 +          if(offsetbits[offssymbol] > 4) {
 +            if (!rar_br_read_ahead(a, br, offsetbits[offssymbol] - 4))
 +              goto truncated_data;
 +            offs += rar_br_bits(br, offsetbits[offssymbol] - 4) << 4;
 +            rar_br_consume(br, offsetbits[offssymbol] - 4);
 +	  }
 +
 +          if(rar->numlowoffsetrepeats > 0)
 +          {
 +            rar->numlowoffsetrepeats--;
 +            offs += rar->lastlowoffset;
 +          }
 +          else
 +          {
 +            if ((lowoffsetsymbol =
 +              read_next_symbol(a, &rar->lowoffsetcode)) < 0)
 +              return (ARCHIVE_FATAL);
 +            if(lowoffsetsymbol == 16)
 +            {
 +              rar->numlowoffsetrepeats = 15;
 +              offs += rar->lastlowoffset;
 +            }
 +            else
 +            {
 +              offs += lowoffsetsymbol;
 +              rar->lastlowoffset = lowoffsetsymbol;
 +            }
 +          }
 +        }
 +        else {
 +          if (!rar_br_read_ahead(a, br, offsetbits[offssymbol]))
 +            goto truncated_data;
 +          offs += rar_br_bits(br, offsetbits[offssymbol]);
 +          rar_br_consume(br, offsetbits[offssymbol]);
 +        }
 +      }
 +
 +      if (offs >= 0x40000)
 +        len++;
 +      if (offs >= 0x2000)
 +        len++;
 +
 +      for(i = 3; i > 0; i--)
 +        rar->oldoffset[i] = rar->oldoffset[i-1];
 +      rar->oldoffset[0] = offs;
 +    }
 +
 +    rar->lastoffset = offs;
 +    rar->lastlength = len;
 +    rar->output_last_match = 1;
 +  }
 +truncated_data:
 +  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                    "Truncated RAR file data");
 +  rar->valid = 0;
 +  return (ARCHIVE_FATAL);
 +bad_data:
 +  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                    "Bad RAR file data");
 +  return (ARCHIVE_FATAL);
 +}
 +
 +static int
 +copy_from_lzss_window(struct archive_read *a, const void **buffer,
 +                        int64_t startpos, int length)
 +{
 +  int windowoffs, firstpart;
 +  struct rar *rar = (struct rar *)(a->format->data);
 +
 +  if (!rar->unp_buffer)
 +  {
 +    if ((rar->unp_buffer = malloc(rar->unp_buffer_size)) == NULL)
 +    {
 +      archive_set_error(&a->archive, ENOMEM,
 +                        "Unable to allocate memory for uncompressed data.");
 +      return (ARCHIVE_FATAL);
 +    }
 +  }
 +
 +  windowoffs = lzss_offset_for_position(&rar->lzss, startpos);
 +  if(windowoffs + length <= lzss_size(&rar->lzss)) {
 +    memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs],
 +           length);
 +  } else if (length <= lzss_size(&rar->lzss)) {
 +    firstpart = lzss_size(&rar->lzss) - windowoffs;
 +    if (firstpart < 0) {
 +      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                        "Bad RAR file data");
 +      return (ARCHIVE_FATAL);
 +    }
 +    if (firstpart < length) {
 +      memcpy(&rar->unp_buffer[rar->unp_offset],
 +             &rar->lzss.window[windowoffs], firstpart);
 +      memcpy(&rar->unp_buffer[rar->unp_offset + firstpart],
 +             &rar->lzss.window[0], length - firstpart);
 +    } else {
 +      memcpy(&rar->unp_buffer[rar->unp_offset],
 +             &rar->lzss.window[windowoffs], length);
 +    }
 +  } else {
 +      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +                        "Bad RAR file data");
 +      return (ARCHIVE_FATAL);
 +  }
 +  rar->unp_offset += length;
 +  if (rar->unp_offset >= rar->unp_buffer_size)
 +    *buffer = rar->unp_buffer;
 +  else
 +    *buffer = NULL;
 +  return (ARCHIVE_OK);
 +}
 +
 +static const void *
 +rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
 +{
 +  struct rar *rar = (struct rar *)(a->format->data);
 +  const void *h = __archive_read_ahead(a, min, avail);
 +  int ret;
 +  if (avail)
 +  {
 +    if (a->archive.read_data_is_posix_read && *avail > (ssize_t)a->archive.read_data_requested)
 +      *avail = a->archive.read_data_requested;
 +    if (*avail > rar->bytes_remaining)
 +      *avail = (ssize_t)rar->bytes_remaining;
 +    if (*avail < 0)
 +      return NULL;
 +    else if (*avail == 0 && rar->main_flags & MHD_VOLUME &&
 +      rar->file_flags & FHD_SPLIT_AFTER)
 +    {
 +      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))
 +        return NULL;
 +      return rar_read_ahead(a, min, avail);
 +    }
 +  }
 +  return h;
 +}
diff --cc Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
index e56bd63,0000000..ddd4458
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
@@@ -1,3112 -1,0 +1,3113 @@@
 +/*-
 + * Copyright (c) 2004-2013 Tim Kientzle
 + * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA
 + * Copyright (c) 2013 Konrad Kleine
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + */
 +
 +#include "archive_platform.h"
 +__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 2009-12-28 03:11:36Z kientzle $");
 +
 +/*
 + * The definitive documentation of the Zip file format is:
 + *   http://www.pkware.com/documents/casestudies/APPNOTE.TXT
 + *
 + * The Info-Zip project has pioneered various extensions to better
 + * support Zip on Unix, including the 0x5455 "UT", 0x5855 "UX", 0x7855
 + * "Ux", and 0x7875 "ux" extensions for time and ownership
 + * information.
 + *
 + * History of this code: The streaming Zip reader was first added to
 + * libarchive in January 2005.  Support for seekable input sources was
 + * added in Nov 2011.  Zip64 support (including a significant code
 + * refactoring) was added in 2014.
 + */
 +
 +#ifdef HAVE_ERRNO_H
 +#include <errno.h>
 +#endif
 +#ifdef HAVE_STDLIB_H
 +#include <stdlib.h>
 +#endif
 +#ifdef HAVE_ZLIB_H
 +#include <cm_zlib.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"
 +
 +#ifndef HAVE_ZLIB_H
 +#include "archive_crc32.h"
 +#endif
 +
 +struct zip_entry {
 +	struct archive_rb_node	node;
 +	struct zip_entry	*next;
 +	int64_t			local_header_offset;
 +	int64_t			compressed_size;
 +	int64_t			uncompressed_size;
 +	int64_t			gid;
 +	int64_t			uid;
 +	struct archive_string	rsrcname;
 +	time_t			mtime;
 +	time_t			atime;
 +	time_t			ctime;
 +	uint32_t		crc32;
 +	uint16_t		mode;
 +	uint16_t		zip_flags; /* From GP Flags Field */
 +	unsigned char		compression;
 +	unsigned char		system; /* From "version written by" */
 +	unsigned char		flags; /* Our extra markers. */
 +	unsigned char		decdat;/* Used for Decryption check */
 +
 +	/* WinZip AES encryption extra field should be available
 +	 * when compression is 99. */
 +	struct {
 +		/* Vendor version: AE-1 - 0x0001, AE-2 - 0x0002 */
 +		unsigned	vendor;
 +#define AES_VENDOR_AE_1	0x0001
 +#define AES_VENDOR_AE_2	0x0002
 +		/* AES encryption strength:
 +		 * 1 - 128 bits, 2 - 192 bits, 2 - 256 bits. */
 +		unsigned	strength;
 +		/* Actual compression method. */
 +		unsigned char	compression;
 +	}			aes_extra;
 +};
 +
 +struct trad_enc_ctx {
 +	uint32_t	keys[3];
 +};
 +
 +/* Bits used in zip_flags. */
 +#define ZIP_ENCRYPTED	(1 << 0)
 +#define ZIP_LENGTH_AT_END	(1 << 3)
 +#define ZIP_STRONG_ENCRYPTED	(1 << 6)
 +#define ZIP_UTF8_NAME	(1 << 11)
 +/* See "7.2 Single Password Symmetric Encryption Method"
 +   in http://www.pkware.com/documents/casestudies/APPNOTE.TXT */
 +#define ZIP_CENTRAL_DIRECTORY_ENCRYPTED	(1 << 13)
 +
 +/* Bits used in flags. */
 +#define LA_USED_ZIP64	(1 << 0)
 +#define LA_FROM_CENTRAL_DIRECTORY (1 << 1)
 +
 +/*
 + * See "WinZip - AES Encryption Information"
 + *     http://www.winzip.com/aes_info.htm
 + */
 +/* Value used in compression method. */
 +#define WINZIP_AES_ENCRYPTION	99
 +/* Authentication code size. */
 +#define AUTH_CODE_SIZE	10
 +/**/
 +#define MAX_DERIVED_KEY_BUF_SIZE	(AES_MAX_KEY_SIZE * 2 + 2)
 +
 +struct zip {
 +	/* Structural information about the archive. */
 +	struct archive_string	format_name;
 +	int64_t			central_directory_offset;
 +	size_t			central_directory_entries_total;
 +	size_t			central_directory_entries_on_this_disk;
 +	int			has_encrypted_entries;
 +
 +	/* List of entries (seekable Zip only) */
 +	struct zip_entry	*zip_entries;
 +	struct archive_rb_tree	tree;
 +	struct archive_rb_tree	tree_rsrc;
 +
 +	/* Bytes read but not yet consumed via __archive_read_consume() */
 +	size_t			unconsumed;
 +
 +	/* Information about entry we're currently reading. */
 +	struct zip_entry	*entry;
 +	int64_t			entry_bytes_remaining;
 +
 +	/* These count the number of bytes actually read for the entry. */
 +	int64_t			entry_compressed_bytes_read;
 +	int64_t			entry_uncompressed_bytes_read;
 +
 +	/* Running CRC32 of the decompressed data */
 +	unsigned long		entry_crc32;
 +	unsigned long		(*crc32func)(unsigned long, const void *,
 +				    size_t);
 +	char			ignore_crc32;
 +
 +	/* Flags to mark progress of decompression. */
 +	char			decompress_init;
 +	char			end_of_entry;
 +
 +#ifdef HAVE_ZLIB_H
 +	unsigned char 		*uncompressed_buffer;
 +	size_t 			uncompressed_buffer_size;
 +	z_stream		stream;
 +	char			stream_valid;
 +#endif
 +
 +	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))
 +
 +/* ------------------------------------------------------------------------ */
 +
 +/*
 +  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 struct {
++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) */
 +	{97, "wav-pack"}, /* WavPack compressed data */
 +	{98, "ppmd-1"},   /* PPMd version I, Rev 1 */
 +	{99, "aes"}       /* WinZip AES encryption  */
 +};
 +
 +static const char *
 +compression_name(const int compression)
 +{
 +	static const int num_compression_methods =
 +		sizeof(compression_methods)/sizeof(compression_methods[0]);
 +	int i=0;
 +
 +	while(compression >= 0 && i < num_compression_methods) {
 +		if (compression_methods[i].id == compression)
 +			return compression_methods[i].name;
 +		i++;
 +	}
 +	return "??";
 +}
 +
 +/* Convert an MSDOS-style date/time into Unix-style time. */
 +static time_t
 +zip_time(const char *p)
 +{
 +	int msTime, msDate;
 +	struct tm ts;
 +
 +	msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]);
 +	msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]);
 +
 +	memset(&ts, 0, sizeof(ts));
 +	ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
 +	ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
 +	ts.tm_mday = msDate & 0x1f; /* Day of month. */
 +	ts.tm_hour = (msTime >> 11) & 0x1f;
 +	ts.tm_min = (msTime >> 5) & 0x3f;
 +	ts.tm_sec = (msTime << 1) & 0x3e;
 +	ts.tm_isdst = -1;
 +	return mktime(&ts);
 +}
 +
 +/*
 + * The extra data is stored as a list of
 + *	id1+size1+data1 + id2+size2+data2 ...
 + *  triplets.  id and size are 2 bytes each.
 + */
 +static int
 +process_extra(struct archive_read *a, const char *p, size_t extra_length, struct zip_entry* zip_entry)
 +{
 +	unsigned offset = 0;
 +
 +	if (extra_length == 0) {
 +		return ARCHIVE_OK;
 +	}
 +
 +	if (extra_length < 4) {
 +		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;
 +	}
 +	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 = 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 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 (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;
 +	}
 +	if (offset != extra_length) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Malformed extra data: Consumed %d bytes of %d bytes",
 +		    (int)offset, (int)extra_length);
 +		return ARCHIVE_FAILED;
 +	}
 +	return ARCHIVE_OK;
 +}
 +
 +/*
 + * Assumes file pointer is at beginning of local file header.
 + */
 +static int
 +zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
 +    struct zip *zip)
 +{
 +	const char *p;
 +	const void *h;
 +	const wchar_t *wp;
 +	const char *cp;
 +	size_t len, filename_length, extra_length;
 +	struct archive_string_conv *sconv;
 +	struct zip_entry *zip_entry = zip->entry;
 +	struct zip_entry zip_entry_central_dir;
 +	int ret = ARCHIVE_OK;
 +	char version;
 +
 +	/* Save a copy of the original for consistency checks. */
 +	zip_entry_central_dir = *zip_entry;
 +
 +	zip->decompress_init = 0;
 +	zip->end_of_entry = 0;
 +	zip->entry_uncompressed_bytes_read = 0;
 +	zip->entry_compressed_bytes_read = 0;
 +	zip->entry_crc32 = zip->crc32func(0, NULL, 0);
 +
 +	/* Setup default conversion. */
 +	if (zip->sconv == NULL && !zip->init_default_conversion) {
 +		zip->sconv_default =
 +		    archive_string_default_conversion_for_read(&(a->archive));
 +		zip->init_default_conversion = 1;
 +	}
 +
 +	if ((p = __archive_read_ahead(a, 30, NULL)) == NULL) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated ZIP file header");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	if (memcmp(p, "PK\003\004", 4) != 0) {
 +		archive_set_error(&a->archive, -1, "Damaged Zip archive");
 +		return ARCHIVE_FATAL;
 +	}
 +	version = p[4];
 +	zip_entry->system = p[5];
 +	zip_entry->zip_flags = archive_le16dec(p + 6);
 +	if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) {
 +		zip->has_encrypted_entries = 1;
 +		archive_entry_set_is_data_encrypted(entry, 1);
 +		if (zip_entry->zip_flags & ZIP_CENTRAL_DIRECTORY_ENCRYPTED &&
 +			zip_entry->zip_flags & ZIP_ENCRYPTED &&
 +			zip_entry->zip_flags & ZIP_STRONG_ENCRYPTED) {
 +			archive_entry_set_is_metadata_encrypted(entry, 1);
 +			return ARCHIVE_FATAL;
 +		}
 +	}
 +	zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED);
 +	zip_entry->compression = (char)archive_le16dec(p + 8);
 +	zip_entry->mtime = zip_time(p + 10);
 +	zip_entry->crc32 = archive_le32dec(p + 14);
 +	if (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
 +		zip_entry->decdat = p[11];
 +	else
 +		zip_entry->decdat = p[17];
 +	zip_entry->compressed_size = archive_le32dec(p + 18);
 +	zip_entry->uncompressed_size = archive_le32dec(p + 22);
 +	filename_length = archive_le16dec(p + 26);
 +	extra_length = archive_le16dec(p + 28);
 +
 +	__archive_read_consume(a, 30);
 +
 +	/* Read the filename. */
 +	if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated ZIP file header");
 +		return (ARCHIVE_FATAL);
 +	}
 +	if (zip_entry->zip_flags & ZIP_UTF8_NAME) {
 +		/* The filename is stored to be UTF-8. */
 +		if (zip->sconv_utf8 == NULL) {
 +			zip->sconv_utf8 =
 +			    archive_string_conversion_from_charset(
 +				&a->archive, "UTF-8", 1);
 +			if (zip->sconv_utf8 == NULL)
 +				return (ARCHIVE_FATAL);
 +		}
 +		sconv = zip->sconv_utf8;
 +	} else if (zip->sconv != NULL)
 +		sconv = zip->sconv;
 +	else
 +		sconv = zip->sconv_default;
 +
 +	if (archive_entry_copy_pathname_l(entry,
 +	    h, filename_length, sconv) != 0) {
 +		if (errno == ENOMEM) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory for Pathname");
 +			return (ARCHIVE_FATAL);
 +		}
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Pathname cannot be converted "
 +		    "from %s to current locale.",
 +		    archive_string_conversion_charset_name(sconv));
 +		ret = ARCHIVE_WARN;
 +	}
 +	__archive_read_consume(a, filename_length);
 +
 +	/* Read the extra data. */
 +	if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated ZIP file header");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	if (ARCHIVE_OK != process_extra(a, 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;
 +	}
 +
 +	/* 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 regularfile. */
 +	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, it's size info
 +		 * is definitive, so ignore the length-at-end flag. */
 +		zip_entry->zip_flags &= ~ZIP_LENGTH_AT_END;
 +		/* If local header is missing a value, use the one from
 +		   the central directory.  If both have it, warn about
 +		   mismatches. */
 +		if (zip_entry->crc32 == 0) {
 +			zip_entry->crc32 = zip_entry_central_dir.crc32;
 +		} else if (!zip->ignore_crc32
 +		    && zip_entry->crc32 != zip_entry_central_dir.crc32) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Inconsistent CRC32 values");
 +			ret = ARCHIVE_WARN;
 +		}
 +		if (zip_entry->compressed_size == 0) {
 +			zip_entry->compressed_size
 +			    = zip_entry_central_dir.compressed_size;
 +		} else if (zip_entry->compressed_size
 +		    != zip_entry_central_dir.compressed_size) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Inconsistent compressed size: "
 +			    "%jd in central directory, %jd in local header",
 +			    (intmax_t)zip_entry_central_dir.compressed_size,
 +			    (intmax_t)zip_entry->compressed_size);
 +			ret = ARCHIVE_WARN;
 +		}
 +		if (zip_entry->uncompressed_size == 0) {
 +			zip_entry->uncompressed_size
 +			    = zip_entry_central_dir.uncompressed_size;
 +		} else if (zip_entry->uncompressed_size
 +		    != zip_entry_central_dir.uncompressed_size) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Inconsistent uncompressed size: "
 +			    "%jd in central directory, %jd in local header",
 +			    (intmax_t)zip_entry_central_dir.uncompressed_size,
 +			    (intmax_t)zip_entry->uncompressed_size);
 +			ret = ARCHIVE_WARN;
 +		}
 +	}
 +
 +	/* Populate some additional entry fields: */
 +	archive_entry_set_mode(entry, zip_entry->mode);
 +	archive_entry_set_uid(entry, zip_entry->uid);
 +	archive_entry_set_gid(entry, zip_entry->gid);
 +	archive_entry_set_mtime(entry, zip_entry->mtime, 0);
 +	archive_entry_set_ctime(entry, zip_entry->ctime, 0);
 +	archive_entry_set_atime(entry, zip_entry->atime, 0);
 +
 +	if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) {
 +		size_t linkname_length;
 +
 +		if (zip_entry->compressed_size > 64 * 1024) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Zip file with oversized link entry");
 +			return ARCHIVE_FATAL;
 +		}
 +
 +		linkname_length = (size_t)zip_entry->compressed_size;
 +
 +		archive_entry_set_size(entry, 0);
 +		p = __archive_read_ahead(a, linkname_length, NULL);
 +		if (p == NULL) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Truncated Zip file");
 +			return ARCHIVE_FATAL;
 +		}
 +
 +		sconv = zip->sconv;
 +		if (sconv == NULL && (zip->entry->zip_flags & ZIP_UTF8_NAME))
 +			sconv = zip->sconv_utf8;
 +		if (sconv == NULL)
 +			sconv = zip->sconv_default;
 +		if (archive_entry_copy_symlink_l(entry, p, linkname_length,
 +		    sconv) != 0) {
 +			if (errno != ENOMEM && sconv == zip->sconv_utf8 &&
 +			    (zip->entry->zip_flags & ZIP_UTF8_NAME))
 +			    archive_entry_copy_symlink_l(entry, p,
 +				linkname_length, NULL);
 +			if (errno == ENOMEM) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "Can't allocate memory for Symlink");
 +				return (ARCHIVE_FATAL);
 +			}
 +			/*
 +			 * Since there is no character-set regulation for
 +			 * symlink name, do not report the conversion error
 +			 * in an automatic conversion.
 +			 */
 +			if (sconv != zip->sconv_utf8 ||
 +			    (zip->entry->zip_flags & ZIP_UTF8_NAME) == 0) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_FILE_FORMAT,
 +				    "Symlink cannot be converted "
 +				    "from %s to current locale.",
 +				    archive_string_conversion_charset_name(
 +					sconv));
 +				ret = ARCHIVE_WARN;
 +			}
 +		}
 +		zip_entry->uncompressed_size = zip_entry->compressed_size = 0;
 +
 +		if (__archive_read_consume(a, linkname_length) < 0) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Read error skipping symlink target name");
 +			return ARCHIVE_FATAL;
 +		}
 +	} else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
 +	    || zip_entry->uncompressed_size > 0) {
 +		/* Set the size only if it's meaningful. */
 +		archive_entry_set_size(entry, zip_entry->uncompressed_size);
 +	}
 +	zip->entry_bytes_remaining = zip_entry->compressed_size;
 +
 +	/* If there's no body, force read_data() to return EOF immediately. */
 +	if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
 +	    && zip->entry_bytes_remaining < 1)
 +		zip->end_of_entry = 1;
 +
 +	/* Set up a more descriptive format name. */
 +	archive_string_sprintf(&zip->format_name, "ZIP %d.%d (%s)",
 +	    version / 10, version % 10,
 +	    compression_name(zip->entry->compression));
 +	a->archive.archive_format_name = zip->format_name.s;
 +
 +	return (ret);
 +}
 +
 +static int
 +check_authentication_code(struct archive_read *a, const void *_p)
 +{
 +	struct zip *zip = (struct zip *)(a->format->data);
 +
 +	/* Check authentication code. */
 +	if (zip->hctx_valid) {
 +		const void *p;
 +		uint8_t hmac[20];
 +		size_t hmac_len = 20;
 +		int cmp;
 +
 +		archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len);
 +		if (_p == NULL) {
 +			/* Read authentication code. */
 +			p = __archive_read_ahead(a, AUTH_CODE_SIZE, NULL);
 +			if (p == NULL) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_FILE_FORMAT,
 +				    "Truncated ZIP file data");
 +				return (ARCHIVE_FATAL);
 +			}
 +		} else {
 +			p = _p;
 +		}
 +		cmp = memcmp(hmac, p, AUTH_CODE_SIZE);
 +		__archive_read_consume(a, AUTH_CODE_SIZE);
 +		if (cmp != 0) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_MISC,
 +			    "ZIP bad Authentication code");
 +			return (ARCHIVE_WARN);
 +		}
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Read "uncompressed" data.  There are three cases:
 + *  1) We know the size of the data.  This is always true for the
 + * seeking reader (we've examined the Central Directory already).
 + *  2) ZIP_LENGTH_AT_END was set, but only the CRC was deferred.
 + * Info-ZIP seems to do this; we know the size but have to grab
 + * the CRC from the data descriptor afterwards.
 + *  3) We're streaming and ZIP_LENGTH_AT_END was specified and
 + * we have no size information.  In this case, we can do pretty
 + * well by watching for the data descriptor record.  The data
 + * descriptor is 16 bytes and includes a computed CRC that should
 + * provide a strong check.
 + *
 + * TODO: Technically, the PK\007\010 signature is optional.
 + * In the original spec, the data descriptor contained CRC
 + * and size fields but had no leading signature.  In practice,
 + * newer writers seem to provide the signature pretty consistently.
 + *
 + * For uncompressed data, the PK\007\010 marker seems essential
 + * to be sure we've actually seen the end of the entry.
 + *
 + * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
 + * zip->end_of_entry if it consumes all of the data.
 + */
 +static int
 +zip_read_data_none(struct archive_read *a, const void **_buff,
 +    size_t *size, int64_t *offset)
 +{
 +	struct zip *zip;
 +	const char *buff;
 +	ssize_t bytes_avail;
 +	int r;
 +
 +	(void)offset; /* UNUSED */
 +
 +	zip = (struct zip *)(a->format->data);
 +
 +	if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) {
 +		const char *p;
 +		ssize_t grabbing_bytes = 24;
 +
 +		if (zip->hctx_valid)
 +			grabbing_bytes += AUTH_CODE_SIZE;
 +		/* Grab at least 24 bytes. */
 +		buff = __archive_read_ahead(a, grabbing_bytes, &bytes_avail);
 +		if (bytes_avail < grabbing_bytes) {
 +			/* Zip archives have end-of-archive markers
 +			   that are longer than this, so a failure to get at
 +			   least 24 bytes really does indicate a truncated
 +			   file. */
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Truncated ZIP file data");
 +			return (ARCHIVE_FATAL);
 +		}
 +		/* Check for a complete PK\007\010 signature, followed
 +		 * by the correct 4-byte CRC. */
 +		p = buff;
 +		if (zip->hctx_valid)
 +			p += AUTH_CODE_SIZE;
 +		if (p[0] == 'P' && p[1] == 'K'
 +		    && p[2] == '\007' && p[3] == '\010'
 +		    && (archive_le32dec(p + 4) == zip->entry_crc32
 +			|| zip->ignore_crc32
 +			|| (zip->hctx_valid
 +			 && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) {
 +			if (zip->entry->flags & LA_USED_ZIP64) {
 +				uint64_t compressed, uncompressed;
 +				zip->entry->crc32 = archive_le32dec(p + 4);
 +				compressed = archive_le64dec(p + 8);
 +				uncompressed = archive_le64dec(p + 16);
 +				if (compressed > INT64_MAX || uncompressed > INT64_MAX) {
 +					archive_set_error(&a->archive,
 +					    ARCHIVE_ERRNO_FILE_FORMAT,
 +					    "Overflow of 64-bit file sizes");
 +					return ARCHIVE_FAILED;
 +				}
 +				zip->entry->compressed_size = compressed;
 +				zip->entry->uncompressed_size = uncompressed;
 +				zip->unconsumed = 24;
 +			} else {
 +				zip->entry->crc32 = archive_le32dec(p + 4);
 +				zip->entry->compressed_size =
 +					archive_le32dec(p + 8);
 +				zip->entry->uncompressed_size =
 +					archive_le32dec(p + 12);
 +				zip->unconsumed = 16;
 +			}
 +			if (zip->hctx_valid) {
 +				r = check_authentication_code(a, buff);
 +				if (r != ARCHIVE_OK)
 +					return (r);
 +			}
 +			zip->end_of_entry = 1;
 +			return (ARCHIVE_OK);
 +		}
 +		/* If not at EOF, ensure we consume at least one byte. */
 +		++p;
 +
 +		/* Scan forward until we see where a PK\007\010 signature
 +		 * might be. */
 +		/* Return bytes up until that point.  On the next call,
 +		 * the code above will verify the data descriptor. */
 +		while (p < buff + bytes_avail - 4) {
 +			if (p[3] == 'P') { p += 3; }
 +			else if (p[3] == 'K') { p += 2; }
 +			else if (p[3] == '\007') { p += 1; }
 +			else if (p[3] == '\010' && p[2] == '\007'
 +			    && p[1] == 'K' && p[0] == 'P') {
 +				if (zip->hctx_valid)
 +					p -= AUTH_CODE_SIZE;
 +				break;
 +			} else { p += 4; }
 +		}
 +		bytes_avail = p - buff;
 +	} else {
 +		if (zip->entry_bytes_remaining == 0) {
 +			zip->end_of_entry = 1;
 +			if (zip->hctx_valid) {
 +				r = check_authentication_code(a, NULL);
 +				if (r != ARCHIVE_OK)
 +					return (r);
 +			}
 +			return (ARCHIVE_OK);
 +		}
 +		/* Grab a bunch of bytes. */
 +		buff = __archive_read_ahead(a, 1, &bytes_avail);
 +		if (bytes_avail <= 0) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Truncated ZIP file data");
 +			return (ARCHIVE_FATAL);
 +		}
 +		if (bytes_avail > zip->entry_bytes_remaining)
 +			bytes_avail = (ssize_t)zip->entry_bytes_remaining;
 +	}
 +	if (zip->tctx_valid || zip->cctx_valid) {
 +		size_t dec_size = bytes_avail;
 +
 +		if (dec_size > zip->decrypted_buffer_size)
 +			dec_size = zip->decrypted_buffer_size;
 +		if (zip->tctx_valid) {
 +			trad_enc_decrypt_update(&zip->tctx,
 +			    (const uint8_t *)buff, dec_size,
 +			    zip->decrypted_buffer, dec_size);
 +		} else {
 +			size_t dsize = dec_size;
 +			archive_hmac_sha1_update(&zip->hctx,
 +			    (const uint8_t *)buff, dec_size);
 +			archive_decrypto_aes_ctr_update(&zip->cctx,
 +			    (const uint8_t *)buff, dec_size,
 +			    zip->decrypted_buffer, &dsize);
 +		}
 +		bytes_avail = dec_size;
 +		buff = (const char *)zip->decrypted_buffer;
 +	}
 +	*size = bytes_avail;
 +	zip->entry_bytes_remaining -= bytes_avail;
 +	zip->entry_uncompressed_bytes_read += bytes_avail;
 +	zip->entry_compressed_bytes_read += bytes_avail;
 +	zip->unconsumed += bytes_avail;
 +	*_buff = buff;
 +	return (ARCHIVE_OK);
 +}
 +
 +#ifdef HAVE_ZLIB_H
 +static int
 +zip_deflate_init(struct archive_read *a, struct zip *zip)
 +{
 +	int r;
 +
 +	/* If we haven't yet read any data, initialize the decompressor. */
 +	if (!zip->decompress_init) {
 +		if (zip->stream_valid)
 +			r = inflateReset(&zip->stream);
 +		else
 +			r = inflateInit2(&zip->stream,
 +			    -15 /* Don't check for zlib header */);
 +		if (r != Z_OK) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Can't initialize ZIP decompression.");
 +			return (ARCHIVE_FATAL);
 +		}
 +		/* Stream structure has been set up. */
 +		zip->stream_valid = 1;
 +		/* We've initialized decompression for this stream. */
 +		zip->decompress_init = 1;
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +zip_read_data_deflate(struct archive_read *a, const void **buff,
 +    size_t *size, int64_t *offset)
 +{
 +	struct zip *zip;
 +	ssize_t bytes_avail;
 +	const void *compressed_buff, *sp;
 +	int r;
 +
 +	(void)offset; /* UNUSED */
 +
 +	zip = (struct zip *)(a->format->data);
 +
 +	/* If the buffer hasn't been allocated, allocate it now. */
 +	if (zip->uncompressed_buffer == NULL) {
 +		zip->uncompressed_buffer_size = 256 * 1024;
 +		zip->uncompressed_buffer
 +		    = (unsigned char *)malloc(zip->uncompressed_buffer_size);
 +		if (zip->uncompressed_buffer == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "No memory for ZIP decompression");
 +			return (ARCHIVE_FATAL);
 +		}
 +	}
 +
 +	r = zip_deflate_init(a, zip);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +
 +	/*
 +	 * Note: '1' here is a performance optimization.
 +	 * Recall that the decompression layer returns a count of
 +	 * available bytes; asking for more than that forces the
 +	 * decompressor to combine reads by copying data.
 +	 */
 +	compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail);
 +	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
 +	    && bytes_avail > zip->entry_bytes_remaining) {
 +		bytes_avail = (ssize_t)zip->entry_bytes_remaining;
 +	}
 +	if (bytes_avail < 0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated ZIP file body");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	if (zip->tctx_valid || zip->cctx_valid) {
 +		if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) {
 +			size_t buff_remaining =
 +			    (zip->decrypted_buffer + zip->decrypted_buffer_size)
 +			    - (zip->decrypted_ptr + zip->decrypted_bytes_remaining);
 +
 +			if (buff_remaining > (size_t)bytes_avail)
 +				buff_remaining = (size_t)bytes_avail;
 +
 +			if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) &&
 +			      zip->entry_bytes_remaining > 0) {
 +				if ((int64_t)(zip->decrypted_bytes_remaining
 +				    + buff_remaining)
 +				      > zip->entry_bytes_remaining) {
 +					if (zip->entry_bytes_remaining <
 +					      (int64_t)zip->decrypted_bytes_remaining)
 +						buff_remaining = 0;
 +					else
 +						buff_remaining =
 +						    (size_t)zip->entry_bytes_remaining
 +						      - zip->decrypted_bytes_remaining;
 +				}
 +			}
 +			if (buff_remaining > 0) {
 +				if (zip->tctx_valid) {
 +					trad_enc_decrypt_update(&zip->tctx,
 +					    compressed_buff, buff_remaining,
 +					    zip->decrypted_ptr
 +					      + zip->decrypted_bytes_remaining,
 +					    buff_remaining);
 +				} else {
 +					size_t dsize = buff_remaining;
 +					archive_decrypto_aes_ctr_update(
 +					    &zip->cctx,
 +					    compressed_buff, buff_remaining,
 +					    zip->decrypted_ptr
 +					      + zip->decrypted_bytes_remaining,
 +					    &dsize);
 +				}
 +				zip->decrypted_bytes_remaining += buff_remaining;
 +			}
 +		}
 +		bytes_avail = zip->decrypted_bytes_remaining;
 +		compressed_buff = (const char *)zip->decrypted_ptr;
 +	}
 +
 +	/*
 +	 * A bug in zlib.h: stream.next_in should be marked 'const'
 +	 * but isn't (the library never alters data through the
 +	 * next_in pointer, only reads it).  The result: this ugly
 +	 * cast to remove 'const'.
 +	 */
 +	zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)compressed_buff;
 +	zip->stream.avail_in = (uInt)bytes_avail;
 +	zip->stream.total_in = 0;
 +	zip->stream.next_out = zip->uncompressed_buffer;
 +	zip->stream.avail_out = (uInt)zip->uncompressed_buffer_size;
 +	zip->stream.total_out = 0;
 +
 +	r = inflate(&zip->stream, 0);
 +	switch (r) {
 +	case Z_OK:
 +		break;
 +	case Z_STREAM_END:
 +		zip->end_of_entry = 1;
 +		break;
 +	case Z_MEM_ERROR:
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Out of memory for ZIP decompression");
 +		return (ARCHIVE_FATAL);
 +	default:
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "ZIP decompression failed (%d)", r);
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	/* Consume as much as the compressor actually used. */
 +	bytes_avail = zip->stream.total_in;
 +	if (zip->tctx_valid || zip->cctx_valid) {
 +		zip->decrypted_bytes_remaining -= bytes_avail;
 +		if (zip->decrypted_bytes_remaining == 0)
 +			zip->decrypted_ptr = zip->decrypted_buffer;
 +		else
 +			zip->decrypted_ptr += bytes_avail;
 +	}
 +	/* Calculate compressed data as much as we used.*/
 +	if (zip->hctx_valid)
 +		archive_hmac_sha1_update(&zip->hctx, sp, bytes_avail);
 +	__archive_read_consume(a, bytes_avail);
 +	zip->entry_bytes_remaining -= bytes_avail;
 +	zip->entry_compressed_bytes_read += bytes_avail;
 +
 +	*size = zip->stream.total_out;
 +	zip->entry_uncompressed_bytes_read += zip->stream.total_out;
 +	*buff = zip->uncompressed_buffer;
 +
 +	if (zip->end_of_entry && zip->hctx_valid) {
 +		r = check_authentication_code(a, NULL);
 +		if (r != ARCHIVE_OK)
 +			return (r);
 +	}
 +
 +	if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
 +		const char *p;
 +
 +		if (NULL == (p = __archive_read_ahead(a, 24, NULL))) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Truncated ZIP end-of-file record");
 +			return (ARCHIVE_FATAL);
 +		}
 +		/* Consume the optional PK\007\010 marker. */
 +		if (p[0] == 'P' && p[1] == 'K' &&
 +		    p[2] == '\007' && p[3] == '\010') {
 +			p += 4;
 +			zip->unconsumed = 4;
 +		}
 +		if (zip->entry->flags & LA_USED_ZIP64) {
 +			uint64_t compressed, uncompressed;
 +			zip->entry->crc32 = archive_le32dec(p);
 +			compressed = archive_le64dec(p + 4);
 +			uncompressed = archive_le64dec(p + 12);
 +			if (compressed > INT64_MAX || uncompressed > INT64_MAX) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_FILE_FORMAT,
 +				    "Overflow of 64-bit file sizes");
 +				return ARCHIVE_FAILED;
 +			}
 +			zip->entry->compressed_size = compressed;
 +			zip->entry->uncompressed_size = uncompressed;
 +			zip->unconsumed += 20;
 +		} else {
 +			zip->entry->crc32 = archive_le32dec(p);
 +			zip->entry->compressed_size = archive_le32dec(p + 4);
 +			zip->entry->uncompressed_size = archive_le32dec(p + 8);
 +			zip->unconsumed += 12;
 +		}
 +	}
 +
 +	return (ARCHIVE_OK);
 +}
 +#endif
 +
 +static int
 +read_decryption_header(struct archive_read *a)
 +{
 +	struct zip *zip = (struct zip *)(a->format->data);
 +	const char *p;
 +	unsigned int remaining_size;
 +	unsigned int ts;
 +
 +	/*
 +	 * Read an initialization vector data field.
 +	 */
 +	p = __archive_read_ahead(a, 2, NULL);
 +	if (p == NULL)
 +		goto truncated;
 +	ts = zip->iv_size;
 +	zip->iv_size = archive_le16dec(p);
 +	__archive_read_consume(a, 2);
 +	if (ts < zip->iv_size) {
 +		free(zip->iv);
 +		zip->iv = NULL;
 +	}
 +	p = __archive_read_ahead(a, zip->iv_size, NULL);
 +	if (p == NULL)
 +		goto truncated;
 +	if (zip->iv == NULL) {
 +		zip->iv = malloc(zip->iv_size);
 +		if (zip->iv == NULL)
 +			goto nomem;
 +	}
 +	memcpy(zip->iv, p, zip->iv_size);
 +	__archive_read_consume(a, zip->iv_size);
 +
 +	/*
 +	 * Read a size of remaining decryption header field.
 +	 */
 +	p = __archive_read_ahead(a, 14, NULL);
 +	if (p == NULL)
 +		goto truncated;
 +	remaining_size = archive_le32dec(p);
 +	if (remaining_size < 16 || remaining_size > (1 << 18))
 +		goto corrupted;
 +
 +	/* Check if format version is supported. */
 +	if (archive_le16dec(p+4) != 3) {
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Unsupported encryption format version: %u",
 +		    archive_le16dec(p+4));
 +		return (ARCHIVE_FAILED);
 +	}
 +
 +	/*
 +	 * Read an encryption algorithm field.
 +	 */
 +	zip->alg_id = archive_le16dec(p+6);
 +	switch (zip->alg_id) {
 +	case 0x6601:/* DES */
 +	case 0x6602:/* RC2 */
 +	case 0x6603:/* 3DES 168 */
 +	case 0x6609:/* 3DES 112 */
 +	case 0x660E:/* AES 128 */
 +	case 0x660F:/* AES 192 */
 +	case 0x6610:/* AES 256 */
 +	case 0x6702:/* RC2 (version >= 5.2) */
 +	case 0x6720:/* Blowfish */
 +	case 0x6721:/* Twofish */
 +	case 0x6801:/* RC4 */
 +		/* Supported encryption algorithm. */
 +		break;
 +	default:
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Unknown encryption algorithm: %u", zip->alg_id);
 +		return (ARCHIVE_FAILED);
 +	}
 +
 +	/*
 +	 * Read a bit length field.
 +	 */
 +	zip->bit_len = archive_le16dec(p+8);
 +
 +	/*
 +	 * Read a flags field.
 +	 */
 +	zip->flags = archive_le16dec(p+10);
 +	switch (zip->flags & 0xf000) {
 +	case 0x0001: /* Password is required to decrypt. */
 +	case 0x0002: /* Certificates only. */
 +	case 0x0003: /* Password or certificate required to decrypt. */
 +		break;
 +	default:
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Unknown encryption flag: %u", zip->flags);
 +		return (ARCHIVE_FAILED);
 +	}
 +	if ((zip->flags & 0xf000) == 0 ||
 +	    (zip->flags & 0xf000) == 0x4000) {
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Unknown encryption flag: %u", zip->flags);
 +		return (ARCHIVE_FAILED);
 +	}
 +
 +	/*
 +	 * Read an encrypted random data field.
 +	 */
 +	ts = zip->erd_size;
 +	zip->erd_size = archive_le16dec(p+12);
 +	__archive_read_consume(a, 14);
 +	if ((zip->erd_size & 0xf) != 0 ||
 +	    (zip->erd_size + 16) > remaining_size ||
 +	    (zip->erd_size + 16) < zip->erd_size)
 +		goto corrupted;
 +
 +	if (ts < zip->erd_size) {
 +		free(zip->erd);
 +		zip->erd = NULL;
 +	}
 +	p = __archive_read_ahead(a, zip->erd_size, NULL);
 +	if (p == NULL)
 +		goto truncated;
 +	if (zip->erd == NULL) {
 +		zip->erd = malloc(zip->erd_size);
 +		if (zip->erd == NULL)
 +			goto nomem;
 +	}
 +	memcpy(zip->erd, p, zip->erd_size);
 +	__archive_read_consume(a, zip->erd_size);
 +
 +	/*
 +	 * Read a reserved data field.
 +	 */
 +	p = __archive_read_ahead(a, 4, NULL);
 +	if (p == NULL)
 +		goto truncated;
 +	/* Reserved data size should be zero. */
 +	if (archive_le32dec(p) != 0)
 +		goto corrupted;
 +	__archive_read_consume(a, 4);
 +
 +	/*
 +	 * Read a password validation data field.
 +	 */
 +	p = __archive_read_ahead(a, 2, NULL);
 +	if (p == NULL)
 +		goto truncated;
 +	ts = zip->v_size;
 +	zip->v_size = archive_le16dec(p);
 +	__archive_read_consume(a, 2);
 +	if ((zip->v_size & 0x0f) != 0 ||
 +	    (zip->erd_size + zip->v_size + 16) > remaining_size ||
 +	    (zip->erd_size + zip->v_size + 16) < (zip->erd_size + zip->v_size))
 +		goto corrupted;
 +	if (ts < zip->v_size) {
 +		free(zip->v_data);
 +		zip->v_data = NULL;
 +	}
 +	p = __archive_read_ahead(a, zip->v_size, NULL);
 +	if (p == NULL)
 +		goto truncated;
 +	if (zip->v_data == NULL) {
 +		zip->v_data = malloc(zip->v_size);
 +		if (zip->v_data == NULL)
 +			goto nomem;
 +	}
 +	memcpy(zip->v_data, p, zip->v_size);
 +	__archive_read_consume(a, zip->v_size);
 +
 +	p = __archive_read_ahead(a, 4, NULL);
 +	if (p == NULL)
 +		goto truncated;
 +	zip->v_crc32 = archive_le32dec(p);
 +	__archive_read_consume(a, 4);
 +
 +	/*return (ARCHIVE_OK);
 +	 * This is not fully implemented yet.*/
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Encrypted file is unsupported");
 +	return (ARCHIVE_FAILED);
 +truncated:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Truncated ZIP file data");
 +	return (ARCHIVE_FATAL);
 +corrupted:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Corrupted ZIP file data");
 +	return (ARCHIVE_FATAL);
 +nomem:
 +	archive_set_error(&a->archive, ENOMEM,
 +	    "No memory for ZIP decryption");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +static int
 +zip_alloc_decryption_buffer(struct archive_read *a)
 +{
 +	struct zip *zip = (struct zip *)(a->format->data);
 +	size_t bs = 256 * 1024;
 +
 +	if (zip->decrypted_buffer == NULL) {
 +		zip->decrypted_buffer_size = bs;
 +		zip->decrypted_buffer = malloc(bs);
 +		if (zip->decrypted_buffer == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "No memory for ZIP decryption");
 +			return (ARCHIVE_FATAL);
 +		}
 +	}
 +	zip->decrypted_ptr = zip->decrypted_buffer;
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +init_traditional_PKWARE_decryption(struct archive_read *a)
 +{
 +	struct zip *zip = (struct zip *)(a->format->data);
 +	const void *p;
 +	int retry;
 +	int r;
 +
 +	if (zip->tctx_valid)
 +		return (ARCHIVE_OK);
 +
 +	/*
 +	   Read the 12 bytes encryption header stored at
 +	   the start of the data area.
 +	 */
 +#define ENC_HEADER_SIZE	12
 +	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
 +	    && zip->entry_bytes_remaining < ENC_HEADER_SIZE) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated Zip encrypted body: only %jd bytes available",
 +		    (intmax_t)zip->entry_bytes_remaining);
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	p = __archive_read_ahead(a, ENC_HEADER_SIZE, NULL);
 +	if (p == NULL) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated ZIP file data");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	for (retry = 0;; retry++) {
 +		const char *passphrase;
 +		uint8_t crcchk;
 +
 +		passphrase = __archive_read_next_passphrase(a);
 +		if (passphrase == NULL) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    (retry > 0)?
 +				"Incorrect passphrase":
 +				"Passphrase required for this entry");
 +			return (ARCHIVE_FAILED);
 +		}
 +
 +		/*
 +		 * Initialize ctx for Traditional PKWARE Decryption.
 +		 */
 +		r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase),
 +			p, ENC_HEADER_SIZE, &crcchk);
 +		if (r == 0 && crcchk == zip->entry->decdat)
 +			break;/* The passphrase is OK. */
 +		if (retry > 10000) {
 +			/* Avoid infinity loop. */
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Too many incorrect passphrases");
 +			return (ARCHIVE_FAILED);
 +		}
 +	}
 +
 +	__archive_read_consume(a, ENC_HEADER_SIZE);
 +	zip->tctx_valid = 1;
 +	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
 +	    zip->entry_bytes_remaining -= ENC_HEADER_SIZE;
 +	}
 +	/*zip->entry_uncompressed_bytes_read += ENC_HEADER_SIZE;*/
 +	zip->entry_compressed_bytes_read += ENC_HEADER_SIZE;
 +	zip->decrypted_bytes_remaining = 0;
 +
 +	return (zip_alloc_decryption_buffer(a));
 +#undef ENC_HEADER_SIZE
 +}
 +
 +static int
 +init_WinZip_AES_decryption(struct archive_read *a)
 +{
 +	struct zip *zip = (struct zip *)(a->format->data);
 +	const void *p;
 +	const uint8_t *pv;
 +	size_t key_len, salt_len;
 +	uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];
 +	int retry;
 +	int r;
 +
 +	if (zip->cctx_valid || zip->hctx_valid)
 +		return (ARCHIVE_OK);
 +
 +	switch (zip->entry->aes_extra.strength) {
 +	case 1: salt_len = 8;  key_len = 16; break;
 +	case 2: salt_len = 12; key_len = 24; break;
 +	case 3: salt_len = 16; key_len = 32; break;
 +	default: goto corrupted;
 +	}
 +	p = __archive_read_ahead(a, salt_len + 2, NULL);
 +	if (p == NULL)
 +		goto truncated;
 +
 +	for (retry = 0;; retry++) {
 +		const char *passphrase;
 +
 +		passphrase = __archive_read_next_passphrase(a);
 +		if (passphrase == NULL) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    (retry > 0)?
 +				"Incorrect passphrase":
 +				"Passphrase required for this entry");
 +			return (ARCHIVE_FAILED);
 +		}
 +		memset(derived_key, 0, sizeof(derived_key));
 +		r = archive_pbkdf2_sha1(passphrase, strlen(passphrase),
 +		    p, salt_len, 1000, derived_key, key_len * 2 + 2);
 +		if (r != 0) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Decryption is unsupported due to lack of "
 +			    "crypto library");
 +			return (ARCHIVE_FAILED);
 +		}
 +
 +		/* Check password verification value. */
 +		pv = ((const uint8_t *)p) + salt_len;
 +		if (derived_key[key_len * 2] == pv[0] &&
 +		    derived_key[key_len * 2 + 1] == pv[1])
 +			break;/* The passphrase is OK. */
 +		if (retry > 10000) {
 +			/* Avoid infinity loop. */
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Too many incorrect passphrases");
 +			return (ARCHIVE_FAILED);
 +		}
 +	}
 +
 +	r = archive_decrypto_aes_ctr_init(&zip->cctx, derived_key, key_len);
 +	if (r != 0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Decryption is unsupported due to lack of crypto library");
 +		return (ARCHIVE_FAILED);
 +	}
 +	r = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len);
 +	if (r != 0) {
 +		archive_decrypto_aes_ctr_release(&zip->cctx);
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Failed to initialize HMAC-SHA1");
 +		return (ARCHIVE_FAILED);
 +	}
 +	zip->cctx_valid = zip->hctx_valid = 1;
 +	__archive_read_consume(a, salt_len + 2);
 +	zip->entry_bytes_remaining -= salt_len + 2 + AUTH_CODE_SIZE;
 +	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
 +	    && zip->entry_bytes_remaining < 0)
 +		goto corrupted;
 +	zip->entry_compressed_bytes_read += salt_len + 2 + AUTH_CODE_SIZE;
 +	zip->decrypted_bytes_remaining = 0;
 +
 +	zip->entry->compression = zip->entry->aes_extra.compression;
 +	return (zip_alloc_decryption_buffer(a));
 +
 +truncated:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Truncated ZIP file data");
 +	return (ARCHIVE_FATAL);
 +corrupted:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Corrupted ZIP file data");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +static int
 +archive_read_format_zip_read_data(struct archive_read *a,
 +    const void **buff, size_t *size, int64_t *offset)
 +{
 +	int r;
 +	struct zip *zip = (struct zip *)(a->format->data);
 +
 +	if (zip->has_encrypted_entries ==
 +			ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
 +		zip->has_encrypted_entries = 0;
 +	}
 +
 +	*offset = zip->entry_uncompressed_bytes_read;
 +	*size = 0;
 +	*buff = NULL;
 +
 +	/* If we hit end-of-entry last time, return ARCHIVE_EOF. */
 +	if (zip->end_of_entry)
 +		return (ARCHIVE_EOF);
 +
 +	/* Return EOF immediately if this is a non-regular file. */
 +	if (AE_IFREG != (zip->entry->mode & AE_IFMT))
 +		return (ARCHIVE_EOF);
 +
 +	__archive_read_consume(a, zip->unconsumed);
 +	zip->unconsumed = 0;
 +
 +	if (zip->init_decryption) {
 +		zip->has_encrypted_entries = 1;
 +		if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED)
 +			r = read_decryption_header(a);
 +		else if (zip->entry->compression == WINZIP_AES_ENCRYPTION)
 +			r = init_WinZip_AES_decryption(a);
 +		else
 +			r = init_traditional_PKWARE_decryption(a);
 +		if (r != ARCHIVE_OK)
 +			return (r);
 +		zip->init_decryption = 0;
 +	}
 +
 +	switch(zip->entry->compression) {
 +	case 0:  /* No compression. */
 +		r =  zip_read_data_none(a, buff, size, offset);
 +		break;
 +#ifdef HAVE_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 (%s)",
 +		    compression_name(zip->entry->compression));
 +		/* We can't decompress this entry, but we will
 +		 * be able to skip() it and try the next entry. */
 +		return (ARCHIVE_FAILED);
 +		break;
 +	}
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	/* Update checksum */
 +	if (*size)
 +		zip->entry_crc32 = zip->crc32func(zip->entry_crc32, *buff,
 +		    (unsigned)*size);
 +	/* If we hit the end, swallow any end-of-data marker. */
 +	if (zip->end_of_entry) {
 +		/* Check file size, CRC against these values. */
 +		if (zip->entry->compressed_size !=
 +		    zip->entry_compressed_bytes_read) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "ZIP compressed data is wrong size "
 +			    "(read %jd, expected %jd)",
 +			    (intmax_t)zip->entry_compressed_bytes_read,
 +			    (intmax_t)zip->entry->compressed_size);
 +			return (ARCHIVE_WARN);
 +		}
 +		/* Size field only stores the lower 32 bits of the actual
 +		 * size. */
 +		if ((zip->entry->uncompressed_size & UINT32_MAX)
 +		    != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "ZIP uncompressed data is wrong size "
 +			    "(read %jd, expected %jd)\n",
 +			    (intmax_t)zip->entry_uncompressed_bytes_read,
 +			    (intmax_t)zip->entry->uncompressed_size);
 +			return (ARCHIVE_WARN);
 +		}
 +		/* Check computed CRC against header */
 +		if ((!zip->hctx_valid ||
 +		      zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) &&
 +		   zip->entry->crc32 != zip->entry_crc32
 +		    && !zip->ignore_crc32) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "ZIP bad CRC: 0x%lx should be 0x%lx",
 +			    (unsigned long)zip->entry_crc32,
 +			    (unsigned long)zip->entry->crc32);
 +			return (ARCHIVE_WARN);
 +		}
 +	}
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +archive_read_format_zip_cleanup(struct archive_read *a)
 +{
 +	struct zip *zip;
 +	struct zip_entry *zip_entry, *next_zip_entry;
 +
 +	zip = (struct zip *)(a->format->data);
 +#ifdef HAVE_ZLIB_H
 +	if (zip->stream_valid)
 +		inflateEnd(&zip->stream);
 +	free(zip->uncompressed_buffer);
 +#endif
 +	if (zip->zip_entries) {
 +		zip_entry = zip->zip_entries;
 +		while (zip_entry != NULL) {
 +			next_zip_entry = zip_entry->next;
 +			archive_string_free(&zip_entry->rsrcname);
 +			free(zip_entry);
 +			zip_entry = next_zip_entry;
 +		}
 +	}
 +	free(zip->decrypted_buffer);
 +	if (zip->cctx_valid)
 +		archive_decrypto_aes_ctr_release(&zip->cctx);
 +	if (zip->hctx_valid)
 +		archive_hmac_sha1_cleanup(&zip->hctx);
 +	free(zip->iv);
 +	free(zip->erd);
 +	free(zip->v_data);
 +	archive_string_free(&zip->format_name);
 +	free(zip);
 +	(a->format->data) = NULL;
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +archive_read_format_zip_has_encrypted_entries(struct archive_read *_a)
 +{
 +	if (_a && _a->format) {
 +		struct zip * zip = (struct zip *)_a->format->data;
 +		if (zip) {
 +			return zip->has_encrypted_entries;
 +		}
 +	}
 +	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
 +}
 +
 +static int
 +archive_read_format_zip_options(struct archive_read *a,
 +    const char *key, const char *val)
 +{
 +	struct zip *zip;
 +	int ret = ARCHIVE_FAILED;
 +
 +	zip = (struct zip *)(a->format->data);
 +	if (strcmp(key, "compat-2x")  == 0) {
 +		/* Handle filenames as libarchive 2.x */
 +		zip->init_default_conversion = (val != NULL) ? 1 : 0;
 +		return (ARCHIVE_OK);
 +	} else if (strcmp(key, "hdrcharset")  == 0) {
 +		if (val == NULL || val[0] == 0)
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "zip: hdrcharset option needs a character-set name"
 +			);
 +		else {
 +			zip->sconv = archive_string_conversion_from_charset(
 +			    &a->archive, val, 0);
 +			if (zip->sconv != NULL) {
 +				if (strcmp(val, "UTF-8") == 0)
 +					zip->sconv_utf8 = zip->sconv;
 +				ret = ARCHIVE_OK;
 +			} else
 +				ret = ARCHIVE_FATAL;
 +		}
 +		return (ret);
 +	} else if (strcmp(key, "ignorecrc32") == 0) {
 +		/* Mostly useful for testing. */
 +		if (val == NULL || val[0] == 0) {
 +			zip->crc32func = real_crc32;
 +			zip->ignore_crc32 = 0;
 +		} else {
 +			zip->crc32func = fake_crc32;
 +			zip->ignore_crc32 = 1;
 +		}
 +		return (ARCHIVE_OK);
 +	} else if (strcmp(key, "mac-ext") == 0) {
 +		zip->process_mac_extensions = (val != NULL && val[0] != 0);
 +		return (ARCHIVE_OK);
 +	}
 +
 +	/* Note: The "warn" return is just to inform the options
 +	 * supervisor that we didn't handle it.  It will generate
 +	 * a suitable error if no one used this option. */
 +	return (ARCHIVE_WARN);
 +}
 +
 +int
 +archive_read_support_format_zip(struct archive *a)
 +{
 +	int r;
 +	r = archive_read_support_format_zip_streamable(a);
 +	if (r != ARCHIVE_OK)
 +		return r;
 +	return (archive_read_support_format_zip_seekable(a));
 +}
 +
 +/* ------------------------------------------------------------------------ */
 +
 +/*
 + * Streaming-mode support
 + */
 +
 +
 +static int
 +archive_read_support_format_zip_capabilities_streamable(struct archive_read * a)
 +{
 +	(void)a; /* UNUSED */
 +	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
 +		ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
 +}
 +
 +static int
 +archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid)
 +{
 +	const char *p;
 +
 +	(void)best_bid; /* UNUSED */
 +
 +	if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)
 +		return (-1);
 +
 +	/*
 +	 * Bid of 29 here comes from:
 +	 *  + 16 bits for "PK",
 +	 *  + next 16-bit field has 6 options so contributes
 +	 *    about 16 - log_2(6) ~= 16 - 2.6 ~= 13 bits
 +	 *
 +	 * So we've effectively verified ~29 total bits of check data.
 +	 */
 +	if (p[0] == 'P' && p[1] == 'K') {
 +		if ((p[2] == '\001' && p[3] == '\002')
 +		    || (p[2] == '\003' && p[3] == '\004')
 +		    || (p[2] == '\005' && p[3] == '\006')
 +		    || (p[2] == '\006' && p[3] == '\006')
 +		    || (p[2] == '\007' && p[3] == '\010')
 +		    || (p[2] == '0' && p[3] == '0'))
 +			return (29);
 +	}
 +
 +	/* TODO: It's worth looking ahead a little bit for a valid
 +	 * PK signature.  In particular, that would make it possible
 +	 * to read some UUEncoded SFX files or SFX files coming from
 +	 * a network socket. */
 +
 +	return (0);
 +}
 +
 +static int
 +archive_read_format_zip_streamable_read_header(struct archive_read *a,
 +    struct archive_entry *entry)
 +{
 +	struct zip *zip;
 +
 +	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
 +	if (a->archive.archive_format_name == NULL)
 +		a->archive.archive_format_name = "ZIP";
 +
 +	zip = (struct zip *)(a->format->data);
 +
 +	/*
 +	 * It should be sufficient to call archive_read_next_header() for
 +	 * a reader to determine if an entry is encrypted or not. If the
 +	 * encryption of an entry is only detectable when calling
 +	 * archive_read_data(), so be it. We'll do the same check there
 +	 * as well.
 +	 */
 +	if (zip->has_encrypted_entries ==
 +			ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW)
 +		zip->has_encrypted_entries = 0;
 +
 +	/* Make sure we have a zip_entry structure to use. */
 +	if (zip->zip_entries == NULL) {
 +		zip->zip_entries = malloc(sizeof(struct zip_entry));
 +		if (zip->zip_entries == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Out  of memory");
 +			return ARCHIVE_FATAL;
 +		}
 +	}
 +	zip->entry = zip->zip_entries;
 +	memset(zip->entry, 0, sizeof(struct zip_entry));
 +
 +	if (zip->cctx_valid)
 +		archive_decrypto_aes_ctr_release(&zip->cctx);
 +	if (zip->hctx_valid)
 +		archive_hmac_sha1_cleanup(&zip->hctx);
 +	zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;
 +	__archive_read_reset_passphrase(a);
 +
 +	/* Search ahead for the next local file header. */
 +	__archive_read_consume(a, zip->unconsumed);
 +	zip->unconsumed = 0;
 +	for (;;) {
 +		int64_t skipped = 0;
 +		const char *p, *end;
 +		ssize_t bytes;
 +
 +		p = __archive_read_ahead(a, 4, &bytes);
 +		if (p == NULL)
 +			return (ARCHIVE_FATAL);
 +		end = p + bytes;
 +
 +		while (p + 4 <= end) {
 +			if (p[0] == 'P' && p[1] == 'K') {
 +				if (p[2] == '\003' && p[3] == '\004') {
 +					/* Regular file entry. */
 +					__archive_read_consume(a, skipped);
 +					return zip_read_local_file_header(a,
 +					    entry, zip);
 +				}
 +
 +                              /*
 +                               * TODO: We cannot restore permissions
 +                               * based only on the local file headers.
 +                               * Consider scanning the central
 +                               * directory and returning additional
 +                               * entries for at least directories.
 +                               * This would allow us to properly set
 +                               * directory permissions.
 +			       *
 +			       * This won't help us fix symlinks
 +			       * and may not help with regular file
 +			       * permissions, either.  <sigh>
 +                               */
 +                              if (p[2] == '\001' && p[3] == '\002') {
 +                                      return (ARCHIVE_EOF);
 +                              }
 +
 +                              /* End of central directory?  Must be an
 +                               * empty archive. */
 +                              if ((p[2] == '\005' && p[3] == '\006')
 +                                  || (p[2] == '\006' && p[3] == '\006'))
 +                                      return (ARCHIVE_EOF);
 +			}
 +			++p;
 +			++skipped;
 +		}
 +		__archive_read_consume(a, skipped);
 +	}
 +}
 +
 +static int
 +archive_read_format_zip_read_data_skip_streamable(struct archive_read *a)
 +{
 +	struct zip *zip;
 +	int64_t bytes_skipped;
 +
 +	zip = (struct zip *)(a->format->data);
 +	bytes_skipped = __archive_read_consume(a, zip->unconsumed);
 +	zip->unconsumed = 0;
 +	if (bytes_skipped < 0)
 +		return (ARCHIVE_FATAL);
 +
 +	/* If we've already read to end of data, we're done. */
 +	if (zip->end_of_entry)
 +		return (ARCHIVE_OK);
 +
 +	/* So we know we're streaming... */
 +	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
 +	    || zip->entry->compressed_size > 0) {
 +		/* We know the compressed length, so we can just skip. */
 +		bytes_skipped = __archive_read_consume(a,
 +					zip->entry_bytes_remaining);
 +		if (bytes_skipped < 0)
 +			return (ARCHIVE_FATAL);
 +		return (ARCHIVE_OK);
 +	}
 +
 +	if (zip->init_decryption) {
 +		int r;
 +
 +		zip->has_encrypted_entries = 1;
 +		if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED)
 +			r = read_decryption_header(a);
 +		else if (zip->entry->compression == WINZIP_AES_ENCRYPTION)
 +			r = init_WinZip_AES_decryption(a);
 +		else
 +			r = init_traditional_PKWARE_decryption(a);
 +		if (r != ARCHIVE_OK)
 +			return (r);
 +		zip->init_decryption = 0;
 +	}
 +
 +	/* We're streaming and we don't know the length. */
 +	/* If the body is compressed and we know the format, we can
 +	 * find an exact end-of-entry by decompressing it. */
 +	switch (zip->entry->compression) {
 +#ifdef HAVE_ZLIB_H
 +	case 8: /* Deflate compression. */
 +		while (!zip->end_of_entry) {
 +			int64_t offset = 0;
 +			const void *buff = NULL;
 +			size_t size = 0;
 +			int r;
 +			r =  zip_read_data_deflate(a, &buff, &size, &offset);
 +			if (r != ARCHIVE_OK)
 +				return (r);
 +		}
 +		return ARCHIVE_OK;
 +#endif
 +	default: /* Uncompressed or unknown. */
 +		/* Scan for a PK\007\010 signature. */
 +		for (;;) {
 +			const char *p, *buff;
 +			ssize_t bytes_avail;
 +			buff = __archive_read_ahead(a, 16, &bytes_avail);
 +			if (bytes_avail < 16) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_FILE_FORMAT,
 +				    "Truncated ZIP file data");
 +				return (ARCHIVE_FATAL);
 +			}
 +			p = buff;
 +			while (p <= buff + bytes_avail - 16) {
 +				if (p[3] == 'P') { p += 3; }
 +				else if (p[3] == 'K') { p += 2; }
 +				else if (p[3] == '\007') { p += 1; }
 +				else if (p[3] == '\010' && p[2] == '\007'
 +				    && p[1] == 'K' && p[0] == 'P') {
 +					if (zip->entry->flags & LA_USED_ZIP64)
 +						__archive_read_consume(a,
 +						    p - buff + 24);
 +					else
 +						__archive_read_consume(a,
 +						    p - buff + 16);
 +					return ARCHIVE_OK;
 +				} else { p += 4; }
 +			}
 +			__archive_read_consume(a, p - buff);
 +		}
 +	}
 +}
 +
 +int
 +archive_read_support_format_zip_streamable(struct archive *_a)
 +{
 +	struct archive_read *a = (struct archive_read *)_a;
 +	struct zip *zip;
 +	int r;
 +
 +	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
 +	    ARCHIVE_STATE_NEW, "archive_read_support_format_zip");
 +
 +	zip = (struct zip *)calloc(1, sizeof(*zip));
 +	if (zip == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate zip data");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	/* Streamable reader doesn't support mac extensions. */
 +	zip->process_mac_extensions = 0;
 +
 +	/*
 +	 * Until enough data has been read, we cannot tell about
 +	 * any encrypted entries yet.
 +	 */
 +	zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
 +	zip->crc32func = real_crc32;
 +
 +	r = __archive_read_register_format(a,
 +	    zip,
 +	    "zip",
 +	    archive_read_format_zip_streamable_bid,
 +	    archive_read_format_zip_options,
 +	    archive_read_format_zip_streamable_read_header,
 +	    archive_read_format_zip_read_data,
 +	    archive_read_format_zip_read_data_skip_streamable,
 +	    NULL,
 +	    archive_read_format_zip_cleanup,
 +	    archive_read_support_format_zip_capabilities_streamable,
 +	    archive_read_format_zip_has_encrypted_entries);
 +
 +	if (r != ARCHIVE_OK)
 +		free(zip);
 +	return (ARCHIVE_OK);
 +}
 +
 +/* ------------------------------------------------------------------------ */
 +
 +/*
 + * Seeking-mode support
 + */
 +
 +static int
 +archive_read_support_format_zip_capabilities_seekable(struct archive_read * a)
 +{
 +	(void)a; /* UNUSED */
 +	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
 +		ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
 +}
 +
 +/*
 + * TODO: This is a performance sink because it forces the read core to
 + * drop buffered data from the start of file, which will then have to
 + * be re-read again if this bidder loses.
 + *
 + * We workaround this a little by passing in the best bid so far so
 + * that later bidders can do nothing if they know they'll never
 + * outbid.  But we can certainly do better...
 + */
 +static int
 +read_eocd(struct zip *zip, const char *p, int64_t current_offset)
 +{
 +	/* Sanity-check the EOCD we've found. */
 +
 +	/* This must be the first volume. */
 +	if (archive_le16dec(p + 4) != 0)
 +		return 0;
 +	/* Central directory must be on this volume. */
 +	if (archive_le16dec(p + 4) != archive_le16dec(p + 6))
 +		return 0;
 +	/* All central directory entries must be on this volume. */
 +	if (archive_le16dec(p + 10) != archive_le16dec(p + 8))
 +		return 0;
 +	/* Central directory can't extend beyond start of EOCD record. */
 +	if (archive_le32dec(p + 16) + archive_le32dec(p + 12)
 +	    > current_offset)
 +		return 0;
 +
 +	/* Save the central directory location for later use. */
 +	zip->central_directory_offset = archive_le32dec(p + 16);
 +
 +	/* This is just a tiny bit higher than the maximum
 +	   returned by the streaming Zip bidder.  This ensures
 +	   that the more accurate seeking Zip parser wins
 +	   whenever seek is available. */
 +	return 32;
 +}
 +
 +/*
 + * Examine Zip64 EOCD locator:  If it's valid, store the information
 + * from it.
 + */
- static void
++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;
++		return 0;
 +	/* Must be only a single volume. */
 +	if (archive_le32dec(p + 16) != 1)
- 		return;
++		return 0;
 +
 +	/* Find the Zip64 EOCD record. */
 +	eocd64_offset = archive_le64dec(p + 8);
 +	if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0)
- 		return;
++		return 0;
 +	if ((p = __archive_read_ahead(a, 56, NULL)) == NULL)
- 		return;
++		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;
++		return 0;
 +	if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL)
- 		return;
++		return 0;
 +
 +	/* Sanity-check the EOCD64 */
 +	if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */
- 		return;
++		return 0;
 +	if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */
- 		return;
++		return 0;
 +	/* CD can't be split. */
 +	if (archive_le64dec(p + 24) != archive_le64dec(p + 32))
- 		return;
++		return 0;
 +
 +	/* Save the central directory offset for later use. */
 +	zip->central_directory_offset = archive_le64dec(p + 48);
++
++	return 32;
 +}
 +
 +static int
 +archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
 +{
 +	struct zip *zip = (struct zip *)a->format->data;
 +	int64_t file_size, current_offset;
 +	const char *p;
 +	int i, tail;
 +
 +	/* If someone has already bid more than 32, then avoid
 +	   trashing the look-ahead buffers with a seek. */
 +	if (best_bid > 32)
 +		return (-1);
 +
 +	file_size = __archive_read_seek(a, 0, SEEK_END);
 +	if (file_size <= 0)
 +		return 0;
 +
 +	/* Search last 16k of file for end-of-central-directory
 +	 * record (which starts with PK\005\006) */
 +	tail = (int)zipmin(1024 * 16, file_size);
 +	current_offset = __archive_read_seek(a, -tail, SEEK_END);
 +	if (current_offset < 0)
 +		return 0;
 +	if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL)
 +		return 0;
 +	/* Boyer-Moore search backwards from the end, since we want
 +	 * to match the last EOCD in the file (there can be more than
 +	 * one if there is an uncompressed Zip archive as a member
 +	 * within this Zip archive). */
 +	for (i = tail - 22; i > 0;) {
 +		switch (p[i]) {
 +		case 'P':
 +			if (memcmp(p + i, "PK\005\006", 4) == 0) {
 +				int ret = read_eocd(zip, p + i,
 +				    current_offset + i);
- 				if (ret > 0) {
- 					/* Zip64 EOCD locator precedes
- 					 * regular EOCD if present. */
- 					if (i >= 20
- 					    && memcmp(p + i - 20, "PK\006\007", 4) == 0) {
- 						read_zip64_eocd(a, zip, p + i - 20);
- 					}
- 					return (ret);
++				/* 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 zip *zip)
 +{
 +	ssize_t i;
 +	unsigned found;
 +	int64_t correction;
 +	ssize_t bytes_avail;
 +	const char *p;
 +
 +	/*
 +	 * Find the start of the central directory.  The end-of-CD
 +	 * record has our starting point, but there are lots of
 +	 * Zip archives which have had other data prepended to the
 +	 * file, which makes the recorded offsets all too small.
 +	 * So we search forward from the specified offset until we
 +	 * find the real start of the central directory.  Then we
 +	 * know the correction we need to apply to account for leading
 +	 * padding.
 +	 */
 +	if (__archive_read_seek(a, zip->central_directory_offset, SEEK_SET) < 0)
 +		return ARCHIVE_FATAL;
 +
 +	found = 0;
 +	while (!found) {
 +		if ((p = __archive_read_ahead(a, 20, &bytes_avail)) == NULL)
 +			return ARCHIVE_FATAL;
 +		for (found = 0, i = 0; !found && i < bytes_avail - 4;) {
 +			switch (p[i + 3]) {
 +			case 'P': i += 3; break;
 +			case 'K': i += 2; break;
 +			case 001: i += 1; break;
 +			case 002:
 +				if (memcmp(p + i, "PK\001\002", 4) == 0) {
 +					p += i;
 +					found = 1;
 +				} else
 +					i += 4;
 +				break;
 +			case 005: i += 1; break;
 +			case 006:
 +				if (memcmp(p + i, "PK\005\006", 4) == 0) {
 +					p += i;
 +					found = 1;
 +				} else if (memcmp(p + i, "PK\006\006", 4) == 0) {
 +					p += i;
 +					found = 1;
 +				} else
 +					i += 1;
 +				break;
 +			default: i += 4; break;
 +			}
 +		}
 +		__archive_read_consume(a, i);
 +	}
 +	correction = archive_filter_bytes(&a->archive, 0)
 +			- zip->central_directory_offset;
 +
 +	__archive_rb_tree_init(&zip->tree, &rb_ops);
 +	__archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops);
 +
 +	zip->central_directory_entries_total = 0;
 +	while (1) {
 +		struct zip_entry *zip_entry;
 +		size_t filename_length, extra_length, comment_length;
 +		uint32_t external_attributes;
 +		const char *name, *r;
 +
 +		if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)
 +			return ARCHIVE_FATAL;
 +		if (memcmp(p, "PK\006\006", 4) == 0
 +		    || memcmp(p, "PK\005\006", 4) == 0) {
 +			break;
 +		} else if (memcmp(p, "PK\001\002", 4) != 0) {
 +			archive_set_error(&a->archive,
 +			    -1, "Invalid central directory signature");
 +			return ARCHIVE_FATAL;
 +		}
 +		if ((p = __archive_read_ahead(a, 46, NULL)) == NULL)
 +			return ARCHIVE_FATAL;
 +
 +		zip_entry = calloc(1, sizeof(struct zip_entry));
 +		zip_entry->next = zip->zip_entries;
 +		zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY;
 +		zip->zip_entries = zip_entry;
 +		zip->central_directory_entries_total++;
 +
 +		/* version = p[4]; */
 +		zip_entry->system = p[5];
 +		/* version_required = archive_le16dec(p + 6); */
 +		zip_entry->zip_flags = archive_le16dec(p + 8);
 +		if (zip_entry->zip_flags
 +		      & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)){
 +			zip->has_encrypted_entries = 1;
 +		}
 +		zip_entry->compression = (char)archive_le16dec(p + 10);
 +		zip_entry->mtime = zip_time(p + 12);
 +		zip_entry->crc32 = archive_le32dec(p + 16);
 +		if (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
 +			zip_entry->decdat = p[13];
 +		else
 +			zip_entry->decdat = p[19];
 +		zip_entry->compressed_size = archive_le32dec(p + 20);
 +		zip_entry->uncompressed_size = archive_le32dec(p + 24);
 +		filename_length = archive_le16dec(p + 28);
 +		extra_length = archive_le16dec(p + 30);
 +		comment_length = archive_le16dec(p + 32);
 +		/* disk_start = archive_le16dec(p + 34); */ /* Better be zero. */
 +		/* internal_attributes = archive_le16dec(p + 36); */ /* text bit */
 +		external_attributes = archive_le32dec(p + 38);
 +		zip_entry->local_header_offset =
 +		    archive_le32dec(p + 42) + correction;
 +
 +		/* If we can't guess the mode, leave it zero here;
 +		   when we read the local file header we might get
 +		   more information. */
 +		if (zip_entry->system == 3) {
 +			zip_entry->mode = external_attributes >> 16;
 +		} else if (zip_entry->system == 0) {
 +			// Interpret MSDOS directory bit
 +			if (0x10 == (external_attributes & 0x10)) {
 +				zip_entry->mode = AE_IFDIR | 0775;
 +			} else {
 +				zip_entry->mode = AE_IFREG | 0664;
 +			}
 +			if (0x01 == (external_attributes & 0x01)) {
 +				// Read-only bit; strip write permissions
 +				zip_entry->mode &= 0555;
 +			}
 +		} else {
 +			zip_entry->mode = 0;
 +		}
 +
 +		/* We're done with the regular data; get the filename and
 +		 * extra data. */
 +		__archive_read_consume(a, 46);
 +		p = __archive_read_ahead(a, filename_length + extra_length,
 +			NULL);
 +		if (p == NULL) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Truncated ZIP file header");
 +			return ARCHIVE_FATAL;
 +		}
 +		if (ARCHIVE_OK != process_extra(a, p + filename_length, extra_length, zip_entry)) {
 +			return ARCHIVE_FATAL;
 +		}
 +
 +		/*
 +		 * Mac resource fork files are stored under the
 +		 * "__MACOSX/" directory, so we should check if
 +		 * it is.
 +		 */
 +		if (!zip->process_mac_extensions) {
 +			/* Treat every entry as a regular entry. */
 +			__archive_rb_tree_insert_node(&zip->tree,
 +			    &zip_entry->node);
 +		} else {
 +			name = p;
 +			r = rsrc_basename(name, filename_length);
 +			if (filename_length >= 9 &&
 +			    strncmp("__MACOSX/", name, 9) == 0) {
 +				/* If this file is not a resource fork nor
 +				 * a directory. We should treat it as a non
 +				 * resource fork file to expose it. */
 +				if (name[filename_length-1] != '/' &&
 +				    (r - name < 3 || r[0] != '.' || r[1] != '_')) {
 +					__archive_rb_tree_insert_node(
 +					    &zip->tree, &zip_entry->node);
 +					/* Expose its parent directories. */
 +					expose_parent_dirs(zip, name,
 +					    filename_length);
 +				} else {
 +					/* This file is a resource fork file or
 +					 * a directory. */
 +					archive_strncpy(&(zip_entry->rsrcname),
 +					     name, filename_length);
 +					__archive_rb_tree_insert_node(
 +					    &zip->tree_rsrc, &zip_entry->node);
 +				}
 +			} else {
 +				/* Generate resource fork name to find its
 +				 * resource file at zip->tree_rsrc. */
 +				archive_strcpy(&(zip_entry->rsrcname),
 +				    "__MACOSX/");
 +				archive_strncat(&(zip_entry->rsrcname),
 +				    name, r - name);
 +				archive_strcat(&(zip_entry->rsrcname), "._");
 +				archive_strncat(&(zip_entry->rsrcname),
 +				    name + (r - name),
 +				    filename_length - (r - name));
 +				/* Register an entry to RB tree to sort it by
 +				 * file offset. */
 +				__archive_rb_tree_insert_node(&zip->tree,
 +				    &zip_entry->node);
 +			}
 +		}
 +
 +		/* Skip the comment too ... */
 +		__archive_read_consume(a,
 +		    filename_length + extra_length + comment_length);
 +	}
 +
 +	return ARCHIVE_OK;
 +}
 +
 +static ssize_t
 +zip_get_local_file_header_size(struct archive_read *a, size_t extra)
 +{
 +	const char *p;
 +	ssize_t filename_length, extra_length;
 +
 +	if ((p = __archive_read_ahead(a, extra + 30, NULL)) == NULL) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated ZIP file header");
 +		return (ARCHIVE_WARN);
 +	}
 +	p += extra;
 +
 +	if (memcmp(p, "PK\003\004", 4) != 0) {
 +		archive_set_error(&a->archive, -1, "Damaged Zip archive");
 +		return ARCHIVE_WARN;
 +	}
 +	filename_length = archive_le16dec(p + 26);
 +	extra_length = archive_le16dec(p + 28);
 +
 +	return (30 + filename_length + extra_length);
 +}
 +
 +static int
 +zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry,
 +    struct zip_entry *rsrc)
 +{
 +	struct zip *zip = (struct zip *)a->format->data;
 +	unsigned char *metadata, *mp;
 +	int64_t offset = archive_filter_bytes(&a->archive, 0);
 +	size_t remaining_bytes, metadata_bytes;
 +	ssize_t hsize;
 +	int ret = ARCHIVE_OK, eof;
 +
 +	switch(rsrc->compression) {
 +	case 0:  /* No compression. */
 +		if (rsrc->uncompressed_size != rsrc->compressed_size) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Malformed OS X metadata entry: inconsistent size");
 +			return (ARCHIVE_FATAL);
 +		}
 +#ifdef HAVE_ZLIB_H
 +	case 8: /* Deflate compression. */
 +#endif
 +		break;
 +	default: /* Unsupported compression. */
 +		/* Return a warning. */
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Unsupported ZIP compression method (%s)",
 +		    compression_name(rsrc->compression));
 +		/* We can't decompress this entry, but we will
 +		 * be able to skip() it and try the next entry. */
 +		return (ARCHIVE_WARN);
 +	}
 +
 +	if (rsrc->uncompressed_size > (4 * 1024 * 1024)) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Mac metadata is too large: %jd > 4M bytes",
 +		    (intmax_t)rsrc->uncompressed_size);
 +		return (ARCHIVE_WARN);
 +	}
 +	if (rsrc->compressed_size > (4 * 1024 * 1024)) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Mac metadata is too large: %jd > 4M bytes",
 +		    (intmax_t)rsrc->compressed_size);
 +		return (ARCHIVE_WARN);
 +	}
 +
 +	metadata = malloc((size_t)rsrc->uncompressed_size);
 +	if (metadata == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate memory for Mac metadata");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	if (offset < rsrc->local_header_offset)
 +		__archive_read_consume(a, rsrc->local_header_offset - offset);
 +	else if (offset != rsrc->local_header_offset) {
 +		__archive_read_seek(a, rsrc->local_header_offset, SEEK_SET);
 +	}
 +
 +	hsize = zip_get_local_file_header_size(a, 0);
 +	__archive_read_consume(a, hsize);
 +
 +	remaining_bytes = (size_t)rsrc->compressed_size;
 +	metadata_bytes = (size_t)rsrc->uncompressed_size;
 +	mp = metadata;
 +	eof = 0;
 +	while (!eof && remaining_bytes) {
 +		const unsigned char *p;
 +		ssize_t bytes_avail;
 +		size_t bytes_used;
 +
 +		p = __archive_read_ahead(a, 1, &bytes_avail);
 +		if (p == NULL) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Truncated ZIP file header");
 +			ret = ARCHIVE_WARN;
 +			goto exit_mac_metadata;
 +		}
 +		if ((size_t)bytes_avail > remaining_bytes)
 +			bytes_avail = remaining_bytes;
 +		switch(rsrc->compression) {
 +		case 0:  /* No compression. */
 +			if ((size_t)bytes_avail > metadata_bytes)
 +				bytes_avail = metadata_bytes;
 +			memcpy(mp, p, bytes_avail);
 +			bytes_used = (size_t)bytes_avail;
 +			metadata_bytes -= bytes_used;
 +			mp += bytes_used;
 +			if (metadata_bytes == 0)
 +				eof = 1;
 +			break;
 +#ifdef HAVE_ZLIB_H
 +		case 8: /* Deflate compression. */
 +		{
 +			int r;
 +
 +			ret = zip_deflate_init(a, zip);
 +			if (ret != ARCHIVE_OK)
 +				goto exit_mac_metadata;
 +			zip->stream.next_in =
 +			    (Bytef *)(uintptr_t)(const void *)p;
 +			zip->stream.avail_in = (uInt)bytes_avail;
 +			zip->stream.total_in = 0;
 +			zip->stream.next_out = mp;
 +			zip->stream.avail_out = (uInt)metadata_bytes;
 +			zip->stream.total_out = 0;
 +
 +			r = inflate(&zip->stream, 0);
 +			switch (r) {
 +			case Z_OK:
 +				break;
 +			case Z_STREAM_END:
 +				eof = 1;
 +				break;
 +			case Z_MEM_ERROR:
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "Out of memory for ZIP decompression");
 +				ret = ARCHIVE_FATAL;
 +				goto exit_mac_metadata;
 +			default:
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "ZIP decompression failed (%d)", r);
 +				ret = ARCHIVE_FATAL;
 +				goto exit_mac_metadata;
 +			}
 +			bytes_used = zip->stream.total_in;
 +			metadata_bytes -= zip->stream.total_out;
 +			mp += zip->stream.total_out;
 +			break;
 +		}
 +#endif
 +		default:
 +			bytes_used = 0;
 +			break;
 +		}
 +		__archive_read_consume(a, bytes_used);
 +		remaining_bytes -= bytes_used;
 +	}
 +	archive_entry_copy_mac_metadata(entry, metadata,
 +	    (size_t)rsrc->uncompressed_size - metadata_bytes);
 +
 +exit_mac_metadata:
 +	__archive_read_seek(a, offset, SEEK_SET);
 +	zip->decompress_init = 0;
 +	free(metadata);
 +	return (ret);
 +}
 +
 +static int
 +archive_read_format_zip_seekable_read_header(struct archive_read *a,
 +	struct archive_entry *entry)
 +{
 +	struct zip *zip = (struct zip *)a->format->data;
 +	struct zip_entry *rsrc;
 +	int64_t offset;
 +	int r, ret = ARCHIVE_OK;
 +
 +	/*
 +	 * It should be sufficient to call archive_read_next_header() for
 +	 * a reader to determine if an entry is encrypted or not. If the
 +	 * encryption of an entry is only detectable when calling
 +	 * archive_read_data(), so be it. We'll do the same check there
 +	 * as well.
 +	 */
 +	if (zip->has_encrypted_entries ==
 +			ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW)
 +		zip->has_encrypted_entries = 0;
 +
 +	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
 +	if (a->archive.archive_format_name == NULL)
 +		a->archive.archive_format_name = "ZIP";
 +
 +	if (zip->zip_entries == NULL) {
 +		r = slurp_central_directory(a, zip);
 +		if (r != ARCHIVE_OK)
 +			return r;
 +		/* Get first entry whose local header offset is lower than
 +		 * other entries in the archive file. */
 +		zip->entry =
 +		    (struct zip_entry *)ARCHIVE_RB_TREE_MIN(&zip->tree);
 +	} else if (zip->entry != NULL) {
 +		/* Get next entry in local header offset order. */
 +		zip->entry = (struct zip_entry *)__archive_rb_tree_iterate(
 +		    &zip->tree, &zip->entry->node, ARCHIVE_RB_DIR_RIGHT);
 +	}
 +
 +	if (zip->entry == NULL)
 +		return ARCHIVE_EOF;
 +
 +	if (zip->entry->rsrcname.s)
 +		rsrc = (struct zip_entry *)__archive_rb_tree_find_node(
 +		    &zip->tree_rsrc, zip->entry->rsrcname.s);
 +	else
 +		rsrc = NULL;
 +
 +	if (zip->cctx_valid)
 +		archive_decrypto_aes_ctr_release(&zip->cctx);
 +	if (zip->hctx_valid)
 +		archive_hmac_sha1_cleanup(&zip->hctx);
 +	zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;
 +	__archive_read_reset_passphrase(a);
 +
 +	/* File entries are sorted by the header offset, we should mostly
 +	 * use __archive_read_consume to advance a read point to avoid redundant
 +	 * data reading.  */
 +	offset = archive_filter_bytes(&a->archive, 0);
 +	if (offset < zip->entry->local_header_offset)
 +		__archive_read_consume(a,
 +		    zip->entry->local_header_offset - offset);
 +	else if (offset != zip->entry->local_header_offset) {
 +		__archive_read_seek(a, zip->entry->local_header_offset,
 +		    SEEK_SET);
 +	}
 +	zip->unconsumed = 0;
 +	r = zip_read_local_file_header(a, entry, zip);
 +	if (r != ARCHIVE_OK)
 +		return r;
 +	if (rsrc) {
 +		int ret2 = zip_read_mac_metadata(a, entry, rsrc);
 +		if (ret2 < ret)
 +			ret = ret2;
 +	}
 +	return (ret);
 +}
 +
 +/*
 + * We're going to seek for the next header anyway, so we don't
 + * need to bother doing anything here.
 + */
 +static int
 +archive_read_format_zip_read_data_skip_seekable(struct archive_read *a)
 +{
 +	struct zip *zip;
 +	zip = (struct zip *)(a->format->data);
 +
 +	zip->unconsumed = 0;
 +	return (ARCHIVE_OK);
 +}
 +
 +int
 +archive_read_support_format_zip_seekable(struct archive *_a)
 +{
 +	struct archive_read *a = (struct archive_read *)_a;
 +	struct zip *zip;
 +	int r;
 +
 +	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
 +	    ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable");
 +
 +	zip = (struct zip *)calloc(1, sizeof(*zip));
 +	if (zip == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate zip data");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +#ifdef HAVE_COPYFILE_H
 +	/* Set this by default on Mac OS. */
 +	zip->process_mac_extensions = 1;
 +#endif
 +
 +	/*
 +	 * Until enough data has been read, we cannot tell about
 +	 * any encrypted entries yet.
 +	 */
 +	zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
 +	zip->crc32func = real_crc32;
 +
 +	r = __archive_read_register_format(a,
 +	    zip,
 +	    "zip",
 +	    archive_read_format_zip_seekable_bid,
 +	    archive_read_format_zip_options,
 +	    archive_read_format_zip_seekable_read_header,
 +	    archive_read_format_zip_read_data,
 +	    archive_read_format_zip_read_data_skip_seekable,
 +	    NULL,
 +	    archive_read_format_zip_cleanup,
 +	    archive_read_support_format_zip_capabilities_seekable,
 +	    archive_read_format_zip_has_encrypted_entries);
 +
 +	if (r != ARCHIVE_OK)
 +		free(zip);
 +	return (ARCHIVE_OK);
 +}
diff --cc Utilities/cmlibarchive/libarchive/archive_util.c
index f56ca33,0000000..1e36ad7
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_util.c
+++ b/Utilities/cmlibarchive/libarchive/archive_util.c
@@@ -1,667 -1,0 +1,585 @@@
 +/*-
 + * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA
 + * Copyright (c) 2003-2007 Tim Kientzle
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + */
 +
 +#include "archive_platform.h"
 +__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
 +
 +#ifdef HAVE_SYS_TYPES_H
 +#include <sys/types.h>
 +#endif
 +#ifdef HAVE_ERRNO_H
 +#include <errno.h>
 +#endif
 +#ifdef HAVE_FCNTL_H
 +#include <fcntl.h>
 +#endif
 +#ifdef HAVE_STDLIB_H
 +#include <stdlib.h>
 +#endif
 +#ifdef HAVE_STRING_H
 +#include <string.h>
 +#endif
 +#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
 +#include <wincrypt.h>
 +#endif
 +#ifdef HAVE_ZLIB_H
 +#include <cm_zlib.h>
 +#endif
 +#ifdef HAVE_LZMA_H
 +#include <cm_lzma.h>
 +#endif
 +#ifdef HAVE_BZLIB_H
 +#include <cm_bzlib.h>
 +#endif
 +#ifdef HAVE_LZ4_H
 +#include <lz4.h>
 +#endif
 +
 +#include "archive.h"
 +#include "archive_private.h"
 +#include "archive_random_private.h"
 +#include "archive_string.h"
 +
 +#ifndef O_CLOEXEC
 +#define O_CLOEXEC	0
 +#endif
 +
 +static int archive_utility_string_sort_helper(char **, unsigned int);
 +
 +/* Generic initialization of 'struct archive' objects. */
 +int
 +__archive_clean(struct archive *a)
 +{
 +	archive_string_conversion_free(a);
 +	return (ARCHIVE_OK);
 +}
 +
 +int
 +archive_version_number(void)
 +{
 +	return (ARCHIVE_VERSION_NUMBER);
 +}
 +
 +const char *
 +archive_version_string(void)
 +{
 +	return (ARCHIVE_VERSION_STRING);
 +}
 +
- 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();
- 
- 	if (!init) {
- 		archive_string_init(&str);
- 
- 		archive_strcat(&str, ARCHIVE_VERSION_STRING);
- 		if (zlib != NULL) {
- 			archive_strcat(&str, " zlib/");
- 			archive_strcat(&str, zlib);
- 		}
- 		if (liblzma) {
- 			archive_strcat(&str, " liblzma/");
- 			archive_strcat(&str, liblzma);
- 		}
- 		if (bzlib) {
- 			const char *p = bzlib;
- 			const char *sep = strchr(p, ',');
- 			if (sep == NULL)
- 				sep = p + strlen(p);
- 			archive_strcat(&str, " bz2lib/");
- 			archive_strncat(&str, p, sep - p);
- 		}
- 		if (liblz4) {
- 			archive_strcat(&str, " liblz4/");
- 			archive_strcat(&str, liblz4);
- 		}
- 	}
- 	return str.s;
- }
- 
- const char *
- archive_zlib_version(void)
- {
- #ifdef HAVE_ZLIB_H
- 	return ZLIB_VERSION;
- #else
- 	return NULL;
- #endif
- }
- 
- const char *
- archive_liblzma_version(void)
- {
- #ifdef HAVE_LZMA_H
- 	return LZMA_VERSION_STRING;
- #else
- 	return NULL;
- #endif
- }
- 
- const char *
- archive_bzlib_version(void)
- {
- #ifdef HAVE_BZLIB_H
- 	return BZ2_bzlibVersion();
- #else
- 	return NULL;
- #endif
- }
- 
- const char *
- archive_liblz4_version(void)
- {
- #if defined(HAVE_LZ4_H) && defined(HAVE_LIBLZ4)
- #define str(s) #s
- #define NUMBER(x) str(x)
- 	return NUMBER(LZ4_VERSION_MAJOR) "." NUMBER(LZ4_VERSION_MINOR) "." NUMBER(LZ4_VERSION_RELEASE);
- #undef NUMBER
- #undef str
- #else
- 	return NULL;
- #endif
- }
- 
 +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.
 + */
 +int64_t
 +archive_position_compressed(struct archive *a)
 +{
 +	return archive_filter_bytes(a, -1);
 +}
 +
 +/*
 + * Return a count of the number of uncompressed bytes processed.
 + */
 +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: ";
++	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.
 + */
 +int
 +__archive_mktemp(const char *tmpdir)
 +{
- 	static const wchar_t *prefix = L"libarchive_";
- 	static const wchar_t *suffix = L"XXXXXXXXXX";
++	static const wchar_t prefix[] = L"libarchive_";
++	static const wchar_t suffix[] = L"XXXXXXXXXX";
 +	static const wchar_t num[] = {
 +		L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
 +		L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
 +		L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
 +		L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
 +		L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
 +		L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
 +		L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
 +		L'u', L'v', L'w', L'x', L'y', L'z'
 +	};
 +	HCRYPTPROV hProv;
 +	struct archive_wstring temp_name;
 +	wchar_t *ws;
 +	DWORD attr;
 +	wchar_t *xp, *ep;
 +	int fd;
 +
 +	hProv = (HCRYPTPROV)NULL;
 +	fd = -1;
 +	ws = NULL;
 +	archive_string_init(&temp_name);
 +
 +	/* Get a temporary directory. */
 +	if (tmpdir == NULL) {
 +		size_t l;
 +		wchar_t *tmp;
 +
 +		l = GetTempPathW(0, NULL);
 +		if (l == 0) {
 +			la_dosmaperr(GetLastError());
 +			goto exit_tmpfile;
 +		}
 +		tmp = malloc(l*sizeof(wchar_t));
 +		if (tmp == NULL) {
 +			errno = ENOMEM;
 +			goto exit_tmpfile;
 +		}
 +		GetTempPathW((DWORD)l, tmp);
 +		archive_wstrcpy(&temp_name, tmp);
 +		free(tmp);
 +	} else {
 +		if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
 +		    strlen(tmpdir)) < 0)
 +			goto exit_tmpfile;
 +		if (temp_name.s[temp_name.length-1] != L'/')
 +			archive_wstrappend_wchar(&temp_name, L'/');
 +	}
 +
 +	/* Check if temp_name is a directory. */
 +	attr = GetFileAttributesW(temp_name.s);
 +	if (attr == (DWORD)-1) {
 +		if (GetLastError() != ERROR_FILE_NOT_FOUND) {
 +			la_dosmaperr(GetLastError());
 +			goto exit_tmpfile;
 +		}
 +		ws = __la_win_permissive_name_w(temp_name.s);
 +		if (ws == NULL) {
 +			errno = EINVAL;
 +			goto exit_tmpfile;
 +		}
 +		attr = GetFileAttributesW(ws);
 +		if (attr == (DWORD)-1) {
 +			la_dosmaperr(GetLastError());
 +			goto exit_tmpfile;
 +		}
 +	}
 +	if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
 +		errno = ENOTDIR;
 +		goto exit_tmpfile;
 +	}
 +
 +	/*
 +	 * Create a temporary file.
 +	 */
 +	archive_wstrcat(&temp_name, prefix);
 +	archive_wstrcat(&temp_name, suffix);
 +	ep = temp_name.s + archive_strlen(&temp_name);
 +	xp = ep - wcslen(suffix);
 +
 +	if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
 +		CRYPT_VERIFYCONTEXT)) {
 +		la_dosmaperr(GetLastError());
 +		goto exit_tmpfile;
 +	}
 +
 +	for (;;) {
 +		wchar_t *p;
 +		HANDLE h;
 +
 +		/* Generate a random file name through CryptGenRandom(). */
 +		p = xp;
 +		if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
 +		    (BYTE*)p)) {
 +			la_dosmaperr(GetLastError());
 +			goto exit_tmpfile;
 +		}
 +		for (; p < ep; p++)
 +			*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
 +
 +		free(ws);
 +		ws = __la_win_permissive_name_w(temp_name.s);
 +		if (ws == NULL) {
 +			errno = EINVAL;
 +			goto exit_tmpfile;
 +		}
 +		/* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to
 +		 * delete this temporary file immediately when this
 +		 * file closed. */
 +		h = CreateFileW(ws,
 +		    GENERIC_READ | GENERIC_WRITE | DELETE,
 +		    0,/* Not share */
 +		    NULL,
 +		    CREATE_NEW,/* Create a new file only */
 +		    FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
 +		    NULL);
 +		if (h == INVALID_HANDLE_VALUE) {
 +			/* The same file already exists. retry with
 +			 * a new filename. */
 +			if (GetLastError() == ERROR_FILE_EXISTS)
 +				continue;
 +			/* Otherwise, fail creation temporary file. */
 +			la_dosmaperr(GetLastError());
 +			goto exit_tmpfile;
 +		}
 +		fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
 +		if (fd == -1) {
 +			CloseHandle(h);
 +			goto exit_tmpfile;
 +		} else
 +			break;/* success! */
 +	}
 +exit_tmpfile:
 +	if (hProv != (HCRYPTPROV)NULL)
 +		CryptReleaseContext(hProv, 0);
 +	free(ws);
 +	archive_wstring_free(&temp_name);
 +	return (fd);
 +}
 +
 +#else
 +
 +static int
 +get_tempdir(struct archive_string *temppath)
 +{
 +	const char *tmp;
 +
 +	tmp = getenv("TMPDIR");
 +	if (tmp == NULL)
 +#ifdef _PATH_TMP
 +		tmp = _PATH_TMP;
 +#else
 +                tmp = "/tmp";
 +#endif
 +	archive_strcpy(temppath, tmp);
 +	if (temppath->s[temppath->length-1] != '/')
 +		archive_strappend_char(temppath, '/');
 +	return (ARCHIVE_OK);
 +}
 +
 +#if defined(HAVE_MKSTEMP)
 +
 +/*
 + * We can use mkstemp().
 + */
 +
 +int
 +__archive_mktemp(const char *tmpdir)
 +{
 +	struct archive_string temp_name;
 +	int fd = -1;
 +
 +	archive_string_init(&temp_name);
 +	if (tmpdir == NULL) {
 +		if (get_tempdir(&temp_name) != ARCHIVE_OK)
 +			goto exit_tmpfile;
 +	} else {
 +		archive_strcpy(&temp_name, tmpdir);
 +		if (temp_name.s[temp_name.length-1] != '/')
 +			archive_strappend_char(&temp_name, '/');
 +	}
 +	archive_strcat(&temp_name, "libarchive_XXXXXX");
 +	fd = mkstemp(temp_name.s);
 +	if (fd < 0)
 +		goto exit_tmpfile;
 +	__archive_ensure_cloexec_flag(fd);
 +	unlink(temp_name.s);
 +exit_tmpfile:
 +	archive_string_free(&temp_name);
 +	return (fd);
 +}
 +
 +#else
 +
 +/*
 + * We use a private routine.
 + */
 +
 +int
 +__archive_mktemp(const char *tmpdir)
 +{
 +        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;
 +	archive_string_init(&temp_name);
 +	if (tmpdir == NULL) {
 +		if (get_tempdir(&temp_name) != ARCHIVE_OK)
 +			goto exit_tmpfile;
 +	} else
 +		archive_strcpy(&temp_name, tmpdir);
 +	if (temp_name.s[temp_name.length-1] == '/') {
 +		temp_name.s[temp_name.length-1] = '\0';
 +		temp_name.length --;
 +	}
 +	if (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);
 +
 +	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(temp_name.s, 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);
 +	unlink(temp_name.s);
 +exit_tmpfile:
 +	archive_string_free(&temp_name);
 +	return (fd);
 +}
 +
 +#endif /* HAVE_MKSTEMP */
 +#endif /* !_WIN32 || __CYGWIN__ */
 +
 +/*
 + * Set FD_CLOEXEC flag to a file descriptor if it is not set.
 + * We have to set the flag if the platform does not provide O_CLOEXEC
 + * or F_DUPFD_CLOEXEC flags.
 + *
 + * Note: This function is absolutely called after creating a new file
 + * descriptor even if the platform seemingly provides O_CLOEXEC or
 + * F_DUPFD_CLOEXEC macros because it is possible that the platform
 + * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
 + */
 +void
 +__archive_ensure_cloexec_flag(int fd)
 +{
 +#if defined(_WIN32) && !defined(__CYGWIN__)
 +	(void)fd; /* UNUSED */
 +#else
 +	int flags;
 +
 +	if (fd >= 0) {
 +		flags = fcntl(fd, F_GETFD);
 +		if (flags != -1 && (flags & FD_CLOEXEC) == 0)
 +			fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
 +	}
 +#endif
 +}
 +
 +/*
 + * Utility function to sort a group of strings using quicksort.
 + */
 +static int
 +archive_utility_string_sort_helper(char **strings, unsigned int n)
 +{
 +	unsigned int i, lesser_count, greater_count;
 +	char **lesser, **greater, **tmp, *pivot;
 +	int retval1, retval2;
 +
 +	/* A list of 0 or 1 elements is already sorted */
 +	if (n <= 1)
 +		return (ARCHIVE_OK);
 +
 +	lesser_count = greater_count = 0;
 +	lesser = greater = NULL;
 +	pivot = strings[0];
 +	for (i = 1; i < n; i++)
 +	{
 +		if (strcmp(strings[i], pivot) < 0)
 +		{
 +			lesser_count++;
 +			tmp = (char **)realloc(lesser,
 +				lesser_count * sizeof(char *));
 +			if (!tmp) {
 +				free(greater);
 +				free(lesser);
 +				return (ARCHIVE_FATAL);
 +			}
 +			lesser = tmp;
 +			lesser[lesser_count - 1] = strings[i];
 +		}
 +		else
 +		{
 +			greater_count++;
 +			tmp = (char **)realloc(greater,
 +				greater_count * sizeof(char *));
 +			if (!tmp) {
 +				free(greater);
 +				free(lesser);
 +				return (ARCHIVE_FATAL);
 +			}
 +			greater = tmp;
 +			greater[greater_count - 1] = strings[i];
 +		}
 +	}
 +
 +	/* quicksort(lesser) */
 +	retval1 = archive_utility_string_sort_helper(lesser, lesser_count);
 +	for (i = 0; i < lesser_count; i++)
 +		strings[i] = lesser[i];
 +	free(lesser);
 +
 +	/* pivot */
 +	strings[lesser_count] = pivot;
 +
 +	/* quicksort(greater) */
 +	retval2 = archive_utility_string_sort_helper(greater, greater_count);
 +	for (i = 0; i < greater_count; i++)
 +		strings[lesser_count + 1 + i] = greater[i];
 +	free(greater);
 +
 +	return (retval1 < retval2) ? retval1 : retval2;
 +}
 +
 +int
 +archive_utility_string_sort(char **strings)
 +{
 +	  unsigned int size = 0;
 +	  while (strings[size] != NULL)
 +		size++;
 +	  return archive_utility_string_sort_helper(strings, size);
 +}
diff --cc Utilities/cmlibarchive/libarchive/archive_version_details.c
index 0000000,813f0f3..813f0f3
mode 000000,100644..100644
--- a/Utilities/cmlibarchive/libarchive/archive_version_details.c
+++ b/Utilities/cmlibarchive/libarchive/archive_version_details.c
diff --cc Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c
index a5f3067,0000000..4a42a3b
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c
@@@ -1,4243 -1,0 +1,4327 @@@
 +/*-
 + * Copyright (c) 2003-2010 Tim Kientzle
 + * Copyright (c) 2012 Michihiro NAKAJIMA
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer
 + *    in this position and unchanged.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + */
 +
 +#include "archive_platform.h"
 +__FBSDID("$FreeBSD$");
 +
 +#if !defined(_WIN32) || defined(__CYGWIN__)
 +
 +#ifdef HAVE_SYS_TYPES_H
 +#include <sys/types.h>
 +#endif
 +#ifdef HAVE_SYS_ACL_H
 +#include <sys/acl.h>
 +#endif
 +#ifdef HAVE_SYS_EXTATTR_H
 +#include <sys/extattr.h>
 +#endif
- #if defined(HAVE_SYS_XATTR_H)
++#if HAVE_SYS_XATTR_H
 +#include <sys/xattr.h>
- #elif defined(HAVE_ATTR_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 <cm_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
 +
 +struct fixup_entry {
 +	struct fixup_entry	*next;
 +	struct archive_acl	 acl;
 +	mode_t			 mode;
 +	int64_t			 atime;
 +	int64_t                  birthtime;
 +	int64_t			 mtime;
 +	int64_t			 ctime;
 +	unsigned long		 atime_nanos;
 +	unsigned long            birthtime_nanos;
 +	unsigned long		 mtime_nanos;
 +	unsigned long		 ctime_nanos;
 +	unsigned long		 fflags_set;
 +	size_t			 mac_metadata_size;
 +	void			*mac_metadata;
 +	int			 fixup; /* bitmask of what needs fixing */
 +	char			*name;
 +};
 +
 +/*
 + * We use a bitmask to track which operations remain to be done for
 + * this file.  In particular, this helps us avoid unnecessary
 + * operations when it's possible to take care of one step as a
 + * side-effect of another.  For example, mkdir() can specify the mode
 + * for the newly-created object but symlink() cannot.  This means we
 + * can skip chmod() if mkdir() succeeded, but we must explicitly
 + * chmod() if we're trying to create a directory that already exists
 + * (mkdir() failed) or if we're restoring a symlink.  Similarly, we
 + * need to verify UID/GID before trying to restore SUID/SGID bits;
 + * that verification can occur explicitly through a stat() call or
 + * implicitly because of a successful chown() call.
 + */
 +#define	TODO_MODE_FORCE		0x40000000
 +#define	TODO_MODE_BASE		0x20000000
 +#define	TODO_SUID		0x10000000
 +#define	TODO_SUID_CHECK		0x08000000
 +#define	TODO_SGID		0x04000000
 +#define	TODO_SGID_CHECK		0x02000000
 +#define	TODO_APPLEDOUBLE	0x01000000
 +#define	TODO_MODE		(TODO_MODE_BASE|TODO_SUID|TODO_SGID)
 +#define	TODO_TIMES		ARCHIVE_EXTRACT_TIME
 +#define	TODO_OWNER		ARCHIVE_EXTRACT_OWNER
 +#define	TODO_FFLAGS		ARCHIVE_EXTRACT_FFLAGS
 +#define	TODO_ACLS		ARCHIVE_EXTRACT_ACL
 +#define	TODO_XATTR		ARCHIVE_EXTRACT_XATTR
 +#define	TODO_MAC_METADATA	ARCHIVE_EXTRACT_MAC_METADATA
 +#define	TODO_HFS_COMPRESSION	ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED
 +
 +struct archive_write_disk {
 +	struct archive	archive;
 +
 +	mode_t			 user_umask;
 +	struct fixup_entry	*fixup_list;
 +	struct fixup_entry	*current_fixup;
 +	int64_t			 user_uid;
 +	int			 skip_file_set;
 +	int64_t			 skip_file_dev;
 +	int64_t			 skip_file_ino;
 +	time_t			 start_time;
 +
 +	int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid);
 +	void  (*cleanup_gid)(void *private);
 +	void			*lookup_gid_data;
 +	int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid);
 +	void  (*cleanup_uid)(void *private);
 +	void			*lookup_uid_data;
 +
 +	/*
 +	 * Full path of last file to satisfy symlink checks.
 +	 */
 +	struct archive_string	path_safe;
 +
 +	/*
 +	 * Cached stat data from disk for the current entry.
 +	 * If this is valid, pst points to st.  Otherwise,
 +	 * pst is null.
 +	 */
 +	struct stat		 st;
 +	struct stat		*pst;
 +
 +	/* Information about the object being restored right now. */
 +	struct archive_entry	*entry; /* Entry being extracted. */
 +	char			*name; /* Name of entry, possibly edited. */
 +	struct archive_string	 _name_data; /* backing store for 'name' */
 +	/* 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 void	fsobj_error(int *, struct archive_string *, int, const char *,
 +		    const char *);
 +static int	check_symlinks_fsobj(char *, int *, struct archive_string *,
 +		    int);
 +static int	check_symlinks(struct archive_write_disk *);
 +static int	create_filesystem_object(struct archive_write_disk *);
 +static struct fixup_entry *current_fixup(struct archive_write_disk *,
 +		    const char *pathname);
 +#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
 +static void	edit_deep_directories(struct archive_write_disk *ad);
 +#endif
 +static int	cleanup_pathname_fsobj(char *, int *, struct archive_string *,
 +		    int);
 +static int	cleanup_pathname(struct archive_write_disk *);
 +static int	create_dir(struct archive_write_disk *, char *);
 +static int	create_parent_dir(struct archive_write_disk *, char *);
 +static ssize_t	hfs_write_data_block(struct archive_write_disk *,
 +		    const char *, size_t);
 +static int	fixup_appledouble(struct archive_write_disk *, const char *);
 +static int	older(struct stat *, struct archive_entry *);
 +static int	restore_entry(struct archive_write_disk *);
 +static int	set_mac_metadata(struct archive_write_disk *, const char *,
 +				 const void *, size_t);
 +static int	set_xattrs(struct archive_write_disk *);
 +static int	clear_nochange_fflags(struct archive_write_disk *);
 +static int	set_fflags(struct archive_write_disk *);
 +static int	set_fflags_platform(struct archive_write_disk *, int fd,
 +		    const char *name, mode_t mode,
 +		    unsigned long fflags_set, unsigned long fflags_clear);
 +static int	set_ownership(struct archive_write_disk *);
 +static int	set_mode(struct archive_write_disk *, int mode);
 +static int	set_time(int, int, const char *, time_t, long, time_t, long);
 +static int	set_times(struct archive_write_disk *, int, int, const char *,
 +		    time_t, long, time_t, long, time_t, long, time_t, long);
 +static int	set_times_from_entry(struct archive_write_disk *);
 +static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
 +static ssize_t	write_data_block(struct archive_write_disk *,
 +		    const char *, size_t);
 +
 +static struct archive_vtable *archive_write_disk_vtable(void);
 +
 +static int	_archive_write_disk_close(struct archive *);
 +static int	_archive_write_disk_free(struct archive *);
 +static int	_archive_write_disk_header(struct archive *,
 +		    struct archive_entry *);
 +static int64_t	_archive_write_disk_filter_bytes(struct archive *, int);
 +static int	_archive_write_disk_finish_entry(struct archive *);
 +static ssize_t	_archive_write_disk_data(struct archive *, const void *,
 +		    size_t);
 +static ssize_t	_archive_write_disk_data_block(struct archive *, const void *,
 +		    size_t, int64_t);
 +
 +static int
 +lazy_stat(struct archive_write_disk *a)
 +{
 +	if (a->pst != NULL) {
 +		/* Already have stat() data available. */
 +		return (ARCHIVE_OK);
 +	}
 +#ifdef HAVE_FSTAT
 +	if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) {
 +		a->pst = &a->st;
 +		return (ARCHIVE_OK);
 +	}
 +#endif
 +	/*
 +	 * XXX At this point, symlinks should not be hit, otherwise
 +	 * XXX a race occurred.  Do we want to check explicitly for that?
 +	 */
 +	if (lstat(a->name, &a->st) == 0) {
 +		a->pst = &a->st;
 +		return (ARCHIVE_OK);
 +	}
 +	archive_set_error(&a->archive, errno, "Couldn't stat file");
 +	return (ARCHIVE_WARN);
 +}
 +
 +static struct archive_vtable *
 +archive_write_disk_vtable(void)
 +{
 +	static struct archive_vtable av;
 +	static int inited = 0;
 +
 +	if (!inited) {
 +		av.archive_close = _archive_write_disk_close;
 +		av.archive_filter_bytes = _archive_write_disk_filter_bytes;
 +		av.archive_free = _archive_write_disk_free;
 +		av.archive_write_header = _archive_write_disk_header;
 +		av.archive_write_finish_entry
 +		    = _archive_write_disk_finish_entry;
 +		av.archive_write_data = _archive_write_disk_data;
 +		av.archive_write_data_block = _archive_write_disk_data_block;
 +		inited = 1;
 +	}
 +	return (&av);
 +}
 +
 +static int64_t
 +_archive_write_disk_filter_bytes(struct archive *_a, int n)
 +{
 +	struct archive_write_disk *a = (struct archive_write_disk *)_a;
 +	(void)n; /* UNUSED */
 +	if (n == -1 || n == 0)
 +		return (a->total_bytes_written);
 +	return (-1);
 +}
 +
 +
 +int
 +archive_write_disk_set_options(struct archive *_a, int flags)
 +{
 +	struct archive_write_disk *a = (struct archive_write_disk *)_a;
 +
 +	a->flags = flags;
 +	return (ARCHIVE_OK);
 +}
 +
 +
 +/*
 + * Extract this entry to disk.
 + *
 + * TODO: Validate hardlinks.  According to the standards, we're
 + * supposed to check each extracted hardlink and squawk if it refers
 + * to a file that we didn't restore.  I'm not entirely convinced this
 + * is a good idea, but more importantly: Is there any way to validate
 + * hardlinks without keeping a complete list of filenames from the
 + * entire archive?? Ugh.
 + *
 + */
 +static int
 +_archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
 +{
 +	struct archive_write_disk *a = (struct archive_write_disk *)_a;
 +	struct fixup_entry *fe;
 +	int ret, r;
 +
 +	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
 +	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
 +	    "archive_write_disk_header");
 +	archive_clear_error(&a->archive);
 +	if (a->archive.state & ARCHIVE_STATE_DATA) {
 +		r = _archive_write_disk_finish_entry(&a->archive);
 +		if (r == ARCHIVE_FATAL)
 +			return (r);
 +	}
 +
 +	/* Set up for this particular entry. */
 +	a->pst = NULL;
 +	a->current_fixup = NULL;
 +	a->deferred = 0;
 +	if (a->entry) {
 +		archive_entry_free(a->entry);
 +		a->entry = NULL;
 +	}
 +	a->entry = archive_entry_clone(entry);
 +	a->fd = -1;
 +	a->fd_offset = 0;
 +	a->offset = 0;
 +	a->restore_pwd = -1;
 +	a->uid = a->user_uid;
 +	a->mode = archive_entry_mode(a->entry);
 +	if (archive_entry_size_is_set(a->entry))
 +		a->filesize = archive_entry_size(a->entry);
 +	else
 +		a->filesize = -1;
 +	archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry));
 +	a->name = a->_name_data.s;
 +	archive_clear_error(&a->archive);
 +
 +	/*
 +	 * Clean up the requested path.  This is necessary for correct
 +	 * dir restores; the dir restore logic otherwise gets messed
 +	 * up by nonsense like "dir/.".
 +	 */
 +	ret = cleanup_pathname(a);
 +	if (ret != ARCHIVE_OK)
 +		return (ret);
 +
 +	/*
 +	 * Query the umask so we get predictable mode settings.
 +	 * This gets done on every call to _write_header in case the
 +	 * user edits their umask during the extraction for some
 +	 * reason.
 +	 */
 +	umask(a->user_umask = umask(0));
 +
 +	/* Figure out what we need to do for this entry. */
 +	a->todo = TODO_MODE_BASE;
 +	if (a->flags & ARCHIVE_EXTRACT_PERM) {
 +		a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */
 +		/*
 +		 * SGID requires an extra "check" step because we
 +		 * cannot easily predict the GID that the system will
 +		 * assign.  (Different systems assign GIDs to files
 +		 * based on a variety of criteria, including process
 +		 * credentials and the gid of the enclosing
 +		 * directory.)  We can only restore the SGID bit if
 +		 * the file has the right GID, and we only know the
 +		 * GID if we either set it (see set_ownership) or if
 +		 * we've actually called stat() on the file after it
 +		 * was restored.  Since there are several places at
 +		 * which we might verify the GID, we need a TODO bit
 +		 * to keep track.
 +		 */
 +		if (a->mode & S_ISGID)
 +			a->todo |= TODO_SGID | TODO_SGID_CHECK;
 +		/*
 +		 * Verifying the SUID is simpler, but can still be
 +		 * done in multiple ways, hence the separate "check" bit.
 +		 */
 +		if (a->mode & S_ISUID)
 +			a->todo |= TODO_SUID | TODO_SUID_CHECK;
 +	} else {
 +		/*
 +		 * User didn't request full permissions, so don't
 +		 * restore SUID, SGID bits and obey umask.
 +		 */
 +		a->mode &= ~S_ISUID;
 +		a->mode &= ~S_ISGID;
 +		a->mode &= ~S_ISVTX;
 +		a->mode &= ~a->user_umask;
 +	}
 +	if (a->flags & ARCHIVE_EXTRACT_OWNER)
 +		a->todo |= TODO_OWNER;
 +	if (a->flags & ARCHIVE_EXTRACT_TIME)
 +		a->todo |= TODO_TIMES;
 +	if (a->flags & ARCHIVE_EXTRACT_ACL) {
++#if ARCHIVE_ACL_DARWIN
++		/*
++		 * On MacOS, platform ACLs get stored in mac_metadata, too.
++		 * If we intend to extract mac_metadata and it is present
++		 * we skip extracting libarchive NFSv4 ACLs.
++		 */
++		size_t metadata_size;
++
++		if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 ||
++		    archive_entry_mac_metadata(a->entry,
++		    &metadata_size) == NULL || metadata_size == 0)
++#endif
++#if ARCHIVE_ACL_LIBRICHACL
++		/*
++		 * RichACLs are stored in an extended attribute.
++		 * If we intend to extract extended attributes and have this
++		 * attribute we skip extracting libarchive NFSv4 ACLs.
++		 */
++		short extract_acls = 1;
++		if (a->flags & ARCHIVE_EXTRACT_XATTR && (
++		    archive_entry_acl_types(a->entry) &
++		    ARCHIVE_ENTRY_ACL_TYPE_NFS4)) {
++			const char *attr_name;
++			const void *attr_value;
++			size_t attr_size;
++			int i = archive_entry_xattr_reset(a->entry);
++			while (i--) {
++				archive_entry_xattr_next(a->entry, &attr_name,
++				    &attr_value, &attr_size);
++				if (attr_name != NULL && attr_value != NULL &&
++				    attr_size > 0 && strcmp(attr_name,
++				    "trusted.richacl") == 0) {
++					extract_acls = 0;
++					break;
++				}
++			}
++		}
++		if (extract_acls)
++#endif
++#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL
++		{
++#endif
 +		if (archive_entry_filetype(a->entry) == AE_IFDIR)
 +			a->deferred |= TODO_ACLS;
 +		else
 +			a->todo |= TODO_ACLS;
++#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL
++		}
++#endif
 +	}
 +	if (a->flags & ARCHIVE_EXTRACT_MAC_METADATA) {
 +		if (archive_entry_filetype(a->entry) == AE_IFDIR)
 +			a->deferred |= TODO_MAC_METADATA;
 +		else
 +			a->todo |= TODO_MAC_METADATA;
 +	}
 +#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
 +	if ((a->flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) == 0) {
 +		unsigned long set, clear;
 +		archive_entry_fflags(a->entry, &set, &clear);
 +		if ((set & ~clear) & UF_COMPRESSED) {
 +			a->todo |= TODO_HFS_COMPRESSION;
 +			a->decmpfs_block_count = (unsigned)-1;
 +		}
 +	}
 +	if ((a->flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) != 0 &&
 +	    (a->mode & AE_IFMT) == AE_IFREG && a->filesize > 0) {
 +		a->todo |= TODO_HFS_COMPRESSION;
 +		a->decmpfs_block_count = (unsigned)-1;
 +	}
 +	{
 +		const char *p;
 +
 +		/* Check if the current file name is a type of the
 +		 * resource fork file. */
 +		p = strrchr(a->name, '/');
 +		if (p == NULL)
 +			p = a->name;
 +		else
 +			p++;
 +		if (p[0] == '.' && p[1] == '_') {
 +			/* Do not compress "._XXX" files. */
 +			a->todo &= ~TODO_HFS_COMPRESSION;
 +			if (a->filesize > 0)
 +				a->todo |= TODO_APPLEDOUBLE;
 +		}
 +	}
 +#endif
 +
- 	if (a->flags & ARCHIVE_EXTRACT_XATTR)
++	if (a->flags & ARCHIVE_EXTRACT_XATTR) {
++#if ARCHIVE_XATTR_DARWIN
++		/*
++		 * On MacOS, extended attributes get stored in mac_metadata,
++		 * too. If we intend to extract mac_metadata and it is present
++		 * we skip extracting extended attributes.
++		 */
++		size_t metadata_size;
++
++		if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 ||
++		    archive_entry_mac_metadata(a->entry,
++		    &metadata_size) == NULL || metadata_size == 0)
++#endif
 +		a->todo |= TODO_XATTR;
++	}
 +	if (a->flags & ARCHIVE_EXTRACT_FFLAGS)
 +		a->todo |= TODO_FFLAGS;
 +	if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {
 +		ret = check_symlinks(a);
 +		if (ret != ARCHIVE_OK)
 +			return (ret);
 +	}
 +#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
 +	/* If path exceeds PATH_MAX, shorten the path. */
 +	edit_deep_directories(a);
 +#endif
 +
 +	ret = restore_entry(a);
 +
 +#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
 +	/*
 +	 * Check if the filesystem the file is restoring on supports
 +	 * HFS+ Compression. If not, cancel HFS+ Compression.
 +	 */
 +	if (a->todo | TODO_HFS_COMPRESSION) {
 +		/*
 +		 * NOTE: UF_COMPRESSED is ignored even if the filesystem
 +		 * supports HFS+ Compression because the file should
 +		 * have at least an extended attribute "com.apple.decmpfs"
 +		 * before the flag is set to indicate that the file have
 +		 * been compressed. If the filesystem does not support
 +		 * HFS+ Compression the system call will fail.
 +		 */
 +		if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0)
 +			a->todo &= ~TODO_HFS_COMPRESSION;
 +	}
 +#endif
 +
 +	/*
 +	 * TODO: There are rumours that some extended attributes must
 +	 * be restored before file data is written.  If this is true,
 +	 * then we either need to write all extended attributes both
 +	 * before and after restoring the data, or find some rule for
 +	 * determining which must go first and which last.  Due to the
 +	 * many ways people are using xattrs, this may prove to be an
 +	 * intractable problem.
 +	 */
 +
 +#ifdef HAVE_FCHDIR
 +	/* If we changed directory above, restore it here. */
 +	if (a->restore_pwd >= 0) {
 +		r = fchdir(a->restore_pwd);
 +		if (r != 0) {
 +			archive_set_error(&a->archive, errno,
 +			    "chdir() failure");
 +			ret = ARCHIVE_FATAL;
 +		}
 +		close(a->restore_pwd);
 +		a->restore_pwd = -1;
 +	}
 +#endif
 +
 +	/*
 +	 * Fixup uses the unedited pathname from archive_entry_pathname(),
 +	 * because it is relative to the base dir and the edited path
 +	 * might be relative to some intermediate dir as a result of the
 +	 * deep restore logic.
 +	 */
 +	if (a->deferred & TODO_MODE) {
 +		fe = current_fixup(a, archive_entry_pathname(entry));
 +		if (fe == NULL)
 +			return (ARCHIVE_FATAL);
 +		fe->fixup |= TODO_MODE_BASE;
 +		fe->mode = a->mode;
 +	}
 +
 +	if ((a->deferred & TODO_TIMES)
 +		&& (archive_entry_mtime_is_set(entry)
 +		    || archive_entry_atime_is_set(entry))) {
 +		fe = current_fixup(a, archive_entry_pathname(entry));
 +		if (fe == NULL)
 +			return (ARCHIVE_FATAL);
 +		fe->mode = a->mode;
 +		fe->fixup |= TODO_TIMES;
 +		if (archive_entry_atime_is_set(entry)) {
 +			fe->atime = archive_entry_atime(entry);
 +			fe->atime_nanos = archive_entry_atime_nsec(entry);
 +		} else {
 +			/* If atime is unset, use start time. */
 +			fe->atime = a->start_time;
 +			fe->atime_nanos = 0;
 +		}
 +		if (archive_entry_mtime_is_set(entry)) {
 +			fe->mtime = archive_entry_mtime(entry);
 +			fe->mtime_nanos = archive_entry_mtime_nsec(entry);
 +		} else {
 +			/* If mtime is unset, use start time. */
 +			fe->mtime = a->start_time;
 +			fe->mtime_nanos = 0;
 +		}
 +		if (archive_entry_birthtime_is_set(entry)) {
 +			fe->birthtime = archive_entry_birthtime(entry);
 +			fe->birthtime_nanos = archive_entry_birthtime_nsec(
 +			    entry);
 +		} else {
 +			/* If birthtime is unset, use mtime. */
 +			fe->birthtime = fe->mtime;
 +			fe->birthtime_nanos = fe->mtime_nanos;
 +		}
 +	}
 +
 +	if (a->deferred & TODO_ACLS) {
 +		fe = current_fixup(a, archive_entry_pathname(entry));
 +		if (fe == NULL)
 +			return (ARCHIVE_FATAL);
 +		fe->fixup |= TODO_ACLS;
 +		archive_acl_copy(&fe->acl, archive_entry_acl(entry));
 +	}
 +
 +	if (a->deferred & TODO_MAC_METADATA) {
 +		const void *metadata;
 +		size_t metadata_size;
 +		metadata = archive_entry_mac_metadata(a->entry, &metadata_size);
 +		if (metadata != NULL && metadata_size > 0) {
 +			fe = current_fixup(a, archive_entry_pathname(entry));
 +			if (fe == NULL)
 +				return (ARCHIVE_FATAL);
 +			fe->mac_metadata = malloc(metadata_size);
 +			if (fe->mac_metadata != NULL) {
 +				memcpy(fe->mac_metadata, metadata,
 +				    metadata_size);
 +				fe->mac_metadata_size = metadata_size;
 +				fe->fixup |= TODO_MAC_METADATA;
 +			}
 +		}
 +	}
 +
 +	if (a->deferred & TODO_FFLAGS) {
 +		fe = current_fixup(a, archive_entry_pathname(entry));
 +		if (fe == NULL)
 +			return (ARCHIVE_FATAL);
 +		fe->fixup |= TODO_FFLAGS;
 +		/* TODO: Complete this.. defer fflags from below. */
 +	}
 +
 +	/* We've created the object and are ready to pour data into it. */
 +	if (ret >= ARCHIVE_WARN)
 +		a->archive.state = ARCHIVE_STATE_DATA;
 +	/*
 +	 * If it's not open, tell our client not to try writing.
 +	 * In particular, dirs, links, etc, don't get written to.
 +	 */
 +	if (a->fd < 0) {
 +		archive_entry_set_size(entry, 0);
 +		a->filesize = 0;
 +	}
 +
 +	return (ret);
 +}
 +
 +int
 +archive_write_disk_set_skip_file(struct archive *_a, int64_t d, int64_t i)
 +{
 +	struct archive_write_disk *a = (struct archive_write_disk *)_a;
 +	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
 +	    ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file");
 +	a->skip_file_set = 1;
 +	a->skip_file_dev = d;
 +	a->skip_file_ino = i;
 +	return (ARCHIVE_OK);
 +}
 +
 +static ssize_t
 +write_data_block(struct archive_write_disk *a, const char *buff, size_t size)
 +{
 +	uint64_t start_size = size;
 +	ssize_t bytes_written = 0;
 +	ssize_t block_size = 0, bytes_to_write;
 +
 +	if (size == 0)
 +		return (ARCHIVE_OK);
 +
 +	if (a->filesize == 0 || a->fd < 0) {
 +		archive_set_error(&a->archive, 0,
 +		    "Attempt to write to an empty file");
 +		return (ARCHIVE_WARN);
 +	}
 +
 +	if (a->flags & ARCHIVE_EXTRACT_SPARSE) {
 +#if HAVE_STRUCT_STAT_ST_BLKSIZE
 +		int r;
 +		if ((r = lazy_stat(a)) != ARCHIVE_OK)
 +			return (r);
 +		block_size = a->pst->st_blksize;
 +#else
 +		/* XXX TODO XXX Is there a more appropriate choice here ? */
 +		/* This needn't match the filesystem allocation size. */
 +		block_size = 16*1024;
 +#endif
 +	}
 +
 +	/* If this write would run beyond the file size, truncate it. */
 +	if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)
 +		start_size = size = (size_t)(a->filesize - a->offset);
 +
 +	/* Write the data. */
 +	while (size > 0) {
 +		if (block_size == 0) {
 +			bytes_to_write = size;
 +		} else {
 +			/* We're sparsifying the file. */
 +			const char *p, *end;
 +			int64_t block_end;
 +
 +			/* Skip leading zero bytes. */
 +			for (p = buff, end = buff + size; p < end; ++p) {
 +				if (*p != '\0')
 +					break;
 +			}
 +			a->offset += p - buff;
 +			size -= p - buff;
 +			buff = p;
 +			if (size == 0)
 +				break;
 +
 +			/* Calculate next block boundary after offset. */
 +			block_end
 +			    = (a->offset / block_size + 1) * block_size;
 +
 +			/* If the adjusted write would cross block boundary,
 +			 * truncate it to the block boundary. */
 +			bytes_to_write = size;
 +			if (a->offset + bytes_to_write > block_end)
 +				bytes_to_write = block_end - a->offset;
 +		}
 +		/* Seek if necessary to the specified offset. */
 +		if (a->offset != a->fd_offset) {
 +			if (lseek(a->fd, a->offset, SEEK_SET) < 0) {
 +				archive_set_error(&a->archive, errno,
 +				    "Seek failed");
 +				return (ARCHIVE_FATAL);
 +			}
 +			a->fd_offset = a->offset;
 +		}
 +		bytes_written = write(a->fd, buff, bytes_to_write);
 +		if (bytes_written < 0) {
 +			archive_set_error(&a->archive, errno, "Write failed");
 +			return (ARCHIVE_WARN);
 +		}
 +		buff += bytes_written;
 +		size -= bytes_written;
 +		a->total_bytes_written += bytes_written;
 +		a->offset += bytes_written;
 +		a->fd_offset = a->offset;
 +	}
 +	return (start_size - size);
 +}
 +
 +#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\
 +	&& defined(HAVE_ZLIB_H)
 +
 +/*
 + * Set UF_COMPRESSED file flag.
 + * This have to be called after hfs_write_decmpfs() because if the
 + * file does not have "com.apple.decmpfs" xattr the flag is ignored.
 + */
 +static int
 +hfs_set_compressed_fflag(struct archive_write_disk *a)
 +{
 +	int r;
 +
 +	if ((r = lazy_stat(a)) != ARCHIVE_OK)
 +		return (r);
 +
 +	a->st.st_flags |= UF_COMPRESSED;
 +	if (fchflags(a->fd, a->st.st_flags) != 0) {
 +		archive_set_error(&a->archive, errno,
 +		    "Failed to set UF_COMPRESSED file flag");
 +		return (ARCHIVE_WARN);
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * HFS+ Compression decmpfs
 + *
 + *     +------------------------------+ +0
 + *     |      Magic(LE 4 bytes)       |
 + *     +------------------------------+
 + *     |      Type(LE 4 bytes)        |
 + *     +------------------------------+
 + *     | Uncompressed size(LE 8 bytes)|
 + *     +------------------------------+ +16
 + *     |                              |
 + *     |       Compressed data        |
 + *     |  (Placed only if Type == 3)  |
 + *     |                              |
 + *     +------------------------------+  +3802 = MAX_DECMPFS_XATTR_SIZE
 + *
 + *  Type is 3: decmpfs has compressed data.
 + *  Type is 4: Resource Fork has compressed data.
 + */
 +/*
 + * Write "com.apple.decmpfs"
 + */
 +static int
 +hfs_write_decmpfs(struct archive_write_disk *a)
 +{
 +	int r;
 +	uint32_t compression_type;
 +
 +	r = fsetxattr(a->fd, DECMPFS_XATTR_NAME, a->decmpfs_header_p,
 +	    a->decmpfs_attr_size, 0, 0);
 +	if (r < 0) {
 +		archive_set_error(&a->archive, errno,
 +		    "Cannot restore xattr:%s", DECMPFS_XATTR_NAME);
 +		compression_type = archive_le32dec(
 +		    &a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE]);
 +		if (compression_type == CMP_RESOURCE_FORK)
 +			fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME,
 +			    XATTR_SHOWCOMPRESSION);
 +		return (ARCHIVE_WARN);
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * HFS+ Compression Resource Fork
 + *
 + *     +-----------------------------+
 + *     |     Header(260 bytes)       |
 + *     +-----------------------------+
 + *     |   Block count(LE 4 bytes)   |
 + *     +-----------------------------+  --+
 + * +-- |     Offset (LE 4 bytes)     |    |
 + * |   | [distance from Block count] |    | Block 0
 + * |   +-----------------------------+    |
 + * |   | Compressed size(LE 4 bytes) |    |
 + * |   +-----------------------------+  --+
 + * |   |                             |
 + * |   |      ..................     |
 + * |   |                             |
 + * |   +-----------------------------+  --+
 + * |   |     Offset (LE 4 bytes)     |    |
 + * |   +-----------------------------+    | Block (Block count -1)
 + * |   | Compressed size(LE 4 bytes) |    |
 + * +-> +-----------------------------+  --+
 + *     |   Compressed data(n bytes)  |  Block 0
 + *     +-----------------------------+
 + *     |                             |
 + *     |      ..................     |
 + *     |                             |
 + *     +-----------------------------+
 + *     |   Compressed data(n bytes)  |  Block (Block count -1)
 + *     +-----------------------------+
 + *     |      Footer(50 bytes)       |
 + *     +-----------------------------+
 + *
 + */
 +/*
 + * Write the header of "com.apple.ResourceFork"
 + */
 +static int
 +hfs_write_resource_fork(struct archive_write_disk *a, unsigned char *buff,
 +    size_t bytes, uint32_t position)
 +{
 +	int ret;
 +
 +	ret = fsetxattr(a->fd, XATTR_RESOURCEFORK_NAME, buff, bytes,
 +	    position, a->rsrc_xattr_options);
 +	if (ret < 0) {
 +		archive_set_error(&a->archive, errno,
 +		    "Cannot restore xattr: %s at %u pos %u bytes",
 +		    XATTR_RESOURCEFORK_NAME,
 +		    (unsigned)position,
 +		    (unsigned)bytes);
 +		return (ARCHIVE_WARN);
 +	}
 +	a->rsrc_xattr_options &= ~XATTR_CREATE;
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +hfs_write_compressed_data(struct archive_write_disk *a, size_t bytes_compressed)
 +{
 +	int ret;
 +
 +	ret = hfs_write_resource_fork(a, a->compressed_buffer,
 +	    bytes_compressed, a->compressed_rsrc_position);
 +	if (ret == ARCHIVE_OK)
 +		a->compressed_rsrc_position += bytes_compressed;
 +	return (ret);
 +}
 +
 +static int
 +hfs_write_resource_fork_header(struct archive_write_disk *a)
 +{
 +	unsigned char *buff;
 +	uint32_t rsrc_bytes;
 +	uint32_t rsrc_header_bytes;
 +
 +	/*
 +	 * Write resource fork header + block info.
 +	 */
 +	buff = a->resource_fork;
 +	rsrc_bytes = a->compressed_rsrc_position - RSRC_F_SIZE;
 +	rsrc_header_bytes =
 +		RSRC_H_SIZE +		/* Header base size. */
 +		4 +			/* Block count. */
 +		(a->decmpfs_block_count * 8);/* Block info */
 +	archive_be32enc(buff, 0x100);
 +	archive_be32enc(buff + 4, rsrc_bytes);
 +	archive_be32enc(buff + 8, rsrc_bytes - 256);
 +	archive_be32enc(buff + 12, 0x32);
 +	memset(buff + 16, 0, 240);
 +	archive_be32enc(buff + 256, rsrc_bytes - 260);
 +	return hfs_write_resource_fork(a, buff, rsrc_header_bytes, 0);
 +}
 +
 +static size_t
 +hfs_set_resource_fork_footer(unsigned char *buff, size_t buff_size)
 +{
 +	static const char rsrc_footer[RSRC_F_SIZE] = {
 +		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 +		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 +		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 +		0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c',  'm',
 +		'p', 'f',   0x00, 0x00, 0x00, 0x0a, 0x00, 0x01,
 +		0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 +		0x00, 0x00
 +	};
 +	if (buff_size < sizeof(rsrc_footer))
 +		return (0);
 +	memcpy(buff, rsrc_footer, sizeof(rsrc_footer));
 +	return (sizeof(rsrc_footer));
 +}
 +
 +static int
 +hfs_reset_compressor(struct archive_write_disk *a)
 +{
 +	int ret;
 +
 +	if (a->stream_valid)
 +		ret = deflateReset(&a->stream);
 +	else
 +		ret = deflateInit(&a->stream, a->decmpfs_compression_level);
 +
 +	if (ret != Z_OK) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Failed to initialize compressor");
 +		return (ARCHIVE_FATAL);
 +	} else
 +		a->stream_valid = 1;
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +hfs_decompress(struct archive_write_disk *a)
 +{
 +	uint32_t *block_info;
 +	unsigned int block_count;
 +	uint32_t data_pos, data_size;
 +	ssize_t r;
 +	ssize_t bytes_written, bytes_to_write;
 +	unsigned char *b;
 +
 +	block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE);
 +	block_count = archive_le32dec(block_info++);
 +	while (block_count--) {
 +		data_pos = RSRC_H_SIZE + archive_le32dec(block_info++);
 +		data_size = archive_le32dec(block_info++);
 +		r = fgetxattr(a->fd, XATTR_RESOURCEFORK_NAME,
 +		    a->compressed_buffer, data_size, data_pos, 0);
 +		if (r != data_size)  {
 +			archive_set_error(&a->archive,
 +			    (r < 0)?errno:ARCHIVE_ERRNO_MISC,
 +			    "Failed to read resource fork");
 +			return (ARCHIVE_WARN);
 +		}
 +		if (a->compressed_buffer[0] == 0xff) {
 +			bytes_to_write = data_size -1;
 +			b = a->compressed_buffer + 1;
 +		} else {
 +			uLong dest_len = MAX_DECMPFS_BLOCK_SIZE;
 +			int zr;
 +
 +			zr = uncompress((Bytef *)a->uncompressed_buffer,
 +			    &dest_len, a->compressed_buffer, data_size);
 +			if (zr != Z_OK) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Failed to decompress resource fork");
 +				return (ARCHIVE_WARN);
 +			}
 +			bytes_to_write = dest_len;
 +			b = (unsigned char *)a->uncompressed_buffer;
 +		}
 +		do {
 +			bytes_written = write(a->fd, b, bytes_to_write);
 +			if (bytes_written < 0) {
 +				archive_set_error(&a->archive, errno,
 +				    "Write failed");
 +				return (ARCHIVE_WARN);
 +			}
 +			bytes_to_write -= bytes_written;
 +			b += bytes_written;
 +		} while (bytes_to_write > 0);
 +	}
 +	r = fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, 0);
 +	if (r == -1)  {
 +		archive_set_error(&a->archive, errno,
 +		    "Failed to remove resource fork");
 +		return (ARCHIVE_WARN);
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +hfs_drive_compressor(struct archive_write_disk *a, const char *buff,
 +    size_t size)
 +{
 +	unsigned char *buffer_compressed;
 +	size_t bytes_compressed;
 +	size_t bytes_used;
 +	int ret;
 +
 +	ret = hfs_reset_compressor(a);
 +	if (ret != ARCHIVE_OK)
 +		return (ret);
 +
 +	if (a->compressed_buffer == NULL) {
 +		size_t block_size;
 +
 +		block_size = COMPRESSED_W_SIZE + RSRC_F_SIZE +
 +		    + compressBound(MAX_DECMPFS_BLOCK_SIZE);
 +		a->compressed_buffer = malloc(block_size);
 +		if (a->compressed_buffer == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory for Resource Fork");
 +			return (ARCHIVE_FATAL);
 +		}
 +		a->compressed_buffer_size = block_size;
 +		a->compressed_buffer_remaining = block_size;
 +	}
 +
 +	buffer_compressed = a->compressed_buffer +
 +	    a->compressed_buffer_size - a->compressed_buffer_remaining;
 +	a->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff;
 +	a->stream.avail_in = size;
 +	a->stream.next_out = buffer_compressed;
 +	a->stream.avail_out = a->compressed_buffer_remaining;
 +	do {
 +		ret = deflate(&a->stream, Z_FINISH);
 +		switch (ret) {
 +		case Z_OK:
 +		case Z_STREAM_END:
 +			break;
 +		default:
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Failed to compress data");
 +			return (ARCHIVE_FAILED);
 +		}
 +	} while (ret == Z_OK);
 +	bytes_compressed = a->compressed_buffer_remaining - a->stream.avail_out;
 +
 +	/*
 +	 * If the compressed size is larger than the original size,
 +	 * throw away compressed data, use uncompressed data instead.
 +	 */
 +	if (bytes_compressed > size) {
 +		buffer_compressed[0] = 0xFF;/* uncompressed marker. */
 +		memcpy(buffer_compressed + 1, buff, size);
 +		bytes_compressed = size + 1;
 +	}
 +	a->compressed_buffer_remaining -= bytes_compressed;
 +
 +	/*
 +	 * If the compressed size is smaller than MAX_DECMPFS_XATTR_SIZE
 +	 * and the block count in the file is only one, store compressed
 +	 * data to decmpfs xattr instead of the resource fork.
 +	 */
 +	if (a->decmpfs_block_count == 1 &&
 +	    (a->decmpfs_attr_size + bytes_compressed)
 +	      <= MAX_DECMPFS_XATTR_SIZE) {
 +		archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE],
 +		    CMP_XATTR);
 +		memcpy(a->decmpfs_header_p + DECMPFS_HEADER_SIZE,
 +		    buffer_compressed, bytes_compressed);
 +		a->decmpfs_attr_size += bytes_compressed;
 +		a->compressed_buffer_remaining = a->compressed_buffer_size;
 +		/*
 +		 * Finish HFS+ Compression.
 +		 * - Write the decmpfs xattr.
 +		 * - Set the UF_COMPRESSED file flag.
 +		 */
 +		ret = hfs_write_decmpfs(a);
 +		if (ret == ARCHIVE_OK)
 +			ret = hfs_set_compressed_fflag(a);
 +		return (ret);
 +	}
 +
 +	/* Update block info. */
 +	archive_le32enc(a->decmpfs_block_info++,
 +	    a->compressed_rsrc_position_v - RSRC_H_SIZE);
 +	archive_le32enc(a->decmpfs_block_info++, bytes_compressed);
 +	a->compressed_rsrc_position_v += bytes_compressed;
 +
 +	/*
 +	 * Write the compressed data to the resource fork.
 +	 */
 +	bytes_used = a->compressed_buffer_size - a->compressed_buffer_remaining;
 +	while (bytes_used >= COMPRESSED_W_SIZE) {
 +		ret = hfs_write_compressed_data(a, COMPRESSED_W_SIZE);
 +		if (ret != ARCHIVE_OK)
 +			return (ret);
 +		bytes_used -= COMPRESSED_W_SIZE;
 +		if (bytes_used > COMPRESSED_W_SIZE)
 +			memmove(a->compressed_buffer,
 +			    a->compressed_buffer + COMPRESSED_W_SIZE,
 +			    bytes_used);
 +		else
 +			memcpy(a->compressed_buffer,
 +			    a->compressed_buffer + COMPRESSED_W_SIZE,
 +			    bytes_used);
 +	}
 +	a->compressed_buffer_remaining = a->compressed_buffer_size - bytes_used;
 +
 +	/*
 +	 * If the current block is the last block, write the remaining
 +	 * compressed data and the resource fork footer.
 +	 */
 +	if (a->file_remaining_bytes == 0) {
 +		size_t rsrc_size;
 +		int64_t bk;
 +
 +		/* Append the resource footer. */
 +		rsrc_size = hfs_set_resource_fork_footer(
 +		    a->compressed_buffer + bytes_used,
 +		    a->compressed_buffer_remaining);
 +		ret = hfs_write_compressed_data(a, bytes_used + rsrc_size);
 +		a->compressed_buffer_remaining = a->compressed_buffer_size;
 +
 +		/* If the compressed size is not enough smaller than
 +		 * the uncompressed size. cancel HFS+ compression.
 +		 * TODO: study a behavior of ditto utility and improve
 +		 * the condition to fall back into no HFS+ compression. */
 +		bk = HFS_BLOCKS(a->compressed_rsrc_position);
 +		bk += bk >> 7;
 +		if (bk > HFS_BLOCKS(a->filesize))
 +			return hfs_decompress(a);
 +		/*
 +		 * Write the resourcefork header.
 +		 */
 +		if (ret == ARCHIVE_OK)
 +			ret = hfs_write_resource_fork_header(a);
 +		/*
 +		 * Finish HFS+ Compression.
 +		 * - Write the decmpfs xattr.
 +		 * - Set the UF_COMPRESSED file flag.
 +		 */
 +		if (ret == ARCHIVE_OK)
 +			ret = hfs_write_decmpfs(a);
 +		if (ret == ARCHIVE_OK)
 +			ret = hfs_set_compressed_fflag(a);
 +	}
 +	return (ret);
 +}
 +
 +static ssize_t
 +hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff,
 +    size_t size)
 +{
 +	const char *buffer_to_write;
 +	size_t bytes_to_write;
 +	int ret;
 +
 +	if (a->decmpfs_block_count == (unsigned)-1) {
 +		void *new_block;
 +		size_t new_size;
 +		unsigned int block_count;
 +
 +		if (a->decmpfs_header_p == NULL) {
 +			new_block = malloc(MAX_DECMPFS_XATTR_SIZE
 +			    + sizeof(uint32_t));
 +			if (new_block == NULL) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "Can't allocate memory for decmpfs");
 +				return (ARCHIVE_FATAL);
 +			}
 +			a->decmpfs_header_p = new_block;
 +		}
 +		a->decmpfs_attr_size = DECMPFS_HEADER_SIZE;
 +		archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_MAGIC],
 +		    DECMPFS_MAGIC);
 +		archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE],
 +		    CMP_RESOURCE_FORK);
 +		archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE],
 +		    a->filesize);
 +
 +		/* Calculate a block count of the file. */
 +		block_count =
 +		    (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) /
 +			MAX_DECMPFS_BLOCK_SIZE;
 +		/*
 +		 * Allocate buffer for resource fork.
 +		 * Set up related pointers;
 +		 */
 +		new_size =
 +		    RSRC_H_SIZE + /* header */
 +		    4 + /* Block count */
 +		    (block_count * sizeof(uint32_t) * 2) +
 +		    RSRC_F_SIZE; /* footer */
 +		if (new_size > a->resource_fork_allocated_size) {
 +			new_block = realloc(a->resource_fork, new_size);
 +			if (new_block == NULL) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "Can't allocate memory for ResourceFork");
 +				return (ARCHIVE_FATAL);
 +			}
 +			a->resource_fork_allocated_size = new_size;
 +			a->resource_fork = new_block;
 +		}
 +
 +		/* Allocate uncompressed buffer */
 +		if (a->uncompressed_buffer == NULL) {
 +			new_block = malloc(MAX_DECMPFS_BLOCK_SIZE);
 +			if (new_block == NULL) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "Can't allocate memory for decmpfs");
 +				return (ARCHIVE_FATAL);
 +			}
 +			a->uncompressed_buffer = new_block;
 +		}
 +		a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE;
 +		a->file_remaining_bytes = a->filesize;
 +		a->compressed_buffer_remaining = a->compressed_buffer_size;
 +
 +		/*
 +		 * Set up a resource fork.
 +		 */
 +		a->rsrc_xattr_options = XATTR_CREATE;
 +		/* Get the position where we are going to set a bunch
 +		 * of block info. */
 +		a->decmpfs_block_info =
 +		    (uint32_t *)(a->resource_fork + RSRC_H_SIZE);
 +		/* Set the block count to the resource fork. */
 +		archive_le32enc(a->decmpfs_block_info++, block_count);
 +		/* Get the position where we are going to set compressed
 +		 * data. */
 +		a->compressed_rsrc_position =
 +		    RSRC_H_SIZE + 4 + (block_count * 8);
 +		a->compressed_rsrc_position_v = a->compressed_rsrc_position;
 +		a->decmpfs_block_count = block_count;
 +	}
 +
 +	/* Ignore redundant bytes. */
 +	if (a->file_remaining_bytes == 0)
 +		return ((ssize_t)size);
 +
 +	/* Do not overrun a block size. */
 +	if (size > a->block_remaining_bytes)
 +		bytes_to_write = a->block_remaining_bytes;
 +	else
 +		bytes_to_write = size;
 +	/* Do not overrun the file size. */
 +	if (bytes_to_write > a->file_remaining_bytes)
 +		bytes_to_write = a->file_remaining_bytes;
 +
 +	/* For efficiency, if a copy length is full of the uncompressed
 +	 * buffer size, do not copy writing data to it. */
 +	if (bytes_to_write == MAX_DECMPFS_BLOCK_SIZE)
 +		buffer_to_write = buff;
 +	else {
 +		memcpy(a->uncompressed_buffer +
 +		    MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes,
 +		    buff, bytes_to_write);
 +		buffer_to_write = a->uncompressed_buffer;
 +	}
 +	a->block_remaining_bytes -= bytes_to_write;
 +	a->file_remaining_bytes -= bytes_to_write;
 +
 +	if (a->block_remaining_bytes == 0 || a->file_remaining_bytes == 0) {
 +		ret = hfs_drive_compressor(a, buffer_to_write,
 +		    MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes);
 +		if (ret < 0)
 +			return (ret);
 +		a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE;
 +	}
 +	/* Ignore redundant bytes. */
 +	if (a->file_remaining_bytes == 0)
 +		return ((ssize_t)size);
 +	return (bytes_to_write);
 +}
 +
 +static ssize_t
 +hfs_write_data_block(struct archive_write_disk *a, const char *buff,
 +    size_t size)
 +{
 +	uint64_t start_size = size;
 +	ssize_t bytes_written = 0;
 +	ssize_t bytes_to_write;
 +
 +	if (size == 0)
 +		return (ARCHIVE_OK);
 +
 +	if (a->filesize == 0 || a->fd < 0) {
 +		archive_set_error(&a->archive, 0,
 +		    "Attempt to write to an empty file");
 +		return (ARCHIVE_WARN);
 +	}
 +
 +	/* If this write would run beyond the file size, truncate it. */
 +	if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)
 +		start_size = size = (size_t)(a->filesize - a->offset);
 +
 +	/* Write the data. */
 +	while (size > 0) {
 +		bytes_to_write = size;
 +		/* Seek if necessary to the specified offset. */
 +		if (a->offset < a->fd_offset) {
 +			/* Can't support backward move. */
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Seek failed");
 +			return (ARCHIVE_FATAL);
 +		} else if (a->offset > a->fd_offset) {
 +			int64_t skip = a->offset - a->fd_offset;
 +			char nullblock[1024];
 +
 +			memset(nullblock, 0, sizeof(nullblock));
 +			while (skip > 0) {
 +				if (skip > (int64_t)sizeof(nullblock))
 +					bytes_written = hfs_write_decmpfs_block(
 +					    a, nullblock, sizeof(nullblock));
 +				else
 +					bytes_written = hfs_write_decmpfs_block(
 +					    a, nullblock, skip);
 +				if (bytes_written < 0) {
 +					archive_set_error(&a->archive, errno,
 +					    "Write failed");
 +					return (ARCHIVE_WARN);
 +				}
 +				skip -= bytes_written;
 +			}
 +
 +			a->fd_offset = a->offset;
 +		}
 +		bytes_written =
 +		    hfs_write_decmpfs_block(a, buff, bytes_to_write);
 +		if (bytes_written < 0)
 +			return (bytes_written);
 +		buff += bytes_written;
 +		size -= bytes_written;
 +		a->total_bytes_written += bytes_written;
 +		a->offset += bytes_written;
 +		a->fd_offset = a->offset;
 +	}
 +	return (start_size - size);
 +}
 +#else
 +static ssize_t
 +hfs_write_data_block(struct archive_write_disk *a, const char *buff,
 +    size_t size)
 +{
 +	return (write_data_block(a, buff, size));
 +}
 +#endif
 +
 +static ssize_t
 +_archive_write_disk_data_block(struct archive *_a,
 +    const void *buff, size_t size, int64_t offset)
 +{
 +	struct archive_write_disk *a = (struct archive_write_disk *)_a;
 +	ssize_t r;
 +
 +	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
 +	    ARCHIVE_STATE_DATA, "archive_write_data_block");
 +
 +	a->offset = offset;
 +	if (a->todo & TODO_HFS_COMPRESSION)
 +		r = hfs_write_data_block(a, buff, size);
 +	else
 +		r = write_data_block(a, buff, size);
 +	if (r < ARCHIVE_OK)
 +		return (r);
 +	if ((size_t)r < size) {
 +		archive_set_error(&a->archive, 0,
 +		    "Too much data: Truncating file at %ju bytes",
 +		    (uintmax_t)a->filesize);
 +		return (ARCHIVE_WARN);
 +	}
 +#if ARCHIVE_VERSION_NUMBER < 3999000
 +	return (ARCHIVE_OK);
 +#else
 +	return (size);
 +#endif
 +}
 +
 +static ssize_t
 +_archive_write_disk_data(struct archive *_a, const void *buff, size_t size)
 +{
 +	struct archive_write_disk *a = (struct archive_write_disk *)_a;
 +
 +	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
 +	    ARCHIVE_STATE_DATA, "archive_write_data");
 +
 +	if (a->todo & TODO_HFS_COMPRESSION)
 +		return (hfs_write_data_block(a, buff, size));
 +	return (write_data_block(a, buff, size));
 +}
 +
 +static int
 +_archive_write_disk_finish_entry(struct archive *_a)
 +{
 +	struct archive_write_disk *a = (struct archive_write_disk *)_a;
 +	int ret = ARCHIVE_OK;
 +
 +	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
 +	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
 +	    "archive_write_finish_entry");
 +	if (a->archive.state & ARCHIVE_STATE_HEADER)
 +		return (ARCHIVE_OK);
 +	archive_clear_error(&a->archive);
 +
 +	/* Pad or truncate file to the right size. */
 +	if (a->fd < 0) {
 +		/* There's no file. */
 +	} else if (a->filesize < 0) {
 +		/* File size is unknown, so we can't set the size. */
 +	} else if (a->fd_offset == a->filesize) {
 +		/* Last write ended at exactly the filesize; we're done. */
 +		/* Hopefully, this is the common case. */
 +#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
 +	} else if (a->todo & TODO_HFS_COMPRESSION) {
 +		char null_d[1024];
 +		ssize_t r;
 +
 +		if (a->file_remaining_bytes)
 +			memset(null_d, 0, sizeof(null_d));
 +		while (a->file_remaining_bytes) {
 +			if (a->file_remaining_bytes > sizeof(null_d))
 +				r = hfs_write_data_block(
 +				    a, null_d, sizeof(null_d));
 +			else
 +				r = hfs_write_data_block(
 +				    a, null_d, a->file_remaining_bytes);
 +			if (r < 0)
 +				return ((int)r);
 +		}
 +#endif
 +	} else {
 +#if HAVE_FTRUNCATE
 +		if (ftruncate(a->fd, a->filesize) == -1 &&
 +		    a->filesize == 0) {
 +			archive_set_error(&a->archive, errno,
 +			    "File size could not be restored");
 +			return (ARCHIVE_FAILED);
 +		}
 +#endif
 +		/*
 +		 * Not all platforms implement the XSI option to
 +		 * extend files via ftruncate.  Stat() the file again
 +		 * to see what happened.
 +		 */
 +		a->pst = NULL;
 +		if ((ret = lazy_stat(a)) != ARCHIVE_OK)
 +			return (ret);
 +		/* We can use lseek()/write() to extend the file if
 +		 * ftruncate didn't work or isn't available. */
 +		if (a->st.st_size < a->filesize) {
 +			const char nul = '\0';
 +			if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) {
 +				archive_set_error(&a->archive, errno,
 +				    "Seek failed");
 +				return (ARCHIVE_FATAL);
 +			}
 +			if (write(a->fd, &nul, 1) < 0) {
 +				archive_set_error(&a->archive, errno,
 +				    "Write to restore size failed");
 +				return (ARCHIVE_FATAL);
 +			}
 +			a->pst = NULL;
 +		}
 +	}
 +
 +	/* Restore metadata. */
 +
 +	/*
 +	 * This is specific to Mac OS X.
 +	 * If the current file is an AppleDouble file, it should be
 +	 * linked with the data fork file and remove it.
 +	 */
 +	if (a->todo & TODO_APPLEDOUBLE) {
 +		int r2 = fixup_appledouble(a, a->name);
 +		if (r2 == ARCHIVE_EOF) {
 +			/* The current file has been successfully linked
 +			 * with the data fork file and removed. So there
 +			 * is nothing to do on the current file.  */
 +			goto finish_metadata;
 +		}
 +		if (r2 < ret) ret = r2;
 +	}
 +
 +	/*
 +	 * Look up the "real" UID only if we're going to need it.
 +	 * TODO: the TODO_SGID condition can be dropped here, can't it?
 +	 */
 +	if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) {
 +		a->uid = archive_write_disk_uid(&a->archive,
 +		    archive_entry_uname(a->entry),
 +		    archive_entry_uid(a->entry));
 +	}
 +	/* Look up the "real" GID only if we're going to need it. */
 +	/* TODO: the TODO_SUID condition can be dropped here, can't it? */
 +	if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) {
 +		a->gid = archive_write_disk_gid(&a->archive,
 +		    archive_entry_gname(a->entry),
 +		    archive_entry_gid(a->entry));
 +	 }
 +
 +	/*
 +	 * Restore ownership before set_mode tries to restore suid/sgid
 +	 * bits.  If we set the owner, we know what it is and can skip
 +	 * a stat() call to examine the ownership of the file on disk.
 +	 */
 +	if (a->todo & TODO_OWNER) {
 +		int r2 = set_ownership(a);
 +		if (r2 < ret) ret = r2;
 +	}
 +
 +	/*
 +	 * 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;
 +	}
 +
 +	/*
 +	 * 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;
- #ifdef HAVE_DARWIN_ACL
- 		/*
- 		 * On Mac OS, platform ACLs are stored also in mac_metadata by
- 		 * the operating system. If mac_metadata is present it takes
- 		 * precedence and we skip extracting libarchive NFSv4 ACLs
- 		 */
- 		const void *metadata;
- 		size_t metadata_size;
- 		metadata = archive_entry_mac_metadata(a->entry, &metadata_size);
- 		if ((a->todo & TODO_MAC_METADATA) == 0 ||
- 		    metadata == NULL || metadata_size == 0) {
- #endif
 +		r2 = archive_write_disk_set_acls(&a->archive, a->fd,
 +		    archive_entry_pathname(a->entry),
- 		    archive_entry_acl(a->entry));
++		    archive_entry_acl(a->entry),
++		    archive_entry_mode(a->entry));
 +		if (r2 < ret) ret = r2;
- #ifdef HAVE_DARWIN_ACL
- 		}
- #endif
 +	}
 +
 +finish_metadata:
 +	/* If there's an fd, we can close it now. */
 +	if (a->fd >= 0) {
 +		close(a->fd);
 +		a->fd = -1;
 +	}
 +	/* If there's an entry, we can release it now. */
 +	if (a->entry) {
 +		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,
 +    int64_t (*lookup_gid)(void *private, const char *gname, 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, 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, int64_t id)
 +{
 +	struct archive_write_disk *a = (struct archive_write_disk *)_a;
 +	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
 +	    ARCHIVE_STATE_ANY, "archive_write_disk_uid");
 +	if (a->lookup_uid)
 +		return (a->lookup_uid)(a->lookup_uid_data, name, id);
 +	return (id);
 +}
 +
 +/*
 + * Create a new archive_write_disk object and initialize it with global state.
 + */
 +struct archive *
 +archive_write_disk_new(void)
 +{
 +	struct archive_write_disk *a;
 +
 +	a = (struct archive_write_disk *)calloc(1, sizeof(*a));
 +	if (a == NULL)
 +		return (NULL);
 +	a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC;
 +	/* We're ready to write a header immediately. */
 +	a->archive.state = ARCHIVE_STATE_HEADER;
 +	a->archive.vtable = archive_write_disk_vtable();
 +	a->start_time = time(NULL);
 +	/* Query and restore the umask. */
 +	umask(a->user_umask = umask(0));
 +#ifdef HAVE_GETEUID
 +	a->user_uid = geteuid();
 +#endif /* HAVE_GETEUID */
 +	if (archive_string_ensure(&a->path_safe, 512) == NULL) {
 +		free(a);
 +		return (NULL);
 +	}
 +#ifdef HAVE_ZLIB_H
 +	a->decmpfs_compression_level = 5;
 +#endif
 +	return (&a->archive);
 +}
 +
 +
 +/*
 + * If pathname is longer than PATH_MAX, chdir to a suitable
 + * intermediate dir and edit the path down to a shorter suffix.  Note
 + * that this routine never returns an error; if the chdir() attempt
 + * fails for any reason, we just go ahead with the long pathname.  The
 + * object creation is likely to fail, but any error will get handled
 + * at that time.
 + */
 +#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
 +static void
 +edit_deep_directories(struct archive_write_disk *a)
 +{
 +	int ret;
 +	char *tail = a->name;
 +
 +	/* If path is short, avoid the open() below. */
 +	if (strlen(tail) < PATH_MAX)
 +		return;
 +
 +	/* Try to record our starting dir. */
 +	a->restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC);
 +	__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. */
 +		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 = stat(a->name, &a->st);
 +		/*
 +		 * If it's not a dir (or it's a broken symlink),
 +		 * then don't follow it.
 +		 */
 +		if (r != 0 || !S_ISDIR(a->mode))
 +			r = lstat(a->name, &a->st);
 +		if (r != 0) {
 +			archive_set_error(&a->archive, errno,
 +			    "Can't stat existing object");
 +			return (ARCHIVE_FAILED);
 +		}
 +
 +		/*
 +		 * NO_OVERWRITE_NEWER doesn't apply to directories.
 +		 */
 +		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER)
 +		    &&  !S_ISDIR(a->st.st_mode)) {
 +			if (!older(&(a->st), a->entry)) {
 +				archive_entry_unset_size(a->entry);
 +				return (ARCHIVE_OK);
 +			}
 +		}
 +
 +		/* If it's our archive, we're done. */
 +		if (a->skip_file_set &&
 +		    a->st.st_dev == (dev_t)a->skip_file_dev &&
 +		    a->st.st_ino == (ino_t)a->skip_file_ino) {
 +			archive_set_error(&a->archive, 0,
 +			    "Refusing to overwrite archive");
 +			return (ARCHIVE_FAILED);
 +		}
 +
 +		if (!S_ISDIR(a->st.st_mode)) {
 +			/* A non-dir is in the way, unlink it. */
 +			if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
 +				(void)clear_nochange_fflags(a);
 +			if (unlink(a->name) != 0) {
 +				archive_set_error(&a->archive, errno,
 +				    "Can't unlink already-existing object");
 +				return (ARCHIVE_FAILED);
 +			}
 +			a->pst = NULL;
 +			/* Try again. */
 +			en = create_filesystem_object(a);
 +		} else if (!S_ISDIR(a->mode)) {
 +			/* A dir is in the way of a non-dir, rmdir it. */
 +			if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
 +				(void)clear_nochange_fflags(a);
 +			if (rmdir(a->name) != 0) {
 +				archive_set_error(&a->archive, errno,
 +				    "Can't replace existing directory with non-directory");
 +				return (ARCHIVE_FAILED);
 +			}
 +			/* Try again. */
 +			en = create_filesystem_object(a);
 +		} else {
 +			/*
 +			 * There's a dir in the way of a dir.  Don't
 +			 * waste time with rmdir()/mkdir(), just fix
 +			 * up the permissions on the existing dir.
 +			 * Note that we don't change perms on existing
 +			 * dirs unless _EXTRACT_PERM is specified.
 +			 */
 +			if ((a->mode != a->st.st_mode)
 +			    && (a->todo & TODO_MODE_FORCE))
 +				a->deferred |= (a->todo & TODO_MODE);
 +			/* Ownership doesn't need deferred fixup. */
 +			en = 0; /* Forget the EEXIST. */
 +		}
 +	}
 +
 +	if (en) {
 +		/* Everything failed; give up here. */
 +		if ((&a->archive)->error == NULL)
 +			archive_set_error(&a->archive, en, "Can't create '%s'",
 +			    a->name);
 +		return (ARCHIVE_FAILED);
 +	}
 +
 +	a->pst = NULL; /* Cached stat data no longer valid. */
 +	return (ret);
 +}
 +
 +/*
 + * Returns 0 if creation succeeds, or else returns errno value from
 + * the failed system call.   Note:  This function should only ever perform
 + * a single system call.
 + */
 +static int
 +create_filesystem_object(struct archive_write_disk *a)
 +{
 +	/* Create the entry. */
 +	const char *linkname;
 +	mode_t final_mode, mode;
 +	int r;
 +	/* these for check_symlinks_fsobj */
 +	char *linkname_copy;	/* non-const copy of linkname */
 +	struct stat st;
 +	struct archive_string error_string;
 +	int error_number;
 +
 +	/* We identify hard/symlinks according to the link names. */
 +	/* Since link(2) and symlink(2) don't handle modes, we're done here. */
 +	linkname = archive_entry_hardlink(a->entry);
 +	if (linkname != NULL) {
 +#if !HAVE_LINK
 +		return (EPERM);
 +#else
 +		archive_string_init(&error_string);
 +		linkname_copy = strdup(linkname);
 +		if (linkname_copy == NULL) {
 +		    return (EPERM);
 +		}
 +		/*
 +		 * TODO: consider using the cleaned-up path as the link
 +		 * target?
 +		 */
 +		r = cleanup_pathname_fsobj(linkname_copy, &error_number,
 +		    &error_string, a->flags);
 +		if (r != ARCHIVE_OK) {
 +			archive_set_error(&a->archive, error_number, "%s",
 +			    error_string.s);
 +			free(linkname_copy);
 +			archive_string_free(&error_string);
 +			/*
 +			 * EPERM is more appropriate than error_number for our
 +			 * callers
 +			 */
 +			return (EPERM);
 +		}
 +		r = check_symlinks_fsobj(linkname_copy, &error_number,
 +		    &error_string, a->flags);
 +		if (r != ARCHIVE_OK) {
 +			archive_set_error(&a->archive, error_number, "%s",
 +			    error_string.s);
 +			free(linkname_copy);
 +			archive_string_free(&error_string);
 +			/*
 +			 * EPERM is more appropriate than error_number for our
 +			 * callers
 +			 */
 +			return (EPERM);
 +		}
 +		free(linkname_copy);
 +		archive_string_free(&error_string);
 +		r = link(linkname, a->name) ? errno : 0;
 +		/*
 +		 * New cpio and pax formats allow hardlink entries
 +		 * to carry data, so we may have to open the file
 +		 * for hardlink entries.
 +		 *
 +		 * If the hardlink was successfully created and
 +		 * the archive doesn't have carry data for it,
 +		 * consider it to be non-authoritative for meta data.
 +		 * This is consistent with GNU tar and BSD pax.
 +		 * If the hardlink does carry data, let the last
 +		 * archive entry decide ownership.
 +		 */
 +		if (r == 0 && a->filesize <= 0) {
 +			a->todo = 0;
 +			a->deferred = 0;
 +		} else if (r == 0 && a->filesize > 0) {
 +#ifdef HAVE_LSTAT
 +			r = lstat(a->name, &st);
 +#else
 +			r = 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
 +		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;
 +
 +	switch (a->mode & AE_IFMT) {
 +	default:
 +		/* POSIX requires that we fall through here. */
 +		/* FALLTHROUGH */
 +	case AE_IFREG:
 +		a->fd = open(a->name,
 +		    O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode);
 +		__archive_ensure_cloexec_flag(a->fd);
 +		r = (a->fd < 0);
 +		break;
 +	case AE_IFCHR:
 +#ifdef HAVE_MKNOD
 +		/* Note: we use AE_IFCHR for the case label, and
 +		 * S_IFCHR for the mknod() call.  This is correct.  */
 +		r = mknod(a->name, mode | S_IFCHR,
 +		    archive_entry_rdev(a->entry));
 +		break;
 +#else
 +		/* TODO: Find a better way to warn about our inability
 +		 * to restore a char device node. */
 +		return (EINVAL);
 +#endif /* HAVE_MKNOD */
 +	case AE_IFBLK:
 +#ifdef HAVE_MKNOD
 +		r = mknod(a->name, mode | S_IFBLK,
 +		    archive_entry_rdev(a->entry));
 +		break;
 +#else
 +		/* TODO: Find a better way to warn about our inability
 +		 * to restore a block device node. */
 +		return (EINVAL);
 +#endif /* HAVE_MKNOD */
 +	case AE_IFDIR:
 +		mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
 +		r = mkdir(a->name, mode);
 +		if (r == 0) {
 +			/* Defer setting dir times. */
 +			a->deferred |= (a->todo & TODO_TIMES);
 +			a->todo &= ~TODO_TIMES;
 +			/* Never use an immediate chmod(). */
 +			/* We can't avoid the chmod() entirely if EXTRACT_PERM
 +			 * because of SysV SGID inheritance. */
 +			if ((mode != final_mode)
 +			    || (a->flags & ARCHIVE_EXTRACT_PERM))
 +				a->deferred |= (a->todo & TODO_MODE);
 +			a->todo &= ~TODO_MODE;
 +		}
 +		break;
 +	case AE_IFIFO:
 +#ifdef HAVE_MKFIFO
 +		r = mkfifo(a->name, mode);
 +		break;
 +#else
 +		/* TODO: Find a better way to warn about our inability
 +		 * to restore a fifo. */
 +		return (EINVAL);
 +#endif /* HAVE_MKFIFO */
 +	}
 +
 +	/* All the system calls above set errno on failure. */
 +	if (r)
 +		return (errno);
 +
 +	/* If we managed to set the final mode, we've avoided a chmod(). */
 +	if (mode == final_mode)
 +		a->todo &= ~TODO_MODE;
 +	return (0);
 +}
 +
 +/*
 + * Cleanup function for archive_extract.  Mostly, this involves processing
 + * the fixup list, which is used to address a number of problems:
 + *   * Dir permissions might prevent us from restoring a file in that
 + *     dir, so we restore the dir with minimum 0700 permissions first,
 + *     then correct the mode at the end.
 + *   * Similarly, the act of restoring a file touches the directory
 + *     and changes the timestamp on the dir, so we have to touch-up dir
 + *     timestamps at the end as well.
 + *   * Some file flags can interfere with the restore by, for example,
 + *     preventing the creation of hardlinks to those files.
 + *   * Mac OS extended metadata includes ACLs, so must be deferred on dirs.
 + *
 + * Note that tar/cpio do not require that archives be in a particular
 + * order; there is no way to know when the last file has been restored
 + * within a directory, so there's no way to optimize the memory usage
 + * here by fixing up the directory any earlier than the
 + * end-of-archive.
 + *
 + * XXX TODO: Directory ACLs should be restored here, for the same
 + * reason we set directory perms here. XXX
 + */
 +static int
 +_archive_write_disk_close(struct archive *_a)
 +{
 +	struct archive_write_disk *a = (struct archive_write_disk *)_a;
 +	struct fixup_entry *next, *p;
 +	int 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, -1, 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)
 +			chmod(p->name, p->mode);
 +		if (p->fixup & TODO_ACLS)
- #ifdef HAVE_DARWIN_ACL
- 			if ((p->fixup & TODO_MAC_METADATA) == 0 ||
- 			    p->mac_metadata == NULL ||
- 			    p->mac_metadata_size == 0)
- #endif
- 				archive_write_disk_set_acls(&a->archive,
- 				    -1, p->name, &p->acl);
++			archive_write_disk_set_acls(&a->archive, -1, p->name,
++			    &p->acl, p->mode);
 +		if (p->fixup & TODO_FFLAGS)
 +			set_fflags_platform(a, -1, p->name,
 +			    p->mode, p->fflags_set, 0);
 +		if (p->fixup & TODO_MAC_METADATA)
 +			set_mac_metadata(a, p->name, p->mac_metadata,
 +					 p->mac_metadata_size);
 +		next = p->next;
 +		archive_acl_clear(&p->acl);
 +		free(p->mac_metadata);
 +		free(p->name);
 +		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);
 +	if (a->entry)
 +		archive_entry_free(a->entry);
 +	archive_string_free(&a->_name_data);
 +	archive_string_free(&a->archive.error_string);
 +	archive_string_free(&a->path_safe);
 +	a->archive.magic = 0;
 +	__archive_clean(&a->archive);
 +	free(a->decmpfs_header_p);
 +	free(a->resource_fork);
 +	free(a->compressed_buffer);
 +	free(a->uncompressed_buffer);
 +#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\
 +	&& defined(HAVE_ZLIB_H)
 +	if (a->stream_valid) {
 +		switch (deflateEnd(&a->stream)) {
 +		case Z_OK:
 +			break;
 +		default:
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Failed to clean up compressor");
 +			ret = ARCHIVE_FATAL;
 +			break;
 +		}
 +	}
 +#endif
 +	free(a);
 +	return (ret);
 +}
 +
 +/*
 + * Simple O(n log n) merge sort to order the fixup list.  In
 + * particular, we want to restore dir timestamps depth-first.
 + */
 +static struct fixup_entry *
 +sort_dir_list(struct fixup_entry *p)
 +{
 +	struct fixup_entry *a, *b, *t;
 +
 +	if (p == NULL)
 +		return (NULL);
 +	/* A one-item list is already sorted. */
 +	if (p->next == NULL)
 +		return (p);
 +
 +	/* Step 1: split the list. */
 +	t = p;
 +	a = p->next->next;
 +	while (a != NULL) {
 +		/* Step a twice, t once. */
 +		a = a->next;
 +		if (a != NULL)
 +			a = a->next;
 +		t = t->next;
 +	}
 +	/* Now, t is at the mid-point, so break the list here. */
 +	b = t->next;
 +	t->next = NULL;
 +	a = p;
 +
 +	/* Step 2: Recursively sort the two sub-lists. */
 +	a = sort_dir_list(a);
 +	b = sort_dir_list(b);
 +
 +	/* Step 3: Merge the returned lists. */
 +	/* Pick the first element for the merged list. */
 +	if (strcmp(a->name, b->name) > 0) {
 +		t = p = a;
 +		a = a->next;
 +	} else {
 +		t = p = b;
 +		b = b->next;
 +	}
 +
 +	/* Always put the later element on the list first. */
 +	while (a != NULL && b != NULL) {
 +		if (strcmp(a->name, b->name) > 0) {
 +			t->next = a;
 +			a = a->next;
 +		} else {
 +			t->next = b;
 +			b = b->next;
 +		}
 +		t = t->next;
 +	}
 +
 +	/* Only one list is non-empty, so just splice it on. */
 +	if (a != NULL)
 +		t->next = a;
 +	if (b != NULL)
 +		t->next = b;
 +
 +	return (p);
 +}
 +
 +/*
 + * Returns a new, initialized fixup entry.
 + *
 + * TODO: Reduce the memory requirements for this list by using a tree
 + * structure rather than a simple list of names.
 + */
 +static struct fixup_entry *
 +new_fixup(struct archive_write_disk *a, const char *pathname)
 +{
 +	struct fixup_entry *fe;
 +
 +	fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry));
 +	if (fe == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate memory for a fixup");
 +		return (NULL);
 +	}
 +	fe->next = a->fixup_list;
 +	a->fixup_list = fe;
 +	fe->fixup = 0;
 +	fe->name = strdup(pathname);
 +	return (fe);
 +}
 +
 +/*
 + * Returns a fixup structure for the current entry.
 + */
 +static struct fixup_entry *
 +current_fixup(struct archive_write_disk *a, const char *pathname)
 +{
 +	if (a->current_fixup == NULL)
 +		a->current_fixup = new_fixup(a, pathname);
 +	return (a->current_fixup);
 +}
 +
 +/* Error helper for new *_fsobj functions */
 +static void
 +fsobj_error(int *a_eno, struct archive_string *a_estr,
 +    int err, const char *errstr, const char *path)
 +{
 +	if (a_eno)
 +		*a_eno = err;
 +	if (a_estr)
- 		archive_string_sprintf(a_estr, errstr, path);
++		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.
 + */
 +/* TODO: Extend this to support symlinks on Windows Vista and later. */
 +
 +/*
 + * Checks the given path to see if any elements along it are symlinks.  Returns
 + * ARCHIVE_OK if there are none, otherwise puts an error in errmsg.
 + */
 +static int
 +check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
 +    int flags)
 +{
 +#if !defined(HAVE_LSTAT)
 +	/* Platform doesn't have lstat, so we can't look for symlinks. */
 +	(void)path; /* UNUSED */
 +	(void)error_number; /* UNUSED */
 +	(void)error_string; /* UNUSED */
 +	(void)flags; /* UNUSED */
 +	return (ARCHIVE_OK);
 +#else
 +	int res = ARCHIVE_OK;
 +	char *tail;
 +	char *head;
 +	int last;
 +	char c;
 +	int r;
 +	struct stat st;
 +	int restore_pwd;
 +
 +	/* 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
 +	 */
 +	restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC);
 +	__archive_ensure_cloexec_flag(restore_pwd);
 +	if (restore_pwd < 0)
 +		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. */
 +		r = lstat(head, &st);
 +		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 %s", path);
++				    "Could not stat ", path);
 +				res = ARCHIVE_FAILED;
 +				break;
 +			}
 +		} else if (S_ISDIR(st.st_mode)) {
 +			if (!last) {
 +				if (chdir(head) != 0) {
 +					tail[0] = c;
 +					fsobj_error(a_eno, a_estr, errno,
- 					    "Could not chdir %s", path);
++					    "Could not chdir ", path);
 +					res = (ARCHIVE_FATAL);
 +					break;
 +				}
 +				/* Our view is now from inside this dir: */
 +				head = tail + 1;
 +			}
 +		} else if (S_ISLNK(st.st_mode)) {
 +			if (last) {
 +				/*
 +				 * Last element is symlink; remove it
 +				 * so we can overwrite it with the
 +				 * item being extracted.
 +				 */
 +				if (unlink(head)) {
 +					tail[0] = c;
 +					fsobj_error(a_eno, a_estr, errno,
- 					    "Could not remove symlink %s",
++					    "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 %s", path);
++					    "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 (unlink(head) != 0) {
 +					tail[0] = c;
 +					fsobj_error(a_eno, a_estr, 0,
 +					    "Cannot remove intervening "
- 					    "symlink %s", path);
++					    "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.
 +				 */
 +				r = stat(head, &st);
 +				if (r != 0) {
 +					tail[0] = c;
 +					if (errno == ENOENT) {
 +						break;
 +					} else {
 +						fsobj_error(a_eno, a_estr,
 +						    errno,
- 						    "Could not stat %s", path);
++						    "Could not stat ", path);
 +						res = (ARCHIVE_FAILED);
 +						break;
 +					}
 +				} else if (S_ISDIR(st.st_mode)) {
 +					if (chdir(head) != 0) {
 +						tail[0] = c;
 +						fsobj_error(a_eno, a_estr,
 +						    errno,
- 						    "Could not chdir %s", path);
++						    "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 %s", path);
++					    "symlink ", path);
 +					res = ARCHIVE_FAILED;
 +					break;
 +				}
 +			} else {
 +				tail[0] = c;
 +				fsobj_error(a_eno, a_estr, 0,
- 				    "Cannot extract through symlink %s", path);
++				    "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;
 +#ifdef HAVE_FCHDIR
 +	/* If we changed directory above, restore it here. */
 +	if (restore_pwd >= 0) {
 +		r = fchdir(restore_pwd);
 +		if (r != 0) {
 +			fsobj_error(a_eno, a_estr, errno,
 +			    "chdir() failure", "");
 +		}
 +		close(restore_pwd);
 +		restore_pwd = -1;
 +		if (r != 0) {
 +			res = (ARCHIVE_FATAL);
 +		}
 +	}
 +#endif
 +	/* TODO: reintroduce a safe cache here? */
 +	return res;
 +#endif
 +}
 +
 +/*
 + * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise
 + * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED}
 + */
 +static int
 +check_symlinks(struct archive_write_disk *a)
 +{
 +	struct archive_string error_string;
 +	int error_number;
 +	int rc;
 +	archive_string_init(&error_string);
 +	rc = check_symlinks_fsobj(a->name, &error_number, &error_string,
 +	    a->flags);
 +	if (rc != ARCHIVE_OK) {
 +		archive_set_error(&a->archive, error_number, "%s",
 +		    error_string.s);
 +	}
 +	archive_string_free(&error_string);
 +	a->pst = NULL;	/* to be safe */
 +	return rc;
 +}
 +
 +
 +#if defined(__CYGWIN__)
 +/*
 + * 1. Convert a path separator from '\' to '/' .
 + *    We shouldn't check multibyte character directly because some
 + *    character-set have been using the '\' character for a part of
 + *    its multibyte character code.
 + * 2. Replace unusable characters in Windows with underscore('_').
 + * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx
 + */
 +static void
 +cleanup_pathname_win(char *path)
 +{
 +	wchar_t wc;
 +	char *p;
 +	size_t alen, l;
 +	int mb, complete, utf8;
 +
 +	alen = 0;
 +	mb = 0;
 +	complete = 1;
 +	utf8 = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)? 1: 0;
 +	for (p = path; *p != '\0'; p++) {
 +		++alen;
 +		if (*p == '\\') {
 +			/* If previous byte is smaller than 128,
 +			 * this is not second byte of multibyte characters,
 +			 * so we can replace '\' with '/'. */
 +			if (utf8 || !mb)
 +				*p = '/';
 +			else
 +				complete = 0;/* uncompleted. */
 +		} else if (*(unsigned char *)p > 127)
 +			mb = 1;
 +		else
 +			mb = 0;
 +		/* Rewrite the path name if its next character is unusable. */
 +		if (*p == ':' || *p == '*' || *p == '?' || *p == '"' ||
 +		    *p == '<' || *p == '>' || *p == '|')
 +			*p = '_';
 +	}
 +	if (complete)
 +		return;
 +
 +	/*
 +	 * Convert path separator in wide-character.
 +	 */
 +	p = path;
 +	while (*p != '\0' && alen) {
 +		l = mbtowc(&wc, p, alen);
 +		if (l == (size_t)-1) {
 +			while (*p != '\0') {
 +				if (*p == '\\')
 +					*p = '/';
 +				++p;
 +			}
 +			break;
 +		}
 +		if (l == 1 && wc == L'\\')
 +			*p = '/';
 +		p += l;
 +		alen -= l;
 +	}
 +}
 +#endif
 +
 +/*
 + * Canonicalize the pathname.  In particular, this strips duplicate
 + * '/' characters, '.' elements, and trailing '/'.  It also raises an
 + * error for an empty path, a trailing '..', (if _SECURE_NODOTDOT is
 + * set) any '..' in the path or (if ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS
 + * is set) if the path is absolute.
 + */
 +static int
 +cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
 +    int flags)
 +{
 +	char *dest, *src;
 +	char separator = '\0';
 +
 +	dest = src = path;
 +	if (*src == '\0') {
 +		fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,
 +		    "Invalid empty ", "pathname");
 +		return (ARCHIVE_FAILED);
 +	}
 +
 +#if defined(__CYGWIN__)
 +	cleanup_pathname_win(path);
 +#endif
 +	/* Skip leading '/'. */
 +	if (*src == '/') {
 +		if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) {
 +			fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,
 +			    "Path is ", "absolute");
 +			return (ARCHIVE_FAILED);
 +		}
 +
 +		separator = *src++;
 +	}
 +
 +	/* Scan the pathname one element at a time. */
 +	for (;;) {
 +		/* src points to first char after '/' */
 +		if (src[0] == '\0') {
 +			break;
 +		} else if (src[0] == '/') {
 +			/* Found '//', ignore second one. */
 +			src++;
 +			continue;
 +		} else if (src[0] == '.') {
 +			if (src[1] == '\0') {
 +				/* Ignore trailing '.' */
 +				break;
 +			} else if (src[1] == '/') {
 +				/* Skip './'. */
 +				src += 2;
 +				continue;
 +			} else if (src[1] == '.') {
 +				if (src[2] == '/' || src[2] == '\0') {
 +					/* Conditionally warn about '..' */
 +					if (flags
 +					    & ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
 +						fsobj_error(a_eno, a_estr,
 +						    ARCHIVE_ERRNO_MISC,
 +						    "Path contains ", "'..'");
 +						return (ARCHIVE_FAILED);
 +					}
 +				}
 +				/*
 +				 * Note: Under no circumstances do we
 +				 * remove '..' elements.  In
 +				 * particular, restoring
 +				 * '/foo/../bar/' should create the
 +				 * 'foo' dir as a side-effect.
 +				 */
 +			}
 +		}
 +
 +		/* Copy current element, including leading '/'. */
 +		if (separator)
 +			*dest++ = '/';
 +		while (*src != '\0' && *src != '/') {
 +			*dest++ = *src++;
 +		}
 +
 +		if (*src == '\0')
 +			break;
 +
 +		/* Skip '/' separator. */
 +		separator = *src++;
 +	}
 +	/*
 +	 * We've just copied zero or more path elements, not including the
 +	 * final '/'.
 +	 */
 +	if (dest == path) {
 +		/*
 +		 * Nothing got copied.  The path must have been something
 +		 * like '.' or '/' or './' or '/././././/./'.
 +		 */
 +		if (separator)
 +			*dest++ = '/';
 +		else
 +			*dest++ = '.';
 +	}
 +	/* Terminate the result. */
 +	*dest = '\0';
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +cleanup_pathname(struct archive_write_disk *a)
 +{
 +	struct archive_string error_string;
 +	int error_number;
 +	int rc;
 +	archive_string_init(&error_string);
 +	rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string,
 +	    a->flags);
 +	if (rc != ARCHIVE_OK) {
 +		archive_set_error(&a->archive, error_number, "%s",
 +		    error_string.s);
 +	}
 +	archive_string_free(&error_string);
 +	return rc;
 +}
 +
 +/*
 + * Create the parent directory of the specified path, assuming path
 + * is already in mutable storage.
 + */
 +static int
 +create_parent_dir(struct archive_write_disk *a, char *path)
 +{
 +	char *slash;
 +	int r;
 +
 +	/* Remove tail element to obtain parent name. */
 +	slash = strrchr(path, '/');
 +	if (slash == NULL)
 +		return (ARCHIVE_OK);
 +	*slash = '\0';
 +	r = create_dir(a, path);
 +	*slash = '/';
 +	return (r);
 +}
 +
 +/*
 + * Create the specified dir, recursing to create parents as necessary.
 + *
 + * Returns ARCHIVE_OK if the path exists when we're done here.
 + * Otherwise, returns ARCHIVE_FAILED.
 + * Assumes path is in mutable storage; path is unchanged on exit.
 + */
 +static int
 +create_dir(struct archive_write_disk *a, char *path)
 +{
 +	struct stat st;
 +	struct fixup_entry *le;
 +	char *slash, *base;
 +	mode_t mode_final, mode;
 +	int r;
 +
 +	/* Check for special names and just skip them. */
 +	slash = strrchr(path, '/');
 +	if (slash == NULL)
 +		base = path;
 +	else
 +		base = slash + 1;
 +
 +	if (base[0] == '\0' ||
 +	    (base[0] == '.' && base[1] == '\0') ||
 +	    (base[0] == '.' && base[1] == '.' && base[2] == '\0')) {
 +		/* Don't bother trying to create null path, '.', or '..'. */
 +		if (slash != NULL) {
 +			*slash = '\0';
 +			r = create_dir(a, path);
 +			*slash = '/';
 +			return (r);
 +		}
 +		return (ARCHIVE_OK);
 +	}
 +
 +	/*
 +	 * Yes, this should be stat() and not lstat().  Using lstat()
 +	 * here loses the ability to extract through symlinks.  Also note
 +	 * that this should not use the a->st cache.
 +	 */
 +	if (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 (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)
 +{
 +#ifndef __CYGWIN__
 +/* 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);
 +	}
 +#endif
 +
 +#ifdef HAVE_FCHOWN
 +	/* If we have an fd, we can avoid a race. */
 +	if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) {
 +		/* We've set owner and know uid/gid are correct. */
 +		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
 +		return (ARCHIVE_OK);
 +	}
 +#endif
 +
 +	/* We prefer lchown() but will use chown() if that's all we have. */
 +	/* Of course, if we have neither, this will always fail. */
 +#ifdef HAVE_LCHOWN
 +	if (lchown(a->name, a->uid, a->gid) == 0) {
 +		/* We've set owner and know uid/gid are correct. */
 +		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
 +		return (ARCHIVE_OK);
 +	}
 +#elif HAVE_CHOWN
 +	if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) {
 +		/* We've set owner and know uid/gid are correct. */
 +		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
 +		return (ARCHIVE_OK);
 +	}
 +#endif
 +
 +	archive_set_error(&a->archive, errno,
 +	    "Can't set user=%jd/group=%jd for %s",
 +	    (intmax_t)a->uid, (intmax_t)a->gid, a->name);
 +	return (ARCHIVE_WARN);
 +}
 +
 +/*
 + * Note: Returns 0 on success, non-zero on failure.
 + */
 +static int
 +set_time(int fd, int mode, const char *name,
 +    time_t atime, long atime_nsec,
 +    time_t mtime, long mtime_nsec)
 +{
 +	/* Select the best implementation for this platform. */
 +#if defined(HAVE_UTIMENSAT) && defined(HAVE_FUTIMENS)
 +	/*
 +	 * utimensat() and futimens() are defined in
 +	 * POSIX.1-2008. They support ns resolution and setting times
 +	 * on fds and symlinks.
 +	 */
 +	struct timespec ts[2];
 +	(void)mode; /* UNUSED */
 +	ts[0].tv_sec = atime;
 +	ts[0].tv_nsec = atime_nsec;
 +	ts[1].tv_sec = mtime;
 +	ts[1].tv_nsec = mtime_nsec;
 +	if (fd >= 0)
 +		return futimens(fd, ts);
 +	return utimensat(AT_FDCWD, name, ts, AT_SYMLINK_NOFOLLOW);
 +
 +#elif HAVE_UTIMES
 +	/*
 +	 * The utimes()-family functions support µs-resolution and
 +	 * setting times fds and symlinks.  utimes() is documented as
 +	 * LEGACY by POSIX, futimes() and lutimes() are not described
 +	 * in POSIX.
 +	 */
 +	struct timeval times[2];
 +
 +	times[0].tv_sec = atime;
 +	times[0].tv_usec = atime_nsec / 1000;
 +	times[1].tv_sec = mtime;
 +	times[1].tv_usec = mtime_nsec / 1000;
 +
 +#ifdef HAVE_FUTIMES
 +	if (fd >= 0)
 +		return (futimes(fd, times));
 +#else
 +	(void)fd; /* UNUSED */
 +#endif
 +#ifdef HAVE_LUTIMES
 +	(void)mode; /* UNUSED */
 +	return (lutimes(name, times));
 +#else
 +	if (S_ISLNK(mode))
 +		return (0);
 +	return (utimes(name, times));
 +#endif
 +
 +#elif defined(HAVE_UTIME)
 +	/*
 +	 * utime() is POSIX-standard but only supports 1s resolution and
 +	 * does not support fds or symlinks.
 +	 */
 +	struct utimbuf times;
 +	(void)fd; /* UNUSED */
 +	(void)name; /* UNUSED */
 +	(void)atime_nsec; /* UNUSED */
 +	(void)mtime_nsec; /* UNUSED */
 +	times.actime = atime;
 +	times.modtime = mtime;
 +	if (S_ISLNK(mode))
 +		return (ARCHIVE_OK);
 +	return (utime(name, &times));
 +
 +#else
 +	/*
 +	 * We don't know how to set the time on this platform.
 +	 */
 +	(void)fd; /* UNUSED */
 +	(void)mode; /* UNUSED */
 +	(void)name; /* UNUSED */
 +	(void)atime_nsec; /* UNUSED */
 +	(void)mtime_nsec; /* UNUSED */
 +	return (ARCHIVE_WARN);
 +#endif
 +}
 +
 +#ifdef F_SETTIMES
 +static int
 +set_time_tru64(int fd, int mode, const char *name,
 +    time_t atime, long atime_nsec,
 +    time_t mtime, long mtime_nsec,
 +    time_t ctime, long ctime_nsec)
 +{
 +	struct attr_timbuf tstamp;
 +	tstamp.atime.tv_sec = atime;
 +	tstamp.mtime.tv_sec = mtime;
 +	tstamp.ctime.tv_sec = ctime;
 +#if defined (__hpux) && defined (__ia64)
 +	tstamp.atime.tv_nsec = atime_nsec;
 +	tstamp.mtime.tv_nsec = mtime_nsec;
 +	tstamp.ctime.tv_nsec = ctime_nsec;
 +#else
 +	tstamp.atime.tv_usec = atime_nsec / 1000;
 +	tstamp.mtime.tv_usec = mtime_nsec / 1000;
 +	tstamp.ctime.tv_usec = ctime_nsec / 1000;
 +#endif
 +	return (fcntl(fd,F_SETTIMES,&tstamp));
 +}
 +#endif /* F_SETTIMES */
 +
 +static int
 +set_times(struct archive_write_disk *a,
 +    int fd, int mode, const char *name,
 +    time_t atime, long atime_nanos,
 +    time_t birthtime, long birthtime_nanos,
 +    time_t mtime, long mtime_nanos,
 +    time_t cctime, long ctime_nanos)
 +{
 +	/* Note: set_time doesn't use libarchive return conventions!
 +	 * It uses syscall conventions.  So 0 here instead of ARCHIVE_OK. */
 +	int r1 = 0, r2 = 0;
 +
 +#ifdef F_SETTIMES
 +	 /*
 +	 * on Tru64 try own fcntl first which can restore even the
 +	 * ctime, fall back to default code path below if it fails
 +	 * or if we are not running as root
 +	 */
 +	if (a->user_uid == 0 &&
 +	    set_time_tru64(fd, mode, name,
 +			   atime, atime_nanos, mtime,
 +			   mtime_nanos, cctime, ctime_nanos) == 0) {
 +		return (ARCHIVE_OK);
 +	}
 +#else /* Tru64 */
 +	(void)cctime; /* UNUSED */
 +	(void)ctime_nanos; /* UNUSED */
 +#endif /* Tru64 */
 +
 +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
 +	/*
 +	 * If you have struct stat.st_birthtime, we assume BSD
 +	 * birthtime semantics, in which {f,l,}utimes() updates
 +	 * birthtime to earliest mtime.  So we set the time twice,
 +	 * first using the birthtime, then using the mtime.  If
 +	 * birthtime == mtime, this isn't necessary, so we skip it.
 +	 * If birthtime > mtime, then this won't work, so we skip it.
 +	 */
 +	if (birthtime < mtime
 +	    || (birthtime == mtime && birthtime_nanos < mtime_nanos))
 +		r1 = set_time(fd, mode, name,
 +			      atime, atime_nanos,
 +			      birthtime, birthtime_nanos);
 +#else
 +	(void)birthtime; /* UNUSED */
 +	(void)birthtime_nanos; /* UNUSED */
 +#endif
 +	r2 = set_time(fd, mode, name,
 +		      atime, atime_nanos,
 +		      mtime, mtime_nanos);
 +	if (r1 != 0 || r2 != 0) {
 +		archive_set_error(&a->archive, errno,
 +				  "Can't restore time");
 +		return (ARCHIVE_WARN);
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +set_times_from_entry(struct archive_write_disk *a)
 +{
 +	time_t atime, birthtime, mtime, cctime;
 +	long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec;
 +
 +	/* Suitable defaults. */
 +	atime = birthtime = mtime = cctime = a->start_time;
 +	atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0;
 +
 +	/* If no time was provided, we're done. */
 +	if (!archive_entry_atime_is_set(a->entry)
 +#if HAVE_STRUCT_STAT_ST_BIRTHTIME
 +	    && !archive_entry_birthtime_is_set(a->entry)
 +#endif
 +	    && !archive_entry_mtime_is_set(a->entry))
 +		return (ARCHIVE_OK);
 +
 +	if (archive_entry_atime_is_set(a->entry)) {
 +		atime = archive_entry_atime(a->entry);
 +		atime_nsec = archive_entry_atime_nsec(a->entry);
 +	}
 +	if (archive_entry_birthtime_is_set(a->entry)) {
 +		birthtime = archive_entry_birthtime(a->entry);
 +		birthtime_nsec = archive_entry_birthtime_nsec(a->entry);
 +	}
 +	if (archive_entry_mtime_is_set(a->entry)) {
 +		mtime = archive_entry_mtime(a->entry);
 +		mtime_nsec = archive_entry_mtime_nsec(a->entry);
 +	}
 +	if (archive_entry_ctime_is_set(a->entry)) {
 +		cctime = archive_entry_ctime(a->entry);
 +		ctime_nsec = archive_entry_ctime_nsec(a->entry);
 +	}
 +
 +	return set_times(a, a->fd, a->mode, a->name,
 +			 atime, atime_nsec,
 +			 birthtime, birthtime_nsec,
 +			 mtime, mtime_nsec,
 +			 cctime, ctime_nsec);
 +}
 +
 +static int
 +set_mode(struct archive_write_disk *a, int mode)
 +{
 +	int r = ARCHIVE_OK;
 +	mode &= 07777; /* Strip off file type bits. */
 +
 +	if (a->todo & TODO_SGID_CHECK) {
 +		/*
 +		 * If we don't know the GID is right, we must stat()
 +		 * to verify it.  We can't just check the GID of this
 +		 * process, since systems sometimes set GID from
 +		 * the enclosing dir or based on ACLs.
 +		 */
 +		if ((r = lazy_stat(a)) != ARCHIVE_OK)
 +			return (r);
 +		if (a->pst->st_gid != a->gid) {
 +			mode &= ~ S_ISGID;
 +			if (a->flags & ARCHIVE_EXTRACT_OWNER) {
 +				/*
 +				 * This is only an error if you
 +				 * requested owner restore.  If you
 +				 * didn't, we'll try to restore
 +				 * sgid/suid, but won't consider it a
 +				 * problem if we can't.
 +				 */
 +				archive_set_error(&a->archive, -1,
 +				    "Can't restore SGID bit");
 +				r = ARCHIVE_WARN;
 +			}
 +		}
 +		/* While we're here, double-check the UID. */
 +		if (a->pst->st_uid != a->uid
 +		    && (a->todo & TODO_SUID)) {
 +			mode &= ~ S_ISUID;
 +			if (a->flags & ARCHIVE_EXTRACT_OWNER) {
 +				archive_set_error(&a->archive, -1,
 +				    "Can't restore SUID bit");
 +				r = ARCHIVE_WARN;
 +			}
 +		}
 +		a->todo &= ~TODO_SGID_CHECK;
 +		a->todo &= ~TODO_SUID_CHECK;
 +	} else if (a->todo & TODO_SUID_CHECK) {
 +		/*
 +		 * If we don't know the UID is right, we can just check
 +		 * the user, since all systems set the file UID from
 +		 * the process UID.
 +		 */
 +		if (a->user_uid != a->uid) {
 +			mode &= ~ S_ISUID;
 +			if (a->flags & ARCHIVE_EXTRACT_OWNER) {
 +				archive_set_error(&a->archive, -1,
 +				    "Can't make file SUID");
 +				r = ARCHIVE_WARN;
 +			}
 +		}
 +		a->todo &= ~TODO_SUID_CHECK;
 +	}
 +
 +	if (S_ISLNK(a->mode)) {
 +#ifdef HAVE_LCHMOD
 +		/*
 +		 * If this is a symlink, use lchmod().  If the
 +		 * platform doesn't support lchmod(), just skip it.  A
 +		 * platform that doesn't provide a way to set
 +		 * permissions on symlinks probably ignores
 +		 * permissions on symlinks, so a failure here has no
 +		 * impact.
 +		 */
 +		if (lchmod(a->name, mode) != 0) {
 +			switch (errno) {
 +			case ENOTSUP:
 +			case ENOSYS:
 +#if ENOTSUP != EOPNOTSUPP
 +			case EOPNOTSUPP:
 +#endif
 +				/*
 +				 * if lchmod is defined but the platform
 +				 * doesn't support it, silently ignore
 +				 * error
 +				 */
 +				break;
 +			default:
 +				archive_set_error(&a->archive, errno,
 +				    "Can't set permissions to 0%o", (int)mode);
 +				r = ARCHIVE_WARN;
 +			}
 +		}
 +#endif
 +	} else if (!S_ISDIR(a->mode)) {
 +		/*
 +		 * If it's not a symlink and not a dir, then use
 +		 * fchmod() or chmod(), depending on whether we have
 +		 * an fd.  Dirs get their perms set during the
 +		 * post-extract fixup, which is handled elsewhere.
 +		 */
 +#ifdef HAVE_FCHMOD
 +		if (a->fd >= 0) {
 +			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 (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(struct archive_write_disk *a)
 +{
 +	struct fixup_entry *le;
 +	unsigned long	set, clear;
 +	int		r;
 +	int		critical_flags;
 +	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.
 +	 */
 +	/* Hopefully, the compiler will optimize this mess into a constant. */
 +	critical_flags = 0;
 +#ifdef SF_IMMUTABLE
 +	critical_flags |= SF_IMMUTABLE;
 +#endif
 +#ifdef UF_IMMUTABLE
 +	critical_flags |= UF_IMMUTABLE;
 +#endif
 +#ifdef SF_APPEND
 +	critical_flags |= SF_APPEND;
 +#endif
 +#ifdef UF_APPEND
 +	critical_flags |= UF_APPEND;
 +#endif
 +#if defined(FS_APPEND_FL)
 +	critical_flags |= FS_APPEND_FL;
 +#elif defined(EXT2_APPEND_FL)
 +	critical_flags |= EXT2_APPEND_FL;
 +#endif
 +#if defined(FS_IMMUTABLE_FL)
 +	critical_flags |= FS_IMMUTABLE_FL;
 +#elif defined(EXT2_IMMUTABLE_FL)
 +	critical_flags |= EXT2_IMMUTABLE_FL;
 +#endif
 +#ifdef FS_JOURNAL_DATA_FL
 +	critical_flags |= FS_JOURNAL_DATA_FL;
 +#endif
 +
 +	if (a->todo & TODO_FFLAGS) {
 +		archive_entry_fflags(a->entry, &set, &clear);
 +
 +		/*
 +		 * The first test encourages the compiler to eliminate
 +		 * all of this if it's not necessary.
 +		 */
 +		if ((critical_flags != 0)  &&  (set & critical_flags)) {
 +			le = current_fixup(a, a->name);
 +			if (le == NULL)
 +				return (ARCHIVE_FATAL);
 +			le->fixup |= TODO_FFLAGS;
 +			le->fflags_set = set;
 +			/* Store the mode if it's not already there. */
 +			if ((le->fixup & TODO_MODE) == 0)
 +				le->mode = mode;
 +		} else {
 +			r = set_fflags_platform(a, a->fd,
 +			    a->name, mode, set, clear);
 +			if (r != ARCHIVE_OK)
 +				return (r);
 +		}
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +clear_nochange_fflags(struct archive_write_disk *a)
 +{
 +	int		nochange_flags;
 +	mode_t		mode = archive_entry_mode(a->entry);
 +
 +	/* Hopefully, the compiler will optimize this mess into a constant. */
 +	nochange_flags = 0;
 +#ifdef SF_IMMUTABLE
 +	nochange_flags |= SF_IMMUTABLE;
 +#endif
 +#ifdef UF_IMMUTABLE
 +	nochange_flags |= UF_IMMUTABLE;
 +#endif
 +#ifdef SF_APPEND
 +	nochange_flags |= SF_APPEND;
 +#endif
 +#ifdef UF_APPEND
 +	nochange_flags |= UF_APPEND;
 +#endif
 +#ifdef EXT2_APPEND_FL
 +	nochange_flags |= EXT2_APPEND_FL;
 +#endif
 +#ifdef EXT2_IMMUTABLE_FL
 +	nochange_flags |= 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;
 +
 +	(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;
 +#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;
 +	int sf_mask = 0;
 +
 +	if (set == 0 && clear == 0)
 +		return (ARCHIVE_OK);
 +	/* Only regular files and dirs can have flags. */
 +	if (!S_ISREG(mode) && !S_ISDIR(mode))
 +		return (ARCHIVE_OK);
 +
 +	/* If we weren't given an fd, open it ourselves. */
 +	if (myfd < 0) {
 +		myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC);
 +		__archive_ensure_cloexec_flag(myfd);
 +	}
 +	if (myfd < 0)
 +		return (ARCHIVE_OK);
 +
 +	/*
 +	 * 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.
 +	 */
 +#if defined(FS_IMMUTABLE_FL)
 +	sf_mask |= FS_IMMUTABLE_FL;
 +#elif defined(EXT2_IMMUTABLE_FL)
 +	sf_mask |= EXT2_IMMUTABLE_FL;
 +#endif
 +#if defined(FS_APPEND_FL)
 +	sf_mask |= FS_APPEND_FL;
 +#elif defined(EXT2_APPEND_FL)
 +	sf_mask |= EXT2_APPEND_FL;
 +#endif
 +#if defined(FS_JOURNAL_DATA_FL)
 +	sf_mask |= FS_JOURNAL_DATA_FL;
 +#endif
 +	/*
 +	 * XXX As above, this would be way simpler if we didn't have
 +	 * to read the current flags from disk. XXX
 +	 */
 +	ret = ARCHIVE_OK;
 +
 +	/* Read the current file flags. */
 +	if (ioctl(myfd,
 +#ifdef FS_IOC_GETFLAGS
 +	    FS_IOC_GETFLAGS,
 +#else
 +	    EXT2_IOC_GETFLAGS,
 +#endif
 +	    &oldflags) < 0)
 +		goto fail;
 +
 +	/* Try setting the flags as given. */
 +	newflags = (oldflags & ~clear) | set;
 +	if (ioctl(myfd,
 +#ifdef FS_IOC_SETFLAGS
 +	    FS_IOC_SETFLAGS,
 +#else
 +	    EXT2_IOC_SETFLAGS,
 +#endif
 +	    &newflags) >= 0)
 +		goto cleanup;
 +	if (errno != EPERM)
 +		goto fail;
 +
 +	/* If we couldn't set all the flags, try again with a subset. */
 +	newflags &= ~sf_mask;
 +	oldflags &= sf_mask;
 +	newflags |= oldflags;
 +	if (ioctl(myfd,
 +#ifdef FS_IOC_SETFLAGS
 +	    FS_IOC_SETFLAGS,
 +#else
 +	    EXT2_IOC_SETFLAGS,
 +#endif
 +	    &newflags) >= 0)
 +		goto cleanup;
 +
 +	/* We couldn't set the flags, so report the failure. */
 +fail:
 +	archive_set_error(&a->archive, errno,
 +	    "Failed to set file flags");
 +	ret = ARCHIVE_WARN;
 +cleanup:
 +	if (fd < 0)
 +		close(myfd);
 +	return (ret);
 +}
 +
 +#else
 +
 +/*
 + * Of course, some systems have neither BSD chflags() nor Linux' flags
 + * support through ioctl().
 + */
 +static int
 +set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
 +    mode_t mode, unsigned long set, unsigned long clear)
 +{
 +	(void)a; /* UNUSED */
 +	(void)fd; /* UNUSED */
 +	(void)name; /* UNUSED */
 +	(void)mode; /* UNUSED */
 +	(void)set; /* UNUSED */
 +	(void)clear; /* UNUSED */
 +	return (ARCHIVE_OK);
 +}
 +
 +#endif /* __linux */
 +
 +#ifndef HAVE_COPYFILE_H
 +/* Default is to simply drop Mac extended metadata. */
 +static int
 +set_mac_metadata(struct archive_write_disk *a, const char *pathname,
 +		 const void *metadata, size_t metadata_size)
 +{
 +	(void)a; /* UNUSED */
 +	(void)pathname; /* UNUSED */
 +	(void)metadata; /* UNUSED */
 +	(void)metadata_size; /* UNUSED */
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +fixup_appledouble(struct archive_write_disk *a, const char *pathname)
 +{
 +	(void)a; /* UNUSED */
 +	(void)pathname; /* UNUSED */
 +	return (ARCHIVE_OK);
 +}
 +#else
 +
 +/*
 + * On Mac OS, we use copyfile() to unpack the metadata and
 + * apply it to the target file.
 + */
 +
 +#if defined(HAVE_SYS_XATTR_H)
 +static int
 +copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd)
 +{
 +	ssize_t xattr_size;
 +	char *xattr_names = NULL, *xattr_val = NULL;
 +	int ret = ARCHIVE_OK, xattr_i;
 +
 +	xattr_size = flistxattr(tmpfd, NULL, 0, 0);
 +	if (xattr_size == -1) {
 +		archive_set_error(&a->archive, errno,
 +		    "Failed to read metadata(xattr)");
 +		ret = ARCHIVE_WARN;
 +		goto exit_xattr;
 +	}
 +	xattr_names = malloc(xattr_size);
 +	if (xattr_names == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate memory for metadata(xattr)");
 +		ret = ARCHIVE_FATAL;
 +		goto exit_xattr;
 +	}
 +	xattr_size = flistxattr(tmpfd, xattr_names, xattr_size, 0);
 +	if (xattr_size == -1) {
 +		archive_set_error(&a->archive, errno,
 +		    "Failed to read metadata(xattr)");
 +		ret = ARCHIVE_WARN;
 +		goto exit_xattr;
 +	}
 +	for (xattr_i = 0; xattr_i < xattr_size;
 +	    xattr_i += strlen(xattr_names + xattr_i) + 1) {
 +		char *xattr_val_saved;
 +		ssize_t s;
 +		int f;
 +
 +		s = fgetxattr(tmpfd, xattr_names + xattr_i, NULL, 0, 0, 0);
 +		if (s == -1) {
 +			archive_set_error(&a->archive, errno,
 +			    "Failed to get metadata(xattr)");
 +			ret = ARCHIVE_WARN;
 +			goto exit_xattr;
 +		}
 +		xattr_val_saved = xattr_val;
 +		xattr_val = realloc(xattr_val, s);
 +		if (xattr_val == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Failed to get metadata(xattr)");
 +			ret = ARCHIVE_WARN;
 +			free(xattr_val_saved);
 +			goto exit_xattr;
 +		}
 +		s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0);
 +		if (s == -1) {
 +			archive_set_error(&a->archive, errno,
 +			    "Failed to get metadata(xattr)");
 +			ret = ARCHIVE_WARN;
 +			goto exit_xattr;
 +		}
 +		f = fsetxattr(dffd, xattr_names + xattr_i, xattr_val, s, 0, 0);
 +		if (f == -1) {
 +			archive_set_error(&a->archive, errno,
 +			    "Failed to get metadata(xattr)");
 +			ret = ARCHIVE_WARN;
 +			goto exit_xattr;
 +		}
 +	}
 +exit_xattr:
 +	free(xattr_names);
 +	free(xattr_val);
 +	return (ret);
 +}
 +#endif
 +
 +static int
 +copy_acls(struct archive_write_disk *a, int tmpfd, int dffd)
 +{
 +#ifndef HAVE_SYS_ACL_H
 +	return 0;
 +#else
 +	acl_t acl, dfacl = NULL;
 +	int acl_r, ret = ARCHIVE_OK;
 +
 +	acl = acl_get_fd(tmpfd);
 +	if (acl == NULL) {
 +		if (errno == ENOENT)
 +			/* There are not any ACLs. */
 +			return (ret);
 +		archive_set_error(&a->archive, errno,
 +		    "Failed to get metadata(acl)");
 +		ret = ARCHIVE_WARN;
 +		goto exit_acl;
 +	}
 +	dfacl = acl_dup(acl);
 +	acl_r = acl_set_fd(dffd, dfacl);
 +	if (acl_r == -1) {
 +		archive_set_error(&a->archive, errno,
 +		    "Failed to get metadata(acl)");
 +		ret = ARCHIVE_WARN;
 +		goto exit_acl;
 +	}
 +exit_acl:
 +	if (acl)
 +		acl_free(acl);
 +	if (dfacl)
 +		acl_free(dfacl);
 +	return (ret);
 +#endif
 +}
 +
 +static int
 +create_tempdatafork(struct archive_write_disk *a, const char *pathname)
 +{
 +	struct archive_string tmpdatafork;
 +	int tmpfd;
 +
 +	archive_string_init(&tmpdatafork);
 +	archive_strcpy(&tmpdatafork, "tar.md.XXXXXX");
 +	tmpfd = mkstemp(tmpdatafork.s);
 +	if (tmpfd < 0) {
 +		archive_set_error(&a->archive, errno,
 +		    "Failed to mkstemp");
 +		archive_string_free(&tmpdatafork);
 +		return (-1);
 +	}
 +	if (copyfile(pathname, tmpdatafork.s, 0,
 +	    COPYFILE_UNPACK | COPYFILE_NOFOLLOW
 +	    | COPYFILE_ACL | COPYFILE_XATTR) < 0) {
 +		archive_set_error(&a->archive, errno,
 +		    "Failed to restore metadata");
 +		close(tmpfd);
 +		tmpfd = -1;
 +	}
 +	unlink(tmpdatafork.s);
 +	archive_string_free(&tmpdatafork);
 +	return (tmpfd);
 +}
 +
 +static int
 +copy_metadata(struct archive_write_disk *a, const char *metadata,
 +    const char *datafork, int datafork_compressed)
 +{
 +	int ret = ARCHIVE_OK;
 +
 +	if (datafork_compressed) {
 +		int dffd, tmpfd;
 +
 +		tmpfd = create_tempdatafork(a, metadata);
 +		if (tmpfd == -1)
 +			return (ARCHIVE_WARN);
 +
 +		/*
 +		 * Do not open the data fork compressed by HFS+ compression
 +		 * with at least a writing mode(O_RDWR or O_WRONLY). it
 +		 * makes the data fork uncompressed.
 +		 */
 +		dffd = open(datafork, 0);
 +		if (dffd == -1) {
 +			archive_set_error(&a->archive, errno,
 +			    "Failed to open the data fork for metadata");
 +			close(tmpfd);
 +			return (ARCHIVE_WARN);
 +		}
 +
 +#if defined(HAVE_SYS_XATTR_H)
 +		ret = copy_xattrs(a, tmpfd, dffd);
 +		if (ret == ARCHIVE_OK)
 +#endif
 +			ret = copy_acls(a, tmpfd, dffd);
 +		close(tmpfd);
 +		close(dffd);
 +	} else {
 +		if (copyfile(metadata, datafork, 0,
 +		    COPYFILE_UNPACK | COPYFILE_NOFOLLOW
 +		    | COPYFILE_ACL | COPYFILE_XATTR) < 0) {
 +			archive_set_error(&a->archive, errno,
 +			    "Failed to restore metadata");
 +			ret = ARCHIVE_WARN;
 +		}
 +	}
 +	return (ret);
 +}
 +
 +static int
 +set_mac_metadata(struct archive_write_disk *a, const char *pathname,
 +		 const void *metadata, size_t metadata_size)
 +{
 +	struct archive_string tmp;
 +	ssize_t written;
 +	int fd;
 +	int ret = ARCHIVE_OK;
 +
 +	/* This would be simpler if copyfile() could just accept the
 +	 * metadata as a block of memory; then we could sidestep this
 +	 * silly dance of writing the data to disk just so that
 +	 * copyfile() can read it back in again. */
 +	archive_string_init(&tmp);
 +	archive_strcpy(&tmp, pathname);
 +	archive_strcat(&tmp, ".XXXXXX");
 +	fd = mkstemp(tmp.s);
 +
 +	if (fd < 0) {
 +		archive_set_error(&a->archive, errno,
 +				  "Failed to restore metadata");
 +		archive_string_free(&tmp);
 +		return (ARCHIVE_WARN);
 +	}
 +	written = write(fd, metadata, metadata_size);
 +	close(fd);
 +	if ((size_t)written != metadata_size) {
 +		archive_set_error(&a->archive, errno,
 +				  "Failed to restore metadata");
 +		ret = ARCHIVE_WARN;
 +	} else {
 +		int compressed;
 +
 +#if defined(UF_COMPRESSED)
 +		if ((a->todo & TODO_HFS_COMPRESSION) != 0 &&
 +		    (ret = lazy_stat(a)) == ARCHIVE_OK)
 +			compressed = a->st.st_flags & UF_COMPRESSED;
 +		else
 +#endif
 +			compressed = 0;
 +		ret = copy_metadata(a, tmp.s, pathname, compressed);
 +	}
 +	unlink(tmp.s);
 +	archive_string_free(&tmp);
 +	return (ret);
 +}
 +
 +static int
 +fixup_appledouble(struct archive_write_disk *a, const char *pathname)
 +{
 +	char buff[8];
 +	struct stat st;
 +	const char *p;
 +	struct archive_string datafork;
 +	int fd = -1, ret = ARCHIVE_OK;
 +
 +	archive_string_init(&datafork);
 +	/* Check if the current file name is a type of the resource
 +	 * fork file. */
 +	p = strrchr(pathname, '/');
 +	if (p == NULL)
 +		p = pathname;
 +	else
 +		p++;
 +	if (p[0] != '.' || p[1] != '_')
 +		goto skip_appledouble;
 +
 +	/*
 +	 * Check if the data fork file exists.
 +	 *
 +	 * TODO: Check if this write disk object has handled it.
 +	 */
 +	archive_strncpy(&datafork, pathname, p - pathname);
 +	archive_strcat(&datafork, p + 2);
 +	if (lstat(datafork.s, &st) == -1 ||
 +	    (st.st_mode & AE_IFMT) != AE_IFREG)
 +		goto skip_appledouble;
 +
 +	/*
 +	 * Check if the file is in the AppleDouble form.
 +	 */
 +	fd = open(pathname, O_RDONLY | O_BINARY | O_CLOEXEC);
 +	__archive_ensure_cloexec_flag(fd);
 +	if (fd == -1) {
 +		archive_set_error(&a->archive, errno,
 +		    "Failed to open a restoring file");
 +		ret = ARCHIVE_WARN;
 +		goto skip_appledouble;
 +	}
 +	if (read(fd, buff, 8) == -1) {
 +		archive_set_error(&a->archive, errno,
 +		    "Failed to read a restoring file");
 +		close(fd);
 +		ret = ARCHIVE_WARN;
 +		goto skip_appledouble;
 +	}
 +	close(fd);
 +	/* Check AppleDouble Magic Code. */
 +	if (archive_be32dec(buff) != 0x00051607)
 +		goto skip_appledouble;
 +	/* Check AppleDouble Version. */
 +	if (archive_be32dec(buff+4) != 0x00020000)
 +		goto skip_appledouble;
 +
 +	ret = copy_metadata(a, pathname, datafork.s,
 +#if defined(UF_COMPRESSED)
 +	    st.st_flags & UF_COMPRESSED);
 +#else
 +	    0);
 +#endif
 +	if (ret == ARCHIVE_OK) {
 +		unlink(pathname);
 +		ret = ARCHIVE_EOF;
 +	}
 +skip_appledouble:
 +	archive_string_free(&datafork);
 +	return (ret);
 +}
 +#endif
 +
- #if HAVE_LSETXATTR || HAVE_LSETEA
++#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX
 +/*
-  * Restore extended attributes -  Linux and AIX implementations:
++ * 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;
- 	static int warning_done = 0;
++	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 &&
- 				strncmp(name, "xfsroot.", 8) != 0 &&
- 				strncmp(name, "system.", 7) != 0) {
- 			int e;
- #if HAVE_FSETXATTR
- 			if (a->fd >= 0)
- 				e = fsetxattr(a->fd, name, value, size, 0);
- 			else
- #elif HAVE_FSETEA
- 			if (a->fd >= 0)
- 				e = fsetea(a->fd, name, value, size, 0);
- 			else
++
++		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 HAVE_LSETXATTR
- 				e = lsetxattr(archive_entry_pathname(entry),
- 				    name, value, size, 0);
- #elif HAVE_LSETEA
- 				e = lsetea(archive_entry_pathname(entry),
- 				    name, value, size, 0);
++
++		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
- 			}
- 			if (e == -1) {
- 				if (errno == ENOTSUP || errno == ENOSYS) {
- 					if (!warning_done) {
- 						warning_done = 1;
- 						archive_set_error(&a->archive,
- 						    errno,
- 						    "Cannot restore extended "
- 						    "attributes on this file "
- 						    "system");
- 					}
- 				} else
- 					archive_set_error(&a->archive, errno,
- 					    "Failed to set extended attribute");
- 				ret = ARCHIVE_WARN;
- 			}
 +		} else {
- 			archive_set_error(&a->archive,
- 			    ARCHIVE_ERRNO_FILE_FORMAT,
- 			    "Invalid extended attribute encountered");
++#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 HAVE_EXTATTR_SET_FILE && HAVE_DECL_EXTATTR_NAMESPACE_USER
++#elif ARCHIVE_XATTR_FREEBSD
 +/*
 + * Restore extended attributes -  FreeBSD implementation
 + */
 +static int
 +set_xattrs(struct archive_write_disk *a)
 +{
 +	struct archive_entry *entry = a->entry;
- 	static int warning_done = 0;
++	struct archive_string errlist;
 +	int ret = ARCHIVE_OK;
 +	int i = archive_entry_xattr_reset(entry);
++	short fail = 0;
++
++	archive_string_init(&errlist);
 +
 +	while (i--) {
 +		const char *name;
 +		const void *value;
 +		size_t size;
 +		archive_entry_xattr_next(entry, &name, &value, &size);
 +		if (name != NULL) {
 +			int e;
 +			int namespace;
 +
 +			if (strncmp(name, "user.", 5) == 0) {
 +				/* "user." attributes go to user namespace */
 +				name += 5;
 +				namespace = EXTATTR_NAMESPACE_USER;
 +			} else {
- 				/* Warn about other extended attributes. */
- 				archive_set_error(&a->archive,
- 				    ARCHIVE_ERRNO_FILE_FORMAT,
- 				    "Can't restore extended attribute ``%s''",
- 				    name);
++				/* Other namespaces are unsupported */
++				archive_strcat(&errlist, name);
++				archive_strappend_char(&errlist, ' ');
++				fail = 1;
 +				ret = ARCHIVE_WARN;
 +				continue;
 +			}
- 			errno = 0;
- #if HAVE_EXTATTR_SET_FD
- 			if (a->fd >= 0)
++
++			if (a->fd >= 0) {
 +				e = extattr_set_fd(a->fd, namespace, name,
 +				    value, size);
- 			else
- #endif
- 			/* TODO: should we use extattr_set_link() instead? */
- 			{
- 				e = extattr_set_file(
++			} else {
++				e = extattr_set_link(
 +				    archive_entry_pathname(entry), namespace,
 +				    name, value, size);
 +			}
 +			if (e != (int)size) {
- 				if (errno == ENOTSUP || errno == ENOSYS) {
- 					if (!warning_done) {
- 						warning_done = 1;
- 						archive_set_error(&a->archive,
- 						    errno,
- 						    "Cannot restore extended "
- 						    "attributes on this file "
- 						    "system");
- 					}
- 				} else {
- 					archive_set_error(&a->archive, errno,
- 					    "Failed to set extended attribute");
- 				}
- 
++				archive_strcat(&errlist, name);
++				archive_strappend_char(&errlist, ' ');
 +				ret = ARCHIVE_WARN;
++				if (errno != ENOTSUP && errno != ENOSYS)
++					fail = 1;
 +			}
 +		}
 +	}
++
++	if (ret == ARCHIVE_WARN) {
++		if (fail && errlist.length > 0) {
++			errlist.length--;
++			errlist.s[errlist.length] = '\0';
++
++			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
++			    "Cannot restore extended attributes: %s",
++			    errlist.s);
++		} else
++			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
++			    "Cannot restore extended "
++			    "attributes on this file system.");
++	}
++
++	archive_string_free(&errlist);
 +	return (ret);
 +}
 +#else
 +/*
 + * Restore extended attributes - stub implementation for unsupported systems
 + */
 +static int
 +set_xattrs(struct archive_write_disk *a)
 +{
 +	static int warning_done = 0;
 +
 +	/* If there aren't any extended attributes, then it's okay not
 +	 * to extract them, otherwise, issue a single warning. */
 +	if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) {
 +		warning_done = 1;
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Cannot restore extended attributes on this system");
 +		return (ARCHIVE_WARN);
 +	}
 +	/* Warning was already emitted; suppress further warnings. */
 +	return (ARCHIVE_OK);
 +}
 +#endif
 +
 +/*
 + * Test if file on disk is older than entry.
 + */
 +static int
 +older(struct stat *st, struct archive_entry *entry)
 +{
 +	/* First, test the seconds and return if we have a definite answer. */
 +	/* Definitely older. */
 +	if (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry)))
 +		return (1);
 +	/* Definitely younger. */
 +	if (to_int64_time(st->st_mtime) > to_int64_time(archive_entry_mtime(entry)))
 +		return (0);
 +	/* If this platform supports fractional seconds, try those. */
 +#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
 +	/* Definitely older. */
 +	if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry))
 +		return (1);
 +#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
 +	/* Definitely older. */
 +	if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry))
 +		return (1);
 +#elif HAVE_STRUCT_STAT_ST_MTIME_N
 +	/* older. */
 +	if (st->st_mtime_n < archive_entry_mtime_nsec(entry))
 +		return (1);
 +#elif HAVE_STRUCT_STAT_ST_UMTIME
 +	/* older. */
 +	if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry))
 +		return (1);
 +#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
 +	/* older. */
 +	if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry))
 +		return (1);
 +#else
 +	/* This system doesn't have high-res timestamps. */
 +#endif
 +	/* Same age or newer, so not older. */
 +	return (0);
 +}
 +
++#ifndef ARCHIVE_ACL_SUPPORT
++int
++archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
++    struct archive_acl *abstract_acl, __LA_MODE_T mode)
++{
++	(void)a; /* UNUSED */
++	(void)fd; /* UNUSED */
++	(void)name; /* UNUSED */
++	(void)abstract_acl; /* UNUSED */
++	(void)mode; /* UNUSED */
++	return (ARCHIVE_OK);
++}
++#endif
++
 +#endif /* !_WIN32 || __CYGWIN__ */
 +

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=11f3dcb04887fccf38a8eda19d498bf7a5060486
commit 11f3dcb04887fccf38a8eda19d498bf7a5060486
Author:     LibArchive Upstream <libarchive-discuss at googlegroups.com>
AuthorDate: Sun Jul 9 19:38:04 2017 -0700
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Thu Jul 20 11:30:03 2017 -0400

    LibArchive 2017-07-09 (98a69539)
    
    Code extracted from:
    
        https://github.com/libarchive/libarchive.git
    
    at commit 98a695399e8e7420635a5448aecde8b0a82fb83a (v3.3.2).

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1ca9d8f..73bf07b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -552,6 +552,7 @@ LA_CHECK_INCLUDE_FILE("sys/types.h" HAVE_SYS_TYPES_H)
 
 # Alphabetize the rest unless there's a compelling reason
 LA_CHECK_INCLUDE_FILE("acl/libacl.h" HAVE_ACL_LIBACL_H)
+LA_CHECK_INCLUDE_FILE("attr/xattr.h" HAVE_ATTR_XATTR_H)
 LA_CHECK_INCLUDE_FILE("ctype.h" HAVE_CTYPE_H)
 LA_CHECK_INCLUDE_FILE("copyfile.h" HAVE_COPYFILE_H)
 LA_CHECK_INCLUDE_FILE("direct.h" HAVE_DIRECT_H)
@@ -579,6 +580,7 @@ int main(void) { return FS_IOC_GETFLAGS; }" HAVE_WORKING_FS_IOC_GETFLAGS)
 
 LA_CHECK_INCLUDE_FILE("linux/magic.h" HAVE_LINUX_MAGIC_H)
 LA_CHECK_INCLUDE_FILE("locale.h" HAVE_LOCALE_H)
+LA_CHECK_INCLUDE_FILE("membership.h" HAVE_MEMBERSHIP_H)
 LA_CHECK_INCLUDE_FILE("memory.h" HAVE_MEMORY_H)
 LA_CHECK_INCLUDE_FILE("paths.h" HAVE_PATHS_H)
 LA_CHECK_INCLUDE_FILE("poll.h" HAVE_POLL_H)
@@ -596,11 +598,13 @@ LA_CHECK_INCLUDE_FILE("string.h" HAVE_STRING_H)
 LA_CHECK_INCLUDE_FILE("strings.h" HAVE_STRINGS_H)
 LA_CHECK_INCLUDE_FILE("sys/acl.h" HAVE_SYS_ACL_H)
 LA_CHECK_INCLUDE_FILE("sys/cdefs.h" HAVE_SYS_CDEFS_H)
+LA_CHECK_INCLUDE_FILE("sys/extattr.h" HAVE_SYS_EXTATTR_H)
 LA_CHECK_INCLUDE_FILE("sys/ioctl.h" HAVE_SYS_IOCTL_H)
 LA_CHECK_INCLUDE_FILE("sys/mkdev.h" HAVE_SYS_MKDEV_H)
 LA_CHECK_INCLUDE_FILE("sys/mount.h" HAVE_SYS_MOUNT_H)
 LA_CHECK_INCLUDE_FILE("sys/param.h" HAVE_SYS_PARAM_H)
 LA_CHECK_INCLUDE_FILE("sys/poll.h" HAVE_SYS_POLL_H)
+LA_CHECK_INCLUDE_FILE("sys/richacl.h" HAVE_SYS_RICHACL_H)
 LA_CHECK_INCLUDE_FILE("sys/select.h" HAVE_SYS_SELECT_H)
 LA_CHECK_INCLUDE_FILE("sys/stat.h" HAVE_SYS_STAT_H)
 LA_CHECK_INCLUDE_FILE("sys/statfs.h" HAVE_SYS_STATFS_H)
@@ -610,6 +614,7 @@ LA_CHECK_INCLUDE_FILE("sys/utime.h" HAVE_SYS_UTIME_H)
 LA_CHECK_INCLUDE_FILE("sys/utsname.h" HAVE_SYS_UTSNAME_H)
 LA_CHECK_INCLUDE_FILE("sys/vfs.h" HAVE_SYS_VFS_H)
 LA_CHECK_INCLUDE_FILE("sys/wait.h" HAVE_SYS_WAIT_H)
+LA_CHECK_INCLUDE_FILE("sys/xattr.h" HAVE_SYS_XATTR_H)
 LA_CHECK_INCLUDE_FILE("time.h" HAVE_TIME_H)
 LA_CHECK_INCLUDE_FILE("unistd.h" HAVE_UNISTD_H)
 LA_CHECK_INCLUDE_FILE("utime.h" HAVE_UTIME_H)
@@ -618,6 +623,9 @@ LA_CHECK_INCLUDE_FILE("wctype.h" HAVE_WCTYPE_H)
 LA_CHECK_INCLUDE_FILE("windows.h" HAVE_WINDOWS_H)
 IF(ENABLE_CNG)
   LA_CHECK_INCLUDE_FILE("Bcrypt.h" HAVE_BCRYPT_H)
+  IF(HAVE_BCRYPT_H)
+    LIST(APPEND ADDITIONAL_LIBS "Bcrypt")
+  ENDIF(HAVE_BCRYPT_H)
 ELSE(ENABLE_CNG)
   UNSET(HAVE_BCRYPT_H CACHE)
 ENDIF(ENABLE_CNG)
@@ -1195,7 +1203,6 @@ CHECK_FUNCTION_EXISTS_GLIBC(chflags HAVE_CHFLAGS)
 CHECK_FUNCTION_EXISTS_GLIBC(chown HAVE_CHOWN)
 CHECK_FUNCTION_EXISTS_GLIBC(chroot HAVE_CHROOT)
 CHECK_FUNCTION_EXISTS_GLIBC(ctime_r HAVE_CTIME_R)
-CHECK_FUNCTION_EXISTS_GLIBC(dirfd HAVE_DIRFD)
 CHECK_FUNCTION_EXISTS_GLIBC(fchdir HAVE_FCHDIR)
 CHECK_FUNCTION_EXISTS_GLIBC(fchflags HAVE_FCHFLAGS)
 CHECK_FUNCTION_EXISTS_GLIBC(fchmod HAVE_FCHMOD)
@@ -1295,6 +1302,10 @@ CHECK_C_SOURCE_COMPILES(
   "#include <dirent.h>\nint main() {DIR *d = opendir(\".\"); struct dirent e,*r; return readdir_r(d,&e,&r);}"
   HAVE_READDIR_R)
 
+# dirfd can be either a function or a macro.
+CHECK_C_SOURCE_COMPILES(
+  "#include <dirent.h>\nint main() {DIR *d = opendir(\".\"); return dirfd(d);}"
+  HAVE_DIRFD)
 
 # Only detect readlinkat() if we also have AT_FDCWD in unistd.h.
 # NOTE: linux requires fcntl.h for AT_FDCWD.
@@ -1527,60 +1538,105 @@ CHECK_FILE_OFFSET_BITS()
 # Check for Extended Attribute libraries, headers, and functions
 #
 IF(ENABLE_XATTR)
-  LA_CHECK_INCLUDE_FILE(attr/xattr.h     HAVE_ATTR_XATTR_H)
-  LA_CHECK_INCLUDE_FILE(sys/xattr.h      HAVE_SYS_XATTR_H)
-  LA_CHECK_INCLUDE_FILE(sys/extattr.h      HAVE_SYS_EXTATTR_H)
   CHECK_LIBRARY_EXISTS(attr "setxattr" "" HAVE_LIBATTR)
   IF(HAVE_LIBATTR)
     SET(CMAKE_REQUIRED_LIBRARIES "attr")
   ENDIF(HAVE_LIBATTR)
   CHECK_SYMBOL_EXISTS(EXTATTR_NAMESPACE_USER "sys/types.h;sys/extattr.h" HAVE_DECL_EXTATTR_NAMESPACE_USER)
-  CHECK_FUNCTION_EXISTS_GLIBC(extattr_get_file HAVE_EXTATTR_GET_FILE)
-  CHECK_FUNCTION_EXISTS_GLIBC(extattr_list_file HAVE_EXTATTR_LIST_FILE)
-  CHECK_FUNCTION_EXISTS_GLIBC(extattr_set_fd HAVE_EXTATTR_SET_FD)
-  CHECK_FUNCTION_EXISTS_GLIBC(extattr_set_file HAVE_EXTATTR_SET_FILE)
-  CHECK_FUNCTION_EXISTS_GLIBC(fgetxattr HAVE_FGETXATTR)
-  CHECK_FUNCTION_EXISTS_GLIBC(flistxattr HAVE_FLISTXATTR)
-  CHECK_FUNCTION_EXISTS_GLIBC(fsetxattr HAVE_FSETXATTR)
-  CHECK_FUNCTION_EXISTS_GLIBC(getxattr HAVE_GETXATTR)
-  CHECK_FUNCTION_EXISTS_GLIBC(lgetxattr HAVE_LGETXATTR)
-  CHECK_FUNCTION_EXISTS_GLIBC(listxattr HAVE_LISTXATTR)
-  CHECK_FUNCTION_EXISTS_GLIBC(llistxattr HAVE_LLISTXATTR)
-  CHECK_FUNCTION_EXISTS_GLIBC(lsetxattr HAVE_LSETXATTR)
-  CHECK_FUNCTION_EXISTS_GLIBC(fgetea HAVE_FGETEA)
-  CHECK_FUNCTION_EXISTS_GLIBC(flistea HAVE_FLISTEA)
-  CHECK_FUNCTION_EXISTS_GLIBC(fsetea HAVE_FSETEA)
-  CHECK_FUNCTION_EXISTS_GLIBC(getea HAVE_GETEA)
-  CHECK_FUNCTION_EXISTS_GLIBC(lgetea HAVE_LGETEA)
-  CHECK_FUNCTION_EXISTS_GLIBC(listea HAVE_LISTEA)
-  CHECK_FUNCTION_EXISTS_GLIBC(llistea HAVE_LLISTEA)
-  CHECK_FUNCTION_EXISTS_GLIBC(lsetea HAVE_LSETEA)
+  CHECK_SYMBOL_EXISTS(XATTR_NOFOLLOW "sys/xattr.h" HAVE_DECL_XATTR_NOFOLLOW)
+  IF(HAVE_SYS_XATTR_H AND HAVE_DECL_XATTR_NOFOLLOW)
+    CHECK_FUNCTION_EXISTS(fgetxattr HAVE_FGETXATTR)
+    CHECK_FUNCTION_EXISTS(flistxattr HAVE_FLISTXATTR)
+    CHECK_FUNCTION_EXISTS(fsetxattr HAVE_FSETXATTR)
+    CHECK_FUNCTION_EXISTS(getxattr HAVE_GETXATTR)
+    CHECK_FUNCTION_EXISTS(listxattr HAVE_LISTXATTR)
+    CHECK_FUNCTION_EXISTS(setxattr HAVE_SETXATTR)
+    IF(HAVE_FGETXATTR AND
+       HAVE_FLISTXATTR AND
+       HAVE_FSETXATTR AND
+       HAVE_GETXATTR AND
+       HAVE_LISTXATTR AND
+       HAVE_SETXATTR)
+      SET(ARCHIVE_XATTR_DARWIN TRUE)
+    ENDIF()
+  ELSEIF(HAVE_SYS_EXTATTR_H AND HAVE_DECL_EXTATTR_NAMESPACE_USER)
+    # FreeBSD xattr support
+    CHECK_FUNCTION_EXISTS(extattr_get_fd HAVE_EXTATTR_GET_FD)
+    CHECK_FUNCTION_EXISTS(extattr_get_file HAVE_EXTATTR_GET_FILE)
+    CHECK_FUNCTION_EXISTS(extattr_get_link HAVE_EXTATTR_GET_LINK)
+    CHECK_FUNCTION_EXISTS(extattr_list_fd HAVE_EXTATTR_LIST_FD)
+    CHECK_FUNCTION_EXISTS(extattr_list_file HAVE_EXTATTR_LIST_FILE)
+    CHECK_FUNCTION_EXISTS(extattr_list_link HAVE_EXTATTR_LIST_LINK)
+    CHECK_FUNCTION_EXISTS(extattr_set_fd HAVE_EXTATTR_SET_FD)
+    CHECK_FUNCTION_EXISTS(extattr_set_link HAVE_EXTATTR_SET_LINK)
+    IF(HAVE_EXTATTR_GET_FD AND
+       HAVE_EXTATTR_GET_FILE AND
+       HAVE_EXTATTR_GET_LINK AND
+       HAVE_EXTATTR_LIST_FD AND
+       HAVE_EXTATTR_LIST_FILE AND
+       HAVE_EXTATTR_LIST_LINK AND
+       HAVE_EXTATTR_SET_FD AND
+       HAVE_EXTATTR_SET_LINK)
+      SET(ARCHIVE_XATTR_FREEBSD TRUE)
+    ENDIF()
+  ELSEIF(HAVE_SYS_XATTR_H OR HAVE_ATTR_XATTR_H)
+    # Linux xattr support
+    CHECK_FUNCTION_EXISTS_GLIBC(fgetxattr HAVE_FGETXATTR)
+    CHECK_FUNCTION_EXISTS_GLIBC(flistxattr HAVE_FLISTXATTR)
+    CHECK_FUNCTION_EXISTS_GLIBC(fsetxattr HAVE_FSETXATTR)
+    CHECK_FUNCTION_EXISTS_GLIBC(getxattr HAVE_GETXATTR)
+    CHECK_FUNCTION_EXISTS_GLIBC(lgetxattr HAVE_LGETXATTR)
+    CHECK_FUNCTION_EXISTS_GLIBC(listxattr HAVE_LISTXATTR)
+    CHECK_FUNCTION_EXISTS_GLIBC(llistxattr HAVE_LLISTXATTR)
+    CHECK_FUNCTION_EXISTS_GLIBC(lsetxattr HAVE_LSETXATTR)
+    IF(HAVE_FGETXATTR AND
+       HAVE_FLISTXATTR AND
+       HAVE_FSETXATTR AND
+       HAVE_GETXATTR AND
+       HAVE_LGETXATTR AND
+       HAVE_LISTXATTR AND
+       HAVE_LLISTXATTR AND
+       HAVE_LSETXATTR)
+      SET(ARCHIVE_XATTR_LINUX TRUE)
+    ENDIF()
+  ELSEIF(HAVE_SYS_EA_H)
+    # AIX xattr support
+    CHECK_FUNCTION_EXISTS(fgetea HAVE_FGETEA)
+    CHECK_FUNCTION_EXISTS(flistea HAVE_FLISTEA)
+    CHECK_FUNCTION_EXISTS(fsetea HAVE_FSETEA)
+    CHECK_FUNCTION_EXISTS(getea HAVE_GETEA)
+    CHECK_FUNCTION_EXISTS(lgetea HAVE_LGETEA)
+    CHECK_FUNCTION_EXISTS(listea HAVE_LISTEA)
+    CHECK_FUNCTION_EXISTS(llistea HAVE_LLISTEA)
+    CHECK_FUNCTION_EXISTS(lsetea HAVE_LSETEA)
+    IF(HAVE_FGETEA AND
+       HAVE_FLISTEA AND
+       HAVE_FSETEA AND
+       HAVE_GETEA AND
+       HAVE_LGETEA AND
+       HAVE_LISTEA AND
+       HAVE_LLISTEA AND
+       HAVE_LSETEA)
+      SET(ARCHIVE_XATTR_AIX TRUE)
+    ENDIF()
+  ENDIF()
+
+  IF(ARCHIVE_XATTR_DARWIN)
+    MESSAGE(STATUS "Extended attributes support: Darwin")
+  ELSEIF(ARCHIVE_XATTR_FREEBSD)
+    MESSAGE(STATUS "Extended attributes support: FreeBSD")
+  ELSEIF(ARCHIVE_XATTR_LINUX)
+    MESSAGE(STATUS "Extended attributes support: Linux")
+  ELSEIF(ARCHIVE_XATTR_AIX)
+    MESSAGE(STATUS "Extended attributes support: AIX")
+  ELSE()
+    MESSAGE(STATUS "Extended attributes support: none")
+  ENDIF()
 ELSE(ENABLE_XATTR)
-  SET(HAVE_ATTR_LIB FALSE)
-  SET(HAVE_ATTR_XATTR_H FALSE)
-  SET(HAVE_DECL_EXTATTR_NAMESPACE_USER FALSE)
-  SET(HAVE_EXTATTR_GET_FILE FALSE)
-  SET(HAVE_EXTATTR_LIST_FILE FALSE)
-  SET(HAVE_EXTATTR_SET_FD FALSE)
-  SET(HAVE_EXTATTR_SET_FILE FALSE)
-  SET(HAVE_FGETEA FALSE)
-  SET(HAVE_FGETXATTR FALSE)
-  SET(HAVE_FLISTEA FALSE)
-  SET(HAVE_FLISTXATTR FALSE)
-  SET(HAVE_FSETEA FALSE)
-  SET(HAVE_FSETXATTR FALSE)
-  SET(HAVE_GETEA FALSE)
-  SET(HAVE_GETXATTR FALSE)
-  SET(HAVE_LGETEA FALSE)
-  SET(HAVE_LGETXATTR FALSE)
-  SET(HAVE_LISTEA FALSE)
-  SET(HAVE_LISTXATTR FALSE)
-  SET(HAVE_LLISTEA FALSE)
-  SET(HAVE_LLISTXATTR FALSE)
-  SET(HAVE_LSETEA FALSE)
-  SET(HAVE_LSETXATTR FALSE)
-  SET(HAVE_SYS_EXTATTR_H FALSE)
-  SET(HAVE_SYS_XATTR_H FALSE)
+  SET(ARCHIVE_XATTR_DARWIN FALSE)
+  SET(ARCHIVE_XATTR_FREEBSD FALSE)
+  SET(ARCHIVE_XATTR_LINUX FALSE)
+  SET(ARCHIVE_XATTR_AIX FALSE)
 ENDIF(ENABLE_XATTR)
 
 #
@@ -1592,78 +1648,212 @@ ENDIF(ENABLE_XATTR)
 # which makes the following checks rather more complex than I would like.
 #
 IF(ENABLE_ACL)
+  # Solaris and derivates ACLs
+  CHECK_FUNCTION_EXISTS(acl HAVE_ACL)
+  CHECK_FUNCTION_EXISTS(facl HAVE_FACL)
+
+  # Libacl
   CHECK_LIBRARY_EXISTS(acl "acl_get_file" "" HAVE_LIBACL)
   IF(HAVE_LIBACL)
     SET(CMAKE_REQUIRED_LIBRARIES "acl")
     FIND_LIBRARY(ACL_LIBRARY NAMES acl)
     LIST(APPEND ADDITIONAL_LIBS ${ACL_LIBRARY})
   ENDIF(HAVE_LIBACL)
-  #
-  CHECK_FUNCTION_EXISTS_GLIBC(acl_create_entry HAVE_ACL_CREATE_ENTRY)
-  CHECK_FUNCTION_EXISTS_GLIBC(acl_init HAVE_ACL_INIT)
-  CHECK_FUNCTION_EXISTS_GLIBC(acl_set_fd HAVE_ACL_SET_FD)
-  CHECK_FUNCTION_EXISTS_GLIBC(acl_set_fd_np HAVE_ACL_SET_FD_NP)
-  CHECK_FUNCTION_EXISTS_GLIBC(acl_set_file HAVE_ACL_SET_FILE)
-  CHECK_TYPE_EXISTS(acl_permset_t "${INCLUDES}"    HAVE_ACL_PERMSET_T)
-
-  # The "acl_get_perm()" function was omitted from the POSIX draft.
-  # (It's a pretty obvious oversight; otherwise, there's no way to
-  # test for specific permissions in a permset.)  Linux uses the obvious
-  # name, FreeBSD adds _np to mark it as "non-Posix extension."
-  # Test for both as a double-check that we really have POSIX-style ACL support.
-  CHECK_FUNCTION_EXISTS(acl_get_fd_np HAVE_ACL_GET_FD_NP)
-  CHECK_FUNCTION_EXISTS(acl_get_perm HAVE_ACL_GET_PERM)
-  CHECK_FUNCTION_EXISTS(acl_get_perm_np HAVE_ACL_GET_PERM_NP)
-  CHECK_FUNCTION_EXISTS(acl_get_link HAVE_ACL_GET_LINK)
-  CHECK_FUNCTION_EXISTS(acl_get_link_np HAVE_ACL_GET_LINK_NP)
-  CHECK_FUNCTION_EXISTS(acl_is_trivial_np HAVE_ACL_IS_TRIVIAL_NP)
-  CHECK_FUNCTION_EXISTS(acl_set_link_np HAVE_ACL_SET_LINK_NP)
-  CHECK_SYMBOL_EXISTS(ACL_TYPE_NFS4 "${INCLUDES}" HAVE_ACL_TYPE_NFS4)
-
-  # MacOS has an acl.h that isn't POSIX.  It can be detected by
-  # checking for ACL_USER
-  CHECK_SYMBOL_EXISTS(ACL_USER "${INCLUDES}" HAVE_ACL_USER)
-  CHECK_C_SOURCE_COMPILES("#include <sys/types.h>
+
+  CHECK_TYPE_EXISTS(acl_t "sys/types.h;sys/acl.h" HAVE_ACL_T)
+  CHECK_TYPE_EXISTS(acl_entry_t "sys/types.h;sys/acl.h" HAVE_ACL_ENTRY_T)
+  CHECK_TYPE_EXISTS(acl_permset_t "sys/types.h;sys/acl.h" HAVE_ACL_PERMSET_T)
+  CHECK_TYPE_EXISTS(acl_tag_t "sys/types.h;sys/acl.h" HAVE_ACL_TAG_T)
+
+  IF(HAVE_ACL AND HAVE_FACL)
+    CHECK_TYPE_EXISTS(aclent_t "sys/acl.h" HAVE_ACLENT_T)
+    IF(HAVE_ACLENT_T)
+      CHECK_SYMBOL_EXISTS(GETACL "sys/acl.h" HAVE_DECL_GETACL)
+      CHECK_SYMBOL_EXISTS(GETACLCNT "sys/acl.h" HAVE_DECL_GETACLCNT)
+      CHECK_SYMBOL_EXISTS(SETACL "sys/acl.h" HAVE_DECL_SETACL)
+      IF(HAVE_DECL_GETACL AND
+         HAVE_DECL_GETACLCNT AND
+         HAVE_DECL_SETACL)
+        SET(ARCHIVE_ACL_SUNOS TRUE)
+      ENDIF()
+      CHECK_TYPE_EXISTS(ace_t "sys/acl.h" HAVE_ACE_T)
+      IF(HAVE_ACE_T)
+        CHECK_SYMBOL_EXISTS(ACE_GETACL "sys/acl.h" HAVE_DECL_ACE_GETACL)
+        CHECK_SYMBOL_EXISTS(ACE_GETACLCNT "sys/acl.h" HAVE_DECL_ACE_GETACLCNT)
+        CHECK_SYMBOL_EXISTS(ACE_SETACL "sys/acl.h" HAVE_DECL_ACE_SETACL)
+        IF(HAVE_DECL_ACE_GETACL AND
+           HAVE_DECL_ACE_GETACLCNT AND
+           HAVE_DECL_ACE_SETACL)
+          SET(ARCHIVE_ACL_SUNOS_NFS4 TRUE)
+        ENDIF()
+      ENDIF(HAVE_ACE_T)
+    ENDIF(HAVE_ACLENT_T)
+  ENDIF(HAVE_ACL AND HAVE_FACL)
+
+  IF(HAVE_ACL_T AND HAVE_ACL_ENTRY_T AND HAVE_ACL_PERMSET_T AND HAVE_ACL_TAG_T)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_add_perm HAVE_ACL_ADD_PERM)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_clear_perms HAVE_ACL_CLEAR_PERMS)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_create_entry HAVE_ACL_CREATE_ENTRY)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_delete_def_file HAVE_ACL_DELETE_DEF_FILE)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_free HAVE_ACL_FREE)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_entry HAVE_ACL_GET_ENTRY)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_fd HAVE_ACL_GET_FD)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_file HAVE_ACL_GET_FILE)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_permset HAVE_ACL_GET_PERMSET)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_qualifier HAVE_ACL_GET_QUALIFIER)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_tag_type HAVE_ACL_GET_TAG_TYPE)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_init HAVE_ACL_INIT)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_set_fd HAVE_ACL_SET_FD)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_set_file HAVE_ACL_SET_FILE)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_set_qualifier HAVE_ACL_SET_QUALIFIER)
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_set_tag_type HAVE_ACL_SET_TAG_TYPE)
+    IF(HAVE_ACL_ADD_PERM AND
+       HAVE_ACL_CLEAR_PERMS AND
+       HAVE_ACL_CREATE_ENTRY AND
+       HAVE_ACL_DELETE_DEF_FILE AND
+       HAVE_ACL_FREE AND
+       HAVE_ACL_GET_ENTRY AND
+       HAVE_ACL_GET_FD AND
+       HAVE_ACL_GET_FILE AND
+       HAVE_ACL_GET_PERMSET AND
+       HAVE_ACL_GET_QUALIFIER AND
+       HAVE_ACL_GET_TAG_TYPE AND
+       HAVE_ACL_INIT AND
+       HAVE_ACL_SET_FD AND
+       HAVE_ACL_SET_FILE AND
+       HAVE_ACL_SET_QUALIFIER AND
+       HAVE_ACL_SET_TAG_TYPE)
+         SET(HAVE_POSIX_ACL_FUNCS 1)
+    ENDIF()
+
+    CHECK_FUNCTION_EXISTS_GLIBC(acl_get_perm HAVE_ACL_GET_PERM)
+
+    IF(HAVE_POSIX_ACL_FUNCS AND HAVE_ACL_LIBACL_H AND HAVE_LIBACL AND
+       HAVE_ACL_GET_PERM)
+      SET(ARCHIVE_ACL_LIBACL TRUE)
+    ELSE()
+      CHECK_FUNCTION_EXISTS(acl_add_flag_np HAVE_ACL_ADD_FLAG_NP)
+      CHECK_FUNCTION_EXISTS(acl_clear_flags_np HAVE_ACL_CLEAR_FLAGS_NP)
+      CHECK_FUNCTION_EXISTS(acl_get_brand_np HAVE_ACL_GET_BRAND_NP)
+      CHECK_FUNCTION_EXISTS(acl_get_entry_type_np HAVE_ACL_GET_ENTRY_TYPE_NP)
+      CHECK_FUNCTION_EXISTS(acl_get_flag_np HAVE_ACL_GET_FLAG_NP)
+      CHECK_FUNCTION_EXISTS(acl_get_flagset_np HAVE_ACL_GET_FLAGSET_NP)
+      CHECK_FUNCTION_EXISTS(acl_get_fd_np HAVE_ACL_GET_FD_NP)
+      CHECK_FUNCTION_EXISTS(acl_get_link_np HAVE_ACL_GET_LINK_NP)
+      CHECK_FUNCTION_EXISTS(acl_get_perm_np HAVE_ACL_GET_PERM_NP)
+      CHECK_FUNCTION_EXISTS(acl_is_trivial_np HAVE_ACL_IS_TRIVIAL_NP)
+      CHECK_FUNCTION_EXISTS(acl_set_entry_type_np HAVE_ACL_SET_ENTRY_TYPE_NP)
+      CHECK_FUNCTION_EXISTS(acl_set_fd_np HAVE_ACL_SET_FD_NP)
+      CHECK_FUNCTION_EXISTS(acl_set_link_np HAVE_ACL_SET_LINK_NP)
+      CHECK_FUNCTION_EXISTS(mbr_gid_to_uuid HAVE_MBR_GID_TO_UUID)
+      CHECK_FUNCTION_EXISTS(mbr_uid_to_uuid HAVE_MBR_UID_TO_UUID)
+      CHECK_FUNCTION_EXISTS(mbr_uuid_to_id HAVE_MBR_UUID_TO_ID)
+
+      CHECK_C_SOURCE_COMPILES("#include <sys/types.h>
 #include <sys/acl.h>
-int main(void) { return ACL_TYPE_EXTENDED; }" HAVE_ACL_TYPE_EXTENDED)
+int main(void) { return ACL_TYPE_EXTENDED; }" HAVE_DECL_ACL_TYPE_EXTENDED)
+      CHECK_C_SOURCE_COMPILES("#include <sys/types.h>
+#include <sys/acl.h>
+int main(void) { return ACL_SYNCHRONIZE; }" HAVE_DECL_ACL_SYNCHRONIZE)
+      CHECK_SYMBOL_EXISTS(ACL_TYPE_NFS4 "sys/acl.h" HAVE_DECL_ACL_TYPE_NFS4)
+      CHECK_SYMBOL_EXISTS(ACL_USER "sys/acl.h" HAVE_DECL_ACL_USER)
+
+      IF(HAVE_POSIX_ACL_FUNCS AND
+         HAVE_ACL_GET_FD_NP AND
+         HAVE_ACL_GET_PERM_NP AND
+         NOT HAVE_ACL_GET_PERM AND
+         HAVE_ACL_SET_FD_NP)
+        IF(HAVE_DECL_ACL_USER)
+          SET(ARCHIVE_ACL_FREEBSD TRUE)
+          IF(HAVE_DECL_ACL_TYPE_NFS4 AND
+             HAVE_ACL_ADD_FLAG_NP AND
+             HAVE_ACL_CLEAR_FLAGS_NP AND
+             HAVE_ACL_GET_BRAND_NP AND
+             HAVE_ACL_GET_ENTRY_TYPE_NP AND
+             HAVE_ACL_GET_FLAGSET_NP AND
+             HAVE_ACL_SET_ENTRY_TYPE_NP)
+            SET(ARCHIVE_ACL_FREEBSD_NFS4 TRUE)
+          ENDIF()
+        ELSEIF(HAVE_DECL_ACL_TYPE_EXTENDED AND
+               HAVE_MEMBERSHIP_H AND
+               HAVE_ACL_ADD_FLAG_NP AND
+               HAVE_ACL_CLEAR_FLAGS_NP AND
+               HAVE_ACL_GET_FLAGSET_NP AND
+               HAVE_ACL_GET_LINK_NP AND
+               HAVE_ACL_SET_LINK_NP AND
+               HAVE_MBR_UID_TO_UUID AND
+               HAVE_MBR_GID_TO_UUID AND
+               HAVE_MBR_UUID_TO_ID)
+          SET(ARCHIVE_ACL_DARWIN TRUE)
+        ENDIF()
+      ENDIF()
+    ENDIF()
+  ENDIF(HAVE_ACL_T AND HAVE_ACL_ENTRY_T AND HAVE_ACL_PERMSET_T AND
+        HAVE_ACL_TAG_T)
+
+  # Richacl
+  CHECK_LIBRARY_EXISTS(richacl "richacl_get_file" "" HAVE_LIBRICHACL)
+  IF(HAVE_LIBRICHACL)
+    SET(CMAKE_REQUIRED_LIBRARIES "richacl")
+    FIND_LIBRARY(RICHACL_LIBRARY NAMES richacl)
+    LIST(APPEND ADDITIONAL_LIBS ${RICHACL_LIBRARY})
+  ENDIF(HAVE_LIBRICHACL)
+
+  CHECK_STRUCT_HAS_MEMBER("struct richace" e_type "sys/richacl.h"
+    HAVE_STRUCT_RICHACE)
+  CHECK_STRUCT_HAS_MEMBER("struct richacl" a_flags "sys/richacl.h"
+    HAVE_STRUCT_RICHACL)
+
+  IF(HAVE_LIBRICHACL AND HAVE_STRUCT_RICHACL AND HAVE_STRUCT_RICHACE)
+    CHECK_FUNCTION_EXISTS_GLIBC(richacl_alloc HAVE_RICHACL_ALLOC)
+    CHECK_FUNCTION_EXISTS_GLIBC(richacl_equiv_mode HAVE_RICHACL_EQUIV_MODE)
+    CHECK_FUNCTION_EXISTS_GLIBC(richacl_free HAVE_RICHACL_FREE)
+    CHECK_FUNCTION_EXISTS_GLIBC(richacl_get_fd HAVE_RICHACL_GET_FD)
+    CHECK_FUNCTION_EXISTS_GLIBC(richacl_get_file HAVE_RICHACL_GET_FILE)
+    CHECK_FUNCTION_EXISTS_GLIBC(richacl_set_fd HAVE_RICHACL_SET_FD)
+    CHECK_FUNCTION_EXISTS_GLIBC(richacl_set_file HAVE_RICHACL_SET_FILE)
+    IF(HAVE_RICHACL_ALLOC AND
+       HAVE_RICHACL_EQUIV_MODE AND
+       HAVE_RICHACL_FREE AND
+       HAVE_RICHACL_GET_FD AND
+       HAVE_RICHACL_GET_FILE AND
+       HAVE_RICHACL_SET_FD AND
+       HAVE_RICHACL_SET_FILE)
+      SET(ARCHIVE_ACL_LIBRICHACL TRUE)
+    ENDIF()
+  ENDIF(HAVE_LIBRICHACL AND HAVE_STRUCT_RICHACL AND HAVE_STRUCT_RICHACE)
+
+  IF(ARCHIVE_ACL_DARWIN)
+    MESSAGE(STATUS "ACL support: Darwin (limited NFSv4)")
+  ELSEIF(ARCHIVE_ACL_FREEBSD_NFS4)
+    MESSAGE(STATUS "ACL support: FreeBSD (POSIX.1e and NFSv4)")
+  ELSEIF(ARCHIVE_ACL_FREEBSD)
+    MESSAGE(STATUS "ACL support: FreeBSD (POSIX.1e)")
+  ELSEIF(ARCHIVE_ACL_LIBACL OR ARCHIVE_ACL_LIBRICHACL)
+    IF(ARCHIVE_ACL_LIBACL AND ARCHIVE_ACL_LIBRICHACL)
+      MESSAGE(STATUS "ACL support: libacl (POSIX.1e) + librichacl (NFSv4)")
+    ELSEIF(ARCHIVE_ACL_LIBRICHACL)
+      MESSAGE(STATUS "ACL support: librichacl (NFSv4)")
+    ELSE()
+      MESSAGE(STATUS "ACL support: libacl (POSIX.1e)")
+    ENDIF()
+  ELSEIF(ARCHIVE_ACL_SUNOS_NFS4)
+    MESSAGE(STATUS "ACL support: Solaris (POSIX.1e and NFSv4)")
+  ELSEIF(ARCHIVE_ACL_SUNOS)
+    MESSAGE(STATUS "ACL support: Solaris (POSIX.1e)")
+  ELSE()
+    MESSAGE(STATUS "ACL support: none")
+  ENDIF()
 
-  # Solaris and derivates ACLs
-  CHECK_LIBRARY_EXISTS(sec "acl_get" "" HAVE_LIBSEC)
-  IF(HAVE_LIBSEC)
-    SET(CMAKE_REQUIRED_LIBRARIES "sec")
-    FIND_LIBRARY(SEC_LIBRARY NAMES sec)
-    LIST(APPEND ADDITIONAL_LIBS ${SEC_LIBRARY})
-  ENDIF(HAVE_LIBSEC)
-  #
-  CHECK_TYPE_EXISTS(aclent_t "${INCLUDES}" HAVE_ACLENT_T)
-  CHECK_TYPE_EXISTS(ace_t "${INCLUDES}" HAVE_ACE_T)
-  CHECK_FUNCTION_EXISTS(acl_get HAVE_FACL_GET)
-  CHECK_FUNCTION_EXISTS(facl_get HAVE_FACL_GET)
-  CHECK_FUNCTION_EXISTS(acl_set HAVE_FACL_SET)
-  CHECK_FUNCTION_EXISTS(facl_set HAVE_FACL_SET)
 ELSE(ENABLE_ACL)
   # If someone runs cmake, then disables ACL support, we need
   # to forcibly override the cached values for these.
-  SET(HAVE_ACL_CREATE_ENTRY FALSE)
-  SET(HAVE_ACL_GET_LINK FALSE)
-  SET(HAVE_ACL_GET_LINK_NP FALSE)
-  SET(HAVE_ACL_GET_PERM FALSE)
-  SET(HAVE_ACL_GET_PERM_NP FALSE)
-  SET(HAVE_ACL_INIT FALSE)
-  SET(HAVE_ACL_LIB FALSE)
-  SET(HAVE_ACL_PERMSET_T FALSE)
-  SET(HAVE_ACL_SET_FD FALSE)
-  SET(HAVE_ACL_SET_FD_NP FALSE)
-  SET(HAVE_ACL_SET_FILE FALSE)
-  SET(HAVE_ACL_TYPE_NFS4 FALSE)
-  SET(HAVE_ACL_USER FALSE)
-  SET(HAVE_ACL_TYPE_EXTENDED FALSE)
-  SET(HAVE_ACL_GET FALSE)
-  SET(HAVE_ACLENT_T FALSE)
-  SET(HAVE_ACE_T FALSE)
-  SET(HAVE_FACL_GET FALSE)
-  SET(HAVE_ACL_SET FALSE)
-  SET(HAVE_FACL_SET FALSE)
+  SET(ARCHIVE_ACL_DARWIN FALSE)
+  SET(ARCHIVE_ACL_FREEBSD FALSE)
+  SET(ARCHIVE_ACL_FREEBSD_NFS4 FALSE)
+  SET(ARCHIVE_ACL_LIBACL FALSE)
+  SET(ARCHIVE_ACL_SUNOS FALSE)
+  SET(ARCHIVE_ACL_SUNOS_NFS4 FALSE)
 ENDIF(ENABLE_ACL)
 
 #
diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in
index 923a78e..e646213 100644
--- a/build/cmake/config.h.in
+++ b/build/cmake/config.h.in
@@ -179,6 +179,27 @@ typedef uint64_t uintmax_t;
 /* Define ZLIB_WINAPI if zlib was built on Visual Studio. */
 #cmakedefine ZLIB_WINAPI 1
 
+/* Darwin ACL support */
+#cmakedefine ARCHIVE_ACL_DARWIN 1
+
+/* FreeBSD ACL support */
+#cmakedefine ARCHIVE_ACL_FREEBSD 1
+
+/* FreeBSD NFSv4 ACL support */
+#cmakedefine ARCHIVE_ACL_FREEBSD_NFS4 1
+
+/* Linux POSIX.1e ACL support via libacl */
+#cmakedefine ARCHIVE_ACL_LIBACL 1
+
+/* Linux NFSv4 ACL support via librichacl */
+#cmakedefine ARCHIVE_ACL_LIBRICHACL 1
+
+/* Solaris ACL support */
+#cmakedefine ARCHIVE_ACL_SUNOS 1
+
+/* Solaris NFSv4 ACL support */
+#cmakedefine ARCHIVE_ACL_SUNOS_NFS4 1
+
 /* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */
 #cmakedefine ARCHIVE_CRYPTO_MD5_LIBC 1
 
@@ -281,6 +302,18 @@ typedef uint64_t uintmax_t;
 /* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */
 #cmakedefine ARCHIVE_CRYPTO_SHA512_WIN 1
 
+/* AIX xattr support */
+#cmakedefine ARCHIVE_XATTR_AIX 1
+
+/* Darwin xattr support */
+#cmakedefine ARCHIVE_XATTR_DARWIN 1
+
+/* FreeBSD xattr support */
+#cmakedefine ARCHIVE_XATTR_FREEBSD 1
+
+/* Linux xattr support */
+#cmakedefine ARCHIVE_XATTR_LINUX 1
+
 /* Version number of bsdcpio */
 #cmakedefine BSDCPIO_VERSION_STRING "${BSDCPIO_VERSION_STRING}"
 
@@ -326,15 +359,6 @@ typedef uint64_t uintmax_t;
 /* Define to 1 if you have the `acl_set_file' function. */
 #cmakedefine HAVE_ACL_SET_FILE 1
 
-/* True for FreeBSD with NFSv4 ACL support */
-#cmakedefine HAVE_ACL_TYPE_NFS4 1
-
-/* True for MacOS ACL support */
-#cmakedefine HAVE_ACL_TYPE_EXTENDED 1
-
-/* True for systems with POSIX ACL support */
-#cmakedefine HAVE_ACL_USER 1
-
 /* Define to 1 if you have the `arc4random_buf' function. */
 #cmakedefine HAVE_ARC4RANDOM_BUF 1
 
@@ -371,6 +395,34 @@ typedef uint64_t uintmax_t;
 /* Define to 1 if you have the `cygwin_conv_path' function. */
 #cmakedefine HAVE_CYGWIN_CONV_PATH 1
 
+/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_ACE_GETACL 1
+
+/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_ACE_GETACLCNT 1
+
+/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_ACE_SETACL 1
+
+/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if
+   you don't. */
+#cmakedefine HAVE_DECL_ACL_SYNCHRONIZE 1
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if
+   you don't. */
+#cmakedefine HAVE_DECL_ACL_TYPE_EXTENDED 1
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_ACL_TYPE_NFS4 1
+
+/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_ACL_USER 1
+
 /* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you
    don't. */
 #cmakedefine HAVE_DECL_INT32_MAX 1
@@ -395,6 +447,10 @@ typedef uint64_t uintmax_t;
    don't. */
 #cmakedefine HAVE_DECL_INTMAX_MIN 1
 
+/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.
+   */
+#cmakedefine HAVE_DECL_SETACL 1
+
 /* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you
    don't. */
 #cmakedefine HAVE_DECL_SIZE_MAX 1
@@ -419,6 +475,10 @@ typedef uint64_t uintmax_t;
    don't. */
 #cmakedefine HAVE_DECL_UINTMAX_MAX 1
 
+/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if
+   you don't. */
+#cmakedefine HAVE_DECL_XATTR_NOFOLLOW 1
+
 /* Define to 1 if you have the <direct.h> header file. */
 #cmakedefine HAVE_DIRECT_H 1
 
@@ -468,6 +528,14 @@ typedef uint64_t uintmax_t;
 /* Define to 1 if EXTATTR_NAMESPACE_USER is defined in sys/extattr.h. */
 #cmakedefine HAVE_DECL_EXTATTR_NAMESPACE_USER 1
 
+/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.
+   */
+#cmakedefine HAVE_DECL_GETACL 1
+
+/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_GETACLCNT 1
+
 /* Define to 1 if you have the `fchdir' function. */
 #cmakedefine HAVE_FCHDIR 1
 
@@ -742,6 +810,9 @@ typedef uint64_t uintmax_t;
 /* Define to 1 if you have the `mbrtowc' function. */
 #cmakedefine HAVE_MBRTOWC 1
 
+/* Define to 1 if you have the <membership.h> header file. */
+#cmakedefine HAVE_MEMBERSHIP_H 1
+
 /* Define to 1 if you have the `memmove' function. */
 #cmakedefine HAVE_MEMMOVE 1
 
@@ -979,6 +1050,9 @@ typedef uint64_t uintmax_t;
 /* Define to 1 if you have the <sys/poll.h> header file. */
 #cmakedefine HAVE_SYS_POLL_H 1
 
+/* Define to 1 if you have the <sys/richacl.h> header file. */
+#cmakedefine HAVE_SYS_RICHACL_H 1
+
 /* Define to 1 if you have the <sys/select.h> header file. */
 #cmakedefine HAVE_SYS_SELECT_H 1
 
diff --git a/build/version b/build/version
index ef83457..2dd0839 100644
--- a/build/version
+++ b/build/version
@@ -1 +1 @@
-3003001
+3003002
diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt
index 1f85c01..5e958da 100644
--- a/libarchive/CMakeLists.txt
+++ b/libarchive/CMakeLists.txt
@@ -14,6 +14,7 @@ SET(include_HEADERS
 # Sources and private headers
 SET(libarchive_SOURCES
   archive_acl.c
+  archive_acl_private.h
   archive_check_magic.c
   archive_cmdline.c
   archive_cmdline_private.h
@@ -47,6 +48,8 @@ SET(libarchive_SOURCES
   archive_pathmatch.c
   archive_pathmatch.h
   archive_platform.h
+  archive_platform_acl.h
+  archive_platform_xattr.h
   archive_ppmd_private.h
   archive_ppmd7.c
   archive_ppmd7_private.h
@@ -106,9 +109,9 @@ SET(libarchive_SOURCES
   archive_string_composition.h
   archive_string_sprintf.c
   archive_util.c
+  archive_version_details.c
   archive_virtual.c
   archive_write.c
-  archive_write_disk_acl.c
   archive_write_disk_posix.c
   archive_write_disk_private.h
   archive_write_disk_set_standard_lookup.c
@@ -210,6 +213,16 @@ IF(WIN32 AND NOT CYGWIN)
   LIST(APPEND libarchive_SOURCES filter_fork_windows.c)
 ENDIF(WIN32 AND NOT CYGWIN)
 
+IF(ARCHIVE_ACL_DARWIN)
+  LIST(APPEND libarchive_SOURCES archive_disk_acl_darwin.c)
+ELSEIF(ARCHIVE_ACL_FREEBSD)
+  LIST(APPEND libarchive_SOURCES archive_disk_acl_freebsd.c)
+ELSEIF(ARCHIVE_ACL_LIBACL)
+  LIST(APPEND libarchive_SOURCES archive_disk_acl_linux.c)
+ELSEIF(ARCHIVE_ACL_SUNOS)
+  LIST(APPEND libarchive_SOURCES archive_disk_acl_sunos.c)
+ENDIF()
+
 # Libarchive is a shared library
 ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS})
 TARGET_LINK_LIBRARIES(archive ${ADDITIONAL_LIBS})
diff --git a/libarchive/archive.h b/libarchive/archive.h
index d400735..316a68a 100644
--- a/libarchive/archive.h
+++ b/libarchive/archive.h
@@ -36,7 +36,7 @@
  * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
  */
 /* Note: Compiler will complain if this does not match archive_entry.h! */
-#define	ARCHIVE_VERSION_NUMBER 3003001
+#define	ARCHIVE_VERSION_NUMBER 3003002
 
 #include <sys/stat.h>
 #include <stddef.h>  /* for wchar_t */
@@ -155,7 +155,7 @@ __LA_DECL int		archive_version_number(void);
 /*
  * Textual name/version of the library, useful for version displays.
  */
-#define	ARCHIVE_VERSION_ONLY_STRING "3.3.1"
+#define	ARCHIVE_VERSION_ONLY_STRING "3.3.2"
 #define	ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
 __LA_DECL const char *	archive_version_string(void);
 
diff --git a/libarchive/archive_check_magic.c b/libarchive/archive_check_magic.c
index c695e58..288ce23 100644
--- a/libarchive/archive_check_magic.c
+++ b/libarchive/archive_check_magic.c
@@ -62,7 +62,7 @@ errmsg(const char *m)
 	}
 }
 
-static void
+static __LA_DEAD void
 diediedie(void)
 {
 #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
diff --git a/libarchive/archive_disk_acl_darwin.c b/libarchive/archive_disk_acl_darwin.c
new file mode 100644
index 0000000..48ad016
--- /dev/null
+++ b/libarchive/archive_disk_acl_darwin.c
@@ -0,0 +1,559 @@
+/*-
+ * Copyright (c) 2017 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+#if ARCHIVE_ACL_DARWIN
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_MEMBERSHIP_H
+#include <membership.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#define _ACL_PRIVATE /* For debugging */
+#include <sys/acl.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+#include "archive_write_disk_private.h"
+
+typedef struct {
+	const int a_perm;	/* Libarchive permission or flag */
+	const int p_perm;	/* Platform permission or flag */
+} acl_perm_map_t;
+
+static const acl_perm_map_t acl_nfs4_perm_map[] = {
+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY},
+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY},
+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER},
+#if HAVE_DECL_ACL_SYNCHRONIZE
+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
+#endif
+};
+
+static const int acl_nfs4_perm_map_size =
+    (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));
+
+static const acl_perm_map_t acl_nfs4_flag_map[] = {
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED},
+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT}
+};
+
+static const int acl_nfs4_flag_map_size =
+    (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
+
+static int translate_guid(struct archive *a, acl_entry_t acl_entry,
+    int *ae_id, int *ae_tag, const char **ae_name)
+{
+	void *q;
+	uid_t ugid;
+	int r, idtype;
+
+	q = acl_get_qualifier(acl_entry);
+	if (q == NULL)
+		return (1);
+	r = mbr_uuid_to_id((const unsigned char *)q, &ugid, &idtype);
+	if (r != 0) {
+		acl_free(q);
+		return (1);
+	}
+	if (idtype == ID_TYPE_UID) {
+		*ae_tag = ARCHIVE_ENTRY_ACL_USER;
+		*ae_id = ugid;
+		*ae_name = archive_read_disk_uname(a, *ae_id);
+	} else if (idtype == ID_TYPE_GID) {
+		*ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+		*ae_id = ugid;
+		*ae_name = archive_read_disk_gname(a, *ae_id);
+	} else
+		r = 1;
+
+	acl_free(q);
+	return (r);
+}
+
+static void
+add_trivial_nfs4_acl(struct archive_entry *entry)
+{
+	mode_t mode;
+	int i;
+	const int rperm = ARCHIVE_ENTRY_ACL_READ_DATA;
+	const int wperm = ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA;
+	const int eperm = ARCHIVE_ENTRY_ACL_EXECUTE;
+	const int pubset = ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+	const int ownset = pubset | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+
+	struct {
+	    const int type;
+	    const int tag;
+	    int permset;
+	} tacl_entry[] = {
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},
+	    {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},
+	    {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0},
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, ownset},
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_GROUP_OBJ, pubset},
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EVERYONE, pubset}
+	};
+
+	mode = archive_entry_mode(entry);
+
+	/* Permissions for everyone@ */
+	if (mode & 0004)
+		tacl_entry[5].permset |= rperm;
+	if (mode & 0002)
+		tacl_entry[5].permset |= wperm;
+	if (mode & 0001)
+		tacl_entry[5].permset |= eperm;
+
+	/* Permissions for group@ */
+	if (mode & 0040)
+		tacl_entry[4].permset |= rperm;
+	else if (mode & 0004)
+		tacl_entry[2].permset |= rperm;
+	if (mode & 0020)
+		tacl_entry[4].permset |= wperm;
+	else if (mode & 0002)
+		tacl_entry[2].permset |= wperm;
+	if (mode & 0010)
+		tacl_entry[4].permset |= eperm;
+	else if (mode & 0001)
+		tacl_entry[2].permset |= eperm;
+
+	/* Permissions for owner@ */
+	if (mode & 0400) {
+		tacl_entry[3].permset |= rperm;
+		if (!(mode & 0040) && (mode & 0004))
+			tacl_entry[0].permset |= rperm;
+	} else if ((mode & 0040) || (mode & 0004))
+		tacl_entry[1].permset |= rperm;
+	if (mode & 0200) {
+		tacl_entry[3].permset |= wperm;
+		if (!(mode & 0020) && (mode & 0002))
+			tacl_entry[0].permset |= wperm;
+	} else if ((mode & 0020) || (mode & 0002))
+		tacl_entry[1].permset |= wperm;
+	if (mode & 0100) {
+		tacl_entry[3].permset |= eperm;
+		if (!(mode & 0010) && (mode & 0001))
+			tacl_entry[0].permset |= eperm;
+	} else if ((mode & 0010) || (mode & 0001))
+		tacl_entry[1].permset |= eperm;
+
+	for (i = 0; i < 6; i++) {
+		if (tacl_entry[i].permset != 0) {
+			archive_entry_acl_add_entry(entry,
+			    tacl_entry[i].type, tacl_entry[i].permset,
+			    tacl_entry[i].tag, -1, NULL);
+		}
+	}
+
+	return;
+}
+
+static int
+translate_acl(struct archive_read_disk *a,
+    struct archive_entry *entry, acl_t acl)
+{
+	acl_tag_t	 acl_tag;
+	acl_flagset_t	 acl_flagset;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+	int		 i, entry_acl_type;
+	int		 r, s, ae_id, ae_tag, ae_perm;
+	const char	*ae_name;
+
+	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
+	if (s == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to get first ACL entry");
+		return (ARCHIVE_WARN);
+	}
+
+	while (s == 0) {
+		ae_id = -1;
+		ae_name = NULL;
+		ae_perm = 0;
+
+		if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL tag type");
+			return (ARCHIVE_WARN);
+		}
+		switch (acl_tag) {
+		case ACL_EXTENDED_ALLOW:
+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+			r = translate_guid(&a->archive, acl_entry,
+			    &ae_id, &ae_tag, &ae_name);
+			break;
+		case ACL_EXTENDED_DENY:
+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+			r = translate_guid(&a->archive, acl_entry,
+			    &ae_id, &ae_tag, &ae_name);
+			break;
+		default:
+			/* Skip types that libarchive can't support. */
+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+			continue;
+		}
+
+		/* Skip if translate_guid() above failed */
+		if (r != 0) {
+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+			continue;
+		}
+
+		/*
+		 * Libarchive stores "flag" (NFSv4 inheritance bits)
+		 * in the ae_perm bitmap.
+		 *
+		 * acl_get_flagset_np() fails with non-NFSv4 ACLs
+		 */
+		if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get flagset from a NFSv4 ACL entry");
+			return (ARCHIVE_WARN);
+		}
+		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+			r = acl_get_flag_np(acl_flagset,
+			    acl_nfs4_flag_map[i].p_perm);
+			if (r == -1) {
+				archive_set_error(&a->archive, errno,
+				    "Failed to check flag in a NFSv4 "
+				    "ACL flagset");
+				return (ARCHIVE_WARN);
+			} else if (r)
+				ae_perm |= acl_nfs4_flag_map[i].a_perm;
+		}
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL permission set");
+			return (ARCHIVE_WARN);
+		}
+
+		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+			/*
+			 * acl_get_perm() is spelled differently on different
+			 * platforms; see above.
+			 */
+			r = acl_get_perm_np(acl_permset,
+			    acl_nfs4_perm_map[i].p_perm);
+			if (r == -1) {
+				archive_set_error(&a->archive, errno,
+				    "Failed to check permission in an ACL "
+				    "permission set");
+				return (ARCHIVE_WARN);
+			} else if (r)
+				ae_perm |= acl_nfs4_perm_map[i].a_perm;
+		}
+
+#if !HAVE_DECL_ACL_SYNCHRONIZE
+		/* On Mac OS X without ACL_SYNCHRONIZE assume it is set */
+		ae_perm |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+#endif
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+					    ae_perm, ae_tag,
+					    ae_id, ae_name);
+
+		s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+set_acl(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl,
+    int ae_requested_type, const char *tname)
+{
+	acl_t		 acl;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+	acl_flagset_t	 acl_flagset;
+	int		 ret;
+	int		 ae_type, ae_permset, ae_tag, ae_id;
+	uuid_t		 ae_uuid;
+	uid_t		 ae_uid;
+	gid_t		 ae_gid;
+	const char	*ae_name;
+	int		 entries;
+	int		 i;
+
+	ret = ARCHIVE_OK;
+	entries = archive_acl_reset(abstract_acl, ae_requested_type);
+	if (entries == 0)
+		return (ARCHIVE_OK);
+
+	if (ae_requested_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+		errno = ENOENT;
+		archive_set_error(a, errno, "Unsupported ACL type");
+		return (ARCHIVE_FAILED);
+	}
+
+	acl = acl_init(entries);
+	if (acl == (acl_t)NULL) {
+		archive_set_error(a, errno,
+		    "Failed to initialize ACL working storage");
+		return (ARCHIVE_FAILED);
+	}
+
+	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+		/*
+		 * Mac OS doesn't support NFSv4 ACLs for
+		 * owner@, group@ and everyone at .
+		 * We skip any of these ACLs found.
+		 */
+		if (ae_tag == ARCHIVE_ENTRY_ACL_USER_OBJ ||
+		    ae_tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ ||
+		    ae_tag == ARCHIVE_ENTRY_ACL_EVERYONE)
+			continue;
+
+		if (acl_create_entry(&acl, &acl_entry) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to create a new ACL entry");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		switch (ae_type) {
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			acl_set_tag_type(acl_entry, ACL_EXTENDED_ALLOW);
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			acl_set_tag_type(acl_entry, ACL_EXTENDED_DENY);
+			break;
+		default:
+			/* We don't support any other types on MacOS */
+			continue;
+		}
+
+		switch (ae_tag) {
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+			if (mbr_uid_to_uuid(ae_uid, ae_uuid) != 0)
+				continue;
+			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
+				continue;
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+			if (mbr_gid_to_uuid(ae_gid, ae_uuid) != 0)
+				continue;
+			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
+				continue;
+			break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL tag");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to get ACL permission set");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+		if (acl_clear_perms(acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to clear ACL permissions");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+			if (ae_permset & acl_nfs4_perm_map[i].a_perm) {
+				if (acl_add_perm(acl_permset,
+				    acl_nfs4_perm_map[i].p_perm) != 0) {
+					archive_set_error(a, errno,
+					    "Failed to add ACL permission");
+					ret = ARCHIVE_FAILED;
+					goto exit_free;
+				}
+			}
+		}
+
+		/*
+		 * acl_get_flagset_np() fails with non-NFSv4 ACLs
+		 */
+		if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to get flagset from an NFSv4 ACL entry");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+		if (acl_clear_flags_np(acl_flagset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to clear flags from an NFSv4 ACL flagset");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+			if (ae_permset & acl_nfs4_flag_map[i].a_perm) {
+				if (acl_add_flag_np(acl_flagset,
+				    acl_nfs4_flag_map[i].p_perm) != 0) {
+					archive_set_error(a, errno,
+					    "Failed to add flag to "
+					    "NFSv4 ACL flagset");
+					ret = ARCHIVE_FAILED;
+					goto exit_free;
+				}
+			}
+		}
+	}
+
+	if (fd >= 0) {
+		if (acl_set_fd_np(fd, acl, ACL_TYPE_EXTENDED) == 0)
+			ret = ARCHIVE_OK;
+		else {
+			if (errno == EOPNOTSUPP) {
+				/* Filesystem doesn't support ACLs */
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(a, errno,
+				    "Failed to set acl on fd: %s", tname);
+				ret = ARCHIVE_WARN;
+			}
+		}
+	} else if (acl_set_link_np(name, ACL_TYPE_EXTENDED, acl) != 0) {
+		if (errno == EOPNOTSUPP) {
+			/* Filesystem doesn't support ACLs */
+			ret = ARCHIVE_OK;
+		} else {
+			archive_set_error(a, errno, "Failed to set acl: %s",
+			    tname);
+			ret = ARCHIVE_WARN;
+		}
+	}
+exit_free:
+	acl_free(acl);
+	return (ret);
+}
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	const char	*accpath;
+	acl_t		acl;
+	int		r;
+
+	accpath = NULL;
+
+	if (*fd < 0) {
+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (accpath == NULL)
+			return (ARCHIVE_WARN);
+	}
+
+	archive_entry_acl_clear(entry);
+
+	acl = NULL;
+
+	if (*fd >= 0)
+		acl = acl_get_fd_np(*fd, ACL_TYPE_EXTENDED);
+	else if (!a->follow_symlinks)
+		acl = acl_get_link_np(accpath, ACL_TYPE_EXTENDED);
+	else
+		acl = acl_get_file(accpath, ACL_TYPE_EXTENDED);
+
+	if (acl != NULL) {
+		r = translate_acl(a, entry, acl);
+		acl_free(acl);
+		acl = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate NFSv4 ACLs");
+		}
+
+		/*
+		 * Because Mac OS doesn't support owner@, group@ and everyone@
+		 * ACLs we need to add NFSv4 ACLs mirroring the file mode to
+		 * the archive entry. Otherwise extraction on non-Mac platforms
+		 * would lead to an invalid file mode.
+		 */
+		if ((archive_entry_acl_types(entry) &
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)
+			add_trivial_nfs4_acl(entry);
+
+		return (r);
+	}
+	return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+	int		ret = ARCHIVE_OK;
+
+	(void)mode;	/* UNUSED */
+
+	if ((archive_acl_types(abstract_acl) &
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+		ret = set_acl(a, fd, name, abstract_acl,
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+	}
+	return (ret);
+}
+#endif	/* ARCHIVE_ACL_DARWIN */
diff --git a/libarchive/archive_disk_acl_freebsd.c b/libarchive/archive_disk_acl_freebsd.c
new file mode 100644
index 0000000..07d08ff
--- /dev/null
+++ b/libarchive/archive_disk_acl_freebsd.c
@@ -0,0 +1,700 @@
+/*-
+ * 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},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}
+};
+
+static const int acl_nfs4_flag_map_size =
+    (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
+#endif /* ARCHIVE_ACL_FREEBSD_NFS4 */
+
+static int
+translate_acl(struct archive_read_disk *a,
+    struct archive_entry *entry, acl_t acl, int default_entry_acl_type)
+{
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	int brand;
+	acl_flagset_t	 acl_flagset;
+	acl_entry_type_t acl_type;
+#endif
+	acl_tag_t	 acl_tag;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+	int		 i, entry_acl_type, perm_map_size;
+	const acl_perm_map_t	*perm_map;
+	int		 r, s, ae_id, ae_tag, ae_perm;
+	void		*q;
+	const char	*ae_name;
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	// FreeBSD "brands" ACLs as POSIX.1e or NFSv4
+	// Make sure the "brand" on this ACL is consistent
+	// with the default_entry_acl_type bits provided.
+	if (acl_get_brand_np(acl, &brand) != 0) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to read ACL brand");
+		return (ARCHIVE_WARN);
+	}
+	switch (brand) {
+	case ACL_BRAND_POSIX:
+		switch (default_entry_acl_type) {
+		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+			break;
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Invalid ACL entry type for POSIX.1e ACL");
+			return (ARCHIVE_WARN);
+		}
+		break;
+	case ACL_BRAND_NFS4:
+		if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Invalid ACL entry type for NFSv4 ACL");
+			return (ARCHIVE_WARN);
+		}
+		break;
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Unknown ACL brand");
+		return (ARCHIVE_WARN);
+	}
+#endif
+
+	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
+	if (s == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to get first ACL entry");
+		return (ARCHIVE_WARN);
+	}
+
+	while (s == 1) {
+		ae_id = -1;
+		ae_name = NULL;
+		ae_perm = 0;
+
+		if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL tag type");
+			return (ARCHIVE_WARN);
+		}
+		switch (acl_tag) {
+		case ACL_USER:
+			q = acl_get_qualifier(acl_entry);
+			if (q != NULL) {
+				ae_id = (int)*(uid_t *)q;
+				acl_free(q);
+				ae_name = archive_read_disk_uname(&a->archive,
+				    ae_id);
+			}
+			ae_tag = ARCHIVE_ENTRY_ACL_USER;
+			break;
+		case ACL_GROUP:
+			q = acl_get_qualifier(acl_entry);
+			if (q != NULL) {
+				ae_id = (int)*(gid_t *)q;
+				acl_free(q);
+				ae_name = archive_read_disk_gname(&a->archive,
+				    ae_id);
+			}
+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+			break;
+		case ACL_MASK:
+			ae_tag = ARCHIVE_ENTRY_ACL_MASK;
+			break;
+		case ACL_USER_OBJ:
+			ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+			break;
+		case ACL_GROUP_OBJ:
+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+			break;
+		case ACL_OTHER:
+			ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
+			break;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		case ACL_EVERYONE:
+			ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+			break;
+#endif
+		default:
+			/* Skip types that libarchive can't support. */
+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+			continue;
+		}
+
+		// XXX acl_type maps to allow/deny/audit/YYYY bits
+		entry_acl_type = default_entry_acl_type;
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			/*
+			 * acl_get_entry_type_np() fails with non-NFSv4 ACLs
+			 */
+			if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) {
+				archive_set_error(&a->archive, errno, "Failed "
+				    "to get ACL type from a NFSv4 ACL entry");
+				return (ARCHIVE_WARN);
+			}
+			switch (acl_type) {
+			case ACL_ENTRY_TYPE_ALLOW:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+				break;
+			case ACL_ENTRY_TYPE_DENY:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+				break;
+			case ACL_ENTRY_TYPE_AUDIT:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+				break;
+			case ACL_ENTRY_TYPE_ALARM:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+				break;
+			default:
+				archive_set_error(&a->archive, errno,
+				    "Invalid NFSv4 ACL entry type");
+				return (ARCHIVE_WARN);
+			}
+
+			/*
+			 * Libarchive stores "flag" (NFSv4 inheritance bits)
+			 * in the ae_perm bitmap.
+			 *
+			 * acl_get_flagset_np() fails with non-NFSv4 ACLs
+			 */
+			if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Failed to get flagset from a NFSv4 "
+				    "ACL entry");
+				return (ARCHIVE_WARN);
+			}
+			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+				r = acl_get_flag_np(acl_flagset,
+				    acl_nfs4_flag_map[i].p_perm);
+				if (r == -1) {
+					archive_set_error(&a->archive, errno,
+					    "Failed to check flag in a NFSv4 "
+					    "ACL flagset");
+					return (ARCHIVE_WARN);
+				} else if (r)
+					ae_perm |= acl_nfs4_flag_map[i].a_perm;
+			}
+		}
+#endif
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL permission set");
+			return (ARCHIVE_WARN);
+		}
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			perm_map_size = acl_nfs4_perm_map_size;
+			perm_map = acl_nfs4_perm_map;
+		} else {
+#endif
+			perm_map_size = acl_posix_perm_map_size;
+			perm_map = acl_posix_perm_map;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		}
+#endif
+
+		for (i = 0; i < perm_map_size; ++i) {
+			r = acl_get_perm_np(acl_permset, perm_map[i].p_perm);
+			if (r == -1) {
+				archive_set_error(&a->archive, errno,
+				    "Failed to check permission in an ACL "
+				    "permission set");
+				return (ARCHIVE_WARN);
+			} else if (r)
+				ae_perm |= perm_map[i].a_perm;
+		}
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+					    ae_perm, ae_tag,
+					    ae_id, ae_name);
+
+		s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+		if (s == -1) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get next ACL entry");
+			return (ARCHIVE_WARN);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+set_acl(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl,
+    int ae_requested_type, const char *tname)
+{
+	int		 acl_type = 0;
+	acl_t		 acl;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	acl_flagset_t	 acl_flagset;
+	int		 r;
+#endif
+	int		 ret;
+	int		 ae_type, ae_permset, ae_tag, ae_id;
+	int		 perm_map_size;
+	const acl_perm_map_t	*perm_map;
+	uid_t		 ae_uid;
+	gid_t		 ae_gid;
+	const char	*ae_name;
+	int		 entries;
+	int		 i;
+
+	ret = ARCHIVE_OK;
+	entries = archive_acl_reset(abstract_acl, ae_requested_type);
+	if (entries == 0)
+		return (ARCHIVE_OK);
+
+
+	switch (ae_requested_type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+		acl_type = ACL_TYPE_ACCESS;
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+		acl_type = ACL_TYPE_DEFAULT;
+		break;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+		acl_type = ACL_TYPE_NFS4;
+		break;
+#endif
+	default:
+		errno = ENOENT;
+		archive_set_error(a, errno, "Unsupported ACL type");
+		return (ARCHIVE_FAILED);
+	}
+
+	acl = acl_init(entries);
+	if (acl == (acl_t)NULL) {
+		archive_set_error(a, errno,
+		    "Failed to initialize ACL working storage");
+		return (ARCHIVE_FAILED);
+	}
+
+	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+		if (acl_create_entry(&acl, &acl_entry) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to create a new ACL entry");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+		switch (ae_tag) {
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+			acl_set_tag_type(acl_entry, ACL_USER);
+			acl_set_qualifier(acl_entry, &ae_uid);
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+			acl_set_tag_type(acl_entry, ACL_GROUP);
+			acl_set_qualifier(acl_entry, &ae_gid);
+			break;
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			acl_set_tag_type(acl_entry, ACL_USER_OBJ);
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
+			break;
+		case ARCHIVE_ENTRY_ACL_MASK:
+			acl_set_tag_type(acl_entry, ACL_MASK);
+			break;
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			acl_set_tag_type(acl_entry, ACL_OTHER);
+			break;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		case ARCHIVE_ENTRY_ACL_EVERYONE:
+			acl_set_tag_type(acl_entry, ACL_EVERYONE);
+			break;
+#endif
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL tag");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		r = 0;
+		switch (ae_type) {
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			r = acl_set_entry_type_np(acl_entry,
+			    ACL_ENTRY_TYPE_ALLOW);
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			r = acl_set_entry_type_np(acl_entry,
+			    ACL_ENTRY_TYPE_DENY);
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+			r = acl_set_entry_type_np(acl_entry,
+			    ACL_ENTRY_TYPE_AUDIT);
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+			r = acl_set_entry_type_np(acl_entry,
+			    ACL_ENTRY_TYPE_ALARM);
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+			// These don't translate directly into the system ACL.
+			break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL entry type");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		if (r != 0) {
+			archive_set_error(a, errno,
+			    "Failed to set ACL entry type");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+#endif
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to get ACL permission set");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+		if (acl_clear_perms(acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to clear ACL permissions");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			perm_map_size = acl_nfs4_perm_map_size;
+			perm_map = acl_nfs4_perm_map;
+		} else {
+#endif
+			perm_map_size = acl_posix_perm_map_size;
+			perm_map = acl_posix_perm_map;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		}
+#endif
+
+		for (i = 0; i < perm_map_size; ++i) {
+			if (ae_permset & perm_map[i].a_perm) {
+				if (acl_add_perm(acl_permset,
+				    perm_map[i].p_perm) != 0) {
+					archive_set_error(a, errno,
+					    "Failed to add ACL permission");
+					ret = ARCHIVE_FAILED;
+					goto exit_free;
+				}
+			}
+		}
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			/*
+			 * acl_get_flagset_np() fails with non-NFSv4 ACLs
+			 */
+			if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+				archive_set_error(a, errno,
+				    "Failed to get flagset from an NFSv4 "
+				    "ACL entry");
+				ret = ARCHIVE_FAILED;
+				goto exit_free;
+			}
+			if (acl_clear_flags_np(acl_flagset) != 0) {
+				archive_set_error(a, errno,
+				    "Failed to clear flags from an NFSv4 "
+				    "ACL flagset");
+				ret = ARCHIVE_FAILED;
+				goto exit_free;
+			}
+			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+				if (ae_permset & acl_nfs4_flag_map[i].a_perm) {
+					if (acl_add_flag_np(acl_flagset,
+					    acl_nfs4_flag_map[i].p_perm) != 0) {
+						archive_set_error(a, errno,
+						    "Failed to add flag to "
+						    "NFSv4 ACL flagset");
+						ret = ARCHIVE_FAILED;
+						goto exit_free;
+					}
+				}
+			}
+		}
+#endif
+	}
+
+	/* Try restoring the ACL through 'fd' if we can. */
+	if (fd >= 0) {
+		if (acl_set_fd_np(fd, acl, acl_type) == 0)
+			ret = ARCHIVE_OK;
+		else {
+			if (errno == EOPNOTSUPP) {
+				/* Filesystem doesn't support ACLs */
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(a, errno,
+				    "Failed to set acl on fd: %s", tname);
+				ret = ARCHIVE_WARN;
+			}
+		}
+	}
+#if HAVE_ACL_SET_LINK_NP
+	else if (acl_set_link_np(name, acl_type, acl) != 0)
+#else
+	/* FreeBSD older than 8.0 */
+	else if (acl_set_file(name, acl_type, acl) != 0)
+#endif
+	{
+		if (errno == EOPNOTSUPP) {
+			/* Filesystem doesn't support ACLs */
+			ret = ARCHIVE_OK;
+		} else {
+			archive_set_error(a, errno, "Failed to set acl: %s",
+			    tname);
+			ret = ARCHIVE_WARN;
+		}
+	}
+exit_free:
+	acl_free(acl);
+	return (ret);
+}
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	const char	*accpath;
+	acl_t		acl;
+	int		r;
+
+	accpath = NULL;
+
+	if (*fd < 0) {
+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (accpath == NULL)
+			return (ARCHIVE_WARN);
+	}
+
+	archive_entry_acl_clear(entry);
+
+	acl = NULL;
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	/* Try NFSv4 ACL first. */
+	if (*fd >= 0)
+		acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4);
+	else if (!a->follow_symlinks)
+		acl = acl_get_link_np(accpath, ACL_TYPE_NFS4);
+	else
+		acl = acl_get_file(accpath, ACL_TYPE_NFS4);
+
+	/* Ignore "trivial" ACLs that just mirror the file mode. */
+	if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {
+		acl_free(acl);
+		acl = NULL;
+		return (ARCHIVE_OK);
+	}
+
+	if (acl != NULL) {
+		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+		acl_free(acl);
+		acl = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate NFSv4 ACLs");
+		}
+
+		return (r);
+	}
+#endif
+
+	/* Retrieve access ACL from file. */
+	if (*fd >= 0)
+		acl = acl_get_fd_np(*fd, ACL_TYPE_ACCESS);
+#if HAVE_ACL_GET_LINK_NP
+	else if (!a->follow_symlinks)
+		acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);
+#else
+	else if ((!a->follow_symlinks)
+	    && (archive_entry_filetype(entry) == AE_IFLNK))
+		/* We can't get the ACL of a symlink, so we assume it can't
+		   have one. */
+		acl = NULL;
+#endif
+	else
+		acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
+
+#if HAVE_ACL_IS_TRIVIAL_NP
+	/* Ignore "trivial" ACLs that just mirror the file mode. */
+	if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {
+		acl_free(acl);
+		acl = NULL;
+	}
+#endif
+
+	if (acl != NULL) {
+		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+		acl_free(acl);
+		acl = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate access ACLs");
+			return (r);
+		}
+	}
+
+	/* Only directories can have default ACLs. */
+	if (S_ISDIR(archive_entry_mode(entry))) {
+		if (*fd >= 0)
+			acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT);
+		else
+			acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
+		if (acl != NULL) {
+			r = translate_acl(a, entry, acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+			acl_free(acl);
+			if (r != ARCHIVE_OK) {
+				archive_set_error(&a->archive, errno,
+				    "Couldn't translate default ACLs");
+				return (r);
+			}
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+	int		ret = ARCHIVE_OK;
+
+	(void)mode;	/* UNUSED */
+
+	if ((archive_acl_types(abstract_acl)
+	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+		if ((archive_acl_types(abstract_acl)
+		    & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+			ret = set_acl(a, fd, name, abstract_acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
+			if (ret != ARCHIVE_OK)
+				return (ret);
+		}
+		if ((archive_acl_types(abstract_acl)
+		    & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+			ret = set_acl(a, fd, name, abstract_acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
+
+		/* Simultaneous POSIX.1e and NFSv4 is not supported */
+		return (ret);
+	}
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	else if ((archive_acl_types(abstract_acl) &
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+		ret = set_acl(a, fd, name, abstract_acl,
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+	}
+#endif
+	return (ret);
+}
+#endif	/* ARCHIVE_ACL_FREEBSD */
diff --git a/libarchive/archive_disk_acl_linux.c b/libarchive/archive_disk_acl_linux.c
new file mode 100644
index 0000000..3928f3d
--- /dev/null
+++ b/libarchive/archive_disk_acl_linux.c
@@ -0,0 +1,743 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2017 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+#if ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_LIBRICHACL
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_ACL_LIBACL_H
+#include <acl/libacl.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_SYS_RICHACL_H
+#include <sys/richacl.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+#include "archive_write_disk_private.h"
+
+typedef struct {
+	const int a_perm;	/* Libarchive permission or flag */
+	const int p_perm;	/* Platform permission or flag */
+} acl_perm_map_t;
+
+#if ARCHIVE_ACL_LIBACL
+static const acl_perm_map_t acl_posix_perm_map[] = {
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
+	{ARCHIVE_ENTRY_ACL_READ, ACL_READ},
+};
+
+static const int acl_posix_perm_map_size =
+    (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0]));
+#endif /* ARCHIVE_ACL_LIBACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+static const acl_perm_map_t acl_nfs4_perm_map[] = {
+	{ARCHIVE_ENTRY_ACL_EXECUTE, RICHACE_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_READ_DATA, RICHACE_READ_DATA},
+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, RICHACE_LIST_DIRECTORY},
+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, RICHACE_WRITE_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_FILE, RICHACE_ADD_FILE},
+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, RICHACE_APPEND_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, RICHACE_ADD_SUBDIRECTORY},
+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, RICHACE_READ_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, RICHACE_WRITE_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, RICHACE_DELETE_CHILD},
+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, RICHACE_READ_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, RICHACE_WRITE_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_DELETE, RICHACE_DELETE},
+	{ARCHIVE_ENTRY_ACL_READ_ACL, RICHACE_READ_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, RICHACE_WRITE_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, RICHACE_WRITE_OWNER},
+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, RICHACE_SYNCHRONIZE}
+};
+
+static const int acl_nfs4_perm_map_size =
+    (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));
+
+static const acl_perm_map_t acl_nfs4_flag_map[] = {
+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, RICHACE_FILE_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, RICHACE_DIRECTORY_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, RICHACE_NO_PROPAGATE_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, RICHACE_INHERIT_ONLY_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, RICHACE_INHERITED_ACE}
+};
+
+static const int acl_nfs4_flag_map_size =
+    (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
+#endif /* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBACL
+/*
+ * Translate POSIX.1e ACLs into libarchive internal structure
+ */
+static int
+translate_acl(struct archive_read_disk *a,
+    struct archive_entry *entry, acl_t acl, int default_entry_acl_type)
+{
+	acl_tag_t	 acl_tag;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+	int		 i, entry_acl_type;
+	int		 r, s, ae_id, ae_tag, ae_perm;
+	void		*q;
+	const char	*ae_name;
+
+	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
+	if (s == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to get first ACL entry");
+		return (ARCHIVE_WARN);
+	}
+
+	while (s == 1) {
+		ae_id = -1;
+		ae_name = NULL;
+		ae_perm = 0;
+
+		if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL tag type");
+			return (ARCHIVE_WARN);
+		}
+		switch (acl_tag) {
+		case ACL_USER:
+			q = acl_get_qualifier(acl_entry);
+			if (q != NULL) {
+				ae_id = (int)*(uid_t *)q;
+				acl_free(q);
+				ae_name = archive_read_disk_uname(&a->archive,
+				    ae_id);
+			}
+			ae_tag = ARCHIVE_ENTRY_ACL_USER;
+			break;
+		case ACL_GROUP:
+			q = acl_get_qualifier(acl_entry);
+			if (q != NULL) {
+				ae_id = (int)*(gid_t *)q;
+				acl_free(q);
+				ae_name = archive_read_disk_gname(&a->archive,
+				    ae_id);
+			}
+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+			break;
+		case ACL_MASK:
+			ae_tag = ARCHIVE_ENTRY_ACL_MASK;
+			break;
+		case ACL_USER_OBJ:
+			ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+			break;
+		case ACL_GROUP_OBJ:
+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+			break;
+		case ACL_OTHER:
+			ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
+			break;
+		default:
+			/* Skip types that libarchive can't support. */
+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+			continue;
+		}
+
+		// XXX acl_type maps to allow/deny/audit/YYYY bits
+		entry_acl_type = default_entry_acl_type;
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL permission set");
+			return (ARCHIVE_WARN);
+		}
+
+		for (i = 0; i < acl_posix_perm_map_size; ++i) {
+			r = acl_get_perm(acl_permset,
+			    acl_posix_perm_map[i].p_perm);
+			if (r == -1) {
+				archive_set_error(&a->archive, errno,
+				    "Failed to check permission in an ACL "
+				    "permission set");
+				return (ARCHIVE_WARN);
+			} else if (r)
+				ae_perm |= acl_posix_perm_map[i].a_perm;
+		}
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+					    ae_perm, ae_tag,
+					    ae_id, ae_name);
+
+		s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+		if (s == -1) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get next ACL entry");
+			return (ARCHIVE_WARN);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+#endif /* ARCHIVE_ACL_LIBACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+/*
+ * Translate RichACL into libarchive internal ACL
+ */
+static int
+translate_richacl(struct archive_read_disk *a, struct archive_entry *entry,
+    struct richacl *richacl)
+{
+	int ae_id, ae_tag, ae_perm;
+	int entry_acl_type, i;
+	const char *ae_name;
+
+	struct richace *richace;
+
+	richacl_for_each_entry(richace, richacl) {
+		ae_name = NULL;
+		ae_tag = 0;
+		ae_perm = 0;
+		ae_id = -1;
+
+		switch (richace->e_type) {
+		case RICHACE_ACCESS_ALLOWED_ACE_TYPE:
+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+			break;
+		case RICHACE_ACCESS_DENIED_ACE_TYPE:
+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+			break;
+		default: /* Unknown entry type, skip */
+			continue;
+		}
+
+		/* Unsupported */
+		if (richace->e_flags & RICHACE_UNMAPPED_WHO)
+			continue;
+
+		if (richace->e_flags & RICHACE_SPECIAL_WHO) {
+			switch (richace->e_id) {
+			case RICHACE_OWNER_SPECIAL_ID:
+				ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				break;
+			case RICHACE_GROUP_SPECIAL_ID:
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case RICHACE_EVERYONE_SPECIAL_ID:
+				ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+				break;
+			default: /* Unknown special ID type */
+				continue;
+			}
+		} else {
+			ae_id = richace->e_id;
+			if (richace->e_flags & RICHACE_IDENTIFIER_GROUP) {
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+				ae_name = archive_read_disk_gname(&a->archive,
+				    (gid_t)(richace->e_id));
+			} else {
+				ae_tag = ARCHIVE_ENTRY_ACL_USER;
+				ae_name = archive_read_disk_uname(&a->archive,
+				    (uid_t)(richace->e_id));
+			}
+		}
+		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+			if ((richace->e_flags &
+			    acl_nfs4_flag_map[i].p_perm) != 0)
+				ae_perm |= acl_nfs4_flag_map[i].a_perm;
+		}
+		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+			if ((richace->e_mask &
+			    acl_nfs4_perm_map[i].p_perm) != 0)
+				ae_perm |=
+				    acl_nfs4_perm_map[i].a_perm;
+		}
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+		    ae_perm, ae_tag, ae_id, ae_name);
+	}
+	return (ARCHIVE_OK);
+}
+#endif	/* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+static int
+_richacl_mode_to_mask(short mode)
+{
+	int mask = 0;
+
+	if (mode & S_IROTH)
+		mask |= RICHACE_POSIX_MODE_READ;
+	if (mode & S_IWOTH)
+		mask |= RICHACE_POSIX_MODE_WRITE;
+	if (mode & S_IXOTH)
+		mask |= RICHACE_POSIX_MODE_EXEC;
+
+	return (mask);
+}
+
+static void
+_richacl_mode_to_masks(struct richacl *richacl, __LA_MODE_T mode)
+{
+	richacl->a_owner_mask = _richacl_mode_to_mask((mode & 0700) >> 6);
+	richacl->a_group_mask = _richacl_mode_to_mask((mode & 0070) >> 3);
+	richacl->a_other_mask = _richacl_mode_to_mask(mode & 0007);
+}
+#endif /* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+static int
+set_richacl(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode,
+    int ae_requested_type, const char *tname)
+{
+	int		 ae_type, ae_permset, ae_tag, ae_id;
+	uid_t		 ae_uid;
+	gid_t		 ae_gid;
+	const char	*ae_name;
+	int		 entries;
+	int		 i;
+	int		 ret;
+	int		 e = 0;
+	struct richacl  *richacl = NULL;
+	struct richace  *richace;
+
+	ret = ARCHIVE_OK;
+	entries = archive_acl_reset(abstract_acl, ae_requested_type);
+	if (entries == 0)
+		return (ARCHIVE_OK);
+
+	if (ae_requested_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+		errno = ENOENT;
+		archive_set_error(a, errno, "Unsupported ACL type");
+		return (ARCHIVE_FAILED);
+	}
+
+	richacl = richacl_alloc(entries);
+	if (richacl == NULL) {
+		archive_set_error(a, errno,
+			"Failed to initialize RichACL working storage");
+		return (ARCHIVE_FAILED);
+	}
+
+	e = 0;
+
+	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+		richace = &(richacl->a_entries[e]);
+
+		richace->e_flags = 0;
+		richace->e_mask = 0;
+
+		switch (ae_tag) {
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+			richace->e_id = ae_uid;
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+			richace->e_id = ae_gid;
+			richace->e_flags |= RICHACE_IDENTIFIER_GROUP;
+			break;
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			richace->e_flags |= RICHACE_SPECIAL_WHO;
+			richace->e_id = RICHACE_OWNER_SPECIAL_ID;
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			richace->e_flags |= RICHACE_SPECIAL_WHO;
+			richace->e_id = RICHACE_GROUP_SPECIAL_ID;
+			break;
+		case ARCHIVE_ENTRY_ACL_EVERYONE:
+			richace->e_flags |= RICHACE_SPECIAL_WHO;
+			richace->e_id = RICHACE_EVERYONE_SPECIAL_ID;
+			break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL tag");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		switch (ae_type) {
+			case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+				richace->e_type =
+				    RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+				break;
+			case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+				richace->e_type =
+				    RICHACE_ACCESS_DENIED_ACE_TYPE;
+				break;
+			case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+			case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+				break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL entry type");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+			if (ae_permset & acl_nfs4_perm_map[i].a_perm)
+				richace->e_mask |= acl_nfs4_perm_map[i].p_perm;
+		}
+
+		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+			if (ae_permset &
+			    acl_nfs4_flag_map[i].a_perm)
+				richace->e_flags |= acl_nfs4_flag_map[i].p_perm;
+		}
+	e++;
+	}
+
+	/* Fill RichACL masks */
+	_richacl_mode_to_masks(richacl, mode);
+
+	if (fd >= 0) {
+		if (richacl_set_fd(fd, richacl) == 0)
+			ret = ARCHIVE_OK;
+		else {
+			if (errno == EOPNOTSUPP) {
+				/* Filesystem doesn't support ACLs */
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(a, errno,
+				    "Failed to set richacl on fd: %s", tname);
+				ret = ARCHIVE_WARN;
+			}
+		}
+	} else if (richacl_set_file(name, richacl) != 0) {
+		if (errno == EOPNOTSUPP) {
+			/* Filesystem doesn't support ACLs */
+			ret = ARCHIVE_OK;
+		} else {
+			archive_set_error(a, errno, "Failed to set richacl: %s",
+			    tname);
+			ret = ARCHIVE_WARN;
+		}
+	}
+exit_free:
+	richacl_free(richacl);
+	return (ret);
+}
+#endif /* ARCHIVE_ACL_RICHACL */
+
+#if ARCHIVE_ACL_LIBACL
+static int
+set_acl(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl,
+    int ae_requested_type, const char *tname)
+{
+	int		 acl_type = 0;
+	int		 ae_type, ae_permset, ae_tag, ae_id;
+	uid_t		 ae_uid;
+	gid_t		 ae_gid;
+	const char	*ae_name;
+	int		 entries;
+	int		 i;
+	int		 ret;
+	acl_t		 acl = NULL;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+
+	ret = ARCHIVE_OK;
+	entries = archive_acl_reset(abstract_acl, ae_requested_type);
+	if (entries == 0)
+		return (ARCHIVE_OK);
+
+	switch (ae_requested_type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+		acl_type = ACL_TYPE_ACCESS;
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+		acl_type = ACL_TYPE_DEFAULT;
+		break;
+	default:
+		errno = ENOENT;
+		archive_set_error(a, errno, "Unsupported ACL type");
+		return (ARCHIVE_FAILED);
+	}
+
+	acl = acl_init(entries);
+	if (acl == (acl_t)NULL) {
+		archive_set_error(a, errno,
+		    "Failed to initialize ACL working storage");
+		return (ARCHIVE_FAILED);
+	}
+
+	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+
+		if (acl_create_entry(&acl, &acl_entry) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to create a new ACL entry");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		switch (ae_tag) {
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+			acl_set_tag_type(acl_entry, ACL_USER);
+			acl_set_qualifier(acl_entry, &ae_uid);
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+			acl_set_tag_type(acl_entry, ACL_GROUP);
+			acl_set_qualifier(acl_entry, &ae_gid);
+			break;
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			acl_set_tag_type(acl_entry, ACL_USER_OBJ);
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
+			break;
+		case ARCHIVE_ENTRY_ACL_MASK:
+			acl_set_tag_type(acl_entry, ACL_MASK);
+			break;
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			acl_set_tag_type(acl_entry, ACL_OTHER);
+			break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL tag");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to get ACL permission set");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+		if (acl_clear_perms(acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to clear ACL permissions");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		for (i = 0; i < acl_posix_perm_map_size; ++i) {
+			if (ae_permset & acl_posix_perm_map[i].a_perm) {
+				if (acl_add_perm(acl_permset,
+				    acl_posix_perm_map[i].p_perm) != 0) {
+					archive_set_error(a, errno,
+					    "Failed to add ACL permission");
+					ret = ARCHIVE_FAILED;
+					goto exit_free;
+				}
+			}
+		}
+
+	}
+
+	if (fd >= 0 && ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+		if (acl_set_fd(fd, acl) == 0)
+			ret = ARCHIVE_OK;
+		else {
+			if (errno == EOPNOTSUPP) {
+				/* Filesystem doesn't support ACLs */
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(a, errno,
+				    "Failed to set acl on fd: %s", tname);
+				ret = ARCHIVE_WARN;
+			}
+		}
+	} else if (acl_set_file(name, acl_type, acl) != 0) {
+		if (errno == EOPNOTSUPP) {
+			/* Filesystem doesn't support ACLs */
+			ret = ARCHIVE_OK;
+		} else {
+			archive_set_error(a, errno, "Failed to set acl: %s",
+			    tname);
+			ret = ARCHIVE_WARN;
+		}
+	}
+exit_free:
+	acl_free(acl);
+	return (ret);
+}
+#endif /* ARCHIVE_ACL_LIBACL */
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	const char	*accpath;
+	int		r;
+#if ARCHIVE_ACL_LIBACL
+	acl_t		acl;
+#endif
+#if ARCHIVE_ACL_LIBRICHACL
+	struct richacl *richacl;
+	mode_t		mode;
+#endif
+
+	accpath = NULL;
+	r = ARCHIVE_OK;
+
+	/* For default ACLs we need reachable accpath */
+	if (*fd < 0 || S_ISDIR(archive_entry_mode(entry))) {
+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (accpath == NULL)
+			return (ARCHIVE_WARN);
+	}
+
+	archive_entry_acl_clear(entry);
+
+#if ARCHIVE_ACL_LIBACL
+	acl = NULL;
+#endif
+#if ARCHIVE_ACL_LIBRICHACL
+	richacl = NULL;
+#endif
+
+#if ARCHIVE_ACL_LIBRICHACL
+	/* Try NFSv4 ACL first. */
+	if (*fd >= 0)
+		richacl = richacl_get_fd(*fd);
+	else if ((!a->follow_symlinks)
+	    && (archive_entry_filetype(entry) == AE_IFLNK))
+		/* We can't get the ACL of a symlink, so we assume it can't
+		   have one */
+		richacl = NULL;
+	else
+		richacl = richacl_get_file(accpath);
+
+	/* Ignore "trivial" ACLs that just mirror the file mode. */
+	if (richacl != NULL) {
+		mode = archive_entry_mode(entry);
+		if (richacl_equiv_mode(richacl, &mode) == 0) {
+			richacl_free(richacl);
+			richacl = NULL;
+			return (ARCHIVE_OK);
+		}
+	}
+
+	if (richacl != NULL) {
+		r = translate_richacl(a, entry, richacl);
+		richacl_free(richacl);
+		richacl = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			"Couldn't translate NFSv4 ACLs");
+		}
+
+		return (r);
+	}
+#endif	/* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBACL
+	/* Retrieve access ACL from file. */
+	if (*fd >= 0)
+		acl = acl_get_fd(*fd);
+	else if ((!a->follow_symlinks)
+	    && (archive_entry_filetype(entry) == AE_IFLNK))
+		/* We can't get the ACL of a symlink, so we assume it can't
+		   have one. */
+		acl = NULL;
+	else
+		acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
+
+	if (acl != NULL) {
+		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+		acl_free(acl);
+		acl = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate access ACLs");
+			return (r);
+		}
+	}
+
+	/* Only directories can have default ACLs. */
+	if (S_ISDIR(archive_entry_mode(entry))) {
+		acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
+		if (acl != NULL) {
+			r = translate_acl(a, entry, acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+			acl_free(acl);
+			if (r != ARCHIVE_OK) {
+				archive_set_error(&a->archive, errno,
+				    "Couldn't translate default ACLs");
+				return (r);
+			}
+		}
+	}
+#endif	/* ARCHIVE_ACL_LIBACL */
+	return (r);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+	int		ret = ARCHIVE_OK;
+
+#if !ARCHIVE_ACL_LIBRICHACL
+	(void)mode;	/* UNUSED */
+#endif
+
+#if ARCHIVE_ACL_LIBRICHACL
+	if ((archive_acl_types(abstract_acl)
+	    & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+		ret = set_richacl(a, fd, name, abstract_acl, mode,
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+	}
+#if ARCHIVE_ACL_LIBACL
+	else
+#endif
+#endif	/* ARCHIVE_ACL_LIBRICHACL */
+#if ARCHIVE_ACL_LIBACL
+	if ((archive_acl_types(abstract_acl)
+	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+		if ((archive_acl_types(abstract_acl)
+		    & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+			ret = set_acl(a, fd, name, abstract_acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
+			if (ret != ARCHIVE_OK)
+				return (ret);
+		}
+		if ((archive_acl_types(abstract_acl)
+		    & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+			ret = set_acl(a, fd, name, abstract_acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
+	}
+#endif	/* ARCHIVE_ACL_LIBACL */
+	return (ret);
+}
+#endif /* ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_LIBRICHACL */
diff --git a/libarchive/archive_disk_acl_sunos.c b/libarchive/archive_disk_acl_sunos.c
new file mode 100644
index 0000000..bc84fd6
--- /dev/null
+++ b/libarchive/archive_disk_acl_sunos.c
@@ -0,0 +1,821 @@
+/*-
+ * 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 {
+			if (aclp != NULL) {
+				free(aclp);
+				aclp = NULL;
+			}
+			break;
+		}
+	}
+
+	*aclcnt = cnt;
+	return (aclp);
+}
+
+/*
+ * Check if acl is trivial
+ * This is a FreeBSD acl_is_trivial_np() implementation for Solaris
+ */
+static int
+sun_acl_is_trivial(void *aclp, int aclcnt, mode_t mode, int is_nfs4,
+    int is_dir, int *trivialp)
+{
+#if ARCHIVE_ACL_SUNOS_NFS4
+	int i, p;
+	const uint32_t rperm = ACE_READ_DATA;
+	const uint32_t wperm = ACE_WRITE_DATA | ACE_APPEND_DATA;
+	const uint32_t eperm = ACE_EXECUTE;
+	const uint32_t pubset = ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
+	    ACE_READ_ACL | ACE_SYNCHRONIZE;
+	const uint32_t ownset = pubset | ACE_WRITE_ATTRIBUTES |
+	    ACE_WRITE_NAMED_ATTRS | ACE_WRITE_ACL | ACE_WRITE_OWNER;
+
+	ace_t *ace;
+	ace_t tace[6];
+#endif
+
+	if (aclp == NULL || trivialp == NULL)
+		return (-1);
+
+	*trivialp = 0;
+
+	/*
+	 * POSIX.1e ACLs marked with ACL_IS_TRIVIAL are compatible with
+	 * FreeBSD acl_is_trivial_np(). On Solaris they have 4 entries,
+	 * including mask.
+	 */
+	if (!is_nfs4) {
+		if (aclcnt == 4)
+			*trivialp = 1;
+		return (0);
+	}
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+	/*
+	 * Continue with checking NFSv4 ACLs
+	 *
+	 * Create list of trivial ace's to be compared
+	 */
+
+	/* owner@ allow pre */
+	tace[0].a_flags = ACE_OWNER;
+	tace[0].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[0].a_access_mask = 0;
+
+	/* owner@ deny */
+	tace[1].a_flags = ACE_OWNER;
+	tace[1].a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+	tace[1].a_access_mask = 0;
+
+	/* group@ deny */
+	tace[2].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP;
+	tace[2].a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+	tace[2].a_access_mask = 0;
+
+	/* owner@ allow */
+	tace[3].a_flags = ACE_OWNER;
+	tace[3].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[3].a_access_mask = ownset;
+
+	/* group@ allow */
+	tace[4].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP;
+	tace[4].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[4].a_access_mask = pubset;
+
+	/* everyone@ allow */
+	tace[5].a_flags = ACE_EVERYONE;
+	tace[5].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[5].a_access_mask = pubset;
+
+	/* Permissions for everyone@ */
+	if (mode & 0004)
+		tace[5].a_access_mask |= rperm;
+	if (mode & 0002)
+		tace[5].a_access_mask |= wperm;
+	if (mode & 0001)
+		tace[5].a_access_mask |= eperm;
+
+	/* Permissions for group@ */
+	if (mode & 0040)
+		tace[4].a_access_mask |= rperm;
+	else if (mode & 0004)
+		tace[2].a_access_mask |= rperm;
+	if (mode & 0020)
+		tace[4].a_access_mask |= wperm;
+	else if (mode & 0002)
+		tace[2].a_access_mask |= wperm;
+	if (mode & 0010)
+		tace[4].a_access_mask |= eperm;
+	else if (mode & 0001)
+		tace[2].a_access_mask |= eperm;
+
+	/* Permissions for owner@ */
+	if (mode & 0400) {
+		tace[3].a_access_mask |= rperm;
+		if (!(mode & 0040) && (mode & 0004))
+			tace[0].a_access_mask |= rperm;
+	} else if ((mode & 0040) || (mode & 0004))
+		tace[1].a_access_mask |= rperm;
+	if (mode & 0200) {
+		tace[3].a_access_mask |= wperm;
+		if (!(mode & 0020) && (mode & 0002))
+			tace[0].a_access_mask |= wperm;
+	} else if ((mode & 0020) || (mode & 0002))
+		tace[1].a_access_mask |= wperm;
+	if (mode & 0100) {
+		tace[3].a_access_mask |= eperm;
+		if (!(mode & 0010) && (mode & 0001))
+			tace[0].a_access_mask |= eperm;
+	} else if ((mode & 0010) || (mode & 0001))
+		tace[1].a_access_mask |= eperm;
+
+	/* Check if the acl count matches */
+	p = 3;
+	for (i = 0; i < 3; i++) {
+		if (tace[i].a_access_mask != 0)
+			p++;
+	}
+	if (aclcnt != p)
+		return (0);
+
+	p = 0;
+	for (i = 0; i < 6; i++) {
+		if (tace[i].a_access_mask != 0) {
+			ace = &((ace_t *)aclp)[p];
+			/*
+			 * Illumos added ACE_DELETE_CHILD to write perms for
+			 * directories. We have to check against that, too.
+			 */
+			if (ace->a_flags != tace[i].a_flags ||
+			    ace->a_type != tace[i].a_type ||
+			    (ace->a_access_mask != tace[i].a_access_mask &&
+			    (!is_dir || (tace[i].a_access_mask & wperm) == 0 ||
+			    ace->a_access_mask !=
+			    (tace[i].a_access_mask | ACE_DELETE_CHILD))))
+				return (0);
+			p++;
+		}
+	}
+
+	*trivialp = 1;
+#else	/* !ARCHIVE_ACL_SUNOS_NFS4 */
+	(void)is_dir;	/* UNUSED */
+	(void)aclp;	/* UNUSED */
+#endif	/* !ARCHIVE_ACL_SUNOS_NFS4 */
+	return (0);
+}
+
+/*
+ * Translate Solaris POSIX.1e and NFSv4 ACLs into libarchive internal ACL
+ */
+static int
+translate_acl(struct archive_read_disk *a,
+    struct archive_entry *entry, void *aclp, int aclcnt,
+    int default_entry_acl_type)
+{
+	int e, i;
+	int ae_id, ae_tag, ae_perm;
+	int entry_acl_type;
+	const char *ae_name;
+	aclent_t *aclent;
+#if ARCHIVE_ACL_SUNOS_NFS4
+	ace_t *ace;
+#endif
+
+	if (aclcnt <= 0)
+		return (ARCHIVE_OK);
+
+	for (e = 0; e < aclcnt; e++) {
+		ae_name = NULL;
+		ae_tag = 0;
+		ae_perm = 0;
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+		if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			ace = &((ace_t *)aclp)[e];
+			ae_id = ace->a_who;
+
+			switch(ace->a_type) {
+			case ACE_ACCESS_ALLOWED_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+				break;
+			case ACE_ACCESS_DENIED_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+				break;
+			case ACE_SYSTEM_AUDIT_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+				break;
+			case ACE_SYSTEM_ALARM_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+				break;
+			default:
+				/* Unknown entry type, skip */
+				continue;
+			}
+
+			if ((ace->a_flags & ACE_OWNER) != 0)
+				ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+			else if ((ace->a_flags & ACE_GROUP) != 0)
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+			else if ((ace->a_flags & ACE_EVERYONE) != 0)
+				ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+			else if ((ace->a_flags & ACE_IDENTIFIER_GROUP) != 0) {
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+				ae_name = archive_read_disk_gname(&a->archive,
+				    ae_id);
+			} else {
+				ae_tag = ARCHIVE_ENTRY_ACL_USER;
+				ae_name = archive_read_disk_uname(&a->archive,
+				    ae_id);
+			}
+
+			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+				if ((ace->a_flags &
+				    acl_nfs4_flag_map[i].p_perm) != 0)
+					ae_perm |= acl_nfs4_flag_map[i].a_perm;
+			}
+
+			for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+				if ((ace->a_access_mask &
+				    acl_nfs4_perm_map[i].p_perm) != 0)
+					ae_perm |= acl_nfs4_perm_map[i].a_perm;
+			}
+		} else
+#endif	/* ARCHIVE_ACL_SUNOS_NFS4 */
+		if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+			aclent = &((aclent_t *)aclp)[e];
+			if ((aclent->a_type & ACL_DEFAULT) != 0)
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+			else
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+			ae_id = aclent->a_id;
+
+			switch(aclent->a_type) {
+			case DEF_USER:
+			case USER:
+				ae_name = archive_read_disk_uname(&a->archive,
+				    ae_id);
+				ae_tag = ARCHIVE_ENTRY_ACL_USER;
+				break;
+			case DEF_GROUP:
+			case GROUP:
+				ae_name = archive_read_disk_gname(&a->archive,
+				    ae_id);
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+				break;
+			case DEF_CLASS_OBJ:
+			case CLASS_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_MASK;
+				break;
+			case DEF_USER_OBJ:
+			case USER_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				break;
+			case DEF_GROUP_OBJ:
+			case GROUP_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case DEF_OTHER_OBJ:
+			case OTHER_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
+				break;
+			default:
+				/* Unknown tag type, skip */
+				continue;
+			}
+
+			for (i = 0; i < acl_posix_perm_map_size; ++i) {
+				if ((aclent->a_perm &
+				    acl_posix_perm_map[i].p_perm) != 0)
+					ae_perm |= acl_posix_perm_map[i].a_perm;
+			}
+		} else
+			return (ARCHIVE_WARN);
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+		    ae_perm, ae_tag, ae_id, ae_name);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+set_acl(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl,
+    int ae_requested_type, const char *tname)
+{
+	aclent_t	 *aclent;
+#if ARCHIVE_ACL_SUNOS_NFS4
+	ace_t		 *ace;
+#endif
+	int		 cmd, e, r;
+	void		 *aclp;
+	int		 ret;
+	int		 ae_type, ae_permset, ae_tag, ae_id;
+	int		 perm_map_size;
+	const acl_perm_map_t	*perm_map;
+	uid_t		 ae_uid;
+	gid_t		 ae_gid;
+	const char	*ae_name;
+	int		 entries;
+	int		 i;
+
+	ret = ARCHIVE_OK;
+	entries = archive_acl_reset(abstract_acl, ae_requested_type);
+	if (entries == 0)
+		return (ARCHIVE_OK);
+
+
+	switch (ae_requested_type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+		cmd = SETACL;
+		aclp = malloc(entries * sizeof(aclent_t));
+		break;
+#if ARCHIVE_ACL_SUNOS_NFS4
+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+		cmd = ACE_SETACL;
+		aclp = malloc(entries * sizeof(ace_t));
+
+		break;
+#endif
+	default:
+		errno = ENOENT;
+		archive_set_error(a, errno, "Unsupported ACL type");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (aclp == NULL) {
+		archive_set_error(a, errno,
+		    "Can't allocate memory for acl buffer");
+		return (ARCHIVE_FAILED);
+	}
+
+	e = 0;
+
+	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+		aclent = NULL;
+#if ARCHIVE_ACL_SUNOS_NFS4
+		ace = NULL;
+#endif
+		if (cmd == SETACL) {
+			aclent = &((aclent_t *)aclp)[e];
+			aclent->a_id = -1;
+			aclent->a_type = 0;
+			aclent->a_perm = 0;
+		}
+#if ARCHIVE_ACL_SUNOS_NFS4
+		else {	/* cmd == ACE_SETACL */
+			ace = &((ace_t *)aclp)[e];
+			ace->a_who = -1;
+			ace->a_access_mask = 0;
+			ace->a_flags = 0;
+		}
+#endif	/* ARCHIVE_ACL_SUNOS_NFS4 */
+
+		switch (ae_tag) {
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+			if (aclent != NULL) {
+				aclent->a_id = ae_uid;
+				aclent->a_type |= USER;
+			}
+#if ARCHIVE_ACL_SUNOS_NFS4
+			else {
+				ace->a_who = ae_uid;
+			}
+#endif
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+			if (aclent != NULL) {
+				aclent->a_id = ae_gid;
+				aclent->a_type |= GROUP;
+			}
+#if ARCHIVE_ACL_SUNOS_NFS4
+			else {
+				ace->a_who = ae_gid;
+				ace->a_flags |= ACE_IDENTIFIER_GROUP;
+			}
+#endif
+			break;
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			if (aclent != NULL)
+				aclent->a_type |= USER_OBJ;
+#if ARCHIVE_ACL_SUNOS_NFS4
+			else {
+				ace->a_flags |= ACE_OWNER;
+			}
+#endif
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			if (aclent != NULL)
+				aclent->a_type |= GROUP_OBJ;
+#if ARCHIVE_ACL_SUNOS_NFS4
+			else {
+				ace->a_flags |= ACE_GROUP;
+				ace->a_flags |= ACE_IDENTIFIER_GROUP;
+			}
+#endif
+			break;
+		case ARCHIVE_ENTRY_ACL_MASK:
+			if (aclent != NULL)
+				aclent->a_type |= CLASS_OBJ;
+			break;
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			if (aclent != NULL)
+				aclent->a_type |= OTHER_OBJ;
+			break;
+#if ARCHIVE_ACL_SUNOS_NFS4
+		case ARCHIVE_ENTRY_ACL_EVERYONE:
+			if (ace != NULL)
+				ace->a_flags |= ACE_EVERYONE;
+			break;
+#endif
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL tag");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		r = 0;
+		switch (ae_type) {
+#if ARCHIVE_ACL_SUNOS_NFS4
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			if (ace != NULL)
+				ace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+			else
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			if (ace != NULL)
+				ace->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+			else
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+			if (ace != NULL)
+				ace->a_type = ACE_SYSTEM_AUDIT_ACE_TYPE;
+			else
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+			if (ace != NULL)
+				ace->a_type = ACE_SYSTEM_ALARM_ACE_TYPE;
+			else
+				r = -1;
+			break;
+#endif
+		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+			if (aclent == NULL)
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+			if (aclent != NULL)
+				aclent->a_type |= ACL_DEFAULT;
+			else
+				r = -1;
+			break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL entry type");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		if (r != 0) {
+			errno = EINVAL;
+			archive_set_error(a, errno,
+			    "Failed to set ACL entry type");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			perm_map_size = acl_nfs4_perm_map_size;
+			perm_map = acl_nfs4_perm_map;
+		} else {
+#endif
+			perm_map_size = acl_posix_perm_map_size;
+			perm_map = acl_posix_perm_map;
+#if ARCHIVE_ACL_SUNOS_NFS4
+		}
+#endif
+		for (i = 0; i < perm_map_size; ++i) {
+			if (ae_permset & perm_map[i].a_perm) {
+#if ARCHIVE_ACL_SUNOS_NFS4
+				if (ae_requested_type ==
+				    ARCHIVE_ENTRY_ACL_TYPE_NFS4)
+					ace->a_access_mask |=
+					    perm_map[i].p_perm;
+				else
+#endif
+					aclent->a_perm |= perm_map[i].p_perm;
+			}
+		}
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+				if (ae_permset & acl_nfs4_flag_map[i].a_perm) {
+					ace->a_flags |=
+					    acl_nfs4_flag_map[i].p_perm;
+				}
+			}
+		}
+#endif
+	e++;
+	}
+
+	/* Try restoring the ACL through 'fd' if we can. */
+	if (fd >= 0) {
+		if (facl(fd, cmd, entries, aclp) == 0)
+			ret = ARCHIVE_OK;
+		else {
+			if (errno == EOPNOTSUPP) {
+				/* Filesystem doesn't support ACLs */
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(a, errno,
+				    "Failed to set acl on fd: %s", tname);
+				ret = ARCHIVE_WARN;
+			}
+		}
+	} else if (acl(name, cmd, entries, aclp) != 0) {
+		if (errno == EOPNOTSUPP) {
+			/* Filesystem doesn't support ACLs */
+			ret = ARCHIVE_OK;
+		} else {
+			archive_set_error(a, errno, "Failed to set acl: %s",
+			    tname);
+			ret = ARCHIVE_WARN;
+		}
+	}
+exit_free:
+	free(aclp);
+	return (ret);
+}
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	const char	*accpath;
+	void		*aclp;
+	int		aclcnt;
+	int		r;
+
+	accpath = NULL;
+
+	if (*fd < 0) {
+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (accpath == NULL)
+			return (ARCHIVE_WARN);
+	}
+
+	archive_entry_acl_clear(entry);
+
+	aclp = NULL;
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+	if (*fd >= 0)
+		aclp = sunacl_get(ACE_GETACL, &aclcnt, *fd, NULL);
+	else if ((!a->follow_symlinks)
+	    && (archive_entry_filetype(entry) == AE_IFLNK))
+		/* We can't get the ACL of a symlink, so we assume it can't
+		   have one. */
+		aclp = NULL;
+	else
+		aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, accpath);
+
+	if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt,
+	    archive_entry_mode(entry), 1, S_ISDIR(archive_entry_mode(entry)),
+	    &r) == 0 && r == 1) {
+		free(aclp);
+		aclp = NULL;
+		return (ARCHIVE_OK);
+	}
+
+	if (aclp != NULL) {
+		r = translate_acl(a, entry, aclp, aclcnt,
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+		free(aclp);
+		aclp = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate NFSv4 ACLs");
+		}
+		return (r);
+	}
+#endif	/* ARCHIVE_ACL_SUNOS_NFS4 */
+
+	/* Retrieve POSIX.1e ACLs from file. */
+	if (*fd >= 0)
+		aclp = sunacl_get(GETACL, &aclcnt, *fd, NULL);
+	else if ((!a->follow_symlinks)
+	    && (archive_entry_filetype(entry) == AE_IFLNK))
+		/* We can't get the ACL of a symlink, so we assume it can't
+		   have one. */
+		aclp = NULL;
+	else
+		aclp = sunacl_get(GETACL, &aclcnt, 0, accpath);
+
+	/* Ignore "trivial" ACLs that just mirror the file mode. */
+	if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt,
+	    archive_entry_mode(entry), 0, S_ISDIR(archive_entry_mode(entry)),
+	    &r) == 0 && r == 1) {
+		free(aclp);
+		aclp = NULL;
+	}
+
+	if (aclp != NULL)
+	{
+		r = translate_acl(a, entry, aclp, aclcnt,
+		    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+		free(aclp);
+		aclp = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate access ACLs");
+			return (r);
+		}
+	}
+
+	return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+	int		ret = ARCHIVE_OK;
+
+	(void)mode;	/* UNUSED */
+
+	if ((archive_acl_types(abstract_acl)
+	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+		/* Solaris writes POSIX.1e access and default ACLs together */
+		ret = set_acl(a, fd, name, abstract_acl,
+		    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, "posix1e");
+
+		/* Simultaneous POSIX.1e and NFSv4 is not supported */
+		return (ret);
+	}
+#if ARCHIVE_ACL_SUNOS_NFS4
+	else if ((archive_acl_types(abstract_acl) &
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+		ret = set_acl(a, fd, name, abstract_acl,
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+	}
+#endif
+	return (ret);
+}
+#endif	/* ARCHIVE_ACL_SUNOS */
diff --git a/libarchive/archive_entry.3 b/libarchive/archive_entry.3
index f5e22af..f75916c 100644
--- a/libarchive/archive_entry.3
+++ b/libarchive/archive_entry.3
@@ -25,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd Feburary 2, 2012
+.Dd February 2, 2012
 .Dt ARCHIVE_ENTRY 3
 .Os
 .Sh NAME
diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c
index 10eff11..30fb456 100644
--- a/libarchive/archive_entry.c
+++ b/libarchive/archive_entry.c
@@ -401,7 +401,7 @@ archive_entry_fflags_text(struct archive_entry *entry)
 	return (NULL);
 }
 
-int64_t
+la_int64_t
 archive_entry_gid(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_gid);
@@ -502,7 +502,7 @@ _archive_entry_hardlink_l(struct archive_entry *entry,
 	return (archive_mstring_get_mbs_l(&entry->ae_hardlink, p, len, sc));
 }
 
-int64_t
+la_int64_t
 archive_entry_ino(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_ino);
@@ -514,7 +514,7 @@ archive_entry_ino_is_set(struct archive_entry *entry)
 	return (entry->ae_set & AE_SET_INO);
 }
 
-int64_t
+la_int64_t
 archive_entry_ino64(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_ino);
@@ -627,7 +627,7 @@ archive_entry_rdevminor(struct archive_entry *entry)
 		return minor(entry->ae_stat.aest_rdev);
 }
 
-int64_t
+la_int64_t
 archive_entry_size(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_size);
@@ -715,7 +715,7 @@ _archive_entry_symlink_l(struct archive_entry *entry,
 	return (archive_mstring_get_mbs_l( &entry->ae_symlink, p, len, sc));
 }
 
-int64_t
+la_int64_t
 archive_entry_uid(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_uid);
@@ -819,7 +819,7 @@ archive_entry_copy_fflags_text_w(struct archive_entry *entry,
 }
 
 void
-archive_entry_set_gid(struct archive_entry *entry, int64_t g)
+archive_entry_set_gid(struct archive_entry *entry, la_int64_t g)
 {
 	entry->stat_valid = 0;
 	entry->ae_stat.aest_gid = g;
@@ -868,7 +868,7 @@ _archive_entry_copy_gname_l(struct archive_entry *entry,
 }
 
 void
-archive_entry_set_ino(struct archive_entry *entry, int64_t ino)
+archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino)
 {
 	entry->stat_valid = 0;
 	entry->ae_set |= AE_SET_INO;
@@ -876,7 +876,7 @@ archive_entry_set_ino(struct archive_entry *entry, int64_t ino)
 }
 
 void
-archive_entry_set_ino64(struct archive_entry *entry, int64_t ino)
+archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino)
 {
 	entry->stat_valid = 0;
 	entry->ae_set |= AE_SET_INO;
@@ -1209,7 +1209,7 @@ archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m)
 }
 
 void
-archive_entry_set_size(struct archive_entry *entry, int64_t s)
+archive_entry_set_size(struct archive_entry *entry, la_int64_t s)
 {
 	entry->stat_valid = 0;
 	entry->ae_stat.aest_size = s;
@@ -1306,7 +1306,7 @@ _archive_entry_copy_symlink_l(struct archive_entry *entry,
 }
 
 void
-archive_entry_set_uid(struct archive_entry *entry, int64_t u)
+archive_entry_set_uid(struct archive_entry *entry, la_int64_t u)
 {
 	entry->stat_valid = 0;
 	entry->ae_stat.aest_uid = u;
@@ -1638,7 +1638,7 @@ _archive_entry_acl_text_l(struct archive_entry *entry, int flags,
  * SUCH DAMAGE.
  */
 
-static struct flag {
+static const struct flag {
 	const char	*name;
 	const wchar_t	*wname;
 	unsigned long	 set;
@@ -1708,6 +1708,9 @@ static struct flag {
 #ifdef UF_COMPRESSED
 	{ "nocompressed",L"nocompressed",	UF_COMPRESSED,	0 },
 #endif
+#ifdef UF_HIDDEN
+	{ "nohidden",	L"nohidden",		UF_HIDDEN,	0 },
+#endif
 #if defined(FS_UNRM_FL)
         { "nouunlink",	L"nouunlink",		FS_UNRM_FL,	0},
 #elif defined(EXT2_UNRM_FL)
@@ -1840,7 +1843,7 @@ ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
 	char *string, *dp;
 	const char *sp;
 	unsigned long bits;
-	struct flag *flag;
+	const struct flag *flag;
 	size_t	length;
 
 	bits = bitset | bitclear;
@@ -1892,7 +1895,7 @@ static const char *
 ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp)
 {
 	const char *start, *end;
-	struct flag *flag;
+	const struct flag *flag;
 	unsigned long set, clear;
 	const char *failed;
 
@@ -1960,7 +1963,7 @@ static const wchar_t *
 ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
 {
 	const wchar_t *start, *end;
-	struct flag *flag;
+	const struct flag *flag;
 	unsigned long set, clear;
 	const wchar_t *failed;
 
diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h
index 7645f0c..bcc2962 100644
--- a/libarchive/archive_entry.h
+++ b/libarchive/archive_entry.h
@@ -30,7 +30,7 @@
 #define	ARCHIVE_ENTRY_H_INCLUDED
 
 /* Note: Compiler will complain if this does not match archive.h! */
-#define	ARCHIVE_VERSION_NUMBER 3003001
+#define	ARCHIVE_VERSION_NUMBER 3003002
 
 /*
  * Note: archive_entry.h is for use outside of libarchive; the
diff --git a/libarchive/archive_entry_acl.3 b/libarchive/archive_entry_acl.3
index c5115f7..534dbfa 100644
--- a/libarchive/archive_entry_acl.3
+++ b/libarchive/archive_entry_acl.3
@@ -32,7 +32,7 @@
 .Nm archive_entry_acl_clear ,
 .Nm archive_entry_acl_count ,
 .Nm archive_entry_acl_from_text ,
-.Nm archive_entry_acl_from_text_w,
+.Nm archive_entry_acl_from_text_w ,
 .Nm archive_entry_acl_next ,
 .Nm archive_entry_acl_next_w ,
 .Nm archive_entry_acl_reset ,
@@ -267,7 +267,7 @@ Only inherit, do not apply the permission on the directory itself.
 .It Dv ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT ( Sy n )
 Do not propagate inherit flags. Only first-level entries inherit ACLs.
 .It Dv ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS ( Sy S )
-Trigger alarm or audit on succesful access.
+Trigger alarm or audit on successful access.
 .It Dv ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS ( Sy F )
 Trigger alarm or audit on failed access.
 .It Dv ARCHIVE_ENTRY_ACL_ENTRY_INHERITED ( Sy I )
@@ -279,7 +279,7 @@ and
 .Fn archive_entry_acl_add_entry_w
 add a single ACL entry.
 For the access ACL and non-extended principals, the classic Unix permissions
-are updated. An archive enry cannot contain both POSIX.1e and NFSv4 ACL
+are updated. An archive entry cannot contain both POSIX.1e and NFSv4 ACL
 entries.
 .Pp
 .Fn archive_entry_acl_clear
@@ -303,7 +303,7 @@ for POSIX.1e ACLs and
 for NFSv4 ACLs. For POSIX.1e ACLs if
 .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
 is included and at least one extended ACL entry is found,
-the three non-extened ACLs are added.
+the three non-extended ACLs are added.
 .Pp
 .Fn archive_entry_acl_from_text
 and
@@ -367,7 +367,7 @@ and
 .Fn archive_entry_acl_to_text_w
 convert the ACL entries for the given type into a
 .Pq wide
-string of ACL entries separated by newline. If the the pointer
+string of ACL entries separated by newline. If the pointer
 .Fa len_p
 is not NULL, then the function shall return the length of the string
 .Pq not including the NULL terminator
diff --git a/libarchive/archive_entry_paths.3 b/libarchive/archive_entry_paths.3
index fd22cf7..f647212 100644
--- a/libarchive/archive_entry_paths.3
+++ b/libarchive/archive_entry_paths.3
@@ -31,25 +31,25 @@
 .Nm archive_entry_set_hardlink ,
 .Nm archive_entry_copy_hardlink ,
 .Nm archive_entry_copy_hardlink_w ,
-.Nm archve_entry_update_hardlink_utf8 ,
+.Nm archive_entry_update_hardlink_utf8 ,
 .Nm archive_entry_set_link ,
 .Nm archive_entry_copy_link ,
 .Nm archive_entry_copy_link_w ,
-.Nm archve_entry_update_link_utf8 ,
+.Nm archive_entry_update_link_utf8 ,
 .Nm archive_entry_pathname ,
 .Nm archive_entry_pathname_w ,
 .Nm archive_entry_set_pathname ,
 .Nm archive_entry_copy_pathname ,
 .Nm archive_entry_copy_pathname_w ,
-.Nm archve_entry_update_pathname_utf8 ,
+.Nm archive_entry_update_pathname_utf8 ,
 .Nm archive_entry_sourcepath ,
 .Nm archive_entry_copy_sourcepath ,
-.Nm archive_entry_symlink,
-.Nm archive_entry_symlink_w,
+.Nm archive_entry_symlink ,
+.Nm archive_entry_symlink_w ,
 .Nm archive_entry_set_symlink ,
 .Nm archive_entry_copy_symlink ,
 .Nm archive_entry_copy_symlink_w ,
-.Nm archve_entry_update_symlink_utf8
+.Nm archive_entry_update_symlink_utf8
 .Nd functions for manipulating path names in archive entry descriptions
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
diff --git a/libarchive/archive_entry_perms.3 b/libarchive/archive_entry_perms.3
index 340c5ea..aae3648 100644
--- a/libarchive/archive_entry_perms.3
+++ b/libarchive/archive_entry_perms.3
@@ -34,8 +34,8 @@
 .Nm archive_entry_perm ,
 .Nm archive_entry_set_perm ,
 .Nm archive_entry_strmode ,
-.Nm archive_entry_uname
-.Nm archive_entry_uname_w
+.Nm archive_entry_uname ,
+.Nm archive_entry_uname_w ,
 .Nm archive_entry_set_uname ,
 .Nm archive_entry_copy_uname ,
 .Nm archive_entry_copy_uname_w ,
diff --git a/libarchive/archive_entry_sparse.c b/libarchive/archive_entry_sparse.c
index fed74f5..74917b3 100644
--- a/libarchive/archive_entry_sparse.c
+++ b/libarchive/archive_entry_sparse.c
@@ -51,7 +51,7 @@ archive_entry_sparse_clear(struct archive_entry *entry)
 
 void
 archive_entry_sparse_add_entry(struct archive_entry *entry,
-	int64_t offset, int64_t length)
+	la_int64_t offset, la_int64_t length)
 {
 	struct ae_sparse *sp;
 
@@ -135,7 +135,7 @@ archive_entry_sparse_reset(struct archive_entry * entry)
 
 int
 archive_entry_sparse_next(struct archive_entry * entry,
-	int64_t *offset, int64_t *length)
+	la_int64_t *offset, la_int64_t *length)
 {
 	if (entry->sparse_p) {
 		*offset = entry->sparse_p->offset;
diff --git a/libarchive/archive_getdate.c b/libarchive/archive_getdate.c
index beb0cba..030c083 100644
--- a/libarchive/archive_getdate.c
+++ b/libarchive/archive_getdate.c
@@ -691,7 +691,7 @@ 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)
 {
-	static int DaysInMonth[12] = {
+	signed char DaysInMonth[12] = {
 		31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
 	};
 	time_t	Julian;
diff --git a/libarchive/archive_openssl_hmac_private.h b/libarchive/archive_openssl_hmac_private.h
index 2deeb5f..59f95b8 100644
--- a/libarchive/archive_openssl_hmac_private.h
+++ b/libarchive/archive_openssl_hmac_private.h
@@ -28,7 +28,7 @@
 #include <openssl/hmac.h>
 #include <openssl/opensslv.h>
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 #include <stdlib.h> /* malloc, free */
 #include <string.h> /* memset */
 static inline HMAC_CTX *HMAC_CTX_new(void)
diff --git a/libarchive/archive_pack_dev.c b/libarchive/archive_pack_dev.c
index 6b7b472..098881b 100644
--- a/libarchive/archive_pack_dev.c
+++ b/libarchive/archive_pack_dev.c
@@ -280,7 +280,7 @@ pack_bsdos(int n, unsigned long numbers[], const char **error)
 
 		/* list of formats and pack functions */
 		/* this list must be sorted lexically */
-static struct format {
+static const struct format {
 	const char	*name;
 	pack_t		*pack;
 } formats[] = {
diff --git a/libarchive/archive_platform.h b/libarchive/archive_platform.h
index c9a9602..34be8ed 100644
--- a/libarchive/archive_platform.h
+++ b/libarchive/archive_platform.h
@@ -143,32 +143,6 @@
 #endif
 
 /*
- * If this platform has <sys/acl.h>, acl_create(), acl_init(),
- * acl_set_file(), and ACL_USER, we assume it has the rest of the
- * POSIX.1e draft functions used in archive_read_extract.c.
- */
-#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE
-#if HAVE_ACL_USER
-#define	HAVE_POSIX_ACL	1
-#elif HAVE_ACL_TYPE_EXTENDED
-#define HAVE_DARWIN_ACL 1
-#endif
-#endif
-
-/*
- * If this platform has <sys/acl.h>, acl_get(), facl_get(), acl_set(),
- * facl_set() and types aclent_t and ace_t it uses Solaris-style ACL functions
- */
-#if HAVE_SYS_ACL_H && HAVE_ACL_GET && HAVE_FACL_GET && HAVE_ACL_SET && HAVE_FACL_SET && HAVE_ACLENT_T && HAVE_ACE_T
-#define	HAVE_SUN_ACL	1
-#endif
-
-/* Define if platform supports NFSv4 ACLs */
-#if (HAVE_POSIX_ACL && HAVE_ACL_TYPE_NFS4) || HAVE_SUN_ACL || HAVE_DARWIN_ACL
-#define HAVE_NFS4_ACL	1
-#endif
-
-/*
  * If we can't restore metadata using a file descriptor, then
  * for compatibility's sake, close files before trying to restore metadata.
  */
diff --git a/libarchive/archive_write_disk_private.h b/libarchive/archive_platform_acl.h
similarity index 68%
copy from libarchive/archive_write_disk_private.h
copy to libarchive/archive_platform_acl.h
index d84e7e1..3498f78 100644
--- a/libarchive/archive_write_disk_private.h
+++ b/libarchive/archive_platform_acl.h
@@ -1,13 +1,12 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * 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
- *    in this position and unchanged.
+ *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
@@ -23,21 +22,28 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/lib/libarchive/archive_write_disk_private.h 201086 2009-12-28 02:17:53Z kientzle $
+ * $FreeBSD$
  */
 
-#ifndef __LIBARCHIVE_BUILD
-#error This header is only to be used internally to libarchive.
-#endif
-
-#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
-#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
+/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
 
-#include "archive_acl_private.h"
+#ifndef ARCHIVE_PLATFORM_ACL_H_INCLUDED
+#define ARCHIVE_PLATFORM_ACL_H_INCLUDED
 
-struct archive_write_disk;
+/*
+ * Determine what ACL types are supported
+ */
+#if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_SUNOS || ARCHIVE_ACL_LIBACL
+#define ARCHIVE_ACL_POSIX1E     1
+#endif
 
-int
-archive_write_disk_set_acls(struct archive *, int /* fd */, const char * /* pathname */, struct archive_acl *);
+#if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_SUNOS_NFS4 || \
+    ARCHIVE_ACL_DARWIN  || ARCHIVE_ACL_LIBRICHACL
+#define ARCHIVE_ACL_NFS4        1
+#endif
 
+#if ARCHIVE_ACL_POSIX1E || ARCHIVE_ACL_NFS4
+#define ARCHIVE_ACL_SUPPORT     1
 #endif
+
+#endif	/* ARCHIVE_PLATFORM_ACL_H_INCLUDED */
diff --git a/libarchive/archive_openssl_hmac_private.h b/libarchive/archive_platform_xattr.h
similarity index 70%
copy from libarchive/archive_openssl_hmac_private.h
copy to libarchive/archive_platform_xattr.h
index 2deeb5f..4edfecf 100644
--- a/libarchive/archive_openssl_hmac_private.h
+++ b/libarchive/archive_platform_xattr.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2017 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -21,28 +21,21 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
  */
-#ifndef ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED
-#define ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED
 
-#include <openssl/hmac.h>
-#include <openssl/opensslv.h>
+/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-#include <stdlib.h> /* malloc, free */
-#include <string.h> /* memset */
-static inline HMAC_CTX *HMAC_CTX_new(void)
-{
-	HMAC_CTX *ctx = (HMAC_CTX *)calloc(1, sizeof(HMAC_CTX));
-	return ctx;
-}
+#ifndef ARCHIVE_PLATFORM_XATTR_H_INCLUDED
+#define ARCHIVE_PLATFORM_XATTR_H_INCLUDED
 
-static inline void HMAC_CTX_free(HMAC_CTX *ctx)
-{
-	HMAC_CTX_cleanup(ctx);
-	memset(ctx, 0, sizeof(*ctx));
-	free(ctx);
-}
+/*
+ * 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
+#endif	/* ARCHIVE_PLATFORM_XATTR_H_INCLUDED */
diff --git a/libarchive/archive_random.c b/libarchive/archive_random.c
index 357f973..65ea691 100644
--- a/libarchive/archive_random.c
+++ b/libarchive/archive_random.c
@@ -221,8 +221,11 @@ arc4_stir(void)
 	/*
 	 * 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 < 1024; i++)
+
+	for (i = 0; i < 3072; i++)
 		(void)arc4_getbyte();
 	arc4_count = 1600000;
 }
diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c
index d1feceb..a642a33 100644
--- a/libarchive/archive_read.c
+++ b/libarchive/archive_read.c
@@ -881,7 +881,8 @@ archive_read_data(struct archive *_a, void *buff, size_t s)
 			len = a->read_data_remaining;
 			if (len > s)
 				len = s;
-			memcpy(dest, a->read_data_block, len);
+			if (len)
+				memcpy(dest, a->read_data_block, len);
 			s -= len;
 			a->read_data_block += len;
 			a->read_data_remaining -= len;
diff --git a/libarchive/archive_read_disk.3 b/libarchive/archive_read_disk.3
index 2a5c130..027f63c 100644
--- a/libarchive/archive_read_disk.3
+++ b/libarchive/archive_read_disk.3
@@ -24,11 +24,12 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd December 30, 2016
+.Dd April 3, 2017
 .Dt ARCHIVE_READ_DISK 3
 .Os
 .Sh NAME
 .Nm archive_read_disk_new ,
+.Nm archive_read_disk_set_behavior ,
 .Nm archive_read_disk_set_symlink_logical ,
 .Nm archive_read_disk_set_symlink_physical ,
 .Nm archive_read_disk_set_symlink_hybrid ,
@@ -37,10 +38,7 @@
 .Nm archive_read_disk_uname ,
 .Nm archive_read_disk_set_uname_lookup ,
 .Nm archive_read_disk_set_gname_lookup ,
-.Nm archive_read_disk_set_standard_lookup ,
-.Nm archive_read_close ,
-.Nm archive_read_finish ,
-.Nm archive_read_free
+.Nm archive_read_disk_set_standard_lookup
 .Nd functions for reading objects from disk
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
@@ -49,6 +47,8 @@ Streaming Archive Library (libarchive, -larchive)
 .Ft struct archive *
 .Fn archive_read_disk_new "void"
 .Ft int
+.Fn archive_read_disk_set_behavior "struct archive *" "int"
+.Ft int
 .Fn archive_read_disk_set_symlink_logical "struct archive *"
 .Ft int
 .Fn archive_read_disk_set_symlink_physical "struct archive *"
@@ -81,12 +81,6 @@ Streaming Archive Library (libarchive, -larchive)
 .Fa "int fd"
 .Fa "const struct stat *"
 .Fc
-.Ft int
-.Fn archive_read_close "struct archive *"
-.Ft int
-.Fn archive_read_finish "struct archive *"
-.Ft int
-.Fn archive_read_free "struct archive *"
 .Sh DESCRIPTION
 These functions provide an API for reading information about
 objects on disk.
@@ -98,6 +92,51 @@ objects.
 Allocates and initializes a
 .Tn struct archive
 object suitable for reading object information from disk.
+.It Fn archive_read_disk_set_behavior
+Configures various behavior options when reading entries from disk.
+The flags field consists of a bitwise OR of one or more of the
+following values:
+.Bl -tag -compact -width "indent"
+.It Cm ARCHIVE_READDISK_HONOR_NODUMP
+Skip files and directories with the nodump file attribute (file flag) set.
+By default, the nodump file atrribute is ignored.
+.It Cm ARCHIVE_READDISK_MAC_COPYFILE
+Mac OS X specific. Read metadata (ACLs and extended attributes) with
+.Xr copyfile 3 .
+By default, metadata is read using
+.Xr copyfile 3 .
+.It Cm ARCHIVE_READDISK_NO_ACL
+Do not read Access Control Lists.
+By default, ACLs are read from disk.
+.It Cm ARCHIVE_READDISK_NO_FFLAGS
+Do not read file attributes (file flags).
+By default, file attributes are read from disk.
+See
+.Xr chattr 1
+.Pq Linux
+or
+.Xr chflags 1
+.Pq FreeBSD, Mac OS X
+for more information on file attributes.
+.It Cm ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS
+Do not traverse mount points.
+By defaut, moint points are traversed.
+.It Cm ARCHIVE_READDISK_NO_XATTR
+Do not read extended file attributes (xattrs).
+By default, extended file attributes are read from disk.
+See
+.Xr xattr 7
+.Pq Linux ,
+.Xr xattr 2
+.Pq Mac OS X ,
+or
+.Xr getextattr 8
+.Pq FreeBSD
+for more information on extended file attributes.
+.It Cm ARCHIVE_READDISK_RESTORE_ATIME
+Restore access time of traversed files.
+By default, access time of traversed files is not restored.
+.El
 .It Xo
 .Fn archive_read_disk_set_symlink_logical ,
 .Fn archive_read_disk_set_symlink_physical ,
@@ -181,17 +220,6 @@ using the currently registered lookup functions above.
 This affects the file ownership fields and ACL values in the
 .Tn struct archive_entry
 object.
-.It Fn archive_read_close
-Does nothing for
-.Tn archive_read_disk
-handles.
-.It Fn archive_read_finish
-This is a deprecated synonym for
-.Fn archive_read_free .
-.It Fn archive_read_free
-Invokes
-.Fn archive_read_close
-if it was not invoked manually, then releases all resources.
 .El
 More information about the
 .Va struct archive
diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c
index b2f1d17..548ba89 100644
--- a/libarchive/archive_read_disk_entry_from_file.c
+++ b/libarchive/archive_read_disk_entry_from_file.c
@@ -26,23 +26,14 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 201084 2009-12-28 02:14:09Z kientzle $");
+__FBSDID("$FreeBSD");
 
 /* This is the tree-walking code for POSIX systems. */
 #if !defined(_WIN32) || defined(__CYGWIN__)
 
 #ifdef HAVE_SYS_TYPES_H
-/* Mac OSX requires sys/types.h before sys/acl.h. */
 #include <sys/types.h>
 #endif
-#ifdef HAVE_SYS_ACL_H
-#include <sys/acl.h>
-#endif
-#ifdef HAVE_DARWIN_ACL
-#include <membership.h>
-#include <grp.h>
-#include <pwd.h>
-#endif
 #ifdef HAVE_SYS_EXTATTR_H
 #include <sys/extattr.h>
 #endif
@@ -63,9 +54,6 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010
 #ifdef HAVE_SYS_EA_H
 #include <sys/ea.h>
 #endif
-#ifdef HAVE_ACL_LIBACL_H
-#include <acl/libacl.h>
-#endif
 #ifdef HAVE_COPYFILE_H
 #include <copyfile.h>
 #endif
@@ -113,27 +101,6 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010
 #define O_CLOEXEC	0
 #endif
 
-/*
- * Linux and FreeBSD plug this obvious hole in POSIX.1e in
- * different ways.
- */
-#if HAVE_ACL_GET_PERM
-#define	ACL_GET_PERM acl_get_perm
-#elif HAVE_ACL_GET_PERM_NP
-#define	ACL_GET_PERM acl_get_perm_np
-#endif
-
-/* NFSv4 platform ACL type */
-#if HAVE_SUN_ACL
-#define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACE_T
-#elif HAVE_DARWIN_ACL
-#define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACL_TYPE_EXTENDED
-#elif HAVE_ACL_TYPE_NFS4
-#define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACL_TYPE_NFS4
-#endif
-
-static int setup_acls(struct archive_read_disk *,
-    struct archive_entry *, int *fd);
 static int setup_mac_metadata(struct archive_read_disk *,
     struct archive_entry *, int *fd);
 static int setup_xattrs(struct archive_read_disk *,
@@ -145,6 +112,45 @@ 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 pahtname.
+ */
+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,
@@ -279,7 +285,7 @@ archive_read_disk_entry_from_file(struct archive *_a,
 
 	r = 0;
 	if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0)
-		r = setup_acls(a, entry, &fd);
+		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)
@@ -328,19 +334,10 @@ setup_mac_metadata(struct archive_read_disk *a,
 	struct archive_string tempfile;
 
 	(void)fd; /* UNUSED */
-	name = archive_entry_sourcepath(entry);
+
+	name = archive_read_disk_entry_setup_path(a, entry, NULL);
 	if (name == NULL)
-		name = archive_entry_pathname(entry);
-	else if (a->tree != NULL && a->tree_enter_working_dir(a->tree) != 0) {
-		archive_set_error(&a->archive, errno,
-			    "Can't change dir to read extended attributes");
-			return (ARCHIVE_FAILED);
-	}
-	if (name == NULL) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Can't open file to read extended attributes: No name");
 		return (ARCHIVE_WARN);
-	}
 
 	/* Short-circuit if there's nothing to do. */
 	have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);
@@ -426,996 +423,10 @@ setup_mac_metadata(struct archive_read_disk *a,
 }
 #endif
 
-#if HAVE_DARWIN_ACL
-static int translate_guid(struct archive *, acl_entry_t,
-    int *, int *, const char **);
-
-static void add_trivial_nfs4_acl(struct archive_entry *);
-#endif
-
-#if HAVE_SUN_ACL
-static int
-sun_acl_is_trivial(acl_t *, mode_t, int *trivialp);
-#endif
-
-#if HAVE_POSIX_ACL || HAVE_NFS4_ACL
-static int translate_acl(struct archive_read_disk *a,
-    struct archive_entry *entry,
-#if HAVE_SUN_ACL
-    acl_t *acl,
-#else
-    acl_t acl,
-#endif
-    int archive_entry_acl_type);
-
-static int
-setup_acls(struct archive_read_disk *a,
-    struct archive_entry *entry, int *fd)
-{
-	const char	*accpath;
-#if HAVE_SUN_ACL
-	acl_t		*acl;
-#else
-	acl_t		acl;
-#endif
-	int		r;
-
-	accpath = NULL;
-
-#if HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_ACL_GET_FD_NP
-	if (*fd < 0)
-#else
-	/* For default ACLs on Linux we need reachable accpath */
-	if (*fd < 0 || S_ISDIR(archive_entry_mode(entry)))
-#endif
-	{
-		accpath = archive_entry_sourcepath(entry);
-		if (accpath == NULL || (a->tree != NULL &&
-		    a->tree_enter_working_dir(a->tree) != 0))
-			accpath = archive_entry_pathname(entry);
-		if (accpath == NULL) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-			    "Couldn't determine file path to read ACLs");
-			return (ARCHIVE_WARN);
-		}
-		if (a->tree != NULL &&
-#if !HAVE_SUN_ACL && !HAVE_DARWIN_ACL && !HAVE_ACL_GET_FD_NP
-		    *fd < 0 &&
-#endif
-		    (a->follow_symlinks ||
-		    archive_entry_filetype(entry) != AE_IFLNK)) {
-			*fd = a->open_on_current_dir(a->tree,
-			    accpath, O_RDONLY | O_NONBLOCK);
-		}
-	}
-
-	archive_entry_acl_clear(entry);
-
-	acl = NULL;
-
-#if HAVE_NFS4_ACL
-	/* Try NFSv4 ACL first. */
-	if (*fd >= 0)
-#if HAVE_SUN_ACL
-		/* Solaris reads both POSIX.1e and NFSv4 ACL here */
-		facl_get(*fd, 0, &acl);
-#elif HAVE_ACL_GET_FD_NP
-		acl = acl_get_fd_np(*fd, ARCHIVE_PLATFORM_ACL_TYPE_NFS4);
-#else
-		acl = acl_get_fd(*fd);
-#endif
-#if HAVE_ACL_GET_LINK_NP
-	else if (!a->follow_symlinks)
-		acl = acl_get_link_np(accpath, ARCHIVE_PLATFORM_ACL_TYPE_NFS4);
-#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
-#if HAVE_SUN_ACL
-		/* Solaris reads both POSIX.1e and NFSv4 ACLs here */
-		acl_get(accpath, 0, &acl);
-#else
-		acl = acl_get_file(accpath, ARCHIVE_PLATFORM_ACL_TYPE_NFS4);
-#endif
-
-
-#if HAVE_ACL_IS_TRIVIAL_NP || HAVE_SUN_ACL
-	/* Ignore "trivial" ACLs that just mirror the file mode. */
-	if (acl != NULL) {
-#if HAVE_SUN_ACL
-		if (sun_acl_is_trivial(acl, archive_entry_mode(entry),
-		    &r) == 0 && r == 1)
-#elif HAVE_ACL_IS_TRIVIAL_NP
-		if (acl_is_trivial_np(acl, &r) == 0 && r == 1)
-#endif
-		{
-			acl_free(acl);
-			acl = NULL;
-			/*
-			 * Simultaneous NFSv4 and POSIX.1e ACLs for the same
-			 * entry are not allowed, so we should return here
-			 */
-			return (ARCHIVE_OK);
-		}
-	}
-#endif	/* HAVE_ACL_IS_TRIVIAL_NP || HAVE_SUN_ACL */
-	if (acl != NULL) {
-		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4);
-		acl_free(acl);
-		if (r != ARCHIVE_OK) {
-			archive_set_error(&a->archive, errno,
-			    "Couldn't translate "
-#if !HAVE_SUN_ACL
-			    "NFSv4 "
-#endif
-			    "ACLs");
-		}
-#if HAVE_DARWIN_ACL
-		/*
-		 * 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);
-#endif
-		return (r);
-	}
-#endif	/* HAVE_NFS4_ACL */
-
-#if HAVE_POSIX_ACL
-	/* This code path is skipped on MacOS and Solaris */
+#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX
 
-	/* Retrieve access ACL from file. */
-	if (*fd >= 0)
-		acl = acl_get_fd(*fd);
-#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) {
-		if (r) {
-			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 HAVE_ACL_GET_FD_NP
-		if (*fd >= 0)
-			acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT);
-		else
-#endif
-		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	/* HAVE_POSIX_ACL */
-	return (ARCHIVE_OK);
-}
-
-/*
- * Translate system ACL permissions into libarchive internal structure
- */
-static const struct {
-	const int archive_perm;
-	const int platform_perm;
-} acl_perm_map[] = {
-#if HAVE_SUN_ACL	/* Solaris NFSv4 ACL permissions */
-	{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}
-#elif HAVE_DARWIN_ACL	/* MacOS ACL permissions */
-	{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},
-	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
-#else	/* POSIX.1e ACL permissions */
-	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
-	{ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
-	{ARCHIVE_ENTRY_ACL_READ, ACL_READ},
-#if HAVE_ACL_TYPE_NFS4	/* FreeBSD NFSv4 ACL permissions */
-	{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}
-#endif
-#endif	/* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */
-};
-
-#if HAVE_NFS4_ACL
-/*
- * Translate system NFSv4 inheritance flags into libarchive internal structure
- */
-static const struct {
-	const int archive_inherit;
-	const int platform_inherit;
-} acl_inherit_map[] = {
-#if HAVE_SUN_ACL	/* Solaris ACL inheritance flags */
-	{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},
-	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE}
-#elif HAVE_DARWIN_ACL	/* MacOS NFSv4 inheritance flags */
-	{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}
-#else	/* FreeBSD NFSv4 ACL inheritance flags */
-	{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},
-	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}
-#endif	/* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */
-};
-#endif	/* HAVE_NFS4_ACL */
-
-#if HAVE_DARWIN_ACL
-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;
-	struct passwd *pwd;
-	struct group *grp;
-
-	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;
-		pwd = getpwuuid(q);
-		if (pwd == NULL) {
-			*ae_id = ugid;
-			*ae_name = NULL;
-		} else {
-			*ae_id = pwd->pw_uid;
-			*ae_name = archive_read_disk_uname(a, *ae_id);
-		}
-	} else if (idtype == ID_TYPE_GID) {
-		*ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
-		grp = getgruuid(q);
-		if (grp == NULL) {
-			*ae_id = ugid;
-			*ae_name = NULL;
-		} else {
-			*ae_id = grp->gr_gid;
-			*ae_name = archive_read_disk_gname(a, *ae_id);
-		}
-	} else
-		r = 1;
-
-	acl_free(q);
-	return (r);
-}
-
-/*
- * Add trivial NFSv4 ACL entries from mode
- */
-static void
-add_trivial_nfs4_acl(struct archive_entry *entry)
-{
-	mode_t mode;
-	int i;
-	const int rperm = ARCHIVE_ENTRY_ACL_READ_DATA;
-	const int wperm = ARCHIVE_ENTRY_ACL_WRITE_DATA |
-	    ARCHIVE_ENTRY_ACL_APPEND_DATA;
-	const int eperm = ARCHIVE_ENTRY_ACL_EXECUTE;
-	const int pubset = ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
-	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
-	    ARCHIVE_ENTRY_ACL_READ_ACL |
-	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
-	const int ownset = pubset | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
-	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
-	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
-	    ARCHIVE_ENTRY_ACL_WRITE_OWNER;
-
-	struct {
-	    const int type;
-	    const int tag;
-	    int permset;
-	} tacl_entry[] = {
-	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},
-	    {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},
-	    {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0},
-	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, ownset},
-	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_GROUP_OBJ, pubset},
-	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EVERYONE, pubset}
-	};
-
-	mode = archive_entry_mode(entry);
-
-	/* Permissions for everyone@ */
-	if (mode & 0004)
-		tacl_entry[5].permset |= rperm;
-	if (mode & 0002)
-		tacl_entry[5].permset |= wperm;
-	if (mode & 0001)
-		tacl_entry[5].permset |= eperm;
-
-	/* Permissions for group@ */
-	if (mode & 0040)
-		tacl_entry[4].permset |= rperm;
-	else if (mode & 0004)
-		tacl_entry[2].permset |= rperm;
-	if (mode & 0020)
-		tacl_entry[4].permset |= wperm;
-	else if (mode & 0002)
-		tacl_entry[2].permset |= wperm;
-	if (mode & 0010)
-		tacl_entry[4].permset |= eperm;
-	else if (mode & 0001)
-		tacl_entry[2].permset |= eperm;
-
-	/* Permissions for owner@ */
-	if (mode & 0400) {
-		tacl_entry[3].permset |= rperm;
-		if (!(mode & 0040) && (mode & 0004))
-			tacl_entry[0].permset |= rperm;
-	} else if ((mode & 0040) || (mode & 0004))
-		tacl_entry[1].permset |= rperm;
-	if (mode & 0200) {
-		tacl_entry[3].permset |= wperm;
-		if (!(mode & 0020) && (mode & 0002))
-			tacl_entry[0].permset |= wperm;
-	} else if ((mode & 0020) || (mode & 0002))
-		tacl_entry[1].permset |= wperm;
-	if (mode & 0100) {
-		tacl_entry[3].permset |= eperm;
-		if (!(mode & 0010) && (mode & 0001))
-			tacl_entry[0].permset |= eperm;
-	} else if ((mode & 0010) || (mode & 0001))
-		tacl_entry[1].permset |= eperm;
-
-	for (i = 0; i < 6; i++) {
-		if (tacl_entry[i].permset != 0) {
-			archive_entry_acl_add_entry(entry,
-			    tacl_entry[i].type, tacl_entry[i].permset,
-			    tacl_entry[i].tag, -1, NULL);
-		}
-	}
-
-	return;
-}
-#elif HAVE_SUN_ACL
-/*
- * Check if acl is trivial
- * This is a FreeBSD acl_is_trivial_np() implementation for Solaris
- */
-static int
-sun_acl_is_trivial(acl_t *acl, mode_t mode, int *trivialp)
-{
-	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];
-
-	if (acl == NULL || trivialp == NULL)
-		return (-1);
-
-	*trivialp = 0;
-
-	/* ACL_IS_TRIVIAL flag must be set for both POSIX.1e and NFSv4 ACLs */
-	if ((acl->acl_flags & ACL_IS_TRIVIAL) == 0)
-		return (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 (acl->acl_type == ACLENT_T) {
-		if (acl->acl_cnt == 4)
-			*trivialp = 1;
-		return (0);
-	}
-
-	if (acl->acl_type != ACE_T || acl->acl_entry_size != sizeof(ace_t))
-		return (-1);
-
-	/*
-	 * 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 (acl->acl_cnt != p)
-		return (0);
-
-	p = 0;
-	for (i = 0; i < 6; i++) {
-		if (tace[i].a_access_mask != 0) {
-			ace = &((ace_t *)acl->acl_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 &&
-			    ((acl->acl_flags & ACL_IS_DIR) == 0 ||
-			    (tace[i].a_access_mask & wperm) == 0 ||
-			    ace->a_access_mask !=
-			    (tace[i].a_access_mask | ACE_DELETE_CHILD))))
-				return (0);
-			p++;
-		}
-	}
-
-	*trivialp = 1;
-	return (0);
-}
-#endif	/* HAVE_SUN_ACL */
-
-#if HAVE_SUN_ACL
 /*
- * Translate Solaris POSIX.1e and NFSv4 ACLs into libarchive internal ACL
- */
-static int
-translate_acl(struct archive_read_disk *a,
-    struct archive_entry *entry, acl_t *acl, 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;
-	ace_t *ace;
-
-	(void)default_entry_acl_type;
-
-	if (acl->acl_cnt <= 0)
-		return (ARCHIVE_OK);
-
-	for (e = 0; e < acl->acl_cnt; e++) {
-		ae_name = NULL;
-		ae_tag = 0;
-		ae_perm = 0;
-
-		if (acl->acl_type == ACE_T) {
-			ace = &((ace_t *)acl->acl_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 < (int)(sizeof(acl_inherit_map) /
-			    sizeof(acl_inherit_map[0])); ++i) {
-				if ((ace->a_flags &
-				    acl_inherit_map[i].platform_inherit) != 0)
-					ae_perm |=
-					    acl_inherit_map[i].archive_inherit;
-			}
-
-			for (i = 0; i < (int)(sizeof(acl_perm_map) /
-			    sizeof(acl_perm_map[0])); ++i) {
-				if ((ace->a_access_mask &
-				    acl_perm_map[i].platform_perm) != 0)
-					ae_perm |=
-					    acl_perm_map[i].archive_perm;
-			}
-		} else {
-			aclent = &((aclent_t *)acl->acl_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;
-			}
-
-			if ((aclent->a_perm & 1) != 0)
-				ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE;
-			if ((aclent->a_perm & 2) != 0)
-				ae_perm |= ARCHIVE_ENTRY_ACL_WRITE;
-			if ((aclent->a_perm & 4) != 0)
-				ae_perm |= ARCHIVE_ENTRY_ACL_READ;
-		} /* default_entry_acl_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4 */
-
-		archive_entry_acl_add_entry(entry, entry_acl_type,
-		    ae_perm, ae_tag, ae_id, ae_name);
-	}
-	return (ARCHIVE_OK);
-}
-#else	/* !HAVE_SUN_ACL */
-/*
- * Translate POSIX.1e (Linux), FreeBSD (both POSIX.1e and NFSv4) and
- * MacOS (NFSv4 only) 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;
-#if HAVE_ACL_TYPE_NFS4
-	acl_entry_type_t acl_type;
-	int brand;
-#endif
-#if HAVE_ACL_TYPE_NFS4 || HAVE_DARWIN_ACL
-	acl_flagset_t	 acl_flagset;
-#endif
-	acl_entry_t	 acl_entry;
-	acl_permset_t	 acl_permset;
-	int		 i, entry_acl_type;
-	int		 r, s, ae_id, ae_tag, ae_perm;
-#if !HAVE_DARWIN_ACL
-	void		*q;
-#endif
-	const char	*ae_name;
-
-#if HAVE_ACL_TYPE_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);
-	}
-
-#if HAVE_DARWIN_ACL
-	while (s == 0)
-#else	/* FreeBSD, Linux */
-	while (s == 1)
-#endif
-	{
-		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) {
-#if !HAVE_DARWIN_ACL	/* FreeBSD, Linux */
-		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 HAVE_ACL_TYPE_NFS4
-		case ACL_EVERYONE:
-			ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
-			break;
-#endif
-#else	/* HAVE_DARWIN_ACL */
-		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;
-#endif	/* HAVE_DARWIN_ACL */
-		default:
-			/* Skip types that libarchive can't support. */
-			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
-			continue;
-		}
-
-#if HAVE_DARWIN_ACL
-		/* Skip if translate_guid() above failed */
-		if (r != 0) {
-			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
-			continue;
-		}
-#endif
-
-#if !HAVE_DARWIN_ACL
-		// XXX acl_type maps to allow/deny/audit/YYYY bits
-		entry_acl_type = default_entry_acl_type;
-#endif
-#if HAVE_ACL_TYPE_NFS4 || HAVE_DARWIN_ACL
-		if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
-#if HAVE_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);
-			}
-#endif	/* HAVE_ACL_TYPE_NFS4 */
-
-			/*
-			 * 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 < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
-				r = acl_get_flag_np(acl_flagset,
-				    acl_inherit_map[i].platform_inherit);
-				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_inherit_map[i].archive_inherit;
-			}
-		}
-#endif	/* HAVE_ACL_TYPE_NFS4 || HAVE_DARWIN_ACL */
-
-		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 < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) {
-			/*
-			 * acl_get_perm() is spelled differently on different
-			 * platforms; see above.
-			 */
-			r = ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_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_perm_map[i].archive_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 !HAVE_DARWIN_ACL
-		if (s == -1) {
-			archive_set_error(&a->archive, errno,
-			    "Failed to get next ACL entry");
-			return (ARCHIVE_WARN);
-		}
-#endif
-	}
-	return (ARCHIVE_OK);
-}
-#endif	/* !HAVE_SUN_ACL */
-#else	/* !HAVE_POSIX_ACL && !HAVE_NFS4_ACL */
-static int
-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	/* !HAVE_POSIX_ACL && !HAVE_NFS4_ACL */
-
-#if (HAVE_FGETXATTR && HAVE_FLISTXATTR && HAVE_LISTXATTR && \
-    HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR) || \
-    (HAVE_FGETEA && HAVE_FLISTEA && HAVE_LISTEA)
-
-/*
- * Linux and AIX extended attribute support.
+ * 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
@@ -1433,21 +444,32 @@ setup_xattr(struct archive_read_disk *a,
 	ssize_t size;
 	void *value = NULL;
 
-#if HAVE_FGETXATTR
-	if (fd >= 0)
+
+	if (fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
 		size = fgetxattr(fd, name, NULL, 0);
-	else if (!a->follow_symlinks)
-		size = lgetxattr(accpath, name, NULL, 0);
-	else
-		size = getxattr(accpath, name, NULL, 0);
-#elif HAVE_FGETEA
-	if (fd >= 0)
+#elif ARCHIVE_XATTR_DARWIN
+		size = fgetxattr(fd, name, NULL, 0, 0, 0);
+#elif ARCHIVE_XATTR_AIX
 		size = fgetea(fd, name, NULL, 0);
-	else if (!a->follow_symlinks)
+#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);
-	else
+#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,
@@ -1460,21 +482,32 @@ setup_xattr(struct archive_read_disk *a,
 		return (ARCHIVE_FATAL);
 	}
 
-#if HAVE_FGETXATTR
-	if (fd >= 0)
+
+	if (fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
 		size = fgetxattr(fd, name, value, size);
-	else if (!a->follow_symlinks)
-		size = lgetxattr(accpath, name, value, size);
-	else
-		size = getxattr(accpath, name, value, size);
-#elif HAVE_FGETEA
-	if (fd >= 0)
+#elif ARCHIVE_XATTR_DARWIN
+		size = fgetxattr(fd, name, value, size, 0, 0);
+#elif ARCHIVE_XATTR_AIX
 		size = fgetea(fd, name, value, size);
-	else if (!a->follow_symlinks)
+#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);
-	else
+#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,
@@ -1499,38 +532,36 @@ setup_xattrs(struct archive_read_disk *a,
 	path = NULL;
 
 	if (*fd < 0) {
-		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 file path to read "
-			    "extended attributes");
+		path = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (path == NULL)
 			return (ARCHIVE_WARN);
-		}
-		if (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);
-		}
 	}
 
-#if HAVE_FLISTXATTR
-	if (*fd >= 0)
+	if (*fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
 		list_size = flistxattr(*fd, NULL, 0);
-	else if (!a->follow_symlinks)
-		list_size = llistxattr(path, NULL, 0);
-	else
-		list_size = listxattr(path, NULL, 0);
-#elif HAVE_FLISTEA
-	if (*fd >= 0)
+#elif ARCHIVE_XATTR_DARWIN
+		list_size = flistxattr(*fd, NULL, 0, 0);
+#elif ARCHIVE_XATTR_AIX
 		list_size = flistea(*fd, NULL, 0);
-	else if (!a->follow_symlinks)
+#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);
-	else
+#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)
@@ -1548,21 +579,31 @@ setup_xattrs(struct archive_read_disk *a,
 		return (ARCHIVE_FATAL);
 	}
 
-#if HAVE_FLISTXATTR
-	if (*fd >= 0)
+	if (*fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
 		list_size = flistxattr(*fd, list, list_size);
-	else if (!a->follow_symlinks)
-		list_size = llistxattr(path, list, list_size);
-	else
-		list_size = listxattr(path, list, list_size);
-#elif HAVE_FLISTEA
-	if (*fd >= 0)
+#elif ARCHIVE_XATTR_DARWIN
+		list_size = flistxattr(*fd, list, list_size, 0);
+#elif ARCHIVE_XATTR_AIX
 		list_size = flistea(*fd, list, list_size);
-	else if (!a->follow_symlinks)
+#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);
-	else
+#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,
@@ -1572,9 +613,21 @@ setup_xattrs(struct archive_read_disk *a,
 	}
 
 	for (p = list; (p - list) < list_size; p += strlen(p) + 1) {
-		if (strncmp(p, "system.", 7) == 0 ||
-				strncmp(p, "xfsroot.", 8) == 0)
+#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);
 	}
 
@@ -1582,8 +635,7 @@ setup_xattrs(struct archive_read_disk *a,
 	return (ARCHIVE_OK);
 }
 
-#elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE && \
-    HAVE_DECL_EXTATTR_NAMESPACE_USER
+#elif ARCHIVE_XATTR_FREEBSD
 
 /*
  * FreeBSD extattr interface.
@@ -1658,21 +710,9 @@ setup_xattrs(struct archive_read_disk *a,
 	path = NULL;
 
 	if (*fd < 0) {
-		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 file path to read "
-			    "extended attributes");
+		path = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (path == NULL)
 			return (ARCHIVE_WARN);
-		}
-		if (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);
-		}
 	}
 
 	if (*fd >= 0)
@@ -1773,6 +813,7 @@ setup_sparse_fiemap(struct archive_read_disk *a,
 	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
@@ -1780,11 +821,10 @@ setup_sparse_fiemap(struct archive_read_disk *a,
 		return (ARCHIVE_OK);
 
 	if (*fd < 0) {
-		const char *path;
-
-		path = archive_entry_sourcepath(entry);
+		path = archive_read_disk_entry_setup_path(a, entry, NULL);
 		if (path == NULL)
-			path = archive_entry_pathname(entry);
+			return (ARCHIVE_FAILED);
+
 		if (a->tree != NULL)
 			*fd = a->open_on_current_dir(a->tree, path,
 				O_RDONLY | O_NONBLOCK | O_CLOEXEC);
@@ -1880,6 +920,7 @@ setup_sparse(struct archive_read_disk *a,
 	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
@@ -1887,20 +928,10 @@ setup_sparse(struct archive_read_disk *a,
 		return (ARCHIVE_OK);
 
 	/* Does filesystem support the reporting of hole ? */
-	if (*fd < 0 && a->tree != NULL) {
-		const char *path;
-
-		path = archive_entry_sourcepath(entry);
-		if (path == NULL)
-			path = archive_entry_pathname(entry);
-		*fd = a->open_on_current_dir(a->tree, path,
-				O_RDONLY | O_NONBLOCK);
-		if (*fd < 0) {
-			archive_set_error(&a->archive, errno,
-			    "Can't open `%s'", path);
-			return (ARCHIVE_FAILED);
-		}
-	}
+	if (*fd < 0)
+		path = archive_read_disk_entry_setup_path(a, entry, fd);
+	else
+		path = NULL;
 
 	if (*fd >= 0) {
 #ifdef _PC_MIN_HOLE_SIZE
@@ -1911,12 +942,8 @@ setup_sparse(struct archive_read_disk *a,
 		if (initial_off != 0)
 			lseek(*fd, 0, SEEK_SET);
 	} else {
-		const char *path;
-
-		path = archive_entry_sourcepath(entry);
 		if (path == NULL)
-			path = archive_entry_pathname(entry);
-			
+			return (ARCHIVE_FAILED);
 #ifdef _PC_MIN_HOLE_SIZE
 		if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)
 			return (ARCHIVE_OK);
diff --git a/libarchive/archive_read_disk_private.h b/libarchive/archive_read_disk_private.h
index b5a8328..f03a0a9 100644
--- a/libarchive/archive_read_disk_private.h
+++ b/libarchive/archive_read_disk_private.h
@@ -33,6 +33,8 @@
 #ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
 #define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
 
+#include "archive_platform_acl.h"
+
 struct tree;
 struct archive_entry;
 
@@ -86,4 +88,11 @@ struct archive_read_disk {
 	void	*excluded_cb_data;
 };
 
+const char *
+archive_read_disk_entry_setup_path(struct archive_read_disk *,
+    struct archive_entry *, int *);
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *,
+    struct archive_entry *, int *);
 #endif
diff --git a/libarchive/archive_read_format.3 b/libarchive/archive_read_format.3
index 53b9a7e..91c5d2c 100644
--- a/libarchive/archive_read_format.3
+++ b/libarchive/archive_read_format.3
@@ -37,9 +37,9 @@
 .Nm archive_read_support_format_empty ,
 .Nm archive_read_support_format_iso9660 ,
 .Nm archive_read_support_format_lha ,
-.Nm archive_read_support_format_mtree,
-.Nm archive_read_support_format_rar,
-.Nm archive_read_support_format_raw,
+.Nm archive_read_support_format_mtree ,
+.Nm archive_read_support_format_rar ,
+.Nm archive_read_support_format_raw ,
 .Nm archive_read_support_format_tar ,
 .Nm archive_read_support_format_xar ,
 .Nm archive_read_support_format_zip
diff --git a/libarchive/archive_read_open.3 b/libarchive/archive_read_open.3
index 4d8272c..2278ebc 100644
--- a/libarchive/archive_read_open.3
+++ b/libarchive/archive_read_open.3
@@ -33,7 +33,7 @@
 .Nm archive_read_open_fd ,
 .Nm archive_read_open_FILE ,
 .Nm archive_read_open_filename ,
-.Nm archive_read_open_memory ,
+.Nm archive_read_open_memory
 .Nd functions for reading streaming archives
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
@@ -67,7 +67,7 @@ Streaming Archive Library (libarchive, -larchive)
 .Fa "size_t block_size"
 .Fc
 .Ft int
-.Fn archive_read_open_memory "struct archive *" "void *buff" "size_t size"
+.Fn archive_read_open_memory "struct archive *" "const void *buff" "size_t size"
 .Sh DESCRIPTION
 .Bl -tag -compact -width indent
 .It Fn archive_read_open
diff --git a/libarchive/archive_read_support_filter_lz4.c b/libarchive/archive_read_support_filter_lz4.c
index 663e2d3..147f502 100644
--- a/libarchive/archive_read_support_filter_lz4.c
+++ b/libarchive/archive_read_support_filter_lz4.c
@@ -494,7 +494,7 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
 	if (read_buf == NULL)
 		goto truncated_error;
 	compressed_size = archive_le32dec(read_buf);
-	if ((compressed_size & ~(1 << 31)) > state->flags.block_maximum_size)
+	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) {
@@ -504,8 +504,8 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
 
 	checksum_size = state->flags.block_checksum;
 	/* Check if the block is uncompressed. */
-	if (compressed_size & (1 << 31)) {
-		compressed_size &= ~(1 << 31);
+	if (compressed_size & 0x80000000U) {
+		compressed_size &= 0x7fffffff;
 		uncompressed_size = compressed_size;
 	} else
 		uncompressed_size = 0;/* Unknown yet. */
diff --git a/libarchive/archive_read_support_format_cab.c b/libarchive/archive_read_support_format_cab.c
index e2f8c6b..e5ff5a1 100644
--- a/libarchive/archive_read_support_format_cab.c
+++ b/libarchive/archive_read_support_format_cab.c
@@ -116,19 +116,11 @@ struct lzx_dec {
 		 * 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;
 	}			 at, lt, mt, pt;
 
 	int			 loop;
@@ -187,7 +179,7 @@ struct lzx_stream {
 #define CFDATA_cbData		4
 #define CFDATA_cbUncomp		6
 
-static const char *compression_name[] = {
+static const char * const compression_name[] = {
 	"NONE",
 	"MSZIP",
 	"Quantum",
@@ -352,7 +344,6 @@ 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);
-static int	lzx_decode_huffman_tree(struct huffman *, unsigned, int);
 
 
 int
@@ -3127,7 +3118,6 @@ getdata:
 static int
 lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
 {
-	int bits;
 
 	if (hf->bitlen == NULL || hf->len_size != (int)len_size) {
 		free(hf->bitlen);
@@ -3138,21 +3128,11 @@ lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
 	} else
 		memset(hf->bitlen, 0, len_size *  sizeof(hf->bitlen[0]));
 	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]));
+		hf->tbl = malloc(((size_t)1 << tbl_bits) * sizeof(hf->tbl[0]));
 		if (hf->tbl == NULL)
 			return (ARCHIVE_FATAL);
 		hf->tbl_bits = tbl_bits;
 	}
-	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);
-	}
 	return (ARCHIVE_OK);
 }
 
@@ -3161,7 +3141,6 @@ lzx_huffman_free(struct huffman *hf)
 {
 	free(hf->bitlen);
 	free(hf->tbl);
-	free(hf->tree);
 }
 
 /*
@@ -3174,7 +3153,7 @@ lzx_make_huffman_table(struct huffman *hf)
 	const unsigned char *bitlen;
 	int bitptn[17], weight[17];
 	int i, maxbits = 0, ptn, tbl_size, w;
-	int diffbits, len_avail;
+	int len_avail;
 
 	/*
 	 * Initialize bit patterns.
@@ -3205,28 +3184,11 @@ lzx_make_huffman_table(struct huffman *hf)
 			weight[i] >>= ebits;
 		}
 	}
-	if (maxbits > HTBL_BITS) {
-		int 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_size = 1 << hf->tbl_bits;
 	tbl = hf->tbl;
 	bitlen = hf->bitlen;
 	len_avail = hf->len_size;
@@ -3234,120 +3196,32 @@ lzx_make_huffman_table(struct huffman *hf)
 	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];
+		if (len > tbl_size)
+			return (0);
 		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]);
-			while (--cnt >= 0)
-				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;
-		}
+		/* 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 int
-lzx_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_size) {
-		c -= hf->len_size;
-		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
 lzx_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];
+	c = hf->tbl[rbits];
 	if (c < hf->len_size)
 		return (c);
-	/* This bit pattern needs to be found out at a huffman tree. */
-	return (lzx_decode_huffman_tree(hf, rbits, c));
+	return (0);
 }
-
diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c
index ffd4a85..ad9f782 100644
--- a/libarchive/archive_read_support_format_cpio.c
+++ b/libarchive/archive_read_support_format_cpio.c
@@ -165,7 +165,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_cpio.c 20116
 struct links_entry {
         struct links_entry      *next;
         struct links_entry      *previous;
-        int                      links;
+        unsigned int             links;
         dev_t                    dev;
         int64_t                  ino;
         char                    *name;
diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c
index 76da406..f01d37b 100644
--- a/libarchive/archive_read_support_format_iso9660.c
+++ b/libarchive/archive_read_support_format_iso9660.c
@@ -3021,8 +3021,9 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap,
 			    ENOMEM, "Out of memory");
 			return (ARCHIVE_FATAL);
 		}
-		memcpy(new_pending_files, heap->files,
-		    heap->allocated * sizeof(new_pending_files[0]));
+		if (heap->allocated)
+			memcpy(new_pending_files, heap->files,
+			    heap->allocated * sizeof(new_pending_files[0]));
 		if (heap->files != NULL)
 			free(heap->files);
 		heap->files = new_pending_files;
diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c
index d77a7c2..b8ef4ae 100644
--- a/libarchive/archive_read_support_format_lha.c
+++ b/libarchive/archive_read_support_format_lha.c
@@ -2477,7 +2477,7 @@ lzh_huffman_free(struct huffman *hf)
 	free(hf->tree);
 }
 
-static char bitlen_tbl[0x400] = {
+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,
diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c
index 4231ff5..44b6083 100644
--- a/libarchive/archive_read_support_format_mtree.c
+++ b/libarchive/archive_read_support_format_mtree.c
@@ -130,9 +130,7 @@ 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_atol10(char **);
-static int64_t	mtree_atol8(char **);
-static int64_t	mtree_atol(char **);
+static int64_t	mtree_atol(char **, int base);
 
 /*
  * There's no standard for TIME_T_MAX/TIME_T_MIN.  So we compute them
@@ -399,41 +397,41 @@ bid_keycmp(const char *p, const char *key, ssize_t len)
 static int
 bid_keyword(const char *p,  ssize_t len)
 {
-	static const char *keys_c[] = {
+	static const char * const keys_c[] = {
 		"content", "contents", "cksum", NULL
 	};
-	static const char *keys_df[] = {
+	static const char * const keys_df[] = {
 		"device", "flags", NULL
 	};
-	static const char *keys_g[] = {
+	static const char * const keys_g[] = {
 		"gid", "gname", NULL
 	};
-	static const char *keys_il[] = {
+	static const char * const keys_il[] = {
 		"ignore", "inode", "link", NULL
 	};
-	static const char *keys_m[] = {
+	static const char * const keys_m[] = {
 		"md5", "md5digest", "mode", NULL
 	};
-	static const char *keys_no[] = {
+	static const char * const keys_no[] = {
 		"nlink", "nochange", "optional", NULL
 	};
-	static const char *keys_r[] = {
+	static const char * const keys_r[] = {
 		"resdevice", "rmd160", "rmd160digest", NULL
 	};
-	static const char *keys_s[] = {
+	static const char * const keys_s[] = {
 		"sha1", "sha1digest",
 		"sha256", "sha256digest",
 		"sha384", "sha384digest",
 		"sha512", "sha512digest",
 		"size", NULL
 	};
-	static const char *keys_t[] = {
+	static const char * const keys_t[] = {
 		"tags", "time", "type", NULL
 	};
-	static const char *keys_u[] = {
+	static const char * const keys_u[] = {
 		"uid", "uname",	NULL
 	};
-	const char **keys;
+	const char * const *keys;
 	int i;
 
 	switch (*p) {
@@ -1418,7 +1416,7 @@ parse_device(dev_t *pdev, struct archive *a, char *val)
 				    "Too many arguments");
 				return ARCHIVE_WARN;
 			}
-			numbers[argc++] = (unsigned long)mtree_atol(&p);
+			numbers[argc++] = (unsigned long)mtree_atol(&p, 0);
 		}
 		if (argc < 2) {
 			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
@@ -1433,7 +1431,7 @@ parse_device(dev_t *pdev, struct archive *a, char *val)
 		}
 	} else {
 		/* file system raw value. */
-		result = (dev_t)mtree_atol(&val);
+		result = (dev_t)mtree_atol(&val, 0);
 	}
 	*pdev = result;
 	return ARCHIVE_OK;
@@ -1513,7 +1511,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree,
 	case 'g':
 		if (strcmp(key, "gid") == 0) {
 			*parsed_kws |= MTREE_HAS_GID;
-			archive_entry_set_gid(entry, mtree_atol10(&val));
+			archive_entry_set_gid(entry, mtree_atol(&val, 10));
 			break;
 		}
 		if (strcmp(key, "gname") == 0) {
@@ -1523,7 +1521,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree,
 		}
 	case 'i':
 		if (strcmp(key, "inode") == 0) {
-			archive_entry_set_ino(entry, mtree_atol10(&val));
+			archive_entry_set_ino(entry, mtree_atol(&val, 10));
 			break;
 		}
 	case 'l':
@@ -1535,14 +1533,14 @@ parse_keyword(struct archive_read *a, struct mtree *mtree,
 		if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0)
 			break;
 		if (strcmp(key, "mode") == 0) {
-			if (val[0] >= '0' && val[0] <= '9') {
+			if (val[0] >= '0' && val[0] <= '7') {
 				*parsed_kws |= MTREE_HAS_PERM;
 				archive_entry_set_perm(entry,
-				    (mode_t)mtree_atol8(&val));
+				    (mode_t)mtree_atol(&val, 8));
 			} else {
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Symbolic mode \"%s\" unsupported", val);
+				    "Symbolic or non-octal mode \"%s\" unsupported", val);
 				return ARCHIVE_WARN;
 			}
 			break;
@@ -1551,7 +1549,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree,
 		if (strcmp(key, "nlink") == 0) {
 			*parsed_kws |= MTREE_HAS_NLINK;
 			archive_entry_set_nlink(entry,
-				(unsigned int)mtree_atol10(&val));
+				(unsigned int)mtree_atol(&val, 10));
 			break;
 		}
 	case 'r':
@@ -1582,7 +1580,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree,
 		    strcmp(key, "sha512digest") == 0)
 			break;
 		if (strcmp(key, "size") == 0) {
-			archive_entry_set_size(entry, mtree_atol10(&val));
+			archive_entry_set_size(entry, mtree_atol(&val, 10));
 			break;
 		}
 	case 't':
@@ -1601,13 +1599,13 @@ parse_keyword(struct archive_read *a, struct mtree *mtree,
 			long ns = 0;
 
 			*parsed_kws |= MTREE_HAS_MTIME;
-			m = mtree_atol10(&val);
+			m = mtree_atol(&val, 10);
 			/* Replicate an old mtree bug:
 			 * 123456789.1 represents 123456789
 			 * seconds and 1 nanosecond. */
 			if (*val == '.') {
 				++val;
-				ns = (long)mtree_atol10(&val);
+				ns = (long)mtree_atol(&val, 10);
 				if (ns < 0)
 					ns = 0;
 				else if (ns > 999999999)
@@ -1670,7 +1668,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree,
 	case 'u':
 		if (strcmp(key, "uid") == 0) {
 			*parsed_kws |= MTREE_HAS_UID;
-			archive_entry_set_uid(entry, mtree_atol10(&val));
+			archive_entry_set_uid(entry, mtree_atol(&val, 10));
 			break;
 		}
 		if (strcmp(key, "uname") == 0) {
@@ -1825,72 +1823,9 @@ parse_escapes(char *src, struct mtree_entry *mentry)
 	*dest = '\0';
 }
 
-/*
- * 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_atol8(char **p)
-{
-	int64_t	l, limit, last_digit_limit;
-	int digit, base;
-
-	base = 8;
-	limit = INT64_MAX / base;
-	last_digit_limit = INT64_MAX % base;
-
-	l = 0;
-	digit = **p - '0';
-	while (digit >= 0 && digit < base) {
-		if (l>limit || (l == limit && digit > last_digit_limit)) {
-			l = INT64_MAX; /* Truncate on overflow. */
-			break;
-		}
-		l = (l * base) + digit;
-		digit = *++(*p) - '0';
-	}
-	return (l);
-}
-
-/*
- * 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_atol10(char **p)
-{
-	int64_t l, limit, last_digit_limit;
-	int base, digit, sign;
-
-	base = 10;
-
-	if (**p == '-') {
-		sign = -1;
-		limit = ((uint64_t)(INT64_MAX) + 1) / base;
-		last_digit_limit = ((uint64_t)(INT64_MAX) + 1) % base;
-		++(*p);
-	} else {
-		sign = 1;
-		limit = INT64_MAX / base;
-		last_digit_limit = INT64_MAX % base;
-	}
-
-	l = 0;
-	digit = **p - '0';
-	while (digit >= 0 && digit < base) {
-		if (l > limit || (l == limit && digit > last_digit_limit))
-			return (sign < 0) ? INT64_MIN : INT64_MAX;
-		l = (l * base) + digit;
-		digit = *++(*p) - '0';
-	}
-	return (sign < 0) ? -l : l;
-}
-
 /* Parse a hex digit. */
 static int
-parsehex(char c)
+parsedigit(char c)
 {
 	if (c >= '0' && c <= '9')
 		return c - '0';
@@ -1908,45 +1843,50 @@ parsehex(char c)
  * it does obey locale.
  */
 static int64_t
-mtree_atol16(char **p)
+mtree_atol(char **p, int base)
 {
-	int64_t l, limit, last_digit_limit;
-	int base, digit, sign;
-
-	base = 16;
+	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 == '-') {
-		sign = -1;
-		limit = ((uint64_t)(INT64_MAX) + 1) / base;
-		last_digit_limit = ((uint64_t)(INT64_MAX) + 1) % base;
+		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 {
-		sign = 1;
 		limit = INT64_MAX / base;
 		last_digit_limit = INT64_MAX % base;
-	}
 
-	l = 0;
-	digit = parsehex(**p);
-	while (digit >= 0 && digit < base) {
-		if (l > limit || (l == limit && digit > last_digit_limit))
-			return (sign < 0) ? INT64_MIN : INT64_MAX;
-		l = (l * base) + digit;
-		digit = parsehex(*++(*p));
-	}
-	return (sign < 0) ? -l : l;
-}
-
-static int64_t
-mtree_atol(char **p)
-{
-	if (**p != '0')
-		return mtree_atol10(p);
-	if ((*p)[1] == 'x' || (*p)[1] == 'X') {
-		*p += 2;
-		return mtree_atol16(p);
+		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;
 	}
-	return mtree_atol8(p);
 }
 
 /*
diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c
index 1e9849f..cbb14c3 100644
--- a/libarchive/archive_read_support_format_rar.c
+++ b/libarchive/archive_read_support_format_rar.c
@@ -1750,7 +1750,7 @@ read_exttime(const char *p, struct rar *rar, const char *endp)
         return (-1);
       for (j = 0; j < count; j++)
       {
-        rem = ((*p) << 16) | (rem >> 8);
+        rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8);
         p++;
       }
       tm = localtime(&t);
diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c
index bd7f13d..30d5bc8 100644
--- a/libarchive/archive_read_support_format_tar.c
+++ b/libarchive/archive_read_support_format_tar.c
@@ -155,6 +155,7 @@ struct tar {
 	int			 compat_2x;
 	int			 process_mac_extensions;
 	int			 read_concatenated_archives;
+	int			 realsize_override;
 };
 
 static int	archive_block_is_null(const char *p);
@@ -527,6 +528,7 @@ archive_read_format_tar_read_header(struct archive_read *a,
 	tar->entry_offset = 0;
 	gnu_clear_sparse_list(tar);
 	tar->realsize = -1; /* Mark this as "unset" */
+	tar->realsize_override = 0;
 
 	/* Setup default string conversion. */
 	tar->sconv = tar->opt_sconv;
@@ -1894,6 +1896,7 @@ pax_attribute(struct archive_read *a, struct tar *tar,
 		if (strcmp(key, "GNU.sparse.size") == 0) {
 			tar->realsize = tar_atol10(value, strlen(value));
 			archive_entry_set_size(entry, tar->realsize);
+			tar->realsize_override = 1;
 		}
 
 		/* GNU "0.1" sparse pax format. */
@@ -1925,6 +1928,7 @@ pax_attribute(struct archive_read *a, struct tar *tar,
 		if (strcmp(key, "GNU.sparse.realsize") == 0) {
 			tar->realsize = tar_atol10(value, strlen(value));
 			archive_entry_set_size(entry, tar->realsize);
+			tar->realsize_override = 1;
 		}
 		break;
 	case 'L':
@@ -1977,6 +1981,7 @@ pax_attribute(struct archive_read *a, struct tar *tar,
 			    tar_atol10(value, strlen(value)));
 		} else if (strcmp(key, "SCHILY.realsize") == 0) {
 			tar->realsize = tar_atol10(value, strlen(value));
+			tar->realsize_override = 1;
 			archive_entry_set_size(entry, tar->realsize);
 		} else if (strncmp(key, "SCHILY.xattr.", 13) == 0) {
 			pax_attribute_schily_xattr(entry, key, value,
@@ -2055,14 +2060,12 @@ pax_attribute(struct archive_read *a, struct tar *tar,
 			tar->entry_bytes_remaining
 			    = tar_atol10(value, strlen(value));
 			/*
-			 * But, "size" is not necessarily the size of
-			 * the file on disk; if this is a sparse file,
-			 * the disk size may have already been set from
-			 * GNU.sparse.realsize or GNU.sparse.size or
-			 * an old GNU header field or SCHILY.realsize
-			 * or ....
+			 * The "size" pax header keyword always overrides the
+			 * "size" field in the tar header.
+			 * GNU.sparse.realsize, GNU.sparse.size and
+			 * SCHILY.realsize override this value.
 			 */
-			if (tar->realsize < 0) {
+			if (!tar->realsize_override) {
 				archive_entry_set_size(entry,
 				    tar->entry_bytes_remaining);
 				tar->realsize
@@ -2206,6 +2209,7 @@ header_gnutar(struct archive_read *a, struct tar *tar,
 		tar->realsize
 		    = tar_atol(header->realsize, sizeof(header->realsize));
 		archive_entry_set_size(entry, tar->realsize);
+		tar->realsize_override = 1;
 	}
 
 	if (header->sparse[0].offset[0] != 0) {
diff --git a/libarchive/archive_read_support_format_warc.c b/libarchive/archive_read_support_format_warc.c
index b162465..e875385 100644
--- a/libarchive/archive_read_support_format_warc.c
+++ b/libarchive/archive_read_support_format_warc.c
@@ -600,9 +600,10 @@ _warc_rdver(const char *buf, size_t bsz)
 	/* looks good so far, read the version number for a laugh */
 	buf += sizeof(magic) - 1U;
 
-	if (isdigit(buf[0U]) && (buf[1U] == '.') && isdigit(buf[2U])) {
+	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(buf[3U]))
+		if (isdigit((unsigned char)buf[3U]))
 			end = 1U;
 		/* set up major version */
 		ver = (buf[0U] - '0') * 10000U;
@@ -686,7 +687,7 @@ _warc_rduri(const char *buf, size_t bsz)
 
 	/* spaces inside uri are not allowed, CRLF should follow */
 	for (p = val; p < eol; p++) {
-		if (isspace(*p))
+		if (isspace((unsigned char)*p))
 			return res;
 	}
 
@@ -736,7 +737,7 @@ _warc_rdlen(const char *buf, size_t bsz)
 	while (val < eol && (*val == ' ' || *val == '\t'))
 		val++;
 	/* there must be at least one digit */
-	if (!isdigit(*val))
+	if (!isdigit((unsigned char)*val))
 		return -1;
 	len = strtol(val, &on, 10);
 	if (on != eol) {
diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c
index 08bcf1f..4c4f6fa 100644
--- a/libarchive/archive_read_support_format_zip.c
+++ b/libarchive/archive_read_support_format_zip.c
@@ -347,7 +347,7 @@ fake_crc32(unsigned long crc, const void *buff, size_t len)
 	return 0;
 }
 
-static struct {
+static const struct {
 	int id;
 	const char * name;
 } compression_methods[] = {
@@ -2407,7 +2407,7 @@ read_eocd(struct zip *zip, const char *p, int64_t current_offset)
  * Examine Zip64 EOCD locator:  If it's valid, store the information
  * from it.
  */
-static void
+static int
 read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p)
 {
 	int64_t eocd64_offset;
@@ -2417,35 +2417,37 @@ read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p)
 
 	/* Central dir must be on first volume. */
 	if (archive_le32dec(p + 4) != 0)
-		return;
+		return 0;
 	/* Must be only a single volume. */
 	if (archive_le32dec(p + 16) != 1)
-		return;
+		return 0;
 
 	/* Find the Zip64 EOCD record. */
 	eocd64_offset = archive_le64dec(p + 8);
 	if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0)
-		return;
+		return 0;
 	if ((p = __archive_read_ahead(a, 56, NULL)) == NULL)
-		return;
+		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;
+		return 0;
 	if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL)
-		return;
+		return 0;
 
 	/* Sanity-check the EOCD64 */
 	if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */
-		return;
+		return 0;
 	if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */
-		return;
+		return 0;
 	/* CD can't be split. */
 	if (archive_le64dec(p + 24) != archive_le64dec(p + 32))
-		return;
+		return 0;
 
 	/* Save the central directory offset for later use. */
 	zip->central_directory_offset = archive_le64dec(p + 48);
+
+	return 32;
 }
 
 static int
@@ -2483,15 +2485,14 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
 			if (memcmp(p + i, "PK\005\006", 4) == 0) {
 				int ret = read_eocd(zip, p + i,
 				    current_offset + i);
-				if (ret > 0) {
-					/* Zip64 EOCD locator precedes
-					 * regular EOCD if present. */
-					if (i >= 20
-					    && memcmp(p + i - 20, "PK\006\007", 4) == 0) {
-						read_zip64_eocd(a, zip, p + i - 20);
-					}
-					return (ret);
+				/* 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;
diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c
index 592ead2..5ae09b6 100644
--- a/libarchive/archive_string.c
+++ b/libarchive/archive_string.c
@@ -202,7 +202,8 @@ 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);
-	memmove(as->s + as->length, p, s);
+	if (s)
+		memmove(as->s + as->length, p, s);
 	as->length += s;
 	as->s[as->length] = 0;
 	return (as);
diff --git a/libarchive/archive_string_sprintf.c b/libarchive/archive_string_sprintf.c
index 964ea2b..969a560 100644
--- a/libarchive/archive_string_sprintf.c
+++ b/libarchive/archive_string_sprintf.c
@@ -53,7 +53,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_string_sprintf.c 189435 2009-03-
 static void
 append_uint(struct archive_string *as, uintmax_t d, unsigned base)
 {
-	static const char *digits = "0123456789abcdef";
+	static const char digits[] = "0123456789abcdef";
 	if (d >= base)
 		append_uint(as, d/base, base);
 	archive_strappend_char(as, digits[d % base]);
diff --git a/libarchive/archive_util.c b/libarchive/archive_util.c
index 6b3bd61..bac9ba1 100644
--- a/libarchive/archive_util.c
+++ b/libarchive/archive_util.c
@@ -89,88 +89,6 @@ archive_version_string(void)
 	return (ARCHIVE_VERSION_STRING);
 }
 
-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();
-
-	if (!init) {
-		archive_string_init(&str);
-
-		archive_strcat(&str, ARCHIVE_VERSION_STRING);
-		if (zlib != NULL) {
-			archive_strcat(&str, " zlib/");
-			archive_strcat(&str, zlib);
-		}
-		if (liblzma) {
-			archive_strcat(&str, " liblzma/");
-			archive_strcat(&str, liblzma);
-		}
-		if (bzlib) {
-			const char *p = bzlib;
-			const char *sep = strchr(p, ',');
-			if (sep == NULL)
-				sep = p + strlen(p);
-			archive_strcat(&str, " bz2lib/");
-			archive_strncat(&str, p, sep - p);
-		}
-		if (liblz4) {
-			archive_strcat(&str, " liblz4/");
-			archive_strcat(&str, liblz4);
-		}
-	}
-	return str.s;
-}
-
-const char *
-archive_zlib_version(void)
-{
-#ifdef HAVE_ZLIB_H
-	return ZLIB_VERSION;
-#else
-	return NULL;
-#endif
-}
-
-const char *
-archive_liblzma_version(void)
-{
-#ifdef HAVE_LZMA_H
-	return LZMA_VERSION_STRING;
-#else
-	return NULL;
-#endif
-}
-
-const char *
-archive_bzlib_version(void)
-{
-#ifdef HAVE_BZLIB_H
-	return BZ2_bzlibVersion();
-#else
-	return NULL;
-#endif
-}
-
-const char *
-archive_liblz4_version(void)
-{
-#if defined(HAVE_LZ4_H) && defined(HAVE_LIBLZ4)
-#define str(s) #s
-#define NUMBER(x) str(x)
-	return NUMBER(LZ4_VERSION_MAJOR) "." NUMBER(LZ4_VERSION_MINOR) "." NUMBER(LZ4_VERSION_RELEASE);
-#undef NUMBER
-#undef str
-#else
-	return NULL;
-#endif
-}
-
 int
 archive_errno(struct archive *a)
 {
@@ -275,7 +193,7 @@ archive_copy_error(struct archive *dest, struct archive *src)
 void
 __archive_errx(int retvalue, const char *msg)
 {
-	static const char *msg1 = "Fatal Internal Error in libarchive: ";
+	static const char msg1[] = "Fatal Internal Error in libarchive: ";
 	size_t s;
 
 	s = write(2, msg1, strlen(msg1));
@@ -303,8 +221,8 @@ __archive_errx(int retvalue, const char *msg)
 int
 __archive_mktemp(const char *tmpdir)
 {
-	static const wchar_t *prefix = L"libarchive_";
-	static const wchar_t *suffix = L"XXXXXXXXXX";
+	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',
diff --git a/libarchive/archive_version_details.c b/libarchive/archive_version_details.c
new file mode 100644
index 0000000..813f0f3
--- /dev/null
+++ b/libarchive/archive_version_details.c
@@ -0,0 +1,133 @@
+/*-
+ * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#include <lzma.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZ4_H
+#include <lz4.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+
+const char *
+archive_version_details(void)
+{
+	static struct archive_string str;
+	static int init = 0;
+	const char *zlib = archive_zlib_version();
+	const char *liblzma = archive_liblzma_version();
+	const char *bzlib = archive_bzlib_version();
+	const char *liblz4 = archive_liblz4_version();
+
+	if (!init) {
+		archive_string_init(&str);
+
+		archive_strcat(&str, ARCHIVE_VERSION_STRING);
+		if (zlib != NULL) {
+			archive_strcat(&str, " zlib/");
+			archive_strcat(&str, zlib);
+		}
+		if (liblzma) {
+			archive_strcat(&str, " liblzma/");
+			archive_strcat(&str, liblzma);
+		}
+		if (bzlib) {
+			const char *p = bzlib;
+			const char *sep = strchr(p, ',');
+			if (sep == NULL)
+				sep = p + strlen(p);
+			archive_strcat(&str, " bz2lib/");
+			archive_strncat(&str, p, sep - p);
+		}
+		if (liblz4) {
+			archive_strcat(&str, " liblz4/");
+			archive_strcat(&str, liblz4);
+		}
+	}
+	return str.s;
+}
+
+const char *
+archive_zlib_version(void)
+{
+#ifdef HAVE_ZLIB_H
+	return ZLIB_VERSION;
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_liblzma_version(void)
+{
+#ifdef HAVE_LZMA_H
+	return LZMA_VERSION_STRING;
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_bzlib_version(void)
+{
+#ifdef HAVE_BZLIB_H
+	return BZ2_bzlibVersion();
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_liblz4_version(void)
+{
+#if defined(HAVE_LZ4_H) && defined(HAVE_LIBLZ4)
+#define str(s) #s
+#define NUMBER(x) str(x)
+	return NUMBER(LZ4_VERSION_MAJOR) "." NUMBER(LZ4_VERSION_MINOR) "." NUMBER(LZ4_VERSION_RELEASE);
+#undef NUMBER
+#undef str
+#else
+	return NULL;
+#endif
+}
diff --git a/libarchive/archive_write_add_filter.c b/libarchive/archive_write_add_filter.c
index ad5dc83..08f518a 100644
--- a/libarchive/archive_write_add_filter.c
+++ b/libarchive/archive_write_add_filter.c
@@ -38,7 +38,7 @@ __FBSDID("$FreeBSD$");
 #include "archive_private.h"
 
 /* A table that maps filter codes to functions. */
-static
+static const
 struct { int code; int (*setter)(struct archive *); } codes[] =
 {
 	{ ARCHIVE_FILTER_NONE,		archive_write_add_filter_none },
diff --git a/libarchive/archive_write_add_filter_by_name.c b/libarchive/archive_write_add_filter_by_name.c
index eac4011..85a8d47 100644
--- a/libarchive/archive_write_add_filter_by_name.c
+++ b/libarchive/archive_write_add_filter_by_name.c
@@ -42,7 +42,7 @@ __FBSDID("$FreeBSD$");
 #include "archive_private.h"
 
 /* A table that maps names to functions. */
-static
+static const
 struct { const char *name; int (*setter)(struct archive *); } names[] =
 {
 	{ "b64encode",		archive_write_add_filter_b64encode },
diff --git a/libarchive/archive_write_add_filter_lz4.c b/libarchive/archive_write_add_filter_lz4.c
index e655185..15fd494 100644
--- a/libarchive/archive_write_add_filter_lz4.c
+++ b/libarchive/archive_write_add_filter_lz4.c
@@ -225,7 +225,7 @@ archive_filter_lz4_open(struct archive_write_filter *f)
 	struct private_data *data = (struct private_data *)f->data;
 	int ret;
 	size_t required_size;
-	static size_t bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024,
+	static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024,
 			   4 * 1024 * 1024 };
 	size_t pre_block_size;
 
diff --git a/libarchive/archive_write_add_filter_program.c b/libarchive/archive_write_add_filter_program.c
index 55b5e8e..660f693 100644
--- a/libarchive/archive_write_add_filter_program.c
+++ b/libarchive/archive_write_add_filter_program.c
@@ -92,7 +92,7 @@ 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: ";
+	static const char prefix[] = "Program: ";
 
 	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_program");
diff --git a/libarchive/archive_write_data.3 b/libarchive/archive_write_data.3
index 0cdd25f..9c16cd9 100644
--- a/libarchive/archive_write_data.3
+++ b/libarchive/archive_write_data.3
@@ -24,11 +24,12 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 2, 2012
+.Dd February 28, 2017
 .Dt ARCHIVE_WRITE_DATA 3
 .Os
 .Sh NAME
-.Nm archive_write_data
+.Nm archive_write_data ,
+.Nm archive_write_data_block
 .Nd functions for creating archives
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
@@ -36,8 +37,27 @@ Streaming Archive Library (libarchive, -larchive)
 .In archive.h
 .Ft la_ssize_t
 .Fn archive_write_data "struct archive *" "const void *" "size_t"
+.Ft la_ssize_t
+.Fn archive_write_data_block "struct archive *" "const void *" "size_t size" "int64_t offset"
 .Sh DESCRIPTION
+.Bl -tag -width indent
+.It Fn archive_write_data
+Write data corresponding to the header just written.
+.It Fn archive_write_data_block
 Write data corresponding to the header just written.
+This is like
+.Fn archive_write_data
+except that it performs a seek on the file being
+written to the specified offset before writing the data.
+This is useful when restoring sparse files from archive
+formats that support sparse files.
+Returns number of bytes written or -1 on error.
+(Note: This is currently not supported for
+.Tn archive_write
+handles, only for
+.Tn archive_write_disk
+handles.
+.El
 .\" .Sh EXAMPLE
 .\"
 .Sh RETURN VALUES
diff --git a/libarchive/archive_write_disk.3 b/libarchive/archive_write_disk.3
index ba6c970..949c9ef 100644
--- a/libarchive/archive_write_disk.3
+++ b/libarchive/archive_write_disk.3
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 2, 2012
+.Dd April 3, 2017
 .Dt ARCHIVE_WRITE_DISK 3
 .Os
 .Sh NAME
@@ -33,14 +33,7 @@
 .Nm archive_write_disk_set_skip_file ,
 .Nm archive_write_disk_set_group_lookup ,
 .Nm archive_write_disk_set_standard_lookup ,
-.Nm archive_write_disk_set_user_lookup ,
-.Nm archive_write_header ,
-.Nm archive_write_data ,
-.Nm archive_write_data_block ,
-.Nm archive_write_finish_entry ,
-.Nm archive_write_close ,
-.Nm archive_write_finish
-.Nm archive_write_free
+.Nm archive_write_disk_set_user_lookup
 .Nd functions for creating objects on disk
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
@@ -68,20 +61,6 @@ Streaming Archive Library (libarchive, -larchive)
 .Fa "uid_t (*)(void *, const char *uname, uid_t uid)"
 .Fa "void (*cleanup)(void *)"
 .Fc
-.Ft int
-.Fn archive_write_header "struct archive *" "struct archive_entry *"
-.Ft la_ssize_t
-.Fn archive_write_data "struct archive *" "const void *" "size_t"
-.Ft la_ssize_t
-.Fn archive_write_data_block "struct archive *" "const void *" "size_t size" "int64_t offset"
-.Ft int
-.Fn archive_write_finish_entry "struct archive *"
-.Ft int
-.Fn archive_write_close "struct archive *"
-.Ft int
-.Fn archive_write_finish "struct archive *"
-.Ft int
-.Fn archive_write_free "struct archive *"
 .Sh DESCRIPTION
 These functions provide a complete API for creating objects on
 disk from
@@ -117,6 +96,33 @@ performance optimization in practice.
 The options field consists of a bitwise OR of one or more of the
 following values:
 .Bl -tag -compact -width "indent"
+.It Cm ARCHIVE_EXTRACT_ACL
+Attempt to restore Access Control Lists.
+By default, extended ACLs are ignored.
+.It Cm ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS
+Before removing a file system object prior to replacing it, clear
+platform-specific file flags which might prevent its removal.
+.It Cm ARCHIVE_EXTRACT_FFLAGS
+Attempt to restore file attributes (file flags).
+By default, file attributes are ignored.
+See
+.Xr chattr 1
+.Pq Linux
+or
+.Xr chflags 1
+.Pq FreeBSD, Mac OS X
+for more information on file attributes.
+.It Cm ARCHIVE_EXTRACT_MAC_METADATA
+Mac OS X specific. Restore metadata using
+.Xr copyfile 3 .
+By default,
+.Xr copyfile 3
+metadata is ignored.
+.It Cm ARCHIVE_EXTRACT_NO_OVERWRITE
+Existing files on disk will not be overwritten.
+By default, existing regular files are truncated and overwritten;
+existing directories will have their permissions updated;
+other pre-existing objects are unlinked and recreated from scratch.
 .It Cm ARCHIVE_EXTRACT_OWNER
 The user and group IDs should be set on the restored file.
 By default, the user and group IDs are not restored.
@@ -132,15 +138,37 @@ is not specified, then SUID and SGID bits will only be restored
 if the default user and group IDs of newly-created objects on disk
 happen to match those specified in the archive entry.
 By default, only basic permissions are restored, and umask is obeyed.
+.It Cm ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS
+Refuse to extract an absolute path.
+The default is to not refuse such paths.
+.It Cm ARCHIVE_EXTRACT_SECURE_NODOTDOT
+Refuse to extract a path that contains a
+.Pa ..
+element anywhere within it.
+The default is to not refuse such paths.
+Note that paths ending in
+.Pa ..
+always cause an error, regardless of this flag.
+.It Cm ARCHIVE_EXTRACT_SECURE_SYMLINKS
+Refuse to extract any object whose final location would be altered
+by a symlink on disk.
+This is intended to help guard against a variety of mischief
+caused by archives that (deliberately or otherwise) extract
+files outside of the current directory.
+The default is not to perform this check.
+If
+.It Cm ARCHIVE_EXTRACT_SPARSE
+Scan data for blocks of NUL bytes and try to recreate them with holes.
+This results in sparse files, independent of whether the archive format
+supports or uses them.
+.Cm ARCHIVE_EXTRACT_UNLINK
+is specified together with this option, the library will
+remove any intermediate symlinks it finds and return an
+error only if such symlink could not be removed.
 .It Cm ARCHIVE_EXTRACT_TIME
 The timestamps (mtime, ctime, and atime) should be restored.
 By default, they are ignored.
 Note that restoring of atime is not currently supported.
-.It Cm ARCHIVE_EXTRACT_NO_OVERWRITE
-Existing files on disk will not be overwritten.
-By default, existing regular files are truncated and overwritten;
-existing directories will have their permissions updated;
-other pre-existing objects are unlinked and recreated from scratch.
 .It Cm ARCHIVE_EXTRACT_UNLINK
 Existing files on disk will be unlinked before any attempt to
 create them.
@@ -148,45 +176,18 @@ In some cases, this can prove to be a significant performance improvement.
 By default, existing files are truncated and rewritten, but
 the file is not recreated.
 In particular, the default behavior does not break existing hard links.
-.It Cm ARCHIVE_EXTRACT_ACL
-Attempt to restore ACLs.
-By default, extended ACLs are ignored.
-.It Cm ARCHIVE_EXTRACT_FFLAGS
-Attempt to restore extended file flags.
-By default, file flags are ignored.
 .It Cm ARCHIVE_EXTRACT_XATTR
-Attempt to restore POSIX.1e extended attributes.
+Attempt to restore extended file attributes.
 By default, they are ignored.
-.It Cm ARCHIVE_EXTRACT_SECURE_SYMLINKS
-Refuse to extract any object whose final location would be altered
-by a symlink on disk.
-This is intended to help guard against a variety of mischief
-caused by archives that (deliberately or otherwise) extract
-files outside of the current directory.
-The default is not to perform this check.
-If
-.Cm ARCHIVE_EXTRACT_UNLINK
-is specified together with this option, the library will
-remove any intermediate symlinks it finds and return an
-error only if such symlink could not be removed.
-.It Cm ARCHIVE_EXTRACT_SECURE_NODOTDOT
-Refuse to extract a path that contains a
-.Pa ..
-element anywhere within it.
-The default is to not refuse such paths.
-Note that paths ending in
-.Pa ..
-always cause an error, regardless of this flag.
-.It Cm ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS
-Refuse to extract an absolute path.
-The default is to not refuse such paths.
-.It Cm ARCHIVE_EXTRACT_SPARSE
-Scan data for blocks of NUL bytes and try to recreate them with holes.
-This results in sparse files, independent of whether the archive format
-supports or uses them.
-.It Cm ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS
-Before removing a file system object prior to replacing it, clear
-platform-specific file flags which might prevent its removal.
+See
+.Xr xattr 7
+.Pq Linux ,
+.Xr xattr 2
+.Pq Mac OS X ,
+or
+.Xr getextattr 8
+.Pq FreeBSD
+for more information on extended file attributes.
 .El
 .It Xo
 .Fn archive_write_disk_set_group_lookup ,
@@ -223,60 +224,6 @@ the number of calls to
 .Xr getpwnam 3
 and
 .Xr getgrnam 3 .
-.It Fn archive_write_header
-Build and write a header using the data in the provided
-.Tn struct archive_entry
-structure.
-See
-.Xr archive_entry 3
-for information on creating and populating
-.Tn struct archive_entry
-objects.
-.It Fn archive_write_data
-Write data corresponding to the header just written.
-Returns number of bytes written or -1 on error.
-.It Fn archive_write_data_block
-Write data corresponding to the header just written.
-This is like
-.Fn archive_write_data
-except that it performs a seek on the file being
-written to the specified offset before writing the data.
-This is useful when restoring sparse files from archive
-formats that support sparse files.
-Returns number of bytes written or -1 on error.
-(Note: This is currently not supported for
-.Tn archive_write
-handles, only for
-.Tn archive_write_disk
-handles.)
-.It Fn archive_write_finish_entry
-Close out the entry just written.
-Ordinarily, clients never need to call this, as it
-is called automatically by
-.Fn archive_write_next_header
-and
-.Fn archive_write_close
-as needed.
-However, some file attributes are written to disk only
-after the file is closed, so this can be necessary
-if you need to work with the file on disk right away.
-.It Fn archive_write_close
-Set any attributes that could not be set during the initial restore.
-For example, directory timestamps are not restored initially because
-restoring a subsequent file would alter that timestamp.
-Similarly, non-writable directories are initially created with
-write permissions (so that their contents can be restored).
-The
-.Nm
-library maintains a list of all such deferred attributes and
-sets them when this function is invoked.
-.It Fn archive_write_finish
-This is a deprecated synonym for
-.Fn archive_write_free .
-.It Fn archive_write_free
-Invokes
-.Fn archive_write_close
-if it was not invoked manually, then releases all resources.
 .El
 More information about the
 .Va struct archive
diff --git a/libarchive/archive_write_disk_acl.c b/libarchive/archive_write_disk_acl.c
deleted file mode 100644
index 144ab7e..0000000
--- a/libarchive/archive_write_disk_acl.c
+++ /dev/null
@@ -1,654 +0,0 @@
-/*-
- * 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
- *    in this position and unchanged.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "archive_platform.h"
-__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_disk.c 201159 2009-12-29 05:35:40Z kientzle $");
-
-#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
-#if HAVE_DARWIN_ACL
-#include <membership.h>
-#endif
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-
-#include "archive.h"
-#include "archive_entry.h"
-#include "archive_acl_private.h"
-#include "archive_write_disk_private.h"
-
-#if !HAVE_POSIX_ACL && !HAVE_NFS4_ACL
-/* Default empty function body to satisfy mainline code. */
-int
-archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
-	 struct archive_acl *abstract_acl)
-{
-	(void)a; /* UNUSED */
-	(void)fd; /* UNUSED */
-	(void)name; /* UNUSED */
-	(void)abstract_acl; /* UNUSED */
-	return (ARCHIVE_OK);
-}
-
-#else /* HAVE_POSIX_ACL || HAVE_NFS4_ACL */
-
-#if HAVE_SUN_ACL
-#define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACE_T
-#elif HAVE_DARWIN_ACL
-#define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACL_TYPE_EXTENDED
-#elif HAVE_ACL_TYPE_NFS4
-#define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACL_TYPE_NFS4
-#endif
-
-static int	set_acl(struct archive *, int fd, const char *,
-			struct archive_acl *,
-			acl_type_t, int archive_entry_acl_type, const char *tn);
-
-int
-archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
-	 struct archive_acl *abstract_acl)
-{
-	int		ret = ARCHIVE_OK;
-
-#if !HAVE_DARWIN_ACL
-	if ((archive_acl_types(abstract_acl)
-	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
-#if HAVE_SUN_ACL
-		/* Solaris writes POSIX.1e access and default ACLs together */
-		ret = set_acl(a, fd, name, abstract_acl, ACLENT_T,
-		    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, "posix1e");
-#else	/* HAVE_POSIX_ACL */
-		if ((archive_acl_types(abstract_acl)
-		    & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
-			ret = set_acl(a, fd, name, abstract_acl,
-			    ACL_TYPE_ACCESS, 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,
-			    ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
-			    "default");
-#endif	/* !HAVE_SUN_ACL */
-		/* Simultaneous POSIX.1e and NFSv4 is not supported */
-		return (ret);
-	}
-#endif	/* !HAVE_DARWIN_ACL */
-#if HAVE_NFS4_ACL
-	if ((archive_acl_types(abstract_acl) &
-	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
-		ret = set_acl(a, fd, name, abstract_acl,
-		    ARCHIVE_PLATFORM_ACL_TYPE_NFS4,
-		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
-	}
-#endif	/* HAVE_NFS4_ACL */
-	return (ret);
-}
-
-/*
- * Translate system ACL permissions into libarchive internal structure
- */
-static const struct {
-	const int archive_perm;
-	const int platform_perm;
-} acl_perm_map[] = {
-#if HAVE_SUN_ACL	/* Solaris NFSv4 ACL permissions */
-	{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}
-#elif HAVE_DARWIN_ACL	/* MacOS ACL permissions */
-	{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},
-	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
-#else	/* POSIX.1e ACL permissions */
-	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
-	{ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
-	{ARCHIVE_ENTRY_ACL_READ, ACL_READ},
-#if HAVE_ACL_TYPE_NFS4	/* FreeBSD NFSv4 ACL permissions */
-	{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}
-#endif
-#endif	/* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */
-};
-
-#if HAVE_NFS4_ACL
-/*
- * Translate system NFSv4 inheritance flags into libarchive internal structure
- */
-static const struct {
-	const int archive_inherit;
-	const int platform_inherit;
-} acl_inherit_map[] = {
-#if HAVE_SUN_ACL	/* Solaris NFSv4 inheritance flags */
-	{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},
-	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE}
-#elif HAVE_DARWIN_ACL	/* MacOS NFSv4 inheritance flags */
-	{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}
-#else	/* FreeBSD NFSv4 ACL inheritance flags */
-	{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},
-	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}
-#endif	/* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */
-};
-#endif	/* HAVE_NFS4_ACL */
-
-static int
-set_acl(struct archive *a, int fd, const char *name,
-    struct archive_acl *abstract_acl,
-    acl_type_t acl_type, int ae_requested_type, const char *tname)
-{
-#if HAVE_SUN_ACL
-	aclent_t	 *aclent;
-	ace_t		 *ace;
-	int		 e, r;
-	acl_t		 *acl;
-#else
-	acl_t		 acl;
-	acl_entry_t	 acl_entry;
-	acl_permset_t	 acl_permset;
-#if HAVE_ACL_TYPE_NFS4 || HAVE_DARWIN_ACL
-	acl_flagset_t	 acl_flagset;
-#endif
-#endif	/* HAVE_SUN_ACL */
-#if HAVE_ACL_TYPE_NFS4
-	int		r;
-#endif
-	int		 ret;
-	int		 ae_type, ae_permset, ae_tag, ae_id;
-#if HAVE_DARWIN_ACL
-	uuid_t		ae_uuid;
-#endif
-	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 HAVE_SUN_ACL
-	acl = NULL;
-	acl = malloc(sizeof(acl_t));
-	if (acl == NULL) {
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
-			"Invalid ACL type");
-		return (ARCHIVE_FAILED);
-	}
-	if (acl_type == ACE_T)
-		acl->acl_entry_size = sizeof(ace_t);
-	else if (acl_type == ACLENT_T)
-		acl->acl_entry_size = sizeof(aclent_t);
-	else {
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
-			"Invalid ACL type");
-		acl_free(acl);
-		return (ARCHIVE_FAILED);
-	}
-	acl->acl_type = acl_type;
-	acl->acl_cnt = entries;
-
-	acl->acl_aclp = malloc(entries * acl->acl_entry_size);
-	if (acl->acl_aclp == NULL) {
-		archive_set_error(a, errno,
-		    "Can't allocate memory for acl buffer");
-		acl_free(acl);
-		return (ARCHIVE_FAILED);
-	}
-#else	/* !HAVE_SUN_ACL */
-	acl = acl_init(entries);
-	if (acl == (acl_t)NULL) {
-		archive_set_error(a, errno,
-		    "Failed to initialize ACL working storage");
-		return (ARCHIVE_FAILED);
-	}
-#endif	/* !HAVE_SUN_ACL */
-#if HAVE_SUN_ACL
-	e = 0;
-#endif
-	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
-		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
-#if HAVE_SUN_ACL
-		ace = NULL;
-		aclent = NULL;
-		if (acl->acl_type == ACE_T)  {
-			ace = &((ace_t *)acl->acl_aclp)[e];
-			ace->a_who = -1;
-			ace->a_access_mask = 0;
-			ace->a_flags = 0;
-		} else {
-			aclent = &((aclent_t *)acl->acl_aclp)[e];
-			aclent->a_id = -1;
-			aclent->a_type = 0;
-			aclent->a_perm = 0;
-		}
-#else	/* !HAVE_SUN_ACL  */
-#if HAVE_DARWIN_ACL
-		/*
-		 * Mac OS doesn't support NFSv4 ACLs for
-		 * owner@, group@ and everyone at .
-		 * 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;
-#endif
-		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;
-		}
-#endif	/* !HAVE_SUN_ACL */
-#if HAVE_DARWIN_ACL
-		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;
-		}
-#endif
-		switch (ae_tag) {
-#if HAVE_SUN_ACL
-		case ARCHIVE_ENTRY_ACL_USER:
-			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
-			if (acl->acl_type == ACE_T)
-				ace->a_who = ae_uid;
-			else {
-				aclent->a_id = ae_uid;
-				aclent->a_type |= USER;
-			}
-			break;
-		case ARCHIVE_ENTRY_ACL_GROUP:
-			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
-			if (acl->acl_type == ACE_T) {
-				ace->a_who = ae_gid;
-				ace->a_flags |= ACE_IDENTIFIER_GROUP;
-			} else {
-				aclent->a_id = ae_gid;
-				aclent->a_type |= GROUP;
-			}
-			break;
-		case ARCHIVE_ENTRY_ACL_USER_OBJ:
-			if (acl->acl_type == ACE_T)
-				ace->a_flags |= ACE_OWNER;
-			else
-				aclent->a_type |= USER_OBJ;
-			break;
-		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
-			if (acl->acl_type == ACE_T) {
-				ace->a_flags |= ACE_GROUP;
-				ace->a_flags |= ACE_IDENTIFIER_GROUP;
-			} else
-				aclent->a_type |= GROUP_OBJ;
-			break;
-		case ARCHIVE_ENTRY_ACL_MASK:
-			aclent->a_type |= CLASS_OBJ;
-			break;
-		case ARCHIVE_ENTRY_ACL_OTHER:
-			aclent->a_type |= OTHER_OBJ;
-			break;
-		case ARCHIVE_ENTRY_ACL_EVERYONE:
-			ace->a_flags |= ACE_EVERYONE;
-			break;
-#else	/* !HAVE_SUN_ACL */
-		case ARCHIVE_ENTRY_ACL_USER:
-			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
-#if !HAVE_DARWIN_ACL	/* FreeBSD, Linux */
-			acl_set_tag_type(acl_entry, ACL_USER);
-			acl_set_qualifier(acl_entry, &ae_uid);
-#else	/* MacOS */
-			if (mbr_identifier_to_uuid(ID_TYPE_UID, &ae_uid,
-			    sizeof(uid_t), ae_uuid) != 0)
-				continue;
-			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
-				continue;
-#endif	/* HAVE_DARWIN_ACL */
-			break;
-		case ARCHIVE_ENTRY_ACL_GROUP:
-			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
-#if !HAVE_DARWIN_ACL	/* FreeBSD, Linux */
-			acl_set_tag_type(acl_entry, ACL_GROUP);
-			acl_set_qualifier(acl_entry, &ae_gid);
-#else	/* MacOS */
-			if (mbr_identifier_to_uuid(ID_TYPE_GID, &ae_gid,
-			    sizeof(gid_t), ae_uuid) != 0)
-				continue;
-			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
-				continue;
-#endif	/* HAVE_DARWIN_ACL */
-			break;
-#if !HAVE_DARWIN_ACL	/* FreeBSD, Linux */
-		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 HAVE_ACL_TYPE_NFS4	/* FreeBSD only */
-		case ARCHIVE_ENTRY_ACL_EVERYONE:
-			acl_set_tag_type(acl_entry, ACL_EVERYONE);
-			break;
-#endif
-#endif	/* !HAVE_DARWIN_ACL */
-#endif	/* !HAVE_SUN_ACL */
-		default:
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
-			    "Unknown ACL tag");
-			ret = ARCHIVE_FAILED;
-			goto exit_free;
-		}
-
-#if HAVE_ACL_TYPE_NFS4 || HAVE_SUN_ACL
-		r = 0;
-		switch (ae_type) {
-#if HAVE_SUN_ACL
-		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;
-		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;
-#else	/* !HAVE_SUN_ACL */
-		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;
-#endif	/* !HAVE_SUN_ACL */
-		default:
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
-			    "Unknown ACL entry type");
-			ret = ARCHIVE_FAILED;
-			goto exit_free;
-		}
-
-		if (r != 0) {
-#if HAVE_SUN_ACL
-			errno = EINVAL;
-#endif
-			archive_set_error(a, errno,
-			    "Failed to set ACL entry type");
-			ret = ARCHIVE_FAILED;
-			goto exit_free;
-		}
-#endif	/* HAVE_ACL_TYPE_NFS4 || HAVE_SUN_ACL */
-
-#if HAVE_SUN_ACL
-		if (acl->acl_type == ACLENT_T) {
-			if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE)
-				aclent->a_perm |= 1;
-			if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE)
-				aclent->a_perm |= 2;
-			if (ae_permset & ARCHIVE_ENTRY_ACL_READ)
-				aclent->a_perm |= 4;
-		} else
-#else
-		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;
-		}
-#endif	/* !HAVE_SUN_ACL */
-		for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) {
-			if (ae_permset & acl_perm_map[i].archive_perm) {
-#if HAVE_SUN_ACL
-				ace->a_access_mask |=
-				    acl_perm_map[i].platform_perm;
-#else
-				if (acl_add_perm(acl_permset,
-				    acl_perm_map[i].platform_perm) != 0) {
-					archive_set_error(a, errno,
-					    "Failed to add ACL permission");
-					ret = ARCHIVE_FAILED;
-					goto exit_free;
-				}
-#endif
-			}
-		}
-
-#if HAVE_NFS4_ACL
-#if HAVE_SUN_ACL
-		if (acl_type == ACE_T)
-#elif HAVE_DARWIN_ACL
-		if (acl_type == ACL_TYPE_EXTENDED)
-#else	/* FreeBSD */
-		if (acl_type == ACL_TYPE_NFS4)
-#endif
-		{
-#if HAVE_POSIX_ACL || HAVE_DARWIN_ACL
-			/*
-			 * 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;
-			}
-#endif /* HAVE_POSIX_ACL || HAVE_DARWIN_ACL */
-			for (i = 0; i < (int)(sizeof(acl_inherit_map) /sizeof(acl_inherit_map[0])); ++i) {
-				if (ae_permset & acl_inherit_map[i].archive_inherit) {
-#if HAVE_SUN_ACL
-					ace->a_flags |=
-					    acl_inherit_map[i].platform_inherit;
-#else	/* !HAVE_SUN_ACL */
-					if (acl_add_flag_np(acl_flagset,
-							acl_inherit_map[i].platform_inherit) != 0) {
-						archive_set_error(a, errno,
-						    "Failed to add flag to NFSv4 ACL flagset");
-						ret = ARCHIVE_FAILED;
-						goto exit_free;
-					}
-#endif	/* HAVE_SUN_ACL */
-				}
-			}
-		}
-#endif	/* HAVE_NFS4_ACL */
-#if HAVE_SUN_ACL
-	e++;
-#endif
-	}
-
-#if HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD || HAVE_SUN_ACL
-	/* Try restoring the ACL through 'fd' if we can. */
-#if HAVE_SUN_ACL || HAVE_ACL_SET_FD_NP
-	if (fd >= 0)
-#else	/* !HAVE_SUN_ACL && !HAVE_ACL_SET_FD_NP */
-	if (fd >= 0 && acl_type == ACL_TYPE_ACCESS)
-#endif
-	{
-#if HAVE_SUN_ACL
-		if (facl_set(fd, acl) == 0)
-#elif HAVE_ACL_SET_FD_NP
-		if (acl_set_fd_np(fd, acl, acl_type) == 0)
-#else	/* !HAVE_SUN_ACL && !HAVE_ACL_SET_FD_NP */
-		if (acl_set_fd(fd, acl) == 0)
-#endif
-			ret = ARCHIVE_OK;
-		else {
-			if (errno == EOPNOTSUPP) {
-				/* Filesystem doesn't support ACLs */
-				ret = ARCHIVE_OK;
-			} else {
-				archive_set_error(a, errno,
-				    "Failed to set %s acl on fd", tname);
-			}
-		}
-	} else
-#endif	/* HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD || HAVE_SUN_ACL */
-#if HAVE_SUN_ACL
-	if (acl_set(name, acl) != 0)
-#elif HAVE_ACL_SET_LINK_NP
-	if (acl_set_link_np(name, acl_type, acl) != 0)
-#else
-	/* TODO: Skip this if 'name' is a symlink. */
-	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 %s acl",
-			    tname);
-			ret = ARCHIVE_WARN;
-		}
-	}
-exit_free:
-	acl_free(acl);
-	return (ret);
-}
-#endif	/* HAVE_POSIX_ACL || HAVE_NFS4_ACL */
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
index 5a01e84..6ad5399 100644
--- a/libarchive/archive_write_disk_posix.c
+++ b/libarchive/archive_write_disk_posix.c
@@ -39,9 +39,9 @@ __FBSDID("$FreeBSD$");
 #ifdef HAVE_SYS_EXTATTR_H
 #include <sys/extattr.h>
 #endif
-#if defined(HAVE_SYS_XATTR_H)
+#if HAVE_SYS_XATTR_H
 #include <sys/xattr.h>
-#elif defined(HAVE_ATTR_XATTR_H)
+#elif HAVE_ATTR_XATTR_H
 #include <attr/xattr.h>
 #endif
 #ifdef HAVE_SYS_EA_H
@@ -575,10 +575,55 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
 	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)
@@ -619,8 +664,21 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
 	}
 #endif
 
-	if (a->flags & ARCHIVE_EXTRACT_XATTR)
+	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) {
@@ -1703,25 +1761,11 @@ _archive_write_disk_finish_entry(struct archive *_a)
 	 */
 	if (a->todo & TODO_ACLS) {
 		int r2;
-#ifdef HAVE_DARWIN_ACL
-		/*
-		 * On Mac OS, platform ACLs are stored also in mac_metadata by
-		 * the operating system. If mac_metadata is present it takes
-		 * precedence and we skip extracting libarchive NFSv4 ACLs
-		 */
-		const void *metadata;
-		size_t metadata_size;
-		metadata = archive_entry_mac_metadata(a->entry, &metadata_size);
-		if ((a->todo & TODO_MAC_METADATA) == 0 ||
-		    metadata == NULL || metadata_size == 0) {
-#endif
 		r2 = archive_write_disk_set_acls(&a->archive, a->fd,
 		    archive_entry_pathname(a->entry),
-		    archive_entry_acl(a->entry));
+		    archive_entry_acl(a->entry),
+		    archive_entry_mode(a->entry));
 		if (r2 < ret) ret = r2;
-#ifdef HAVE_DARWIN_ACL
-		}
-#endif
 	}
 
 finish_metadata:
@@ -2293,13 +2337,8 @@ _archive_write_disk_close(struct archive *_a)
 		if (p->fixup & TODO_MODE_BASE)
 			chmod(p->name, p->mode);
 		if (p->fixup & TODO_ACLS)
-#ifdef HAVE_DARWIN_ACL
-			if ((p->fixup & TODO_MAC_METADATA) == 0 ||
-			    p->mac_metadata == NULL ||
-			    p->mac_metadata_size == 0)
-#endif
-				archive_write_disk_set_acls(&a->archive,
-				    -1, p->name, &p->acl);
+			archive_write_disk_set_acls(&a->archive, -1, p->name,
+			    &p->acl, p->mode);
 		if (p->fixup & TODO_FFLAGS)
 			set_fflags_platform(a, -1, p->name,
 			    p->mode, p->fflags_set, 0);
@@ -2467,7 +2506,7 @@ fsobj_error(int *a_eno, struct archive_string *a_estr,
 	if (a_eno)
 		*a_eno = err;
 	if (a_estr)
-		archive_string_sprintf(a_estr, errstr, path);
+		archive_string_sprintf(a_estr, "%s%s", errstr, path);
 }
 
 /*
@@ -2573,7 +2612,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
 				 * with the deep-directory editing.
 				 */
 				fsobj_error(a_eno, a_estr, errno,
-				    "Could not stat %s", path);
+				    "Could not stat ", path);
 				res = ARCHIVE_FAILED;
 				break;
 			}
@@ -2582,7 +2621,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
 				if (chdir(head) != 0) {
 					tail[0] = c;
 					fsobj_error(a_eno, a_estr, errno,
-					    "Could not chdir %s", path);
+					    "Could not chdir ", path);
 					res = (ARCHIVE_FATAL);
 					break;
 				}
@@ -2599,7 +2638,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
 				if (unlink(head)) {
 					tail[0] = c;
 					fsobj_error(a_eno, a_estr, errno,
-					    "Could not remove symlink %s",
+					    "Could not remove symlink ",
 					    path);
 					res = ARCHIVE_FAILED;
 					break;
@@ -2618,7 +2657,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
 				/*
 				if (!S_ISLNK(path)) {
 					fsobj_error(a_eno, a_estr, 0,
-					    "Removing symlink %s", path);
+					    "Removing symlink ", path);
 				}
 				*/
 				/* Symlink gone.  No more problem! */
@@ -2630,7 +2669,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
 					tail[0] = c;
 					fsobj_error(a_eno, a_estr, 0,
 					    "Cannot remove intervening "
-					    "symlink %s", path);
+					    "symlink ", path);
 					res = ARCHIVE_FAILED;
 					break;
 				}
@@ -2652,7 +2691,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
 					} else {
 						fsobj_error(a_eno, a_estr,
 						    errno,
-						    "Could not stat %s", path);
+						    "Could not stat ", path);
 						res = (ARCHIVE_FAILED);
 						break;
 					}
@@ -2661,7 +2700,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
 						tail[0] = c;
 						fsobj_error(a_eno, a_estr,
 						    errno,
-						    "Could not chdir %s", path);
+						    "Could not chdir ", path);
 						res = (ARCHIVE_FATAL);
 						break;
 					}
@@ -2674,14 +2713,14 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
 					tail[0] = c;
 					fsobj_error(a_eno, a_estr, 0,
 					    "Cannot extract through "
-					    "symlink %s", path);
+					    "symlink ", path);
 					res = ARCHIVE_FAILED;
 					break;
 				}
 			} else {
 				tail[0] = c;
 				fsobj_error(a_eno, a_estr, 0,
-				    "Cannot extract through symlink %s", path);
+				    "Cannot extract through symlink ", path);
 				res = ARCHIVE_FAILED;
 				break;
 			}
@@ -4044,71 +4083,98 @@ skip_appledouble:
 }
 #endif
 
-#if HAVE_LSETXATTR || HAVE_LSETEA
+#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX
 /*
- * Restore extended attributes -  Linux and AIX implementations:
+ * 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;
-	static int warning_done = 0;
+	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 &&
-				strncmp(name, "xfsroot.", 8) != 0 &&
-				strncmp(name, "system.", 7) != 0) {
-			int e;
-#if HAVE_FSETXATTR
-			if (a->fd >= 0)
-				e = fsetxattr(a->fd, name, value, size, 0);
-			else
-#elif HAVE_FSETEA
-			if (a->fd >= 0)
-				e = fsetea(a->fd, name, value, size, 0);
-			else
+
+		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 HAVE_LSETXATTR
-				e = lsetxattr(archive_entry_pathname(entry),
-				    name, value, size, 0);
-#elif HAVE_LSETEA
-				e = lsetea(archive_entry_pathname(entry),
-				    name, value, size, 0);
+
+		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
-			}
-			if (e == -1) {
-				if (errno == ENOTSUP || errno == ENOSYS) {
-					if (!warning_done) {
-						warning_done = 1;
-						archive_set_error(&a->archive,
-						    errno,
-						    "Cannot restore extended "
-						    "attributes on this file "
-						    "system");
-					}
-				} else
-					archive_set_error(&a->archive, errno,
-					    "Failed to set extended attribute");
-				ret = ARCHIVE_WARN;
-			}
 		} else {
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Invalid extended attribute encountered");
+#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 HAVE_EXTATTR_SET_FILE && HAVE_DECL_EXTATTR_NAMESPACE_USER
+#elif ARCHIVE_XATTR_FREEBSD
 /*
  * Restore extended attributes -  FreeBSD implementation
  */
@@ -4116,9 +4182,12 @@ static int
 set_xattrs(struct archive_write_disk *a)
 {
 	struct archive_entry *entry = a->entry;
-	static int warning_done = 0;
+	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;
@@ -4134,46 +4203,47 @@ set_xattrs(struct archive_write_disk *a)
 				name += 5;
 				namespace = EXTATTR_NAMESPACE_USER;
 			} else {
-				/* Warn about other extended attributes. */
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Can't restore extended attribute ``%s''",
-				    name);
+				/* Other namespaces are unsupported */
+				archive_strcat(&errlist, name);
+				archive_strappend_char(&errlist, ' ');
+				fail = 1;
 				ret = ARCHIVE_WARN;
 				continue;
 			}
-			errno = 0;
-#if HAVE_EXTATTR_SET_FD
-			if (a->fd >= 0)
+
+			if (a->fd >= 0) {
 				e = extattr_set_fd(a->fd, namespace, name,
 				    value, size);
-			else
-#endif
-			/* TODO: should we use extattr_set_link() instead? */
-			{
-				e = extattr_set_file(
+			} else {
+				e = extattr_set_link(
 				    archive_entry_pathname(entry), namespace,
 				    name, value, size);
 			}
 			if (e != (int)size) {
-				if (errno == ENOTSUP || errno == ENOSYS) {
-					if (!warning_done) {
-						warning_done = 1;
-						archive_set_error(&a->archive,
-						    errno,
-						    "Cannot restore extended "
-						    "attributes on this file "
-						    "system");
-					}
-				} else {
-					archive_set_error(&a->archive, errno,
-					    "Failed to set extended attribute");
-				}
-
+				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
@@ -4239,5 +4309,19 @@ older(struct stat *st, struct archive_entry *entry)
 	return (0);
 }
 
+#ifndef ARCHIVE_ACL_SUPPORT
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+	(void)a; /* UNUSED */
+	(void)fd; /* UNUSED */
+	(void)name; /* UNUSED */
+	(void)abstract_acl; /* UNUSED */
+	(void)mode; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+#endif
+
 #endif /* !_WIN32 || __CYGWIN__ */
 
diff --git a/libarchive/archive_write_disk_private.h b/libarchive/archive_write_disk_private.h
index d84e7e1..b655dea 100644
--- a/libarchive/archive_write_disk_private.h
+++ b/libarchive/archive_write_disk_private.h
@@ -33,11 +33,13 @@
 #ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
 #define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
 
+#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 /* fd */, const char * /* pathname */, struct archive_acl *);
+int archive_write_disk_set_acls(struct archive *, int, const char *,
+    struct archive_acl *, __LA_MODE_T);
 
 #endif
diff --git a/libarchive/archive_write_finish_entry.3 b/libarchive/archive_write_finish_entry.3
index c5ef69e..dc1b94b 100644
--- a/libarchive/archive_write_finish_entry.3
+++ b/libarchive/archive_write_finish_entry.3
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 2, 2012
+.Dd February 28, 2017
 .Dt ARCHIVE_WRITE_FINISH_ENTRY 3
 .Os
 .Sh NAME
@@ -45,6 +45,9 @@ is called automatically by
 and
 .Fn archive_write_close
 as needed.
+For
+.Tn archive_write_disk
+handles, this flushes pending file attribute changes like modification time.
 .\" .Sh EXAMPLE
 .Sh RETURN VALUES
 This function returns
diff --git a/libarchive/archive_write_format.3 b/libarchive/archive_write_format.3
index d4ba6ab..aaafb0a 100644
--- a/libarchive/archive_write_format.3
+++ b/libarchive/archive_write_format.3
@@ -108,7 +108,6 @@ Streaming Archive Library (libarchive, -larchive)
 These functions set the format that will be used for the archive.
 .Pp
 The library can write a variety of common archive formats.
-
 .Bl -tag -width indent
 .It Fn archive_write_set_format
 Sets the format based on the format code (see
diff --git a/libarchive/archive_write_set_format.c b/libarchive/archive_write_set_format.c
index 744302d..0f70623 100644
--- a/libarchive/archive_write_set_format.c
+++ b/libarchive/archive_write_set_format.c
@@ -38,7 +38,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format.c 201168 2009-1
 #include "archive_private.h"
 
 /* A table that maps format codes to functions. */
-static
+static const
 struct { int code; int (*setter)(struct archive *); } codes[] =
 {
 	{ ARCHIVE_FORMAT_7ZIP,		archive_write_set_format_7zip },
diff --git a/libarchive/archive_write_set_format_by_name.c b/libarchive/archive_write_set_format_by_name.c
index a2ce7c6..86e8621 100644
--- a/libarchive/archive_write_set_format_by_name.c
+++ b/libarchive/archive_write_set_format_by_name.c
@@ -41,7 +41,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_by_name.c 20116
 #include "archive_private.h"
 
 /* A table that maps names to functions. */
-static
+static const
 struct { const char *name; int (*setter)(struct archive *); } names[] =
 {
 	{ "7zip",	archive_write_set_format_7zip },
diff --git a/libarchive/archive_write_set_format_filter_by_ext.c b/libarchive/archive_write_set_format_filter_by_ext.c
index adec9b2..9fe21e4 100644
--- a/libarchive/archive_write_set_format_filter_by_ext.c
+++ b/libarchive/archive_write_set_format_filter_by_ext.c
@@ -42,7 +42,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_by_name.c 20116
 #include "archive_private.h"
 
 /* A table that maps names to functions. */
-static
+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},
diff --git a/libarchive/archive_write_set_format_pax.c b/libarchive/archive_write_set_format_pax.c
index 6a301ac..0eaf733 100644
--- a/libarchive/archive_write_set_format_pax.c
+++ b/libarchive/archive_write_set_format_pax.c
@@ -1196,8 +1196,12 @@ archive_write_pax_header(struct archive_write *a,
 			    "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", entry_name.s);
+			    "GNU.sparse.name", path);
 			add_pax_attr_int(&(pax->pax_header),
 			    "GNU.sparse.realsize",
 			    archive_entry_size(entry_main));
@@ -1650,13 +1654,14 @@ build_pax_attribute_name(char *dest, const char *src)
  * GNU PAX Format 1.0 requires the special name, which pattern is:
  * <dir>/GNUSparseFile.<pid>/<original file name>
  *
+ * Since reproducable 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)
 {
-	char buff[64];
 	const char *p;
 
 	/* Handle the null filename case. */
@@ -1682,15 +1687,9 @@ build_gnu_sparse_name(char *dest, const char *src)
 		break;
 	}
 
-#if HAVE_GETPID && 0  /* Disable this as pax attribute name. */
-	sprintf(buff, "GNUSparseFile.%d", getpid());
-#else
-	/* If the platform can't fetch the pid, don't include it. */
-	strcpy(buff, "GNUSparseFile");
-#endif
 	/* General case: build a ustar-compatible name adding
 	 * "/GNUSparseFile/". */
-	build_ustar_entry_name(dest, src, p - src, buff);
+	build_ustar_entry_name(dest, src, p - src, "GNUSparseFile.0");
 
 	return (dest);
 }
diff --git a/libarchive/archive_write_set_format_warc.c b/libarchive/archive_write_set_format_warc.c
index 8b6daf9..edad072 100644
--- a/libarchive/archive_write_set_format_warc.c
+++ b/libarchive/archive_write_set_format_warc.c
@@ -354,7 +354,7 @@ 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 *_typ[LAST_WT] = {
+	static const char * const _typ[LAST_WT] = {
 		NULL, "warcinfo", "metadata", "resource", NULL
 	};
 	char std_uuid[48U];
diff --git a/libarchive/config_freebsd.h b/libarchive/config_freebsd.h
index 215e886..be25258 100644
--- a/libarchive/config_freebsd.h
+++ b/libarchive/config_freebsd.h
@@ -22,141 +22,238 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/lib/libarchive/config_freebsd.h 201079 2009-12-28 02:01:42Z kientzle $
+ * $FreeBSD$
  */
 
-/* FreeBSD 5.0 and later have ACL and extattr support. */
+#include <osreldate.h>
+
+/* FreeBSD 5.0 and later has ACL and extattr support. */
 #if __FreeBSD__ > 4
-#define	HAVE_ACL_CREATE_ENTRY 1
-#define	HAVE_ACL_GET_FD_NP 1
-#define	HAVE_ACL_GET_LINK_NP 1
-#define	HAVE_ACL_GET_PERM_NP 1
-#define	HAVE_ACL_INIT 1
-#define	HAVE_ACL_SET_FD 1
-#define	HAVE_ACL_SET_FD_NP 1
-#define	HAVE_ACL_SET_FILE 1
-#define	HAVE_ACL_USER 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
-#endif
+#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_OPENSSL_MD5_H 1
-#define	HAVE_OPENSSL_RIPEMD_H 1
-#define	HAVE_OPENSSL_SHA_H 1
-#define	HAVE_SHA384 1
-#define	HAVE_SHA512 1
+#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_DECL_INT64_MAX 1
-#define	HAVE_DECL_INT64_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_DIRENT_H 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_FSEEKO 1
-#define	HAVE_FSTAT 1
-#define	HAVE_FTRUNCATE 1
-#define	HAVE_FUTIMES 1
-#define	HAVE_GETEUID 1
-#define	HAVE_GETGRGID_R 1
-#define	HAVE_GETPID 1
-#define	HAVE_GETPWUID_R 1
-#define	HAVE_GRP_H 1
-#define	HAVE_INTTYPES_H 1
-#define	HAVE_LCHFLAGS 1
-#define	HAVE_LCHMOD 1
-#define	HAVE_LCHOWN 1
-#define	HAVE_LIMITS_H 1
-#define	HAVE_LINK 1
-#define	HAVE_LSTAT 1
-#define	HAVE_LUTIMES 1
-#define	HAVE_MALLOC 1
-#define	HAVE_MD5 1
-#define	HAVE_MD5_H 1
-#define	HAVE_MEMMOVE 1
-#define	HAVE_MKDIR 1
-#define	HAVE_MKFIFO 1
-#define	HAVE_MKNOD 1
-#define	HAVE_PIPE 1
-#define	HAVE_POLL 1
-#define	HAVE_POLL_H 1
-#define	HAVE_PWD_H 1
-#define	HAVE_READLINK 1
-#define	HAVE_RMD160 1
-#define	HAVE_SELECT 1
-#define	HAVE_SETENV 1
-#define	HAVE_SHA_H 1
-#define	HAVE_SHA1 1
-#define	HAVE_SHA256 1
-#define	HAVE_SHA256_H 1
-#define	HAVE_SIGNAL_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_STRINGS_H 1
-#define	HAVE_STRING_H 1
-#define	HAVE_STRRCHR 1
-#define	HAVE_STRUCT_STAT_ST_BLKSIZE 1
-#define	HAVE_STRUCT_STAT_ST_BIRTHTIME 1
-#define	HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
-#define	HAVE_STRUCT_STAT_ST_FLAGS 1
-#define	HAVE_STRUCT_STAT_ST_MTIMESPEC_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_SELECT_H 1
-#define	HAVE_SYS_STAT_H 1
-#define	HAVE_SYS_TIME_H 1
-#define	HAVE_SYS_TYPES_H 1
-#undef	HAVE_SYS_UTIME_H
-#define	HAVE_SYS_UTSNAME_H 1
-#define	HAVE_SYS_WAIT_H 1
-#define	HAVE_TIMEGM 1
-#define	HAVE_TZSET 1
-#define	HAVE_UNISTD_H 1
-#define	HAVE_UNSETENV 1
-#define	HAVE_UTIME 1
-#define	HAVE_UTIMES 1
-#define	HAVE_UTIME_H 1
-#define	HAVE_VFORK 1
-#define	HAVE_WCHAR_H 1
-#define	HAVE_WCSCPY 1
-#define	HAVE_WCSLEN 1
-#define	HAVE_WCTOMB 1
-#define	HAVE_WMEMCMP 1
-#define	HAVE_WMEMCPY 1
-#define	HAVE_ZLIB_H 1
-#define	TIME_WITH_SYS_TIME 1
+#define HAVE_BSDXML_H 1
+#define HAVE_BZLIB_H 1
+#define HAVE_CHFLAGS 1
+#define HAVE_CHOWN 1
+#define HAVE_CHROOT 1
+#define HAVE_CTIME_R 1
+#define HAVE_CTYPE_H 1
+#define HAVE_DECL_EXTATTR_NAMESPACE_USER 1
+#define HAVE_DECL_INT32_MAX 1
+#define HAVE_DECL_INT32_MIN 1
+#define HAVE_DECL_INT64_MAX 1
+#define HAVE_DECL_INT64_MIN 1
+#define HAVE_DECL_INTMAX_MAX 1
+#define HAVE_DECL_INTMAX_MIN 1
+#define HAVE_DECL_SIZE_MAX 1
+#define HAVE_DECL_SSIZE_MAX 1
+#define HAVE_DECL_STRERROR_R 1
+#define HAVE_DECL_UINT32_MAX 1
+#define HAVE_DECL_UINT64_MAX 1
+#define HAVE_DECL_UINTMAX_MAX 1
+#define HAVE_DIRENT_H 1
+#define HAVE_DLFCN_H 1
+#define HAVE_D_MD_ORDER 1
+#define HAVE_EFTYPE 1
+#define HAVE_EILSEQ 1
+#define HAVE_ERRNO_H 1
+#define HAVE_FCHDIR 1
+#define HAVE_FCHFLAGS 1
+#define HAVE_FCHMOD 1
+#define HAVE_FCHOWN 1
+#define HAVE_FCNTL 1
+#define HAVE_FCNTL_H 1
+#define HAVE_FDOPENDIR 1
+#define HAVE_FORK 1
+#define HAVE_FSEEKO 1
+#define HAVE_FSTAT 1
+#define HAVE_FSTATAT 1
+#define HAVE_FSTATFS 1
+#define HAVE_FSTATVFS 1
+#define HAVE_FTRUNCATE 1
+#define HAVE_FUTIMES 1
+#define HAVE_FUTIMESAT 1
+#define HAVE_GETEUID 1
+#define HAVE_GETGRGID_R 1
+#define HAVE_GETGRNAM_R 1
+#define HAVE_GETPID 1
+#define HAVE_GETPWNAM_R 1
+#define HAVE_GETPWUID_R 1
+#define HAVE_GETVFSBYNAME 1
+#define HAVE_GMTIME_R 1
+#define HAVE_GRP_H 1
+#define HAVE_INTMAX_T 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_LANGINFO_H 1
+#define HAVE_LCHFLAGS 1
+#define HAVE_LCHMOD 1
+#define HAVE_LCHOWN 1
+#define HAVE_LIBZ 1
+#define HAVE_LIMITS_H 1
+#define HAVE_LINK 1
+#define HAVE_LOCALE_H 1
+#define HAVE_LOCALTIME_R 1
+#define HAVE_LONG_LONG_INT 1
+#define HAVE_LSTAT 1
+#define HAVE_LUTIMES 1
+#define HAVE_MBRTOWC 1
+#define HAVE_MEMMOVE 1
+#define HAVE_MEMORY_H 1
+#define HAVE_MEMSET 1
+#define HAVE_MKDIR 1
+#define HAVE_MKFIFO 1
+#define HAVE_MKNOD 1
+#define HAVE_MKSTEMP 1
+#define HAVE_NL_LANGINFO 1
+#define HAVE_OPENAT 1
+#define HAVE_PATHS_H 1
+#define HAVE_PIPE 1
+#define HAVE_POLL 1
+#define HAVE_POLL_H 1
+#define HAVE_POSIX_SPAWNP 1
+#define HAVE_PTHREAD_H 1
+#define HAVE_PWD_H 1
+#define HAVE_READDIR_R 1
+#define HAVE_READLINK 1
+#define HAVE_READLINKAT 1
+#define HAVE_READPASSPHRASE 1
+#define HAVE_READPASSPHRASE_H 1
+#define HAVE_REGEX_H 1
+#define HAVE_SELECT 1
+#define HAVE_SETENV 1
+#define HAVE_SETLOCALE 1
+#define HAVE_SIGACTION 1
+#define HAVE_SIGNAL_H 1
+#define HAVE_SPAWN_H 1
+#define HAVE_STATFS 1
+#define HAVE_STATVFS 1
+#define HAVE_STDARG_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRCHR 1
+#define HAVE_STRDUP 1
+#define HAVE_STRERROR 1
+#define HAVE_STRERROR_R 1
+#define HAVE_STRFTIME 1
+#define HAVE_STRINGS_H 1
+#define HAVE_STRING_H 1
+#define HAVE_STRRCHR 1
+#define HAVE_STRUCT_STATFS_F_NAMEMAX 1
+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
+#define HAVE_STRUCT_TM_TM_GMTOFF 1
+#define HAVE_SYMLINK 1
+#define HAVE_SYS_CDEFS_H 1
+#define HAVE_SYS_IOCTL_H 1
+#define HAVE_SYS_MOUNT_H 1
+#define HAVE_SYS_PARAM_H 1
+#define HAVE_SYS_POLL_H 1
+#define HAVE_SYS_SELECT_H 1
+#define HAVE_SYS_STATVFS_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_SYS_UTSNAME_H 1
+#define HAVE_SYS_WAIT_H 1
+#define HAVE_TIMEGM 1
+#define HAVE_TIME_H 1
+#define HAVE_TZSET 1
+#define HAVE_UINTMAX_T 1
+#define HAVE_UNISTD_H 1
+#define HAVE_UNSETENV 1
+#define HAVE_UNSIGNED_LONG_LONG 1
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+#define HAVE_UTIME 1
+#define HAVE_UTIMES 1
+#define HAVE_UTIME_H 1
+#define HAVE_VFORK 1
+#define HAVE_VPRINTF 1
+#define HAVE_WCHAR_H 1
+#define HAVE_WCHAR_T 1
+#define HAVE_WCRTOMB 1
+#define HAVE_WCSCMP 1
+#define HAVE_WCSCPY 1
+#define HAVE_WCSLEN 1
+#define HAVE_WCTOMB 1
+#define HAVE_WCTYPE_H 1
+#define HAVE_WMEMCMP 1
+#define HAVE_WMEMCPY 1
+#define HAVE_WMEMMOVE 1
+#define HAVE_ZLIB_H 1
+#define TIME_WITH_SYS_TIME 1
+
+#if __FreeBSD_version >= 1100056
+#define HAVE_FUTIMENS 1
+#define HAVE_UTIMENSAT 1
+#endif
 
 /* FreeBSD 4 and earlier lack intmax_t/uintmax_t */
 #if __FreeBSD__ < 5
-#define	intmax_t int64_t
-#define	uintmax_t uint64_t
+#define intmax_t int64_t
+#define uintmax_t uint64_t
+#endif
+
+/* FreeBSD defines for archive_hash.h */
+#ifdef WITH_OPENSSL
+#define ARCHIVE_CRYPTO_MD5_OPENSSL 1
+#define ARCHIVE_CRYPTO_RMD160_OPENSSL 1
+#define ARCHIVE_CRYPTO_SHA1_OPENSSL
+#define ARCHIVE_CRYPTO_SHA256_OPENSSL 1
+#define ARCHIVE_CRYPTO_SHA384_OPENSSL 1
+#define ARCHIVE_CRYPTO_SHA512_OPENSSL 1
+#else
+#define ARCHIVE_CRYPTO_MD5_LIBMD 1
+#define ARCHIVE_CRYPTO_SHA1_LIBMD 1
+#define ARCHIVE_CRYPTO_SHA256_LIBMD 1
+#define ARCHIVE_CRYPTO_SHA512_LIBMD 1
 #endif
diff --git a/libarchive/libarchive_changes.3 b/libarchive/libarchive_changes.3
index 881a67c..adc87fe 100644
--- a/libarchive/libarchive_changes.3
+++ b/libarchive/libarchive_changes.3
@@ -28,6 +28,7 @@
 .Dt LIBARCHIVE_CHANGES 3
 .Os
 .Sh NAME
+.Nm libarchive_changes
 .Nd changes in libarchive interface
 .\"
 .Sh CHANGES IN LIBARCHIVE 3
diff --git a/libarchive/mtree.5 b/libarchive/mtree.5
index 16c8abe..e607e4a 100644
--- a/libarchive/mtree.5
+++ b/libarchive/mtree.5
@@ -48,7 +48,7 @@ Leading whitespace is always ignored.
 .Pp
 When encoding file or pathnames, any backslash character or
 character outside of the 95 printable ASCII characters must be
-encoded as a a backslash followed by three
+encoded as a backslash followed by three
 octal digits.
 When reading mtree files, any appearance of a backslash
 followed by three octal digits should be converted into the
diff --git a/libarchive/xxhash.c b/libarchive/xxhash.c
index 6f5ba52..70750ba 100644
--- a/libarchive/xxhash.c
+++ b/libarchive/xxhash.c
@@ -141,13 +141,19 @@ typedef struct _U32_S { U32 v; } _PACKED U32_S;
 #  pragma pack(pop)
 #endif
 
-#define A32(x) (((const U32_S *)(x))->v)
-
 
 /****************************************
 ** Compiler-specific Functions and Macros
 *****************************************/
-#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#define GCC_VERSION ((__GNUC__-0) * 100 + (__GNUC_MINOR__ - 0))
+
+#if GCC_VERSION >= 409
+__attribute__((__no_sanitize_undefined__))
+#endif
+static inline U32 A32(const void * x)
+{
+    return (((const U32_S *)(x))->v);
+}
 
 /* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
 #if defined(_MSC_VER)

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=b6674431af4790ba5deea9e5e6bd7b2620beb69e
commit b6674431af4790ba5deea9e5e6bd7b2620beb69e
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Thu Jul 20 11:29:40 2017 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Thu Jul 20 11:29:40 2017 -0400

    libarchive: Update script to get 3.3.2

diff --git a/Utilities/Scripts/update-libarchive.bash b/Utilities/Scripts/update-libarchive.bash
index 41c6a66..7534f94 100755
--- a/Utilities/Scripts/update-libarchive.bash
+++ b/Utilities/Scripts/update-libarchive.bash
@@ -8,7 +8,7 @@ readonly name="LibArchive"
 readonly ownership="LibArchive Upstream <libarchive-discuss at googlegroups.com>"
 readonly subtree="Utilities/cmlibarchive"
 readonly repo="https://github.com/libarchive/libarchive.git"
-readonly tag="v3.3.1"
+readonly tag="v3.3.2"
 readonly shortlog=false
 readonly paths="
   CMakeLists.txt

-----------------------------------------------------------------------

Summary of changes:
 Utilities/Scripts/update-libarchive.bash           |    2 +-
 Utilities/cmlibarchive/CMakeLists.txt              |  409 +++++--
 Utilities/cmlibarchive/build/cmake/config.h.in     |   92 +-
 Utilities/cmlibarchive/build/version               |    2 +-
 Utilities/cmlibarchive/libarchive/CMakeLists.txt   |   15 +-
 Utilities/cmlibarchive/libarchive/archive.h        |    4 +-
 .../cmlibarchive/libarchive/archive_check_magic.c  |    2 +-
 .../libarchive/archive_disk_acl_darwin.c           |  559 +++++++++
 .../libarchive/archive_disk_acl_freebsd.c          |  700 +++++++++++
 .../libarchive/archive_disk_acl_linux.c            |  743 +++++++++++
 .../libarchive/archive_disk_acl_sunos.c            |  821 +++++++++++++
 Utilities/cmlibarchive/libarchive/archive_entry.3  |    2 +-
 Utilities/cmlibarchive/libarchive/archive_entry.c  |   31 +-
 Utilities/cmlibarchive/libarchive/archive_entry.h  |    2 +-
 .../cmlibarchive/libarchive/archive_entry_acl.3    |   10 +-
 .../cmlibarchive/libarchive/archive_entry_paths.3  |   12 +-
 .../cmlibarchive/libarchive/archive_entry_perms.3  |    4 +-
 .../cmlibarchive/libarchive/archive_entry_sparse.c |    4 +-
 .../cmlibarchive/libarchive/archive_getdate.c      |    2 +-
 .../libarchive/archive_openssl_hmac_private.h      |    2 +-
 .../cmlibarchive/libarchive/archive_pack_dev.c     |    2 +-
 .../cmlibarchive/libarchive/archive_platform.h     |   26 -
 ...ve_cmdline_private.h => archive_platform_acl.h} |   34 +-
 ...{archive_getdate.h => archive_platform_xattr.h} |   22 +-
 Utilities/cmlibarchive/libarchive/archive_random.c |    3 +
 Utilities/cmlibarchive/libarchive/archive_read.c   |    3 +-
 .../cmlibarchive/libarchive/archive_read_disk.3    |   72 +-
 .../libarchive/archive_read_disk_entry_from_file.c | 1285 +++-----------------
 .../libarchive/archive_read_disk_private.h         |    9 +
 .../cmlibarchive/libarchive/archive_read_format.3  |    6 +-
 .../cmlibarchive/libarchive/archive_read_open.3    |    4 +-
 .../libarchive/archive_read_support_filter_lz4.c   |    6 +-
 .../libarchive/archive_read_support_format_cab.c   |  156 +--
 .../libarchive/archive_read_support_format_cpio.c  |    2 +-
 .../archive_read_support_format_iso9660.c          |    5 +-
 .../libarchive/archive_read_support_format_lha.c   |    2 +-
 .../libarchive/archive_read_support_format_mtree.c |  180 +--
 .../libarchive/archive_read_support_format_rar.c   |    2 +-
 .../libarchive/archive_read_support_format_tar.c   |   18 +-
 .../libarchive/archive_read_support_format_warc.c  |    9 +-
 .../libarchive/archive_read_support_format_zip.c   |   39 +-
 Utilities/cmlibarchive/libarchive/archive_string.c |    3 +-
 .../libarchive/archive_string_sprintf.c            |    2 +-
 Utilities/cmlibarchive/libarchive/archive_util.c   |   88 +-
 .../libarchive/archive_version_details.c           |  133 ++
 .../libarchive/archive_write_add_filter.c          |    2 +-
 .../libarchive/archive_write_add_filter_by_name.c  |    2 +-
 .../libarchive/archive_write_add_filter_lz4.c      |    2 +-
 .../libarchive/archive_write_add_filter_program.c  |    2 +-
 .../cmlibarchive/libarchive/archive_write_data.3   |   24 +-
 .../cmlibarchive/libarchive/archive_write_disk.3   |  185 +--
 .../libarchive/archive_write_disk_acl.c            |  654 ----------
 .../libarchive/archive_write_disk_posix.c          |  296 +++--
 .../libarchive/archive_write_disk_private.h        |    6 +-
 .../libarchive/archive_write_finish_entry.3        |    5 +-
 .../cmlibarchive/libarchive/archive_write_format.3 |    1 -
 .../libarchive/archive_write_set_format.c          |    2 +-
 .../libarchive/archive_write_set_format_by_name.c  |    2 +-
 .../archive_write_set_format_filter_by_ext.c       |    2 +-
 .../libarchive/archive_write_set_format_pax.c      |   17 +-
 .../libarchive/archive_write_set_format_warc.c     |    2 +-
 Utilities/cmlibarchive/libarchive/config_freebsd.h |  351 ++++--
 .../cmlibarchive/libarchive/libarchive_changes.3   |    1 +
 Utilities/cmlibarchive/libarchive/mtree.5          |    2 +-
 Utilities/cmlibarchive/libarchive/xxhash.c         |   12 +-
 65 files changed, 4312 insertions(+), 2787 deletions(-)
 create mode 100644 Utilities/cmlibarchive/libarchive/archive_disk_acl_darwin.c
 create mode 100644 Utilities/cmlibarchive/libarchive/archive_disk_acl_freebsd.c
 create mode 100644 Utilities/cmlibarchive/libarchive/archive_disk_acl_linux.c
 create mode 100644 Utilities/cmlibarchive/libarchive/archive_disk_acl_sunos.c
 copy Utilities/cmlibarchive/libarchive/{archive_cmdline_private.h => archive_platform_acl.h} (69%)
 copy Utilities/cmlibarchive/libarchive/{archive_getdate.h => archive_platform_xattr.h} (76%)
 create mode 100644 Utilities/cmlibarchive/libarchive/archive_version_details.c
 delete mode 100644 Utilities/cmlibarchive/libarchive/archive_write_disk_acl.c


hooks/post-receive
-- 
CMake


More information about the Cmake-commits mailing list