[Cmake-commits] CMake branch, next, updated. v3.6.0-rc2-453-g5c86380

Brad King brad.king at kitware.com
Mon Jun 20 10:53:20 EDT 2016


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, next has been updated
       via  5c863804bae75118e2c06b75c817f7afd9bb372b (commit)
       via  52f58267c311550db83f4a9430f378e730bd3d6b (commit)
       via  2b94d71d8850d68b677d5653c698371528344a10 (commit)
      from  c0c34e04c3e00dae9c16d9cf0d4b47d3d752d482 (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=5c863804bae75118e2c06b75c817f7afd9bb372b
commit 5c863804bae75118e2c06b75c817f7afd9bb372b
Merge: c0c34e0 52f5826
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Mon Jun 20 10:53:18 2016 -0400
Commit:     CMake Topic Stage <kwrobot at kitware.com>
CommitDate: Mon Jun 20 10:53:18 2016 -0400

    Merge topic 'update-libarchive' into next
    
    52f58267 Merge branch 'upstream-LibArchive' into update-libarchive
    2b94d71d LibArchive 2016-06-19 (139d0576)


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=52f58267c311550db83f4a9430f378e730bd3d6b
commit 52f58267c311550db83f4a9430f378e730bd3d6b
Merge: 9d81f1b 2b94d71
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Mon Jun 20 10:50:13 2016 -0400
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Mon Jun 20 10:50:13 2016 -0400

    Merge branch 'upstream-LibArchive' into update-libarchive
    
    * upstream-LibArchive:
      LibArchive 2016-06-19 (139d0576)

diff --cc Utilities/cmlibarchive/libarchive/archive.h
index a8aa704,0000000..3b5104b
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive.h
+++ b/Utilities/cmlibarchive/libarchive/archive.h
@@@ -1,1180 -1,0 +1,1180 @@@
 +/*-
 + * 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 3002000
++#define	ARCHIVE_VERSION_NUMBER 3002001
 +
 +#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.2.0"
++#define	ARCHIVE_VERSION_ONLY_STRING "3.2.1"
 +#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_finish 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 uncapable 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 travesal 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 travesal 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)
 +
 +__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 functinos.
 + */
 +/* 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 particluar 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_entry.h
index 2bc2928,0000000..81cd425
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_entry.h
+++ b/Utilities/cmlibarchive/libarchive/archive_entry.h
@@@ -1,642 -1,0 +1,642 @@@
 +/*-
 + * Copyright (c) 2003-2008 Tim Kientzle
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + *
 + * $FreeBSD: head/lib/libarchive/archive_entry.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 3002000
++#define	ARCHIVE_VERSION_NUMBER 3002001
 +
 +/*
 + * 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
 +
 +/* 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
 +
 +#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_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)
 +
 +/* We need to be able to specify combinations of these. */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_ACCESS	256  /* POSIX.1e only */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_DEFAULT	512  /* POSIX.1e only */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_ALLOW	1024 /* NFS4 only */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_DENY	2048 /* NFS4 only */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_AUDIT	4096 /* NFS4 only */
 +#define	ARCHIVE_ENTRY_ACL_TYPE_ALARM	8192 /* 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:
 + *
 + * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries.
 + * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries.
 + * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - Include NFS4 entries.
 + * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
 + *    each ACL entry.  ('star' introduced this for POSIX.1e, this flag
 + *    also applies to NFS4.)
 + * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
 + *    default ACL entry, as used in old Solaris ACLs.
 + */
 +#define	ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID	1024
 +#define	ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	2048
 +__LA_DECL const wchar_t	*archive_entry_acl_text_w(struct archive_entry *,
 +		    int /* flags */);
 +__LA_DECL const char *archive_entry_acl_text(struct archive_entry *,
 +		    int /* flags */);
 +
 +/* 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_read_support_format_7zip.c
index f045b8f,0000000..a33ea4f
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c
@@@ -1,3880 -1,0 +1,3883 @@@
 +/*-
 + * Copyright (c) 2011 Michihiro NAKAJIMA
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + */
 +
 +#include "archive_platform.h"
 +__FBSDID("$FreeBSD$");
 +
 +#ifdef HAVE_ERRNO_H
 +#include <errno.h>
 +#endif
 +#ifdef HAVE_STDLIB_H
 +#include <stdlib.h>
 +#endif
 +#ifdef HAVE_BZLIB_H
 +#include <cm_bzlib.h>
 +#endif
 +#ifdef HAVE_LZMA_H
 +#include <cm_lzma.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_ppmd7_private.h"
 +#include "archive_private.h"
 +#include "archive_read_private.h"
 +#include "archive_endian.h"
 +
 +#ifndef HAVE_ZLIB_H
 +#include "archive_crc32.h"
 +#endif
 +
 +#define _7ZIP_SIGNATURE	"7z\xBC\xAF\x27\x1C"
 +#define SFX_MIN_ADDR	0x27000
 +#define SFX_MAX_ADDR	0x60000
 +
 +
 +/*
 + * Codec ID
 + */
 +#define _7Z_COPY	0
 +#define _7Z_LZMA	0x030101
 +#define _7Z_LZMA2	0x21
 +#define _7Z_DEFLATE	0x040108
 +#define _7Z_BZ2		0x040202
 +#define _7Z_PPMD	0x030401
 +#define _7Z_DELTA	0x03
 +#define _7Z_CRYPTO_MAIN_ZIP			0x06F10101 /* Main Zip crypto algo */
 +#define _7Z_CRYPTO_RAR_29			0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */
 +#define _7Z_CRYPTO_AES_256_SHA_256	0x06F10701 /* AES-256 + SHA-256 */
 +
 +
 +#define _7Z_X86		0x03030103
 +#define _7Z_X86_BCJ2	0x0303011B
 +#define _7Z_POWERPC	0x03030205
 +#define _7Z_IA64	0x03030401
 +#define _7Z_ARM		0x03030501
 +#define _7Z_ARMTHUMB	0x03030701
 +#define _7Z_SPARC	0x03030805
 +
 +/*
 + * 7-Zip header property IDs.
 + */
 +#define kEnd			0x00
 +#define kHeader			0x01
 +#define kArchiveProperties	0x02
 +#define kAdditionalStreamsInfo	0x03
 +#define kMainStreamsInfo	0x04
 +#define kFilesInfo		0x05
 +#define kPackInfo		0x06
 +#define kUnPackInfo		0x07
 +#define kSubStreamsInfo		0x08
 +#define kSize			0x09
 +#define kCRC			0x0A
 +#define kFolder			0x0B
 +#define kCodersUnPackSize	0x0C
 +#define kNumUnPackStream	0x0D
 +#define kEmptyStream		0x0E
 +#define kEmptyFile		0x0F
 +#define kAnti			0x10
 +#define kName			0x11
 +#define kCTime			0x12
 +#define kATime			0x13
 +#define kMTime			0x14
 +#define kAttributes		0x15
 +#define kEncodedHeader		0x17
 +#define kDummy			0x19
 +
 +struct _7z_digests {
 +	unsigned char	*defineds;
 +	uint32_t	*digests;
 +};
 +
 +
 +struct _7z_folder {
 +	uint64_t		 numCoders;
 +	struct _7z_coder {
 +		unsigned long	 codec;
 +		uint64_t	 numInStreams;
 +		uint64_t	 numOutStreams;
 +		uint64_t	 propertiesSize;
 +		unsigned char	*properties;
 +	} *coders;
 +	uint64_t		 numBindPairs;
 +	struct {
 +		uint64_t	 inIndex;
 +		uint64_t	 outIndex;
 +	} *bindPairs;
 +	uint64_t		 numPackedStreams;
 +	uint64_t		*packedStreams;
 +	uint64_t		 numInStreams;
 +	uint64_t		 numOutStreams;
 +	uint64_t		*unPackSize;
 +	unsigned char		 digest_defined;
 +	uint32_t		 digest;
 +	uint64_t		 numUnpackStreams;
 +	uint32_t		 packIndex;
 +	/* Unoperated bytes. */
 +	uint64_t		 skipped_bytes;
 +};
 +
 +struct _7z_coders_info {
 +	uint64_t		 numFolders;
 +	struct _7z_folder	*folders;
 +	uint64_t		 dataStreamIndex;
 +};
 +
 +struct _7z_pack_info {
 +	uint64_t		 pos;
 +	uint64_t		 numPackStreams;
 +	uint64_t		*sizes;
 +	struct _7z_digests	 digest;
 +	/* Calculated from pos and numPackStreams. */
 +	uint64_t		*positions;
 +};
 +
 +struct _7z_substream_info {
 +	size_t			 unpack_streams;
 +	uint64_t		*unpackSizes;
 +	unsigned char		*digestsDefined;
 +	uint32_t		*digests;
 +};
 +
 +struct _7z_stream_info {
 +	struct _7z_pack_info	 pi;
 +	struct _7z_coders_info	 ci;
 +	struct _7z_substream_info ss;
 +};
 +
 +struct _7z_header_info {
 +	uint64_t		 dataIndex;
 +
 +	unsigned char		*emptyStreamBools;
 +	unsigned char		*emptyFileBools;
 +	unsigned char		*antiBools;
 +	unsigned char		*attrBools;
 +};
 +
 +struct _7zip_entry {
 +	size_t			 name_len;
 +	unsigned char		*utf16name;
 +#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
 +	const wchar_t		*wname;
 +#endif
 +	uint32_t		 folderIndex;
 +	uint32_t		 ssIndex;
 +	unsigned		 flg;
 +#define MTIME_IS_SET	(1<<0)
 +#define ATIME_IS_SET	(1<<1)
 +#define CTIME_IS_SET	(1<<2)
 +#define CRC32_IS_SET	(1<<3)
 +#define HAS_STREAM	(1<<4)
 +
 +	time_t			 mtime;
 +	time_t			 atime;
 +	time_t			 ctime;
 +	long			 mtime_ns;
 +	long			 atime_ns;
 +	long			 ctime_ns;
 +	uint32_t		 mode;
 +	uint32_t		 attr;
 +};
 +
 +struct _7zip {
 +	/* Structural information about the archive. */
 +	struct _7z_stream_info	 si;
 +
 +	int			 header_is_being_read;
 +	int			 header_is_encoded;
 +	uint64_t		 header_bytes_remaining;
 +	unsigned long		 header_crc32;
 +	/* Header offset to check that reading pointes of the file contens
 +	 * will not exceed the header. */
 +	uint64_t		 header_offset;
 +	/* Base offset of the archive file for a seek in case reading SFX. */
 +	uint64_t		 seek_base;
 +
 +	/* List of entries */
 +	size_t			 entries_remaining;
 +	uint64_t		 numFiles;
 +	struct _7zip_entry	*entries;
 +	struct _7zip_entry	*entry;
 +	unsigned char		*entry_names;
 +
 +	/* entry_bytes_remaining is the number of bytes we expect. */
 +	int64_t			 entry_offset;
 +	uint64_t		 entry_bytes_remaining;
 +
 +	/* Running CRC32 of the decompressed data */
 +	unsigned long		 entry_crc32;
 +
 +	/* Flags to mark progress of decompression. */
 +	char			 end_of_entry;
 +
 +	/* Uncompressed buffer control.  */
 +#define UBUFF_SIZE	(64 * 1024)
 +	unsigned char 		*uncompressed_buffer;
 +	unsigned char 		*uncompressed_buffer_pointer;
 +	size_t 			 uncompressed_buffer_size;
 +	size_t			 uncompressed_buffer_bytes_remaining;
 +
 +	/* Offset of the compressed data. */
 +	int64_t			 stream_offset;
 +
 +	/*
 +	 * Decompressing control data.
 +	 */
 +	unsigned		 folder_index;
 +	uint64_t		 folder_outbytes_remaining;
 +	unsigned		 pack_stream_index;
 +	unsigned		 pack_stream_remaining;
 +	uint64_t		 pack_stream_inbytes_remaining;
 +	size_t			 pack_stream_bytes_unconsumed;
 +
 +	/* The codec information of a folder. */
 +	unsigned long		 codec;
 +	unsigned long		 codec2;
 +
 +	/*
 +	 * Decompressor controllers.
 +	 */
 +	/* Decording LZMA1 and LZMA2 data. */
 +#ifdef HAVE_LZMA_H
 +	lzma_stream		 lzstream;
 +	int			 lzstream_valid;
 +#endif
 +	/* Decording bzip2 data. */
 +#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
 +	bz_stream		 bzstream;
 +	int			 bzstream_valid;
 +#endif
 +	/* Decording deflate data. */
 +#ifdef HAVE_ZLIB_H
 +	z_stream		 stream;
 +	int			 stream_valid;
 +#endif
 +	/* Decording PPMd data. */
 +	int			 ppmd7_stat;
 +	CPpmd7			 ppmd7_context;
 +	CPpmd7z_RangeDec	 range_dec;
 +	IByteIn			 bytein;
 +	struct {
 +		const unsigned char	*next_in;
 +		int64_t			 avail_in;
 +		int64_t			 total_in;
 +		unsigned char		*next_out;
 +		int64_t			 avail_out;
 +		int64_t			 total_out;
 +		int			 overconsumed;
 +	} ppstream;
 +	int			 ppmd7_valid;
 +
 +	/* Decoding BCJ and BCJ2 data. */
 +	uint32_t		 bcj_state;
 +	size_t			 odd_bcj_size;
 +	unsigned char		 odd_bcj[4];
 +	/* Decoding BCJ data. */
 +	size_t			 bcj_prevPosT;
 +	uint32_t		 bcj_prevMask;
 +	uint32_t		 bcj_ip;
 +
 +	/* Decoding BCJ2 data. */
 +	size_t			 main_stream_bytes_remaining;
 +	unsigned char		*sub_stream_buff[3];
 +	size_t			 sub_stream_size[3];
 +	size_t			 sub_stream_bytes_remaining[3];
 +	unsigned char		*tmp_stream_buff;
 +	size_t			 tmp_stream_buff_size;
 +	size_t			 tmp_stream_bytes_avail;
 +	size_t			 tmp_stream_bytes_remaining;
 +#ifdef _LZMA_PROB32
 +#define CProb uint32_t
 +#else
 +#define CProb uint16_t
 +#endif
 +	CProb			 bcj2_p[256 + 2];
 +	uint8_t			 bcj2_prevByte;
 +	uint32_t		 bcj2_range;
 +	uint32_t		 bcj2_code;
 +	uint64_t		 bcj2_outPos;
 +
 +	/* Filename character-set conversion data. */
 +	struct archive_string_conv *sconv;
 +
 +	char			 format_name[64];
 +
 +	/* Custom value that is non-zero if this archive contains encrypted entries. */
 +	int			 has_encrypted_entries;
 +};
 +
 +/* Maximum entry size. This limitation prevents reading intentional
 + * corrupted 7-zip files on assuming there are not so many entries in
 + * the files. */
 +#define UMAX_ENTRY	ARCHIVE_LITERAL_ULL(100000000)
 +
 +static int	archive_read_format_7zip_has_encrypted_entries(struct archive_read *);
 +static int	archive_read_support_format_7zip_capabilities(struct archive_read *a);
 +static int	archive_read_format_7zip_bid(struct archive_read *, int);
 +static int	archive_read_format_7zip_cleanup(struct archive_read *);
 +static int	archive_read_format_7zip_read_data(struct archive_read *,
 +		    const void **, size_t *, int64_t *);
 +static int	archive_read_format_7zip_read_data_skip(struct archive_read *);
 +static int	archive_read_format_7zip_read_header(struct archive_read *,
 +		    struct archive_entry *);
 +static int	check_7zip_header_in_sfx(const char *);
 +static unsigned long decode_codec_id(const unsigned char *, size_t);
 +static int	decode_encoded_header_info(struct archive_read *,
 +		    struct _7z_stream_info *);
 +static int	decompress(struct archive_read *, struct _7zip *,
 +		    void *, size_t *, const void *, size_t *);
 +static ssize_t	extract_pack_stream(struct archive_read *, size_t);
 +static void	fileTimeToUtc(uint64_t, time_t *, long *);
 +static uint64_t folder_uncompressed_size(struct _7z_folder *);
 +static void	free_CodersInfo(struct _7z_coders_info *);
 +static void	free_Digest(struct _7z_digests *);
 +static void	free_Folder(struct _7z_folder *);
 +static void	free_Header(struct _7z_header_info *);
 +static void	free_PackInfo(struct _7z_pack_info *);
 +static void	free_StreamsInfo(struct _7z_stream_info *);
 +static void	free_SubStreamsInfo(struct _7z_substream_info *);
 +static int	free_decompression(struct archive_read *, struct _7zip *);
 +static ssize_t	get_uncompressed_data(struct archive_read *, const void **,
 +		    size_t, size_t);
 +static const unsigned char * header_bytes(struct archive_read *, size_t);
 +static int	init_decompression(struct archive_read *, struct _7zip *,
 +		    const struct _7z_coder *, const struct _7z_coder *);
 +static int	parse_7zip_uint64(struct archive_read *, uint64_t *);
 +static int	read_Bools(struct archive_read *, unsigned char *, size_t);
 +static int	read_CodersInfo(struct archive_read *,
 +		    struct _7z_coders_info *);
 +static int	read_Digests(struct archive_read *, struct _7z_digests *,
 +		    size_t);
 +static int	read_Folder(struct archive_read *, struct _7z_folder *);
 +static int	read_Header(struct archive_read *, struct _7z_header_info *,
 +		    int);
 +static int	read_PackInfo(struct archive_read *, struct _7z_pack_info *);
 +static int	read_StreamsInfo(struct archive_read *,
 +		    struct _7z_stream_info *);
 +static int	read_SubStreamsInfo(struct archive_read *,
 +		    struct _7z_substream_info *, struct _7z_folder *, size_t);
 +static int	read_Times(struct archive_read *, struct _7z_header_info *,
 +		    int);
 +static void	read_consume(struct archive_read *);
 +static ssize_t	read_stream(struct archive_read *, const void **, size_t,
 +		    size_t);
 +static int	seek_pack(struct archive_read *);
 +static int64_t	skip_stream(struct archive_read *, size_t);
 +static int	skip_sfx(struct archive_read *, ssize_t);
 +static int	slurp_central_directory(struct archive_read *, struct _7zip *,
 +		    struct _7z_header_info *);
 +static int	setup_decode_folder(struct archive_read *, struct _7z_folder *,
 +		    int);
 +static void	x86_Init(struct _7zip *);
 +static size_t	x86_Convert(struct _7zip *, uint8_t *, size_t);
 +static ssize_t		Bcj2_Decode(struct _7zip *, uint8_t *, size_t);
 +
 +
 +int
 +archive_read_support_format_7zip(struct archive *_a)
 +{
 +	struct archive_read *a = (struct archive_read *)_a;
 +	struct _7zip *zip;
 +	int r;
 +
 +	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
 +	    ARCHIVE_STATE_NEW, "archive_read_support_format_7zip");
 +
 +	zip = calloc(1, sizeof(*zip));
 +	if (zip == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate 7zip data");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	/*
 +	 * Until enough data has been read, we cannot tell about
 +	 * any encrypted entries yet.
 +	 */
 +	zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
 +
 +
 +	r = __archive_read_register_format(a,
 +	    zip,
 +	    "7zip",
 +	    archive_read_format_7zip_bid,
 +	    NULL,
 +	    archive_read_format_7zip_read_header,
 +	    archive_read_format_7zip_read_data,
 +	    archive_read_format_7zip_read_data_skip,
 +	    NULL,
 +	    archive_read_format_7zip_cleanup,
 +	    archive_read_support_format_7zip_capabilities,
 +	    archive_read_format_7zip_has_encrypted_entries);
 +
 +	if (r != ARCHIVE_OK)
 +		free(zip);
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +archive_read_support_format_7zip_capabilities(struct archive_read * a)
 +{
 +	(void)a; /* UNUSED */
 +	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
 +			ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
 +}
 +
 +
 +static int
 +archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a)
 +{
 +	if (_a && _a->format) {
 +		struct _7zip * zip = (struct _7zip *)_a->format->data;
 +		if (zip) {
 +			return zip->has_encrypted_entries;
 +		}
 +	}
 +	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
 +}
 +
 +static int
 +archive_read_format_7zip_bid(struct archive_read *a, int best_bid)
 +{
 +	const char *p;
 +
 +	/* If someone has already bid more than 32, then avoid
 +	   trashing the look-ahead buffers with a seek. */
 +	if (best_bid > 32)
 +		return (-1);
 +
 +	if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)
 +		return (0);
 +
 +	/* If first six bytes are the 7-Zip signature,
 +	 * return the bid right now. */
 +	if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0)
 +		return (48);
 +
 +	/*
 +	 * It may a 7-Zip SFX archive file. If first two bytes are
 +	 * 'M' and 'Z' available on Windows or first four bytes are
 +	 * "\x7F\x45LF" available on posix like system, seek the 7-Zip
 +	 * signature. Although we will perform a seek when reading
 +	 * a header, what we do not use __archive_read_seek() here is
 +	 * due to a bidding performance.
 +	 */
 +	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
 +		ssize_t offset = SFX_MIN_ADDR;
 +		ssize_t window = 4096;
 +		ssize_t bytes_avail;
 +		while (offset + window <= (SFX_MAX_ADDR)) {
 +			const char *buff = __archive_read_ahead(a,
 +					offset + window, &bytes_avail);
 +			if (buff == NULL) {
 +				/* Remaining bytes are less than window. */
 +				window >>= 1;
 +				if (window < 0x40)
 +					return (0);
 +				continue;
 +			}
 +			p = buff + offset;
 +			while (p + 32 < buff + bytes_avail) {
 +				int step = check_7zip_header_in_sfx(p);
 +				if (step == 0)
 +					return (48);
 +				p += step;
 +			}
 +			offset = p - buff;
 +		}
 +	}
 +	return (0);
 +}
 +
 +static int
 +check_7zip_header_in_sfx(const char *p)
 +{
 +	switch ((unsigned char)p[5]) {
 +	case 0x1C:
 +		if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0)
 +			return (6);
 +		/*
 +		 * Test the CRC because its extraction code has 7-Zip
 +		 * Magic Code, so we should do this in order not to
 +		 * make a mis-detection.
 +		 */
 +		if (crc32(0, (const unsigned char *)p + 12, 20)
 +			!= archive_le32dec(p + 8))
 +			return (6);
 +		/* Hit the header! */
 +		return (0);
 +	case 0x37: return (5);
 +	case 0x7A: return (4);
 +	case 0xBC: return (3);
 +	case 0xAF: return (2);
 +	case 0x27: return (1);
 +	default: return (6);
 +	}
 +}
 +
 +static int
 +skip_sfx(struct archive_read *a, ssize_t bytes_avail)
 +{
 +	const void *h;
 +	const char *p, *q;
 +	size_t skip, offset;
 +	ssize_t bytes, window;
 +
 +	/*
 +	 * If bytes_avail > SFX_MIN_ADDR we do not have to call
 +	 * __archive_read_seek() at this time since we have
 +	 * alredy had enough data.
 +	 */
 +	if (bytes_avail > SFX_MIN_ADDR)
 +		__archive_read_consume(a, SFX_MIN_ADDR);
 +	else if (__archive_read_seek(a, SFX_MIN_ADDR, SEEK_SET) < 0)
 +		return (ARCHIVE_FATAL);
 +
 +	offset = 0;
 +	window = 1;
 +	while (offset + window <= SFX_MAX_ADDR - SFX_MIN_ADDR) {
 +		h = __archive_read_ahead(a, window, &bytes);
 +		if (h == NULL) {
 +			/* Remaining bytes are less than window. */
 +			window >>= 1;
 +			if (window < 0x40)
 +				goto fatal;
 +			continue;
 +		}
 +		if (bytes < 6) {
 +			/* This case might happen when window == 1. */
 +			window = 4096;
 +			continue;
 +		}
 +		p = (const char *)h;
 +		q = p + bytes;
 +
 +		/*
 +		 * Scan ahead until we find something that looks
 +		 * like the 7-Zip header.
 +		 */
 +		while (p + 32 < q) {
 +			int step = check_7zip_header_in_sfx(p);
 +			if (step == 0) {
 +				struct _7zip *zip =
 +				    (struct _7zip *)a->format->data;
 +				skip = p - (const char *)h;
 +				__archive_read_consume(a, skip);
 +				zip->seek_base = SFX_MIN_ADDR + offset + skip;
 +				return (ARCHIVE_OK);
 +			}
 +			p += step;
 +		}
 +		skip = p - (const char *)h;
 +		__archive_read_consume(a, skip);
 +		offset += skip;
 +		if (window == 1)
 +			window = 4096;
 +	}
 +fatal:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +	    "Couldn't find out 7-Zip header");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +static int
 +archive_read_format_7zip_read_header(struct archive_read *a,
 +	struct archive_entry *entry)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	struct _7zip_entry *zip_entry;
 +	int r, ret = ARCHIVE_OK;
 +	struct _7z_folder *folder = 0;
 +	uint64_t fidx = 0;
 +
 +	/*
 +	 * It should be sufficient to call archive_read_next_header() for
 +	 * a reader to determine if an entry is encrypted or not. If the
 +	 * encryption of an entry is only detectable when calling
 +	 * archive_read_data(), so be it. We'll do the same check there
 +	 * as well.
 +	 */
 +	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
 +		zip->has_encrypted_entries = 0;
 +	}
 +
 +	a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;
 +	if (a->archive.archive_format_name == NULL)
 +		a->archive.archive_format_name = "7-Zip";
 +
 +	if (zip->entries == NULL) {
 +		struct _7z_header_info header;
 +
 +		memset(&header, 0, sizeof(header));
 +		r = slurp_central_directory(a, zip, &header);
 +		free_Header(&header);
 +		if (r != ARCHIVE_OK)
 +			return (r);
 +		zip->entries_remaining = (size_t)zip->numFiles;
 +		zip->entry = zip->entries;
 +	} else {
 +		++zip->entry;
 +	}
 +	zip_entry = zip->entry;
 +
 +	if (zip->entries_remaining <= 0 || zip_entry == NULL)
 +		return ARCHIVE_EOF;
 +	--zip->entries_remaining;
 +
 +	zip->entry_offset = 0;
 +	zip->end_of_entry = 0;
 +	zip->entry_crc32 = crc32(0, NULL, 0);
 +
 +	/* Setup a string conversion for a filename. */
 +	if (zip->sconv == NULL) {
 +		zip->sconv = archive_string_conversion_from_charset(
 +		    &a->archive, "UTF-16LE", 1);
 +		if (zip->sconv == NULL)
 +			return (ARCHIVE_FATAL);
 +	}
 +
 +	/* Figure out if the entry is encrypted by looking at the folder
 +	   that is associated to the current 7zip entry. If the folder
 +	   has a coder with a _7Z_CRYPTO codec then the folder is encrypted.
 +	   Hence the entry must also be encrypted. */
 +	if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) {
 +		folder = &(zip->si.ci.folders[zip_entry->folderIndex]);
 +		for (fidx=0; folder && fidx<folder->numCoders; fidx++) {
 +			switch(folder->coders[fidx].codec) {
 +				case _7Z_CRYPTO_MAIN_ZIP:
 +				case _7Z_CRYPTO_RAR_29:
 +				case _7Z_CRYPTO_AES_256_SHA_256: {
 +					archive_entry_set_is_data_encrypted(entry, 1);
 +					zip->has_encrypted_entries = 1;
 +					break;
 +				}
 +			}
 +		}
 +	}
 +
 +	/* Now that we've checked for encryption, if there were still no
 +	 * encrypted entries found we can say for sure that there are none.
 +	 */
 +	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
 +		zip->has_encrypted_entries = 0;
 +	}
 +
 +	if (archive_entry_copy_pathname_l(entry,
 +	    (const char *)zip_entry->utf16name,
 +	    zip_entry->name_len, zip->sconv) != 0) {
 +		if (errno == ENOMEM) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory for Pathname");
 +			return (ARCHIVE_FATAL);
 +		}
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Pathname cannot be converted "
 +		    "from %s to current locale.",
 +		    archive_string_conversion_charset_name(zip->sconv));
 +		ret = ARCHIVE_WARN;
 +	}
 +
 +	/* Populate some additional entry fields: */
 +	archive_entry_set_mode(entry, zip_entry->mode);
 +	if (zip_entry->flg & MTIME_IS_SET)
 +		archive_entry_set_mtime(entry, zip_entry->mtime,
 +			zip_entry->mtime_ns);
 +	if (zip_entry->flg & CTIME_IS_SET)
 +		archive_entry_set_ctime(entry, zip_entry->ctime,
 +		    zip_entry->ctime_ns);
 +	if (zip_entry->flg & ATIME_IS_SET)
 +		archive_entry_set_atime(entry, zip_entry->atime,
 +		    zip_entry->atime_ns);
 +	if (zip_entry->ssIndex != (uint32_t)-1) {
 +		zip->entry_bytes_remaining =
 +		    zip->si.ss.unpackSizes[zip_entry->ssIndex];
 +		archive_entry_set_size(entry, zip->entry_bytes_remaining);
 +	} else {
 +		zip->entry_bytes_remaining = 0;
 +		archive_entry_set_size(entry, 0);
 +	}
 +
 +	/* If there's no body, force read_data() to return EOF immediately. */
 +	if (zip->entry_bytes_remaining < 1)
 +		zip->end_of_entry = 1;
 +
 +	if ((zip_entry->mode & AE_IFMT) == AE_IFLNK) {
 +		unsigned char *symname = NULL;
 +		size_t symsize = 0;
 +
 +		/*
 +		 * Symbolic-name is recorded as its contents. We have to
 +		 * read the contents at this time.
 +		 */
 +		while (zip->entry_bytes_remaining > 0) {
 +			const void *buff;
 +			unsigned char *mem;
 +			size_t size;
 +			int64_t offset;
 +
 +			r = archive_read_format_7zip_read_data(a, &buff,
 +				&size, &offset);
 +			if (r < ARCHIVE_WARN) {
 +				free(symname);
 +				return (r);
 +			}
 +			mem = realloc(symname, symsize + size + 1);
 +			if (mem == NULL) {
 +				free(symname);
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "Can't allocate memory for Symname");
 +				return (ARCHIVE_FATAL);
 +			}
 +			symname = mem;
 +			memcpy(symname+symsize, buff, size);
 +			symsize += size;
 +		}
 +		if (symsize == 0) {
 +			/* If there is no synname, handle it as a regular
 +			 * file. */
 +			zip_entry->mode &= ~AE_IFMT;
 +			zip_entry->mode |= AE_IFREG;
 +			archive_entry_set_mode(entry, zip_entry->mode);
 +		} else {
 +			symname[symsize] = '\0';
 +			archive_entry_copy_symlink(entry,
 +			    (const char *)symname);
 +		}
 +		free(symname);
 +		archive_entry_set_size(entry, 0);
 +	}
 +
 +	/* Set up a more descriptive format name. */
 +	sprintf(zip->format_name, "7-Zip");
 +	a->archive.archive_format_name = zip->format_name;
 +
 +	return (ret);
 +}
 +
 +static int
 +archive_read_format_7zip_read_data(struct archive_read *a,
 +    const void **buff, size_t *size, int64_t *offset)
 +{
 +	struct _7zip *zip;
 +	ssize_t bytes;
 +	int ret = ARCHIVE_OK;
 +
 +	zip = (struct _7zip *)(a->format->data);
 +
 +	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
 +		zip->has_encrypted_entries = 0;
 +	}
 +
 +	if (zip->pack_stream_bytes_unconsumed)
 +		read_consume(a);
 +
 +	*offset = zip->entry_offset;
 +	*size = 0;
 +	*buff = NULL;
 +	/*
 +	 * If we hit end-of-entry last time, clean up and return
 +	 * ARCHIVE_EOF this time.
 +	 */
 +	if (zip->end_of_entry)
 +		return (ARCHIVE_EOF);
 +
 +	bytes = read_stream(a, buff,
 +		(size_t)zip->entry_bytes_remaining, 0);
 +	if (bytes < 0)
 +		return ((int)bytes);
 +	if (bytes == 0) {
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated 7-Zip file body");
 +		return (ARCHIVE_FATAL);
 +	}
 +	zip->entry_bytes_remaining -= bytes;
 +	if (zip->entry_bytes_remaining == 0)
 +		zip->end_of_entry = 1;
 +
 +	/* Update checksum */
 +	if ((zip->entry->flg & CRC32_IS_SET) && bytes)
 +		zip->entry_crc32 = crc32(zip->entry_crc32, *buff,
 +		    (unsigned)bytes);
 +
 +	/* If we hit the end, swallow any end-of-data marker. */
 +	if (zip->end_of_entry) {
 +		/* Check computed CRC against file contents. */
 +		if ((zip->entry->flg & CRC32_IS_SET) &&
 +			zip->si.ss.digests[zip->entry->ssIndex] !=
 +		    zip->entry_crc32) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "7-Zip bad CRC: 0x%lx should be 0x%lx",
 +			    (unsigned long)zip->entry_crc32,
 +			    (unsigned long)zip->si.ss.digests[
 +			    		zip->entry->ssIndex]);
 +			ret = ARCHIVE_WARN;
 +		}
 +	}
 +
 +	*size = bytes;
 +	*offset = zip->entry_offset;
 +	zip->entry_offset += bytes;
 +
 +	return (ret);
 +}
 +
 +static int
 +archive_read_format_7zip_read_data_skip(struct archive_read *a)
 +{
 +	struct _7zip *zip;
 +	int64_t bytes_skipped;
 +
 +	zip = (struct _7zip *)(a->format->data);
 +
 +	if (zip->pack_stream_bytes_unconsumed)
 +		read_consume(a);
 +
 +	/* If we've already read to end of data, we're done. */
 +	if (zip->end_of_entry)
 +		return (ARCHIVE_OK);
 +
 +	/*
 +	 * If the length is at the beginning, we can skip the
 +	 * compressed data much more quickly.
 +	 */
 +	bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining);
 +	if (bytes_skipped < 0)
 +		return (ARCHIVE_FATAL);
 +	zip->entry_bytes_remaining = 0;
 +
 +	/* This entry is finished and done. */
 +	zip->end_of_entry = 1;
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +archive_read_format_7zip_cleanup(struct archive_read *a)
 +{
 +	struct _7zip *zip;
 +
 +	zip = (struct _7zip *)(a->format->data);
 +	free_StreamsInfo(&(zip->si));
 +	free(zip->entries);
 +	free(zip->entry_names);
 +	free_decompression(a, zip);
 +	free(zip->uncompressed_buffer);
 +	free(zip->sub_stream_buff[0]);
 +	free(zip->sub_stream_buff[1]);
 +	free(zip->sub_stream_buff[2]);
 +	free(zip->tmp_stream_buff);
 +	free(zip);
 +	(a->format->data) = NULL;
 +	return (ARCHIVE_OK);
 +}
 +
 +static void
 +read_consume(struct archive_read *a)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +
 +	if (zip->pack_stream_bytes_unconsumed) {
 +		__archive_read_consume(a, zip->pack_stream_bytes_unconsumed);
 +		zip->stream_offset += zip->pack_stream_bytes_unconsumed;
 +		zip->pack_stream_bytes_unconsumed = 0;
 +	}
 +}
 +
 +#ifdef HAVE_LZMA_H
 +
 +/*
 + * Set an error code and choose an error message for liblzma.
 + */
 +static void
 +set_error(struct archive_read *a, int ret)
 +{
 +
 +	switch (ret) {
 +	case LZMA_STREAM_END: /* Found end of stream. */
 +	case LZMA_OK: /* Decompressor made some progress. */
 +		break;
 +	case LZMA_MEM_ERROR:
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Lzma library error: Cannot allocate memory");
 +		break;
 +	case LZMA_MEMLIMIT_ERROR:
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Lzma library error: Out of memory");
 +		break;
 +	case LZMA_FORMAT_ERROR:
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_MISC,
 +		    "Lzma library error: format not recognized");
 +		break;
 +	case LZMA_OPTIONS_ERROR:
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_MISC,
 +		    "Lzma library error: Invalid options");
 +		break;
 +	case LZMA_DATA_ERROR:
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_MISC,
 +		    "Lzma library error: Corrupted input data");
 +		break;
 +	case LZMA_BUF_ERROR:
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_MISC,
 +		    "Lzma library error:  No progress is possible");
 +		break;
 +	default:
 +		/* Return an error. */
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_MISC,
 +		    "Lzma decompression failed:  Unknown error");
 +		break;
 +	}
 +}
 +
 +#endif
 +
 +static unsigned long
 +decode_codec_id(const unsigned char *codecId, size_t id_size)
 +{
 +	unsigned i;
 +	unsigned long id = 0;
 +
 +	for (i = 0; i < id_size; i++) {
 +		id <<= 8;
 +		id += codecId[i];
 +	}
 +	return (id);
 +}
 +
 +static 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 Byte
 +ppmd_read(void *p)
 +{
 +	struct archive_read *a = ((IByteIn*)p)->a;
 +	struct _7zip *zip = (struct _7zip *)(a->format->data);
 +	Byte b;
 +
 +	if (zip->ppstream.avail_in == 0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated RAR file data");
 +		zip->ppstream.overconsumed = 1;
 +		return (0);
 +	}
 +	b = *zip->ppstream.next_in++;
 +	zip->ppstream.avail_in--;
 +	zip->ppstream.total_in++;
 +	return (b);
 +}
 +
 +static ISzAlloc g_szalloc = { ppmd_alloc, ppmd_free };
 +
 +static int
 +init_decompression(struct archive_read *a, struct _7zip *zip,
 +    const struct _7z_coder *coder1, const struct _7z_coder *coder2)
 +{
 +	int r;
 +
 +	zip->codec = coder1->codec;
 +	zip->codec2 = -1;
 +
 +	switch (zip->codec) {
 +	case _7Z_COPY:
 +	case _7Z_BZ2:
 +	case _7Z_DEFLATE:
 +	case _7Z_PPMD:
 +		if (coder2 != NULL) {
 +			if (coder2->codec != _7Z_X86 &&
 +			    coder2->codec != _7Z_X86_BCJ2) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Unsupported filter %lx for %lx",
 +				    coder2->codec, coder1->codec);
 +				return (ARCHIVE_FAILED);
 +			}
 +			zip->codec2 = coder2->codec;
 +			zip->bcj_state = 0;
 +			if (coder2->codec == _7Z_X86)
 +				x86_Init(zip);
 +		}
 +		break;
 +	default:
 +		break;
 +	}
 +
 +	switch (zip->codec) {
 +	case _7Z_COPY:
 +		break;
 +
 +	case _7Z_LZMA: case _7Z_LZMA2:
 +#ifdef HAVE_LZMA_H
 +#if LZMA_VERSION_MAJOR >= 5
 +/* Effectively disable the limiter. */
 +#define LZMA_MEMLIMIT   UINT64_MAX
 +#else
 +/* NOTE: This needs to check memory size which running system has. */
 +#define LZMA_MEMLIMIT   (1U << 30)
 +#endif
 +	{
 +		lzma_options_delta delta_opt;
 +		lzma_filter filters[LZMA_FILTERS_MAX];
 +#if LZMA_VERSION < 50010000
 +		lzma_filter *ff;
 +#endif
 +		int fi = 0;
 +
 +		if (zip->lzstream_valid) {
 +			lzma_end(&(zip->lzstream));
 +			zip->lzstream_valid = 0;
 +		}
 +
 +		/*
 +		 * NOTE: liblzma incompletely handle the BCJ+LZMA compressed
 +		 * data made by 7-Zip because 7-Zip does not add End-Of-
 +		 * Payload Marker(EOPM) at the end of LZMA compressed data,
 +		 * and so liblzma cannot know the end of the compressed data
 +		 * without EOPM. So consequently liblzma will not return last
 +		 * three or four bytes of uncompressed data because
 +		 * LZMA_FILTER_X86 filter does not handle input data if its
 +		 * data size is less than five bytes. If liblzma detect EOPM
 +		 * or know the uncompressed data size, liblzma will flush out
 +		 * the remaining that three or four bytes of uncompressed
 +		 * data. That is why we have to use our converting program
 +		 * for BCJ+LZMA. If we were able to tell the uncompressed
 +		 * size to liblzma when using lzma_raw_decoder() liblzma
 +		 * could correctly deal with BCJ+LZMA. But unfortunately
 +		 * there is no way to do that.
 +		 * Discussion about this can be found at XZ Utils forum.
 +		 */
 +		if (coder2 != NULL) {
 +			zip->codec2 = coder2->codec;
 +
 +			filters[fi].options = NULL;
 +			switch (zip->codec2) {
 +			case _7Z_X86:
 +				if (zip->codec == _7Z_LZMA2) {
 +					filters[fi].id = LZMA_FILTER_X86;
 +					fi++;
 +				} else
 +					/* Use our filter. */
 +					x86_Init(zip);
 +				break;
 +			case _7Z_X86_BCJ2:
 +				/* Use our filter. */
 +				zip->bcj_state = 0;
 +				break;
 +			case _7Z_DELTA:
 +				filters[fi].id = LZMA_FILTER_DELTA;
 +				memset(&delta_opt, 0, sizeof(delta_opt));
 +				delta_opt.type = LZMA_DELTA_TYPE_BYTE;
 +				delta_opt.dist = 1;
 +				filters[fi].options = &delta_opt;
 +				fi++;
 +				break;
 +			/* Following filters have not been tested yet. */
 +			case _7Z_POWERPC:
 +				filters[fi].id = LZMA_FILTER_POWERPC;
 +				fi++;
 +				break;
 +			case _7Z_IA64:
 +				filters[fi].id = LZMA_FILTER_IA64;
 +				fi++;
 +				break;
 +			case _7Z_ARM:
 +				filters[fi].id = LZMA_FILTER_ARM;
 +				fi++;
 +				break;
 +			case _7Z_ARMTHUMB:
 +				filters[fi].id = LZMA_FILTER_ARMTHUMB;
 +				fi++;
 +				break;
 +			case _7Z_SPARC:
 +				filters[fi].id = LZMA_FILTER_SPARC;
 +				fi++;
 +				break;
 +			default:
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Unexpected codec ID: %lX", zip->codec2);
 +				return (ARCHIVE_FAILED);
 +			}
 +		}
 +
 +		if (zip->codec == _7Z_LZMA2)
 +			filters[fi].id = LZMA_FILTER_LZMA2;
 +		else
 +			filters[fi].id = LZMA_FILTER_LZMA1;
 +		filters[fi].options = NULL;
 +#if LZMA_VERSION < 50010000
 +		ff = &filters[fi];
 +#endif
 +		r = lzma_properties_decode(&filters[fi], NULL,
 +		    coder1->properties, (size_t)coder1->propertiesSize);
 +		if (r != LZMA_OK) {
 +			set_error(a, r);
 +			return (ARCHIVE_FAILED);
 +		}
 +		fi++;
 +
 +		filters[fi].id = LZMA_VLI_UNKNOWN;
 +		filters[fi].options = NULL;
 +		r = lzma_raw_decoder(&(zip->lzstream), filters);
 +#if LZMA_VERSION < 50010000
 +		free(ff->options);
 +#endif
 +		if (r != LZMA_OK) {
 +			set_error(a, r);
 +			return (ARCHIVE_FAILED);
 +		}
 +		zip->lzstream_valid = 1;
 +		zip->lzstream.total_in = 0;
 +		zip->lzstream.total_out = 0;
 +		break;
 +	}
 +#else
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "LZMA codec is unsupported");
 +		return (ARCHIVE_FAILED);
 +#endif
 +	case _7Z_BZ2:
 +#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
 +		if (zip->bzstream_valid) {
 +			BZ2_bzDecompressEnd(&(zip->bzstream));
 +			zip->bzstream_valid = 0;
 +		}
 +		r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 0);
 +		if (r == BZ_MEM_ERROR)
 +			r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 1);
 +		if (r != BZ_OK) {
 +			int err = ARCHIVE_ERRNO_MISC;
 +			const char *detail = NULL;
 +			switch (r) {
 +			case BZ_PARAM_ERROR:
 +				detail = "invalid setup parameter";
 +				break;
 +			case BZ_MEM_ERROR:
 +				err = ENOMEM;
 +				detail = "out of memory";
 +				break;
 +			case BZ_CONFIG_ERROR:
 +				detail = "mis-compiled library";
 +				break;
 +			}
 +			archive_set_error(&a->archive, err,
 +			    "Internal error initializing decompressor: %s",
 +			    detail != NULL ? detail : "??");
 +			zip->bzstream_valid = 0;
 +			return (ARCHIVE_FAILED);
 +		}
 +		zip->bzstream_valid = 1;
 +		zip->bzstream.total_in_lo32 = 0;
 +		zip->bzstream.total_in_hi32 = 0;
 +		zip->bzstream.total_out_lo32 = 0;
 +		zip->bzstream.total_out_hi32 = 0;
 +		break;
 +#else
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "BZ2 codec is unsupported");
 +		return (ARCHIVE_FAILED);
 +#endif
 +	case _7Z_DEFLATE:
 +#ifdef HAVE_ZLIB_H
 +		if (zip->stream_valid)
 +			r = inflateReset(&(zip->stream));
 +		else
 +			r = inflateInit2(&(zip->stream),
 +			    -15 /* Don't check for zlib header */);
 +		if (r != Z_OK) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Couldn't initialize zlib stream.");
 +			return (ARCHIVE_FAILED);
 +		}
 +		zip->stream_valid = 1;
 +		zip->stream.total_in = 0;
 +		zip->stream.total_out = 0;
 +		break;
 +#else
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "DEFLATE codec is unsupported");
 +		return (ARCHIVE_FAILED);
 +#endif
 +	case _7Z_PPMD:
 +	{
 +		unsigned order;
 +		uint32_t msize;
 +
 +		if (zip->ppmd7_valid) {
 +			__archive_ppmd7_functions.Ppmd7_Free(
 +			    &zip->ppmd7_context, &g_szalloc);
 +			zip->ppmd7_valid = 0;
 +		}
 +
 +		if (coder1->propertiesSize < 5) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Malformed PPMd parameter");
 +			return (ARCHIVE_FAILED);
 +		}
 +		order = coder1->properties[0];
 +		msize = archive_le32dec(&(coder1->properties[1]));
 +		if (order < PPMD7_MIN_ORDER || order > PPMD7_MAX_ORDER ||
 +		    msize < PPMD7_MIN_MEM_SIZE || msize > PPMD7_MAX_MEM_SIZE) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Malformed PPMd parameter");
 +			return (ARCHIVE_FAILED);
 +		}
 +		__archive_ppmd7_functions.Ppmd7_Construct(&zip->ppmd7_context);
 +		r = __archive_ppmd7_functions.Ppmd7_Alloc(
 +			&zip->ppmd7_context, msize, &g_szalloc);
 +		if (r == 0) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Coludn't allocate memory for PPMd");
 +			return (ARCHIVE_FATAL);
 +		}
 +		__archive_ppmd7_functions.Ppmd7_Init(
 +			&zip->ppmd7_context, order);
 +		__archive_ppmd7_functions.Ppmd7z_RangeDec_CreateVTable(
 +			&zip->range_dec);
 +		zip->ppmd7_valid = 1;
 +		zip->ppmd7_stat = 0;
 +		zip->ppstream.overconsumed = 0;
 +		zip->ppstream.total_in = 0;
 +		zip->ppstream.total_out = 0;
 +		break;
 +	}
 +	case _7Z_X86:
 +	case _7Z_X86_BCJ2:
 +	case _7Z_POWERPC:
 +	case _7Z_IA64:
 +	case _7Z_ARM:
 +	case _7Z_ARMTHUMB:
 +	case _7Z_SPARC:
 +	case _7Z_DELTA:
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Unexpected codec ID: %lX", zip->codec);
 +		return (ARCHIVE_FAILED);
 +	case _7Z_CRYPTO_MAIN_ZIP:
 +	case _7Z_CRYPTO_RAR_29:
 +	case _7Z_CRYPTO_AES_256_SHA_256:
 +		if (a->entry) {
 +			archive_entry_set_is_metadata_encrypted(a->entry, 1);
 +			archive_entry_set_is_data_encrypted(a->entry, 1);
 +			zip->has_encrypted_entries = 1;
 +		}
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Crypto codec not supported yet (ID: 0x%lX)", zip->codec);
 +		return (ARCHIVE_FAILED);
 +	default:
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Unknown codec ID: %lX", zip->codec);
 +		return (ARCHIVE_FAILED);
 +	}
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +decompress(struct archive_read *a, struct _7zip *zip,
 +    void *buff, size_t *outbytes, const void *b, size_t *used)
 +{
 +	const uint8_t *t_next_in;
 +	uint8_t *t_next_out;
 +	size_t o_avail_in, o_avail_out;
 +	size_t t_avail_in, t_avail_out;
 +	uint8_t *bcj2_next_out;
 +	size_t bcj2_avail_out;
 +	int r, ret = ARCHIVE_OK;
 +
 +	t_avail_in = o_avail_in = *used;
 +	t_avail_out = o_avail_out = *outbytes;
 +	t_next_in = b;
 +	t_next_out = buff;
 +
 +	if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) {
 +		int i;
 +
 +		/* Do not copy out the BCJ remaining bytes when the output
 +		 * buffer size is less than five bytes. */
 +		if (o_avail_in != 0 && t_avail_out < 5 && zip->odd_bcj_size) {
 +			*used = 0;
 +			*outbytes = 0;
 +			return (ret);
 +		}
 +		for (i = 0; zip->odd_bcj_size > 0 && t_avail_out; i++) {
 +			*t_next_out++ = zip->odd_bcj[i];
 +			t_avail_out--;
 +			zip->odd_bcj_size--;
 +		}
 +		if (o_avail_in == 0 || t_avail_out == 0) {
 +			*used = o_avail_in - t_avail_in;
 +			*outbytes = o_avail_out - t_avail_out;
 +			if (o_avail_in == 0)
 +				ret = ARCHIVE_EOF;
 +			return (ret);
 +		}
 +	}
 +
 +	bcj2_next_out = t_next_out;
 +	bcj2_avail_out = t_avail_out;
 +	if (zip->codec2 == _7Z_X86_BCJ2) {
 +		/*
 +		 * Decord a remaining decompressed main stream for BCJ2.
 +		 */
 +		if (zip->tmp_stream_bytes_remaining) {
 +			ssize_t bytes;
 +			size_t remaining = zip->tmp_stream_bytes_remaining;
 +			bytes = Bcj2_Decode(zip, t_next_out, t_avail_out);
 +			if (bytes < 0) {
 +				archive_set_error(&(a->archive),
 +				    ARCHIVE_ERRNO_MISC,
 +				    "BCJ2 conversion Failed");
 +				return (ARCHIVE_FAILED);
 +			}
 +			zip->main_stream_bytes_remaining -=
 +			    remaining - zip->tmp_stream_bytes_remaining;
 +			t_avail_out -= bytes;
 +			if (o_avail_in == 0 || t_avail_out == 0) {
 +				*used = 0;
 +				*outbytes = o_avail_out - t_avail_out;
 +				if (o_avail_in == 0 &&
 +				    zip->tmp_stream_bytes_remaining)
 +					ret = ARCHIVE_EOF;
 +				return (ret);
 +			}
 +			t_next_out += bytes;
 +			bcj2_next_out = t_next_out;
 +			bcj2_avail_out = t_avail_out;
 +		}
 +		t_next_out = zip->tmp_stream_buff;
 +		t_avail_out = zip->tmp_stream_buff_size;
 +	}
 +
 +	switch (zip->codec) {
 +	case _7Z_COPY:
 +	{
 +		size_t bytes =
 +		    (t_avail_in > t_avail_out)?t_avail_out:t_avail_in;
 +
 +		memcpy(t_next_out, t_next_in, bytes);
 +		t_avail_in -= bytes;
 +		t_avail_out -= bytes;
 +		if (o_avail_in == 0)
 +			ret = ARCHIVE_EOF;
 +		break;
 +	}
 +#ifdef HAVE_LZMA_H
 +	case _7Z_LZMA: case _7Z_LZMA2:
 +		zip->lzstream.next_in = t_next_in;
 +		zip->lzstream.avail_in = t_avail_in;
 +		zip->lzstream.next_out = t_next_out;
 +		zip->lzstream.avail_out = t_avail_out;
 +
 +		r = lzma_code(&(zip->lzstream), LZMA_RUN);
 +		switch (r) {
 +		case LZMA_STREAM_END: /* Found end of stream. */
 +			lzma_end(&(zip->lzstream));
 +			zip->lzstream_valid = 0;
 +			ret = ARCHIVE_EOF;
 +			break;
 +		case LZMA_OK: /* Decompressor made some progress. */
 +			break;
 +		default:
 +			archive_set_error(&(a->archive),
 +			    ARCHIVE_ERRNO_MISC,
 +				"Decompression failed(%d)",
 +			    r);
 +			return (ARCHIVE_FAILED);
 +		}
 +		t_avail_in = zip->lzstream.avail_in;
 +		t_avail_out = zip->lzstream.avail_out;
 +		break;
 +#endif
 +#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
 +	case _7Z_BZ2:
 +		zip->bzstream.next_in = (char *)(uintptr_t)t_next_in;
 +		zip->bzstream.avail_in = t_avail_in;
 +		zip->bzstream.next_out = (char *)(uintptr_t)t_next_out;
 +		zip->bzstream.avail_out = t_avail_out;
 +		r = BZ2_bzDecompress(&(zip->bzstream));
 +		switch (r) {
 +		case BZ_STREAM_END: /* Found end of stream. */
 +			switch (BZ2_bzDecompressEnd(&(zip->bzstream))) {
 +			case BZ_OK:
 +				break;
 +			default:
 +				archive_set_error(&(a->archive),
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Failed to clean up decompressor");
 +				return (ARCHIVE_FAILED);
 +			}
 +			zip->bzstream_valid = 0;
 +			ret = ARCHIVE_EOF;
 +			break;
 +		case BZ_OK: /* Decompressor made some progress. */
 +			break;
 +		default:
 +			archive_set_error(&(a->archive),
 +			    ARCHIVE_ERRNO_MISC,
 +			    "bzip decompression failed");
 +			return (ARCHIVE_FAILED);
 +		}
 +		t_avail_in = zip->bzstream.avail_in;
 +		t_avail_out = zip->bzstream.avail_out;
 +		break;
 +#endif
 +#ifdef HAVE_ZLIB_H
 +	case _7Z_DEFLATE:
 +		zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in;
 +		zip->stream.avail_in = (uInt)t_avail_in;
 +		zip->stream.next_out = t_next_out;
 +		zip->stream.avail_out = (uInt)t_avail_out;
 +		r = inflate(&(zip->stream), 0);
 +		switch (r) {
 +		case Z_STREAM_END: /* Found end of stream. */
 +			ret = ARCHIVE_EOF;
 +			break;
 +		case Z_OK: /* Decompressor made some progress.*/
 +			break;
 +		default:
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "File decompression failed (%d)", r);
 +			return (ARCHIVE_FAILED);
 +		}
 +		t_avail_in = zip->stream.avail_in;
 +		t_avail_out = zip->stream.avail_out;
 +		break;
 +#endif
 +	case _7Z_PPMD:
 +	{
 +		uint64_t flush_bytes;
 +
 +		if (!zip->ppmd7_valid || zip->ppmd7_stat < 0 ||
 +		    t_avail_out <= 0) {
 +			archive_set_error(&(a->archive),
 +			    ARCHIVE_ERRNO_MISC,
 +			    "Decompression internal error");
 +			return (ARCHIVE_FAILED);
 +		}
 +		zip->ppstream.next_in = t_next_in;
 +		zip->ppstream.avail_in = t_avail_in;
 +		zip->ppstream.next_out = t_next_out;
 +		zip->ppstream.avail_out = t_avail_out;
 +		if (zip->ppmd7_stat == 0) {
 +			zip->bytein.a = a;
 +			zip->bytein.Read = &ppmd_read;
 +			zip->range_dec.Stream = &zip->bytein;
 +			r = __archive_ppmd7_functions.Ppmd7z_RangeDec_Init(
 +				&(zip->range_dec));
 +			if (r == 0) {
 +				zip->ppmd7_stat = -1;
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Failed to initialize PPMd range decorder");
 +				return (ARCHIVE_FAILED);
 +			}
 +			if (zip->ppstream.overconsumed) {
 +				zip->ppmd7_stat = -1;
 +				return (ARCHIVE_FAILED);
 +			}
 +			zip->ppmd7_stat = 1;
 +		}
 +
 +		if (t_avail_in == 0)
 +			/* XXX Flush out remaining decoded data XXX */
 +			flush_bytes = zip->folder_outbytes_remaining;
 +		else
 +			flush_bytes = 0;
 +
 +		do {
 +			int sym;
 +
 +			sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
 +				&(zip->ppmd7_context), &(zip->range_dec.p));
 +			if (sym < 0) {
 +				zip->ppmd7_stat = -1;
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_FILE_FORMAT,
 +				    "Failed to decode PPMd");
 +				return (ARCHIVE_FAILED);
 +			}
 +			if (zip->ppstream.overconsumed) {
 +				zip->ppmd7_stat = -1;
 +				return (ARCHIVE_FAILED);
 +			}
 +			*zip->ppstream.next_out++ = (unsigned char)sym;
 +			zip->ppstream.avail_out--;
 +			zip->ppstream.total_out++;
 +			if (flush_bytes)
 +				flush_bytes--;
 +		} while (zip->ppstream.avail_out &&
 +			(zip->ppstream.avail_in || flush_bytes));
 +
 +		t_avail_in = (size_t)zip->ppstream.avail_in;
 +		t_avail_out = (size_t)zip->ppstream.avail_out;
 +		break;
 +	}
 +	default:
 +		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
 +		    "Decompression internal error");
 +		return (ARCHIVE_FAILED);
 +	}
 +	if (ret != ARCHIVE_OK && ret != ARCHIVE_EOF)
 +		return (ret);
 +
 +	*used = o_avail_in - t_avail_in;
 +	*outbytes = o_avail_out - t_avail_out;
 +
 +	/*
 +	 * Decord BCJ.
 +	 */
 +	if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) {
 +		size_t l = x86_Convert(zip, buff, *outbytes);
 +		zip->odd_bcj_size = *outbytes - l;
 +		if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 &&
 +		    o_avail_in && ret != ARCHIVE_EOF) {
 +			memcpy(zip->odd_bcj, ((unsigned char *)buff) + l,
 +			    zip->odd_bcj_size);
 +			*outbytes = l;
 +		} else
 +			zip->odd_bcj_size = 0;
 +	}
 +
 +	/*
 +	 * Decord BCJ2 with a decompressed main stream.
 +	 */
 +	if (zip->codec2 == _7Z_X86_BCJ2) {
 +		ssize_t bytes;
 +
 +		zip->tmp_stream_bytes_avail =
 +		    zip->tmp_stream_buff_size - t_avail_out;
 +		if (zip->tmp_stream_bytes_avail >
 +		      zip->main_stream_bytes_remaining)
 +			zip->tmp_stream_bytes_avail =
 +			    zip->main_stream_bytes_remaining;
 +		zip->tmp_stream_bytes_remaining = zip->tmp_stream_bytes_avail;
 +		bytes = Bcj2_Decode(zip, bcj2_next_out, bcj2_avail_out);
 +		if (bytes < 0) {
 +			archive_set_error(&(a->archive),
 +			    ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed");
 +			return (ARCHIVE_FAILED);
 +		}
 +		zip->main_stream_bytes_remaining -=
 +		    zip->tmp_stream_bytes_avail
 +		      - zip->tmp_stream_bytes_remaining;
 +		bcj2_avail_out -= bytes;
 +		*outbytes = o_avail_out - bcj2_avail_out;
 +	}
 +
 +	return (ret);
 +}
 +
 +static int
 +free_decompression(struct archive_read *a, struct _7zip *zip)
 +{
 +	int r = ARCHIVE_OK;
 +
 +#if !defined(HAVE_ZLIB_H) &&\
 +	!(defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR))
 +	(void)a;/* UNUSED */
 +#endif
 +#ifdef HAVE_LZMA_H
 +	if (zip->lzstream_valid)
 +		lzma_end(&(zip->lzstream));
 +#endif
 +#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
 +	if (zip->bzstream_valid) {
 +		if (BZ2_bzDecompressEnd(&(zip->bzstream)) != BZ_OK) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_MISC,
 +			    "Failed to clean up bzip2 decompressor");
 +			r = ARCHIVE_FATAL;
 +		}
 +		zip->bzstream_valid = 0;
 +	}
 +#endif
 +#ifdef HAVE_ZLIB_H
 +	if (zip->stream_valid) {
 +		if (inflateEnd(&(zip->stream)) != Z_OK) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_MISC,
 +			    "Failed to clean up zlib decompressor");
 +			r = ARCHIVE_FATAL;
 +		}
 +		zip->stream_valid = 0;
 +	}
 +#endif
 +	if (zip->ppmd7_valid) {
 +		__archive_ppmd7_functions.Ppmd7_Free(
 +			&zip->ppmd7_context, &g_szalloc);
 +		zip->ppmd7_valid = 0;
 +	}
 +	return (r);
 +}
 +
 +static int
 +parse_7zip_uint64(struct archive_read *a, uint64_t *val)
 +{
 +	const unsigned char *p;
 +	unsigned char avail, mask;
 +	int i;
 +
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		return (-1);
 +	avail = *p;
 +	mask = 0x80;
 +	*val = 0;
 +	for (i = 0; i < 8; i++) {
 +		if (avail & mask) {
 +			if ((p = header_bytes(a, 1)) == NULL)
 +				return (-1);
 +			*val |= ((uint64_t)*p) << (8 * i);
 +			mask >>= 1;
 +			continue;
 +		}
 +		*val += ((uint64_t)(avail & (mask -1))) << (8 * i);
 +		break;
 +	}
 +	return (0);
 +}
 +
 +static int
 +read_Bools(struct archive_read *a, unsigned char *data, size_t num)
 +{
 +	const unsigned char *p;
 +	unsigned i, mask = 0, avail = 0;
 +
 +	for (i = 0; i < num; i++) {
 +		if (mask == 0) {
 +			if ((p = header_bytes(a, 1)) == NULL)
 +				return (-1);
 +			avail = *p;
 +			mask = 0x80;
 +		}
 +		data[i] = (avail & mask)?1:0;
 +		mask >>= 1;
 +	}
 +	return (0);
 +}
 +
 +static void
 +free_Digest(struct _7z_digests *d)
 +{
 +	free(d->defineds);
 +	free(d->digests);
 +}
 +
 +static int
 +read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num)
 +{
 +	const unsigned char *p;
 +	unsigned i;
 +
 +	if (num == 0)
 +		return (-1);
 +	memset(d, 0, sizeof(*d));
 +
 +	d->defineds = malloc(num);
 +	if (d->defineds == NULL)
 +		return (-1);
 +	/*
 +	 * Read Bools.
 +	 */
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		return (-1);
 +	if (*p == 0) {
 +		if (read_Bools(a, d->defineds, num) < 0)
 +			return (-1);
 +	} else
 +		/* All are defined */
 +		memset(d->defineds, 1, num);
 +
 +	d->digests = calloc(num, sizeof(*d->digests));
 +	if (d->digests == NULL)
 +		return (-1);
 +	for (i = 0; i < num; i++) {
 +		if (d->defineds[i]) {
 +			if ((p = header_bytes(a, 4)) == NULL)
 +				return (-1);
 +			d->digests[i] = archive_le32dec(p);
 +		}
 +	}
 +
 +	return (0);
 +}
 +
 +static void
 +free_PackInfo(struct _7z_pack_info *pi)
 +{
 +	free(pi->sizes);
 +	free(pi->positions);
 +	free_Digest(&(pi->digest));
 +}
 +
 +static int
 +read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
 +{
 +	const unsigned char *p;
 +	unsigned i;
 +
 +	memset(pi, 0, sizeof(*pi));
 +
 +	/*
 +	 * Read PackPos.
 +	 */
 +	if (parse_7zip_uint64(a, &(pi->pos)) < 0)
 +		return (-1);
 +
 +	/*
 +	 * Read NumPackStreams.
 +	 */
 +	if (parse_7zip_uint64(a, &(pi->numPackStreams)) < 0)
 +		return (-1);
 +	if (pi->numPackStreams == 0)
 +		return (-1);
 +	if (UMAX_ENTRY < pi->numPackStreams)
 +		return (-1);
 +
 +	/*
 +	 * Read PackSizes[num]
 +	 */
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		return (-1);
 +	if (*p == kEnd)
 +		/* PackSizes[num] are not present. */
 +		return (0);
 +	if (*p != kSize)
 +		return (-1);
 +	pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
 +	pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
 +	if (pi->sizes == NULL || pi->positions == NULL)
 +		return (-1);
 +
 +	for (i = 0; i < pi->numPackStreams; i++) {
 +		if (parse_7zip_uint64(a, &(pi->sizes[i])) < 0)
 +			return (-1);
 +	}
 +
 +	/*
 +	 * Read PackStreamDigests[num]
 +	 */
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		return (-1);
 +	if (*p == kEnd) {
 +		/* PackStreamDigests[num] are not present. */
 +		pi->digest.defineds =
 +		    calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds));
 +		pi->digest.digests =
 +		    calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests));
 +		if (pi->digest.defineds == NULL || pi->digest.digests == NULL)
 +			return (-1);
 +		return (0);
 +	}
 +
 +	if (*p != kSize)
 +		return (-1);
 +
 +	if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0)
 +		return (-1);
 +
 +	/*
 +	 *  Must be marked by kEnd.
 +	 */
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		return (-1);
 +	if (*p != kEnd)
 +		return (-1);
 +	return (0);
 +}
 +
 +static void
 +free_Folder(struct _7z_folder *f)
 +{
 +	unsigned i;
 +
 +	if (f->coders) {
 +		for (i = 0; i< f->numCoders; i++) {
 +			free(f->coders[i].properties);
 +		}
 +		free(f->coders);
 +	}
 +	free(f->bindPairs);
 +	free(f->packedStreams);
 +	free(f->unPackSize);
 +}
 +
 +static int
 +read_Folder(struct archive_read *a, struct _7z_folder *f)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	const unsigned char *p;
 +	uint64_t numInStreamsTotal = 0;
 +	uint64_t numOutStreamsTotal = 0;
 +	unsigned i;
 +
 +	memset(f, 0, sizeof(*f));
 +
 +	/*
 +	 * Read NumCoders.
 +	 */
 +	if (parse_7zip_uint64(a, &(f->numCoders)) < 0)
 +		return (-1);
 +	if (f->numCoders > 4)
 +		/* Too many coders. */
 +		return (-1);
 +
 +	f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders));
 +	if (f->coders == NULL)
 +		return (-1);
 +	for (i = 0; i< f->numCoders; i++) {
 +		size_t codec_size;
 +		int simple, attr;
 +
 +		if ((p = header_bytes(a, 1)) == NULL)
 +			return (-1);
 +		/*
 +		 * 0:3 CodecIdSize
 +		 * 4:  0 - IsSimple
 +		 *     1 - Is not Simple
 +		 * 5:  0 - No Attributes
 +		 *     1 - There are Attributes;
 +		 * 7:  Must be zero.
 +		 */
 +		codec_size = *p & 0xf;
 +		simple = (*p & 0x10)?0:1;
 +		attr = *p & 0x20;
 +		if (*p & 0x80)
 +			return (-1);/* Not supported. */
 +
 +		/*
 +		 * Read Decompression Method IDs.
 +		 */
 +		if ((p = header_bytes(a, codec_size)) == NULL)
 +			return (-1);
 +
 +		f->coders[i].codec = decode_codec_id(p, codec_size);
 +
 +		if (simple) {
 +			f->coders[i].numInStreams = 1;
 +			f->coders[i].numOutStreams = 1;
 +		} else {
 +			if (parse_7zip_uint64(
 +			    a, &(f->coders[i].numInStreams)) < 0)
 +				return (-1);
 +			if (UMAX_ENTRY < f->coders[i].numInStreams)
 +				return (-1);
 +			if (parse_7zip_uint64(
 +			    a, &(f->coders[i].numOutStreams)) < 0)
 +				return (-1);
 +			if (UMAX_ENTRY < f->coders[i].numOutStreams)
 +				return (-1);
 +		}
 +
 +		if (attr) {
 +			if (parse_7zip_uint64(
 +			    a, &(f->coders[i].propertiesSize)) < 0)
 +				return (-1);
 +			if ((p = header_bytes(
 +			    a, (size_t)f->coders[i].propertiesSize)) == NULL)
 +				return (-1);
 +			f->coders[i].properties =
 +			    malloc((size_t)f->coders[i].propertiesSize);
 +			if (f->coders[i].properties == NULL)
 +				return (-1);
 +			memcpy(f->coders[i].properties, p,
 +			    (size_t)f->coders[i].propertiesSize);
 +		}
 +
 +		numInStreamsTotal += f->coders[i].numInStreams;
 +		numOutStreamsTotal += f->coders[i].numOutStreams;
 +	}
 +
 +	if (numOutStreamsTotal == 0 ||
 +	    numInStreamsTotal < numOutStreamsTotal-1)
 +		return (-1);
 +
 +	f->numBindPairs = numOutStreamsTotal - 1;
 +	if (zip->header_bytes_remaining < f->numBindPairs)
 +			return (-1);
 +	if (f->numBindPairs > 0) {
 +		f->bindPairs =
 +			calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs));
 +		if (f->bindPairs == NULL)
 +			return (-1);
 +	} else
 +		f->bindPairs = NULL;
 +	for (i = 0; i < f->numBindPairs; i++) {
 +		if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0)
 +			return (-1);
 +		if (UMAX_ENTRY < f->bindPairs[i].inIndex)
 +			return (-1);
 +		if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0)
 +			return (-1);
 +		if (UMAX_ENTRY < f->bindPairs[i].outIndex)
 +			return (-1);
 +	}
 +
 +	f->numPackedStreams = numInStreamsTotal - f->numBindPairs;
 +	f->packedStreams =
 +	    calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams));
 +	if (f->packedStreams == NULL)
 +		return (-1);
 +	if (f->numPackedStreams == 1) {
 +		for (i = 0; i < numInStreamsTotal; i++) {
 +			unsigned j;
 +			for (j = 0; j < f->numBindPairs; j++) {
 +				if (f->bindPairs[j].inIndex == i)
 +					break;
 +			}
 +			if (j == f->numBindPairs)
 +				break;
 +		}
 +		if (i == numInStreamsTotal)
 +			return (-1);
 +		f->packedStreams[0] = i;
 +	} else {
 +		for (i = 0; i < f->numPackedStreams; i++) {
 +			if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0)
 +				return (-1);
 +			if (UMAX_ENTRY < f->packedStreams[i])
 +				return (-1);
 +		}
 +	}
 +	f->numInStreams = numInStreamsTotal;
 +	f->numOutStreams = numOutStreamsTotal;
 +
 +	return (0);
 +}
 +
 +static void
 +free_CodersInfo(struct _7z_coders_info *ci)
 +{
 +	unsigned i;
 +
 +	if (ci->folders) {
 +		for (i = 0; i < ci->numFolders; i++)
 +			free_Folder(&(ci->folders[i]));
 +		free(ci->folders);
 +	}
 +}
 +
 +static int
 +read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
 +{
 +	const unsigned char *p;
 +	struct _7z_digests digest;
 +	unsigned i;
 +
 +	memset(ci, 0, sizeof(*ci));
 +	memset(&digest, 0, sizeof(digest));
 +
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		goto failed;
 +	if (*p != kFolder)
 +		goto failed;
 +
 +	/*
 +	 * Read NumFolders.
 +	 */
 +	if (parse_7zip_uint64(a, &(ci->numFolders)) < 0)
 +		goto failed;
 +	if (UMAX_ENTRY < ci->numFolders)
 +		return (-1);
 +
 +	/*
 +	 * Read External.
 +	 */
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		goto failed;
 +	switch (*p) {
 +	case 0:
 +		ci->folders =
 +			calloc((size_t)ci->numFolders, sizeof(*ci->folders));
 +		if (ci->folders == NULL)
 +			return (-1);
 +		for (i = 0; i < ci->numFolders; i++) {
 +			if (read_Folder(a, &(ci->folders[i])) < 0)
 +				goto failed;
 +		}
 +		break;
 +	case 1:
 +		if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0)
 +			return (-1);
 +		if (UMAX_ENTRY < ci->dataStreamIndex)
 +			return (-1);
 +		if (ci->numFolders > 0) {
 +			archive_set_error(&a->archive, -1,
 +			    "Malformed 7-Zip archive");
 +			goto failed;
 +		}
 +		break;
 +	default:
 +		archive_set_error(&a->archive, -1,
 +		    "Malformed 7-Zip archive");
 +		goto failed;
 +	}
 +
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		goto failed;
 +	if (*p != kCodersUnPackSize)
 +		goto failed;
 +
 +	for (i = 0; i < ci->numFolders; i++) {
 +		struct _7z_folder *folder = &(ci->folders[i]);
 +		unsigned j;
 +
 +		folder->unPackSize =
 +		    calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize));
 +		if (folder->unPackSize == NULL)
 +			goto failed;
 +		for (j = 0; j < folder->numOutStreams; j++) {
 +			if (parse_7zip_uint64(a, &(folder->unPackSize[j])) < 0)
 +				goto failed;
 +		}
 +	}
 +
 +	/*
 +	 * Read CRCs.
 +	 */
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		goto failed;
 +	if (*p == kEnd)
 +		return (0);
 +	if (*p != kCRC)
 +		goto failed;
 +	if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0)
 +		goto failed;
 +	for (i = 0; i < ci->numFolders; i++) {
 +		ci->folders[i].digest_defined = digest.defineds[i];
 +		ci->folders[i].digest = digest.digests[i];
 +	}
 +
 +	/*
 +	 *  Must be kEnd.
 +	 */
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		goto failed;
 +	if (*p != kEnd)
 +		goto failed;
 +	free_Digest(&digest);
 +	return (0);
 +failed:
 +	free_Digest(&digest);
 +	return (-1);
 +}
 +
 +static uint64_t
 +folder_uncompressed_size(struct _7z_folder *f)
 +{
 +	int n = (int)f->numOutStreams;
 +	unsigned pairs = (unsigned)f->numBindPairs;
 +
 +	while (--n >= 0) {
 +		unsigned i;
 +		for (i = 0; i < pairs; i++) {
 +			if (f->bindPairs[i].outIndex == (uint64_t)n)
 +				break;
 +		}
 +		if (i >= pairs)
 +			return (f->unPackSize[n]);
 +	}
 +	return (0);
 +}
 +
 +static void
 +free_SubStreamsInfo(struct _7z_substream_info *ss)
 +{
 +	free(ss->unpackSizes);
 +	free(ss->digestsDefined);
 +	free(ss->digests);
 +}
 +
 +static int
 +read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
 +    struct _7z_folder *f, size_t numFolders)
 +{
 +	const unsigned char *p;
 +	uint64_t *usizes;
 +	size_t unpack_streams;
 +	int type;
 +	unsigned i;
 +	uint32_t numDigests;
 +
 +	memset(ss, 0, sizeof(*ss));
 +
 +	for (i = 0; i < numFolders; i++)
 +		f[i].numUnpackStreams = 1;
 +
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		return (-1);
 +	type = *p;
 +
 +	if (type == kNumUnPackStream) {
 +		unpack_streams = 0;
 +		for (i = 0; i < numFolders; i++) {
 +			if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0)
 +				return (-1);
 +			if (UMAX_ENTRY < f[i].numUnpackStreams)
 +				return (-1);
++			if (unpack_streams > SIZE_MAX - UMAX_ENTRY) {
++				return (-1);
++			}
 +			unpack_streams += (size_t)f[i].numUnpackStreams;
 +		}
 +		if ((p = header_bytes(a, 1)) == NULL)
 +			return (-1);
 +		type = *p;
 +	} else
 +		unpack_streams = numFolders;
 +
 +	ss->unpack_streams = unpack_streams;
 +	if (unpack_streams) {
 +		ss->unpackSizes = calloc(unpack_streams,
 +		    sizeof(*ss->unpackSizes));
 +		ss->digestsDefined = calloc(unpack_streams,
 +		    sizeof(*ss->digestsDefined));
 +		ss->digests = calloc(unpack_streams,
 +		    sizeof(*ss->digests));
 +		if (ss->unpackSizes == NULL || ss->digestsDefined == NULL ||
 +		    ss->digests == NULL)
 +			return (-1);
 +	}
 +
 +	usizes = ss->unpackSizes;
 +	for (i = 0; i < numFolders; i++) {
 +		unsigned pack;
 +		uint64_t sum;
 +
 +		if (f[i].numUnpackStreams == 0)
 +			continue;
 +
 +		sum = 0;
 +		if (type == kSize) {
 +			for (pack = 1; pack < f[i].numUnpackStreams; pack++) {
 +				if (parse_7zip_uint64(a, usizes) < 0)
 +					return (-1);
 +				sum += *usizes++;
 +			}
 +		}
 +		*usizes++ = folder_uncompressed_size(&f[i]) - sum;
 +	}
 +
 +	if (type == kSize) {
 +		if ((p = header_bytes(a, 1)) == NULL)
 +			return (-1);
 +		type = *p;
 +	}
 +
 +	for (i = 0; i < unpack_streams; i++) {
 +		ss->digestsDefined[i] = 0;
 +		ss->digests[i] = 0;
 +	}
 +
 +	numDigests = 0;
 +	for (i = 0; i < numFolders; i++) {
 +		if (f[i].numUnpackStreams != 1 || !f[i].digest_defined)
 +			numDigests += (uint32_t)f[i].numUnpackStreams;
 +	}
 +
 +	if (type == kCRC) {
 +		struct _7z_digests tmpDigests;
 +		unsigned char *digestsDefined = ss->digestsDefined;
 +		uint32_t * digests = ss->digests;
 +		int di = 0;
 +
 +		memset(&tmpDigests, 0, sizeof(tmpDigests));
 +		if (read_Digests(a, &(tmpDigests), numDigests) < 0) {
 +			free_Digest(&tmpDigests);
 +			return (-1);
 +		}
 +		for (i = 0; i < numFolders; i++) {
 +			if (f[i].numUnpackStreams == 1 && f[i].digest_defined) {
 +				*digestsDefined++ = 1;
 +				*digests++ = f[i].digest;
 +			} else {
 +				unsigned j;
 +
 +				for (j = 0; j < f[i].numUnpackStreams;
 +				    j++, di++) {
 +					*digestsDefined++ =
 +					    tmpDigests.defineds[di];
 +					*digests++ =
 +					    tmpDigests.digests[di];
 +				}
 +			}
 +		}
 +		free_Digest(&tmpDigests);
 +		if ((p = header_bytes(a, 1)) == NULL)
 +			return (-1);
 +		type = *p;
 +	}
 +
 +	/*
 +	 *  Must be kEnd.
 +	 */
 +	if (type != kEnd)
 +		return (-1);
 +	return (0);
 +}
 +
 +static void
 +free_StreamsInfo(struct _7z_stream_info *si)
 +{
 +	free_PackInfo(&(si->pi));
 +	free_CodersInfo(&(si->ci));
 +	free_SubStreamsInfo(&(si->ss));
 +}
 +
 +static int
 +read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	const unsigned char *p;
 +	unsigned i;
 +
 +	memset(si, 0, sizeof(*si));
 +
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		return (-1);
 +	if (*p == kPackInfo) {
 +		uint64_t packPos;
 +
 +		if (read_PackInfo(a, &(si->pi)) < 0)
 +			return (-1);
 +
 +		if (si->pi.positions == NULL || si->pi.sizes == NULL)
 +			return (-1);
 +		/*
 +		 * Calculate packed stream positions.
 +		 */
 +		packPos = si->pi.pos;
 +		for (i = 0; i < si->pi.numPackStreams; i++) {
 +			si->pi.positions[i] = packPos;
 +			packPos += si->pi.sizes[i];
 +			if (packPos > zip->header_offset)
 +				return (-1);
 +		}
 +		if ((p = header_bytes(a, 1)) == NULL)
 +			return (-1);
 +	}
 +	if (*p == kUnPackInfo) {
 +		uint32_t packIndex;
 +		struct _7z_folder *f;
 +
 +		if (read_CodersInfo(a, &(si->ci)) < 0)
 +			return (-1);
 +
 +		/*
 +		 * Calculate packed stream indexes.
 +		 */
 +		packIndex = 0;
 +		f = si->ci.folders;
 +		for (i = 0; i < si->ci.numFolders; i++) {
 +			f[i].packIndex = packIndex;
 +			packIndex += (uint32_t)f[i].numPackedStreams;
 +			if (packIndex > si->pi.numPackStreams)
 +				return (-1);
 +		}
 +		if ((p = header_bytes(a, 1)) == NULL)
 +			return (-1);
 +	}
 +
 +	if (*p == kSubStreamsInfo) {
 +		if (read_SubStreamsInfo(a, &(si->ss),
 +		    si->ci.folders, (size_t)si->ci.numFolders) < 0)
 +			return (-1);
 +		if ((p = header_bytes(a, 1)) == NULL)
 +			return (-1);
 +	}
 +
 +	/*
 +	 *  Must be kEnd.
 +	 */
 +	if (*p != kEnd)
 +		return (-1);
 +	return (0);
 +}
 +
 +static void
 +free_Header(struct _7z_header_info *h)
 +{
 +	free(h->emptyStreamBools);
 +	free(h->emptyFileBools);
 +	free(h->antiBools);
 +	free(h->attrBools);
 +}
 +
 +static int
 +read_Header(struct archive_read *a, struct _7z_header_info *h,
 +    int check_header_id)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	const unsigned char *p;
 +	struct _7z_folder *folders;
 +	struct _7z_stream_info *si = &(zip->si);
 +	struct _7zip_entry *entries;
 +	uint32_t folderIndex, indexInFolder;
 +	unsigned i;
 +	int eindex, empty_streams, sindex;
 +
 +	if (check_header_id) {
 +		/*
 +		 * Read Header.
 +		 */
 +		if ((p = header_bytes(a, 1)) == NULL)
 +			return (-1);
 +		if (*p != kHeader)
 +			return (-1);
 +	}
 +
 +	/*
 +	 * Read ArchiveProperties.
 +	 */
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		return (-1);
 +	if (*p == kArchiveProperties) {
 +		for (;;) {
 +			uint64_t size;
 +			if ((p = header_bytes(a, 1)) == NULL)
 +				return (-1);
 +			if (*p == 0)
 +				break;
 +			if (parse_7zip_uint64(a, &size) < 0)
 +				return (-1);
 +		}
 +		if ((p = header_bytes(a, 1)) == NULL)
 +			return (-1);
 +	}
 +
 +	/*
 +	 * Read MainStreamsInfo.
 +	 */
 +	if (*p == kMainStreamsInfo) {
 +		if (read_StreamsInfo(a, &(zip->si)) < 0)
 +			return (-1);
 +		if ((p = header_bytes(a, 1)) == NULL)
 +			return (-1);
 +	}
 +	if (*p == kEnd)
 +		return (0);
 +
 +	/*
 +	 * Read FilesInfo.
 +	 */
 +	if (*p != kFilesInfo)
 +		return (-1);
 +
 +	if (parse_7zip_uint64(a, &(zip->numFiles)) < 0)
 +		return (-1);
 +	if (UMAX_ENTRY < zip->numFiles)
 +		return (-1);
 +
 +	zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries));
 +	if (zip->entries == NULL)
 +		return (-1);
 +	entries = zip->entries;
 +
 +	empty_streams = 0;
 +	for (;;) {
 +		int type;
 +		uint64_t size;
 +		size_t ll;
 +
 +		if ((p = header_bytes(a, 1)) == NULL)
 +			return (-1);
 +		type = *p;
 +		if (type == kEnd)
 +			break;
 +
 +		if (parse_7zip_uint64(a, &size) < 0)
 +			return (-1);
 +		if (zip->header_bytes_remaining < size)
 +			return (-1);
 +		ll = (size_t)size;
 +
 +		switch (type) {
 +		case kEmptyStream:
 +			h->emptyStreamBools = calloc((size_t)zip->numFiles,
 +			    sizeof(*h->emptyStreamBools));
 +			if (h->emptyStreamBools == NULL)
 +				return (-1);
 +			if (read_Bools(
 +			    a, h->emptyStreamBools, (size_t)zip->numFiles) < 0)
 +				return (-1);
 +			empty_streams = 0;
 +			for (i = 0; i < zip->numFiles; i++) {
 +				if (h->emptyStreamBools[i])
 +					empty_streams++;
 +			}
 +			break;
 +		case kEmptyFile:
 +			if (empty_streams <= 0) {
 +				/* Unexcepted sequence. Skip this. */
 +				if (header_bytes(a, ll) == NULL)
 +					return (-1);
 +				break;
 +			}
 +			h->emptyFileBools = calloc(empty_streams,
 +			    sizeof(*h->emptyFileBools));
 +			if (h->emptyFileBools == NULL)
 +				return (-1);
 +			if (read_Bools(a, h->emptyFileBools, empty_streams) < 0)
 +				return (-1);
 +			break;
 +		case kAnti:
 +			if (empty_streams <= 0) {
 +				/* Unexcepted sequence. Skip this. */
 +				if (header_bytes(a, ll) == NULL)
 +					return (-1);
 +				break;
 +			}
 +			h->antiBools = calloc(empty_streams,
 +			    sizeof(*h->antiBools));
 +			if (h->antiBools == NULL)
 +				return (-1);
 +			if (read_Bools(a, h->antiBools, empty_streams) < 0)
 +				return (-1);
 +			break;
 +		case kCTime:
 +		case kATime:
 +		case kMTime:
 +			if (read_Times(a, h, type) < 0)
 +				return (-1);
 +			break;
 +		case kName:
 +		{
 +			unsigned char *np;
 +			size_t nl, nb;
 +
 +			/* Skip one byte. */
 +			if ((p = header_bytes(a, 1)) == NULL)
 +				return (-1);
 +			ll--;
 +
 +			if ((ll & 1) || ll < zip->numFiles * 4)
 +				return (-1);
 +
 +			zip->entry_names = malloc(ll);
 +			if (zip->entry_names == NULL)
 +				return (-1);
 +			np = zip->entry_names;
 +			nb = ll;
 +			/*
 +			 * Copy whole file names.
 +			 * NOTE: This loop prevents from expanding
 +			 * the uncompressed buffer in order not to
 +			 * use extra memory resource.
 +			 */
 +			while (nb) {
 +				size_t b;
 +				if (nb > UBUFF_SIZE)
 +					b = UBUFF_SIZE;
 +				else
 +					b = nb;
 +				if ((p = header_bytes(a, b)) == NULL)
 +					return (-1);
 +				memcpy(np, p, b);
 +				np += b;
 +				nb -= b;
 +			}
 +			np = zip->entry_names;
 +			nl = ll;
 +
 +			for (i = 0; i < zip->numFiles; i++) {
 +				entries[i].utf16name = np;
 +#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
 +				entries[i].wname = (wchar_t *)np;
 +#endif
 +
 +				/* Find a terminator. */
 +				while (nl >= 2 && (np[0] || np[1])) {
 +					np += 2;
 +					nl -= 2;
 +				}
 +				if (nl < 2)
 +					return (-1);/* Terminator not found */
 +				entries[i].name_len = np - entries[i].utf16name;
 +				np += 2;
 +				nl -= 2;
 +			}
 +			break;
 +		}
 +		case kAttributes:
 +		{
 +			int allAreDefined;
 +
 +			if ((p = header_bytes(a, 2)) == NULL)
 +				return (-1);
 +			allAreDefined = *p;
 +			h->attrBools = calloc((size_t)zip->numFiles,
 +			    sizeof(*h->attrBools));
 +			if (h->attrBools == NULL)
 +				return (-1);
 +			if (allAreDefined)
 +				memset(h->attrBools, 1, (size_t)zip->numFiles);
 +			else {
 +				if (read_Bools(a, h->attrBools,
 +				      (size_t)zip->numFiles) < 0)
 +					return (-1);
 +			}
 +			for (i = 0; i < zip->numFiles; i++) {
 +				if (h->attrBools[i]) {
 +					if ((p = header_bytes(a, 4)) == NULL)
 +						return (-1);
 +					entries[i].attr = archive_le32dec(p);
 +				}
 +			}
 +			break;
 +		}
 +		case kDummy:
 +			if (ll == 0)
 +				break;
 +		default:
 +			if (header_bytes(a, ll) == NULL)
 +				return (-1);
 +			break;
 +		}
 +	}
 +
 +	/*
 +	 * Set up entry's attributes.
 +	 */
 +	folders = si->ci.folders;
 +	eindex = sindex = 0;
 +	folderIndex = indexInFolder = 0;
 +	for (i = 0; i < zip->numFiles; i++) {
 +		if (h->emptyStreamBools == NULL || h->emptyStreamBools[i] == 0)
 +			entries[i].flg |= HAS_STREAM;
 +		/* The high 16 bits of attributes is a posix file mode. */
 +		entries[i].mode = entries[i].attr >> 16;
 +		if (entries[i].flg & HAS_STREAM) {
 +			if ((size_t)sindex >= si->ss.unpack_streams)
 +				return (-1);
 +			if (entries[i].mode == 0)
 +				entries[i].mode = AE_IFREG | 0666;
 +			if (si->ss.digestsDefined[sindex])
 +				entries[i].flg |= CRC32_IS_SET;
 +			entries[i].ssIndex = sindex;
 +			sindex++;
 +		} else {
 +			int dir;
 +			if (h->emptyFileBools == NULL)
 +				dir = 1;
 +			else {
 +				if (h->emptyFileBools[eindex])
 +					dir = 0;
 +				else
 +					dir = 1;
 +				eindex++;
 +			}
 +			if (entries[i].mode == 0) {
 +				if (dir)
 +					entries[i].mode = AE_IFDIR | 0777;
 +				else
 +					entries[i].mode = AE_IFREG | 0666;
 +			} else if (dir &&
 +			    (entries[i].mode & AE_IFMT) != AE_IFDIR) {
 +				entries[i].mode &= ~AE_IFMT;
 +				entries[i].mode |= AE_IFDIR;
 +			}
 +			if ((entries[i].mode & AE_IFMT) == AE_IFDIR &&
 +			    entries[i].name_len >= 2 &&
 +			    (entries[i].utf16name[entries[i].name_len-2] != '/' ||
 +			     entries[i].utf16name[entries[i].name_len-1] != 0)) {
 +				entries[i].utf16name[entries[i].name_len] = '/';
 +				entries[i].utf16name[entries[i].name_len+1] = 0;
 +				entries[i].name_len += 2;
 +			}
 +			entries[i].ssIndex = -1;
 +		}
 +		if (entries[i].attr & 0x01)
 +			entries[i].mode &= ~0222;/* Read only. */
 +
 +		if ((entries[i].flg & HAS_STREAM) == 0 && indexInFolder == 0) {
 +			/*
 +			 * The entry is an empty file or a directory file,
 +			 * those both have no contents.
 +			 */
 +			entries[i].folderIndex = -1;
 +			continue;
 +		}
 +		if (indexInFolder == 0) {
 +			for (;;) {
 +				if (folderIndex >= si->ci.numFolders)
 +					return (-1);
 +				if (folders[folderIndex].numUnpackStreams)
 +					break;
 +				folderIndex++;
 +			}
 +		}
 +		entries[i].folderIndex = folderIndex;
 +		if ((entries[i].flg & HAS_STREAM) == 0)
 +			continue;
 +		indexInFolder++;
 +		if (indexInFolder >= folders[folderIndex].numUnpackStreams) {
 +			folderIndex++;
 +			indexInFolder = 0;
 +		}
 +	}
 +
 +	return (0);
 +}
 +
 +#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
 +static void
 +fileTimeToUtc(uint64_t fileTime, time_t *timep, long *ns)
 +{
 +
 +	if (fileTime >= EPOC_TIME) {
 +		fileTime -= EPOC_TIME;
 +		/* milli seconds base */
 +		*timep = (time_t)(fileTime / 10000000);
 +		/* nano seconds base */
 +		*ns = (long)(fileTime % 10000000) * 100;
 +	} else {
 +		*timep = 0;
 +		*ns = 0;
 +	}
 +}
 +
 +static int
 +read_Times(struct archive_read *a, struct _7z_header_info *h, int type)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	const unsigned char *p;
 +	struct _7zip_entry *entries = zip->entries;
 +	unsigned char *timeBools;
 +	int allAreDefined;
 +	unsigned i;
 +
 +	timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools));
 +	if (timeBools == NULL)
 +		return (-1);
 +
 +	/* Read allAreDefined. */
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		goto failed;
 +	allAreDefined = *p;
 +	if (allAreDefined)
 +		memset(timeBools, 1, (size_t)zip->numFiles);
 +	else {
 +		if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0)
 +			goto failed;
 +	}
 +
 +	/* Read external. */
 +	if ((p = header_bytes(a, 1)) == NULL)
 +		goto failed;
 +	if (*p) {
 +		if (parse_7zip_uint64(a, &(h->dataIndex)) < 0)
 +			goto failed;
 +		if (UMAX_ENTRY < h->dataIndex)
 +			goto failed;
 +	}
 +
 +	for (i = 0; i < zip->numFiles; i++) {
 +		if (!timeBools[i])
 +			continue;
 +		if ((p = header_bytes(a, 8)) == NULL)
 +			goto failed;
 +		switch (type) {
 +		case kCTime:
 +			fileTimeToUtc(archive_le64dec(p),
 +			    &(entries[i].ctime),
 +			    &(entries[i].ctime_ns));
 +			entries[i].flg |= CTIME_IS_SET;
 +			break;
 +		case kATime:
 +			fileTimeToUtc(archive_le64dec(p),
 +			    &(entries[i].atime),
 +			    &(entries[i].atime_ns));
 +			entries[i].flg |= ATIME_IS_SET;
 +			break;
 +		case kMTime:
 +			fileTimeToUtc(archive_le64dec(p),
 +			    &(entries[i].mtime),
 +			    &(entries[i].mtime_ns));
 +			entries[i].flg |= MTIME_IS_SET;
 +			break;
 +		}
 +	}
 +
 +	free(timeBools);
 +	return (0);
 +failed:
 +	free(timeBools);
 +	return (-1);
 +}
 +
 +static int
 +decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +
 +	errno = 0;
 +	if (read_StreamsInfo(a, si) < 0) {
 +		if (errno == ENOMEM)
 +			archive_set_error(&a->archive, -1,
 +			    "Couldn't allocate memory");
 +		else
 +			archive_set_error(&a->archive, -1,
 +			    "Malformed 7-Zip archive");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	if (si->pi.numPackStreams == 0 || si->ci.numFolders == 0) {
 +		archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	if (zip->header_offset < si->pi.pos + si->pi.sizes[0] ||
 +	    (int64_t)(si->pi.pos + si->pi.sizes[0]) < 0 ||
 +	    si->pi.sizes[0] == 0 || (int64_t)si->pi.pos < 0) {
 +		archive_set_error(&a->archive, -1, "Malformed Header offset");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static const unsigned char *
 +header_bytes(struct archive_read *a, size_t rbytes)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	const unsigned char *p;
 +
 +	if (zip->header_bytes_remaining < rbytes)
 +		return (NULL);
 +	if (zip->pack_stream_bytes_unconsumed)
 +		read_consume(a);
 +
 +	if (zip->header_is_encoded == 0) {
 +		p = __archive_read_ahead(a, rbytes, NULL);
 +		if (p == NULL)
 +			return (NULL);
 +		zip->header_bytes_remaining -= rbytes;
 +		zip->pack_stream_bytes_unconsumed = rbytes;
 +	} else {
 +		const void *buff;
 +		ssize_t bytes;
 +
 +		bytes = read_stream(a, &buff, rbytes, rbytes);
 +		if (bytes <= 0)
 +			return (NULL);
 +		zip->header_bytes_remaining -= bytes;
 +		p = buff;
 +	}
 +
 +	/* Update checksum */
 +	zip->header_crc32 = crc32(zip->header_crc32, p, (unsigned)rbytes);
 +	return (p);
 +}
 +
 +static int
 +slurp_central_directory(struct archive_read *a, struct _7zip *zip,
 +    struct _7z_header_info *header)
 +{
 +	const unsigned char *p;
 +	uint64_t next_header_offset;
 +	uint64_t next_header_size;
 +	uint32_t next_header_crc;
 +	ssize_t bytes_avail;
 +	int check_header_crc, r;
 +
 +	if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL)
 +		return (ARCHIVE_FATAL);
 +
 +	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
 +		/* This is an executable ? Must be self-extracting... */
 +		r = skip_sfx(a, bytes_avail);
 +		if (r < ARCHIVE_WARN)
 +			return (r);
 +		if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL)
 +			return (ARCHIVE_FATAL);
 +	}
 +	zip->seek_base += 32;
 +
 +	if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) {
 +		archive_set_error(&a->archive, -1, "Not 7-Zip archive file");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	/* CRC check. */
 +	if (crc32(0, (const unsigned char *)p + 12, 20)
 +	    != archive_le32dec(p + 8)) {
 +		archive_set_error(&a->archive, -1, "Header CRC error");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	next_header_offset = archive_le64dec(p + 12);
 +	next_header_size = archive_le64dec(p + 20);
 +	next_header_crc = archive_le32dec(p + 28);
 +
 +	if (next_header_size == 0)
 +		/* There is no entry in an archive file. */
 +		return (ARCHIVE_EOF);
 +
 +	if (((int64_t)next_header_offset) < 0) {
 +		archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
 +		return (ARCHIVE_FATAL);
 +	}
 +	__archive_read_consume(a, 32);
 +	if (next_header_offset != 0) {
 +		if (bytes_avail >= (ssize_t)next_header_offset)
 +			__archive_read_consume(a, next_header_offset);
 +		else if (__archive_read_seek(a,
 +		    next_header_offset + zip->seek_base, SEEK_SET) < 0)
 +			return (ARCHIVE_FATAL);
 +	}
 +	zip->stream_offset = next_header_offset;
 +	zip->header_offset = next_header_offset;
 +	zip->header_bytes_remaining = next_header_size;
 +	zip->header_crc32 = 0;
 +	zip->header_is_encoded = 0;
 +	zip->header_is_being_read = 1;
 +	zip->has_encrypted_entries = 0;
 +	check_header_crc = 1;
 +
 +	if ((p = header_bytes(a, 1)) == NULL) {
 +		archive_set_error(&a->archive,
 +		    ARCHIVE_ERRNO_FILE_FORMAT,
 +		    "Truncated 7-Zip file body");
 +		return (ARCHIVE_FATAL);
 +	}
 +	/* Parse ArchiveProperties. */
 +	switch (p[0]) {
 +	case kEncodedHeader:
 +		/*
 +		 * The archive has an encoded header and we have to decode it
 +		 * in order to parse the header correctly.
 +		 */
 +		r = decode_encoded_header_info(a, &(zip->si));
 +
 +		/* Check the EncodedHeader CRC.*/
 +		if (r == 0 && zip->header_crc32 != next_header_crc) {
 +			archive_set_error(&a->archive, -1,
 +			    "Damaged 7-Zip archive");
 +			r = -1;
 +		}
 +		if (r == 0) {
 +			if (zip->si.ci.folders[0].digest_defined)
 +				next_header_crc = zip->si.ci.folders[0].digest;
 +			else
 +				check_header_crc = 0;
 +			if (zip->pack_stream_bytes_unconsumed)
 +				read_consume(a);
 +			r = setup_decode_folder(a, zip->si.ci.folders, 1);
 +			if (r == 0) {
 +				zip->header_bytes_remaining =
 +					zip->folder_outbytes_remaining;
 +				r = seek_pack(a);
 +			}
 +		}
 +		/* Clean up StreamsInfo. */
 +		free_StreamsInfo(&(zip->si));
 +		memset(&(zip->si), 0, sizeof(zip->si));
 +		if (r < 0)
 +			return (ARCHIVE_FATAL);
 +		zip->header_is_encoded = 1;
 +		zip->header_crc32 = 0;
 +		/* FALL THROUGH */
 +	case kHeader:
 +		/*
 +		 * Parse the header.
 +		 */
 +		errno = 0;
 +		r = read_Header(a, header, zip->header_is_encoded);
 +		if (r < 0) {
 +			if (errno == ENOMEM)
 +				archive_set_error(&a->archive, -1,
 +				    "Couldn't allocate memory");
 +			else
 +				archive_set_error(&a->archive, -1,
 +				    "Damaged 7-Zip archive");
 +			return (ARCHIVE_FATAL);
 +		}
 +
 +		/*
 +		 *  Must be kEnd.
 +		 */
 +		if ((p = header_bytes(a, 1)) == NULL ||*p != kEnd) {
 +			archive_set_error(&a->archive, -1,
 +			    "Malformed 7-Zip archive");
 +			return (ARCHIVE_FATAL);
 +		}
 +
 +		/* Check the Header CRC.*/
 +		if (check_header_crc && zip->header_crc32 != next_header_crc) {
 +			archive_set_error(&a->archive, -1,
 +			    "Malformed 7-Zip archive");
 +			return (ARCHIVE_FATAL);
 +		}
 +		break;
 +	default:
 +		archive_set_error(&a->archive, -1,
 +		    "Unexpected Property ID = %X", p[0]);
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	/* Clean up variables be used for decoding the archive header */
 +	zip->pack_stream_remaining = 0;
 +	zip->pack_stream_index = 0;
 +	zip->folder_outbytes_remaining = 0;
 +	zip->uncompressed_buffer_bytes_remaining = 0;
 +	zip->pack_stream_bytes_unconsumed = 0;
 +	zip->header_is_being_read = 0;
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static ssize_t
 +get_uncompressed_data(struct archive_read *a, const void **buff, size_t size,
 +    size_t minimum)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	ssize_t bytes_avail;
 +
 +	if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
 +		/* Copy mode. */
 +
 +		/*
 +		 * 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 7-Zip file data");
 +			return (ARCHIVE_FATAL);
 +		}
 +		if ((size_t)bytes_avail >
 +		    zip->uncompressed_buffer_bytes_remaining)
 +			bytes_avail = (ssize_t)
 +			    zip->uncompressed_buffer_bytes_remaining;
 +		if ((size_t)bytes_avail > size)
 +			bytes_avail = (ssize_t)size;
 +
 +		zip->pack_stream_bytes_unconsumed = bytes_avail;
 +	} else if (zip->uncompressed_buffer_pointer == NULL) {
 +		/* Decompression has failed. */
 +		archive_set_error(&(a->archive),
 +		    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
 +		return (ARCHIVE_FATAL);
 +	} else {
 +		/* Packed mode. */
 +		if (minimum > zip->uncompressed_buffer_bytes_remaining) {
 +			/*
 +			 * If remaining uncompressed data size is less than
 +			 * the minimum size, fill the buffer up to the
 +			 * minimum size.
 +			 */
 +			if (extract_pack_stream(a, minimum) < 0)
 +				return (ARCHIVE_FATAL);
 +		}
 +		if (size > zip->uncompressed_buffer_bytes_remaining)
 +			bytes_avail = (ssize_t)
 +			    zip->uncompressed_buffer_bytes_remaining;
 +		else
 +			bytes_avail = (ssize_t)size;
 +		*buff = zip->uncompressed_buffer_pointer;
 +		zip->uncompressed_buffer_pointer += bytes_avail;
 +	}
 +	zip->uncompressed_buffer_bytes_remaining -= bytes_avail;
 +	return (bytes_avail);
 +}
 +
 +static ssize_t
 +extract_pack_stream(struct archive_read *a, size_t minimum)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	ssize_t bytes_avail;
 +	int r;
 +
 +	if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
 +		if (minimum == 0)
 +			minimum = 1;
 +		if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL
 +		    || bytes_avail <= 0) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Truncated 7-Zip file body");
 +			return (ARCHIVE_FATAL);
 +		}
 +		if (bytes_avail > (ssize_t)zip->pack_stream_inbytes_remaining)
 +			bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining;
 +		zip->pack_stream_inbytes_remaining -= bytes_avail;
 +		if (bytes_avail > (ssize_t)zip->folder_outbytes_remaining)
 +			bytes_avail = (ssize_t)zip->folder_outbytes_remaining;
 +		zip->folder_outbytes_remaining -= bytes_avail;
 +		zip->uncompressed_buffer_bytes_remaining = bytes_avail;
 +		return (ARCHIVE_OK);
 +	}
 +
 +	/* If the buffer hasn't been allocated, allocate it now. */
 +	if (zip->uncompressed_buffer == NULL) {
 +		zip->uncompressed_buffer_size = UBUFF_SIZE;
 +		if (zip->uncompressed_buffer_size < minimum) {
 +			zip->uncompressed_buffer_size = minimum + 1023;
 +			zip->uncompressed_buffer_size &= ~0x3ff;
 +		}
 +		zip->uncompressed_buffer =
 +		    malloc(zip->uncompressed_buffer_size);
 +		if (zip->uncompressed_buffer == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "No memory for 7-Zip decompression");
 +			return (ARCHIVE_FATAL);
 +		}
 +		zip->uncompressed_buffer_bytes_remaining = 0;
 +	} else if (zip->uncompressed_buffer_size < minimum ||
 +	    zip->uncompressed_buffer_bytes_remaining < minimum) {
 +		/*
 +		 * Make sure the uncompressed buffer can have bytes
 +		 * at least `minimum' bytes.
 +		 * NOTE: This case happen when reading the header.
 +		 */
 +		size_t used;
 +		if (zip->uncompressed_buffer_pointer != 0)
 +			used = zip->uncompressed_buffer_pointer -
 +				zip->uncompressed_buffer;
 +		else
 +			used = 0;
 +		if (zip->uncompressed_buffer_size < minimum) {
 +			/*
 +			 * Expand the uncompressed buffer up to
 +			 * the minimum size.
 +			 */
 +			void *p;
 +			size_t new_size;
 +
 +			new_size = minimum + 1023;
 +			new_size &= ~0x3ff;
 +			p = realloc(zip->uncompressed_buffer, new_size);
 +			if (p == NULL) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "No memory for 7-Zip decompression");
 +				return (ARCHIVE_FATAL);
 +			}
 +			zip->uncompressed_buffer = (unsigned char *)p;
 +			zip->uncompressed_buffer_size = new_size;
 +		}
 +		/*
 +		 * Move unconsumed bytes to the head.
 +		 */
 +		if (used) {
 +			memmove(zip->uncompressed_buffer,
 +				zip->uncompressed_buffer + used,
 +				zip->uncompressed_buffer_bytes_remaining);
 +		}
 +	} else
 +		zip->uncompressed_buffer_bytes_remaining = 0;
 +	zip->uncompressed_buffer_pointer = NULL;
 +	for (;;) {
 +		size_t bytes_in, bytes_out;
 +		const void *buff_in;
 +		unsigned char *buff_out;
 +		int end_of_data;
 +
 +		/*
 +		 * Note: '1' here is a performance optimization.
 +		 * Recall that the decompression layer returns a count of
 +		 * available bytes; asking for more than that forces the
 +		 * decompressor to combine reads by copying data.
 +		 */
 +		buff_in = __archive_read_ahead(a, 1, &bytes_avail);
 +		if (bytes_avail <= 0) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Truncated 7-Zip file body");
 +			return (ARCHIVE_FATAL);
 +		}
 +
 +		buff_out = zip->uncompressed_buffer
 +			+ zip->uncompressed_buffer_bytes_remaining;
 +		bytes_out = zip->uncompressed_buffer_size
 +			- zip->uncompressed_buffer_bytes_remaining;
 +		bytes_in = bytes_avail;
 +		if (bytes_in > zip->pack_stream_inbytes_remaining)
 +			bytes_in = (size_t)zip->pack_stream_inbytes_remaining;
 +		/* Drive decompression. */
 +		r = decompress(a, zip, buff_out, &bytes_out,
 +			buff_in, &bytes_in);
 +		switch (r) {
 +		case ARCHIVE_OK:
 +			end_of_data = 0;
 +			break;
 +		case ARCHIVE_EOF:
 +			end_of_data = 1;
 +			break;
 +		default:
 +			return (ARCHIVE_FATAL);
 +		}
 +		zip->pack_stream_inbytes_remaining -= bytes_in;
 +		if (bytes_out > zip->folder_outbytes_remaining)
 +			bytes_out = (size_t)zip->folder_outbytes_remaining;
 +		zip->folder_outbytes_remaining -= bytes_out;
 +		zip->uncompressed_buffer_bytes_remaining += bytes_out;
 +		zip->pack_stream_bytes_unconsumed = bytes_in;
 +
 +		/*
 +		 * Continue decompression until uncompressed_buffer is full.
 +		 */
 +		if (zip->uncompressed_buffer_bytes_remaining ==
 +		    zip->uncompressed_buffer_size)
 +			break;
 +		if (zip->codec2 == _7Z_X86 && zip->odd_bcj_size &&
 +		    zip->uncompressed_buffer_bytes_remaining + 5 >
 +		    zip->uncompressed_buffer_size)
 +			break;
 +		if (zip->pack_stream_inbytes_remaining == 0 &&
 +		    zip->folder_outbytes_remaining == 0)
 +			break;
 +		if (end_of_data || (bytes_in == 0 && bytes_out == 0)) {
 +			archive_set_error(&(a->archive),
 +			    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
 +			return (ARCHIVE_FATAL);
 +		}
 +		read_consume(a);
 +	}
 +	if (zip->uncompressed_buffer_bytes_remaining < minimum) {
 +		archive_set_error(&(a->archive),
 +		    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
 +		return (ARCHIVE_FATAL);
 +	}
 +	zip->uncompressed_buffer_pointer = zip->uncompressed_buffer;
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +seek_pack(struct archive_read *a)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	int64_t pack_offset;
 +
 +	if (zip->pack_stream_remaining <= 0) {
 +		archive_set_error(&(a->archive),
 +		    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
 +		return (ARCHIVE_FATAL);
 +	}
 +	zip->pack_stream_inbytes_remaining =
 +	    zip->si.pi.sizes[zip->pack_stream_index];
 +	pack_offset = zip->si.pi.positions[zip->pack_stream_index];
 +	if (zip->stream_offset != pack_offset) {
 +		if (0 > __archive_read_seek(a, pack_offset + zip->seek_base,
 +		    SEEK_SET))
 +			return (ARCHIVE_FATAL);
 +		zip->stream_offset = pack_offset;
 +	}
 +	zip->pack_stream_index++;
 +	zip->pack_stream_remaining--;
 +	return (ARCHIVE_OK);
 +}
 +
 +static ssize_t
 +read_stream(struct archive_read *a, const void **buff, size_t size,
 +    size_t minimum)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	uint64_t skip_bytes = 0;
 +	ssize_t r;
 +
 +	if (zip->uncompressed_buffer_bytes_remaining == 0) {
 +		if (zip->pack_stream_inbytes_remaining > 0) {
 +			r = extract_pack_stream(a, 0);
 +			if (r < 0)
 +				return (r);
 +			return (get_uncompressed_data(a, buff, size, minimum));
 +		} else if (zip->folder_outbytes_remaining > 0) {
 +			/* Extract a remaining pack stream. */
 +			r = extract_pack_stream(a, 0);
 +			if (r < 0)
 +				return (r);
 +			return (get_uncompressed_data(a, buff, size, minimum));
 +		}
 +	} else
 +		return (get_uncompressed_data(a, buff, size, minimum));
 +
 +	/*
 +	 * Current pack stream has been consumed.
 +	 */
 +	if (zip->pack_stream_remaining == 0) {
 +		if (zip->header_is_being_read) {
 +			/* Invalid sequence. This might happen when
 +			 * reading a malformed archive. */
 +			archive_set_error(&(a->archive),
 +			    ARCHIVE_ERRNO_MISC, "Malformed 7-Zip archive");
 +			return (ARCHIVE_FATAL);
 +		}
 +
 +		/*
 +		 * All current folder's pack streams have been
 +		 * consumed. Switch to next folder.
 +		 */
 +		if (zip->folder_index == 0 &&
 +		    (zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
 +		     || zip->folder_index != zip->entry->folderIndex)) {
 +			zip->folder_index = zip->entry->folderIndex;
 +			skip_bytes =
 +			    zip->si.ci.folders[zip->folder_index].skipped_bytes;
 +		}
 +
 +		if (zip->folder_index >= zip->si.ci.numFolders) {
 +			/*
 +			 * We have consumed all folders and its pack streams.
 +			 */
 +			*buff = NULL;
 +			return (0);
 +		}
 +		r = setup_decode_folder(a,
 +			&(zip->si.ci.folders[zip->folder_index]), 0);
 +		if (r != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +
 +		zip->folder_index++;
 +	}
 +
 +	/*
 +	 * Switch to next pack stream.
 +	 */
 +	r = seek_pack(a);
 +	if (r < 0)
 +		return (r);
 +
 +	/* Extract a new pack stream. */
 +	r = extract_pack_stream(a, 0);
 +	if (r < 0)
 +		return (r);
 +
 +	/*
 +	 * Skip the bytes we alrady has skipped in skip_stream().
 +	 */
 +	while (skip_bytes) {
 +		ssize_t skipped;
 +
 +		if (zip->uncompressed_buffer_bytes_remaining == 0) {
 +			if (zip->pack_stream_inbytes_remaining > 0) {
 +				r = extract_pack_stream(a, 0);
 +				if (r < 0)
 +					return (r);
 +			} else if (zip->folder_outbytes_remaining > 0) {
 +				/* Extract a remaining pack stream. */
 +				r = extract_pack_stream(a, 0);
 +				if (r < 0)
 +					return (r);
 +			} else {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_FILE_FORMAT,
 +				    "Truncated 7-Zip file body");
 +				return (ARCHIVE_FATAL);
 +			}
 +		}
 +		skipped = get_uncompressed_data(
 +			a, buff, (size_t)skip_bytes, 0);
 +		if (skipped < 0)
 +			return (skipped);
 +		skip_bytes -= skipped;
 +		if (zip->pack_stream_bytes_unconsumed)
 +			read_consume(a);
 +	}
 +
 +	return (get_uncompressed_data(a, buff, size, minimum));
 +}
 +
 +static int
 +setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
 +    int header)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	const struct _7z_coder *coder1, *coder2;
 +	const char *cname = (header)?"archive header":"file content";
 +	unsigned i;
 +	int r, found_bcj2 = 0;
 +
 +	/*
 +	 * Release the memory which the previous folder used for BCJ2.
 +	 */
 +	for (i = 0; i < 3; i++) {
 +		if (zip->sub_stream_buff[i] != NULL)
 +			free(zip->sub_stream_buff[i]);
 +		zip->sub_stream_buff[i] = NULL;
 +	}
 +
 +	/*
 +	 * Initialize a stream reader.
 +	 */
 +	zip->pack_stream_remaining = (unsigned)folder->numPackedStreams;
 +	zip->pack_stream_index = (unsigned)folder->packIndex;
 +	zip->folder_outbytes_remaining = folder_uncompressed_size(folder);
 +	zip->uncompressed_buffer_bytes_remaining = 0;
 +
 +	/*
 +	 * Check coder types.
 +	 */
 +	for (i = 0; i < folder->numCoders; i++) {
 +		switch(folder->coders[i].codec) {
 +			case _7Z_CRYPTO_MAIN_ZIP:
 +			case _7Z_CRYPTO_RAR_29:
 +			case _7Z_CRYPTO_AES_256_SHA_256: {
 +				/* For entry that is associated with this folder, mark
 +				   it as encrypted (data+metadata). */
 +				zip->has_encrypted_entries = 1;
 +				if (a->entry) {
 +					archive_entry_set_is_data_encrypted(a->entry, 1);
 +					archive_entry_set_is_metadata_encrypted(a->entry, 1);
 +				}
 +				archive_set_error(&(a->archive),
 +					ARCHIVE_ERRNO_MISC,
 +					"The %s is encrypted, "
 +					"but currently not supported", cname);
 +				return (ARCHIVE_FATAL);
 +			}
 +			case _7Z_X86_BCJ2: {
 +				found_bcj2++;
 +				break;
 +			}
 +		}
 +	}
 +	/* Now that we've checked for encryption, if there were still no
 +	 * encrypted entries found we can say for sure that there are none.
 +	 */
 +	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
 +		zip->has_encrypted_entries = 0;
 +	}
 +
 +	if ((folder->numCoders > 2 && !found_bcj2) || found_bcj2 > 1) {
 +		archive_set_error(&(a->archive),
 +		    ARCHIVE_ERRNO_MISC,
 +		    "The %s is encoded with many filters, "
 +		    "but currently not supported", cname);
 +		return (ARCHIVE_FATAL);
 +	}
 +	coder1 = &(folder->coders[0]);
 +	if (folder->numCoders == 2)
 +		coder2 = &(folder->coders[1]);
 +	else
 +		coder2 = NULL;
 +
 +	if (found_bcj2) {
 +		/*
 +		 * Preparation to decode BCJ2.
 +		 * Decoding BCJ2 requires four sources. Those are at least,
 +		 * as far as I know, two types of the storage form.
 +		 */
 +		const struct _7z_coder *fc = folder->coders;
 +		static const struct _7z_coder coder_copy = {0, 1, 1, 0, NULL};
 +		const struct _7z_coder *scoder[3] =
 +			{&coder_copy, &coder_copy, &coder_copy};
 +		const void *buff;
 +		ssize_t bytes;
 +		unsigned char *b[3] = {NULL, NULL, NULL};
 +		uint64_t sunpack[3] ={-1, -1, -1};
 +		size_t s[3] = {0, 0, 0};
 +		int idx[3] = {0, 1, 2};
 +
 +		if (folder->numCoders == 4 && fc[3].codec == _7Z_X86_BCJ2 &&
 +		    folder->numInStreams == 7 && folder->numOutStreams == 4 &&
 +		    zip->pack_stream_remaining == 4) {
 +			/* Source type 1 made by 7zr or 7z with -m options. */
 +			if (folder->bindPairs[0].inIndex == 5) {
 +				/* The form made by 7zr */
 +				idx[0] = 1; idx[1] = 2; idx[2] = 0;
 +				scoder[1] = &(fc[1]);
 +				scoder[2] = &(fc[0]);
 +				sunpack[1] = folder->unPackSize[1];
 +				sunpack[2] = folder->unPackSize[0];
 +				coder1 = &(fc[2]);
 +			} else {
 +				/*
 +				 * NOTE: Some patterns do not work.
 +				 * work:
 +				 *  7z a -m0=BCJ2 -m1=COPY -m2=COPY
 +				 *       -m3=(any)
 +				 *  7z a -m0=BCJ2 -m1=COPY -m2=(any)
 +				 *       -m3=COPY
 +				 *  7z a -m0=BCJ2 -m1=(any) -m2=COPY
 +				 *       -m3=COPY
 +				 * not work:
 +				 *  other patterns.
 +				 *
 +				 * We have to handle this like `pipe' or
 +				 * our libarchive7s filter frame work,
 +				 * decoding the BCJ2 main stream sequentially,
 +				 * m3 -> m2 -> m1 -> BCJ2.
 +				 *
 +				 */
 +				if (fc[0].codec == _7Z_COPY &&
 +				    fc[1].codec == _7Z_COPY)
 +					coder1 = &(folder->coders[2]);
 +				else if (fc[0].codec == _7Z_COPY &&
 +				    fc[2].codec == _7Z_COPY)
 +					coder1 = &(folder->coders[1]);
 +				else if (fc[1].codec == _7Z_COPY &&
 +				    fc[2].codec == _7Z_COPY)
 +					coder1 = &(folder->coders[0]);
 +				else {
 +					archive_set_error(&(a->archive),
 +					    ARCHIVE_ERRNO_MISC,
 +					    "Unsupported form of "
 +					    "BCJ2 streams");
 +					return (ARCHIVE_FATAL);
 +				}
 +			}
 +			coder2 = &(fc[3]);
 +			zip->main_stream_bytes_remaining =
 +				(size_t)folder->unPackSize[2];
 +		} else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 &&
 +		    zip->pack_stream_remaining == 4 &&
 +		    folder->numInStreams == 5 && folder->numOutStreams == 2) {
 +			/* Source type 0 made by 7z */
 +			zip->main_stream_bytes_remaining =
 +				(size_t)folder->unPackSize[0];
 +		} else {
 +			/* We got an unexpected form. */
 +			archive_set_error(&(a->archive),
 +			    ARCHIVE_ERRNO_MISC,
 +			    "Unsupported form of BCJ2 streams");
 +			return (ARCHIVE_FATAL);
 +		}
 +
 +		/* Skip the main stream at this time. */
 +		if ((r = seek_pack(a)) < 0)
 +			return (r);
 +		zip->pack_stream_bytes_unconsumed =
 +		    (size_t)zip->pack_stream_inbytes_remaining;
 +		read_consume(a);
 +
 +		/* Read following three sub streams. */
 +		for (i = 0; i < 3; i++) {
 +			const struct _7z_coder *coder = scoder[i];
 +
 +			if ((r = seek_pack(a)) < 0) {
 +				free(b[0]); free(b[1]); free(b[2]);
 +				return (r);
 +			}
 +
 +			if (sunpack[i] == (uint64_t)-1)
 +				zip->folder_outbytes_remaining =
 +				    zip->pack_stream_inbytes_remaining;
 +			else
 +				zip->folder_outbytes_remaining = sunpack[i];
 +
 +			r = init_decompression(a, zip, coder, NULL);
 +			if (r != ARCHIVE_OK) {
 +				free(b[0]); free(b[1]); free(b[2]);
 +				return (ARCHIVE_FATAL);
 +			}
 +
 +			/* Allocate memory for the decorded data of a sub
 +			 * stream. */
 +			b[i] = malloc((size_t)zip->folder_outbytes_remaining);
 +			if (b[i] == NULL) {
 +				free(b[0]); free(b[1]); free(b[2]);
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "No memory for 7-Zip decompression");
 +				return (ARCHIVE_FATAL);
 +			}
 +
 +			/* Extract a sub stream. */
 +			while (zip->pack_stream_inbytes_remaining > 0) {
 +				r = (int)extract_pack_stream(a, 0);
 +				if (r < 0) {
 +					free(b[0]); free(b[1]); free(b[2]);
 +					return (r);
 +				}
 +				bytes = get_uncompressed_data(a, &buff,
 +				    zip->uncompressed_buffer_bytes_remaining,
 +				    0);
 +				if (bytes < 0) {
 +					free(b[0]); free(b[1]); free(b[2]);
 +					return ((int)bytes);
 +				}
 +				memcpy(b[i]+s[i], buff, bytes);
 +				s[i] += bytes;
 +				if (zip->pack_stream_bytes_unconsumed)
 +					read_consume(a);
 +			}
 +		}
 +
 +		/* Set the sub streams to the right place. */
 +		for (i = 0; i < 3; i++) {
 +			zip->sub_stream_buff[i] = b[idx[i]];
 +			zip->sub_stream_size[i] = s[idx[i]];
 +			zip->sub_stream_bytes_remaining[i] = s[idx[i]];
 +		}
 +
 +		/* Allocate memory used for decoded main stream bytes. */
 +		if (zip->tmp_stream_buff == NULL) {
 +			zip->tmp_stream_buff_size = 32 * 1024;
 +			zip->tmp_stream_buff =
 +			    malloc(zip->tmp_stream_buff_size);
 +			if (zip->tmp_stream_buff == NULL) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "No memory for 7-Zip decompression");
 +				return (ARCHIVE_FATAL);
 +			}
 +		}
 +		zip->tmp_stream_bytes_avail = 0;
 +		zip->tmp_stream_bytes_remaining = 0;
 +		zip->odd_bcj_size = 0;
 +		zip->bcj2_outPos = 0;
 +
 +		/*
 +		 * Reset a stream reader in order to read the main stream
 +		 * of BCJ2.
 +		 */
 +		zip->pack_stream_remaining = 1;
 +		zip->pack_stream_index = (unsigned)folder->packIndex;
 +		zip->folder_outbytes_remaining =
 +		    folder_uncompressed_size(folder);
 +		zip->uncompressed_buffer_bytes_remaining = 0;
 +	}
 +
 +	/*
 +	 * Initialize the decompressor for the new folder's pack streams.
 +	 */
 +	r = init_decompression(a, zip, coder1, coder2);
 +	if (r != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +	return (ARCHIVE_OK);
 +}
 +
 +static int64_t
 +skip_stream(struct archive_read *a, size_t skip_bytes)
 +{
 +	struct _7zip *zip = (struct _7zip *)a->format->data;
 +	const void *p;
 +	int64_t skipped_bytes;
 +	size_t bytes = skip_bytes;
 +
 +	if (zip->folder_index == 0) {
 +		/*
 +		 * Optimization for a list mode.
 +		 * Avoid unncecessary decoding operations.
 +		 */
 +		zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
 +		    += skip_bytes;
 +		return (skip_bytes);
 +	}
 +
 +	while (bytes) {
 +		skipped_bytes = read_stream(a, &p, bytes, 0);
 +		if (skipped_bytes < 0)
 +			return (skipped_bytes);
 +		if (skipped_bytes == 0) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Truncated 7-Zip file body");
 +			return (ARCHIVE_FATAL);
 +		}
 +		bytes -= (size_t)skipped_bytes;
 +		if (zip->pack_stream_bytes_unconsumed)
 +			read_consume(a);
 +	}
 +	return (skip_bytes);
 +}
 +
 +/*
 + * Brought from LZMA SDK.
 + *
 + * Bra86.c -- Converter for x86 code (BCJ)
 + * 2008-10-04 : Igor Pavlov : Public domain
 + *
 + */
 +
 +#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF)
 +
 +static void
 +x86_Init(struct _7zip *zip)
 +{
 +	zip->bcj_state = 0;
 +	zip->bcj_prevPosT = (size_t)0 - 1;
 +	zip->bcj_prevMask = 0;
 +	zip->bcj_ip = 5;
 +}
 +
 +static size_t
 +x86_Convert(struct _7zip *zip, uint8_t *data, size_t size)
 +{
 +	static const uint8_t kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0};
 +	static const uint8_t kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3};
 +	size_t bufferPos, prevPosT;
 +	uint32_t ip, prevMask;
 +
 +	if (size < 5)
 +		return 0;
 +
 +	bufferPos = 0;
 +	prevPosT = zip->bcj_prevPosT;
 +	prevMask = zip->bcj_prevMask;
 +	ip = zip->bcj_ip;
 +
 +	for (;;) {
 +		uint8_t *p = data + bufferPos;
 +		uint8_t *limit = data + size - 4;
 +
 +		for (; p < limit; p++)
 +			if ((*p & 0xFE) == 0xE8)
 +				break;
 +		bufferPos = (size_t)(p - data);
 +		if (p >= limit)
 +			break;
 +		prevPosT = bufferPos - prevPosT;
 +		if (prevPosT > 3)
 +			prevMask = 0;
 +		else {
 +			prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7;
 +			if (prevMask != 0) {
 +				unsigned char b =
 +					p[4 - kMaskToBitNumber[prevMask]];
 +				if (!kMaskToAllowedStatus[prevMask] ||
 +				    Test86MSByte(b)) {
 +					prevPosT = bufferPos;
 +					prevMask = ((prevMask << 1) & 0x7) | 1;
 +					bufferPos++;
 +					continue;
 +				}
 +			}
 +		}
 +		prevPosT = bufferPos;
 +
 +		if (Test86MSByte(p[4])) {
 +			uint32_t src = ((uint32_t)p[4] << 24) |
 +				((uint32_t)p[3] << 16) | ((uint32_t)p[2] << 8) |
 +				((uint32_t)p[1]);
 +			uint32_t dest;
 +			for (;;) {
 +				uint8_t b;
 +				int b_index;
 +
 +				dest = src - (ip + (uint32_t)bufferPos);
 +				if (prevMask == 0)
 +					break;
 +				b_index = kMaskToBitNumber[prevMask] * 8;
 +				b = (uint8_t)(dest >> (24 - b_index));
 +				if (!Test86MSByte(b))
 +					break;
 +				src = dest ^ ((1 << (32 - b_index)) - 1);
 +			}
 +			p[4] = (uint8_t)(~(((dest >> 24) & 1) - 1));
 +			p[3] = (uint8_t)(dest >> 16);
 +			p[2] = (uint8_t)(dest >> 8);
 +			p[1] = (uint8_t)dest;
 +			bufferPos += 5;
 +		} else {
 +			prevMask = ((prevMask << 1) & 0x7) | 1;
 +			bufferPos++;
 +		}
 +	}
 +	zip->bcj_prevPosT = prevPosT;
 +	zip->bcj_prevMask = prevMask;
 +	zip->bcj_ip += (uint32_t)bufferPos;
 +	return (bufferPos);
 +}
 +
 +/*
 + * Brought from LZMA SDK.
 + *
 + * Bcj2.c -- Converter for x86 code (BCJ2)
 + * 2008-10-04 : Igor Pavlov : Public domain
 + *
 + */
 +
 +#define SZ_ERROR_DATA	 ARCHIVE_FAILED
 +
 +#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80)
 +#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1))
 +
 +#define kNumTopBits 24
 +#define kTopValue ((uint32_t)1 << kNumTopBits)
 +
 +#define kNumBitModelTotalBits 11
 +#define kBitModelTotal (1 << kNumBitModelTotalBits)
 +#define kNumMoveBits 5
 +
 +#define RC_READ_BYTE (*buffer++)
 +#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; }
 +#define RC_INIT2 zip->bcj2_code = 0; zip->bcj2_range = 0xFFFFFFFF; \
 +  { int ii; for (ii = 0; ii < 5; ii++) { RC_TEST; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }}
 +
 +#define NORMALIZE if (zip->bcj2_range < kTopValue) { RC_TEST; zip->bcj2_range <<= 8; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }
 +
 +#define IF_BIT_0(p) ttt = *(p); bound = (zip->bcj2_range >> kNumBitModelTotalBits) * ttt; if (zip->bcj2_code < bound)
 +#define UPDATE_0(p) zip->bcj2_range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE;
 +#define UPDATE_1(p) zip->bcj2_range -= bound; zip->bcj2_code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE;
 +
 +static ssize_t
 +Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize)
 +{
 +	size_t inPos = 0, outPos = 0;
 +	const uint8_t *buf0, *buf1, *buf2, *buf3;
 +	size_t size0, size1, size2, size3;
 +	const uint8_t *buffer, *bufferLim;
 +	unsigned int i, j;
 +
 +	size0 = zip->tmp_stream_bytes_remaining;
 +	buf0 = zip->tmp_stream_buff + zip->tmp_stream_bytes_avail - size0;
 +	size1 = zip->sub_stream_bytes_remaining[0];
 +	buf1 = zip->sub_stream_buff[0] + zip->sub_stream_size[0] - size1;
 +	size2 = zip->sub_stream_bytes_remaining[1];
 +	buf2 = zip->sub_stream_buff[1] + zip->sub_stream_size[1] - size2;
 +	size3 = zip->sub_stream_bytes_remaining[2];
 +	buf3 = zip->sub_stream_buff[2] + zip->sub_stream_size[2] - size3;
 +
 +	buffer = buf3;
 +	bufferLim = buffer + size3;
 +
 +	if (zip->bcj_state == 0) {
 +		/*
 +		 * Initialize.
 +		 */
 +		zip->bcj2_prevByte = 0;
 +		for (i = 0;
 +		    i < sizeof(zip->bcj2_p) / sizeof(zip->bcj2_p[0]); i++)
 +			zip->bcj2_p[i] = kBitModelTotal >> 1;
 +		RC_INIT2;
 +		zip->bcj_state = 1;
 +	}
 +
 +	/*
 +	 * Gather the odd bytes of a previous call.
 +	 */
 +	for (i = 0; zip->odd_bcj_size > 0 && outPos < outSize; i++) {
 +		outBuf[outPos++] = zip->odd_bcj[i];
 +		zip->odd_bcj_size--;
 +	}
 +
 +	if (outSize == 0) {
 +		zip->bcj2_outPos += outPos;
 +		return (outPos);
 +	}
 +
 +	for (;;) {
 +		uint8_t b;
 +		CProb *prob;
 +		uint32_t bound;
 +		uint32_t ttt;
 +
 +		size_t limit = size0 - inPos;
 +		if (outSize - outPos < limit)
 +			limit = outSize - outPos;
 +
 +		if (zip->bcj_state == 1) {
 +			while (limit != 0) {
 +				uint8_t bb = buf0[inPos];
 +				outBuf[outPos++] = bb;
 +				if (IsJ(zip->bcj2_prevByte, bb)) {
 +					zip->bcj_state = 2;
 +					break;
 +				}
 +				inPos++;
 +				zip->bcj2_prevByte = bb;
 +				limit--;
 +			}
 +		}
 +
 +		if (limit == 0 || outPos == outSize)
 +			break;
 +		zip->bcj_state = 1;
 +
 +		b = buf0[inPos++];
 +
 +		if (b == 0xE8)
 +			prob = zip->bcj2_p + zip->bcj2_prevByte;
 +		else if (b == 0xE9)
 +			prob = zip->bcj2_p + 256;
 +		else
 +			prob = zip->bcj2_p + 257;
 +
 +		IF_BIT_0(prob) {
 +			UPDATE_0(prob)
 +			zip->bcj2_prevByte = b;
 +		} else {
 +			uint32_t dest;
 +			const uint8_t *v;
 +			uint8_t out[4];
 +
 +			UPDATE_1(prob)
 +			if (b == 0xE8) {
 +				v = buf1;
 +				if (size1 < 4)
 +					return SZ_ERROR_DATA;
 +				buf1 += 4;
 +				size1 -= 4;
 +			} else {
 +				v = buf2;
 +				if (size2 < 4)
 +					return SZ_ERROR_DATA;
 +				buf2 += 4;
 +				size2 -= 4;
 +			}
 +			dest = (((uint32_t)v[0] << 24) |
 +			    ((uint32_t)v[1] << 16) |
 +			    ((uint32_t)v[2] << 8) |
 +			    ((uint32_t)v[3])) -
 +			    ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4);
 +			out[0] = (uint8_t)dest;
 +			out[1] = (uint8_t)(dest >> 8);
 +			out[2] = (uint8_t)(dest >> 16);
 +			out[3] = zip->bcj2_prevByte = (uint8_t)(dest >> 24);
 +
 +			for (i = 0; i < 4 && outPos < outSize; i++)
 +				outBuf[outPos++] = out[i];
 +			if (i < 4) {
 +				/*
 +				 * Save odd bytes which we could not add into
 +				 * the output buffer because of out of space.
 +				 */
 +				zip->odd_bcj_size = 4 -i;
 +				for (; i < 4; i++) {
 +					j = i - 4 + (unsigned)zip->odd_bcj_size;
 +					zip->odd_bcj[j] = out[i];
 +				}
 +				break;
 +			}
 +		}
 +	}
 +	zip->tmp_stream_bytes_remaining -= inPos;
 +	zip->sub_stream_bytes_remaining[0] = size1;
 +	zip->sub_stream_bytes_remaining[1] = size2;
 +	zip->sub_stream_bytes_remaining[2] = bufferLim - buffer;
 +	zip->bcj2_outPos += outPos;
 +
 +	return ((ssize_t)outPos);
 +}
 +
diff --cc Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c
index 3628025,0000000..c19614a
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,3263 -1,0 +1,3263 @@@
 +/*-
 + * 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 beging 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 buufer 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 * vd->location;
++	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 * vd->location;
++		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)
 +				/* Coundn'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");
 +			return (NULL);
 +		}
 +		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) {
 +				free(file);
 +				return (NULL);
 +			}
 +			/*
 +			 * 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");
 +				return (NULL);
 +			}
 +			/*
 +			 * 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");
 +				return (NULL);
 +			}
 +			/*
 +			 * Sanity check: The file type must be a directory.
 +			 */
 +			if ((flags & 0x02) == 0) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Invalid Rockridge RE");
 +				return (NULL);
 +			}
 +		} 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");
 +				return (NULL);
 +			}
 +			/*
 +			 * 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");
 +				return (NULL);
 +			}
 +			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");
 +					return (NULL);
 +				}
 +			}
 +			if (file->cl_offset == file->offset ||
 +			    parent->rr_moved) {
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Invalid Rockridge CL");
 +				return (NULL);
 +			}
 +		}
 +	}
 +
 +#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);
 +}
 +
 +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 fron 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 stil remaning, 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 bak. */
 +	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 offse 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->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_rar.c
index c52c429,0000000..fc8153f
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,2944 -1,0 +1,2954 @@@
 +/*-
 +* 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 *)malloc(sizeof(*rar));
 +  if (rar == NULL)
 +  {
 +    archive_set_error(&a->archive, ENOMEM, "Can't allocate rar data");
 +    return (ARCHIVE_FATAL);
 +  }
 +  memset(rar, 0, sizeof(*rar));
 +
 +	/*
 +	 * 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 encrytped, 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);
 +        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))
++  if(windowoffs + length <= lzss_size(&rar->lzss)) {
 +    memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs],
 +           length);
-   else
-   {
++  } 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
++    } 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 1399e07,0000000..c50ba84
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,3053 -1,0 +1,3062 @@@
 +/*-
 + * 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 decyption. */
 +	/* 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_decypt_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_decypt_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 {
 +	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 void
 +process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry)
 +{
 +	unsigned offset = 0;
 +
 +	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) {
 +			break;
 +		}
 +#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) {
 +				if (datasize < 8)
 +					break;
 +				zip_entry->uncompressed_size =
 +				    archive_le64dec(p + offset);
 +				offset += 8;
 +				datasize -= 8;
 +			}
 +			if (zip_entry->compressed_size == 0xffffffff) {
 +				if (datasize < 8)
 +					break;
 +				zip_entry->compressed_size =
 +				    archive_le64dec(p + offset);
 +				offset += 8;
 +				datasize -= 8;
 +			}
 +			if (zip_entry->local_header_offset == 0xffffffff) {
 +				if (datasize < 8)
 +					break;
 +				zip_entry->local_header_offset =
 +				    archive_le64dec(p + offset);
 +				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;
 +	}
 +#ifdef DEBUG
 +	if (offset != extra_length)
 +	{
 +		fprintf(stderr,
 +		    "Extra data field contents do not match reported size!\n");
 +	}
 +#endif
 +}
 +
 +/*
 + * 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);
 +	}
 +
 +	process_extra(h, extra_length, zip_entry);
 +	__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 ((zip_entry->mode & AE_IFMT) == 0) {
 +		/* Especially in streaming mode, we can end up
 +		   here without having seen proper mode information.
 +		   Guess from the filename. */
 +		wp = archive_entry_pathname_w(entry);
 +		if (wp != NULL) {
 +			len = wcslen(wp);
 +			if (len > 0 && wp[len - 1] == L'/')
 +				zip_entry->mode |= AE_IFDIR;
 +			else
 +				zip_entry->mode |= AE_IFREG;
 +		} else {
 +			cp = archive_entry_pathname(entry);
 +			len = (cp != NULL)?strlen(cp):0;
 +			if (len > 0 && cp[len - 1] == '/')
 +				zip_entry->mode |= AE_IFDIR;
 +			else
 +				zip_entry->mode |= AE_IFREG;
 +		}
 +		if (zip_entry->mode == AE_IFDIR) {
 +			zip_entry->mode |= 0775;
 +		} else if (zip_entry->mode == AE_IFREG) {
 +			zip_entry->mode |= 0664;
 +		}
 +	}
 +
 +	/* 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);
 +			}
 +		} 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);
 +			}
 +		}
 +	}
 +
 +	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) {
 +				zip->entry->crc32 = archive_le32dec(p + 4);
 +				zip->entry->compressed_size =
 +					archive_le64dec(p + 8);
 +				zip->entry->uncompressed_size =
 +					archive_le64dec(p + 16);
 +				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_size
- 			    - (zip->decrypted_ptr - zip->decrypted_buffer);
++			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) {
 +			zip->entry->crc32 = archive_le32dec(p);
 +			zip->entry->compressed_size = archive_le64dec(p + 4);
 +			zip->entry->uncompressed_size = archive_le64dec(p + 12);
 +			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 */
 +		/* Suuported 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 implemnted 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 Decyption.
 +		 */
 +		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
 +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;
 +	/* Must be only a single volume. */
 +	if (archive_le32dec(p + 16) != 1)
 +		return;
 +
 +	/* Find the Zip64 EOCD record. */
 +	eocd64_offset = archive_le64dec(p + 8);
 +	if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0)
 +		return;
 +	if ((p = __archive_read_ahead(a, 56, NULL)) == NULL)
 +		return;
 +	/* Make sure we can read all of it. */
 +	eocd64_size = archive_le64dec(p + 4) + 12;
 +	if (eocd64_size < 56 || eocd64_size > 16384)
 +		return;
 +	if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL)
 +		return;
 +
 +	/* Sanity-check the EOCD64 */
 +	if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */
 +		return;
 +	if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */
 +		return;
 +	/* CD can't be split. */
 +	if (archive_le64dec(p + 24) != archive_le64dec(p + 32))
 +		return;
 +
 +	/* Save the central directory offset for later use. */
 +	zip->central_directory_offset = archive_le64dec(p + 48);
 +}
 +
 +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);
 +				}
 +			}
 +			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;
 +		}
 +		process_extra(p + filename_length, extra_length, zip_entry);
 +
 +		/*
 +		 * 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_write_set_format_iso9660.c
index 576f4c2,0000000..879a776
mode 100644,000000..100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c
@@@ -1,8158 -1,0 +1,8160 @@@
 +/*-
 + * Copyright (c) 2009-2012 Michihiro NAKAJIMA
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions
 + * are met:
 + * 1. Redistributions of source code must retain the above copyright
 + *    notice, this list of conditions and the following disclaimer.
 + * 2. Redistributions in binary form must reproduce the above copyright
 + *    notice, this list of conditions and the following disclaimer in the
 + *    documentation and/or other materials provided with the distribution.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + */
 +
 +#include "archive_platform.h"
 +
 +#ifdef HAVE_SYS_TYPES_H
 +#include <sys/types.h>
 +#endif
 +#ifdef HAVE_SYS_UTSNAME_H
 +#include <sys/utsname.h>
 +#endif
 +#ifdef HAVE_ERRNO_H
 +#include <errno.h>
 +#endif
 +#ifdef HAVE_LIMITS_H
 +#include <limits.h>
 +#endif
 +#include <stdio.h>
 +#include <stdarg.h>
 +#ifdef HAVE_STDLIB_H
 +#include <stdlib.h>
 +#endif
 +#include <time.h>
 +#ifdef HAVE_UNISTD_H
 +#include <unistd.h>
 +#endif
 +#ifdef HAVE_ZLIB_H
 +#include <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_rb.h"
 +#include "archive_write_private.h"
 +
 +#if defined(_WIN32) && !defined(__CYGWIN__)
 +#define getuid()			0
 +#define getgid()			0
 +#endif
 +
 +/*#define DEBUG 1*/
 +#ifdef DEBUG
 +/* To compare to the ISO image file made by mkisofs. */
 +#define COMPAT_MKISOFS		1
 +#endif
 +
 +#define LOGICAL_BLOCK_BITS			11
 +#define LOGICAL_BLOCK_SIZE			2048
 +#define PATH_TABLE_BLOCK_SIZE			4096
 +
 +#define SYSTEM_AREA_BLOCK			16
 +#define PRIMARY_VOLUME_DESCRIPTOR_BLOCK 	1
 +#define SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK 	1
 +#define BOOT_RECORD_DESCRIPTOR_BLOCK	 	1
 +#define VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK	1
 +#define NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK	1
 +#define RRIP_ER_BLOCK				1
 +#define PADDING_BLOCK				150
 +
 +#define FD_1_2M_SIZE		(1024 * 1200)
 +#define FD_1_44M_SIZE		(1024 * 1440)
 +#define FD_2_88M_SIZE		(1024 * 2880)
 +#define MULTI_EXTENT_SIZE	(ARCHIVE_LITERAL_LL(1) << 32)	/* 4Gi bytes. */
 +#define MAX_DEPTH		8
 +#define RR_CE_SIZE		28		/* SUSP "CE" extension size */
 +
 +#define FILE_FLAG_EXISTENCE	0x01
 +#define FILE_FLAG_DIRECTORY	0x02
 +#define FILE_FLAG_ASSOCIATED	0x04
 +#define FILE_FLAG_RECORD	0x08
 +#define FILE_FLAG_PROTECTION	0x10
 +#define FILE_FLAG_MULTI_EXTENT	0x80
 +
 +static const char rrip_identifier[] =
 +	"RRIP_1991A";
 +static const char rrip_descriptor[] =
 +	"THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR "
 +	"POSIX FILE SYSTEM SEMANTICS";
 +static const char rrip_source[] =
 +	"PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE.  "
 +	"SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR "
 +	"CONTACT INFORMATION.";
 +#define RRIP_ER_ID_SIZE		(sizeof(rrip_identifier)-1)
 +#define RRIP_ER_DSC_SIZE	(sizeof(rrip_descriptor)-1)
 +#define RRIP_ER_SRC_SIZE	(sizeof(rrip_source)-1)
 +#define RRIP_ER_SIZE		(8 + RRIP_ER_ID_SIZE + \
 +				RRIP_ER_DSC_SIZE + RRIP_ER_SRC_SIZE)
 +
 +static const unsigned char zisofs_magic[8] = {
 +	0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07
 +};
 +
 +#define ZF_HEADER_SIZE	16	/* zisofs header size. */
 +#define ZF_LOG2_BS	15	/* log2 block size; 32K bytes. */
 +#define ZF_BLOCK_SIZE	(1UL << ZF_LOG2_BS)
 +
 +/*
 + * Manage extra records.
 + */
 +struct extr_rec {
 +	int		 location;
 +	int		 offset;
 +	unsigned char	 buf[LOGICAL_BLOCK_SIZE];
 +	struct extr_rec	*next;
 +};
 +
 +struct ctl_extr_rec {
 +	int		 use_extr;
 +	unsigned char	*bp;
 +	struct isoent	*isoent;
 +	unsigned char	*ce_ptr;
 +	int		 cur_len;
 +	int		 dr_len;
 +	int		 limit;
 +	int		 extr_off;
 +	int		 extr_loc;
 +};
 +#define DR_SAFETY	RR_CE_SIZE
 +#define DR_LIMIT	(254 - DR_SAFETY)
 +
 +/*
 + * The relation of struct isofile and isoent and archive_entry.
 + *
 + * Primary volume tree  --> struct isoent
 + *                                |
 + *                                v
 + *                          struct isofile --> archive_entry
 + *                                ^
 + *                                |
 + * Joliet volume tree   --> struct isoent
 + *
 + * struct isoent has specific information for volume.
 + */
 +
 +struct isofile {
 +	/* Used for managing struct isofile list. */
 +	struct isofile		*allnext;
 +	struct isofile		*datanext;
 +	/* Used for managing a hardlined struct isofile list. */
 +	struct isofile		*hlnext;
 +	struct isofile		*hardlink_target;
 +
 +	struct archive_entry	*entry;
 +
 +	/*
 +	 * Used for making a directory tree.
 +	 */
 +	struct archive_string	 parentdir;
 +	struct archive_string	 basename;
 +	struct archive_string	 basename_utf16;
 +	struct archive_string	 symlink;
 +	int			 dircnt;	/* The number of elements of
 +						 * its parent directory */
 +
 +	/*
 +	 * Used for a Directory Record.
 +	 */
 +	struct content {
 +		int64_t		 offset_of_temp;
 +		int64_t		 size;
 +		int		 blocks;
 +		uint32_t 	 location;
 +		/*
 +		 * One extent equals one content.
 +		 * If this entry has multi extent, `next' variable points
 +		 * next content data.
 +		 */
 +		struct content	*next;		/* next content	*/
 +	} content, *cur_content;
 +	int			 write_content;
 +
 +	enum {
 +		NO = 0,
 +		BOOT_CATALOG,
 +		BOOT_IMAGE
 +	} boot;
 +
 +	/*
 +	 * Used for a zisofs.
 +	 */
 +	struct {
 +		unsigned char	 header_size;
 +		unsigned char	 log2_bs;
 +		uint32_t	 uncompressed_size;
 +	} zisofs;
 +};
 +
 +struct isoent {
 +	/* Keep `rbnode' at the first member of struct isoent. */
 +	struct archive_rb_node	 rbnode;
 +
 +	struct isofile		*file;
 +
 +	struct isoent		*parent;
 +	/* A list of children.(use chnext) */
 +	struct {
 +		struct isoent	*first;
 +		struct isoent	**last;
 +		int		 cnt;
 +	}			 children;
 +	struct archive_rb_tree	 rbtree;
 +
 +	/* A list of sub directories.(use drnext) */
 +	struct {
 +		struct isoent	*first;
 +		struct isoent	**last;
 +		int		 cnt;
 +	}			 subdirs;
 +	/* A sorted list of sub directories. */
 +	struct isoent		**children_sorted;
 +	/* Used for managing struct isoent list. */
 +	struct isoent		*chnext;
 +	struct isoent		*drnext;
 +	struct isoent		*ptnext;
 +
 +	/*
 +	 * Used for making a Directory Record.
 +	 */
 +	int			 dir_number;
 +	struct {
 +		int		 vd;
 +		int		 self;
 +		int		 parent;
 +		int		 normal;
 +	}			 dr_len;
 +	uint32_t 		 dir_location;
 +	int			 dir_block;
 +
 +	/*
 +	 * Identifier:
 +	 *   on primary, ISO9660 file/directory name.
 +	 *   on joliet, UCS2 file/directory name.
 +	 * ext_off   : offset of identifier extension.
 +	 * ext_len   : length of identifier extension.
 +	 * id_len    : byte size of identifier.
 +	 *   on primary, this is ext_off + ext_len + version length.
 +	 *   on joliet, this is ext_off + ext_len.
 +	 * mb_len    : length of multibyte-character of identifier.
 +	 *   on primary, mb_len and id_len are always the same.
 +	 *   on joliet, mb_len and id_len are different.
 +	 */
 +	char			*identifier;
 +	int			 ext_off;
 +	int			 ext_len;
 +	int			 id_len;
 +	int			 mb_len;
 +
 +	/*
 +	 * Used for making a Rockridge extension.
 +	 * This is a part of Directory Records.
 +	 */
 +	struct isoent		*rr_parent;
 +	struct isoent		*rr_child;
 +
 +	/* Extra Record.(which we call in this source file)
 +	 * A maximum size of the Directory Record is 254.
 +	 * so, if generated RRIP data of a file cannot into a Directory
 +	 * Record because of its size, that surplus data relocate this
 +	 * Extra Record.
 +	 */
 +	struct {
 +		struct extr_rec	*first;
 +		struct extr_rec	**last;
 +		struct extr_rec	*current;
 +	}			 extr_rec_list;
 +
 +	int			 virtual:1;
 +	/* If set to one, this file type is a directory.
 +	 * A convenience flag to be used as
 +	 * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR".
 +	 */
 +	int			 dir:1;
 +};
 +
 +struct hardlink {
 +	struct archive_rb_node	 rbnode;
 +	int			 nlink;
 +	struct {
 +		struct isofile	*first;
 +		struct isofile	**last;
 +	}			 file_list;
 +};
 +
 +/*
 + * ISO writer options
 + */
 +struct iso_option {
 +	/*
 +	 * Usage  : abstract-file=<value>
 +	 * Type   : string, max 37 bytes
 +	 * Default: Not specified
 +	 * COMPAT : mkisofs -abstract <value>
 +	 *
 +	 * Specifies Abstract Filename.
 +	 * This file shall be described in the Root Directory
 +	 * and containing a abstract statement.
 +	 */
 +	unsigned int	 abstract_file:1;
 +#define OPT_ABSTRACT_FILE_DEFAULT	0	/* Not specified */
 +#define ABSTRACT_FILE_SIZE		37
 +
 +	/*
 +	 * Usage  : application-id=<value>
 +	 * Type   : string, max 128 bytes
 +	 * Default: Not specified
 +	 * COMPAT : mkisofs -A/-appid <value>.
 +	 *
 +	 * Specifies Application Identifier.
 +	 * If the first byte is set to '_'(5F), the remaining
 +	 * bytes of this option shall specify an identifier
 +	 * for a file containing the identification of the
 +	 * application.
 +	 * This file shall be described in the Root Directory.
 +	 */
 +	unsigned int	 application_id:1;
 +#define OPT_APPLICATION_ID_DEFAULT	0	/* Use default identifier */
 +#define APPLICATION_IDENTIFIER_SIZE	128
 +
 +	/*
 +	 * Usage : !allow-vernum
 +	 * Type  : boolean
 +	 * Default: Enabled
 +	 *	  : Violates the ISO9660 standard if disable.
 +	 * COMPAT: mkisofs -N
 +	 *
 +	 * Allow filenames to use version numbers.
 +	 */
 +	unsigned int	 allow_vernum:1;
 +#define OPT_ALLOW_VERNUM_DEFAULT	1	/* Enabled */
 +
 +	/*
 +	 * Usage  : biblio-file=<value>
 +	 * Type   : string, max 37 bytes
 +	 * Default: Not specified
 +	 * COMPAT : mkisofs -biblio <value>
 +	 *
 +	 * Specifies Bibliographic Filename.
 +	 * This file shall be described in the Root Directory
 +	 * and containing bibliographic records.
 +	 */
 +	unsigned int	 biblio_file:1;
 +#define OPT_BIBLIO_FILE_DEFAULT		0	/* Not specified */
 +#define BIBLIO_FILE_SIZE		37
 +
 +	/*
 +	 * Usage  : boot=<value>
 +	 * Type   : string
 +	 * Default: Not specified
 +	 * COMPAT : mkisofs -b/-eltorito-boot <value>
 +	 *
 +	 * Specifies "El Torito" boot image file to make
 +	 * a bootable CD.
 +	 */
 +	unsigned int	 boot:1;
 +#define OPT_BOOT_DEFAULT		0	/* Not specified */
 +
 +	/*
 +	 * Usage  : boot-catalog=<value>
 +	 * Type   : string
 +	 * Default: "boot.catalog"
 +	 * COMPAT : mkisofs -c/-eltorito-catalog <value>
 +	 *
 +	 * Specifies a fullpath of El Torito boot catalog.
 +	 */
 +	unsigned int	 boot_catalog:1;
 +#define OPT_BOOT_CATALOG_DEFAULT	0	/* Not specified */
 +
 +	/*
 +	 * Usage  : boot-info-table
 +	 * Type   : boolean
 +	 * Default: Disabled
 +	 * COMPAT : mkisofs -boot-info-table
 +	 *
 +	 * Modify the boot image file specified by `boot'
 +	 * option; ISO writer stores boot file information
 +	 * into the boot file in ISO image at offset 8
 +	 * through offset 64.
 +	 */
 +	unsigned int	 boot_info_table:1;
 +#define OPT_BOOT_INFO_TABLE_DEFAULT	0	/* Disabled */
 +
 +	/*
 +	 * Usage  : boot-load-seg=<value>
 +	 * Type   : hexadecimal
 +	 * Default: Not specified
 +	 * COMPAT : mkisofs -boot-load-seg <value>
 +	 *
 +	 * Specifies a load segment for boot image.
 +	 * This is used with no-emulation mode.
 +	 */
 +	unsigned int	 boot_load_seg:1;
 +#define OPT_BOOT_LOAD_SEG_DEFAULT	0	/* Not specified */
 +
 +	/*
 +	 * Usage  : boot-load-size=<value>
 +	 * Type   : decimal
 +	 * Default: Not specified
 +	 * COMPAT : mkisofs -boot-load-size <value>
 +	 *
 +	 * Specifies a sector count for boot image.
 +	 * This is used with no-emulation mode.
 +	 */
 +	unsigned int	 boot_load_size:1;
 +#define OPT_BOOT_LOAD_SIZE_DEFAULT	0	/* Not specified */
 +
 +	/*
 +	 * Usage  : boot-type=<boot-media-type>
 +	 *        : 'no-emulation' : 'no emulation' image
 +	 *        :           'fd' : floppy disk image
 +	 *        :    'hard-disk' : hard disk image
 +	 * Type   : string
 +	 * Default: Auto detect
 +	 *        : We check a size of boot image;
 +	 *        : If ths size is just 1.22M/1.44M/2.88M,
 +	 *        : we assume boot_type is 'fd';
 +	 *        : otherwise boot_type is 'no-emulation'.
 +	 * COMPAT :
 +	 *    boot=no-emulation
 +	 *	mkisofs -no-emul-boot
 +	 *    boot=fd
 +	 *	This is a default on the mkisofs.
 +	 *    boot=hard-disk
 +	 *	mkisofs -hard-disk-boot
 +	 *
 +	 * Specifies a type of "El Torito" boot image.
 +	 */
 +	unsigned int	 boot_type:2;
 +#define OPT_BOOT_TYPE_AUTO		0	/* auto detect		  */
 +#define OPT_BOOT_TYPE_NO_EMU		1	/* ``no emulation'' image */
 +#define OPT_BOOT_TYPE_FD		2	/* floppy disk image	  */
 +#define OPT_BOOT_TYPE_HARD_DISK		3	/* hard disk image	  */
 +#define OPT_BOOT_TYPE_DEFAULT		OPT_BOOT_TYPE_AUTO
 +
 +	/*
 +	 * Usage  : compression-level=<value>
 +	 * Type   : decimal
 +	 * Default: Not specified
 +	 * COMPAT : NONE
 +	 *
 +	 * Specifies compression level for option zisofs=direct.
 +	 */
 +	unsigned int	 compression_level:1;
 +#define OPT_COMPRESSION_LEVEL_DEFAULT	0	/* Not specified */
 +
 +	/*
 +	 * Usage  : copyright-file=<value>
 +	 * Type   : string, max 37 bytes
 +	 * Default: Not specified
 +	 * COMPAT : mkisofs -copyright <value>
 +	 *
 +	 * Specifies Copyright Filename.
 +	 * This file shall be described in the Root Directory
 +	 * and containing a copyright statement.
 +	 */
 +	unsigned int	 copyright_file:1;
 +#define OPT_COPYRIGHT_FILE_DEFAULT	0	/* Not specified */
 +#define COPYRIGHT_FILE_SIZE		37
 +
 +	/*
 +	 * Usage  : gid=<value>
 +	 * Type   : decimal
 +	 * Default: Not specified
 +	 * COMPAT : mkisofs -gid <value>
 +	 *
 +	 * Specifies a group id to rewrite the group id of all files.
 +	 */
 +	unsigned int	 gid:1;
 +#define OPT_GID_DEFAULT			0	/* Not specified */
 +
 +	/*
 +	 * Usage  : iso-level=[1234]
 +	 * Type   : decimal
 +	 * Default: 1
 +	 * COMPAT : mkisofs -iso-level <value>
 +	 *
 +	 * Specifies ISO9600 Level.
 +	 * Level 1: [DEFAULT]
 +	 *   - limits each file size less than 4Gi bytes;
 +	 *   - a File Name shall not contain more than eight
 +	 *     d-characters or eight d1-characters;
 +	 *   - a File Name Extension shall not contain more than
 +	 *     three d-characters or three d1-characters;
 +	 *   - a Directory Identifier shall not contain more
 +	 *     than eight d-characters or eight d1-characters.
 +	 * Level 2:
 +	 *   - limits each file size less than 4Giga bytes;
 +	 *   - a File Name shall not contain more than thirty
 +	 *     d-characters or thirty d1-characters;
 +	 *   - a File Name Extension shall not contain more than
 +	 *     thirty d-characters or thirty d1-characters;
 +	 *   - a Directory Identifier shall not contain more
 +	 *     than thirty-one d-characters or thirty-one
 +	 *     d1-characters.
 +	 * Level 3:
 +	 *   - no limit of file size; use multi extent.
 +	 * Level 4:
 +	 *   - this level 4 simulates mkisofs option
 +	 *     '-iso-level 4';
 +	 *   - crate a enhanced volume as mkisofs doing;
 +	 *   - allow a File Name to have leading dot;
 +	 *   - allow a File Name to have all ASCII letters;
 +	 *   - allow a File Name to have multiple dots;
 +	 *   - allow more then 8 depths of directory trees;
 +	 *   - disable a version number to a File Name;
 +	 *   - disable a forced period to the tail of a File Name;
 +	 *   - the maxinum length of files and directories is raised to 193.
 +	 *     if rockridge option is disabled, raised to 207.
 +	 */
 +	unsigned int	 iso_level:3;
 +#define OPT_ISO_LEVEL_DEFAULT		1	/* ISO Level 1 */
 +
 +	/*
 +	 * Usage  : joliet[=long]
 +	 *        : !joliet
 +	 *        :   Do not generate Joliet Volume and Records.
 +	 *        : joliet [DEFAULT]
 +	 *        :   Generates Joliet Volume and Directory Records.
 +	 *        :   [COMPAT: mkisofs -J/-joliet]
 +	 *        : joliet=long
 +	 *        :   The joliet filenames are up to 103 Unicode
 +	 *        :   characters.
 +	 *        :   This option breaks the Joliet specification.
 +	 *        :   [COMPAT: mkisofs -J -joliet-long]
 +	 * Type   : boolean/string
 +	 * Default: Enabled
 +	 * COMPAT : mkisofs -J / -joliet-long
 +	 *
 +	 * Generates Joliet Volume and Directory Records.
 +	 */
 +	unsigned int	 joliet:2;
 +#define OPT_JOLIET_DISABLE		0	/* Not generate Joliet Records. */
 +#define OPT_JOLIET_ENABLE		1	/* Generate Joliet Records.  */
 +#define OPT_JOLIET_LONGNAME		2	/* Use long joliet filenames.*/
 +#define OPT_JOLIET_DEFAULT		OPT_JOLIET_ENABLE
 +
 +	/*
 +	 * Usage  : !limit-depth
 +	 * Type   : boolean
 +	 * Default: Enabled
 +	 *	  : Violates the ISO9660 standard if disable.
 +	 * COMPAT : mkisofs -D/-disable-deep-relocation
 +	 *
 +	 * The number of levels in hierarchy cannot exceed eight.
 +	 */
 +	unsigned int	 limit_depth:1;
 +#define OPT_LIMIT_DEPTH_DEFAULT		1	/* Enabled */
 +
 +	/*
 +	 * Usage  : !limit-dirs
 +	 * Type   : boolean
 +	 * Default: Enabled
 +	 *	  : Violates the ISO9660 standard if disable.
 +	 * COMPAT : mkisofs -no-limit-pathtables
 +	 *
 +	 * Limits the number of directories less than 65536 due
 +	 * to the size of the Parent Directory Number of Path
 +	 * Table.
 +	 */
 +	unsigned int	 limit_dirs:1;
 +#define OPT_LIMIT_DIRS_DEFAULT		1	/* Enabled */
 +
 +	/*
 +	 * Usage  : !pad
 +	 * Type   : boolean
 +	 * Default: Enabled
 +	 * COMPAT : -pad/-no-pad
 +	 *
 +	 * Pads the end of the ISO image by null of 300Ki bytes.
 +	 */
 +	unsigned int	 pad:1;
 +#define OPT_PAD_DEFAULT			1	/* Enabled */
 +
 +	/*
 +	 * Usage  : publisher=<value>
 +	 * Type   : string, max 128 bytes
 +	 * Default: Not specified
 +	 * COMPAT : mkisofs -publisher <value>
 +	 *
 +	 * Specifies Publisher Identifier.
 +	 * If the first byte is set to '_'(5F), the remaining
 +	 * bytes of this option shall specify an identifier
 +	 * for a file containing the identification of the user.
 +	 * This file shall be described in the Root Directory.
 +	 */
 +	unsigned int	 publisher:1;
 +#define OPT_PUBLISHER_DEFAULT		0	/* Not specified */
 +#define PUBLISHER_IDENTIFIER_SIZE	128
 +
 +	/*
 +	 * Usage  : rockridge
 +	 *        : !rockridge
 +	 *        :    disable to generate SUSP and RR records.
 +	 *        : rockridge
 +	 *        :    the same as 'rockridge=useful'.
 +	 *        : rockridge=strict
 +	 *        :    generate SUSP and RR records.
 +	 *        :    [COMPAT: mkisofs -R]
 +	 *        : rockridge=useful [DEFAULT]
 +	 *        :    generate SUSP and RR records.
 +	 *        :    [COMPAT: mkisofs -r]
 +	 *        :    NOTE  Our rockridge=useful option does not set a zero
 +	 *        :          to uid and gid, you should use application
 +	 *        :          option such as --gid,--gname,--uid and --uname
 +	 *        :          badtar options instead.
 +	 * Type   : boolean/string
 +	 * Default: Enabled as rockridge=useful
 +	 * COMPAT : mkisofs -r / -R
 +	 *
 +	 * Generates SUSP and RR records.
 +	 */
 +	unsigned int	 rr:2;
 +#define OPT_RR_DISABLED			0
 +#define OPT_RR_STRICT			1
 +#define OPT_RR_USEFUL			2
 +#define OPT_RR_DEFAULT			OPT_RR_USEFUL
 +
 +	/*
 +	 * Usage  : volume-id=<value>
 +	 * Type   : string, max 32 bytes
 +	 * Default: Not specified
 +	 * COMPAT : mkisofs -V <value>
 +	 *
 +	 * Specifies Volume Identifier.
 +	 */
 +	unsigned int	 volume_id:1;
 +#define OPT_VOLUME_ID_DEFAULT		0	/* Use default identifier */
 +#define VOLUME_IDENTIFIER_SIZE		32
 +
 +	/*
 +	 * Usage  : !zisofs [DEFAULT] 
 +	 *        :    Disable to generate RRIP 'ZF' extension.
 +	 *        : zisofs
 +	 *        :    Make files zisofs file and generate RRIP 'ZF'
 + 	 *        :    extension. So you do not need mkzftree utility
 +	 *        :    for making zisofs.
 +	 *        :    When the file size is less than one Logical Block
 +	 *        :    size, that file will not zisofs'ed since it does
 +	 *        :    reduece an ISO-image size.
 +	 *        :
 +	 *        :    When you specify option 'boot=<boot-image>', that
 +	 *        :    'boot-image' file won't be converted to zisofs file.
 +	 * Type   : boolean
 +	 * Default: Disabled
 +	 *
 +	 * Generates RRIP 'ZF' System Use Entry.
 +	 */
 +	unsigned int	 zisofs:1;
 +#define OPT_ZISOFS_DISABLED		0
 +#define OPT_ZISOFS_DIRECT		1
 +#define OPT_ZISOFS_DEFAULT		OPT_ZISOFS_DISABLED
 +
 +};
 +
 +struct iso9660 {
 +	/* The creation time of ISO image. */
 +	time_t			 birth_time;
 +	/* A file stream of a temporary file, which file contents
 +	 * save to until ISO iamge can be created. */
 +	int			 temp_fd;
 +
 +	struct isofile		*cur_file;
 +	struct isoent		*cur_dirent;
 +	struct archive_string	 cur_dirstr;
 +	uint64_t		 bytes_remaining;
 +	int			 need_multi_extent;
 +
 +	/* Temporary string buffer for Joliet extension. */ 
 +	struct archive_string	 utf16be;
 +	struct archive_string	 mbs;
 +
 +	struct archive_string_conv *sconv_to_utf16be;
 +	struct archive_string_conv *sconv_from_utf16be;
 +
 +	/* A list of all of struct isofile entries. */
 +	struct {
 +		struct isofile	*first;
 +		struct isofile	**last;
 +	}			 all_file_list;
 +
 +	/* A list of struct isofile entries which have its
 +	 * contents and are not a directory, a hardlined file
 +	 * and a symlink file. */
 +	struct {
 +		struct isofile	*first;
 +		struct isofile	**last;
 +	}			 data_file_list;
 +
 +	/* Used for managing to find hardlinking files. */
 +	struct archive_rb_tree	 hardlink_rbtree;
 +
 +	/* Used for making the Path Table Record. */
 +	struct vdd {
 +		/* the root of entry tree. */
 +		struct isoent	*rootent;
 +		enum vdd_type {
 +			VDD_PRIMARY,
 +			VDD_JOLIET,
 +			VDD_ENHANCED
 +		} vdd_type;
 +
 +		struct path_table {
 +			struct isoent		*first;
 +			struct isoent		**last;
 +			struct isoent		**sorted;
 +			int			 cnt;
 +		} *pathtbl;
 +		int				 max_depth;
 +
 +		int		 path_table_block;
 +		int		 path_table_size;
 +		int		 location_type_L_path_table;
 +		int		 location_type_M_path_table;
 +		int		 total_dir_block;
 +	} primary, joliet;
 +
 +	/* Used for making a Volume Descriptor. */
 +	int			 volume_space_size;
 +	int			 volume_sequence_number;
 +	int			 total_file_block;
 +	struct archive_string	 volume_identifier;
 +	struct archive_string	 publisher_identifier;
 +	struct archive_string	 data_preparer_identifier;
 +	struct archive_string	 application_identifier;
 +	struct archive_string	 copyright_file_identifier;
 +	struct archive_string	 abstract_file_identifier;
 +	struct archive_string	 bibliographic_file_identifier;
 +
 +	/* Used for making rockridge extensions. */
 +	int			 location_rrip_er;
 +
 +	/* Used for making zisofs. */
 +	struct {
 +		int		 detect_magic:1;
 +		int		 making:1;
 +		int		 allzero:1;
 +		unsigned char	 magic_buffer[64];
 +		int		 magic_cnt;
 +
 +#ifdef HAVE_ZLIB_H
 +		/*
 +		 * Copy a compressed file to iso9660.zisofs.temp_fd
 +		 * and also copy a uncompressed file(original file) to
 +		 * iso9660.temp_fd . If the number of logical block
 +		 * of the compressed file is less than the number of
 +		 * logical block of the uncompressed file, use it and
 +		 * remove the copy of the uncompressed file.
 +		 * but if not, we use uncompressed file and remove
 +		 * the copy of the compressed file.
 +		 */
 +		uint32_t	*block_pointers;
 +		size_t		 block_pointers_allocated;
 +		int		 block_pointers_cnt;
 +		int		 block_pointers_idx;
 +		int64_t		 total_size;
 +		int64_t		 block_offset;
 +
 +		z_stream	 stream;
 +		int		 stream_valid;
 +		int64_t		 remaining;
 +		int		 compression_level;
 +#endif
 +	} zisofs;
 +
 +	struct isoent		*directories_too_deep;
 +	int			 dircnt_max;
 +
 +	/* Write buffer. */
 +#define wb_buffmax()	(LOGICAL_BLOCK_SIZE * 32)
 +#define wb_remaining(a)	(((struct iso9660 *)(a)->format_data)->wbuff_remaining)
 +#define wb_offset(a)	(((struct iso9660 *)(a)->format_data)->wbuff_offset \
 +		+ wb_buffmax() - wb_remaining(a))
 +	unsigned char		 wbuff[LOGICAL_BLOCK_SIZE * 32];
 +	size_t			 wbuff_remaining;
 +	enum {
 +		WB_TO_STREAM,
 +		WB_TO_TEMP
 +	} 			 wbuff_type;
 +	int64_t			 wbuff_offset;
 +	int64_t			 wbuff_written;
 +	int64_t			 wbuff_tail;
 +
 +	/* 'El Torito' boot data. */
 +	struct {
 +		/* boot catalog file */
 +		struct archive_string	 catalog_filename;
 +		struct isoent		*catalog;
 +		/* boot image file */
 +		struct archive_string	 boot_filename;
 +		struct isoent		*boot;
 +
 +		unsigned char		 platform_id;
 +#define BOOT_PLATFORM_X86	0
 +#define BOOT_PLATFORM_PPC	1
 +#define BOOT_PLATFORM_MAC	2
 +		struct archive_string	 id;
 +		unsigned char		 media_type;
 +#define BOOT_MEDIA_NO_EMULATION		0
 +#define BOOT_MEDIA_1_2M_DISKETTE	1
 +#define BOOT_MEDIA_1_44M_DISKETTE	2
 +#define BOOT_MEDIA_2_88M_DISKETTE	3
 +#define BOOT_MEDIA_HARD_DISK		4
 +		unsigned char		 system_type;
 +		uint16_t		 boot_load_seg;
 +		uint16_t		 boot_load_size;
 +#define BOOT_LOAD_SIZE		4
 +	} el_torito;
 +
 +	struct iso_option	 opt;
 +};
 +
 +/*
 + * Types of Volume Descriptor
 + */
 +enum VD_type {
 +	VDT_BOOT_RECORD=0,	/* Boot Record Volume Descriptor 	*/
 +	VDT_PRIMARY=1,		/* Primary Volume Descriptor		*/
 +	VDT_SUPPLEMENTARY=2,	/* Supplementary Volume Descriptor	*/
 +	VDT_TERMINATOR=255	/* Volume Descriptor Set Terminator	*/
 +};
 +
 +/*
 + * Types of Directory Record
 + */
 +enum dir_rec_type {
 +	DIR_REC_VD,		/* Stored in Volume Descriptor.	*/
 +	DIR_REC_SELF,		/* Stored as Current Directory.	*/
 +	DIR_REC_PARENT,		/* Stored as Parent Directory.	*/
 +	DIR_REC_NORMAL 		/* Stored as Child.		*/
 +};
 +
 +/*
 + * Kinds of Volume Descriptor Character
 + */
 +enum vdc {
 +	VDC_STD,
 +	VDC_LOWERCASE,
 +	VDC_UCS2,
 +	VDC_UCS2_DIRECT
 +};
 +
 +/*
 + * IDentifier Resolver.
 + * Used for resolving duplicated filenames.
 + */
 +struct idr {
 +	struct idrent {
 +		struct archive_rb_node	rbnode;
 +		/* Used in wait_list. */
 +		struct idrent		*wnext;
 +		struct idrent		*avail;
 +
 +		struct isoent		*isoent;
 +		int			 weight;
 +		int			 noff;
 +		int			 rename_num;
 +	} *idrent_pool;
 +
 +	struct archive_rb_tree		 rbtree;
 +
 +	struct {
 +		struct idrent		*first;
 +		struct idrent		**last;
 +	} wait_list;
 +
 +	int				 pool_size;
 +	int				 pool_idx;
 +	int				 num_size;
 +	int				 null_size;
 +
 +	char				 char_map[0x80];
 +};
 +
 +enum char_type {
 +	A_CHAR,
 +	D_CHAR
 +};
 +
 +
 +static int	iso9660_options(struct archive_write *,
 +		    const char *, const char *);
 +static int	iso9660_write_header(struct archive_write *,
 +		    struct archive_entry *);
 +static ssize_t	iso9660_write_data(struct archive_write *,
 +		    const void *, size_t);
 +static int	iso9660_finish_entry(struct archive_write *);
 +static int	iso9660_close(struct archive_write *);
 +static int	iso9660_free(struct archive_write *);
 +
 +static void	get_system_identitier(char *, size_t);
 +static void	set_str(unsigned char *, const char *, size_t, char,
 +		    const char *);
 +static inline int joliet_allowed_char(unsigned char, unsigned char);
 +static int	set_str_utf16be(struct archive_write *, unsigned char *,
 +			const char *, size_t, uint16_t, enum vdc);
 +static int	set_str_a_characters_bp(struct archive_write *,
 +			unsigned char *, int, int, const char *, enum vdc);
 +static int	set_str_d_characters_bp(struct archive_write *,
 +			unsigned char *, int, int, const char *, enum  vdc);
 +static void	set_VD_bp(unsigned char *, enum VD_type, unsigned char);
 +static inline void set_unused_field_bp(unsigned char *, int, int);
 +
 +static unsigned char *extra_open_record(unsigned char *, int,
 +		    struct isoent *, struct ctl_extr_rec *);
 +static void	extra_close_record(struct ctl_extr_rec *, int);
 +static unsigned char * extra_next_record(struct ctl_extr_rec *, int);
 +static unsigned char *extra_get_record(struct isoent *, int *, int *, int *);
 +static void	extra_tell_used_size(struct ctl_extr_rec *, int);
 +static int	extra_setup_location(struct isoent *, int);
 +static int	set_directory_record_rr(unsigned char *, int,
 +		    struct isoent *, struct iso9660 *, enum dir_rec_type);
 +static int	set_directory_record(unsigned char *, size_t,
 +		    struct isoent *, struct iso9660 *, enum dir_rec_type,
 +		    enum vdd_type);
 +static inline int get_dir_rec_size(struct iso9660 *, struct isoent *,
 +		    enum dir_rec_type, enum vdd_type);
 +static inline unsigned char *wb_buffptr(struct archive_write *);
 +static int	wb_write_out(struct archive_write *);
 +static int	wb_consume(struct archive_write *, size_t);
 +#ifdef HAVE_ZLIB_H
 +static int	wb_set_offset(struct archive_write *, int64_t);
 +#endif
 +static int	write_null(struct archive_write *, size_t);
 +static int	write_VD_terminator(struct archive_write *);
 +static int	set_file_identifier(unsigned char *, int, int, enum vdc,
 +		    struct archive_write *, struct vdd *,
 +		    struct archive_string *, const char *, int,
 +		    enum char_type);
 +static int	write_VD(struct archive_write *, struct vdd *);
 +static int	write_VD_boot_record(struct archive_write *);
 +static int	write_information_block(struct archive_write *);
 +static int	write_path_table(struct archive_write *, int,
 +		    struct vdd *);
 +static int	write_directory_descriptors(struct archive_write *,
 +		    struct vdd *);
 +static int	write_file_descriptors(struct archive_write *);
 +static int	write_rr_ER(struct archive_write *);
 +static void	calculate_path_table_size(struct vdd *);
 +
 +static void	isofile_init_entry_list(struct iso9660 *);
 +static void	isofile_add_entry(struct iso9660 *, struct isofile *);
 +static void	isofile_free_all_entries(struct iso9660 *);
 +static void	isofile_init_entry_data_file_list(struct iso9660 *);
 +static void	isofile_add_data_file(struct iso9660 *, struct isofile *);
 +static struct isofile * isofile_new(struct archive_write *,
 +		    struct archive_entry *);
 +static void	isofile_free(struct isofile *);
 +static int	isofile_gen_utility_names(struct archive_write *,
 +		    struct isofile *);
 +static int	isofile_register_hardlink(struct archive_write *,
 +		    struct isofile *);
 +static void	isofile_connect_hardlink_files(struct iso9660 *);
 +static void	isofile_init_hardlinks(struct iso9660 *);
 +static void	isofile_free_hardlinks(struct iso9660 *);
 +
 +static struct isoent *isoent_new(struct isofile *);
 +static int	isoent_clone_tree(struct archive_write *,
 +		    struct isoent **, struct isoent *);
 +static void	_isoent_free(struct isoent *isoent);
 +static void	isoent_free_all(struct isoent *);
 +static struct isoent * isoent_create_virtual_dir(struct archive_write *,
 +		    struct iso9660 *, const char *);
 +static int	isoent_cmp_node(const struct archive_rb_node *,
 +		    const struct archive_rb_node *);
 +static int	isoent_cmp_key(const struct archive_rb_node *,
 +		    const void *);
 +static int	isoent_add_child_head(struct isoent *, struct isoent *);
 +static int	isoent_add_child_tail(struct isoent *, struct isoent *);
 +static void	isoent_remove_child(struct isoent *, struct isoent *);
 +static void	isoent_setup_directory_location(struct iso9660 *,
 +		    int, struct vdd *);
 +static void	isoent_setup_file_location(struct iso9660 *, int);
 +static int	get_path_component(char *, size_t, const char *);
 +static int	isoent_tree(struct archive_write *, struct isoent **);
 +static struct isoent *isoent_find_child(struct isoent *, const char *);
 +static struct isoent *isoent_find_entry(struct isoent *, const char *);
 +static void	idr_relaxed_filenames(char *);
 +static void	idr_init(struct iso9660 *, struct vdd *, struct idr *);
 +static void	idr_cleanup(struct idr *);
 +static int	idr_ensure_poolsize(struct archive_write *, struct idr *,
 +		    int);
 +static int	idr_start(struct archive_write *, struct idr *,
 +		    int, int, int, int, const struct archive_rb_tree_ops *);
 +static void	idr_register(struct idr *, struct isoent *, int,
 +		    int);
 +static void	idr_extend_identifier(struct idrent *, int, int);
 +static void	idr_resolve(struct idr *, void (*)(unsigned char *, int));
 +static void	idr_set_num(unsigned char *, int);
 +static void	idr_set_num_beutf16(unsigned char *, int);
 +static int	isoent_gen_iso9660_identifier(struct archive_write *,
 +		    struct isoent *, struct idr *);
 +static int	isoent_gen_joliet_identifier(struct archive_write *,
 +		    struct isoent *, struct idr *);
 +static int	isoent_cmp_iso9660_identifier(const struct isoent *,
 +		    const struct isoent *);
 +static int	isoent_cmp_node_iso9660(const struct archive_rb_node *,
 +		    const struct archive_rb_node *);
 +static int	isoent_cmp_key_iso9660(const struct archive_rb_node *,
 +		    const void *);
 +static int	isoent_cmp_joliet_identifier(const struct isoent *,
 +		    const struct isoent *);
 +static int	isoent_cmp_node_joliet(const struct archive_rb_node *,
 +		    const struct archive_rb_node *);
 +static int	isoent_cmp_key_joliet(const struct archive_rb_node *,
 +		    const void *);
 +static inline void path_table_add_entry(struct path_table *, struct isoent *);
 +static inline struct isoent * path_table_last_entry(struct path_table *);
 +static int	isoent_make_path_table(struct archive_write *);
 +static int	isoent_find_out_boot_file(struct archive_write *,
 +		    struct isoent *);
 +static int	isoent_create_boot_catalog(struct archive_write *,
 +		    struct isoent *);
 +static size_t	fd_boot_image_size(int);
 +static int	make_boot_catalog(struct archive_write *);
 +static int	setup_boot_information(struct archive_write *);
 +
 +static int	zisofs_init(struct archive_write *, struct isofile *);
 +static void	zisofs_detect_magic(struct archive_write *,
 +		    const void *, size_t);
 +static int	zisofs_write_to_temp(struct archive_write *,
 +		    const void *, size_t);
 +static int	zisofs_finish_entry(struct archive_write *);
 +static int	zisofs_rewind_boot_file(struct archive_write *);
 +static int	zisofs_free(struct archive_write *);
 +
 +int
 +archive_write_set_format_iso9660(struct archive *_a)
 +{
 +	struct archive_write *a = (struct archive_write *)_a;
 +	struct iso9660 *iso9660;
 +
 +	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
 +	    ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660");
 +
 +	/* If another format was already registered, unregister it. */
 +	if (a->format_free != NULL)
 +		(a->format_free)(a);
 +
 +	iso9660 = calloc(1, sizeof(*iso9660));
 +	if (iso9660 == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate iso9660 data");
 +		return (ARCHIVE_FATAL);
 +	}
 +	iso9660->birth_time = 0;
 +	iso9660->temp_fd = -1;
 +	iso9660->cur_file = NULL;
 +	iso9660->primary.max_depth = 0;
 +	iso9660->primary.vdd_type = VDD_PRIMARY;
 +	iso9660->primary.pathtbl = NULL;
 +	iso9660->joliet.rootent = NULL;
 +	iso9660->joliet.max_depth = 0;
 +	iso9660->joliet.vdd_type = VDD_JOLIET;
 +	iso9660->joliet.pathtbl = NULL;
 +	isofile_init_entry_list(iso9660);
 +	isofile_init_entry_data_file_list(iso9660);
 +	isofile_init_hardlinks(iso9660);
 +	iso9660->directories_too_deep = NULL;
 +	iso9660->dircnt_max = 1;
 +	iso9660->wbuff_remaining = wb_buffmax();
 +	iso9660->wbuff_type = WB_TO_TEMP;
 +	iso9660->wbuff_offset = 0;
 +	iso9660->wbuff_written = 0;
 +	iso9660->wbuff_tail = 0;
 +	archive_string_init(&(iso9660->utf16be));
 +	archive_string_init(&(iso9660->mbs));
 +
 +	/*
 +	 * Init Identifiers used for PVD and SVD.
 +	 */
 +	archive_string_init(&(iso9660->volume_identifier));
 +	archive_strcpy(&(iso9660->volume_identifier), "CDROM");
 +	archive_string_init(&(iso9660->publisher_identifier));
 +	archive_string_init(&(iso9660->data_preparer_identifier));
 +	archive_string_init(&(iso9660->application_identifier));
 +	archive_strcpy(&(iso9660->application_identifier),
 +	    archive_version_string());
 +	archive_string_init(&(iso9660->copyright_file_identifier));
 +	archive_string_init(&(iso9660->abstract_file_identifier));
 +	archive_string_init(&(iso9660->bibliographic_file_identifier));
 +
 +	/*
 +	 * Init El Torito bootable CD variables.
 +	 */
 +	archive_string_init(&(iso9660->el_torito.catalog_filename));
 +	iso9660->el_torito.catalog = NULL;
 +	/* Set default file name of boot catalog  */
 +	archive_strcpy(&(iso9660->el_torito.catalog_filename),
 +	    "boot.catalog");
 +	archive_string_init(&(iso9660->el_torito.boot_filename));
 +	iso9660->el_torito.boot = NULL;
 +	iso9660->el_torito.platform_id = BOOT_PLATFORM_X86;
 +	archive_string_init(&(iso9660->el_torito.id));
 +	iso9660->el_torito.boot_load_seg = 0;
 +	iso9660->el_torito.boot_load_size = BOOT_LOAD_SIZE;
 +
 +	/*
 +	 * Init zisofs variables.
 +	 */
 +#ifdef HAVE_ZLIB_H
 +	iso9660->zisofs.block_pointers = NULL;
 +	iso9660->zisofs.block_pointers_allocated = 0;
 +	iso9660->zisofs.stream_valid = 0;
 +	iso9660->zisofs.compression_level = 9;
 +	memset(&(iso9660->zisofs.stream), 0,
 +	    sizeof(iso9660->zisofs.stream));
 +#endif
 +
 +	/*
 +	 * Set default value of iso9660 options.
 +	 */
 +	iso9660->opt.abstract_file = OPT_ABSTRACT_FILE_DEFAULT;
 +	iso9660->opt.application_id = OPT_APPLICATION_ID_DEFAULT;
 +	iso9660->opt.allow_vernum = OPT_ALLOW_VERNUM_DEFAULT;
 +	iso9660->opt.biblio_file = OPT_BIBLIO_FILE_DEFAULT;
 +	iso9660->opt.boot = OPT_BOOT_DEFAULT;
 +	iso9660->opt.boot_catalog = OPT_BOOT_CATALOG_DEFAULT;
 +	iso9660->opt.boot_info_table = OPT_BOOT_INFO_TABLE_DEFAULT;
 +	iso9660->opt.boot_load_seg = OPT_BOOT_LOAD_SEG_DEFAULT;
 +	iso9660->opt.boot_load_size = OPT_BOOT_LOAD_SIZE_DEFAULT;
 +	iso9660->opt.boot_type = OPT_BOOT_TYPE_DEFAULT;
 +	iso9660->opt.compression_level = OPT_COMPRESSION_LEVEL_DEFAULT;
 +	iso9660->opt.copyright_file = OPT_COPYRIGHT_FILE_DEFAULT;
 +	iso9660->opt.iso_level = OPT_ISO_LEVEL_DEFAULT;
 +	iso9660->opt.joliet = OPT_JOLIET_DEFAULT;
 +	iso9660->opt.limit_depth = OPT_LIMIT_DEPTH_DEFAULT;
 +	iso9660->opt.limit_dirs = OPT_LIMIT_DIRS_DEFAULT;
 +	iso9660->opt.pad = OPT_PAD_DEFAULT;
 +	iso9660->opt.publisher = OPT_PUBLISHER_DEFAULT;
 +	iso9660->opt.rr = OPT_RR_DEFAULT;
 +	iso9660->opt.volume_id = OPT_VOLUME_ID_DEFAULT;
 +	iso9660->opt.zisofs = OPT_ZISOFS_DEFAULT;
 +
 +	/* Create the root directory. */
 +	iso9660->primary.rootent =
 +	    isoent_create_virtual_dir(a, iso9660, "");
 +	if (iso9660->primary.rootent == NULL) {
 +		free(iso9660);
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate memory");
 +		return (ARCHIVE_FATAL);
 +	}
 +	iso9660->primary.rootent->parent = iso9660->primary.rootent;
 +	iso9660->cur_dirent = iso9660->primary.rootent;
 +	archive_string_init(&(iso9660->cur_dirstr));
 +	archive_string_ensure(&(iso9660->cur_dirstr), 1);
 +	iso9660->cur_dirstr.s[0] = 0;
 +	iso9660->sconv_to_utf16be = NULL;
 +	iso9660->sconv_from_utf16be = NULL;
 +
 +	a->format_data = iso9660;
 +	a->format_name = "iso9660";
 +	a->format_options = iso9660_options;
 +	a->format_write_header = iso9660_write_header;
 +	a->format_write_data = iso9660_write_data;
 +	a->format_finish_entry = iso9660_finish_entry;
 +	a->format_close = iso9660_close;
 +	a->format_free = iso9660_free;
 +	a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
 +	a->archive.archive_format_name = "ISO9660";
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +get_str_opt(struct archive_write *a, struct archive_string *s,
 +    size_t maxsize, const char *key, const char *value)
 +{
 +
 +	if (strlen(value) > maxsize) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Value is longer than %zu characters "
 +		    "for option ``%s''", maxsize, key);
 +		return (ARCHIVE_FATAL);
 +	}
 +	archive_strcpy(s, value);
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +get_num_opt(struct archive_write *a, int *num, int high, int low,
 +    const char *key, const char *value)
 +{
 +	const char *p = value;
 +	int data = 0;
 +	int neg = 0;
 +
 +	if (p == NULL) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Invalid value(empty) for option ``%s''", key);
 +		return (ARCHIVE_FATAL);
 +	}
 +	if (*p == '-') {
 +		neg = 1;
 +		p++;
 +	}
 +	while (*p) {
 +		if (*p >= '0' && *p <= '9')
 +			data = data * 10 + *p - '0';
 +		else {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Invalid value for option ``%s''", key);
 +			return (ARCHIVE_FATAL);
 +		}
 +		if (data > high) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Invalid value(over %d) for "
 +			    "option ``%s''", high, key);
 +			return (ARCHIVE_FATAL);
 +		}
 +		if (data < low) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Invalid value(under %d) for "
 +			    "option ``%s''", low, key);
 +			return (ARCHIVE_FATAL);
 +		}
 +		p++;
 +	}
 +	if (neg)
 +		data *= -1;
 +	*num = data;
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +iso9660_options(struct archive_write *a, const char *key, const char *value)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	const char *p;
 +	int r;
 +
 +	switch (key[0]) {
 +	case 'a':
 +		if (strcmp(key, "abstract-file") == 0) {
 +			r = get_str_opt(a,
 +			    &(iso9660->abstract_file_identifier),
 +			    ABSTRACT_FILE_SIZE, key, value);
 +			iso9660->opt.abstract_file = r == ARCHIVE_OK;
 +			return (r);
 +		}
 +		if (strcmp(key, "application-id") == 0) {
 +			r = get_str_opt(a,
 +			    &(iso9660->application_identifier),
 +			    APPLICATION_IDENTIFIER_SIZE, key, value);
 +			iso9660->opt.application_id = r == ARCHIVE_OK;
 +			return (r);
 +		}
 +		if (strcmp(key, "allow-vernum") == 0) {
 +			iso9660->opt.allow_vernum = value != NULL;
 +			return (ARCHIVE_OK);
 +		}
 +		break;
 +	case 'b':
 +		if (strcmp(key, "biblio-file") == 0) {
 +			r = get_str_opt(a,
 +			    &(iso9660->bibliographic_file_identifier),
 +			    BIBLIO_FILE_SIZE, key, value);
 +			iso9660->opt.biblio_file = r == ARCHIVE_OK;
 +			return (r);
 +		}
 +		if (strcmp(key, "boot") == 0) {
 +			if (value == NULL)
 +				iso9660->opt.boot = 0;
 +			else {
 +				iso9660->opt.boot = 1;
 +				archive_strcpy(
 +				    &(iso9660->el_torito.boot_filename),
 +				    value);
 +			}
 +			return (ARCHIVE_OK);
 +		}
 +		if (strcmp(key, "boot-catalog") == 0) {
 +			r = get_str_opt(a,
 +			    &(iso9660->el_torito.catalog_filename),
 +			    1024, key, value);
 +			iso9660->opt.boot_catalog = r == ARCHIVE_OK;
 +			return (r);
 +		}
 +		if (strcmp(key, "boot-info-table") == 0) {
 +			iso9660->opt.boot_info_table = value != NULL;
 +			return (ARCHIVE_OK);
 +		}
 +		if (strcmp(key, "boot-load-seg") == 0) {
 +			uint32_t seg;
 +
 +			iso9660->opt.boot_load_seg = 0;
 +			if (value == NULL)
 +				goto invalid_value;
 +			seg = 0;
 +			p = value;
 +			if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
 +				p += 2;
 +			while (*p) {
 +				if (seg)
 +					seg <<= 4;
 +				if (*p >= 'A' && *p <= 'F')
 +					seg += *p - 'A' + 0x0a;
 +				else if (*p >= 'a' && *p <= 'f')
 +					seg += *p - 'a' + 0x0a;
 +				else if (*p >= '0' && *p <= '9')
 +					seg += *p - '0';
 +				else
 +					goto invalid_value;
 +				if (seg > 0xffff) {
 +					archive_set_error(&a->archive,
 +					    ARCHIVE_ERRNO_MISC,
 +					    "Invalid value(over 0xffff) for "
 +					    "option ``%s''", key);
 +					return (ARCHIVE_FATAL);
 +				}
 +				p++;
 +			}
 +			iso9660->el_torito.boot_load_seg = (uint16_t)seg;
 +			iso9660->opt.boot_load_seg = 1;
 +			return (ARCHIVE_OK);
 +		}
 +		if (strcmp(key, "boot-load-size") == 0) {
 +			int num = 0;
 +			r = get_num_opt(a, &num, 0xffff, 1, key, value);
 +			iso9660->opt.boot_load_size = r == ARCHIVE_OK;
 +			if (r != ARCHIVE_OK)
 +				return (ARCHIVE_FATAL);
 +			iso9660->el_torito.boot_load_size = (uint16_t)num;
 +			return (ARCHIVE_OK);
 +		}
 +		if (strcmp(key, "boot-type") == 0) {
 +			if (value == NULL)
 +				goto invalid_value;
 +			if (strcmp(value, "no-emulation") == 0)
 +				iso9660->opt.boot_type = OPT_BOOT_TYPE_NO_EMU;
 +			else if (strcmp(value, "fd") == 0)
 +				iso9660->opt.boot_type = OPT_BOOT_TYPE_FD;
 +			else if (strcmp(value, "hard-disk") == 0)
 +				iso9660->opt.boot_type = OPT_BOOT_TYPE_HARD_DISK;
 +			else
 +				goto invalid_value;
 +			return (ARCHIVE_OK);
 +		}
 +		break;
 +	case 'c':
 +		if (strcmp(key, "compression-level") == 0) {
 +#ifdef HAVE_ZLIB_H
 +			if (value == NULL ||
 +			    !(value[0] >= '0' && value[0] <= '9') ||
 +			    value[1] != '\0')
 +				goto invalid_value;
 +                	iso9660->zisofs.compression_level = value[0] - '0';
 +			iso9660->opt.compression_level = 1;
 +                	return (ARCHIVE_OK);
 +#else
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_MISC,
 +			    "Option ``%s'' "
 +			    "is not supported on this platform.", key);
 +			return (ARCHIVE_FATAL);
 +#endif
 +		}
 +		if (strcmp(key, "copyright-file") == 0) {
 +			r = get_str_opt(a,
 +			    &(iso9660->copyright_file_identifier),
 +			    COPYRIGHT_FILE_SIZE, key, value);
 +			iso9660->opt.copyright_file = r == ARCHIVE_OK;
 +			return (r);
 +		}
 +#ifdef DEBUG
 +		/* Specifies Volume creation date and time;
 +		 * year(4),month(2),day(2),hour(2),minute(2),second(2).
 +		 * e.g. "20090929033757"
 +		 */
 +		if (strcmp(key, "creation") == 0) {
 +			struct tm tm;
 +			char buf[5];
 +
 +			p = value;
 +			if (p == NULL || strlen(p) < 14)
 +				goto invalid_value;
 +			memset(&tm, 0, sizeof(tm));
 +			memcpy(buf, p, 4); buf[4] = '\0'; p += 4;
 +			tm.tm_year = strtol(buf, NULL, 10) - 1900;
 +			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
 +			tm.tm_mon = strtol(buf, NULL, 10) - 1;
 +			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
 +			tm.tm_mday = strtol(buf, NULL, 10);
 +			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
 +			tm.tm_hour = strtol(buf, NULL, 10);
 +			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
 +			tm.tm_min = strtol(buf, NULL, 10);
 +			memcpy(buf, p, 2); buf[2] = '\0';
 +			tm.tm_sec = strtol(buf, NULL, 10);
 +			iso9660->birth_time = mktime(&tm);
 +			return (ARCHIVE_OK);
 +		}
 +#endif
 +		break;
 +	case 'i':
 +		if (strcmp(key, "iso-level") == 0) {
 +			if (value != NULL && value[1] == '\0' &&
 +			    (value[0] >= '1' && value[0] <= '4')) {
 +				iso9660->opt.iso_level = value[0]-'0';
 +				return (ARCHIVE_OK);
 +			}
 +			goto invalid_value;
 +		}
 +		break;
 +	case 'j':
 +		if (strcmp(key, "joliet") == 0) {
 +			if (value == NULL)
 +				iso9660->opt.joliet = OPT_JOLIET_DISABLE;
 +			else if (strcmp(value, "1") == 0)
 +				iso9660->opt.joliet = OPT_JOLIET_ENABLE;
 +			else if (strcmp(value, "long") == 0)
 +				iso9660->opt.joliet = OPT_JOLIET_LONGNAME;
 +			else
 +				goto invalid_value;
 +			return (ARCHIVE_OK);
 +		}
 +		break;
 +	case 'l':
 +		if (strcmp(key, "limit-depth") == 0) {
 +			iso9660->opt.limit_depth = value != NULL;
 +			return (ARCHIVE_OK);
 +		}
 +		if (strcmp(key, "limit-dirs") == 0) {
 +			iso9660->opt.limit_dirs = value != NULL;
 +			return (ARCHIVE_OK);
 +		}
 +		break;
 +	case 'p':
 +		if (strcmp(key, "pad") == 0) {
 +			iso9660->opt.pad = value != NULL;
 +			return (ARCHIVE_OK);
 +		}
 +		if (strcmp(key, "publisher") == 0) {
 +			r = get_str_opt(a,
 +			    &(iso9660->publisher_identifier),
 +			    PUBLISHER_IDENTIFIER_SIZE, key, value);
 +			iso9660->opt.publisher = r == ARCHIVE_OK;
 +			return (r);
 +		}
 +		break;
 +	case 'r':
 +		if (strcmp(key, "rockridge") == 0 ||
 +		    strcmp(key, "Rockridge") == 0) {
 +			if (value == NULL)
 +				iso9660->opt.rr = OPT_RR_DISABLED;
 +			else if (strcmp(value, "1") == 0)
 +				iso9660->opt.rr = OPT_RR_USEFUL;
 +			else if (strcmp(value, "strict") == 0)
 +				iso9660->opt.rr = OPT_RR_STRICT;
 +			else if (strcmp(value, "useful") == 0)
 +				iso9660->opt.rr = OPT_RR_USEFUL;
 +			else
 +				goto invalid_value;
 +			return (ARCHIVE_OK);
 +		}
 +		break;
 +	case 'v':
 +		if (strcmp(key, "volume-id") == 0) {
 +			r = get_str_opt(a, &(iso9660->volume_identifier),
 +			    VOLUME_IDENTIFIER_SIZE, key, value);
 +			iso9660->opt.volume_id = r == ARCHIVE_OK;
 +			return (r);
 +		}
 +		break;
 +	case 'z':
 +		if (strcmp(key, "zisofs") == 0) {
 +			if (value == NULL)
 +				iso9660->opt.zisofs = OPT_ZISOFS_DISABLED;
 +			else {
 +#ifdef HAVE_ZLIB_H
 +				iso9660->opt.zisofs = OPT_ZISOFS_DIRECT;
 +#else
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "``zisofs'' "
 +				    "is not supported on this platform.");
 +				return (ARCHIVE_FATAL);
 +#endif
 +			}
 +			return (ARCHIVE_OK);
 +		}
 +		break;
 +	}
 +
 +	/* Note: The "warn" return is just to inform the options
 +	 * supervisor that we didn't handle it.  It will generate
 +	 * a suitable error if no one used this option. */
 +	return (ARCHIVE_WARN);
 +
 +invalid_value:
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +	    "Invalid value for option ``%s''", key);
 +	return (ARCHIVE_FAILED);
 +}
 +
 +static int
 +iso9660_write_header(struct archive_write *a, struct archive_entry *entry)
 +{
 +	struct iso9660 *iso9660;
 +	struct isofile *file;
 +	struct isoent *isoent;
 +	int r, ret = ARCHIVE_OK;
 +
 +	iso9660 = a->format_data;
 +
 +	iso9660->cur_file = NULL;
 +	iso9660->bytes_remaining = 0;
 +	iso9660->need_multi_extent = 0;
 +	if (archive_entry_filetype(entry) == AE_IFLNK
 +	    && iso9660->opt.rr == OPT_RR_DISABLED) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Ignore symlink file.");
 +		iso9660->cur_file = NULL;
 +		return (ARCHIVE_WARN);
 +	}
 +	if (archive_entry_filetype(entry) == AE_IFREG &&
 +	    archive_entry_size(entry) >= MULTI_EXTENT_SIZE) {
 +		if (iso9660->opt.iso_level < 3) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_MISC,
 +			    "Ignore over %lld bytes file. "
 +			    "This file too large.",
 +			    MULTI_EXTENT_SIZE);
 +				iso9660->cur_file = NULL;
 +			return (ARCHIVE_WARN);
 +		}
 +		iso9660->need_multi_extent = 1;
 +	}
 +
 +	file = isofile_new(a, entry);
 +	if (file == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate data");
 +		return (ARCHIVE_FATAL);
 +	}
 +	r = isofile_gen_utility_names(a, file);
 +	if (r < ARCHIVE_WARN) {
 +		isofile_free(file);
 +		return (r);
 +	}
 +	else if (r < ret)
 +		ret = r;
 +
 +	/*
 +	 * Ignore a path which looks like the top of directory name
 +	 * since we have already made the root directory of an ISO image.
 +	 */
 +	if (archive_strlen(&(file->parentdir)) == 0 &&
 +	    archive_strlen(&(file->basename)) == 0) {
 +		isofile_free(file);
 +		return (r);
 +	}
 +
 +	isofile_add_entry(iso9660, file);
 +	isoent = isoent_new(file);
 +	if (isoent == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate data");
 +		return (ARCHIVE_FATAL);
 +	}
 +	if (isoent->file->dircnt > iso9660->dircnt_max)
 +		iso9660->dircnt_max = isoent->file->dircnt;
 +
 +	/* Add the current file into tree */
 +	r = isoent_tree(a, &isoent);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +
 +	/* If there is the same file in tree and
 +	 * the current file is older than the file in tree.
 +	 * So we don't need the current file data anymore. */
 +	if (isoent->file != file)
 +		return (ARCHIVE_OK);
 +
 +	/* Non regular files contents are unneeded to be saved to
 +	 * temporary files. */
 +	if (archive_entry_filetype(file->entry) != AE_IFREG)
 +		return (ret);
 +
 +	/*
 +	 * Set the current file to cur_file to read its contents.
 +	 */
 +	iso9660->cur_file = file;
 +
 +	if (archive_entry_nlink(file->entry) > 1) {
 +		r = isofile_register_hardlink(a, file);
 +		if (r != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +	}
 +
 +	/*
 +	 * Prepare to save the contents of the file.
 +	 */
 +	if (iso9660->temp_fd < 0) {
 +		iso9660->temp_fd = __archive_mktemp(NULL);
 +		if (iso9660->temp_fd < 0) {
 +			archive_set_error(&a->archive, errno,
 +			    "Couldn't create temporary file");
 +			return (ARCHIVE_FATAL);
 +		}
 +	}
 +
 +	/* Save an offset of current file in temporary file. */
 +	file->content.offset_of_temp = wb_offset(a);
 +	file->cur_content = &(file->content);
 +	r = zisofs_init(a, file);
 +	if (r < ret)
 +		ret = r;
 +	iso9660->bytes_remaining =  archive_entry_size(file->entry);
 +
 +	return (ret);
 +}
 +
 +static int
 +write_to_temp(struct archive_write *a, const void *buff, size_t s)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	ssize_t written;
 +	const unsigned char *b;
 +
 +	b = (const unsigned char *)buff;
 +	while (s) {
 +		written = write(iso9660->temp_fd, b, s);
 +		if (written < 0) {
 +			archive_set_error(&a->archive, errno,
 +			    "Can't write to temporary file");
 +			return (ARCHIVE_FATAL);
 +		}
 +		s -= written;
 +		b += written;
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +wb_write_to_temp(struct archive_write *a, const void *buff, size_t s)
 +{
 +	const char *xp = buff;
 +	size_t xs = s;
 +
 +	/*
 +	 * If a written data size is big enough to use system-call
 +	 * and there is no waiting data, this calls write_to_temp() in
 +	 * order to reduce a extra memory copy.
 +	 */
 +	if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) {
 +		struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
 +		xs = s % LOGICAL_BLOCK_SIZE;
 +		iso9660->wbuff_offset += s - xs;
 +		if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +		if (xs == 0)
 +			return (ARCHIVE_OK);
 +		xp += s - xs;
 +	}
 +
 +	while (xs) {
 +		size_t size = xs;
 +		if (size > wb_remaining(a))
 +			size = wb_remaining(a);
 +		memcpy(wb_buffptr(a), xp, size);
 +		if (wb_consume(a, size) != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +		xs -= size;
 +		xp += size;
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +wb_write_padding_to_temp(struct archive_write *a, int64_t csize)
 +{
 +	size_t ns;
 +	int ret;
 +
 +	ns = (size_t)(csize % LOGICAL_BLOCK_SIZE);
 +	if (ns != 0)
 +		ret = write_null(a, LOGICAL_BLOCK_SIZE - ns);
 +	else
 +		ret = ARCHIVE_OK;
 +	return (ret);
 +}
 +
 +static ssize_t
 +write_iso9660_data(struct archive_write *a, const void *buff, size_t s)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	size_t ws;
 +
 +	if (iso9660->temp_fd < 0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Couldn't create temporary file");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	ws = s;
 +	if (iso9660->need_multi_extent &&
 +	    (iso9660->cur_file->cur_content->size + ws) >=
 +	      (MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE)) {
 +		struct content *con;
 +		size_t ts;
 +
 +		ts = (size_t)(MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE -
 +		    iso9660->cur_file->cur_content->size);
 +
 +		if (iso9660->zisofs.detect_magic)
 +			zisofs_detect_magic(a, buff, ts);
 +
 +		if (iso9660->zisofs.making) {
 +			if (zisofs_write_to_temp(a, buff, ts) != ARCHIVE_OK)
 +				return (ARCHIVE_FATAL);
 +		} else {
 +			if (wb_write_to_temp(a, buff, ts) != ARCHIVE_OK)
 +				return (ARCHIVE_FATAL);
 +			iso9660->cur_file->cur_content->size += ts;
 +		}
 +
 +		/* Write padding. */
 +		if (wb_write_padding_to_temp(a,
 +		    iso9660->cur_file->cur_content->size) != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +
 +		/* Compute the logical block number. */
 +		iso9660->cur_file->cur_content->blocks = (int)
 +		    ((iso9660->cur_file->cur_content->size
 +		     + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
 +
 +		/*
 +		 * Make next extent.
 +		 */
 +		ws -= ts;
 +		buff = (const void *)(((const unsigned char *)buff) + ts);
 +		/* Make a content for next extent. */
 +		con = calloc(1, sizeof(*con));
 +		if (con == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate content data");
 +			return (ARCHIVE_FATAL);
 +		}
 +		con->offset_of_temp = wb_offset(a);
 +		iso9660->cur_file->cur_content->next = con;
 +		iso9660->cur_file->cur_content = con;
 +#ifdef HAVE_ZLIB_H
 +		iso9660->zisofs.block_offset = 0;
 +#endif
 +	}
 +
 +	if (iso9660->zisofs.detect_magic)
 +		zisofs_detect_magic(a, buff, ws);
 +
 +	if (iso9660->zisofs.making) {
 +		if (zisofs_write_to_temp(a, buff, ws) != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +	} else {
 +		if (wb_write_to_temp(a, buff, ws) != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +		iso9660->cur_file->cur_content->size += ws;
 +	}
 +
 +	return (s);
 +}
 +
 +static ssize_t
 +iso9660_write_data(struct archive_write *a, const void *buff, size_t s)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	ssize_t r;
 +
 +	if (iso9660->cur_file == NULL)
 +		return (0);
 +	if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG)
 +		return (0);
 +	if (s > iso9660->bytes_remaining)
 +		s = (size_t)iso9660->bytes_remaining;
 +	if (s == 0)
 +		return (0);
 +
 +	r = write_iso9660_data(a, buff, s);
 +	if (r > 0)
 +		iso9660->bytes_remaining -= r;
 +	return (r);
 +}
 +
 +static int
 +iso9660_finish_entry(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +
 +	if (iso9660->cur_file == NULL)
 +		return (ARCHIVE_OK);
 +	if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG)
 +		return (ARCHIVE_OK);
 +	if (iso9660->cur_file->content.size == 0)
 +		return (ARCHIVE_OK);
 +
 +	/* If there are unwritten data, write null data instead. */
 +	while (iso9660->bytes_remaining > 0) {
 +		size_t s;
 +
 +		s = (iso9660->bytes_remaining > a->null_length)?
 +		    a->null_length: (size_t)iso9660->bytes_remaining;
 +		if (write_iso9660_data(a, a->nulls, s) < 0)
 +			return (ARCHIVE_FATAL);
 +		iso9660->bytes_remaining -= s;
 +	}
 +
 +	if (iso9660->zisofs.making && zisofs_finish_entry(a) != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Write padding. */
 +	if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size)
 +	    != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Compute the logical block number. */
 +	iso9660->cur_file->cur_content->blocks = (int)
 +	    ((iso9660->cur_file->cur_content->size
 +	     + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
 +
 +	/* Add the current file to data file list. */
 +	isofile_add_data_file(iso9660, iso9660->cur_file);
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +iso9660_close(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660;
 +	int ret, blocks;
 +
 +	iso9660 = a->format_data;
 +
 +	/*
 +	 * Write remaining data out to the temporary file.
 +	 */
 +	if (wb_remaining(a) > 0) {
 +		ret = wb_write_out(a);
 +		if (ret < 0)
 +			return (ret);
 +	}
 +
 +	/*
 +	 * Preparations...
 +	 */
 +#ifdef DEBUG
 +	if (iso9660->birth_time == 0)
 +#endif
 +		time(&(iso9660->birth_time));
 +
 +	/*
 +	 * Prepare a bootable ISO image.
 +	 */
 +	if (iso9660->opt.boot) {
 +		/* Find out the boot file entry. */
 +		ret = isoent_find_out_boot_file(a, iso9660->primary.rootent);
 +		if (ret < 0)
 +			return (ret);
 +		/* Reconvert the boot file from zisofs'ed form to
 +		 * plain form. */
 +		ret = zisofs_rewind_boot_file(a);
 +		if (ret < 0)
 +			return (ret);
 +		/* Write remaining data out to the temporary file. */
 +		if (wb_remaining(a) > 0) {
 +			ret = wb_write_out(a);
 +			if (ret < 0)
 +				return (ret);
 +		}
 +		/* Create the boot catalog. */
 +		ret = isoent_create_boot_catalog(a, iso9660->primary.rootent);
 +		if (ret < 0)
 +			return (ret);
 +	}
 +
 +	/*
 +	 * Prepare joliet extensions.
 +	 */
 +	if (iso9660->opt.joliet) {
 +		/* Make a new tree for joliet. */
 +		ret = isoent_clone_tree(a, &(iso9660->joliet.rootent),
 +		    iso9660->primary.rootent);
 +		if (ret < 0)
 +			return (ret);
 +		/* Make sure we have UTF-16BE convertors.
 +		 * if there is no file entry, convertors are still
 +		 * uninitilized. */
 +		if (iso9660->sconv_to_utf16be == NULL) {
 +			iso9660->sconv_to_utf16be =
 +			    archive_string_conversion_to_charset(
 +				&(a->archive), "UTF-16BE", 1);
 +			if (iso9660->sconv_to_utf16be == NULL)
 +				/* Couldn't allocate memory */
 +				return (ARCHIVE_FATAL);
 +			iso9660->sconv_from_utf16be =
 +			    archive_string_conversion_from_charset(
 +				&(a->archive), "UTF-16BE", 1);
 +			if (iso9660->sconv_from_utf16be == NULL)
 +				/* Couldn't allocate memory */
 +				return (ARCHIVE_FATAL);
 +		}
 +	}
 +
 +	/*
 +	 * Make Path Tables.
 +	 */
 +	ret = isoent_make_path_table(a);
 +	if (ret < 0)
 +		return (ret);
 +
 +	/*
 +	 * Calculate a total volume size and setup all locations of
 +	 * contents of an iso9660 image.
 +	 */
 +	blocks = SYSTEM_AREA_BLOCK
 +		+ PRIMARY_VOLUME_DESCRIPTOR_BLOCK
 +		+ VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK
 +		+ NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;
 +	if (iso9660->opt.boot)
 +		blocks += BOOT_RECORD_DESCRIPTOR_BLOCK;
 +	if (iso9660->opt.joliet)
 +		blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK;
 +	if (iso9660->opt.iso_level == 4)
 +		blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK;
 +
 +	/* Setup the locations of Path Table. */
 +	iso9660->primary.location_type_L_path_table = blocks;
 +	blocks += iso9660->primary.path_table_block;
 +	iso9660->primary.location_type_M_path_table = blocks;
 +	blocks += iso9660->primary.path_table_block;
 +	if (iso9660->opt.joliet) {
 +		iso9660->joliet.location_type_L_path_table = blocks;
 +		blocks += iso9660->joliet.path_table_block;
 +		iso9660->joliet.location_type_M_path_table = blocks;
 +		blocks += iso9660->joliet.path_table_block;
 +	}
 +
 +	/* Setup the locations of directories. */
 +	isoent_setup_directory_location(iso9660, blocks,
 +	    &(iso9660->primary));
 +	blocks += iso9660->primary.total_dir_block;
 +	if (iso9660->opt.joliet) {
 +		isoent_setup_directory_location(iso9660, blocks,
 +		    &(iso9660->joliet));
 +		blocks += iso9660->joliet.total_dir_block;
 +	}
 +
 +	if (iso9660->opt.rr) {
 +		iso9660->location_rrip_er = blocks;
 +		blocks += RRIP_ER_BLOCK;
 +	}
 +
 +	/* Setup the locations of all file contents. */
 + 	isoent_setup_file_location(iso9660, blocks);
 +	blocks += iso9660->total_file_block;
 +	if (iso9660->opt.boot && iso9660->opt.boot_info_table) {
 +		ret = setup_boot_information(a);
 +		if (ret < 0)
 +			return (ret);
 +	}
 +
 +	/* Now we have a total volume size. */
 +	iso9660->volume_space_size = blocks;
 +	if (iso9660->opt.pad)
 +		iso9660->volume_space_size += PADDING_BLOCK;
 +	iso9660->volume_sequence_number = 1;
 +
 +
 +	/*
 +	 * Write an ISO 9660 image.
 +	 */
 +
 +	/* Switc to start using wbuff as file buffer. */
 +	iso9660->wbuff_remaining = wb_buffmax();
 +	iso9660->wbuff_type = WB_TO_STREAM;
 +	iso9660->wbuff_offset = 0;
 +	iso9660->wbuff_written = 0;
 +	iso9660->wbuff_tail = 0;
 +
 +	/* Write The System Area */
 +	ret = write_null(a, SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE);
 +	if (ret != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Write Primary Volume Descriptor */
 +	ret = write_VD(a, &(iso9660->primary));
 +	if (ret != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	if (iso9660->opt.boot) {
 +		/* Write Boot Record Volume Descriptor */
 +		ret = write_VD_boot_record(a);
 +		if (ret != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +	}
 +
 +	if (iso9660->opt.iso_level == 4) {
 +		/* Write Enhanced Volume Descriptor */
 +		iso9660->primary.vdd_type = VDD_ENHANCED;
 +		ret = write_VD(a, &(iso9660->primary));
 +		iso9660->primary.vdd_type = VDD_PRIMARY;
 +		if (ret != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +	}
 +
 +	if (iso9660->opt.joliet) {
 +		ret = write_VD(a, &(iso9660->joliet));
 +		if (ret != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +	}
 +
 +	/* Write Volume Descriptor Set Terminator */
 +	ret = write_VD_terminator(a);
 +	if (ret != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Write Non-ISO File System Information */
 +	ret = write_information_block(a);
 +	if (ret != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Write Type L Path Table */
 +	ret = write_path_table(a, 0, &(iso9660->primary));
 +	if (ret != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Write Type M Path Table */
 +	ret = write_path_table(a, 1, &(iso9660->primary));
 +	if (ret != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	if (iso9660->opt.joliet) {
 +		/* Write Type L Path Table */
 +		ret = write_path_table(a, 0, &(iso9660->joliet));
 +		if (ret != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +
 +		/* Write Type M Path Table */
 +		ret = write_path_table(a, 1, &(iso9660->joliet));
 +		if (ret != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +	}
 +
 +	/* Write Directory Descriptors */
 +	ret = write_directory_descriptors(a, &(iso9660->primary));
 +	if (ret != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	if (iso9660->opt.joliet) {
 +		ret = write_directory_descriptors(a, &(iso9660->joliet));
 +		if (ret != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +	}
 +
 +	if (iso9660->opt.rr) {
 +		/* Write Rockridge ER(Extensions Reference) */
 +		ret = write_rr_ER(a);
 +		if (ret != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +	}
 +
 +	/* Write File Descriptors */
 +	ret = write_file_descriptors(a);
 +	if (ret != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Write Padding  */
 +	if (iso9660->opt.pad) {
 +		ret = write_null(a, PADDING_BLOCK * LOGICAL_BLOCK_SIZE);
 +		if (ret != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +	}
 +
 +	if (iso9660->directories_too_deep != NULL) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "%s: Directories too deep.",
 +		    archive_entry_pathname(
 +			iso9660->directories_too_deep->file->entry));
 +		return (ARCHIVE_WARN);
 +	}
 +
 +	/* Write remaining data out. */
 +	ret = wb_write_out(a);
 +
 +	return (ret);
 +}
 +
 +static int
 +iso9660_free(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660;
 +	int i, ret;
 +
 +	iso9660 = a->format_data;
 +
 +	/* Close the temporary file. */
 +	if (iso9660->temp_fd >= 0)
 +		close(iso9660->temp_fd);
 +
 +	/* Free some stuff for zisofs operations. */
 +	ret = zisofs_free(a);
 +
 +	/* Remove directory entries in tree which includes file entries. */
 +	isoent_free_all(iso9660->primary.rootent);
 +	for (i = 0; i < iso9660->primary.max_depth; i++)
 +		free(iso9660->primary.pathtbl[i].sorted);
 +	free(iso9660->primary.pathtbl);
 +
 +	if (iso9660->opt.joliet) {
 +		isoent_free_all(iso9660->joliet.rootent);
 +		for (i = 0; i < iso9660->joliet.max_depth; i++)
 +			free(iso9660->joliet.pathtbl[i].sorted);
 +		free(iso9660->joliet.pathtbl);
 +	}
 +
 +	/* Remove isofile entries. */
 +	isofile_free_all_entries(iso9660);
 +	isofile_free_hardlinks(iso9660);
 +
 +	archive_string_free(&(iso9660->cur_dirstr));
 +	archive_string_free(&(iso9660->volume_identifier));
 +	archive_string_free(&(iso9660->publisher_identifier));
 +	archive_string_free(&(iso9660->data_preparer_identifier));
 +	archive_string_free(&(iso9660->application_identifier));
 +	archive_string_free(&(iso9660->copyright_file_identifier));
 +	archive_string_free(&(iso9660->abstract_file_identifier));
 +	archive_string_free(&(iso9660->bibliographic_file_identifier));
 +	archive_string_free(&(iso9660->el_torito.catalog_filename));
 +	archive_string_free(&(iso9660->el_torito.boot_filename));
 +	archive_string_free(&(iso9660->el_torito.id));
 +	archive_string_free(&(iso9660->utf16be));
 +	archive_string_free(&(iso9660->mbs));
 +
 +	free(iso9660);
 +	a->format_data = NULL;
 +
 +	return (ret);
 +}
 +
 +/*
 + * Get the System Identifier
 + */
 +static void
 +get_system_identitier(char *system_id, size_t size)
 +{
 +#if defined(HAVE_SYS_UTSNAME_H)
 +	struct utsname u;
 +
 +	uname(&u);
 +	strncpy(system_id, u.sysname, size-1);
 +	system_id[size-1] = '\0';
 +#elif defined(_WIN32) && !defined(__CYGWIN__)
 +	strncpy(system_id, "Windows", size-1);
 +	system_id[size-1] = '\0';
 +#else
 +#error no way to get the system identifier on your platform.
 +#endif
 +}
 +
 +static void
 +set_str(unsigned char *p, const char *s, size_t l, char f, const char *map)
 +{
 +	unsigned char c;
 +
 +	if (s == NULL)
 +		s = "";
 +	while ((c = *s++) != 0 && l > 0) {
 +		if (c >= 0x80 || map[c] == 0)
 +		 {
 +			/* illegal character */
 +			if (c >= 'a' && c <= 'z') {
 +				/* convert c from a-z to A-Z */
 +				c -= 0x20;
 +			} else
 +				c = 0x5f;
 +		}
 +		*p++ = c;
 +		l--;
 +	}
 +	/* If l isn't zero, fill p buffer by the character
 +	 * which indicated by f. */
 +	if (l > 0)
 +		memset(p , f, l);
 +}
 +
 +static inline int
 +joliet_allowed_char(unsigned char high, unsigned char low)
 +{
 +	int utf16 = (high << 8) | low;
 +
 +	if (utf16 <= 0x001F)
 +		return (0);
 +
 +	switch (utf16) {
 +	case 0x002A: /* '*' */
 +	case 0x002F: /* '/' */
 +	case 0x003A: /* ':' */
 +	case 0x003B: /* ';' */
 +	case 0x003F: /* '?' */
 +	case 0x005C: /* '\' */
 +		return (0);/* Not allowed. */
 +	}
 +	return (1);
 +}
 +
 +static int
 +set_str_utf16be(struct archive_write *a, unsigned char *p, const char *s,
 +    size_t l, uint16_t uf, enum vdc vdc)
 +{
 +	size_t size, i;
 +	int onepad;
 +
 +	if (s == NULL)
 +		s = "";
 +	if (l & 0x01) {
 +		onepad = 1;
 +		l &= ~1;
 +	} else
 +		onepad = 0;
 +	if (vdc == VDC_UCS2) {
 +		struct iso9660 *iso9660 = a->format_data;
 +		if (archive_strncpy_l(&iso9660->utf16be, s, strlen(s),
 +		    iso9660->sconv_to_utf16be) != 0 && errno == ENOMEM) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory for UTF-16BE");
 +			return (ARCHIVE_FATAL);
 +		}
 +		size = iso9660->utf16be.length;
 +		if (size > l)
 +			size = l;
 +		memcpy(p, iso9660->utf16be.s, size);
 +	} else {
 +		const uint16_t *u16 = (const uint16_t *)s;
 +
 +		size = 0;
 +		while (*u16++)
 +			size += 2;
 +		if (size > l)
 +			size = l;
 +		memcpy(p, s, size);
 +	}
 +	for (i = 0; i < size; i += 2, p += 2) {
 +		if (!joliet_allowed_char(p[0], p[1]))
 +			archive_be16enc(p, 0x005F);/* '_' */
 +	}
 +	l -= size;
 +	while (l > 0) {
 +		archive_be16enc(p, uf);
 +		p += 2;
 +		l -= 2;
 +	}
 +	if (onepad)
 +		*p = 0;
 +	return (ARCHIVE_OK);
 +}
 +
 +static const char a_characters_map[0x80] = {
 +/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
 +    1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */
 +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */
 +    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
 +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */
 +};
 +
 +static const char a1_characters_map[0x80] = {
 +/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
 +    1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */
 +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */
 +    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
 +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
 +    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */
 +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */
 +};
 +
 +static const char d_characters_map[0x80] = {
 +/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */
 +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */
 +    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
 +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */
 +};
 +
 +static const char d1_characters_map[0x80] = {
 +/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */
 +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */
 +    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
 +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
 +    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */
 +    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */
 +};
 +
 +static int
 +set_str_a_characters_bp(struct archive_write *a, unsigned char *bp,
 +    int from, int to, const char *s, enum vdc vdc)
 +{
 +	int r;
 +
 +	switch (vdc) {
 +	case VDC_STD:
 +		set_str(bp+from, s, to - from + 1, 0x20,
 +		    a_characters_map);
 +		r = ARCHIVE_OK;
 +		break;
 +	case VDC_LOWERCASE:
 +		set_str(bp+from, s, to - from + 1, 0x20,
 +		    a1_characters_map);
 +		r = ARCHIVE_OK;
 +		break;
 +	case VDC_UCS2:
 +	case VDC_UCS2_DIRECT:
 +		r = set_str_utf16be(a, bp+from, s, to - from + 1,
 +		    0x0020, vdc);
 +		break;
 +	default:
 +		r = ARCHIVE_FATAL;
 +	}
 +	return (r);
 +}
 +
 +static int
 +set_str_d_characters_bp(struct archive_write *a, unsigned char *bp,
 +    int from, int to, const char *s, enum  vdc vdc)
 +{
 +	int r;
 +
 +	switch (vdc) {
 +	case VDC_STD:
 +		set_str(bp+from, s, to - from + 1, 0x20,
 +		    d_characters_map);
 +		r = ARCHIVE_OK;
 +		break;
 +	case VDC_LOWERCASE:
 +		set_str(bp+from, s, to - from + 1, 0x20,
 +		    d1_characters_map);
 +		r = ARCHIVE_OK;
 +		break;
 +	case VDC_UCS2:
 +	case VDC_UCS2_DIRECT:
 +		r = set_str_utf16be(a, bp+from, s, to - from + 1,
 +		    0x0020, vdc);
 +		break;
 +	default:
 +		r = ARCHIVE_FATAL;
 +	}
 +	return (r);
 +}
 +
 +static void
 +set_VD_bp(unsigned char *bp, enum VD_type type, unsigned char ver)
 +{
 +
 +	/* Volume Descriptor Type */
 +	bp[1] = (unsigned char)type;
 +	/* Standard Identifier */
 +	memcpy(bp + 2, "CD001", 5);
 +	/* Volume Descriptor Version */
 +	bp[7] = ver;
 +}
 +
 +static inline void
 +set_unused_field_bp(unsigned char *bp, int from, int to)
 +{
 +	memset(bp + from, 0, to - from + 1);
 +}
 +
 +/*
 + * 8-bit unsigned numerical values.
 + * ISO9660 Standard 7.1.1
 + */
 +static inline void
 +set_num_711(unsigned char *p, unsigned char value)
 +{
 +	*p = value;
 +}
 +
 +/*
 + * 8-bit signed numerical values.
 + * ISO9660 Standard 7.1.2
 + */
 +static inline void
 +set_num_712(unsigned char *p, char value)
 +{
 +	*((char *)p) = value;
 +}
 +
 +/*
 + * Least significant byte first.
 + * ISO9660 Standard 7.2.1
 + */
 +static inline void
 +set_num_721(unsigned char *p, uint16_t value)
 +{
 +	archive_le16enc(p, value);
 +}
 +
 +/*
 + * Most significant byte first.
 + * ISO9660 Standard 7.2.2
 + */
 +static inline void
 +set_num_722(unsigned char *p, uint16_t value)
 +{
 +	archive_be16enc(p, value);
 +}
 +
 +/*
 + * Both-byte orders.
 + * ISO9660 Standard 7.2.3
 + */
 +static void
 +set_num_723(unsigned char *p, uint16_t value)
 +{
 +	archive_le16enc(p, value);
 +	archive_be16enc(p+2, value);
 +}
 +
 +/*
 + * Least significant byte first.
 + * ISO9660 Standard 7.3.1
 + */
 +static inline void
 +set_num_731(unsigned char *p, uint32_t value)
 +{
 +	archive_le32enc(p, value);
 +}
 +
 +/*
 + * Most significant byte first.
 + * ISO9660 Standard 7.3.2
 + */
 +static inline void
 +set_num_732(unsigned char *p, uint32_t value)
 +{
 +	archive_be32enc(p, value);
 +}
 +
 +/*
 + * Both-byte orders.
 + * ISO9660 Standard 7.3.3
 + */
 +static inline void
 +set_num_733(unsigned char *p, uint32_t value)
 +{
 +	archive_le32enc(p, value);
 +	archive_be32enc(p+4, value);
 +}
 +
 +static void
 +set_digit(unsigned char *p, size_t s, int value)
 +{
 +
 +	while (s--) {
 +		p[s] = '0' + (value % 10);
 +		value /= 10;
 +	}
 +}
 +
 +#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
 +#define get_gmoffset(tm)	((tm)->tm_gmtoff)
 +#elif defined(HAVE_STRUCT_TM___TM_GMTOFF)
 +#define get_gmoffset(tm)	((tm)->__tm_gmtoff)
 +#else
 +static long
 +get_gmoffset(struct tm *tm)
 +{
 +	long offset;
 +
 +#if defined(HAVE__GET_TIMEZONE)
 +	_get_timezone(&offset);
 +#elif defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
 +	offset = _timezone;
 +#else
 +	offset = timezone;
 +#endif
 +	offset *= -1;
 +	if (tm->tm_isdst)
 +		offset += 3600;
 +	return (offset);
 +}
 +#endif
 +
 +static void
 +get_tmfromtime(struct tm *tm, time_t *t)
 +{
 +#if HAVE_LOCALTIME_R
 +	tzset();
 +	localtime_r(t, tm);
 +#elif HAVE__LOCALTIME64_S
 +	_localtime64_s(tm, t);
 +#else
 +	memcpy(tm, localtime(t), sizeof(*tm));
 +#endif
 +}
 +
 +/*
 + * Date and Time Format.
 + * ISO9660 Standard 8.4.26.1
 + */
 +static void
 +set_date_time(unsigned char *p, time_t t)
 +{
 +	struct tm tm;
 +
 +	get_tmfromtime(&tm, &t);
 +	set_digit(p, 4, tm.tm_year + 1900);
 +	set_digit(p+4, 2, tm.tm_mon + 1);
 +	set_digit(p+6, 2, tm.tm_mday);
 +	set_digit(p+8, 2, tm.tm_hour);
 +	set_digit(p+10, 2, tm.tm_min);
 +	set_digit(p+12, 2, tm.tm_sec);
 +	set_digit(p+14, 2, 0);
 +	set_num_712(p+16, (char)(get_gmoffset(&tm)/(60*15)));
 +}
 +
 +static void
 +set_date_time_null(unsigned char *p)
 +{
 +	memset(p, '0', 16);
 +	p[16] = 0;
 +}
 +
 +static void
 +set_time_915(unsigned char *p, time_t t)
 +{
 +	struct tm tm;
 +
 +	get_tmfromtime(&tm, &t);
 +	set_num_711(p+0, tm.tm_year);
 +	set_num_711(p+1, tm.tm_mon+1);
 +	set_num_711(p+2, tm.tm_mday);
 +	set_num_711(p+3, tm.tm_hour);
 +	set_num_711(p+4, tm.tm_min);
 +	set_num_711(p+5, tm.tm_sec);
 +	set_num_712(p+6, (char)(get_gmoffset(&tm)/(60*15)));
 +}
 +
 +
 +/*
 + * Write SUSP "CE" System Use Entry.
 + */
 +static int
 +set_SUSP_CE(unsigned char *p, int location, int offset, int size)
 +{
 +	unsigned char *bp = p -1;
 +	/*  Extend the System Use Area
 +	 *   "CE" Format:
 +	 *               len  ver
 +	 *    +----+----+----+----+-----------+-----------+
 +	 *    | 'C'| 'E'| 1C | 01 | LOCATION1 | LOCATION2 |
 +	 *    +----+----+----+----+-----------+-----------+
 +	 *    0    1    2    3    4          12          20
 +	 *    +-----------+
 +	 *    | LOCATION3 |
 +	 *    +-----------+
 +	 *   20          28
 +	 *   LOCATION1 : Location of Continuation of System Use Area.
 +	 *   LOCATION2 : Offset to Start of Continuation.
 +	 *   LOCATION3 : Length of the Continuation.
 +	 */
 +
 +	bp[1] = 'C';
 +	bp[2] = 'E';
 +	bp[3] = RR_CE_SIZE;	/* length	*/
 +	bp[4] = 1;		/* version	*/
 +	set_num_733(bp+5, location);
 +	set_num_733(bp+13, offset);
 +	set_num_733(bp+21, size);
 +	return (RR_CE_SIZE);
 +}
 +
 +/*
 + * The functions, which names are beginning with extra_, are used to
 + * control extra records.
 + * The maximum size of a Directory Record is 254. When a filename is
 + * very long, all of RRIP data of a file won't stored to the Directory
 + * Record and so remaining RRIP data store to an extra record instead.
 + */
 +static unsigned char *
 +extra_open_record(unsigned char *bp, int dr_len, struct isoent *isoent,
 +    struct ctl_extr_rec *ctl)
 +{
 +	ctl->bp = bp;
 +	if (bp != NULL)
 +		bp += dr_len;
 +	ctl->use_extr = 0;
 +	ctl->isoent = isoent;
 +	ctl->ce_ptr = NULL;
 +	ctl->cur_len = ctl->dr_len = dr_len;
 +	ctl->limit = DR_LIMIT;
 +
 +	return (bp);
 +}
 +
 +static void
 +extra_close_record(struct ctl_extr_rec *ctl, int ce_size)
 +{
 +	int padding = 0;
 +
 +	if (ce_size > 0)
 +		extra_tell_used_size(ctl, ce_size);
 +	/* Padding. */
 +	if (ctl->cur_len & 0x01) {
 +		ctl->cur_len++;
 +		if (ctl->bp != NULL)
 +			ctl->bp[ctl->cur_len] = 0;
 +		padding = 1;
 +	}
 +	if (ctl->use_extr) {
 +		if (ctl->ce_ptr != NULL)
 +			set_SUSP_CE(ctl->ce_ptr, ctl->extr_loc,
 +			    ctl->extr_off, ctl->cur_len - padding);
 +	} else
 +		ctl->dr_len = ctl->cur_len;
 +}
 +
 +#define extra_space(ctl)	((ctl)->limit - (ctl)->cur_len)
 +
 +static unsigned char *
 +extra_next_record(struct ctl_extr_rec *ctl, int length)
 +{
 +	int cur_len = ctl->cur_len;/* save cur_len */
 +
 +	/* Close the current extra record or Directory Record. */
 +	extra_close_record(ctl, RR_CE_SIZE);
 +
 +	/* Get a next extra record. */
 +	ctl->use_extr = 1;
 +	if (ctl->bp != NULL) {
 +		/* Storing data into an extra record. */
 +		unsigned char *p;
 +
 +		/* Save the pointer where a CE extension will be
 +		 * stored to. */
 +		ctl->ce_ptr = &ctl->bp[cur_len+1];
 +		p = extra_get_record(ctl->isoent,
 +		    &ctl->limit, &ctl->extr_off, &ctl->extr_loc);
 +		ctl->bp = p - 1;/* the base of bp offset is 1. */
 +	} else
 +		/* Calculating the size of an extra record. */
 +		(void)extra_get_record(ctl->isoent,
 +		    &ctl->limit, NULL, NULL);
 +	ctl->cur_len = 0;
 +	/* Check if an extra record is almost full.
 +	 * If so, get a next one. */
 +	if (extra_space(ctl) < length)
 +		(void)extra_next_record(ctl, length);
 +
 +	return (ctl->bp);
 +}
 +
 +static inline struct extr_rec *
 +extra_last_record(struct isoent *isoent)
 +{
 +	if (isoent->extr_rec_list.first == NULL)
 +		return (NULL);
 +	return ((struct extr_rec *)(void *)
 +		((char *)(isoent->extr_rec_list.last)
 +		    - offsetof(struct extr_rec, next)));
 +}
 +
 +static unsigned char *
 +extra_get_record(struct isoent *isoent, int *space, int *off, int *loc)
 +{
 +	struct extr_rec *rec;
 +
 +	isoent = isoent->parent;
 +	if (off != NULL) {
 +		/* Storing data into an extra record. */
 +		rec = isoent->extr_rec_list.current;
 +		if (DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset)
 +			rec = rec->next;
 +	} else {
 +		/* Calculating the size of an extra record. */
 +		rec = extra_last_record(isoent);
 +		if (rec == NULL ||
 +		    DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) {
 +			rec = malloc(sizeof(*rec));
 +			if (rec == NULL)
 +				return (NULL);
 +			rec->location = 0;
 +			rec->offset = 0;
 +			/* Insert `rec` into the tail of isoent->extr_rec_list */
 +			rec->next = NULL;
 +			/*
 +			 * Note: testing isoent->extr_rec_list.last == NULL
 +			 * here is really unneeded since it has been already
 +			 * initialized at isoent_new function but Clang Static
 +			 * Analyzer claims that it is dereference of null
 +			 * pointer.
 +			 */
 +			if (isoent->extr_rec_list.last == NULL)
 +				isoent->extr_rec_list.last =
 +					&(isoent->extr_rec_list.first);
 +			*isoent->extr_rec_list.last = rec;
 +			isoent->extr_rec_list.last = &(rec->next);
 +		}
 +	}
 +	*space = LOGICAL_BLOCK_SIZE - rec->offset - DR_SAFETY;
 +	if (*space & 0x01)
 +		*space -= 1;/* Keep padding space. */
 +	if (off != NULL)
 +		*off = rec->offset;
 +	if (loc != NULL)
 +		*loc = rec->location;
 +	isoent->extr_rec_list.current = rec;
 +
 +	return (&rec->buf[rec->offset]);
 +}
 +
 +static void
 +extra_tell_used_size(struct ctl_extr_rec *ctl, int size)
 +{
 +	struct isoent *isoent;
 +	struct extr_rec *rec;
 +
 +	if (ctl->use_extr) {
 +		isoent = ctl->isoent->parent;
 +		rec = isoent->extr_rec_list.current;
 +		if (rec != NULL)
 +			rec->offset += size;
 +	}
 +	ctl->cur_len += size;
 +}
 +
 +static int
 +extra_setup_location(struct isoent *isoent, int location)
 +{
 +	struct extr_rec *rec;
 +	int cnt;
 +
 +	cnt = 0;
 +	rec = isoent->extr_rec_list.first;
 +	isoent->extr_rec_list.current = rec;
 +	while (rec) {
 +		cnt++;
 +		rec->location = location++;
 +		rec->offset = 0;
 +		rec = rec->next;
 +	}
 +	return (cnt);
 +}
 +
 +/*
 + * Create the RRIP entries.
 + */
 +static int
 +set_directory_record_rr(unsigned char *bp, int dr_len,
 +    struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t)
 +{
 +	/* Flags(BP 5) of the Rockridge "RR" System Use Field */
 +	unsigned char rr_flag;
 +#define RR_USE_PX	0x01
 +#define RR_USE_PN	0x02
 +#define RR_USE_SL	0x04
 +#define RR_USE_NM	0x08
 +#define RR_USE_CL	0x10
 +#define RR_USE_PL	0x20
 +#define RR_USE_RE	0x40
 +#define RR_USE_TF	0x80
 +	int length;
 +	struct ctl_extr_rec ctl;
 +	struct isoent *rr_parent, *pxent;
 +	struct isofile *file;
 +
 +	bp = extra_open_record(bp, dr_len, isoent, &ctl);
 +
 +	if (t == DIR_REC_PARENT) {
 +		rr_parent = isoent->rr_parent;
 +		pxent = isoent->parent;
 +		if (rr_parent != NULL)
 +			isoent = rr_parent;
 +		else
 +			isoent = isoent->parent;
 +	} else {
 +		rr_parent = NULL;
 +		pxent = isoent;
 +	}
 +	file = isoent->file;
 +
 +	if (t != DIR_REC_NORMAL) {
 +		rr_flag = RR_USE_PX | RR_USE_TF;
 +		if (rr_parent != NULL)
 +			rr_flag |= RR_USE_PL;
 +	} else {
 +		rr_flag = RR_USE_PX | RR_USE_NM | RR_USE_TF;
 +		if (archive_entry_filetype(file->entry) == AE_IFLNK)
 +			rr_flag |= RR_USE_SL;
 +		if (isoent->rr_parent != NULL)
 +			rr_flag |= RR_USE_RE;
 +		if (isoent->rr_child != NULL)
 +			rr_flag |= RR_USE_CL;
 +		if (archive_entry_filetype(file->entry) == AE_IFCHR ||
 +		    archive_entry_filetype(file->entry) == AE_IFBLK)
 +			rr_flag |= RR_USE_PN;
 +#ifdef COMPAT_MKISOFS
 +		/*
 +		 * mkisofs 2.01.01a63 records "RE" extension to
 +		 * the entry of "rr_moved" directory.
 +		 * I don't understand this behavior.
 +		 */
 +		if (isoent->virtual &&
 +		    isoent->parent == iso9660->primary.rootent &&
 +		    strcmp(isoent->file->basename.s, "rr_moved") == 0)
 +			rr_flag |= RR_USE_RE;
 +#endif
 +	}
 +
 +	/* Write "SP" System Use Entry. */
 +	if (t == DIR_REC_SELF && isoent == isoent->parent) {
 +		length = 7;
 +		if (bp != NULL) {
 +			bp[1] = 'S';
 +			bp[2] = 'P';
 +			bp[3] = length;
 +			bp[4] = 1;	/* version	*/
 +			bp[5] = 0xBE;  /* Check Byte	*/
 +			bp[6] = 0xEF;  /* Check Byte	*/
 +			bp[7] = 0;
 +			bp += length;
 +		}
 +		extra_tell_used_size(&ctl, length);
 +	}
 +
 +	/* Write "RR" System Use Entry. */
 +	length = 5;
 +	if (extra_space(&ctl) < length)
 +		bp = extra_next_record(&ctl, length);
 +	if (bp != NULL) {
 +		bp[1] = 'R';
 +		bp[2] = 'R';
 +		bp[3] = length;
 +		bp[4] = 1;	/* version */
 +		bp[5] = rr_flag;
 +		bp += length;
 +	}
 +	extra_tell_used_size(&ctl, length);
 +
 +	/* Write "NM" System Use Entry. */
 +	if (rr_flag & RR_USE_NM) {
 +		/*
 +		 *   "NM" Format:
 +		 *     e.g. a basename is 'foo'
 +		 *               len  ver  flg
 +		 *    +----+----+----+----+----+----+----+----+
 +		 *    | 'N'| 'M'| 08 | 01 | 00 | 'f'| 'o'| 'o'|
 +		 *    +----+----+----+----+----+----+----+----+
 +		 *    <----------------- len ----------------->
 +		 */
 +		size_t nmlen = file->basename.length;
 +		const char *nm = file->basename.s;
 +		size_t nmmax;
 +
 +		if (extra_space(&ctl) < 6)
 +			bp = extra_next_record(&ctl, 6);
 +		if (bp != NULL) {
 +			bp[1] = 'N';
 +			bp[2] = 'M';
 +			bp[4] = 1;	    /* version	*/
 +		}
 +		nmmax = extra_space(&ctl);
 +		if (nmmax > 0xff)
 +			nmmax = 0xff;
 +		while (nmlen + 5 > nmmax) {
 +			length = (int)nmmax;
 +			if (bp != NULL) {
 +				bp[3] = length;
 +				bp[5] = 0x01;/* Alternate Name continues
 +					       * in next "NM" field */
 +				memcpy(bp+6, nm, length - 5);
 +				bp += length;
 +			}
 +			nmlen -= length - 5;
 +			nm += length - 5;
 +			extra_tell_used_size(&ctl, length);
 +			if (extra_space(&ctl) < 6) {
 +				bp = extra_next_record(&ctl, 6);
 +				nmmax = extra_space(&ctl);
 +				if (nmmax > 0xff)
 +					nmmax = 0xff;
 +			}
 +			if (bp != NULL) {
 +				bp[1] = 'N';
 +				bp[2] = 'M';
 +				bp[4] = 1;    /* version */
 +			}
 +		}
 +		length = 5 + (int)nmlen;
 +		if (bp != NULL) {
 +			bp[3] = length;
 +			bp[5] = 0;
 +			memcpy(bp+6, nm, nmlen);
 +			bp += length;
 +		}
 +		extra_tell_used_size(&ctl, length);
 +	}
 +
 +	/* Write "PX" System Use Entry. */
 +	if (rr_flag & RR_USE_PX) {
 +		/*
 +		 *   "PX" Format:
 +		 *               len  ver
 +		 *    +----+----+----+----+-----------+-----------+
 +		 *    | 'P'| 'X'| 2C | 01 | FILE MODE |   LINKS   |
 +		 *    +----+----+----+----+-----------+-----------+
 +		 *    0    1    2    3    4          12          20
 +		 *    +-----------+-----------+------------------+
 +		 *    |  USER ID  | GROUP ID  |FILE SERIAL NUMBER|
 +		 *    +-----------+-----------+------------------+
 +		 *   20          28          36                 44
 +		 */
 +		length = 44;
 +		if (extra_space(&ctl) < length)
 +			bp = extra_next_record(&ctl, length);
 +		if (bp != NULL) {
 +			mode_t mode;
 +			int64_t uid;
 +			int64_t gid;
 +
 +			mode = archive_entry_mode(file->entry);
 +			uid = archive_entry_uid(file->entry);
 +			gid = archive_entry_gid(file->entry);
 +			if (iso9660->opt.rr == OPT_RR_USEFUL) {
 +				/*
 +				 * This action is simular mkisofs -r option
 +				 * but our rockridge=useful option does not
 +				 * set a zero to uid and gid.
 +				 */
 +				/* set all read bit ON */
 +				mode |= 0444;
 +#if !defined(_WIN32) && !defined(__CYGWIN__)
 +				if (mode & 0111)
 +#endif
 +					/* set all exec bit ON */
 +					mode |= 0111;
 +				/* clear all write bits. */
 +				mode &= ~0222;
 +				/* clear setuid,setgid,sticky bits. */
 +				mode &= ~07000;
 +			}
 +
 +			bp[1] = 'P';
 +			bp[2] = 'X';
 +			bp[3] = length;
 +			bp[4] = 1;	/* version	*/
 +			/* file mode */
 +			set_num_733(bp+5, mode);
 +			/* file links (stat.st_nlink) */
 +			set_num_733(bp+13,
 +			    archive_entry_nlink(file->entry));
 +			set_num_733(bp+21, (uint32_t)uid);
 +			set_num_733(bp+29, (uint32_t)gid);
 +			/* File Serial Number */
 +			if (pxent->dir)
 +				set_num_733(bp+37, pxent->dir_location);
 +			else if (file->hardlink_target != NULL)
 +				set_num_733(bp+37,
 +				    file->hardlink_target->cur_content->location);
 +			else
 +				set_num_733(bp+37,
 +				    file->cur_content->location);
 +			bp += length;
 +		}
 +		extra_tell_used_size(&ctl, length);
 +	}
 +
 +	/* Write "SL" System Use Entry. */
 +	if (rr_flag & RR_USE_SL) {
 +		/*
 +		 *   "SL" Format:
 +		 *     e.g. a symbolic name is 'foo/bar'
 +		 *               len  ver  flg
 +		 *    +----+----+----+----+----+------------+
 +		 *    | 'S'| 'L'| 0F | 01 | 00 | components |
 +		 *    +----+----+----+----+----+-----+------+
 +		 *    0    1    2    3    4    5  ...|...  15
 +		 *    <----------------- len --------+------>
 +		 *    components :                   |
 +		 *     cflg clen                     |
 +		 *    +----+----+----+----+----+     |
 +		 *    | 00 | 03 | 'f'| 'o'| 'o'| <---+
 +		 *    +----+----+----+----+----+     |
 +		 *    5    6    7    8    9   10     |
 +		 *     cflg clen                     |
 +		 *    +----+----+----+----+----+     |
 +		 *    | 00 | 03 | 'b'| 'a'| 'r'| <---+
 +		 *    +----+----+----+----+----+
 +		 *   10   11   12   13   14   15
 +		 *
 +	 	 *    - cflg : flag of componet
 +		 *    - clen : length of componet
 +		 */
 +		const char *sl;
 +		char sl_last;
 +
 +		if (extra_space(&ctl) < 7)
 +			bp = extra_next_record(&ctl, 7);
 +		sl = file->symlink.s;
 +		sl_last = '\0';
 +		if (bp != NULL) {
 +			bp[1] = 'S';
 +			bp[2] = 'L';
 +			bp[4] = 1;	/* version	*/
 +		}
 +		for (;;) {
 +			unsigned char *nc, *cf,  *cl, cldmy = 0;
 +			int sllen, slmax;
 +
 +			slmax = extra_space(&ctl);
 +			if (slmax > 0xff)
 +				slmax = 0xff;
 +			if (bp != NULL)
 +				nc = &bp[6];
 +			else
 +				nc = NULL;
 +			cf = cl = NULL;
 +			sllen = 0;
 +			while (*sl && sllen + 11 < slmax) {
 +				if (sl_last == '\0' && sl[0] == '/') {
 +					/*
 +					 *     flg  len
 +					 *    +----+----+
 +					 *    | 08 | 00 | ROOT component.
 +					 *    +----+----+ ("/")
 +					 *
 +				 	 * Root component has to appear
 +				 	 * at the first component only.
 +					 */
 +					if (nc != NULL) {
 +						cf = nc++;
 +						*cf = 0x08; /* ROOT */
 +						*nc++ = 0;
 +					}
 +					sllen += 2;
 +					sl++;
 +					sl_last = '/';
 +					cl = NULL;
 +					continue;
 +				}
 +				if (((sl_last == '\0' || sl_last == '/') &&
 +				      sl[0] == '.' && sl[1] == '.' &&
 +				     (sl[2] == '/' || sl[2] == '\0')) ||
 +				    (sl[0] == '/' &&
 +				      sl[1] == '.' && sl[2] == '.' &&
 +				     (sl[3] == '/' || sl[3] == '\0'))) {
 +					/*
 +					 *     flg  len
 +					 *    +----+----+
 +					 *    | 04 | 00 | PARENT component.
 +					 *    +----+----+ ("..")
 +					 */
 +					if (nc != NULL) {
 +						cf = nc++;
 +						*cf = 0x04; /* PARENT */
 +						*nc++ = 0;
 +					}
 +					sllen += 2;
 +					if (sl[0] == '/')
 +						sl += 3;/* skip "/.." */
 +					else
 +						sl += 2;/* skip ".." */
 +					sl_last = '.';
 +					cl = NULL;
 +					continue;
 +				}
 +				if (((sl_last == '\0' || sl_last == '/') &&
 +				      sl[0] == '.' &&
 +				     (sl[1] == '/' || sl[1] == '\0')) ||
 +				    (sl[0] == '/' && sl[1] == '.' &&
 +				     (sl[2] == '/' || sl[2] == '\0'))) {
 +					/*
 +					 *     flg  len
 +					 *    +----+----+
 +					 *    | 02 | 00 | CURREENT component.
 +					 *    +----+----+ (".")
 +					 */
 +					if (nc != NULL) {
 +						cf = nc++;
 +						*cf = 0x02; /* CURRENT */
 +						*nc++ = 0;
 +					}
 +					sllen += 2;
 +					if (sl[0] == '/')
 +						sl += 2;/* skip "/." */
 +					else
 +						sl ++;  /* skip "." */
 +					sl_last = '.';
 +					cl = NULL;
 +					continue;
 +				}
 +				if (sl[0] == '/' || cl == NULL) {
 +					if (nc != NULL) {
 +						cf = nc++;
 +						*cf = 0;
 +						cl = nc++;
 +						*cl = 0;
 +					} else
 +						cl = &cldmy;
 +					sllen += 2;
 +					if (sl[0] == '/') {
 +						sl_last = *sl++;
 +						continue;
 +					}
 +				}
 +				sl_last = *sl++;
 +				if (nc != NULL) {
 +					*nc++ = sl_last;
 +					(*cl) ++;
 +				}
 +				sllen++;
 +			}
 +			if (*sl) {
 +				length = 5 + sllen;
 +				if (bp != NULL) {
 +					/*
 +					 * Mark flg as CONTINUE component.
 +					 */
 +					*cf |= 0x01;
 +					/*
 +					 *               len  ver  flg
 +					 *    +----+----+----+----+----+-
 +					 *    | 'S'| 'L'| XX | 01 | 01 |
 +					 *    +----+----+----+----+----+-
 +					 *                           ^
 +					 *           continues in next "SL"
 +					 */
 +					bp[3] = length;
 +					bp[5] = 0x01;/* This Symbolic Link
 +						      * continues in next
 +						      * "SL" field */
 +					bp += length;
 +				}
 +				extra_tell_used_size(&ctl, length);
 +				if (extra_space(&ctl) < 11)
 +					bp = extra_next_record(&ctl, 11);
 +				if (bp != NULL) {
 +					/* Next 'SL' */
 +					bp[1] = 'S';
 +					bp[2] = 'L';
 +					bp[4] = 1;    /* version */
 +				}
 +			} else {
 +				length = 5 + sllen;
 +				if (bp != NULL) {
 +					bp[3] = length;
 +					bp[5] = 0;
 +					bp += length;
 +				}
 +				extra_tell_used_size(&ctl, length);
 +				break;
 +			}
 +		}
 +	}
 +
 +	/* Write "TF" System Use Entry. */
 +	if (rr_flag & RR_USE_TF) {
 +		/*
 +		 *   "TF" Format:
 +		 *               len  ver
 +		 *    +----+----+----+----+-----+-------------+
 +		 *    | 'T'| 'F'| XX | 01 |FLAGS| TIME STAMPS |
 +		 *    +----+----+----+----+-----+-------------+
 +		 *    0    1    2    3    4     5            XX
 +		 *    TIME STAMPS : ISO 9660 Standard 9.1.5.
 +		 *                  If TF_LONG_FORM FLAGS is set,
 +		 *                  use ISO9660 Standard 8.4.26.1.
 +		 */
 +#define TF_CREATION	0x01	/* Creation time recorded		*/
 +#define TF_MODIFY	0x02	/* Modification time recorded		*/
 +#define TF_ACCESS	0x04	/* Last Access time recorded		*/
 +#define TF_ATTRIBUTES	0x08	/* Last Attribute Change time recorded  */
 +#define TF_BACKUP	0x10	/* Last Backup time recorded		*/
 +#define TF_EXPIRATION	0x20	/* Expiration time recorded		*/
 +#define TF_EFFECTIVE	0x40	/* Effective time recorded		*/
 +#define TF_LONG_FORM	0x80	/* ISO 9660 17-byte time format used	*/
 +		unsigned char tf_flags;
 +
 +		length = 5;
 +		tf_flags = 0;
 +#ifndef COMPAT_MKISOFS
 +		if (archive_entry_birthtime_is_set(file->entry) &&
 +		    archive_entry_birthtime(file->entry) <=
 +		    archive_entry_mtime(file->entry)) {
 +			length += 7;
 +			tf_flags |= TF_CREATION;
 +		}
 +#endif
 +		if (archive_entry_mtime_is_set(file->entry)) {
 +			length += 7;
 +			tf_flags |= TF_MODIFY;
 +		}
 +		if (archive_entry_atime_is_set(file->entry)) {
 +			length += 7;
 +			tf_flags |= TF_ACCESS;
 +		}
 +		if (archive_entry_ctime_is_set(file->entry)) {
 +			length += 7;
 +			tf_flags |= TF_ATTRIBUTES;
 +		}
 +		if (extra_space(&ctl) < length)
 +			bp = extra_next_record(&ctl, length);
 +		if (bp != NULL) {
 +			bp[1] = 'T';
 +			bp[2] = 'F';
 +			bp[3] = length;
 +			bp[4] = 1;	/* version	*/
 +			bp[5] = tf_flags;
 +			bp += 5;
 +			/* Creation time */
 +			if (tf_flags & TF_CREATION) {
 +				set_time_915(bp+1,
 +				    archive_entry_birthtime(file->entry));
 +				bp += 7;
 +			}
 +			/* Modification time */
 +			if (tf_flags & TF_MODIFY) {
 +				set_time_915(bp+1,
 +				    archive_entry_mtime(file->entry));
 +				bp += 7;
 +			}
 +			/* Last Access time */
 +			if (tf_flags & TF_ACCESS) {
 +				set_time_915(bp+1,
 +				    archive_entry_atime(file->entry));
 +				bp += 7;
 +			}
 +			/* Last Attribute Change time */
 +			if (tf_flags & TF_ATTRIBUTES) {
 +				set_time_915(bp+1,
 +				    archive_entry_ctime(file->entry));
 +				bp += 7;
 +			}
 +		}
 +		extra_tell_used_size(&ctl, length);
 +	}
 +
 +	/* Write "RE" System Use Entry. */
 +	if (rr_flag & RR_USE_RE) {
 +		/*
 +		 *   "RE" Format:
 +		 *               len  ver
 +		 *    +----+----+----+----+
 +		 *    | 'R'| 'E'| 04 | 01 |
 +		 *    +----+----+----+----+
 +		 *    0    1    2    3    4
 +		 */
 +		length = 4;
 +		if (extra_space(&ctl) < length)
 +			bp = extra_next_record(&ctl, length);
 +		if (bp != NULL) {
 +			bp[1] = 'R';
 +			bp[2] = 'E';
 +			bp[3] = length;
 +			bp[4] = 1;	/* version	*/
 +			bp += length;
 +		}
 +		extra_tell_used_size(&ctl, length);
 +	}
 +
 +	/* Write "PL" System Use Entry. */
 +	if (rr_flag & RR_USE_PL) {
 +		/*
 +		 *   "PL" Format:
 +		 *               len  ver
 +		 *    +----+----+----+----+------------+
 +		 *    | 'P'| 'L'| 0C | 01 | *LOCATION  |
 +		 *    +----+----+----+----+------------+
 +		 *    0    1    2    3    4           12
 +		 *    *LOCATION: location of parent directory
 +		 */
 +		length = 12;
 +		if (extra_space(&ctl) < length)
 +			bp = extra_next_record(&ctl, length);
 +		if (bp != NULL) {
 +			bp[1] = 'P';
 +			bp[2] = 'L';
 +			bp[3] = length;
 +			bp[4] = 1;	/* version	*/
 +			set_num_733(bp + 5,
 +			    rr_parent->dir_location);
 +			bp += length;
 +		}
 +		extra_tell_used_size(&ctl, length);
 +	}
 +
 +	/* Write "CL" System Use Entry. */
 +	if (rr_flag & RR_USE_CL) {
 +		/*
 +		 *   "CL" Format:
 +		 *               len  ver
 +		 *    +----+----+----+----+------------+
 +		 *    | 'C'| 'L'| 0C | 01 | *LOCATION  |
 +		 *    +----+----+----+----+------------+
 +		 *    0    1    2    3    4           12
 +		 *    *LOCATION: location of child directory
 +		 */
 +		length = 12;
 +		if (extra_space(&ctl) < length)
 +			bp = extra_next_record(&ctl, length);
 +		if (bp != NULL) {
 +			bp[1] = 'C';
 +			bp[2] = 'L';
 +			bp[3] = length;
 +			bp[4] = 1;	/* version	*/
 +			set_num_733(bp + 5,
 +			    isoent->rr_child->dir_location);
 +			bp += length;
 +		}
 +		extra_tell_used_size(&ctl, length);
 +	}
 +
 +	/* Write "PN" System Use Entry. */
 +	if (rr_flag & RR_USE_PN) {
 +		/*
 +		 *   "PN" Format:
 +		 *               len  ver
 +		 *    +----+----+----+----+------------+------------+
 +		 *    | 'P'| 'N'| 14 | 01 | dev_t high | dev_t low  |
 +		 *    +----+----+----+----+------------+------------+
 +		 *    0    1    2    3    4           12           20
 +		 */
 +		length = 20;
 +		if (extra_space(&ctl) < length)
 +			bp = extra_next_record(&ctl, length);
 +		if (bp != NULL) {
 +			uint64_t dev;
 +
 +			bp[1] = 'P';
 +			bp[2] = 'N';
 +			bp[3] = length;
 +			bp[4] = 1;	/* version	*/
 +			dev = (uint64_t)archive_entry_rdev(file->entry);
 +			set_num_733(bp + 5, (uint32_t)(dev >> 32));
 +			set_num_733(bp + 13, (uint32_t)(dev & 0xFFFFFFFF));
 +			bp += length;
 +		}
 +		extra_tell_used_size(&ctl, length);
 +	}
 +
 +	/* Write "ZF" System Use Entry. */
 +	if (file->zisofs.header_size) {
 +		/*
 +		 *   "ZF" Format:
 +		 *               len  ver
 +		 *    +----+----+----+----+----+----+-------------+
 +		 *    | 'Z'| 'F'| 10 | 01 | 'p'| 'z'| Header Size |
 +		 *    +----+----+----+----+----+----+-------------+
 +		 *    0    1    2    3    4    5    6             7
 +		 *    +--------------------+-------------------+
 +		 *    | Log2 of block Size | Uncompressed Size |
 +		 *    +--------------------+-------------------+
 +		 *    7                    8                   16
 +		 */
 +		length = 16;
 +		if (extra_space(&ctl) < length)
 +			bp = extra_next_record(&ctl, length);
 +		if (bp != NULL) {
 +			bp[1] = 'Z';
 +			bp[2] = 'F';
 +			bp[3] = length;
 +			bp[4] = 1;	/* version	*/
 +			bp[5] = 'p';
 +			bp[6] = 'z';
 +			bp[7] = file->zisofs.header_size;
 +			bp[8] = file->zisofs.log2_bs;
 +			set_num_733(bp + 9, file->zisofs.uncompressed_size);
 +			bp += length;
 +		}
 +		extra_tell_used_size(&ctl, length);
 +	}
 +
 +	/* Write "CE" System Use Entry. */
 +	if (t == DIR_REC_SELF && isoent == isoent->parent) {
 +		length = RR_CE_SIZE;
 +		if (bp != NULL)
 +			set_SUSP_CE(bp+1, iso9660->location_rrip_er,
 +			    0, RRIP_ER_SIZE);
 +		extra_tell_used_size(&ctl, length);
 +	}
 +
 +	extra_close_record(&ctl, 0);
 +
 +	return (ctl.dr_len);
 +}
 +
 +/*
 + * Write data of a Directory Record or calculate writing bytes itself.
 + * If parameter `p' is NULL, calculates the size of writing data, which
 + * a Directory Record needs to write, then it saved and return
 + * the calculated size.
 + * Parameter `n' is a remaining size of buffer. when parameter `p' is
 + * not NULL, check whether that `n' is not less than the saved size.
 + * if that `n' is small, return zero.
 + *
 + * This format of the Directory Record is according to
 + * ISO9660 Standard 9.1
 + */
 +static int
 +set_directory_record(unsigned char *p, size_t n, struct isoent *isoent,
 +    struct iso9660 *iso9660, enum dir_rec_type t,
 +    enum vdd_type vdd_type)
 +{
 +	unsigned char *bp;
 +	size_t dr_len;
 +	size_t fi_len;
 +
 +	if (p != NULL) {
 +		/*
 +		 * Check whether a write buffer size is less than the
 +		 * saved size which is needed to write this Directory
 +		 * Record.
 +		 */
 +		switch (t) {
 +		case DIR_REC_VD:
 +			dr_len = isoent->dr_len.vd; break;
 +		case DIR_REC_SELF:
 +			dr_len = isoent->dr_len.self; break;
 +		case DIR_REC_PARENT:
 +			dr_len = isoent->dr_len.parent; break;
 +		case DIR_REC_NORMAL:
 +		default:
 +			dr_len = isoent->dr_len.normal; break;
 +		}
 +		if (dr_len > n)
 +			return (0);/* Needs more buffer size. */
 +	}
 +
 +	if (t == DIR_REC_NORMAL && isoent->identifier != NULL)
 +		fi_len = isoent->id_len;
 +	else
 +		fi_len = 1;
 +
 +	if (p != NULL) {
 +		struct isoent *xisoent;
 +		struct isofile *file;
 +		unsigned char flag;
 +
 +		if (t == DIR_REC_PARENT)
 +			xisoent = isoent->parent;
 +		else
 +			xisoent = isoent;
 +		file = isoent->file;
 +		if (file->hardlink_target != NULL)
 +			file = file->hardlink_target;
 +		/* Make a file flag. */
 +		if (xisoent->dir)
 +			flag = FILE_FLAG_DIRECTORY;
 +		else {
 +			if (file->cur_content->next != NULL)
 +				flag = FILE_FLAG_MULTI_EXTENT;
 +			else
 +				flag = 0;
 +		}
 +
 +		bp = p -1;
 +		/* Extended Attribute Record Length */
 +		set_num_711(bp+2, 0);
 +		/* Location of Extent */
 +		if (xisoent->dir)
 +			set_num_733(bp+3, xisoent->dir_location);
 +		else
 +			set_num_733(bp+3, file->cur_content->location);
 +		/* Data Length */
 +		if (xisoent->dir)
 +			set_num_733(bp+11,
 +			    xisoent->dir_block * LOGICAL_BLOCK_SIZE);
 +		else
 +			set_num_733(bp+11, (uint32_t)file->cur_content->size);
 +		/* Recording Date and Time */
 +		/* NOTE:
 +		 *  If a file type is symbolic link, you are seeing this
 +		 *  field value is different from a value mkisofs makes.
 +		 *  libarchive uses lstat to get this one, but it
 +		 *  seems mkisofs uses stat to get.
 +		 */
 +		set_time_915(bp+19,
 +		    archive_entry_mtime(xisoent->file->entry));
 +		/* File Flags */
 +		bp[26] = flag;
 +		/* File Unit Size */
 +		set_num_711(bp+27, 0);
 +		/* Interleave Gap Size */
 +		set_num_711(bp+28, 0);
 +		/* Volume Sequence Number */
 +		set_num_723(bp+29, iso9660->volume_sequence_number);
 +		/* Length of File Identifier */
 +		set_num_711(bp+33, (unsigned char)fi_len);
 +		/* File Identifier */
 +		switch (t) {
 +		case DIR_REC_VD:
 +		case DIR_REC_SELF:
 +			set_num_711(bp+34, 0);
 +			break;
 +		case DIR_REC_PARENT:
 +			set_num_711(bp+34, 1);
 +			break;
 +		case DIR_REC_NORMAL:
 +			if (isoent->identifier != NULL)
 +				memcpy(bp+34, isoent->identifier, fi_len);
 +			else
 +				set_num_711(bp+34, 0);
 +			break;
 +		}
 +	} else
 +		bp = NULL;
 +	dr_len = 33 + fi_len;
 +	/* Padding Field */
 +	if (dr_len & 0x01) {
 +		dr_len ++;
 +		if (p != NULL)
 +			bp[dr_len] = 0;
 +	}
 +
 +	/* Volume Descriptor does not record extension. */
 +	if (t == DIR_REC_VD) {
 +		if (p != NULL)
 +			/* Length of Directory Record */
 +			set_num_711(p, (unsigned char)dr_len);
 +		else
 +			isoent->dr_len.vd = (int)dr_len;
 +		return ((int)dr_len);
 +	}
 +
 +	/* Rockridge */
 +	if (iso9660->opt.rr && vdd_type != VDD_JOLIET)
 +		dr_len = set_directory_record_rr(bp, (int)dr_len,
 +		    isoent, iso9660, t);
 +
 +	if (p != NULL)
 +		/* Length of Directory Record */
 +		set_num_711(p, (unsigned char)dr_len);
 +	else {
 +		/*
 +		 * Save the size which is needed to write this
 +		 * Directory Record.
 +		 */
 +		switch (t) {
 +		case DIR_REC_VD:
 +			/* This case does not come, but compiler
 +			 * complains that DIR_REC_VD not handled
 +			 *  in switch ....  */
 +			break;
 +		case DIR_REC_SELF:
 +			isoent->dr_len.self = (int)dr_len; break;
 +		case DIR_REC_PARENT:
 +			isoent->dr_len.parent = (int)dr_len; break;
 +		case DIR_REC_NORMAL:
 +			isoent->dr_len.normal = (int)dr_len; break;
 +		}
 +	}
 +
 +	return ((int)dr_len);
 +}
 +
 +/*
 + * Calculate the size of a directory record.
 + */
 +static inline int
 +get_dir_rec_size(struct iso9660 *iso9660, struct isoent *isoent,
 +    enum dir_rec_type t, enum vdd_type vdd_type)
 +{
 +
 +	return (set_directory_record(NULL, SIZE_MAX,
 +	    isoent, iso9660, t, vdd_type));
 +}
 +
 +/*
 + * Manage to write ISO-image data with wbuff to reduce calling
 + * __archive_write_output() for performance.
 + */
 +
 +
 +static inline unsigned char *
 +wb_buffptr(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
 +
 +	return (&(iso9660->wbuff[sizeof(iso9660->wbuff)
 +		- iso9660->wbuff_remaining]));
 +}
 +
 +static int
 +wb_write_out(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
 +	size_t wsize, nw;
 +	int r;
 +
 +	wsize = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining;
 +	nw = wsize % LOGICAL_BLOCK_SIZE;
 +	if (iso9660->wbuff_type == WB_TO_STREAM)
 +		r = __archive_write_output(a, iso9660->wbuff, wsize - nw);
 +	else
 +		r = write_to_temp(a, iso9660->wbuff, wsize - nw);
 +	/* Increase the offset. */
 +	iso9660->wbuff_offset += wsize - nw;
 +	if (iso9660->wbuff_offset > iso9660->wbuff_written)
 +		iso9660->wbuff_written = iso9660->wbuff_offset;
 +	iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
 +	if (nw) {
 +		iso9660->wbuff_remaining -= nw;
 +		memmove(iso9660->wbuff, iso9660->wbuff + wsize - nw, nw);
 +	}
 +	return (r);
 +}
 +
 +static int
 +wb_consume(struct archive_write *a, size_t size)
 +{
 +	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
 +
 +	if (size > iso9660->wbuff_remaining ||
 +	    iso9660->wbuff_remaining == 0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Internal Programing error: iso9660:wb_consume()"
 +		    " size=%jd, wbuff_remaining=%jd",
 +		    (intmax_t)size, (intmax_t)iso9660->wbuff_remaining);
 +		return (ARCHIVE_FATAL);
 +	}
 +	iso9660->wbuff_remaining -= size;
 +	if (iso9660->wbuff_remaining < LOGICAL_BLOCK_SIZE)
 +		return (wb_write_out(a));
 +	return (ARCHIVE_OK);
 +}
 +
 +#ifdef HAVE_ZLIB_H
 +
 +static int
 +wb_set_offset(struct archive_write *a, int64_t off)
 +{
 +	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
 +	int64_t used, ext_bytes;
 +
 +	if (iso9660->wbuff_type != WB_TO_TEMP) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Internal Programing error: iso9660:wb_set_offset()");
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	used = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining;
 +	if (iso9660->wbuff_offset + used > iso9660->wbuff_tail)
 +		iso9660->wbuff_tail = iso9660->wbuff_offset + used;
 +	if (iso9660->wbuff_offset < iso9660->wbuff_written) {
 +		if (used > 0 &&
 +		    write_to_temp(a, iso9660->wbuff, (size_t)used) != ARCHIVE_OK)
 +			return (ARCHIVE_FATAL);
 +		iso9660->wbuff_offset = iso9660->wbuff_written;
 +		lseek(iso9660->temp_fd, iso9660->wbuff_offset, SEEK_SET);
 +		iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
 +		used = 0;
 +	}
 +	if (off < iso9660->wbuff_offset) {
 +		/*
 +		 * Write out waiting data.
 +		 */
 +		if (used > 0) {
 +			if (wb_write_out(a) != ARCHIVE_OK)
 +				return (ARCHIVE_FATAL);
 +		}
 +		lseek(iso9660->temp_fd, off, SEEK_SET);
 +		iso9660->wbuff_offset = off;
 +		iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
 +	} else if (off <= iso9660->wbuff_tail) {
 +		iso9660->wbuff_remaining = (size_t)
 +		    (sizeof(iso9660->wbuff) - (off - iso9660->wbuff_offset));
 +	} else {
 +		ext_bytes = off - iso9660->wbuff_tail;
 +		iso9660->wbuff_remaining = (size_t)(sizeof(iso9660->wbuff)
 +		   - (iso9660->wbuff_tail - iso9660->wbuff_offset));
 +		while (ext_bytes >= (int64_t)iso9660->wbuff_remaining) {
 +			if (write_null(a, (size_t)iso9660->wbuff_remaining)
 +			    != ARCHIVE_OK)
 +				return (ARCHIVE_FATAL);
 +			ext_bytes -= iso9660->wbuff_remaining;
 +		}
 +		if (ext_bytes > 0) {
 +			if (write_null(a, (size_t)ext_bytes) != ARCHIVE_OK)
 +				return (ARCHIVE_FATAL);
 +		}
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +#endif /* HAVE_ZLIB_H */
 +
 +static int
 +write_null(struct archive_write *a, size_t size)
 +{
 +	size_t remaining;
 +	unsigned char *p, *old;
 +	int r;
 +
 +	remaining = wb_remaining(a);
 +	p = wb_buffptr(a);
 +	if (size <= remaining) {
 +		memset(p, 0, size);
 +		return (wb_consume(a, size));
 +	}
 +	memset(p, 0, remaining);
 +	r = wb_consume(a, remaining);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	size -= remaining;
 +	old = p;
 +	p = wb_buffptr(a);
 +	memset(p, 0, old - p);
 +	remaining = wb_remaining(a);
 +	while (size) {
 +		size_t wsize = size;
 +
 +		if (wsize > remaining)
 +			wsize = remaining;
 +		r = wb_consume(a, wsize);
 +		if (r != ARCHIVE_OK)
 +			return (r);
 +		size -= wsize;
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Write Volume Descriptor Set Terminator
 + */
 +static int
 +write_VD_terminator(struct archive_write *a)
 +{
 +	unsigned char *bp;
 +
 +	bp = wb_buffptr(a) -1;
 +	set_VD_bp(bp, VDT_TERMINATOR, 1);
 +	set_unused_field_bp(bp, 8, LOGICAL_BLOCK_SIZE);
 +
 +	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
 +}
 +
 +static int
 +set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc,
 +    struct archive_write *a, struct vdd *vdd, struct archive_string *id,
 +    const char *label, int leading_under, enum char_type char_type)
 +{
 +	char identifier[256];
 +	struct isoent *isoent;
 +	const char *ids;
 +	size_t len;
 +	int r;
 +
 +	if (id->length > 0 && leading_under && id->s[0] != '_') {
 +		if (char_type == A_CHAR)
 +			r = set_str_a_characters_bp(a, bp, from, to, id->s, vdc);
 +		else
 +			r = set_str_d_characters_bp(a, bp, from, to, id->s, vdc);
 +	} else if (id->length > 0) {
 +		ids = id->s;
 +		if (leading_under)
 +			ids++;
 +		isoent = isoent_find_entry(vdd->rootent, ids);
 +		if (isoent == NULL) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Not Found %s `%s'.",
 +			    label, ids);
 +			return (ARCHIVE_FATAL);
 +		}
 +		len = isoent->ext_off + isoent->ext_len;
 +		if (vdd->vdd_type == VDD_JOLIET) {
 +			if (len > sizeof(identifier)-2)
 +				len = sizeof(identifier)-2;
 +		} else {
 +			if (len > sizeof(identifier)-1)
 +				len = sizeof(identifier)-1;
 +		}
 +		memcpy(identifier, isoent->identifier, len);
 +		identifier[len] = '\0';
 +		if (vdd->vdd_type == VDD_JOLIET) {
 +			identifier[len+1] = 0;
 +			vdc = VDC_UCS2_DIRECT;
 +		}
 +		if (char_type == A_CHAR)
 +			r = set_str_a_characters_bp(a, bp, from, to,
 +			    identifier, vdc);
 +		else
 +			r = set_str_d_characters_bp(a, bp, from, to,
 +			    identifier, vdc);
 +	} else {
 +		if (char_type == A_CHAR)
 +			r = set_str_a_characters_bp(a, bp, from, to, NULL, vdc);
 +		else
 +			r = set_str_d_characters_bp(a, bp, from, to, NULL, vdc);
 +	}
 +	return (r);
 +}
 +
 +/*
 + * Write Primary/Supplementary Volume Descriptor
 + */
 +static int
 +write_VD(struct archive_write *a, struct vdd *vdd)
 +{
 +	struct iso9660 *iso9660;
 +	unsigned char *bp;
 +	uint16_t volume_set_size = 1;
 +	char identifier[256];
 +	enum VD_type vdt;
 +	enum vdc vdc;
 +	unsigned char vd_ver, fst_ver;
 +	int r;
 +
 +	iso9660 = a->format_data;
 +	switch (vdd->vdd_type) {
 +	case VDD_JOLIET:
 +		vdt = VDT_SUPPLEMENTARY;
 +		vd_ver = fst_ver = 1;
 +		vdc = VDC_UCS2;
 +		break;
 +	case VDD_ENHANCED:
 +		vdt = VDT_SUPPLEMENTARY;
 +		vd_ver = fst_ver = 2;
 +		vdc = VDC_LOWERCASE;
 +		break;
 +	case VDD_PRIMARY:
 +	default:
 +		vdt = VDT_PRIMARY;
 +		vd_ver = fst_ver = 1;
 +#ifdef COMPAT_MKISOFS
 +		vdc = VDC_LOWERCASE;
 +#else
 +		vdc = VDC_STD;
 +#endif
 +		break;
 +	}
 +
 +	bp = wb_buffptr(a) -1;
 +	/* Volume Descriptor Type */
 +	set_VD_bp(bp, vdt, vd_ver);
 +	/* Unused Field */
 +	set_unused_field_bp(bp, 8, 8);
 +	/* System Identifier */
 +	get_system_identitier(identifier, sizeof(identifier));
 +	r = set_str_a_characters_bp(a, bp, 9, 40, identifier, vdc);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	/* Volume Identifier */
 +	r = set_str_d_characters_bp(a, bp, 41, 72,
 +	    iso9660->volume_identifier.s, vdc);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	/* Unused Field */
 +	set_unused_field_bp(bp, 73, 80);
 +	/* Volume Space Size */
 +	set_num_733(bp+81, iso9660->volume_space_size);
 +	if (vdd->vdd_type == VDD_JOLIET) {
 +		/* Escape Sequences */
 +		bp[89] = 0x25;/* UCS-2 Level 3 */
 +		bp[90] = 0x2F;
 +		bp[91] = 0x45;
 +		memset(bp + 92, 0, 120 - 92 + 1);
 +	} else {
 +		/* Unused Field */
 +		set_unused_field_bp(bp, 89, 120);
 +	}
 +	/* Volume Set Size */
 +	set_num_723(bp+121, volume_set_size);
 +	/* Volume Sequence Number */
 +	set_num_723(bp+125, iso9660->volume_sequence_number);
 +	/* Logical Block Size */
 +	set_num_723(bp+129, LOGICAL_BLOCK_SIZE);
 +	/* Path Table Size */
 +	set_num_733(bp+133, vdd->path_table_size);
 +	/* Location of Occurrence of Type L Path Table */
 +	set_num_731(bp+141, vdd->location_type_L_path_table);
 +	/* Location of Optional Occurrence of Type L Path Table */
 +	set_num_731(bp+145, 0);
 +	/* Location of Occurrence of Type M Path Table */
 +	set_num_732(bp+149, vdd->location_type_M_path_table);
 +	/* Location of Optional Occurrence of Type M Path Table */
 +	set_num_732(bp+153, 0);
 +	/* Directory Record for Root Directory(BP 157 to 190) */
 +	set_directory_record(bp+157, 190-157+1, vdd->rootent,
 +	    iso9660, DIR_REC_VD, vdd->vdd_type);
 +	/* Volume Set Identifier */
 +	r = set_str_d_characters_bp(a, bp, 191, 318, "", vdc);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	/* Publisher Identifier */
 +	r = set_file_identifier(bp, 319, 446, vdc, a, vdd,
 +	    &(iso9660->publisher_identifier),
 +	    "Publisher File", 1, A_CHAR);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	/* Data Preparer Identifier */
 +	r = set_file_identifier(bp, 447, 574, vdc, a, vdd,
 +	    &(iso9660->data_preparer_identifier),
 +	    "Data Preparer File", 1, A_CHAR);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	/* Application Identifier */
 +	r = set_file_identifier(bp, 575, 702, vdc, a, vdd,
 +	    &(iso9660->application_identifier),
 +	    "Application File", 1, A_CHAR);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	/* Copyright File Identifier */
 +	r = set_file_identifier(bp, 703, 739, vdc, a, vdd,
 +	    &(iso9660->copyright_file_identifier),
 +	    "Copyright File", 0, D_CHAR);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	/* Abstract File Identifier */
 +	r = set_file_identifier(bp, 740, 776, vdc, a, vdd,
 +	    &(iso9660->abstract_file_identifier),
 +	    "Abstract File", 0, D_CHAR);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	/* Bibliongraphic File Identifier */
 +	r = set_file_identifier(bp, 777, 813, vdc, a, vdd,
 +	    &(iso9660->bibliographic_file_identifier),
 +	    "Bibliongraphic File", 0, D_CHAR);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	/* Volume Creation Date and Time */
 +	set_date_time(bp+814, iso9660->birth_time);
 +	/* Volume Modification Date and Time */
 +	set_date_time(bp+831, iso9660->birth_time);
 +	/* Volume Expiration Date and Time(obsolete) */
 +	set_date_time_null(bp+848);
 +	/* Volume Effective Date and Time */
 +	set_date_time(bp+865, iso9660->birth_time);
 +	/* File Structure Version */
 +	bp[882] = fst_ver;
 +	/* Reserved */
 +	bp[883] = 0;
 +	/* Application Use */
 +	memset(bp + 884, 0x20, 1395 - 884 + 1);
 +	/* Reserved */
 +	set_unused_field_bp(bp, 1396, LOGICAL_BLOCK_SIZE);
 +
 +	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
 +}
 +
 +/*
 + * Write Boot Record Volume Descriptor
 + */
 +static int
 +write_VD_boot_record(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660;
 +	unsigned char *bp;
 +
 +	iso9660 = a->format_data;
 +	bp = wb_buffptr(a) -1;
 +	/* Volume Descriptor Type */
 +	set_VD_bp(bp, VDT_BOOT_RECORD, 1);
 +	/* Boot System Identifier */
 +	memcpy(bp+8, "EL TORITO SPECIFICATION", 23);
 +	set_unused_field_bp(bp, 8+23, 39);
 +	/* Unused */
 +	set_unused_field_bp(bp, 40, 71);
 +	/* Absolute pointer to first sector of Boot Catalog */
 +	set_num_731(bp+72,
 +	    iso9660->el_torito.catalog->file->content.location);
 +	/* Unused */
 +	set_unused_field_bp(bp, 76, LOGICAL_BLOCK_SIZE);
 +
 +	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
 +}
 +
 +enum keytype {
 +	KEY_FLG,
 +	KEY_STR,
 +	KEY_INT,
 +	KEY_HEX
 +};
 +static void
 +set_option_info(struct archive_string *info, int *opt, const char *key,
 +    enum keytype type,  ...)
 +{
 +	va_list ap;
 +	char prefix;
 +	const char *s;
 +	int d;
 +
 +	prefix = (*opt==0)? ' ':',';
 +	va_start(ap, type);
 +	switch (type) {
 +	case KEY_FLG:
 +		d = va_arg(ap, int);
 +		archive_string_sprintf(info, "%c%s%s",
 +		    prefix, (d == 0)?"!":"", key);
 +		break;
 +	case KEY_STR:
 +		s = va_arg(ap, const char *);
 +		archive_string_sprintf(info, "%c%s=%s",
 +		    prefix, key, s);
 +		break;
 +	case KEY_INT:
 +		d = va_arg(ap, int);
 +		archive_string_sprintf(info, "%c%s=%d",
 +		    prefix, key, d);
 +		break;
 +	case KEY_HEX:
 +		d = va_arg(ap, int);
 +		archive_string_sprintf(info, "%c%s=%x",
 +		    prefix, key, d);
 +		break;
 +	}
 +	va_end(ap);
 +
 +	*opt = 1;
 +}
 +
 +/*
 + * Make Non-ISO File System Information
 + */
 +static int
 +write_information_block(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660;
 +	char buf[128];
 +	const char *v;
 +	int opt, r;
 +	struct archive_string info;
 +	size_t info_size = LOGICAL_BLOCK_SIZE *
 +			       NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;
 +
 +	iso9660 = (struct iso9660 *)a->format_data;
 +	if (info_size > wb_remaining(a)) {
 +		r = wb_write_out(a);
 +		if (r != ARCHIVE_OK)
 +			return (r);
 +	}
 +	archive_string_init(&info);
 +	if (archive_string_ensure(&info, info_size) == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate memory");
 +		return (ARCHIVE_FATAL);
 +	}
 +	memset(info.s, 0, info_size);
 +	opt = 0;
 +#if defined(HAVE__CTIME64_S)
 +	_ctime64_s(buf, sizeof(buf), &(iso9660->birth_time));
 +#elif defined(HAVE_CTIME_R)
 +	ctime_r(&(iso9660->birth_time), buf);
 +#else
 +	strncpy(buf, ctime(&(iso9660->birth_time)), sizeof(buf)-1);
 +	buf[sizeof(buf)-1] = '\0';
 +#endif
 +	archive_string_sprintf(&info,
 +	    "INFO %s%s", buf, archive_version_string());
 +	if (iso9660->opt.abstract_file != OPT_ABSTRACT_FILE_DEFAULT)
 +		set_option_info(&info, &opt, "abstract-file",
 +		    KEY_STR, iso9660->abstract_file_identifier.s);
 +	if (iso9660->opt.application_id != OPT_APPLICATION_ID_DEFAULT)
 +		set_option_info(&info, &opt, "application-id",
 +		    KEY_STR, iso9660->application_identifier.s);
 +	if (iso9660->opt.allow_vernum != OPT_ALLOW_VERNUM_DEFAULT)
 +		set_option_info(&info, &opt, "allow-vernum",
 +		    KEY_FLG, iso9660->opt.allow_vernum);
 +	if (iso9660->opt.biblio_file != OPT_BIBLIO_FILE_DEFAULT)
 +		set_option_info(&info, &opt, "biblio-file",
 +		    KEY_STR, iso9660->bibliographic_file_identifier.s);
 +	if (iso9660->opt.boot != OPT_BOOT_DEFAULT)
 +		set_option_info(&info, &opt, "boot",
 +		    KEY_STR, iso9660->el_torito.boot_filename.s);
 +	if (iso9660->opt.boot_catalog != OPT_BOOT_CATALOG_DEFAULT)
 +		set_option_info(&info, &opt, "boot-catalog",
 +		    KEY_STR, iso9660->el_torito.catalog_filename.s);
 +	if (iso9660->opt.boot_info_table != OPT_BOOT_INFO_TABLE_DEFAULT)
 +		set_option_info(&info, &opt, "boot-info-table",
 +		    KEY_FLG, iso9660->opt.boot_info_table);
 +	if (iso9660->opt.boot_load_seg != OPT_BOOT_LOAD_SEG_DEFAULT)
 +		set_option_info(&info, &opt, "boot-load-seg",
 +		    KEY_HEX, iso9660->el_torito.boot_load_seg);
 +	if (iso9660->opt.boot_load_size != OPT_BOOT_LOAD_SIZE_DEFAULT)
 +		set_option_info(&info, &opt, "boot-load-size",
 +		    KEY_INT, iso9660->el_torito.boot_load_size);
 +	if (iso9660->opt.boot_type != OPT_BOOT_TYPE_DEFAULT) {
 +		v = "no-emulation";
 +		if (iso9660->opt.boot_type == OPT_BOOT_TYPE_FD)
 +			v = "fd";
 +		if (iso9660->opt.boot_type == OPT_BOOT_TYPE_HARD_DISK)
 +			v = "hard-disk";
 +		set_option_info(&info, &opt, "boot-type",
 +		    KEY_STR, v);
 +	}
 +#ifdef HAVE_ZLIB_H
 +	if (iso9660->opt.compression_level != OPT_COMPRESSION_LEVEL_DEFAULT)
 +		set_option_info(&info, &opt, "compression-level",
 +		    KEY_INT, iso9660->zisofs.compression_level);
 +#endif
 +	if (iso9660->opt.copyright_file != OPT_COPYRIGHT_FILE_DEFAULT)
 +		set_option_info(&info, &opt, "copyright-file",
 +		    KEY_STR, iso9660->copyright_file_identifier.s);
 +	if (iso9660->opt.iso_level != OPT_ISO_LEVEL_DEFAULT)
 +		set_option_info(&info, &opt, "iso-level",
 +		    KEY_INT, iso9660->opt.iso_level);
 +	if (iso9660->opt.joliet != OPT_JOLIET_DEFAULT) {
 +		if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)
 +			set_option_info(&info, &opt, "joliet",
 +			    KEY_STR, "long");
 +		else
 +			set_option_info(&info, &opt, "joliet",
 +			    KEY_FLG, iso9660->opt.joliet);
 +	}
 +	if (iso9660->opt.limit_depth != OPT_LIMIT_DEPTH_DEFAULT)
 +		set_option_info(&info, &opt, "limit-depth",
 +		    KEY_FLG, iso9660->opt.limit_depth);
 +	if (iso9660->opt.limit_dirs != OPT_LIMIT_DIRS_DEFAULT)
 +		set_option_info(&info, &opt, "limit-dirs",
 +		    KEY_FLG, iso9660->opt.limit_dirs);
 +	if (iso9660->opt.pad != OPT_PAD_DEFAULT)
 +		set_option_info(&info, &opt, "pad",
 +		    KEY_FLG, iso9660->opt.pad);
 +	if (iso9660->opt.publisher != OPT_PUBLISHER_DEFAULT)
 +		set_option_info(&info, &opt, "publisher",
 +		    KEY_STR, iso9660->publisher_identifier.s);
 +	if (iso9660->opt.rr != OPT_RR_DEFAULT) {
 +		if (iso9660->opt.rr == OPT_RR_DISABLED)
 +			set_option_info(&info, &opt, "rockridge",
 +			    KEY_FLG, iso9660->opt.rr);
 +		else if (iso9660->opt.rr == OPT_RR_STRICT)
 +			set_option_info(&info, &opt, "rockridge",
 +			    KEY_STR, "strict");
 +		else if (iso9660->opt.rr == OPT_RR_USEFUL)
 +			set_option_info(&info, &opt, "rockridge",
 +			    KEY_STR, "useful");
 +	}
 +	if (iso9660->opt.volume_id != OPT_VOLUME_ID_DEFAULT)
 +		set_option_info(&info, &opt, "volume-id",
 +		    KEY_STR, iso9660->volume_identifier.s);
 +	if (iso9660->opt.zisofs != OPT_ZISOFS_DEFAULT)
 +		set_option_info(&info, &opt, "zisofs",
 +		    KEY_FLG, iso9660->opt.zisofs);
 +
 +	memcpy(wb_buffptr(a), info.s, info_size);
 +	archive_string_free(&info);
 +	return (wb_consume(a, info_size));
 +}
 +
 +static int
 +write_rr_ER(struct archive_write *a)
 +{
 +	unsigned char *p;
 +
 +	p = wb_buffptr(a);
 +
 +	memset(p, 0, LOGICAL_BLOCK_SIZE);
 +	p[0] = 'E';
 +	p[1] = 'R';
 +	p[3] = 0x01;
 +	p[2] = RRIP_ER_SIZE;
 +	p[4] = RRIP_ER_ID_SIZE;
 +	p[5] = RRIP_ER_DSC_SIZE;
 +	p[6] = RRIP_ER_SRC_SIZE;
 +	p[7] = 0x01;
 +	memcpy(&p[8], rrip_identifier, p[4]);
 +	memcpy(&p[8+p[4]], rrip_descriptor, p[5]);
 +	memcpy(&p[8+p[4]+p[5]], rrip_source, p[6]);
 +
 +	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
 +}
 +
 +static void
 +calculate_path_table_size(struct vdd *vdd)
 +{
 +	int depth, size;
 +	struct path_table *pt;
 +
 +	pt = vdd->pathtbl;
 +	size = 0;
 +	for (depth = 0; depth < vdd->max_depth; depth++) {
 +		struct isoent **ptbl;
 +		int i, cnt;
 +
 +		if ((cnt = pt[depth].cnt) == 0)
 +			break;
 +
 +		ptbl = pt[depth].sorted;
 +		for (i = 0; i < cnt; i++) {
 +			int len;
 +
 +			if (ptbl[i]->identifier == NULL)
 +				len = 1; /* root directory */
 +			else
 +				len = ptbl[i]->id_len;
 +			if (len & 0x01)
 +				len++; /* Padding Field */
 +			size += 8 + len;
 +		}
 +	}
 +	vdd->path_table_size = size;
 +	vdd->path_table_block =
 +	    ((size + PATH_TABLE_BLOCK_SIZE -1) /
 +	    PATH_TABLE_BLOCK_SIZE) *
 +	    (PATH_TABLE_BLOCK_SIZE / LOGICAL_BLOCK_SIZE);
 +}
 +
 +static int
 +_write_path_table(struct archive_write *a, int type_m, int depth,
 +    struct vdd *vdd)
 +{
 +	unsigned char *bp, *wb;
 +	struct isoent **ptbl;
 +	size_t wbremaining;
 +	int i, r, wsize;
 +
 +	if (vdd->pathtbl[depth].cnt == 0)
 +		return (0);
 +
 +	wsize = 0;
 +	wb = wb_buffptr(a);
 +	wbremaining = wb_remaining(a);
 +	bp = wb - 1;
 +	ptbl = vdd->pathtbl[depth].sorted;
 +	for (i = 0; i < vdd->pathtbl[depth].cnt; i++) {
 +		struct isoent *np;
 +		size_t len;
 +
 +		np = ptbl[i];
 +		if (np->identifier == NULL)
 +			len = 1; /* root directory */
 +		else
 +			len = np->id_len;
 +		if (wbremaining - ((bp+1) - wb) < (len + 1 + 8)) {
 +			r = wb_consume(a, (bp+1) - wb);
 +			if (r < 0)
 +				return (r);
 +			wb = wb_buffptr(a);
 +			wbremaining = wb_remaining(a);
 +			bp = wb -1;
 +		}
 +		/* Length of Directory Identifier */
 +		set_num_711(bp+1, (unsigned char)len);
 +		/* Extended Attribute Record Length */
 +		set_num_711(bp+2, 0);
 +		/* Location of Extent */
 +		if (type_m)
 +			set_num_732(bp+3, np->dir_location);
 +		else
 +			set_num_731(bp+3, np->dir_location);
 +		/* Parent Directory Number */
 +		if (type_m)
 +			set_num_722(bp+7, np->parent->dir_number);
 +		else
 +			set_num_721(bp+7, np->parent->dir_number);
 +		/* Directory Identifier */
 +		if (np->identifier == NULL)
 +			bp[9] = 0;
 +		else
 +			memcpy(&bp[9], np->identifier, len);
 +		if (len & 0x01) {
 +			/* Padding Field */
 +			bp[9+len] = 0;
 +			len++;
 +		}
 +		wsize += 8 + (int)len;
 +		bp += 8 + len;
 +	}
 +	if ((bp + 1) > wb) {
 +		r = wb_consume(a, (bp+1)-wb);
 +		if (r < 0)
 +			return (r);
 +	}
 +	return (wsize);
 +}
 +
 +static int
 +write_path_table(struct archive_write *a, int type_m, struct vdd *vdd)
 +{
 +	int depth, r;
 +	size_t path_table_size;
 +
 +	r = ARCHIVE_OK;
 +	path_table_size = 0;
 +	for (depth = 0; depth < vdd->max_depth; depth++) {
 +		r = _write_path_table(a, type_m, depth, vdd);
 +		if (r < 0)
 +			return (r);
 +		path_table_size += r;
 +	}
 +
 +	/* Write padding data. */
 +	path_table_size = path_table_size % PATH_TABLE_BLOCK_SIZE;
 +	if (path_table_size > 0)
 +		r = write_null(a, PATH_TABLE_BLOCK_SIZE - path_table_size);
 +	return (r);
 +}
 +
 +static int
 +calculate_directory_descriptors(struct iso9660 *iso9660, struct vdd *vdd,
 +    struct isoent *isoent, int depth)
 +{
 +	struct isoent **enttbl;
 +	int bs, block, i;
 +
 +	block = 1;
 +	bs = get_dir_rec_size(iso9660, isoent, DIR_REC_SELF, vdd->vdd_type);
 +	bs += get_dir_rec_size(iso9660, isoent, DIR_REC_PARENT, vdd->vdd_type);
 +
 +	if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET &&
 +	    !iso9660->opt.rr && depth + 1 >= vdd->max_depth))
 +		return (block);
 +
 +	enttbl = isoent->children_sorted;
 +	for (i = 0; i < isoent->children.cnt; i++) {
 +		struct isoent *np = enttbl[i];
 +		struct isofile *file;
 +
 +		file = np->file;
 +		if (file->hardlink_target != NULL)
 +			file = file->hardlink_target;
 +		file->cur_content = &(file->content);
 +		do {
 +			int dr_l;
 +
 +			dr_l = get_dir_rec_size(iso9660, np, DIR_REC_NORMAL,
 +			    vdd->vdd_type);
 +			if ((bs + dr_l) > LOGICAL_BLOCK_SIZE) {
 +				block ++;
 +				bs = dr_l;
 +			} else
 +				bs += dr_l;
 +			file->cur_content = file->cur_content->next;
 +		} while (file->cur_content != NULL);
 +	}
 +	return (block);
 +}
 +
 +static int
 +_write_directory_descriptors(struct archive_write *a, struct vdd *vdd,
 +    struct isoent *isoent, int depth)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct isoent **enttbl;
 +	unsigned char *p, *wb;
 +	int i, r;
 +	int dr_l;
 +
 +	p = wb = wb_buffptr(a);
 +#define WD_REMAINING	(LOGICAL_BLOCK_SIZE - (p - wb))
 +	p += set_directory_record(p, WD_REMAINING, isoent,
 +	    iso9660, DIR_REC_SELF, vdd->vdd_type);
 +	p += set_directory_record(p, WD_REMAINING, isoent,
 +	    iso9660, DIR_REC_PARENT, vdd->vdd_type);
 +
 +	if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET &&
 +	    !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) {
 +		memset(p, 0, WD_REMAINING);
 +		return (wb_consume(a, LOGICAL_BLOCK_SIZE));
 +	}
 +
 +	enttbl = isoent->children_sorted;
 +	for (i = 0; i < isoent->children.cnt; i++) {
 +		struct isoent *np = enttbl[i];
 +		struct isofile *file = np->file;
 +
 +		if (file->hardlink_target != NULL)
 +			file = file->hardlink_target;
 +		file->cur_content = &(file->content);
 +		do {
 +			dr_l = set_directory_record(p, WD_REMAINING,
 +			    np, iso9660, DIR_REC_NORMAL,
 +			    vdd->vdd_type);
 +			if (dr_l == 0) {
 +				memset(p, 0, WD_REMAINING);
 +				r = wb_consume(a, LOGICAL_BLOCK_SIZE);
 +				if (r < 0)
 +					return (r);
 +				p = wb = wb_buffptr(a);
 +				dr_l = set_directory_record(p,
 +				    WD_REMAINING, np, iso9660,
 +				    DIR_REC_NORMAL, vdd->vdd_type);
 +			}
 +			p += dr_l;
 +			file->cur_content = file->cur_content->next;
 +		} while (file->cur_content != NULL);
 +	}
 +	memset(p, 0, WD_REMAINING);
 +	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
 +}
 +
 +static int
 +write_directory_descriptors(struct archive_write *a, struct vdd *vdd)
 +{
 +	struct isoent *np;
 +	int depth, r;
 +
 +	depth = 0;
 +	np = vdd->rootent;
 +	do {
 +		struct extr_rec *extr;
 +
 +		r = _write_directory_descriptors(a, vdd, np, depth);
 +		if (r < 0)
 +			return (r);
 +		if (vdd->vdd_type != VDD_JOLIET) {
 +			/*
 +			 * This extract record is used by SUSP,RRIP.
 +			 * Not for joliet.
 +			 */
 +			for (extr = np->extr_rec_list.first;
 +			    extr != NULL;
 +			    extr = extr->next) {
 +				unsigned char *wb;
 +
 +				wb = wb_buffptr(a);
 +				memcpy(wb, extr->buf, extr->offset);
 +				memset(wb + extr->offset, 0,
 +				    LOGICAL_BLOCK_SIZE - extr->offset);
 +				r = wb_consume(a, LOGICAL_BLOCK_SIZE);
 +				if (r < 0)
 +					return (r);
 +			}
 +		}
 +
 +		if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
 +			/* Enter to sub directories. */
 +			np = np->subdirs.first;
 +			depth++;
 +			continue;
 +		}
 +		while (np != np->parent) {
 +			if (np->drnext == NULL) {
 +				/* Return to the parent directory. */
 +				np = np->parent;
 +				depth--;
 +			} else {
 +				np = np->drnext;
 +				break;
 +			}
 +		}
 +	} while (np != np->parent);
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Read file contents from the temporary file, and write it.
 + */
 +static int
 +write_file_contents(struct archive_write *a, int64_t offset, int64_t size)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	int r;
 +
 +	lseek(iso9660->temp_fd, offset, SEEK_SET);
 +
 +	while (size) {
 +		size_t rsize;
 +		ssize_t rs;
 +		unsigned char *wb;
 +
 +		wb = wb_buffptr(a);
 +		rsize = wb_remaining(a);
 +		if (rsize > (size_t)size)
 +			rsize = (size_t)size;
 +		rs = read(iso9660->temp_fd, wb, rsize);
 +		if (rs <= 0) {
 +			archive_set_error(&a->archive, errno,
 +			    "Can't read temporary file(%jd)", (intmax_t)rs);
 +			return (ARCHIVE_FATAL);
 +		}
 +		size -= rs;
 +		r = wb_consume(a, rs);
 +		if (r < 0)
 +			return (r);
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +write_file_descriptors(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct isofile *file;
 +	int64_t blocks, offset;
 +	int r;
 +
 +	blocks = 0;
 +	offset = 0;
 +
 +	/* Make the boot catalog contents, and write it. */
 +	if (iso9660->el_torito.catalog != NULL) {
 +		r = make_boot_catalog(a);
 +		if (r < 0)
 +			return (r);
 +	}
 +
 +	/* Write the boot file contents. */
 +	if (iso9660->el_torito.boot != NULL) {
 +		file = iso9660->el_torito.boot->file;
 +		blocks = file->content.blocks;
 +		offset = file->content.offset_of_temp;
 +		if (offset != 0) {
 +			r = write_file_contents(a, offset,
 +			    blocks << LOGICAL_BLOCK_BITS);
 +			if (r < 0)
 +				return (r);
 +			blocks = 0;
 +			offset = 0;
 +		}
 +	}
 +
 +	/* Write out all file contents. */
 +	for (file = iso9660->data_file_list.first;
 +	    file != NULL; file = file->datanext) {
 +
 +		if (!file->write_content)
 +			continue;
 +
 +		if ((offset + (blocks << LOGICAL_BLOCK_BITS)) <
 +		     file->content.offset_of_temp) {
 +			if (blocks > 0) {
 +				r = write_file_contents(a, offset,
 +				    blocks << LOGICAL_BLOCK_BITS);
 +				if (r < 0)
 +					return (r);
 +			}
 +			blocks = 0;
 +			offset = file->content.offset_of_temp;
 +		}
 +
 +		file->cur_content = &(file->content);
 +		do {
 +			blocks += file->cur_content->blocks;
 +			/* Next fragument */
 +			file->cur_content = file->cur_content->next;
 +		} while (file->cur_content != NULL);
 +	}
 +
 +	/* Flush out remaining blocks. */
 +	if (blocks > 0) {
 +		r = write_file_contents(a, offset,
 +		    blocks << LOGICAL_BLOCK_BITS);
 +		if (r < 0)
 +			return (r);
 +	}
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static void
 +isofile_init_entry_list(struct iso9660 *iso9660)
 +{
 +	iso9660->all_file_list.first = NULL;
 +	iso9660->all_file_list.last = &(iso9660->all_file_list.first);
 +}
 +
 +static void
 +isofile_add_entry(struct iso9660 *iso9660, struct isofile *file)
 +{
 +	file->allnext = NULL;
 +	*iso9660->all_file_list.last = file;
 +	iso9660->all_file_list.last = &(file->allnext);
 +}
 +
 +static void
 +isofile_free_all_entries(struct iso9660 *iso9660)
 +{
 +	struct isofile *file, *file_next;
 +
 +	file = iso9660->all_file_list.first;
 +	while (file != NULL) {
 +		file_next = file->allnext;
 +		isofile_free(file);
 +		file = file_next;
 +	}
 +}
 +
 +static void
 +isofile_init_entry_data_file_list(struct iso9660 *iso9660)
 +{
 +	iso9660->data_file_list.first = NULL;
 +	iso9660->data_file_list.last = &(iso9660->data_file_list.first);
 +}
 +
 +static void
 +isofile_add_data_file(struct iso9660 *iso9660, struct isofile *file)
 +{
 +	file->datanext = NULL;
 +	*iso9660->data_file_list.last = file;
 +	iso9660->data_file_list.last = &(file->datanext);
 +}
 +
 +
 +static struct isofile *
 +isofile_new(struct archive_write *a, struct archive_entry *entry)
 +{
 +	struct isofile *file;
 +
 +	file = calloc(1, sizeof(*file));
 +	if (file == NULL)
 +		return (NULL);
 +
 +	if (entry != NULL)
 +		file->entry = archive_entry_clone(entry);
 +	else
 +		file->entry = archive_entry_new2(&a->archive);
 +	if (file->entry == NULL) {
 +		free(file);
 +		return (NULL);
 +	}
 +	archive_string_init(&(file->parentdir));
 +	archive_string_init(&(file->basename));
 +	archive_string_init(&(file->basename_utf16));
 +	archive_string_init(&(file->symlink));
 +	file->cur_content = &(file->content);
 +
 +	return (file);
 +}
 +
 +static void
 +isofile_free(struct isofile *file)
 +{
 +	struct content *con, *tmp;
 +
 +	con = file->content.next;
 +	while (con != NULL) {
 +		tmp = con;
 +		con = con->next;
 +		free(tmp);
 +	}
 +	archive_entry_free(file->entry);
 +	archive_string_free(&(file->parentdir));
 +	archive_string_free(&(file->basename));
 +	archive_string_free(&(file->basename_utf16));
 +	archive_string_free(&(file->symlink));
 +	free(file);
 +}
 +
 +#if defined(_WIN32) || defined(__CYGWIN__)
 +static int
 +cleanup_backslash_1(char *p)
 +{
 +	int mb, dos;
 +
 +	mb = dos = 0;
 +	while (*p) {
 +		if (*(unsigned char *)p > 127)
 +			mb = 1;
 +		if (*p == '\\') {
 +			/* If we have not met any multi-byte characters,
 +			 * we can replace '\' with '/'. */
 +			if (!mb)
 +				*p = '/';
 +			dos = 1;
 +		}
 +		p++;
 +	}
 +	if (!mb || !dos)
 +		return (0);
 +	return (-1);
 +}
 +
 +static void
 +cleanup_backslash_2(wchar_t *p)
 +{
 +
 +	/* Convert a path-separator from '\' to  '/' */
 +	while (*p != L'\0') {
 +		if (*p == L'\\')
 +			*p = L'/';
 +		p++;
 +	}
 +}
 +#endif
 +
 +/*
 + * Generate a parent directory name and a base name from a pathname.
 + */
 +static int
 +isofile_gen_utility_names(struct archive_write *a, struct isofile *file)
 +{
 +	struct iso9660 *iso9660;
 +	const char *pathname;
 +	char *p, *dirname, *slash;
 +	size_t len;
 +	int ret = ARCHIVE_OK;
 +
 +	iso9660 = a->format_data;
 +
 +	archive_string_empty(&(file->parentdir));
 +	archive_string_empty(&(file->basename));
 +	archive_string_empty(&(file->basename_utf16));
 +	archive_string_empty(&(file->symlink));
 +
 +	pathname =  archive_entry_pathname(file->entry);
 +	if (pathname == NULL || pathname[0] == '\0') {/* virtual root */
 +		file->dircnt = 0;
 +		return (ret);
 +	}
 +
 +	/*
 +	 * Make a UTF-16BE basename if Joliet extension enabled.
 +	 */
 +	if (iso9660->opt.joliet) {
 +		const char *u16, *ulast;
 +		size_t u16len, ulen_last;
 +
 +		if (iso9660->sconv_to_utf16be == NULL) {
 +			iso9660->sconv_to_utf16be =
 +			    archive_string_conversion_to_charset(
 +				&(a->archive), "UTF-16BE", 1);
 +			if (iso9660->sconv_to_utf16be == NULL)
 +				/* Couldn't allocate memory */
 +				return (ARCHIVE_FATAL);
 +			iso9660->sconv_from_utf16be =
 +			    archive_string_conversion_from_charset(
 +				&(a->archive), "UTF-16BE", 1);
 +			if (iso9660->sconv_from_utf16be == NULL)
 +				/* Couldn't allocate memory */
 +				return (ARCHIVE_FATAL);
 +		}
 +
 +		/*
 +		 * Converte a filename to UTF-16BE.
 +		 */
 +		if (0 > archive_entry_pathname_l(file->entry, &u16, &u16len,
 +		    iso9660->sconv_to_utf16be)) {
 +			if (errno == ENOMEM) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "Can't allocate memory for UTF-16BE");
 +				return (ARCHIVE_FATAL);
 +			}
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "A filename cannot be converted to UTF-16BE;"
 +			    "You should disable making Joliet extension");
 +			ret = ARCHIVE_WARN;
 +		}
 +
 +		/*
 +		 * Make sure a path separator is not in the last;
 +		 * Remove trailing '/'.
 +		 */
 +		while (u16len >= 2) {
 +#if defined(_WIN32) || defined(__CYGWIN__)
 +			if (u16[u16len-2] == 0 &&
 +			    (u16[u16len-1] == '/' || u16[u16len-1] == '\\'))
 +#else
 +			if (u16[u16len-2] == 0 && u16[u16len-1] == '/')
 +#endif
 +			{
 +				u16len -= 2;
 +			} else
 +				break;
 +		}
 +
 +		/*
 +		 * Find a basename in UTF-16BE.
 +		 */
 +		ulast = u16;
 +		u16len >>= 1;
 +		ulen_last = u16len;
 +		while (u16len > 0) {
 +#if defined(_WIN32) || defined(__CYGWIN__)
 +			if (u16[0] == 0 && (u16[1] == '/' || u16[1] == '\\'))
 +#else
 +			if (u16[0] == 0 && u16[1] == '/')
 +#endif
 +			{
 +				ulast = u16 + 2;
 +				ulen_last = u16len -1;
 +			}
 +			u16 += 2;
 +			u16len --;
 +		}
 +		ulen_last <<= 1;
 +		if (archive_string_ensure(&(file->basename_utf16),
 +		    ulen_last) == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory for UTF-16BE");
 +			return (ARCHIVE_FATAL);
 +		}
 +
 +		/*
 +		 * Set UTF-16BE basename.
 +		 */
 +		memcpy(file->basename_utf16.s, ulast, ulen_last);
 +		file->basename_utf16.length = ulen_last;
 +	}
 +
 +	archive_strcpy(&(file->parentdir), pathname);
 +#if defined(_WIN32) || defined(__CYGWIN__)
 +	/*
 +	 * Convert a path-separator from '\' to  '/'
 +	 */
 +	if (cleanup_backslash_1(file->parentdir.s) != 0) {
 +		const wchar_t *wp = archive_entry_pathname_w(file->entry);
 +		struct archive_wstring ws;
 +
 +		if (wp != NULL) {
 +			int r;
 +			archive_string_init(&ws);
 +			archive_wstrcpy(&ws, wp);
 +			cleanup_backslash_2(ws.s);
 +			archive_string_empty(&(file->parentdir));
 +			r = archive_string_append_from_wcs(&(file->parentdir),
 +			    ws.s, ws.length);
 +			archive_wstring_free(&ws);
 +			if (r < 0 && errno == ENOMEM) {
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "Can't allocate memory");
 +				return (ARCHIVE_FATAL);
 +			}
 +		}
 +	}
 +#endif
 +
 +	len = file->parentdir.length;
 +	p = dirname = file->parentdir.s;
 +
 +	/*
 +	 * Remove leading '/', '../' and './' elements
 +	 */
 +	while (*p) {
 +		if (p[0] == '/') {
 +			p++;
 +			len--;
 +		} else if (p[0] != '.')
 +			break;
 +		else if (p[1] == '.' && p[2] == '/') {
 +			p += 3;
 +			len -= 3;
 +		} else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) {
 +			p += 2;
 +			len -= 2;
 +		} else if (p[1] == '\0') {
 +			p++;
 +			len--;
 +		} else
 +			break;
 +	}
 +	if (p != dirname) {
 +		memmove(dirname, p, len+1);
 +		p = dirname;
 +	}
 +	/*
 +	 * Remove "/","/." and "/.." elements from tail.
 +	 */
 +	while (len > 0) {
 +		size_t ll = len;
 +
 +		if (len > 0 && p[len-1] == '/') {
 +			p[len-1] = '\0';
 +			len--;
 +		}
 +		if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
 +			p[len-2] = '\0';
 +			len -= 2;
 +		}
 +		if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
 +		    p[len-1] == '.') {
 +			p[len-3] = '\0';
 +			len -= 3;
 +		}
 +		if (ll == len)
 +			break;
 +	}
 +	while (*p) {
 +		if (p[0] == '/') {
 +			if (p[1] == '/')
 +				/* Convert '//' --> '/' */
 +				strcpy(p, p+1);
 +			else if (p[1] == '.' && p[2] == '/')
 +				/* Convert '/./' --> '/' */
 +				strcpy(p, p+2);
 +			else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
 +				/* Convert 'dir/dir1/../dir2/'
 +				 *     --> 'dir/dir2/'
 +				 */
 +				char *rp = p -1;
 +				while (rp >= dirname) {
 +					if (*rp == '/')
 +						break;
 +					--rp;
 +				}
 +				if (rp > dirname) {
 +					strcpy(rp, p+3);
 +					p = rp;
 +				} else {
 +					strcpy(dirname, p+4);
 +					p = dirname;
 +				}
 +			} else
 +				p++;
 +		} else
 +			p++;
 +	}
 +	p = dirname;
 +	len = strlen(p);
 +
 +	if (archive_entry_filetype(file->entry) == AE_IFLNK) {
 +		/* Convert symlink name too. */
 +		pathname = archive_entry_symlink(file->entry);
 +		archive_strcpy(&(file->symlink),  pathname);
 +#if defined(_WIN32) || defined(__CYGWIN__)
 +		/*
 +		 * Convert a path-separator from '\' to  '/'
 +		 */
 +		if (archive_strlen(&(file->symlink)) > 0 &&
 +		    cleanup_backslash_1(file->symlink.s) != 0) {
 +			const wchar_t *wp =
 +			    archive_entry_symlink_w(file->entry);
 +			struct archive_wstring ws;
 +
 +			if (wp != NULL) {
 +				int r;
 +				archive_string_init(&ws);
 +				archive_wstrcpy(&ws, wp);
 +				cleanup_backslash_2(ws.s);
 +				archive_string_empty(&(file->symlink));
 +				r = archive_string_append_from_wcs(
 +				    &(file->symlink),
 +				    ws.s, ws.length);
 +				archive_wstring_free(&ws);
 +				if (r < 0 && errno == ENOMEM) {
 +					archive_set_error(&a->archive, ENOMEM,
 +					    "Can't allocate memory");
 +					return (ARCHIVE_FATAL);
 +				}
 +			}
 +		}
 +#endif
 +	}
 +	/*
 +	 * - Count up directory elements.
 +	 * - Find out the position which points the last position of
 +	 *   path separator('/').
 +	 */
 +	slash = NULL;
 +	file->dircnt = 0;
 +	for (; *p != '\0'; p++)
 +		if (*p == '/') {
 +			slash = p;
 +			file->dircnt++;
 +		}
 +	if (slash == NULL) {
 +		/* The pathname doesn't have a parent directory. */
 +		file->parentdir.length = len;
 +		archive_string_copy(&(file->basename), &(file->parentdir));
 +		archive_string_empty(&(file->parentdir));
 +		*file->parentdir.s = '\0';
 +		return (ret);
 +	}
 +
 +	/* Make a basename from dirname and slash */
 +	*slash  = '\0';
 +	file->parentdir.length = slash - dirname;
 +	archive_strcpy(&(file->basename),  slash + 1);
 +	if (archive_entry_filetype(file->entry) == AE_IFDIR)
 +		file->dircnt ++;
 +	return (ret);
 +}
 +
 +/*
 + * Register a entry to get a hardlink target.
 + */
 +static int
 +isofile_register_hardlink(struct archive_write *a, struct isofile *file)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct hardlink *hl;
 +	const char *pathname;
 +
 +	archive_entry_set_nlink(file->entry, 1);
 +	pathname = archive_entry_hardlink(file->entry);
 +	if (pathname == NULL) {
 +		/* This `file` is a hardlink target. */
 +		hl = malloc(sizeof(*hl));
 +		if (hl == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory");
 +			return (ARCHIVE_FATAL);
 +		}
 +		hl->nlink = 1;
 +		/* A hardlink target must be the first position. */
 +		file->hlnext = NULL;
 +		hl->file_list.first = file;
 +		hl->file_list.last = &(file->hlnext);
 +		__archive_rb_tree_insert_node(&(iso9660->hardlink_rbtree),
 +		    (struct archive_rb_node *)hl);
 +	} else {
 +		hl = (struct hardlink *)__archive_rb_tree_find_node(
 +		    &(iso9660->hardlink_rbtree), pathname);
 +		if (hl != NULL) {
 +			/* Insert `file` entry into the tail. */
 +			file->hlnext = NULL;
 +			*hl->file_list.last = file;
 +			hl->file_list.last = &(file->hlnext);
 +			hl->nlink++;
 +		}
 +		archive_entry_unset_size(file->entry);
 +	}
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Hardlinked files have to have the same location of extent.
 + * We have to find out hardlink target entries for the entries
 + * which have a hardlink target name.
 + */
 +static void
 +isofile_connect_hardlink_files(struct iso9660 *iso9660)
 +{
 +	struct archive_rb_node *n;
 +	struct hardlink *hl;
 +	struct isofile *target, *nf;
 +
 +	ARCHIVE_RB_TREE_FOREACH(n, &(iso9660->hardlink_rbtree)) {
 +		hl = (struct hardlink *)n;
 +
 +		/* The first entry must be a hardlink target. */
 +		target = hl->file_list.first;
 +		archive_entry_set_nlink(target->entry, hl->nlink);
 +		/* Set a hardlink target to reference entries. */
 +		for (nf = target->hlnext;
 +		    nf != NULL; nf = nf->hlnext) {
 +			nf->hardlink_target = target;
 +			archive_entry_set_nlink(nf->entry, hl->nlink);
 +		}
 +	}
 +}
 +
 +static int
 +isofile_hd_cmp_node(const struct archive_rb_node *n1,
 +    const struct archive_rb_node *n2)
 +{
 +	const struct hardlink *h1 = (const struct hardlink *)n1;
 +	const struct hardlink *h2 = (const struct hardlink *)n2;
 +
 +	return (strcmp(archive_entry_pathname(h1->file_list.first->entry),
 +		       archive_entry_pathname(h2->file_list.first->entry)));
 +}
 +
 +static int
 +isofile_hd_cmp_key(const struct archive_rb_node *n, const void *key)
 +{
 +	const struct hardlink *h = (const struct hardlink *)n;
 +
 +	return (strcmp(archive_entry_pathname(h->file_list.first->entry),
 +		       (const char *)key));
 +}
 +
 +static void
 +isofile_init_hardlinks(struct iso9660 *iso9660)
 +{
 +	static const struct archive_rb_tree_ops rb_ops = {
 +		isofile_hd_cmp_node, isofile_hd_cmp_key,
 +	};
 +
 +	__archive_rb_tree_init(&(iso9660->hardlink_rbtree), &rb_ops);
 +}
 +
 +static void
 +isofile_free_hardlinks(struct iso9660 *iso9660)
 +{
 +	struct archive_rb_node *n, *next;
 +
 +	for (n = ARCHIVE_RB_TREE_MIN(&(iso9660->hardlink_rbtree)); n;) {
 +		next = __archive_rb_tree_iterate(&(iso9660->hardlink_rbtree),
 +		    n, ARCHIVE_RB_DIR_RIGHT);
 +		free(n);
 +		n = next;
 +	}
 +}
 +
 +static struct isoent *
 +isoent_new(struct isofile *file)
 +{
 +	struct isoent *isoent;
 +	static const struct archive_rb_tree_ops rb_ops = {
 +		isoent_cmp_node, isoent_cmp_key,
 +	};
 +
 +	isoent = calloc(1, sizeof(*isoent));
 +	if (isoent == NULL)
 +		return (NULL);
 +	isoent->file = file;
 +	isoent->children.first = NULL;
 +	isoent->children.last = &(isoent->children.first);
 +	__archive_rb_tree_init(&(isoent->rbtree), &rb_ops);
 +	isoent->subdirs.first = NULL;
 +	isoent->subdirs.last = &(isoent->subdirs.first);
 +	isoent->extr_rec_list.first = NULL;
 +	isoent->extr_rec_list.last = &(isoent->extr_rec_list.first);
 +	isoent->extr_rec_list.current = NULL;
 +	if (archive_entry_filetype(file->entry) == AE_IFDIR)
 +		isoent->dir = 1;
 +
 +	return (isoent);
 +}
 +
 +static inline struct isoent *
 +isoent_clone(struct isoent *src)
 +{
 +	return (isoent_new(src->file));
 +}
 +
 +static void
 +_isoent_free(struct isoent *isoent)
 +{
 +	struct extr_rec *er, *er_next;
 +
 +	free(isoent->children_sorted);
 +	free(isoent->identifier);
 +	er = isoent->extr_rec_list.first;
 +	while (er != NULL) {
 +		er_next = er->next;
 +		free(er);
 +		er = er_next;
 +	}
 +	free(isoent);
 +}
 +
 +static void
 +isoent_free_all(struct isoent *isoent)
 +{
 +	struct isoent *np, *np_temp;
 +
 +	if (isoent == NULL)
 +		return;
 +	np = isoent;
 +	for (;;) {
 +		if (np->dir) {
 +			if (np->children.first != NULL) {
 +				/* Enter to sub directories. */
 +				np = np->children.first;
 +				continue;
 +			}
 +		}
 +		for (;;) {
 +			np_temp = np;
 +			if (np->chnext == NULL) {
 +				/* Return to the parent directory. */
 +				np = np->parent;
 +				_isoent_free(np_temp);
 +				if (np == np_temp)
 +					return;
 +			} else {
 +				np = np->chnext;
 +				_isoent_free(np_temp);
 +				break;
 +			}
 +		}
 +	}
 +}
 +
 +static struct isoent *
 +isoent_create_virtual_dir(struct archive_write *a, struct iso9660 *iso9660, const char *pathname)
 +{
 +	struct isofile *file;
 +	struct isoent *isoent;
 +
 +	file = isofile_new(a, NULL);
 +	if (file == NULL)
 +		return (NULL);
 +	archive_entry_set_pathname(file->entry, pathname);
 +	archive_entry_unset_mtime(file->entry);
 +	archive_entry_unset_atime(file->entry);
 +	archive_entry_unset_ctime(file->entry);
 +	archive_entry_set_uid(file->entry, getuid());
 +	archive_entry_set_gid(file->entry, getgid());
 +	archive_entry_set_mode(file->entry, 0555 | AE_IFDIR);
 +	archive_entry_set_nlink(file->entry, 2);
 +	if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) {
 +		isofile_free(file);
 +		return (NULL);
 +	}
 +	isofile_add_entry(iso9660, file);
 +
 +	isoent = isoent_new(file);
 +	if (isoent == NULL)
 +		return (NULL);
 +	isoent->dir = 1;
 +	isoent->virtual = 1;
 +
 +	return (isoent);
 +}
 +
 +static int
 +isoent_cmp_node(const struct archive_rb_node *n1,
 +    const struct archive_rb_node *n2)
 +{
 +	const struct isoent *e1 = (const struct isoent *)n1;
 +	const struct isoent *e2 = (const struct isoent *)n2;
 +
 +	return (strcmp(e1->file->basename.s, e2->file->basename.s));
 +}
 +
 +static int
 +isoent_cmp_key(const struct archive_rb_node *n, const void *key)
 +{
 +	const struct isoent *e = (const struct isoent *)n;
 +
 +	return (strcmp(e->file->basename.s, (const char *)key));
 +}
 +
 +static int
 +isoent_add_child_head(struct isoent *parent, struct isoent *child)
 +{
 +
 +	if (!__archive_rb_tree_insert_node(
 +	    &(parent->rbtree), (struct archive_rb_node *)child))
 +		return (0);
 +	if ((child->chnext = parent->children.first) == NULL)
 +		parent->children.last = &(child->chnext);
 +	parent->children.first = child;
 +	parent->children.cnt++;
 +	child->parent = parent;
 +
 +	/* Add a child to a sub-directory chain */
 +	if (child->dir) {
 +		if ((child->drnext = parent->subdirs.first) == NULL)
 +			parent->subdirs.last = &(child->drnext);
 +		parent->subdirs.first = child;
 +		parent->subdirs.cnt++;
 +		child->parent = parent;
 +	} else
 +		child->drnext = NULL;
 +	return (1);
 +}
 +
 +static int
 +isoent_add_child_tail(struct isoent *parent, struct isoent *child)
 +{
 +
 +	if (!__archive_rb_tree_insert_node(
 +	    &(parent->rbtree), (struct archive_rb_node *)child))
 +		return (0);
 +	child->chnext = NULL;
 +	*parent->children.last = child;
 +	parent->children.last = &(child->chnext);
 +	parent->children.cnt++;
 +	child->parent = parent;
 +
 +	/* Add a child to a sub-directory chain */
 +	child->drnext = NULL;
 +	if (child->dir) {
 +		*parent->subdirs.last = child;
 +		parent->subdirs.last = &(child->drnext);
 +		parent->subdirs.cnt++;
 +		child->parent = parent;
 +	}
 +	return (1);
 +}
 +
 +static void
 +isoent_remove_child(struct isoent *parent, struct isoent *child)
 +{
 +	struct isoent *ent;
 +
 +	/* Remove a child entry from children chain. */
 +	ent = parent->children.first;
 +	while (ent->chnext != child)
 +		ent = ent->chnext;
 +	if ((ent->chnext = ent->chnext->chnext) == NULL)
 +		parent->children.last = &(ent->chnext);
 +	parent->children.cnt--;
 +
 +	if (child->dir) {
 +		/* Remove a child entry from sub-directory chain. */
 +		ent = parent->subdirs.first;
 +		while (ent->drnext != child)
 +			ent = ent->drnext;
 +		if ((ent->drnext = ent->drnext->drnext) == NULL)
 +			parent->subdirs.last = &(ent->drnext);
 +		parent->subdirs.cnt--;
 +	}
 +
 +	__archive_rb_tree_remove_node(&(parent->rbtree),
 +	    (struct archive_rb_node *)child);
 +}
 +
 +static int
 +isoent_clone_tree(struct archive_write *a, struct isoent **nroot,
 +    struct isoent *root)
 +{
 +	struct isoent *np, *xroot, *newent;
 +
 +	np = root;
 +	xroot = NULL;
 +	do {
 +		newent = isoent_clone(np);
 +		if (newent == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory");
 +			return (ARCHIVE_FATAL);
 +		}
 +		if (xroot == NULL) {
 +			*nroot = xroot = newent;
 +			newent->parent = xroot;
 +		} else
 +			isoent_add_child_tail(xroot, newent);
 +		if (np->dir && np->children.first != NULL) {
 +			/* Enter to sub directories. */
 +			np = np->children.first;
 +			xroot = newent;
 +			continue;
 +		}
 +		while (np != np->parent) {
 +			if (np->chnext == NULL) {
 +				/* Return to the parent directory. */
 +				np = np->parent;
 +				xroot = xroot->parent;
 +			} else {
 +				np = np->chnext;
 +				break;
 +			}
 +		}
 +	} while (np != np->parent);
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Setup directory locations.
 + */
 +static void
 +isoent_setup_directory_location(struct iso9660 *iso9660, int location,
 +    struct vdd *vdd)
 +{
 +	struct isoent *np;
 +	int depth;
 +
 +	vdd->total_dir_block = 0;
 +	depth = 0;
 +	np = vdd->rootent;
 +	do {
 +		int block;
 +
 +		np->dir_block = calculate_directory_descriptors(
 +		    iso9660, vdd, np, depth);
 +		vdd->total_dir_block += np->dir_block;
 +		np->dir_location = location;
 +		location += np->dir_block;
 +		block = extra_setup_location(np, location);
 +		vdd->total_dir_block += block;
 +		location += block;
 +
 +		if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
 +			/* Enter to sub directories. */
 +			np = np->subdirs.first;
 +			depth++;
 +			continue;
 +		}
 +		while (np != np->parent) {
 +			if (np->drnext == NULL) {
 +				/* Return to the parent directory. */
 +				np = np->parent;
 +				depth--;
 +			} else {
 +				np = np->drnext;
 +				break;
 +			}
 +		}
 +	} while (np != np->parent);
 +}
 +
 +static void
 +_isoent_file_location(struct iso9660 *iso9660, struct isoent *isoent,
 +    int *symlocation)
 +{
 +	struct isoent **children;
 +	int n;
 +
 +	if (isoent->children.cnt == 0)
 +		return;
 +
 +	children = isoent->children_sorted;
 +	for (n = 0; n < isoent->children.cnt; n++) {
 +		struct isoent *np;
 +		struct isofile *file;
 +
 +		np = children[n];
 +		if (np->dir)
 +			continue;
 +		if (np == iso9660->el_torito.boot)
 +			continue;
 +		file = np->file;
 +		if (file->boot || file->hardlink_target != NULL)
 +			continue;
 +		if (archive_entry_filetype(file->entry) == AE_IFLNK ||
 +		    file->content.size == 0) {
 +			/*
 +			 * Do not point a valid location.
 +			 * Make sure entry is not hardlink file.
 +			 */
 +			file->content.location = (*symlocation)--;
 +			continue;
 +		}
 +
 +		file->write_content = 1;
 +	}
 +}
 +
 +/*
 + * Setup file locations.
 + */
 +static void
 +isoent_setup_file_location(struct iso9660 *iso9660, int location)
 +{
 +	struct isoent *isoent;
 +	struct isoent *np;
 +	struct isofile *file;
 +	size_t size;
 +	int block;
 +	int depth;
 +	int joliet;
 +	int symlocation;
 +	int total_block;
 +
 +	iso9660->total_file_block = 0;
 +	if ((isoent = iso9660->el_torito.catalog) != NULL) {
 +		isoent->file->content.location = location;
 +		block = (int)((archive_entry_size(isoent->file->entry) +
 +		    LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
 +		location += block;
 +		iso9660->total_file_block += block;
 +	}
 +	if ((isoent = iso9660->el_torito.boot) != NULL) {
 +		isoent->file->content.location = location;
 +		size = fd_boot_image_size(iso9660->el_torito.media_type);
 +		if (size == 0)
 +			size = (size_t)archive_entry_size(isoent->file->entry);
 +		block = ((int)size + LOGICAL_BLOCK_SIZE -1)
 +		    >> LOGICAL_BLOCK_BITS;
 +		location += block;
 +		iso9660->total_file_block += block;
 +		isoent->file->content.blocks = block;
 +	}
 +
 +	depth = 0;
 +	symlocation = -16;
 +	if (!iso9660->opt.rr && iso9660->opt.joliet) {
 +		joliet = 1;
 +		np = iso9660->joliet.rootent;
 +	} else {
 +		joliet = 0;
 +		np = iso9660->primary.rootent;
 +	}
 +	do {
 +		_isoent_file_location(iso9660, np, &symlocation);
 +
 +		if (np->subdirs.first != NULL &&
 +		    (joliet ||
 +		    ((iso9660->opt.rr == OPT_RR_DISABLED &&
 +		      depth + 2 < iso9660->primary.max_depth) ||
 +		     (iso9660->opt.rr &&
 +		      depth + 1 < iso9660->primary.max_depth)))) {
 +			/* Enter to sub directories. */
 +			np = np->subdirs.first;
 +			depth++;
 +			continue;
 +		}
 +		while (np != np->parent) {
 +			if (np->drnext == NULL) {
 +				/* Return to the parent directory. */
 +				np = np->parent;
 +				depth--;
 +			} else {
 +				np = np->drnext;
 +				break;
 +			}
 +		}
 +	} while (np != np->parent);
 +
 +	total_block = 0;
 +	for (file = iso9660->data_file_list.first;
 +	    file != NULL; file = file->datanext) {
 +
 +		if (!file->write_content)
 +			continue;
 +
 +		file->cur_content = &(file->content);
 +		do {
 +			file->cur_content->location = location;
 +			location += file->cur_content->blocks;
 +			total_block += file->cur_content->blocks;
 +			/* Next fragument */
 +			file->cur_content = file->cur_content->next;
 +		} while (file->cur_content != NULL);
 +	}
 +	iso9660->total_file_block += total_block;
 +}
 +
 +static int
 +get_path_component(char *name, size_t n, const char *fn)
 +{
 +	char *p;
 +	size_t l;
 +
 +	p = strchr(fn, '/');
 +	if (p == NULL) {
 +		if ((l = strlen(fn)) == 0)
 +			return (0);
 +	} else
 +		l = p - fn;
 +	if (l > n -1)
 +		return (-1);
 +	memcpy(name, fn, l);
 +	name[l] = '\0';
 +
 +	return ((int)l);
 +}
 +
 +/*
 + * Add a new entry into the tree.
 + */
 +static int
 +isoent_tree(struct archive_write *a, struct isoent **isoentpp)
 +{
 +#if defined(_WIN32) && !defined(__CYGWIN__)
 +	char name[_MAX_FNAME];/* Included null terminator size. */
 +#elif defined(NAME_MAX) && NAME_MAX >= 255
 +	char name[NAME_MAX+1];
 +#else
 +	char name[256];
 +#endif
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct isoent *dent, *isoent, *np;
 +	struct isofile *f1, *f2;
 +	const char *fn, *p;
 +	int l;
 +
 +	isoent = *isoentpp;
 +	dent = iso9660->primary.rootent;
 +	if (isoent->file->parentdir.length > 0)
 +		fn = p = isoent->file->parentdir.s;
 +	else
 +		fn = p = "";
 +
 +	/*
 +	 * If the path of the parent directory of `isoent' entry is
 +	 * the same as the path of `cur_dirent', add isoent to
 +	 * `cur_dirent'.
 +	 */
 +	if (archive_strlen(&(iso9660->cur_dirstr))
 +	      == archive_strlen(&(isoent->file->parentdir)) &&
 +	    strcmp(iso9660->cur_dirstr.s, fn) == 0) {
 +		if (!isoent_add_child_tail(iso9660->cur_dirent, isoent)) {
 +			np = (struct isoent *)__archive_rb_tree_find_node(
 +			    &(iso9660->cur_dirent->rbtree),
 +			    isoent->file->basename.s);
 +			goto same_entry;
 +		}
 +		return (ARCHIVE_OK);
 +	}
 +
 +	for (;;) {
 +		l = get_path_component(name, sizeof(name), fn);
 +		if (l == 0) {
 +			np = NULL;
 +			break;
 +		}
 +		if (l < 0) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_MISC,
 +			    "A name buffer is too small");
 +			_isoent_free(isoent);
 +			return (ARCHIVE_FATAL);
 +		}
 +
 +		np = isoent_find_child(dent, name);
 +		if (np == NULL || fn[0] == '\0')
 +			break;
 +
 +		/* Find next subdirectory. */
 +		if (!np->dir) {
 +			/* NOT Directory! */
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_MISC,
 +			    "`%s' is not directory, we cannot insert `%s' ",
 +			    archive_entry_pathname(np->file->entry),
 +			    archive_entry_pathname(isoent->file->entry));
 +			_isoent_free(isoent);
 +			*isoentpp = NULL;
 +			return (ARCHIVE_FAILED);
 +		}
 +		fn += l;
 +		if (fn[0] == '/')
 +			fn++;
 +		dent = np;
 +	}
 +	if (np == NULL) {
 +		/*
 +		 * Create virtual parent directories.
 +		 */
 +		while (fn[0] != '\0') {
 +			struct isoent *vp;
 +			struct archive_string as;
 +
 +			archive_string_init(&as);
 +			archive_strncat(&as, p, fn - p + l);
 +			if (as.s[as.length-1] == '/') {
 +				as.s[as.length-1] = '\0';
 +				as.length--;
 +			}
 +			vp = isoent_create_virtual_dir(a, iso9660, as.s);
 +			if (vp == NULL) {
 +				archive_string_free(&as);
 +				archive_set_error(&a->archive, ENOMEM,
 +				    "Can't allocate memory");
 +				_isoent_free(isoent);
 +				*isoentpp = NULL;
 +				return (ARCHIVE_FATAL);
 +			}
 +			archive_string_free(&as);
 +
 +			if (vp->file->dircnt > iso9660->dircnt_max)
 +				iso9660->dircnt_max = vp->file->dircnt;
 +			isoent_add_child_tail(dent, vp);
 +			np = vp;
 +
 +			fn += l;
 +			if (fn[0] == '/')
 +				fn++;
 +			l = get_path_component(name, sizeof(name), fn);
 +			if (l < 0) {
 +				archive_string_free(&as);
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "A name buffer is too small");
 +				_isoent_free(isoent);
 +				*isoentpp = NULL;
 +				return (ARCHIVE_FATAL);
 +			}
 +			dent = np;
 +		}
 +
 +		/* Found out the parent directory where isoent can be
 +		 * inserted. */
 +		iso9660->cur_dirent = dent;
 +		archive_string_empty(&(iso9660->cur_dirstr));
 +		archive_string_ensure(&(iso9660->cur_dirstr),
 +		    archive_strlen(&(dent->file->parentdir)) +
 +		    archive_strlen(&(dent->file->basename)) + 2);
 +		if (archive_strlen(&(dent->file->parentdir)) +
 +		    archive_strlen(&(dent->file->basename)) == 0)
 +			iso9660->cur_dirstr.s[0] = 0;
 +		else {
 +			if (archive_strlen(&(dent->file->parentdir)) > 0) {
 +				archive_string_copy(&(iso9660->cur_dirstr),
 +				    &(dent->file->parentdir));
 +				archive_strappend_char(&(iso9660->cur_dirstr), '/');
 +			}
 +			archive_string_concat(&(iso9660->cur_dirstr),
 +			    &(dent->file->basename));
 +		}
 +
 +		if (!isoent_add_child_tail(dent, isoent)) {
 +			np = (struct isoent *)__archive_rb_tree_find_node(
 +			    &(dent->rbtree), isoent->file->basename.s);
 +			goto same_entry;
 +		}
 +		return (ARCHIVE_OK);
 +	}
 +
 +same_entry:
 +	/*
 +	 * We have already has the entry the filename of which is
 +	 * the same.
 +	 */
 +	f1 = np->file;
 +	f2 = isoent->file;
 +
 +	/* If the file type of entries is different,
 +	 * we cannot handle it. */
 +	if (archive_entry_filetype(f1->entry) !=
 +	    archive_entry_filetype(f2->entry)) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Found duplicate entries `%s' and its file type is "
 +		    "different",
 +		    archive_entry_pathname(f1->entry));
 +		_isoent_free(isoent);
 +		*isoentpp = NULL;
 +		return (ARCHIVE_FAILED);
 +	}
 +
 +	/* Swap file entries. */
 +	np->file = f2;
 +	isoent->file = f1;
 +	np->virtual = 0;
 +
 +	_isoent_free(isoent);
 +	*isoentpp = np;
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Find a entry from `isoent'
 + */
 +static struct isoent *
 +isoent_find_child(struct isoent *isoent, const char *child_name)
 +{
 +	struct isoent *np;
 +
 +	np = (struct isoent *)__archive_rb_tree_find_node(
 +	    &(isoent->rbtree), child_name);
 +	return (np);
 +}
 +
 +/*
 + * Find a entry full-path of which is specified by `fn' parameter,
 + * in the tree.
 + */
 +static struct isoent *
 +isoent_find_entry(struct isoent *rootent, const char *fn)
 +{
 +#if defined(_WIN32) && !defined(__CYGWIN__)
 +	char name[_MAX_FNAME];/* Included null terminator size. */
 +#elif defined(NAME_MAX) && NAME_MAX >= 255
 +	char name[NAME_MAX+1];
 +#else
 +	char name[256];
 +#endif
 +	struct isoent *isoent, *np;
 +	int l;
 +
 +	isoent = rootent;
 +	np = NULL;
 +	for (;;) {
 +		l = get_path_component(name, sizeof(name), fn);
 +		if (l == 0)
 +			break;
 +		fn += l;
 +		if (fn[0] == '/')
 +			fn++;
 +
 +		np = isoent_find_child(isoent, name);
 +		if (np == NULL)
 +			break;
 +		if (fn[0] == '\0')
 +			break;/* We found out the entry */
 +
 +		/* Try sub directory. */
 +		isoent = np;
 +		np = NULL;
 +		if (!isoent->dir)
 +			break;/* Not directory */
 +	}
 +
 +	return (np);
 +}
 +
 +/*
 + * Following idr_* functions are used for resolving duplicated filenames
 + * and unreceivable filenames to generate ISO9660/Joliet Identifiers.
 + */
 +
 +static void
 +idr_relaxed_filenames(char *map)
 +{
 +	int i;
 +
 +	for (i = 0x21; i <= 0x2F; i++)
 +		map[i] = 1;
 +	for (i = 0x3A; i <= 0x41; i++)
 +		map[i] = 1;
 +	for (i = 0x5B; i <= 0x5E; i++)
 +		map[i] = 1;
 +	map[0x60] = 1;
 +	for (i = 0x7B; i <= 0x7E; i++)
 +		map[i] = 1;
 +}
 +
 +static void
 +idr_init(struct iso9660 *iso9660, struct vdd *vdd, struct idr *idr)
 +{
 +
 +	idr->idrent_pool = NULL;
 +	idr->pool_size = 0;
 +	if (vdd->vdd_type != VDD_JOLIET) {
 +		if (iso9660->opt.iso_level <= 3) {
 +			memcpy(idr->char_map, d_characters_map,
 +			    sizeof(idr->char_map));
 +		} else {
 +			memcpy(idr->char_map, d1_characters_map,
 +			    sizeof(idr->char_map));
 +			idr_relaxed_filenames(idr->char_map);
 +		}
 +	}
 +}
 +
 +static void
 +idr_cleanup(struct idr *idr)
 +{
 +	free(idr->idrent_pool);
 +}
 +
 +static int
 +idr_ensure_poolsize(struct archive_write *a, struct idr *idr,
 +    int cnt)
 +{
 +
 +	if (idr->pool_size < cnt) {
 +		void *p;
 +		const int bk = (1 << 7) - 1;
 +		int psize;
 +
 +		psize = (cnt + bk) & ~bk;
 +		p = realloc(idr->idrent_pool, sizeof(struct idrent) * psize);
 +		if (p == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory");
 +			return (ARCHIVE_FATAL);
 +		}
 +		idr->idrent_pool = (struct idrent *)p;
 +		idr->pool_size = psize;
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +idr_start(struct archive_write *a, struct idr *idr, int cnt, int ffmax,
 +    int num_size, int null_size, const struct archive_rb_tree_ops *rbt_ops)
 +{
 +	int r;
 +
 +	(void)ffmax; /* UNUSED */
 +
 +	r = idr_ensure_poolsize(a, idr, cnt);
 +	if (r != ARCHIVE_OK)
 +		return (r);
 +	__archive_rb_tree_init(&(idr->rbtree), rbt_ops);
 +	idr->wait_list.first = NULL;
 +	idr->wait_list.last = &(idr->wait_list.first);
 +	idr->pool_idx = 0;
 +	idr->num_size = num_size;
 +	idr->null_size = null_size;
 +	return (ARCHIVE_OK);
 +}
 +
 +static void
 +idr_register(struct idr *idr, struct isoent *isoent, int weight, int noff)
 +{
 +	struct idrent *idrent, *n;
 +
 +	idrent = &(idr->idrent_pool[idr->pool_idx++]);
 +	idrent->wnext = idrent->avail = NULL;
 +	idrent->isoent = isoent;
 +	idrent->weight = weight;
 +	idrent->noff = noff;
 +	idrent->rename_num = 0;
 +
 +	if (!__archive_rb_tree_insert_node(&(idr->rbtree), &(idrent->rbnode))) {
 +		n = (struct idrent *)__archive_rb_tree_find_node(
 +		    &(idr->rbtree), idrent->isoent);
 +		if (n != NULL) {
 +			/* this `idrent' needs to rename. */
 +			idrent->avail = n;
 +			*idr->wait_list.last = idrent;
 +			idr->wait_list.last = &(idrent->wnext);
 +		}
 +	}
 +}
 +
 +static void
 +idr_extend_identifier(struct idrent *wnp, int numsize, int nullsize)
 +{
 +	unsigned char *p;
 +	int wnp_ext_off;
 +
 +	wnp_ext_off = wnp->isoent->ext_off;
 +	if (wnp->noff + numsize != wnp_ext_off) {
 +		p = (unsigned char *)wnp->isoent->identifier;
 +		/* Extend the filename; foo.c --> foo___.c */
 +		memmove(p + wnp->noff + numsize, p + wnp_ext_off,
 +		    wnp->isoent->ext_len + nullsize);
 +		wnp->isoent->ext_off = wnp_ext_off = wnp->noff + numsize;
 +		wnp->isoent->id_len = wnp_ext_off + wnp->isoent->ext_len;
 +	}
 +}
 +
 +static void
 +idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num))
 +{
 +	struct idrent *n;
 +	unsigned char *p;
 +
 +	for (n = idr->wait_list.first; n != NULL; n = n->wnext) {
 +		idr_extend_identifier(n, idr->num_size, idr->null_size);
 +		p = (unsigned char *)n->isoent->identifier + n->noff;
 +		do {
 +			fsetnum(p, n->avail->rename_num++);
 +		} while (!__archive_rb_tree_insert_node(
 +		    &(idr->rbtree), &(n->rbnode)));
 +	}
 +}
 +
 +static void
 +idr_set_num(unsigned char *p, int num)
 +{
 +	static const char xdig[] = {
 +		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
 +		'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
 +		'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
 +		'U', 'V', 'W', 'X', 'Y', 'Z'
 +	};
 +
 +	num %= sizeof(xdig) * sizeof(xdig) * sizeof(xdig);
 +	p[0] = xdig[(num / (sizeof(xdig) * sizeof(xdig)))];
 +	num %= sizeof(xdig) * sizeof(xdig);
 +	p[1] = xdig[ (num / sizeof(xdig))];
 +	num %= sizeof(xdig);
 +	p[2] = xdig[num];
 +}
 +
 +static void
 +idr_set_num_beutf16(unsigned char *p, int num)
 +{
 +	static const uint16_t xdig[] = {
 +		0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
 +		0x0036, 0x0037, 0x0038, 0x0039,
 +		0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046,
 +		0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C,
 +		0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052,
 +		0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
 +		0x0059, 0x005A
 +	};
 +#define XDIG_CNT	(sizeof(xdig)/sizeof(xdig[0]))
 +
 +	num %= XDIG_CNT * XDIG_CNT * XDIG_CNT;
 +	archive_be16enc(p, xdig[(num / (XDIG_CNT * XDIG_CNT))]);
 +	num %= XDIG_CNT * XDIG_CNT;
 +	archive_be16enc(p+2, xdig[ (num / XDIG_CNT)]);
 +	num %= XDIG_CNT;
 +	archive_be16enc(p+4, xdig[num]);
 +}
 +
 +/*
 + * Generate ISO9660 Identifier.
 + */
 +static int
 +isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent,
 +    struct idr *idr)
 +{
 +	struct iso9660 *iso9660;
 +	struct isoent *np;
 +	char *p;
 +	int l, r;
 +	const char *char_map;
 +	char allow_ldots, allow_multidot, allow_period, allow_vernum;
 +	int fnmax, ffmax, dnmax;
 +	static const struct archive_rb_tree_ops rb_ops = {
 +		isoent_cmp_node_iso9660, isoent_cmp_key_iso9660
 +	};
 +
 +	if (isoent->children.cnt == 0)
 +		return (0);
 +
 +	iso9660 = a->format_data;
 +	char_map = idr->char_map;
 +	if (iso9660->opt.iso_level <= 3) {
 +		allow_ldots = 0;
 +		allow_multidot = 0;
 +		allow_period = 1;
 +		allow_vernum = iso9660->opt.allow_vernum;
 +		if (iso9660->opt.iso_level == 1) {
 +			fnmax = 8;
 +			ffmax = 12;/* fnmax + '.' + 3 */
 +			dnmax = 8;
 +		} else {
 +			fnmax = 30;
 +			ffmax = 31;
 +			dnmax = 31;
 +		}
 +	} else {
 +		allow_ldots = allow_multidot = 1;
 +		allow_period = allow_vernum = 0;
 +		if (iso9660->opt.rr)
 +			/*
 +			 * MDR : The maximum size of Directory Record(254).
 +			 * DRL : A Directory Record Length(33).
 +			 * CE  : A size of SUSP CE System Use Entry(28).
 +			 * MDR - DRL - CE = 254 - 33 - 28 = 193.
 +			 */
 +			fnmax = ffmax = dnmax = 193;
 +		else
 +			/*
 +			 * XA  : CD-ROM XA System Use Extension
 +			 *       Information(14).
 +			 * MDR - DRL - XA = 254 - 33 -14 = 207.
 +			 */
 +			fnmax = ffmax = dnmax = 207;
 +	}
 +
 +	r = idr_start(a, idr, isoent->children.cnt, ffmax, 3, 1, &rb_ops);
 +	if (r < 0)
 +		return (r);
 +
 +	for (np = isoent->children.first; np != NULL; np = np->chnext) {
 +		char *dot, *xdot;
 +		int ext_off, noff, weight;
 +
 +		l = (int)np->file->basename.length;
 +		p = malloc(l+31+2+1);
 +		if (p == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory");
 +			return (ARCHIVE_FATAL);
 +		}
 +		memcpy(p, np->file->basename.s, l);
 +		p[l] = '\0';
 +		np->identifier = p;
 +
 +		dot = xdot = NULL;
 +		if (!allow_ldots) {
 +			/*
 +			 * If there is a '.' character at the first byte,
 +			 * it has to be replaced by '_' character.
 +			 */
 +			if (*p == '.')
 +				*p++ = '_';
 +		}
 +		for (;*p; p++) {
 +			if (*p & 0x80) {
 +				*p = '_';
 +				continue;
 +			}
 +			if (char_map[(unsigned char)*p]) {
 +				/* if iso-level is '4', a character '.' is
 +				 * allowed by char_map. */
 +				if (*p == '.') {
 +					xdot = dot;
 +					dot = p;
 +				}
 +				continue;
 +			}
 +			if (*p >= 'a' && *p <= 'z') {
 +				*p -= 'a' - 'A';
 +				continue;
 +			}
 +			if (*p == '.') {
 +				xdot = dot;
 +				dot = p;
 +				if (allow_multidot)
 +					continue;
 +			}
 +			*p = '_';
 +		}
 +		p = np->identifier;
 +		weight = -1;
 +		if (dot == NULL) {
 +			int nammax;
 +
 +			if (np->dir)
 +				nammax = dnmax;
 +			else
 +				nammax = fnmax;
 +
 +			if (l > nammax) {
 +				p[nammax] = '\0';
 +				weight = nammax;
 +				ext_off = nammax;
 +			} else
 +				ext_off = l;
 +		} else {
 +			*dot = '.';
 +			ext_off = (int)(dot - p);
 +
 +			if (iso9660->opt.iso_level == 1) {
 +				if (dot - p <= 8) {
 +					if (strlen(dot) > 4) {
 +						/* A length of a file extension
 +						 * must be less than 4 */
 +						dot[4] = '\0';
 +						weight = 0;
 +					}
 +				} else {
 +					p[8] = dot[0];
 +					p[9] = dot[1];
 +					p[10] = dot[2];
 +					p[11] = dot[3];
 +					p[12] = '\0';
 +					weight = 8;
 +					ext_off = 8;
 +				}
 +			} else if (np->dir) {
 +				if (l > dnmax) {
 +					p[dnmax] = '\0';
 +					weight = dnmax;
 +					if (ext_off > dnmax)
 +						ext_off = dnmax;
 +				}
 +			} else if (l > ffmax) {
 +				int extlen = (int)strlen(dot);
 +				int xdoff;
 +
 +				if (xdot != NULL)
 +					xdoff = (int)(xdot - p);
 +				else
 +					xdoff = 0;
 +
 +				if (extlen > 1 && xdoff < fnmax-1) {
 +					int off;
 +
 +					if (extlen > ffmax)
 +						extlen = ffmax;
 +					off = ffmax - extlen;
 +					if (off == 0) {
 +						/* A dot('.')  character
 +						 * does't place to the first
 +						 * byte of identifier. */
 +						off ++;
 +						extlen --;
 +					}
 +					memmove(p+off, dot, extlen);
 +					p[ffmax] = '\0';
 +					ext_off = off;
 +					weight = off;
 +#ifdef COMPAT_MKISOFS
 +				} else if (xdoff >= fnmax-1) {
 +					/* Simulate a bug(?) of mkisofs. */
 +					p[fnmax-1] = '\0';
 +					ext_off = fnmax-1;
 +					weight = fnmax-1;
 +#endif
 +				} else {
 +					p[fnmax] = '\0';
 +					ext_off = fnmax;
 +					weight = fnmax;
 +				}
 +			}
 +		}
 +		/* Save an offset of a file name extension to sort files. */
 +		np->ext_off = ext_off;
 +		np->ext_len = (int)strlen(&p[ext_off]);
 +		np->id_len = l = ext_off + np->ext_len;
 +
 +		/* Make an offset of the number which is used to be set
 +		 * hexadecimal number to avoid duplicate identififier. */
 +		if (iso9660->opt.iso_level == 1) {
 +			if (ext_off >= 5)
 +				noff = 5;
 +			else
 +				noff = ext_off;
 +		} else {
 +			if (l == ffmax)
 +				noff = ext_off - 3;
 +			else if (l == ffmax-1)
 +				noff = ext_off - 2;
 +			else if (l == ffmax-2)
 +				noff = ext_off - 1;
 +			else
 +				noff = ext_off;
 +		}
 +		/* Register entry to the identifier resolver. */
 +		idr_register(idr, np, weight, noff);
 +	}
 +
 +	/* Resolve duplicate identifier. */
 +	idr_resolve(idr, idr_set_num);
 +
 +	/* Add a period and a version number to identifiers. */
 +	for (np = isoent->children.first; np != NULL; np = np->chnext) {
 +		if (!np->dir && np->rr_child == NULL) {
 +			p = np->identifier + np->ext_off + np->ext_len;
 +			if (np->ext_len == 0 && allow_period) {
 +				*p++ = '.';
 +				np->ext_len = 1;
 +			}
 +			if (np->ext_len == 1 && !allow_period) {
 +				*--p = '\0';
 +				np->ext_len = 0;
 +			}
 +			np->id_len = np->ext_off + np->ext_len;
 +			if (allow_vernum) {
 +				*p++ = ';';
 +				*p++ = '1';
 +				np->id_len += 2;
 +			}
 +			*p = '\0';
 +		} else
 +			np->id_len = np->ext_off + np->ext_len;
 +		np->mb_len = np->id_len;
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Generate Joliet Identifier.
 + */
 +static int
 +isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
 +    struct idr *idr)
 +{
 +	struct iso9660 *iso9660;
 +	struct isoent *np;
 +	unsigned char *p;
 +	size_t l;
 +	int r;
- 	int ffmax, parent_len;
++	size_t ffmax, parent_len;
 +	static const struct archive_rb_tree_ops rb_ops = {
 +		isoent_cmp_node_joliet, isoent_cmp_key_joliet
 +	};
 +
 +	if (isoent->children.cnt == 0)
 +		return (0);
 +
 +	iso9660 = a->format_data;
 +	if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)
 +		ffmax = 206;
 +	else
 +		ffmax = 128;
 +
- 	r = idr_start(a, idr, isoent->children.cnt, ffmax, 6, 2, &rb_ops);
++	r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops);
 +	if (r < 0)
 +		return (r);
 +
 +	parent_len = 1;
 +	for (np = isoent; np->parent != np; np = np->parent)
 +		parent_len += np->mb_len + 1;
 +
 +	for (np = isoent->children.first; np != NULL; np = np->chnext) {
 +		unsigned char *dot;
 +		int ext_off, noff, weight;
 +		size_t lt;
 +
- 		if ((int)(l = np->file->basename_utf16.length) > ffmax)
++		if ((l = np->file->basename_utf16.length) > ffmax)
 +			l = ffmax;
 +
 +		p = malloc((l+1)*2);
 +		if (p == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory");
 +			return (ARCHIVE_FATAL);
 +		}
 +		memcpy(p, np->file->basename_utf16.s, l);
 +		p[l] = 0;
 +		p[l+1] = 0;
 +
 +		np->identifier = (char *)p;
 +		lt = l;
 +		dot = p + l;
 +		weight = 0;
 +		while (lt > 0) {
 +			if (!joliet_allowed_char(p[0], p[1]))
 +				archive_be16enc(p, 0x005F); /* '_' */
 +			else if (p[0] == 0 && p[1] == 0x2E) /* '.' */
 +				dot = p;
 +			p += 2;
 +			lt -= 2;
 +		}
 +		ext_off = (int)(dot - (unsigned char *)np->identifier);
 +		np->ext_off = ext_off;
 +		np->ext_len = (int)l - ext_off;
 +		np->id_len = (int)l;
 +
 +		/*
 +		 * Get a length of MBS of a full-pathname.
 +		 */
- 		if ((int)np->file->basename_utf16.length > ffmax) {
++		if (np->file->basename_utf16.length > ffmax) {
 +			if (archive_strncpy_l(&iso9660->mbs,
 +			    (const char *)np->identifier, l,
 +				iso9660->sconv_from_utf16be) != 0 &&
 +			    errno == ENOMEM) {
 +				archive_set_error(&a->archive, errno,
 +				    "No memory");
 +				return (ARCHIVE_FATAL);
 +			}
 +			np->mb_len = (int)iso9660->mbs.length;
 +			if (np->mb_len != (int)np->file->basename.length)
 +				weight = np->mb_len;
 +		} else
 +			np->mb_len = (int)np->file->basename.length;
 +
 +		/* If a length of full-pathname is longer than 240 bytes,
 +		 * it violates Joliet extensions regulation. */
- 		if (parent_len + np->mb_len > 240) {
++		if (parent_len > 240
++		    || np->mb_len > 240
++		    || parent_len + np->mb_len > 240) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "The regulation of Joliet extensions;"
 +			    " A length of a full-pathname of `%s' is "
 +			    "longer than 240 bytes, (p=%d, b=%d)",
 +			    archive_entry_pathname(np->file->entry),
 +			    (int)parent_len, (int)np->mb_len);
 +			return (ARCHIVE_FATAL);
 +		}
 +
 +		/* Make an offset of the number which is used to be set
 +		 * hexadecimal number to avoid duplicate identifier. */
- 		if ((int)l == ffmax)
++		if (l == ffmax)
 +			noff = ext_off - 6;
- 		else if ((int)l == ffmax-2)
++		else if (l == ffmax-2)
 +			noff = ext_off - 4;
- 		else if ((int)l == ffmax-4)
++		else if (l == ffmax-4)
 +			noff = ext_off - 2;
 +		else
 +			noff = ext_off;
 +		/* Register entry to the identifier resolver. */
 +		idr_register(idr, np, weight, noff);
 +	}
 +
 +	/* Resolve duplicate identifier with Joliet Volume. */
 +	idr_resolve(idr, idr_set_num_beutf16);
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * This comparing rule is according to ISO9660 Standard 9.3
 + */
 +static int
 +isoent_cmp_iso9660_identifier(const struct isoent *p1, const struct isoent *p2)
 +{
 +	const char *s1, *s2;
 +	int cmp;
 +	int l;
 +
 +	s1 = p1->identifier;
 +	s2 = p2->identifier;
 +
 +	/* Compare File Name */
 +	l = p1->ext_off;
 +	if (l > p2->ext_off)
 +		l = p2->ext_off;
 +	cmp = memcmp(s1, s2, l);
 +	if (cmp != 0)
 +		return (cmp);
 +	if (p1->ext_off < p2->ext_off) {
 +		s2 += l;
 +		l = p2->ext_off - p1->ext_off;
 +		while (l--)
 +			if (0x20 != *s2++)
 +				return (0x20
 +				    - *(const unsigned char *)(s2 - 1));
 +	} else if (p1->ext_off > p2->ext_off) {
 +		s1 += l;
 +		l = p1->ext_off - p2->ext_off;
 +		while (l--)
 +			if (0x20 != *s1++)
 +				return (*(const unsigned char *)(s1 - 1)
 +				    - 0x20);
 +	}
 +	/* Compare File Name Extension */
 +	if (p1->ext_len == 0 && p2->ext_len == 0)
 +		return (0);
 +	if (p1->ext_len == 1 && p2->ext_len == 1)
 +		return (0);
 +	if (p1->ext_len <= 1)
 +		return (-1);
 +	if (p2->ext_len <= 1)
 +		return (1);
 +	l = p1->ext_len;
 +	if (l > p2->ext_len)
 +		l = p2->ext_len;
 +	s1 = p1->identifier + p1->ext_off;
 +	s2 = p2->identifier + p2->ext_off;
 +	if (l > 1) {
 +		cmp = memcmp(s1, s2, l);
 +		if (cmp != 0)
 +			return (cmp);
 +	}
 +	if (p1->ext_len < p2->ext_len) {
 +		s2 += l;
 +		l = p2->ext_len - p1->ext_len;
 +		while (l--)
 +			if (0x20 != *s2++)
 +				return (0x20
 +				    - *(const unsigned char *)(s2 - 1));
 +	} else if (p1->ext_len > p2->ext_len) {
 +		s1 += l;
 +		l = p1->ext_len - p2->ext_len;
 +		while (l--)
 +			if (0x20 != *s1++)
 +				return (*(const unsigned char *)(s1 - 1)
 +				    - 0x20);
 +	}
 +	/* Compare File Version Number */
 +	/* No operation. The File Version Number is always one. */
 +
 +	return (cmp);
 +}
 +
 +static int
 +isoent_cmp_node_iso9660(const struct archive_rb_node *n1,
 +    const struct archive_rb_node *n2)
 +{
 +	const struct idrent *e1 = (const struct idrent *)n1;
 +	const struct idrent *e2 = (const struct idrent *)n2;
 +
 +	return (isoent_cmp_iso9660_identifier(e2->isoent, e1->isoent));
 +}
 +
 +static int
 +isoent_cmp_key_iso9660(const struct archive_rb_node *node, const void *key)
 +{
 +	const struct isoent *isoent = (const struct isoent *)key;
 +	const struct idrent *idrent = (const struct idrent *)node;
 +
 +	return (isoent_cmp_iso9660_identifier(isoent, idrent->isoent));
 +}
 +
 +static int
 +isoent_cmp_joliet_identifier(const struct isoent *p1, const struct isoent *p2)
 +{
 +	const unsigned char *s1, *s2;
 +	int cmp;
 +	int l;
 +
 +	s1 = (const unsigned char *)p1->identifier;
 +	s2 = (const unsigned char *)p2->identifier;
 +
 +	/* Compare File Name */
 +	l = p1->ext_off;
 +	if (l > p2->ext_off)
 +		l = p2->ext_off;
 +	cmp = memcmp(s1, s2, l);
 +	if (cmp != 0)
 +		return (cmp);
 +	if (p1->ext_off < p2->ext_off) {
 +		s2 += l;
 +		l = p2->ext_off - p1->ext_off;
 +		while (l--)
 +			if (0 != *s2++)
 +				return (- *(const unsigned char *)(s2 - 1));
 +	} else if (p1->ext_off > p2->ext_off) {
 +		s1 += l;
 +		l = p1->ext_off - p2->ext_off;
 +		while (l--)
 +			if (0 != *s1++)
 +				return (*(const unsigned char *)(s1 - 1));
 +	}
 +	/* Compare File Name Extension */
 +	if (p1->ext_len == 0 && p2->ext_len == 0)
 +		return (0);
 +	if (p1->ext_len == 2 && p2->ext_len == 2)
 +		return (0);
 +	if (p1->ext_len <= 2)
 +		return (-1);
 +	if (p2->ext_len <= 2)
 +		return (1);
 +	l = p1->ext_len;
 +	if (l > p2->ext_len)
 +		l = p2->ext_len;
 +	s1 = (unsigned char *)(p1->identifier + p1->ext_off);
 +	s2 = (unsigned char *)(p2->identifier + p2->ext_off);
 +	if (l > 1) {
 +		cmp = memcmp(s1, s2, l);
 +		if (cmp != 0)
 +			return (cmp);
 +	}
 +	if (p1->ext_len < p2->ext_len) {
 +		s2 += l;
 +		l = p2->ext_len - p1->ext_len;
 +		while (l--)
 +			if (0 != *s2++)
 +				return (- *(const unsigned char *)(s2 - 1));
 +	} else if (p1->ext_len > p2->ext_len) {
 +		s1 += l;
 +		l = p1->ext_len - p2->ext_len;
 +		while (l--)
 +			if (0 != *s1++)
 +				return (*(const unsigned char *)(s1 - 1));
 +	}
 +	/* Compare File Version Number */
 +	/* No operation. The File Version Number is always one. */
 +
 +	return (cmp);
 +}
 +
 +static int
 +isoent_cmp_node_joliet(const struct archive_rb_node *n1,
 +    const struct archive_rb_node *n2)
 +{
 +	const struct idrent *e1 = (const struct idrent *)n1;
 +	const struct idrent *e2 = (const struct idrent *)n2;
 +
 +	return (isoent_cmp_joliet_identifier(e2->isoent, e1->isoent));
 +}
 +
 +static int
 +isoent_cmp_key_joliet(const struct archive_rb_node *node, const void *key)
 +{
 +	const struct isoent *isoent = (const struct isoent *)key;
 +	const struct idrent *idrent = (const struct idrent *)node;
 +
 +	return (isoent_cmp_joliet_identifier(isoent, idrent->isoent));
 +}
 +
 +static int
 +isoent_make_sorted_files(struct archive_write *a, struct isoent *isoent,
 +    struct idr *idr)
 +{
 +	struct archive_rb_node *rn;
 +	struct isoent **children;
 +
 +	children = malloc(isoent->children.cnt * sizeof(struct isoent *));
 +	if (children == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate memory");
 +		return (ARCHIVE_FATAL);
 +	}
 +	isoent->children_sorted = children;
 +
 +	ARCHIVE_RB_TREE_FOREACH(rn, &(idr->rbtree)) {
 +		struct idrent *idrent = (struct idrent *)rn;
 +		*children ++ = idrent->isoent;
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * - Generate ISO9660 and Joliet identifiers from basenames.
 + * - Sort files by each directory.
 + */
 +static int
 +isoent_traverse_tree(struct archive_write *a, struct vdd* vdd)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct isoent *np;
 +	struct idr idr;
 +	int depth;
 +	int r;
 +	int (*genid)(struct archive_write *, struct isoent *, struct idr *);
 +
 +	idr_init(iso9660, vdd, &idr);
 +	np = vdd->rootent;
 +	depth = 0;
 +	if (vdd->vdd_type == VDD_JOLIET)
 +		genid = isoent_gen_joliet_identifier;
 +	else
 +		genid = isoent_gen_iso9660_identifier;
 +	do {
 +		if (np->virtual &&
 +		    !archive_entry_mtime_is_set(np->file->entry)) {
 +			/* Set properly times to virtual directory */
 +			archive_entry_set_mtime(np->file->entry,
 +			    iso9660->birth_time, 0);
 +			archive_entry_set_atime(np->file->entry,
 +			    iso9660->birth_time, 0);
 +			archive_entry_set_ctime(np->file->entry,
 +			    iso9660->birth_time, 0);
 +		}
 +		if (np->children.first != NULL) {
 +			if (vdd->vdd_type != VDD_JOLIET &&
 +			    !iso9660->opt.rr && depth + 1 >= vdd->max_depth) {
 +				if (np->children.cnt > 0)
 +					iso9660->directories_too_deep = np;
 +			} else {
 +				/* Generate Identifier */
 +				r = genid(a, np, &idr);
 +				if (r < 0)
 +					goto exit_traverse_tree;
 +				r = isoent_make_sorted_files(a, np, &idr);
 +				if (r < 0)
 +					goto exit_traverse_tree;
 +
 +				if (np->subdirs.first != NULL &&
 +				    depth + 1 < vdd->max_depth) {
 +					/* Enter to sub directories. */
 +					np = np->subdirs.first;
 +					depth++;
 +					continue;
 +				}
 +			}
 +		}
 +		while (np != np->parent) {
 +			if (np->drnext == NULL) {
 +				/* Return to the parent directory. */
 +				np = np->parent;
 +				depth--;
 +			} else {
 +				np = np->drnext;
 +				break;
 +			}
 +		}
 +	} while (np != np->parent);
 +
 +	r = ARCHIVE_OK;
 +exit_traverse_tree:
 +	idr_cleanup(&idr);
 +
 +	return (r);
 +}
 +
 +/*
 + * Collect directory entries into path_table by a directory depth.
 + */
 +static int
 +isoent_collect_dirs(struct vdd *vdd, struct isoent *rootent, int depth)
 +{
 +	struct isoent *np;
 +
 +	if (rootent == NULL)
 +		rootent = vdd->rootent;
 +	np = rootent;
 +	do {
 +		/* Register current directory to pathtable. */
 +		path_table_add_entry(&(vdd->pathtbl[depth]), np);
 +
 +		if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
 +			/* Enter to sub directories. */
 +			np = np->subdirs.first;
 +			depth++;
 +			continue;
 +		}
 +		while (np != rootent) {
 +			if (np->drnext == NULL) {
 +				/* Return to the parent directory. */
 +				np = np->parent;
 +				depth--;
 +			} else {
 +				np = np->drnext;
 +				break;
 +			}
 +		}
 +	} while (np != rootent);
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * The entry whose number of levels in a directory hierarchy is
 + * large than eight relocate to rr_move directory.
 + */
 +static int
 +isoent_rr_move_dir(struct archive_write *a, struct isoent **rr_moved,
 +    struct isoent *curent, struct isoent **newent)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct isoent *rrmoved, *mvent, *np;
 +
 +	if ((rrmoved = *rr_moved) == NULL) {
 +		struct isoent *rootent = iso9660->primary.rootent;
 +		/* There isn't rr_move entry.
 +		 * Create rr_move entry and insert it into the root entry.
 +		 */
 +		rrmoved = isoent_create_virtual_dir(a, iso9660, "rr_moved");
 +		if (rrmoved == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate memory");
 +			return (ARCHIVE_FATAL);
 +		}
 +		/* Add "rr_moved" entry to the root entry. */
 +		isoent_add_child_head(rootent, rrmoved);
 +		archive_entry_set_nlink(rootent->file->entry,
 +		    archive_entry_nlink(rootent->file->entry) + 1);
 +		/* Register "rr_moved" entry to second level pathtable. */
 +		path_table_add_entry(&(iso9660->primary.pathtbl[1]), rrmoved);
 +		/* Save rr_moved. */
 +		*rr_moved = rrmoved;
 +	}
 +	/*
 +	 * Make a clone of curent which is going to be relocated
 +	 * to rr_moved.
 +	 */
 +	mvent = isoent_clone(curent);
 +	if (mvent == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate memory");
 +		return (ARCHIVE_FATAL);
 +	}
 +	/* linking..  and use for creating "CL", "PL" and "RE" */
 +	mvent->rr_parent = curent->parent;
 +	curent->rr_child = mvent;
 +	/*
 +	 * Move subdirectories from the curent to mvent
 +	 */
 +	if (curent->children.first != NULL) {
 +		*mvent->children.last = curent->children.first;
 +		mvent->children.last = curent->children.last;
 +	}
 +	for (np = mvent->children.first; np != NULL; np = np->chnext)
 +		np->parent = mvent;
 +	mvent->children.cnt = curent->children.cnt;
 +	curent->children.cnt = 0;
 +	curent->children.first = NULL;
 +	curent->children.last = &curent->children.first;
 +
 +	if (curent->subdirs.first != NULL) {
 +		*mvent->subdirs.last = curent->subdirs.first;
 +		mvent->subdirs.last = curent->subdirs.last;
 +	}
 +	mvent->subdirs.cnt = curent->subdirs.cnt;
 +	curent->subdirs.cnt = 0;
 +	curent->subdirs.first = NULL;
 +	curent->subdirs.last = &curent->subdirs.first;
 +
 +	/*
 +	 * The mvent becomes a child of the rr_moved entry.
 +	 */
 +	isoent_add_child_tail(rrmoved, mvent);
 +	archive_entry_set_nlink(rrmoved->file->entry,
 +	    archive_entry_nlink(rrmoved->file->entry) + 1);
 +	/*
 +	 * This entry which relocated to the rr_moved directory
 +	 * has to set the flag as a file.
 +	 * See also RRIP 4.1.5.1 Description of the "CL" System Use Entry.
 +	 */
 +	curent->dir = 0;
 +
 +	*newent = mvent;
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +isoent_rr_move(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct path_table *pt;
 +	struct isoent *rootent, *rr_moved;
 +	struct isoent *np, *last;
 +	int r;
 +
 +	pt = &(iso9660->primary.pathtbl[MAX_DEPTH-1]);
 +	/* Theare aren't level 8 directories reaching a deepr level. */
 +	if (pt->cnt == 0)
 +		return (ARCHIVE_OK);
 +
 +	rootent = iso9660->primary.rootent;
 +	/* If "rr_moved" directory is already existing,
 +	 * we have to use it. */
 +	rr_moved = isoent_find_child(rootent, "rr_moved");
 +	if (rr_moved != NULL &&
 +	    rr_moved != rootent->children.first) {
 +		/*
 +		 * It's necessary that rr_move is the first entry
 +		 * of the root.
 +		 */
 +		/* Remove "rr_moved" entry from children chain. */
 +		isoent_remove_child(rootent, rr_moved);
 +
 +		/* Add "rr_moved" entry into the head of children chain. */
 +		isoent_add_child_head(rootent, rr_moved);
 +	}
 +
 +	/*
 +	 * Check level 8 path_table.
 +	 * If find out sub directory entries, that entries move to rr_move.
 +	 */
 +	np = pt->first;
 +	while (np != NULL) {
 +		last = path_table_last_entry(pt);
 +		for (; np != NULL; np = np->ptnext) {
 +			struct isoent *mvent;
 +			struct isoent *newent;
 +
 +			if (!np->dir)
 +				continue;
 +			for (mvent = np->subdirs.first;
 +			    mvent != NULL; mvent = mvent->drnext) {
 +				r = isoent_rr_move_dir(a, &rr_moved,
 +				    mvent, &newent);
 +				if (r < 0)
 +					return (r);
 +				isoent_collect_dirs(&(iso9660->primary),
 +				    newent, 2);
 +			}
 +		}
 +		/* If new entries are added to level 8 path_talbe,
 +		 * its sub directory entries move to rr_move too.
 +		 */
 +		np = last->ptnext;
 +	}
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * This comparing rule is according to ISO9660 Standard 6.9.1
 + */
 +static int
 +_compare_path_table(const void *v1, const void *v2)
 +{
 +	const struct isoent *p1, *p2;
 +	const char *s1, *s2;
 +	int cmp, l;
 +
 +	p1 = *((const struct isoent **)(uintptr_t)v1);
 +	p2 = *((const struct isoent **)(uintptr_t)v2);
 +
 +	/* Compare parent directory number */
 +	cmp = p1->parent->dir_number - p2->parent->dir_number;
 +	if (cmp != 0)
 +		return (cmp);
 +
 +	/* Compare indetifier */
 +	s1 = p1->identifier;
 +	s2 = p2->identifier;
 +	l = p1->ext_off;
 +	if (l > p2->ext_off)
 +		l = p2->ext_off;
 +	cmp = strncmp(s1, s2, l);
 +	if (cmp != 0)
 +		return (cmp);
 +	if (p1->ext_off < p2->ext_off) {
 +		s2 += l;
 +		l = p2->ext_off - p1->ext_off;
 +		while (l--)
 +			if (0x20 != *s2++)
 +				return (0x20
 +				    - *(const unsigned char *)(s2 - 1));
 +	} else if (p1->ext_off > p2->ext_off) {
 +		s1 += l;
 +		l = p1->ext_off - p2->ext_off;
 +		while (l--)
 +			if (0x20 != *s1++)
 +				return (*(const unsigned char *)(s1 - 1)
 +				    - 0x20);
 +	}
 +	return (0);
 +}
 +
 +static int
 +_compare_path_table_joliet(const void *v1, const void *v2)
 +{
 +	const struct isoent *p1, *p2;
 +	const unsigned char *s1, *s2;
 +	int cmp, l;
 +
 +	p1 = *((const struct isoent **)(uintptr_t)v1);
 +	p2 = *((const struct isoent **)(uintptr_t)v2);
 +
 +	/* Compare parent directory number */
 +	cmp = p1->parent->dir_number - p2->parent->dir_number;
 +	if (cmp != 0)
 +		return (cmp);
 +
 +	/* Compare indetifier */
 +	s1 = (const unsigned char *)p1->identifier;
 +	s2 = (const unsigned char *)p2->identifier;
 +	l = p1->ext_off;
 +	if (l > p2->ext_off)
 +		l = p2->ext_off;
 +	cmp = memcmp(s1, s2, l);
 +	if (cmp != 0)
 +		return (cmp);
 +	if (p1->ext_off < p2->ext_off) {
 +		s2 += l;
 +		l = p2->ext_off - p1->ext_off;
 +		while (l--)
 +			if (0 != *s2++)
 +				return (- *(const unsigned char *)(s2 - 1));
 +	} else if (p1->ext_off > p2->ext_off) {
 +		s1 += l;
 +		l = p1->ext_off - p2->ext_off;
 +		while (l--)
 +			if (0 != *s1++)
 +				return (*(const unsigned char *)(s1 - 1));
 +	}
 +	return (0);
 +}
 +
 +static inline void
 +path_table_add_entry(struct path_table *pathtbl, struct isoent *ent)
 +{
 +	ent->ptnext = NULL;
 +	*pathtbl->last = ent;
 +	pathtbl->last = &(ent->ptnext);
 +	pathtbl->cnt ++;
 +}
 +
 +static inline struct isoent *
 +path_table_last_entry(struct path_table *pathtbl)
 +{
 +	if (pathtbl->first == NULL)
 +		return (NULL);
 +	return (((struct isoent *)(void *)
 +		((char *)(pathtbl->last) - offsetof(struct isoent, ptnext))));
 +}
 +
 +/*
 + * Sort directory entries in path_table
 + * and assign directory number to each entries.
 + */
 +static int
 +isoent_make_path_table_2(struct archive_write *a, struct vdd *vdd,
 +    int depth, int *dir_number)
 +{
 +	struct isoent *np;
 +	struct isoent **enttbl;
 +	struct path_table *pt;
 +	int i;
 +
 +	pt = &vdd->pathtbl[depth];
 +	if (pt->cnt == 0) {
 +		pt->sorted = NULL;
 +		return (ARCHIVE_OK);
 +	}
 +	enttbl = malloc(pt->cnt * sizeof(struct isoent *));
 +	if (enttbl == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate memory");
 +		return (ARCHIVE_FATAL);
 +	}
 +	pt->sorted = enttbl;
 +	for (np = pt->first; np != NULL; np = np->ptnext)
 +		*enttbl ++ = np;
 +	enttbl = pt->sorted;
 +
 +	switch (vdd->vdd_type) {
 +	case VDD_PRIMARY:
 +	case VDD_ENHANCED:
 +#ifdef __COMPAR_FN_T
 +		qsort(enttbl, pt->cnt, sizeof(struct isoent *),
 +		    (__compar_fn_t)_compare_path_table);
 +#else
 +		qsort(enttbl, pt->cnt, sizeof(struct isoent *),
 +		    _compare_path_table);
 +#endif
 +		break;
 +	case VDD_JOLIET:
 +#ifdef __COMPAR_FN_T
 +		qsort(enttbl, pt->cnt, sizeof(struct isoent *),
 +		    (__compar_fn_t)_compare_path_table_joliet);
 +#else
 +		qsort(enttbl, pt->cnt, sizeof(struct isoent *),
 +		    _compare_path_table_joliet);
 +#endif
 +		break;
 +	}
 +	for (i = 0; i < pt->cnt; i++)
 +		enttbl[i]->dir_number = (*dir_number)++;
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +isoent_alloc_path_table(struct archive_write *a, struct vdd *vdd,
 +    int max_depth)
 +{
 +	int i;
 +
 +	vdd->max_depth = max_depth;
 +	vdd->pathtbl = malloc(sizeof(*vdd->pathtbl) * vdd->max_depth);
 +	if (vdd->pathtbl == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate memory");
 +		return (ARCHIVE_FATAL);
 +	}
 +	for (i = 0; i < vdd->max_depth; i++) {
 +		vdd->pathtbl[i].first = NULL;
 +		vdd->pathtbl[i].last = &(vdd->pathtbl[i].first);
 +		vdd->pathtbl[i].sorted = NULL;
 +		vdd->pathtbl[i].cnt = 0;
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * Make Path Tables
 + */
 +static int
 +isoent_make_path_table(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	int depth, r;
 +	int dir_number;
 +
 +	/*
 +	 * Init Path Table.
 +	 */
 +	if (iso9660->dircnt_max >= MAX_DEPTH &&
 +	    (!iso9660->opt.limit_depth || iso9660->opt.iso_level == 4))
 +		r = isoent_alloc_path_table(a, &(iso9660->primary),
 +		    iso9660->dircnt_max + 1);
 +	else
 +		/* The number of levels in the hierarchy cannot exceed
 +		 * eight. */
 +		r = isoent_alloc_path_table(a, &(iso9660->primary),
 +		    MAX_DEPTH);
 +	if (r < 0)
 +		return (r);
 +	if (iso9660->opt.joliet) {
 +		r = isoent_alloc_path_table(a, &(iso9660->joliet),
 +		    iso9660->dircnt_max + 1);
 +		if (r < 0)
 +			return (r);
 +	}
 +
 +	/* Step 0.
 +	 * - Collect directories for primary and joliet.
 +	 */
 +	isoent_collect_dirs(&(iso9660->primary), NULL, 0);
 +	if (iso9660->opt.joliet)
 +		isoent_collect_dirs(&(iso9660->joliet), NULL, 0);
 +	/*
 +	 * Rockridge; move deeper depth directories to rr_moved.
 +	 */
 +	if (iso9660->opt.rr) {
 +		r = isoent_rr_move(a);
 +		if (r < 0)
 +			return (r);
 +	}
 +
 + 	/* Update nlink. */
 +	isofile_connect_hardlink_files(iso9660);
 +
 +	/* Step 1.
 +	 * - Renew a value of the depth of that directories.
 +	 * - Resolve hardlinks.
 + 	 * - Convert pathnames to ISO9660 name or UCS2(joliet).
 +	 * - Sort files by each directory.
 +	 */
 +	r = isoent_traverse_tree(a, &(iso9660->primary));
 +	if (r < 0)
 +		return (r);
 +	if (iso9660->opt.joliet) {
 +		r = isoent_traverse_tree(a, &(iso9660->joliet));
 +		if (r < 0)
 +			return (r);
 +	}
 +
 +	/* Step 2.
 +	 * - Sort directories.
 +	 * - Assign all directory number.
 +	 */
 +	dir_number = 1;
 +	for (depth = 0; depth < iso9660->primary.max_depth; depth++) {
 +		r = isoent_make_path_table_2(a, &(iso9660->primary),
 +		    depth, &dir_number);
 +		if (r < 0)
 +			return (r);
 +	}
 +	if (iso9660->opt.joliet) {
 +		dir_number = 1;
 +		for (depth = 0; depth < iso9660->joliet.max_depth; depth++) {
 +			r = isoent_make_path_table_2(a, &(iso9660->joliet),
 +			    depth, &dir_number);
 +			if (r < 0)
 +				return (r);
 +		}
 +	}
 +	if (iso9660->opt.limit_dirs && dir_number > 0xffff) {
 +		/*
 +		 * Maximum number of directories is 65535(0xffff)
 +		 * doe to size(16bit) of Parent Directory Number of
 +		 * the Path Table.
 +		 * See also ISO9660 Standard 9.4.
 +		 */
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Too many directories(%d) over 65535.", dir_number);
 +		return (ARCHIVE_FATAL);
 +	}
 +
 +	/* Get the size of the Path Table. */
 +	calculate_path_table_size(&(iso9660->primary));
 +	if (iso9660->opt.joliet)
 +		calculate_path_table_size(&(iso9660->joliet));
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +isoent_find_out_boot_file(struct archive_write *a, struct isoent *rootent)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +
 +	/* Find a isoent of the boot file. */
 +	iso9660->el_torito.boot = isoent_find_entry(rootent,
 +	    iso9660->el_torito.boot_filename.s);
 +	if (iso9660->el_torito.boot == NULL) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Can't find the boot image file ``%s''",
 +		    iso9660->el_torito.boot_filename.s);
 +		return (ARCHIVE_FATAL);
 +	}
 +	iso9660->el_torito.boot->file->boot = BOOT_IMAGE;
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct isofile *file;
 +	struct isoent *isoent;
 +	struct archive_entry *entry;
 +
 +	(void)rootent; /* UNUSED */
 +	/*
 +	 * Create the entry which is the "boot.catalog" file.
 +	 */
 +	file = isofile_new(a, NULL);
 +	if (file == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate memory");
 +		return (ARCHIVE_FATAL);
 +	}
 +	archive_entry_set_pathname(file->entry,
 +	    iso9660->el_torito.catalog_filename.s);
 +	archive_entry_set_size(file->entry, LOGICAL_BLOCK_SIZE);
 +	archive_entry_set_mtime(file->entry, iso9660->birth_time, 0);
 +	archive_entry_set_atime(file->entry, iso9660->birth_time, 0);
 +	archive_entry_set_ctime(file->entry, iso9660->birth_time, 0);
 +	archive_entry_set_uid(file->entry, getuid());
 +	archive_entry_set_gid(file->entry, getgid());
 +	archive_entry_set_mode(file->entry, AE_IFREG | 0444);
 +	archive_entry_set_nlink(file->entry, 1);
 +
 +	if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) {
 +		isofile_free(file);
 +		return (ARCHIVE_FATAL);
 +	}
 +	file->boot = BOOT_CATALOG;
 +	file->content.size = LOGICAL_BLOCK_SIZE;
 +	isofile_add_entry(iso9660, file);
 +
 +	isoent = isoent_new(file);
 +	if (isoent == NULL) {
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Can't allocate memory");
 +		return (ARCHIVE_FATAL);
 +	}
 +	isoent->virtual = 1;
 +
 +	/* Add the "boot.catalog" entry into tree */
 +	if (isoent_tree(a, &isoent) != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	iso9660->el_torito.catalog = isoent;
 +	/*
 +	 * Get a boot medai type.
 +	 */
 +	switch (iso9660->opt.boot_type) {
 +	default:
 +	case OPT_BOOT_TYPE_AUTO:
 +		/* Try detecting a media type of the boot image. */
 +		entry = iso9660->el_torito.boot->file->entry;
 +		if (archive_entry_size(entry) == FD_1_2M_SIZE)
 +			iso9660->el_torito.media_type =
 +			    BOOT_MEDIA_1_2M_DISKETTE;
 +		else if (archive_entry_size(entry) == FD_1_44M_SIZE)
 +			iso9660->el_torito.media_type =
 +			    BOOT_MEDIA_1_44M_DISKETTE;
 +		else if (archive_entry_size(entry) == FD_2_88M_SIZE)
 +			iso9660->el_torito.media_type =
 +			    BOOT_MEDIA_2_88M_DISKETTE;
 +		else
 +			/* We cannot decide whether the boot image is
 +			 * hard-disk. */
 +			iso9660->el_torito.media_type =
 +			    BOOT_MEDIA_NO_EMULATION;
 +		break;
 +	case OPT_BOOT_TYPE_NO_EMU:
 +		iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION;
 +		break;
 +	case OPT_BOOT_TYPE_HARD_DISK:
 +		iso9660->el_torito.media_type = BOOT_MEDIA_HARD_DISK;
 +		break;
 +	case OPT_BOOT_TYPE_FD:
 +		entry = iso9660->el_torito.boot->file->entry;
 +		if (archive_entry_size(entry) <= FD_1_2M_SIZE)
 +			iso9660->el_torito.media_type =
 +			    BOOT_MEDIA_1_2M_DISKETTE;
 +		else if (archive_entry_size(entry) <= FD_1_44M_SIZE)
 +			iso9660->el_torito.media_type =
 +			    BOOT_MEDIA_1_44M_DISKETTE;
 +		else if (archive_entry_size(entry) <= FD_2_88M_SIZE)
 +			iso9660->el_torito.media_type =
 +			    BOOT_MEDIA_2_88M_DISKETTE;
 +		else {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Boot image file(``%s'') size is too big "
 +			    "for fd type.",
 +			    iso9660->el_torito.boot_filename.s);
 +			return (ARCHIVE_FATAL);
 +		}
 +		break;
 +	}
 +
 +	/*
 +	 * Get a system type.
 +	 * TODO: `El Torito' specification says "A copy of byte 5 from the
 +	 *       Partition Table found in the boot image".
 +	 */
 +	iso9660->el_torito.system_type = 0;
 +
 +	/*
 +	 * Get an ID.
 +	 */
 +	if (iso9660->opt.publisher)
 +		archive_string_copy(&(iso9660->el_torito.id),
 +		    &(iso9660->publisher_identifier));
 +
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +/*
 + * If a media type is floppy, return its image size.
 + * otherwise return 0.
 + */
 +static size_t
 +fd_boot_image_size(int media_type)
 +{
 +	switch (media_type) {
 +	case BOOT_MEDIA_1_2M_DISKETTE:
 +		return (FD_1_2M_SIZE);
 +	case BOOT_MEDIA_1_44M_DISKETTE:
 +		return (FD_1_44M_SIZE);
 +	case BOOT_MEDIA_2_88M_DISKETTE:
 +		return (FD_2_88M_SIZE);
 +	default:
 +		return (0);
 +	}
 +}
 +
 +/*
 + * Make a boot catalog image data.
 + */
 +static int
 +make_boot_catalog(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	unsigned char *block;
 +	unsigned char *p;
 +	uint16_t sum, *wp;
 +
 +	block = wb_buffptr(a);
 +	memset(block, 0, LOGICAL_BLOCK_SIZE);
 +	p = block;
 +	/*
 +	 * Validation Entry
 +	 */
 +	/* Header ID */
 +	p[0] = 1;
 +	/* Platform ID */
 +	p[1] = iso9660->el_torito.platform_id;
 +	/* Reserved */
 +	p[2] = p[3] = 0;
 +	/* ID */
 +	if (archive_strlen(&(iso9660->el_torito.id)) > 0)
 +		strncpy((char *)p+4, iso9660->el_torito.id.s, 23);
 +	p[27] = 0;
 +	/* Checksum */
 +	p[28] = p[29] = 0;
 +	/* Key */
 +	p[30] = 0x55;
 +	p[31] = 0xAA;
 +
 +	sum = 0;
 +	wp = (uint16_t *)block;
 +	while (wp < (uint16_t *)&block[32])
 +		sum += archive_le16dec(wp++);
 +	set_num_721(&block[28], (~sum) + 1);
 +
 +	/*
 +	 * Initial/Default Entry
 +	 */
 +	p = &block[32];
 +	/* Boot Indicator */
 +	p[0] = 0x88;
 +	/* Boot media type */
 +	p[1] = iso9660->el_torito.media_type;
 +	/* Load Segment */
 +	if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION)
 +		set_num_721(&p[2], iso9660->el_torito.boot_load_seg);
 +	else
 +		set_num_721(&p[2], 0);
 +	/* System Type */
 +	p[4] = iso9660->el_torito.system_type;
 +	/* Unused */
 +	p[5] = 0;
 +	/* Sector Count */
 +	if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION)
 +		set_num_721(&p[6], iso9660->el_torito.boot_load_size);
 +	else
 +		set_num_721(&p[6], 1);
 +	/* Load RBA */
 +	set_num_731(&p[8],
 +	    iso9660->el_torito.boot->file->content.location);
 +	/* Unused */
 +	memset(&p[12], 0, 20);
 +
 +	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
 +}
 +
 +static int
 +setup_boot_information(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct isoent *np;
 +	int64_t size;
 +	uint32_t sum;
 +	unsigned char buff[4096];
 +
 +	np = iso9660->el_torito.boot;
 +	lseek(iso9660->temp_fd,
 +	    np->file->content.offset_of_temp + 64, SEEK_SET);
 +	size = archive_entry_size(np->file->entry) - 64;
 +	if (size <= 0) {
 +		archive_set_error(&a->archive, errno,
 +		    "Boot file(%jd) is too small", (intmax_t)size + 64);
 +		return (ARCHIVE_FATAL);
 +	}
 +	sum = 0;
 +	while (size > 0) {
 +		size_t rsize;
 +		ssize_t i, rs;
 +
 +		if (size > (int64_t)sizeof(buff))
 +			rsize = sizeof(buff);
 +		else
 +			rsize = (size_t)size;
 +
 +		rs = read(iso9660->temp_fd, buff, rsize);
 +		if (rs <= 0) {
 +			archive_set_error(&a->archive, errno,
 +			    "Can't read temporary file(%jd)",
 +			    (intmax_t)rs);
 +			return (ARCHIVE_FATAL);
 +		}
 +		for (i = 0; i < rs; i += 4)
 +			sum += archive_le32dec(buff + i);
 +		size -= rs;
 +	}
 +	/* Set the location of Primary Volume Descriptor. */
 +	set_num_731(buff, SYSTEM_AREA_BLOCK);
 +	/* Set the location of the boot file. */
 +	set_num_731(buff+4, np->file->content.location);
 +	/* Set the size of the boot file. */
 +	size = fd_boot_image_size(iso9660->el_torito.media_type);
 +	if (size == 0)
 +		size = archive_entry_size(np->file->entry);
 +	set_num_731(buff+8, (uint32_t)size);
 +	/* Set the sum of the boot file. */
 +	set_num_731(buff+12, sum);
 +	/* Clear reserved bytes. */
 +	memset(buff+16, 0, 40);
 +
 +	/* Overwrite the boot file. */
 +	lseek(iso9660->temp_fd,
 +	    np->file->content.offset_of_temp + 8, SEEK_SET);
 +	return (write_to_temp(a, buff, 56));
 +}
 +
 +#ifdef HAVE_ZLIB_H
 +
 +static int
 +zisofs_init_zstream(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	int r;
 +
 +	iso9660->zisofs.stream.next_in = NULL;
 +	iso9660->zisofs.stream.avail_in = 0;
 +	iso9660->zisofs.stream.total_in = 0;
 +	iso9660->zisofs.stream.total_out = 0;
 +	if (iso9660->zisofs.stream_valid)
 +		r = deflateReset(&(iso9660->zisofs.stream));
 +	else {
 +		r = deflateInit(&(iso9660->zisofs.stream),
 +		    iso9660->zisofs.compression_level);
 +		iso9660->zisofs.stream_valid = 1;
 +	}
 +	switch (r) {
 +	case Z_OK:
 +		break;
 +	default:
 +	case Z_STREAM_ERROR:
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Internal error initializing "
 +		    "compression library: invalid setup parameter");
 +		return (ARCHIVE_FATAL);
 +	case Z_MEM_ERROR:
 +		archive_set_error(&a->archive, ENOMEM,
 +		    "Internal error initializing "
 +		    "compression library");
 +		return (ARCHIVE_FATAL);
 +	case Z_VERSION_ERROR:
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Internal error initializing "
 +		    "compression library: invalid library version");
 +		return (ARCHIVE_FATAL);
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +#endif /* HAVE_ZLIB_H */
 +
 +static int
 +zisofs_init(struct archive_write *a,  struct isofile *file)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +#ifdef HAVE_ZLIB_H
 +	uint64_t tsize;
 +	size_t _ceil, bpsize;
 +	int r;
 +#endif
 +
 +	iso9660->zisofs.detect_magic = 0;
 +	iso9660->zisofs.making = 0;
 +
 +	if (!iso9660->opt.rr || !iso9660->opt.zisofs)
 +		return (ARCHIVE_OK);
 +
 +	if (archive_entry_size(file->entry) >= 24 &&
 +	    archive_entry_size(file->entry) < MULTI_EXTENT_SIZE) {
 +		/* Acceptable file size for zisofs. */
 +		iso9660->zisofs.detect_magic = 1;
 +		iso9660->zisofs.magic_cnt = 0;
 +	}
 +	if (!iso9660->zisofs.detect_magic)
 +		return (ARCHIVE_OK);
 +
 +#ifdef HAVE_ZLIB_H
 +	/* The number of Logical Blocks which uncompressed data
 +	 * will use in iso-image file is the same as the number of
 +	 * Logical Blocks which zisofs(compressed) data will use
 +	 * in ISO-image file. It won't reduce iso-image file size. */
 +	if (archive_entry_size(file->entry) <= LOGICAL_BLOCK_SIZE)
 +		return (ARCHIVE_OK);
 +
 +	/* Initialize compression library */
 +	r = zisofs_init_zstream(a);
 +	if (r != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Mark file->zisofs to create RRIP 'ZF' Use Entry. */
 +	file->zisofs.header_size = ZF_HEADER_SIZE >> 2;
 +	file->zisofs.log2_bs = ZF_LOG2_BS;
 +	file->zisofs.uncompressed_size =
 +		(uint32_t)archive_entry_size(file->entry);
 +
 +	/* Calculate a size of Block Pointers of zisofs. */
 +	_ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1)
 +		>> file->zisofs.log2_bs;
 +	iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1;
 +	iso9660->zisofs.block_pointers_idx = 0;
 +
 +	/* Ensure a buffer size used for Block Pointers */
 +	bpsize = iso9660->zisofs.block_pointers_cnt *
 +	    sizeof(iso9660->zisofs.block_pointers[0]);
 +	if (iso9660->zisofs.block_pointers_allocated < bpsize) {
 +		free(iso9660->zisofs.block_pointers);
 +		iso9660->zisofs.block_pointers = malloc(bpsize);
 +		if (iso9660->zisofs.block_pointers == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "Can't allocate data");
 +			return (ARCHIVE_FATAL);
 +		}
 +		iso9660->zisofs.block_pointers_allocated = bpsize;
 +	}
 +
 +	/*
 +	 * Skip zisofs header and Block Pointers, which we will write
 +	 * after all compressed data of a file written to the temporary
 +	 * file.
 +	 */
 +	tsize = ZF_HEADER_SIZE + bpsize;
 +	if (write_null(a, (size_t)tsize) != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/*
 +	 * Initialize some variables to make zisofs.
 +	 */
 +	archive_le32enc(&(iso9660->zisofs.block_pointers[0]),
 +		(uint32_t)tsize);
 +	iso9660->zisofs.remaining = file->zisofs.uncompressed_size;
 +	iso9660->zisofs.making = 1;
 +	iso9660->zisofs.allzero = 1;
 +	iso9660->zisofs.block_offset = tsize;
 +	iso9660->zisofs.total_size = tsize;
 +	iso9660->cur_file->cur_content->size = tsize;
 +#endif
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static void
 +zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct isofile *file = iso9660->cur_file;
 +	const unsigned char *p, *endp;
 +	const unsigned char *magic_buff;
 +	uint32_t uncompressed_size;
 +	unsigned char header_size;
 +	unsigned char log2_bs;
 +	size_t _ceil, doff;
 +	uint32_t bst, bed;
 +	int magic_max;
 +	int64_t entry_size;
 +
 +	entry_size = archive_entry_size(file->entry);
 +	if ((int64_t)sizeof(iso9660->zisofs.magic_buffer) > entry_size)
 +		magic_max = (int)entry_size;
 +	else
 +		magic_max = sizeof(iso9660->zisofs.magic_buffer);
 +
 +	if (iso9660->zisofs.magic_cnt == 0 && s >= (size_t)magic_max)
 +		/* It's unnecessary we copy buffer. */
 +		magic_buff = buff;
 +	else {
 +		if (iso9660->zisofs.magic_cnt < magic_max) {
 +			size_t l;
 +
 +			l = sizeof(iso9660->zisofs.magic_buffer)
 +			    - iso9660->zisofs.magic_cnt;
 +			if (l > s)
 +				l = s;
 +			memcpy(iso9660->zisofs.magic_buffer
 +			    + iso9660->zisofs.magic_cnt, buff, l);
 +			iso9660->zisofs.magic_cnt += (int)l;
 +			if (iso9660->zisofs.magic_cnt < magic_max)
 +				return;
 +		}
 +		magic_buff = iso9660->zisofs.magic_buffer;
 +	}
 +	iso9660->zisofs.detect_magic = 0;
 +	p = magic_buff;
 +
 +	/* Check the magic code of zisofs. */
 +	if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0)
 +		/* This is not zisofs file which made by mkzftree. */
 +		return;
 +	p += sizeof(zisofs_magic);
 +
 +	/* Read a zisofs header. */
 +	uncompressed_size = archive_le32dec(p);
 +	header_size = p[4];
 +	log2_bs = p[5];
 +	if (uncompressed_size < 24 || header_size != 4 ||
 +	    log2_bs > 30 || log2_bs < 7)
 +		return;/* Invalid or not supported header. */
 +
 +	/* Calculate a size of Block Pointers of zisofs. */
 +	_ceil = (uncompressed_size +
 +	        (ARCHIVE_LITERAL_LL(1) << log2_bs) -1) >> log2_bs;
 +	doff = (_ceil + 1) * 4 + 16;
 +	if (entry_size < (int64_t)doff)
 +		return;/* Invalid data. */
 +
 +	/* Check every Block Pointer has valid value. */
 +	p = magic_buff + 16;
 +	endp = magic_buff + magic_max;
 +	while (_ceil && p + 8 <= endp) {
 +		bst = archive_le32dec(p);
 +		if (bst != doff)
 +			return;/* Invalid data. */
 +		p += 4;
 +		bed = archive_le32dec(p);
 +		if (bed < bst || bed > entry_size)
 +			return;/* Invalid data. */
 +		doff += bed - bst;
 +		_ceil--;
 +	}
 +
 +	file->zisofs.uncompressed_size = uncompressed_size;
 +	file->zisofs.header_size = header_size;
 +	file->zisofs.log2_bs = log2_bs;
 +
 +	/* Disable making a zisofs image. */
 +	iso9660->zisofs.making = 0;
 +}
 +
 +#ifdef HAVE_ZLIB_H
 +
 +/*
 + * Compress data and write it to a temporary file.
 + */
 +static int
 +zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct isofile *file = iso9660->cur_file;
 +	const unsigned char *b;
 +	z_stream *zstrm;
 +	size_t avail, csize;
 +	int flush, r;
 +
 +	zstrm = &(iso9660->zisofs.stream);
 +	zstrm->next_out = wb_buffptr(a);
 +	zstrm->avail_out = (uInt)wb_remaining(a);
 +	b = (const unsigned char *)buff;
 +	do {
 +		avail = ZF_BLOCK_SIZE - zstrm->total_in;
 +		if (s < avail) {
 +			avail = s;
 +			flush = Z_NO_FLUSH;
 +		} else
 +			flush = Z_FINISH;
 +		iso9660->zisofs.remaining -= avail;
 +		if (iso9660->zisofs.remaining <= 0)
 +			flush = Z_FINISH;
 +
 +		zstrm->next_in = (Bytef *)(uintptr_t)(const void *)b;
 +		zstrm->avail_in = (uInt)avail;
 +
 +		/*
 +		 * Check if current data block are all zero.
 +		 */
 +		if (iso9660->zisofs.allzero) {
 +			const unsigned char *nonzero = b;
 +			const unsigned char *nonzeroend = b + avail;
 +
 +			while (nonzero < nonzeroend)
 +				if (*nonzero++) {
 +					iso9660->zisofs.allzero = 0;
 +					break;
 +				}
 +		}
 +		b += avail;
 +		s -= avail;
 +
 +		/*
 +		 * If current data block are all zero, we do not use
 +		 * compressed data.
 +		 */
 +		if (flush == Z_FINISH && iso9660->zisofs.allzero &&
 +		    avail + zstrm->total_in == ZF_BLOCK_SIZE) {
 +			if (iso9660->zisofs.block_offset !=
 +			    file->cur_content->size) {
 +				int64_t diff;
 +
 +				r = wb_set_offset(a,
 +				    file->cur_content->offset_of_temp +
 +				        iso9660->zisofs.block_offset);
 +				if (r != ARCHIVE_OK)
 +					return (r);
 +				diff = file->cur_content->size -
 +				    iso9660->zisofs.block_offset;
 +				file->cur_content->size -= diff;
 +				iso9660->zisofs.total_size -= diff;
 +			}
 +			zstrm->avail_in = 0;
 +		}
 +
 +		/*
 +		 * Compress file data.
 +		 */
 +		while (zstrm->avail_in > 0) {
 +			csize = zstrm->total_out;
 +			r = deflate(zstrm, flush);
 +			switch (r) {
 +			case Z_OK:
 +			case Z_STREAM_END:
 +				csize = zstrm->total_out - csize;
 +				if (wb_consume(a, csize) != ARCHIVE_OK)
 +					return (ARCHIVE_FATAL);
 +				iso9660->zisofs.total_size += csize;
 +				iso9660->cur_file->cur_content->size += csize;
 +				zstrm->next_out = wb_buffptr(a);
 +				zstrm->avail_out = (uInt)wb_remaining(a);
 +				break;
 +			default:
 +				archive_set_error(&a->archive,
 +				    ARCHIVE_ERRNO_MISC,
 +				    "Compression failed:"
 +				    " deflate() call returned status %d",
 +				    r);
 +				return (ARCHIVE_FATAL);
 +			}
 +		}
 +
 +		if (flush == Z_FINISH) {
 +			/*
 +			 * Save the information of one zisofs block.
 +			 */
 +			iso9660->zisofs.block_pointers_idx ++;
 +			archive_le32enc(&(iso9660->zisofs.block_pointers[
 +			    iso9660->zisofs.block_pointers_idx]),
 +				(uint32_t)iso9660->zisofs.total_size);
 +			r = zisofs_init_zstream(a);
 +			if (r != ARCHIVE_OK)
 +				return (ARCHIVE_FATAL);
 +			iso9660->zisofs.allzero = 1;
 +			iso9660->zisofs.block_offset = file->cur_content->size;
 +		}
 +	} while (s);
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +zisofs_finish_entry(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct isofile *file = iso9660->cur_file;
 +	unsigned char buff[16];
 +	size_t s;
 +	int64_t tail;
 +
 +	/* Direct temp file stream to zisofs temp file stream. */
 +	archive_entry_set_size(file->entry, iso9660->zisofs.total_size);
 +
 +	/*
 +	 * Save a file pointer which points the end of current zisofs data.
 +	 */
 +	tail = wb_offset(a);
 +
 +	/*
 +	 * Make a header.
 +	 *
 +	 * +-----------------+----------------+-----------------+
 +	 * | Header 16 bytes | Block Pointers | Compressed data |
 +	 * +-----------------+----------------+-----------------+
 +	 * 0                16               +X
 +	 * Block Pointers :
 +	 *   4 * (((Uncompressed file size + block_size -1) / block_size) + 1)
 +	 *
 +	 * Write zisofs header.
 +	 *    Magic number
 +	 * +----+----+----+----+----+----+----+----+
 +	 * | 37 | E4 | 53 | 96 | C9 | DB | D6 | 07 |
 +	 * +----+----+----+----+----+----+----+----+
 +	 * 0    1    2    3    4    5    6    7    8
 +	 *
 +	 * +------------------------+------------------+
 +	 * | Uncompressed file size | header_size >> 2 |
 +	 * +------------------------+------------------+
 +	 * 8                       12                 13
 +	 *
 +	 * +-----------------+----------------+
 +	 * | log2 block_size | Reserved(0000) |
 +	 * +-----------------+----------------+
 +	 * 13               14               16
 +	 */
 +	memcpy(buff, zisofs_magic, 8);
 +	set_num_731(buff+8, file->zisofs.uncompressed_size);
 +	buff[12] = file->zisofs.header_size;
 +	buff[13] = file->zisofs.log2_bs;
 +	buff[14] = buff[15] = 0;/* Reserved */
 +
 +	/* Move to the right position to write the header. */
 +	wb_set_offset(a, file->content.offset_of_temp);
 +
 +	/* Write the header. */
 +	if (wb_write_to_temp(a, buff, 16) != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/*
 +	 * Write zisofs Block Pointers.
 +	 */
 +	s = iso9660->zisofs.block_pointers_cnt *
 +	    sizeof(iso9660->zisofs.block_pointers[0]);
 +	if (wb_write_to_temp(a, iso9660->zisofs.block_pointers, s)
 +	    != ARCHIVE_OK)
 +		return (ARCHIVE_FATAL);
 +
 +	/* Set a file pointer back to the end of the temporary file. */
 +	wb_set_offset(a, tail);
 +
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +zisofs_free(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	int ret = ARCHIVE_OK;
 +
 +	free(iso9660->zisofs.block_pointers);
 +	if (iso9660->zisofs.stream_valid &&
 +	    deflateEnd(&(iso9660->zisofs.stream)) != Z_OK) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Failed to clean up compressor");
 +		ret = ARCHIVE_FATAL;
 +	}
 +	iso9660->zisofs.block_pointers = NULL;
 +	iso9660->zisofs.stream_valid = 0;
 +	return (ret);
 +}
 +
 +struct zisofs_extract {
 +	int		 pz_log2_bs; /* Log2 of block size */
 +	uint64_t	 pz_uncompressed_size;
 +	size_t		 uncompressed_buffer_size;
 +
 +	int		 initialized:1;
 +	int		 header_passed:1;
 +
 +	uint32_t	 pz_offset;
 +	unsigned char	*block_pointers;
 +	size_t		 block_pointers_size;
 +	size_t		 block_pointers_avail;
 +	size_t		 block_off;
 +	uint32_t	 block_avail;
 +
 +	z_stream	 stream;
 +	int		 stream_valid;
 +};
 +
 +static ssize_t
 +zisofs_extract_init(struct archive_write *a, struct zisofs_extract *zisofs,
 +    const unsigned char *p, size_t bytes)
 +{
 +	size_t avail = bytes;
 +	size_t _ceil, xsize;
 +
 +	/* Allocate block pointers buffer. */
 +	_ceil = (size_t)((zisofs->pz_uncompressed_size +
 +		(((int64_t)1) << zisofs->pz_log2_bs) - 1)
 +		>> zisofs->pz_log2_bs);
 +	xsize = (_ceil + 1) * 4;
 +	if (zisofs->block_pointers == NULL) {
 +		size_t alloc = ((xsize >> 10) + 1) << 10;
 +		zisofs->block_pointers = malloc(alloc);
 +		if (zisofs->block_pointers == NULL) {
 +			archive_set_error(&a->archive, ENOMEM,
 +			    "No memory for zisofs decompression");
 +			return (ARCHIVE_FATAL);
 +		}
 +	}
 +	zisofs->block_pointers_size = xsize;
 +
 +	/* Allocate uncompressed data buffer. */
 +	zisofs->uncompressed_buffer_size = (size_t)1UL << zisofs->pz_log2_bs;
 +
 +	/*
 +	 * Read the file header, and check the magic code of zisofs.
 +	 */
 +	if (!zisofs->header_passed) {
 +		int err = 0;
 +		if (avail < 16) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Illegal zisofs file body");
 +			return (ARCHIVE_FATAL);
 +		}
 +
 +		if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0)
 +			err = 1;
 +		else if (archive_le32dec(p + 8) != zisofs->pz_uncompressed_size)
 +			err = 1;
 +		else if (p[12] != 4 || p[13] != zisofs->pz_log2_bs)
 +			err = 1;
 +		if (err) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Illegal zisofs file body");
 +			return (ARCHIVE_FATAL);
 +		}
 +		avail -= 16;
 +		p += 16;
 +		zisofs->header_passed = 1;
 +	}
 +
 +	/*
 +	 * Read block pointers.
 +	 */
 +	if (zisofs->header_passed &&
 +	    zisofs->block_pointers_avail < zisofs->block_pointers_size) {
 +		xsize = zisofs->block_pointers_size
 +		    - zisofs->block_pointers_avail;
 +		if (avail < xsize)
 +			xsize = avail;
 +		memcpy(zisofs->block_pointers
 +		    + zisofs->block_pointers_avail, p, xsize);
 +		zisofs->block_pointers_avail += xsize;
 +		avail -= xsize;
 +	    	if (zisofs->block_pointers_avail
 +		    == zisofs->block_pointers_size) {
 +			/* We've got all block pointers and initialize
 +			 * related variables.	*/
 +			zisofs->block_off = 0;
 +			zisofs->block_avail = 0;
 +			/* Complete a initialization */
 +			zisofs->initialized = 1;
 +		}
 +	}
 +	return ((ssize_t)avail);
 +}
 +
 +static ssize_t
 +zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs,
 +    const unsigned char *p, size_t bytes)
 +{
 +	size_t avail;
 +	int r;
 +
 +	if (!zisofs->initialized) {
 +		ssize_t rs = zisofs_extract_init(a, zisofs, p, bytes);
 +		if (rs < 0)
 +			return (rs);
 +		if (!zisofs->initialized) {
 +			/* We need more data. */
 +			zisofs->pz_offset += (uint32_t)bytes;
 +			return (bytes);
 +		}
 +		avail = rs;
 +		p += bytes - avail;
 +	} else
 +		avail = bytes;
 +
 +	/*
 +	 * Get block offsets from block pointers.
 +	 */
 +	if (zisofs->block_avail == 0) {
 +		uint32_t bst, bed;
 +
 +		if (zisofs->block_off + 4 >= zisofs->block_pointers_size) {
 +			/* There isn't a pair of offsets. */
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Illegal zisofs block pointers");
 +			return (ARCHIVE_FATAL);
 +		}
 +		bst = archive_le32dec(
 +		    zisofs->block_pointers + zisofs->block_off);
 +		if (bst != zisofs->pz_offset + (bytes - avail)) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Illegal zisofs block pointers(cannot seek)");
 +			return (ARCHIVE_FATAL);
 +		}
 +		bed = archive_le32dec(
 +		    zisofs->block_pointers + zisofs->block_off + 4);
 +		if (bed < bst) {
 +			archive_set_error(&a->archive,
 +			    ARCHIVE_ERRNO_FILE_FORMAT,
 +			    "Illegal zisofs block pointers");
 +			return (ARCHIVE_FATAL);
 +		}
 +		zisofs->block_avail = bed - bst;
 +		zisofs->block_off += 4;
 +
 +		/* Initialize compression library for new block. */
 +		if (zisofs->stream_valid)
 +			r = inflateReset(&zisofs->stream);
 +		else
 +			r = inflateInit(&zisofs->stream);
 +		if (r != Z_OK) {
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "Can't initialize zisofs decompression.");
 +			return (ARCHIVE_FATAL);
 +		}
 +		zisofs->stream_valid = 1;
 +		zisofs->stream.total_in = 0;
 +		zisofs->stream.total_out = 0;
 +	}
 +
 +	/*
 +	 * Make uncompressed data.
 +	 */
 +	if (zisofs->block_avail == 0) {
 +		/*
 +		 * It's basically 32K bytes NUL data.
 +		 */
 +		unsigned char *wb;
 +		size_t size, wsize;
 +
 +		size = zisofs->uncompressed_buffer_size;
 +		while (size) {
 +			wb = wb_buffptr(a);
 +			if (size > wb_remaining(a))
 +				wsize = wb_remaining(a);
 +			else
 +				wsize = size;
 +			memset(wb, 0, wsize);
 +			r = wb_consume(a, wsize);
 +			if (r < 0)
 +				return (r);
 +			size -= wsize;
 +		}
 +	} else {
 +		zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p;
 +		if (avail > zisofs->block_avail)
 +			zisofs->stream.avail_in = zisofs->block_avail;
 +		else
 +			zisofs->stream.avail_in = (uInt)avail;
 +		zisofs->stream.next_out = wb_buffptr(a);
 +		zisofs->stream.avail_out = (uInt)wb_remaining(a);
 +
 +		r = inflate(&zisofs->stream, 0);
 +		switch (r) {
 +		case Z_OK: /* Decompressor made some progress.*/
 +		case Z_STREAM_END: /* Found end of stream. */
 +			break;
 +		default:
 +			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +			    "zisofs decompression failed (%d)", r);
 +			return (ARCHIVE_FATAL);
 +		}
 +		avail -= zisofs->stream.next_in - p;
 +		zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p);
 +		r = wb_consume(a, wb_remaining(a) - zisofs->stream.avail_out);
 +		if (r < 0)
 +			return (r);
 +	}
 +	zisofs->pz_offset += (uint32_t)bytes;
 +	return (bytes - avail);
 +}
 +
 +static int
 +zisofs_rewind_boot_file(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +	struct isofile *file;
 +	unsigned char *rbuff;
 +	ssize_t r;
 +	size_t remaining, rbuff_size;
 +	struct zisofs_extract zext;
 +	int64_t read_offset, write_offset, new_offset;
 +	int fd, ret = ARCHIVE_OK;
 +
 +	file = iso9660->el_torito.boot->file;
 +	/*
 +	 * There is nothing to do if this boot file does not have
 +	 * zisofs header.
 +	 */
 +	if (file->zisofs.header_size == 0)
 +		return (ARCHIVE_OK);
 +
 +	/*
 +	 * Uncompress the zisofs'ed file contents.
 +	 */
 +	memset(&zext, 0, sizeof(zext));
 +	zext.pz_uncompressed_size = file->zisofs.uncompressed_size;
 +	zext.pz_log2_bs = file->zisofs.log2_bs;
 +
 +	fd = iso9660->temp_fd;
 +	new_offset = wb_offset(a);
 +	read_offset = file->content.offset_of_temp;
 +	remaining = (size_t)file->content.size;
 +	if (remaining > 1024 * 32)
 +		rbuff_size = 1024 * 32;
 +	else
 +		rbuff_size = remaining;
 +
 +	rbuff = malloc(rbuff_size);
 +	if (rbuff == NULL) {
 +		archive_set_error(&a->archive, ENOMEM, "Can't allocate memory");
 +		return (ARCHIVE_FATAL);
 +	}
 +	while (remaining) {
 +		size_t rsize;
 +		ssize_t rs;
 +
 +		/* Get the current file pointer. */
 +		write_offset = lseek(fd, 0, SEEK_CUR);
 +
 +		/* Change the file pointer to read. */
 +		lseek(fd, read_offset, SEEK_SET);
 +
 +		rsize = rbuff_size;
 +		if (rsize > remaining)
 +			rsize = remaining;
 +		rs = read(iso9660->temp_fd, rbuff, rsize);
 +		if (rs <= 0) {
 +			archive_set_error(&a->archive, errno,
 +			    "Can't read temporary file(%jd)", (intmax_t)rs);
 +			ret = ARCHIVE_FATAL;
 +			break;
 +		}
 +		remaining -= rs;
 +		read_offset += rs;
 +
 +		/* Put the file pointer back to write. */
 +		lseek(fd, write_offset, SEEK_SET);
 +
 +		r = zisofs_extract(a, &zext, rbuff, rs);
 +		if (r < 0) {
 +			ret = (int)r;
 +			break;
 +		}
 +	}
 +
 +	if (ret == ARCHIVE_OK) {
 +		/*
 +		 * Change the boot file content from zisofs'ed data
 +		 * to plain data.
 +		 */
 +		file->content.offset_of_temp = new_offset;
 +		file->content.size = file->zisofs.uncompressed_size;
 +		archive_entry_set_size(file->entry, file->content.size);
 +		/* Set to be no zisofs. */
 +		file->zisofs.header_size = 0;
 +		file->zisofs.log2_bs = 0;
 +		file->zisofs.uncompressed_size = 0;
 +		r = wb_write_padding_to_temp(a, file->content.size);
 +		if (r < 0)
 +			ret = ARCHIVE_FATAL;
 +	}
 +
 +	/*
 +	 * Free the resource we used in this function only.
 +	 */
 +	free(rbuff);
 +	free(zext.block_pointers);
 +	if (zext.stream_valid && inflateEnd(&(zext.stream)) != Z_OK) {
 +        	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "Failed to clean up compressor");
 +		ret = ARCHIVE_FATAL;
 +	}
 +
 +	return (ret);
 +}
 +
 +#else
 +
 +static int
 +zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s)
 +{
 +	(void)buff; /* UNUSED */
 +	(void)s; /* UNUSED */
 +	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Programing error");
 +	return (ARCHIVE_FATAL);
 +}
 +
 +static int
 +zisofs_rewind_boot_file(struct archive_write *a)
 +{
 +	struct iso9660 *iso9660 = a->format_data;
 +
 +	if (iso9660->el_torito.boot->file->zisofs.header_size != 0) {
 +		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 +		    "We cannot extract the zisofs imaged boot file;"
 +		    " this may not boot in being zisofs imaged");
 +		return (ARCHIVE_FAILED);
 +	}
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +zisofs_finish_entry(struct archive_write *a)
 +{
 +	(void)a; /* UNUSED */
 +	return (ARCHIVE_OK);
 +}
 +
 +static int
 +zisofs_free(struct archive_write *a)
 +{
 +	(void)a; /* UNUSED */
 +	return (ARCHIVE_OK);
 +}
 +
 +#endif /* HAVE_ZLIB_H */
 +

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=2b94d71d8850d68b677d5653c698371528344a10
commit 2b94d71d8850d68b677d5653c698371528344a10
Author:     LibArchive Upstream <libarchive-discuss at googlegroups.com>
AuthorDate: Sun Jun 19 20:30:48 2016 -0700
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Mon Jun 20 10:50:05 2016 -0400

    LibArchive 2016-06-19 (139d0576)
    
    Code extracted from:
    
        https://github.com/libarchive/libarchive.git
    
    at commit 139d0576b51a253732a5ab1f66805dffbf8b00af (master).

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4b97039..68e94a6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -365,7 +365,7 @@ IF(DEFINED __GNUWIN32PATH AND EXISTS "${__GNUWIN32PATH}")
   #   e.g.
   #     cmake -DCMAKE_PREFIX_PATH=<your-GnuWin32-path> <path-to-source>
   #
-  # If compiling error occured in zconf.h, You may need patch to zconf.h.
+  # 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 @@
diff --git a/build/version b/build/version
index 595378f..f293156 100644
--- a/build/version
+++ b/build/version
@@ -1 +1 @@
-3002000
+3002001
diff --git a/libarchive/archive.h b/libarchive/archive.h
index 59e9ef1..1794dd3 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 3002000
+#define	ARCHIVE_VERSION_NUMBER 3002001
 
 #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.2.0"
+#define	ARCHIVE_VERSION_ONLY_STRING "3.2.1"
 #define	ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
 __LA_DECL const char *	archive_version_string(void);
 
diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h
index 423c8d3..dab2c9d 100644
--- a/libarchive/archive_entry.h
+++ b/libarchive/archive_entry.h
@@ -29,7 +29,7 @@
 #define	ARCHIVE_ENTRY_H_INCLUDED
 
 /* Note: Compiler will complain if this does not match archive.h! */
-#define	ARCHIVE_VERSION_NUMBER 3002000
+#define	ARCHIVE_VERSION_NUMBER 3002001
 
 /*
  * Note: archive_entry.h is for use outside of libarchive; the
diff --git a/libarchive/archive_entry_xattr.c b/libarchive/archive_entry_xattr.c
index 05eb90f..5fe726b 100644
--- a/libarchive/archive_entry_xattr.c
+++ b/libarchive/archive_entry_xattr.c
@@ -91,16 +91,11 @@ archive_entry_xattr_add_entry(struct archive_entry *entry,
 {
 	struct ae_xattr	*xp;
 
-	for (xp = entry->xattr_head; xp != NULL; xp = xp->next)
-		;
-
 	if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL)
-		/* XXX Error XXX */
-		return;
+		__archive_errx(1, "Out of memory");
 
 	if ((xp->name = strdup(name)) == NULL)
-		/* XXX Error XXX */
-		return;
+		__archive_errx(1, "Out of memory");
 
 	if ((xp->value = malloc(size)) != NULL) {
 		memcpy(xp->value, value, size);
diff --git a/libarchive/archive_ppmd7.c b/libarchive/archive_ppmd7.c
index fe0b031..1aed922 100644
--- a/libarchive/archive_ppmd7.c
+++ b/libarchive/archive_ppmd7.c
@@ -126,6 +126,11 @@ static Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc)
 {
   if (p->Base == 0 || p->Size != size)
   {
+    /* RestartModel() below assumes that p->Size >= UNIT_SIZE
+       (see the calculation of m->MinContext). */
+    if (size < UNIT_SIZE) {
+      return False;
+    }
     Ppmd7_Free(p, alloc);
     p->AlignOffset =
       #ifdef PPMD_32BIT
diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c
index 53bd4b8..566d264 100644
--- a/libarchive/archive_read_disk_windows.c
+++ b/libarchive/archive_read_disk_windows.c
@@ -1586,7 +1586,7 @@ tree_reopen(struct tree *t, const wchar_t *path, int restore_time)
 	t->stack->flags = needsFirstVisit;
 	/*
 	 * Debug flag for Direct IO(No buffering) or Async IO.
-	 * Those dependant on environment variable switches
+	 * Those dependent on environment variable switches
 	 * will be removed until next release.
 	 */
 	{
diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c
index 90901ac..1dfe52b 100644
--- a/libarchive/archive_read_support_format_7zip.c
+++ b/libarchive/archive_read_support_format_7zip.c
@@ -2153,6 +2153,9 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
 				return (-1);
 			if (UMAX_ENTRY < f[i].numUnpackStreams)
 				return (-1);
+			if (unpack_streams > SIZE_MAX - UMAX_ENTRY) {
+				return (-1);
+			}
 			unpack_streams += (size_t)f[i].numUnpackStreams;
 		}
 		if ((p = header_bytes(a, 1)) == NULL)
diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c
index c2ca85b..b09db0e 100644
--- a/libarchive/archive_read_support_format_cpio.c
+++ b/libarchive/archive_read_support_format_cpio.c
@@ -401,6 +401,11 @@ archive_read_format_cpio_read_header(struct archive_read *a,
 
 	/* If this is a symlink, read the link contents. */
 	if (archive_entry_filetype(entry) == AE_IFLNK) {
+		if (cpio->entry_bytes_remaining > 1024 * 1024) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte");
+			return (ARCHIVE_FATAL);
+		}
 		h = __archive_read_ahead(a,
 			(size_t)cpio->entry_bytes_remaining, NULL);
 		if (h == NULL)
diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c
index 6934cee..f41ba38 100644
--- a/libarchive/archive_read_support_format_iso9660.c
+++ b/libarchive/archive_read_support_format_iso9660.c
@@ -1091,7 +1091,7 @@ choose_volume(struct archive_read *a, struct iso9660 *iso9660)
 		/* This condition is unlikely; by way of caution. */
 		vd = &(iso9660->joliet);
 
-	skipsize = LOGICAL_BLOCK_SIZE * vd->location;
+	skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location;
 	skipsize = __archive_read_consume(a, skipsize);
 	if (skipsize < 0)
 		return ((int)skipsize);
@@ -1129,7 +1129,7 @@ choose_volume(struct archive_read *a, struct iso9660 *iso9660)
 	    && iso9660->seenJoliet) {
 		/* Switch reading data from primary to joliet. */
 		vd = &(iso9660->joliet);
-		skipsize = LOGICAL_BLOCK_SIZE * vd->location;
+		skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location;
 		skipsize -= iso9660->current_position;
 		skipsize = __archive_read_consume(a, skipsize);
 		if (skipsize < 0)
diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c
index 81d9652..8c3be9a 100644
--- a/libarchive/archive_read_support_format_mtree.c
+++ b/libarchive/archive_read_support_format_mtree.c
@@ -1342,7 +1342,7 @@ parse_line(struct archive_read *a, struct archive_entry *entry,
 /* strsep() is not in C90, but strcspn() is. */
 /* Taken from http://unixpapa.com/incnote/string.html */
 static char *
-la_strsep(char **sp, char *sep)
+la_strsep(char **sp, const char *sep)
 {
 	char *p, *s;
 	if (sp == NULL || *sp == NULL || **sp == '\0')
@@ -1385,12 +1385,12 @@ parse_device(dev_t *pdev, struct archive *a, char *val)
 				    "Missing number");
 				return ARCHIVE_WARN;
 			}
-			numbers[argc++] = (unsigned long)mtree_atol(&p);
-			if (argc > MAX_PACK_ARGS) {
+			if (argc >= MAX_PACK_ARGS) {
 				archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
 				    "Too many arguments");
 				return ARCHIVE_WARN;
 			}
+			numbers[argc++] = (unsigned long)mtree_atol(&p);
 		}
 		if (argc < 2) {
 			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c
index 6450aac..f729f17 100644
--- a/libarchive/archive_read_support_format_rar.c
+++ b/libarchive/archive_read_support_format_rar.c
@@ -2127,6 +2127,12 @@ parse_codes(struct archive_read *a)
       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))
       {
@@ -2884,11 +2890,10 @@ copy_from_lzss_window(struct archive_read *a, const void **buffer,
   }
 
   windowoffs = lzss_offset_for_position(&rar->lzss, startpos);
-  if(windowoffs + length <= lzss_size(&rar->lzss))
+  if(windowoffs + length <= lzss_size(&rar->lzss)) {
     memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs],
            length);
-  else
-  {
+  } 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,
@@ -2900,9 +2905,14 @@ copy_from_lzss_window(struct archive_read *a, const void **buffer,
              &rar->lzss.window[windowoffs], firstpart);
       memcpy(&rar->unp_buffer[rar->unp_offset + firstpart],
              &rar->lzss.window[0], length - firstpart);
-    } else
+    } 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)
diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c
index 01d85cf..b0521a6 100644
--- a/libarchive/archive_read_support_format_tar.c
+++ b/libarchive/archive_read_support_format_tar.c
@@ -202,7 +202,7 @@ static int	archive_read_format_tar_read_header(struct archive_read *,
 		    struct archive_entry *);
 static int	checksum(struct archive_read *, const void *);
 static int 	pax_attribute(struct archive_read *, struct tar *,
-		    struct archive_entry *, char *key, char *value);
+		    struct archive_entry *, const char *key, const char *value);
 static int 	pax_header(struct archive_read *, struct tar *,
 		    struct archive_entry *, char *attr);
 static void	pax_time(const char *, int64_t *sec, long *nanos);
@@ -1664,7 +1664,7 @@ pax_header(struct archive_read *a, struct tar *tar,
 
 static int
 pax_attribute_xattr(struct archive_entry *entry,
-	char *name, char *value)
+	const char *name, const char *value)
 {
 	char *name_decoded;
 	void *value_decoded;
@@ -1710,7 +1710,7 @@ pax_attribute_xattr(struct archive_entry *entry,
  */
 static int
 pax_attribute(struct archive_read *a, struct tar *tar,
-    struct archive_entry *entry, char *key, char *value)
+    struct archive_entry *entry, const char *key, const char *value)
 {
 	int64_t s;
 	long n;
diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c
index 0a0be96..34ab04e 100644
--- a/libarchive/archive_read_support_format_zip.c
+++ b/libarchive/archive_read_support_format_zip.c
@@ -181,6 +181,14 @@ struct zip {
 	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;
@@ -1293,8 +1301,9 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
 
 	if (zip->tctx_valid || zip->cctx_valid) {
 		if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) {
-			size_t buff_remaining = zip->decrypted_buffer_size
-			    - (zip->decrypted_ptr - zip->decrypted_buffer);
+			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;
diff --git a/libarchive/archive_write_disk_windows.c b/libarchive/archive_write_disk_windows.c
index 800aa89..da76c54 100644
--- a/libarchive/archive_write_disk_windows.c
+++ b/libarchive/archive_write_disk_windows.c
@@ -468,9 +468,17 @@ permissive_name_w(struct archive_write_disk *a)
 		return (-1);
 	archive_wstring_ensure(&(a->_name_data), 4 + l + 1 + wcslen(wn) + 1);
 	a->name = a->_name_data.s;
-	/* Prepend "\\?\" and drive name. */
-	archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);
-	archive_wstrncat(&(a->_name_data), wsp, l);
+	/* Prepend "\\?\" and drive name if not already added. */
+	if (l > 3 && wsp[0] == L'\\' && wsp[1] == L'\\' &&
+		wsp[2] == L'?' && wsp[3] == L'\\')
+	{
+		archive_wstrncpy(&(a->_name_data), wsp, l);
+	}
+	else
+	{
+		archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);
+		archive_wstrncat(&(a->_name_data), wsp, l);
+	}
 	archive_wstrncat(&(a->_name_data), L"\\", 1);
 	archive_wstrcat(&(a->_name_data), wn);
 	a->name = a->_name_data.s;
diff --git a/libarchive/archive_write_filter.3 b/libarchive/archive_write_filter.3
index 869dc46..e1d1891 100644
--- a/libarchive/archive_write_filter.3
+++ b/libarchive/archive_write_filter.3
@@ -43,6 +43,7 @@
 .Nm archive_write_add_filter_program ,
 .Nm archive_write_add_filter_uuencode ,
 .Nm archive_write_add_filter_xz
+.Nd functions enabling output filters
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
diff --git a/libarchive/archive_write_set_format_gnutar.c b/libarchive/archive_write_set_format_gnutar.c
index 647079d..1d635d2 100644
--- a/libarchive/archive_write_set_format_gnutar.c
+++ b/libarchive/archive_write_set_format_gnutar.c
@@ -467,7 +467,7 @@ archive_write_gnutar_header(struct archive_write *a,
 		}
 	}
 	if (gnutar->linkname_length > GNUTAR_linkname_size) {
-		size_t todo = gnutar->linkname_length;
+		size_t length = gnutar->linkname_length + 1;
 		struct archive_entry *temp = archive_entry_new2(&a->archive);
 
 		/* Uname/gname here don't really matter since no one reads them;
@@ -476,7 +476,7 @@ archive_write_gnutar_header(struct archive_write *a,
 		archive_entry_set_gname(temp, "wheel");
 
 		archive_entry_set_pathname(temp, "././@LongLink");
-		archive_entry_set_size(temp, gnutar->linkname_length + 1);
+		archive_entry_set_size(temp, length);
 		ret = archive_format_gnutar_header(a, buff, temp, 'K');
 		if (ret < ARCHIVE_WARN)
 			goto exit_write_header;
@@ -484,11 +484,12 @@ archive_write_gnutar_header(struct archive_write *a,
 		if(ret < ARCHIVE_WARN)
 			goto exit_write_header;
 		archive_entry_free(temp);
-		/* Write as many 512 bytes blocks as needed to write full name. */
-		ret = __archive_write_output(a, gnutar->linkname, todo);
+		/* Write name and trailing null byte. */
+		ret = __archive_write_output(a, gnutar->linkname, length);
 		if(ret < ARCHIVE_WARN)
 			goto exit_write_header;
-		ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)todo));
+		/* Pad to 512 bytes */
+		ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));
 		if (ret < ARCHIVE_WARN)
 			goto exit_write_header;
 	}
@@ -496,7 +497,7 @@ archive_write_gnutar_header(struct archive_write *a,
 	/* If pathname is longer than 100 chars we need to add an 'L' header. */
 	if (gnutar->pathname_length > GNUTAR_name_size) {
 		const char *pathname = gnutar->pathname;
-		size_t todo = gnutar->pathname_length;
+		size_t length = gnutar->pathname_length + 1;
 		struct archive_entry *temp = archive_entry_new2(&a->archive);
 
 		/* Uname/gname here don't really matter since no one reads them;
@@ -505,7 +506,7 @@ archive_write_gnutar_header(struct archive_write *a,
 		archive_entry_set_gname(temp, "wheel");
 
 		archive_entry_set_pathname(temp, "././@LongLink");
-		archive_entry_set_size(temp, gnutar->pathname_length + 1);
+		archive_entry_set_size(temp, length);
 		ret = archive_format_gnutar_header(a, buff, temp, 'L');
 		if (ret < ARCHIVE_WARN)
 			goto exit_write_header;
@@ -513,11 +514,12 @@ archive_write_gnutar_header(struct archive_write *a,
 		if(ret < ARCHIVE_WARN)
 			goto exit_write_header;
 		archive_entry_free(temp);
-		/* Write as many 512 bytes blocks as needed to write full name. */
-		ret = __archive_write_output(a, pathname, todo);
+		/* Write pathname + trailing null byte. */
+		ret = __archive_write_output(a, pathname, length);
 		if(ret < ARCHIVE_WARN)
 			goto exit_write_header;
-		ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)todo));
+		/* Pad to multiple of 512 bytes. */
+		ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));
 		if (ret < ARCHIVE_WARN)
 			goto exit_write_header;
 	}
diff --git a/libarchive/archive_write_set_format_iso9660.c b/libarchive/archive_write_set_format_iso9660.c
index 4d832fb..cb3e54e 100644
--- a/libarchive/archive_write_set_format_iso9660.c
+++ b/libarchive/archive_write_set_format_iso9660.c
@@ -6225,7 +6225,7 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
 	unsigned char *p;
 	size_t l;
 	int r;
-	int ffmax, parent_len;
+	size_t ffmax, parent_len;
 	static const struct archive_rb_tree_ops rb_ops = {
 		isoent_cmp_node_joliet, isoent_cmp_key_joliet
 	};
@@ -6239,7 +6239,7 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
 	else
 		ffmax = 128;
 
-	r = idr_start(a, idr, isoent->children.cnt, ffmax, 6, 2, &rb_ops);
+	r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops);
 	if (r < 0)
 		return (r);
 
@@ -6252,7 +6252,7 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
 		int ext_off, noff, weight;
 		size_t lt;
 
-		if ((int)(l = np->file->basename_utf16.length) > ffmax)
+		if ((l = np->file->basename_utf16.length) > ffmax)
 			l = ffmax;
 
 		p = malloc((l+1)*2);
@@ -6285,7 +6285,7 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
 		/*
 		 * Get a length of MBS of a full-pathname.
 		 */
-		if ((int)np->file->basename_utf16.length > ffmax) {
+		if (np->file->basename_utf16.length > ffmax) {
 			if (archive_strncpy_l(&iso9660->mbs,
 			    (const char *)np->identifier, l,
 				iso9660->sconv_from_utf16be) != 0 &&
@@ -6302,7 +6302,9 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
 
 		/* If a length of full-pathname is longer than 240 bytes,
 		 * it violates Joliet extensions regulation. */
-		if (parent_len + np->mb_len > 240) {
+		if (parent_len > 240
+		    || np->mb_len > 240
+		    || parent_len + np->mb_len > 240) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "The regulation of Joliet extensions;"
 			    " A length of a full-pathname of `%s' is "
@@ -6314,11 +6316,11 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
 
 		/* Make an offset of the number which is used to be set
 		 * hexadecimal number to avoid duplicate identifier. */
-		if ((int)l == ffmax)
+		if (l == ffmax)
 			noff = ext_off - 6;
-		else if ((int)l == ffmax-2)
+		else if (l == ffmax-2)
 			noff = ext_off - 4;
-		else if ((int)l == ffmax-4)
+		else if (l == ffmax-4)
 			noff = ext_off - 2;
 		else
 			noff = ext_off;
diff --git a/libarchive/archive_write_set_options.3 b/libarchive/archive_write_set_options.3
index ce7ed89..aeb7a18 100644
--- a/libarchive/archive_write_set_options.3
+++ b/libarchive/archive_write_set_options.3
@@ -32,7 +32,7 @@
 .Nm archive_write_set_format_option ,
 .Nm archive_write_set_option ,
 .Nm archive_write_set_options
-.Nd functions controlling options for reading archives
+.Nd functions controlling options for writing archives
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
diff --git a/libarchive/libarchive-formats.5 b/libarchive/libarchive-formats.5
index e619fe5..9cec760 100644
--- a/libarchive/libarchive-formats.5
+++ b/libarchive/libarchive-formats.5
@@ -65,7 +65,6 @@ Later variants have extended this by either appropriating undefined
 areas of the header record, extending the header to multiple records,
 or by storing special entries that modify the interpretation of
 subsequent entries.
-.Pp
 .Bl -tag -width indent
 .It Cm gnutar
 The
diff --git a/libarchive/libarchive_changes.3 b/libarchive/libarchive_changes.3
index bacd6e1..881a67c 100644
--- a/libarchive/libarchive_changes.3
+++ b/libarchive/libarchive_changes.3
@@ -28,7 +28,7 @@
 .Dt LIBARCHIVE_CHANGES 3
 .Os
 .Sh NAME
-.Nm changes in libarchive interface
+.Nd changes in libarchive interface
 .\"
 .Sh CHANGES IN LIBARCHIVE 3
 This page describes user-visible changes in libarchive3, and lists
diff --git a/libarchive/xxhash.c b/libarchive/xxhash.c
index 7a55fc8..d7f8e96 100644
--- a/libarchive/xxhash.c
+++ b/libarchive/xxhash.c
@@ -57,7 +57,7 @@ You can contact the author at :
 ** #define XXH_ACCEPT_NULL_INPUT_POINTER 1
 
 ** XXH_FORCE_NATIVE_FORMAT :
-** By default, xxHash library provides endian-independant Hash values, based on little-endian convention.
+** By default, xxHash library provides endian-independent Hash values, based on little-endian convention.
 ** Results are therefore identical for little-endian and big-endian CPU.
 ** This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
 ** Should endian-independance be of no importance for your application, you may set the #define below to 1.

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

Summary of changes:
 Utilities/cmlibarchive/build/version               |    2 +-
 Utilities/cmlibarchive/libarchive/archive.h        |    4 ++--
 Utilities/cmlibarchive/libarchive/archive_entry.h  |    2 +-
 .../cmlibarchive/libarchive/archive_entry_xattr.c  |    9 ++------
 Utilities/cmlibarchive/libarchive/archive_ppmd7.c  |    5 +++++
 .../libarchive/archive_read_support_format_7zip.c  |    3 +++
 .../libarchive/archive_read_support_format_cpio.c  |    5 +++++
 .../archive_read_support_format_iso9660.c          |    4 ++--
 .../libarchive/archive_read_support_format_mtree.c |    6 +++---
 .../libarchive/archive_read_support_format_rar.c   |   18 ++++++++++++----
 .../libarchive/archive_read_support_format_tar.c   |    6 +++---
 .../libarchive/archive_read_support_format_zip.c   |   13 ++++++++++--
 .../libarchive/archive_write_disk_windows.c        |   14 ++++++++++---
 .../cmlibarchive/libarchive/archive_write_filter.3 |    1 +
 .../libarchive/archive_write_set_format_gnutar.c   |   22 +++++++++++---------
 .../libarchive/archive_write_set_format_iso9660.c  |   18 +++++++++-------
 .../libarchive/archive_write_set_options.3         |    2 +-
 .../cmlibarchive/libarchive/libarchive-formats.5   |    1 -
 .../cmlibarchive/libarchive/libarchive_changes.3   |    2 +-
 19 files changed, 88 insertions(+), 49 deletions(-)


hooks/post-receive
-- 
CMake


More information about the Cmake-commits mailing list