packages feed

libarchive 2.2.5.0 → 2.2.5.1

raw patch · 176 files changed

+130481/−19 lines, 176 filesdep +unbuildabledep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: unbuildable

Dependency ranges changed: base

API changes (from Hackage documentation)

- Codec.Archive.Foreign.Archive: type LaInt64 = (CLLong)
+ Codec.Archive.Foreign.Archive: type LaInt64 = (CLong)

Files

CHANGELOG.md view
@@ -1,5 +1,10 @@ # libarchive +## 2.2.5.1++  * Add `static` flag to cabal which builds from bundled sources rather than+    `pkg-config`+ ## 2.2.5.0    * Speed improvements in places
README.md view
@@ -21,7 +21,7 @@ make ``` -so that you have appropriate test data downloaded.+to download test data.  ## Performance 
+ c/archive.h view
@@ -0,0 +1,1197 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.50 2008/05/26 17:00:22 kientzle Exp $+ */++#ifndef ARCHIVE_H_INCLUDED+#define	ARCHIVE_H_INCLUDED++/*+ * The version number is expressed as a single integer that makes it+ * easy to compare versions at build time: for version a.b.c, the+ * version number is printf("%d%03d%03d",a,b,c).  For example, if you+ * know your application requires version 2.12.108 or later, you can+ * assert that ARCHIVE_VERSION_NUMBER >= 2012108.+ */+/* Note: Compiler will complain if this does not match archive_entry.h! */+#define	ARCHIVE_VERSION_NUMBER 3004003++#include <sys/stat.h>+#include <stddef.h>  /* for wchar_t */+#include <stdio.h> /* For FILE * */+#include <time.h> /* For time_t */++/*+ * Note: archive.h is for use outside of libarchive; the configuration+ * headers (config.h, archive_platform.h, etc.) are purely internal.+ * Do NOT use HAVE_XXX configuration macros to control the behavior of+ * this header!  If you must conditionalize, use predefined compiler and/or+ * platform macros.+ */+#if defined(__BORLANDC__) && __BORLANDC__ >= 0x560+# include <stdint.h>+#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__) && !defined(__CLANG_INTTYPES_H)+# include <inttypes.h>+#endif++/* Get appropriate definitions of 64-bit integer */+#if !defined(__LA_INT64_T_DEFINED)+/* Older code relied on the __LA_INT64_T macro; after 4.0 we'll switch to the typedef exclusively. */+# if ARCHIVE_VERSION_NUMBER < 4000000+#define __LA_INT64_T la_int64_t+# endif+#define __LA_INT64_T_DEFINED+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)+typedef __int64 la_int64_t;+# else+# include <unistd.h>  /* ssize_t */+#  if defined(_SCO_DS) || defined(__osf__)+typedef long long la_int64_t;+#  else+typedef int64_t la_int64_t;+#  endif+# endif+#endif++/* The la_ssize_t should match the type used in 'struct stat' */+#if !defined(__LA_SSIZE_T_DEFINED)+/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */+# if ARCHIVE_VERSION_NUMBER < 4000000+#define __LA_SSIZE_T la_ssize_t+# endif+#define __LA_SSIZE_T_DEFINED+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)+#  if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)+typedef ssize_t la_ssize_t;+#  elif defined(_WIN64)+typedef __int64 la_ssize_t;+#  else+typedef long la_ssize_t;+#  endif+# else+# include <unistd.h>  /* ssize_t */+typedef ssize_t la_ssize_t;+# endif+#endif++/* Large file support for Android */+#ifdef __ANDROID__+#include "android_lf.h"+#endif++/*+ * On Windows, define LIBARCHIVE_STATIC if you're building or using a+ * .lib.  The default here assumes you're building a DLL.  Only+ * libarchive source should ever define __LIBARCHIVE_BUILD.+ */+#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)+# ifdef __LIBARCHIVE_BUILD+#  ifdef __GNUC__+#   define __LA_DECL	__attribute__((dllexport)) extern+#  else+#   define __LA_DECL	__declspec(dllexport)+#  endif+# else+#  ifdef __GNUC__+#   define __LA_DECL+#  else+#   define __LA_DECL	__declspec(dllimport)+#  endif+# endif+#else+/* Static libraries or non-Windows needs no special declaration. */+# define __LA_DECL+#endif++#if defined(__GNUC__) && __GNUC__ >= 3 && !defined(__MINGW32__)+#define	__LA_PRINTF(fmtarg, firstvararg) \+	__attribute__((__format__ (__printf__, fmtarg, firstvararg)))+#else+#define	__LA_PRINTF(fmtarg, firstvararg)	/* nothing */+#endif++#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1+# define __LA_DEPRECATED __attribute__((deprecated))+#else+# define __LA_DEPRECATED+#endif++#ifdef __cplusplus+extern "C" {+#endif++/*+ * The version number is provided as both a macro and a function.+ * The macro identifies the installed header; the function identifies+ * the library version (which may not be the same if you're using a+ * dynamically-linked version of the library).  Of course, if the+ * header and library are very different, you should expect some+ * strangeness.  Don't do that.+ */+__LA_DECL int		archive_version_number(void);++/*+ * Textual name/version of the library, useful for version displays.+ */+#define	ARCHIVE_VERSION_ONLY_STRING "3.4.3"+#define	ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING+__LA_DECL const char *	archive_version_string(void);++/*+ * Detailed textual name/version of the library and its dependencies.+ * This has the form:+ *    "libarchive x.y.z zlib/a.b.c liblzma/d.e.f ... etc ..."+ * the list of libraries described here will vary depending on how+ * libarchive was compiled.+ */+__LA_DECL const char *	archive_version_details(void);++/*+ * Returns NULL if libarchive was compiled without the associated library.+ * Otherwise, returns the version number that libarchive was compiled+ * against.+ */+__LA_DECL const char *  archive_zlib_version(void);+__LA_DECL const char *  archive_liblzma_version(void);+__LA_DECL const char *  archive_bzlib_version(void);+__LA_DECL const char *  archive_liblz4_version(void);+__LA_DECL const char *  archive_libzstd_version(void);++/* Declare our basic types. */+struct archive;+struct archive_entry;++/*+ * Error codes: Use archive_errno() and archive_error_string()+ * to retrieve details.  Unless specified otherwise, all functions+ * that return 'int' use these codes.+ */+#define	ARCHIVE_EOF	  1	/* Found end of archive. */+#define	ARCHIVE_OK	  0	/* Operation was successful. */+#define	ARCHIVE_RETRY	(-10)	/* Retry might succeed. */+#define	ARCHIVE_WARN	(-20)	/* Partial success. */+/* For example, if write_header "fails", then you can't push data. */+#define	ARCHIVE_FAILED	(-25)	/* Current operation cannot complete. */+/* But if write_header is "fatal," then this archive is dead and useless. */+#define	ARCHIVE_FATAL	(-30)	/* No more operations are possible. */++/*+ * As far as possible, archive_errno returns standard platform errno codes.+ * Of course, the details vary by platform, so the actual definitions+ * here are stored in "archive_platform.h".  The symbols are listed here+ * for reference; as a rule, clients should not need to know the exact+ * platform-dependent error code.+ */+/* Unrecognized or invalid file format. */+/* #define	ARCHIVE_ERRNO_FILE_FORMAT */+/* Illegal usage of the library. */+/* #define	ARCHIVE_ERRNO_PROGRAMMER_ERROR */+/* Unknown or unclassified error. */+/* #define	ARCHIVE_ERRNO_MISC */++/*+ * Callbacks are invoked to automatically read/skip/write/open/close the+ * archive. You can provide your own for complex tasks (like breaking+ * archives across multiple tapes) or use standard ones built into the+ * library.+ */++/* Returns pointer and size of next block of data from archive. */+typedef la_ssize_t	archive_read_callback(struct archive *,+			    void *_client_data, const void **_buffer);++/* Skips at most request bytes from archive and returns the skipped amount.+ * This may skip fewer bytes than requested; it may even skip zero bytes.+ * If you do skip fewer bytes than requested, libarchive will invoke your+ * read callback and discard data as necessary to make up the full skip.+ */+typedef la_int64_t	archive_skip_callback(struct archive *,+			    void *_client_data, la_int64_t request);++/* Seeks to specified location in the file and returns the position.+ * Whence values are SEEK_SET, SEEK_CUR, SEEK_END from stdio.h.+ * Return ARCHIVE_FATAL if the seek fails for any reason.+ */+typedef la_int64_t	archive_seek_callback(struct archive *,+    void *_client_data, la_int64_t offset, int whence);++/* Returns size actually written, zero on EOF, -1 on error. */+typedef la_ssize_t	archive_write_callback(struct archive *,+			    void *_client_data,+			    const void *_buffer, size_t _length);++typedef int	archive_open_callback(struct archive *, void *_client_data);++typedef int	archive_close_callback(struct archive *, void *_client_data);++/* Switches from one client data object to the next/prev client data object.+ * This is useful for reading from different data blocks such as a set of files+ * that make up one large file.+ */+typedef int archive_switch_callback(struct archive *, void *_client_data1,+			    void *_client_data2);++/*+ * Returns a passphrase used for encryption or decryption, NULL on nothing+ * to do and give it up.+ */+typedef const char *archive_passphrase_callback(struct archive *,+			    void *_client_data);++/*+ * Codes to identify various stream filters.+ */+#define	ARCHIVE_FILTER_NONE	0+#define	ARCHIVE_FILTER_GZIP	1+#define	ARCHIVE_FILTER_BZIP2	2+#define	ARCHIVE_FILTER_COMPRESS	3+#define	ARCHIVE_FILTER_PROGRAM	4+#define	ARCHIVE_FILTER_LZMA	5+#define	ARCHIVE_FILTER_XZ	6+#define	ARCHIVE_FILTER_UU	7+#define	ARCHIVE_FILTER_RPM	8+#define	ARCHIVE_FILTER_LZIP	9+#define	ARCHIVE_FILTER_LRZIP	10+#define	ARCHIVE_FILTER_LZOP	11+#define	ARCHIVE_FILTER_GRZIP	12+#define	ARCHIVE_FILTER_LZ4	13+#define	ARCHIVE_FILTER_ZSTD	14++#if ARCHIVE_VERSION_NUMBER < 4000000+#define	ARCHIVE_COMPRESSION_NONE	ARCHIVE_FILTER_NONE+#define	ARCHIVE_COMPRESSION_GZIP	ARCHIVE_FILTER_GZIP+#define	ARCHIVE_COMPRESSION_BZIP2	ARCHIVE_FILTER_BZIP2+#define	ARCHIVE_COMPRESSION_COMPRESS	ARCHIVE_FILTER_COMPRESS+#define	ARCHIVE_COMPRESSION_PROGRAM	ARCHIVE_FILTER_PROGRAM+#define	ARCHIVE_COMPRESSION_LZMA	ARCHIVE_FILTER_LZMA+#define	ARCHIVE_COMPRESSION_XZ		ARCHIVE_FILTER_XZ+#define	ARCHIVE_COMPRESSION_UU		ARCHIVE_FILTER_UU+#define	ARCHIVE_COMPRESSION_RPM		ARCHIVE_FILTER_RPM+#define	ARCHIVE_COMPRESSION_LZIP	ARCHIVE_FILTER_LZIP+#define	ARCHIVE_COMPRESSION_LRZIP	ARCHIVE_FILTER_LRZIP+#endif++/*+ * Codes returned by archive_format.+ *+ * Top 16 bits identifies the format family (e.g., "tar"); lower+ * 16 bits indicate the variant.  This is updated by read_next_header.+ * Note that the lower 16 bits will often vary from entry to entry.+ * In some cases, this variation occurs as libarchive learns more about+ * the archive (for example, later entries might utilize extensions that+ * weren't necessary earlier in the archive; in this case, libarchive+ * will change the format code to indicate the extended format that+ * was used).  In other cases, it's because different tools have+ * modified the archive and so different parts of the archive+ * actually have slightly different formats.  (Both tar and cpio store+ * format codes in each entry, so it is quite possible for each+ * entry to be in a different format.)+ */+#define	ARCHIVE_FORMAT_BASE_MASK		0xff0000+#define	ARCHIVE_FORMAT_CPIO			0x10000+#define	ARCHIVE_FORMAT_CPIO_POSIX		(ARCHIVE_FORMAT_CPIO | 1)+#define	ARCHIVE_FORMAT_CPIO_BIN_LE		(ARCHIVE_FORMAT_CPIO | 2)+#define	ARCHIVE_FORMAT_CPIO_BIN_BE		(ARCHIVE_FORMAT_CPIO | 3)+#define	ARCHIVE_FORMAT_CPIO_SVR4_NOCRC		(ARCHIVE_FORMAT_CPIO | 4)+#define	ARCHIVE_FORMAT_CPIO_SVR4_CRC		(ARCHIVE_FORMAT_CPIO | 5)+#define	ARCHIVE_FORMAT_CPIO_AFIO_LARGE		(ARCHIVE_FORMAT_CPIO | 6)+#define	ARCHIVE_FORMAT_SHAR			0x20000+#define	ARCHIVE_FORMAT_SHAR_BASE		(ARCHIVE_FORMAT_SHAR | 1)+#define	ARCHIVE_FORMAT_SHAR_DUMP		(ARCHIVE_FORMAT_SHAR | 2)+#define	ARCHIVE_FORMAT_TAR			0x30000+#define	ARCHIVE_FORMAT_TAR_USTAR		(ARCHIVE_FORMAT_TAR | 1)+#define	ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE	(ARCHIVE_FORMAT_TAR | 2)+#define	ARCHIVE_FORMAT_TAR_PAX_RESTRICTED	(ARCHIVE_FORMAT_TAR | 3)+#define	ARCHIVE_FORMAT_TAR_GNUTAR		(ARCHIVE_FORMAT_TAR | 4)+#define	ARCHIVE_FORMAT_ISO9660			0x40000+#define	ARCHIVE_FORMAT_ISO9660_ROCKRIDGE	(ARCHIVE_FORMAT_ISO9660 | 1)+#define	ARCHIVE_FORMAT_ZIP			0x50000+#define	ARCHIVE_FORMAT_EMPTY			0x60000+#define	ARCHIVE_FORMAT_AR			0x70000+#define	ARCHIVE_FORMAT_AR_GNU			(ARCHIVE_FORMAT_AR | 1)+#define	ARCHIVE_FORMAT_AR_BSD			(ARCHIVE_FORMAT_AR | 2)+#define	ARCHIVE_FORMAT_MTREE			0x80000+#define	ARCHIVE_FORMAT_RAW			0x90000+#define	ARCHIVE_FORMAT_XAR			0xA0000+#define	ARCHIVE_FORMAT_LHA			0xB0000+#define	ARCHIVE_FORMAT_CAB			0xC0000+#define	ARCHIVE_FORMAT_RAR			0xD0000+#define	ARCHIVE_FORMAT_7ZIP			0xE0000+#define	ARCHIVE_FORMAT_WARC			0xF0000+#define	ARCHIVE_FORMAT_RAR_V5			0x100000++/*+ * Codes returned by archive_read_format_capabilities().+ *+ * This list can be extended with values between 0 and 0xffff.+ * The original purpose of this list was to let different archive+ * format readers expose their general capabilities in terms of+ * encryption.+ */+#define ARCHIVE_READ_FORMAT_CAPS_NONE (0) /* no special capabilities */+#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA (1<<0)  /* reader can detect encrypted data */+#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA (1<<1)  /* reader can detect encryptable metadata (pathname, mtime, etc.) */++/*+ * Codes returned by archive_read_has_encrypted_entries().+ *+ * In case the archive does not support encryption detection at all+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. If the reader+ * for some other reason (e.g. not enough bytes read) cannot say if+ * there are encrypted entries, ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW+ * is returned.+ */+#define ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED -2+#define ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW -1++/*-+ * Basic outline for reading an archive:+ *   1) Ask archive_read_new for an archive reader object.+ *   2) Update any global properties as appropriate.+ *      In particular, you'll certainly want to call appropriate+ *      archive_read_support_XXX functions.+ *   3) Call archive_read_open_XXX to open the archive+ *   4) Repeatedly call archive_read_next_header to get information about+ *      successive archive entries.  Call archive_read_data to extract+ *      data for entries of interest.+ *   5) Call archive_read_free to end processing.+ */+__LA_DECL struct archive	*archive_read_new(void);++/*+ * The archive_read_support_XXX calls enable auto-detect for this+ * archive handle.  They also link in the necessary support code.+ * For example, if you don't want bzlib linked in, don't invoke+ * support_compression_bzip2().  The "all" functions provide the+ * obvious shorthand.+ */++#if ARCHIVE_VERSION_NUMBER < 4000000+__LA_DECL int archive_read_support_compression_all(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_read_support_compression_bzip2(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_read_support_compression_compress(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_read_support_compression_gzip(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_read_support_compression_lzip(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_read_support_compression_lzma(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_read_support_compression_none(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_read_support_compression_program(struct archive *,+		     const char *command) __LA_DEPRECATED;+__LA_DECL int archive_read_support_compression_program_signature+		(struct archive *, const char *,+		 const void * /* match */, size_t) __LA_DEPRECATED;++__LA_DECL int archive_read_support_compression_rpm(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_read_support_compression_uu(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_read_support_compression_xz(struct archive *)+		__LA_DEPRECATED;+#endif++__LA_DECL int archive_read_support_filter_all(struct archive *);+__LA_DECL int archive_read_support_filter_bzip2(struct archive *);+__LA_DECL int archive_read_support_filter_compress(struct archive *);+__LA_DECL int archive_read_support_filter_gzip(struct archive *);+__LA_DECL int archive_read_support_filter_grzip(struct archive *);+__LA_DECL int archive_read_support_filter_lrzip(struct archive *);+__LA_DECL int archive_read_support_filter_lz4(struct archive *);+__LA_DECL int archive_read_support_filter_lzip(struct archive *);+__LA_DECL int archive_read_support_filter_lzma(struct archive *);+__LA_DECL int archive_read_support_filter_lzop(struct archive *);+__LA_DECL int archive_read_support_filter_none(struct archive *);+__LA_DECL int archive_read_support_filter_program(struct archive *,+		     const char *command);+__LA_DECL int archive_read_support_filter_program_signature+		(struct archive *, const char * /* cmd */,+				    const void * /* match */, size_t);+__LA_DECL int archive_read_support_filter_rpm(struct archive *);+__LA_DECL int archive_read_support_filter_uu(struct archive *);+__LA_DECL int archive_read_support_filter_xz(struct archive *);+__LA_DECL int archive_read_support_filter_zstd(struct archive *);++__LA_DECL int archive_read_support_format_7zip(struct archive *);+__LA_DECL int archive_read_support_format_all(struct archive *);+__LA_DECL int archive_read_support_format_ar(struct archive *);+__LA_DECL int archive_read_support_format_by_code(struct archive *, int);+__LA_DECL int archive_read_support_format_cab(struct archive *);+__LA_DECL int archive_read_support_format_cpio(struct archive *);+__LA_DECL int archive_read_support_format_empty(struct archive *);+__LA_DECL int archive_read_support_format_gnutar(struct archive *);+__LA_DECL int archive_read_support_format_iso9660(struct archive *);+__LA_DECL int archive_read_support_format_lha(struct archive *);+__LA_DECL int archive_read_support_format_mtree(struct archive *);+__LA_DECL int archive_read_support_format_rar(struct archive *);+__LA_DECL int archive_read_support_format_rar5(struct archive *);+__LA_DECL int archive_read_support_format_raw(struct archive *);+__LA_DECL int archive_read_support_format_tar(struct archive *);+__LA_DECL int archive_read_support_format_warc(struct archive *);+__LA_DECL int archive_read_support_format_xar(struct archive *);+/* archive_read_support_format_zip() enables both streamable and seekable+ * zip readers. */+__LA_DECL int archive_read_support_format_zip(struct archive *);+/* Reads Zip archives as stream from beginning to end.  Doesn't+ * correctly handle SFX ZIP files or ZIP archives that have been modified+ * in-place. */+__LA_DECL int archive_read_support_format_zip_streamable(struct archive *);+/* Reads starting from central directory; requires seekable input. */+__LA_DECL int archive_read_support_format_zip_seekable(struct archive *);++/* Functions to manually set the format and filters to be used. This is+ * useful to bypass the bidding process when the format and filters to use+ * is known in advance.+ */+__LA_DECL int archive_read_set_format(struct archive *, int);+__LA_DECL int archive_read_append_filter(struct archive *, int);+__LA_DECL int archive_read_append_filter_program(struct archive *,+    const char *);+__LA_DECL int archive_read_append_filter_program_signature+    (struct archive *, const char *, const void * /* match */, size_t);++/* Set various callbacks. */+__LA_DECL int archive_read_set_open_callback(struct archive *,+    archive_open_callback *);+__LA_DECL int archive_read_set_read_callback(struct archive *,+    archive_read_callback *);+__LA_DECL int archive_read_set_seek_callback(struct archive *,+    archive_seek_callback *);+__LA_DECL int archive_read_set_skip_callback(struct archive *,+    archive_skip_callback *);+__LA_DECL int archive_read_set_close_callback(struct archive *,+    archive_close_callback *);+/* Callback used to switch between one data object to the next */+__LA_DECL int archive_read_set_switch_callback(struct archive *,+    archive_switch_callback *);++/* This sets the first data object. */+__LA_DECL int archive_read_set_callback_data(struct archive *, void *);+/* This sets data object at specified index */+__LA_DECL int archive_read_set_callback_data2(struct archive *, void *,+    unsigned int);+/* This adds a data object at the specified index. */+__LA_DECL int archive_read_add_callback_data(struct archive *, void *,+    unsigned int);+/* This appends a data object to the end of list */+__LA_DECL int archive_read_append_callback_data(struct archive *, void *);+/* This prepends a data object to the beginning of list */+__LA_DECL int archive_read_prepend_callback_data(struct archive *, void *);++/* Opening freezes the callbacks. */+__LA_DECL int archive_read_open1(struct archive *);++/* Convenience wrappers around the above. */+__LA_DECL int archive_read_open(struct archive *, void *_client_data,+		     archive_open_callback *, archive_read_callback *,+		     archive_close_callback *);+__LA_DECL int archive_read_open2(struct archive *, void *_client_data,+		     archive_open_callback *, archive_read_callback *,+		     archive_skip_callback *, archive_close_callback *);++/*+ * A variety of shortcuts that invoke archive_read_open() with+ * canned callbacks suitable for common situations.  The ones that+ * accept a block size handle tape blocking correctly.+ */+/* Use this if you know the filename.  Note: NULL indicates stdin. */+__LA_DECL int archive_read_open_filename(struct archive *,+		     const char *_filename, size_t _block_size);+/* Use this for reading multivolume files by filenames.+ * NOTE: Must be NULL terminated. Sorting is NOT done. */+__LA_DECL int archive_read_open_filenames(struct archive *,+		     const char **_filenames, size_t _block_size);+__LA_DECL int archive_read_open_filename_w(struct archive *,+		     const wchar_t *_filename, size_t _block_size);+/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */+__LA_DECL int archive_read_open_file(struct archive *,+		     const char *_filename, size_t _block_size) __LA_DEPRECATED;+/* Read an archive that's stored in memory. */+__LA_DECL int archive_read_open_memory(struct archive *,+		     const void * buff, size_t size);+/* A more involved version that is only used for internal testing. */+__LA_DECL int archive_read_open_memory2(struct archive *a, const void *buff,+		     size_t size, size_t read_size);+/* Read an archive that's already open, using the file descriptor. */+__LA_DECL int archive_read_open_fd(struct archive *, int _fd,+		     size_t _block_size);+/* Read an archive that's already open, using a FILE *. */+/* Note: DO NOT use this with tape drives. */+__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file);++/* Parses and returns next entry header. */+__LA_DECL int archive_read_next_header(struct archive *,+		     struct archive_entry **);++/* Parses and returns next entry header using the archive_entry passed in */+__LA_DECL int archive_read_next_header2(struct archive *,+		     struct archive_entry *);++/*+ * Retrieve the byte offset in UNCOMPRESSED data where last-read+ * header started.+ */+__LA_DECL la_int64_t		 archive_read_header_position(struct archive *);++/*+ * Returns 1 if the archive contains at least one encrypted entry.+ * If the archive format not support encryption at all+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.+ * If for any other reason (e.g. not enough data read so far)+ * we cannot say whether there are encrypted entries, then+ * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.+ * In general, this function will return values below zero when the+ * reader is uncertain or totally incapable of encryption support.+ * When this function returns 0 you can be sure that the reader+ * supports encryption detection but no encrypted entries have+ * been found yet.+ *+ * NOTE: If the metadata/header of an archive is also encrypted, you+ * cannot rely on the number of encrypted entries. That is why this+ * function does not return the number of encrypted entries but#+ * just shows that there are some.+ */+__LA_DECL int	archive_read_has_encrypted_entries(struct archive *);++/*+ * Returns a bitmask of capabilities that are supported by the archive format reader.+ * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned.+ */+__LA_DECL int		 archive_read_format_capabilities(struct archive *);++/* Read data from the body of an entry.  Similar to read(2). */+__LA_DECL la_ssize_t		 archive_read_data(struct archive *,+				    void *, size_t);++/* Seek within the body of an entry.  Similar to lseek(2). */+__LA_DECL la_int64_t archive_seek_data(struct archive *, la_int64_t, int);++/*+ * A zero-copy version of archive_read_data that also exposes the file offset+ * of each returned block.  Note that the client has no way to specify+ * the desired size of the block.  The API does guarantee that offsets will+ * be strictly increasing and that returned blocks will not overlap.+ */+__LA_DECL int archive_read_data_block(struct archive *a,+		    const void **buff, size_t *size, la_int64_t *offset);++/*-+ * Some convenience functions that are built on archive_read_data:+ *  'skip': skips entire entry+ *  'into_buffer': writes data into memory buffer that you provide+ *  'into_fd': writes data to specified filedes+ */+__LA_DECL int archive_read_data_skip(struct archive *);+__LA_DECL int archive_read_data_into_fd(struct archive *, int fd);++/*+ * Set read options.+ */+/* Apply option to the format only. */+__LA_DECL int archive_read_set_format_option(struct archive *_a,+			    const char *m, const char *o,+			    const char *v);+/* Apply option to the filter only. */+__LA_DECL int archive_read_set_filter_option(struct archive *_a,+			    const char *m, const char *o,+			    const char *v);+/* Apply option to both the format and the filter. */+__LA_DECL int archive_read_set_option(struct archive *_a,+			    const char *m, const char *o,+			    const char *v);+/* Apply option string to both the format and the filter. */+__LA_DECL int archive_read_set_options(struct archive *_a,+			    const char *opts);++/*+ * Add a decryption passphrase.+ */+__LA_DECL int archive_read_add_passphrase(struct archive *, const char *);+__LA_DECL int archive_read_set_passphrase_callback(struct archive *,+			    void *client_data, archive_passphrase_callback *);+++/*-+ * Convenience function to recreate the current entry (whose header+ * has just been read) on disk.+ *+ * This does quite a bit more than just copy data to disk. It also:+ *  - Creates intermediate directories as required.+ *  - Manages directory permissions:  non-writable directories will+ *    be initially created with write permission enabled; when the+ *    archive is closed, dir permissions are edited to the values specified+ *    in the archive.+ *  - Checks hardlinks:  hardlinks will not be extracted unless the+ *    linked-to file was also extracted within the same session. (TODO)+ */++/* The "flags" argument selects optional behavior, 'OR' the flags you want. */++/* Default: Do not try to set owner/group. */+#define	ARCHIVE_EXTRACT_OWNER			(0x0001)+/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */+#define	ARCHIVE_EXTRACT_PERM			(0x0002)+/* Default: Do not restore mtime/atime. */+#define	ARCHIVE_EXTRACT_TIME			(0x0004)+/* Default: Replace existing files. */+#define	ARCHIVE_EXTRACT_NO_OVERWRITE 		(0x0008)+/* Default: Try create first, unlink only if create fails with EEXIST. */+#define	ARCHIVE_EXTRACT_UNLINK			(0x0010)+/* Default: Do not restore ACLs. */+#define	ARCHIVE_EXTRACT_ACL			(0x0020)+/* Default: Do not restore fflags. */+#define	ARCHIVE_EXTRACT_FFLAGS			(0x0040)+/* Default: Do not restore xattrs. */+#define	ARCHIVE_EXTRACT_XATTR 			(0x0080)+/* Default: Do not try to guard against extracts redirected by symlinks. */+/* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */+#define	ARCHIVE_EXTRACT_SECURE_SYMLINKS		(0x0100)+/* Default: Do not reject entries with '..' as path elements. */+#define	ARCHIVE_EXTRACT_SECURE_NODOTDOT		(0x0200)+/* Default: Create parent directories as needed. */+#define	ARCHIVE_EXTRACT_NO_AUTODIR		(0x0400)+/* Default: Overwrite files, even if one on disk is newer. */+#define	ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER	(0x0800)+/* Detect blocks of 0 and write holes instead. */+#define	ARCHIVE_EXTRACT_SPARSE			(0x1000)+/* Default: Do not restore Mac extended metadata. */+/* This has no effect except on Mac OS. */+#define	ARCHIVE_EXTRACT_MAC_METADATA		(0x2000)+/* Default: Use HFS+ compression if it was compressed. */+/* This has no effect except on Mac OS v10.6 or later. */+#define	ARCHIVE_EXTRACT_NO_HFS_COMPRESSION	(0x4000)+/* Default: Do not use HFS+ compression if it was not compressed. */+/* This has no effect except on Mac OS v10.6 or later. */+#define	ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED	(0x8000)+/* Default: Do not reject entries with absolute paths */+#define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000)+/* Default: Do not clear no-change flags when unlinking object */+#define	ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS	(0x20000)+/* Default: Do not extract atomically (using rename) */+#define	ARCHIVE_EXTRACT_SAFE_WRITES		(0x40000)++__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,+		     int flags);+__LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *,+		     struct archive * /* dest */);+__LA_DECL void	 archive_read_extract_set_progress_callback(struct archive *,+		     void (*_progress_func)(void *), void *_user_data);++/* Record the dev/ino of a file that will not be written.  This is+ * generally set to the dev/ino of the archive being read. */+__LA_DECL void		archive_read_extract_set_skip_file(struct archive *,+		     la_int64_t, la_int64_t);++/* Close the file and release most resources. */+__LA_DECL int		 archive_read_close(struct archive *);+/* Release all resources and destroy the object. */+/* Note that archive_read_free will call archive_read_close for you. */+__LA_DECL int		 archive_read_free(struct archive *);+#if ARCHIVE_VERSION_NUMBER < 4000000+/* Synonym for archive_read_free() for backwards compatibility. */+__LA_DECL int		 archive_read_finish(struct archive *) __LA_DEPRECATED;+#endif++/*-+ * To create an archive:+ *   1) Ask archive_write_new for an archive writer object.+ *   2) Set any global properties.  In particular, you should set+ *      the compression and format to use.+ *   3) Call archive_write_open to open the file (most people+ *       will use archive_write_open_file or archive_write_open_fd,+ *       which provide convenient canned I/O callbacks for you).+ *   4) For each entry:+ *      - construct an appropriate struct archive_entry structure+ *      - archive_write_header to write the header+ *      - archive_write_data to write the entry data+ *   5) archive_write_close to close the output+ *   6) archive_write_free to cleanup the writer and release resources+ */+__LA_DECL struct archive	*archive_write_new(void);+__LA_DECL int archive_write_set_bytes_per_block(struct archive *,+		     int bytes_per_block);+__LA_DECL int archive_write_get_bytes_per_block(struct archive *);+/* XXX This is badly misnamed; suggestions appreciated. XXX */+__LA_DECL int archive_write_set_bytes_in_last_block(struct archive *,+		     int bytes_in_last_block);+__LA_DECL int archive_write_get_bytes_in_last_block(struct archive *);++/* The dev/ino of a file that won't be archived.  This is used+ * to avoid recursively adding an archive to itself. */+__LA_DECL int archive_write_set_skip_file(struct archive *,+    la_int64_t, la_int64_t);++#if ARCHIVE_VERSION_NUMBER < 4000000+__LA_DECL int archive_write_set_compression_bzip2(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_write_set_compression_compress(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_write_set_compression_gzip(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_write_set_compression_lzip(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_write_set_compression_lzma(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_write_set_compression_none(struct archive *)+		__LA_DEPRECATED;+__LA_DECL int archive_write_set_compression_program(struct archive *,+		     const char *cmd) __LA_DEPRECATED;+__LA_DECL int archive_write_set_compression_xz(struct archive *)+		__LA_DEPRECATED;+#endif++/* A convenience function to set the filter based on the code. */+__LA_DECL int archive_write_add_filter(struct archive *, int filter_code);+__LA_DECL int archive_write_add_filter_by_name(struct archive *,+		     const char *name);+__LA_DECL int archive_write_add_filter_b64encode(struct archive *);+__LA_DECL int archive_write_add_filter_bzip2(struct archive *);+__LA_DECL int archive_write_add_filter_compress(struct archive *);+__LA_DECL int archive_write_add_filter_grzip(struct archive *);+__LA_DECL int archive_write_add_filter_gzip(struct archive *);+__LA_DECL int archive_write_add_filter_lrzip(struct archive *);+__LA_DECL int archive_write_add_filter_lz4(struct archive *);+__LA_DECL int archive_write_add_filter_lzip(struct archive *);+__LA_DECL int archive_write_add_filter_lzma(struct archive *);+__LA_DECL int archive_write_add_filter_lzop(struct archive *);+__LA_DECL int archive_write_add_filter_none(struct archive *);+__LA_DECL int archive_write_add_filter_program(struct archive *,+		     const char *cmd);+__LA_DECL int archive_write_add_filter_uuencode(struct archive *);+__LA_DECL int archive_write_add_filter_xz(struct archive *);+__LA_DECL int archive_write_add_filter_zstd(struct archive *);+++/* A convenience function to set the format based on the code or name. */+__LA_DECL int archive_write_set_format(struct archive *, int format_code);+__LA_DECL int archive_write_set_format_by_name(struct archive *,+		     const char *name);+/* To minimize link pollution, use one or more of the following. */+__LA_DECL int archive_write_set_format_7zip(struct archive *);+__LA_DECL int archive_write_set_format_ar_bsd(struct archive *);+__LA_DECL int archive_write_set_format_ar_svr4(struct archive *);+__LA_DECL int archive_write_set_format_cpio(struct archive *);+__LA_DECL int archive_write_set_format_cpio_newc(struct archive *);+__LA_DECL int archive_write_set_format_gnutar(struct archive *);+__LA_DECL int archive_write_set_format_iso9660(struct archive *);+__LA_DECL int archive_write_set_format_mtree(struct archive *);+__LA_DECL int archive_write_set_format_mtree_classic(struct archive *);+/* TODO: int archive_write_set_format_old_tar(struct archive *); */+__LA_DECL int archive_write_set_format_pax(struct archive *);+__LA_DECL int archive_write_set_format_pax_restricted(struct archive *);+__LA_DECL int archive_write_set_format_raw(struct archive *);+__LA_DECL int archive_write_set_format_shar(struct archive *);+__LA_DECL int archive_write_set_format_shar_dump(struct archive *);+__LA_DECL int archive_write_set_format_ustar(struct archive *);+__LA_DECL int archive_write_set_format_v7tar(struct archive *);+__LA_DECL int archive_write_set_format_warc(struct archive *);+__LA_DECL int archive_write_set_format_xar(struct archive *);+__LA_DECL int archive_write_set_format_zip(struct archive *);+__LA_DECL int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename);+__LA_DECL int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext);+__LA_DECL int archive_write_zip_set_compression_deflate(struct archive *);+__LA_DECL int archive_write_zip_set_compression_store(struct archive *);+__LA_DECL int archive_write_open(struct archive *, void *,+		     archive_open_callback *, archive_write_callback *,+		     archive_close_callback *);+__LA_DECL int archive_write_open_fd(struct archive *, int _fd);+__LA_DECL int archive_write_open_filename(struct archive *, const char *_file);+__LA_DECL int archive_write_open_filename_w(struct archive *,+		     const wchar_t *_file);+/* A deprecated synonym for archive_write_open_filename() */+__LA_DECL int archive_write_open_file(struct archive *, const char *_file)+		__LA_DEPRECATED;+__LA_DECL int archive_write_open_FILE(struct archive *, FILE *);+/* _buffSize is the size of the buffer, _used refers to a variable that+ * will be updated after each write into the buffer. */+__LA_DECL int archive_write_open_memory(struct archive *,+			void *_buffer, size_t _buffSize, size_t *_used);++/*+ * Note that the library will truncate writes beyond the size provided+ * to archive_write_header or pad if the provided data is short.+ */+__LA_DECL int archive_write_header(struct archive *,+		     struct archive_entry *);+__LA_DECL la_ssize_t	archive_write_data(struct archive *,+			    const void *, size_t);++/* This interface is currently only available for archive_write_disk handles.  */+__LA_DECL la_ssize_t	 archive_write_data_block(struct archive *,+				    const void *, size_t, la_int64_t);++__LA_DECL int		 archive_write_finish_entry(struct archive *);+__LA_DECL int		 archive_write_close(struct archive *);+/* Marks the archive as FATAL so that a subsequent free() operation+ * won't try to close() cleanly.  Provides a fast abort capability+ * when the client discovers that things have gone wrong. */+__LA_DECL int            archive_write_fail(struct archive *);+/* This can fail if the archive wasn't already closed, in which case+ * archive_write_free() will implicitly call archive_write_close(). */+__LA_DECL int		 archive_write_free(struct archive *);+#if ARCHIVE_VERSION_NUMBER < 4000000+/* Synonym for archive_write_free() for backwards compatibility. */+__LA_DECL int		 archive_write_finish(struct archive *) __LA_DEPRECATED;+#endif++/*+ * Set write options.+ */+/* Apply option to the format only. */+__LA_DECL int archive_write_set_format_option(struct archive *_a,+			    const char *m, const char *o,+			    const char *v);+/* Apply option to the filter only. */+__LA_DECL int archive_write_set_filter_option(struct archive *_a,+			    const char *m, const char *o,+			    const char *v);+/* Apply option to both the format and the filter. */+__LA_DECL int archive_write_set_option(struct archive *_a,+			    const char *m, const char *o,+			    const char *v);+/* Apply option string to both the format and the filter. */+__LA_DECL int archive_write_set_options(struct archive *_a,+			    const char *opts);++/*+ * Set a encryption passphrase.+ */+__LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p);+__LA_DECL int archive_write_set_passphrase_callback(struct archive *,+			    void *client_data, archive_passphrase_callback *);++/*-+ * ARCHIVE_WRITE_DISK API+ *+ * To create objects on disk:+ *   1) Ask archive_write_disk_new for a new archive_write_disk object.+ *   2) Set any global properties.  In particular, you probably+ *      want to set the options.+ *   3) For each entry:+ *      - construct an appropriate struct archive_entry structure+ *      - archive_write_header to create the file/dir/etc on disk+ *      - archive_write_data to write the entry data+ *   4) archive_write_free to cleanup the writer and release resources+ *+ * In particular, you can use this in conjunction with archive_read()+ * to pull entries out of an archive and create them on disk.+ */+__LA_DECL struct archive	*archive_write_disk_new(void);+/* This file will not be overwritten. */+__LA_DECL int archive_write_disk_set_skip_file(struct archive *,+    la_int64_t, la_int64_t);+/* Set flags to control how the next item gets created.+ * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */+__LA_DECL int		 archive_write_disk_set_options(struct archive *,+		     int flags);+/*+ * The lookup functions are given uname/uid (or gname/gid) pairs and+ * return a uid (gid) suitable for this system.  These are used for+ * restoring ownership and for setting ACLs.  The default functions+ * are naive, they just return the uid/gid.  These are small, so reasonable+ * for applications that don't need to preserve ownership; they+ * are probably also appropriate for applications that are doing+ * same-system backup and restore.+ */+/*+ * The "standard" lookup functions use common system calls to lookup+ * the uname/gname, falling back to the uid/gid if the names can't be+ * found.  They cache lookups and are reasonably fast, but can be very+ * large, so they are not used unless you ask for them.  In+ * particular, these match the specifications of POSIX "pax" and old+ * POSIX "tar".+ */+__LA_DECL int	 archive_write_disk_set_standard_lookup(struct archive *);+/*+ * If neither the default (naive) nor the standard (big) functions suit+ * your needs, you can write your own and register them.  Be sure to+ * include a cleanup function if you have allocated private data.+ */+__LA_DECL int archive_write_disk_set_group_lookup(struct archive *,+    void * /* private_data */,+    la_int64_t (*)(void *, const char *, la_int64_t),+    void (* /* cleanup */)(void *));+__LA_DECL int archive_write_disk_set_user_lookup(struct archive *,+    void * /* private_data */,+    la_int64_t (*)(void *, const char *, la_int64_t),+    void (* /* cleanup */)(void *));+__LA_DECL la_int64_t archive_write_disk_gid(struct archive *, const char *, la_int64_t);+__LA_DECL la_int64_t archive_write_disk_uid(struct archive *, const char *, la_int64_t);++/*+ * ARCHIVE_READ_DISK API+ *+ * This is still evolving and somewhat experimental.+ */+__LA_DECL struct archive *archive_read_disk_new(void);+/* The names for symlink modes here correspond to an old BSD+ * command-line argument convention: -L, -P, -H */+/* Follow all symlinks. */+__LA_DECL int archive_read_disk_set_symlink_logical(struct archive *);+/* Follow no symlinks. */+__LA_DECL int archive_read_disk_set_symlink_physical(struct archive *);+/* Follow symlink initially, then not. */+__LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *);+/* TODO: Handle Linux stat32/stat64 ugliness. <sigh> */+__LA_DECL int archive_read_disk_entry_from_file(struct archive *,+    struct archive_entry *, int /* fd */, const struct stat *);+/* Look up gname for gid or uname for uid. */+/* Default implementations are very, very stupid. */+__LA_DECL const char *archive_read_disk_gname(struct archive *, la_int64_t);+__LA_DECL const char *archive_read_disk_uname(struct archive *, la_int64_t);+/* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the+ * results for performance. */+__LA_DECL int	archive_read_disk_set_standard_lookup(struct archive *);+/* You can install your own lookups if you like. */+__LA_DECL int	archive_read_disk_set_gname_lookup(struct archive *,+    void * /* private_data */,+    const char *(* /* lookup_fn */)(void *, la_int64_t),+    void (* /* cleanup_fn */)(void *));+__LA_DECL int	archive_read_disk_set_uname_lookup(struct archive *,+    void * /* private_data */,+    const char *(* /* lookup_fn */)(void *, la_int64_t),+    void (* /* cleanup_fn */)(void *));+/* Start traversal. */+__LA_DECL int	archive_read_disk_open(struct archive *, const char *);+__LA_DECL int	archive_read_disk_open_w(struct archive *, const wchar_t *);+/*+ * Request that current entry be visited.  If you invoke it on every+ * directory, you'll get a physical traversal.  This is ignored if the+ * current entry isn't a directory or a link to a directory.  So, if+ * you invoke this on every returned path, you'll get a full logical+ * traversal.+ */+__LA_DECL int	archive_read_disk_descend(struct archive *);+__LA_DECL int	archive_read_disk_can_descend(struct archive *);+__LA_DECL int	archive_read_disk_current_filesystem(struct archive *);+__LA_DECL int	archive_read_disk_current_filesystem_is_synthetic(struct archive *);+__LA_DECL int	archive_read_disk_current_filesystem_is_remote(struct archive *);+/* Request that the access time of the entry visited by traversal be restored. */+__LA_DECL int  archive_read_disk_set_atime_restored(struct archive *);+/*+ * Set behavior. The "flags" argument selects optional behavior.+ */+/* Request that the access time of the entry visited by traversal be restored.+ * This is the same as archive_read_disk_set_atime_restored. */+#define	ARCHIVE_READDISK_RESTORE_ATIME		(0x0001)+/* Default: Do not skip an entry which has nodump flags. */+#define	ARCHIVE_READDISK_HONOR_NODUMP		(0x0002)+/* Default: Skip a mac resource fork file whose prefix is "._" because of+ * using copyfile. */+#define	ARCHIVE_READDISK_MAC_COPYFILE		(0x0004)+/* Default: Traverse mount points. */+#define	ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS	(0x0008)+/* Default: Xattrs are read from disk. */+#define	ARCHIVE_READDISK_NO_XATTR		(0x0010)+/* Default: ACLs are read from disk. */+#define	ARCHIVE_READDISK_NO_ACL			(0x0020)+/* Default: File flags are read from disk. */+#define	ARCHIVE_READDISK_NO_FFLAGS		(0x0040)++__LA_DECL int  archive_read_disk_set_behavior(struct archive *,+		    int flags);++/*+ * Set archive_match object that will be used in archive_read_disk to+ * know whether an entry should be skipped. The callback function+ * _excluded_func will be invoked when an entry is skipped by the result+ * of archive_match.+ */+__LA_DECL int	archive_read_disk_set_matching(struct archive *,+		    struct archive *_matching, void (*_excluded_func)+		    (struct archive *, void *, struct archive_entry *),+		    void *_client_data);+__LA_DECL int	archive_read_disk_set_metadata_filter_callback(struct archive *,+		    int (*_metadata_filter_func)(struct archive *, void *,+		    	struct archive_entry *), void *_client_data);++/* Simplified cleanup interface;+ * This calls archive_read_free() or archive_write_free() as needed. */+__LA_DECL int	archive_free(struct archive *);++/*+ * Accessor functions to read/set various information in+ * the struct archive object:+ */++/* Number of filters in the current filter pipeline. */+/* Filter #0 is the one closest to the format, -1 is a synonym for the+ * last filter, which is always the pseudo-filter that wraps the+ * client callbacks. */+__LA_DECL int		 archive_filter_count(struct archive *);+__LA_DECL la_int64_t	 archive_filter_bytes(struct archive *, int);+__LA_DECL int		 archive_filter_code(struct archive *, int);+__LA_DECL const char *	 archive_filter_name(struct archive *, int);++#if ARCHIVE_VERSION_NUMBER < 4000000+/* These don't properly handle multiple filters, so are deprecated and+ * will eventually be removed. */+/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */+__LA_DECL la_int64_t	 archive_position_compressed(struct archive *)+				__LA_DEPRECATED;+/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */+__LA_DECL la_int64_t	 archive_position_uncompressed(struct archive *)+				__LA_DEPRECATED;+/* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */+__LA_DECL const char	*archive_compression_name(struct archive *)+				__LA_DEPRECATED;+/* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */+__LA_DECL int		 archive_compression(struct archive *)+				__LA_DEPRECATED;+#endif++__LA_DECL int		 archive_errno(struct archive *);+__LA_DECL const char	*archive_error_string(struct archive *);+__LA_DECL const char	*archive_format_name(struct archive *);+__LA_DECL int		 archive_format(struct archive *);+__LA_DECL void		 archive_clear_error(struct archive *);+__LA_DECL void		 archive_set_error(struct archive *, int _err,+			    const char *fmt, ...) __LA_PRINTF(3, 4);+__LA_DECL void		 archive_copy_error(struct archive *dest,+			    struct archive *src);+__LA_DECL int		 archive_file_count(struct archive *);++/*+ * ARCHIVE_MATCH API+ */+__LA_DECL struct archive *archive_match_new(void);+__LA_DECL int	archive_match_free(struct archive *);++/*+ * Test if archive_entry is excluded.+ * This is a convenience function. This is the same as calling all+ * archive_match_path_excluded, archive_match_time_excluded+ * and archive_match_owner_excluded.+ */+__LA_DECL int	archive_match_excluded(struct archive *,+		    struct archive_entry *);++/*+ * Test if pathname is excluded. The conditions are set by following functions.+ */+__LA_DECL int	archive_match_path_excluded(struct archive *,+		    struct archive_entry *);+/* Control recursive inclusion of directory content when directory is included. Default on. */+__LA_DECL int	archive_match_set_inclusion_recursion(struct archive *, int);+/* Add exclusion pathname pattern. */+__LA_DECL int	archive_match_exclude_pattern(struct archive *, const char *);+__LA_DECL int	archive_match_exclude_pattern_w(struct archive *,+		    const wchar_t *);+/* Add exclusion pathname pattern from file. */+__LA_DECL int	archive_match_exclude_pattern_from_file(struct archive *,+		    const char *, int _nullSeparator);+__LA_DECL int	archive_match_exclude_pattern_from_file_w(struct archive *,+		    const wchar_t *, int _nullSeparator);+/* Add inclusion pathname pattern. */+__LA_DECL int	archive_match_include_pattern(struct archive *, const char *);+__LA_DECL int	archive_match_include_pattern_w(struct archive *,+		    const wchar_t *);+/* Add inclusion pathname pattern from file. */+__LA_DECL int	archive_match_include_pattern_from_file(struct archive *,+		    const char *, int _nullSeparator);+__LA_DECL int	archive_match_include_pattern_from_file_w(struct archive *,+		    const wchar_t *, int _nullSeparator);+/*+ * How to get statistic information for inclusion patterns.+ */+/* Return the amount number of unmatched inclusion patterns. */+__LA_DECL int	archive_match_path_unmatched_inclusions(struct archive *);+/* Return the pattern of unmatched inclusion with ARCHIVE_OK.+ * Return ARCHIVE_EOF if there is no inclusion pattern. */+__LA_DECL int	archive_match_path_unmatched_inclusions_next(+		    struct archive *, const char **);+__LA_DECL int	archive_match_path_unmatched_inclusions_next_w(+		    struct archive *, const wchar_t **);++/*+ * Test if a file is excluded by its time stamp.+ * The conditions are set by following functions.+ */+__LA_DECL int	archive_match_time_excluded(struct archive *,+		    struct archive_entry *);++/*+ * Flags to tell a matching type of time stamps. These are used for+ * following functions.+ */+/* Time flag: mtime to be tested. */+#define ARCHIVE_MATCH_MTIME	(0x0100)+/* Time flag: ctime to be tested. */+#define ARCHIVE_MATCH_CTIME	(0x0200)+/* Comparison flag: Match the time if it is newer than. */+#define ARCHIVE_MATCH_NEWER	(0x0001)+/* Comparison flag: Match the time if it is older than. */+#define ARCHIVE_MATCH_OLDER	(0x0002)+/* Comparison flag: Match the time if it is equal to. */+#define ARCHIVE_MATCH_EQUAL	(0x0010)+/* Set inclusion time. */+__LA_DECL int	archive_match_include_time(struct archive *, int _flag,+		    time_t _sec, long _nsec);+/* Set inclusion time by a date string. */+__LA_DECL int	archive_match_include_date(struct archive *, int _flag,+		    const char *_datestr);+__LA_DECL int	archive_match_include_date_w(struct archive *, int _flag,+		    const wchar_t *_datestr);+/* Set inclusion time by a particular file. */+__LA_DECL int	archive_match_include_file_time(struct archive *,+		    int _flag, const char *_pathname);+__LA_DECL int	archive_match_include_file_time_w(struct archive *,+		    int _flag, const wchar_t *_pathname);+/* Add exclusion entry. */+__LA_DECL int	archive_match_exclude_entry(struct archive *,+		    int _flag, struct archive_entry *);++/*+ * Test if a file is excluded by its uid ,gid, uname or gname.+ * The conditions are set by following functions.+ */+__LA_DECL int	archive_match_owner_excluded(struct archive *,+		    struct archive_entry *);+/* Add inclusion uid, gid, uname and gname. */+__LA_DECL int	archive_match_include_uid(struct archive *, la_int64_t);+__LA_DECL int	archive_match_include_gid(struct archive *, la_int64_t);+__LA_DECL int	archive_match_include_uname(struct archive *, const char *);+__LA_DECL int	archive_match_include_uname_w(struct archive *,+		    const wchar_t *);+__LA_DECL int	archive_match_include_gname(struct archive *, const char *);+__LA_DECL int	archive_match_include_gname_w(struct archive *,+		    const wchar_t *);++/* Utility functions */+/* Convenience function to sort a NULL terminated list of strings */+__LA_DECL int archive_utility_string_sort(char **);++#ifdef __cplusplus+}+#endif++/* These are meaningless outside of this header. */+#undef __LA_DECL++#endif /* !ARCHIVE_H_INCLUDED */
+ c/archive_acl.c view
@@ -0,0 +1,2097 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * Copyright (c) 2016 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_WCHAR_H+#include <wchar.h>+#endif++#include "archive_acl_private.h"+#include "archive_entry.h"+#include "archive_private.h"++#undef max+#define	max(a, b)	((a)>(b)?(a):(b))++#ifndef HAVE_WMEMCMP+/* Good enough for simple equality testing, but not for sorting. */+#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))+#endif++static int	acl_special(struct archive_acl *acl,+		    int type, int permset, int tag);+static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,+		    int type, int permset, int tag, int id);+static int	archive_acl_add_entry_len_l(struct archive_acl *acl,+		    int type, int permset, int tag, int id, const char *name,+		    size_t len, struct archive_string_conv *sc);+static int	archive_acl_text_want_type(struct archive_acl *acl, int flags);+static ssize_t	archive_acl_text_len(struct archive_acl *acl, int want_type,+		    int flags, int wide, struct archive *a,+		    struct archive_string_conv *sc);+static int	isint_w(const wchar_t *start, const wchar_t *end, int *result);+static int	ismode_w(const wchar_t *start, const wchar_t *end, int *result);+static int	is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,+		    int *result);+static int	is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,+		    int *result);+static void	next_field_w(const wchar_t **wp, const wchar_t **start,+		    const wchar_t **end, wchar_t *sep);+static void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,+		    int tag, int flags, const wchar_t *wname, int perm, int id);+static void	append_id_w(wchar_t **wp, int id);+static int	isint(const char *start, const char *end, int *result);+static int	ismode(const char *start, const char *end, int *result);+static int	is_nfs4_flags(const char *start, const char *end,+		    int *result);+static int	is_nfs4_perms(const char *start, const char *end,+		    int *result);+static void	next_field(const char **p, const char **start,+		    const char **end, char *sep);+static void	append_entry(char **p, const char *prefix, int type,+		    int tag, int flags, const char *name, int perm, int id);+static void	append_id(char **p, int id);++static const struct {+	const int perm;+	const char c;+	const wchar_t wc;+} nfsv4_acl_perm_map[] = {+	{ ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',+	    L'r' },+	{ ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',+	    L'w' },+	{ ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },+	{ ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,+	    'p', L'p' },+	{ ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },+	{ ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },+	{ ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },+	{ ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },+	{ ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },+	{ ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },+	{ ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },+	{ ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },+	{ ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },+	{ ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }+};++static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /+    sizeof(nfsv4_acl_perm_map[0]));++static const struct {+	const int perm;+	const char c;+	const wchar_t wc;+} nfsv4_acl_flag_map[] = {+	{ ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },+	{ ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },+	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },+	{ ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },+	{ ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },+	{ ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },+	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }+};++static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /+    sizeof(nfsv4_acl_flag_map[0]));++void+archive_acl_clear(struct archive_acl *acl)+{+	struct archive_acl_entry *ap;++	while (acl->acl_head != NULL) {+		ap = acl->acl_head->next;+		archive_mstring_clean(&acl->acl_head->name);+		free(acl->acl_head);+		acl->acl_head = ap;+	}+	free(acl->acl_text_w);+	acl->acl_text_w = NULL;+	free(acl->acl_text);+	acl->acl_text = NULL;+	acl->acl_p = NULL;+	acl->acl_types = 0;+	acl->acl_state = 0; /* Not counting. */+}++void+archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)+{+	struct archive_acl_entry *ap, *ap2;++	archive_acl_clear(dest);++	dest->mode = src->mode;+	ap = src->acl_head;+	while (ap != NULL) {+		ap2 = acl_new_entry(dest,+		    ap->type, ap->permset, ap->tag, ap->id);+		if (ap2 != NULL)+			archive_mstring_copy(&ap2->name, &ap->name);+		ap = ap->next;+	}+}++int+archive_acl_add_entry(struct archive_acl *acl,+    int type, int permset, int tag, int id, const char *name)+{+	struct archive_acl_entry *ap;++	if (acl_special(acl, type, permset, tag) == 0)+		return ARCHIVE_OK;+	ap = acl_new_entry(acl, type, permset, tag, id);+	if (ap == NULL) {+		/* XXX Error XXX */+		return ARCHIVE_FAILED;+	}+	if (name != NULL  &&  *name != '\0')+		archive_mstring_copy_mbs(&ap->name, name);+	else+		archive_mstring_clean(&ap->name);+	return ARCHIVE_OK;+}++int+archive_acl_add_entry_w_len(struct archive_acl *acl,+    int type, int permset, int tag, int id, const wchar_t *name, size_t len)+{+	struct archive_acl_entry *ap;++	if (acl_special(acl, type, permset, tag) == 0)+		return ARCHIVE_OK;+	ap = acl_new_entry(acl, type, permset, tag, id);+	if (ap == NULL) {+		/* XXX Error XXX */+		return ARCHIVE_FAILED;+	}+	if (name != NULL  &&  *name != L'\0' && len > 0)+		archive_mstring_copy_wcs_len(&ap->name, name, len);+	else+		archive_mstring_clean(&ap->name);+	return ARCHIVE_OK;+}++static int+archive_acl_add_entry_len_l(struct archive_acl *acl,+    int type, int permset, int tag, int id, const char *name, size_t len,+    struct archive_string_conv *sc)+{+	struct archive_acl_entry *ap;+	int r;++	if (acl_special(acl, type, permset, tag) == 0)+		return ARCHIVE_OK;+	ap = acl_new_entry(acl, type, permset, tag, id);+	if (ap == NULL) {+		/* XXX Error XXX */+		return ARCHIVE_FAILED;+	}+	if (name != NULL  &&  *name != '\0' && len > 0) {+		r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);+	} else {+		r = 0;+		archive_mstring_clean(&ap->name);+	}+	if (r == 0)+		return (ARCHIVE_OK);+	else if (errno == ENOMEM)+		return (ARCHIVE_FATAL);+	else+		return (ARCHIVE_WARN);+}++/*+ * If this ACL entry is part of the standard POSIX permissions set,+ * store the permissions in the stat structure and return zero.+ */+static int+acl_special(struct archive_acl *acl, int type, int permset, int tag)+{+	if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS+	    && ((permset & ~007) == 0)) {+		switch (tag) {+		case ARCHIVE_ENTRY_ACL_USER_OBJ:+			acl->mode &= ~0700;+			acl->mode |= (permset & 7) << 6;+			return (0);+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+			acl->mode &= ~0070;+			acl->mode |= (permset & 7) << 3;+			return (0);+		case ARCHIVE_ENTRY_ACL_OTHER:+			acl->mode &= ~0007;+			acl->mode |= permset & 7;+			return (0);+		}+	}+	return (1);+}++/*+ * Allocate and populate a new ACL entry with everything but the+ * name.+ */+static struct archive_acl_entry *+acl_new_entry(struct archive_acl *acl,+    int type, int permset, int tag, int id)+{+	struct archive_acl_entry *ap, *aq;++	/* Type argument must be a valid NFS4 or POSIX.1e type.+	 * The type must agree with anything already set and+	 * the permset must be compatible. */+	if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			return (NULL);+		}+		if (permset &+		    ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4+			| ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {+			return (NULL);+		}+	} else	if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {+		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {+			return (NULL);+		}+		if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {+			return (NULL);+		}+	} else {+		return (NULL);+	}++	/* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */+	switch (tag) {+	case ARCHIVE_ENTRY_ACL_USER:+	case ARCHIVE_ENTRY_ACL_USER_OBJ:+	case ARCHIVE_ENTRY_ACL_GROUP:+	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+		/* Tags valid in both NFS4 and POSIX.1e */+		break;+	case ARCHIVE_ENTRY_ACL_MASK:+	case ARCHIVE_ENTRY_ACL_OTHER:+		/* Tags valid only in POSIX.1e. */+		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {+			return (NULL);+		}+		break;+	case ARCHIVE_ENTRY_ACL_EVERYONE:+		/* Tags valid only in NFS4. */+		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			return (NULL);+		}+		break;+	default:+		/* No other values are valid. */+		return (NULL);+	}++	free(acl->acl_text_w);+	acl->acl_text_w = NULL;+	free(acl->acl_text);+	acl->acl_text = NULL;++	/*+	 * If there's a matching entry already in the list, overwrite it.+	 * NFSv4 entries may be repeated and are not overwritten.+	 *+	 * TODO: compare names of no id is provided (needs more rework)+	 */+	ap = acl->acl_head;+	aq = NULL;+	while (ap != NULL) {+		if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&+		    ap->type == type && ap->tag == tag && ap->id == id) {+			if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&+			    tag != ARCHIVE_ENTRY_ACL_GROUP)) {+				ap->permset = permset;+				return (ap);+			}+		}+		aq = ap;+		ap = ap->next;+	}++	/* Add a new entry to the end of the list. */+	ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));+	if (ap == NULL)+		return (NULL);+	if (aq == NULL)+		acl->acl_head = ap;+	else+		aq->next = ap;+	ap->type = type;+	ap->tag = tag;+	ap->id = id;+	ap->permset = permset;+	acl->acl_types |= type;+	return (ap);+}++/*+ * Return a count of entries matching "want_type".+ */+int+archive_acl_count(struct archive_acl *acl, int want_type)+{+	int count;+	struct archive_acl_entry *ap;++	count = 0;+	ap = acl->acl_head;+	while (ap != NULL) {+		if ((ap->type & want_type) != 0)+			count++;+		ap = ap->next;+	}++	if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))+		count += 3;+	return (count);+}++/*+ * Return a bitmask of stored ACL types in an ACL list+ */+int+archive_acl_types(struct archive_acl *acl)+{+	return (acl->acl_types);+}++/*+ * Prepare for reading entries from the ACL data.  Returns a count+ * of entries matching "want_type", or zero if there are no+ * non-extended ACL entries of that type.+ */+int+archive_acl_reset(struct archive_acl *acl, int want_type)+{+	int count, cutoff;++	count = archive_acl_count(acl, want_type);++	/*+	 * If the only entries are the three standard ones,+	 * then don't return any ACL data.  (In this case,+	 * client can just use chmod(2) to set permissions.)+	 */+	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)+		cutoff = 3;+	else+		cutoff = 0;++	if (count > cutoff)+		acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;+	else+		acl->acl_state = 0;+	acl->acl_p = acl->acl_head;+	return (count);+}+++/*+ * Return the next ACL entry in the list.  Fake entries for the+ * standard permissions and include them in the returned list.+ */+int+archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,+    int *type, int *permset, int *tag, int *id, const char **name)+{+	*name = NULL;+	*id = -1;++	/*+	 * The acl_state is either zero (no entries available), -1+	 * (reading from list), or an entry type (retrieve that type+	 * from ae_stat.aest_mode).+	 */+	if (acl->acl_state == 0)+		return (ARCHIVE_WARN);++	/* The first three access entries are special. */+	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {+		switch (acl->acl_state) {+		case ARCHIVE_ENTRY_ACL_USER_OBJ:+			*permset = (acl->mode >> 6) & 7;+			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;+			*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;+			acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;+			return (ARCHIVE_OK);+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+			*permset = (acl->mode >> 3) & 7;+			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;+			*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;+			acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;+			return (ARCHIVE_OK);+		case ARCHIVE_ENTRY_ACL_OTHER:+			*permset = acl->mode & 7;+			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;+			*tag = ARCHIVE_ENTRY_ACL_OTHER;+			acl->acl_state = -1;+			acl->acl_p = acl->acl_head;+			return (ARCHIVE_OK);+		default:+			break;+		}+	}++	while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)+		acl->acl_p = acl->acl_p->next;+	if (acl->acl_p == NULL) {+		acl->acl_state = 0;+		*type = 0;+		*permset = 0;+		*tag = 0;+		*id = -1;+		*name = NULL;+		return (ARCHIVE_EOF); /* End of ACL entries. */+	}+	*type = acl->acl_p->type;+	*permset = acl->acl_p->permset;+	*tag = acl->acl_p->tag;+	*id = acl->acl_p->id;+	if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {+		if (errno == ENOMEM)+			return (ARCHIVE_FATAL);+		*name = NULL;+	}+	acl->acl_p = acl->acl_p->next;+	return (ARCHIVE_OK);+}++/*+ * Determine what type of ACL do we want+ */+static int+archive_acl_text_want_type(struct archive_acl *acl, int flags)+{+	int want_type;++	/* Check if ACL is NFSv4 */+	if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {+		/* NFSv4 should never mix with POSIX.1e */+		if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)+			return (0);+		else+			return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);+	}++	/* Now deal with POSIX.1e ACLs */++	want_type = 0;+	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)+		want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;+	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)+		want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;++	/* By default we want both access and default ACLs */+	if (want_type == 0)+		return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);++	return (want_type);+}++/*+ * Calculate ACL text string length+ */+static ssize_t+archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,+    int wide, struct archive *a, struct archive_string_conv *sc) {+	struct archive_acl_entry *ap;+	const char *name;+	const wchar_t *wname;+	int count, idlen, tmp, r;+	ssize_t length;+	size_t len;++	count = 0;+	length = 0;+	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {+		if ((ap->type & want_type) == 0)+			continue;+		/*+		 * Filemode-mapping ACL entries are stored exclusively in+		 * ap->mode so they should not be in the list+		 */+		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)+		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ+		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ+		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))+			continue;+		count++;+		if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0+		    && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)+			length += 8; /* "default:" */+		switch (ap->tag) {+		case ARCHIVE_ENTRY_ACL_USER_OBJ:+			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+				length += 6; /* "owner@" */+				break;+			}+			/* FALLTHROUGH */+		case ARCHIVE_ENTRY_ACL_USER:+		case ARCHIVE_ENTRY_ACL_MASK:+			length += 4; /* "user", "mask" */+			break;+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+				length += 6; /* "group@" */+				break;+			}+			/* FALLTHROUGH */+		case ARCHIVE_ENTRY_ACL_GROUP:+		case ARCHIVE_ENTRY_ACL_OTHER:+			length += 5; /* "group", "other" */+			break;+		case ARCHIVE_ENTRY_ACL_EVERYONE:+			length += 9; /* "everyone@" */+			break;+		}+		length += 1; /* colon after tag */+		if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||+		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {+			if (wide) {+				r = archive_mstring_get_wcs(a, &ap->name,+				    &wname);+				if (r == 0 && wname != NULL)+					length += wcslen(wname);+				else if (r < 0 && errno == ENOMEM)+					return (0);+				else+					length += sizeof(uid_t) * 3 + 1;+			} else {+				r = archive_mstring_get_mbs_l(&ap->name, &name,+				    &len, sc);+				if (r != 0)+					return (0);+				if (len > 0 && name != NULL)+					length += len;+				else+					length += sizeof(uid_t) * 3 + 1;+			}+			length += 1; /* colon after user or group name */+		} else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)+			length += 1; /* 2nd colon empty user,group or other */++		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)+		    && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)+		    && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER+		    || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {+			/* Solaris has no colon after other: and mask: */+			length = length - 1;+		}++		if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			/* rwxpdDaARWcCos:fdinSFI:deny */+			length += 27;+			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)+				length += 1; /* allow, alarm, audit */+		} else+			length += 3; /* rwx */++		if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||+		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {+			length += 1; /* colon */+			/* ID digit count */+			idlen = 1;+			tmp = ap->id;+			while (tmp > 9) {+				tmp = tmp / 10;+				idlen++;+			}+			length += idlen;+		}+		length ++; /* entry separator */+	}++	/* Add filemode-mapping access entries to the length */+	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {+		if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {+			/* "user::rwx\ngroup::rwx\nother:rwx\n" */+			length += 31;+		} else {+			/* "user::rwx\ngroup::rwx\nother::rwx\n" */+			length += 32;+		}+	} else if (count == 0)+		return (0);++	/* The terminating character is included in count */+	return (length);+}++/*+ * Generate a wide text version of the ACL. The flags parameter controls+ * the type and style of the generated ACL.+ */+wchar_t *+archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,+    struct archive *a)+{+	int count;+	ssize_t length;+	size_t len;+	const wchar_t *wname;+	const wchar_t *prefix;+	wchar_t separator;+	struct archive_acl_entry *ap;+	int id, r, want_type;+	wchar_t *wp, *ws;++	want_type = archive_acl_text_want_type(acl, flags);++	/* Both NFSv4 and POSIX.1 types found */+	if (want_type == 0)+		return (NULL);++	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)+		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;++	length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);++	if (length == 0)+		return (NULL);++	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)+		separator = L',';+	else+		separator = L'\n';++	/* Now, allocate the string and actually populate it. */+	wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));+	if (wp == NULL) {+		if (errno == ENOMEM)+			__archive_errx(1, "No memory");+		return (NULL);+	}+	count = 0;++	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {+		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,+		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,+		    acl->mode & 0700, -1);+		*wp++ = separator;+		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,+		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,+		    acl->mode & 0070, -1);+		*wp++ = separator;+		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,+		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,+		    acl->mode & 0007, -1);+		count += 3;+	}++	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {+		if ((ap->type & want_type) == 0)+			continue;+		/*+		 * Filemode-mapping ACL entries are stored exclusively in+		 * ap->mode so they should not be in the list+		 */+		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)+		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ+		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ+		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))+			continue;+		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)+			prefix = L"default:";+		else+			prefix = NULL;+		r = archive_mstring_get_wcs(a, &ap->name, &wname);+		if (r == 0) {+			if (count > 0)+				*wp++ = separator;+			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)+				id = ap->id;+			else+				id = -1;+			append_entry_w(&wp, prefix, ap->type, ap->tag, flags,+			    wname, ap->permset, id);+			count++;+		} else if (r < 0 && errno == ENOMEM) {+			free(ws);+			return (NULL);+		}+	}++	/* Add terminating character */+	*wp++ = L'\0';++	len = wcslen(ws);++	if ((ssize_t)len > (length - 1))+		__archive_errx(1, "Buffer overrun");++	if (text_len != NULL)+		*text_len = len;++	return (ws);+}++static void+append_id_w(wchar_t **wp, int id)+{+	if (id < 0)+		id = 0;+	if (id > 9)+		append_id_w(wp, id / 10);+	*(*wp)++ = L"0123456789"[id % 10];+}++static void+append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,+    int tag, int flags, const wchar_t *wname, int perm, int id)+{+	int i;++	if (prefix != NULL) {+		wcscpy(*wp, prefix);+		*wp += wcslen(*wp);+	}+	switch (tag) {+	case ARCHIVE_ENTRY_ACL_USER_OBJ:+		wname = NULL;+		id = -1;+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {+			wcscpy(*wp, L"owner@");+			break;+		}+		/* FALLTHROUGH */+	case ARCHIVE_ENTRY_ACL_USER:+		wcscpy(*wp, L"user");+		break;+	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+		wname = NULL;+		id = -1;+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {+			wcscpy(*wp, L"group@");+			break;+		}+		/* FALLTHROUGH */+	case ARCHIVE_ENTRY_ACL_GROUP:+		wcscpy(*wp, L"group");+		break;+	case ARCHIVE_ENTRY_ACL_MASK:+		wcscpy(*wp, L"mask");+		wname = NULL;+		id = -1;+		break;+	case ARCHIVE_ENTRY_ACL_OTHER:+		wcscpy(*wp, L"other");+		wname = NULL;+		id = -1;+		break;+	case ARCHIVE_ENTRY_ACL_EVERYONE:+		wcscpy(*wp, L"everyone@");+		wname = NULL;+		id = -1;+		break;+	}+	*wp += wcslen(*wp);+	*(*wp)++ = L':';+	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||+	    tag == ARCHIVE_ENTRY_ACL_USER ||+	    tag == ARCHIVE_ENTRY_ACL_GROUP) {+		if (wname != NULL) {+			wcscpy(*wp, wname);+			*wp += wcslen(*wp);+		} else if (tag == ARCHIVE_ENTRY_ACL_USER+		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {+			append_id_w(wp, id);+			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)+				id = -1;+		}+		/* Solaris style has no second colon after other and mask */+		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)+		    || (tag != ARCHIVE_ENTRY_ACL_OTHER+		    && tag != ARCHIVE_ENTRY_ACL_MASK))+			*(*wp)++ = L':';+	}+	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {+		/* POSIX.1e ACL perms */+		*(*wp)++ = (perm & 0444) ? L'r' : L'-';+		*(*wp)++ = (perm & 0222) ? L'w' : L'-';+		*(*wp)++ = (perm & 0111) ? L'x' : L'-';+	} else {+		/* NFSv4 ACL perms */+		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {+			if (perm & nfsv4_acl_perm_map[i].perm)+				*(*wp)++ = nfsv4_acl_perm_map[i].wc;+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)+				*(*wp)++ = L'-';+		}+		*(*wp)++ = L':';+		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {+			if (perm & nfsv4_acl_flag_map[i].perm)+				*(*wp)++ = nfsv4_acl_flag_map[i].wc;+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)+				*(*wp)++ = L'-';+		}+		*(*wp)++ = L':';+		switch (type) {+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:+			wcscpy(*wp, L"allow");+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:+			wcscpy(*wp, L"deny");+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:+			wcscpy(*wp, L"audit");+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:+			wcscpy(*wp, L"alarm");+			break;+		default:+			break;+		}+		*wp += wcslen(*wp);+	}+	if (id != -1) {+		*(*wp)++ = L':';+		append_id_w(wp, id);+	}+}++/*+ * Generate a text version of the ACL. The flags parameter controls+ * the type and style of the generated ACL.+ */+char *+archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,+    struct archive_string_conv *sc)+{+	int count;+	ssize_t length;+	size_t len;+	const char *name;+	const char *prefix;+	char separator;+	struct archive_acl_entry *ap;+	int id, r, want_type;+	char *p, *s;++	want_type = archive_acl_text_want_type(acl, flags);++	/* Both NFSv4 and POSIX.1 types found */+	if (want_type == 0)+		return (NULL);++	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)+		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;++	length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);++	if (length == 0)+		return (NULL);++	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)+		separator = ',';+	else+		separator = '\n';++	/* Now, allocate the string and actually populate it. */+	p = s = (char *)malloc(length * sizeof(char));+	if (p == NULL) {+		if (errno == ENOMEM)+			__archive_errx(1, "No memory");+		return (NULL);+	}+	count = 0;++	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {+		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,+		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,+		    acl->mode & 0700, -1);+		*p++ = separator;+		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,+		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,+		    acl->mode & 0070, -1);+		*p++ = separator;+		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,+		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,+		    acl->mode & 0007, -1);+		count += 3;+	}++	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {+		if ((ap->type & want_type) == 0)+			continue;+		/*+		 * Filemode-mapping ACL entries are stored exclusively in+		 * ap->mode so they should not be in the list+		 */+		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)+		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ+		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ+		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))+			continue;+		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)+			prefix = "default:";+		else+			prefix = NULL;+		r = archive_mstring_get_mbs_l(+		    &ap->name, &name, &len, sc);+		if (r != 0) {+			free(s);+			return (NULL);+		}+		if (count > 0)+			*p++ = separator;+		if (name == NULL ||+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {+			id = ap->id;+		} else {+			id = -1;+		}+		append_entry(&p, prefix, ap->type, ap->tag, flags, name,+		    ap->permset, id);+		count++;+	}++	/* Add terminating character */+	*p++ = '\0';++	len = strlen(s);++	if ((ssize_t)len > (length - 1))+		__archive_errx(1, "Buffer overrun");++	if (text_len != NULL)+		*text_len = len;++	return (s);+}++static void+append_id(char **p, int id)+{+	if (id < 0)+		id = 0;+	if (id > 9)+		append_id(p, id / 10);+	*(*p)++ = "0123456789"[id % 10];+}++static void+append_entry(char **p, const char *prefix, int type,+    int tag, int flags, const char *name, int perm, int id)+{+	int i;++	if (prefix != NULL) {+		strcpy(*p, prefix);+		*p += strlen(*p);+	}+	switch (tag) {+	case ARCHIVE_ENTRY_ACL_USER_OBJ:+		name = NULL;+		id = -1;+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {+			strcpy(*p, "owner@");+			break;+		}+		/* FALLTHROUGH */+	case ARCHIVE_ENTRY_ACL_USER:+		strcpy(*p, "user");+		break;+	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+		name = NULL;+		id = -1;+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {+			strcpy(*p, "group@");+			break;+		}+		/* FALLTHROUGH */+	case ARCHIVE_ENTRY_ACL_GROUP:+		strcpy(*p, "group");+		break;+	case ARCHIVE_ENTRY_ACL_MASK:+		strcpy(*p, "mask");+		name = NULL;+		id = -1;+		break;+	case ARCHIVE_ENTRY_ACL_OTHER:+		strcpy(*p, "other");+		name = NULL;+		id = -1;+		break;+	case ARCHIVE_ENTRY_ACL_EVERYONE:+		strcpy(*p, "everyone@");+		name = NULL;+		id = -1;+		break;+	}+	*p += strlen(*p);+	*(*p)++ = ':';+	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||+	    tag == ARCHIVE_ENTRY_ACL_USER ||+	    tag == ARCHIVE_ENTRY_ACL_GROUP) {+		if (name != NULL) {+			strcpy(*p, name);+			*p += strlen(*p);+		} else if (tag == ARCHIVE_ENTRY_ACL_USER+		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {+			append_id(p, id);+			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)+				id = -1;+		}+		/* Solaris style has no second colon after other and mask */+		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)+		    || (tag != ARCHIVE_ENTRY_ACL_OTHER+		    && tag != ARCHIVE_ENTRY_ACL_MASK))+			*(*p)++ = ':';+	}+	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {+		/* POSIX.1e ACL perms */+		*(*p)++ = (perm & 0444) ? 'r' : '-';+		*(*p)++ = (perm & 0222) ? 'w' : '-';+		*(*p)++ = (perm & 0111) ? 'x' : '-';+	} else {+		/* NFSv4 ACL perms */+		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {+			if (perm & nfsv4_acl_perm_map[i].perm)+				*(*p)++ = nfsv4_acl_perm_map[i].c;+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)+				*(*p)++ = '-';+		}+		*(*p)++ = ':';+		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {+			if (perm & nfsv4_acl_flag_map[i].perm)+				*(*p)++ = nfsv4_acl_flag_map[i].c;+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)+				*(*p)++ = '-';+		}+		*(*p)++ = ':';+		switch (type) {+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:+			strcpy(*p, "allow");+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:+			strcpy(*p, "deny");+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:+			strcpy(*p, "audit");+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:+			strcpy(*p, "alarm");+			break;+		}+		*p += strlen(*p);+	}+	if (id != -1) {+		*(*p)++ = ':';+		append_id(p, id);+	}+}++/*+ * Parse a wide ACL text string.+ *+ * The want_type argument may be one of the following:+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL+ *+ * POSIX.1e ACL entries prefixed with "default:" are treated as+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4+ */+int+archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,+    int want_type)+{+	struct {+		const wchar_t *start;+		const wchar_t *end;+	} field[6], name;++	const wchar_t *s, *st;++	int numfields, fields, n, r, sol, ret;+	int type, types, tag, permset, id;+	size_t len;+	wchar_t sep;++	ret = ARCHIVE_OK;+	types = 0;++	switch (want_type) {+	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:+		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;+		__LA_FALLTHROUGH;+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:+		numfields = 5;+		break;+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:+		numfields = 6;+		break;+	default:+		return (ARCHIVE_FATAL);+	}++	while (text != NULL && *text != L'\0') {+		/*+		 * Parse the fields out of the next entry,+		 * advance 'text' to start of next entry.+		 */+		fields = 0;+		do {+			const wchar_t *start, *end;+			next_field_w(&text, &start, &end, &sep);+			if (fields < numfields) {+				field[fields].start = start;+				field[fields].end = end;+			}+			++fields;+		} while (sep == L':');++		/* Set remaining fields to blank. */+		for (n = fields; n < numfields; ++n)+			field[n].start = field[n].end = NULL;++		if (field[0].start != NULL && *(field[0].start) == L'#') {+			/* Comment, skip entry */+			continue;+		}++		n = 0;+		sol = 0;+		id = -1;+		permset = 0;+		name.start = name.end = NULL;++		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			/* POSIX.1e ACLs */+			/*+			 * Default keyword "default:user::rwx"+			 * if found, we have one more field+			 *+			 * We also support old Solaris extension:+			 * "defaultuser::rwx" is the default ACL corresponding+			 * to "user::rwx", etc. valid only for first field+			 */+			s = field[0].start;+			len = field[0].end - field[0].start;+			if (*s == L'd' && (len == 1 || (len >= 7+			    && wmemcmp((s + 1), L"efault", 6) == 0))) {+				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;+				if (len > 7)+					field[0].start += 7;+				else+					n = 1;+			} else+				type = want_type;++			/* Check for a numeric ID in field n+1 or n+3. */+			isint_w(field[n + 1].start, field[n + 1].end, &id);+			/* Field n+3 is optional. */+			if (id == -1 && fields > n+3)+				isint_w(field[n + 3].start, field[n + 3].end,+				    &id);++			tag = 0;+			s = field[n].start;+			st = field[n].start + 1;+			len = field[n].end - field[n].start;++			switch (*s) {+			case L'u':+				if (len == 1 || (len == 4+				    && wmemcmp(st, L"ser", 3) == 0))+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;+				break;+			case L'g':+				if (len == 1 || (len == 5+				    && wmemcmp(st, L"roup", 4) == 0))+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;+				break;+			case L'o':+				if (len == 1 || (len == 5+				    && wmemcmp(st, L"ther", 4) == 0))+					tag = ARCHIVE_ENTRY_ACL_OTHER;+				break;+			case L'm':+				if (len == 1 || (len == 4+				    && wmemcmp(st, L"ask", 3) == 0))+					tag = ARCHIVE_ENTRY_ACL_MASK;+				break;+			default:+					break;+			}++			switch (tag) {+			case ARCHIVE_ENTRY_ACL_OTHER:+			case ARCHIVE_ENTRY_ACL_MASK:+				if (fields == (n + 2)+				    && field[n + 1].start < field[n + 1].end+				    && ismode_w(field[n + 1].start,+				    field[n + 1].end, &permset)) {+					/* This is Solaris-style "other:rwx" */+					sol = 1;+				} else if (fields == (n + 3) &&+				    field[n + 1].start < field[n + 1].end) {+					/* Invalid mask or other field */+					ret = ARCHIVE_WARN;+					continue;+				}+				break;+			case ARCHIVE_ENTRY_ACL_USER_OBJ:+			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+				if (id != -1 ||+				    field[n + 1].start < field[n + 1].end) {+					name = field[n + 1];+					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)+						tag = ARCHIVE_ENTRY_ACL_USER;+					else+						tag = ARCHIVE_ENTRY_ACL_GROUP;+				}+				break;+			default:+				/* Invalid tag, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			}++			/*+			 * Without "default:" we expect mode in field 2+			 * Exception: Solaris other and mask fields+			 */+			if (permset == 0 && !ismode_w(field[n + 2 - sol].start,+			    field[n + 2 - sol].end, &permset)) {+				/* Invalid mode, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			}+		} else {+			/* NFS4 ACLs */+			s = field[0].start;+			len = field[0].end - field[0].start;+			tag = 0;++			switch (len) {+			case 4:+				if (wmemcmp(s, L"user", 4) == 0)+					tag = ARCHIVE_ENTRY_ACL_USER;+				break;+			case 5:+				if (wmemcmp(s, L"group", 5) == 0)+					tag = ARCHIVE_ENTRY_ACL_GROUP;+				break;+			case 6:+				if (wmemcmp(s, L"owner@", 6) == 0)+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;+				else if (wmemcmp(s, L"group@", len) == 0)+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;+				break;+			case 9:+				if (wmemcmp(s, L"everyone@", 9) == 0)+					tag = ARCHIVE_ENTRY_ACL_EVERYONE;+			default:+				break;+			}++			if (tag == 0) {+				/* Invalid tag, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||+			    tag == ARCHIVE_ENTRY_ACL_GROUP) {+				n = 1;+				name = field[1];+				isint_w(name.start, name.end, &id);+			} else+				n = 0;++			if (!is_nfs4_perms_w(field[1 + n].start,+			    field[1 + n].end, &permset)) {+				/* Invalid NFSv4 perms, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			}+			if (!is_nfs4_flags_w(field[2 + n].start,+			    field[2 + n].end, &permset)) {+				/* Invalid NFSv4 flags, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			}+			s = field[3 + n].start;+			len = field[3 + n].end - field[3 + n].start;+			type = 0;+			if (len == 4) {+				if (wmemcmp(s, L"deny", 4) == 0)+					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;+			} else if (len == 5) {+				if (wmemcmp(s, L"allow", 5) == 0)+					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;+				else if (wmemcmp(s, L"audit", 5) == 0)+					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;+				else if (wmemcmp(s, L"alarm", 5) == 0)+					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;+			}+			if (type == 0) {+				/* Invalid entry type, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			}+			isint_w(field[4 + n].start, field[4 + n].end, &id);+		}++		/* Add entry to the internal list. */+		r = archive_acl_add_entry_w_len(acl, type, permset,+		    tag, id, name.start, name.end - name.start);+		if (r < ARCHIVE_WARN)+			return (r);+		if (r != ARCHIVE_OK)+			ret = ARCHIVE_WARN;+		types |= type;+	}++	/* Reset ACL */+	archive_acl_reset(acl, types);++	return (ret);+}++/*+ * Parse a string to a positive decimal integer.  Returns true if+ * the string is non-empty and consists only of decimal digits,+ * false otherwise.+ */+static int+isint_w(const wchar_t *start, const wchar_t *end, int *result)+{+	int n = 0;+	if (start >= end)+		return (0);+	while (start < end) {+		if (*start < '0' || *start > '9')+			return (0);+		if (n > (INT_MAX / 10) ||+		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {+			n = INT_MAX;+		} else {+			n *= 10;+			n += *start - '0';+		}+		start++;+	}+	*result = n;+	return (1);+}++/*+ * Parse a string as a mode field.  Returns true if+ * the string is non-empty and consists only of mode characters,+ * false otherwise.+ */+static int+ismode_w(const wchar_t *start, const wchar_t *end, int *permset)+{+	const wchar_t *p;++	if (start >= end)+		return (0);+	p = start;+	*permset = 0;+	while (p < end) {+		switch (*p++) {+		case L'r': case L'R':+			*permset |= ARCHIVE_ENTRY_ACL_READ;+			break;+		case L'w': case L'W':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE;+			break;+		case L'x': case L'X':+			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;+			break;+		case L'-':+			break;+		default:+			return (0);+		}+	}+	return (1);+}++/*+ * Parse a string as a NFS4 ACL permission field.+ * Returns true if the string is non-empty and consists only of NFS4 ACL+ * permission characters, false otherwise+ */+static int+is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)+{+	const wchar_t *p = start;++	while (p < end) {+		switch (*p++) {+		case L'r':+			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;+			break;+		case L'w':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;+			break;+		case L'x':+			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;+			break;+		case L'p':+			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;+			break;+		case L'D':+			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;+			break;+		case L'd':+			*permset |= ARCHIVE_ENTRY_ACL_DELETE;+			break;+		case L'a':+			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;+			break;+		case L'A':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;+			break;+		case L'R':+			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;+			break;+		case L'W':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;+			break;+		case L'c':+			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;+			break;+		case L'C':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;+			break;+		case L'o':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;+			break;+		case L's':+			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;+			break;+		case L'-':+			break;+		default:+			return(0);+		}+	}+	return (1);+}++/*+ * Parse a string as a NFS4 ACL flags field.+ * Returns true if the string is non-empty and consists only of NFS4 ACL+ * flag characters, false otherwise+ */+static int+is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)+{+	const wchar_t *p = start;++	while (p < end) {+		switch(*p++) {+		case L'f':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;+			break;+		case L'd':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;+			break;+		case L'i':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;+			break;+		case L'n':+			*permset |=+			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;+			break;+		case L'S':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;+			break;+		case L'F':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;+			break;+		case L'I':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;+			break;+		case L'-':+			break;+		default:+			return (0);+		}+	}+	return (1);+}++/*+ * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated+ * to point to just after the separator.  *start points to the first+ * character of the matched text and *end just after the last+ * character of the matched identifier.  In particular *end - *start+ * is the length of the field body, not including leading or trailing+ * whitespace.+ */+static void+next_field_w(const wchar_t **wp, const wchar_t **start,+    const wchar_t **end, wchar_t *sep)+{+	/* Skip leading whitespace to find start of field. */+	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {+		(*wp)++;+	}+	*start = *wp;++	/* Scan for the separator. */+	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&+	    **wp != L'\n' && **wp != L'#') {+		(*wp)++;+	}+	*sep = **wp;++	/* Locate end of field, trim trailing whitespace if necessary */+	if (*wp == *start) {+		*end = *wp;+	} else {+		*end = *wp - 1;+		while (**end == L' ' || **end == L'\t' || **end == L'\n') {+			(*end)--;+		}+		(*end)++;+	}++	/* Handle in-field comments */+	if (*sep == L'#') {+		while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {+			(*wp)++;+		}+		*sep = **wp;+	}++	/* Adjust scanner location. */+	if (**wp != L'\0')+		(*wp)++;+}++/*+ * Parse an ACL text string.+ *+ * The want_type argument may be one of the following:+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL+ *+ * POSIX.1e ACL entries prefixed with "default:" are treated as+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4+ */+int+archive_acl_from_text_l(struct archive_acl *acl, const char *text,+    int want_type, struct archive_string_conv *sc)+{+	struct {+		const char *start;+		const char *end;+	} field[6], name;++	const char *s, *st;+	int numfields, fields, n, r, sol, ret;+	int type, types, tag, permset, id;+	size_t len;+	char sep;++	switch (want_type) {+	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:+		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;+		__LA_FALLTHROUGH;+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:+		numfields = 5;+		break;+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:+		numfields = 6;+		break;+	default:+		return (ARCHIVE_FATAL);+	}++	ret = ARCHIVE_OK;+	types = 0;++	while (text != NULL &&  *text != '\0') {+		/*+		 * Parse the fields out of the next entry,+		 * advance 'text' to start of next entry.+		 */+		fields = 0;+		do {+			const char *start, *end;+			next_field(&text, &start, &end, &sep);+			if (fields < numfields) {+				field[fields].start = start;+				field[fields].end = end;+			}+			++fields;+		} while (sep == ':');++		/* Set remaining fields to blank. */+		for (n = fields; n < numfields; ++n)+			field[n].start = field[n].end = NULL;++		if (field[0].start != NULL && *(field[0].start) == '#') {+			/* Comment, skip entry */+			continue;+		}++		n = 0;+		sol = 0;+		id = -1;+		permset = 0;+		name.start = name.end = NULL;++		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			/* POSIX.1e ACLs */+			/*+			 * Default keyword "default:user::rwx"+			 * if found, we have one more field+			 *+			 * We also support old Solaris extension:+			 * "defaultuser::rwx" is the default ACL corresponding+			 * to "user::rwx", etc. valid only for first field+			 */+			s = field[0].start;+			len = field[0].end - field[0].start;+			if (*s == 'd' && (len == 1 || (len >= 7+			    && memcmp((s + 1), "efault", 6) == 0))) {+				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;+				if (len > 7)+					field[0].start += 7;+				else+					n = 1;+			} else+				type = want_type;++			/* Check for a numeric ID in field n+1 or n+3. */+			isint(field[n + 1].start, field[n + 1].end, &id);+			/* Field n+3 is optional. */+			if (id == -1 && fields > (n + 3))+				isint(field[n + 3].start, field[n + 3].end,+				    &id);++			tag = 0;+			s = field[n].start;+			st = field[n].start + 1;+			len = field[n].end - field[n].start;++			if (len == 0) {+				ret = ARCHIVE_WARN;+				continue;+			}++			switch (*s) {+			case 'u':+				if (len == 1 || (len == 4+				    && memcmp(st, "ser", 3) == 0))+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;+				break;+			case 'g':+				if (len == 1 || (len == 5+				    && memcmp(st, "roup", 4) == 0))+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;+				break;+			case 'o':+				if (len == 1 || (len == 5+				    && memcmp(st, "ther", 4) == 0))+					tag = ARCHIVE_ENTRY_ACL_OTHER;+				break;+			case 'm':+				if (len == 1 || (len == 4+				    && memcmp(st, "ask", 3) == 0))+					tag = ARCHIVE_ENTRY_ACL_MASK;+				break;+			default:+					break;+			}++			switch (tag) {+			case ARCHIVE_ENTRY_ACL_OTHER:+			case ARCHIVE_ENTRY_ACL_MASK:+				if (fields == (n + 2)+				    && field[n + 1].start < field[n + 1].end+				    && ismode(field[n + 1].start,+				    field[n + 1].end, &permset)) {+					/* This is Solaris-style "other:rwx" */+					sol = 1;+				} else if (fields == (n + 3) &&+				    field[n + 1].start < field[n + 1].end) {+					/* Invalid mask or other field */+					ret = ARCHIVE_WARN;+					continue;+				}+				break;+			case ARCHIVE_ENTRY_ACL_USER_OBJ:+			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+				if (id != -1 ||+				    field[n + 1].start < field[n + 1].end) {+					name = field[n + 1];+					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)+						tag = ARCHIVE_ENTRY_ACL_USER;+					else+						tag = ARCHIVE_ENTRY_ACL_GROUP;+				}+				break;+			default:+				/* Invalid tag, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			}++			/*+			 * Without "default:" we expect mode in field 3+			 * Exception: Solaris other and mask fields+			 */+			if (permset == 0 && !ismode(field[n + 2 - sol].start,+			    field[n + 2 - sol].end, &permset)) {+				/* Invalid mode, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			}+		} else {+			/* NFS4 ACLs */+			s = field[0].start;+			len = field[0].end - field[0].start;+			tag = 0;++			switch (len) {+			case 4:+				if (memcmp(s, "user", 4) == 0)+					tag = ARCHIVE_ENTRY_ACL_USER;+				break;+			case 5:+				if (memcmp(s, "group", 5) == 0)+					tag = ARCHIVE_ENTRY_ACL_GROUP;+				break;+			case 6:+				if (memcmp(s, "owner@", 6) == 0)+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;+				else if (memcmp(s, "group@", 6) == 0)+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;+				break;+			case 9:+				if (memcmp(s, "everyone@", 9) == 0)+					tag = ARCHIVE_ENTRY_ACL_EVERYONE;+				break;+			default:+				break;+			}++			if (tag == 0) {+				/* Invalid tag, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||+			    tag == ARCHIVE_ENTRY_ACL_GROUP) {+				n = 1;+				name = field[1];+				isint(name.start, name.end, &id);+			} else+				n = 0;++			if (!is_nfs4_perms(field[1 + n].start,+			    field[1 + n].end, &permset)) {+				/* Invalid NFSv4 perms, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			}+			if (!is_nfs4_flags(field[2 + n].start,+			    field[2 + n].end, &permset)) {+				/* Invalid NFSv4 flags, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			}+			s = field[3 + n].start;+			len = field[3 + n].end - field[3 + n].start;+			type = 0;+			if (len == 4) {+				if (memcmp(s, "deny", 4) == 0)+					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;+			} else if (len == 5) {+				if (memcmp(s, "allow", 5) == 0)+					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;+				else if (memcmp(s, "audit", 5) == 0)+					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;+				else if (memcmp(s, "alarm", 5) == 0)+					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;+			}+			if (type == 0) {+				/* Invalid entry type, skip entry */+				ret = ARCHIVE_WARN;+				continue;+			}+			isint(field[4 + n].start, field[4 + n].end,+			    &id);+		}++		/* Add entry to the internal list. */+		r = archive_acl_add_entry_len_l(acl, type, permset,+		    tag, id, name.start, name.end - name.start, sc);+		if (r < ARCHIVE_WARN)+			return (r);+		if (r != ARCHIVE_OK)+			ret = ARCHIVE_WARN;+		types |= type;+	}++	/* Reset ACL */+	archive_acl_reset(acl, types);++	return (ret);+}++/*+ * Parse a string to a positive decimal integer.  Returns true if+ * the string is non-empty and consists only of decimal digits,+ * false otherwise.+ */+static int+isint(const char *start, const char *end, int *result)+{+	int n = 0;+	if (start >= end)+		return (0);+	while (start < end) {+		if (*start < '0' || *start > '9')+			return (0);+		if (n > (INT_MAX / 10) ||+		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {+			n = INT_MAX;+		} else {+			n *= 10;+			n += *start - '0';+		}+		start++;+	}+	*result = n;+	return (1);+}++/*+ * Parse a string as a mode field.  Returns true if+ * the string is non-empty and consists only of mode characters,+ * false otherwise.+ */+static int+ismode(const char *start, const char *end, int *permset)+{+	const char *p;++	if (start >= end)+		return (0);+	p = start;+	*permset = 0;+	while (p < end) {+		switch (*p++) {+		case 'r': case 'R':+			*permset |= ARCHIVE_ENTRY_ACL_READ;+			break;+		case 'w': case 'W':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE;+			break;+		case 'x': case 'X':+			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;+			break;+		case '-':+			break;+		default:+			return (0);+		}+	}+	return (1);+}++/*+ * Parse a string as a NFS4 ACL permission field.+ * Returns true if the string is non-empty and consists only of NFS4 ACL+ * permission characters, false otherwise+ */+static int+is_nfs4_perms(const char *start, const char *end, int *permset)+{+	const char *p = start;++	while (p < end) {+		switch (*p++) {+		case 'r':+			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;+			break;+		case 'w':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;+			break;+		case 'x':+			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;+			break;+		case 'p':+			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;+			break;+		case 'D':+			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;+			break;+		case 'd':+			*permset |= ARCHIVE_ENTRY_ACL_DELETE;+			break;+		case 'a':+			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;+			break;+		case 'A':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;+			break;+		case 'R':+			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;+			break;+		case 'W':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;+			break;+		case 'c':+			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;+			break;+		case 'C':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;+			break;+		case 'o':+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;+			break;+		case 's':+			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;+			break;+		case '-':+			break;+		default:+			return(0);+		}+	}+	return (1);+}++/*+ * Parse a string as a NFS4 ACL flags field.+ * Returns true if the string is non-empty and consists only of NFS4 ACL+ * flag characters, false otherwise+ */+static int+is_nfs4_flags(const char *start, const char *end, int *permset)+{+	const char *p = start;++	while (p < end) {+		switch(*p++) {+		case 'f':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;+			break;+		case 'd':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;+			break;+		case 'i':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;+			break;+		case 'n':+			*permset |=+			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;+			break;+		case 'S':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;+			break;+		case 'F':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;+			break;+		case 'I':+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;+			break;+		case '-':+			break;+		default:+			return (0);+		}+	}+	return (1);+}++/*+ * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated+ * to point to just after the separator.  *start points to the first+ * character of the matched text and *end just after the last+ * character of the matched identifier.  In particular *end - *start+ * is the length of the field body, not including leading or trailing+ * whitespace.+ */+static void+next_field(const char **p, const char **start,+    const char **end, char *sep)+{+	/* Skip leading whitespace to find start of field. */+	while (**p == ' ' || **p == '\t' || **p == '\n') {+		(*p)++;+	}+	*start = *p;++	/* Scan for the separator. */+	while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' &&+	    **p != '#') {+		(*p)++;+	}+	*sep = **p;++	/* Locate end of field, trim trailing whitespace if necessary */+	if (*p == *start) {+		*end = *p;+	} else {+		*end = *p - 1;+		while (**end == ' ' || **end == '\t' || **end == '\n') {+			(*end)--;+		}+		(*end)++;+	}++	/* Handle in-field comments */+	if (*sep == '#') {+		while (**p != '\0' && **p != ',' && **p != '\n') {+			(*p)++;+		}+		*sep = **p;+	}++	/* Adjust scanner location. */+	if (**p != '\0')+		(*p)++;+}
+ c/archive_acl_private.h view
@@ -0,0 +1,83 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ */++#ifndef ARCHIVE_ACL_PRIVATE_H_INCLUDED+#define ARCHIVE_ACL_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++#include "archive_string.h"++struct archive_acl_entry {+	struct archive_acl_entry *next;+	int	type;			/* E.g., access or default */+	int	tag;			/* E.g., user/group/other/mask */+	int	permset;		/* r/w/x bits */+	int	id;			/* uid/gid for user/group */+	struct archive_mstring name;		/* uname/gname */+};++struct archive_acl {+	mode_t		mode;+	struct archive_acl_entry	*acl_head;+	struct archive_acl_entry	*acl_p;+	int		 acl_state;	/* See acl_next for details. */+	wchar_t		*acl_text_w;+	char		*acl_text;+	int		 acl_types;+};++void archive_acl_clear(struct archive_acl *);+void archive_acl_copy(struct archive_acl *, struct archive_acl *);+int archive_acl_count(struct archive_acl *, int);+int archive_acl_types(struct archive_acl *);+int archive_acl_reset(struct archive_acl *, int);+int archive_acl_next(struct archive *, struct archive_acl *, int,+    int *, int *, int *, int *, const char **);++int archive_acl_add_entry(struct archive_acl *, int, int, int, int, const char *);+int archive_acl_add_entry_w_len(struct archive_acl *,+    int, int, int, int, const wchar_t *, size_t);+int archive_acl_add_entry_len(struct archive_acl *,+    int, int, int, int, const char *, size_t);++wchar_t *archive_acl_to_text_w(struct archive_acl *, ssize_t *, int,+    struct archive *);+char *archive_acl_to_text_l(struct archive_acl *, ssize_t *, int,+    struct archive_string_conv *);++/*+ * ACL text parser.+ */+int archive_acl_from_text_w(struct archive_acl *, const wchar_t * /* wtext */,+    int /* type */);+int archive_acl_from_text_l(struct archive_acl *, const char * /* text */,+    int /* type */, struct archive_string_conv *);++#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
+ c/archive_blake2.h view
@@ -0,0 +1,195 @@+/*+   BLAKE2 reference source code package - reference C implementations++   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the+   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at+   your option.  The terms of these licenses can be found at:++   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0+   - OpenSSL license   : https://www.openssl.org/source/license.html+   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0++   More information about the BLAKE2 hash function can be found at+   https://blake2.net.+*/++#ifndef ARCHIVE_BLAKE2_H+#define ARCHIVE_BLAKE2_H++#include <stddef.h>+#include <stdint.h>++#if defined(_MSC_VER)+#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))+#else+#define BLAKE2_PACKED(x) x __attribute__((packed))+#endif++#if defined(__cplusplus)+extern "C" {+#endif++  enum blake2s_constant+  {+    BLAKE2S_BLOCKBYTES = 64,+    BLAKE2S_OUTBYTES   = 32,+    BLAKE2S_KEYBYTES   = 32,+    BLAKE2S_SALTBYTES  = 8,+    BLAKE2S_PERSONALBYTES = 8+  };++  enum blake2b_constant+  {+    BLAKE2B_BLOCKBYTES = 128,+    BLAKE2B_OUTBYTES   = 64,+    BLAKE2B_KEYBYTES   = 64,+    BLAKE2B_SALTBYTES  = 16,+    BLAKE2B_PERSONALBYTES = 16+  };++  typedef struct blake2s_state__+  {+    uint32_t h[8];+    uint32_t t[2];+    uint32_t f[2];+    uint8_t  buf[BLAKE2S_BLOCKBYTES];+    size_t   buflen;+    size_t   outlen;+    uint8_t  last_node;+  } blake2s_state;++  typedef struct blake2b_state__+  {+    uint64_t h[8];+    uint64_t t[2];+    uint64_t f[2];+    uint8_t  buf[BLAKE2B_BLOCKBYTES];+    size_t   buflen;+    size_t   outlen;+    uint8_t  last_node;+  } blake2b_state;++  typedef struct blake2sp_state__+  {+    blake2s_state S[8][1];+    blake2s_state R[1];+    uint8_t       buf[8 * BLAKE2S_BLOCKBYTES];+    size_t        buflen;+    size_t        outlen;+  } blake2sp_state;++  typedef struct blake2bp_state__+  {+    blake2b_state S[4][1];+    blake2b_state R[1];+    uint8_t       buf[4 * BLAKE2B_BLOCKBYTES];+    size_t        buflen;+    size_t        outlen;+  } blake2bp_state;++  BLAKE2_PACKED(struct blake2s_param__+  {+    uint8_t  digest_length; /* 1 */+    uint8_t  key_length;    /* 2 */+    uint8_t  fanout;        /* 3 */+    uint8_t  depth;         /* 4 */+    uint32_t leaf_length;   /* 8 */+    uint32_t node_offset;  /* 12 */+    uint16_t xof_length;    /* 14 */+    uint8_t  node_depth;    /* 15 */+    uint8_t  inner_length;  /* 16 */+    /* uint8_t  reserved[0]; */+    uint8_t  salt[BLAKE2S_SALTBYTES]; /* 24 */+    uint8_t  personal[BLAKE2S_PERSONALBYTES];  /* 32 */+  });++  typedef struct blake2s_param__ blake2s_param;++  BLAKE2_PACKED(struct blake2b_param__+  {+    uint8_t  digest_length; /* 1 */+    uint8_t  key_length;    /* 2 */+    uint8_t  fanout;        /* 3 */+    uint8_t  depth;         /* 4 */+    uint32_t leaf_length;   /* 8 */+    uint32_t node_offset;   /* 12 */+    uint32_t xof_length;    /* 16 */+    uint8_t  node_depth;    /* 17 */+    uint8_t  inner_length;  /* 18 */+    uint8_t  reserved[14];  /* 32 */+    uint8_t  salt[BLAKE2B_SALTBYTES]; /* 48 */+    uint8_t  personal[BLAKE2B_PERSONALBYTES];  /* 64 */+  });++  typedef struct blake2b_param__ blake2b_param;++  typedef struct blake2xs_state__+  {+    blake2s_state S[1];+    blake2s_param P[1];+  } blake2xs_state;++  typedef struct blake2xb_state__+  {+    blake2b_state S[1];+    blake2b_param P[1];+  } blake2xb_state;++  /* Padded structs result in a compile-time error */+  enum {+    BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES),+    BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES)+  };++  /* Streaming API */+  int blake2s_init( blake2s_state *S, size_t outlen );+  int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );+  int blake2s_init_param( blake2s_state *S, const blake2s_param *P );+  int blake2s_update( blake2s_state *S, const void *in, size_t inlen );+  int blake2s_final( blake2s_state *S, void *out, size_t outlen );++  int blake2b_init( blake2b_state *S, size_t outlen );+  int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );+  int blake2b_init_param( blake2b_state *S, const blake2b_param *P );+  int blake2b_update( blake2b_state *S, const void *in, size_t inlen );+  int blake2b_final( blake2b_state *S, void *out, size_t outlen );++  int blake2sp_init( blake2sp_state *S, size_t outlen );+  int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );+  int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen );+  int blake2sp_final( blake2sp_state *S, void *out, size_t outlen );++  int blake2bp_init( blake2bp_state *S, size_t outlen );+  int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );+  int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen );+  int blake2bp_final( blake2bp_state *S, void *out, size_t outlen );++  /* Variable output length API */+  int blake2xs_init( blake2xs_state *S, const size_t outlen );+  int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen );+  int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen );+  int blake2xs_final(blake2xs_state *S, void *out, size_t outlen);++  int blake2xb_init( blake2xb_state *S, const size_t outlen );+  int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen );+  int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen );+  int blake2xb_final(blake2xb_state *S, void *out, size_t outlen);++  /* Simple API */+  int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );+  int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );++  int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );+  int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );++  int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );+  int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );++  /* This is simply an alias for blake2b */+  int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );++#if defined(__cplusplus)+}+#endif++#endif
+ c/archive_blake2_impl.h view
@@ -0,0 +1,161 @@+/*+   BLAKE2 reference source code package - reference C implementations++   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the+   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at+   your option.  The terms of these licenses can be found at:++   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0+   - OpenSSL license   : https://www.openssl.org/source/license.html+   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0++   More information about the BLAKE2 hash function can be found at+   https://blake2.net.+*/++#ifndef ARCHIVE_BLAKE2_IMPL_H+#define ARCHIVE_BLAKE2_IMPL_H++#include <stdint.h>+#include <string.h>++#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)+  #if   defined(_MSC_VER)+    #define BLAKE2_INLINE __inline+  #elif defined(__GNUC__)+    #define BLAKE2_INLINE __inline__+  #else+    #define BLAKE2_INLINE+  #endif+#else+  #define BLAKE2_INLINE inline+#endif++static BLAKE2_INLINE uint32_t load32( const void *src )+{+#if defined(NATIVE_LITTLE_ENDIAN)+  uint32_t w;+  memcpy(&w, src, sizeof w);+  return w;+#else+  const uint8_t *p = ( const uint8_t * )src;+  return (( uint32_t )( p[0] ) <<  0) |+         (( uint32_t )( p[1] ) <<  8) |+         (( uint32_t )( p[2] ) << 16) |+         (( uint32_t )( p[3] ) << 24) ;+#endif+}++static BLAKE2_INLINE uint64_t load64( const void *src )+{+#if defined(NATIVE_LITTLE_ENDIAN)+  uint64_t w;+  memcpy(&w, src, sizeof w);+  return w;+#else+  const uint8_t *p = ( const uint8_t * )src;+  return (( uint64_t )( p[0] ) <<  0) |+         (( uint64_t )( p[1] ) <<  8) |+         (( uint64_t )( p[2] ) << 16) |+         (( uint64_t )( p[3] ) << 24) |+         (( uint64_t )( p[4] ) << 32) |+         (( uint64_t )( p[5] ) << 40) |+         (( uint64_t )( p[6] ) << 48) |+         (( uint64_t )( p[7] ) << 56) ;+#endif+}++static BLAKE2_INLINE uint16_t load16( const void *src )+{+#if defined(NATIVE_LITTLE_ENDIAN)+  uint16_t w;+  memcpy(&w, src, sizeof w);+  return w;+#else+  const uint8_t *p = ( const uint8_t * )src;+  return ( uint16_t )((( uint32_t )( p[0] ) <<  0) |+                      (( uint32_t )( p[1] ) <<  8));+#endif+}++static BLAKE2_INLINE void store16( void *dst, uint16_t w )+{+#if defined(NATIVE_LITTLE_ENDIAN)+  memcpy(dst, &w, sizeof w);+#else+  uint8_t *p = ( uint8_t * )dst;+  *p++ = ( uint8_t )w; w >>= 8;+  *p++ = ( uint8_t )w;+#endif+}++static BLAKE2_INLINE void store32( void *dst, uint32_t w )+{+#if defined(NATIVE_LITTLE_ENDIAN)+  memcpy(dst, &w, sizeof w);+#else+  uint8_t *p = ( uint8_t * )dst;+  p[0] = (uint8_t)(w >>  0);+  p[1] = (uint8_t)(w >>  8);+  p[2] = (uint8_t)(w >> 16);+  p[3] = (uint8_t)(w >> 24);+#endif+}++static BLAKE2_INLINE void store64( void *dst, uint64_t w )+{+#if defined(NATIVE_LITTLE_ENDIAN)+  memcpy(dst, &w, sizeof w);+#else+  uint8_t *p = ( uint8_t * )dst;+  p[0] = (uint8_t)(w >>  0);+  p[1] = (uint8_t)(w >>  8);+  p[2] = (uint8_t)(w >> 16);+  p[3] = (uint8_t)(w >> 24);+  p[4] = (uint8_t)(w >> 32);+  p[5] = (uint8_t)(w >> 40);+  p[6] = (uint8_t)(w >> 48);+  p[7] = (uint8_t)(w >> 56);+#endif+}++static BLAKE2_INLINE uint64_t load48( const void *src )+{+  const uint8_t *p = ( const uint8_t * )src;+  return (( uint64_t )( p[0] ) <<  0) |+         (( uint64_t )( p[1] ) <<  8) |+         (( uint64_t )( p[2] ) << 16) |+         (( uint64_t )( p[3] ) << 24) |+         (( uint64_t )( p[4] ) << 32) |+         (( uint64_t )( p[5] ) << 40) ;+}++static BLAKE2_INLINE void store48( void *dst, uint64_t w )+{+  uint8_t *p = ( uint8_t * )dst;+  p[0] = (uint8_t)(w >>  0);+  p[1] = (uint8_t)(w >>  8);+  p[2] = (uint8_t)(w >> 16);+  p[3] = (uint8_t)(w >> 24);+  p[4] = (uint8_t)(w >> 32);+  p[5] = (uint8_t)(w >> 40);+}++static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c )+{+  return ( w >> c ) | ( w << ( 32 - c ) );+}++static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c )+{+  return ( w >> c ) | ( w << ( 64 - c ) );+}++/* prevents compiler optimizing out memset() */+static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n)+{+  static void *(*const volatile memset_v)(void *, int, size_t) = &memset;+  memset_v(v, 0, n);+}++#endif
+ c/archive_blake2s_ref.c view
@@ -0,0 +1,367 @@+/*+   BLAKE2 reference source code package - reference C implementations++   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the+   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at+   your option.  The terms of these licenses can be found at:++   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0+   - OpenSSL license   : https://www.openssl.org/source/license.html+   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0++   More information about the BLAKE2 hash function can be found at+   https://blake2.net.+*/++#include <stdint.h>+#include <string.h>+#include <stdio.h>++#include "archive_blake2.h"+#include "archive_blake2_impl.h"++static const uint32_t blake2s_IV[8] =+{+  0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,+  0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL+};++static const uint8_t blake2s_sigma[10][16] =+{+  {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,+  { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 } ,+  { 11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4 } ,+  {  7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8 } ,+  {  9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13 } ,+  {  2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9 } ,+  { 12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11 } ,+  { 13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10 } ,+  {  6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5 } ,+  { 10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13 , 0 } ,+};++static void blake2s_set_lastnode( blake2s_state *S )+{+  S->f[1] = (uint32_t)-1;+}++/* Some helper functions, not necessarily useful */+static int blake2s_is_lastblock( const blake2s_state *S )+{+  return S->f[0] != 0;+}++static void blake2s_set_lastblock( blake2s_state *S )+{+  if( S->last_node ) blake2s_set_lastnode( S );++  S->f[0] = (uint32_t)-1;+}++static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc )+{+  S->t[0] += inc;+  S->t[1] += ( S->t[0] < inc );+}++static void blake2s_init0( blake2s_state *S )+{+  size_t i;+  memset( S, 0, sizeof( blake2s_state ) );++  for( i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i];+}++/* init2 xors IV with input parameter block */+int blake2s_init_param( blake2s_state *S, const blake2s_param *P )+{+  const unsigned char *p = ( const unsigned char * )( P );+  size_t i;++  blake2s_init0( S );++  /* IV XOR ParamBlock */+  for( i = 0; i < 8; ++i )+    S->h[i] ^= load32( &p[i * 4] );++  S->outlen = P->digest_length;+  return 0;+}+++/* Sequential blake2s initialization */+int blake2s_init( blake2s_state *S, size_t outlen )+{+  blake2s_param P[1];++  /* Move interval verification here? */+  if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;++  P->digest_length = (uint8_t)outlen;+  P->key_length    = 0;+  P->fanout        = 1;+  P->depth         = 1;+  store32( &P->leaf_length, 0 );+  store32( &P->node_offset, 0 );+  store16( &P->xof_length, 0 );+  P->node_depth    = 0;+  P->inner_length  = 0;+  /* memset(P->reserved, 0, sizeof(P->reserved) ); */+  memset( P->salt,     0, sizeof( P->salt ) );+  memset( P->personal, 0, sizeof( P->personal ) );+  return blake2s_init_param( S, P );+}++int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )+{+  blake2s_param P[1];++  if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;++  if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;++  P->digest_length = (uint8_t)outlen;+  P->key_length    = (uint8_t)keylen;+  P->fanout        = 1;+  P->depth         = 1;+  store32( &P->leaf_length, 0 );+  store32( &P->node_offset, 0 );+  store16( &P->xof_length, 0 );+  P->node_depth    = 0;+  P->inner_length  = 0;+  /* memset(P->reserved, 0, sizeof(P->reserved) ); */+  memset( P->salt,     0, sizeof( P->salt ) );+  memset( P->personal, 0, sizeof( P->personal ) );++  if( blake2s_init_param( S, P ) < 0 ) return -1;++  {+    uint8_t block[BLAKE2S_BLOCKBYTES];+    memset( block, 0, BLAKE2S_BLOCKBYTES );+    memcpy( block, key, keylen );+    blake2s_update( S, block, BLAKE2S_BLOCKBYTES );+    secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */+  }+  return 0;+}++#define G(r,i,a,b,c,d)                      \+  do {                                      \+    a = a + b + m[blake2s_sigma[r][2*i+0]]; \+    d = rotr32(d ^ a, 16);                  \+    c = c + d;                              \+    b = rotr32(b ^ c, 12);                  \+    a = a + b + m[blake2s_sigma[r][2*i+1]]; \+    d = rotr32(d ^ a, 8);                   \+    c = c + d;                              \+    b = rotr32(b ^ c, 7);                   \+  } while(0)++#define ROUND(r)                    \+  do {                              \+    G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \+    G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \+    G(r,2,v[ 2],v[ 6],v[10],v[14]); \+    G(r,3,v[ 3],v[ 7],v[11],v[15]); \+    G(r,4,v[ 0],v[ 5],v[10],v[15]); \+    G(r,5,v[ 1],v[ 6],v[11],v[12]); \+    G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \+    G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \+  } while(0)++static void blake2s_compress( blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES] )+{+  uint32_t m[16];+  uint32_t v[16];+  size_t i;++  for( i = 0; i < 16; ++i ) {+    m[i] = load32( in + i * sizeof( m[i] ) );+  }++  for( i = 0; i < 8; ++i ) {+    v[i] = S->h[i];+  }++  v[ 8] = blake2s_IV[0];+  v[ 9] = blake2s_IV[1];+  v[10] = blake2s_IV[2];+  v[11] = blake2s_IV[3];+  v[12] = S->t[0] ^ blake2s_IV[4];+  v[13] = S->t[1] ^ blake2s_IV[5];+  v[14] = S->f[0] ^ blake2s_IV[6];+  v[15] = S->f[1] ^ blake2s_IV[7];++  ROUND( 0 );+  ROUND( 1 );+  ROUND( 2 );+  ROUND( 3 );+  ROUND( 4 );+  ROUND( 5 );+  ROUND( 6 );+  ROUND( 7 );+  ROUND( 8 );+  ROUND( 9 );++  for( i = 0; i < 8; ++i ) {+    S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];+  }+}++#undef G+#undef ROUND++int blake2s_update( blake2s_state *S, const void *pin, size_t inlen )+{+  const unsigned char * in = (const unsigned char *)pin;+  if( inlen > 0 )+  {+    size_t left = S->buflen;+    size_t fill = BLAKE2S_BLOCKBYTES - left;+    if( inlen > fill )+    {+      S->buflen = 0;+      memcpy( S->buf + left, in, fill ); /* Fill buffer */+      blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );+      blake2s_compress( S, S->buf ); /* Compress */+      in += fill; inlen -= fill;+      while(inlen > BLAKE2S_BLOCKBYTES) {+        blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES);+        blake2s_compress( S, in );+        in += BLAKE2S_BLOCKBYTES;+        inlen -= BLAKE2S_BLOCKBYTES;+      }+    }+    memcpy( S->buf + S->buflen, in, inlen );+    S->buflen += inlen;+  }+  return 0;+}++int blake2s_final( blake2s_state *S, void *out, size_t outlen )+{+  uint8_t buffer[BLAKE2S_OUTBYTES] = {0};+  size_t i;++  if( out == NULL || outlen < S->outlen )+    return -1;++  if( blake2s_is_lastblock( S ) )+    return -1;++  blake2s_increment_counter( S, ( uint32_t )S->buflen );+  blake2s_set_lastblock( S );+  memset( S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */+  blake2s_compress( S, S->buf );++  for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */+    store32( buffer + sizeof( S->h[i] ) * i, S->h[i] );++  memcpy( out, buffer, outlen );+  secure_zero_memory(buffer, sizeof(buffer));+  return 0;+}++int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )+{+  blake2s_state S[1];++  /* Verify parameters */+  if ( NULL == in && inlen > 0 ) return -1;++  if ( NULL == out ) return -1;++  if ( NULL == key && keylen > 0) return -1;++  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;++  if( keylen > BLAKE2S_KEYBYTES ) return -1;++  if( keylen > 0 )+  {+    if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;+  }+  else+  {+    if( blake2s_init( S, outlen ) < 0 ) return -1;+  }++  blake2s_update( S, ( const uint8_t * )in, inlen );+  blake2s_final( S, out, outlen );+  return 0;+}++#if defined(SUPERCOP)+int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )+{+  return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 );+}+#endif++#if defined(BLAKE2S_SELFTEST)+#include <string.h>+#include "blake2-kat.h"+int main( void )+{+  uint8_t key[BLAKE2S_KEYBYTES];+  uint8_t buf[BLAKE2_KAT_LENGTH];+  size_t i, step;++  for( i = 0; i < BLAKE2S_KEYBYTES; ++i )+    key[i] = ( uint8_t )i;++  for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )+    buf[i] = ( uint8_t )i;++  /* Test simple API */+  for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )+  {+    uint8_t hash[BLAKE2S_OUTBYTES];+    blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );++    if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) )+    {+      goto fail;+    }+  }++  /* Test streaming API */+  for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) {+    for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {+      uint8_t hash[BLAKE2S_OUTBYTES];+      blake2s_state S;+      uint8_t * p = buf;+      size_t mlen = i;+      int err = 0;++      if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {+        goto fail;+      }++      while (mlen >= step) {+        if ( (err = blake2s_update(&S, p, step)) < 0 ) {+          goto fail;+        }+        mlen -= step;+        p += step;+      }+      if ( (err = blake2s_update(&S, p, mlen)) < 0) {+        goto fail;+      }+      if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {+        goto fail;+      }++      if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) {+        goto fail;+      }+    }+  }++  puts( "ok" );+  return 0;+fail:+  puts("error");+  return -1;+}+#endif
+ c/archive_blake2sp_ref.c view
@@ -0,0 +1,359 @@+/*+   BLAKE2 reference source code package - reference C implementations++   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the+   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at+   your option.  The terms of these licenses can be found at:++   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0+   - OpenSSL license   : https://www.openssl.org/source/license.html+   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0++   More information about the BLAKE2 hash function can be found at+   https://blake2.net.+*/++#include <stdlib.h>+#include <string.h>+#include <stdio.h>++#if defined(_OPENMP)+#include <omp.h>+#endif++#include "archive_blake2.h"+#include "archive_blake2_impl.h"++#define PARALLELISM_DEGREE 8++/*+  blake2sp_init_param defaults to setting the expecting output length+  from the digest_length parameter block field.++  In some cases, however, we do not want this, as the output length+  of these instances is given by inner_length instead.+*/+static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P )+{+  int err = blake2s_init_param(S, P);+  S->outlen = P->inner_length;+  return err;+}++static int blake2sp_init_leaf( blake2s_state *S, size_t outlen, size_t keylen, uint32_t offset )+{+  blake2s_param P[1];+  P->digest_length = (uint8_t)outlen;+  P->key_length = (uint8_t)keylen;+  P->fanout = PARALLELISM_DEGREE;+  P->depth = 2;+  store32( &P->leaf_length, 0 );+  store32( &P->node_offset, offset );+  store16( &P->xof_length, 0 );+  P->node_depth = 0;+  P->inner_length = BLAKE2S_OUTBYTES;+  memset( P->salt, 0, sizeof( P->salt ) );+  memset( P->personal, 0, sizeof( P->personal ) );+  return blake2sp_init_leaf_param( S, P );+}++static int blake2sp_init_root( blake2s_state *S, size_t outlen, size_t keylen )+{+  blake2s_param P[1];+  P->digest_length = (uint8_t)outlen;+  P->key_length = (uint8_t)keylen;+  P->fanout = PARALLELISM_DEGREE;+  P->depth = 2;+  store32( &P->leaf_length, 0 );+  store32( &P->node_offset, 0 );+  store16( &P->xof_length, 0 );+  P->node_depth = 1;+  P->inner_length = BLAKE2S_OUTBYTES;+  memset( P->salt, 0, sizeof( P->salt ) );+  memset( P->personal, 0, sizeof( P->personal ) );+  return blake2s_init_param( S, P );+}+++int blake2sp_init( blake2sp_state *S, size_t outlen )+{+  size_t i;++  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;++  memset( S->buf, 0, sizeof( S->buf ) );+  S->buflen = 0;+  S->outlen = outlen;++  if( blake2sp_init_root( S->R, outlen, 0 ) < 0 )+    return -1;++  for( i = 0; i < PARALLELISM_DEGREE; ++i )+    if( blake2sp_init_leaf( S->S[i], outlen, 0, (uint32_t)i ) < 0 ) return -1;++  S->R->last_node = 1;+  S->S[PARALLELISM_DEGREE - 1]->last_node = 1;+  return 0;+}++int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen )+{+  size_t i;++  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;++  if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;++  memset( S->buf, 0, sizeof( S->buf ) );+  S->buflen = 0;+  S->outlen = outlen;++  if( blake2sp_init_root( S->R, outlen, keylen ) < 0 )+    return -1;++  for( i = 0; i < PARALLELISM_DEGREE; ++i )+    if( blake2sp_init_leaf( S->S[i], outlen, keylen, (uint32_t)i ) < 0 ) return -1;++  S->R->last_node = 1;+  S->S[PARALLELISM_DEGREE - 1]->last_node = 1;+  {+    uint8_t block[BLAKE2S_BLOCKBYTES];+    memset( block, 0, BLAKE2S_BLOCKBYTES );+    memcpy( block, key, keylen );++    for( i = 0; i < PARALLELISM_DEGREE; ++i )+      blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES );++    secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */+  }+  return 0;+}+++int blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen )+{+  const unsigned char * in = (const unsigned char *)pin;+  size_t left = S->buflen;+  size_t fill = sizeof( S->buf ) - left;+  size_t i;++  if( left && inlen >= fill )+  {+    memcpy( S->buf + left, in, fill );++    for( i = 0; i < PARALLELISM_DEGREE; ++i )+      blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );++    in += fill;+    inlen -= fill;+    left = 0;+  }++#if defined(_OPENMP)+  #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE)+#else+  for( i = 0; i < PARALLELISM_DEGREE; ++i )+#endif+  {+#if defined(_OPENMP)+    size_t      i = omp_get_thread_num();+#endif+    size_t inlen__ = inlen;+    const unsigned char *in__ = ( const unsigned char * )in;+    in__ += i * BLAKE2S_BLOCKBYTES;++    while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )+    {+      blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES );+      in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;+      inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;+    }+  }++  in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );+  inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;++  if( inlen > 0 )+    memcpy( S->buf + left, in, inlen );++  S->buflen = left + inlen;+  return 0;+}+++int blake2sp_final( blake2sp_state *S, void *out, size_t outlen )+{+  uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];+  size_t i;++  if(out == NULL || outlen < S->outlen) {+    return -1;+  }++  for( i = 0; i < PARALLELISM_DEGREE; ++i )+  {+    if( S->buflen > i * BLAKE2S_BLOCKBYTES )+    {+      size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;++      if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;++      blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );+    }++    blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES );+  }++  for( i = 0; i < PARALLELISM_DEGREE; ++i )+    blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );++  return blake2s_final( S->R, out, S->outlen );+}+++int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )+{+  uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];+  blake2s_state S[PARALLELISM_DEGREE][1];+  blake2s_state FS[1];+  size_t i;++  /* Verify parameters */+  if ( NULL == in && inlen > 0 ) return -1;++  if ( NULL == out ) return -1;++  if ( NULL == key && keylen > 0) return -1;++  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;++  if( keylen > BLAKE2S_KEYBYTES ) return -1;++  for( i = 0; i < PARALLELISM_DEGREE; ++i )+    if( blake2sp_init_leaf( S[i], outlen, keylen, (uint32_t)i ) < 0 ) return -1;++  S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */++  if( keylen > 0 )+  {+    uint8_t block[BLAKE2S_BLOCKBYTES];+    memset( block, 0, BLAKE2S_BLOCKBYTES );+    memcpy( block, key, keylen );++    for( i = 0; i < PARALLELISM_DEGREE; ++i )+      blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES );++    secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */+  }++#if defined(_OPENMP)+  #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE)+#else++  for( i = 0; i < PARALLELISM_DEGREE; ++i )+#endif+  {+#if defined(_OPENMP)+    size_t      i = omp_get_thread_num();+#endif+    size_t inlen__ = inlen;+    const unsigned char *in__ = ( const unsigned char * )in;+    in__ += i * BLAKE2S_BLOCKBYTES;++    while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )+    {+      blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES );+      in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;+      inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;+    }++    if( inlen__ > i * BLAKE2S_BLOCKBYTES )+    {+      const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES;+      const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES;+      blake2s_update( S[i], in__, len );+    }++    blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES );+  }++  if( blake2sp_init_root( FS, outlen, keylen ) < 0 )+    return -1;++  FS->last_node = 1;++  for( i = 0; i < PARALLELISM_DEGREE; ++i )+    blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );++  return blake2s_final( FS, out, outlen );+}++++#if defined(BLAKE2SP_SELFTEST)+#include <string.h>+#include "blake2-kat.h"+int main( void )+{+  uint8_t key[BLAKE2S_KEYBYTES];+  uint8_t buf[BLAKE2_KAT_LENGTH];+  size_t i, step;++  for( i = 0; i < BLAKE2S_KEYBYTES; ++i )+    key[i] = ( uint8_t )i;++  for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )+    buf[i] = ( uint8_t )i;++  /* Test simple API */+  for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )+  {+    uint8_t hash[BLAKE2S_OUTBYTES];+    blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );++    if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) )+    {+      goto fail;+    }+  }++  /* Test streaming API */+  for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) {+    for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {+      uint8_t hash[BLAKE2S_OUTBYTES];+      blake2sp_state S;+      uint8_t * p = buf;+      size_t mlen = i;+      int err = 0;++      if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {+        goto fail;+      }++      while (mlen >= step) {+        if ( (err = blake2sp_update(&S, p, step)) < 0 ) {+          goto fail;+        }+        mlen -= step;+        p += step;+      }+      if ( (err = blake2sp_update(&S, p, mlen)) < 0) {+        goto fail;+      }+      if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {+        goto fail;+      }++      if (0 != memcmp(hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES)) {+        goto fail;+      }+    }+  }++  puts( "ok" );+  return 0;+fail:+  puts("error");+  return -1;+}+#endif
+ c/archive_check_magic.c view
@@ -0,0 +1,175 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_check_magic.c 201089 2009-12-28 02:20:23Z kientzle $");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif++#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#if defined(_WIN32) && !defined(__CYGWIN__)+#include <windows.h>+#include <winbase.h>+#endif++#include "archive_private.h"++static void+errmsg(const char *m)+{+	size_t s = strlen(m);+	ssize_t written;++	while (s > 0) {+		written = write(2, m, strlen(m));+		if (written <= 0)+			return;+		m += written;+		s -= written;+	}+}++static __LA_DEAD void+diediedie(void)+{+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)+	/* Cause a breakpoint exception  */+	DebugBreak();+#endif+	abort();        /* Terminate the program abnormally. */+}++static const char *+state_name(unsigned s)+{+	switch (s) {+	case ARCHIVE_STATE_NEW:		return ("new");+	case ARCHIVE_STATE_HEADER:	return ("header");+	case ARCHIVE_STATE_DATA:	return ("data");+	case ARCHIVE_STATE_EOF:		return ("eof");+	case ARCHIVE_STATE_CLOSED:	return ("closed");+	case ARCHIVE_STATE_FATAL:	return ("fatal");+	default:			return ("??");+	}+}++static const char *+archive_handle_type_name(unsigned m)+{+	switch (m) {+	case ARCHIVE_WRITE_MAGIC:	return ("archive_write");+	case ARCHIVE_READ_MAGIC:	return ("archive_read");+	case ARCHIVE_WRITE_DISK_MAGIC:	return ("archive_write_disk");+	case ARCHIVE_READ_DISK_MAGIC:	return ("archive_read_disk");+	case ARCHIVE_MATCH_MAGIC:	return ("archive_match");+	default:			return NULL;+	}+}+++static char *+write_all_states(char *buff, unsigned int states)+{+	unsigned int lowbit;++	buff[0] = '\0';++	/* A trick for computing the lowest set bit. */+	while ((lowbit = states & (1 + ~states)) != 0) {+		states &= ~lowbit;		/* Clear the low bit. */+		strcat(buff, state_name(lowbit));+		if (states != 0)+			strcat(buff, "/");+	}+	return buff;+}++/*+ * Check magic value and current state.+ *   Magic value mismatches are fatal and result in calls to abort().+ *   State mismatches return ARCHIVE_FATAL.+ *   Otherwise, returns ARCHIVE_OK.+ *+ * This is designed to catch serious programming errors that violate+ * the libarchive API.+ */+int+__archive_check_magic(struct archive *a, unsigned int magic,+    unsigned int state, const char *function)+{+	char states1[64];+	char states2[64];+	const char *handle_type;++	/*+	 * If this isn't some form of archive handle,+	 * then the library user has screwed up so bad that+	 * we don't even have a reliable way to report an error.+	 */+	handle_type = archive_handle_type_name(a->magic);++	if (!handle_type) {+		errmsg("PROGRAMMER ERROR: Function ");+		errmsg(function);+		errmsg(" invoked with invalid archive handle.\n");+		diediedie();+	}++	if (a->magic != magic) {+		archive_set_error(a, -1,+		    "PROGRAMMER ERROR: Function '%s' invoked"+		    " on '%s' archive object, which is not supported.",+		    function,+		    handle_type);+		a->state = ARCHIVE_STATE_FATAL;+		return (ARCHIVE_FATAL);+	}++	if ((a->state & state) == 0) {+		/* If we're already FATAL, don't overwrite the error. */+		if (a->state != ARCHIVE_STATE_FATAL)+			archive_set_error(a, -1,+			    "INTERNAL ERROR: Function '%s' invoked with"+			    " archive structure in state '%s',"+			    " should be in state '%s'",+			    function,+			    write_all_states(states1, a->state),+			    write_all_states(states2, state));+		a->state = ARCHIVE_STATE_FATAL;+		return (ARCHIVE_FATAL);+	}+	return ARCHIVE_OK;+}
+ c/archive_cmdline.c view
@@ -0,0 +1,227 @@+/*-+ * Copyright (c) 2012 Michihiro NAKAJIMA + * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");++#ifdef HAVE_STRING_H+#  include <string.h>+#endif+#ifdef HAVE_STDLIB_H+#  include <stdlib.h>+#endif++#include "archive.h"+#include "archive_cmdline_private.h"+#include "archive_string.h"++static int cmdline_set_path(struct archive_cmdline *, const char *);+static int cmdline_add_arg(struct archive_cmdline *, const char *);++static ssize_t+extract_quotation(struct archive_string *as, const char *p)+{+	const char *s;++	for (s = p + 1; *s;) {+		if (*s == '\\') {+			if (s[1] != '\0') {+				archive_strappend_char(as, s[1]);+				s += 2;+			} else+				s++;+		} else if (*s == '"')+			break;+		else {+			archive_strappend_char(as, s[0]);+			s++;+		}+	}+	if (*s != '"')+		return (ARCHIVE_FAILED);/* Invalid sequence. */+	return ((ssize_t)(s + 1 - p));+}++static ssize_t+get_argument(struct archive_string *as, const char *p)+{+	const char *s = p;++	archive_string_empty(as);++	/* Skip beginning space characters. */+	while (*s != '\0' && *s == ' ')+		s++;+	/* Copy non-space characters. */+	while (*s != '\0' && *s != ' ') {+		if (*s == '\\') {+			if (s[1] != '\0') {+				archive_strappend_char(as, s[1]);+				s += 2;+			} else {+				s++;/* Ignore this character.*/+				break;+			}+		} else if (*s == '"') {+			ssize_t q = extract_quotation(as, s);+			if (q < 0)+				return (ARCHIVE_FAILED);/* Invalid sequence. */+			s += q;+		} else {+			archive_strappend_char(as, s[0]);+			s++;+		}+	}+	return ((ssize_t)(s - p));+}++/*+ * Set up command line arguments.+ * Returns ARCHIVE_OK if everything okey.+ * Returns ARCHIVE_FAILED if there is a lack of the `"' terminator or an+ * empty command line.+ * Returns ARCHIVE_FATAL if no memory.+ */+int+__archive_cmdline_parse(struct archive_cmdline *data, const char *cmd)+{+	struct archive_string as;+	const char *p;+	ssize_t al;+	int r;++	archive_string_init(&as);++	/* Get first argument as a command path. */+	al = get_argument(&as, cmd);+	if (al < 0) {+		r = ARCHIVE_FAILED;/* Invalid sequence. */+		goto exit_function;+	}+	if (archive_strlen(&as) == 0) {+		r = ARCHIVE_FAILED;/* An empty command path. */+		goto exit_function;+	}+	r = cmdline_set_path(data, as.s);+	if (r != ARCHIVE_OK)+		goto exit_function;+	p = strrchr(as.s, '/');+	if (p == NULL)+		p = as.s;+	else+		p++;+	r = cmdline_add_arg(data, p);+	if (r != ARCHIVE_OK)+		goto exit_function;+	cmd += al;++	for (;;) {+		al = get_argument(&as, cmd);+		if (al < 0) {+			r = ARCHIVE_FAILED;/* Invalid sequence. */+			goto exit_function;+		}+		if (al == 0)+			break;+		cmd += al;+		if (archive_strlen(&as) == 0 && *cmd == '\0')+			break;+		r = cmdline_add_arg(data, as.s);+		if (r != ARCHIVE_OK)+			goto exit_function;+	}+	r = ARCHIVE_OK;+exit_function:+	archive_string_free(&as);+	return (r);+}++/*+ * Set the program path.+ */+static int+cmdline_set_path(struct archive_cmdline *data, const char *path)+{+	char *newptr;++	newptr = realloc(data->path, strlen(path) + 1);+	if (newptr == NULL)+		return (ARCHIVE_FATAL);+	data->path = newptr;+	strcpy(data->path, path);+	return (ARCHIVE_OK);+}++/*+ * Add a argument for the program.+ */+static int+cmdline_add_arg(struct archive_cmdline *data, const char *arg)+{+	char **newargv;++	if (data->path == NULL)+		return (ARCHIVE_FAILED);++	newargv = realloc(data->argv, (data->argc + 2) * sizeof(char *));+	if (newargv == NULL)+		return (ARCHIVE_FATAL);+	data->argv = newargv;+	data->argv[data->argc] = strdup(arg);+	if (data->argv[data->argc] == NULL)+		return (ARCHIVE_FATAL);+	/* Set the terminator of argv. */+	data->argv[++data->argc] = NULL;+	return (ARCHIVE_OK);+}++struct archive_cmdline *+__archive_cmdline_allocate(void)+{+	return (struct archive_cmdline *)+		calloc(1, sizeof(struct archive_cmdline));+}++/*+ * Release the resources.+ */+int+__archive_cmdline_free(struct archive_cmdline *data)+{++	if (data) {+		free(data->path);+		if (data->argv != NULL) {+			int i;+			for (i = 0; data->argv[i] != NULL; i++)+				free(data->argv[i]);+			free(data->argv);+		}+		free(data);+	}+	return (ARCHIVE_OK);+}+
+ c/archive_cmdline_private.h view
@@ -0,0 +1,47 @@+/*-+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ */++#ifndef ARCHIVE_CMDLINE_PRIVATE_H+#define ARCHIVE_CMDLINE_PRIVATE_H++#ifndef __LIBARCHIVE_BUILD+#ifndef __LIBARCHIVE_TEST+#error This header is only to be used internally to libarchive.+#endif+#endif++struct archive_cmdline {+        char            *path;+        char            **argv;+        int              argc;+};++struct archive_cmdline *__archive_cmdline_allocate(void);+int __archive_cmdline_parse(struct archive_cmdline *, const char *);+int __archive_cmdline_free(struct archive_cmdline *);++#endif
+ c/archive_crc32.h view
@@ -0,0 +1,83 @@+/*-+ * Copyright (c) 2009 Joerg  Sonnenberger+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/archive_crc32.h 201102 2009-12-28 03:11:36Z kientzle $+ */++#ifndef ARCHIVE_CRC32_H+#define ARCHIVE_CRC32_H++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++/*+ * When zlib is unavailable, we should still be able to validate+ * uncompressed zip archives.  That requires us to be able to compute+ * the CRC32 check value.  This is a drop-in compatible replacement+ * for crc32() from zlib.  It's slower than the zlib implementation,+ * but still pretty fast: This runs about 300MB/s on my 3GHz P4+ * compared to about 800MB/s for the zlib implementation.+ */+static unsigned long+crc32(unsigned long crc, const void *_p, size_t len)+{+	unsigned long crc2, b, i;+	const unsigned char *p = _p;+	static volatile int crc_tbl_inited = 0;+	static unsigned long crc_tbl[256];++	if (!crc_tbl_inited) {+		for (b = 0; b < 256; ++b) {+			crc2 = b;+			for (i = 8; i > 0; --i) {+				if (crc2 & 1)+					crc2 = (crc2 >> 1) ^ 0xedb88320UL;+				else    +					crc2 = (crc2 >> 1);+			}+			crc_tbl[b] = crc2;+		}+		crc_tbl_inited = 1;+	}++	crc = crc ^ 0xffffffffUL;+	/* A use of this loop is about 20% - 30% faster than+	 * no use version in any optimization option of gcc.  */+	for (;len >= 8; len -= 8) {+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);+	}+	while (len--)+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);+	return (crc ^ 0xffffffffUL);+}++#endif
+ c/archive_cryptor.c view
@@ -0,0 +1,519 @@+/*-+* Copyright (c) 2014 Michihiro NAKAJIMA+* All rights reserved.+*+* Redistribution and use in source and binary forms, with or without+* modification, are permitted provided that the following conditions+* are met:+* 1. Redistributions of source code must retain the above copyright+*    notice, this list of conditions and the following disclaimer.+* 2. Redistributions in binary form must reproduce the above copyright+*    notice, this list of conditions and the following disclaimer in the+*    documentation and/or other materials provided with the distribution.+*+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*/++#include "archive_platform.h"++#ifdef HAVE_STRING_H+#include <string.h>+#endif+#include "archive.h"+#include "archive_cryptor_private.h"++/*+ * On systems that do not support any recognized crypto libraries,+ * this file will normally define no usable symbols.+ *+ * But some compilers and linkers choke on empty object files, so+ * define a public symbol that will always exist.  This could+ * be removed someday if this file gains another always-present+ * symbol definition.+ */+int __libarchive_cryptor_build_hack(void) {+	return 0;+}++#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto++static int+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,+    size_t salt_len, unsigned rounds, uint8_t *derived_key,+    size_t derived_key_len)+{+	CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pw,+	    pw_len, salt, salt_len, kCCPRFHmacAlgSHA1, rounds,+	    derived_key, derived_key_len);+	return 0;+}++#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)+#ifdef _MSC_VER+#pragma comment(lib, "Bcrypt.lib")+#endif++static int+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,+	size_t salt_len, unsigned rounds, uint8_t *derived_key,+	size_t derived_key_len)+{+	NTSTATUS status;+	BCRYPT_ALG_HANDLE hAlg;++	status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM,+		MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG);+	if (!BCRYPT_SUCCESS(status))+		return -1;++	status = BCryptDeriveKeyPBKDF2(hAlg,+		(PUCHAR)(uintptr_t)pw, (ULONG)pw_len,+		(PUCHAR)(uintptr_t)salt, (ULONG)salt_len, rounds,+		(PUCHAR)derived_key, (ULONG)derived_key_len, 0);++	BCryptCloseAlgorithmProvider(hAlg, 0);++	return (BCRYPT_SUCCESS(status)) ? 0: -1;+}++#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_PKCS5_H)++static int+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,+    size_t salt_len, unsigned rounds, uint8_t *derived_key,+    size_t derived_key_len)+{+	mbedtls_md_context_t ctx;+	const mbedtls_md_info_t *info;+	int ret;++	mbedtls_md_init(&ctx);+	info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);+	if (info == NULL) {+		mbedtls_md_free(&ctx);+		return (-1);+	}+	ret = mbedtls_md_setup(&ctx, info, 1);+	if (ret != 0) {+		mbedtls_md_free(&ctx);+		return (-1);+	}+	ret = mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char *)pw,+	    pw_len, salt, salt_len, rounds, derived_key_len, derived_key);++	mbedtls_md_free(&ctx);+	return (ret);+}++#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_PBKDF2_H)++static int+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,+    size_t salt_len, unsigned rounds, uint8_t *derived_key,+    size_t derived_key_len) {+	pbkdf2_hmac_sha1((unsigned)pw_len, (const uint8_t *)pw, rounds,+	    salt_len, salt, derived_key_len, derived_key);+	return 0;+}++#elif defined(HAVE_LIBCRYPTO) && defined(HAVE_PKCS5_PBKDF2_HMAC_SHA1)++static int+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,+    size_t salt_len, unsigned rounds, uint8_t *derived_key,+    size_t derived_key_len) {++	PKCS5_PBKDF2_HMAC_SHA1(pw, pw_len, salt, salt_len, rounds,+	    derived_key_len, derived_key);+	return 0;+}++#else++/* Stub */+static int+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,+    size_t salt_len, unsigned rounds, uint8_t *derived_key,+    size_t derived_key_len) {+	(void)pw; /* UNUSED */+	(void)pw_len; /* UNUSED */+	(void)salt; /* UNUSED */+	(void)salt_len; /* UNUSED */+	(void)rounds; /* UNUSED */+	(void)derived_key; /* UNUSED */+	(void)derived_key_len; /* UNUSED */+	return -1; /* UNSUPPORTED */+}++#endif++#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto+# if MAC_OS_X_VERSION_MAX_ALLOWED < 1090+#  define kCCAlgorithmAES kCCAlgorithmAES128+# endif++static int+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)+{+	CCCryptorStatus r;++	ctx->key_len = key_len;+	memcpy(ctx->key, key, key_len);+	memset(ctx->nonce, 0, sizeof(ctx->nonce));+	ctx->encr_pos = AES_BLOCK_SIZE;+	r = CCCryptorCreateWithMode(kCCEncrypt, kCCModeECB, kCCAlgorithmAES,+	    ccNoPadding, NULL, key, key_len, NULL, 0, 0, 0, &ctx->ctx);+	return (r == kCCSuccess)? 0: -1;+}++static int+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)+{+	CCCryptorRef ref = ctx->ctx;+	CCCryptorStatus r;++	r = CCCryptorReset(ref, NULL);+	if (r != kCCSuccess && r != kCCUnimplemented)+		return -1;+	r = CCCryptorUpdate(ref, ctx->nonce, AES_BLOCK_SIZE, ctx->encr_buf,+	    AES_BLOCK_SIZE, NULL);+	return (r == kCCSuccess)? 0: -1;+}++static int+aes_ctr_release(archive_crypto_ctx *ctx)+{+	memset(ctx->key, 0, ctx->key_len);+	memset(ctx->nonce, 0, sizeof(ctx->nonce));+	return 0;+}++#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)++static int+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)+{+	BCRYPT_ALG_HANDLE hAlg;+	BCRYPT_KEY_HANDLE hKey;+	DWORD keyObj_len, aes_key_len;+	PBYTE keyObj;+	ULONG result;+	NTSTATUS status;+	BCRYPT_KEY_LENGTHS_STRUCT key_lengths;++	ctx->hAlg = NULL;+	ctx->hKey = NULL;+	ctx->keyObj = NULL;+	switch (key_len) {+	case 16: aes_key_len = 128; break;+	case 24: aes_key_len = 192; break;+	case 32: aes_key_len = 256; break;+	default: return -1;+	}+	status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM,+		MS_PRIMITIVE_PROVIDER, 0);+	if (!BCRYPT_SUCCESS(status))+		return -1;+	status = BCryptGetProperty(hAlg, BCRYPT_KEY_LENGTHS, (PUCHAR)&key_lengths,+		sizeof(key_lengths), &result, 0);+	if (!BCRYPT_SUCCESS(status)) {+		BCryptCloseAlgorithmProvider(hAlg, 0);+		return -1;+	}+	if (key_lengths.dwMinLength > aes_key_len+		|| key_lengths.dwMaxLength < aes_key_len) {+		BCryptCloseAlgorithmProvider(hAlg, 0);+		return -1;+	}+	status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&keyObj_len,+		sizeof(keyObj_len), &result, 0);+	if (!BCRYPT_SUCCESS(status)) {+		BCryptCloseAlgorithmProvider(hAlg, 0);+		return -1;+	}+	keyObj = (PBYTE)HeapAlloc(GetProcessHeap(), 0, keyObj_len);+	if (keyObj == NULL) {+		BCryptCloseAlgorithmProvider(hAlg, 0);+		return -1;+	}+	status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE,+		(PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0);+	if (!BCRYPT_SUCCESS(status)) {+		BCryptCloseAlgorithmProvider(hAlg, 0);+		HeapFree(GetProcessHeap(), 0, keyObj);+		return -1;+	}+	status = BCryptGenerateSymmetricKey(hAlg, &hKey,+		keyObj, keyObj_len,+		(PUCHAR)(uintptr_t)key, (ULONG)key_len, 0);+	if (!BCRYPT_SUCCESS(status)) {+		BCryptCloseAlgorithmProvider(hAlg, 0);+		HeapFree(GetProcessHeap(), 0, keyObj);+		return -1;+	}++	ctx->hAlg = hAlg;+	ctx->hKey = hKey;+	ctx->keyObj = keyObj;+	ctx->keyObj_len = keyObj_len;+	ctx->encr_pos = AES_BLOCK_SIZE;++	return 0;+}++static int+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)+{+	NTSTATUS status;+	ULONG result;++	status = BCryptEncrypt(ctx->hKey, (PUCHAR)ctx->nonce, AES_BLOCK_SIZE,+		NULL, NULL, 0, (PUCHAR)ctx->encr_buf, AES_BLOCK_SIZE,+		&result, 0);+	return BCRYPT_SUCCESS(status) ? 0 : -1;+}++static int+aes_ctr_release(archive_crypto_ctx *ctx)+{++	if (ctx->hAlg != NULL) {+		BCryptCloseAlgorithmProvider(ctx->hAlg, 0);+		ctx->hAlg = NULL;+		BCryptDestroyKey(ctx->hKey);+		ctx->hKey = NULL;+		HeapFree(GetProcessHeap(), 0, ctx->keyObj);+		ctx->keyObj = NULL;+	}+	memset(ctx, 0, sizeof(*ctx));+	return 0;+}++#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H)++static int+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)+{+	mbedtls_aes_init(&ctx->ctx);+	ctx->key_len = key_len;+	memcpy(ctx->key, key, key_len);+	memset(ctx->nonce, 0, sizeof(ctx->nonce));+	ctx->encr_pos = AES_BLOCK_SIZE;+	return 0;+}++static int+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)+{+	if (mbedtls_aes_setkey_enc(&ctx->ctx, ctx->key,+	    ctx->key_len * 8) != 0)+		return (-1);+	if (mbedtls_aes_crypt_ecb(&ctx->ctx, MBEDTLS_AES_ENCRYPT, ctx->nonce,+	    ctx->encr_buf) != 0)+		return (-1);+	return 0;+}++static int+aes_ctr_release(archive_crypto_ctx *ctx)+{+	mbedtls_aes_free(&ctx->ctx);+	memset(ctx, 0, sizeof(*ctx));+	return 0;+}++#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H)++static int+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)+{+	ctx->key_len = key_len;+	memcpy(ctx->key, key, key_len);+	memset(ctx->nonce, 0, sizeof(ctx->nonce));+	ctx->encr_pos = AES_BLOCK_SIZE;+	memset(&ctx->ctx, 0, sizeof(ctx->ctx));+	return 0;+}++static int+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)+{+	aes_set_encrypt_key(&ctx->ctx, ctx->key_len, ctx->key);+	aes_encrypt(&ctx->ctx, AES_BLOCK_SIZE, ctx->encr_buf, ctx->nonce);+	return 0;+}++static int+aes_ctr_release(archive_crypto_ctx *ctx)+{+	memset(ctx, 0, sizeof(*ctx));+	return 0;+}++#elif defined(HAVE_LIBCRYPTO)++static int+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)+{+	if ((ctx->ctx = EVP_CIPHER_CTX_new()) == NULL)+		return -1;++	switch (key_len) {+	case 16: ctx->type = EVP_aes_128_ecb(); break;+	case 24: ctx->type = EVP_aes_192_ecb(); break;+	case 32: ctx->type = EVP_aes_256_ecb(); break;+	default: ctx->type = NULL; return -1;+	}++	ctx->key_len = key_len;+	memcpy(ctx->key, key, key_len);+	memset(ctx->nonce, 0, sizeof(ctx->nonce));+	ctx->encr_pos = AES_BLOCK_SIZE;+#if OPENSSL_VERSION_NUMBER  >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)+	if (!EVP_CIPHER_CTX_reset(ctx->ctx)) {+		EVP_CIPHER_CTX_free(ctx->ctx);+		ctx->ctx = NULL;+	}+#else+	EVP_CIPHER_CTX_init(ctx->ctx);+#endif+	return 0;+}++static int+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)+{+	int outl = 0;+	int r;++	r = EVP_EncryptInit_ex(ctx->ctx, ctx->type, NULL, ctx->key, NULL);+	if (r == 0)+		return -1;+	r = EVP_EncryptUpdate(ctx->ctx, ctx->encr_buf, &outl, ctx->nonce,+	    AES_BLOCK_SIZE);+	if (r == 0 || outl != AES_BLOCK_SIZE)+		return -1;+	return 0;+}++static int+aes_ctr_release(archive_crypto_ctx *ctx)+{+	EVP_CIPHER_CTX_free(ctx->ctx);+	memset(ctx->key, 0, ctx->key_len);+	memset(ctx->nonce, 0, sizeof(ctx->nonce));+	return 0;+}++#else++#define ARCHIVE_CRYPTOR_STUB+/* Stub */+static int+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)+{+	(void)ctx; /* UNUSED */+	(void)key; /* UNUSED */+	(void)key_len; /* UNUSED */+	return -1;+}++static int+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)+{+	(void)ctx; /* UNUSED */+	return -1;+}++static int+aes_ctr_release(archive_crypto_ctx *ctx)+{+	(void)ctx; /* UNUSED */+	return 0;+}++#endif++#ifdef ARCHIVE_CRYPTOR_STUB+static int+aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in,+    size_t in_len, uint8_t * const out, size_t *out_len)+{+	(void)ctx; /* UNUSED */+	(void)in; /* UNUSED */+	(void)in_len; /* UNUSED */+	(void)out; /* UNUSED */+	(void)out_len; /* UNUSED */+	aes_ctr_encrypt_counter(ctx); /* UNUSED */ /* Fix unused function warning */+	return -1;+}++#else+static void+aes_ctr_increase_counter(archive_crypto_ctx *ctx)+{+	uint8_t *const nonce = ctx->nonce;+	int j;++	for (j = 0; j < 8; j++) {+		if (++nonce[j])+			break;+	}+}++static int+aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in,+    size_t in_len, uint8_t * const out, size_t *out_len)+{+	uint8_t *const ebuf = ctx->encr_buf;+	unsigned pos = ctx->encr_pos;+	unsigned max = (unsigned)((in_len < *out_len)? in_len: *out_len);+	unsigned i;++	for (i = 0; i < max; ) {+		if (pos == AES_BLOCK_SIZE) {+			aes_ctr_increase_counter(ctx);+			if (aes_ctr_encrypt_counter(ctx) != 0)+				return -1;+			while (max -i >= AES_BLOCK_SIZE) {+				for (pos = 0; pos < AES_BLOCK_SIZE; pos++)+					out[i+pos] = in[i+pos] ^ ebuf[pos];+				i += AES_BLOCK_SIZE;+				aes_ctr_increase_counter(ctx);+				if (aes_ctr_encrypt_counter(ctx) != 0)+					return -1;+			}+			pos = 0;+			if (i >= max)+				break;+		}+		out[i] = in[i] ^ ebuf[pos++];+		i++;+	}+	ctx->encr_pos = pos;+	*out_len = i;++	return 0;+}+#endif /* ARCHIVE_CRYPTOR_STUB */+++const struct archive_cryptor __archive_cryptor =+{+  &pbkdf2_sha1,+  &aes_ctr_init,+  &aes_ctr_update,+  &aes_ctr_release,+  &aes_ctr_init,+  &aes_ctr_update,+  &aes_ctr_release,+};
+ c/archive_cryptor_private.h view
@@ -0,0 +1,179 @@+/*-+* Copyright (c) 2014 Michihiro NAKAJIMA+* All rights reserved.+*+* Redistribution and use in source and binary forms, with or without+* modification, are permitted provided that the following conditions+* are met:+* 1. Redistributions of source code must retain the above copyright+*    notice, this list of conditions and the following disclaimer.+* 2. Redistributions in binary form must reproduce the above copyright+*    notice, this list of conditions and the following disclaimer in the+*    documentation and/or other materials provided with the distribution.+*+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*/++#ifndef ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED+#define ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif+/*+ * On systems that do not support any recognized crypto libraries,+ * the archive_cryptor.c file will normally define no usable symbols.+ *+ * But some compilers and linkers choke on empty object files, so+ * define a public symbol that will always exist.  This could+ * be removed someday if this file gains another always-present+ * symbol definition.+ */+int __libarchive_cryptor_build_hack(void);++#ifdef __APPLE__+# include <AvailabilityMacros.h>+# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080+#  define ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto+# endif+#endif++#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto+#include <CommonCrypto/CommonCryptor.h>+#include <CommonCrypto/CommonKeyDerivation.h>+#define AES_BLOCK_SIZE	16+#define AES_MAX_KEY_SIZE kCCKeySizeAES256++typedef struct {+	CCCryptorRef	ctx;+	uint8_t		key[AES_MAX_KEY_SIZE];+	unsigned	key_len;+	uint8_t		nonce[AES_BLOCK_SIZE];+	uint8_t		encr_buf[AES_BLOCK_SIZE];+	unsigned	encr_pos;+} archive_crypto_ctx;++#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)+#include <bcrypt.h>++/* Common in other bcrypt implementations, but missing from VS2008. */+#ifndef BCRYPT_SUCCESS+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)+#endif++#define AES_MAX_KEY_SIZE 32+#define AES_BLOCK_SIZE 16+typedef struct {+	BCRYPT_ALG_HANDLE hAlg;+	BCRYPT_KEY_HANDLE hKey;+	PBYTE		keyObj;+	DWORD		keyObj_len;+	uint8_t		nonce[AES_BLOCK_SIZE];+	uint8_t		encr_buf[AES_BLOCK_SIZE];+	unsigned	encr_pos;+} archive_crypto_ctx;++#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H)+#include <mbedtls/aes.h>+#include <mbedtls/md.h>+#include <mbedtls/pkcs5.h>++#define AES_MAX_KEY_SIZE 32+#define AES_BLOCK_SIZE 16++typedef struct {+	mbedtls_aes_context	ctx;+	uint8_t		key[AES_MAX_KEY_SIZE];+	unsigned	key_len;+	uint8_t		nonce[AES_BLOCK_SIZE];+	uint8_t		encr_buf[AES_BLOCK_SIZE];+	unsigned	encr_pos;+} archive_crypto_ctx;++#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H)+#if defined(HAVE_NETTLE_PBKDF2_H)+#include <nettle/pbkdf2.h>+#endif+#include <nettle/aes.h>++typedef struct {+	struct aes_ctx	ctx;+	uint8_t		key[AES_MAX_KEY_SIZE];+	unsigned	key_len;+	uint8_t		nonce[AES_BLOCK_SIZE];+	uint8_t		encr_buf[AES_BLOCK_SIZE];+	unsigned	encr_pos;+} archive_crypto_ctx;++#elif defined(HAVE_LIBCRYPTO)+#include "archive_openssl_evp_private.h"+#define AES_BLOCK_SIZE	16+#define AES_MAX_KEY_SIZE 32++typedef struct {+	EVP_CIPHER_CTX	*ctx;+	const EVP_CIPHER *type;+	uint8_t		key[AES_MAX_KEY_SIZE];+	unsigned	key_len;+	uint8_t		nonce[AES_BLOCK_SIZE];+	uint8_t		encr_buf[AES_BLOCK_SIZE];+	unsigned	encr_pos;+} archive_crypto_ctx;++#else++#define AES_BLOCK_SIZE	16+#define AES_MAX_KEY_SIZE 32+typedef int archive_crypto_ctx;++#endif++/* defines */+#define archive_pbkdf2_sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len)\+  __archive_cryptor.pbkdf2sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len)++#define archive_decrypto_aes_ctr_init(ctx, key, key_len) \+  __archive_cryptor.decrypto_aes_ctr_init(ctx, key, key_len)+#define archive_decrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \+  __archive_cryptor.decrypto_aes_ctr_update(ctx, in, in_len, out, out_len)+#define archive_decrypto_aes_ctr_release(ctx) \+  __archive_cryptor.decrypto_aes_ctr_release(ctx)++#define archive_encrypto_aes_ctr_init(ctx, key, key_len) \+  __archive_cryptor.encrypto_aes_ctr_init(ctx, key, key_len)+#define archive_encrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \+  __archive_cryptor.encrypto_aes_ctr_update(ctx, in, in_len, out, out_len)+#define archive_encrypto_aes_ctr_release(ctx) \+  __archive_cryptor.encrypto_aes_ctr_release(ctx)++/* Minimal interface to cryptographic functionality for internal use in+ * libarchive */+struct archive_cryptor+{+  /* PKCS5 PBKDF2 HMAC-SHA1 */+  int (*pbkdf2sha1)(const char *pw, size_t pw_len, const uint8_t *salt,+    size_t salt_len, unsigned rounds, uint8_t *derived_key,+    size_t derived_key_len);+  /* AES CTR mode(little endian version) */+  int (*decrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t);+  int (*decrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *,+    size_t, uint8_t *, size_t *);+  int (*decrypto_aes_ctr_release)(archive_crypto_ctx *);+  int (*encrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t);+  int (*encrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *,+    size_t, uint8_t *, size_t *);+  int (*encrypto_aes_ctr_release)(archive_crypto_ctx *);+};++extern const struct archive_cryptor __archive_cryptor;++#endif
+ c/archive_digest.c view
@@ -0,0 +1,1499 @@+/*-+* Copyright (c) 2003-2007 Tim Kientzle+* Copyright (c) 2011 Andres Mejia+* Copyright (c) 2011 Michihiro NAKAJIMA+* All rights reserved.+*+* Redistribution and use in source and binary forms, with or without+* modification, are permitted provided that the following conditions+* are met:+* 1. Redistributions of source code must retain the above copyright+*    notice, this list of conditions and the following disclaimer.+* 2. Redistributions in binary form must reproduce the above copyright+*    notice, this list of conditions and the following disclaimer in the+*    documentation and/or other materials provided with the distribution.+*+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*/++#include "archive_platform.h"++#include "archive.h"+#include "archive_digest_private.h"++/* In particular, force the configure probe to break if it tries+ * to test a combination of OpenSSL and libmd. */+#if defined(ARCHIVE_CRYPTO_OPENSSL) && defined(ARCHIVE_CRYPTO_LIBMD)+#error Cannot use both OpenSSL and libmd.+#endif++/*+ * Message digest functions for Windows platform.+ */+#if defined(ARCHIVE_CRYPTO_MD5_WIN)    ||\+	defined(ARCHIVE_CRYPTO_SHA1_WIN)   ||\+	defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\+	defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\+	defined(ARCHIVE_CRYPTO_SHA512_WIN)++/*+ * Initialize a Message digest.+ */+static int+win_crypto_init(Digest_CTX *ctx, ALG_ID algId)+{++	ctx->valid = 0;+	if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,+	    PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {+		if (GetLastError() != (DWORD)NTE_BAD_KEYSET)+			return (ARCHIVE_FAILED);+		if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,+		    PROV_RSA_FULL, CRYPT_NEWKEYSET))+			return (ARCHIVE_FAILED);+	}++	if (!CryptCreateHash(ctx->cryptProv, algId, 0, 0, &ctx->hash)) {+		CryptReleaseContext(ctx->cryptProv, 0);+		return (ARCHIVE_FAILED);+	}++	ctx->valid = 1;+	return (ARCHIVE_OK);+}++/*+ * Update a Message digest.+ */+static int+win_crypto_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len)+{++	if (!ctx->valid)+		return (ARCHIVE_FAILED);++	CryptHashData(ctx->hash,+		      (unsigned char *)(uintptr_t)buf,+		      (DWORD)len, 0);+	return (ARCHIVE_OK);+}++static int+win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx)+{+	DWORD siglen = (DWORD)bufsize;++	if (!ctx->valid)+		return (ARCHIVE_FAILED);++	CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0);+	CryptDestroyHash(ctx->hash);+	CryptReleaseContext(ctx->cryptProv, 0);+	ctx->valid = 0;+	return (ARCHIVE_OK);+}++#endif /* defined(ARCHIVE_CRYPTO_*_WIN) */+++/* MD5 implementations */+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)++static int+__archive_md5init(archive_md5_ctx *ctx)+{+  MD5Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_md5update(archive_md5_ctx *ctx, const void *indata,+    size_t insize)+{+  MD5Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_md5final(archive_md5_ctx *ctx, void *md)+{+  MD5Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_MD5_LIBMD)++static int+__archive_md5init(archive_md5_ctx *ctx)+{+  MD5Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_md5update(archive_md5_ctx *ctx, const void *indata,+    size_t insize)+{+  MD5Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_md5final(archive_md5_ctx *ctx, void *md)+{+  MD5Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM)++static int+__archive_md5init(archive_md5_ctx *ctx)+{+  CC_MD5_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_md5update(archive_md5_ctx *ctx, const void *indata,+    size_t insize)+{+  CC_MD5_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_md5final(archive_md5_ctx *ctx, void *md)+{+  CC_MD5_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)++static int+__archive_md5init(archive_md5_ctx *ctx)+{+  mbedtls_md5_init(ctx);+  if (mbedtls_md5_starts_ret(ctx) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_md5update(archive_md5_ctx *ctx, const void *indata,+    size_t insize)+{+  if (mbedtls_md5_update_ret(ctx, indata, insize) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_md5final(archive_md5_ctx *ctx, void *md)+{+  if (mbedtls_md5_finish_ret(ctx, md) == 0) {+    mbedtls_md5_free(ctx);+    return (ARCHIVE_OK);+  } else {+    mbedtls_md5_free(ctx);+    return (ARCHIVE_FATAL);+  }+}++#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)++static int+__archive_md5init(archive_md5_ctx *ctx)+{+  md5_init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_md5update(archive_md5_ctx *ctx, const void *indata,+    size_t insize)+{+  md5_update(ctx, insize, indata);+  return (ARCHIVE_OK);+}++static int+__archive_md5final(archive_md5_ctx *ctx, void *md)+{+  md5_digest(ctx, MD5_DIGEST_SIZE, md);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)++static int+__archive_md5init(archive_md5_ctx *ctx)+{+  if ((*ctx = EVP_MD_CTX_new()) == NULL)+	return (ARCHIVE_FAILED);+  EVP_DigestInit(*ctx, EVP_md5());+  return (ARCHIVE_OK);+}++static int+__archive_md5update(archive_md5_ctx *ctx, const void *indata,+    size_t insize)+{+  EVP_DigestUpdate(*ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_md5final(archive_md5_ctx *ctx, void *md)+{+  /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so+   * this is meant to cope with that. Real fix is probably to fix+   * archive_write_set_format_xar.c+   */+  if (*ctx) {+    EVP_DigestFinal(*ctx, md, NULL);+    EVP_MD_CTX_free(*ctx);+    *ctx = NULL;+  }+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_MD5_WIN)++static int+__archive_md5init(archive_md5_ctx *ctx)+{+  return (win_crypto_init(ctx, CALG_MD5));+}++static int+__archive_md5update(archive_md5_ctx *ctx, const void *indata,+    size_t insize)+{+  return (win_crypto_Update(ctx, indata, insize));+}++static int+__archive_md5final(archive_md5_ctx *ctx, void *md)+{+  return (win_crypto_Final(md, 16, ctx));+}++#else++static int+__archive_md5init(archive_md5_ctx *ctx)+{+	(void)ctx; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_md5update(archive_md5_ctx *ctx, const void *indata,+    size_t insize)+{+	(void)ctx; /* UNUSED */+	(void)indata; /* UNUSED */+	(void)insize; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_md5final(archive_md5_ctx *ctx, void *md)+{+	(void)ctx; /* UNUSED */+	(void)md; /* UNUSED */+	return (ARCHIVE_FAILED);+}++#endif++/* RIPEMD160 implementations */+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)++static int+__archive_ripemd160init(archive_rmd160_ctx *ctx)+{+  RMD160Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,+    size_t insize)+{+  RMD160Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)+{+  RMD160Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD)++static int+__archive_ripemd160init(archive_rmd160_ctx *ctx)+{+  RIPEMD160_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,+    size_t insize)+{+  RIPEMD160_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)+{+  RIPEMD160_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)++static int+__archive_ripemd160init(archive_rmd160_ctx *ctx)+{+  mbedtls_ripemd160_init(ctx);+  if (mbedtls_ripemd160_starts_ret(ctx) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,+    size_t insize)+{+  if (mbedtls_ripemd160_update_ret(ctx, indata, insize) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)+{+  if (mbedtls_ripemd160_finish_ret(ctx, md) == 0) {+    mbedtls_ripemd160_free(ctx);+    return (ARCHIVE_OK);+  } else {+    mbedtls_ripemd160_free(ctx);+    return (ARCHIVE_FATAL);+  }+}++#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)++static int+__archive_ripemd160init(archive_rmd160_ctx *ctx)+{+  ripemd160_init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,+    size_t insize)+{+  ripemd160_update(ctx, insize, indata);+  return (ARCHIVE_OK);+}++static int+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)+{+  ripemd160_digest(ctx, RIPEMD160_DIGEST_SIZE, md);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)++static int+__archive_ripemd160init(archive_rmd160_ctx *ctx)+{+  if ((*ctx = EVP_MD_CTX_new()) == NULL)+	return (ARCHIVE_FAILED);+  EVP_DigestInit(*ctx, EVP_ripemd160());+  return (ARCHIVE_OK);+}++static int+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,+    size_t insize)+{+  EVP_DigestUpdate(*ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)+{+  if (*ctx) {+    EVP_DigestFinal(*ctx, md, NULL);+    EVP_MD_CTX_free(*ctx);+    *ctx = NULL;+  }+  return (ARCHIVE_OK);+}++#else++static int+__archive_ripemd160init(archive_rmd160_ctx *ctx)+{+	(void)ctx; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,+    size_t insize)+{+	(void)ctx; /* UNUSED */+	(void)indata; /* UNUSED */+	(void)insize; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)+{+	(void)ctx; /* UNUSED */+	(void)md; /* UNUSED */+	return (ARCHIVE_FAILED);+}++#endif++/* SHA1 implementations */+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)++static int+__archive_sha1init(archive_sha1_ctx *ctx)+{+  SHA1Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA1Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha1final(archive_sha1_ctx *ctx, void *md)+{+  SHA1Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD)++static int+__archive_sha1init(archive_sha1_ctx *ctx)+{+  SHA1_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA1_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha1final(archive_sha1_ctx *ctx, void *md)+{+  SHA1_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM)++static int+__archive_sha1init(archive_sha1_ctx *ctx)+{+  CC_SHA1_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,+    size_t insize)+{+  CC_SHA1_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha1final(archive_sha1_ctx *ctx, void *md)+{+  CC_SHA1_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)++static int+__archive_sha1init(archive_sha1_ctx *ctx)+{+  mbedtls_sha1_init(ctx);+  if (mbedtls_sha1_starts_ret(ctx) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,+    size_t insize)+{+  if (mbedtls_sha1_update_ret(ctx, indata, insize) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_sha1final(archive_sha1_ctx *ctx, void *md)+{+  if (mbedtls_sha1_finish_ret(ctx, md) == 0) {+    mbedtls_sha1_free(ctx);+    return (ARCHIVE_OK);+  } else {+    mbedtls_sha1_free(ctx);+    return (ARCHIVE_FATAL);+  }+}++#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)++static int+__archive_sha1init(archive_sha1_ctx *ctx)+{+  sha1_init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,+    size_t insize)+{+  sha1_update(ctx, insize, indata);+  return (ARCHIVE_OK);+}++static int+__archive_sha1final(archive_sha1_ctx *ctx, void *md)+{+  sha1_digest(ctx, SHA1_DIGEST_SIZE, md);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)++static int+__archive_sha1init(archive_sha1_ctx *ctx)+{+  if ((*ctx = EVP_MD_CTX_new()) == NULL)+	return (ARCHIVE_FAILED);+  EVP_DigestInit(*ctx, EVP_sha1());+  return (ARCHIVE_OK);+}++static int+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,+    size_t insize)+{+  EVP_DigestUpdate(*ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha1final(archive_sha1_ctx *ctx, void *md)+{+  /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so+   * this is meant to cope with that. Real fix is probably to fix+   * archive_write_set_format_xar.c+   */+  if (*ctx) {+    EVP_DigestFinal(*ctx, md, NULL);+    EVP_MD_CTX_free(*ctx);+    *ctx = NULL;+  }+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA1_WIN)++static int+__archive_sha1init(archive_sha1_ctx *ctx)+{+  return (win_crypto_init(ctx, CALG_SHA1));+}++static int+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,+    size_t insize)+{+  return (win_crypto_Update(ctx, indata, insize));+}++static int+__archive_sha1final(archive_sha1_ctx *ctx, void *md)+{+  return (win_crypto_Final(md, 20, ctx));+}++#else++static int+__archive_sha1init(archive_sha1_ctx *ctx)+{+	(void)ctx; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,+    size_t insize)+{+	(void)ctx; /* UNUSED */+	(void)indata; /* UNUSED */+	(void)insize; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_sha1final(archive_sha1_ctx *ctx, void *md)+{+	(void)ctx; /* UNUSED */+	(void)md; /* UNUSED */+	return (ARCHIVE_FAILED);+}++#endif++/* SHA256 implementations */+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC)++static int+__archive_sha256init(archive_sha256_ctx *ctx)+{+  SHA256_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA256_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha256final(archive_sha256_ctx *ctx, void *md)+{+  SHA256_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2)++static int+__archive_sha256init(archive_sha256_ctx *ctx)+{+  SHA256Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA256Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha256final(archive_sha256_ctx *ctx, void *md)+{+  SHA256Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3)++static int+__archive_sha256init(archive_sha256_ctx *ctx)+{+  SHA256Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA256Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha256final(archive_sha256_ctx *ctx, void *md)+{+  SHA256Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD)++static int+__archive_sha256init(archive_sha256_ctx *ctx)+{+  SHA256_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA256_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha256final(archive_sha256_ctx *ctx, void *md)+{+  SHA256_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM)++static int+__archive_sha256init(archive_sha256_ctx *ctx)+{+  CC_SHA256_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,+    size_t insize)+{+  CC_SHA256_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha256final(archive_sha256_ctx *ctx, void *md)+{+  CC_SHA256_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)++static int+__archive_sha256init(archive_sha256_ctx *ctx)+{+  mbedtls_sha256_init(ctx);+  if (mbedtls_sha256_starts_ret(ctx, 0) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,+    size_t insize)+{+  if (mbedtls_sha256_update_ret(ctx, indata, insize) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_sha256final(archive_sha256_ctx *ctx, void *md)+{+  if (mbedtls_sha256_finish_ret(ctx, md) == 0) {+    mbedtls_sha256_free(ctx);+    return (ARCHIVE_OK);+  } else {+    mbedtls_sha256_free(ctx);+    return (ARCHIVE_FATAL);+  }+}++#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)++static int+__archive_sha256init(archive_sha256_ctx *ctx)+{+  sha256_init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,+    size_t insize)+{+  sha256_update(ctx, insize, indata);+  return (ARCHIVE_OK);+}++static int+__archive_sha256final(archive_sha256_ctx *ctx, void *md)+{+  sha256_digest(ctx, SHA256_DIGEST_SIZE, md);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)++static int+__archive_sha256init(archive_sha256_ctx *ctx)+{+  if ((*ctx = EVP_MD_CTX_new()) == NULL)+	return (ARCHIVE_FAILED);+  EVP_DigestInit(*ctx, EVP_sha256());+  return (ARCHIVE_OK);+}++static int+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,+    size_t insize)+{+  EVP_DigestUpdate(*ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha256final(archive_sha256_ctx *ctx, void *md)+{+  if (*ctx) {+    EVP_DigestFinal(*ctx, md, NULL);+    EVP_MD_CTX_free(*ctx);+    *ctx = NULL;+  }+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA256_WIN)++static int+__archive_sha256init(archive_sha256_ctx *ctx)+{+  return (win_crypto_init(ctx, CALG_SHA_256));+}++static int+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,+    size_t insize)+{+  return (win_crypto_Update(ctx, indata, insize));+}++static int+__archive_sha256final(archive_sha256_ctx *ctx, void *md)+{+  return (win_crypto_Final(md, 32, ctx));+}++#else++static int+__archive_sha256init(archive_sha256_ctx *ctx)+{+	(void)ctx; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,+    size_t insize)+{+	(void)ctx; /* UNUSED */+	(void)indata; /* UNUSED */+	(void)insize; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_sha256final(archive_sha256_ctx *ctx, void *md)+{+	(void)ctx; /* UNUSED */+	(void)md; /* UNUSED */+	return (ARCHIVE_FAILED);+}++#endif++/* SHA384 implementations */+#if defined(ARCHIVE_CRYPTO_SHA384_LIBC)++static int+__archive_sha384init(archive_sha384_ctx *ctx)+{+  SHA384_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA384_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha384final(archive_sha384_ctx *ctx, void *md)+{+  SHA384_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2)++static int+__archive_sha384init(archive_sha384_ctx *ctx)+{+  SHA384Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA384Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha384final(archive_sha384_ctx *ctx, void *md)+{+  SHA384Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3)++static int+__archive_sha384init(archive_sha384_ctx *ctx)+{+  SHA384Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA384Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha384final(archive_sha384_ctx *ctx, void *md)+{+  SHA384Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM)++static int+__archive_sha384init(archive_sha384_ctx *ctx)+{+  CC_SHA384_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,+    size_t insize)+{+  CC_SHA384_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha384final(archive_sha384_ctx *ctx, void *md)+{+  CC_SHA384_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)++static int+__archive_sha384init(archive_sha384_ctx *ctx)+{+  mbedtls_sha512_init(ctx);+  if (mbedtls_sha512_starts_ret(ctx, 1) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,+    size_t insize)+{+  if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_sha384final(archive_sha384_ctx *ctx, void *md)+{+  if (mbedtls_sha512_finish_ret(ctx, md) == 0) {+    mbedtls_sha512_free(ctx);+    return (ARCHIVE_OK);+  } else {+    mbedtls_sha512_free(ctx);+    return (ARCHIVE_FATAL);+  }+}++#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)++static int+__archive_sha384init(archive_sha384_ctx *ctx)+{+  sha384_init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,+    size_t insize)+{+  sha384_update(ctx, insize, indata);+  return (ARCHIVE_OK);+}++static int+__archive_sha384final(archive_sha384_ctx *ctx, void *md)+{+  sha384_digest(ctx, SHA384_DIGEST_SIZE, md);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)++static int+__archive_sha384init(archive_sha384_ctx *ctx)+{+  if ((*ctx = EVP_MD_CTX_new()) == NULL)+	return (ARCHIVE_FAILED);+  EVP_DigestInit(*ctx, EVP_sha384());+  return (ARCHIVE_OK);+}++static int+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,+    size_t insize)+{+  EVP_DigestUpdate(*ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha384final(archive_sha384_ctx *ctx, void *md)+{+  if (*ctx) {+    EVP_DigestFinal(*ctx, md, NULL);+    EVP_MD_CTX_free(*ctx);+    *ctx = NULL;+  }+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA384_WIN)++static int+__archive_sha384init(archive_sha384_ctx *ctx)+{+  return (win_crypto_init(ctx, CALG_SHA_384));+}++static int+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,+    size_t insize)+{+  return (win_crypto_Update(ctx, indata, insize));+}++static int+__archive_sha384final(archive_sha384_ctx *ctx, void *md)+{+  return (win_crypto_Final(md, 48, ctx));+}++#else++static int+__archive_sha384init(archive_sha384_ctx *ctx)+{+	(void)ctx; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,+    size_t insize)+{+	(void)ctx; /* UNUSED */+	(void)indata; /* UNUSED */+	(void)insize; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_sha384final(archive_sha384_ctx *ctx, void *md)+{+	(void)ctx; /* UNUSED */+	(void)md; /* UNUSED */+	return (ARCHIVE_FAILED);+}++#endif++/* SHA512 implementations */+#if defined(ARCHIVE_CRYPTO_SHA512_LIBC)++static int+__archive_sha512init(archive_sha512_ctx *ctx)+{+  SHA512_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA512_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha512final(archive_sha512_ctx *ctx, void *md)+{+  SHA512_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2)++static int+__archive_sha512init(archive_sha512_ctx *ctx)+{+  SHA512Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA512Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha512final(archive_sha512_ctx *ctx, void *md)+{+  SHA512Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3)++static int+__archive_sha512init(archive_sha512_ctx *ctx)+{+  SHA512Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA512Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha512final(archive_sha512_ctx *ctx, void *md)+{+  SHA512Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD)++static int+__archive_sha512init(archive_sha512_ctx *ctx)+{+  SHA512_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,+    size_t insize)+{+  SHA512_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha512final(archive_sha512_ctx *ctx, void *md)+{+  SHA512_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)++static int+__archive_sha512init(archive_sha512_ctx *ctx)+{+  CC_SHA512_Init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,+    size_t insize)+{+  CC_SHA512_Update(ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha512final(archive_sha512_ctx *ctx, void *md)+{+  CC_SHA512_Final(md, ctx);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)++static int+__archive_sha512init(archive_sha512_ctx *ctx)+{+  mbedtls_sha512_init(ctx);+  if (mbedtls_sha512_starts_ret(ctx, 0) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,+    size_t insize)+{+  if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)+    return (ARCHIVE_OK);+  else+    return (ARCHIVE_FATAL);+}++static int+__archive_sha512final(archive_sha512_ctx *ctx, void *md)+{+  if (mbedtls_sha512_finish_ret(ctx, md) == 0) {+    mbedtls_sha512_free(ctx);+    return (ARCHIVE_OK);+  } else {+    mbedtls_sha512_free(ctx);+    return (ARCHIVE_FATAL);+  }+}++#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)++static int+__archive_sha512init(archive_sha512_ctx *ctx)+{+  sha512_init(ctx);+  return (ARCHIVE_OK);+}++static int+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,+    size_t insize)+{+  sha512_update(ctx, insize, indata);+  return (ARCHIVE_OK);+}++static int+__archive_sha512final(archive_sha512_ctx *ctx, void *md)+{+  sha512_digest(ctx, SHA512_DIGEST_SIZE, md);+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)++static int+__archive_sha512init(archive_sha512_ctx *ctx)+{+  if ((*ctx = EVP_MD_CTX_new()) == NULL)+	return (ARCHIVE_FAILED);+  EVP_DigestInit(*ctx, EVP_sha512());+  return (ARCHIVE_OK);+}++static int+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,+    size_t insize)+{+  EVP_DigestUpdate(*ctx, indata, insize);+  return (ARCHIVE_OK);+}++static int+__archive_sha512final(archive_sha512_ctx *ctx, void *md)+{+  if (*ctx) {+    EVP_DigestFinal(*ctx, md, NULL);+    EVP_MD_CTX_free(*ctx);+    *ctx = NULL;+  }+  return (ARCHIVE_OK);+}++#elif defined(ARCHIVE_CRYPTO_SHA512_WIN)++static int+__archive_sha512init(archive_sha512_ctx *ctx)+{+  return (win_crypto_init(ctx, CALG_SHA_512));+}++static int+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,+    size_t insize)+{+  return (win_crypto_Update(ctx, indata, insize));+}++static int+__archive_sha512final(archive_sha512_ctx *ctx, void *md)+{+  return (win_crypto_Final(md, 64, ctx));+}++#else++static int+__archive_sha512init(archive_sha512_ctx *ctx)+{+	(void)ctx; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,+    size_t insize)+{+	(void)ctx; /* UNUSED */+	(void)indata; /* UNUSED */+	(void)insize; /* UNUSED */+	return (ARCHIVE_FAILED);+}++static int+__archive_sha512final(archive_sha512_ctx *ctx, void *md)+{+	(void)ctx; /* UNUSED */+	(void)md; /* UNUSED */+	return (ARCHIVE_FAILED);+}++#endif++/* NOTE: Message Digest functions are set based on availability and by the+ * following order of preference.+ * 1. libc+ * 2. libc2+ * 3. libc3+ * 4. libSystem+ * 5. Nettle+ * 6. OpenSSL+ * 7. libmd+ * 8. Windows API+ */+const struct archive_digest __archive_digest =+{+/* MD5 */+  &__archive_md5init,+  &__archive_md5update,+  &__archive_md5final,++/* RIPEMD160 */+  &__archive_ripemd160init,+  &__archive_ripemd160update,+  &__archive_ripemd160final,++/* SHA1 */+  &__archive_sha1init,+  &__archive_sha1update,+  &__archive_sha1final,++/* SHA256 */+  &__archive_sha256init,+  &__archive_sha256update,+  &__archive_sha256final,++/* SHA384 */+  &__archive_sha384init,+  &__archive_sha384update,+  &__archive_sha384final,++/* SHA512 */+  &__archive_sha512init,+  &__archive_sha512update,+  &__archive_sha512final+};
+ c/archive_digest_private.h view
@@ -0,0 +1,412 @@+/*-+* Copyright (c) 2003-2007 Tim Kientzle+* Copyright (c) 2011 Andres Mejia+* All rights reserved.+*+* Redistribution and use in source and binary forms, with or without+* modification, are permitted provided that the following conditions+* are met:+* 1. Redistributions of source code must retain the above copyright+*    notice, this list of conditions and the following disclaimer.+* 2. Redistributions in binary form must reproduce the above copyright+*    notice, this list of conditions and the following disclaimer in the+*    documentation and/or other materials provided with the distribution.+*+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*/++#ifndef ARCHIVE_DIGEST_PRIVATE_H_INCLUDED+#define ARCHIVE_DIGEST_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif+/*+ * Crypto support in various Operating Systems:+ *+ * NetBSD:+ * - MD5 and SHA1 in libc: without _ after algorithm name+ * - SHA2 in libc: with _ after algorithm name+ *+ * OpenBSD:+ * - MD5, SHA1 and SHA2 in libc: without _ after algorithm name+ * - OpenBSD 4.4 and earlier have SHA2 in libc with _ after algorithm name+ *+ * DragonFly and FreeBSD:+ * - MD5 libmd: without _ after algorithm name+ * - SHA1, SHA256 and SHA512 in libmd: with _ after algorithm name+ *+ * Mac OS X (10.4 and later):+ * - MD5, SHA1 and SHA2 in libSystem: with CC_ prefix and _ after algorithm name+ *+ * OpenSSL:+ * - MD5, SHA1 and SHA2 in libcrypto: with _ after algorithm name+ *+ * Windows:+ * - MD5, SHA1 and SHA2 in archive_crypto.c using Windows crypto API+ */++/* libc crypto headers */+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)+#include <md5.h>+#endif+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)+#include <rmd160.h>+#endif+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)+#include <sha1.h>+#endif+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\+  defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\+  defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\+  defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\+  defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\+  defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\+  defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\+  defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\+  defined(ARCHIVE_CRYPTO_SHA512_LIBC3)+#include <sha2.h>+#endif++/* libmd crypto headers */+#if defined(ARCHIVE_CRYPTO_MD5_LIBMD) ||\+  defined(ARCHIVE_CRYPTO_RMD160_LIBMD) ||\+  defined(ARCHIVE_CRYPTO_SHA1_LIBMD) ||\+  defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\+  defined(ARCHIVE_CRYPTO_SHA512_LIBMD)+#define	ARCHIVE_CRYPTO_LIBMD 1+#endif++#if defined(ARCHIVE_CRYPTO_MD5_LIBMD)+#include <md5.h>+#endif+#if defined(ARCHIVE_CRYPTO_RMD160_LIBMD)+#include <ripemd.h>+#endif+#if defined(ARCHIVE_CRYPTO_SHA1_LIBMD)+#include <sha.h>+#endif+#if defined(ARCHIVE_CRYPTO_SHA256_LIBMD)+#include <sha256.h>+#endif+#if defined(ARCHIVE_CRYPTO_SHA512_LIBMD)+#include <sha512.h>+#endif++/* libSystem crypto headers */+#if defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\+  defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\+  defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\+  defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\+  defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)+#include <CommonCrypto/CommonDigest.h>+#endif++/* mbed TLS crypto headers */+#if defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)+#include <mbedtls/md5.h>+#endif+#if defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)+#include <mbedtls/ripemd160.h>+#endif+#if defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)+#include <mbedtls/sha1.h>+#endif+#if defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)+#include <mbedtls/sha256.h>+#endif+#if defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\+  defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)+#include <mbedtls/sha512.h>+#endif++/* Nettle crypto headers */+#if defined(ARCHIVE_CRYPTO_MD5_NETTLE)+#include <nettle/md5.h>+#endif+#if defined(ARCHIVE_CRYPTO_RMD160_NETTLE)+#include <nettle/ripemd160.h>+#endif+#if defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\+  defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\+  defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\+  defined(ARCHIVE_CRYPTO_SHA512_NETTLE)+#include <nettle/sha.h>+#endif++/* OpenSSL crypto headers */+#if defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\+  defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) ||\+  defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\+  defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\+  defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\+  defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)+#define	ARCHIVE_CRYPTO_OPENSSL 1+#include "archive_openssl_evp_private.h"+#endif++/* Windows crypto headers */+#if defined(ARCHIVE_CRYPTO_MD5_WIN)    ||\+  defined(ARCHIVE_CRYPTO_SHA1_WIN)   ||\+  defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\+  defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\+  defined(ARCHIVE_CRYPTO_SHA512_WIN)+#include <windows.h>+#include <wincrypt.h>+typedef struct {+  int   valid;+  HCRYPTPROV  cryptProv;+  HCRYPTHASH  hash;+} Digest_CTX;+#endif++/* typedefs */+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)+typedef MD5_CTX archive_md5_ctx;+#elif defined(ARCHIVE_CRYPTO_MD5_LIBMD)+typedef MD5_CTX archive_md5_ctx;+#elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM)+typedef CC_MD5_CTX archive_md5_ctx;+#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)+typedef mbedtls_md5_context archive_md5_ctx;+#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)+typedef struct md5_ctx archive_md5_ctx;+#elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)+typedef EVP_MD_CTX *archive_md5_ctx;+#elif defined(ARCHIVE_CRYPTO_MD5_WIN)+typedef Digest_CTX archive_md5_ctx;+#else+typedef unsigned char archive_md5_ctx;+#endif++#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)+typedef RMD160_CTX archive_rmd160_ctx;+#elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD)+typedef RIPEMD160_CTX archive_rmd160_ctx;+#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)+typedef mbedtls_ripemd160_context archive_rmd160_ctx;+#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)+typedef struct ripemd160_ctx archive_rmd160_ctx;+#elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)+typedef EVP_MD_CTX *archive_rmd160_ctx;+#else+typedef unsigned char archive_rmd160_ctx;+#endif++#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)+typedef SHA1_CTX archive_sha1_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD)+typedef SHA1_CTX archive_sha1_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM)+typedef CC_SHA1_CTX archive_sha1_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)+typedef mbedtls_sha1_context archive_sha1_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)+typedef struct sha1_ctx archive_sha1_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)+typedef EVP_MD_CTX *archive_sha1_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA1_WIN)+typedef Digest_CTX archive_sha1_ctx;+#else+typedef unsigned char archive_sha1_ctx;+#endif++#if defined(ARCHIVE_CRYPTO_SHA256_LIBC)+typedef SHA256_CTX archive_sha256_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2)+typedef SHA256_CTX archive_sha256_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3)+typedef SHA2_CTX archive_sha256_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD)+typedef SHA256_CTX archive_sha256_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM)+typedef CC_SHA256_CTX archive_sha256_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)+typedef mbedtls_sha256_context archive_sha256_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)+typedef struct sha256_ctx archive_sha256_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)+typedef EVP_MD_CTX *archive_sha256_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA256_WIN)+typedef Digest_CTX archive_sha256_ctx;+#else+typedef unsigned char archive_sha256_ctx;+#endif++#if defined(ARCHIVE_CRYPTO_SHA384_LIBC)+typedef SHA384_CTX archive_sha384_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2)+typedef SHA384_CTX archive_sha384_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3)+typedef SHA2_CTX archive_sha384_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM)+typedef CC_SHA512_CTX archive_sha384_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)+typedef mbedtls_sha512_context archive_sha384_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)+typedef struct sha384_ctx archive_sha384_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)+typedef EVP_MD_CTX *archive_sha384_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA384_WIN)+typedef Digest_CTX archive_sha384_ctx;+#else+typedef unsigned char archive_sha384_ctx;+#endif++#if defined(ARCHIVE_CRYPTO_SHA512_LIBC)+typedef SHA512_CTX archive_sha512_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2)+typedef SHA512_CTX archive_sha512_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3)+typedef SHA2_CTX archive_sha512_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD)+typedef SHA512_CTX archive_sha512_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)+typedef CC_SHA512_CTX archive_sha512_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)+typedef mbedtls_sha512_context archive_sha512_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)+typedef struct sha512_ctx archive_sha512_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)+typedef EVP_MD_CTX *archive_sha512_ctx;+#elif defined(ARCHIVE_CRYPTO_SHA512_WIN)+typedef Digest_CTX archive_sha512_ctx;+#else+typedef unsigned char archive_sha512_ctx;+#endif++/* defines */+#if defined(ARCHIVE_CRYPTO_MD5_LIBC) ||\+  defined(ARCHIVE_CRYPTO_MD5_LIBMD) ||	\+  defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\+  defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) ||\+  defined(ARCHIVE_CRYPTO_MD5_NETTLE) ||\+  defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\+  defined(ARCHIVE_CRYPTO_MD5_WIN)+#define ARCHIVE_HAS_MD5+#endif+#define archive_md5_init(ctx)\+  __archive_digest.md5init(ctx)+#define archive_md5_final(ctx, md)\+  __archive_digest.md5final(ctx, md)+#define archive_md5_update(ctx, buf, n)\+  __archive_digest.md5update(ctx, buf, n)++#if defined(ARCHIVE_CRYPTO_RMD160_LIBC) ||\+  defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) ||\+  defined(ARCHIVE_CRYPTO_RMD160_NETTLE) ||\+  defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)+#define ARCHIVE_HAS_RMD160+#endif+#define archive_rmd160_init(ctx)\+  __archive_digest.rmd160init(ctx)+#define archive_rmd160_final(ctx, md)\+  __archive_digest.rmd160final(ctx, md)+#define archive_rmd160_update(ctx, buf, n)\+  __archive_digest.rmd160update(ctx, buf, n)++#if defined(ARCHIVE_CRYPTO_SHA1_LIBC) ||\+  defined(ARCHIVE_CRYPTO_SHA1_LIBMD) ||	\+  defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\+  defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) ||\+  defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\+  defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\+  defined(ARCHIVE_CRYPTO_SHA1_WIN)+#define ARCHIVE_HAS_SHA1+#endif+#define archive_sha1_init(ctx)\+  __archive_digest.sha1init(ctx)+#define archive_sha1_final(ctx, md)\+  __archive_digest.sha1final(ctx, md)+#define archive_sha1_update(ctx, buf, n)\+  __archive_digest.sha1update(ctx, buf, n)++#if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\+  defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\+  defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\+  defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\+  defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\+  defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) ||\+  defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\+  defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\+  defined(ARCHIVE_CRYPTO_SHA256_WIN)+#define ARCHIVE_HAS_SHA256+#endif+#define archive_sha256_init(ctx)\+  __archive_digest.sha256init(ctx)+#define archive_sha256_final(ctx, md)\+  __archive_digest.sha256final(ctx, md)+#define archive_sha256_update(ctx, buf, n)\+  __archive_digest.sha256update(ctx, buf, n)++#if defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\+  defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\+  defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\+  defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\+  defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\+  defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\+  defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\+  defined(ARCHIVE_CRYPTO_SHA384_WIN)+#define ARCHIVE_HAS_SHA384+#endif+#define archive_sha384_init(ctx)\+  __archive_digest.sha384init(ctx)+#define archive_sha384_final(ctx, md)\+  __archive_digest.sha384final(ctx, md)+#define archive_sha384_update(ctx, buf, n)\+  __archive_digest.sha384update(ctx, buf, n)++#if defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\+  defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\+  defined(ARCHIVE_CRYPTO_SHA512_LIBC3) ||\+  defined(ARCHIVE_CRYPTO_SHA512_LIBMD) ||\+  defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) ||\+  defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) ||\+  defined(ARCHIVE_CRYPTO_SHA512_NETTLE) ||\+  defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) ||\+  defined(ARCHIVE_CRYPTO_SHA512_WIN)+#define ARCHIVE_HAS_SHA512+#endif+#define archive_sha512_init(ctx)\+  __archive_digest.sha512init(ctx)+#define archive_sha512_final(ctx, md)\+  __archive_digest.sha512final(ctx, md)+#define archive_sha512_update(ctx, buf, n)\+  __archive_digest.sha512update(ctx, buf, n)++/* Minimal interface to digest functionality for internal use in libarchive */+struct archive_digest+{+  /* Message Digest */+  int (*md5init)(archive_md5_ctx *ctx);+  int (*md5update)(archive_md5_ctx *, const void *, size_t);+  int (*md5final)(archive_md5_ctx *, void *);+  int (*rmd160init)(archive_rmd160_ctx *);+  int (*rmd160update)(archive_rmd160_ctx *, const void *, size_t);+  int (*rmd160final)(archive_rmd160_ctx *, void *);+  int (*sha1init)(archive_sha1_ctx *);+  int (*sha1update)(archive_sha1_ctx *, const void *, size_t);+  int (*sha1final)(archive_sha1_ctx *, void *);+  int (*sha256init)(archive_sha256_ctx *);+  int (*sha256update)(archive_sha256_ctx *, const void *, size_t);+  int (*sha256final)(archive_sha256_ctx *, void *);+  int (*sha384init)(archive_sha384_ctx *);+  int (*sha384update)(archive_sha384_ctx *, const void *, size_t);+  int (*sha384final)(archive_sha384_ctx *, void *);+  int (*sha512init)(archive_sha512_ctx *);+  int (*sha512update)(archive_sha512_ctx *, const void *, size_t);+  int (*sha512final)(archive_sha512_ctx *, void *);+};++extern const struct archive_digest __archive_digest;++#endif
+ c/archive_disk_acl_darwin.c view
@@ -0,0 +1,559 @@+/*-+ * Copyright (c) 2017 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++#if ARCHIVE_ACL_DARWIN++#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#if HAVE_ERRNO_H+#include <errno.h>+#endif+#if HAVE_MEMBERSHIP_H+#include <membership.h>+#endif+#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_SYS_ACL_H+#define _ACL_PRIVATE /* For debugging */+#include <sys/acl.h>+#endif++#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_disk_private.h"+#include "archive_write_disk_private.h"++typedef struct {+	const int a_perm;	/* Libarchive permission or flag */+	const int p_perm;	/* Platform permission or flag */+} acl_perm_map_t;++static const acl_perm_map_t acl_nfs4_perm_map[] = {+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},+	{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES},+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES},+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY},+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY},+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER},+#if HAVE_DECL_ACL_SYNCHRONIZE+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}+#endif+};++static const int acl_nfs4_perm_map_size =+    (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));++static const acl_perm_map_t acl_nfs4_flag_map[] = {+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED},+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT},+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT}+};++static const int acl_nfs4_flag_map_size =+    (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));++static int translate_guid(struct archive *a, acl_entry_t acl_entry,+    int *ae_id, int *ae_tag, const char **ae_name)+{+	void *q;+	uid_t ugid;+	int r, idtype;++	q = acl_get_qualifier(acl_entry);+	if (q == NULL)+		return (1);+	r = mbr_uuid_to_id((const unsigned char *)q, &ugid, &idtype);+	if (r != 0) {+		acl_free(q);+		return (1);+	}+	if (idtype == ID_TYPE_UID) {+		*ae_tag = ARCHIVE_ENTRY_ACL_USER;+		*ae_id = ugid;+		*ae_name = archive_read_disk_uname(a, *ae_id);+	} else if (idtype == ID_TYPE_GID) {+		*ae_tag = ARCHIVE_ENTRY_ACL_GROUP;+		*ae_id = ugid;+		*ae_name = archive_read_disk_gname(a, *ae_id);+	} else+		r = 1;++	acl_free(q);+	return (r);+}++static void+add_trivial_nfs4_acl(struct archive_entry *entry)+{+	mode_t mode;+	int i;+	const int rperm = ARCHIVE_ENTRY_ACL_READ_DATA;+	const int wperm = ARCHIVE_ENTRY_ACL_WRITE_DATA |+	    ARCHIVE_ENTRY_ACL_APPEND_DATA;+	const int eperm = ARCHIVE_ENTRY_ACL_EXECUTE;+	const int pubset = ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |+	    ARCHIVE_ENTRY_ACL_READ_ACL |+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE;+	const int ownset = pubset | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER;++	struct {+	    const int type;+	    const int tag;+	    int permset;+	} tacl_entry[] = {+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},+	    {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},+	    {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0},+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, ownset},+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_GROUP_OBJ, pubset},+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EVERYONE, pubset}+	};++	mode = archive_entry_mode(entry);++	/* Permissions for everyone@ */+	if (mode & 0004)+		tacl_entry[5].permset |= rperm;+	if (mode & 0002)+		tacl_entry[5].permset |= wperm;+	if (mode & 0001)+		tacl_entry[5].permset |= eperm;++	/* Permissions for group@ */+	if (mode & 0040)+		tacl_entry[4].permset |= rperm;+	else if (mode & 0004)+		tacl_entry[2].permset |= rperm;+	if (mode & 0020)+		tacl_entry[4].permset |= wperm;+	else if (mode & 0002)+		tacl_entry[2].permset |= wperm;+	if (mode & 0010)+		tacl_entry[4].permset |= eperm;+	else if (mode & 0001)+		tacl_entry[2].permset |= eperm;++	/* Permissions for owner@ */+	if (mode & 0400) {+		tacl_entry[3].permset |= rperm;+		if (!(mode & 0040) && (mode & 0004))+			tacl_entry[0].permset |= rperm;+	} else if ((mode & 0040) || (mode & 0004))+		tacl_entry[1].permset |= rperm;+	if (mode & 0200) {+		tacl_entry[3].permset |= wperm;+		if (!(mode & 0020) && (mode & 0002))+			tacl_entry[0].permset |= wperm;+	} else if ((mode & 0020) || (mode & 0002))+		tacl_entry[1].permset |= wperm;+	if (mode & 0100) {+		tacl_entry[3].permset |= eperm;+		if (!(mode & 0010) && (mode & 0001))+			tacl_entry[0].permset |= eperm;+	} else if ((mode & 0010) || (mode & 0001))+		tacl_entry[1].permset |= eperm;++	for (i = 0; i < 6; i++) {+		if (tacl_entry[i].permset != 0) {+			archive_entry_acl_add_entry(entry,+			    tacl_entry[i].type, tacl_entry[i].permset,+			    tacl_entry[i].tag, -1, NULL);+		}+	}++	return;+}++static int+translate_acl(struct archive_read_disk *a,+    struct archive_entry *entry, acl_t acl)+{+	acl_tag_t	 acl_tag;+	acl_flagset_t	 acl_flagset;+	acl_entry_t	 acl_entry;+	acl_permset_t	 acl_permset;+	int		 i, entry_acl_type;+	int		 r, s, ae_id, ae_tag, ae_perm;+	const char	*ae_name;++	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);+	if (s == -1) {+		archive_set_error(&a->archive, errno,+		    "Failed to get first ACL entry");+		return (ARCHIVE_WARN);+	}++	while (s == 0) {+		ae_id = -1;+		ae_name = NULL;+		ae_perm = 0;++		if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {+			archive_set_error(&a->archive, errno,+			    "Failed to get ACL tag type");+			return (ARCHIVE_WARN);+		}+		switch (acl_tag) {+		case ACL_EXTENDED_ALLOW:+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;+			r = translate_guid(&a->archive, acl_entry,+			    &ae_id, &ae_tag, &ae_name);+			break;+		case ACL_EXTENDED_DENY:+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;+			r = translate_guid(&a->archive, acl_entry,+			    &ae_id, &ae_tag, &ae_name);+			break;+		default:+			/* Skip types that libarchive can't support. */+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);+			continue;+		}++		/* Skip if translate_guid() above failed */+		if (r != 0) {+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);+			continue;+		}++		/*+		 * Libarchive stores "flag" (NFSv4 inheritance bits)+		 * in the ae_perm bitmap.+		 *+		 * acl_get_flagset_np() fails with non-NFSv4 ACLs+		 */+		if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {+			archive_set_error(&a->archive, errno,+			    "Failed to get flagset from a NFSv4 ACL entry");+			return (ARCHIVE_WARN);+		}+		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {+			r = acl_get_flag_np(acl_flagset,+			    acl_nfs4_flag_map[i].p_perm);+			if (r == -1) {+				archive_set_error(&a->archive, errno,+				    "Failed to check flag in a NFSv4 "+				    "ACL flagset");+				return (ARCHIVE_WARN);+			} else if (r)+				ae_perm |= acl_nfs4_flag_map[i].a_perm;+		}++		if (acl_get_permset(acl_entry, &acl_permset) != 0) {+			archive_set_error(&a->archive, errno,+			    "Failed to get ACL permission set");+			return (ARCHIVE_WARN);+		}++		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {+			/*+			 * acl_get_perm() is spelled differently on different+			 * platforms; see above.+			 */+			r = acl_get_perm_np(acl_permset,+			    acl_nfs4_perm_map[i].p_perm);+			if (r == -1) {+				archive_set_error(&a->archive, errno,+				    "Failed to check permission in an ACL "+				    "permission set");+				return (ARCHIVE_WARN);+			} else if (r)+				ae_perm |= acl_nfs4_perm_map[i].a_perm;+		}++#if !HAVE_DECL_ACL_SYNCHRONIZE+		/* On Mac OS X without ACL_SYNCHRONIZE assume it is set */+		ae_perm |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;+#endif++		archive_entry_acl_add_entry(entry, entry_acl_type,+					    ae_perm, ae_tag,+					    ae_id, ae_name);++		s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);+	}+	return (ARCHIVE_OK);+}++static int+set_acl(struct archive *a, int fd, const char *name,+    struct archive_acl *abstract_acl,+    int ae_requested_type, const char *tname)+{+	acl_t		 acl;+	acl_entry_t	 acl_entry;+	acl_permset_t	 acl_permset;+	acl_flagset_t	 acl_flagset;+	int		 ret;+	int		 ae_type, ae_permset, ae_tag, ae_id;+	uuid_t		 ae_uuid;+	uid_t		 ae_uid;+	gid_t		 ae_gid;+	const char	*ae_name;+	int		 entries;+	int		 i;++	ret = ARCHIVE_OK;+	entries = archive_acl_reset(abstract_acl, ae_requested_type);+	if (entries == 0)+		return (ARCHIVE_OK);++	if (ae_requested_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+		errno = ENOENT;+		archive_set_error(a, errno, "Unsupported ACL type");+		return (ARCHIVE_FAILED);+	}++	acl = acl_init(entries);+	if (acl == (acl_t)NULL) {+		archive_set_error(a, errno,+		    "Failed to initialize ACL working storage");+		return (ARCHIVE_FAILED);+	}++	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {+		/*+		 * Mac OS doesn't support NFSv4 ACLs for+		 * owner@, group@ and everyone@.+		 * We skip any of these ACLs found.+		 */+		if (ae_tag == ARCHIVE_ENTRY_ACL_USER_OBJ ||+		    ae_tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ ||+		    ae_tag == ARCHIVE_ENTRY_ACL_EVERYONE)+			continue;++		if (acl_create_entry(&acl, &acl_entry) != 0) {+			archive_set_error(a, errno,+			    "Failed to create a new ACL entry");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		switch (ae_type) {+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:+			acl_set_tag_type(acl_entry, ACL_EXTENDED_ALLOW);+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:+			acl_set_tag_type(acl_entry, ACL_EXTENDED_DENY);+			break;+		default:+			/* We don't support any other types on MacOS */+			continue;+		}++		switch (ae_tag) {+		case ARCHIVE_ENTRY_ACL_USER:+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);+			if (mbr_uid_to_uuid(ae_uid, ae_uuid) != 0)+				continue;+			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)+				continue;+			break;+		case ARCHIVE_ENTRY_ACL_GROUP:+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);+			if (mbr_gid_to_uuid(ae_gid, ae_uuid) != 0)+				continue;+			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)+				continue;+			break;+		default:+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Unsupported ACL tag");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		if (acl_get_permset(acl_entry, &acl_permset) != 0) {+			archive_set_error(a, errno,+			    "Failed to get ACL permission set");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}+		if (acl_clear_perms(acl_permset) != 0) {+			archive_set_error(a, errno,+			    "Failed to clear ACL permissions");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {+			if (ae_permset & acl_nfs4_perm_map[i].a_perm) {+				if (acl_add_perm(acl_permset,+				    acl_nfs4_perm_map[i].p_perm) != 0) {+					archive_set_error(a, errno,+					    "Failed to add ACL permission");+					ret = ARCHIVE_FAILED;+					goto exit_free;+				}+			}+		}++		/*+		 * acl_get_flagset_np() fails with non-NFSv4 ACLs+		 */+		if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {+			archive_set_error(a, errno,+			    "Failed to get flagset from an NFSv4 ACL entry");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}+		if (acl_clear_flags_np(acl_flagset) != 0) {+			archive_set_error(a, errno,+			    "Failed to clear flags from an NFSv4 ACL flagset");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {+			if (ae_permset & acl_nfs4_flag_map[i].a_perm) {+				if (acl_add_flag_np(acl_flagset,+				    acl_nfs4_flag_map[i].p_perm) != 0) {+					archive_set_error(a, errno,+					    "Failed to add flag to "+					    "NFSv4 ACL flagset");+					ret = ARCHIVE_FAILED;+					goto exit_free;+				}+			}+		}+	}++	if (fd >= 0) {+		if (acl_set_fd_np(fd, acl, ACL_TYPE_EXTENDED) == 0)+			ret = ARCHIVE_OK;+		else {+			if (errno == EOPNOTSUPP) {+				/* Filesystem doesn't support ACLs */+				ret = ARCHIVE_OK;+			} else {+				archive_set_error(a, errno,+				    "Failed to set acl on fd: %s", tname);+				ret = ARCHIVE_WARN;+			}+		}+	} else if (acl_set_link_np(name, ACL_TYPE_EXTENDED, acl) != 0) {+		if (errno == EOPNOTSUPP) {+			/* Filesystem doesn't support ACLs */+			ret = ARCHIVE_OK;+		} else {+			archive_set_error(a, errno, "Failed to set acl: %s",+			    tname);+			ret = ARCHIVE_WARN;+		}+	}+exit_free:+	acl_free(acl);+	return (ret);+}++int+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	const char	*accpath;+	acl_t		acl;+	int		r;++	accpath = NULL;++	if (*fd < 0) {+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);+		if (accpath == NULL)+			return (ARCHIVE_WARN);+	}++	archive_entry_acl_clear(entry);++	acl = NULL;++	if (*fd >= 0)+		acl = acl_get_fd_np(*fd, ACL_TYPE_EXTENDED);+	else if (!a->follow_symlinks)+		acl = acl_get_link_np(accpath, ACL_TYPE_EXTENDED);+	else+		acl = acl_get_file(accpath, ACL_TYPE_EXTENDED);++	if (acl != NULL) {+		r = translate_acl(a, entry, acl);+		acl_free(acl);+		acl = NULL;++		if (r != ARCHIVE_OK) {+			archive_set_error(&a->archive, errno,+			    "Couldn't translate NFSv4 ACLs");+		}++		/*+		 * Because Mac OS doesn't support owner@, group@ and everyone@+		 * ACLs we need to add NFSv4 ACLs mirroring the file mode to+		 * the archive entry. Otherwise extraction on non-Mac platforms+		 * would lead to an invalid file mode.+		 */+		if ((archive_entry_acl_types(entry) &+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)+			add_trivial_nfs4_acl(entry);++		return (r);+	}+	return (ARCHIVE_OK);+}++int+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,+    struct archive_acl *abstract_acl, __LA_MODE_T mode)+{+	int		ret = ARCHIVE_OK;++	(void)mode;	/* UNUSED */++	if ((archive_acl_types(abstract_acl) &+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {+		ret = set_acl(a, fd, name, abstract_acl,+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");+	}+	return (ret);+}+#endif	/* ARCHIVE_ACL_DARWIN */
+ c/archive_disk_acl_freebsd.c view
@@ -0,0 +1,702 @@+/*-+ * Copyright (c) 2003-2009 Tim Kientzle+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA+ * Copyright (c) 2017 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++#if ARCHIVE_ACL_FREEBSD++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_SYS_ACL_H+#define _ACL_PRIVATE /* For debugging */+#include <sys/acl.h>+#endif++#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_disk_private.h"+#include "archive_write_disk_private.h"++typedef struct {+	const int a_perm;	/* Libarchive permission or flag */+	const int p_perm;	/* Platform permission or flag */+} acl_perm_map_t;++static const acl_perm_map_t acl_posix_perm_map[] = {+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},+	{ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},+	{ARCHIVE_ENTRY_ACL_READ, ACL_READ},+};++static const int acl_posix_perm_map_size =+    (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0]));++#if ARCHIVE_ACL_FREEBSD_NFS4+static const acl_perm_map_t acl_nfs4_perm_map[] = {+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS},+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS},+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},+	{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL},+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL},+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER},+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}+};++static const int acl_nfs4_perm_map_size =+    (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));++static const acl_perm_map_t acl_nfs4_flag_map[] = {+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT},+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY},+	{ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS},+	{ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS},+#ifdef ACL_ENTRY_INHERITED+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}+#endif+};++static const int acl_nfs4_flag_map_size =+    (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));+#endif /* ARCHIVE_ACL_FREEBSD_NFS4 */++static int+translate_acl(struct archive_read_disk *a,+    struct archive_entry *entry, acl_t acl, int default_entry_acl_type)+{+#if ARCHIVE_ACL_FREEBSD_NFS4+	int brand;+	acl_flagset_t	 acl_flagset;+	acl_entry_type_t acl_type;+#endif+	acl_tag_t	 acl_tag;+	acl_entry_t	 acl_entry;+	acl_permset_t	 acl_permset;+	int		 i, entry_acl_type, perm_map_size;+	const acl_perm_map_t	*perm_map;+	int		 r, s, ae_id, ae_tag, ae_perm;+	void		*q;+	const char	*ae_name;++#if ARCHIVE_ACL_FREEBSD_NFS4+	// FreeBSD "brands" ACLs as POSIX.1e or NFSv4+	// Make sure the "brand" on this ACL is consistent+	// with the default_entry_acl_type bits provided.+	if (acl_get_brand_np(acl, &brand) != 0) {+		archive_set_error(&a->archive, errno,+		    "Failed to read ACL brand");+		return (ARCHIVE_WARN);+	}+	switch (brand) {+	case ACL_BRAND_POSIX:+		switch (default_entry_acl_type) {+		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:+		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:+			break;+		default:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Invalid ACL entry type for POSIX.1e ACL");+			return (ARCHIVE_WARN);+		}+		break;+	case ACL_BRAND_NFS4:+		if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Invalid ACL entry type for NFSv4 ACL");+			return (ARCHIVE_WARN);+		}+		break;+	default:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Unknown ACL brand");+		return (ARCHIVE_WARN);+	}+#endif++	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);+	if (s == -1) {+		archive_set_error(&a->archive, errno,+		    "Failed to get first ACL entry");+		return (ARCHIVE_WARN);+	}++	while (s == 1) {+		ae_id = -1;+		ae_name = NULL;+		ae_perm = 0;++		if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {+			archive_set_error(&a->archive, errno,+			    "Failed to get ACL tag type");+			return (ARCHIVE_WARN);+		}+		switch (acl_tag) {+		case ACL_USER:+			q = acl_get_qualifier(acl_entry);+			if (q != NULL) {+				ae_id = (int)*(uid_t *)q;+				acl_free(q);+				ae_name = archive_read_disk_uname(&a->archive,+				    ae_id);+			}+			ae_tag = ARCHIVE_ENTRY_ACL_USER;+			break;+		case ACL_GROUP:+			q = acl_get_qualifier(acl_entry);+			if (q != NULL) {+				ae_id = (int)*(gid_t *)q;+				acl_free(q);+				ae_name = archive_read_disk_gname(&a->archive,+				    ae_id);+			}+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP;+			break;+		case ACL_MASK:+			ae_tag = ARCHIVE_ENTRY_ACL_MASK;+			break;+		case ACL_USER_OBJ:+			ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;+			break;+		case ACL_GROUP_OBJ:+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;+			break;+		case ACL_OTHER:+			ae_tag = ARCHIVE_ENTRY_ACL_OTHER;+			break;+#if ARCHIVE_ACL_FREEBSD_NFS4+		case ACL_EVERYONE:+			ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;+			break;+#endif+		default:+			/* Skip types that libarchive can't support. */+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);+			continue;+		}++		// XXX acl_type maps to allow/deny/audit/YYYY bits+		entry_acl_type = default_entry_acl_type;++#if ARCHIVE_ACL_FREEBSD_NFS4+		if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			/*+			 * acl_get_entry_type_np() fails with non-NFSv4 ACLs+			 */+			if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) {+				archive_set_error(&a->archive, errno, "Failed "+				    "to get ACL type from a NFSv4 ACL entry");+				return (ARCHIVE_WARN);+			}+			switch (acl_type) {+			case ACL_ENTRY_TYPE_ALLOW:+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;+				break;+			case ACL_ENTRY_TYPE_DENY:+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;+				break;+			case ACL_ENTRY_TYPE_AUDIT:+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;+				break;+			case ACL_ENTRY_TYPE_ALARM:+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;+				break;+			default:+				archive_set_error(&a->archive, errno,+				    "Invalid NFSv4 ACL entry type");+				return (ARCHIVE_WARN);+			}++			/*+			 * Libarchive stores "flag" (NFSv4 inheritance bits)+			 * in the ae_perm bitmap.+			 *+			 * acl_get_flagset_np() fails with non-NFSv4 ACLs+			 */+			if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {+				archive_set_error(&a->archive, errno,+				    "Failed to get flagset from a NFSv4 "+				    "ACL entry");+				return (ARCHIVE_WARN);+			}+			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {+				r = acl_get_flag_np(acl_flagset,+				    acl_nfs4_flag_map[i].p_perm);+				if (r == -1) {+					archive_set_error(&a->archive, errno,+					    "Failed to check flag in a NFSv4 "+					    "ACL flagset");+					return (ARCHIVE_WARN);+				} else if (r)+					ae_perm |= acl_nfs4_flag_map[i].a_perm;+			}+		}+#endif++		if (acl_get_permset(acl_entry, &acl_permset) != 0) {+			archive_set_error(&a->archive, errno,+			    "Failed to get ACL permission set");+			return (ARCHIVE_WARN);+		}++#if ARCHIVE_ACL_FREEBSD_NFS4+		if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			perm_map_size = acl_nfs4_perm_map_size;+			perm_map = acl_nfs4_perm_map;+		} else {+#endif+			perm_map_size = acl_posix_perm_map_size;+			perm_map = acl_posix_perm_map;+#if ARCHIVE_ACL_FREEBSD_NFS4+		}+#endif++		for (i = 0; i < perm_map_size; ++i) {+			r = acl_get_perm_np(acl_permset, perm_map[i].p_perm);+			if (r == -1) {+				archive_set_error(&a->archive, errno,+				    "Failed to check permission in an ACL "+				    "permission set");+				return (ARCHIVE_WARN);+			} else if (r)+				ae_perm |= perm_map[i].a_perm;+		}++		archive_entry_acl_add_entry(entry, entry_acl_type,+					    ae_perm, ae_tag,+					    ae_id, ae_name);++		s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);+		if (s == -1) {+			archive_set_error(&a->archive, errno,+			    "Failed to get next ACL entry");+			return (ARCHIVE_WARN);+		}+	}+	return (ARCHIVE_OK);+}++static int+set_acl(struct archive *a, int fd, const char *name,+    struct archive_acl *abstract_acl,+    int ae_requested_type, const char *tname)+{+	int		 acl_type = 0;+	acl_t		 acl;+	acl_entry_t	 acl_entry;+	acl_permset_t	 acl_permset;+#if ARCHIVE_ACL_FREEBSD_NFS4+	acl_flagset_t	 acl_flagset;+	int		 r;+#endif+	int		 ret;+	int		 ae_type, ae_permset, ae_tag, ae_id;+	int		 perm_map_size;+	const acl_perm_map_t	*perm_map;+	uid_t		 ae_uid;+	gid_t		 ae_gid;+	const char	*ae_name;+	int		 entries;+	int		 i;++	ret = ARCHIVE_OK;+	entries = archive_acl_reset(abstract_acl, ae_requested_type);+	if (entries == 0)+		return (ARCHIVE_OK);+++	switch (ae_requested_type) {+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:+		acl_type = ACL_TYPE_ACCESS;+		break;+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:+		acl_type = ACL_TYPE_DEFAULT;+		break;+#if ARCHIVE_ACL_FREEBSD_NFS4+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:+		acl_type = ACL_TYPE_NFS4;+		break;+#endif+	default:+		errno = ENOENT;+		archive_set_error(a, errno, "Unsupported ACL type");+		return (ARCHIVE_FAILED);+	}++	acl = acl_init(entries);+	if (acl == (acl_t)NULL) {+		archive_set_error(a, errno,+		    "Failed to initialize ACL working storage");+		return (ARCHIVE_FAILED);+	}++	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {+		if (acl_create_entry(&acl, &acl_entry) != 0) {+			archive_set_error(a, errno,+			    "Failed to create a new ACL entry");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}+		switch (ae_tag) {+		case ARCHIVE_ENTRY_ACL_USER:+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);+			acl_set_tag_type(acl_entry, ACL_USER);+			acl_set_qualifier(acl_entry, &ae_uid);+			break;+		case ARCHIVE_ENTRY_ACL_GROUP:+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);+			acl_set_tag_type(acl_entry, ACL_GROUP);+			acl_set_qualifier(acl_entry, &ae_gid);+			break;+		case ARCHIVE_ENTRY_ACL_USER_OBJ:+			acl_set_tag_type(acl_entry, ACL_USER_OBJ);+			break;+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+			acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);+			break;+		case ARCHIVE_ENTRY_ACL_MASK:+			acl_set_tag_type(acl_entry, ACL_MASK);+			break;+		case ARCHIVE_ENTRY_ACL_OTHER:+			acl_set_tag_type(acl_entry, ACL_OTHER);+			break;+#if ARCHIVE_ACL_FREEBSD_NFS4+		case ARCHIVE_ENTRY_ACL_EVERYONE:+			acl_set_tag_type(acl_entry, ACL_EVERYONE);+			break;+#endif+		default:+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Unsupported ACL tag");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++#if ARCHIVE_ACL_FREEBSD_NFS4+		r = 0;+		switch (ae_type) {+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:+			r = acl_set_entry_type_np(acl_entry,+			    ACL_ENTRY_TYPE_ALLOW);+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:+			r = acl_set_entry_type_np(acl_entry,+			    ACL_ENTRY_TYPE_DENY);+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:+			r = acl_set_entry_type_np(acl_entry,+			    ACL_ENTRY_TYPE_AUDIT);+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:+			r = acl_set_entry_type_np(acl_entry,+			    ACL_ENTRY_TYPE_ALARM);+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:+		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:+			// These don't translate directly into the system ACL.+			break;+		default:+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Unsupported ACL entry type");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		if (r != 0) {+			archive_set_error(a, errno,+			    "Failed to set ACL entry type");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}+#endif++		if (acl_get_permset(acl_entry, &acl_permset) != 0) {+			archive_set_error(a, errno,+			    "Failed to get ACL permission set");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}+		if (acl_clear_perms(acl_permset) != 0) {+			archive_set_error(a, errno,+			    "Failed to clear ACL permissions");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}+#if ARCHIVE_ACL_FREEBSD_NFS4+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			perm_map_size = acl_nfs4_perm_map_size;+			perm_map = acl_nfs4_perm_map;+		} else {+#endif+			perm_map_size = acl_posix_perm_map_size;+			perm_map = acl_posix_perm_map;+#if ARCHIVE_ACL_FREEBSD_NFS4+		}+#endif++		for (i = 0; i < perm_map_size; ++i) {+			if (ae_permset & perm_map[i].a_perm) {+				if (acl_add_perm(acl_permset,+				    perm_map[i].p_perm) != 0) {+					archive_set_error(a, errno,+					    "Failed to add ACL permission");+					ret = ARCHIVE_FAILED;+					goto exit_free;+				}+			}+		}++#if ARCHIVE_ACL_FREEBSD_NFS4+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			/*+			 * acl_get_flagset_np() fails with non-NFSv4 ACLs+			 */+			if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {+				archive_set_error(a, errno,+				    "Failed to get flagset from an NFSv4 "+				    "ACL entry");+				ret = ARCHIVE_FAILED;+				goto exit_free;+			}+			if (acl_clear_flags_np(acl_flagset) != 0) {+				archive_set_error(a, errno,+				    "Failed to clear flags from an NFSv4 "+				    "ACL flagset");+				ret = ARCHIVE_FAILED;+				goto exit_free;+			}+			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {+				if (ae_permset & acl_nfs4_flag_map[i].a_perm) {+					if (acl_add_flag_np(acl_flagset,+					    acl_nfs4_flag_map[i].p_perm) != 0) {+						archive_set_error(a, errno,+						    "Failed to add flag to "+						    "NFSv4 ACL flagset");+						ret = ARCHIVE_FAILED;+						goto exit_free;+					}+				}+			}+		}+#endif+	}++	/* Try restoring the ACL through 'fd' if we can. */+	if (fd >= 0) {+		if (acl_set_fd_np(fd, acl, acl_type) == 0)+			ret = ARCHIVE_OK;+		else {+			if (errno == EOPNOTSUPP) {+				/* Filesystem doesn't support ACLs */+				ret = ARCHIVE_OK;+			} else {+				archive_set_error(a, errno,+				    "Failed to set acl on fd: %s", tname);+				ret = ARCHIVE_WARN;+			}+		}+	}+#if HAVE_ACL_SET_LINK_NP+	else if (acl_set_link_np(name, acl_type, acl) != 0)+#else+	/* FreeBSD older than 8.0 */+	else if (acl_set_file(name, acl_type, acl) != 0)+#endif+	{+		if (errno == EOPNOTSUPP) {+			/* Filesystem doesn't support ACLs */+			ret = ARCHIVE_OK;+		} else {+			archive_set_error(a, errno, "Failed to set acl: %s",+			    tname);+			ret = ARCHIVE_WARN;+		}+	}+exit_free:+	acl_free(acl);+	return (ret);+}++int+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	const char	*accpath;+	acl_t		acl;+	int		r;++	accpath = NULL;++	if (*fd < 0) {+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);+		if (accpath == NULL)+			return (ARCHIVE_WARN);+	}++	archive_entry_acl_clear(entry);++	acl = NULL;++#if ARCHIVE_ACL_FREEBSD_NFS4+	/* Try NFSv4 ACL first. */+	if (*fd >= 0)+		acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4);+	else if (!a->follow_symlinks)+		acl = acl_get_link_np(accpath, ACL_TYPE_NFS4);+	else+		acl = acl_get_file(accpath, ACL_TYPE_NFS4);++	/* Ignore "trivial" ACLs that just mirror the file mode. */+	if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {+		acl_free(acl);+		acl = NULL;+		return (ARCHIVE_OK);+	}++	if (acl != NULL) {+		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4);+		acl_free(acl);+		acl = NULL;++		if (r != ARCHIVE_OK) {+			archive_set_error(&a->archive, errno,+			    "Couldn't translate NFSv4 ACLs");+		}++		return (r);+	}+#endif++	/* Retrieve access ACL from file. */+	if (*fd >= 0)+		acl = acl_get_fd_np(*fd, ACL_TYPE_ACCESS);+#if HAVE_ACL_GET_LINK_NP+	else if (!a->follow_symlinks)+		acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);+#else+	else if ((!a->follow_symlinks)+	    && (archive_entry_filetype(entry) == AE_IFLNK))+		/* We can't get the ACL of a symlink, so we assume it can't+		   have one. */+		acl = NULL;+#endif+	else+		acl = acl_get_file(accpath, ACL_TYPE_ACCESS);++#if HAVE_ACL_IS_TRIVIAL_NP+	/* Ignore "trivial" ACLs that just mirror the file mode. */+	if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {+		acl_free(acl);+		acl = NULL;+	}+#endif++	if (acl != NULL) {+		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);+		acl_free(acl);+		acl = NULL;++		if (r != ARCHIVE_OK) {+			archive_set_error(&a->archive, errno,+			    "Couldn't translate access ACLs");+			return (r);+		}+	}++	/* Only directories can have default ACLs. */+	if (S_ISDIR(archive_entry_mode(entry))) {+		if (*fd >= 0)+			acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT);+		else+			acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);+		if (acl != NULL) {+			r = translate_acl(a, entry, acl,+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);+			acl_free(acl);+			if (r != ARCHIVE_OK) {+				archive_set_error(&a->archive, errno,+				    "Couldn't translate default ACLs");+				return (r);+			}+		}+	}+	return (ARCHIVE_OK);+}++int+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,+    struct archive_acl *abstract_acl, __LA_MODE_T mode)+{+	int		ret = ARCHIVE_OK;++	(void)mode;	/* UNUSED */++	if ((archive_acl_types(abstract_acl)+	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {+		if ((archive_acl_types(abstract_acl)+		    & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {+			ret = set_acl(a, fd, name, abstract_acl,+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");+			if (ret != ARCHIVE_OK)+				return (ret);+		}+		if ((archive_acl_types(abstract_acl)+		    & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)+			ret = set_acl(a, fd, name, abstract_acl,+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");++		/* Simultaneous POSIX.1e and NFSv4 is not supported */+		return (ret);+	}+#if ARCHIVE_ACL_FREEBSD_NFS4+	else if ((archive_acl_types(abstract_acl) &+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {+		ret = set_acl(a, fd, name, abstract_acl,+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");+	}+#endif+	return (ret);+}+#endif	/* ARCHIVE_ACL_FREEBSD */
+ c/archive_disk_acl_linux.c view
@@ -0,0 +1,743 @@+/*-+ * Copyright (c) 2003-2009 Tim Kientzle+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA+ * Copyright (c) 2017 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++#if ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_LIBRICHACL++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#if HAVE_ACL_LIBACL_H+#include <acl/libacl.h>+#endif+#ifdef HAVE_SYS_ACL_H+#include <sys/acl.h>+#endif+#ifdef HAVE_SYS_RICHACL_H+#include <sys/richacl.h>+#endif++#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_disk_private.h"+#include "archive_write_disk_private.h"++typedef struct {+	const int a_perm;	/* Libarchive permission or flag */+	const int p_perm;	/* Platform permission or flag */+} acl_perm_map_t;++#if ARCHIVE_ACL_LIBACL+static const acl_perm_map_t acl_posix_perm_map[] = {+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},+	{ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},+	{ARCHIVE_ENTRY_ACL_READ, ACL_READ},+};++static const int acl_posix_perm_map_size =+    (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0]));+#endif /* ARCHIVE_ACL_LIBACL */++#if ARCHIVE_ACL_LIBRICHACL+static const acl_perm_map_t acl_nfs4_perm_map[] = {+	{ARCHIVE_ENTRY_ACL_EXECUTE, RICHACE_EXECUTE},+	{ARCHIVE_ENTRY_ACL_READ_DATA, RICHACE_READ_DATA},+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, RICHACE_LIST_DIRECTORY},+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, RICHACE_WRITE_DATA},+	{ARCHIVE_ENTRY_ACL_ADD_FILE, RICHACE_ADD_FILE},+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, RICHACE_APPEND_DATA},+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, RICHACE_ADD_SUBDIRECTORY},+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, RICHACE_READ_NAMED_ATTRS},+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, RICHACE_WRITE_NAMED_ATTRS},+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, RICHACE_DELETE_CHILD},+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, RICHACE_READ_ATTRIBUTES},+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, RICHACE_WRITE_ATTRIBUTES},+	{ARCHIVE_ENTRY_ACL_DELETE, RICHACE_DELETE},+	{ARCHIVE_ENTRY_ACL_READ_ACL, RICHACE_READ_ACL},+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, RICHACE_WRITE_ACL},+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, RICHACE_WRITE_OWNER},+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, RICHACE_SYNCHRONIZE}+};++static const int acl_nfs4_perm_map_size =+    (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));++static const acl_perm_map_t acl_nfs4_flag_map[] = {+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, RICHACE_FILE_INHERIT_ACE},+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, RICHACE_DIRECTORY_INHERIT_ACE},+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, RICHACE_NO_PROPAGATE_INHERIT_ACE},+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, RICHACE_INHERIT_ONLY_ACE},+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, RICHACE_INHERITED_ACE}+};++static const int acl_nfs4_flag_map_size =+    (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));+#endif /* ARCHIVE_ACL_LIBRICHACL */++#if ARCHIVE_ACL_LIBACL+/*+ * Translate POSIX.1e ACLs into libarchive internal structure+ */+static int+translate_acl(struct archive_read_disk *a,+    struct archive_entry *entry, acl_t acl, int default_entry_acl_type)+{+	acl_tag_t	 acl_tag;+	acl_entry_t	 acl_entry;+	acl_permset_t	 acl_permset;+	int		 i, entry_acl_type;+	int		 r, s, ae_id, ae_tag, ae_perm;+	void		*q;+	const char	*ae_name;++	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);+	if (s == -1) {+		archive_set_error(&a->archive, errno,+		    "Failed to get first ACL entry");+		return (ARCHIVE_WARN);+	}++	while (s == 1) {+		ae_id = -1;+		ae_name = NULL;+		ae_perm = 0;++		if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {+			archive_set_error(&a->archive, errno,+			    "Failed to get ACL tag type");+			return (ARCHIVE_WARN);+		}+		switch (acl_tag) {+		case ACL_USER:+			q = acl_get_qualifier(acl_entry);+			if (q != NULL) {+				ae_id = (int)*(uid_t *)q;+				acl_free(q);+				ae_name = archive_read_disk_uname(&a->archive,+				    ae_id);+			}+			ae_tag = ARCHIVE_ENTRY_ACL_USER;+			break;+		case ACL_GROUP:+			q = acl_get_qualifier(acl_entry);+			if (q != NULL) {+				ae_id = (int)*(gid_t *)q;+				acl_free(q);+				ae_name = archive_read_disk_gname(&a->archive,+				    ae_id);+			}+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP;+			break;+		case ACL_MASK:+			ae_tag = ARCHIVE_ENTRY_ACL_MASK;+			break;+		case ACL_USER_OBJ:+			ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;+			break;+		case ACL_GROUP_OBJ:+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;+			break;+		case ACL_OTHER:+			ae_tag = ARCHIVE_ENTRY_ACL_OTHER;+			break;+		default:+			/* Skip types that libarchive can't support. */+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);+			continue;+		}++		// XXX acl_type maps to allow/deny/audit/YYYY bits+		entry_acl_type = default_entry_acl_type;++		if (acl_get_permset(acl_entry, &acl_permset) != 0) {+			archive_set_error(&a->archive, errno,+			    "Failed to get ACL permission set");+			return (ARCHIVE_WARN);+		}++		for (i = 0; i < acl_posix_perm_map_size; ++i) {+			r = acl_get_perm(acl_permset,+			    acl_posix_perm_map[i].p_perm);+			if (r == -1) {+				archive_set_error(&a->archive, errno,+				    "Failed to check permission in an ACL "+				    "permission set");+				return (ARCHIVE_WARN);+			} else if (r)+				ae_perm |= acl_posix_perm_map[i].a_perm;+		}++		archive_entry_acl_add_entry(entry, entry_acl_type,+					    ae_perm, ae_tag,+					    ae_id, ae_name);++		s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);+		if (s == -1) {+			archive_set_error(&a->archive, errno,+			    "Failed to get next ACL entry");+			return (ARCHIVE_WARN);+		}+	}+	return (ARCHIVE_OK);+}+#endif /* ARCHIVE_ACL_LIBACL */++#if ARCHIVE_ACL_LIBRICHACL+/*+ * Translate RichACL into libarchive internal ACL+ */+static int+translate_richacl(struct archive_read_disk *a, struct archive_entry *entry,+    struct richacl *richacl)+{+	int ae_id, ae_tag, ae_perm;+	int entry_acl_type, i;+	const char *ae_name;++	struct richace *richace;++	richacl_for_each_entry(richace, richacl) {+		ae_name = NULL;+		ae_tag = 0;+		ae_perm = 0;+		ae_id = -1;++		switch (richace->e_type) {+		case RICHACE_ACCESS_ALLOWED_ACE_TYPE:+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;+			break;+		case RICHACE_ACCESS_DENIED_ACE_TYPE:+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;+			break;+		default: /* Unknown entry type, skip */+			continue;+		}++		/* Unsupported */+		if (richace->e_flags & RICHACE_UNMAPPED_WHO)+			continue;++		if (richace->e_flags & RICHACE_SPECIAL_WHO) {+			switch (richace->e_id) {+			case RICHACE_OWNER_SPECIAL_ID:+				ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;+				break;+			case RICHACE_GROUP_SPECIAL_ID:+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;+				break;+			case RICHACE_EVERYONE_SPECIAL_ID:+				ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;+				break;+			default: /* Unknown special ID type */+				continue;+			}+		} else {+			ae_id = richace->e_id;+			if (richace->e_flags & RICHACE_IDENTIFIER_GROUP) {+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP;+				ae_name = archive_read_disk_gname(&a->archive,+				    (gid_t)(richace->e_id));+			} else {+				ae_tag = ARCHIVE_ENTRY_ACL_USER;+				ae_name = archive_read_disk_uname(&a->archive,+				    (uid_t)(richace->e_id));+			}+		}+		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {+			if ((richace->e_flags &+			    acl_nfs4_flag_map[i].p_perm) != 0)+				ae_perm |= acl_nfs4_flag_map[i].a_perm;+		}+		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {+			if ((richace->e_mask &+			    acl_nfs4_perm_map[i].p_perm) != 0)+				ae_perm |=+				    acl_nfs4_perm_map[i].a_perm;+		}++		archive_entry_acl_add_entry(entry, entry_acl_type,+		    ae_perm, ae_tag, ae_id, ae_name);+	}+	return (ARCHIVE_OK);+}+#endif	/* ARCHIVE_ACL_LIBRICHACL */++#if ARCHIVE_ACL_LIBRICHACL+static int+_richacl_mode_to_mask(short mode)+{+	int mask = 0;++	if (mode & S_IROTH)+		mask |= RICHACE_POSIX_MODE_READ;+	if (mode & S_IWOTH)+		mask |= RICHACE_POSIX_MODE_WRITE;+	if (mode & S_IXOTH)+		mask |= RICHACE_POSIX_MODE_EXEC;++	return (mask);+}++static void+_richacl_mode_to_masks(struct richacl *richacl, __LA_MODE_T mode)+{+	richacl->a_owner_mask = _richacl_mode_to_mask((mode & 0700) >> 6);+	richacl->a_group_mask = _richacl_mode_to_mask((mode & 0070) >> 3);+	richacl->a_other_mask = _richacl_mode_to_mask(mode & 0007);+}+#endif /* ARCHIVE_ACL_LIBRICHACL */++#if ARCHIVE_ACL_LIBRICHACL+static int+set_richacl(struct archive *a, int fd, const char *name,+    struct archive_acl *abstract_acl, __LA_MODE_T mode,+    int ae_requested_type, const char *tname)+{+	int		 ae_type, ae_permset, ae_tag, ae_id;+	uid_t		 ae_uid;+	gid_t		 ae_gid;+	const char	*ae_name;+	int		 entries;+	int		 i;+	int		 ret;+	int		 e = 0;+	struct richacl  *richacl = NULL;+	struct richace  *richace;++	ret = ARCHIVE_OK;+	entries = archive_acl_reset(abstract_acl, ae_requested_type);+	if (entries == 0)+		return (ARCHIVE_OK);++	if (ae_requested_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+		errno = ENOENT;+		archive_set_error(a, errno, "Unsupported ACL type");+		return (ARCHIVE_FAILED);+	}++	richacl = richacl_alloc(entries);+	if (richacl == NULL) {+		archive_set_error(a, errno,+			"Failed to initialize RichACL working storage");+		return (ARCHIVE_FAILED);+	}++	e = 0;++	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {+		richace = &(richacl->a_entries[e]);++		richace->e_flags = 0;+		richace->e_mask = 0;++		switch (ae_tag) {+		case ARCHIVE_ENTRY_ACL_USER:+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);+			richace->e_id = ae_uid;+			break;+		case ARCHIVE_ENTRY_ACL_GROUP:+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);+			richace->e_id = ae_gid;+			richace->e_flags |= RICHACE_IDENTIFIER_GROUP;+			break;+		case ARCHIVE_ENTRY_ACL_USER_OBJ:+			richace->e_flags |= RICHACE_SPECIAL_WHO;+			richace->e_id = RICHACE_OWNER_SPECIAL_ID;+			break;+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+			richace->e_flags |= RICHACE_SPECIAL_WHO;+			richace->e_id = RICHACE_GROUP_SPECIAL_ID;+			break;+		case ARCHIVE_ENTRY_ACL_EVERYONE:+			richace->e_flags |= RICHACE_SPECIAL_WHO;+			richace->e_id = RICHACE_EVERYONE_SPECIAL_ID;+			break;+		default:+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Unsupported ACL tag");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		switch (ae_type) {+			case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:+				richace->e_type =+				    RICHACE_ACCESS_ALLOWED_ACE_TYPE;+				break;+			case ARCHIVE_ENTRY_ACL_TYPE_DENY:+				richace->e_type =+				    RICHACE_ACCESS_DENIED_ACE_TYPE;+				break;+			case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:+			case ARCHIVE_ENTRY_ACL_TYPE_ALARM:+				break;+		default:+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Unsupported ACL entry type");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {+			if (ae_permset & acl_nfs4_perm_map[i].a_perm)+				richace->e_mask |= acl_nfs4_perm_map[i].p_perm;+		}++		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {+			if (ae_permset &+			    acl_nfs4_flag_map[i].a_perm)+				richace->e_flags |= acl_nfs4_flag_map[i].p_perm;+		}+	e++;+	}++	/* Fill RichACL masks */+	_richacl_mode_to_masks(richacl, mode);++	if (fd >= 0) {+		if (richacl_set_fd(fd, richacl) == 0)+			ret = ARCHIVE_OK;+		else {+			if (errno == EOPNOTSUPP) {+				/* Filesystem doesn't support ACLs */+				ret = ARCHIVE_OK;+			} else {+				archive_set_error(a, errno,+				    "Failed to set richacl on fd: %s", tname);+				ret = ARCHIVE_WARN;+			}+		}+	} else if (richacl_set_file(name, richacl) != 0) {+		if (errno == EOPNOTSUPP) {+			/* Filesystem doesn't support ACLs */+			ret = ARCHIVE_OK;+		} else {+			archive_set_error(a, errno, "Failed to set richacl: %s",+			    tname);+			ret = ARCHIVE_WARN;+		}+	}+exit_free:+	richacl_free(richacl);+	return (ret);+}+#endif /* ARCHIVE_ACL_RICHACL */++#if ARCHIVE_ACL_LIBACL+static int+set_acl(struct archive *a, int fd, const char *name,+    struct archive_acl *abstract_acl,+    int ae_requested_type, const char *tname)+{+	int		 acl_type = 0;+	int		 ae_type, ae_permset, ae_tag, ae_id;+	uid_t		 ae_uid;+	gid_t		 ae_gid;+	const char	*ae_name;+	int		 entries;+	int		 i;+	int		 ret;+	acl_t		 acl = NULL;+	acl_entry_t	 acl_entry;+	acl_permset_t	 acl_permset;++	ret = ARCHIVE_OK;+	entries = archive_acl_reset(abstract_acl, ae_requested_type);+	if (entries == 0)+		return (ARCHIVE_OK);++	switch (ae_requested_type) {+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:+		acl_type = ACL_TYPE_ACCESS;+		break;+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:+		acl_type = ACL_TYPE_DEFAULT;+		break;+	default:+		errno = ENOENT;+		archive_set_error(a, errno, "Unsupported ACL type");+		return (ARCHIVE_FAILED);+	}++	acl = acl_init(entries);+	if (acl == (acl_t)NULL) {+		archive_set_error(a, errno,+		    "Failed to initialize ACL working storage");+		return (ARCHIVE_FAILED);+	}++	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {++		if (acl_create_entry(&acl, &acl_entry) != 0) {+			archive_set_error(a, errno,+			    "Failed to create a new ACL entry");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		switch (ae_tag) {+		case ARCHIVE_ENTRY_ACL_USER:+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);+			acl_set_tag_type(acl_entry, ACL_USER);+			acl_set_qualifier(acl_entry, &ae_uid);+			break;+		case ARCHIVE_ENTRY_ACL_GROUP:+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);+			acl_set_tag_type(acl_entry, ACL_GROUP);+			acl_set_qualifier(acl_entry, &ae_gid);+			break;+		case ARCHIVE_ENTRY_ACL_USER_OBJ:+			acl_set_tag_type(acl_entry, ACL_USER_OBJ);+			break;+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+			acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);+			break;+		case ARCHIVE_ENTRY_ACL_MASK:+			acl_set_tag_type(acl_entry, ACL_MASK);+			break;+		case ARCHIVE_ENTRY_ACL_OTHER:+			acl_set_tag_type(acl_entry, ACL_OTHER);+			break;+		default:+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Unsupported ACL tag");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		if (acl_get_permset(acl_entry, &acl_permset) != 0) {+			archive_set_error(a, errno,+			    "Failed to get ACL permission set");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}+		if (acl_clear_perms(acl_permset) != 0) {+			archive_set_error(a, errno,+			    "Failed to clear ACL permissions");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		for (i = 0; i < acl_posix_perm_map_size; ++i) {+			if (ae_permset & acl_posix_perm_map[i].a_perm) {+				if (acl_add_perm(acl_permset,+				    acl_posix_perm_map[i].p_perm) != 0) {+					archive_set_error(a, errno,+					    "Failed to add ACL permission");+					ret = ARCHIVE_FAILED;+					goto exit_free;+				}+			}+		}++	}++	if (fd >= 0 && ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {+		if (acl_set_fd(fd, acl) == 0)+			ret = ARCHIVE_OK;+		else {+			if (errno == EOPNOTSUPP) {+				/* Filesystem doesn't support ACLs */+				ret = ARCHIVE_OK;+			} else {+				archive_set_error(a, errno,+				    "Failed to set acl on fd: %s", tname);+				ret = ARCHIVE_WARN;+			}+		}+	} else if (acl_set_file(name, acl_type, acl) != 0) {+		if (errno == EOPNOTSUPP) {+			/* Filesystem doesn't support ACLs */+			ret = ARCHIVE_OK;+		} else {+			archive_set_error(a, errno, "Failed to set acl: %s",+			    tname);+			ret = ARCHIVE_WARN;+		}+	}+exit_free:+	acl_free(acl);+	return (ret);+}+#endif /* ARCHIVE_ACL_LIBACL */++int+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	const char	*accpath;+	int		r;+#if ARCHIVE_ACL_LIBACL+	acl_t		acl;+#endif+#if ARCHIVE_ACL_LIBRICHACL+	struct richacl *richacl;+	mode_t		mode;+#endif++	accpath = NULL;+	r = ARCHIVE_OK;++	/* For default ACLs we need reachable accpath */+	if (*fd < 0 || S_ISDIR(archive_entry_mode(entry))) {+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);+		if (accpath == NULL)+			return (ARCHIVE_WARN);+	}++	archive_entry_acl_clear(entry);++#if ARCHIVE_ACL_LIBACL+	acl = NULL;+#endif+#if ARCHIVE_ACL_LIBRICHACL+	richacl = NULL;+#endif++#if ARCHIVE_ACL_LIBRICHACL+	/* Try NFSv4 ACL first. */+	if (*fd >= 0)+		richacl = richacl_get_fd(*fd);+	else if ((!a->follow_symlinks)+	    && (archive_entry_filetype(entry) == AE_IFLNK))+		/* We can't get the ACL of a symlink, so we assume it can't+		   have one */+		richacl = NULL;+	else+		richacl = richacl_get_file(accpath);++	/* Ignore "trivial" ACLs that just mirror the file mode. */+	if (richacl != NULL) {+		mode = archive_entry_mode(entry);+		if (richacl_equiv_mode(richacl, &mode) == 0) {+			richacl_free(richacl);+			richacl = NULL;+			return (ARCHIVE_OK);+		}+	}++	if (richacl != NULL) {+		r = translate_richacl(a, entry, richacl);+		richacl_free(richacl);+		richacl = NULL;++		if (r != ARCHIVE_OK) {+			archive_set_error(&a->archive, errno,+			"Couldn't translate NFSv4 ACLs");+		}++		return (r);+	}+#endif	/* ARCHIVE_ACL_LIBRICHACL */++#if ARCHIVE_ACL_LIBACL+	/* Retrieve access ACL from file. */+	if (*fd >= 0)+		acl = acl_get_fd(*fd);+	else if ((!a->follow_symlinks)+	    && (archive_entry_filetype(entry) == AE_IFLNK))+		/* We can't get the ACL of a symlink, so we assume it can't+		   have one. */+		acl = NULL;+	else+		acl = acl_get_file(accpath, ACL_TYPE_ACCESS);++	if (acl != NULL) {+		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);+		acl_free(acl);+		acl = NULL;++		if (r != ARCHIVE_OK) {+			archive_set_error(&a->archive, errno,+			    "Couldn't translate access ACLs");+			return (r);+		}+	}++	/* Only directories can have default ACLs. */+	if (S_ISDIR(archive_entry_mode(entry))) {+		acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);+		if (acl != NULL) {+			r = translate_acl(a, entry, acl,+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);+			acl_free(acl);+			if (r != ARCHIVE_OK) {+				archive_set_error(&a->archive, errno,+				    "Couldn't translate default ACLs");+				return (r);+			}+		}+	}+#endif	/* ARCHIVE_ACL_LIBACL */+	return (r);+}++int+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,+    struct archive_acl *abstract_acl, __LA_MODE_T mode)+{+	int		ret = ARCHIVE_OK;++#if !ARCHIVE_ACL_LIBRICHACL+	(void)mode;	/* UNUSED */+#endif++#if ARCHIVE_ACL_LIBRICHACL+	if ((archive_acl_types(abstract_acl)+	    & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {+		ret = set_richacl(a, fd, name, abstract_acl, mode,+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");+	}+#if ARCHIVE_ACL_LIBACL+	else+#endif+#endif	/* ARCHIVE_ACL_LIBRICHACL */+#if ARCHIVE_ACL_LIBACL+	if ((archive_acl_types(abstract_acl)+	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {+		if ((archive_acl_types(abstract_acl)+		    & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {+			ret = set_acl(a, fd, name, abstract_acl,+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");+			if (ret != ARCHIVE_OK)+				return (ret);+		}+		if ((archive_acl_types(abstract_acl)+		    & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)+			ret = set_acl(a, fd, name, abstract_acl,+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");+	}+#endif	/* ARCHIVE_ACL_LIBACL */+	return (ret);+}+#endif /* ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_LIBRICHACL */
+ c/archive_disk_acl_sunos.c view
@@ -0,0 +1,819 @@+/*-+ * Copyright (c) 2017 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++#if ARCHIVE_ACL_SUNOS++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_SYS_ACL_H+#define _ACL_PRIVATE /* For debugging */+#include <sys/acl.h>+#endif++#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_disk_private.h"+#include "archive_write_disk_private.h"++typedef struct {+	const int a_perm;	/* Libarchive permission or flag */+	const int p_perm;	/* Platform permission or flag */+} acl_perm_map_t;++static const acl_perm_map_t acl_posix_perm_map[] = {+	{ARCHIVE_ENTRY_ACL_EXECUTE, S_IXOTH },+	{ARCHIVE_ENTRY_ACL_WRITE, S_IWOTH },+	{ARCHIVE_ENTRY_ACL_READ, S_IROTH }+};++static const int acl_posix_perm_map_size =+    (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0]));++#if ARCHIVE_ACL_SUNOS_NFS4+static const acl_perm_map_t acl_nfs4_perm_map[] = {+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACE_EXECUTE},+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACE_READ_DATA},+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACE_LIST_DIRECTORY},+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACE_WRITE_DATA},+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACE_ADD_FILE},+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACE_APPEND_DATA},+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACE_ADD_SUBDIRECTORY},+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACE_READ_NAMED_ATTRS},+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACE_WRITE_NAMED_ATTRS},+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACE_DELETE_CHILD},+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACE_READ_ATTRIBUTES},+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACE_WRITE_ATTRIBUTES},+	{ARCHIVE_ENTRY_ACL_DELETE, ACE_DELETE},+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACE_READ_ACL},+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACE_WRITE_ACL},+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACE_WRITE_OWNER},+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACE_SYNCHRONIZE}+};++static const int acl_nfs4_perm_map_size =+    (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));++static const acl_perm_map_t acl_nfs4_flag_map[] = {+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACE_FILE_INHERIT_ACE},+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACE_DIRECTORY_INHERIT_ACE},+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACE_NO_PROPAGATE_INHERIT_ACE},+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACE_INHERIT_ONLY_ACE},+	{ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACE_SUCCESSFUL_ACCESS_ACE_FLAG},+	{ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACE_FAILED_ACCESS_ACE_FLAG},+#ifdef ACE_INHERITED_ACE+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE}+#endif+};++const int acl_nfs4_flag_map_size =+    (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));++#endif /* ARCHIVE_ACL_SUNOS_NFS4 */++static void *+sunacl_get(int cmd, int *aclcnt, int fd, const char *path)+{+	int cnt, cntcmd;+	size_t size;+	void *aclp;++	if (cmd == GETACL) {+		cntcmd = GETACLCNT;+		size = sizeof(aclent_t);+	}+#if ARCHIVE_ACL_SUNOS_NFS4+	else if (cmd == ACE_GETACL) {+		cntcmd = ACE_GETACLCNT;+		size = sizeof(ace_t);+	}+#endif+	else {+		errno = EINVAL;+		*aclcnt = -1;+		return (NULL);+	}++	aclp = NULL;+	cnt = -2;++	while (cnt == -2 || (cnt == -1 && errno == ENOSPC)) {+		if (path != NULL)+			cnt = acl(path, cntcmd, 0, NULL);+		else+			cnt = facl(fd, cntcmd, 0, NULL);++		if (cnt > 0) {+			if (aclp == NULL)+				aclp = malloc(cnt * size);+			else+				aclp = realloc(NULL, cnt * size);+			if (aclp != NULL) {+				if (path != NULL)+					cnt = acl(path, cmd, cnt, aclp);+				else+					cnt = facl(fd, cmd, cnt, aclp);+			}+		} else {+			free(aclp);+			aclp = NULL;+			break;+		}+	}++	*aclcnt = cnt;+	return (aclp);+}++/*+ * Check if acl is trivial+ * This is a FreeBSD acl_is_trivial_np() implementation for Solaris+ */+static int+sun_acl_is_trivial(void *aclp, int aclcnt, mode_t mode, int is_nfs4,+    int is_dir, int *trivialp)+{+#if ARCHIVE_ACL_SUNOS_NFS4+	int i, p;+	const uint32_t rperm = ACE_READ_DATA;+	const uint32_t wperm = ACE_WRITE_DATA | ACE_APPEND_DATA;+	const uint32_t eperm = ACE_EXECUTE;+	const uint32_t pubset = ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS |+	    ACE_READ_ACL | ACE_SYNCHRONIZE;+	const uint32_t ownset = pubset | ACE_WRITE_ATTRIBUTES |+	    ACE_WRITE_NAMED_ATTRS | ACE_WRITE_ACL | ACE_WRITE_OWNER;++	ace_t *ace;+	ace_t tace[6];+#endif++	if (aclp == NULL || trivialp == NULL)+		return (-1);++	*trivialp = 0;++	/*+	 * POSIX.1e ACLs marked with ACL_IS_TRIVIAL are compatible with+	 * FreeBSD acl_is_trivial_np(). On Solaris they have 4 entries,+	 * including mask.+	 */+	if (!is_nfs4) {+		if (aclcnt == 4)+			*trivialp = 1;+		return (0);+	}++#if ARCHIVE_ACL_SUNOS_NFS4+	/*+	 * Continue with checking NFSv4 ACLs+	 *+	 * Create list of trivial ace's to be compared+	 */++	/* owner@ allow pre */+	tace[0].a_flags = ACE_OWNER;+	tace[0].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;+	tace[0].a_access_mask = 0;++	/* owner@ deny */+	tace[1].a_flags = ACE_OWNER;+	tace[1].a_type = ACE_ACCESS_DENIED_ACE_TYPE;+	tace[1].a_access_mask = 0;++	/* group@ deny */+	tace[2].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP;+	tace[2].a_type = ACE_ACCESS_DENIED_ACE_TYPE;+	tace[2].a_access_mask = 0;++	/* owner@ allow */+	tace[3].a_flags = ACE_OWNER;+	tace[3].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;+	tace[3].a_access_mask = ownset;++	/* group@ allow */+	tace[4].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP;+	tace[4].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;+	tace[4].a_access_mask = pubset;++	/* everyone@ allow */+	tace[5].a_flags = ACE_EVERYONE;+	tace[5].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;+	tace[5].a_access_mask = pubset;++	/* Permissions for everyone@ */+	if (mode & 0004)+		tace[5].a_access_mask |= rperm;+	if (mode & 0002)+		tace[5].a_access_mask |= wperm;+	if (mode & 0001)+		tace[5].a_access_mask |= eperm;++	/* Permissions for group@ */+	if (mode & 0040)+		tace[4].a_access_mask |= rperm;+	else if (mode & 0004)+		tace[2].a_access_mask |= rperm;+	if (mode & 0020)+		tace[4].a_access_mask |= wperm;+	else if (mode & 0002)+		tace[2].a_access_mask |= wperm;+	if (mode & 0010)+		tace[4].a_access_mask |= eperm;+	else if (mode & 0001)+		tace[2].a_access_mask |= eperm;++	/* Permissions for owner@ */+	if (mode & 0400) {+		tace[3].a_access_mask |= rperm;+		if (!(mode & 0040) && (mode & 0004))+			tace[0].a_access_mask |= rperm;+	} else if ((mode & 0040) || (mode & 0004))+		tace[1].a_access_mask |= rperm;+	if (mode & 0200) {+		tace[3].a_access_mask |= wperm;+		if (!(mode & 0020) && (mode & 0002))+			tace[0].a_access_mask |= wperm;+	} else if ((mode & 0020) || (mode & 0002))+		tace[1].a_access_mask |= wperm;+	if (mode & 0100) {+		tace[3].a_access_mask |= eperm;+		if (!(mode & 0010) && (mode & 0001))+			tace[0].a_access_mask |= eperm;+	} else if ((mode & 0010) || (mode & 0001))+		tace[1].a_access_mask |= eperm;++	/* Check if the acl count matches */+	p = 3;+	for (i = 0; i < 3; i++) {+		if (tace[i].a_access_mask != 0)+			p++;+	}+	if (aclcnt != p)+		return (0);++	p = 0;+	for (i = 0; i < 6; i++) {+		if (tace[i].a_access_mask != 0) {+			ace = &((ace_t *)aclp)[p];+			/*+			 * Illumos added ACE_DELETE_CHILD to write perms for+			 * directories. We have to check against that, too.+			 */+			if (ace->a_flags != tace[i].a_flags ||+			    ace->a_type != tace[i].a_type ||+			    (ace->a_access_mask != tace[i].a_access_mask &&+			    (!is_dir || (tace[i].a_access_mask & wperm) == 0 ||+			    ace->a_access_mask !=+			    (tace[i].a_access_mask | ACE_DELETE_CHILD))))+				return (0);+			p++;+		}+	}++	*trivialp = 1;+#else	/* !ARCHIVE_ACL_SUNOS_NFS4 */+	(void)is_dir;	/* UNUSED */+	(void)aclp;	/* UNUSED */+#endif	/* !ARCHIVE_ACL_SUNOS_NFS4 */+	return (0);+}++/*+ * Translate Solaris POSIX.1e and NFSv4 ACLs into libarchive internal ACL+ */+static int+translate_acl(struct archive_read_disk *a,+    struct archive_entry *entry, void *aclp, int aclcnt,+    int default_entry_acl_type)+{+	int e, i;+	int ae_id, ae_tag, ae_perm;+	int entry_acl_type;+	const char *ae_name;+	aclent_t *aclent;+#if ARCHIVE_ACL_SUNOS_NFS4+	ace_t *ace;+#endif++	if (aclcnt <= 0)+		return (ARCHIVE_OK);++	for (e = 0; e < aclcnt; e++) {+		ae_name = NULL;+		ae_tag = 0;+		ae_perm = 0;++#if ARCHIVE_ACL_SUNOS_NFS4+		if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			ace = &((ace_t *)aclp)[e];+			ae_id = ace->a_who;++			switch(ace->a_type) {+			case ACE_ACCESS_ALLOWED_ACE_TYPE:+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;+				break;+			case ACE_ACCESS_DENIED_ACE_TYPE:+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;+				break;+			case ACE_SYSTEM_AUDIT_ACE_TYPE:+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;+				break;+			case ACE_SYSTEM_ALARM_ACE_TYPE:+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;+				break;+			default:+				/* Unknown entry type, skip */+				continue;+			}++			if ((ace->a_flags & ACE_OWNER) != 0)+				ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;+			else if ((ace->a_flags & ACE_GROUP) != 0)+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;+			else if ((ace->a_flags & ACE_EVERYONE) != 0)+				ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;+			else if ((ace->a_flags & ACE_IDENTIFIER_GROUP) != 0) {+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP;+				ae_name = archive_read_disk_gname(&a->archive,+				    ae_id);+			} else {+				ae_tag = ARCHIVE_ENTRY_ACL_USER;+				ae_name = archive_read_disk_uname(&a->archive,+				    ae_id);+			}++			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {+				if ((ace->a_flags &+				    acl_nfs4_flag_map[i].p_perm) != 0)+					ae_perm |= acl_nfs4_flag_map[i].a_perm;+			}++			for (i = 0; i < acl_nfs4_perm_map_size; ++i) {+				if ((ace->a_access_mask &+				    acl_nfs4_perm_map[i].p_perm) != 0)+					ae_perm |= acl_nfs4_perm_map[i].a_perm;+			}+		} else+#endif	/* ARCHIVE_ACL_SUNOS_NFS4 */+		if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {+			aclent = &((aclent_t *)aclp)[e];+			if ((aclent->a_type & ACL_DEFAULT) != 0)+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;+			else+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;+			ae_id = aclent->a_id;++			switch(aclent->a_type) {+			case DEF_USER:+			case USER:+				ae_name = archive_read_disk_uname(&a->archive,+				    ae_id);+				ae_tag = ARCHIVE_ENTRY_ACL_USER;+				break;+			case DEF_GROUP:+			case GROUP:+				ae_name = archive_read_disk_gname(&a->archive,+				    ae_id);+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP;+				break;+			case DEF_CLASS_OBJ:+			case CLASS_OBJ:+				ae_tag = ARCHIVE_ENTRY_ACL_MASK;+				break;+			case DEF_USER_OBJ:+			case USER_OBJ:+				ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;+				break;+			case DEF_GROUP_OBJ:+			case GROUP_OBJ:+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;+				break;+			case DEF_OTHER_OBJ:+			case OTHER_OBJ:+				ae_tag = ARCHIVE_ENTRY_ACL_OTHER;+				break;+			default:+				/* Unknown tag type, skip */+				continue;+			}++			for (i = 0; i < acl_posix_perm_map_size; ++i) {+				if ((aclent->a_perm &+				    acl_posix_perm_map[i].p_perm) != 0)+					ae_perm |= acl_posix_perm_map[i].a_perm;+			}+		} else+			return (ARCHIVE_WARN);++		archive_entry_acl_add_entry(entry, entry_acl_type,+		    ae_perm, ae_tag, ae_id, ae_name);+	}+	return (ARCHIVE_OK);+}++static int+set_acl(struct archive *a, int fd, const char *name,+    struct archive_acl *abstract_acl,+    int ae_requested_type, const char *tname)+{+	aclent_t	 *aclent;+#if ARCHIVE_ACL_SUNOS_NFS4+	ace_t		 *ace;+#endif+	int		 cmd, e, r;+	void		 *aclp;+	int		 ret;+	int		 ae_type, ae_permset, ae_tag, ae_id;+	int		 perm_map_size;+	const acl_perm_map_t	*perm_map;+	uid_t		 ae_uid;+	gid_t		 ae_gid;+	const char	*ae_name;+	int		 entries;+	int		 i;++	ret = ARCHIVE_OK;+	entries = archive_acl_reset(abstract_acl, ae_requested_type);+	if (entries == 0)+		return (ARCHIVE_OK);+++	switch (ae_requested_type) {+	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:+		cmd = SETACL;+		aclp = malloc(entries * sizeof(aclent_t));+		break;+#if ARCHIVE_ACL_SUNOS_NFS4+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:+		cmd = ACE_SETACL;+		aclp = malloc(entries * sizeof(ace_t));++		break;+#endif+	default:+		errno = ENOENT;+		archive_set_error(a, errno, "Unsupported ACL type");+		return (ARCHIVE_FAILED);+	}++	if (aclp == NULL) {+		archive_set_error(a, errno,+		    "Can't allocate memory for acl buffer");+		return (ARCHIVE_FAILED);+	}++	e = 0;++	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {+		aclent = NULL;+#if ARCHIVE_ACL_SUNOS_NFS4+		ace = NULL;+#endif+		if (cmd == SETACL) {+			aclent = &((aclent_t *)aclp)[e];+			aclent->a_id = -1;+			aclent->a_type = 0;+			aclent->a_perm = 0;+		}+#if ARCHIVE_ACL_SUNOS_NFS4+		else {	/* cmd == ACE_SETACL */+			ace = &((ace_t *)aclp)[e];+			ace->a_who = -1;+			ace->a_access_mask = 0;+			ace->a_flags = 0;+		}+#endif	/* ARCHIVE_ACL_SUNOS_NFS4 */++		switch (ae_tag) {+		case ARCHIVE_ENTRY_ACL_USER:+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);+			if (aclent != NULL) {+				aclent->a_id = ae_uid;+				aclent->a_type |= USER;+			}+#if ARCHIVE_ACL_SUNOS_NFS4+			else {+				ace->a_who = ae_uid;+			}+#endif+			break;+		case ARCHIVE_ENTRY_ACL_GROUP:+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);+			if (aclent != NULL) {+				aclent->a_id = ae_gid;+				aclent->a_type |= GROUP;+			}+#if ARCHIVE_ACL_SUNOS_NFS4+			else {+				ace->a_who = ae_gid;+				ace->a_flags |= ACE_IDENTIFIER_GROUP;+			}+#endif+			break;+		case ARCHIVE_ENTRY_ACL_USER_OBJ:+			if (aclent != NULL)+				aclent->a_type |= USER_OBJ;+#if ARCHIVE_ACL_SUNOS_NFS4+			else {+				ace->a_flags |= ACE_OWNER;+			}+#endif+			break;+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:+			if (aclent != NULL)+				aclent->a_type |= GROUP_OBJ;+#if ARCHIVE_ACL_SUNOS_NFS4+			else {+				ace->a_flags |= ACE_GROUP;+				ace->a_flags |= ACE_IDENTIFIER_GROUP;+			}+#endif+			break;+		case ARCHIVE_ENTRY_ACL_MASK:+			if (aclent != NULL)+				aclent->a_type |= CLASS_OBJ;+			break;+		case ARCHIVE_ENTRY_ACL_OTHER:+			if (aclent != NULL)+				aclent->a_type |= OTHER_OBJ;+			break;+#if ARCHIVE_ACL_SUNOS_NFS4+		case ARCHIVE_ENTRY_ACL_EVERYONE:+			if (ace != NULL)+				ace->a_flags |= ACE_EVERYONE;+			break;+#endif+		default:+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Unsupported ACL tag");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		r = 0;+		switch (ae_type) {+#if ARCHIVE_ACL_SUNOS_NFS4+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:+			if (ace != NULL)+				ace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;+			else+				r = -1;+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:+			if (ace != NULL)+				ace->a_type = ACE_ACCESS_DENIED_ACE_TYPE;+			else+				r = -1;+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:+			if (ace != NULL)+				ace->a_type = ACE_SYSTEM_AUDIT_ACE_TYPE;+			else+				r = -1;+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:+			if (ace != NULL)+				ace->a_type = ACE_SYSTEM_ALARM_ACE_TYPE;+			else+				r = -1;+			break;+#endif+		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:+			if (aclent == NULL)+				r = -1;+			break;+		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:+			if (aclent != NULL)+				aclent->a_type |= ACL_DEFAULT;+			else+				r = -1;+			break;+		default:+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Unsupported ACL entry type");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++		if (r != 0) {+			errno = EINVAL;+			archive_set_error(a, errno,+			    "Failed to set ACL entry type");+			ret = ARCHIVE_FAILED;+			goto exit_free;+		}++#if ARCHIVE_ACL_SUNOS_NFS4+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			perm_map_size = acl_nfs4_perm_map_size;+			perm_map = acl_nfs4_perm_map;+		} else {+#endif+			perm_map_size = acl_posix_perm_map_size;+			perm_map = acl_posix_perm_map;+#if ARCHIVE_ACL_SUNOS_NFS4+		}+#endif+		for (i = 0; i < perm_map_size; ++i) {+			if (ae_permset & perm_map[i].a_perm) {+#if ARCHIVE_ACL_SUNOS_NFS4+				if (ae_requested_type ==+				    ARCHIVE_ENTRY_ACL_TYPE_NFS4)+					ace->a_access_mask |=+					    perm_map[i].p_perm;+				else+#endif+					aclent->a_perm |= perm_map[i].p_perm;+			}+		}++#if ARCHIVE_ACL_SUNOS_NFS4+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {+			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {+				if (ae_permset & acl_nfs4_flag_map[i].a_perm) {+					ace->a_flags |=+					    acl_nfs4_flag_map[i].p_perm;+				}+			}+		}+#endif+	e++;+	}++	/* Try restoring the ACL through 'fd' if we can. */+	if (fd >= 0) {+		if (facl(fd, cmd, entries, aclp) == 0)+			ret = ARCHIVE_OK;+		else {+			if (errno == EOPNOTSUPP) {+				/* Filesystem doesn't support ACLs */+				ret = ARCHIVE_OK;+			} else {+				archive_set_error(a, errno,+				    "Failed to set acl on fd: %s", tname);+				ret = ARCHIVE_WARN;+			}+		}+	} else if (acl(name, cmd, entries, aclp) != 0) {+		if (errno == EOPNOTSUPP) {+			/* Filesystem doesn't support ACLs */+			ret = ARCHIVE_OK;+		} else {+			archive_set_error(a, errno, "Failed to set acl: %s",+			    tname);+			ret = ARCHIVE_WARN;+		}+	}+exit_free:+	free(aclp);+	return (ret);+}++int+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	const char	*accpath;+	void		*aclp;+	int		aclcnt;+	int		r;++	accpath = NULL;++	if (*fd < 0) {+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);+		if (accpath == NULL)+			return (ARCHIVE_WARN);+	}++	archive_entry_acl_clear(entry);++	aclp = NULL;++#if ARCHIVE_ACL_SUNOS_NFS4+	if (*fd >= 0)+		aclp = sunacl_get(ACE_GETACL, &aclcnt, *fd, NULL);+	else if ((!a->follow_symlinks)+	    && (archive_entry_filetype(entry) == AE_IFLNK))+		/* We can't get the ACL of a symlink, so we assume it can't+		   have one. */+		aclp = NULL;+	else+		aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, accpath);++	if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt,+	    archive_entry_mode(entry), 1, S_ISDIR(archive_entry_mode(entry)),+	    &r) == 0 && r == 1) {+		free(aclp);+		aclp = NULL;+		return (ARCHIVE_OK);+	}++	if (aclp != NULL) {+		r = translate_acl(a, entry, aclp, aclcnt,+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4);+		free(aclp);+		aclp = NULL;++		if (r != ARCHIVE_OK) {+			archive_set_error(&a->archive, errno,+			    "Couldn't translate NFSv4 ACLs");+		}+		return (r);+	}+#endif	/* ARCHIVE_ACL_SUNOS_NFS4 */++	/* Retrieve POSIX.1e ACLs from file. */+	if (*fd >= 0)+		aclp = sunacl_get(GETACL, &aclcnt, *fd, NULL);+	else if ((!a->follow_symlinks)+	    && (archive_entry_filetype(entry) == AE_IFLNK))+		/* We can't get the ACL of a symlink, so we assume it can't+		   have one. */+		aclp = NULL;+	else+		aclp = sunacl_get(GETACL, &aclcnt, 0, accpath);++	/* Ignore "trivial" ACLs that just mirror the file mode. */+	if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt,+	    archive_entry_mode(entry), 0, S_ISDIR(archive_entry_mode(entry)),+	    &r) == 0 && r == 1) {+		free(aclp);+		aclp = NULL;+	}++	if (aclp != NULL)+	{+		r = translate_acl(a, entry, aclp, aclcnt,+		    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);+		free(aclp);+		aclp = NULL;++		if (r != ARCHIVE_OK) {+			archive_set_error(&a->archive, errno,+			    "Couldn't translate access ACLs");+			return (r);+		}+	}++	return (ARCHIVE_OK);+}++int+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,+    struct archive_acl *abstract_acl, __LA_MODE_T mode)+{+	int		ret = ARCHIVE_OK;++	(void)mode;	/* UNUSED */++	if ((archive_acl_types(abstract_acl)+	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {+		/* Solaris writes POSIX.1e access and default ACLs together */+		ret = set_acl(a, fd, name, abstract_acl,+		    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, "posix1e");++		/* Simultaneous POSIX.1e and NFSv4 is not supported */+		return (ret);+	}+#if ARCHIVE_ACL_SUNOS_NFS4+	else if ((archive_acl_types(abstract_acl) &+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {+		ret = set_acl(a, fd, name, abstract_acl,+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");+	}+#endif+	return (ret);+}+#endif	/* ARCHIVE_ACL_SUNOS */
+ c/archive_endian.h view
@@ -0,0 +1,195 @@+/*-+ * Copyright (c) 2002 Thomas Moestl <tmm@FreeBSD.org>+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+ * SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/archive_endian.h 201085 2009-12-28 02:17:15Z kientzle $+ *+ * Borrowed from FreeBSD's <sys/endian.h>+ */++#ifndef ARCHIVE_ENDIAN_H_INCLUDED+#define ARCHIVE_ENDIAN_H_INCLUDED++/* Note:  This is a purely internal header! */+/* Do not use this outside of libarchive internal code! */++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++/*+ * Disabling inline keyword for compilers known to choke on it:+ * - Watcom C++ in C code.  (For any version?)+ * - SGI MIPSpro+ * - Microsoft Visual C++ 6.0 (supposedly newer versions too)+ * - IBM VisualAge 6 (XL v6)+ * - Sun WorkShop C (SunPro) before 5.9+ */+#if defined(__WATCOMC__) || defined(__sgi) || defined(__hpux) || defined(__BORLANDC__)+#define	inline+#elif defined(__IBMC__) && __IBMC__ < 700+#define	inline+#elif defined(__SUNPRO_C) && __SUNPRO_C < 0x590+#define inline+#elif defined(_MSC_VER) || defined(__osf__)+#define inline __inline+#endif++/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */++static inline uint16_t+archive_be16dec(const void *pp)+{+	unsigned char const *p = (unsigned char const *)pp;++	/* Store into unsigned temporaries before left shifting, to avoid+	promotion to signed int and then left shifting into the sign bit,+	which is undefined behaviour. */+	unsigned int p1 = p[1];+	unsigned int p0 = p[0];++	return ((p0 << 8) | p1);+}++static inline uint32_t+archive_be32dec(const void *pp)+{+	unsigned char const *p = (unsigned char const *)pp;++	/* Store into unsigned temporaries before left shifting, to avoid+	promotion to signed int and then left shifting into the sign bit,+	which is undefined behaviour. */+	unsigned int p3 = p[3];+	unsigned int p2 = p[2];+	unsigned int p1 = p[1];+	unsigned int p0 = p[0];++	return ((p0 << 24) | (p1 << 16) | (p2 << 8) | p3);+}++static inline uint64_t+archive_be64dec(const void *pp)+{+	unsigned char const *p = (unsigned char const *)pp;++	return (((uint64_t)archive_be32dec(p) << 32) | archive_be32dec(p + 4));+}++static inline uint16_t+archive_le16dec(const void *pp)+{+	unsigned char const *p = (unsigned char const *)pp;++	/* Store into unsigned temporaries before left shifting, to avoid+	promotion to signed int and then left shifting into the sign bit,+	which is undefined behaviour. */+	unsigned int p1 = p[1];+	unsigned int p0 = p[0];++	return ((p1 << 8) | p0);+}++static inline uint32_t+archive_le32dec(const void *pp)+{+	unsigned char const *p = (unsigned char const *)pp;++	/* Store into unsigned temporaries before left shifting, to avoid+	promotion to signed int and then left shifting into the sign bit,+	which is undefined behaviour. */+	unsigned int p3 = p[3];+	unsigned int p2 = p[2];+	unsigned int p1 = p[1];+	unsigned int p0 = p[0];++	return ((p3 << 24) | (p2 << 16) | (p1 << 8) | p0);+}++static inline uint64_t+archive_le64dec(const void *pp)+{+	unsigned char const *p = (unsigned char const *)pp;++	return (((uint64_t)archive_le32dec(p + 4) << 32) | archive_le32dec(p));+}++static inline void+archive_be16enc(void *pp, uint16_t u)+{+	unsigned char *p = (unsigned char *)pp;++	p[0] = (u >> 8) & 0xff;+	p[1] = u & 0xff;+}++static inline void+archive_be32enc(void *pp, uint32_t u)+{+	unsigned char *p = (unsigned char *)pp;++	p[0] = (u >> 24) & 0xff;+	p[1] = (u >> 16) & 0xff;+	p[2] = (u >> 8) & 0xff;+	p[3] = u & 0xff;+}++static inline void+archive_be64enc(void *pp, uint64_t u)+{+	unsigned char *p = (unsigned char *)pp;++	archive_be32enc(p, (uint32_t)(u >> 32));+	archive_be32enc(p + 4, (uint32_t)(u & 0xffffffff));+}++static inline void+archive_le16enc(void *pp, uint16_t u)+{+	unsigned char *p = (unsigned char *)pp;++	p[0] = u & 0xff;+	p[1] = (u >> 8) & 0xff;+}++static inline void+archive_le32enc(void *pp, uint32_t u)+{+	unsigned char *p = (unsigned char *)pp;++	p[0] = u & 0xff;+	p[1] = (u >> 8) & 0xff;+	p[2] = (u >> 16) & 0xff;+	p[3] = (u >> 24) & 0xff;+}++static inline void+archive_le64enc(void *pp, uint64_t u)+{+	unsigned char *p = (unsigned char *)pp;++	archive_le32enc(p, (uint32_t)(u & 0xffffffff));+	archive_le32enc(p + 4, (uint32_t)(u >> 32));+}++#endif
+ c/archive_entry.c view
@@ -0,0 +1,2066 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2016 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry.c 201096 2009-12-28 02:41:27Z kientzle $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#if MAJOR_IN_MKDEV+#include <sys/mkdev.h>+#define HAVE_MAJOR+#elif MAJOR_IN_SYSMACROS+#include <sys/sysmacros.h>+#define HAVE_MAJOR+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_LINUX_FS_H+#include <linux/fs.h>	/* for Linux file flags */+#endif+/*+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.+ * As the include guards don't agree, the order of include is important.+ */+#ifdef HAVE_LINUX_EXT2_FS_H+#include <linux/ext2_fs.h>	/* for Linux file flags */+#endif+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)+#include <ext2fs/ext2_fs.h>	/* for Linux file flags */+#endif+#include <stddef.h>+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_WCHAR_H+#include <wchar.h>+#endif++#include "archive.h"+#include "archive_acl_private.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_entry_private.h"++#if !defined(HAVE_MAJOR) && !defined(major)+/* Replacement for major/minor/makedev. */+#define	major(x) ((int)(0x00ff & ((x) >> 8)))+#define	minor(x) ((int)(0xffff00ff & (x)))+#define	makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min)))+#endif++/* Play games to come up with a suitable makedev() definition. */+#ifdef __QNXNTO__+/* QNX.  <sigh> */+#include <sys/netmgr.h>+#define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))+#elif defined makedev+/* There's a "makedev" macro. */+#define ae_makedev(maj, min) makedev((maj), (min))+#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__))+/* Windows. <sigh> */+#define ae_makedev(maj, min) mkdev((maj), (min))+#else+/* There's a "makedev" function. */+#define ae_makedev(maj, min) makedev((maj), (min))+#endif++/*+ * This adjustment is needed to support the following idiom for adding+ * 1000ns to the stored time:+ * archive_entry_set_atime(archive_entry_atime(),+ *                         archive_entry_atime_nsec() + 1000)+ * The additional if() here compensates for ambiguity in the C standard,+ * which permits two possible interpretations of a % b when a is negative.+ */+#define FIX_NS(t,ns) \+	do {	\+		t += ns / 1000000000; \+		ns %= 1000000000; \+		if (ns < 0) { --t; ns += 1000000000; } \+	} while (0)++static char *	 ae_fflagstostr(unsigned long bitset, unsigned long bitclear);+static const wchar_t	*ae_wcstofflags(const wchar_t *stringp,+		    unsigned long *setp, unsigned long *clrp);+static const char	*ae_strtofflags(const char *stringp,+		    unsigned long *setp, unsigned long *clrp);++#ifndef HAVE_WCSCPY+static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)+{+	wchar_t *dest = s1;+	while ((*s1 = *s2) != L'\0')+		++s1, ++s2;+	return dest;+}+#endif+#ifndef HAVE_WCSLEN+static size_t wcslen(const wchar_t *s)+{+	const wchar_t *p = s;+	while (*p != L'\0')+		++p;+	return p - s;+}+#endif+#ifndef HAVE_WMEMCMP+/* Good enough for simple equality testing, but not for sorting. */+#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))+#endif++/****************************************************************************+ *+ * Public Interface+ *+ ****************************************************************************/++struct archive_entry *+archive_entry_clear(struct archive_entry *entry)+{+	if (entry == NULL)+		return (NULL);+	archive_mstring_clean(&entry->ae_fflags_text);+	archive_mstring_clean(&entry->ae_gname);+	archive_mstring_clean(&entry->ae_hardlink);+	archive_mstring_clean(&entry->ae_pathname);+	archive_mstring_clean(&entry->ae_sourcepath);+	archive_mstring_clean(&entry->ae_symlink);+	archive_mstring_clean(&entry->ae_uname);+	archive_entry_copy_mac_metadata(entry, NULL, 0);+	archive_acl_clear(&entry->acl);+	archive_entry_xattr_clear(entry);+	archive_entry_sparse_clear(entry);+	free(entry->stat);+	entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED;+	memset(entry, 0, sizeof(*entry));+	return entry;+}++struct archive_entry *+archive_entry_clone(struct archive_entry *entry)+{+	struct archive_entry *entry2;+	struct ae_xattr *xp;+	struct ae_sparse *sp;+	size_t s;+	const void *p;++	/* Allocate new structure and copy over all of the fields. */+	/* TODO: Should we copy the archive over?  Or require a new archive+	 * as an argument? */+	entry2 = archive_entry_new2(entry->archive);+	if (entry2 == NULL)+		return (NULL);+	entry2->ae_stat = entry->ae_stat;+	entry2->ae_fflags_set = entry->ae_fflags_set;+	entry2->ae_fflags_clear = entry->ae_fflags_clear;++	/* TODO: XXX If clone can have a different archive, what do we do here if+	 * character sets are different? XXX */+	archive_mstring_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);+	archive_mstring_copy(&entry2->ae_gname, &entry->ae_gname);+	archive_mstring_copy(&entry2->ae_hardlink, &entry->ae_hardlink);+	archive_mstring_copy(&entry2->ae_pathname, &entry->ae_pathname);+	archive_mstring_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath);+	archive_mstring_copy(&entry2->ae_symlink, &entry->ae_symlink);+	entry2->ae_set = entry->ae_set;+	archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname);++	/* Copy symlink type */+	entry2->ae_symlink_type = entry->ae_symlink_type;++	/* Copy encryption status */+	entry2->encryption = entry->encryption;+	+	/* Copy ACL data over. */+	archive_acl_copy(&entry2->acl, &entry->acl);++	/* Copy Mac OS metadata. */+	p = archive_entry_mac_metadata(entry, &s);+	archive_entry_copy_mac_metadata(entry2, p, s);++	/* Copy xattr data over. */+	xp = entry->xattr_head;+	while (xp != NULL) {+		archive_entry_xattr_add_entry(entry2,+		    xp->name, xp->value, xp->size);+		xp = xp->next;+	}++	/* Copy sparse data over. */+	sp = entry->sparse_head;+	while (sp != NULL) {+		archive_entry_sparse_add_entry(entry2,+		    sp->offset, sp->length);+		sp = sp->next;+	}++	return (entry2);+}++void+archive_entry_free(struct archive_entry *entry)+{+	archive_entry_clear(entry);+	free(entry);+}++struct archive_entry *+archive_entry_new(void)+{+	return archive_entry_new2(NULL);+}++struct archive_entry *+archive_entry_new2(struct archive *a)+{+	struct archive_entry *entry;++	entry = (struct archive_entry *)calloc(1, sizeof(*entry));+	if (entry == NULL)+		return (NULL);+	entry->archive = a;+	entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED;+	return (entry);+}++/*+ * Functions for reading fields from an archive_entry.+ */++time_t+archive_entry_atime(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_atime);+}++long+archive_entry_atime_nsec(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_atime_nsec);+}++int+archive_entry_atime_is_set(struct archive_entry *entry)+{+	return (entry->ae_set & AE_SET_ATIME);+}++time_t+archive_entry_birthtime(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_birthtime);+}++long+archive_entry_birthtime_nsec(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_birthtime_nsec);+}++int+archive_entry_birthtime_is_set(struct archive_entry *entry)+{+	return (entry->ae_set & AE_SET_BIRTHTIME);+}++time_t+archive_entry_ctime(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_ctime);+}++int+archive_entry_ctime_is_set(struct archive_entry *entry)+{+	return (entry->ae_set & AE_SET_CTIME);+}++long+archive_entry_ctime_nsec(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_ctime_nsec);+}++dev_t+archive_entry_dev(struct archive_entry *entry)+{+	if (entry->ae_stat.aest_dev_is_broken_down)+		return ae_makedev(entry->ae_stat.aest_devmajor,+		    entry->ae_stat.aest_devminor);+	else+		return (entry->ae_stat.aest_dev);+}++int+archive_entry_dev_is_set(struct archive_entry *entry)+{+	return (entry->ae_set & AE_SET_DEV);+}++dev_t+archive_entry_devmajor(struct archive_entry *entry)+{+	if (entry->ae_stat.aest_dev_is_broken_down)+		return (entry->ae_stat.aest_devmajor);+	else+		return major(entry->ae_stat.aest_dev);+}++dev_t+archive_entry_devminor(struct archive_entry *entry)+{+	if (entry->ae_stat.aest_dev_is_broken_down)+		return (entry->ae_stat.aest_devminor);+	else+		return minor(entry->ae_stat.aest_dev);+}++__LA_MODE_T+archive_entry_filetype(struct archive_entry *entry)+{+	return (AE_IFMT & entry->acl.mode);+}++void+archive_entry_fflags(struct archive_entry *entry,+    unsigned long *set, unsigned long *clear)+{+	*set = entry->ae_fflags_set;+	*clear = entry->ae_fflags_clear;+}++/*+ * Note: if text was provided, this just returns that text.  If you+ * really need the text to be rebuilt in a canonical form, set the+ * text, ask for the bitmaps, then set the bitmaps.  (Setting the+ * bitmaps clears any stored text.)  This design is deliberate: if+ * we're editing archives, we don't want to discard flags just because+ * they aren't supported on the current system.  The bitmap<->text+ * conversions are platform-specific (see below).+ */+const char *+archive_entry_fflags_text(struct archive_entry *entry)+{+	const char *f;+	char *p;++	if (archive_mstring_get_mbs(entry->archive,+	    &entry->ae_fflags_text, &f) == 0) {+		if (f != NULL)+			return (f);+	} else if (errno == ENOMEM)+		__archive_errx(1, "No memory");++	if (entry->ae_fflags_set == 0  &&  entry->ae_fflags_clear == 0)+		return (NULL);++	p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear);+	if (p == NULL)+		return (NULL);++	archive_mstring_copy_mbs(&entry->ae_fflags_text, p);+	free(p);+	if (archive_mstring_get_mbs(entry->archive,+	    &entry->ae_fflags_text, &f) == 0)+		return (f);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++la_int64_t+archive_entry_gid(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_gid);+}++const char *+archive_entry_gname(struct archive_entry *entry)+{+	const char *p;+	if (archive_mstring_get_mbs(entry->archive, &entry->ae_gname, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++const char *+archive_entry_gname_utf8(struct archive_entry *entry)+{+	const char *p;+	if (archive_mstring_get_utf8(entry->archive, &entry->ae_gname, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}+++const wchar_t *+archive_entry_gname_w(struct archive_entry *entry)+{+	const wchar_t *p;+	if (archive_mstring_get_wcs(entry->archive, &entry->ae_gname, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++int+_archive_entry_gname_l(struct archive_entry *entry,+    const char **p, size_t *len, struct archive_string_conv *sc)+{+	return (archive_mstring_get_mbs_l(&entry->ae_gname, p, len, sc));+}++const char *+archive_entry_hardlink(struct archive_entry *entry)+{+	const char *p;+	if ((entry->ae_set & AE_SET_HARDLINK) == 0)+		return (NULL);+	if (archive_mstring_get_mbs(+	    entry->archive, &entry->ae_hardlink, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++const char *+archive_entry_hardlink_utf8(struct archive_entry *entry)+{+	const char *p;+	if ((entry->ae_set & AE_SET_HARDLINK) == 0)+		return (NULL);+	if (archive_mstring_get_utf8(+	    entry->archive, &entry->ae_hardlink, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++const wchar_t *+archive_entry_hardlink_w(struct archive_entry *entry)+{+	const wchar_t *p;+	if ((entry->ae_set & AE_SET_HARDLINK) == 0)+		return (NULL);+	if (archive_mstring_get_wcs(+	    entry->archive, &entry->ae_hardlink, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++int+_archive_entry_hardlink_l(struct archive_entry *entry,+    const char **p, size_t *len, struct archive_string_conv *sc)+{+	if ((entry->ae_set & AE_SET_HARDLINK) == 0) {+		*p = NULL;+		*len = 0;+		return (0);+	}+	return (archive_mstring_get_mbs_l(&entry->ae_hardlink, p, len, sc));+}++la_int64_t+archive_entry_ino(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_ino);+}++int+archive_entry_ino_is_set(struct archive_entry *entry)+{+	return (entry->ae_set & AE_SET_INO);+}++la_int64_t+archive_entry_ino64(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_ino);+}++__LA_MODE_T+archive_entry_mode(struct archive_entry *entry)+{+	return (entry->acl.mode);+}++time_t+archive_entry_mtime(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_mtime);+}++long+archive_entry_mtime_nsec(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_mtime_nsec);+}++int+archive_entry_mtime_is_set(struct archive_entry *entry)+{+	return (entry->ae_set & AE_SET_MTIME);+}++unsigned int+archive_entry_nlink(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_nlink);+}++const char *+archive_entry_pathname(struct archive_entry *entry)+{+	const char *p;+	if (archive_mstring_get_mbs(+	    entry->archive, &entry->ae_pathname, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++const char *+archive_entry_pathname_utf8(struct archive_entry *entry)+{+	const char *p;+	if (archive_mstring_get_utf8(+	    entry->archive, &entry->ae_pathname, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++const wchar_t *+archive_entry_pathname_w(struct archive_entry *entry)+{+	const wchar_t *p;+	if (archive_mstring_get_wcs(+	    entry->archive, &entry->ae_pathname, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++int+_archive_entry_pathname_l(struct archive_entry *entry,+    const char **p, size_t *len, struct archive_string_conv *sc)+{+	return (archive_mstring_get_mbs_l(&entry->ae_pathname, p, len, sc));+}++__LA_MODE_T+archive_entry_perm(struct archive_entry *entry)+{+	return (~AE_IFMT & entry->acl.mode);+}++dev_t+archive_entry_rdev(struct archive_entry *entry)+{+	if (entry->ae_stat.aest_rdev_is_broken_down)+		return ae_makedev(entry->ae_stat.aest_rdevmajor,+		    entry->ae_stat.aest_rdevminor);+	else+		return (entry->ae_stat.aest_rdev);+}++dev_t+archive_entry_rdevmajor(struct archive_entry *entry)+{+	if (entry->ae_stat.aest_rdev_is_broken_down)+		return (entry->ae_stat.aest_rdevmajor);+	else+		return major(entry->ae_stat.aest_rdev);+}++dev_t+archive_entry_rdevminor(struct archive_entry *entry)+{+	if (entry->ae_stat.aest_rdev_is_broken_down)+		return (entry->ae_stat.aest_rdevminor);+	else+		return minor(entry->ae_stat.aest_rdev);+}++la_int64_t+archive_entry_size(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_size);+}++int+archive_entry_size_is_set(struct archive_entry *entry)+{+	return (entry->ae_set & AE_SET_SIZE);+}++const char *+archive_entry_sourcepath(struct archive_entry *entry)+{+	const char *p;+	if (archive_mstring_get_mbs(+	    entry->archive, &entry->ae_sourcepath, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++const wchar_t *+archive_entry_sourcepath_w(struct archive_entry *entry)+{+	const wchar_t *p;+	if (archive_mstring_get_wcs(+	    entry->archive, &entry->ae_sourcepath, &p) == 0)+		return (p);+	return (NULL);+}++const char *+archive_entry_symlink(struct archive_entry *entry)+{+	const char *p;+	if ((entry->ae_set & AE_SET_SYMLINK) == 0)+		return (NULL);+	if (archive_mstring_get_mbs(+	    entry->archive, &entry->ae_symlink, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++int+archive_entry_symlink_type(struct archive_entry *entry)+{+	return (entry->ae_symlink_type);+}++const char *+archive_entry_symlink_utf8(struct archive_entry *entry)+{+	const char *p;+	if ((entry->ae_set & AE_SET_SYMLINK) == 0)+		return (NULL);+	if (archive_mstring_get_utf8(+	    entry->archive, &entry->ae_symlink, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++const wchar_t *+archive_entry_symlink_w(struct archive_entry *entry)+{+	const wchar_t *p;+	if ((entry->ae_set & AE_SET_SYMLINK) == 0)+		return (NULL);+	if (archive_mstring_get_wcs(+	    entry->archive, &entry->ae_symlink, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++int+_archive_entry_symlink_l(struct archive_entry *entry,+    const char **p, size_t *len, struct archive_string_conv *sc)+{+	if ((entry->ae_set & AE_SET_SYMLINK) == 0) {+		*p = NULL;+		*len = 0;+		return (0);+	}+	return (archive_mstring_get_mbs_l( &entry->ae_symlink, p, len, sc));+}++la_int64_t+archive_entry_uid(struct archive_entry *entry)+{+	return (entry->ae_stat.aest_uid);+}++const char *+archive_entry_uname(struct archive_entry *entry)+{+	const char *p;+	if (archive_mstring_get_mbs(entry->archive, &entry->ae_uname, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++const char *+archive_entry_uname_utf8(struct archive_entry *entry)+{+	const char *p;+	if (archive_mstring_get_utf8(entry->archive, &entry->ae_uname, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++const wchar_t *+archive_entry_uname_w(struct archive_entry *entry)+{+	const wchar_t *p;+	if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0)+		return (p);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (NULL);+}++int+_archive_entry_uname_l(struct archive_entry *entry,+    const char **p, size_t *len, struct archive_string_conv *sc)+{+	return (archive_mstring_get_mbs_l(&entry->ae_uname, p, len, sc));+}++int+archive_entry_is_data_encrypted(struct archive_entry *entry)+{+	return ((entry->encryption & AE_ENCRYPTION_DATA) == AE_ENCRYPTION_DATA);+}++int+archive_entry_is_metadata_encrypted(struct archive_entry *entry)+{+	return ((entry->encryption & AE_ENCRYPTION_METADATA) == AE_ENCRYPTION_METADATA);+}++int+archive_entry_is_encrypted(struct archive_entry *entry)+{+	return (entry->encryption & (AE_ENCRYPTION_DATA|AE_ENCRYPTION_METADATA));+}++/*+ * Functions to set archive_entry properties.+ */++void+archive_entry_set_filetype(struct archive_entry *entry, unsigned int type)+{+	entry->stat_valid = 0;+	entry->acl.mode &= ~AE_IFMT;+	entry->acl.mode |= AE_IFMT & type;+}++void+archive_entry_set_fflags(struct archive_entry *entry,+    unsigned long set, unsigned long clear)+{+	archive_mstring_clean(&entry->ae_fflags_text);+	entry->ae_fflags_set = set;+	entry->ae_fflags_clear = clear;+}++const char *+archive_entry_copy_fflags_text(struct archive_entry *entry,+    const char *flags)+{+	archive_mstring_copy_mbs(&entry->ae_fflags_text, flags);+	return (ae_strtofflags(flags,+		    &entry->ae_fflags_set, &entry->ae_fflags_clear));+}++const wchar_t *+archive_entry_copy_fflags_text_w(struct archive_entry *entry,+    const wchar_t *flags)+{+	archive_mstring_copy_wcs(&entry->ae_fflags_text, flags);+	return (ae_wcstofflags(flags,+		    &entry->ae_fflags_set, &entry->ae_fflags_clear));+}++void+archive_entry_set_gid(struct archive_entry *entry, la_int64_t g)+{+	entry->stat_valid = 0;+	entry->ae_stat.aest_gid = g;+}++void+archive_entry_set_gname(struct archive_entry *entry, const char *name)+{+	archive_mstring_copy_mbs(&entry->ae_gname, name);+}++void+archive_entry_set_gname_utf8(struct archive_entry *entry, const char *name)+{+	archive_mstring_copy_utf8(&entry->ae_gname, name);+}++void+archive_entry_copy_gname(struct archive_entry *entry, const char *name)+{+	archive_mstring_copy_mbs(&entry->ae_gname, name);+}++void+archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)+{+	archive_mstring_copy_wcs(&entry->ae_gname, name);+}++int+archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)+{+	if (archive_mstring_update_utf8(entry->archive,+	    &entry->ae_gname, name) == 0)+		return (1);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (0);+}++int+_archive_entry_copy_gname_l(struct archive_entry *entry,+    const char *name, size_t len, struct archive_string_conv *sc)+{+	return (archive_mstring_copy_mbs_len_l(&entry->ae_gname, name, len, sc));+}++void+archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino)+{+	entry->stat_valid = 0;+	entry->ae_set |= AE_SET_INO;+	entry->ae_stat.aest_ino = ino;+}++void+archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino)+{+	entry->stat_valid = 0;+	entry->ae_set |= AE_SET_INO;+	entry->ae_stat.aest_ino = ino;+}++void+archive_entry_set_hardlink(struct archive_entry *entry, const char *target)+{+	archive_mstring_copy_mbs(&entry->ae_hardlink, target);+	if (target != NULL)+		entry->ae_set |= AE_SET_HARDLINK;+	else+		entry->ae_set &= ~AE_SET_HARDLINK;+}++void+archive_entry_set_hardlink_utf8(struct archive_entry *entry, const char *target)+{+	archive_mstring_copy_utf8(&entry->ae_hardlink, target);+	if (target != NULL)+		entry->ae_set |= AE_SET_HARDLINK;+	else+		entry->ae_set &= ~AE_SET_HARDLINK;+}++void+archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)+{+	archive_mstring_copy_mbs(&entry->ae_hardlink, target);+	if (target != NULL)+		entry->ae_set |= AE_SET_HARDLINK;+	else+		entry->ae_set &= ~AE_SET_HARDLINK;+}++void+archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)+{+	archive_mstring_copy_wcs(&entry->ae_hardlink, target);+	if (target != NULL)+		entry->ae_set |= AE_SET_HARDLINK;+	else+		entry->ae_set &= ~AE_SET_HARDLINK;+}++int+archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target)+{+	if (target != NULL)+		entry->ae_set |= AE_SET_HARDLINK;+	else+		entry->ae_set &= ~AE_SET_HARDLINK;+	if (archive_mstring_update_utf8(entry->archive,+	    &entry->ae_hardlink, target) == 0)+		return (1);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (0);+}++int+_archive_entry_copy_hardlink_l(struct archive_entry *entry,+    const char *target, size_t len, struct archive_string_conv *sc)+{+	int r;++	r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink,+	    target, len, sc);+	if (target != NULL && r == 0)+		entry->ae_set |= AE_SET_HARDLINK;+	else+		entry->ae_set &= ~AE_SET_HARDLINK;+	return (r);+}++void+archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)+{+	FIX_NS(t, ns);+	entry->stat_valid = 0;+	entry->ae_set |= AE_SET_ATIME;+	entry->ae_stat.aest_atime = t;+	entry->ae_stat.aest_atime_nsec = ns;+}++void+archive_entry_unset_atime(struct archive_entry *entry)+{+	archive_entry_set_atime(entry, 0, 0);+	entry->ae_set &= ~AE_SET_ATIME;+}++void+archive_entry_set_birthtime(struct archive_entry *entry, time_t t, long ns)+{+	FIX_NS(t, ns);+	entry->stat_valid = 0;+	entry->ae_set |= AE_SET_BIRTHTIME;+	entry->ae_stat.aest_birthtime = t;+	entry->ae_stat.aest_birthtime_nsec = ns;+}++void+archive_entry_unset_birthtime(struct archive_entry *entry)+{+	archive_entry_set_birthtime(entry, 0, 0);+	entry->ae_set &= ~AE_SET_BIRTHTIME;+}++void+archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)+{+	FIX_NS(t, ns);+	entry->stat_valid = 0;+	entry->ae_set |= AE_SET_CTIME;+	entry->ae_stat.aest_ctime = t;+	entry->ae_stat.aest_ctime_nsec = ns;+}++void+archive_entry_unset_ctime(struct archive_entry *entry)+{+	archive_entry_set_ctime(entry, 0, 0);+	entry->ae_set &= ~AE_SET_CTIME;+}++void+archive_entry_set_dev(struct archive_entry *entry, dev_t d)+{+	entry->stat_valid = 0;+	entry->ae_set |= AE_SET_DEV;+	entry->ae_stat.aest_dev_is_broken_down = 0;+	entry->ae_stat.aest_dev = d;+}++void+archive_entry_set_devmajor(struct archive_entry *entry, dev_t m)+{+	entry->stat_valid = 0;+	entry->ae_set |= AE_SET_DEV;+	entry->ae_stat.aest_dev_is_broken_down = 1;+	entry->ae_stat.aest_devmajor = m;+}++void+archive_entry_set_devminor(struct archive_entry *entry, dev_t m)+{+	entry->stat_valid = 0;+	entry->ae_set |= AE_SET_DEV;+	entry->ae_stat.aest_dev_is_broken_down = 1;+	entry->ae_stat.aest_devminor = m;+}++/* Set symlink if symlink is already set, else set hardlink. */+void+archive_entry_set_link(struct archive_entry *entry, const char *target)+{+	if (entry->ae_set & AE_SET_SYMLINK)+		archive_mstring_copy_mbs(&entry->ae_symlink, target);+	else+		archive_mstring_copy_mbs(&entry->ae_hardlink, target);+}++void+archive_entry_set_link_utf8(struct archive_entry *entry, const char *target)+{+	if (entry->ae_set & AE_SET_SYMLINK)+		archive_mstring_copy_utf8(&entry->ae_symlink, target);+	else+		archive_mstring_copy_utf8(&entry->ae_hardlink, target);+}++/* Set symlink if symlink is already set, else set hardlink. */+void+archive_entry_copy_link(struct archive_entry *entry, const char *target)+{+	if (entry->ae_set & AE_SET_SYMLINK)+		archive_mstring_copy_mbs(&entry->ae_symlink, target);+	else+		archive_mstring_copy_mbs(&entry->ae_hardlink, target);+}++/* Set symlink if symlink is already set, else set hardlink. */+void+archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)+{+	if (entry->ae_set & AE_SET_SYMLINK)+		archive_mstring_copy_wcs(&entry->ae_symlink, target);+	else+		archive_mstring_copy_wcs(&entry->ae_hardlink, target);+}++int+archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)+{+	int r;+	if (entry->ae_set & AE_SET_SYMLINK)+		r = archive_mstring_update_utf8(entry->archive,+		    &entry->ae_symlink, target);+	else+		r = archive_mstring_update_utf8(entry->archive,+		    &entry->ae_hardlink, target);+	if (r == 0)+		return (1);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (0);+}++int+_archive_entry_copy_link_l(struct archive_entry *entry,+    const char *target, size_t len, struct archive_string_conv *sc)+{+	int r;++	if (entry->ae_set & AE_SET_SYMLINK)+		r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink,+		    target, len, sc);+	else+		r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink,+		    target, len, sc);+	return (r);+}++void+archive_entry_set_mode(struct archive_entry *entry, mode_t m)+{+	entry->stat_valid = 0;+	entry->acl.mode = m;+}++void+archive_entry_set_mtime(struct archive_entry *entry, time_t t, long ns)+{+	FIX_NS(t, ns);+	entry->stat_valid = 0;+	entry->ae_set |= AE_SET_MTIME;+	entry->ae_stat.aest_mtime = t;+	entry->ae_stat.aest_mtime_nsec = ns;+}++void+archive_entry_unset_mtime(struct archive_entry *entry)+{+	archive_entry_set_mtime(entry, 0, 0);+	entry->ae_set &= ~AE_SET_MTIME;+}++void+archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink)+{+	entry->stat_valid = 0;+	entry->ae_stat.aest_nlink = nlink;+}++void+archive_entry_set_pathname(struct archive_entry *entry, const char *name)+{+	archive_mstring_copy_mbs(&entry->ae_pathname, name);+}++void+archive_entry_set_pathname_utf8(struct archive_entry *entry, const char *name)+{+	archive_mstring_copy_utf8(&entry->ae_pathname, name);+}++void+archive_entry_copy_pathname(struct archive_entry *entry, const char *name)+{+	archive_mstring_copy_mbs(&entry->ae_pathname, name);+}++void+archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)+{+	archive_mstring_copy_wcs(&entry->ae_pathname, name);+}++int+archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name)+{+	if (archive_mstring_update_utf8(entry->archive,+	    &entry->ae_pathname, name) == 0)+		return (1);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (0);+}++int+_archive_entry_copy_pathname_l(struct archive_entry *entry,+    const char *name, size_t len, struct archive_string_conv *sc)+{+	return (archive_mstring_copy_mbs_len_l(&entry->ae_pathname,+	    name, len, sc));+}++void+archive_entry_set_perm(struct archive_entry *entry, mode_t p)+{+	entry->stat_valid = 0;+	entry->acl.mode &= AE_IFMT;+	entry->acl.mode |= ~AE_IFMT & p;+}++void+archive_entry_set_rdev(struct archive_entry *entry, dev_t m)+{+	entry->stat_valid = 0;+	entry->ae_stat.aest_rdev = m;+	entry->ae_stat.aest_rdev_is_broken_down = 0;+}++void+archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m)+{+	entry->stat_valid = 0;+	entry->ae_stat.aest_rdev_is_broken_down = 1;+	entry->ae_stat.aest_rdevmajor = m;+}++void+archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m)+{+	entry->stat_valid = 0;+	entry->ae_stat.aest_rdev_is_broken_down = 1;+	entry->ae_stat.aest_rdevminor = m;+}++void+archive_entry_set_size(struct archive_entry *entry, la_int64_t s)+{+	entry->stat_valid = 0;+	entry->ae_stat.aest_size = s;+	entry->ae_set |= AE_SET_SIZE;+}++void+archive_entry_unset_size(struct archive_entry *entry)+{+	archive_entry_set_size(entry, 0);+	entry->ae_set &= ~AE_SET_SIZE;+}++void+archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path)+{+	archive_mstring_copy_mbs(&entry->ae_sourcepath, path);+}++void+archive_entry_copy_sourcepath_w(struct archive_entry *entry, const wchar_t *path)+{+	archive_mstring_copy_wcs(&entry->ae_sourcepath, path);+}++void+archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)+{+	archive_mstring_copy_mbs(&entry->ae_symlink, linkname);+	if (linkname != NULL)+		entry->ae_set |= AE_SET_SYMLINK;+	else+		entry->ae_set &= ~AE_SET_SYMLINK;+}++void+archive_entry_set_symlink_type(struct archive_entry *entry, int type)+{+	entry->ae_symlink_type = type;+}++void+archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname)+{+	archive_mstring_copy_utf8(&entry->ae_symlink, linkname);+	if (linkname != NULL)+		entry->ae_set |= AE_SET_SYMLINK;+	else+		entry->ae_set &= ~AE_SET_SYMLINK;+}++void+archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname)+{+	archive_mstring_copy_mbs(&entry->ae_symlink, linkname);+	if (linkname != NULL)+		entry->ae_set |= AE_SET_SYMLINK;+	else+		entry->ae_set &= ~AE_SET_SYMLINK;+}++void+archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)+{+	archive_mstring_copy_wcs(&entry->ae_symlink, linkname);+	if (linkname != NULL)+		entry->ae_set |= AE_SET_SYMLINK;+	else+		entry->ae_set &= ~AE_SET_SYMLINK;+}++int+archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname)+{+	if (linkname != NULL)+		entry->ae_set |= AE_SET_SYMLINK;+	else+		entry->ae_set &= ~AE_SET_SYMLINK;+	if (archive_mstring_update_utf8(entry->archive,+	    &entry->ae_symlink, linkname) == 0)+		return (1);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (0);+}++int+_archive_entry_copy_symlink_l(struct archive_entry *entry,+    const char *linkname, size_t len, struct archive_string_conv *sc)+{+	int r;++	r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink,+	    linkname, len, sc);+	if (linkname != NULL && r == 0)+		entry->ae_set |= AE_SET_SYMLINK;+	else+		entry->ae_set &= ~AE_SET_SYMLINK;+	return (r);+}++void+archive_entry_set_uid(struct archive_entry *entry, la_int64_t u)+{+	entry->stat_valid = 0;+	entry->ae_stat.aest_uid = u;+}++void+archive_entry_set_uname(struct archive_entry *entry, const char *name)+{+	archive_mstring_copy_mbs(&entry->ae_uname, name);+}++void+archive_entry_set_uname_utf8(struct archive_entry *entry, const char *name)+{+	archive_mstring_copy_utf8(&entry->ae_uname, name);+}++void+archive_entry_copy_uname(struct archive_entry *entry, const char *name)+{+	archive_mstring_copy_mbs(&entry->ae_uname, name);+}++void+archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)+{+	archive_mstring_copy_wcs(&entry->ae_uname, name);+}++int+archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name)+{+	if (archive_mstring_update_utf8(entry->archive,+	    &entry->ae_uname, name) == 0)+		return (1);+	if (errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (0);+}++void+archive_entry_set_is_data_encrypted(struct archive_entry *entry, char is_encrypted)+{+	if (is_encrypted) {+		entry->encryption |= AE_ENCRYPTION_DATA;+	} else {+		entry->encryption &= ~AE_ENCRYPTION_DATA;+	}+}++void+archive_entry_set_is_metadata_encrypted(struct archive_entry *entry, char is_encrypted)+{+	if (is_encrypted) {+		entry->encryption |= AE_ENCRYPTION_METADATA;+	} else {+		entry->encryption &= ~AE_ENCRYPTION_METADATA;+	}+}++int+_archive_entry_copy_uname_l(struct archive_entry *entry,+    const char *name, size_t len, struct archive_string_conv *sc)+{+	return (archive_mstring_copy_mbs_len_l(&entry->ae_uname,+	    name, len, sc));+}++const void *+archive_entry_mac_metadata(struct archive_entry *entry, size_t *s)+{+  *s = entry->mac_metadata_size;+  return entry->mac_metadata;+}++void+archive_entry_copy_mac_metadata(struct archive_entry *entry,+    const void *p, size_t s)+{+  free(entry->mac_metadata);+  if (p == NULL || s == 0) {+    entry->mac_metadata = NULL;+    entry->mac_metadata_size = 0;+  } else {+    entry->mac_metadata_size = s;+    entry->mac_metadata = malloc(s);+    if (entry->mac_metadata == NULL)+      abort();+    memcpy(entry->mac_metadata, p, s);+  }+}++/*+ * ACL management.  The following would, of course, be a lot simpler+ * if: 1) the last draft of POSIX.1e were a really thorough and+ * complete standard that addressed the needs of ACL archiving and 2)+ * everyone followed it faithfully.  Alas, neither is true, so the+ * following is a lot more complex than might seem necessary to the+ * uninitiated.+ */++struct archive_acl *+archive_entry_acl(struct archive_entry *entry)+{+	return &entry->acl;+}++void+archive_entry_acl_clear(struct archive_entry *entry)+{+	archive_acl_clear(&entry->acl);+}++/*+ * Add a single ACL entry to the internal list of ACL data.+ */+int+archive_entry_acl_add_entry(struct archive_entry *entry,+    int type, int permset, int tag, int id, const char *name)+{+	return archive_acl_add_entry(&entry->acl, type, permset, tag, id, name);+}++/*+ * As above, but with a wide-character name.+ */+int+archive_entry_acl_add_entry_w(struct archive_entry *entry,+    int type, int permset, int tag, int id, const wchar_t *name)+{+	return archive_acl_add_entry_w_len(&entry->acl,+	    type, permset, tag, id, name, wcslen(name));+}++/*+ * Return a bitmask of ACL types in an archive entry ACL list+ */+int+archive_entry_acl_types(struct archive_entry *entry)+{+	return (archive_acl_types(&entry->acl));+}++/*+ * Return a count of entries matching "want_type".+ */+int+archive_entry_acl_count(struct archive_entry *entry, int want_type)+{+	return archive_acl_count(&entry->acl, want_type);+}++/*+ * Prepare for reading entries from the ACL data.  Returns a count+ * of entries matching "want_type", or zero if there are no+ * non-extended ACL entries of that type.+ */+int+archive_entry_acl_reset(struct archive_entry *entry, int want_type)+{+	return archive_acl_reset(&entry->acl, want_type);+}++/*+ * Return the next ACL entry in the list.  Fake entries for the+ * standard permissions and include them in the returned list.+ */+int+archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,+    int *permset, int *tag, int *id, const char **name)+{+	int r;+	r = archive_acl_next(entry->archive, &entry->acl, want_type, type,+		permset, tag, id, name);+	if (r == ARCHIVE_FATAL && errno == ENOMEM)+		__archive_errx(1, "No memory");+	return (r);+}++/*+ * Generate a text version of the ACL. The flags parameter controls+ * the style of the generated ACL.+ */+wchar_t *+archive_entry_acl_to_text_w(struct archive_entry *entry, la_ssize_t *len,+    int flags)+{+	return (archive_acl_to_text_w(&entry->acl, len, flags,+	    entry->archive));+}++char *+archive_entry_acl_to_text(struct archive_entry *entry, la_ssize_t *len,+    int flags)+{+	return (archive_acl_to_text_l(&entry->acl, len, flags, NULL));+}++char *+_archive_entry_acl_to_text_l(struct archive_entry *entry, ssize_t *len,+   int flags, struct archive_string_conv *sc)+{+	return (archive_acl_to_text_l(&entry->acl, len, flags, sc));+}++/*+ * ACL text parser.+ */+int+archive_entry_acl_from_text_w(struct archive_entry *entry,+    const wchar_t *wtext, int type)+{+	return (archive_acl_from_text_w(&entry->acl, wtext, type));+}++int+archive_entry_acl_from_text(struct archive_entry *entry,+    const char *text, int type)+{+	return (archive_acl_from_text_l(&entry->acl, text, type, NULL));+}++int+_archive_entry_acl_from_text_l(struct archive_entry *entry, const char *text,+    int type, struct archive_string_conv *sc)+{+	return (archive_acl_from_text_l(&entry->acl, text, type, sc));+}++/* Deprecated */+static int+archive_entry_acl_text_compat(int *flags)+{+	if ((*flags & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0)+		return (1);++	/* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID */+	if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0)+		*flags |= ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID;++	/* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT */+	if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)+		*flags |=  ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;++	*flags |= ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA;++	return (0);+}++/* Deprecated */+const wchar_t *+archive_entry_acl_text_w(struct archive_entry *entry, int flags)+{+	free(entry->acl.acl_text_w);+	entry->acl.acl_text_w = NULL;+	if (archive_entry_acl_text_compat(&flags) == 0)+		entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl,+		    NULL, flags, entry->archive);+	return (entry->acl.acl_text_w);+}++/* Deprecated */+const char *+archive_entry_acl_text(struct archive_entry *entry, int flags)+{+	free(entry->acl.acl_text);+	entry->acl.acl_text = NULL;+	if (archive_entry_acl_text_compat(&flags) == 0)+		entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL,+		    flags, NULL);++	return (entry->acl.acl_text);+}++/* Deprecated */+int+_archive_entry_acl_text_l(struct archive_entry *entry, int flags,+    const char **acl_text, size_t *len, struct archive_string_conv *sc)+{+	free(entry->acl.acl_text);+	entry->acl.acl_text = NULL;++	if (archive_entry_acl_text_compat(&flags) == 0)+		entry->acl.acl_text = archive_acl_to_text_l(&entry->acl,+		    (ssize_t *)len, flags, sc);++	*acl_text = entry->acl.acl_text;++	return (0);+}++/*+ * Following code is modified from UC Berkeley sources, and+ * is subject to the following copyright notice.+ */++/*-+ * Copyright (c) 1993+ *	The Regents of the University of California.  All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ * 4. Neither the name of the University nor the names of its contributors+ *    may be used to endorse or promote products derived from this software+ *    without specific prior written permission.+ *+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+ * SUCH DAMAGE.+ */++/*+ * Supported file flags on FreeBSD and Mac OS:+ * sappnd,sappend		SF_APPEND+ * arch,archived		SF_ARCHIVED+ * schg,schange,simmutable	SF_IMMUTABLE+ * sunlnk,sunlink		SF_NOUNLINK	(FreeBSD only)+ * uappnd,uappend		UF_APPEND+ * compressed			UF_COMPRESSED	(Mac OS only)+ * hidden,uhidden		UF_HIDDEN+ * uchg,uchange,uimmutable	UF_IMMUTABLE+ * nodump			UF_NODUMP+ * uunlnk,uunlink		UF_NOUNLINK	(FreeBSD only)+ * offline,uoffline		UF_OFFLINE	(FreeBSD only)+ * opaque			UF_OPAQUE+ * rdonly,urdonly,readonly	UF_READONLY	(FreeBSD only)+ * reparse,ureparse		UF_REPARSE	(FreeBSD only)+ * sparse,usparse		UF_SPARSE	(FreeBSD only)+ * system,usystem		UF_SYSTEM	(FreeBSD only)+ *+ * See chflags(2) for more information+ *+ * Supported file attributes on Linux:+ * a	append only			FS_APPEND_FL		sappnd+ * A	no atime updates		FS_NOATIME_FL		atime+ * c	compress			FS_COMPR_FL		compress+ * C	no copy on write		FS_NOCOW_FL		cow+ * d	no dump				FS_NODUMP_FL		dump+ * D	synchronous directory updates	FS_DIRSYNC_FL		dirsync+ * i	immutable			FS_IMMUTABLE_FL		schg+ * j	data journalling		FS_JOURNAL_DATA_FL	journal+ * P	project hierarchy		FS_PROJINHERIT_FL	projinherit+ * s	secure deletion			FS_SECRM_FL		securedeletion+ * S	synchronous updates		FS_SYNC_FL		sync+ * t	no tail-merging			FS_NOTAIL_FL		tail+ * T	top of directory hierarchy	FS_TOPDIR_FL		topdir+ * u	undeletable			FS_UNRM_FL		undel+ *+ * See ioctl_iflags(2) for more information+ *+ * Equivalent file flags supported on FreeBSD / Mac OS and Linux:+ * SF_APPEND		FS_APPEND_FL		sappnd+ * SF_IMMUTABLE		FS_IMMUTABLE_FL		schg+ * UF_NODUMP		FS_NODUMP_FL		nodump+ */++static const struct flag {+	const char	*name;+	const wchar_t	*wname;+	unsigned long	 set;+	unsigned long	 clear;+} fileflags[] = {+	/* Preferred (shorter) names per flag first, all prefixed by "no" */+#ifdef SF_APPEND+	{ "nosappnd",	L"nosappnd",		SF_APPEND,	0},+	{ "nosappend",	L"nosappend",		SF_APPEND,	0},+#endif+#if defined(FS_APPEND_FL)			/* 'a' */+	{ "nosappnd",	L"nosappnd",		FS_APPEND_FL,	0},+	{ "nosappend",	L"nosappend",		FS_APPEND_FL,	0},+#elif defined(EXT2_APPEND_FL)			/* 'a' */+	{ "nosappnd",	L"nosappnd",		EXT2_APPEND_FL,	0},+	{ "nosappend",	L"nosappend",		EXT2_APPEND_FL,	0},+#endif+#ifdef SF_ARCHIVED+	{ "noarch",	L"noarch",		SF_ARCHIVED,	0},+	{ "noarchived",	L"noarchived",       	SF_ARCHIVED,	0},+#endif+#ifdef SF_IMMUTABLE+	{ "noschg",	L"noschg",		SF_IMMUTABLE,	0},+	{ "noschange",	L"noschange",		SF_IMMUTABLE,	0},+	{ "nosimmutable",	L"nosimmutable",	SF_IMMUTABLE,	0},+#endif+#if defined(FS_IMMUTABLE_FL)			/* 'i' */+	{ "noschg",	L"noschg",		FS_IMMUTABLE_FL,	0},+	{ "noschange",	L"noschange",		FS_IMMUTABLE_FL,	0},+	{ "nosimmutable",	L"nosimmutable",	FS_IMMUTABLE_FL,	0},+#elif defined(EXT2_IMMUTABLE_FL)		/* 'i' */+	{ "noschg",	L"noschg",		EXT2_IMMUTABLE_FL,	0},+	{ "noschange",	L"noschange",		EXT2_IMMUTABLE_FL,	0},+	{ "nosimmutable",	L"nosimmutable",	EXT2_IMMUTABLE_FL,	0},+#endif+#ifdef SF_NOUNLINK+	{ "nosunlnk",	L"nosunlnk",		SF_NOUNLINK,	0},+	{ "nosunlink",	L"nosunlink",		SF_NOUNLINK,	0},+#endif+#ifdef UF_APPEND+	{ "nouappnd",	L"nouappnd",		UF_APPEND,	0},+	{ "nouappend",	L"nouappend",		UF_APPEND,	0},+#endif+#ifdef UF_IMMUTABLE+	{ "nouchg",	L"nouchg",		UF_IMMUTABLE,	0},+	{ "nouchange",	L"nouchange",		UF_IMMUTABLE,	0},+	{ "nouimmutable",	L"nouimmutable",	UF_IMMUTABLE,	0},+#endif+#ifdef UF_NODUMP+	{ "nodump",	L"nodump",		0,		UF_NODUMP},+#endif+#if defined(FS_NODUMP_FL)	/* 'd' */+	{ "nodump",	L"nodump",		0,		FS_NODUMP_FL},+#elif defined(EXT2_NODUMP_FL)+	{ "nodump",	L"nodump",		0,		EXT2_NODUMP_FL},+#endif+#ifdef UF_OPAQUE+	{ "noopaque",	L"noopaque",		UF_OPAQUE,	0},+#endif+#ifdef UF_NOUNLINK+	{ "nouunlnk",	L"nouunlnk",		UF_NOUNLINK,	0},+	{ "nouunlink",	L"nouunlink",		UF_NOUNLINK,	0},+#endif+#ifdef UF_COMPRESSED+	/* Mac OS */+	{ "nocompressed",	L"nocompressed",	UF_COMPRESSED,	0},+#endif+#ifdef UF_HIDDEN+	{ "nohidden",	L"nohidden",		UF_HIDDEN,	0},+	{ "nouhidden",	L"nouhidden",		UF_HIDDEN,	0},+#endif+#ifdef FILE_ATTRIBUTE_HIDDEN+	{ "nohidden",	L"nohidden",	FILE_ATTRIBUTE_HIDDEN,	0},+	{ "nouhidden",	L"nouhidden",	FILE_ATTRIBUTE_HIDDEN,	0},+#endif+#ifdef UF_OFFLINE+	{ "nooffline",	L"nooffline",		UF_OFFLINE,	0},+	{ "nouoffline",	L"nouoffline",		UF_OFFLINE,	0},+#endif+#ifdef UF_READONLY+	{ "nordonly",	L"nordonly",		UF_READONLY,	0},+	{ "nourdonly",	L"nourdonly",		UF_READONLY,	0},+	{ "noreadonly",	L"noreadonly",		UF_READONLY,	0},+#endif+#ifdef FILE_ATTRIBUTE_READONLY+	{ "nordonly",	L"nordonly",	FILE_ATTRIBUTE_READONLY,	0},+	{ "nourdonly",	L"nourdonly",	FILE_ATTRIBUTE_READONLY,	0},+	{ "noreadonly",	L"noreadonly",	FILE_ATTRIBUTE_READONLY,	0},+#endif+#ifdef UF_SPARSE+	{ "nosparse",	L"nosparse",		UF_SPARSE,	0},+	{ "nousparse",	L"nousparse",		UF_SPARSE,	0},+#endif+#ifdef UF_REPARSE+	{ "noreparse",	L"noreparse",		UF_REPARSE,	0},+	{ "noureparse",	L"noureparse",		UF_REPARSE,	0},+#endif+#ifdef UF_SYSTEM+	{ "nosystem",	L"nosystem",		UF_SYSTEM,	0},+	{ "nousystem",	L"nousystem",		UF_SYSTEM,	0},+#endif+#ifdef FILE_ATTRIBUTE_SYSTEM+	{ "nosystem",	L"nosystem",	FILE_ATTRIBUTE_SYSTEM,	0},+	{ "nousystem",	L"nousystem",	FILE_ATTRIBUTE_SYSTEM,	0},+#endif+#if defined(FS_UNRM_FL)		/* 'u' */+	{ "noundel",	L"noundel",		FS_UNRM_FL,	0},+#elif defined(EXT2_UNRM_FL)+	{ "noundel",	L"noundel",		EXT2_UNRM_FL,	0},+#endif++#if defined(FS_COMPR_FL)	/* 'c' */+	{ "nocompress",	L"nocompress",       	FS_COMPR_FL,	0},+#elif defined(EXT2_COMPR_FL)+	{ "nocompress",	L"nocompress",       	EXT2_COMPR_FL,	0},+#endif++#if defined(FS_NOATIME_FL)	/* 'A' */+	{ "noatime",	L"noatime",		0,		FS_NOATIME_FL},+#elif defined(EXT2_NOATIME_FL)+	{ "noatime",	L"noatime",		0,		EXT2_NOATIME_FL},+#endif+#if defined(FS_DIRSYNC_FL)	/* 'D' */+	{ "nodirsync",	L"nodirsync",		FS_DIRSYNC_FL,		0},+#elif defined(EXT2_DIRSYNC_FL)+	{ "nodirsync",	L"nodirsync",		EXT2_DIRSYNC_FL,	0},+#endif+#if defined(FS_JOURNAL_DATA_FL)	/* 'j' */+	{ "nojournal-data",L"nojournal-data",	FS_JOURNAL_DATA_FL,	0},+	{ "nojournal",	L"nojournal",		FS_JOURNAL_DATA_FL,	0},+#elif defined(EXT3_JOURNAL_DATA_FL)+	{ "nojournal-data",L"nojournal-data",	EXT3_JOURNAL_DATA_FL,	0},+	{ "nojournal",	L"nojournal",		EXT3_JOURNAL_DATA_FL,	0},+#endif+#if defined(FS_SECRM_FL)	/* 's' */+	{ "nosecdel",	L"nosecdel",		FS_SECRM_FL,		0},+	{ "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL,		0},+#elif defined(EXT2_SECRM_FL)+	{ "nosecdel",	L"nosecdel",		EXT2_SECRM_FL,		0},+	{ "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL,		0},+#endif+#if defined(FS_SYNC_FL)		/* 'S' */+	{ "nosync",	L"nosync",		FS_SYNC_FL,		0},+#elif defined(EXT2_SYNC_FL)+	{ "nosync",	L"nosync",		EXT2_SYNC_FL,		0},+#endif+#if defined(FS_NOTAIL_FL)	/* 't' */+	{ "notail",	L"notail",		0,		FS_NOTAIL_FL},+#elif defined(EXT2_NOTAIL_FL)+	{ "notail",	L"notail",		0,		EXT2_NOTAIL_FL},+#endif+#if defined(FS_TOPDIR_FL)	/* 'T' */+	{ "notopdir",	L"notopdir",		FS_TOPDIR_FL,		0},+#elif defined(EXT2_TOPDIR_FL)+	{ "notopdir",	L"notopdir",		EXT2_TOPDIR_FL,		0},+#endif+#ifdef FS_NOCOW_FL	/* 'C' */+	{ "nocow",	L"nocow",		0,	FS_NOCOW_FL},+#endif+#ifdef FS_PROJINHERIT_FL	/* 'P' */+	{ "noprojinherit",L"noprojinherit",	FS_PROJINHERIT_FL,	0},+#endif+	{ NULL,		NULL,			0,		0}+};++/*+ * fflagstostr --+ *	Convert file flags to a comma-separated string.  If no flags+ *	are set, return the empty string.+ */+static char *+ae_fflagstostr(unsigned long bitset, unsigned long bitclear)+{+	char *string, *dp;+	const char *sp;+	unsigned long bits;+	const struct flag *flag;+	size_t	length;++	bits = bitset | bitclear;+	length = 0;+	for (flag = fileflags; flag->name != NULL; flag++)+		if (bits & (flag->set | flag->clear)) {+			length += strlen(flag->name) + 1;+			bits &= ~(flag->set | flag->clear);+		}++	if (length == 0)+		return (NULL);+	string = (char *)malloc(length);+	if (string == NULL)+		return (NULL);++	dp = string;+	for (flag = fileflags; flag->name != NULL; flag++) {+		if (bitset & flag->set || bitclear & flag->clear) {+			sp = flag->name + 2;+		} else if (bitset & flag->clear  ||  bitclear & flag->set) {+			sp = flag->name;+		} else+			continue;+		bitset &= ~(flag->set | flag->clear);+		bitclear &= ~(flag->set | flag->clear);+		if (dp > string)+			*dp++ = ',';+		while ((*dp++ = *sp++) != '\0')+			;+		dp--;+	}++	*dp = '\0';+	return (string);+}++/*+ * strtofflags --+ *	Take string of arguments and return file flags.  This+ *	version works a little differently than strtofflags(3).+ *	In particular, it always tests every token, skipping any+ *	unrecognized tokens.  It returns a pointer to the first+ *	unrecognized token, or NULL if every token was recognized.+ *	This version is also const-correct and does not modify the+ *	provided string.+ */+static const char *+ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp)+{+	const char *start, *end;+	const struct flag *flag;+	unsigned long set, clear;+	const char *failed;++	set = clear = 0;+	start = s;+	failed = NULL;+	/* Find start of first token. */+	while (*start == '\t'  ||  *start == ' '  ||  *start == ',')+		start++;+	while (*start != '\0') {+		size_t length;+		/* Locate end of token. */+		end = start;+		while (*end != '\0'  &&  *end != '\t'  &&+		    *end != ' '  &&  *end != ',')+			end++;+		length = end - start;+		for (flag = fileflags; flag->name != NULL; flag++) {+			size_t flag_length = strlen(flag->name);+			if (length == flag_length+			    && memcmp(start, flag->name, length) == 0) {+				/* Matched "noXXXX", so reverse the sense. */+				clear |= flag->set;+				set |= flag->clear;+				break;+			} else if (length == flag_length - 2+			    && memcmp(start, flag->name + 2, length) == 0) {+				/* Matched "XXXX", so don't reverse. */+				set |= flag->set;+				clear |= flag->clear;+				break;+			}+		}+		/* Ignore unknown flag names. */+		if (flag->name == NULL  &&  failed == NULL)+			failed = start;++		/* Find start of next token. */+		start = end;+		while (*start == '\t'  ||  *start == ' '  ||  *start == ',')+			start++;++	}++	if (setp)+		*setp = set;+	if (clrp)+		*clrp = clear;++	/* Return location of first failure. */+	return (failed);+}++/*+ * wcstofflags --+ *	Take string of arguments and return file flags.  This+ *	version works a little differently than strtofflags(3).+ *	In particular, it always tests every token, skipping any+ *	unrecognized tokens.  It returns a pointer to the first+ *	unrecognized token, or NULL if every token was recognized.+ *	This version is also const-correct and does not modify the+ *	provided string.+ */+static const wchar_t *+ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)+{+	const wchar_t *start, *end;+	const struct flag *flag;+	unsigned long set, clear;+	const wchar_t *failed;++	set = clear = 0;+	start = s;+	failed = NULL;+	/* Find start of first token. */+	while (*start == L'\t'  ||  *start == L' '  ||  *start == L',')+		start++;+	while (*start != L'\0') {+		size_t length;+		/* Locate end of token. */+		end = start;+		while (*end != L'\0'  &&  *end != L'\t'  &&+		    *end != L' '  &&  *end != L',')+			end++;+		length = end - start;+		for (flag = fileflags; flag->wname != NULL; flag++) {+			size_t flag_length = wcslen(flag->wname);+			if (length == flag_length+			    && wmemcmp(start, flag->wname, length) == 0) {+				/* Matched "noXXXX", so reverse the sense. */+				clear |= flag->set;+				set |= flag->clear;+				break;+			} else if (length == flag_length - 2+			    && wmemcmp(start, flag->wname + 2, length) == 0) {+				/* Matched "XXXX", so don't reverse. */+				set |= flag->set;+				clear |= flag->clear;+				break;+			}+		}+		/* Ignore unknown flag names. */+		if (flag->wname == NULL  &&  failed == NULL)+			failed = start;++		/* Find start of next token. */+		start = end;+		while (*start == L'\t'  ||  *start == L' '  ||  *start == L',')+			start++;++	}++	if (setp)+		*setp = set;+	if (clrp)+		*clrp = clear;++	/* Return location of first failure. */+	return (failed);+}+++#ifdef TEST+#include <stdio.h>+int+main(int argc, char **argv)+{+	struct archive_entry *entry = archive_entry_new();+	unsigned long set, clear;+	const wchar_t *remainder;++	remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,");+	archive_entry_fflags(entry, &set, &clear);++	wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder);++	wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry));+	return (0);+}+#endif
+ c/archive_entry.h view
@@ -0,0 +1,708 @@+/*-+ * Copyright (c) 2003-2008 Tim Kientzle+ * Copyright (c) 2016 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/archive_entry.h 201096 2009-12-28 02:41:27Z kientzle $+ */++#ifndef ARCHIVE_ENTRY_H_INCLUDED+#define	ARCHIVE_ENTRY_H_INCLUDED++/* Note: Compiler will complain if this does not match archive.h! */+#define	ARCHIVE_VERSION_NUMBER 3004003++/*+ * Note: archive_entry.h is for use outside of libarchive; the+ * configuration headers (config.h, archive_platform.h, etc.) are+ * purely internal.  Do NOT use HAVE_XXX configuration macros to+ * control the behavior of this header!  If you must conditionalize,+ * use predefined compiler and/or platform macros.+ */++#include <sys/types.h>+#include <stddef.h>  /* for wchar_t */+#include <stdint.h>+#include <time.h>++#if defined(_WIN32) && !defined(__CYGWIN__)+#include <windows.h>+#endif++/* Get a suitable 64-bit integer type. */+#if !defined(__LA_INT64_T_DEFINED)+# if ARCHIVE_VERSION_NUMBER < 4000000+#define __LA_INT64_T la_int64_t+# endif+#define __LA_INT64_T_DEFINED+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)+typedef __int64 la_int64_t;+# else+#include <unistd.h>+#  if defined(_SCO_DS) || defined(__osf__)+typedef long long la_int64_t;+#  else+typedef int64_t la_int64_t;+#  endif+# endif+#endif++/* The la_ssize_t should match the type used in 'struct stat' */+#if !defined(__LA_SSIZE_T_DEFINED)+/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */+# if ARCHIVE_VERSION_NUMBER < 4000000+#define __LA_SSIZE_T la_ssize_t+# endif+#define __LA_SSIZE_T_DEFINED+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)+#  if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)+typedef ssize_t la_ssize_t;+#  elif defined(_WIN64)+typedef __int64 la_ssize_t;+#  else+typedef long la_ssize_t;+#  endif+# else+# include <unistd.h>  /* ssize_t */+typedef ssize_t la_ssize_t;+# endif+#endif++/* Get a suitable definition for mode_t */+#if ARCHIVE_VERSION_NUMBER >= 3999000+/* Switch to plain 'int' for libarchive 4.0.  It's less broken than 'mode_t' */+# define	__LA_MODE_T	int+#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) && !defined(__WATCOMC__)+# define	__LA_MODE_T	unsigned short+#else+# define	__LA_MODE_T	mode_t+#endif++/* Large file support for Android */+#ifdef __ANDROID__+#include "android_lf.h"+#endif++/*+ * On Windows, define LIBARCHIVE_STATIC if you're building or using a+ * .lib.  The default here assumes you're building a DLL.  Only+ * libarchive source should ever define __LIBARCHIVE_BUILD.+ */+#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)+# ifdef __LIBARCHIVE_BUILD+#  ifdef __GNUC__+#   define __LA_DECL	__attribute__((dllexport)) extern+#  else+#   define __LA_DECL	__declspec(dllexport)+#  endif+# else+#  ifdef __GNUC__+#   define __LA_DECL+#  else+#   define __LA_DECL	__declspec(dllimport)+#  endif+# endif+#else+/* Static libraries on all platforms and shared libraries on non-Windows. */+# define __LA_DECL+#endif++#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1+# define __LA_DEPRECATED __attribute__((deprecated))+#else+# define __LA_DEPRECATED+#endif++#ifdef __cplusplus+extern "C" {+#endif++/*+ * Description of an archive entry.+ *+ * You can think of this as "struct stat" with some text fields added in.+ *+ * TODO: Add "comment", "charset", and possibly other entries that are+ * supported by "pax interchange" format.  However, GNU, ustar, cpio,+ * and other variants don't support these features, so they're not an+ * excruciatingly high priority right now.+ *+ * TODO: "pax interchange" format allows essentially arbitrary+ * key/value attributes to be attached to any entry.  Supporting+ * such extensions may make this library useful for special+ * applications (e.g., a package manager could attach special+ * package-management attributes to each entry).+ */+struct archive;+struct archive_entry;++/*+ * File-type constants.  These are returned from archive_entry_filetype()+ * and passed to archive_entry_set_filetype().+ *+ * These values match S_XXX defines on every platform I've checked,+ * including Windows, AIX, Linux, Solaris, and BSD.  They're+ * (re)defined here because platforms generally don't define the ones+ * they don't support.  For example, Windows doesn't define S_IFLNK or+ * S_IFBLK.  Instead of having a mass of conditional logic and system+ * checks to define any S_XXX values that aren't supported locally,+ * I've just defined a new set of such constants so that+ * libarchive-based applications can manipulate and identify archive+ * entries properly even if the hosting platform can't store them on+ * disk.+ *+ * These values are also used directly within some portable formats,+ * such as cpio.  If you find a platform that varies from these, the+ * correct solution is to leave these alone and translate from these+ * portable values to platform-native values when entries are read from+ * or written to disk.+ */+/*+ * In libarchive 4.0, we can drop the casts here.+ * They're needed to work around Borland C's broken mode_t.+ */+#define AE_IFMT		((__LA_MODE_T)0170000)+#define AE_IFREG	((__LA_MODE_T)0100000)+#define AE_IFLNK	((__LA_MODE_T)0120000)+#define AE_IFSOCK	((__LA_MODE_T)0140000)+#define AE_IFCHR	((__LA_MODE_T)0020000)+#define AE_IFBLK	((__LA_MODE_T)0060000)+#define AE_IFDIR	((__LA_MODE_T)0040000)+#define AE_IFIFO	((__LA_MODE_T)0010000)++/*+ * Symlink types+ */+#define AE_SYMLINK_TYPE_UNDEFINED	0+#define AE_SYMLINK_TYPE_FILE		1+#define AE_SYMLINK_TYPE_DIRECTORY	2++/*+ * Basic object manipulation+ */++__LA_DECL struct archive_entry	*archive_entry_clear(struct archive_entry *);+/* The 'clone' function does a deep copy; all of the strings are copied too. */+__LA_DECL struct archive_entry	*archive_entry_clone(struct archive_entry *);+__LA_DECL void			 archive_entry_free(struct archive_entry *);+__LA_DECL struct archive_entry	*archive_entry_new(void);++/*+ * This form of archive_entry_new2() will pull character-set+ * conversion information from the specified archive handle.  The+ * older archive_entry_new(void) form is equivalent to calling+ * archive_entry_new2(NULL) and will result in the use of an internal+ * default character-set conversion.+ */+__LA_DECL struct archive_entry	*archive_entry_new2(struct archive *);++/*+ * Retrieve fields from an archive_entry.+ *+ * There are a number of implicit conversions among these fields.  For+ * example, if a regular string field is set and you read the _w wide+ * character field, the entry will implicitly convert narrow-to-wide+ * using the current locale.  Similarly, dev values are automatically+ * updated when you write devmajor or devminor and vice versa.+ *+ * In addition, fields can be "set" or "unset."  Unset string fields+ * return NULL, non-string fields have _is_set() functions to test+ * whether they've been set.  You can "unset" a string field by+ * assigning NULL; non-string fields have _unset() functions to+ * unset them.+ *+ * Note: There is one ambiguity in the above; string fields will+ * also return NULL when implicit character set conversions fail.+ * This is usually what you want.+ */+__LA_DECL time_t	 archive_entry_atime(struct archive_entry *);+__LA_DECL long		 archive_entry_atime_nsec(struct archive_entry *);+__LA_DECL int		 archive_entry_atime_is_set(struct archive_entry *);+__LA_DECL time_t	 archive_entry_birthtime(struct archive_entry *);+__LA_DECL long		 archive_entry_birthtime_nsec(struct archive_entry *);+__LA_DECL int		 archive_entry_birthtime_is_set(struct archive_entry *);+__LA_DECL time_t	 archive_entry_ctime(struct archive_entry *);+__LA_DECL long		 archive_entry_ctime_nsec(struct archive_entry *);+__LA_DECL int		 archive_entry_ctime_is_set(struct archive_entry *);+__LA_DECL dev_t		 archive_entry_dev(struct archive_entry *);+__LA_DECL int		 archive_entry_dev_is_set(struct archive_entry *);+__LA_DECL dev_t		 archive_entry_devmajor(struct archive_entry *);+__LA_DECL dev_t		 archive_entry_devminor(struct archive_entry *);+__LA_DECL __LA_MODE_T	 archive_entry_filetype(struct archive_entry *);+__LA_DECL void		 archive_entry_fflags(struct archive_entry *,+			    unsigned long * /* set */,+			    unsigned long * /* clear */);+__LA_DECL const char	*archive_entry_fflags_text(struct archive_entry *);+__LA_DECL la_int64_t	 archive_entry_gid(struct archive_entry *);+__LA_DECL const char	*archive_entry_gname(struct archive_entry *);+__LA_DECL const char	*archive_entry_gname_utf8(struct archive_entry *);+__LA_DECL const wchar_t	*archive_entry_gname_w(struct archive_entry *);+__LA_DECL const char	*archive_entry_hardlink(struct archive_entry *);+__LA_DECL const char	*archive_entry_hardlink_utf8(struct archive_entry *);+__LA_DECL const wchar_t	*archive_entry_hardlink_w(struct archive_entry *);+__LA_DECL la_int64_t	 archive_entry_ino(struct archive_entry *);+__LA_DECL la_int64_t	 archive_entry_ino64(struct archive_entry *);+__LA_DECL int		 archive_entry_ino_is_set(struct archive_entry *);+__LA_DECL __LA_MODE_T	 archive_entry_mode(struct archive_entry *);+__LA_DECL time_t	 archive_entry_mtime(struct archive_entry *);+__LA_DECL long		 archive_entry_mtime_nsec(struct archive_entry *);+__LA_DECL int		 archive_entry_mtime_is_set(struct archive_entry *);+__LA_DECL unsigned int	 archive_entry_nlink(struct archive_entry *);+__LA_DECL const char	*archive_entry_pathname(struct archive_entry *);+__LA_DECL const char	*archive_entry_pathname_utf8(struct archive_entry *);+__LA_DECL const wchar_t	*archive_entry_pathname_w(struct archive_entry *);+__LA_DECL __LA_MODE_T	 archive_entry_perm(struct archive_entry *);+__LA_DECL dev_t		 archive_entry_rdev(struct archive_entry *);+__LA_DECL dev_t		 archive_entry_rdevmajor(struct archive_entry *);+__LA_DECL dev_t		 archive_entry_rdevminor(struct archive_entry *);+__LA_DECL const char	*archive_entry_sourcepath(struct archive_entry *);+__LA_DECL const wchar_t	*archive_entry_sourcepath_w(struct archive_entry *);+__LA_DECL la_int64_t	 archive_entry_size(struct archive_entry *);+__LA_DECL int		 archive_entry_size_is_set(struct archive_entry *);+__LA_DECL const char	*archive_entry_strmode(struct archive_entry *);+__LA_DECL const char	*archive_entry_symlink(struct archive_entry *);+__LA_DECL const char	*archive_entry_symlink_utf8(struct archive_entry *);+__LA_DECL int		 archive_entry_symlink_type(struct archive_entry *);+__LA_DECL const wchar_t	*archive_entry_symlink_w(struct archive_entry *);+__LA_DECL la_int64_t	 archive_entry_uid(struct archive_entry *);+__LA_DECL const char	*archive_entry_uname(struct archive_entry *);+__LA_DECL const char	*archive_entry_uname_utf8(struct archive_entry *);+__LA_DECL const wchar_t	*archive_entry_uname_w(struct archive_entry *);+__LA_DECL int archive_entry_is_data_encrypted(struct archive_entry *);+__LA_DECL int archive_entry_is_metadata_encrypted(struct archive_entry *);+__LA_DECL int archive_entry_is_encrypted(struct archive_entry *);++/*+ * Set fields in an archive_entry.+ *+ * Note: Before libarchive 2.4, there were 'set' and 'copy' versions+ * of the string setters.  'copy' copied the actual string, 'set' just+ * stored the pointer.  In libarchive 2.4 and later, strings are+ * always copied.+ */++__LA_DECL void	archive_entry_set_atime(struct archive_entry *, time_t, long);+__LA_DECL void  archive_entry_unset_atime(struct archive_entry *);+#if defined(_WIN32) && !defined(__CYGWIN__)+__LA_DECL void archive_entry_copy_bhfi(struct archive_entry *, BY_HANDLE_FILE_INFORMATION *);+#endif+__LA_DECL void	archive_entry_set_birthtime(struct archive_entry *, time_t, long);+__LA_DECL void  archive_entry_unset_birthtime(struct archive_entry *);+__LA_DECL void	archive_entry_set_ctime(struct archive_entry *, time_t, long);+__LA_DECL void  archive_entry_unset_ctime(struct archive_entry *);+__LA_DECL void	archive_entry_set_dev(struct archive_entry *, dev_t);+__LA_DECL void	archive_entry_set_devmajor(struct archive_entry *, dev_t);+__LA_DECL void	archive_entry_set_devminor(struct archive_entry *, dev_t);+__LA_DECL void	archive_entry_set_filetype(struct archive_entry *, unsigned int);+__LA_DECL void	archive_entry_set_fflags(struct archive_entry *,+	    unsigned long /* set */, unsigned long /* clear */);+/* Returns pointer to start of first invalid token, or NULL if none. */+/* Note that all recognized tokens are processed, regardless. */+__LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *,+	    const char *);+__LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,+	    const wchar_t *);+__LA_DECL void	archive_entry_set_gid(struct archive_entry *, la_int64_t);+__LA_DECL void	archive_entry_set_gname(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_gname_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_gname(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);+__LA_DECL int	archive_entry_update_gname_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_hardlink(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_hardlink_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_hardlink(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);+__LA_DECL int	archive_entry_update_hardlink_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_ino(struct archive_entry *, la_int64_t);+__LA_DECL void	archive_entry_set_ino64(struct archive_entry *, la_int64_t);+__LA_DECL void	archive_entry_set_link(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_link_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_link(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);+__LA_DECL int	archive_entry_update_link_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_mode(struct archive_entry *, __LA_MODE_T);+__LA_DECL void	archive_entry_set_mtime(struct archive_entry *, time_t, long);+__LA_DECL void  archive_entry_unset_mtime(struct archive_entry *);+__LA_DECL void	archive_entry_set_nlink(struct archive_entry *, unsigned int);+__LA_DECL void	archive_entry_set_pathname(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_pathname_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_pathname(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);+__LA_DECL int	archive_entry_update_pathname_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_perm(struct archive_entry *, __LA_MODE_T);+__LA_DECL void	archive_entry_set_rdev(struct archive_entry *, dev_t);+__LA_DECL void	archive_entry_set_rdevmajor(struct archive_entry *, dev_t);+__LA_DECL void	archive_entry_set_rdevminor(struct archive_entry *, dev_t);+__LA_DECL void	archive_entry_set_size(struct archive_entry *, la_int64_t);+__LA_DECL void	archive_entry_unset_size(struct archive_entry *);+__LA_DECL void	archive_entry_copy_sourcepath(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *);+__LA_DECL void	archive_entry_set_symlink(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_symlink_type(struct archive_entry *, int);+__LA_DECL void	archive_entry_set_symlink_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_symlink(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);+__LA_DECL int	archive_entry_update_symlink_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_uid(struct archive_entry *, la_int64_t);+__LA_DECL void	archive_entry_set_uname(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_uname_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_uname(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);+__LA_DECL int	archive_entry_update_uname_utf8(struct archive_entry *, const char *);+__LA_DECL void	archive_entry_set_is_data_encrypted(struct archive_entry *, char is_encrypted);+__LA_DECL void	archive_entry_set_is_metadata_encrypted(struct archive_entry *, char is_encrypted);+/*+ * Routines to bulk copy fields to/from a platform-native "struct+ * stat."  Libarchive used to just store a struct stat inside of each+ * archive_entry object, but this created issues when trying to+ * manipulate archives on systems different than the ones they were+ * created on.+ *+ * TODO: On Linux and other LFS systems, provide both stat32 and+ * stat64 versions of these functions and all of the macro glue so+ * that archive_entry_stat is magically defined to+ * archive_entry_stat32 or archive_entry_stat64 as appropriate.+ */+__LA_DECL const struct stat	*archive_entry_stat(struct archive_entry *);+__LA_DECL void	archive_entry_copy_stat(struct archive_entry *, const struct stat *);++/*+ * Storage for Mac OS-specific AppleDouble metadata information.+ * Apple-format tar files store a separate binary blob containing+ * encoded metadata with ACL, extended attributes, etc.+ * This provides a place to store that blob.+ */++__LA_DECL const void * archive_entry_mac_metadata(struct archive_entry *, size_t *);+__LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_t);++/*+ * ACL routines.  This used to simply store and return text-format ACL+ * strings, but that proved insufficient for a number of reasons:+ *   = clients need control over uname/uid and gname/gid mappings+ *   = there are many different ACL text formats+ *   = would like to be able to read/convert archives containing ACLs+ *     on platforms that lack ACL libraries+ *+ *  This last point, in particular, forces me to implement a reasonably+ *  complete set of ACL support routines.+ */++/*+ * Permission bits.+ */+#define	ARCHIVE_ENTRY_ACL_EXECUTE             0x00000001+#define	ARCHIVE_ENTRY_ACL_WRITE               0x00000002+#define	ARCHIVE_ENTRY_ACL_READ                0x00000004+#define	ARCHIVE_ENTRY_ACL_READ_DATA           0x00000008+#define	ARCHIVE_ENTRY_ACL_LIST_DIRECTORY      0x00000008+#define	ARCHIVE_ENTRY_ACL_WRITE_DATA          0x00000010+#define	ARCHIVE_ENTRY_ACL_ADD_FILE            0x00000010+#define	ARCHIVE_ENTRY_ACL_APPEND_DATA         0x00000020+#define	ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY    0x00000020+#define	ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS    0x00000040+#define	ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS   0x00000080+#define	ARCHIVE_ENTRY_ACL_DELETE_CHILD        0x00000100+#define	ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES     0x00000200+#define	ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES    0x00000400+#define	ARCHIVE_ENTRY_ACL_DELETE              0x00000800+#define	ARCHIVE_ENTRY_ACL_READ_ACL            0x00001000+#define	ARCHIVE_ENTRY_ACL_WRITE_ACL           0x00002000+#define	ARCHIVE_ENTRY_ACL_WRITE_OWNER         0x00004000+#define	ARCHIVE_ENTRY_ACL_SYNCHRONIZE         0x00008000++#define	ARCHIVE_ENTRY_ACL_PERMS_POSIX1E			\+	(ARCHIVE_ENTRY_ACL_EXECUTE			\+	    | ARCHIVE_ENTRY_ACL_WRITE			\+	    | ARCHIVE_ENTRY_ACL_READ)++#define ARCHIVE_ENTRY_ACL_PERMS_NFS4			\+	(ARCHIVE_ENTRY_ACL_EXECUTE			\+	    | ARCHIVE_ENTRY_ACL_READ_DATA		\+	    | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY 		\+	    | ARCHIVE_ENTRY_ACL_WRITE_DATA		\+	    | ARCHIVE_ENTRY_ACL_ADD_FILE		\+	    | ARCHIVE_ENTRY_ACL_APPEND_DATA		\+	    | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY	\+	    | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS	\+	    | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS	\+	    | ARCHIVE_ENTRY_ACL_DELETE_CHILD		\+	    | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES		\+	    | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES	\+	    | ARCHIVE_ENTRY_ACL_DELETE			\+	    | ARCHIVE_ENTRY_ACL_READ_ACL		\+	    | ARCHIVE_ENTRY_ACL_WRITE_ACL		\+	    | ARCHIVE_ENTRY_ACL_WRITE_OWNER		\+	    | ARCHIVE_ENTRY_ACL_SYNCHRONIZE)++/*+ * Inheritance values (NFS4 ACLs only); included in permset.+ */+#define	ARCHIVE_ENTRY_ACL_ENTRY_INHERITED                   0x01000000+#define	ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT                0x02000000+#define	ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT           0x04000000+#define	ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT        0x08000000+#define	ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY                0x10000000+#define	ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS           0x20000000+#define	ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS               0x40000000++#define	ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4			\+	(ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT			\+	    | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT		\+	    | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT	\+	    | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY		\+	    | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS		\+	    | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS		\+	    | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED)++/* We need to be able to specify combinations of these. */+#define	ARCHIVE_ENTRY_ACL_TYPE_ACCESS	0x00000100  /* POSIX.1e only */+#define	ARCHIVE_ENTRY_ACL_TYPE_DEFAULT	0x00000200  /* POSIX.1e only */+#define	ARCHIVE_ENTRY_ACL_TYPE_ALLOW	0x00000400 /* NFS4 only */+#define	ARCHIVE_ENTRY_ACL_TYPE_DENY	0x00000800 /* NFS4 only */+#define	ARCHIVE_ENTRY_ACL_TYPE_AUDIT	0x00001000 /* NFS4 only */+#define	ARCHIVE_ENTRY_ACL_TYPE_ALARM	0x00002000 /* NFS4 only */+#define	ARCHIVE_ENTRY_ACL_TYPE_POSIX1E	(ARCHIVE_ENTRY_ACL_TYPE_ACCESS \+	    | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)+#define	ARCHIVE_ENTRY_ACL_TYPE_NFS4	(ARCHIVE_ENTRY_ACL_TYPE_ALLOW \+	    | ARCHIVE_ENTRY_ACL_TYPE_DENY \+	    | ARCHIVE_ENTRY_ACL_TYPE_AUDIT \+	    | ARCHIVE_ENTRY_ACL_TYPE_ALARM)++/* Tag values mimic POSIX.1e */+#define	ARCHIVE_ENTRY_ACL_USER		10001	/* Specified user. */+#define	ARCHIVE_ENTRY_ACL_USER_OBJ 	10002	/* User who owns the file. */+#define	ARCHIVE_ENTRY_ACL_GROUP		10003	/* Specified group. */+#define	ARCHIVE_ENTRY_ACL_GROUP_OBJ	10004	/* Group who owns the file. */+#define	ARCHIVE_ENTRY_ACL_MASK		10005	/* Modify group access (POSIX.1e only) */+#define	ARCHIVE_ENTRY_ACL_OTHER		10006	/* Public (POSIX.1e only) */+#define	ARCHIVE_ENTRY_ACL_EVERYONE	10107   /* Everyone (NFS4 only) */++/*+ * Set the ACL by clearing it and adding entries one at a time.+ * Unlike the POSIX.1e ACL routines, you must specify the type+ * (access/default) for each entry.  Internally, the ACL data is just+ * a soup of entries.  API calls here allow you to retrieve just the+ * entries of interest.  This design (which goes against the spirit of+ * POSIX.1e) is useful for handling archive formats that combine+ * default and access information in a single ACL list.+ */+__LA_DECL void	 archive_entry_acl_clear(struct archive_entry *);+__LA_DECL int	 archive_entry_acl_add_entry(struct archive_entry *,+	    int /* type */, int /* permset */, int /* tag */,+	    int /* qual */, const char * /* name */);+__LA_DECL int	 archive_entry_acl_add_entry_w(struct archive_entry *,+	    int /* type */, int /* permset */, int /* tag */,+	    int /* qual */, const wchar_t * /* name */);++/*+ * To retrieve the ACL, first "reset", then repeatedly ask for the+ * "next" entry.  The want_type parameter allows you to request only+ * certain types of entries.+ */+__LA_DECL int	 archive_entry_acl_reset(struct archive_entry *, int /* want_type */);+__LA_DECL int	 archive_entry_acl_next(struct archive_entry *, int /* want_type */,+	    int * /* type */, int * /* permset */, int * /* tag */,+	    int * /* qual */, const char ** /* name */);++/*+ * Construct a text-format ACL.  The flags argument is a bitmask that+ * can include any of the following:+ *+ * Flags only for archive entries with POSIX.1e ACL:+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries.+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries.+ * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each+ *    default ACL entry.+ * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS - Output only one colon after "other" and+ *    "mask" entries.+ *+ * Flags only for archive entries with NFSv4 ACL:+ * ARCHIVE_ENTRY_ACL_STYLE_COMPACT - Do not output the minus character for+ *    unset permissions and flags in NFSv4 ACL permission and flag fields+ *+ * Flags for for archive entries with POSIX.1e ACL or NFSv4 ACL:+ * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in+ *    each ACL entry.+ * ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA - Separate entries with comma+ *    instead of newline.+ */+#define	ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID	0x00000001+#define	ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	0x00000002+#define	ARCHIVE_ENTRY_ACL_STYLE_SOLARIS		0x00000004+#define	ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA	0x00000008+#define	ARCHIVE_ENTRY_ACL_STYLE_COMPACT		0x00000010++__LA_DECL wchar_t *archive_entry_acl_to_text_w(struct archive_entry *,+	    la_ssize_t * /* len */, int /* flags */);+__LA_DECL char *archive_entry_acl_to_text(struct archive_entry *,+	    la_ssize_t * /* len */, int /* flags */);+__LA_DECL int archive_entry_acl_from_text_w(struct archive_entry *,+	    const wchar_t * /* wtext */, int /* type */);+__LA_DECL int archive_entry_acl_from_text(struct archive_entry *,+	    const char * /* text */, int /* type */);++/* Deprecated constants */+#define	OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID		1024+#define	OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	2048++/* Deprecated functions */+__LA_DECL const wchar_t	*archive_entry_acl_text_w(struct archive_entry *,+		    int /* flags */) __LA_DEPRECATED;+__LA_DECL const char *archive_entry_acl_text(struct archive_entry *,+		    int /* flags */) __LA_DEPRECATED;++/* Return bitmask of ACL types in an archive entry */+__LA_DECL int	 archive_entry_acl_types(struct archive_entry *);++/* Return a count of entries matching 'want_type' */+__LA_DECL int	 archive_entry_acl_count(struct archive_entry *, int /* want_type */);++/* Return an opaque ACL object. */+/* There's not yet anything clients can actually do with this... */+struct archive_acl;+__LA_DECL struct archive_acl *archive_entry_acl(struct archive_entry *);++/*+ * extended attributes+ */++__LA_DECL void	 archive_entry_xattr_clear(struct archive_entry *);+__LA_DECL void	 archive_entry_xattr_add_entry(struct archive_entry *,+	    const char * /* name */, const void * /* value */,+	    size_t /* size */);++/*+ * To retrieve the xattr list, first "reset", then repeatedly ask for the+ * "next" entry.+ */++__LA_DECL int	archive_entry_xattr_count(struct archive_entry *);+__LA_DECL int	archive_entry_xattr_reset(struct archive_entry *);+__LA_DECL int	archive_entry_xattr_next(struct archive_entry *,+	    const char ** /* name */, const void ** /* value */, size_t *);++/*+ * sparse+ */++__LA_DECL void	 archive_entry_sparse_clear(struct archive_entry *);+__LA_DECL void	 archive_entry_sparse_add_entry(struct archive_entry *,+	    la_int64_t /* offset */, la_int64_t /* length */);++/*+ * To retrieve the xattr list, first "reset", then repeatedly ask for the+ * "next" entry.+ */++__LA_DECL int	archive_entry_sparse_count(struct archive_entry *);+__LA_DECL int	archive_entry_sparse_reset(struct archive_entry *);+__LA_DECL int	archive_entry_sparse_next(struct archive_entry *,+	    la_int64_t * /* offset */, la_int64_t * /* length */);++/*+ * Utility to match up hardlinks.+ *+ * The 'struct archive_entry_linkresolver' is a cache of archive entries+ * for files with multiple links.  Here's how to use it:+ *   1. Create a lookup object with archive_entry_linkresolver_new()+ *   2. Tell it the archive format you're using.+ *   3. Hand each archive_entry to archive_entry_linkify().+ *      That function will return 0, 1, or 2 entries that should+ *      be written.+ *   4. Call archive_entry_linkify(resolver, NULL) until+ *      no more entries are returned.+ *   5. Call archive_entry_linkresolver_free(resolver) to free resources.+ *+ * The entries returned have their hardlink and size fields updated+ * appropriately.  If an entry is passed in that does not refer to+ * a file with multiple links, it is returned unchanged.  The intention+ * is that you should be able to simply filter all entries through+ * this machine.+ *+ * To make things more efficient, be sure that each entry has a valid+ * nlinks value.  The hardlink cache uses this to track when all links+ * have been found.  If the nlinks value is zero, it will keep every+ * name in the cache indefinitely, which can use a lot of memory.+ *+ * Note that archive_entry_size() is reset to zero if the file+ * body should not be written to the archive.  Pay attention!+ */+struct archive_entry_linkresolver;++/*+ * There are three different strategies for marking hardlinks.+ * The descriptions below name them after the best-known+ * formats that rely on each strategy:+ *+ * "Old cpio" is the simplest, it always returns any entry unmodified.+ *    As far as I know, only cpio formats use this.  Old cpio archives+ *    store every link with the full body; the onus is on the dearchiver+ *    to detect and properly link the files as they are restored.+ * "tar" is also pretty simple; it caches a copy the first time it sees+ *    any link.  Subsequent appearances are modified to be hardlink+ *    references to the first one without any body.  Used by all tar+ *    formats, although the newest tar formats permit the "old cpio" strategy+ *    as well.  This strategy is very simple for the dearchiver,+ *    and reasonably straightforward for the archiver.+ * "new cpio" is trickier.  It stores the body only with the last+ *    occurrence.  The complication is that we might not+ *    see every link to a particular file in a single session, so+ *    there's no easy way to know when we've seen the last occurrence.+ *    The solution here is to queue one link until we see the next.+ *    At the end of the session, you can enumerate any remaining+ *    entries by calling archive_entry_linkify(NULL) and store those+ *    bodies.  If you have a file with three links l1, l2, and l3,+ *    you'll get the following behavior if you see all three links:+ *           linkify(l1) => NULL   (the resolver stores l1 internally)+ *           linkify(l2) => l1     (resolver stores l2, you write l1)+ *           linkify(l3) => l2, l3 (all links seen, you can write both).+ *    If you only see l1 and l2, you'll get this behavior:+ *           linkify(l1) => NULL+ *           linkify(l2) => l1+ *           linkify(NULL) => l2   (at end, you retrieve remaining links)+ *    As the name suggests, this strategy is used by newer cpio variants.+ *    It's noticeably more complex for the archiver, slightly more complex+ *    for the dearchiver than the tar strategy, but makes it straightforward+ *    to restore a file using any link by simply continuing to scan until+ *    you see a link that is stored with a body.  In contrast, the tar+ *    strategy requires you to rescan the archive from the beginning to+ *    correctly extract an arbitrary link.+ */++__LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);+__LA_DECL void archive_entry_linkresolver_set_strategy(+	struct archive_entry_linkresolver *, int /* format_code */);+__LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);+__LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,+    struct archive_entry **, struct archive_entry **);+__LA_DECL struct archive_entry *archive_entry_partial_links(+    struct archive_entry_linkresolver *res, unsigned int *links);+#ifdef __cplusplus+}+#endif++/* This is meaningless outside of this header. */+#undef __LA_DECL++#endif /* !ARCHIVE_ENTRY_H_INCLUDED */
+ c/archive_entry_copy_bhfi.c view
@@ -0,0 +1,75 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#include "archive_private.h"+#include "archive_entry.h"++#if defined(_WIN32) && !defined(__CYGWIN__)++#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)++__inline static void+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)+{+	ULARGE_INTEGER utc;++	utc.HighPart = filetime->dwHighDateTime;+	utc.LowPart  = filetime->dwLowDateTime;+	if (utc.QuadPart >= EPOC_TIME) {+		utc.QuadPart -= EPOC_TIME;+		*t = (time_t)(utc.QuadPart / 10000000);	/* milli seconds base */+		*ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */+	} else {+		*t = 0;+		*ns = 0;+	}+}++void+archive_entry_copy_bhfi(struct archive_entry *entry,+			BY_HANDLE_FILE_INFORMATION *bhfi)+{+	time_t secs;+	long nsecs;++	fileTimeToUtc(&bhfi->ftLastAccessTime, &secs, &nsecs);+	archive_entry_set_atime(entry, secs, nsecs);+	fileTimeToUtc(&bhfi->ftLastWriteTime, &secs, &nsecs);+	archive_entry_set_mtime(entry, secs, nsecs);+	fileTimeToUtc(&bhfi->ftCreationTime, &secs, &nsecs);+	archive_entry_set_birthtime(entry, secs, nsecs);+	archive_entry_set_ctime(entry, secs, nsecs);+	archive_entry_set_dev(entry, bhfi->dwVolumeSerialNumber);+	archive_entry_set_ino64(entry, (((int64_t)bhfi->nFileIndexHigh) << 32)+		+ bhfi->nFileIndexLow);+	archive_entry_set_nlink(entry, bhfi->nNumberOfLinks);+	archive_entry_set_size(entry, (((int64_t)bhfi->nFileSizeHigh) << 32)+		+ bhfi->nFileSizeLow);+	/* archive_entry_set_mode(entry, st->st_mode); */+}+#endif
+ c/archive_entry_copy_stat.c view
@@ -0,0 +1,83 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_copy_stat.c 189466 2009-03-07 00:52:02Z kientzle $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif++#include "archive.h"+#include "archive_entry.h"++void+archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)+{+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC+	archive_entry_set_atime(entry, st->st_atime, st->st_atimespec.tv_nsec);+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctimespec.tv_nsec);+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec);+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC+	archive_entry_set_atime(entry, st->st_atime, st->st_atim.tv_nsec);+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctim.tv_nsec);+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec);+#elif HAVE_STRUCT_STAT_ST_MTIME_NSEC+	archive_entry_set_atime(entry, st->st_atime, st->st_atime_nsec);+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_nsec);+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_nsec);+#elif HAVE_STRUCT_STAT_ST_MTIME_N+	archive_entry_set_atime(entry, st->st_atime, st->st_atime_n);+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_n);+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n);+#elif HAVE_STRUCT_STAT_ST_UMTIME+	archive_entry_set_atime(entry, st->st_atime, st->st_uatime * 1000);+	archive_entry_set_ctime(entry, st->st_ctime, st->st_uctime * 1000);+	archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime * 1000);+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC+	archive_entry_set_atime(entry, st->st_atime, st->st_atime_usec * 1000);+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_usec * 1000);+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec * 1000);+#else+	archive_entry_set_atime(entry, st->st_atime, 0);+	archive_entry_set_ctime(entry, st->st_ctime, 0);+	archive_entry_set_mtime(entry, st->st_mtime, 0);+#endif+#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC+	archive_entry_set_birthtime(entry, st->st_birthtime, st->st_birthtimespec.tv_nsec);+#elif HAVE_STRUCT_STAT_ST_BIRTHTIME+	archive_entry_set_birthtime(entry, st->st_birthtime, 0);+#else+	archive_entry_unset_birthtime(entry);+#endif+	archive_entry_set_dev(entry, st->st_dev);+	archive_entry_set_gid(entry, st->st_gid);+	archive_entry_set_uid(entry, st->st_uid);+	archive_entry_set_ino(entry, st->st_ino);+	archive_entry_set_nlink(entry, st->st_nlink);+	archive_entry_set_rdev(entry, st->st_rdev);+	archive_entry_set_size(entry, st->st_size);+	archive_entry_set_mode(entry, st->st_mode);+}
+ c/archive_entry_link_resolver.c view
@@ -0,0 +1,447 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_link_resolver.c 201100 2009-12-28 03:05:31Z kientzle $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"++/*+ * This is mostly a pretty straightforward hash table implementation.+ * The only interesting bit is the different strategies used to+ * match up links.  These strategies match those used by various+ * archiving formats:+ *   tar - content stored with first link, remainder refer back to it.+ *       This requires us to match each subsequent link up with the+ *       first appearance.+ *   cpio - Old cpio just stored body with each link, match-ups were+ *       implicit.  This is trivial.+ *   new cpio - New cpio only stores body with last link, match-ups+ *       are implicit.  This is actually quite tricky; see the notes+ *       below.+ */++/* Users pass us a format code, we translate that into a strategy here. */+#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR	0+#define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1+#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2+#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3++/* Initial size of link cache. */+#define	links_cache_initial_size 1024++struct links_entry {+	struct links_entry	*next;+	struct links_entry	*previous;+	struct archive_entry	*canonical;+	struct archive_entry	*entry;+	size_t			 hash;+	unsigned int		 links; /* # links not yet seen */+};++struct archive_entry_linkresolver {+	struct links_entry	**buckets;+	struct links_entry	 *spare;+	unsigned long		  number_entries;+	size_t			  number_buckets;+	int			  strategy;+};++#define	NEXT_ENTRY_DEFERRED	1+#define	NEXT_ENTRY_PARTIAL	2+#define	NEXT_ENTRY_ALL		(NEXT_ENTRY_DEFERRED | NEXT_ENTRY_PARTIAL)++static struct links_entry *find_entry(struct archive_entry_linkresolver *,+		    struct archive_entry *);+static void grow_hash(struct archive_entry_linkresolver *);+static struct links_entry *insert_entry(struct archive_entry_linkresolver *,+		    struct archive_entry *);+static struct links_entry *next_entry(struct archive_entry_linkresolver *,+    int);++struct archive_entry_linkresolver *+archive_entry_linkresolver_new(void)+{+	struct archive_entry_linkresolver *res;++	/* Check for positive power-of-two */+	if (links_cache_initial_size == 0 ||+	    (links_cache_initial_size & (links_cache_initial_size - 1)) != 0)+		return (NULL);++	res = calloc(1, sizeof(struct archive_entry_linkresolver));+	if (res == NULL)+		return (NULL);+	res->number_buckets = links_cache_initial_size;+	res->buckets = calloc(res->number_buckets, sizeof(res->buckets[0]));+	if (res->buckets == NULL) {+		free(res);+		return (NULL);+	}+	return (res);+}++void+archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,+    int fmt)+{+	int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;++	switch (fmtbase) {+	case ARCHIVE_FORMAT_7ZIP:+	case ARCHIVE_FORMAT_AR:+	case ARCHIVE_FORMAT_ZIP:+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;+		break;+	case ARCHIVE_FORMAT_CPIO:+		switch (fmt) {+		case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:+		case ARCHIVE_FORMAT_CPIO_SVR4_CRC:+			res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO;+			break;+		default:+			res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;+			break;+		}+		break;+	case ARCHIVE_FORMAT_MTREE:+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;+		break;+	case ARCHIVE_FORMAT_ISO9660:+	case ARCHIVE_FORMAT_SHAR:+	case ARCHIVE_FORMAT_TAR:+	case ARCHIVE_FORMAT_XAR:+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;+		break;+	default:+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;+		break;+	}+}++void+archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)+{+	struct links_entry *le;++	if (res == NULL)+		return;++	while ((le = next_entry(res, NEXT_ENTRY_ALL)) != NULL)+		archive_entry_free(le->entry);+	free(res->buckets);+	free(res);+}++void+archive_entry_linkify(struct archive_entry_linkresolver *res,+    struct archive_entry **e, struct archive_entry **f)+{+	struct links_entry *le;+	struct archive_entry *t;++	*f = NULL; /* Default: Don't return a second entry. */++	if (*e == NULL) {+		le = next_entry(res, NEXT_ENTRY_DEFERRED);+		if (le != NULL) {+			*e = le->entry;+			le->entry = NULL;+		}+		return;+	}++	/* If it has only one link, then we're done. */+	if (archive_entry_nlink(*e) == 1)+		return;+	/* Directories, devices never have hardlinks. */+	if (archive_entry_filetype(*e) == AE_IFDIR+	    || archive_entry_filetype(*e) == AE_IFBLK+	    || archive_entry_filetype(*e) == AE_IFCHR)+		return;++	switch (res->strategy) {+	case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:+		le = find_entry(res, *e);+		if (le != NULL) {+			archive_entry_unset_size(*e);+			archive_entry_copy_hardlink(*e,+			    archive_entry_pathname(le->canonical));+		} else+			insert_entry(res, *e);+		return;+	case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:+		le = find_entry(res, *e);+		if (le != NULL) {+			archive_entry_copy_hardlink(*e,+			    archive_entry_pathname(le->canonical));+		} else+			insert_entry(res, *e);+		return;+	case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:+		/* This one is trivial. */+		return;+	case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:+		le = find_entry(res, *e);+		if (le != NULL) {+			/*+			 * Put the new entry in le, return the+			 * old entry from le.+			 */+			t = *e;+			*e = le->entry;+			le->entry = t;+			/* Make the old entry into a hardlink. */+			archive_entry_unset_size(*e);+			archive_entry_copy_hardlink(*e,+			    archive_entry_pathname(le->canonical));+			/* If we ran out of links, return the+			 * final entry as well. */+			if (le->links == 0) {+				*f = le->entry;+				le->entry = NULL;+			}+		} else {+			/*+			 * If we haven't seen it, tuck it away+			 * for future use.+			 */+			le = insert_entry(res, *e);+			if (le == NULL)+				/* XXX We should return an error code XXX */+				return;+			le->entry = *e;+			*e = NULL;+		}+		return;+	default:+		break;+	}+	return;+}++static struct links_entry *+find_entry(struct archive_entry_linkresolver *res,+    struct archive_entry *entry)+{+	struct links_entry	*le;+	size_t			 hash, bucket;+	dev_t			 dev;+	int64_t			 ino;++	/* Free a held entry. */+	if (res->spare != NULL) {+		archive_entry_free(res->spare->canonical);+		archive_entry_free(res->spare->entry);+		free(res->spare);+		res->spare = NULL;+	}++	dev = archive_entry_dev(entry);+	ino = archive_entry_ino64(entry);+	hash = (size_t)(dev ^ ino);++	/* Try to locate this entry in the links cache. */+	bucket = hash & (res->number_buckets - 1);+	for (le = res->buckets[bucket]; le != NULL; le = le->next) {+		if (le->hash == hash+		    && dev == archive_entry_dev(le->canonical)+		    && ino == archive_entry_ino64(le->canonical)) {+			/*+			 * Decrement link count each time and release+			 * the entry if it hits zero.  This saves+			 * memory and is necessary for detecting+			 * missed links.+			 */+			--le->links;+			if (le->links > 0)+				return (le);+			/* Remove it from this hash bucket. */+			if (le->previous != NULL)+				le->previous->next = le->next;+			if (le->next != NULL)+				le->next->previous = le->previous;+			if (res->buckets[bucket] == le)+				res->buckets[bucket] = le->next;+			res->number_entries--;+			/* Defer freeing this entry. */+			res->spare = le;+			return (le);+		}+	}+	return (NULL);+}++static struct links_entry *+next_entry(struct archive_entry_linkresolver *res, int mode)+{+	struct links_entry	*le;+	size_t			 bucket;++	/* Free a held entry. */+	if (res->spare != NULL) {+		archive_entry_free(res->spare->canonical);+		archive_entry_free(res->spare->entry);+		free(res->spare);+		res->spare = NULL;+	}++	/* Look for next non-empty bucket in the links cache. */+	for (bucket = 0; bucket < res->number_buckets; bucket++) {+		for (le = res->buckets[bucket]; le != NULL; le = le->next) {+			if (le->entry != NULL &&+			    (mode & NEXT_ENTRY_DEFERRED) == 0)+				continue;+			if (le->entry == NULL &&+			    (mode & NEXT_ENTRY_PARTIAL) == 0)+				continue;+			/* Remove it from this hash bucket. */+			if (le->next != NULL)+				le->next->previous = le->previous;+			if (le->previous != NULL)+				le->previous->next = le->next;+			else+				res->buckets[bucket] = le->next;+			res->number_entries--;+			/* Defer freeing this entry. */+			res->spare = le;+			return (le);+		}+	}+	return (NULL);+}++static struct links_entry *+insert_entry(struct archive_entry_linkresolver *res,+    struct archive_entry *entry)+{+	struct links_entry *le;+	size_t hash, bucket;++	/* Add this entry to the links cache. */+	le = calloc(1, sizeof(struct links_entry));+	if (le == NULL)+		return (NULL);+	le->canonical = archive_entry_clone(entry);++	/* If the links cache is getting too full, enlarge the hash table. */+	if (res->number_entries > res->number_buckets * 2)+		grow_hash(res);++	hash = (size_t)(archive_entry_dev(entry) ^ archive_entry_ino64(entry));+	bucket = hash & (res->number_buckets - 1);++	/* If we could allocate the entry, record it. */+	if (res->buckets[bucket] != NULL)+		res->buckets[bucket]->previous = le;+	res->number_entries++;+	le->next = res->buckets[bucket];+	le->previous = NULL;+	res->buckets[bucket] = le;+	le->hash = hash;+	le->links = archive_entry_nlink(entry) - 1;+	return (le);+}++static void+grow_hash(struct archive_entry_linkresolver *res)+{+	struct links_entry *le, **new_buckets;+	size_t new_size;+	size_t i, bucket;++	/* Try to enlarge the bucket list. */+	new_size = res->number_buckets * 2;+	if (new_size < res->number_buckets)+		return;+	new_buckets = calloc(new_size, sizeof(struct links_entry *));++	if (new_buckets == NULL)+		return;++	for (i = 0; i < res->number_buckets; i++) {+		while (res->buckets[i] != NULL) {+			/* Remove entry from old bucket. */+			le = res->buckets[i];+			res->buckets[i] = le->next;++			/* Add entry to new bucket. */+			bucket = le->hash & (new_size - 1);++			if (new_buckets[bucket] != NULL)+				new_buckets[bucket]->previous = le;+			le->next = new_buckets[bucket];+			le->previous = NULL;+			new_buckets[bucket] = le;+		}+	}+	free(res->buckets);+	res->buckets = new_buckets;+	res->number_buckets = new_size;+}++struct archive_entry *+archive_entry_partial_links(struct archive_entry_linkresolver *res,+    unsigned int *links)+{+	struct archive_entry	*e;+	struct links_entry	*le;++	/* Free a held entry. */+	if (res->spare != NULL) {+		archive_entry_free(res->spare->canonical);+		archive_entry_free(res->spare->entry);+		free(res->spare);+		res->spare = NULL;+	}++	le = next_entry(res, NEXT_ENTRY_PARTIAL);+	if (le != NULL) {+		e = le->canonical;+		if (links != NULL)+			*links = le->links;+		le->canonical = NULL;+	} else {+		e = NULL;+		if (links != NULL)+			*links = 0;+	}+	return (e);+}
+ c/archive_entry_locale.h view
@@ -0,0 +1,92 @@+/*-+ * Copyright (c) 2011 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ */++#ifndef ARCHIVE_ENTRY_LOCALE_H_INCLUDED+#define ARCHIVE_ENTRY_LOCALE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++struct archive_entry;+struct archive_string_conv;++/*+ * Utility functions to set and get entry attributes by translating+ * character-set. These are designed for use in format readers and writers.+ *+ * The return code and interface of these are quite different from other+ * functions for archive_entry defined in archive_entry.h.+ * Common return code are:+ *   Return 0 if the string conversion succeeded.+ *   Return -1 if the string conversion failed.+ */++#define archive_entry_gname_l	_archive_entry_gname_l+int _archive_entry_gname_l(struct archive_entry *,+    const char **, size_t *, struct archive_string_conv *);+#define archive_entry_hardlink_l	_archive_entry_hardlink_l+int _archive_entry_hardlink_l(struct archive_entry *,+    const char **, size_t *, struct archive_string_conv *);+#define archive_entry_pathname_l	_archive_entry_pathname_l+int _archive_entry_pathname_l(struct archive_entry *,+    const char **, size_t *, struct archive_string_conv *);+#define archive_entry_symlink_l	_archive_entry_symlink_l+int _archive_entry_symlink_l(struct archive_entry *,+    const char **, size_t *, struct archive_string_conv *);+#define archive_entry_uname_l	_archive_entry_uname_l+int _archive_entry_uname_l(struct archive_entry *,+    const char **, size_t *, struct archive_string_conv *);+#define archive_entry_acl_text_l _archive_entry_acl_text_l+int _archive_entry_acl_text_l(struct archive_entry *, int,+const char **, size_t *, struct archive_string_conv *) __LA_DEPRECATED;+#define archive_entry_acl_to_text_l _archive_entry_acl_to_text_l+char *_archive_entry_acl_to_text_l(struct archive_entry *, ssize_t *, int,+    struct archive_string_conv *);+#define archive_entry_acl_from_text_l _archive_entry_acl_from_text_l+int _archive_entry_acl_from_text_l(struct archive_entry *, const char* text,+    int type, struct archive_string_conv *);+#define archive_entry_copy_gname_l	_archive_entry_copy_gname_l+int _archive_entry_copy_gname_l(struct archive_entry *,+    const char *, size_t, struct archive_string_conv *);+#define archive_entry_copy_hardlink_l	_archive_entry_copy_hardlink_l+int _archive_entry_copy_hardlink_l(struct archive_entry *,+    const char *, size_t, struct archive_string_conv *);+#define archive_entry_copy_link_l	_archive_entry_copy_link_l+int _archive_entry_copy_link_l(struct archive_entry *,+    const char *, size_t, struct archive_string_conv *);+#define archive_entry_copy_pathname_l	_archive_entry_copy_pathname_l+int _archive_entry_copy_pathname_l(struct archive_entry *,+    const char *, size_t, struct archive_string_conv *);+#define archive_entry_copy_symlink_l	_archive_entry_copy_symlink_l+int _archive_entry_copy_symlink_l(struct archive_entry *,+    const char *, size_t, struct archive_string_conv *);+#define archive_entry_copy_uname_l	_archive_entry_copy_uname_l+int _archive_entry_copy_uname_l(struct archive_entry *,+    const char *, size_t, struct archive_string_conv *);++#endif /* ARCHIVE_ENTRY_LOCALE_H_INCLUDED */
+ c/archive_entry_private.h view
@@ -0,0 +1,184 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/archive_entry_private.h 201096 2009-12-28 02:41:27Z kientzle $+ */++#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED+#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++#include "archive_acl_private.h"+#include "archive_string.h"++struct ae_xattr {+	struct ae_xattr *next;++	char	*name;+	void	*value;+	size_t	size;+};++struct ae_sparse {+	struct ae_sparse *next;++	int64_t	 offset;+	int64_t	 length;+};++/*+ * Description of an archive entry.+ *+ * Basically, this is a "struct stat" with a few text fields added in.+ *+ * TODO: Add "comment", "charset", and possibly other entries+ * that are supported by "pax interchange" format.  However, GNU, ustar,+ * cpio, and other variants don't support these features, so they're not an+ * excruciatingly high priority right now.+ *+ * TODO: "pax interchange" format allows essentially arbitrary+ * key/value attributes to be attached to any entry.  Supporting+ * such extensions may make this library useful for special+ * applications (e.g., a package manager could attach special+ * package-management attributes to each entry).  There are tricky+ * API issues involved, so this is not going to happen until+ * there's a real demand for it.+ *+ * TODO: Design a good API for handling sparse files.+ */+struct archive_entry {+	struct archive *archive;++	/*+	 * Note that ae_stat.st_mode & AE_IFMT  can be  0!+	 *+	 * This occurs when the actual file type of the object is not+	 * in the archive.  For example, 'tar' archives store+	 * hardlinks without marking the type of the underlying+	 * object.+	 */++	/*+	 * We have a "struct aest" for holding file metadata rather than just+	 * a "struct stat" because on some platforms the "struct stat" has+	 * fields which are too narrow to hold the range of possible values;+	 * we don't want to lose information if we read an archive and write+	 * out another (e.g., in "tar -cf new.tar @old.tar").+	 *+	 * The "stat" pointer points to some form of platform-specific struct+	 * stat; it is declared as a void * rather than a struct stat * as+	 * some platforms have multiple varieties of stat structures.+	 */+	void *stat;+	int  stat_valid; /* Set to 0 whenever a field in aest changes. */++	struct aest {+		int64_t		aest_atime;+		uint32_t	aest_atime_nsec;+		int64_t		aest_ctime;+		uint32_t	aest_ctime_nsec;+		int64_t		aest_mtime;+		uint32_t	aest_mtime_nsec;+		int64_t		aest_birthtime;+		uint32_t	aest_birthtime_nsec;+		int64_t		aest_gid;+		int64_t		aest_ino;+		uint32_t	aest_nlink;+		uint64_t	aest_size;+		int64_t		aest_uid;+		/*+		 * Because converting between device codes and+		 * major/minor values is platform-specific and+		 * inherently a bit risky, we only do that conversion+		 * lazily.  That way, we will do a better job of+		 * preserving information in those cases where no+		 * conversion is actually required.+		 */+		int		aest_dev_is_broken_down;+		dev_t		aest_dev;+		dev_t		aest_devmajor;+		dev_t		aest_devminor;+		int		aest_rdev_is_broken_down;+		dev_t		aest_rdev;+		dev_t		aest_rdevmajor;+		dev_t		aest_rdevminor;+	} ae_stat;++	int ae_set; /* bitmap of fields that are currently set */+#define	AE_SET_HARDLINK	1+#define	AE_SET_SYMLINK	2+#define	AE_SET_ATIME	4+#define	AE_SET_CTIME	8+#define	AE_SET_MTIME	16+#define	AE_SET_BIRTHTIME 32+#define	AE_SET_SIZE	64+#define	AE_SET_INO	128+#define	AE_SET_DEV	256++	/*+	 * Use aes here so that we get transparent mbs<->wcs conversions.+	 */+	struct archive_mstring ae_fflags_text;	/* Text fflags per fflagstostr(3) */+	unsigned long ae_fflags_set;		/* Bitmap fflags */+	unsigned long ae_fflags_clear;+	struct archive_mstring ae_gname;		/* Name of owning group */+	struct archive_mstring ae_hardlink;	/* Name of target for hardlink */+	struct archive_mstring ae_pathname;	/* Name of entry */+	struct archive_mstring ae_symlink;		/* symlink contents */+	struct archive_mstring ae_uname;		/* Name of owner */++	/* Not used within libarchive; useful for some clients. */+	struct archive_mstring ae_sourcepath;	/* Path this entry is sourced from. */++#define AE_ENCRYPTION_NONE 0+#define AE_ENCRYPTION_DATA 1+#define AE_ENCRYPTION_METADATA 2+	char encryption;+	+	void *mac_metadata;+	size_t mac_metadata_size;++	/* ACL support. */+	struct archive_acl    acl;++	/* extattr support. */+	struct ae_xattr *xattr_head;+	struct ae_xattr *xattr_p;++	/* sparse support. */+	struct ae_sparse *sparse_head;+	struct ae_sparse *sparse_tail;+	struct ae_sparse *sparse_p;++	/* Miscellaneous. */+	char		 strmode[12];++	/* Symlink type support */+	int ae_symlink_type;+};++#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
+ c/archive_entry_sparse.c view
@@ -0,0 +1,156 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2010-2011 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_entry_private.h"++/*+ * sparse handling+ */++void+archive_entry_sparse_clear(struct archive_entry *entry)+{+	struct ae_sparse *sp;++	while (entry->sparse_head != NULL) {+		sp = entry->sparse_head->next;+		free(entry->sparse_head);+		entry->sparse_head = sp;+	}+	entry->sparse_tail = NULL;+}++void+archive_entry_sparse_add_entry(struct archive_entry *entry,+	la_int64_t offset, la_int64_t length)+{+	struct ae_sparse *sp;++	if (offset < 0 || length < 0)+		/* Invalid value */+		return;+	if (offset > INT64_MAX - length ||+	    offset + length > archive_entry_size(entry))+		/* A value of "length" parameter is too large. */+		return;+	if ((sp = entry->sparse_tail) != NULL) {+		if (sp->offset + sp->length > offset)+			/* Invalid value. */+			return;+		if (sp->offset + sp->length == offset) {+			if (sp->offset + sp->length + length < 0)+				/* A value of "length" parameter is+				 * too large. */+				return;+			/* Expand existing sparse block size. */+			sp->length += length;+			return;+		}+	}++	if ((sp = (struct ae_sparse *)malloc(sizeof(*sp))) == NULL)+		/* XXX Error XXX */+		return;++	sp->offset = offset;+	sp->length = length;+	sp->next = NULL;++	if (entry->sparse_head == NULL)+		entry->sparse_head = entry->sparse_tail = sp;+	else {+		/* Add a new sparse block to the tail of list. */+		if (entry->sparse_tail != NULL)+			entry->sparse_tail->next = sp;+		entry->sparse_tail = sp;+	}+}+++/*+ * returns number of the sparse entries+ */+int+archive_entry_sparse_count(struct archive_entry *entry)+{+	struct ae_sparse *sp;+	int count = 0;++	for (sp = entry->sparse_head; sp != NULL; sp = sp->next)+		count++;++	/*+	 * Sanity check if this entry is exactly sparse.+	 * If amount of sparse blocks is just one and it indicates the whole+	 * file data, we should remove it and return zero.+	 */+	if (count == 1) {+		sp = entry->sparse_head;+		if (sp->offset == 0 &&+		    sp->length >= archive_entry_size(entry)) {+			count = 0;+			archive_entry_sparse_clear(entry);+		}+	}++	return (count);+}++int+archive_entry_sparse_reset(struct archive_entry * entry)+{+	entry->sparse_p = entry->sparse_head;++	return archive_entry_sparse_count(entry);+}++int+archive_entry_sparse_next(struct archive_entry * entry,+	la_int64_t *offset, la_int64_t *length)+{+	if (entry->sparse_p) {+		*offset = entry->sparse_p->offset;+		*length = entry->sparse_p->length;++		entry->sparse_p = entry->sparse_p->next;++		return (ARCHIVE_OK);+	} else {+		*offset = 0;+		*length = 0;+		return (ARCHIVE_WARN);+	}+}++/*+ * end of sparse handling+ */
+ c/archive_entry_stat.c view
@@ -0,0 +1,118 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_stat.c 201100 2009-12-28 03:05:31Z kientzle $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif++#include "archive_entry.h"+#include "archive_entry_private.h"++const struct stat *+archive_entry_stat(struct archive_entry *entry)+{+	struct stat *st;+	if (entry->stat == NULL) {+		entry->stat = calloc(1, sizeof(*st));+		if (entry->stat == NULL)+			return (NULL);+		entry->stat_valid = 0;+	}++	/*+	 * If none of the underlying fields have been changed, we+	 * don't need to regenerate.  In theory, we could use a bitmap+	 * here to flag only those items that have changed, but the+	 * extra complexity probably isn't worth it.  It will be very+	 * rare for anyone to change just one field then request a new+	 * stat structure.+	 */+	if (entry->stat_valid)+		return (entry->stat);++	st = entry->stat;+	/*+	 * Use the public interfaces to extract items, so that+	 * the appropriate conversions get invoked.+	 */+	st->st_atime = archive_entry_atime(entry);+#if HAVE_STRUCT_STAT_ST_BIRTHTIME+	st->st_birthtime = archive_entry_birthtime(entry);+#endif+	st->st_ctime = archive_entry_ctime(entry);+	st->st_mtime = archive_entry_mtime(entry);+	st->st_dev = archive_entry_dev(entry);+	st->st_gid = (gid_t)archive_entry_gid(entry);+	st->st_uid = (uid_t)archive_entry_uid(entry);+	st->st_ino = (ino_t)archive_entry_ino64(entry);+	st->st_nlink = archive_entry_nlink(entry);+	st->st_rdev = archive_entry_rdev(entry);+	st->st_size = (off_t)archive_entry_size(entry);+	st->st_mode = archive_entry_mode(entry);++	/*+	 * On systems that support high-res timestamps, copy that+	 * information into struct stat.+	 */+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC+	st->st_atimespec.tv_nsec = archive_entry_atime_nsec(entry);+	st->st_ctimespec.tv_nsec = archive_entry_ctime_nsec(entry);+	st->st_mtimespec.tv_nsec = archive_entry_mtime_nsec(entry);+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC+	st->st_atim.tv_nsec = archive_entry_atime_nsec(entry);+	st->st_ctim.tv_nsec = archive_entry_ctime_nsec(entry);+	st->st_mtim.tv_nsec = archive_entry_mtime_nsec(entry);+#elif HAVE_STRUCT_STAT_ST_MTIME_N+	st->st_atime_n = archive_entry_atime_nsec(entry);+	st->st_ctime_n = archive_entry_ctime_nsec(entry);+	st->st_mtime_n = archive_entry_mtime_nsec(entry);+#elif HAVE_STRUCT_STAT_ST_UMTIME+	st->st_uatime = archive_entry_atime_nsec(entry) / 1000;+	st->st_uctime = archive_entry_ctime_nsec(entry) / 1000;+	st->st_umtime = archive_entry_mtime_nsec(entry) / 1000;+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC+	st->st_atime_usec = archive_entry_atime_nsec(entry) / 1000;+	st->st_ctime_usec = archive_entry_ctime_nsec(entry) / 1000;+	st->st_mtime_usec = archive_entry_mtime_nsec(entry) / 1000;+#endif+#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC+	st->st_birthtimespec.tv_nsec = archive_entry_birthtime_nsec(entry);+#endif++	/*+	 * TODO: On Linux, store 32 or 64 here depending on whether+	 * the cached stat structure is a stat32 or a stat64.  This+	 * will allow us to support both variants interchangeably.+	 */+	entry->stat_valid = 1;++	return (st);+}
+ c/archive_entry_strmode.c view
@@ -0,0 +1,87 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_strmode.c,v 1.4 2008/06/15 05:14:01 kientzle Exp $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive_entry.h"+#include "archive_entry_private.h"++const char *+archive_entry_strmode(struct archive_entry *entry)+{+	static const mode_t permbits[] =+	    { 0400, 0200, 0100, 0040, 0020, 0010, 0004, 0002, 0001 };+	char *bp = entry->strmode;+	mode_t mode;+	int i;++	/* Fill in a default string, then selectively override. */+	strcpy(bp, "?rwxrwxrwx ");++	mode = archive_entry_mode(entry);+	switch (archive_entry_filetype(entry)) {+	case AE_IFREG:  bp[0] = '-'; break;+	case AE_IFBLK:  bp[0] = 'b'; break;+	case AE_IFCHR:  bp[0] = 'c'; break;+	case AE_IFDIR:  bp[0] = 'd'; break;+	case AE_IFLNK:  bp[0] = 'l'; break;+	case AE_IFSOCK: bp[0] = 's'; break;+	case AE_IFIFO:  bp[0] = 'p'; break;+	default:+		if (archive_entry_hardlink(entry) != NULL) {+			bp[0] = 'h';+			break;+		}+	}++	for (i = 0; i < 9; i++)+		if (!(mode & permbits[i]))+			bp[i+1] = '-';++	if (mode & S_ISUID) {+		if (mode & 0100) bp[3] = 's';+		else bp[3] = 'S';+	}+	if (mode & S_ISGID) {+		if (mode & 0010) bp[6] = 's';+		else bp[6] = 'S';+	}+	if (mode & S_ISVTX) {+		if (mode & 0001) bp[9] = 't';+		else bp[9] = 'T';+	}+	if (archive_entry_acl_types(entry) != 0)+		bp[10] = '+';++	return (bp);+}
+ c/archive_entry_xattr.c view
@@ -0,0 +1,156 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_xattr.c 201096 2009-12-28 02:41:27Z kientzle $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_LINUX_FS_H+#include <linux/fs.h>	/* for Linux file flags */+#endif+/*+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.+ * As the include guards don't agree, the order of include is important.+ */+#ifdef HAVE_LINUX_EXT2_FS_H+#include <linux/ext2_fs.h>	/* for Linux file flags */+#endif+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)+#include <ext2fs/ext2_fs.h>	/* for Linux file flags */+#endif+#include <stddef.h>+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_WCHAR_H+#include <wchar.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_entry_private.h"++/*+ * extended attribute handling+ */++void+archive_entry_xattr_clear(struct archive_entry *entry)+{+	struct ae_xattr	*xp;++	while (entry->xattr_head != NULL) {+		xp = entry->xattr_head->next;+		free(entry->xattr_head->name);+		free(entry->xattr_head->value);+		free(entry->xattr_head);+		entry->xattr_head = xp;+	}++	entry->xattr_head = NULL;+}++void+archive_entry_xattr_add_entry(struct archive_entry *entry,+	const char *name, const void *value, size_t size)+{+	struct ae_xattr	*xp;++	if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL)+		__archive_errx(1, "Out of memory");++	if ((xp->name = strdup(name)) == NULL)+		__archive_errx(1, "Out of memory");++	if ((xp->value = malloc(size)) != NULL) {+		memcpy(xp->value, value, size);+		xp->size = size;+	} else+		xp->size = 0;++	xp->next = entry->xattr_head;+	entry->xattr_head = xp;+}+++/*+ * returns number of the extended attribute entries+ */+int+archive_entry_xattr_count(struct archive_entry *entry)+{+	struct ae_xattr *xp;+	int count = 0;++	for (xp = entry->xattr_head; xp != NULL; xp = xp->next)+		count++;++	return count;+}++int+archive_entry_xattr_reset(struct archive_entry * entry)+{+	entry->xattr_p = entry->xattr_head;++	return archive_entry_xattr_count(entry);+}++int+archive_entry_xattr_next(struct archive_entry * entry,+	const char **name, const void **value, size_t *size)+{+	if (entry->xattr_p) {+		*name = entry->xattr_p->name;+		*value = entry->xattr_p->value;+		*size = entry->xattr_p->size;++		entry->xattr_p = entry->xattr_p->next;++		return (ARCHIVE_OK);+	} else {+		*name = NULL;+		*value = NULL;+		*size = (size_t)0;+		return (ARCHIVE_WARN);+	}+}++/*+ * end of xattr handling+ */
+ c/archive_getdate.c view
@@ -0,0 +1,1165 @@+/*+ * This code is in the public domain and has no copyright.+ *+ * This is a plain C recursive-descent translation of an old+ * public-domain YACC grammar that has been used for parsing dates in+ * very many open-source projects.+ *+ * Since the original authors were generous enough to donate their+ * work to the public domain, I feel compelled to match their+ * generosity.+ *+ * Tim Kientzle, February 2009.+ */++/*+ * Header comment from original getdate.y:+ */++/*+**  Originally written by Steven M. Bellovin <smb@research.att.com> while+**  at the University of North Carolina at Chapel Hill.  Later tweaked by+**  a couple of people on Usenet.  Completely overhauled by Rich $alz+**  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;+**+**  This grammar has 10 shift/reduce conflicts.+**+**  This code is in the public domain and has no copyright.+*/++#include "archive_platform.h"+#ifdef __FreeBSD__+#include <sys/cdefs.h>+__FBSDID("$FreeBSD$");+#endif++#include <ctype.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <time.h>++#define __LIBARCHIVE_BUILD 1+#include "archive_getdate.h"++/* Basic time units. */+#define	EPOCH		1970+#define	MINUTE		(60L)+#define	HOUR		(60L * MINUTE)+#define	DAY		(24L * HOUR)++/* Daylight-savings mode:  on, off, or not yet known. */+enum DSTMODE { DSTon, DSToff, DSTmaybe };+/* Meridian:  am or pm. */+enum { tAM, tPM };+/* Token types returned by nexttoken() */+enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT,+       tUNUMBER, tZONE, tDST };+struct token { int token; time_t value; };++/*+ * Parser state.+ */+struct gdstate {+	struct token *tokenp; /* Pointer to next token. */+	/* HaveXxxx counts how many of this kind of phrase we've seen;+	 * it's a fatal error to have more than one time, zone, day,+	 * or date phrase. */+	int	HaveYear;+	int	HaveMonth;+	int	HaveDay;+	int	HaveWeekDay; /* Day of week */+	int	HaveTime; /* Hour/minute/second */+	int	HaveZone; /* timezone and/or DST info */+	int	HaveRel; /* time offset; we can have more than one */+	/* Absolute time values. */+	time_t	Timezone;  /* Seconds offset from GMT */+	time_t	Day;+	time_t	Hour;+	time_t	Minutes;+	time_t	Month;+	time_t	Seconds;+	time_t	Year;+	/* DST selection */+	enum DSTMODE	DSTmode;+	/* Day of week accounting, e.g., "3rd Tuesday" */+	time_t	DayOrdinal; /* "3" in "3rd Tuesday" */+	time_t	DayNumber; /* "Tuesday" in "3rd Tuesday" */+	/* Relative time values: hour/day/week offsets are measured in+	 * seconds, month/year are counted in months. */+	time_t	RelMonth;+	time_t	RelSeconds;+};++/*+ * A series of functions that recognize certain common time phrases.+ * Each function returns 1 if it managed to make sense of some of the+ * tokens, zero otherwise.+ */++/*+ *  hour:minute or hour:minute:second with optional AM, PM, or numeric+ *  timezone offset+ */+static int+timephrase(struct gdstate *gds)+{+	if (gds->tokenp[0].token == tUNUMBER+	    && gds->tokenp[1].token == ':'+	    && gds->tokenp[2].token == tUNUMBER+	    && gds->tokenp[3].token == ':'+	    && gds->tokenp[4].token == tUNUMBER) {+		/* "12:14:18" or "22:08:07" */+		++gds->HaveTime;+		gds->Hour = gds->tokenp[0].value;+		gds->Minutes = gds->tokenp[2].value;+		gds->Seconds = gds->tokenp[4].value;+		gds->tokenp += 5;+	}+	else if (gds->tokenp[0].token == tUNUMBER+	    && gds->tokenp[1].token == ':'+	    && gds->tokenp[2].token == tUNUMBER) {+		/* "12:14" or "22:08" */+		++gds->HaveTime;+		gds->Hour = gds->tokenp[0].value;+		gds->Minutes = gds->tokenp[2].value;+		gds->Seconds = 0;+		gds->tokenp += 3;+	}+	else if (gds->tokenp[0].token == tUNUMBER+	    && gds->tokenp[1].token == tAMPM) {+		/* "7" is a time if it's followed by "am" or "pm" */+		++gds->HaveTime;+		gds->Hour = gds->tokenp[0].value;+		gds->Minutes = gds->Seconds = 0;+		/* We'll handle the AM/PM below. */+		gds->tokenp += 1;+	} else {+		/* We can't handle this. */+		return 0;+	}++	if (gds->tokenp[0].token == tAMPM) {+		/* "7:12pm", "12:20:13am" */+		if (gds->Hour == 12)+			gds->Hour = 0;+		if (gds->tokenp[0].value == tPM)+			gds->Hour += 12;+		gds->tokenp += 1;+	}+	if (gds->tokenp[0].token == '+'+	    && gds->tokenp[1].token == tUNUMBER) {+		/* "7:14+0700" */+		gds->HaveZone++;+		gds->DSTmode = DSToff;+		gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR+		    + (gds->tokenp[1].value % 100) * MINUTE);+		gds->tokenp += 2;+	}+	if (gds->tokenp[0].token == '-'+	    && gds->tokenp[1].token == tUNUMBER) {+		/* "19:14:12-0530" */+		gds->HaveZone++;+		gds->DSTmode = DSToff;+		gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR+		    + (gds->tokenp[1].value % 100) * MINUTE);+		gds->tokenp += 2;+	}+	return 1;+}++/*+ * Timezone name, possibly including DST.+ */+static int+zonephrase(struct gdstate *gds)+{+	if (gds->tokenp[0].token == tZONE+	    && gds->tokenp[1].token == tDST) {+		gds->HaveZone++;+		gds->Timezone = gds->tokenp[0].value;+		gds->DSTmode = DSTon;+		gds->tokenp += 1;+		return 1;+	}++	if (gds->tokenp[0].token == tZONE) {+		gds->HaveZone++;+		gds->Timezone = gds->tokenp[0].value;+		gds->DSTmode = DSToff;+		gds->tokenp += 1;+		return 1;+	}++	if (gds->tokenp[0].token == tDAYZONE) {+		gds->HaveZone++;+		gds->Timezone = gds->tokenp[0].value;+		gds->DSTmode = DSTon;+		gds->tokenp += 1;+		return 1;+	}+	return 0;+}++/*+ * Year/month/day in various combinations.+ */+static int+datephrase(struct gdstate *gds)+{+	if (gds->tokenp[0].token == tUNUMBER+	    && gds->tokenp[1].token == '/'+	    && gds->tokenp[2].token == tUNUMBER+	    && gds->tokenp[3].token == '/'+	    && gds->tokenp[4].token == tUNUMBER) {+		gds->HaveYear++;+		gds->HaveMonth++;+		gds->HaveDay++;+		if (gds->tokenp[0].value >= 13) {+			/* First number is big:  2004/01/29, 99/02/17 */+			gds->Year = gds->tokenp[0].value;+			gds->Month = gds->tokenp[2].value;+			gds->Day = gds->tokenp[4].value;+		} else if ((gds->tokenp[4].value >= 13)+		    || (gds->tokenp[2].value >= 13)) {+			/* Last number is big:  01/07/98 */+			/* Middle number is big:  01/29/04 */+			gds->Month = gds->tokenp[0].value;+			gds->Day = gds->tokenp[2].value;+			gds->Year = gds->tokenp[4].value;+		} else {+			/* No significant clues: 02/03/04 */+			gds->Month = gds->tokenp[0].value;+			gds->Day = gds->tokenp[2].value;+			gds->Year = gds->tokenp[4].value;+		}+		gds->tokenp += 5;+		return 1;+	}++	if (gds->tokenp[0].token == tUNUMBER+	    && gds->tokenp[1].token == '/'+	    && gds->tokenp[2].token == tUNUMBER) {+		/* "1/15" */+		gds->HaveMonth++;+		gds->HaveDay++;+		gds->Month = gds->tokenp[0].value;+		gds->Day = gds->tokenp[2].value;+		gds->tokenp += 3;+		return 1;+	}++	if (gds->tokenp[0].token == tUNUMBER+	    && gds->tokenp[1].token == '-'+	    && gds->tokenp[2].token == tUNUMBER+	    && gds->tokenp[3].token == '-'+	    && gds->tokenp[4].token == tUNUMBER) {+		/* ISO 8601 format.  yyyy-mm-dd.  */+		gds->HaveYear++;+		gds->HaveMonth++;+		gds->HaveDay++;+		gds->Year = gds->tokenp[0].value;+		gds->Month = gds->tokenp[2].value;+		gds->Day = gds->tokenp[4].value;+		gds->tokenp += 5;+		return 1;+	}++	if (gds->tokenp[0].token == tUNUMBER+	    && gds->tokenp[1].token == '-'+	    && gds->tokenp[2].token == tMONTH+	    && gds->tokenp[3].token == '-'+	    && gds->tokenp[4].token == tUNUMBER) {+		gds->HaveYear++;+		gds->HaveMonth++;+		gds->HaveDay++;+		if (gds->tokenp[0].value > 31) {+			/* e.g. 1992-Jun-17 */+			gds->Year = gds->tokenp[0].value;+			gds->Month = gds->tokenp[2].value;+			gds->Day = gds->tokenp[4].value;+		} else {+			/* e.g. 17-JUN-1992.  */+			gds->Day = gds->tokenp[0].value;+			gds->Month = gds->tokenp[2].value;+			gds->Year = gds->tokenp[4].value;+		}+		gds->tokenp += 5;+		return 1;+	}++	if (gds->tokenp[0].token == tMONTH+	    && gds->tokenp[1].token == tUNUMBER+	    && gds->tokenp[2].token == ','+	    && gds->tokenp[3].token == tUNUMBER) {+		/* "June 17, 2001" */+		gds->HaveYear++;+		gds->HaveMonth++;+		gds->HaveDay++;+		gds->Month = gds->tokenp[0].value;+		gds->Day = gds->tokenp[1].value;+		gds->Year = gds->tokenp[3].value;+		gds->tokenp += 4;+		return 1;+	}++	if (gds->tokenp[0].token == tMONTH+	    && gds->tokenp[1].token == tUNUMBER) {+		/* "May 3" */+		gds->HaveMonth++;+		gds->HaveDay++;+		gds->Month = gds->tokenp[0].value;+		gds->Day = gds->tokenp[1].value;+		gds->tokenp += 2;+		return 1;+	}++	if (gds->tokenp[0].token == tUNUMBER+	    && gds->tokenp[1].token == tMONTH+	    && gds->tokenp[2].token == tUNUMBER) {+		/* "12 Sept 1997" */+		gds->HaveYear++;+		gds->HaveMonth++;+		gds->HaveDay++;+		gds->Day = gds->tokenp[0].value;+		gds->Month = gds->tokenp[1].value;+		gds->Year = gds->tokenp[2].value;+		gds->tokenp += 3;+		return 1;+	}++	if (gds->tokenp[0].token == tUNUMBER+	    && gds->tokenp[1].token == tMONTH) {+		/* "12 Sept" */+		gds->HaveMonth++;+		gds->HaveDay++;+		gds->Day = gds->tokenp[0].value;+		gds->Month = gds->tokenp[1].value;+		gds->tokenp += 2;+		return 1;+	}++	return 0;+}++/*+ * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc.+ */+static int+relunitphrase(struct gdstate *gds)+{+	if (gds->tokenp[0].token == '-'+	    && gds->tokenp[1].token == tUNUMBER+	    && gds->tokenp[2].token == tSEC_UNIT) {+		/* "-3 hours" */+		gds->HaveRel++;+		gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value;+		gds->tokenp += 3;+		return 1;+	}+	if (gds->tokenp[0].token == '+'+	    && gds->tokenp[1].token == tUNUMBER+	    && gds->tokenp[2].token == tSEC_UNIT) {+		/* "+1 minute" */+		gds->HaveRel++;+		gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value;+		gds->tokenp += 3;+		return 1;+	}+	if (gds->tokenp[0].token == tUNUMBER+	    && gds->tokenp[1].token == tSEC_UNIT) {+		/* "1 day" */+		gds->HaveRel++;+		gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value;+		gds->tokenp += 2;+		return 1;+	}+	if (gds->tokenp[0].token == '-'+	    && gds->tokenp[1].token == tUNUMBER+	    && gds->tokenp[2].token == tMONTH_UNIT) {+		/* "-3 months" */+		gds->HaveRel++;+		gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value;+		gds->tokenp += 3;+		return 1;+	}+	if (gds->tokenp[0].token == '+'+	    && gds->tokenp[1].token == tUNUMBER+	    && gds->tokenp[2].token == tMONTH_UNIT) {+		/* "+5 years" */+		gds->HaveRel++;+		gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value;+		gds->tokenp += 3;+		return 1;+	}+	if (gds->tokenp[0].token == tUNUMBER+	    && gds->tokenp[1].token == tMONTH_UNIT) {+		/* "2 years" */+		gds->HaveRel++;+		gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value;+		gds->tokenp += 2;+		return 1;+	}+	if (gds->tokenp[0].token == tSEC_UNIT) {+		/* "now", "tomorrow" */+		gds->HaveRel++;+		gds->RelSeconds += gds->tokenp[0].value;+		gds->tokenp += 1;+		return 1;+	}+	if (gds->tokenp[0].token == tMONTH_UNIT) {+		/* "month" */+		gds->HaveRel++;+		gds->RelMonth += gds->tokenp[0].value;+		gds->tokenp += 1;+		return 1;+	}+	return 0;+}++/*+ * Day of the week specification.+ */+static int+dayphrase(struct gdstate *gds)+{+	if (gds->tokenp[0].token == tDAY) {+		/* "tues", "wednesday," */+		gds->HaveWeekDay++;+		gds->DayOrdinal = 1;+		gds->DayNumber = gds->tokenp[0].value;+		gds->tokenp += 1;+		if (gds->tokenp[0].token == ',')+			gds->tokenp += 1;+		return 1;+	}+	if (gds->tokenp[0].token == tUNUMBER+		&& gds->tokenp[1].token == tDAY) {+		/* "second tues" "3 wed" */+		gds->HaveWeekDay++;+		gds->DayOrdinal = gds->tokenp[0].value;+		gds->DayNumber = gds->tokenp[1].value;+		gds->tokenp += 2;+		return 1;+	}+	return 0;+}++/*+ * Try to match a phrase using one of the above functions.+ * This layer also deals with a couple of generic issues.+ */+static int+phrase(struct gdstate *gds)+{+	if (timephrase(gds))+		return 1;+	if (zonephrase(gds))+		return 1;+	if (datephrase(gds))+		return 1;+	if (dayphrase(gds))+		return 1;+	if (relunitphrase(gds)) {+		if (gds->tokenp[0].token == tAGO) {+			gds->RelSeconds = -gds->RelSeconds;+			gds->RelMonth = -gds->RelMonth;+			gds->tokenp += 1;+		}+		return 1;+	}++	/* Bare numbers sometimes have meaning. */+	if (gds->tokenp[0].token == tUNUMBER) {+		if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) {+			gds->HaveYear++;+			gds->Year = gds->tokenp[0].value;+			gds->tokenp += 1;+			return 1;+		}++		if(gds->tokenp[0].value > 10000) {+			/* "20040301" */+			gds->HaveYear++;+			gds->HaveMonth++;+			gds->HaveDay++;+			gds->Day= (gds->tokenp[0].value)%100;+			gds->Month= (gds->tokenp[0].value/100)%100;+			gds->Year = gds->tokenp[0].value/10000;+			gds->tokenp += 1;+			return 1;+		}++		if (gds->tokenp[0].value < 24) {+			gds->HaveTime++;+			gds->Hour = gds->tokenp[0].value;+			gds->Minutes = 0;+			gds->Seconds = 0;+			gds->tokenp += 1;+			return 1;+		}++		if ((gds->tokenp[0].value / 100 < 24)+		    && (gds->tokenp[0].value % 100 < 60)) {+			/* "513" is same as "5:13" */+			gds->Hour = gds->tokenp[0].value / 100;+			gds->Minutes = gds->tokenp[0].value % 100;+			gds->Seconds = 0;+			gds->tokenp += 1;+			return 1;+		}+	}++	return 0;+}++/*+ * A dictionary of time words.+ */+static struct LEXICON {+	size_t		abbrev;+	const char	*name;+	int		type;+	time_t		value;+} const TimeWords[] = {+	/* am/pm */+	{ 0, "am",		tAMPM,	tAM },+	{ 0, "pm",		tAMPM,	tPM },++	/* Month names. */+	{ 3, "january",		tMONTH,  1 },+	{ 3, "february",	tMONTH,  2 },+	{ 3, "march",		tMONTH,  3 },+	{ 3, "april",		tMONTH,  4 },+	{ 3, "may",		tMONTH,  5 },+	{ 3, "june",		tMONTH,  6 },+	{ 3, "july",		tMONTH,  7 },+	{ 3, "august",		tMONTH,  8 },+	{ 3, "september",	tMONTH,  9 },+	{ 3, "october",		tMONTH, 10 },+	{ 3, "november",	tMONTH, 11 },+	{ 3, "december",	tMONTH, 12 },++	/* Days of the week. */+	{ 2, "sunday",		tDAY, 0 },+	{ 3, "monday",		tDAY, 1 },+	{ 2, "tuesday",		tDAY, 2 },+	{ 3, "wednesday",	tDAY, 3 },+	{ 2, "thursday",	tDAY, 4 },+	{ 2, "friday",		tDAY, 5 },+	{ 2, "saturday",	tDAY, 6 },++	/* Timezones: Offsets are in seconds. */+	{ 0, "gmt",  tZONE,     0*HOUR }, /* Greenwich Mean */+	{ 0, "ut",   tZONE,     0*HOUR }, /* Universal (Coordinated) */+	{ 0, "utc",  tZONE,     0*HOUR },+	{ 0, "wet",  tZONE,     0*HOUR }, /* Western European */+	{ 0, "bst",  tDAYZONE,  0*HOUR }, /* British Summer */+	{ 0, "wat",  tZONE,     1*HOUR }, /* West Africa */+	{ 0, "at",   tZONE,     2*HOUR }, /* Azores */+	/* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */+	/* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/+	{ 0, "nft",  tZONE,     3*HOUR+30*MINUTE }, /* Newfoundland */+	{ 0, "nst",  tZONE,     3*HOUR+30*MINUTE }, /* Newfoundland Standard */+	{ 0, "ndt",  tDAYZONE,  3*HOUR+30*MINUTE }, /* Newfoundland Daylight */+	{ 0, "ast",  tZONE,     4*HOUR }, /* Atlantic Standard */+	{ 0, "adt",  tDAYZONE,  4*HOUR }, /* Atlantic Daylight */+	{ 0, "est",  tZONE,     5*HOUR }, /* Eastern Standard */+	{ 0, "edt",  tDAYZONE,  5*HOUR }, /* Eastern Daylight */+	{ 0, "cst",  tZONE,     6*HOUR }, /* Central Standard */+	{ 0, "cdt",  tDAYZONE,  6*HOUR }, /* Central Daylight */+	{ 0, "mst",  tZONE,     7*HOUR }, /* Mountain Standard */+	{ 0, "mdt",  tDAYZONE,  7*HOUR }, /* Mountain Daylight */+	{ 0, "pst",  tZONE,     8*HOUR }, /* Pacific Standard */+	{ 0, "pdt",  tDAYZONE,  8*HOUR }, /* Pacific Daylight */+	{ 0, "yst",  tZONE,     9*HOUR }, /* Yukon Standard */+	{ 0, "ydt",  tDAYZONE,  9*HOUR }, /* Yukon Daylight */+	{ 0, "hst",  tZONE,     10*HOUR }, /* Hawaii Standard */+	{ 0, "hdt",  tDAYZONE,  10*HOUR }, /* Hawaii Daylight */+	{ 0, "cat",  tZONE,     10*HOUR }, /* Central Alaska */+	{ 0, "ahst", tZONE,     10*HOUR }, /* Alaska-Hawaii Standard */+	{ 0, "nt",   tZONE,     11*HOUR }, /* Nome */+	{ 0, "idlw", tZONE,     12*HOUR }, /* Intl Date Line West */+	{ 0, "cet",  tZONE,     -1*HOUR }, /* Central European */+	{ 0, "met",  tZONE,     -1*HOUR }, /* Middle European */+	{ 0, "mewt", tZONE,     -1*HOUR }, /* Middle European Winter */+	{ 0, "mest", tDAYZONE,  -1*HOUR }, /* Middle European Summer */+	{ 0, "swt",  tZONE,     -1*HOUR }, /* Swedish Winter */+	{ 0, "sst",  tDAYZONE,  -1*HOUR }, /* Swedish Summer */+	{ 0, "fwt",  tZONE,     -1*HOUR }, /* French Winter */+	{ 0, "fst",  tDAYZONE,  -1*HOUR }, /* French Summer */+	{ 0, "eet",  tZONE,     -2*HOUR }, /* Eastern Eur, USSR Zone 1 */+	{ 0, "bt",   tZONE,     -3*HOUR }, /* Baghdad, USSR Zone 2 */+	{ 0, "it",   tZONE,     -3*HOUR-30*MINUTE },/* Iran */+	{ 0, "zp4",  tZONE,     -4*HOUR }, /* USSR Zone 3 */+	{ 0, "zp5",  tZONE,     -5*HOUR }, /* USSR Zone 4 */+	{ 0, "ist",  tZONE,     -5*HOUR-30*MINUTE },/* Indian Standard */+	{ 0, "zp6",  tZONE,     -6*HOUR }, /* USSR Zone 5 */+	/* { 0, "nst",  tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */+	/* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */+	{ 0, "wast", tZONE,     -7*HOUR }, /* West Australian Standard */+	{ 0, "wadt", tDAYZONE,  -7*HOUR }, /* West Australian Daylight */+	{ 0, "jt",   tZONE,     -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/+	{ 0, "cct",  tZONE,     -8*HOUR }, /* China Coast, USSR Zone 7 */+	{ 0, "jst",  tZONE,     -9*HOUR }, /* Japan Std, USSR Zone 8 */+	{ 0, "cast", tZONE,     -9*HOUR-30*MINUTE },/* Ctrl Australian Std */+	{ 0, "cadt", tDAYZONE,  -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */+	{ 0, "east", tZONE,     -10*HOUR }, /* Eastern Australian Std */+	{ 0, "eadt", tDAYZONE,  -10*HOUR }, /* Eastern Australian Daylt */+	{ 0, "gst",  tZONE,     -10*HOUR }, /* Guam Std, USSR Zone 9 */+	{ 0, "nzt",  tZONE,     -12*HOUR }, /* New Zealand */+	{ 0, "nzst", tZONE,     -12*HOUR }, /* New Zealand Standard */+	{ 0, "nzdt", tDAYZONE,  -12*HOUR }, /* New Zealand Daylight */+	{ 0, "idle", tZONE,     -12*HOUR }, /* Intl Date Line East */++	{ 0, "dst",  tDST,		0 },++	/* Time units. */+	{ 4, "years",		tMONTH_UNIT,	12 },+	{ 5, "months",		tMONTH_UNIT,	1 },+	{ 9, "fortnights",	tSEC_UNIT,	14 * DAY },+	{ 4, "weeks",		tSEC_UNIT,	7 * DAY },+	{ 3, "days",		tSEC_UNIT,	DAY },+	{ 4, "hours",		tSEC_UNIT,	HOUR },+	{ 3, "minutes",		tSEC_UNIT,	MINUTE },+	{ 3, "seconds",		tSEC_UNIT,	1 },++	/* Relative-time words. */+	{ 0, "tomorrow",	tSEC_UNIT,	DAY },+	{ 0, "yesterday",	tSEC_UNIT,	-DAY },+	{ 0, "today",		tSEC_UNIT,	0 },+	{ 0, "now",		tSEC_UNIT,	0 },+	{ 0, "last",		tUNUMBER,	-1 },+	{ 0, "this",		tSEC_UNIT,	0 },+	{ 0, "next",		tUNUMBER,	2 },+	{ 0, "first",		tUNUMBER,	1 },+	{ 0, "1st",		tUNUMBER,	1 },+/*	{ 0, "second",		tUNUMBER,	2 }, */+	{ 0, "2nd",		tUNUMBER,	2 },+	{ 0, "third",		tUNUMBER,	3 },+	{ 0, "3rd",		tUNUMBER,	3 },+	{ 0, "fourth",		tUNUMBER,	4 },+	{ 0, "4th",		tUNUMBER,	4 },+	{ 0, "fifth",		tUNUMBER,	5 },+	{ 0, "5th",		tUNUMBER,	5 },+	{ 0, "sixth",		tUNUMBER,	6 },+	{ 0, "seventh",		tUNUMBER,	7 },+	{ 0, "eighth",		tUNUMBER,	8 },+	{ 0, "ninth",		tUNUMBER,	9 },+	{ 0, "tenth",		tUNUMBER,	10 },+	{ 0, "eleventh",	tUNUMBER,	11 },+	{ 0, "twelfth",		tUNUMBER,	12 },+	{ 0, "ago",		tAGO,		1 },++	/* Military timezones. */+	{ 0, "a",	tZONE,	1*HOUR },+	{ 0, "b",	tZONE,	2*HOUR },+	{ 0, "c",	tZONE,	3*HOUR },+	{ 0, "d",	tZONE,	4*HOUR },+	{ 0, "e",	tZONE,	5*HOUR },+	{ 0, "f",	tZONE,	6*HOUR },+	{ 0, "g",	tZONE,	7*HOUR },+	{ 0, "h",	tZONE,	8*HOUR },+	{ 0, "i",	tZONE,	9*HOUR },+	{ 0, "k",	tZONE,	10*HOUR },+	{ 0, "l",	tZONE,	11*HOUR },+	{ 0, "m",	tZONE,	12*HOUR },+	{ 0, "n",	tZONE,	-1*HOUR },+	{ 0, "o",	tZONE,	-2*HOUR },+	{ 0, "p",	tZONE,	-3*HOUR },+	{ 0, "q",	tZONE,	-4*HOUR },+	{ 0, "r",	tZONE,	-5*HOUR },+	{ 0, "s",	tZONE,	-6*HOUR },+	{ 0, "t",	tZONE,	-7*HOUR },+	{ 0, "u",	tZONE,	-8*HOUR },+	{ 0, "v",	tZONE,	-9*HOUR },+	{ 0, "w",	tZONE,	-10*HOUR },+	{ 0, "x",	tZONE,	-11*HOUR },+	{ 0, "y",	tZONE,	-12*HOUR },+	{ 0, "z",	tZONE,	0*HOUR },++	/* End of table. */+	{ 0, NULL,	0,	0 }+};++/*+ * Year is either:+ *  = A number from 0 to 99, which means a year from 1970 to 2069, or+ *  = The actual year (>=100).+ */+static time_t+Convert(time_t Month, time_t Day, time_t Year,+	time_t Hours, time_t Minutes, time_t Seconds,+	time_t Timezone, enum DSTMODE DSTmode)+{+	signed char DaysInMonth[12] = {+		31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31+	};+	time_t		Julian;+	int		i;+	struct tm	*ltime;+#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)+	struct tm	tmbuf;+#endif+#if defined(HAVE__LOCALTIME64_S)+	errno_t		terr;+	__time64_t	tmptime;+#endif++	if (Year < 69)+		Year += 2000;+	else if (Year < 100)+		Year += 1900;+	DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)+	    ? 29 : 28;+	/* Checking for 2038 bogusly assumes that time_t is 32 bits.  But+	   I'm too lazy to try to check for time_t overflow in another way.  */+	if (Year < EPOCH || Year > 2038+	    || Month < 1 || Month > 12+	    /* Lint fluff:  "conversion from long may lose accuracy" */+	    || Day < 1 || Day > DaysInMonth[(int)--Month]+	    || Hours < 0 || Hours > 23+	    || Minutes < 0 || Minutes > 59+	    || Seconds < 0 || Seconds > 59)+		return -1;++	Julian = Day - 1;+	for (i = 0; i < Month; i++)+		Julian += DaysInMonth[i];+	for (i = EPOCH; i < Year; i++)+		Julian += 365 + (i % 4 == 0);+	Julian *= DAY;+	Julian += Timezone;+	Julian += Hours * HOUR + Minutes * MINUTE + Seconds;+#if defined(HAVE_LOCALTIME_R)+	ltime = localtime_r(&Julian, &tmbuf);+#elif defined(HAVE__LOCALTIME64_S)+	tmptime = Julian;+	terr = _localtime64_s(&tmbuf, &tmptime);+	if (terr)+		ltime = NULL;+	else+		ltime = &tmbuf;+#else+	ltime = localtime(&Julian);+#endif+	if (DSTmode == DSTon+	    || (DSTmode == DSTmaybe && ltime->tm_isdst))+		Julian -= HOUR;+	return Julian;+}++static time_t+DSTcorrect(time_t Start, time_t Future)+{+	time_t		StartDay;+	time_t		FutureDay;+	struct tm	*ltime;+#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)+	struct tm	tmbuf;+#endif+#if defined(HAVE__LOCALTIME64_S)+	errno_t		terr;+	__time64_t	tmptime;+#endif++#if defined(HAVE_LOCALTIME_R)+	ltime = localtime_r(&Start, &tmbuf);+#elif defined(HAVE__LOCALTIME64_S)+	tmptime = Start;+	terr = _localtime64_s(&tmbuf, &tmptime);+	if (terr)+		ltime = NULL;+	else+		ltime = &tmbuf;+#else+	ltime = localtime(&Start);+#endif+	StartDay = (ltime->tm_hour + 1) % 24;+#if defined(HAVE_LOCALTIME_R)+	ltime = localtime_r(&Future, &tmbuf);+#elif defined(HAVE__LOCALTIME64_S)+	tmptime = Future;+	terr = _localtime64_s(&tmbuf, &tmptime);+	if (terr)+		ltime = NULL;+	else+		ltime = &tmbuf;+#else+	ltime = localtime(&Future);+#endif+	FutureDay = (ltime->tm_hour + 1) % 24;+	return (Future - Start) + (StartDay - FutureDay) * HOUR;+}+++static time_t+RelativeDate(time_t Start, time_t zone, int dstmode,+    time_t DayOrdinal, time_t DayNumber)+{+	struct tm	*tm;+	time_t	t, now;+#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S)+	struct tm	tmbuf;+#endif+#if defined(HAVE__GMTIME64_S)+	errno_t		terr;+	__time64_t	tmptime;+#endif++	t = Start - zone;+#if defined(HAVE_GMTIME_R)+	tm = gmtime_r(&t, &tmbuf);+#elif defined(HAVE__GMTIME64_S)+	tmptime = t;+	terr = _gmtime64_s(&tmbuf, &tmptime);+	if (terr)+		tm = NULL;+	else+		tm = &tmbuf;+#else+	tm = gmtime(&t);+#endif+	now = Start;+	now += DAY * ((DayNumber - tm->tm_wday + 7) % 7);+	now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);+	if (dstmode == DSTmaybe)+		return DSTcorrect(Start, now);+	return now - Start;+}+++static time_t+RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth)+{+	struct tm	*tm;+	time_t	Month;+	time_t	Year;+#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)+	struct tm	tmbuf;+#endif+#if defined(HAVE__LOCALTIME64_S)+	errno_t		terr;+	__time64_t	tmptime;+#endif++	if (RelMonth == 0)+		return 0;+#if defined(HAVE_LOCALTIME_R)+	tm = localtime_r(&Start, &tmbuf);+#elif defined(HAVE__LOCALTIME64_S)+	tmptime = Start;+	terr = _localtime64_s(&tmbuf, &tmptime);+	if (terr)+		tm = NULL;+	else+		tm = &tmbuf;+#else+	tm = localtime(&Start);+#endif+	Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;+	Year = Month / 12;+	Month = Month % 12 + 1;+	return DSTcorrect(Start,+	    Convert(Month, (time_t)tm->tm_mday, Year,+		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,+		Timezone, DSTmaybe));+}++/*+ * Tokenizer.+ */+static int+nexttoken(const char **in, time_t *value)+{+	char	c;+	char	buff[64];++	for ( ; ; ) {+		while (isspace((unsigned char)**in))+			++*in;++		/* Skip parenthesized comments. */+		if (**in == '(') {+			int Count = 0;+			do {+				c = *(*in)++;+				if (c == '\0')+					return c;+				if (c == '(')+					Count++;+				else if (c == ')')+					Count--;+			} while (Count > 0);+			continue;+		}++		/* Try the next token in the word table first. */+		/* This allows us to match "2nd", for example. */+		{+			const char *src = *in;+			const struct LEXICON *tp;+			unsigned i = 0;++			/* Force to lowercase and strip '.' characters. */+			while (*src != '\0'+			    && (isalnum((unsigned char)*src) || *src == '.')+			    && i < sizeof(buff)-1) {+				if (*src != '.') {+					if (isupper((unsigned char)*src))+						buff[i++] = tolower((unsigned char)*src);+					else+						buff[i++] = *src;+				}+				src++;+			}+			buff[i] = '\0';++			/*+			 * Find the first match.  If the word can be+			 * abbreviated, make sure we match at least+			 * the minimum abbreviation.+			 */+			for (tp = TimeWords; tp->name; tp++) {+				size_t abbrev = tp->abbrev;+				if (abbrev == 0)+					abbrev = strlen(tp->name);+				if (strlen(buff) >= abbrev+				    && strncmp(tp->name, buff, strlen(buff))+				    	== 0) {+					/* Skip over token. */+					*in = src;+					/* Return the match. */+					*value = tp->value;+					return tp->type;+				}+			}+		}++		/*+		 * Not in the word table, maybe it's a number.  Note:+		 * Because '-' and '+' have other special meanings, I+		 * don't deal with signed numbers here.+		 */+		if (isdigit((unsigned char)(c = **in))) {+			for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); )+				*value = 10 * *value + c - '0';+			(*in)--;+			return (tUNUMBER);+		}++		return *(*in)++;+	}+}++#define	TM_YEAR_ORIGIN 1900++/* Yield A - B, measured in seconds.  */+static long+difftm (struct tm *a, struct tm *b)+{+	int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);+	int by = b->tm_year + (TM_YEAR_ORIGIN - 1);+	int days = (+		/* difference in day of year */+		a->tm_yday - b->tm_yday+		/* + intervening leap days */+		+  ((ay >> 2) - (by >> 2))+		-  (ay/100 - by/100)+		+  ((ay/100 >> 2) - (by/100 >> 2))+		/* + difference in years * 365 */+		+  (long)(ay-by) * 365+		);+	return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR+	    + (a->tm_min - b->tm_min) * MINUTE+	    + (a->tm_sec - b->tm_sec));+}++/*+ *+ * The public function.+ *+ * TODO: tokens[] array should be dynamically sized.+ */+time_t+__archive_get_date(time_t now, const char *p)+{+	struct token	tokens[256];+	struct gdstate	_gds;+	struct token	*lasttoken;+	struct gdstate	*gds;+	struct tm	local, *tm;+	struct tm	gmt, *gmt_ptr;+	time_t		Start;+	time_t		tod;+	long		tzone;+#if defined(HAVE__LOCALTIME64_S) || defined(HAVE__GMTIME64_S)+	errno_t		terr;+	__time64_t	tmptime;+#endif++	/* Clear out the parsed token array. */+	memset(tokens, 0, sizeof(tokens));+	/* Initialize the parser state. */+	memset(&_gds, 0, sizeof(_gds));+	gds = &_gds;++	/* Look up the current time. */+#if defined(HAVE_LOCALTIME_R)+	tm = localtime_r(&now, &local);+#elif defined(HAVE__LOCALTIME64_S)+	tmptime = now;+	terr = _localtime64_s(&local, &tmptime);+	if (terr)+		tm = NULL;+	else+		tm = &local;+#else+	memset(&local, 0, sizeof(local));+	tm = localtime(&now);+#endif+	if (tm == NULL)+		return -1;+#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE__LOCALTIME64_S)+	local = *tm;+#endif++	/* Look up UTC if we can and use that to determine the current+	 * timezone offset. */+#if defined(HAVE_GMTIME_R)+	gmt_ptr = gmtime_r(&now, &gmt);+#elif defined(HAVE__GMTIME64_S)+	tmptime = now;+	terr = _gmtime64_s(&gmt, &tmptime);+	if (terr)+		gmt_ptr = NULL;+	else+		gmt_ptr = &gmt;+#else+	memset(&gmt, 0, sizeof(gmt));+	gmt_ptr = gmtime(&now);+	if (gmt_ptr != NULL) {+		/* Copy, in case localtime and gmtime use the same buffer. */+		gmt = *gmt_ptr;+	}+#endif+	if (gmt_ptr != NULL)+		tzone = difftm (&gmt, &local);+	else+		/* This system doesn't understand timezones; fake it. */+		tzone = 0;+	if(local.tm_isdst)+		tzone += HOUR;++	/* Tokenize the input string. */+	lasttoken = tokens;+	while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) {+		++lasttoken;+		if (lasttoken > tokens + 255)+			return -1;+	}+	gds->tokenp = tokens;++	/* Match phrases until we run out of input tokens. */+	while (gds->tokenp < lasttoken) {+		if (!phrase(gds))+			return -1;+	}++	/* Use current local timezone if none was specified. */+	if (!gds->HaveZone) {+		gds->Timezone = tzone;+		gds->DSTmode = DSTmaybe;+	}++	/* If a timezone was specified, use that for generating the default+	 * time components instead of the local timezone. */+	if (gds->HaveZone && gmt_ptr != NULL) {+		now -= gds->Timezone;+#if defined(HAVE_GMTIME_R)+		gmt_ptr = gmtime_r(&now, &gmt);+#elif defined(HAVE__GMTIME64_S)+		tmptime = now;+		terr = _gmtime64_s(&gmt, &tmptime);+		if (terr)+			gmt_ptr = NULL;+		else+			gmt_ptr = &gmt;+#else+		gmt_ptr = gmtime(&now);+#endif+		if (gmt_ptr != NULL)+			local = *gmt_ptr;+		now += gds->Timezone;+	}++	if (!gds->HaveYear)+		gds->Year = local.tm_year + 1900;+	if (!gds->HaveMonth)+		gds->Month = local.tm_mon + 1;+	if (!gds->HaveDay)+		gds->Day = local.tm_mday;+	/* Note: No default for hour/min/sec; a specifier that just+	 * gives date always refers to 00:00 on that date. */++	/* If we saw more than one time, timezone, weekday, year, month,+	 * or day, then give up. */+	if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1+	    || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1)+		return -1;++	/* Compute an absolute time based on whatever absolute information+	 * we collected. */+	if (gds->HaveYear || gds->HaveMonth || gds->HaveDay+	    || gds->HaveTime || gds->HaveWeekDay) {+		Start = Convert(gds->Month, gds->Day, gds->Year,+		    gds->Hour, gds->Minutes, gds->Seconds,+		    gds->Timezone, gds->DSTmode);+		if (Start < 0)+			return -1;+	} else {+		Start = now;+		if (!gds->HaveRel)+			Start -= local.tm_hour * HOUR + local.tm_min * MINUTE+			    + local.tm_sec;+	}++	/* Add the relative offset. */+	Start += gds->RelSeconds;+	Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth);++	/* Adjust for day-of-week offsets. */+	if (gds->HaveWeekDay+	    && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) {+		tod = RelativeDate(Start, gds->Timezone,+		    gds->DSTmode, gds->DayOrdinal, gds->DayNumber);+		Start += tod;+	}++	/* -1 is an error indicator, so return 0 instead of -1 if+	 * that's the actual time. */+	return Start == -1 ? 0 : Start;+}+++#if	defined(TEST)++/* ARGSUSED */+int+main(int argc, char **argv)+{+    time_t	d;+    time_t	now = time(NULL);++    while (*++argv != NULL) {+	    (void)printf("Input: %s\n", *argv);+	    d = get_date(now, *argv);+	    if (d == -1)+		    (void)printf("Bad format - couldn't convert.\n");+	    else+		    (void)printf("Output: %s\n", ctime(&d));+    }+    exit(0);+    /* NOTREACHED */+}+#endif	/* defined(TEST) */
+ c/archive_getdate.h view
@@ -0,0 +1,39 @@+/*-+ * Copyright (c) 2003-2015 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ */++#ifndef ARCHIVE_GETDATE_H_INCLUDED+#define ARCHIVE_GETDATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++#include <time.h>++time_t __archive_get_date(time_t now, const char *);++#endif
+ c/archive_hmac.c view
@@ -0,0 +1,305 @@+/*-+* Copyright (c) 2014 Michihiro NAKAJIMA+* All rights reserved.+*+* Redistribution and use in source and binary forms, with or without+* modification, are permitted provided that the following conditions+* are met:+* 1. Redistributions of source code must retain the above copyright+*    notice, this list of conditions and the following disclaimer.+* 2. Redistributions in binary form must reproduce the above copyright+*    notice, this list of conditions and the following disclaimer in the+*    documentation and/or other materials provided with the distribution.+*+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*/++#include "archive_platform.h"++#ifdef HAVE_STRING_H+#include <string.h>+#endif+#include "archive.h"+#include "archive_hmac_private.h"++/*+ * On systems that do not support any recognized crypto libraries,+ * the archive_hmac.c file is expected to define no usable symbols.+ *+ * But some compilers and linkers choke on empty object files, so+ * define a public symbol that will always exist.  This could+ * be removed someday if this file gains another always-present+ * symbol definition.+ */+int __libarchive_hmac_build_hack(void) {+	return 0;+}+++#ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto++static int+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)+{+	CCHmacInit(ctx, kCCHmacAlgSHA1, key, key_len);+	return 0;+}++static void+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,+    size_t data_len)+{+	CCHmacUpdate(ctx, data, data_len);+}++static void+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)+{+	CCHmacFinal(ctx, out);+	*out_len = 20;+}++static void+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)+{+	memset(ctx, 0, sizeof(*ctx));+}++#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)++#ifndef BCRYPT_HASH_REUSABLE_FLAG+# define BCRYPT_HASH_REUSABLE_FLAG 0x00000020+#endif++static int+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)+{+#ifdef __GNUC__+#pragma GCC diagnostic ignored "-Wcast-qual"+#endif+	BCRYPT_ALG_HANDLE hAlg;+	BCRYPT_HASH_HANDLE hHash;+	DWORD hash_len;+	PBYTE hash;+	ULONG result;+	NTSTATUS status;++	ctx->hAlg = NULL;+	status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM,+		MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG);+	if (!BCRYPT_SUCCESS(status))+		return -1;+	status = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PUCHAR)&hash_len,+		sizeof(hash_len), &result, 0);+	if (!BCRYPT_SUCCESS(status)) {+		BCryptCloseAlgorithmProvider(hAlg, 0);+		return -1;+	}+	hash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, hash_len);+	if (hash == NULL) {+		BCryptCloseAlgorithmProvider(hAlg, 0);+		return -1;+	}+	status = BCryptCreateHash(hAlg, &hHash, NULL, 0,+		(PUCHAR)key, (ULONG)key_len, BCRYPT_HASH_REUSABLE_FLAG);+	if (!BCRYPT_SUCCESS(status)) {+		BCryptCloseAlgorithmProvider(hAlg, 0);+		HeapFree(GetProcessHeap(), 0, hash);+		return -1;+	}++	ctx->hAlg = hAlg;+	ctx->hHash = hHash;+	ctx->hash_len = hash_len;+	ctx->hash = hash;++	return 0;+}++static void+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,+	size_t data_len)+{+	BCryptHashData(ctx->hHash, (PUCHAR)(uintptr_t)data, (ULONG)data_len, 0);+}++static void+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)+{+	BCryptFinishHash(ctx->hHash, ctx->hash, ctx->hash_len, 0);+	if (ctx->hash_len == *out_len)+		memcpy(out, ctx->hash, *out_len);+}++static void+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)+{+	if (ctx->hAlg != NULL) {+		BCryptCloseAlgorithmProvider(ctx->hAlg, 0);+		HeapFree(GetProcessHeap(), 0, ctx->hash);+		ctx->hAlg = NULL;+	}+}++#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H)++static int+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)+{+        const mbedtls_md_info_t *info;+        int ret;++        mbedtls_md_init(ctx);+        info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);+        if (info == NULL) {+                mbedtls_md_free(ctx);+                return (-1);+        }+        ret = mbedtls_md_setup(ctx, info, 1);+        if (ret != 0) {+                mbedtls_md_free(ctx);+                return (-1);+        }+	ret = mbedtls_md_hmac_starts(ctx, key, key_len);+	if (ret != 0) {+		mbedtls_md_free(ctx);+		return (-1);+	}+	return 0;+}++static void+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,+    size_t data_len)+{+	mbedtls_md_hmac_update(ctx, data, data_len);+}++static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)+{+	(void)out_len;	/* UNUSED */++	mbedtls_md_hmac_finish(ctx, out);+}++static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)+{+	mbedtls_md_free(ctx);+	memset(ctx, 0, sizeof(*ctx));+}++#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)++static int+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)+{+	hmac_sha1_set_key(ctx, key_len, key);+	return 0;+}++static void+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,+    size_t data_len)+{+	hmac_sha1_update(ctx, data_len, data);+}++static void+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)+{+	hmac_sha1_digest(ctx, (unsigned)*out_len, out);+}++static void+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)+{+	memset(ctx, 0, sizeof(*ctx));+}++#elif defined(HAVE_LIBCRYPTO)++static int+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)+{+	*ctx = HMAC_CTX_new();+	if (*ctx == NULL)+		return -1;+	HMAC_Init_ex(*ctx, key, key_len, EVP_sha1(), NULL);+	return 0;+}++static void+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,+    size_t data_len)+{+	HMAC_Update(*ctx, data, data_len);+}++static void+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)+{+	unsigned int len = (unsigned int)*out_len;++	HMAC_Final(*ctx, out, &len);+	*out_len = len;+}++static void+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)+{+	HMAC_CTX_free(*ctx);+	*ctx = NULL;+}++#else++/* Stub */+static int+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)+{+	(void)ctx;/* UNUSED */+	(void)key;/* UNUSED */+	(void)key_len;/* UNUSED */+	return -1;+}++static void+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,+    size_t data_len)+{+	(void)ctx;/* UNUSED */+	(void)data;/* UNUSED */+	(void)data_len;/* UNUSED */+}++static void+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)+{+	(void)ctx;/* UNUSED */+	(void)out;/* UNUSED */+	(void)out_len;/* UNUSED */+}++static void+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)+{+	(void)ctx;/* UNUSED */+}++#endif++const struct archive_hmac __archive_hmac = {+	&__hmac_sha1_init,+	&__hmac_sha1_update,+	&__hmac_sha1_final,+	&__hmac_sha1_cleanup,+};
+ c/archive_hmac_private.h view
@@ -0,0 +1,110 @@+/*-+* Copyright (c) 2014 Michihiro NAKAJIMA+* All rights reserved.+*+* Redistribution and use in source and binary forms, with or without+* modification, are permitted provided that the following conditions+* are met:+* 1. Redistributions of source code must retain the above copyright+*    notice, this list of conditions and the following disclaimer.+* 2. Redistributions in binary form must reproduce the above copyright+*    notice, this list of conditions and the following disclaimer in the+*    documentation and/or other materials provided with the distribution.+*+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*/++#ifndef ARCHIVE_HMAC_PRIVATE_H_INCLUDED+#define ARCHIVE_HMAC_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif+/*+ * On systems that do not support any recognized crypto libraries,+ * the archive_hmac.c file is expected to define no usable symbols.+ *+ * But some compilers and linkers choke on empty object files, so+ * define a public symbol that will always exist.  This could+ * be removed someday if this file gains another always-present+ * symbol definition.+ */+int __libarchive_hmac_build_hack(void);++#ifdef __APPLE__+# include <AvailabilityMacros.h>+# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060+#  define ARCHIVE_HMAC_USE_Apple_CommonCrypto+# endif+#endif++#ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto+#include <CommonCrypto/CommonHMAC.h>++typedef	CCHmacContext archive_hmac_sha1_ctx;++#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)+#include <bcrypt.h>++typedef struct {+	BCRYPT_ALG_HANDLE	hAlg;+	BCRYPT_HASH_HANDLE	hHash;+	DWORD				hash_len;+	PBYTE				hash;++} archive_hmac_sha1_ctx;++#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H)+#include <mbedtls/md.h>++typedef mbedtls_md_context_t archive_hmac_sha1_ctx;++#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)+#include <nettle/hmac.h>++typedef	struct hmac_sha1_ctx archive_hmac_sha1_ctx;++#elif defined(HAVE_LIBCRYPTO)+#include "archive_openssl_hmac_private.h"++typedef	HMAC_CTX* archive_hmac_sha1_ctx;++#else++typedef int archive_hmac_sha1_ctx;++#endif+++/* HMAC */+#define archive_hmac_sha1_init(ctx, key, key_len)\+	__archive_hmac.__hmac_sha1_init(ctx, key, key_len)+#define archive_hmac_sha1_update(ctx, data, data_len)\+	__archive_hmac.__hmac_sha1_update(ctx, data, data_len)+#define archive_hmac_sha1_final(ctx, out, out_len)\+  	__archive_hmac.__hmac_sha1_final(ctx, out, out_len)+#define archive_hmac_sha1_cleanup(ctx)\+	__archive_hmac.__hmac_sha1_cleanup(ctx)+++struct archive_hmac {+	/* HMAC */+	int (*__hmac_sha1_init)(archive_hmac_sha1_ctx *, const uint8_t *,+		size_t);+	void (*__hmac_sha1_update)(archive_hmac_sha1_ctx *, const uint8_t *,+		size_t);+	void (*__hmac_sha1_final)(archive_hmac_sha1_ctx *, uint8_t *, size_t *);+	void (*__hmac_sha1_cleanup)(archive_hmac_sha1_ctx *);+};++extern const struct archive_hmac __archive_hmac;+#endif /* ARCHIVE_HMAC_PRIVATE_H_INCLUDED */
+ c/archive_match.c view
@@ -0,0 +1,1875 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_entry.h"+#include "archive_getdate.h"+#include "archive_pathmatch.h"+#include "archive_rb.h"+#include "archive_string.h"++struct match {+	struct match		*next;+	int			 matches;+	struct archive_mstring	 pattern;+};++struct match_list {+	struct match		*first;+	struct match		**last;+	int			 count;+	int			 unmatched_count;+	struct match		*unmatched_next;+	int			 unmatched_eof;+};++struct match_file {+	struct archive_rb_node	 node;+	struct match_file	*next;+	struct archive_mstring	 pathname;+	int			 flag;+	time_t			 mtime_sec;+	long			 mtime_nsec;+	time_t			 ctime_sec;+	long			 ctime_nsec;+};++struct entry_list {+	struct match_file	*first;+	struct match_file	**last;+	int			 count;+};++struct id_array {+	size_t			 size;/* Allocated size */+	size_t			 count;+	int64_t			*ids;+};++#define PATTERN_IS_SET		1+#define TIME_IS_SET		2+#define ID_IS_SET		4++struct archive_match {+	struct archive		 archive;++	/* exclusion/inclusion set flag. */+	int			 setflag;++	/* Recursively include directory content? */+	int			 recursive_include;++	/*+	 * Matching filename patterns.+	 */+	struct match_list	 exclusions;+	struct match_list	 inclusions;++	/*+	 * Matching time stamps.+	 */+	time_t			 now;+	int			 newer_mtime_filter;+	time_t			 newer_mtime_sec;+	long			 newer_mtime_nsec;+	int			 newer_ctime_filter;+	time_t			 newer_ctime_sec;+	long			 newer_ctime_nsec;+	int			 older_mtime_filter;+	time_t			 older_mtime_sec;+	long			 older_mtime_nsec;+	int			 older_ctime_filter;+	time_t			 older_ctime_sec;+	long			 older_ctime_nsec;+	/*+	 * Matching time stamps with its filename.+	 */+	struct archive_rb_tree	 exclusion_tree;+	struct entry_list 	 exclusion_entry_list;++	/*+	 * Matching file owners.+	 */+	struct id_array 	 inclusion_uids;+	struct id_array 	 inclusion_gids;+	struct match_list	 inclusion_unames;+	struct match_list	 inclusion_gnames;+};++static int	add_pattern_from_file(struct archive_match *,+		    struct match_list *, int, const void *, int);+static int	add_entry(struct archive_match *, int,+		    struct archive_entry *);+static int	add_owner_id(struct archive_match *, struct id_array *,+		    int64_t);+static int	add_owner_name(struct archive_match *, struct match_list *,+		    int, const void *);+static int	add_pattern_mbs(struct archive_match *, struct match_list *,+		    const char *);+static int	add_pattern_wcs(struct archive_match *, struct match_list *,+		    const wchar_t *);+static int	cmp_key_mbs(const struct archive_rb_node *, const void *);+static int	cmp_key_wcs(const struct archive_rb_node *, const void *);+static int	cmp_node_mbs(const struct archive_rb_node *,+		    const struct archive_rb_node *);+static int	cmp_node_wcs(const struct archive_rb_node *,+		    const struct archive_rb_node *);+static void	entry_list_add(struct entry_list *, struct match_file *);+static void	entry_list_free(struct entry_list *);+static void	entry_list_init(struct entry_list *);+static int	error_nomem(struct archive_match *);+static void	match_list_add(struct match_list *, struct match *);+static void	match_list_free(struct match_list *);+static void	match_list_init(struct match_list *);+static int	match_list_unmatched_inclusions_next(struct archive_match *,+		    struct match_list *, int, const void **);+static int	match_owner_id(struct id_array *, int64_t);+#if !defined(_WIN32) || defined(__CYGWIN__)+static int	match_owner_name_mbs(struct archive_match *,+		    struct match_list *, const char *);+#else+static int	match_owner_name_wcs(struct archive_match *,+		    struct match_list *, const wchar_t *);+#endif+static int	match_path_exclusion(struct archive_match *,+		    struct match *, int, const void *);+static int	match_path_inclusion(struct archive_match *,+		    struct match *, int, const void *);+static int	owner_excluded(struct archive_match *,+		    struct archive_entry *);+static int	path_excluded(struct archive_match *, int, const void *);+static int	set_timefilter(struct archive_match *, int, time_t, long,+		    time_t, long);+static int	set_timefilter_pathname_mbs(struct archive_match *,+		    int, const char *);+static int	set_timefilter_pathname_wcs(struct archive_match *,+		    int, const wchar_t *);+static int	set_timefilter_date(struct archive_match *, int, const char *);+static int	set_timefilter_date_w(struct archive_match *, int,+		    const wchar_t *);+static int	time_excluded(struct archive_match *,+		    struct archive_entry *);+static int	validate_time_flag(struct archive *, int, const char *);++#define get_date __archive_get_date++static const struct archive_rb_tree_ops rb_ops_mbs = {+	cmp_node_mbs, cmp_key_mbs+};++static const struct archive_rb_tree_ops rb_ops_wcs = {+	cmp_node_wcs, cmp_key_wcs+};++/*+ * The matching logic here needs to be re-thought.  I started out to+ * try to mimic gtar's matching logic, but it's not entirely+ * consistent.  In particular 'tar -t' and 'tar -x' interpret patterns+ * on the command line as anchored, but --exclude doesn't.+ */++static int+error_nomem(struct archive_match *a)+{+	archive_set_error(&(a->archive), ENOMEM, "No memory");+	a->archive.state = ARCHIVE_STATE_FATAL;+	return (ARCHIVE_FATAL);+}++/*+ * Create an ARCHIVE_MATCH object.+ */+struct archive *+archive_match_new(void)+{+	struct archive_match *a;++	a = (struct archive_match *)calloc(1, sizeof(*a));+	if (a == NULL)+		return (NULL);+	a->archive.magic = ARCHIVE_MATCH_MAGIC;+	a->archive.state = ARCHIVE_STATE_NEW;+	a->recursive_include = 1;+	match_list_init(&(a->inclusions));+	match_list_init(&(a->exclusions));+	__archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs);+	entry_list_init(&(a->exclusion_entry_list));+	match_list_init(&(a->inclusion_unames));+	match_list_init(&(a->inclusion_gnames));+	time(&a->now);+	return (&(a->archive));+}++/*+ * Free an ARCHIVE_MATCH object.+ */+int+archive_match_free(struct archive *_a)+{+	struct archive_match *a;++	if (_a == NULL)+		return (ARCHIVE_OK);+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_match_free");+	a = (struct archive_match *)_a;+	match_list_free(&(a->inclusions));+	match_list_free(&(a->exclusions));+	entry_list_free(&(a->exclusion_entry_list));+	free(a->inclusion_uids.ids);+	free(a->inclusion_gids.ids);+	match_list_free(&(a->inclusion_unames));+	match_list_free(&(a->inclusion_gnames));+	free(a);+	return (ARCHIVE_OK);+}++/*+ * Convenience function to perform all exclusion tests.+ *+ * Returns 1 if archive entry is excluded.+ * Returns 0 if archive entry is not excluded.+ * Returns <0 if something error happened.+ */+int+archive_match_excluded(struct archive *_a, struct archive_entry *entry)+{+	struct archive_match *a;+	int r;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_excluded_ae");++	a = (struct archive_match *)_a;+	if (entry == NULL) {+		archive_set_error(&(a->archive), EINVAL, "entry is NULL");+		return (ARCHIVE_FAILED);+	}++	r = 0;+	if (a->setflag & PATTERN_IS_SET) {+#if defined(_WIN32) && !defined(__CYGWIN__)+		r = path_excluded(a, 0, archive_entry_pathname_w(entry));+#else+		r = path_excluded(a, 1, archive_entry_pathname(entry));+#endif+		if (r != 0)+			return (r);+	}++	if (a->setflag & TIME_IS_SET) {+		r = time_excluded(a, entry);+		if (r != 0)+			return (r);+	}++	if (a->setflag & ID_IS_SET)+		r = owner_excluded(a, entry);+	return (r);+}++/*+ * Utility functions to manage exclusion/inclusion patterns+ */++int+archive_match_exclude_pattern(struct archive *_a, const char *pattern)+{+	struct archive_match *a;+	int r;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern");+	a = (struct archive_match *)_a;++	if (pattern == NULL || *pattern == '\0') {+		archive_set_error(&(a->archive), EINVAL, "pattern is empty");+		return (ARCHIVE_FAILED);+	}+	if ((r = add_pattern_mbs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)+		return (r);+	return (ARCHIVE_OK);+}++int+archive_match_exclude_pattern_w(struct archive *_a, const wchar_t *pattern)+{+	struct archive_match *a;+	int r;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_w");+	a = (struct archive_match *)_a;++	if (pattern == NULL || *pattern == L'\0') {+		archive_set_error(&(a->archive), EINVAL, "pattern is empty");+		return (ARCHIVE_FAILED);+	}+	if ((r = add_pattern_wcs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)+		return (r);+	return (ARCHIVE_OK);+}++int+archive_match_exclude_pattern_from_file(struct archive *_a,+    const char *pathname, int nullSeparator)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file");+	a = (struct archive_match *)_a;++	return add_pattern_from_file(a, &(a->exclusions), 1, pathname,+		nullSeparator);+}++int+archive_match_exclude_pattern_from_file_w(struct archive *_a,+    const wchar_t *pathname, int nullSeparator)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file_w");+	a = (struct archive_match *)_a;++	return add_pattern_from_file(a, &(a->exclusions), 0, pathname,+		nullSeparator);+}++int+archive_match_include_pattern(struct archive *_a, const char *pattern)+{+	struct archive_match *a;+	int r;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_include_pattern");+	a = (struct archive_match *)_a;++	if (pattern == NULL || *pattern == '\0') {+		archive_set_error(&(a->archive), EINVAL, "pattern is empty");+		return (ARCHIVE_FAILED);+	}+	if ((r = add_pattern_mbs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)+		return (r);+	return (ARCHIVE_OK);+}++int+archive_match_include_pattern_w(struct archive *_a, const wchar_t *pattern)+{+	struct archive_match *a;+	int r;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_w");+	a = (struct archive_match *)_a;++	if (pattern == NULL || *pattern == L'\0') {+		archive_set_error(&(a->archive), EINVAL, "pattern is empty");+		return (ARCHIVE_FAILED);+	}+	if ((r = add_pattern_wcs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)+		return (r);+	return (ARCHIVE_OK);+}++int+archive_match_include_pattern_from_file(struct archive *_a,+    const char *pathname, int nullSeparator)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file");+	a = (struct archive_match *)_a;++	return add_pattern_from_file(a, &(a->inclusions), 1, pathname,+		nullSeparator);+}++int+archive_match_include_pattern_from_file_w(struct archive *_a,+    const wchar_t *pathname, int nullSeparator)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file_w");+	a = (struct archive_match *)_a;++	return add_pattern_from_file(a, &(a->inclusions), 0, pathname,+		nullSeparator);+}++/*+ * Test functions for pathname patterns.+ *+ * Returns 1 if archive entry is excluded.+ * Returns 0 if archive entry is not excluded.+ * Returns <0 if something error happened.+ */+int+archive_match_path_excluded(struct archive *_a,+    struct archive_entry *entry)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_path_excluded");++	a = (struct archive_match *)_a;+	if (entry == NULL) {+		archive_set_error(&(a->archive), EINVAL, "entry is NULL");+		return (ARCHIVE_FAILED);+	}++	/* If we don't have exclusion/inclusion pattern set at all,+	 * the entry is always not excluded. */+	if ((a->setflag & PATTERN_IS_SET) == 0)+		return (0);+#if defined(_WIN32) && !defined(__CYGWIN__)+	return (path_excluded(a, 0, archive_entry_pathname_w(entry)));+#else+	return (path_excluded(a, 1, archive_entry_pathname(entry)));+#endif+}++/*+ * When recursive inclusion of directory content is enabled,+ * an inclusion pattern that matches a directory will also+ * include everything beneath that directory. Enabled by default.+ *+ * For compatibility with GNU tar, exclusion patterns always+ * match if a subset of the full patch matches (i.e., they are+ * are not rooted at the beginning of the path) and thus there+ * is no corresponding non-recursive exclusion mode.+ */+int+archive_match_set_inclusion_recursion(struct archive *_a, int enabled)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion");+	a = (struct archive_match *)_a;+	a->recursive_include = enabled;+	return (ARCHIVE_OK);+}++/*+ * Utility functions to get statistic information for inclusion patterns.+ */+int+archive_match_path_unmatched_inclusions(struct archive *_a)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions");+	a = (struct archive_match *)_a;++	return (a->inclusions.unmatched_count);+}++int+archive_match_path_unmatched_inclusions_next(struct archive *_a,+    const char **_p)+{+	struct archive_match *a;+	const void *v;+	int r;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next");+	a = (struct archive_match *)_a;++	r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 1, &v);+	*_p = (const char *)v;+	return (r);+}++int+archive_match_path_unmatched_inclusions_next_w(struct archive *_a,+    const wchar_t **_p)+{+	struct archive_match *a;+	const void *v;+	int r;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next_w");+	a = (struct archive_match *)_a;++	r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 0, &v);+	*_p = (const wchar_t *)v;+	return (r);+}++/*+ * Add inclusion/exclusion patterns.+ */+static int+add_pattern_mbs(struct archive_match *a, struct match_list *list,+    const char *pattern)+{+	struct match *match;+	size_t len;++	match = calloc(1, sizeof(*match));+	if (match == NULL)+		return (error_nomem(a));+	/* Both "foo/" and "foo" should match "foo/bar". */+	len = strlen(pattern);+	if (len && pattern[len - 1] == '/')+		--len;+	archive_mstring_copy_mbs_len(&(match->pattern), pattern, len);+	match_list_add(list, match);+	a->setflag |= PATTERN_IS_SET;+	return (ARCHIVE_OK);+}++static int+add_pattern_wcs(struct archive_match *a, struct match_list *list,+    const wchar_t *pattern)+{+	struct match *match;+	size_t len;++	match = calloc(1, sizeof(*match));+	if (match == NULL)+		return (error_nomem(a));+	/* Both "foo/" and "foo" should match "foo/bar". */+	len = wcslen(pattern);+	if (len && pattern[len - 1] == L'/')+		--len;+	archive_mstring_copy_wcs_len(&(match->pattern), pattern, len);+	match_list_add(list, match);+	a->setflag |= PATTERN_IS_SET;+	return (ARCHIVE_OK);+}++static int+add_pattern_from_file(struct archive_match *a, struct match_list *mlist,+    int mbs, const void *pathname, int nullSeparator)+{+	struct archive *ar;+	struct archive_entry *ae;+	struct archive_string as;+	const void *buff;+	size_t size;+	int64_t offset;+	int r;++	ar = archive_read_new(); +	if (ar == NULL) {+		archive_set_error(&(a->archive), ENOMEM, "No memory");+		return (ARCHIVE_FATAL);+	}+	r = archive_read_support_format_raw(ar);+	r = archive_read_support_format_empty(ar);+	if (r != ARCHIVE_OK) {+		archive_copy_error(&(a->archive), ar);+		archive_read_free(ar);+		return (r);+	}+	if (mbs)+		r = archive_read_open_filename(ar, pathname, 512*20);+	else+		r = archive_read_open_filename_w(ar, pathname, 512*20);+	if (r != ARCHIVE_OK) {+		archive_copy_error(&(a->archive), ar);+		archive_read_free(ar);+		return (r);+	}+	r = archive_read_next_header(ar, &ae);+	if (r != ARCHIVE_OK) {+		archive_read_free(ar);+		if (r == ARCHIVE_EOF) {+			return (ARCHIVE_OK);+		} else {+			archive_copy_error(&(a->archive), ar);+			return (r);+		}+	}++	archive_string_init(&as);++	while ((r = archive_read_data_block(ar, &buff, &size, &offset))+	    == ARCHIVE_OK) {+		const char *b = (const char *)buff;++		while (size) {+			const char *s = (const char *)b;+			size_t length = 0;+			int found_separator = 0;++			while (length < size) {+				if (nullSeparator) {+					if (*b == '\0') {+						found_separator = 1;+						break;+					}+				} else {+			            	if (*b == 0x0d || *b == 0x0a) {+						found_separator = 1;+						break;+					}+				}+				b++;+				length++;+			}+			if (!found_separator) {+				archive_strncat(&as, s, length);+				/* Read next data block. */+				break;+			}+			b++;+			size -= length + 1;+			archive_strncat(&as, s, length);++			/* If the line is not empty, add the pattern. */+			if (archive_strlen(&as) > 0) {+				/* Add pattern. */+				r = add_pattern_mbs(a, mlist, as.s);+				if (r != ARCHIVE_OK) {+					archive_read_free(ar);+					archive_string_free(&as);+					return (r);+				}+				archive_string_empty(&as);+			}+		}+	}++	/* If an error occurred, report it immediately. */+	if (r < ARCHIVE_OK) {+		archive_copy_error(&(a->archive), ar);+		archive_read_free(ar);+		archive_string_free(&as);+		return (r);+	}++	/* If the line is not empty, add the pattern. */+	if (r == ARCHIVE_EOF && archive_strlen(&as) > 0) {+		/* Add pattern. */+		r = add_pattern_mbs(a, mlist, as.s);+		if (r != ARCHIVE_OK) {+			archive_read_free(ar);+			archive_string_free(&as);+			return (r);+		}+	}+	archive_read_free(ar);+	archive_string_free(&as);+	return (ARCHIVE_OK);+}++/*+ * Test if pathname is excluded by inclusion/exclusion patterns.+ */+static int+path_excluded(struct archive_match *a, int mbs, const void *pathname)+{+	struct match *match;+	struct match *matched;+	int r;++	if (a == NULL)+		return (0);++	/* Mark off any unmatched inclusions. */+	/* In particular, if a filename does appear in the archive and+	 * is explicitly included and excluded, then we don't report+	 * it as missing even though we don't extract it.+	 */+	matched = NULL;+	for (match = a->inclusions.first; match != NULL;+	    match = match->next){+		if (match->matches == 0 &&+		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {+			if (r < 0)+				return (r);+			a->inclusions.unmatched_count--;+			match->matches++;+			matched = match;+		}+	}++	/* Exclusions take priority */+	for (match = a->exclusions.first; match != NULL;+	    match = match->next){+		r = match_path_exclusion(a, match, mbs, pathname);+		if (r)+			return (r);+	}++	/* It's not excluded and we found an inclusion above, so it's+	 * included. */+	if (matched != NULL)+		return (0);+++	/* We didn't find an unmatched inclusion, check the remaining ones. */+	for (match = a->inclusions.first; match != NULL;+	    match = match->next){+		/* We looked at previously-unmatched inclusions already. */+		if (match->matches > 0 &&+		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {+			if (r < 0)+				return (r);+			match->matches++;+			return (0);+		}+	}++	/* If there were inclusions, default is to exclude. */+	if (a->inclusions.first != NULL)+	    return (1);++	/* No explicit inclusions, default is to match. */+	return (0);+}++/*+ * This is a little odd, but it matches the default behavior of+ * gtar.  In particular, 'a*b' will match 'foo/a1111/222b/bar'+ *+ */+static int+match_path_exclusion(struct archive_match *a, struct match *m,+    int mbs, const void *pn)+{+	int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;+	int r;++	if (mbs) {+		const char *p;+		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);+		if (r == 0)+			return (archive_pathmatch(p, (const char *)pn, flag));+	} else {+		const wchar_t *p;+		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);+		if (r == 0)+			return (archive_pathmatch_w(p, (const wchar_t *)pn,+				flag));+	}+	if (errno == ENOMEM)+		return (error_nomem(a));+	return (0);+}++/*+ * Again, mimic gtar:  inclusions are always anchored (have to match+ * the beginning of the path) even though exclusions are not anchored.+ */+static int+match_path_inclusion(struct archive_match *a, struct match *m,+    int mbs, const void *pn)+{+	/* Recursive operation requires only a prefix match. */+	int flag = a->recursive_include ?+		PATHMATCH_NO_ANCHOR_END :+		0;+	int r;++	if (mbs) {+		const char *p;+		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);+		if (r == 0)+			return (archive_pathmatch(p, (const char *)pn, flag));+	} else {+		const wchar_t *p;+		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);+		if (r == 0)+			return (archive_pathmatch_w(p, (const wchar_t *)pn,+				flag));+	}+	if (errno == ENOMEM)+		return (error_nomem(a));+	return (0);+}++static void+match_list_init(struct match_list *list)+{+	list->first = NULL;+	list->last = &(list->first);+	list->count = 0;+}++static void+match_list_free(struct match_list *list)+{+	struct match *p, *q;++	for (p = list->first; p != NULL; ) {+		q = p;+		p = p->next;+		archive_mstring_clean(&(q->pattern));+		free(q);+	}+}++static void+match_list_add(struct match_list *list, struct match *m)+{+	*list->last = m;+	list->last = &(m->next);+	list->count++;+	list->unmatched_count++;+}++static int+match_list_unmatched_inclusions_next(struct archive_match *a,+    struct match_list *list, int mbs, const void **vp)+{+	struct match *m;++	*vp = NULL;+	if (list->unmatched_eof) {+		list->unmatched_eof = 0;+		return (ARCHIVE_EOF);+	}+	if (list->unmatched_next == NULL) {+		if (list->unmatched_count == 0)+			return (ARCHIVE_EOF);+		list->unmatched_next = list->first;+	}++	for (m = list->unmatched_next; m != NULL; m = m->next) {+		int r;++		if (m->matches)+			continue;+		if (mbs) {+			const char *p;+			r = archive_mstring_get_mbs(&(a->archive),+				&(m->pattern), &p);+			if (r < 0 && errno == ENOMEM)+				return (error_nomem(a));+			if (p == NULL)+				p = "";+			*vp = p;+		} else {+			const wchar_t *p;+			r = archive_mstring_get_wcs(&(a->archive),+				&(m->pattern), &p);+			if (r < 0 && errno == ENOMEM)+				return (error_nomem(a));+			if (p == NULL)+				p = L"";+			*vp = p;+		}+		list->unmatched_next = m->next;+		if (list->unmatched_next == NULL)+			/* To return EOF next time. */+			list->unmatched_eof = 1;+		return (ARCHIVE_OK);+	}+	list->unmatched_next = NULL;+	return (ARCHIVE_EOF);+}++/*+ * Utility functions to manage inclusion timestamps.+ */+int+archive_match_include_time(struct archive *_a, int flag, time_t sec,+    long nsec)+{+	int r;++	r = validate_time_flag(_a, flag, "archive_match_include_time");+	if (r != ARCHIVE_OK)+		return (r);+	return set_timefilter((struct archive_match *)_a, flag,+			sec, nsec, sec, nsec);+}++int+archive_match_include_date(struct archive *_a, int flag,+    const char *datestr)+{+	int r;++	r = validate_time_flag(_a, flag, "archive_match_include_date");+	if (r != ARCHIVE_OK)+		return (r);+	return set_timefilter_date((struct archive_match *)_a, flag, datestr);+}++int+archive_match_include_date_w(struct archive *_a, int flag,+    const wchar_t *datestr)+{+	int r;++	r = validate_time_flag(_a, flag, "archive_match_include_date_w");+	if (r != ARCHIVE_OK)+		return (r);++	return set_timefilter_date_w((struct archive_match *)_a, flag, datestr);+}++int+archive_match_include_file_time(struct archive *_a, int flag,+    const char *pathname)+{+	int r;++	r = validate_time_flag(_a, flag, "archive_match_include_file_time");+	if (r != ARCHIVE_OK)+		return (r);+	return set_timefilter_pathname_mbs((struct archive_match *)_a,+			flag, pathname);+}++int+archive_match_include_file_time_w(struct archive *_a, int flag,+    const wchar_t *pathname)+{+	int r;++	r = validate_time_flag(_a, flag, "archive_match_include_file_time_w");+	if (r != ARCHIVE_OK)+		return (r);+	return set_timefilter_pathname_wcs((struct archive_match *)_a,+			flag, pathname);+}++int+archive_match_exclude_entry(struct archive *_a, int flag,+    struct archive_entry *entry)+{+	struct archive_match *a;+	int r;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_time_include_entry");+	a = (struct archive_match *)_a;++	if (entry == NULL) {+		archive_set_error(&(a->archive), EINVAL, "entry is NULL");+		return (ARCHIVE_FAILED);+	}+	r = validate_time_flag(_a, flag, "archive_match_exclude_entry");+	if (r != ARCHIVE_OK)+		return (r);+	return (add_entry(a, flag, entry));+}++/*+ * Test function for time stamps.+ *+ * Returns 1 if archive entry is excluded.+ * Returns 0 if archive entry is not excluded.+ * Returns <0 if something error happened.+ */+int+archive_match_time_excluded(struct archive *_a,+    struct archive_entry *entry)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_time_excluded_ae");++	a = (struct archive_match *)_a;+	if (entry == NULL) {+		archive_set_error(&(a->archive), EINVAL, "entry is NULL");+		return (ARCHIVE_FAILED);+	}++	/* If we don't have inclusion time set at all, the entry is always+	 * not excluded. */+	if ((a->setflag & TIME_IS_SET) == 0)+		return (0);+	return (time_excluded(a, entry));+}++static int+validate_time_flag(struct archive *_a, int flag, const char *_fn)+{+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, _fn);++	/* Check a type of time. */+	if (flag &+	   ((~(ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) & 0xff00)) {+		archive_set_error(_a, EINVAL, "Invalid time flag");+		return (ARCHIVE_FAILED);+	}+	if ((flag & (ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) == 0) {+		archive_set_error(_a, EINVAL, "No time flag");+		return (ARCHIVE_FAILED);+	}++	/* Check a type of comparison. */+	if (flag &+	   ((~(ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER+			| ARCHIVE_MATCH_EQUAL)) & 0x00ff)) {+		archive_set_error(_a, EINVAL, "Invalid comparison flag");+		return (ARCHIVE_FAILED);+	}+	if ((flag & (ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER+	    | ARCHIVE_MATCH_EQUAL)) == 0) {+		archive_set_error(_a, EINVAL, "No comparison flag");+		return (ARCHIVE_FAILED);+	}++	return (ARCHIVE_OK);+}++#define JUST_EQUAL(t) (((t) &  (ARCHIVE_MATCH_EQUAL |\+	ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER)) == ARCHIVE_MATCH_EQUAL)+static int+set_timefilter(struct archive_match *a, int timetype,+    time_t mtime_sec, long mtime_nsec, time_t ctime_sec, long ctime_nsec)+{+	if (timetype & ARCHIVE_MATCH_MTIME) {+		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {+			a->newer_mtime_filter = timetype;+			a->newer_mtime_sec = mtime_sec;+			a->newer_mtime_nsec = mtime_nsec;+			a->setflag |= TIME_IS_SET;+		}+		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {+			a->older_mtime_filter = timetype;+			a->older_mtime_sec = mtime_sec;+			a->older_mtime_nsec = mtime_nsec;+			a->setflag |= TIME_IS_SET;+		}+	}+	if (timetype & ARCHIVE_MATCH_CTIME) {+		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {+			a->newer_ctime_filter = timetype;+			a->newer_ctime_sec = ctime_sec;+			a->newer_ctime_nsec = ctime_nsec;+			a->setflag |= TIME_IS_SET;+		}+		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {+			a->older_ctime_filter = timetype;+			a->older_ctime_sec = ctime_sec;+			a->older_ctime_nsec = ctime_nsec;+			a->setflag |= TIME_IS_SET;+		}+	}+	return (ARCHIVE_OK);+}++static int+set_timefilter_date(struct archive_match *a, int timetype, const char *datestr)+{+	time_t t;++	if (datestr == NULL || *datestr == '\0') {+		archive_set_error(&(a->archive), EINVAL, "date is empty");+		return (ARCHIVE_FAILED);+	}+	t = get_date(a->now, datestr);+	if (t == (time_t)-1) {+		archive_set_error(&(a->archive), EINVAL, "invalid date string");+		return (ARCHIVE_FAILED);+	}+	return set_timefilter(a, timetype, t, 0, t, 0);+}++static int+set_timefilter_date_w(struct archive_match *a, int timetype,+    const wchar_t *datestr)+{+	struct archive_string as;+	time_t t;++	if (datestr == NULL || *datestr == L'\0') {+		archive_set_error(&(a->archive), EINVAL, "date is empty");+		return (ARCHIVE_FAILED);+	}++	archive_string_init(&as);+	if (archive_string_append_from_wcs(&as, datestr, wcslen(datestr)) < 0) {+		archive_string_free(&as);+		if (errno == ENOMEM)+			return (error_nomem(a));+		archive_set_error(&(a->archive), -1,+		    "Failed to convert WCS to MBS");+		return (ARCHIVE_FAILED);+	}+	t = get_date(a->now, as.s);+	archive_string_free(&as);+	if (t == (time_t)-1) {+		archive_set_error(&(a->archive), EINVAL, "invalid date string");+		return (ARCHIVE_FAILED);+	}+	return set_timefilter(a, timetype, t, 0, t, 0);+}++#if defined(_WIN32) && !defined(__CYGWIN__)+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)+static int+set_timefilter_find_data(struct archive_match *a, int timetype,+    DWORD ftLastWriteTime_dwHighDateTime, DWORD ftLastWriteTime_dwLowDateTime,+    DWORD ftCreationTime_dwHighDateTime, DWORD ftCreationTime_dwLowDateTime)+{+	ULARGE_INTEGER utc;+	time_t ctime_sec, mtime_sec;+	long ctime_ns, mtime_ns;++	utc.HighPart = ftCreationTime_dwHighDateTime;+	utc.LowPart = ftCreationTime_dwLowDateTime;+	if (utc.QuadPart >= EPOC_TIME) {+		utc.QuadPart -= EPOC_TIME;+		ctime_sec = (time_t)(utc.QuadPart / 10000000);+		ctime_ns = (long)(utc.QuadPart % 10000000) * 100;+	} else {+		ctime_sec = 0;+		ctime_ns = 0;+	}+	utc.HighPart = ftLastWriteTime_dwHighDateTime;+	utc.LowPart = ftLastWriteTime_dwLowDateTime;+	if (utc.QuadPart >= EPOC_TIME) {+		utc.QuadPart -= EPOC_TIME;+		mtime_sec = (time_t)(utc.QuadPart / 10000000);+		mtime_ns = (long)(utc.QuadPart % 10000000) * 100;+	} else {+		mtime_sec = 0;+		mtime_ns = 0;+	}+	return set_timefilter(a, timetype,+			mtime_sec, mtime_ns, ctime_sec, ctime_ns);+}++static int+set_timefilter_pathname_mbs(struct archive_match *a, int timetype,+    const char *path)+{+	/* NOTE: stat() on Windows cannot handle nano seconds. */+	HANDLE h;+	WIN32_FIND_DATAA d;++	if (path == NULL || *path == '\0') {+		archive_set_error(&(a->archive), EINVAL, "pathname is empty");+		return (ARCHIVE_FAILED);+	}+	h = FindFirstFileA(path, &d);+	if (h == INVALID_HANDLE_VALUE) {+		la_dosmaperr(GetLastError());+		archive_set_error(&(a->archive), errno,+		    "Failed to FindFirstFileA");+		return (ARCHIVE_FAILED);+	}+	FindClose(h);+	return set_timefilter_find_data(a, timetype,+	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,+	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);+}++static int+set_timefilter_pathname_wcs(struct archive_match *a, int timetype,+    const wchar_t *path)+{+	HANDLE h;+	WIN32_FIND_DATAW d;++	if (path == NULL || *path == L'\0') {+		archive_set_error(&(a->archive), EINVAL, "pathname is empty");+		return (ARCHIVE_FAILED);+	}+	h = FindFirstFileW(path, &d);+	if (h == INVALID_HANDLE_VALUE) {+		la_dosmaperr(GetLastError());+		archive_set_error(&(a->archive), errno,+		    "Failed to FindFirstFile");+		return (ARCHIVE_FAILED);+	}+	FindClose(h);+	return set_timefilter_find_data(a, timetype,+	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,+	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);+}++#else /* _WIN32 && !__CYGWIN__ */++static int+set_timefilter_stat(struct archive_match *a, int timetype, struct stat *st)+{+	struct archive_entry *ae;+	time_t ctime_sec, mtime_sec;+	long ctime_ns, mtime_ns;++	ae = archive_entry_new();+	if (ae == NULL)+		return (error_nomem(a));+	archive_entry_copy_stat(ae, st);+	ctime_sec = archive_entry_ctime(ae);+	ctime_ns = archive_entry_ctime_nsec(ae);+	mtime_sec = archive_entry_mtime(ae);+	mtime_ns = archive_entry_mtime_nsec(ae);+	archive_entry_free(ae);+	return set_timefilter(a, timetype, mtime_sec, mtime_ns,+			ctime_sec, ctime_ns);+}++static int+set_timefilter_pathname_mbs(struct archive_match *a, int timetype,+    const char *path)+{+	struct stat st;++	if (path == NULL || *path == '\0') {+		archive_set_error(&(a->archive), EINVAL, "pathname is empty");+		return (ARCHIVE_FAILED);+	}+	if (la_stat(path, &st) != 0) {+		archive_set_error(&(a->archive), errno, "Failed to stat()");+		return (ARCHIVE_FAILED);+	}+	return (set_timefilter_stat(a, timetype, &st));+}++static int+set_timefilter_pathname_wcs(struct archive_match *a, int timetype,+    const wchar_t *path)+{+	struct archive_string as;+	int r;++	if (path == NULL || *path == L'\0') {+		archive_set_error(&(a->archive), EINVAL, "pathname is empty");+		return (ARCHIVE_FAILED);+	}++	/* Convert WCS filename to MBS filename. */+	archive_string_init(&as);+	if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {+		archive_string_free(&as);+		if (errno == ENOMEM)+			return (error_nomem(a));+		archive_set_error(&(a->archive), -1,+		    "Failed to convert WCS to MBS");+		return (ARCHIVE_FAILED);+	}++	r = set_timefilter_pathname_mbs(a, timetype, as.s);+	archive_string_free(&as);++	return (r);+}+#endif /* _WIN32 && !__CYGWIN__ */++/*+ * Call back functions for archive_rb.+ */+static int+cmp_node_mbs(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;+	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;+	const char *p1, *p2;++	archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);+	archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);+	if (p1 == NULL)+		return (1);+	if (p2 == NULL)+		return (-1);+	return (strcmp(p1, p2));+}+        +static int+cmp_key_mbs(const struct archive_rb_node *n, const void *key)+{+	struct match_file *f = (struct match_file *)(uintptr_t)n;+	const char *p;++	archive_mstring_get_mbs(NULL, &(f->pathname), &p);+	if (p == NULL)+		return (-1);+	return (strcmp(p, (const char *)key));+}++static int+cmp_node_wcs(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;+	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;+	const wchar_t *p1, *p2;++	archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);+	archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);+	if (p1 == NULL)+		return (1);+	if (p2 == NULL)+		return (-1);+	return (wcscmp(p1, p2));+}+        +static int+cmp_key_wcs(const struct archive_rb_node *n, const void *key)+{+	struct match_file *f = (struct match_file *)(uintptr_t)n;+	const wchar_t *p;++	archive_mstring_get_wcs(NULL, &(f->pathname), &p);+	if (p == NULL)+		return (-1);+	return (wcscmp(p, (const wchar_t *)key));+}++static void+entry_list_init(struct entry_list *list)+{+	list->first = NULL;+	list->last = &(list->first);+	list->count = 0;+}++static void+entry_list_free(struct entry_list *list)+{+	struct match_file *p, *q;++	for (p = list->first; p != NULL; ) {+		q = p;+		p = p->next;+		archive_mstring_clean(&(q->pathname));+		free(q);+	}+}++static void+entry_list_add(struct entry_list *list, struct match_file *file)+{+	*list->last = file;+	list->last = &(file->next);+	list->count++;+}++static int+add_entry(struct archive_match *a, int flag,+    struct archive_entry *entry)+{+	struct match_file *f;+	const void *pathname;+	int r;++	f = calloc(1, sizeof(*f));+	if (f == NULL)+		return (error_nomem(a));++#if defined(_WIN32) && !defined(__CYGWIN__)+	pathname = archive_entry_pathname_w(entry);+	if (pathname == NULL) {+		free(f);+		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");+		return (ARCHIVE_FAILED);+	}+	archive_mstring_copy_wcs(&(f->pathname), pathname);+	a->exclusion_tree.rbt_ops = &rb_ops_wcs;+#else+	(void)rb_ops_wcs;+	pathname = archive_entry_pathname(entry);+	if (pathname == NULL) {+		free(f);+		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");+		return (ARCHIVE_FAILED);+	}+	archive_mstring_copy_mbs(&(f->pathname), pathname);+	a->exclusion_tree.rbt_ops = &rb_ops_mbs;+#endif+	f->flag = flag;+	f->mtime_sec = archive_entry_mtime(entry);+	f->mtime_nsec = archive_entry_mtime_nsec(entry);+	f->ctime_sec = archive_entry_ctime(entry);+	f->ctime_nsec = archive_entry_ctime_nsec(entry);+	r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node));+	if (!r) {+		struct match_file *f2;++		/* Get the duplicated file. */+		f2 = (struct match_file *)__archive_rb_tree_find_node(+			&(a->exclusion_tree), pathname);++		/*+		 * We always overwrite comparison condition.+		 * If you do not want to overwrite it, you should not+		 * call archive_match_exclude_entry(). We cannot know+		 * what behavior you really expect since overwriting+		 * condition might be different with the flag.+		 */+		if (f2 != NULL) {+			f2->flag = f->flag;+			f2->mtime_sec = f->mtime_sec;+			f2->mtime_nsec = f->mtime_nsec;+			f2->ctime_sec = f->ctime_sec;+			f2->ctime_nsec = f->ctime_nsec;+		}+		/* Release the duplicated file. */+		archive_mstring_clean(&(f->pathname));+		free(f);+		return (ARCHIVE_OK);+	}+	entry_list_add(&(a->exclusion_entry_list), f);+	a->setflag |= TIME_IS_SET;+	return (ARCHIVE_OK);+}++/*+ * Test if entry is excluded by its timestamp.+ */+static int+time_excluded(struct archive_match *a, struct archive_entry *entry)+{+	struct match_file *f;+	const void *pathname;+	time_t sec;+	long nsec;++	/*+	 * If this file/dir is excluded by a time comparison, skip it.+	 */+	if (a->newer_ctime_filter) {+		/* If ctime is not set, use mtime instead. */+		if (archive_entry_ctime_is_set(entry))+			sec = archive_entry_ctime(entry);+		else+			sec = archive_entry_mtime(entry);+		if (sec < a->newer_ctime_sec)+			return (1); /* Too old, skip it. */+		if (sec == a->newer_ctime_sec) {+			if (archive_entry_ctime_is_set(entry))+				nsec = archive_entry_ctime_nsec(entry);+			else+				nsec = archive_entry_mtime_nsec(entry);+			if (nsec < a->newer_ctime_nsec)+				return (1); /* Too old, skip it. */+			if (nsec == a->newer_ctime_nsec &&+			    (a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL)+			      == 0)+				return (1); /* Equal, skip it. */+		}+	}+	if (a->older_ctime_filter) {+		/* If ctime is not set, use mtime instead. */+		if (archive_entry_ctime_is_set(entry))+			sec = archive_entry_ctime(entry);+		else+			sec = archive_entry_mtime(entry);+		if (sec > a->older_ctime_sec)+			return (1); /* Too new, skip it. */+		if (sec == a->older_ctime_sec) {+			if (archive_entry_ctime_is_set(entry))+				nsec = archive_entry_ctime_nsec(entry);+			else+				nsec = archive_entry_mtime_nsec(entry);+			if (nsec > a->older_ctime_nsec)+				return (1); /* Too new, skip it. */+			if (nsec == a->older_ctime_nsec &&+			    (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)+			      == 0)+				return (1); /* Equal, skip it. */+		}+	}+	if (a->newer_mtime_filter) {+		sec = archive_entry_mtime(entry);+		if (sec < a->newer_mtime_sec)+			return (1); /* Too old, skip it. */+		if (sec == a->newer_mtime_sec) {+			nsec = archive_entry_mtime_nsec(entry);+			if (nsec < a->newer_mtime_nsec)+				return (1); /* Too old, skip it. */+			if (nsec == a->newer_mtime_nsec &&+			    (a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL)+			       == 0)+				return (1); /* Equal, skip it. */+		}+	}+	if (a->older_mtime_filter) {+		sec = archive_entry_mtime(entry);+		if (sec > a->older_mtime_sec)+			return (1); /* Too new, skip it. */+		nsec = archive_entry_mtime_nsec(entry);+		if (sec == a->older_mtime_sec) {+			if (nsec > a->older_mtime_nsec)+				return (1); /* Too new, skip it. */+			if (nsec == a->older_mtime_nsec &&+			    (a->older_mtime_filter & ARCHIVE_MATCH_EQUAL)+			       == 0)+				return (1); /* Equal, skip it. */+		}+	}++	/* If there is no exclusion list, include the file. */+	if (a->exclusion_entry_list.count == 0)+		return (0);++#if defined(_WIN32) && !defined(__CYGWIN__)+	pathname = archive_entry_pathname_w(entry);+	a->exclusion_tree.rbt_ops = &rb_ops_wcs;+#else+	(void)rb_ops_wcs;+	pathname = archive_entry_pathname(entry);+	a->exclusion_tree.rbt_ops = &rb_ops_mbs;+#endif+	if (pathname == NULL)+		return (0);++	f = (struct match_file *)__archive_rb_tree_find_node(+		&(a->exclusion_tree), pathname);+	/* If the file wasn't rejected, include it. */+	if (f == NULL)+		return (0);++	if (f->flag & ARCHIVE_MATCH_CTIME) {+		sec = archive_entry_ctime(entry);+		if (f->ctime_sec > sec) {+			if (f->flag & ARCHIVE_MATCH_OLDER)+				return (1);+		} else if (f->ctime_sec < sec) {+			if (f->flag & ARCHIVE_MATCH_NEWER)+				return (1);+		} else {+			nsec = archive_entry_ctime_nsec(entry);+			if (f->ctime_nsec > nsec) {+				if (f->flag & ARCHIVE_MATCH_OLDER)+					return (1);+			} else if (f->ctime_nsec < nsec) {+				if (f->flag & ARCHIVE_MATCH_NEWER)+					return (1);+			} else if (f->flag & ARCHIVE_MATCH_EQUAL)+				return (1);+		}+	}+	if (f->flag & ARCHIVE_MATCH_MTIME) {+		sec = archive_entry_mtime(entry);+		if (f->mtime_sec > sec) {+			if (f->flag & ARCHIVE_MATCH_OLDER)+				return (1);+		} else if (f->mtime_sec < sec) {+			if (f->flag & ARCHIVE_MATCH_NEWER)+				return (1);+		} else {+			nsec = archive_entry_mtime_nsec(entry);+			if (f->mtime_nsec > nsec) {+				if (f->flag & ARCHIVE_MATCH_OLDER)+					return (1);+			} else if (f->mtime_nsec < nsec) {+				if (f->flag & ARCHIVE_MATCH_NEWER)+					return (1);+			} else if (f->flag & ARCHIVE_MATCH_EQUAL)+				return (1);+		}+	}+	return (0);+}++/*+ * Utility functions to manage inclusion owners+ */++int+archive_match_include_uid(struct archive *_a, la_int64_t uid)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_include_uid");+	a = (struct archive_match *)_a;+	return (add_owner_id(a, &(a->inclusion_uids), uid));+}++int+archive_match_include_gid(struct archive *_a, la_int64_t gid)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_include_gid");+	a = (struct archive_match *)_a;+	return (add_owner_id(a, &(a->inclusion_gids), gid));+}++int+archive_match_include_uname(struct archive *_a, const char *uname)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_include_uname");+	a = (struct archive_match *)_a;+	return (add_owner_name(a, &(a->inclusion_unames), 1, uname));+}++int+archive_match_include_uname_w(struct archive *_a, const wchar_t *uname)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_include_uname_w");+	a = (struct archive_match *)_a;+	return (add_owner_name(a, &(a->inclusion_unames), 0, uname));+}++int+archive_match_include_gname(struct archive *_a, const char *gname)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_include_gname");+	a = (struct archive_match *)_a;+	return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));+}++int+archive_match_include_gname_w(struct archive *_a, const wchar_t *gname)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_include_gname_w");+	a = (struct archive_match *)_a;+	return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));+}++/*+ * Test function for owner(uid, gid, uname, gname).+ *+ * Returns 1 if archive entry is excluded.+ * Returns 0 if archive entry is not excluded.+ * Returns <0 if something error happened.+ */+int+archive_match_owner_excluded(struct archive *_a,+    struct archive_entry *entry)+{+	struct archive_match *a;++	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_match_id_excluded_ae");++	a = (struct archive_match *)_a;+	if (entry == NULL) {+		archive_set_error(&(a->archive), EINVAL, "entry is NULL");+		return (ARCHIVE_FAILED);+	}++	/* If we don't have inclusion id set at all, the entry is always+	 * not excluded. */+	if ((a->setflag & ID_IS_SET) == 0)+		return (0);+	return (owner_excluded(a, entry));+}++static int+add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id)+{+	unsigned i;++	if (ids->count + 1 >= ids->size) {+		void *p;++		if (ids->size == 0)+			ids->size = 8;+		else+			ids->size *= 2;+		p = realloc(ids->ids, sizeof(*ids->ids) * ids->size);+		if (p == NULL)+			return (error_nomem(a));+		ids->ids = (int64_t *)p;+	}++	/* Find an insert point. */+	for (i = 0; i < ids->count; i++) {+		if (ids->ids[i] >= id)+			break;+	}++	/* Add owner id. */+	if (i == ids->count)+		ids->ids[ids->count++] = id;+	else if (ids->ids[i] != id) {+		memmove(&(ids->ids[i+1]), &(ids->ids[i]),+		    (ids->count - i) * sizeof(ids->ids[0]));+		ids->ids[i] = id;+		ids->count++;+	}+	a->setflag |= ID_IS_SET;+	return (ARCHIVE_OK);+}++static int+match_owner_id(struct id_array *ids, int64_t id)+{+	unsigned b, m, t;++	t = 0;+	b = (unsigned)ids->count;+	while (t < b) {+		m = (t + b)>>1;+		if (ids->ids[m] == id)+			return (1);+		if (ids->ids[m] < id)+			t = m + 1;+		else+			b = m;+	}+	return (0);+}++static int+add_owner_name(struct archive_match *a, struct match_list *list,+    int mbs, const void *name)+{+	struct match *match;++	match = calloc(1, sizeof(*match));+	if (match == NULL)+		return (error_nomem(a));+	if (mbs)+		archive_mstring_copy_mbs(&(match->pattern), name);+	else+		archive_mstring_copy_wcs(&(match->pattern), name);+	match_list_add(list, match);+	a->setflag |= ID_IS_SET;+	return (ARCHIVE_OK);+}++#if !defined(_WIN32) || defined(__CYGWIN__)+static int+match_owner_name_mbs(struct archive_match *a, struct match_list *list,+    const char *name)+{+	struct match *m;+	const char *p;++	if (name == NULL || *name == '\0')+		return (0);+	for (m = list->first; m; m = m->next) {+		if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)+		    < 0 && errno == ENOMEM)+			return (error_nomem(a));+		if (p != NULL && strcmp(p, name) == 0) {+			m->matches++;+			return (1);+		}+	}+	return (0);+}+#else+static int+match_owner_name_wcs(struct archive_match *a, struct match_list *list,+    const wchar_t *name)+{+	struct match *m;+	const wchar_t *p;++	if (name == NULL || *name == L'\0')+		return (0);+	for (m = list->first; m; m = m->next) {+		if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)+		    < 0 && errno == ENOMEM)+			return (error_nomem(a));+		if (p != NULL && wcscmp(p, name) == 0) {+			m->matches++;+			return (1);+		}+	}+	return (0);+}+#endif++/*+ * Test if entry is excluded by uid, gid, uname or gname.+ */+static int+owner_excluded(struct archive_match *a, struct archive_entry *entry)+{+	int r;++	if (a->inclusion_uids.count) {+		if (!match_owner_id(&(a->inclusion_uids),+		    archive_entry_uid(entry)))+			return (1);+	}++	if (a->inclusion_gids.count) {+		if (!match_owner_id(&(a->inclusion_gids),+		    archive_entry_gid(entry)))+			return (1);+	}++	if (a->inclusion_unames.count) {+#if defined(_WIN32) && !defined(__CYGWIN__)+		r = match_owner_name_wcs(a, &(a->inclusion_unames),+			archive_entry_uname_w(entry));+#else+		r = match_owner_name_mbs(a, &(a->inclusion_unames),+			archive_entry_uname(entry));+#endif+		if (!r)+			return (1);+		else if (r < 0)+			return (r);+	}++	if (a->inclusion_gnames.count) {+#if defined(_WIN32) && !defined(__CYGWIN__)+		r = match_owner_name_wcs(a, &(a->inclusion_gnames),+			archive_entry_gname_w(entry));+#else+		r = match_owner_name_mbs(a, &(a->inclusion_gnames),+			archive_entry_gname(entry));+#endif+		if (!r)+			return (1);+		else if (r < 0)+			return (r);+	}+	return (0);+}+
+ c/archive_openssl_evp_private.h view
@@ -0,0 +1,53 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED+#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++#include <openssl/evp.h>+#include <openssl/opensslv.h>++#if OPENSSL_VERSION_NUMBER < 0x10100000L+#include <stdlib.h> /* malloc, free */+#include <string.h> /* memset */+static inline EVP_MD_CTX *EVP_MD_CTX_new(void)+{+	EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX));+	return ctx;+}++static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx)+{+	EVP_MD_CTX_cleanup(ctx);+	memset(ctx, 0, sizeof(*ctx));+	free(ctx);+}+#endif++#endif
+ c/archive_openssl_hmac_private.h view
@@ -0,0 +1,54 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#ifndef ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED+#define ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++#include <openssl/hmac.h>+#include <openssl/opensslv.h>++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \+	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)+#include <stdlib.h> /* malloc, free */+#include <string.h> /* memset */+static inline HMAC_CTX *HMAC_CTX_new(void)+{+	HMAC_CTX *ctx = (HMAC_CTX *)calloc(1, sizeof(HMAC_CTX));+	return ctx;+}++static inline void HMAC_CTX_free(HMAC_CTX *ctx)+{+	HMAC_CTX_cleanup(ctx);+	memset(ctx, 0, sizeof(*ctx));+	free(ctx);+}+#endif++#endif
+ c/archive_options.c view
@@ -0,0 +1,218 @@+/*-+ * Copyright (c) 2011 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif++#include "archive_options_private.h"++static const char *+parse_option(const char **str,+    const char **mod, const char **opt, const char **val);++int+_archive_set_option(struct archive *a,+    const char *m, const char *o, const char *v,+    int magic, const char *fn, option_handler use_option)+{+	const char *mp, *op, *vp;+	int r;++	archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);++	mp = (m != NULL && m[0] != '\0') ? m : NULL;+	op = (o != NULL && o[0] != '\0') ? o : NULL;+	vp = (v != NULL && v[0] != '\0') ? v : NULL;++	if (op == NULL && vp == NULL)+		return (ARCHIVE_OK);+	if (op == NULL) {+		archive_set_error(a, ARCHIVE_ERRNO_MISC, "Empty option");+		return (ARCHIVE_FAILED);+	}++	r = use_option(a, mp, op, vp);+	if (r == ARCHIVE_WARN - 1) {+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Unknown module name: `%s'", mp);+		return (ARCHIVE_FAILED);+	}+	if (r == ARCHIVE_WARN) {+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Undefined option: `%s%s%s%s%s%s'",+		    vp?"":"!", mp?mp:"", mp?":":"", op, vp?"=":"", vp?vp:"");+		return (ARCHIVE_FAILED);+	}+	return (r);+}++int+_archive_set_either_option(struct archive *a, const char *m, const char *o, const char *v,+    option_handler use_format_option, option_handler use_filter_option)+{+	int r1, r2;++	if (o == NULL && v == NULL)+		return (ARCHIVE_OK);+	if (o == NULL)+		return (ARCHIVE_FAILED);++	r1 = use_format_option(a, m, o, v);+	if (r1 == ARCHIVE_FATAL)+		return (ARCHIVE_FATAL);++	r2 = use_filter_option(a, m, o, v);+	if (r2 == ARCHIVE_FATAL)+		return (ARCHIVE_FATAL);++	if (r2 == ARCHIVE_WARN - 1)+		return r1;+	return r1 > r2 ? r1 : r2;+}++int+_archive_set_options(struct archive *a, const char *options,+    int magic, const char *fn, option_handler use_option)+{+	int allok = 1, anyok = 0, ignore_mod_err = 0, r;+	char *data;+	const char *s, *mod, *opt, *val;++	archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);++	if (options == NULL || options[0] == '\0')+		return ARCHIVE_OK;++	if ((data = strdup(options)) == NULL) {+		archive_set_error(a,+		    ENOMEM, "Out of memory adding file to list");+		return (ARCHIVE_FATAL);+	}+	s = (const char *)data;++	do {+		mod = opt = val = NULL;++		parse_option(&s, &mod, &opt, &val);+		if (mod == NULL && opt != NULL &&+		    strcmp("__ignore_wrong_module_name__", opt) == 0) {+			/* Ignore module name error */+			if (val != NULL) {+				ignore_mod_err = 1;+				anyok = 1;+			}+			continue;+		}++		r = use_option(a, mod, opt, val);+		if (r == ARCHIVE_FATAL) {+			free(data);+			return (ARCHIVE_FATAL);+		}+		if (r == ARCHIVE_FAILED && mod != NULL) {+			free(data);+			return (ARCHIVE_FAILED);+		}+		if (r == ARCHIVE_WARN - 1) {+			if (ignore_mod_err)+				continue;+			/* The module name is wrong. */+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Unknown module name: `%s'", mod);+			free(data);+			return (ARCHIVE_FAILED);+		}+		if (r == ARCHIVE_WARN) {+			/* The option name is wrong. No-one used this. */+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Undefined option: `%s%s%s'",+			    mod?mod:"", mod?":":"", opt);+			free(data);+			return (ARCHIVE_FAILED);+		}+		if (r == ARCHIVE_OK)+			anyok = 1;+		else+			allok = 0;+	} while (s != NULL);++	free(data);+	return allok ? ARCHIVE_OK : anyok ? ARCHIVE_WARN : ARCHIVE_FAILED;+}++static const char *+parse_option(const char **s, const char **m, const char **o, const char **v)+{+	const char *end, *mod, *opt, *val;+	char *p;++	end = NULL;+	mod = NULL;+	opt = *s;+	val = "1";++	p = strchr(opt, ',');++	if (p != NULL) {+		*p = '\0';+		end = ((const char *)p) + 1;+	}++	if (0 == strlen(opt)) {+		*s = end;+		*m = NULL;+		*o = NULL;+		*v = NULL;+		return end;+	}++	p = strchr(opt, ':');+	if (p != NULL) {+		*p = '\0';+		mod = opt;+		opt = ++p;+	}++	p = strchr(opt, '=');+	if (p != NULL) {+		*p = '\0';+		val = ++p;+	} else if (opt[0] == '!') {+		++opt;+		val = NULL;+	}++	*s = end;+	*m = mod;+	*o = opt;+	*v = val;++	return end;+}+
+ c/archive_options_private.h view
@@ -0,0 +1,51 @@+/*-+ * Copyright (c) 2011 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#ifndef ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED+#define ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#include "archive_private.h"++typedef int (*option_handler)(struct archive *a,+    const char *mod, const char *opt, const char *val);++int+_archive_set_option(struct archive *a,+    const char *mod, const char *opt, const char *val,+    int magic, const char *fn, option_handler use_option);++int+_archive_set_options(struct archive *a, const char *options,+    int magic, const char *fn, option_handler use_option);++int+_archive_set_either_option(struct archive *a,+    const char *m, const char *o, const char *v,+    option_handler use_format_option, option_handler use_filter_option);++#endif
+ c/archive_pack_dev.c view
@@ -0,0 +1,336 @@+/*	$NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $	*/++/*-+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.+ * All rights reserved.+ *+ * This code is derived from software contributed to The NetBSD Foundation+ * by Charles M. Hannum.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+ * POSSIBILITY OF SUCH DAMAGE.+ */++/* Originally from NetBSD's mknod(8) source. */++#include "archive_platform.h"++#if HAVE_SYS_CDEFS_H+#include <sys/cdefs.h>+#endif+#if !defined(lint)+__RCSID("$NetBSD$");+#endif /* not lint */++#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif++#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#if MAJOR_IN_MKDEV+#include <sys/mkdev.h>+#define HAVE_MAJOR+#elif MAJOR_IN_SYSMACROS+#include <sys/sysmacros.h>+#define HAVE_MAJOR+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive_pack_dev.h"++static	pack_t	pack_netbsd;+static	pack_t	pack_freebsd;+static	pack_t	pack_8_8;+static	pack_t	pack_12_20;+static	pack_t	pack_14_18;+static	pack_t	pack_8_24;+static	pack_t	pack_bsdos;+static	int	compare_format(const void *, const void *);++static const char iMajorError[] = "invalid major number";+static const char iMinorError[] = "invalid minor number";+static const char tooManyFields[] = "too many fields for format";++/* This is blatantly stolen from libarchive/archive_entry.c,+ * in an attempt to get this to play nice on MinGW... */+#if !defined(HAVE_MAJOR) && !defined(major)+/* Replacement for major/minor/makedev. */+#define major(x) ((int)(0x00ff & ((x) >> 8)))+#define minor(x) ((int)(0xffff00ff & (x)))+#define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min)))+#endif++/* Play games to come up with a suitable makedev() definition. */+#ifdef __QNXNTO__+/* QNX.  <sigh> */+#include <sys/netmgr.h>+#define apd_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))+#elif defined makedev+/* There's a "makedev" macro. */+#define apd_makedev(maj, min) makedev((maj), (min))+#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__))+/* Windows. <sigh> */+#define apd_makedev(maj, min) mkdev((maj), (min))+#else+/* There's a "makedev" function. */+#define apd_makedev(maj, min) makedev((maj), (min))+#endif++/* exported */+dev_t+pack_native(int n, unsigned long numbers[], const char **error)+{+	dev_t dev = 0;++	if (n == 2) {+		dev = apd_makedev(numbers[0], numbers[1]);+		if ((unsigned long)major(dev) != numbers[0])+			*error = iMajorError;+		else if ((unsigned long)minor(dev) != numbers[1])+			*error = iMinorError;+	} else+		*error = tooManyFields;+	return (dev);+}+++static dev_t+pack_netbsd(int n, unsigned long numbers[], const char **error)+{+	dev_t dev = 0;++	if (n == 2) {+		dev = makedev_netbsd(numbers[0], numbers[1]);+		if ((unsigned long)major_netbsd(dev) != numbers[0])+			*error = iMajorError;+		else if ((unsigned long)minor_netbsd(dev) != numbers[1])+			*error = iMinorError;+	} else+		*error = tooManyFields;+	return (dev);+}+++#define	major_freebsd(x)	((int32_t)(((x) & 0x0000ff00) >> 8))+#define	minor_freebsd(x)	((int32_t)(((x) & 0xffff00ff) >> 0))+#define	makedev_freebsd(x,y)	((dev_t)((((x) << 8) & 0x0000ff00) | \+					 (((y) << 0) & 0xffff00ff)))++static dev_t+pack_freebsd(int n, unsigned long numbers[], const char **error)+{+	dev_t dev = 0;++	if (n == 2) {+		dev = makedev_freebsd(numbers[0], numbers[1]);+		if ((unsigned long)major_freebsd(dev) != numbers[0])+			*error = iMajorError;+		if ((unsigned long)minor_freebsd(dev) != numbers[1])+			*error = iMinorError;+	} else+		*error = tooManyFields;+	return (dev);+}+++#define	major_8_8(x)		((int32_t)(((x) & 0x0000ff00) >> 8))+#define	minor_8_8(x)		((int32_t)(((x) & 0x000000ff) >> 0))+#define	makedev_8_8(x,y)	((dev_t)((((x) << 8) & 0x0000ff00) | \+					 (((y) << 0) & 0x000000ff)))++static dev_t+pack_8_8(int n, unsigned long numbers[], const char **error)+{+	dev_t dev = 0;++	if (n == 2) {+		dev = makedev_8_8(numbers[0], numbers[1]);+		if ((unsigned long)major_8_8(dev) != numbers[0])+			*error = iMajorError;+		if ((unsigned long)minor_8_8(dev) != numbers[1])+			*error = iMinorError;+	} else+		*error = tooManyFields;+	return (dev);+}+++#define	major_12_20(x)		((int32_t)(((x) & 0xfff00000) >> 20))+#define	minor_12_20(x)		((int32_t)(((x) & 0x000fffff) >>  0))+#define	makedev_12_20(x,y)	((dev_t)((((x) << 20) & 0xfff00000) | \+					 (((y) <<  0) & 0x000fffff)))++static dev_t+pack_12_20(int n, unsigned long numbers[], const char **error)+{+	dev_t dev = 0;++	if (n == 2) {+		dev = makedev_12_20(numbers[0], numbers[1]);+		if ((unsigned long)major_12_20(dev) != numbers[0])+			*error = iMajorError;+		if ((unsigned long)minor_12_20(dev) != numbers[1])+			*error = iMinorError;+	} else+		*error = tooManyFields;+	return (dev);+}+++#define	major_14_18(x)		((int32_t)(((x) & 0xfffc0000) >> 18))+#define	minor_14_18(x)		((int32_t)(((x) & 0x0003ffff) >>  0))+#define	makedev_14_18(x,y)	((dev_t)((((x) << 18) & 0xfffc0000) | \+					 (((y) <<  0) & 0x0003ffff)))++static dev_t+pack_14_18(int n, unsigned long numbers[], const char **error)+{+	dev_t dev = 0;++	if (n == 2) {+		dev = makedev_14_18(numbers[0], numbers[1]);+		if ((unsigned long)major_14_18(dev) != numbers[0])+			*error = iMajorError;+		if ((unsigned long)minor_14_18(dev) != numbers[1])+			*error = iMinorError;+	} else+		*error = tooManyFields;+	return (dev);+}+++#define	major_8_24(x)		((int32_t)(((x) & 0xff000000) >> 24))+#define	minor_8_24(x)		((int32_t)(((x) & 0x00ffffff) >>  0))+#define	makedev_8_24(x,y)	((dev_t)((((x) << 24) & 0xff000000) | \+					 (((y) <<  0) & 0x00ffffff)))++static dev_t+pack_8_24(int n, unsigned long numbers[], const char **error)+{+	dev_t dev = 0;++	if (n == 2) {+		dev = makedev_8_24(numbers[0], numbers[1]);+		if ((unsigned long)major_8_24(dev) != numbers[0])+			*error = iMajorError;+		if ((unsigned long)minor_8_24(dev) != numbers[1])+			*error = iMinorError;+	} else+		*error = tooManyFields;+	return (dev);+}+++#define	major_12_12_8(x)	((int32_t)(((x) & 0xfff00000) >> 20))+#define	unit_12_12_8(x)		((int32_t)(((x) & 0x000fff00) >>  8))+#define	subunit_12_12_8(x)	((int32_t)(((x) & 0x000000ff) >>  0))+#define	makedev_12_12_8(x,y,z)	((dev_t)((((x) << 20) & 0xfff00000) | \+					 (((y) <<  8) & 0x000fff00) | \+					 (((z) <<  0) & 0x000000ff)))++static dev_t+pack_bsdos(int n, unsigned long numbers[], const char **error)+{+	dev_t dev = 0;++	if (n == 2) {+		dev = makedev_12_20(numbers[0], numbers[1]);+		if ((unsigned long)major_12_20(dev) != numbers[0])+			*error = iMajorError;+		if ((unsigned long)minor_12_20(dev) != numbers[1])+			*error = iMinorError;+	} else if (n == 3) {+		dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]);+		if ((unsigned long)major_12_12_8(dev) != numbers[0])+			*error = iMajorError;+		if ((unsigned long)unit_12_12_8(dev) != numbers[1])+			*error = "invalid unit number";+		if ((unsigned long)subunit_12_12_8(dev) != numbers[2])+			*error = "invalid subunit number";+	} else+		*error = tooManyFields;+	return (dev);+}+++		/* list of formats and pack functions */+		/* this list must be sorted lexically */+static const struct format {+	const char	*name;+	pack_t		*pack;+} formats[] = {+	{"386bsd",  pack_8_8},+	{"4bsd",    pack_8_8},+	{"bsdos",   pack_bsdos},+	{"freebsd", pack_freebsd},+	{"hpux",    pack_8_24},+	{"isc",     pack_8_8},+	{"linux",   pack_8_8},+	{"native",  pack_native},+	{"netbsd",  pack_netbsd},+	{"osf1",    pack_12_20},+	{"sco",     pack_8_8},+	{"solaris", pack_14_18},+	{"sunos",   pack_8_8},+	{"svr3",    pack_8_8},+	{"svr4",    pack_14_18},+	{"ultrix",  pack_8_8},+};++static int+compare_format(const void *key, const void *element)+{+	const char		*name;+	const struct format	*format;++	name = key;+	format = element;++	return (strcmp(name, format->name));+}+++pack_t *+pack_find(const char *name)+{+	struct format	*format;++	format = bsearch(name, formats,+	    sizeof(formats)/sizeof(formats[0]),+	    sizeof(formats[0]), compare_format);+	if (format == 0)+		return (NULL);+	return (format->pack);+}
+ c/archive_pack_dev.h view
@@ -0,0 +1,49 @@+/*	$NetBSD: pack_dev.h,v 1.8 2013/06/14 16:28:20 tsutsui Exp $	*/++/*-+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.+ * All rights reserved.+ *+ * This code is derived from software contributed to The NetBSD Foundation+ * by Charles M. Hannum.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+ * POSSIBILITY OF SUCH DAMAGE.+ */++/* Originally from NetBSD's mknod(8) source. */++#ifndef	ARCHIVE_PACK_DEV_H+#define	ARCHIVE_PACK_DEV_H++typedef	dev_t pack_t(int, unsigned long [], const char **);++pack_t	*pack_find(const char *);+pack_t	 pack_native;++#define	major_netbsd(x)		((int32_t)((((x) & 0x000fff00) >>  8)))+#define	minor_netbsd(x)		((int32_t)((((x) & 0xfff00000) >> 12) | \+					   (((x) & 0x000000ff) >>  0)))+#define	makedev_netbsd(x,y)	((dev_t)((((x) <<  8) & 0x000fff00) | \+					 (((y) << 12) & 0xfff00000) | \+					 (((y) <<  0) & 0x000000ff)))++#endif	/* ARCHIVE_PACK_DEV_H */
+ c/archive_pathmatch.c view
@@ -0,0 +1,459 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer+ *    in this position and unchanged.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_WCHAR_H+#include <wchar.h>+#endif++#include "archive_pathmatch.h"++/*+ * Check whether a character 'c' is matched by a list specification [...]:+ *    * Leading '!' or '^' negates the class.+ *    * <char>-<char> is a range of characters+ *    * \<char> removes any special meaning for <char>+ *+ * Some interesting boundary cases:+ *   a-d-e is one range (a-d) followed by two single characters - and e.+ *   \a-\d is same as a-d+ *   a\-d is three single characters: a, d, -+ *   Trailing - is not special (so [a-] is two characters a and -).+ *   Initial - is not special ([a-] is same as [-a] is same as [\\-a])+ *   This function never sees a trailing \.+ *   [] always fails+ *   [!] always succeeds+ */+static int+pm_list(const char *start, const char *end, const char c, int flags)+{+	const char *p = start;+	char rangeStart = '\0', nextRangeStart;+	int match = 1, nomatch = 0;++	/* This will be used soon... */+	(void)flags; /* UNUSED */++	/* If this is a negated class, return success for nomatch. */+	if ((*p == '!' || *p == '^') && p < end) {+		match = 0;+		nomatch = 1;+		++p;+	}++	while (p < end) {+		nextRangeStart = '\0';+		switch (*p) {+		case '-':+			/* Trailing or initial '-' is not special. */+			if ((rangeStart == '\0') || (p == end - 1)) {+				if (*p == c)+					return (match);+			} else {+				char rangeEnd = *++p;+				if (rangeEnd == '\\')+					rangeEnd = *++p;+				if ((rangeStart <= c) && (c <= rangeEnd))+					return (match);+			}+			break;+		case '\\':+			++p;+			/* Fall through */+		default:+			if (*p == c)+				return (match);+			nextRangeStart = *p; /* Possible start of range. */+		}+		rangeStart = nextRangeStart;+		++p;+	}+	return (nomatch);+}++static int+pm_list_w(const wchar_t *start, const wchar_t *end, const wchar_t c, int flags)+{+	const wchar_t *p = start;+	wchar_t rangeStart = L'\0', nextRangeStart;+	int match = 1, nomatch = 0;++	/* This will be used soon... */+	(void)flags; /* UNUSED */++	/* If this is a negated class, return success for nomatch. */+	if ((*p == L'!' || *p == L'^') && p < end) {+		match = 0;+		nomatch = 1;+		++p;+	}++	while (p < end) {+		nextRangeStart = L'\0';+		switch (*p) {+		case L'-':+			/* Trailing or initial '-' is not special. */+			if ((rangeStart == L'\0') || (p == end - 1)) {+				if (*p == c)+					return (match);+			} else {+				wchar_t rangeEnd = *++p;+				if (rangeEnd == L'\\')+					rangeEnd = *++p;+				if ((rangeStart <= c) && (c <= rangeEnd))+					return (match);+			}+			break;+		case L'\\':+			++p;+			/* Fall through */+		default:+			if (*p == c)+				return (match);+			nextRangeStart = *p; /* Possible start of range. */+		}+		rangeStart = nextRangeStart;+		++p;+	}+	return (nomatch);+}++/*+ * If s is pointing to "./", ".//", "./././" or the like, skip it.+ */+static const char *+pm_slashskip(const char *s) {+	while ((*s == '/')+	    || (s[0] == '.' && s[1] == '/')+	    || (s[0] == '.' && s[1] == '\0'))+		++s;+	return (s);+}++static const wchar_t *+pm_slashskip_w(const wchar_t *s) {+	while ((*s == L'/')+	    || (s[0] == L'.' && s[1] == L'/')+	    || (s[0] == L'.' && s[1] == L'\0'))+		++s;+	return (s);+}++static int+pm(const char *p, const char *s, int flags)+{+	const char *end;++	/*+	 * Ignore leading './', './/', '././', etc.+	 */+	if (s[0] == '.' && s[1] == '/')+		s = pm_slashskip(s + 1);+	if (p[0] == '.' && p[1] == '/')+		p = pm_slashskip(p + 1);++	for (;;) {+		switch (*p) {+		case '\0':+			if (s[0] == '/') {+				if (flags & PATHMATCH_NO_ANCHOR_END)+					return (1);+				/* "dir" == "dir/" == "dir/." */+				s = pm_slashskip(s);+			}+			return (*s == '\0');+		case '?':+			/* ? always succeeds, unless we hit end of 's' */+			if (*s == '\0')+				return (0);+			break;+		case '*':+			/* "*" == "**" == "***" ... */+			while (*p == '*')+				++p;+			/* Trailing '*' always succeeds. */+			if (*p == '\0')+				return (1);+			while (*s) {+				if (archive_pathmatch(p, s, flags))+					return (1);+				++s;+			}+			return (0);+		case '[':+			/*+			 * Find the end of the [...] character class,+			 * ignoring \] that might occur within the class.+			 */+			end = p + 1;+			while (*end != '\0' && *end != ']') {+				if (*end == '\\' && end[1] != '\0')+					++end;+				++end;+			}+			if (*end == ']') {+				/* We found [...], try to match it. */+				if (!pm_list(p + 1, end, *s, flags))+					return (0);+				p = end; /* Jump to trailing ']' char. */+				break;+			} else+				/* No final ']', so just match '['. */+				if (*p != *s)+					return (0);+			break;+		case '\\':+			/* Trailing '\\' matches itself. */+			if (p[1] == '\0') {+				if (*s != '\\')+					return (0);+			} else {+				++p;+				if (*p != *s)+					return (0);+			}+			break;+		case '/':+			if (*s != '/' && *s != '\0')+				return (0);+			/* Note: pattern "/\./" won't match "/";+			 * pm_slashskip() correctly stops at backslash. */+			p = pm_slashskip(p);+			s = pm_slashskip(s);+			if (*p == '\0' && (flags & PATHMATCH_NO_ANCHOR_END))+				return (1);+			--p; /* Counteract the increment below. */+			--s;+			break;+		case '$':+			/* '$' is special only at end of pattern and only+			 * if PATHMATCH_NO_ANCHOR_END is specified. */+			if (p[1] == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)){+				/* "dir" == "dir/" == "dir/." */+				return (*pm_slashskip(s) == '\0');+			}+			/* Otherwise, '$' is not special. */+			/* FALL THROUGH */+		default:+			if (*p != *s)+				return (0);+			break;+		}+		++p;+		++s;+	}+}++static int+pm_w(const wchar_t *p, const wchar_t *s, int flags)+{+	const wchar_t *end;++	/*+	 * Ignore leading './', './/', '././', etc.+	 */+	if (s[0] == L'.' && s[1] == L'/')+		s = pm_slashskip_w(s + 1);+	if (p[0] == L'.' && p[1] == L'/')+		p = pm_slashskip_w(p + 1);++	for (;;) {+		switch (*p) {+		case L'\0':+			if (s[0] == L'/') {+				if (flags & PATHMATCH_NO_ANCHOR_END)+					return (1);+				/* "dir" == "dir/" == "dir/." */+				s = pm_slashskip_w(s);+			}+			return (*s == L'\0');+		case L'?':+			/* ? always succeeds, unless we hit end of 's' */+			if (*s == L'\0')+				return (0);+			break;+		case L'*':+			/* "*" == "**" == "***" ... */+			while (*p == L'*')+				++p;+			/* Trailing '*' always succeeds. */+			if (*p == L'\0')+				return (1);+			while (*s) {+				if (archive_pathmatch_w(p, s, flags))+					return (1);+				++s;+			}+			return (0);+		case L'[':+			/*+			 * Find the end of the [...] character class,+			 * ignoring \] that might occur within the class.+			 */+			end = p + 1;+			while (*end != L'\0' && *end != L']') {+				if (*end == L'\\' && end[1] != L'\0')+					++end;+				++end;+			}+			if (*end == L']') {+				/* We found [...], try to match it. */+				if (!pm_list_w(p + 1, end, *s, flags))+					return (0);+				p = end; /* Jump to trailing ']' char. */+				break;+			} else+				/* No final ']', so just match '['. */+				if (*p != *s)+					return (0);+			break;+		case L'\\':+			/* Trailing '\\' matches itself. */+			if (p[1] == L'\0') {+				if (*s != L'\\')+					return (0);+			} else {+				++p;+				if (*p != *s)+					return (0);+			}+			break;+		case L'/':+			if (*s != L'/' && *s != L'\0')+				return (0);+			/* Note: pattern "/\./" won't match "/";+			 * pm_slashskip() correctly stops at backslash. */+			p = pm_slashskip_w(p);+			s = pm_slashskip_w(s);+			if (*p == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END))+				return (1);+			--p; /* Counteract the increment below. */+			--s;+			break;+		case L'$':+			/* '$' is special only at end of pattern and only+			 * if PATHMATCH_NO_ANCHOR_END is specified. */+			if (p[1] == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END)){+				/* "dir" == "dir/" == "dir/." */+				return (*pm_slashskip_w(s) == L'\0');+			}+			/* Otherwise, '$' is not special. */+			/* FALL THROUGH */+		default:+			if (*p != *s)+				return (0);+			break;+		}+		++p;+		++s;+	}+}++/* Main entry point. */+int+__archive_pathmatch(const char *p, const char *s, int flags)+{+	/* Empty pattern only matches the empty string. */+	if (p == NULL || *p == '\0')+		return (s == NULL || *s == '\0');++	/* Leading '^' anchors the start of the pattern. */+	if (*p == '^') {+		++p;+		flags &= ~PATHMATCH_NO_ANCHOR_START;+	}++	if (*p == '/' && *s != '/')+		return (0);++	/* Certain patterns anchor implicitly. */+	if (*p == '*' || *p == '/') {+		while (*p == '/')+			++p;+		while (*s == '/')+			++s;+		return (pm(p, s, flags));+	}++	/* If start is unanchored, try to match start of each path element. */+	if (flags & PATHMATCH_NO_ANCHOR_START) {+		for ( ; s != NULL; s = strchr(s, '/')) {+			if (*s == '/')+				s++;+			if (pm(p, s, flags))+				return (1);+		}+		return (0);+	}++	/* Default: Match from beginning. */+	return (pm(p, s, flags));+}++int+__archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags)+{+	/* Empty pattern only matches the empty string. */+	if (p == NULL || *p == L'\0')+		return (s == NULL || *s == L'\0');++	/* Leading '^' anchors the start of the pattern. */+	if (*p == L'^') {+		++p;+		flags &= ~PATHMATCH_NO_ANCHOR_START;+	}++	if (*p == L'/' && *s != L'/')+		return (0);++	/* Certain patterns anchor implicitly. */+	if (*p == L'*' || *p == L'/') {+		while (*p == L'/')+			++p;+		while (*s == L'/')+			++s;+		return (pm_w(p, s, flags));+	}++	/* If start is unanchored, try to match start of each path element. */+	if (flags & PATHMATCH_NO_ANCHOR_START) {+		for ( ; s != NULL; s = wcschr(s, L'/')) {+			if (*s == L'/')+				s++;+			if (pm_w(p, s, flags))+				return (1);+		}+		return (0);+	}++	/* Default: Match from beginning. */+	return (pm_w(p, s, flags));+}
+ c/archive_pathmatch.h view
@@ -0,0 +1,52 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer+ *    in this position and unchanged.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ */++#ifndef ARCHIVE_PATHMATCH_H+#define ARCHIVE_PATHMATCH_H++#ifndef __LIBARCHIVE_BUILD+#ifndef __LIBARCHIVE_TEST+#error This header is only to be used internally to libarchive.+#endif+#endif++/* Don't anchor at beginning unless the pattern starts with "^" */+#define PATHMATCH_NO_ANCHOR_START	1+/* Don't anchor at end unless the pattern ends with "$" */+#define PATHMATCH_NO_ANCHOR_END 	2++/* Note that "^" and "$" are not special unless you set the corresponding+ * flag above. */++int __archive_pathmatch(const char *p, const char *s, int flags);+int __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags);++#define archive_pathmatch(p, s, f)	__archive_pathmatch(p, s, f)+#define archive_pathmatch_w(p, s, f)	__archive_pathmatch_w(p, s, f)++#endif
+ c/archive_platform.h view
@@ -0,0 +1,202 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/archive_platform.h 201090 2009-12-28 02:22:04Z kientzle $+ */++/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */++/*+ * This header is the first thing included in any of the libarchive+ * source files.  As far as possible, platform-specific issues should+ * be dealt with here and not within individual source files.  I'm+ * actively trying to minimize #if blocks within the main source,+ * since they obfuscate the code.+ */++#ifndef ARCHIVE_PLATFORM_H_INCLUDED+#define	ARCHIVE_PLATFORM_H_INCLUDED++/* archive.h and archive_entry.h require this. */+#define	__LIBARCHIVE_BUILD 1++#if defined(PLATFORM_CONFIG_H)+/* Use hand-built config.h in environments that need it. */+#include PLATFORM_CONFIG_H+#elif defined(HAVE_CONFIG_H)+/* Most POSIX platforms use the 'configure' script to build config.h */+#include "config.h"+#else+/* Warn if the library hasn't been (automatically or manually) configured. */+#error Oops: No config.h and no pre-built configuration in archive_platform.h.+#endif++/* On macOS check for some symbols based on the deployment target version.  */+#if defined(__APPLE__)+# undef HAVE_FUTIMENS+# undef HAVE_UTIMENSAT+# include <AvailabilityMacros.h>+# if MAC_OS_X_VERSION_MIN_REQUIRED >= 101300+#  define HAVE_FUTIMENS 1+#  define HAVE_UTIMENSAT 1+# endif+#endif++/* It should be possible to get rid of this by extending the feature-test+ * macros to cover Windows API functions, probably along with non-trivial+ * refactoring of code to find structures that sit more cleanly on top of+ * either Windows or Posix APIs. */+#if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)+#include "archive_windows.h"+#else+#define la_stat(path,stref)		stat(path,stref)+#endif++/*+ * The config files define a lot of feature macros.  The following+ * uses those macros to select/define replacements and include key+ * headers as required.+ */++/* Get a real definition for __FBSDID or __RCSID if we can */+#if HAVE_SYS_CDEFS_H+#include <sys/cdefs.h>+#endif++/* If not, define them so as to avoid dangling semicolons. */+#ifndef __FBSDID+#define	__FBSDID(a)     struct _undefined_hack+#endif+#ifndef __RCSID+#define	__RCSID(a)     struct _undefined_hack+#endif++/* Try to get standard C99-style integer type definitions. */+#if HAVE_INTTYPES_H+#include <inttypes.h>+#endif+#if HAVE_STDINT_H+#include <stdint.h>+#endif++/* Borland warns about its own constants!  */+#if defined(__BORLANDC__)+# if HAVE_DECL_UINT64_MAX+#  undef	UINT64_MAX+#  undef	HAVE_DECL_UINT64_MAX+# endif+# if HAVE_DECL_UINT64_MIN+#  undef	UINT64_MIN+#  undef	HAVE_DECL_UINT64_MIN+# endif+# if HAVE_DECL_INT64_MAX+#  undef	INT64_MAX+#  undef	HAVE_DECL_INT64_MAX+# endif+# if HAVE_DECL_INT64_MIN+#  undef	INT64_MIN+#  undef	HAVE_DECL_INT64_MIN+# endif+#endif++/* Some platforms lack the standard *_MAX definitions. */+#if !HAVE_DECL_SIZE_MAX+#define	SIZE_MAX (~(size_t)0)+#endif+#if !HAVE_DECL_SSIZE_MAX+#define	SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1))+#endif+#if !HAVE_DECL_UINT32_MAX+#define	UINT32_MAX (~(uint32_t)0)+#endif+#if !HAVE_DECL_INT32_MAX+#define	INT32_MAX ((int32_t)(UINT32_MAX >> 1))+#endif+#if !HAVE_DECL_INT32_MIN+#define	INT32_MIN ((int32_t)(~INT32_MAX))+#endif+#if !HAVE_DECL_UINT64_MAX+#define	UINT64_MAX (~(uint64_t)0)+#endif+#if !HAVE_DECL_INT64_MAX+#define	INT64_MAX ((int64_t)(UINT64_MAX >> 1))+#endif+#if !HAVE_DECL_INT64_MIN+#define	INT64_MIN ((int64_t)(~INT64_MAX))+#endif+#if !HAVE_DECL_UINTMAX_MAX+#define	UINTMAX_MAX (~(uintmax_t)0)+#endif+#if !HAVE_DECL_INTMAX_MAX+#define	INTMAX_MAX ((intmax_t)(UINTMAX_MAX >> 1))+#endif+#if !HAVE_DECL_INTMAX_MIN+#define	INTMAX_MIN ((intmax_t)(~INTMAX_MAX))+#endif++/*+ * If we can't restore metadata using a file descriptor, then+ * for compatibility's sake, close files before trying to restore metadata.+ */+#if defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(HAVE_ACL_SET_FD) || defined(HAVE_ACL_SET_FD_NP) || defined(HAVE_FCHOWN)+#define	CAN_RESTORE_METADATA_FD+#endif++/*+ * glibc 2.24 deprecates readdir_r+ */+#if defined(HAVE_READDIR_R) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24))+#define	USE_READDIR_R	1+#else+#undef	USE_READDIR_R+#endif++/* Set up defaults for internal error codes. */+#ifndef ARCHIVE_ERRNO_FILE_FORMAT+#if HAVE_EFTYPE+#define	ARCHIVE_ERRNO_FILE_FORMAT EFTYPE+#else+#if HAVE_EILSEQ+#define	ARCHIVE_ERRNO_FILE_FORMAT EILSEQ+#else+#define	ARCHIVE_ERRNO_FILE_FORMAT EINVAL+#endif+#endif+#endif++#ifndef ARCHIVE_ERRNO_PROGRAMMER+#define	ARCHIVE_ERRNO_PROGRAMMER EINVAL+#endif++#ifndef ARCHIVE_ERRNO_MISC+#define	ARCHIVE_ERRNO_MISC (-1)+#endif++#if defined(__GNUC__) && (__GNUC__ >= 7)+#define	__LA_FALLTHROUGH	__attribute__((fallthrough))+#else+#define	__LA_FALLTHROUGH+#endif++#endif /* !ARCHIVE_PLATFORM_H_INCLUDED */
+ c/archive_platform_acl.h view
@@ -0,0 +1,55 @@+/*-+ * Copyright (c) 2017 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ */++/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */++#ifndef ARCHIVE_PLATFORM_ACL_H_INCLUDED+#define ARCHIVE_PLATFORM_ACL_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#ifndef __LIBARCHIVE_TEST_COMMON+#error This header is only to be used internally to libarchive.+#endif+#endif++/*+ * Determine what ACL types are supported+ */+#if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_SUNOS || ARCHIVE_ACL_LIBACL+#define ARCHIVE_ACL_POSIX1E     1+#endif++#if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_SUNOS_NFS4 || \+    ARCHIVE_ACL_DARWIN  || ARCHIVE_ACL_LIBRICHACL+#define ARCHIVE_ACL_NFS4        1+#endif++#if ARCHIVE_ACL_POSIX1E || ARCHIVE_ACL_NFS4+#define ARCHIVE_ACL_SUPPORT     1+#endif++#endif	/* ARCHIVE_PLATFORM_ACL_H_INCLUDED */
+ c/archive_platform_xattr.h view
@@ -0,0 +1,47 @@+/*-+ * Copyright (c) 2017 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ */++/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */++#ifndef ARCHIVE_PLATFORM_XATTR_H_INCLUDED+#define ARCHIVE_PLATFORM_XATTR_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#ifndef __LIBARCHIVE_TEST_COMMON+#error This header is only to be used internally to libarchive.+#endif+#endif++/*+ * Determine if we support extended attributes+ */+#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_FREEBSD || \+    ARCHIVE_XATTR_AIX+#define ARCHIVE_XATTR_SUPPORT     1+#endif++#endif	/* ARCHIVE_PLATFORM_XATTR_H_INCLUDED */
+ c/archive_ppmd7.c view
@@ -0,0 +1,1168 @@+/* Ppmd7.c -- PPMdH codec+2010-03-12 : Igor Pavlov : Public domain+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */++#include "archive_platform.h"++#include <memory.h>++#include "archive_ppmd7_private.h"++#ifdef PPMD_32BIT+  #define Ppmd7_GetPtr(p, ptr) (ptr)+  #define Ppmd7_GetContext(p, ptr) (ptr)+  #define Ppmd7_GetStats(p, ctx) ((ctx)->Stats)+#else+  #define Ppmd7_GetPtr(p, offs) ((void *)((p)->Base + (offs)))+  #define Ppmd7_GetContext(p, offs) ((CPpmd7_Context *)Ppmd7_GetPtr((p), (offs)))+  #define Ppmd7_GetStats(p, ctx) ((CPpmd_State *)Ppmd7_GetPtr((p), ((ctx)->Stats)))+#endif++#define Ppmd7_GetBinSumm(p) \+    &p->BinSumm[Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \+    p->NS2BSIndx[Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \+    (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \+    2 * p->HB2Flag[Ppmd7Context_OneState(p->MinContext)->Symbol] + \+    ((p->RunLength >> 26) & 0x20)]++#define kTopValue (1 << 24)+#define MAX_FREQ 124+#define UNIT_SIZE 12++#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)+#define U2I(nu) (p->Units2Indx[(nu) - 1])+#define I2U(indx) (p->Indx2Units[indx])++#ifdef PPMD_32BIT+  #define REF(ptr) (ptr)+#else+  #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))+#endif++#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))++#define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref))+#define STATS(ctx) Ppmd7_GetStats(p, ctx)+#define ONE_STATE(ctx) Ppmd7Context_OneState(ctx)+#define SUFFIX(ctx) CTX((ctx)->Suffix)++static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};+static const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };++typedef CPpmd7_Context * CTX_PTR;++struct CPpmd7_Node_;++typedef+  #ifdef PPMD_32BIT+    struct CPpmd7_Node_ *+  #else+    UInt32+  #endif+  CPpmd7_Node_Ref;++typedef struct CPpmd7_Node_+{+  UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */+  UInt16 NU;+  CPpmd7_Node_Ref Next; /* must be at offset >= 4 */+  CPpmd7_Node_Ref Prev;+} CPpmd7_Node;++#ifdef PPMD_32BIT+  #define NODE(ptr) (ptr)+#else+  #define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs)))+#endif++static void Ppmd7_Update1(CPpmd7 *p);+static void Ppmd7_Update1_0(CPpmd7 *p);+static void Ppmd7_Update2(CPpmd7 *p);+static void Ppmd7_UpdateBin(CPpmd7 *p);+static CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked,+                                    UInt32 *scale);++/* ----------- Base ----------- */++static void Ppmd7_Construct(CPpmd7 *p)+{+  unsigned i, k, m;++  p->Base = 0;++  for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)+  {+    unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);+    do { p->Units2Indx[k++] = (Byte)i; } while(--step);+    p->Indx2Units[i] = (Byte)k;+  }++  p->NS2BSIndx[0] = (0 << 1);+  p->NS2BSIndx[1] = (1 << 1);+  memset(p->NS2BSIndx + 2, (2 << 1), 9);+  memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);++  for (i = 0; i < 3; i++)+    p->NS2Indx[i] = (Byte)i;+  for (m = i, k = 1; i < 256; i++)+  {+    p->NS2Indx[i] = (Byte)m;+    if (--k == 0)+      k = (++m) - 2;+  }++  memset(p->HB2Flag, 0, 0x40);+  memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40);+}++static void Ppmd7_Free(CPpmd7 *p)+{+  free(p->Base);+  p->Size = 0;+  p->Base = 0;+}++static Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size)+{+  if (p->Base == 0 || p->Size != size)+  {+    /* RestartModel() below assumes that p->Size >= UNIT_SIZE+       (see the calculation of m->MinContext). */+    if (size < UNIT_SIZE) {+      return False;+    }+    Ppmd7_Free(p);+    p->AlignOffset =+      #ifdef PPMD_32BIT+        (4 - size) & 3;+      #else+        4 - (size & 3);+      #endif+    if ((p->Base = (Byte *)malloc(p->AlignOffset + size+        #ifndef PPMD_32BIT+        + UNIT_SIZE+        #endif+        )) == 0)+      return False;+    p->Size = size;+  }+  return True;+}++static void InsertNode(CPpmd7 *p, void *node, unsigned indx)+{+  *((CPpmd_Void_Ref *)node) = p->FreeList[indx];+  p->FreeList[indx] = REF(node);+}++static void *RemoveNode(CPpmd7 *p, unsigned indx)+{+  CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]);+  p->FreeList[indx] = *node;+  return node;+}++static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx)+{+  unsigned i, nu = I2U(oldIndx) - I2U(newIndx);+  ptr = (Byte *)ptr + U2B(I2U(newIndx));+  if (I2U(i = U2I(nu)) != nu)+  {+    unsigned k = I2U(--i);+    InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);+  }+  InsertNode(p, ptr, i);+}++static void GlueFreeBlocks(CPpmd7 *p)+{+  #ifdef PPMD_32BIT+  CPpmd7_Node headItem;+  CPpmd7_Node_Ref head = &headItem;+  #else+  CPpmd7_Node_Ref head = p->AlignOffset + p->Size;+  #endif+  +  CPpmd7_Node_Ref n = head;+  unsigned i;++  p->GlueCount = 255;++  /* create doubly-linked list of free blocks */+  for (i = 0; i < PPMD_NUM_INDEXES; i++)+  {+    UInt16 nu = I2U(i);+    CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i];+    p->FreeList[i] = 0;+    while (next != 0)+    {+      CPpmd7_Node *node = NODE(next);+      node->Next = n;+      n = NODE(n)->Prev = next;+      next = *(const CPpmd7_Node_Ref *)node;+      node->Stamp = 0;+      node->NU = (UInt16)nu;+    }+  }+  NODE(head)->Stamp = 1;+  NODE(head)->Next = n;+  NODE(n)->Prev = head;+  if (p->LoUnit != p->HiUnit)+    ((CPpmd7_Node *)p->LoUnit)->Stamp = 1;+  +  /* Glue free blocks */+  while (n != head)+  {+    CPpmd7_Node *node = NODE(n);+    UInt32 nu = (UInt32)node->NU;+    for (;;)+    {+      CPpmd7_Node *node2 = NODE(n) + nu;+      nu += node2->NU;+      if (node2->Stamp != 0 || nu >= 0x10000)+        break;+      NODE(node2->Prev)->Next = node2->Next;+      NODE(node2->Next)->Prev = node2->Prev;+      node->NU = (UInt16)nu;+    }+    n = node->Next;+  }+  +  /* Fill lists of free blocks */+  for (n = NODE(head)->Next; n != head;)+  {+    CPpmd7_Node *node = NODE(n);+    unsigned nu;+    CPpmd7_Node_Ref next = node->Next;+    for (nu = node->NU; nu > 128; nu -= 128, node += 128)+      InsertNode(p, node, PPMD_NUM_INDEXES - 1);+    if (I2U(i = U2I(nu)) != nu)+    {+      unsigned k = I2U(--i);+      InsertNode(p, node + k, nu - k - 1);+    }+    InsertNode(p, node, i);+    n = next;+  }+}++static void *AllocUnitsRare(CPpmd7 *p, unsigned indx)+{+  unsigned i;+  void *retVal;+  if (p->GlueCount == 0)+  {+    GlueFreeBlocks(p);+    if (p->FreeList[indx] != 0)+      return RemoveNode(p, indx);+  }+  i = indx;+  do+  {+    if (++i == PPMD_NUM_INDEXES)+    {+      UInt32 numBytes = U2B(I2U(indx));+      p->GlueCount--;+      return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);+    }+  }+  while (p->FreeList[i] == 0);+  retVal = RemoveNode(p, i);+  SplitBlock(p, retVal, i, indx);+  return retVal;+}++static void *AllocUnits(CPpmd7 *p, unsigned indx)+{+  UInt32 numBytes;+  if (p->FreeList[indx] != 0)+    return RemoveNode(p, indx);+  numBytes = U2B(I2U(indx));+  if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))+  {+    void *retVal = p->LoUnit;+    p->LoUnit += numBytes;+    return retVal;+  }+  return AllocUnitsRare(p, indx);+}++#define MyMem12Cpy(dest, src, num) \+  { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \+    do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); }++static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU)+{+  unsigned i0 = U2I(oldNU);+  unsigned i1 = U2I(newNU);+  if (i0 == i1)+    return oldPtr;+  if (p->FreeList[i1] != 0)+  {+    void *ptr = RemoveNode(p, i1);+    MyMem12Cpy(ptr, oldPtr, newNU);+    InsertNode(p, oldPtr, i0);+    return ptr;+  }+  SplitBlock(p, oldPtr, i0, i1);+  return oldPtr;+}++#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))++static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)+{+  (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);+  (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);+}++static void RestartModel(CPpmd7 *p)+{+  unsigned i, k, m;++  memset(p->FreeList, 0, sizeof(p->FreeList));+  p->Text = p->Base + p->AlignOffset;+  p->HiUnit = p->Text + p->Size;+  p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;+  p->GlueCount = 0;++  p->OrderFall = p->MaxOrder;+  p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;+  p->PrevSuccess = 0;++  p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */+  p->MinContext->Suffix = 0;+  p->MinContext->NumStats = 256;+  p->MinContext->SummFreq = 256 + 1;+  p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */+  p->LoUnit += U2B(256 / 2);+  p->MinContext->Stats = REF(p->FoundState);+  for (i = 0; i < 256; i++)+  {+    CPpmd_State *s = &p->FoundState[i];+    s->Symbol = (Byte)i;+    s->Freq = 1;+    SetSuccessor(s, 0);+  }++  for (i = 0; i < 128; i++)+    for (k = 0; k < 8; k++)+    {+      UInt16 *dest = p->BinSumm[i] + k;+      UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2));+      for (m = 0; m < 64; m += 8)+        dest[m] = val;+    }+  +  for (i = 0; i < 25; i++)+    for (k = 0; k < 16; k++)+    {+      CPpmd_See *s = &p->See[i][k];+      s->Summ = (UInt16)((5 * i + 10) << (s->Shift = PPMD_PERIOD_BITS - 4));+      s->Count = 4;+    }+}++static void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder)+{+  p->MaxOrder = maxOrder;+  RestartModel(p);+  p->DummySee.Shift = PPMD_PERIOD_BITS;+  p->DummySee.Summ = 0; /* unused */+  p->DummySee.Count = 64; /* unused */+}++static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip)+{+  CPpmd_State upState;+  CTX_PTR c = p->MinContext;+  CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);+  CPpmd_State *ps[PPMD7_MAX_ORDER];+  unsigned numPs = 0;+  +  if (!skip)+    ps[numPs++] = p->FoundState;+  +  while (c->Suffix)+  {+    CPpmd_Void_Ref successor;+    CPpmd_State *s;+    c = SUFFIX(c);+    if (c->NumStats != 1)+    {+      for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);+    }+    else+      s = ONE_STATE(c);+    successor = SUCCESSOR(s);+    if (successor != upBranch)+    {+      c = CTX(successor);+      if (numPs == 0)+        return c;+      break;+    }+    ps[numPs++] = s;+  }+  +  upState.Symbol = *(const Byte *)Ppmd7_GetPtr(p, upBranch);+  SetSuccessor(&upState, upBranch + 1);+  +  if (c->NumStats == 1)+    upState.Freq = ONE_STATE(c)->Freq;+  else+  {+    UInt32 cf, s0;+    CPpmd_State *s;+    for (s = STATS(c); s->Symbol != upState.Symbol; s++);+    cf = s->Freq - 1;+    s0 = c->SummFreq - c->NumStats - cf;+    upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0))));+  }++  while (numPs != 0)+  {+    /* Create Child */+    CTX_PTR c1; /* = AllocContext(p); */+    if (p->HiUnit != p->LoUnit)+      c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);+    else if (p->FreeList[0] != 0)+      c1 = (CTX_PTR)RemoveNode(p, 0);+    else+    {+      c1 = (CTX_PTR)AllocUnitsRare(p, 0);+      if (!c1)+        return NULL;+    }+    c1->NumStats = 1;+    *ONE_STATE(c1) = upState;+    c1->Suffix = REF(c);+    SetSuccessor(ps[--numPs], REF(c1));+    c = c1;+  }+  +  return c;+}++static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)+{+  CPpmd_State tmp = *t1;+  *t1 = *t2;+  *t2 = tmp;+}++static void UpdateModel(CPpmd7 *p)+{+  CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);+  CTX_PTR c;+  unsigned s0, ns;+  +  if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)+  {+    c = SUFFIX(p->MinContext);+    +    if (c->NumStats == 1)+    {+      CPpmd_State *s = ONE_STATE(c);+      if (s->Freq < 32)+        s->Freq++;+    }+    else+    {+      CPpmd_State *s = STATS(c);+      if (s->Symbol != p->FoundState->Symbol)+      {+        do { s++; } while (s->Symbol != p->FoundState->Symbol);+        if (s[0].Freq >= s[-1].Freq)+        {+          SwapStates(&s[0], &s[-1]);+          s--;+        }+      }+      if (s->Freq < MAX_FREQ - 9)+      {+        s->Freq += 2;+        c->SummFreq += 2;+      }+    }+  }++  if (p->OrderFall == 0)+  {+    p->MinContext = p->MaxContext = CreateSuccessors(p, True);+    if (p->MinContext == 0)+    {+      RestartModel(p);+      return;+    }+    SetSuccessor(p->FoundState, REF(p->MinContext));+    return;+  }+  +  *p->Text++ = p->FoundState->Symbol;+  successor = REF(p->Text);+  if (p->Text >= p->UnitsStart)+  {+    RestartModel(p);+    return;+  }+  +  if (fSuccessor)+  {+    if (fSuccessor <= successor)+    {+      CTX_PTR cs = CreateSuccessors(p, False);+      if (cs == NULL)+      {+        RestartModel(p);+        return;+      }+      fSuccessor = REF(cs);+    }+    if (--p->OrderFall == 0)+    {+      successor = fSuccessor;+      p->Text -= (p->MaxContext != p->MinContext);+    }+  }+  else+  {+    SetSuccessor(p->FoundState, successor);+    fSuccessor = REF(p->MinContext);+  }+  +  s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - (p->FoundState->Freq - 1);+  +  for (c = p->MaxContext; c != p->MinContext; c = SUFFIX(c))+  {+    unsigned ns1;+    UInt32 cf, sf;+    if ((ns1 = c->NumStats) != 1)+    {+      if ((ns1 & 1) == 0)+      {+        /* Expand for one UNIT */+        unsigned oldNU = ns1 >> 1;+        unsigned i = U2I(oldNU);+        if (i != U2I(oldNU + 1))+        {+          void *ptr = AllocUnits(p, i + 1);+          void *oldPtr;+          if (!ptr)+          {+            RestartModel(p);+            return;+          }+          oldPtr = STATS(c);+          MyMem12Cpy(ptr, oldPtr, oldNU);+          InsertNode(p, oldPtr, i);+          c->Stats = STATS_REF(ptr);+        }+      }+      c->SummFreq = (UInt16)(c->SummFreq + (2 * ns1 < ns) + 2 * ((4 * ns1 <= ns) & (c->SummFreq <= 8 * ns1)));+    }+    else+    {+      CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0);+      if (!s)+      {+        RestartModel(p);+        return;+      }+      *s = *ONE_STATE(c);+      c->Stats = REF(s);+      if (s->Freq < MAX_FREQ / 4 - 1)+        s->Freq <<= 1;+      else+        s->Freq = MAX_FREQ - 4;+      c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 3));+    }+    cf = 2 * (UInt32)p->FoundState->Freq * (c->SummFreq + 6);+    sf = (UInt32)s0 + c->SummFreq;+    if (cf < 6 * sf)+    {+      cf = 1 + (cf > sf) + (cf >= 4 * sf);+      c->SummFreq += 3;+    }+    else+    {+      cf = 4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf);+      c->SummFreq = (UInt16)(c->SummFreq + cf);+    }+    {+      CPpmd_State *s = STATS(c) + ns1;+      SetSuccessor(s, successor);+      s->Symbol = p->FoundState->Symbol;+      s->Freq = (Byte)cf;+      c->NumStats = (UInt16)(ns1 + 1);+    }+  }+  p->MaxContext = p->MinContext = CTX(fSuccessor);+}+  +static void Rescale(CPpmd7 *p)+{+  unsigned i, adder, sumFreq, escFreq;+  CPpmd_State *stats = STATS(p->MinContext);+  CPpmd_State *s = p->FoundState;+  {+    CPpmd_State tmp = *s;+    for (; s != stats; s--)+      s[0] = s[-1];+    *s = tmp;+  }+  escFreq = p->MinContext->SummFreq - s->Freq;+  s->Freq += 4;+  adder = (p->OrderFall != 0);+  s->Freq = (Byte)((s->Freq + adder) >> 1);+  sumFreq = s->Freq;+  +  i = p->MinContext->NumStats - 1;+  do+  {+    escFreq -= (++s)->Freq;+    s->Freq = (Byte)((s->Freq + adder) >> 1);+    sumFreq += s->Freq;+    if (s[0].Freq > s[-1].Freq)+    {+      CPpmd_State *s1 = s;+      CPpmd_State tmp = *s1;+      do+        s1[0] = s1[-1];+      while (--s1 != stats && tmp.Freq > s1[-1].Freq);+      *s1 = tmp;+    }+  }+  while (--i);+  +  if (s->Freq == 0)+  {+    unsigned numStats = p->MinContext->NumStats;+    unsigned n0, n1;+    do { i++; } while ((--s)->Freq == 0);+    escFreq += i;+    p->MinContext->NumStats = (UInt16)(p->MinContext->NumStats - i);+    if (p->MinContext->NumStats == 1)+    {+      CPpmd_State tmp = *stats;+      do+      {+        tmp.Freq = (Byte)(tmp.Freq - (tmp.Freq >> 1));+        escFreq >>= 1;+      }+      while (escFreq > 1);+      InsertNode(p, stats, U2I(((numStats + 1) >> 1)));+      *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;+      return;+    }+    n0 = (numStats + 1) >> 1;+    n1 = (p->MinContext->NumStats + 1) >> 1;+    if (n0 != n1)+      p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));+  }+  p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));+  p->FoundState = STATS(p->MinContext);+}++static CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq)+{+  CPpmd_See *see;+  unsigned nonMasked = p->MinContext->NumStats - numMasked;+  if (p->MinContext->NumStats != 256)+  {+    see = p->See[p->NS2Indx[nonMasked - 1]] ++        (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) ++        2 * (p->MinContext->SummFreq < 11 * p->MinContext->NumStats) ++        4 * (numMasked > nonMasked) ++        p->HiBitsFlag;+    {+      unsigned r = (see->Summ >> see->Shift);+      see->Summ = (UInt16)(see->Summ - r);+      *escFreq = r + (r == 0);+    }+  }+  else+  {+    see = &p->DummySee;+    *escFreq = 1;+  }+  return see;+}++static void NextContext(CPpmd7 *p)+{+  CTX_PTR c = CTX(SUCCESSOR(p->FoundState));+  if (p->OrderFall == 0 && (Byte *)c > p->Text)+    p->MinContext = p->MaxContext = c;+  else+    UpdateModel(p);+}++static void Ppmd7_Update1(CPpmd7 *p)+{+  CPpmd_State *s = p->FoundState;+  s->Freq += 4;+  p->MinContext->SummFreq += 4;+  if (s[0].Freq > s[-1].Freq)+  {+    SwapStates(&s[0], &s[-1]);+    p->FoundState = --s;+    if (s->Freq > MAX_FREQ)+      Rescale(p);+  }+  NextContext(p);+}++static void Ppmd7_Update1_0(CPpmd7 *p)+{+  p->PrevSuccess = (2 * p->FoundState->Freq > p->MinContext->SummFreq);+  p->RunLength += p->PrevSuccess;+  p->MinContext->SummFreq += 4;+  if ((p->FoundState->Freq += 4) > MAX_FREQ)+    Rescale(p);+  NextContext(p);+}++static void Ppmd7_UpdateBin(CPpmd7 *p)+{+  p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 128 ? 1: 0));+  p->PrevSuccess = 1;+  p->RunLength++;+  NextContext(p);+}++static void Ppmd7_Update2(CPpmd7 *p)+{+  p->MinContext->SummFreq += 4;+  if ((p->FoundState->Freq += 4) > MAX_FREQ)+    Rescale(p);+  p->RunLength = p->InitRL;+  UpdateModel(p);+}++/* ---------- Decode ---------- */++static Bool Ppmd_RangeDec_Init(CPpmd7z_RangeDec *p)+{+  unsigned i;+  p->Low = p->Bottom = 0;+  p->Range = 0xFFFFFFFF;+  for (i = 0; i < 4; i++)+    p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);+  return (p->Code < 0xFFFFFFFF);+}++static Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p)+{+  if (p->Stream->Read((void *)p->Stream) != 0)+    return False;+  return Ppmd_RangeDec_Init(p);+}++static Bool PpmdRAR_RangeDec_Init(CPpmd7z_RangeDec *p)+{+  if (!Ppmd_RangeDec_Init(p))+    return False;+  p->Bottom = 0x8000;+  return True;+}++static UInt32 Range_GetThreshold(void *pp, UInt32 total)+{+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;+  return (p->Code - p->Low) / (p->Range /= total);+}++static void Range_Normalize(CPpmd7z_RangeDec *p)+{+  while (1)+  {+    if((p->Low ^ (p->Low + p->Range)) >= kTopValue)+    {+      if(p->Range >= p->Bottom)+        break;+      else+        p->Range = ((uint32_t)(-(int32_t)p->Low)) & (p->Bottom - 1);+    }+    p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);+    p->Range <<= 8;+    p->Low <<= 8;+  }+}++static void Range_Decode_7z(void *pp, UInt32 start, UInt32 size)+{+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;+  p->Code -= start * p->Range;+  p->Range *= size;+  Range_Normalize(p);+}++static void Range_Decode_RAR(void *pp, UInt32 start, UInt32 size)+{+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;+  p->Low += start * p->Range;+  p->Range *= size;+  Range_Normalize(p);+}++static UInt32 Range_DecodeBit_7z(void *pp, UInt32 size0)+{+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;+  UInt32 newBound = (p->Range >> 14) * size0;+  UInt32 symbol;+  if (p->Code < newBound)+  {+    symbol = 0;+    p->Range = newBound;+  }+  else+  {+    symbol = 1;+    p->Code -= newBound;+    p->Range -= newBound;+  }+  Range_Normalize(p);+  return symbol;+}++static UInt32 Range_DecodeBit_RAR(void *pp, UInt32 size0)+{+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;+  UInt32 bit, value = p->p.GetThreshold(p, PPMD_BIN_SCALE);+  if(value < size0)+  {+    bit = 0;+    p->p.Decode(p, 0, size0);+  }+  else+  {+    bit = 1;+    p->p.Decode(p, size0, PPMD_BIN_SCALE - size0);+  }+  return bit;+}++static void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)+{+  p->p.GetThreshold = Range_GetThreshold;+  p->p.Decode = Range_Decode_7z;+  p->p.DecodeBit = Range_DecodeBit_7z;+}++static void PpmdRAR_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)+{+  p->p.GetThreshold = Range_GetThreshold;+  p->p.Decode = Range_Decode_RAR;+  p->p.DecodeBit = Range_DecodeBit_RAR;+}++#define MASK(sym) ((signed char *)charMask)[sym]++static int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc)+{+  size_t charMask[256 / sizeof(size_t)];+  if (p->MinContext->NumStats != 1)+  {+    CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext);+    unsigned i;+    UInt32 count, hiCnt;+    if ((count = rc->GetThreshold(rc, p->MinContext->SummFreq)) < (hiCnt = s->Freq))+    {+      Byte symbol;+      rc->Decode(rc, 0, s->Freq);+      p->FoundState = s;+      symbol = s->Symbol;+      Ppmd7_Update1_0(p);+      return symbol;+    }+    p->PrevSuccess = 0;+    i = p->MinContext->NumStats - 1;+    do+    {+      if ((hiCnt += (++s)->Freq) > count)+      {+        Byte symbol;+        rc->Decode(rc, hiCnt - s->Freq, s->Freq);+        p->FoundState = s;+        symbol = s->Symbol;+        Ppmd7_Update1(p);+        return symbol;+      }+    }+    while (--i);+    if (count >= p->MinContext->SummFreq)+      return -2;+    p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol];+    rc->Decode(rc, hiCnt, p->MinContext->SummFreq - hiCnt);+    PPMD_SetAllBitsIn256Bytes(charMask);+    MASK(s->Symbol) = 0;+    i = p->MinContext->NumStats - 1;+    do { MASK((--s)->Symbol) = 0; } while (--i);+  }+  else+  {+    UInt16 *prob = Ppmd7_GetBinSumm(p);+    if (rc->DecodeBit(rc, *prob) == 0)+    {+      Byte symbol;+      *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);+      symbol = (p->FoundState = Ppmd7Context_OneState(p->MinContext))->Symbol;+      Ppmd7_UpdateBin(p);+      return symbol;+    }+    *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);+    p->InitEsc = PPMD7_kExpEscape[*prob >> 10];+    PPMD_SetAllBitsIn256Bytes(charMask);+    MASK(Ppmd7Context_OneState(p->MinContext)->Symbol) = 0;+    p->PrevSuccess = 0;+  }+  for (;;)+  {+    CPpmd_State *ps[256], *s;+    UInt32 freqSum, count, hiCnt;+    CPpmd_See *see;+    unsigned i, num, numMasked = p->MinContext->NumStats;+    do+    {+      p->OrderFall++;+      if (!p->MinContext->Suffix)+        return -1;+      p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix);+    }+    while (p->MinContext->NumStats == numMasked);+    hiCnt = 0;+    s = Ppmd7_GetStats(p, p->MinContext);+    i = 0;+    num = p->MinContext->NumStats - numMasked;+    do+    {+      int k = (int)(MASK(s->Symbol));+      hiCnt += (s->Freq & k);+      ps[i] = s++;+      i -= k;+    }+    while (i != num);++    see = Ppmd7_MakeEscFreq(p, numMasked, &freqSum);+    freqSum += hiCnt;+    count = rc->GetThreshold(rc, freqSum);++    if (count < hiCnt)+    {+      Byte symbol;+      CPpmd_State **pps = ps;+      for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);+      s = *pps;+      rc->Decode(rc, hiCnt - s->Freq, s->Freq);+      Ppmd_See_Update(see);+      p->FoundState = s;+      symbol = s->Symbol;+      Ppmd7_Update2(p);+      return symbol;+    }+    if (count >= freqSum)+      return -2;+    rc->Decode(rc, hiCnt, freqSum - hiCnt);+    see->Summ = (UInt16)(see->Summ + freqSum);+    do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);+  }+}++/* ---------- Encode ---------- Ppmd7Enc.c */++#define kTopValue (1 << 24)++static void Ppmd7z_RangeEnc_Init(CPpmd7z_RangeEnc *p)+{+  p->Low = 0;+  p->Range = 0xFFFFFFFF;+  p->Cache = 0;+  p->CacheSize = 1;+}++static void RangeEnc_ShiftLow(CPpmd7z_RangeEnc *p)+{+  if ((UInt32)p->Low < (UInt32)0xFF000000 || (unsigned)(p->Low >> 32) != 0)+  {+    Byte temp = p->Cache;+    do+    {+      p->Stream->Write(p->Stream, (Byte)(temp + (Byte)(p->Low >> 32)));+      temp = 0xFF;+    }+    while(--p->CacheSize != 0);+    p->Cache = (Byte)((UInt32)p->Low >> 24);+  }+  p->CacheSize++;+  p->Low = ((UInt32)p->Low << 8) & 0xFFFFFFFF;+}++static void RangeEnc_Encode(CPpmd7z_RangeEnc *p, UInt32 start, UInt32 size, UInt32 total)+{+  p->Low += (UInt64)start * (UInt64)(p->Range /= total);+  p->Range *= size;+  while (p->Range < kTopValue)+  {+    p->Range <<= 8;+    RangeEnc_ShiftLow(p);+  }+}++static void RangeEnc_EncodeBit_0(CPpmd7z_RangeEnc *p, UInt32 size0)+{+  p->Range = (p->Range >> 14) * size0;+  while (p->Range < kTopValue)+  {+    p->Range <<= 8;+    RangeEnc_ShiftLow(p);+  }+}++static void RangeEnc_EncodeBit_1(CPpmd7z_RangeEnc *p, UInt32 size0)+{+  UInt32 newBound = (p->Range >> 14) * size0;+  p->Low += newBound;+  p->Range -= newBound;+  while (p->Range < kTopValue)+  {+    p->Range <<= 8;+    RangeEnc_ShiftLow(p);+  }+}++static void Ppmd7z_RangeEnc_FlushData(CPpmd7z_RangeEnc *p)+{+  unsigned i;+  for (i = 0; i < 5; i++)+    RangeEnc_ShiftLow(p);+}+++#define MASK(sym) ((signed char *)charMask)[sym]++static void Ppmd7_EncodeSymbol(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol)+{+  size_t charMask[256 / sizeof(size_t)];+  if (p->MinContext->NumStats != 1)+  {+    CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext);+    UInt32 sum;+    unsigned i;+    if (s->Symbol == symbol)+    {+      RangeEnc_Encode(rc, 0, s->Freq, p->MinContext->SummFreq);+      p->FoundState = s;+      Ppmd7_Update1_0(p);+      return;+    }+    p->PrevSuccess = 0;+    sum = s->Freq;+    i = p->MinContext->NumStats - 1;+    do+    {+      if ((++s)->Symbol == symbol)+      {+        RangeEnc_Encode(rc, sum, s->Freq, p->MinContext->SummFreq);+        p->FoundState = s;+        Ppmd7_Update1(p);+        return;+      }+      sum += s->Freq;+    }+    while (--i);+    +    p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol];+    PPMD_SetAllBitsIn256Bytes(charMask);+    MASK(s->Symbol) = 0;+    i = p->MinContext->NumStats - 1;+    do { MASK((--s)->Symbol) = 0; } while (--i);+    RangeEnc_Encode(rc, sum, p->MinContext->SummFreq - sum, p->MinContext->SummFreq);+  }+  else+  {+    UInt16 *prob = Ppmd7_GetBinSumm(p);+    CPpmd_State *s = Ppmd7Context_OneState(p->MinContext);+    if (s->Symbol == symbol)+    {+      RangeEnc_EncodeBit_0(rc, *prob);+      *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);+      p->FoundState = s;+      Ppmd7_UpdateBin(p);+      return;+    }+    else+    {+      RangeEnc_EncodeBit_1(rc, *prob);+      *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);+      p->InitEsc = PPMD7_kExpEscape[*prob >> 10];+      PPMD_SetAllBitsIn256Bytes(charMask);+      MASK(s->Symbol) = 0;+      p->PrevSuccess = 0;+    }+  }+  for (;;)+  {+    UInt32 escFreq;+    CPpmd_See *see;+    CPpmd_State *s;+    UInt32 sum;+    unsigned i, numMasked = p->MinContext->NumStats;+    do+    {+      p->OrderFall++;+      if (!p->MinContext->Suffix)+        return; /* EndMarker (symbol = -1) */+      p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix);+    }+    while (p->MinContext->NumStats == numMasked);+    +    see = Ppmd7_MakeEscFreq(p, numMasked, &escFreq);+    s = Ppmd7_GetStats(p, p->MinContext);+    sum = 0;+    i = p->MinContext->NumStats;+    do+    {+      int cur = s->Symbol;+      if (cur == symbol)+      {+        UInt32 low = sum;+        CPpmd_State *s1 = s;+        do+        {+          sum += (s->Freq & (int)(MASK(s->Symbol)));+          s++;+        }+        while (--i);+        RangeEnc_Encode(rc, low, s1->Freq, sum + escFreq);+        Ppmd_See_Update(see);+        p->FoundState = s1;+        Ppmd7_Update2(p);+        return;+      }+      sum += (s->Freq & (int)(MASK(cur)));+      MASK(cur) = 0;+      s++;+    }+    while (--i);+    +    RangeEnc_Encode(rc, sum, escFreq, sum + escFreq);+    see->Summ = (UInt16)(see->Summ + sum + escFreq);+  }+}++const IPpmd7 __archive_ppmd7_functions =+{+  &Ppmd7_Construct,+  &Ppmd7_Alloc,+  &Ppmd7_Free,+  &Ppmd7_Init,+  &Ppmd7z_RangeDec_CreateVTable,+  &PpmdRAR_RangeDec_CreateVTable,+  &Ppmd7z_RangeDec_Init,+  &PpmdRAR_RangeDec_Init,+  &Ppmd7_DecodeSymbol,+  &Ppmd7z_RangeEnc_Init,+  &Ppmd7z_RangeEnc_FlushData,+  &Ppmd7_EncodeSymbol+};
+ c/archive_ppmd7_private.h view
@@ -0,0 +1,119 @@+/* Ppmd7.h -- PPMdH compression codec+2010-03-12 : Igor Pavlov : Public domain+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */++/* This code supports virtual RangeDecoder and includes the implementation+of RangeCoder from 7z, instead of RangeCoder from original PPMd var.H.+If you need the compatibility with original PPMd var.H, you can use external RangeDecoder */++#ifndef ARCHIVE_PPMD7_PRIVATE_H_INCLUDED+#define ARCHIVE_PPMD7_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++#include "archive_ppmd_private.h"++#define PPMD7_MIN_ORDER 2+#define PPMD7_MAX_ORDER 64++#define PPMD7_MIN_MEM_SIZE (1 << 11)+#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFFu - 12 * 3)++struct CPpmd7_Context_;++typedef+  #ifdef PPMD_32BIT+    struct CPpmd7_Context_ *+  #else+    UInt32+  #endif+  CPpmd7_Context_Ref;++typedef struct CPpmd7_Context_+{+  UInt16 NumStats;+  UInt16 SummFreq;+  CPpmd_State_Ref Stats;+  CPpmd7_Context_Ref Suffix;+} CPpmd7_Context;++#define Ppmd7Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)++typedef struct+{+  CPpmd7_Context *MinContext, *MaxContext;+  CPpmd_State *FoundState;+  unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder, HiBitsFlag;+  Int32 RunLength, InitRL; /* must be 32-bit at least */++  UInt32 Size;+  UInt32 GlueCount;+  Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;+  UInt32 AlignOffset;++  Byte Indx2Units[PPMD_NUM_INDEXES];+  Byte Units2Indx[128];+  CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];+  Byte NS2Indx[256], NS2BSIndx[256], HB2Flag[256];+  CPpmd_See DummySee, See[25][16];+  UInt16 BinSumm[128][64];+} CPpmd7;++/* ---------- Decode ---------- */++typedef struct+{+  UInt32 (*GetThreshold)(void *p, UInt32 total);+  void (*Decode)(void *p, UInt32 start, UInt32 size);+  UInt32 (*DecodeBit)(void *p, UInt32 size0);+} IPpmd7_RangeDec;++typedef struct+{+  IPpmd7_RangeDec p;+  UInt32 Range;+  UInt32 Code;+  UInt32 Low;+  UInt32 Bottom;+  IByteIn *Stream;+} CPpmd7z_RangeDec;++/* ---------- Encode ---------- */++typedef struct+{+  UInt64 Low;+  UInt32 Range;+  Byte Cache;+  UInt64 CacheSize;+  IByteOut *Stream;+} CPpmd7z_RangeEnc;++typedef struct+{+  /* Base Functions */+  void (*Ppmd7_Construct)(CPpmd7 *p);+  Bool (*Ppmd7_Alloc)(CPpmd7 *p, UInt32 size);+  void (*Ppmd7_Free)(CPpmd7 *p);+  void (*Ppmd7_Init)(CPpmd7 *p, unsigned maxOrder);+  #define Ppmd7_WasAllocated(p) ((p)->Base != NULL)++  /* Decode Functions */+  void (*Ppmd7z_RangeDec_CreateVTable)(CPpmd7z_RangeDec *p);+  void (*PpmdRAR_RangeDec_CreateVTable)(CPpmd7z_RangeDec *p);+  Bool (*Ppmd7z_RangeDec_Init)(CPpmd7z_RangeDec *p);+  Bool (*PpmdRAR_RangeDec_Init)(CPpmd7z_RangeDec *p);+  #define Ppmd7z_RangeDec_IsFinishedOK(p) ((p)->Code == 0)+  int (*Ppmd7_DecodeSymbol)(CPpmd7 *p, IPpmd7_RangeDec *rc);++  /* Encode Functions */+  void (*Ppmd7z_RangeEnc_Init)(CPpmd7z_RangeEnc *p);+  void (*Ppmd7z_RangeEnc_FlushData)(CPpmd7z_RangeEnc *p);++  void (*Ppmd7_EncodeSymbol)(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol);+} IPpmd7;++extern const IPpmd7 __archive_ppmd7_functions;+#endif
+ c/archive_ppmd8.c view
@@ -0,0 +1,1287 @@+/* Ppmd8.c -- PPMdI codec+2016-05-21 : Igor Pavlov : Public domain+This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */++#include "archive_platform.h"++#include <string.h>++#include "archive_ppmd8_private.h"++const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };+static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};++#define MAX_FREQ 124+#define UNIT_SIZE 12++#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)+#define U2I(nu) (p->Units2Indx[(nu) - 1])+#define I2U(indx) (p->Indx2Units[indx])++#ifdef PPMD_32BIT+  #define REF(ptr) (ptr)+#else+  #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))+#endif++#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))++#define CTX(ref) ((CPpmd8_Context *)Ppmd8_GetContext(p, ref))+#define STATS(ctx) Ppmd8_GetStats(p, ctx)+#define ONE_STATE(ctx) Ppmd8Context_OneState(ctx)+#define SUFFIX(ctx) CTX((ctx)->Suffix)++#define kTop (1 << 24)+#define kBot (1 << 15)++typedef CPpmd8_Context * CTX_PTR;++struct CPpmd8_Node_;++typedef+  #ifdef PPMD_32BIT+    struct CPpmd8_Node_ *+  #else+    UInt32+  #endif+  CPpmd8_Node_Ref;++typedef struct CPpmd8_Node_+{+  UInt32 Stamp;+  CPpmd8_Node_Ref Next;+  UInt32 NU;+} CPpmd8_Node;++#ifdef PPMD_32BIT+  #define NODE(ptr) (ptr)+#else+  #define NODE(offs) ((CPpmd8_Node *)(p->Base + (offs)))+#endif++#define EMPTY_NODE 0xFFFFFFFF++void Ppmd8_Construct(CPpmd8 *p)+{+  unsigned i, k, m;++  p->Base = 0;++  for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)+  {+    unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);+    do { p->Units2Indx[k++] = (Byte)i; } while (--step);+    p->Indx2Units[i] = (Byte)k;+  }++  p->NS2BSIndx[0] = (0 << 1);+  p->NS2BSIndx[1] = (1 << 1);+  memset(p->NS2BSIndx + 2, (2 << 1), 9);+  memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);++  for (i = 0; i < 5; i++)+    p->NS2Indx[i] = (Byte)i;+  for (m = i, k = 1; i < 260; i++)+  {+    p->NS2Indx[i] = (Byte)m;+    if (--k == 0)+      k = (++m) - 4;+  }+}++void Ppmd8_Free(CPpmd8 *p)+{+  free(p->Base);+  p->Size = 0;+  p->Base = 0;+}++Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size)+{+  if (p->Base == 0 || p->Size != size)+  {+    Ppmd8_Free(p);+    p->AlignOffset =+      #ifdef PPMD_32BIT+        (4 - size) & 3;+      #else+        4 - (size & 3);+      #endif+    if ((p->Base = (Byte *)malloc(p->AlignOffset + size)) == 0)+      return False;+    p->Size = size;+  }+  return True;+}++static void InsertNode(CPpmd8 *p, void *node, unsigned indx)+{+  ((CPpmd8_Node *)node)->Stamp = EMPTY_NODE;+  ((CPpmd8_Node *)node)->Next = (CPpmd8_Node_Ref)p->FreeList[indx];+  ((CPpmd8_Node *)node)->NU = I2U(indx);+  p->FreeList[indx] = REF(node);+  p->Stamps[indx]++;+}++static void *RemoveNode(CPpmd8 *p, unsigned indx)+{+  CPpmd8_Node *node = NODE((CPpmd8_Node_Ref)p->FreeList[indx]);+  p->FreeList[indx] = node->Next;+  p->Stamps[indx]--;+  return node;+}++static void SplitBlock(CPpmd8 *p, void *ptr, unsigned oldIndx, unsigned newIndx)+{+  unsigned i, nu = I2U(oldIndx) - I2U(newIndx);+  ptr = (Byte *)ptr + U2B(I2U(newIndx));+  if (I2U(i = U2I(nu)) != nu)+  {+    unsigned k = I2U(--i);+    InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);+  }+  InsertNode(p, ptr, i);+}++static void GlueFreeBlocks(CPpmd8 *p)+{+  CPpmd8_Node_Ref head = 0;+  CPpmd8_Node_Ref *prev = &head;+  unsigned i;++  p->GlueCount = 1 << 13;+  memset(p->Stamps, 0, sizeof(p->Stamps));+  +  /* Order-0 context is always at top UNIT, so we don't need guard NODE at the end.+     All blocks up to p->LoUnit can be free, so we need guard NODE at LoUnit. */+  if (p->LoUnit != p->HiUnit)+    ((CPpmd8_Node *)p->LoUnit)->Stamp = 0;++  /* Glue free blocks */+  for (i = 0; i < PPMD_NUM_INDEXES; i++)+  {+    CPpmd8_Node_Ref next = (CPpmd8_Node_Ref)p->FreeList[i];+    p->FreeList[i] = 0;+    while (next != 0)+    {+      CPpmd8_Node *node = NODE(next);+      if (node->NU != 0)+      {+        CPpmd8_Node *node2;+        *prev = next;+        prev = &(node->Next);+        while ((node2 = node + node->NU)->Stamp == EMPTY_NODE)+        {+          node->NU += node2->NU;+          node2->NU = 0;+        }+      }+      next = node->Next;+    }+  }+  *prev = 0;+  +  /* Fill lists of free blocks */+  while (head != 0)+  {+    CPpmd8_Node *node = NODE(head);+    unsigned nu;+    head = node->Next;+    nu = node->NU;+    if (nu == 0)+      continue;+    for (; nu > 128; nu -= 128, node += 128)+      InsertNode(p, node, PPMD_NUM_INDEXES - 1);+    if (I2U(i = U2I(nu)) != nu)+    {+      unsigned k = I2U(--i);+      InsertNode(p, node + k, nu - k - 1);+    }+    InsertNode(p, node, i);+  }+}++static void *AllocUnitsRare(CPpmd8 *p, unsigned indx)+{+  unsigned i;+  void *retVal;+  if (p->GlueCount == 0)+  {+    GlueFreeBlocks(p);+    if (p->FreeList[indx] != 0)+      return RemoveNode(p, indx);+  }+  i = indx;+  do+  {+    if (++i == PPMD_NUM_INDEXES)+    {+      UInt32 numBytes = U2B(I2U(indx));+      p->GlueCount--;+      return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);+    }+  }+  while (p->FreeList[i] == 0);+  retVal = RemoveNode(p, i);+  SplitBlock(p, retVal, i, indx);+  return retVal;+}++static void *AllocUnits(CPpmd8 *p, unsigned indx)+{+  UInt32 numBytes;+  if (p->FreeList[indx] != 0)+    return RemoveNode(p, indx);+  numBytes = U2B(I2U(indx));+  if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))+  {+    void *retVal = p->LoUnit;+    p->LoUnit += numBytes;+    return retVal;+  }+  return AllocUnitsRare(p, indx);+}++#define MyMem12Cpy(dest, src, num) \+  { UInt32 *d = (UInt32 *)dest; const UInt32 *z = (const UInt32 *)src; UInt32 n = num; \+    do { d[0] = z[0]; d[1] = z[1]; d[2] = z[2]; z += 3; d += 3; } while (--n); }++static void *ShrinkUnits(CPpmd8 *p, void *oldPtr, unsigned oldNU, unsigned newNU)+{+  unsigned i0 = U2I(oldNU);+  unsigned i1 = U2I(newNU);+  if (i0 == i1)+    return oldPtr;+  if (p->FreeList[i1] != 0)+  {+    void *ptr = RemoveNode(p, i1);+    MyMem12Cpy(ptr, oldPtr, newNU);+    InsertNode(p, oldPtr, i0);+    return ptr;+  }+  SplitBlock(p, oldPtr, i0, i1);+  return oldPtr;+}++static void FreeUnits(CPpmd8 *p, void *ptr, unsigned nu)+{+  InsertNode(p, ptr, U2I(nu));+}++static void SpecialFreeUnit(CPpmd8 *p, void *ptr)+{+  if ((Byte *)ptr != p->UnitsStart)+    InsertNode(p, ptr, 0);+  else+  {+    #ifdef PPMD8_FREEZE_SUPPORT+    *(UInt32 *)ptr = EMPTY_NODE; /* it's used for (Flags == 0xFF) check in RemoveBinContexts */+    #endif+    p->UnitsStart += UNIT_SIZE;+  }+}++static void *MoveUnitsUp(CPpmd8 *p, void *oldPtr, unsigned nu)+{+  unsigned indx = U2I(nu);+  void *ptr;+  if ((Byte *)oldPtr > p->UnitsStart + 16 * 1024 || REF(oldPtr) > p->FreeList[indx])+    return oldPtr;+  ptr = RemoveNode(p, indx);+  MyMem12Cpy(ptr, oldPtr, nu);+  if ((Byte*)oldPtr != p->UnitsStart)+    InsertNode(p, oldPtr, indx);+  else+    p->UnitsStart += U2B(I2U(indx));+  return ptr;+}++static void ExpandTextArea(CPpmd8 *p)+{+  UInt32 count[PPMD_NUM_INDEXES];+  unsigned i;+  memset(count, 0, sizeof(count));+  if (p->LoUnit != p->HiUnit)+    ((CPpmd8_Node *)p->LoUnit)->Stamp = 0;+  +  {+    CPpmd8_Node *node = (CPpmd8_Node *)p->UnitsStart;+    for (; node->Stamp == EMPTY_NODE; node += node->NU)+    {+      node->Stamp = 0;+      count[U2I(node->NU)]++;+    }+    p->UnitsStart = (Byte *)node;+  }+  +  for (i = 0; i < PPMD_NUM_INDEXES; i++)+  {+    CPpmd8_Node_Ref *next = (CPpmd8_Node_Ref *)&p->FreeList[i];+    while (count[i] != 0)+    {+      CPpmd8_Node *node = NODE(*next);+      while (node->Stamp == 0)+      {+        *next = node->Next;+        node = NODE(*next);+        p->Stamps[i]--;+        if (--count[i] == 0)+          break;+      }+      next = &node->Next;+    }+  }+}++#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))++static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)+{+  (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);+  (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);+}++#define RESET_TEXT(offs) { p->Text = p->Base + p->AlignOffset + (offs); }++static void RestartModel(CPpmd8 *p)+{+  unsigned i, k, m, r;++  memset(p->FreeList, 0, sizeof(p->FreeList));+  memset(p->Stamps, 0, sizeof(p->Stamps));+  RESET_TEXT(0);+  p->HiUnit = p->Text + p->Size;+  p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;+  p->GlueCount = 0;++  p->OrderFall = p->MaxOrder;+  p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;+  p->PrevSuccess = 0;++  p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */+  p->MinContext->Suffix = 0;+  p->MinContext->NumStats = 255;+  p->MinContext->Flags = 0;+  p->MinContext->SummFreq = 256 + 1;+  p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */+  p->LoUnit += U2B(256 / 2);+  p->MinContext->Stats = REF(p->FoundState);+  for (i = 0; i < 256; i++)+  {+    CPpmd_State *s = &p->FoundState[i];+    s->Symbol = (Byte)i;+    s->Freq = 1;+    SetSuccessor(s, 0);+  }++  for (i = m = 0; m < 25; m++)+  {+    while (p->NS2Indx[i] == m)+      i++;+    for (k = 0; k < 8; k++)+    {+      UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 1));+      UInt16 *dest = p->BinSumm[m] + k;+      for (r = 0; r < 64; r += 8)+        dest[r] = val;+    }+  }++  for (i = m = 0; m < 24; m++)+  {+    while (p->NS2Indx[i + 3] == m + 3)+      i++;+    for (k = 0; k < 32; k++)+    {+      CPpmd_See *s = &p->See[m][k];+      s->Summ = (UInt16)((2 * i + 5) << (s->Shift = PPMD_PERIOD_BITS - 4));+      s->Count = 7;+    }+  }+}++void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod)+{+  p->MaxOrder = maxOrder;+  p->RestoreMethod = restoreMethod;+  RestartModel(p);+  p->DummySee.Shift = PPMD_PERIOD_BITS;+  p->DummySee.Summ = 0; /* unused */+  p->DummySee.Count = 64; /* unused */+}++static void Refresh(CPpmd8 *p, CTX_PTR ctx, unsigned oldNU, unsigned scale)+{+  unsigned i = ctx->NumStats, escFreq, sumFreq, flags;+  CPpmd_State *s = (CPpmd_State *)ShrinkUnits(p, STATS(ctx), oldNU, (i + 2) >> 1);+  ctx->Stats = REF(s);+  #ifdef PPMD8_FREEZE_SUPPORT+  /* fixed over Shkarin's code. Fixed code is not compatible with original code for some files in FREEZE mode. */+  scale |= (ctx->SummFreq >= ((UInt32)1 << 15));+  #endif+  flags = (ctx->Flags & (0x10 + 0x04 * scale)) + 0x08 * (s->Symbol >= 0x40);+  escFreq = ctx->SummFreq - s->Freq;+  sumFreq = (s->Freq = (Byte)((s->Freq + scale) >> scale));+  do+  {+    escFreq -= (++s)->Freq;+    sumFreq += (s->Freq = (Byte)((s->Freq + scale) >> scale));+    flags |= 0x08 * (s->Symbol >= 0x40);+  }+  while (--i);+  ctx->SummFreq = (UInt16)(sumFreq + ((escFreq + scale) >> scale));+  ctx->Flags = (Byte)flags;+}++static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)+{+  CPpmd_State tmp = *t1;+  *t1 = *t2;+  *t2 = tmp;+}++static CPpmd_Void_Ref CutOff(CPpmd8 *p, CTX_PTR ctx, unsigned order)+{+  int i;+  unsigned tmp;+  CPpmd_State *s;+  +  if (!ctx->NumStats)+  {+    s = ONE_STATE(ctx);+    if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart)+    {+      if (order < p->MaxOrder)+        SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));+      else+        SetSuccessor(s, 0);+      if (SUCCESSOR(s) || order <= 9) /* O_BOUND */+        return REF(ctx);+    }+    SpecialFreeUnit(p, ctx);+    return 0;+  }++  ctx->Stats = STATS_REF(MoveUnitsUp(p, STATS(ctx), tmp = ((unsigned)ctx->NumStats + 2) >> 1));++  for (s = STATS(ctx) + (i = ctx->NumStats); s >= STATS(ctx); s--)+    if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) < p->UnitsStart)+    {+      CPpmd_State *s2 = STATS(ctx) + (i--);+      SetSuccessor(s, 0);+      SwapStates(s, s2);+    }+    else if (order < p->MaxOrder)+      SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));+    else+      SetSuccessor(s, 0);+    +  if (i != ctx->NumStats && order)+  {+    ctx->NumStats = (Byte)i;+    s = STATS(ctx);+    if (i < 0)+    {+      FreeUnits(p, s, tmp);+      SpecialFreeUnit(p, ctx);+      return 0;+    }+    if (i == 0)+    {+      ctx->Flags = (Byte)((ctx->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40));+      *ONE_STATE(ctx) = *s;+      FreeUnits(p, s, tmp);+      /* 9.31: the code was fixed. It's was not BUG, if Freq <= MAX_FREQ = 124 */+      ONE_STATE(ctx)->Freq = (Byte)(((unsigned)ONE_STATE(ctx)->Freq + 11) >> 3);+    }+    else+      Refresh(p, ctx, tmp, ctx->SummFreq > 16 * i);+  }+  return REF(ctx);+}++#ifdef PPMD8_FREEZE_SUPPORT+static CPpmd_Void_Ref RemoveBinContexts(CPpmd8 *p, CTX_PTR ctx, unsigned order)+{+  CPpmd_State *s;+  if (!ctx->NumStats)+  {+    s = ONE_STATE(ctx);+    if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)+      SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));+    else+      SetSuccessor(s, 0);+    /* Suffix context can be removed already, since different (high-order)+       Successors may refer to same context. So we check Flags == 0xFF (Stamp == EMPTY_NODE) */+    if (!SUCCESSOR(s) && (!SUFFIX(ctx)->NumStats || SUFFIX(ctx)->Flags == 0xFF))+    {+      FreeUnits(p, ctx, 1);+      return 0;+    }+    else+      return REF(ctx);+  }++  for (s = STATS(ctx) + ctx->NumStats; s >= STATS(ctx); s--)+    if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)+      SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));+    else+      SetSuccessor(s, 0);+  +  return REF(ctx);+}+#endif++static UInt32 GetUsedMemory(const CPpmd8 *p)+{+  UInt32 v = 0;+  unsigned i;+  for (i = 0; i < PPMD_NUM_INDEXES; i++)+    v += p->Stamps[i] * I2U(i);+  return p->Size - (UInt32)(p->HiUnit - p->LoUnit) - (UInt32)(p->UnitsStart - p->Text) - U2B(v);+}++#ifdef PPMD8_FREEZE_SUPPORT+  #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1, fSuccessor)+#else+  #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1)+#endif++static void RestoreModel(CPpmd8 *p, CTX_PTR c1+    #ifdef PPMD8_FREEZE_SUPPORT+    , CTX_PTR fSuccessor+    #endif+    )+{+  CTX_PTR c;+  CPpmd_State *s;+  RESET_TEXT(0);+  for (c = p->MaxContext; c != c1; c = SUFFIX(c))+    if (--(c->NumStats) == 0)+    {+      s = STATS(c);+      c->Flags = (Byte)((c->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40));+      *ONE_STATE(c) = *s;+      SpecialFreeUnit(p, s);+      ONE_STATE(c)->Freq = (Byte)(((unsigned)ONE_STATE(c)->Freq + 11) >> 3);+    }+    else+      Refresh(p, c, (c->NumStats+3) >> 1, 0);+ +  for (; c != p->MinContext; c = SUFFIX(c))+    if (!c->NumStats)+      ONE_STATE(c)->Freq = (Byte)(ONE_STATE(c)->Freq - (ONE_STATE(c)->Freq >> 1));+    else if ((c->SummFreq += 4) > 128 + 4 * c->NumStats)+      Refresh(p, c, (c->NumStats + 2) >> 1, 1);++  #ifdef PPMD8_FREEZE_SUPPORT+  if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)+  {+    p->MaxContext = fSuccessor;+    p->GlueCount += !(p->Stamps[1] & 1);+  }+  else if (p->RestoreMethod == PPMD8_RESTORE_METHOD_FREEZE)+  {+    while (p->MaxContext->Suffix)+      p->MaxContext = SUFFIX(p->MaxContext);+    RemoveBinContexts(p, p->MaxContext, 0);+    p->RestoreMethod++;+    p->GlueCount = 0;+    p->OrderFall = p->MaxOrder;+  }+  else+  #endif+  if (p->RestoreMethod == PPMD8_RESTORE_METHOD_RESTART || GetUsedMemory(p) < (p->Size >> 1))+    RestartModel(p);+  else+  {+    while (p->MaxContext->Suffix)+      p->MaxContext = SUFFIX(p->MaxContext);+    do+    {+      CutOff(p, p->MaxContext, 0);+      ExpandTextArea(p);+    }+    while (GetUsedMemory(p) > 3 * (p->Size >> 2));+    p->GlueCount = 0;+    p->OrderFall = p->MaxOrder;+  }+}++static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c)+{+  CPpmd_State upState;+  Byte flags;+  CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);+  /* fixed over Shkarin's code. Maybe it could work without + 1 too. */+  CPpmd_State *ps[PPMD8_MAX_ORDER + 1];+  unsigned numPs = 0;+  +  if (!skip)+    ps[numPs++] = p->FoundState;+  +  while (c->Suffix)+  {+    CPpmd_Void_Ref successor;+    CPpmd_State *s;+    c = SUFFIX(c);+    if (s1)+    {+      s = s1;+      s1 = NULL;+    }+    else if (c->NumStats != 0)+    {+      for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);+      if (s->Freq < MAX_FREQ - 9)+      {+        s->Freq++;+        c->SummFreq++;+      }+    }+    else+    {+      s = ONE_STATE(c);+      s->Freq = (Byte)(s->Freq + (!SUFFIX(c)->NumStats & (s->Freq < 24)));+    }+    successor = SUCCESSOR(s);+    if (successor != upBranch)+    {+      c = CTX(successor);+      if (numPs == 0)+        return c;+      break;+    }+    ps[numPs++] = s;+  }+  +  upState.Symbol = *(const Byte *)Ppmd8_GetPtr(p, upBranch);+  SetSuccessor(&upState, upBranch + 1);+  flags = (Byte)(0x10 * (p->FoundState->Symbol >= 0x40) + 0x08 * (upState.Symbol >= 0x40));++  if (c->NumStats == 0)+    upState.Freq = ONE_STATE(c)->Freq;+  else+  {+    UInt32 cf, s0;+    CPpmd_State *s;+    for (s = STATS(c); s->Symbol != upState.Symbol; s++);+    cf = s->Freq - 1;+    s0 = c->SummFreq - c->NumStats - cf;+    upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0)));+  }++  do+  {+    /* Create Child */+    CTX_PTR c1; /* = AllocContext(p); */+    if (p->HiUnit != p->LoUnit)+      c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);+    else if (p->FreeList[0] != 0)+      c1 = (CTX_PTR)RemoveNode(p, 0);+    else+    {+      c1 = (CTX_PTR)AllocUnitsRare(p, 0);+      if (!c1)+        return NULL;+    }+    c1->NumStats = 0;+    c1->Flags = flags;+    *ONE_STATE(c1) = upState;+    c1->Suffix = REF(c);+    SetSuccessor(ps[--numPs], REF(c1));+    c = c1;+  }+  while (numPs != 0);+  +  return c;+}++static CTX_PTR ReduceOrder(CPpmd8 *p, CPpmd_State *s1, CTX_PTR c)+{+  CPpmd_State *s = NULL;+  CTX_PTR c1 = c;+  CPpmd_Void_Ref upBranch = REF(p->Text);+  +  #ifdef PPMD8_FREEZE_SUPPORT+  /* The BUG in Shkarin's code was fixed: ps could overflow in CUT_OFF mode. */+  CPpmd_State *ps[PPMD8_MAX_ORDER + 1];+  unsigned numPs = 0;+  ps[numPs++] = p->FoundState;+  #endif++  SetSuccessor(p->FoundState, upBranch);+  p->OrderFall++;++  for (;;)+  {+    if (s1)+    {+      c = SUFFIX(c);+      s = s1;+      s1 = NULL;+    }+    else+    {+      if (!c->Suffix)+      {+        #ifdef PPMD8_FREEZE_SUPPORT+        if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)+        {+          do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);+          RESET_TEXT(1);+          p->OrderFall = 1;+        }+        #endif+        return c;+      }+      c = SUFFIX(c);+      if (c->NumStats)+      {+        if ((s = STATS(c))->Symbol != p->FoundState->Symbol)+          do { s++; } while (s->Symbol != p->FoundState->Symbol);+        if (s->Freq < MAX_FREQ - 9)+        {+          s->Freq += 2;+          c->SummFreq += 2;+        }+      }+      else+      {+        s = ONE_STATE(c);+        s->Freq = (Byte)(s->Freq + (s->Freq < 32));+      }+    }+    if (SUCCESSOR(s))+      break;+    #ifdef PPMD8_FREEZE_SUPPORT+    ps[numPs++] = s;+    #endif+    SetSuccessor(s, upBranch);+    p->OrderFall++;+  }+  +  #ifdef PPMD8_FREEZE_SUPPORT+  if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)+  {+    c = CTX(SUCCESSOR(s));+    do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);+    RESET_TEXT(1);+    p->OrderFall = 1;+    return c;+  }+  else+  #endif+  if (SUCCESSOR(s) <= upBranch)+  {+    CTX_PTR successor;+    CPpmd_State *s2 = p->FoundState;+    p->FoundState = s;++    successor = CreateSuccessors(p, False, NULL, c);+    if (successor == NULL)+      SetSuccessor(s, 0);+    else+      SetSuccessor(s, REF(successor));+    p->FoundState = s2;+  }+  +  if (p->OrderFall == 1 && c1 == p->MaxContext)+  {+    SetSuccessor(p->FoundState, SUCCESSOR(s));+    p->Text--;+  }+  if (SUCCESSOR(s) == 0)+    return NULL;+  return CTX(SUCCESSOR(s));+}++static void UpdateModel(CPpmd8 *p)+{+  CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);+  CTX_PTR c;+  unsigned s0, ns, fFreq = p->FoundState->Freq;+  Byte flag, fSymbol = p->FoundState->Symbol;+  CPpmd_State *s = NULL;+  +  if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)+  {+    c = SUFFIX(p->MinContext);+    +    if (c->NumStats == 0)+    {+      s = ONE_STATE(c);+      if (s->Freq < 32)+        s->Freq++;+    }+    else+    {+      s = STATS(c);+      if (s->Symbol != p->FoundState->Symbol)+      {+        do { s++; } while (s->Symbol != p->FoundState->Symbol);+        if (s[0].Freq >= s[-1].Freq)+        {+          SwapStates(&s[0], &s[-1]);+          s--;+        }+      }+      if (s->Freq < MAX_FREQ - 9)+      {+        s->Freq += 2;+        c->SummFreq += 2;+      }+    }+  }+  +  c = p->MaxContext;+  if (p->OrderFall == 0 && fSuccessor)+  {+    CTX_PTR cs = CreateSuccessors(p, True, s, p->MinContext);+    if (cs == 0)+    {+      SetSuccessor(p->FoundState, 0);+      RESTORE_MODEL(c, CTX(fSuccessor));+    }+    else+    {+      SetSuccessor(p->FoundState, REF(cs));+      p->MaxContext = cs;+    }+    return;+  }+  +  *p->Text++ = p->FoundState->Symbol;+  successor = REF(p->Text);+  if (p->Text >= p->UnitsStart)+  {+    RESTORE_MODEL(c, CTX(fSuccessor)); /* check it */+    return;+  }+  +  if (!fSuccessor)+  {+    CTX_PTR cs = ReduceOrder(p, s, p->MinContext);+    if (cs == NULL)+    {+      RESTORE_MODEL(c, 0);+      return;+    }+    fSuccessor = REF(cs);+  }+  else if ((Byte *)Ppmd8_GetPtr(p, fSuccessor) < p->UnitsStart)+  {+    CTX_PTR cs = CreateSuccessors(p, False, s, p->MinContext);+    if (cs == NULL)+    {+      RESTORE_MODEL(c, 0);+      return;+    }+    fSuccessor = REF(cs);+  }+  +  if (--p->OrderFall == 0)+  {+    successor = fSuccessor;+    p->Text -= (p->MaxContext != p->MinContext);+  }+  #ifdef PPMD8_FREEZE_SUPPORT+  else if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)+  {+    successor = fSuccessor;+    RESET_TEXT(0);+    p->OrderFall = 0;+  }+  #endif+  +  s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - fFreq;+  flag = (Byte)(0x08 * (fSymbol >= 0x40));+  +  for (; c != p->MinContext; c = SUFFIX(c))+  {+    unsigned ns1;+    UInt32 cf, sf;+    if ((ns1 = c->NumStats) != 0)+    {+      if ((ns1 & 1) != 0)+      {+        /* Expand for one UNIT */+        unsigned oldNU = (ns1 + 1) >> 1;+        unsigned i = U2I(oldNU);+        if (i != U2I(oldNU + 1))+        {+          void *ptr = AllocUnits(p, i + 1);+          void *oldPtr;+          if (!ptr)+          {+            RESTORE_MODEL(c, CTX(fSuccessor));+            return;+          }+          oldPtr = STATS(c);+          MyMem12Cpy(ptr, oldPtr, oldNU);+          InsertNode(p, oldPtr, i);+          c->Stats = STATS_REF(ptr);+        }+      }+      c->SummFreq = (UInt16)(c->SummFreq + (3 * ns1 + 1 < ns));+    }+    else+    {+      CPpmd_State *s2 = (CPpmd_State*)AllocUnits(p, 0);+      if (!s2)+      {+        RESTORE_MODEL(c, CTX(fSuccessor));+        return;+      }+      *s2 = *ONE_STATE(c);+      c->Stats = REF(s2);+      if (s2->Freq < MAX_FREQ / 4 - 1)+        s2->Freq <<= 1;+      else+        s2->Freq = MAX_FREQ - 4;+      c->SummFreq = (UInt16)(s2->Freq + p->InitEsc + (ns > 2));+    }+    cf = 2 * fFreq * (c->SummFreq + 6);+    sf = (UInt32)s0 + c->SummFreq;+    if (cf < 6 * sf)+    {+      cf = 1 + (cf > sf) + (cf >= 4 * sf);+      c->SummFreq += 4;+    }+    else+    {+      cf = 4 + (cf > 9 * sf) + (cf > 12 * sf) + (cf > 15 * sf);+      c->SummFreq = (UInt16)(c->SummFreq + cf);+    }+    {+      CPpmd_State *s2 = STATS(c) + ns1 + 1;+      SetSuccessor(s2, successor);+      s2->Symbol = fSymbol;+      s2->Freq = (Byte)cf;+      c->Flags |= flag;+      c->NumStats = (Byte)(ns1 + 1);+    }+  }+  p->MaxContext = p->MinContext = CTX(fSuccessor);+}+  +static void Rescale(CPpmd8 *p)+{+  unsigned i, adder, sumFreq, escFreq;+  CPpmd_State *stats = STATS(p->MinContext);+  CPpmd_State *s = p->FoundState;+  {+    CPpmd_State tmp = *s;+    for (; s != stats; s--)+      s[0] = s[-1];+    *s = tmp;+  }+  escFreq = p->MinContext->SummFreq - s->Freq;+  s->Freq += 4;+  adder = (p->OrderFall != 0+      #ifdef PPMD8_FREEZE_SUPPORT+      || p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE+      #endif+      );+  s->Freq = (Byte)((s->Freq + adder) >> 1);+  sumFreq = s->Freq;+  +  i = p->MinContext->NumStats;+  do+  {+    escFreq -= (++s)->Freq;+    s->Freq = (Byte)((s->Freq + adder) >> 1);+    sumFreq += s->Freq;+    if (s[0].Freq > s[-1].Freq)+    {+      CPpmd_State *s1 = s;+      CPpmd_State tmp = *s1;+      do+        s1[0] = s1[-1];+      while (--s1 != stats && tmp.Freq > s1[-1].Freq);+      *s1 = tmp;+    }+  }+  while (--i);+  +  if (s->Freq == 0)+  {+    unsigned numStats = p->MinContext->NumStats;+    unsigned n0, n1;+    do { i++; } while ((--s)->Freq == 0);+    escFreq += i;+    p->MinContext->NumStats = (Byte)(p->MinContext->NumStats - i);+    if (p->MinContext->NumStats == 0)+    {+      CPpmd_State tmp = *stats;+      tmp.Freq = (Byte)((2 * tmp.Freq + escFreq - 1) / escFreq);+      if (tmp.Freq > MAX_FREQ / 3)+        tmp.Freq = MAX_FREQ / 3;+      InsertNode(p, stats, U2I((numStats + 2) >> 1));+      p->MinContext->Flags = (Byte)((p->MinContext->Flags & 0x10) + 0x08 * (tmp.Symbol >= 0x40));+      *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;+      return;+    }+    n0 = (numStats + 2) >> 1;+    n1 = (p->MinContext->NumStats + 2) >> 1;+    if (n0 != n1)+      p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));+    p->MinContext->Flags &= ~0x08;+    p->MinContext->Flags |= 0x08 * ((s = STATS(p->MinContext))->Symbol >= 0x40);+    i = p->MinContext->NumStats;+    do { p->MinContext->Flags |= 0x08*((++s)->Symbol >= 0x40); } while (--i);+  }+  p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));+  p->MinContext->Flags |= 0x4;+  p->FoundState = STATS(p->MinContext);+}++CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq)+{+  CPpmd_See *see;+  if (p->MinContext->NumStats != 0xFF)+  {+    see = p->See[(unsigned)p->NS2Indx[(unsigned)p->MinContext->NumStats + 2] - 3] ++        (p->MinContext->SummFreq > 11 * ((unsigned)p->MinContext->NumStats + 1)) ++        2 * (unsigned)(2 * (unsigned)p->MinContext->NumStats <+        ((unsigned)SUFFIX(p->MinContext)->NumStats + numMasked1)) ++        p->MinContext->Flags;+    {+      unsigned r = (see->Summ >> see->Shift);+      see->Summ = (UInt16)(see->Summ - r);+      *escFreq = r + (r == 0);+    }+  }+  else+  {+    see = &p->DummySee;+    *escFreq = 1;+  }+  return see;+}++static void NextContext(CPpmd8 *p)+{+  CTX_PTR c = CTX(SUCCESSOR(p->FoundState));+  if (p->OrderFall == 0 && (Byte *)c >= p->UnitsStart)+    p->MinContext = p->MaxContext = c;+  else+  {+    UpdateModel(p);+    p->MinContext = p->MaxContext;+  }+}++void Ppmd8_Update1(CPpmd8 *p)+{+  CPpmd_State *s = p->FoundState;+  s->Freq += 4;+  p->MinContext->SummFreq += 4;+  if (s[0].Freq > s[-1].Freq)+  {+    SwapStates(&s[0], &s[-1]);+    p->FoundState = --s;+    if (s->Freq > MAX_FREQ)+      Rescale(p);+  }+  NextContext(p);+}++void Ppmd8_Update1_0(CPpmd8 *p)+{+  p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq);+  p->RunLength += p->PrevSuccess;+  p->MinContext->SummFreq += 4;+  if ((p->FoundState->Freq += 4) > MAX_FREQ)+    Rescale(p);+  NextContext(p);+}++void Ppmd8_UpdateBin(CPpmd8 *p)+{+  p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196));+  p->PrevSuccess = 1;+  p->RunLength++;+  NextContext(p);+}++void Ppmd8_Update2(CPpmd8 *p)+{+  p->MinContext->SummFreq += 4;+  if ((p->FoundState->Freq += 4) > MAX_FREQ)+    Rescale(p);+  p->RunLength = p->InitRL;+  UpdateModel(p);+  p->MinContext = p->MaxContext;+}++/* Ppmd8Dec.c -- PPMdI Decoder+2010-04-16 : Igor Pavlov : Public domain+This code is based on:+  PPMd var.I (2002): Dmitry Shkarin : Public domain+  Carryless rangecoder (1999): Dmitry Subbotin : Public domain */++Bool Ppmd8_RangeDec_Init(CPpmd8 *p)+{+  unsigned i;+  p->Low = 0;+  p->Range = 0xFFFFFFFF;+  p->Code = 0;+  for (i = 0; i < 4; i++)+    p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In);+  return (p->Code < 0xFFFFFFFF);+}++static UInt32 RangeDec_GetThreshold(CPpmd8 *p, UInt32 total)+{+  return p->Code / (p->Range /= total);+}++static void RangeDec_Decode(CPpmd8 *p, UInt32 start, UInt32 size)+{+  start *= p->Range;+  p->Low += start;+  p->Code -= start;+  p->Range *= size;++  while ((p->Low ^ (p->Low + p->Range)) < kTop ||+      (p->Range < kBot && ((p->Range = (0 - p->Low) & (kBot - 1)), 1)))+  {+    p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In);+    p->Range <<= 8;+    p->Low <<= 8;+  }+}++#define MASK(sym) ((signed char *)charMask)[sym]++int Ppmd8_DecodeSymbol(CPpmd8 *p)+{+  size_t charMask[256 / sizeof(size_t)];+  if (p->MinContext->NumStats != 0)+  {+    CPpmd_State *s = Ppmd8_GetStats(p, p->MinContext);+    unsigned i;+    UInt32 count, hiCnt;+    if ((count = RangeDec_GetThreshold(p, p->MinContext->SummFreq)) < (hiCnt = s->Freq))+    {+      Byte symbol;+      RangeDec_Decode(p, 0, s->Freq);+      p->FoundState = s;+      symbol = s->Symbol;+      Ppmd8_Update1_0(p);+      return symbol;+    }+    p->PrevSuccess = 0;+    i = p->MinContext->NumStats;+    do+    {+      if ((hiCnt += (++s)->Freq) > count)+      {+        Byte symbol;+        RangeDec_Decode(p, hiCnt - s->Freq, s->Freq);+        p->FoundState = s;+        symbol = s->Symbol;+        Ppmd8_Update1(p);+        return symbol;+      }+    }+    while (--i);+    if (count >= p->MinContext->SummFreq)+      return -2;+    RangeDec_Decode(p, hiCnt, p->MinContext->SummFreq - hiCnt);+    PPMD_SetAllBitsIn256Bytes(charMask);+    MASK(s->Symbol) = 0;+    i = p->MinContext->NumStats;+    do { MASK((--s)->Symbol) = 0; } while (--i);+  }+  else+  {+    UInt16 *prob = Ppmd8_GetBinSumm(p);+    if (((p->Code / (p->Range >>= 14)) < *prob))+    {+      Byte symbol;+      RangeDec_Decode(p, 0, *prob);+      *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);+      symbol = (p->FoundState = Ppmd8Context_OneState(p->MinContext))->Symbol;+      Ppmd8_UpdateBin(p);+      return symbol;+    }+    RangeDec_Decode(p, *prob, (1 << 14) - *prob);+    *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);+    p->InitEsc = PPMD8_kExpEscape[*prob >> 10];+    PPMD_SetAllBitsIn256Bytes(charMask);+    MASK(Ppmd8Context_OneState(p->MinContext)->Symbol) = 0;+    p->PrevSuccess = 0;+  }+  for (;;)+  {+    CPpmd_State *ps[256], *s;+    UInt32 freqSum, count, hiCnt;+    CPpmd_See *see;+    unsigned i, num, numMasked = p->MinContext->NumStats;+    do+    {+      p->OrderFall++;+      if (!p->MinContext->Suffix)+        return -1;+      p->MinContext = Ppmd8_GetContext(p, p->MinContext->Suffix);+    }+    while (p->MinContext->NumStats == numMasked);+    hiCnt = 0;+    s = Ppmd8_GetStats(p, p->MinContext);+    i = 0;+    num = p->MinContext->NumStats - numMasked;+    do+    {+      int k = (int)(MASK(s->Symbol));+      hiCnt += (s->Freq & k);+      ps[i] = s++;+      i -= k;+    }+    while (i != num);+    +    see = Ppmd8_MakeEscFreq(p, numMasked, &freqSum);+    freqSum += hiCnt;+    count = RangeDec_GetThreshold(p, freqSum);+    +    if (count < hiCnt)+    {+      Byte symbol;+      CPpmd_State **pps = ps;+      for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);+      s = *pps;+      RangeDec_Decode(p, hiCnt - s->Freq, s->Freq);+      Ppmd_See_Update(see);+      p->FoundState = s;+      symbol = s->Symbol;+      Ppmd8_Update2(p);+      return symbol;+    }+    if (count >= freqSum)+      return -2;+    RangeDec_Decode(p, hiCnt, freqSum - hiCnt);+    see->Summ = (UInt16)(see->Summ + freqSum);+    do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);+  }+}++/* H->I changes:+  NS2Indx+  GlewCount, and Glue method+  BinSum+  See / EscFreq+  CreateSuccessors updates more suffix contexts+  UpdateModel consts.+  PrevSuccess Update+*/++const IPpmd8 __archive_ppmd8_functions =+{+  &Ppmd8_Construct,+  &Ppmd8_Alloc,+  &Ppmd8_Free,+  &Ppmd8_Init,+  &Ppmd8_RangeDec_Init,+  &Ppmd8_DecodeSymbol,+};
+ c/archive_ppmd8_private.h view
@@ -0,0 +1,148 @@+/* Ppmd8.h -- PPMdI codec+2011-01-27 : Igor Pavlov : Public domain+This code is based on:+  PPMd var.I (2002): Dmitry Shkarin : Public domain+  Carryless rangecoder (1999): Dmitry Subbotin : Public domain */++#ifndef ARCHIVE_PPMD8_PRIVATE_H_INCLUDED+#define ARCHIVE_PPMD8_PRIVATE_H_INCLUDED++#include "archive_ppmd_private.h"++#define PPMD8_MIN_ORDER 2+#define PPMD8_MAX_ORDER 16++struct CPpmd8_Context_;++typedef+  #ifdef PPMD_32BIT+    struct CPpmd8_Context_ *+  #else+    UInt32+  #endif+  CPpmd8_Context_Ref;++#pragma pack(push, 1)++typedef struct CPpmd8_Context_+{+  Byte NumStats;+  Byte Flags;+  UInt16 SummFreq;+  CPpmd_State_Ref Stats;+  CPpmd8_Context_Ref Suffix;+} CPpmd8_Context;++#pragma pack(pop)++#define Ppmd8Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)++/* The BUG in Shkarin's code for FREEZE mode was fixed, but that fixed+   code is not compatible with original code for some files compressed+   in FREEZE mode. So we disable FREEZE mode support. */++enum+{+  PPMD8_RESTORE_METHOD_RESTART,+  PPMD8_RESTORE_METHOD_CUT_OFF+  #ifdef PPMD8_FREEZE_SUPPORT+  , PPMD8_RESTORE_METHOD_FREEZE+  #endif+};++typedef struct+{+  CPpmd8_Context *MinContext, *MaxContext;+  CPpmd_State *FoundState;+  unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder;+  Int32 RunLength, InitRL; /* must be 32-bit at least */++  UInt32 Size;+  UInt32 GlueCount;+  Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;+  UInt32 AlignOffset;+  unsigned RestoreMethod;++  /* Range Coder */+  UInt32 Range;+  UInt32 Code;+  UInt32 Low;+  union+  {+    IByteIn *In;+    IByteOut *Out;+  } Stream;++  Byte Indx2Units[PPMD_NUM_INDEXES];+  Byte Units2Indx[128];+  CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];+  UInt32 Stamps[PPMD_NUM_INDEXES];++  Byte NS2BSIndx[256], NS2Indx[260];+  CPpmd_See DummySee, See[24][32];+  UInt16 BinSumm[25][64];+} CPpmd8;++void Ppmd8_Construct(CPpmd8 *p);+Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size);+void Ppmd8_Free(CPpmd8 *p);+void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod);+#define Ppmd8_WasAllocated(p) ((p)->Base != NULL)+++/* ---------- Internal Functions ---------- */++extern const Byte PPMD8_kExpEscape[16];++#ifdef PPMD_32BIT+  #define Ppmd8_GetPtr(p, ptr) (ptr)+  #define Ppmd8_GetContext(p, ptr) (ptr)+  #define Ppmd8_GetStats(p, ctx) ((ctx)->Stats)+#else+  #define Ppmd8_GetPtr(p, offs) ((void *)((p)->Base + (offs)))+  #define Ppmd8_GetContext(p, offs) ((CPpmd8_Context *)Ppmd8_GetPtr((p), (offs)))+  #define Ppmd8_GetStats(p, ctx) ((CPpmd_State *)Ppmd8_GetPtr((p), ((ctx)->Stats)))+#endif++void Ppmd8_Update1(CPpmd8 *p);+void Ppmd8_Update1_0(CPpmd8 *p);+void Ppmd8_Update2(CPpmd8 *p);+void Ppmd8_UpdateBin(CPpmd8 *p);++#define Ppmd8_GetBinSumm(p) \+    &p->BinSumm[p->NS2Indx[Ppmd8Context_OneState(p->MinContext)->Freq - 1]][ \+    p->NS2BSIndx[Ppmd8_GetContext(p, p->MinContext->Suffix)->NumStats] + \+    p->PrevSuccess + p->MinContext->Flags + ((p->RunLength >> 26) & 0x20)]++CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked, UInt32 *scale);+++/* ---------- Decode ---------- */++Bool Ppmd8_RangeDec_Init(CPpmd8 *p);+#define Ppmd8_RangeDec_IsFinishedOK(p) ((p)->Code == 0)+int Ppmd8_DecodeSymbol(CPpmd8 *p); /* returns: -1 as EndMarker, -2 as DataError */++/* ---------- Encode ---------- */++#define Ppmd8_RangeEnc_Init(p) { (p)->Low = 0; (p)->Range = 0xFFFFFFFF; }+void Ppmd8_RangeEnc_FlushData(CPpmd8 *p);+void Ppmd8_EncodeSymbol(CPpmd8 *p, int symbol); /* symbol = -1 means EndMarker */++typedef struct+{+  /* Base Functions */+  void (*Ppmd8_Construct)(CPpmd8 *p);+  Bool (*Ppmd8_Alloc)(CPpmd8 *p, UInt32 size);+  void (*Ppmd8_Free)(CPpmd8 *p);+  void (*Ppmd8_Init)(CPpmd8 *p, unsigned max_order, unsigned restore_method);+  #define Ppmd7_WasAllocated(p) ((p)->Base != NULL)++  /* Decode Functions */+  int (*Ppmd8_RangeDec_Init)(CPpmd8 *p);+  int (*Ppmd8_DecodeSymbol)(CPpmd8 *p);+} IPpmd8;++extern const IPpmd8 __archive_ppmd8_functions;++#endif
+ c/archive_ppmd_private.h view
@@ -0,0 +1,151 @@+/* Ppmd.h -- PPMD codec common code+2010-03-12 : Igor Pavlov : Public domain+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */++#ifndef ARCHIVE_PPMD_PRIVATE_H_INCLUDED+#define ARCHIVE_PPMD_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++#include <stddef.h>++#include "archive_read_private.h"++/*** Begin defined in Types.h ***/++#if !defined(ZCONF_H)+typedef unsigned char Byte;+#endif+typedef short Int16;+typedef unsigned short UInt16;++#ifdef _LZMA_UINT32_IS_ULONG+typedef long Int32;+typedef unsigned long UInt32;+#else+typedef int Int32;+typedef unsigned int UInt32;+#endif++#ifdef _SZ_NO_INT_64++/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.+   NOTES: Some code will work incorrectly in that case! */++typedef long Int64;+typedef unsigned long UInt64;++#else++#if defined(_MSC_VER) || defined(__BORLANDC__)+typedef __int64 Int64;+typedef unsigned __int64 UInt64;+#define UINT64_CONST(n) n+#else+typedef long long int Int64;+typedef unsigned long long int UInt64;+#define UINT64_CONST(n) n ## ULL+#endif++#endif++typedef int Bool;+#define True 1+#define False 0++/* The following interfaces use first parameter as pointer to structure */++typedef struct+{+  struct archive_read *a;+  Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */+} IByteIn;++typedef struct+{+  struct archive_write *a;+  void (*Write)(void *p, Byte b);+} IByteOut;++/*** End defined in Types.h ***/+/*** Begin defined in CpuArch.h ***/++#if defined(_M_IX86) || defined(__i386__)+#define MY_CPU_X86+#endif++#if defined(MY_CPU_X86) || defined(_M_ARM)+#define MY_CPU_32BIT+#endif++#ifdef MY_CPU_32BIT+#define PPMD_32BIT+#endif++/*** End defined in CpuArch.h ***/++#define PPMD_INT_BITS 7+#define PPMD_PERIOD_BITS 7+#define PPMD_BIN_SCALE (1 << (PPMD_INT_BITS + PPMD_PERIOD_BITS))++#define PPMD_GET_MEAN_SPEC(summ, shift, round) (((summ) + (1 << ((shift) - (round)))) >> (shift))+#define PPMD_GET_MEAN(summ) PPMD_GET_MEAN_SPEC((summ), PPMD_PERIOD_BITS, 2)+#define PPMD_UPDATE_PROB_0(prob) ((prob) + (1 << PPMD_INT_BITS) - PPMD_GET_MEAN(prob))+#define PPMD_UPDATE_PROB_1(prob) ((prob) - PPMD_GET_MEAN(prob))++#define PPMD_N1 4+#define PPMD_N2 4+#define PPMD_N3 4+#define PPMD_N4 ((128 + 3 - 1 * PPMD_N1 - 2 * PPMD_N2 - 3 * PPMD_N3) / 4)+#define PPMD_NUM_INDEXES (PPMD_N1 + PPMD_N2 + PPMD_N3 + PPMD_N4)++/* SEE-contexts for PPM-contexts with masked symbols */+typedef struct+{+  UInt16 Summ; /* Freq */+  Byte Shift;  /* Speed of Freq change; low Shift is for fast change */+  Byte Count;  /* Count to next change of Shift */+} CPpmd_See;++#define Ppmd_See_Update(p)  if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) \+    { (p)->Summ <<= 1; (p)->Count = (Byte)(3 << (p)->Shift++); }++typedef struct+{+  Byte Symbol;+  Byte Freq;+  UInt16 SuccessorLow;+  UInt16 SuccessorHigh;+} CPpmd_State;++typedef+  #ifdef PPMD_32BIT+    CPpmd_State *+  #else+    UInt32+  #endif+  CPpmd_State_Ref;++typedef+  #ifdef PPMD_32BIT+    void *+  #else+    UInt32+  #endif+  CPpmd_Void_Ref;++typedef+  #ifdef PPMD_32BIT+    Byte *+  #else+    UInt32+  #endif+  CPpmd_Byte_Ref;++#define PPMD_SetAllBitsIn256Bytes(p) \+  { unsigned j; for (j = 0; j < 256 / sizeof(p[0]); j += 8) { \+  p[j+7] = p[j+6] = p[j+5] = p[j+4] = p[j+3] = p[j+2] = p[j+1] = p[j+0] = ~(size_t)0; }}++#endif
+ c/archive_private.h view
@@ -0,0 +1,176 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/archive_private.h 201098 2009-12-28 02:58:14Z kientzle $+ */++#ifndef ARCHIVE_PRIVATE_H_INCLUDED+#define ARCHIVE_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++#if HAVE_ICONV_H+#include <iconv.h>+#endif++#include "archive.h"+#include "archive_string.h"++#if defined(__GNUC__) && (__GNUC__ > 2 || \+			  (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))+#define	__LA_DEAD	__attribute__((__noreturn__))+#else+#define	__LA_DEAD+#endif++#define	ARCHIVE_WRITE_MAGIC	(0xb0c5c0deU)+#define	ARCHIVE_READ_MAGIC	(0xdeb0c5U)+#define	ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U)+#define	ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U)+#define	ARCHIVE_MATCH_MAGIC	(0xcad11c9U)++#define	ARCHIVE_STATE_NEW	1U+#define	ARCHIVE_STATE_HEADER	2U+#define	ARCHIVE_STATE_DATA	4U+#define	ARCHIVE_STATE_EOF	0x10U+#define	ARCHIVE_STATE_CLOSED	0x20U+#define	ARCHIVE_STATE_FATAL	0x8000U+#define	ARCHIVE_STATE_ANY	(0xFFFFU & ~ARCHIVE_STATE_FATAL)++struct archive_vtable {+	int	(*archive_close)(struct archive *);+	int	(*archive_free)(struct archive *);+	int	(*archive_write_header)(struct archive *,+	    struct archive_entry *);+	int	(*archive_write_finish_entry)(struct archive *);+	ssize_t	(*archive_write_data)(struct archive *,+	    const void *, size_t);+	ssize_t	(*archive_write_data_block)(struct archive *,+	    const void *, size_t, int64_t);++	int	(*archive_read_next_header)(struct archive *,+	    struct archive_entry **);+	int	(*archive_read_next_header2)(struct archive *,+	    struct archive_entry *);+	int	(*archive_read_data_block)(struct archive *,+	    const void **, size_t *, int64_t *);++	int	(*archive_filter_count)(struct archive *);+	int64_t (*archive_filter_bytes)(struct archive *, int);+	int	(*archive_filter_code)(struct archive *, int);+	const char * (*archive_filter_name)(struct archive *, int);+};++struct archive_string_conv;++struct archive {+	/*+	 * The magic/state values are used to sanity-check the+	 * client's usage.  If an API function is called at a+	 * ridiculous time, or the client passes us an invalid+	 * pointer, these values allow me to catch that.+	 */+	unsigned int	magic;+	unsigned int	state;++	/*+	 * Some public API functions depend on the "real" type of the+	 * archive object.+	 */+	struct archive_vtable *vtable;++	int		  archive_format;+	const char	 *archive_format_name;++	int	  compression_code;	/* Currently active compression. */+	const char *compression_name;++	/* Number of file entries processed. */+	int		  file_count;++	int		  archive_error_number;+	const char	 *error;+	struct archive_string	error_string;++	char *current_code;+	unsigned current_codepage; /* Current ACP(ANSI CodePage). */+	unsigned current_oemcp; /* Current OEMCP(OEM CodePage). */+	struct archive_string_conv *sconv;++	/*+	 * Used by archive_read_data() to track blocks and copy+	 * data to client buffers, filling gaps with zero bytes.+	 */+	const char	 *read_data_block;+	int64_t		  read_data_offset;+	int64_t		  read_data_output_offset;+	size_t		  read_data_remaining;++	/*+	 * Used by formats/filters to determine the amount of data+	 * requested from a call to archive_read_data(). This is only+	 * useful when the format/filter has seek support.+	 */+	char		  read_data_is_posix_read;+	size_t		  read_data_requested;+};++/* Check magic value and state; return(ARCHIVE_FATAL) if it isn't valid. */+int	__archive_check_magic(struct archive *, unsigned int magic,+	    unsigned int state, const char *func);+#define	archive_check_magic(a, expected_magic, allowed_states, function_name) \+	do { \+		int magic_test = __archive_check_magic((a), (expected_magic), \+			(allowed_states), (function_name)); \+		if (magic_test == ARCHIVE_FATAL) \+			return ARCHIVE_FATAL; \+	} while (0)++void	__archive_errx(int retvalue, const char *msg) __LA_DEAD;++void	__archive_ensure_cloexec_flag(int fd);+int	__archive_mktemp(const char *tmpdir);+#if defined(_WIN32) && !defined(__CYGWIN__)+int	__archive_mkstemp(wchar_t *template);+#else+int	__archive_mkstemp(char *template);+#endif++int	__archive_clean(struct archive *);++void __archive_reset_read_data(struct archive *);++#define	err_combine(a,b)	((a) < (b) ? (a) : (b))++#if defined(__BORLANDC__) || (defined(_MSC_VER) &&  _MSC_VER <= 1300)+# define	ARCHIVE_LITERAL_LL(x)	x##i64+# define	ARCHIVE_LITERAL_ULL(x)	x##ui64+#else+# define	ARCHIVE_LITERAL_LL(x)	x##ll+# define	ARCHIVE_LITERAL_ULL(x)	x##ull+#endif++#endif
+ c/archive_random.c view
@@ -0,0 +1,272 @@+/*-+ * Copyright (c) 2014 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif++#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))++#ifdef HAVE_FCNTL+#include <fcntl.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_SYS_TIME_H+#include <sys/time.h>+#endif+#ifdef HAVE_PTHREAD_H+#include <pthread.h>+#endif++static void arc4random_buf(void *, size_t);++#endif /* HAVE_ARC4RANDOM_BUF */++#include "archive.h"+#include "archive_random_private.h"++#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)+#include <wincrypt.h>+#endif++#ifndef O_CLOEXEC+#define O_CLOEXEC	0+#endif++/*+ * Random number generator function.+ * This simply calls arc4random_buf function if the platform provides it.+ */++int+archive_random(void *buf, size_t nbytes)+{+#if defined(_WIN32) && !defined(__CYGWIN__)+	HCRYPTPROV hProv;+	BOOL success;++	success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,+	    CRYPT_VERIFYCONTEXT);+	if (!success && GetLastError() == (DWORD)NTE_BAD_KEYSET) {+		success = CryptAcquireContext(&hProv, NULL, NULL,+		    PROV_RSA_FULL, CRYPT_NEWKEYSET);+	}+	if (success) {+		success = CryptGenRandom(hProv, (DWORD)nbytes, (BYTE*)buf);+		CryptReleaseContext(hProv, 0);+		if (success)+			return ARCHIVE_OK;+	}+	/* TODO: Does this case really happen? */+	return ARCHIVE_FAILED;+#else+	arc4random_buf(buf, nbytes);+	return ARCHIVE_OK;+#endif+}++#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))++/*	$OpenBSD: arc4random.c,v 1.24 2013/06/11 16:59:50 deraadt Exp $	*/+/*+ * Copyright (c) 1996, David Mazieres <dm@uun.org>+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>+ *+ * Permission to use, copy, modify, and distribute this software for any+ * purpose with or without fee is hereby granted, provided that the above+ * copyright notice and this permission notice appear in all copies.+ *+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.+ */++/*+ * Arc4 random number generator for OpenBSD.+ *+ * This code is derived from section 17.1 of Applied Cryptography,+ * second edition, which describes a stream cipher allegedly+ * compatible with RSA Labs "RC4" cipher (the actual description of+ * which is a trade secret).  The same algorithm is used as a stream+ * cipher called "arcfour" in Tatu Ylonen's ssh package.+ *+ * RC4 is a registered trademark of RSA Laboratories.+ */++#ifdef __GNUC__+#define inline __inline+#else				/* !__GNUC__ */+#define inline+#endif				/* !__GNUC__ */++struct arc4_stream {+	uint8_t i;+	uint8_t j;+	uint8_t s[256];+};++#define	RANDOMDEV	"/dev/urandom"+#define	KEYSIZE		128+#ifdef HAVE_PTHREAD_H+static pthread_mutex_t	arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;+#define	_ARC4_LOCK()	pthread_mutex_lock(&arc4random_mtx);+#define	_ARC4_UNLOCK()  pthread_mutex_unlock(&arc4random_mtx);+#else+#define	_ARC4_LOCK()+#define	_ARC4_UNLOCK()+#endif++static int rs_initialized;+static struct arc4_stream rs;+static pid_t arc4_stir_pid;+static int arc4_count;++static inline uint8_t arc4_getbyte(void);+static void arc4_stir(void);++static inline void+arc4_init(void)+{+	int     n;++	for (n = 0; n < 256; n++)+		rs.s[n] = n;+	rs.i = 0;+	rs.j = 0;+}++static inline void+arc4_addrandom(u_char *dat, int datlen)+{+	int     n;+	uint8_t si;++	rs.i--;+	for (n = 0; n < 256; n++) {+		rs.i = (rs.i + 1);+		si = rs.s[rs.i];+		rs.j = (rs.j + si + dat[n % datlen]);+		rs.s[rs.i] = rs.s[rs.j];+		rs.s[rs.j] = si;+	}+	rs.j = rs.i;+}++static void+arc4_stir(void)+{+	int done, fd, i;+	struct {+		struct timeval	tv;+		pid_t		pid;+		u_char	 	rnd[KEYSIZE];+	} rdat;++	if (!rs_initialized) {+		arc4_init();+		rs_initialized = 1;+	}+	done = 0;+	fd = open(RANDOMDEV, O_RDONLY | O_CLOEXEC, 0);+	if (fd >= 0) {+		if (read(fd, &rdat, KEYSIZE) == KEYSIZE)+			done = 1;+		(void)close(fd);+	}+	if (!done) {+		(void)gettimeofday(&rdat.tv, NULL);+		rdat.pid = getpid();+		/* We'll just take whatever was on the stack too... */+	}++	arc4_addrandom((u_char *)&rdat, KEYSIZE);++	/*+	 * Discard early keystream, as per recommendations in:+	 * "(Not So) Random Shuffles of RC4" by Ilya Mironov.+	 * As per the Network Operations Division, cryptographic requirements+	 * published on wikileaks on March 2017.+	 */++	for (i = 0; i < 3072; i++)+		(void)arc4_getbyte();+	arc4_count = 1600000;+}++static void+arc4_stir_if_needed(void)+{+	pid_t pid = getpid();++	if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid) {+		arc4_stir_pid = pid;+		arc4_stir();+	}+}++static inline uint8_t+arc4_getbyte(void)+{+	uint8_t si, sj;++	rs.i = (rs.i + 1);+	si = rs.s[rs.i];+	rs.j = (rs.j + si);+	sj = rs.s[rs.j];+	rs.s[rs.i] = sj;+	rs.s[rs.j] = si;+	return (rs.s[(si + sj) & 0xff]);+}++static void+arc4random_buf(void *_buf, size_t n)+{+	u_char *buf = (u_char *)_buf;+	_ARC4_LOCK();+	arc4_stir_if_needed();+	while (n--) {+		if (--arc4_count <= 0)+			arc4_stir();+		buf[n] = arc4_getbyte();+	}+	_ARC4_UNLOCK();+}++#endif /* !HAVE_ARC4RANDOM_BUF */
+ c/archive_random_private.h view
@@ -0,0 +1,36 @@+/*-+ * Copyright (c) 2014 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#ifndef ARCHIVE_RANDOM_PRIVATE_H_INCLUDED+#define ARCHIVE_RANDOM_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++/* Random number generator. */+int archive_random(void *buf, size_t nbytes);++#endif /* ARCHIVE_RANDOM_PRIVATE_H_INCLUDED */
+ c/archive_rb.c view
@@ -0,0 +1,709 @@+/*-+ * Copyright (c) 2001 The NetBSD Foundation, Inc.+ * All rights reserved.+ *+ * This code is derived from software contributed to The NetBSD Foundation+ * by Matt Thomas <matt@3am-software.com>.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+ * POSSIBILITY OF SUCH DAMAGE.+ *+ * Based on: NetBSD: rb.c,v 1.6 2010/04/30 13:58:09 joerg Exp+ */++#include "archive_platform.h"++#include <stddef.h>++#include "archive_rb.h"++/* Keep in sync with archive_rb.h */+#define	RB_DIR_LEFT		0+#define	RB_DIR_RIGHT		1+#define	RB_DIR_OTHER		1+#define	rb_left			rb_nodes[RB_DIR_LEFT]+#define	rb_right		rb_nodes[RB_DIR_RIGHT]++#define	RB_FLAG_POSITION	0x2+#define	RB_FLAG_RED		0x1+#define	RB_FLAG_MASK		(RB_FLAG_POSITION|RB_FLAG_RED)+#define	RB_FATHER(rb) \+    ((struct archive_rb_node *)((rb)->rb_info & ~RB_FLAG_MASK))+#define	RB_SET_FATHER(rb, father) \+    ((void)((rb)->rb_info = (uintptr_t)(father)|((rb)->rb_info & RB_FLAG_MASK)))++#define	RB_SENTINEL_P(rb)	((rb) == NULL)+#define	RB_LEFT_SENTINEL_P(rb)	RB_SENTINEL_P((rb)->rb_left)+#define	RB_RIGHT_SENTINEL_P(rb)	RB_SENTINEL_P((rb)->rb_right)+#define	RB_FATHER_SENTINEL_P(rb) RB_SENTINEL_P(RB_FATHER((rb)))+#define	RB_CHILDLESS_P(rb) \+    (RB_SENTINEL_P(rb) || (RB_LEFT_SENTINEL_P(rb) && RB_RIGHT_SENTINEL_P(rb)))+#define	RB_TWOCHILDREN_P(rb) \+    (!RB_SENTINEL_P(rb) && !RB_LEFT_SENTINEL_P(rb) && !RB_RIGHT_SENTINEL_P(rb))++#define	RB_POSITION(rb)	\+    (((rb)->rb_info & RB_FLAG_POSITION) ? RB_DIR_RIGHT : RB_DIR_LEFT)+#define	RB_RIGHT_P(rb)		(RB_POSITION(rb) == RB_DIR_RIGHT)+#define	RB_LEFT_P(rb)		(RB_POSITION(rb) == RB_DIR_LEFT)+#define	RB_RED_P(rb) 		(!RB_SENTINEL_P(rb) && ((rb)->rb_info & RB_FLAG_RED) != 0)+#define	RB_BLACK_P(rb) 		(RB_SENTINEL_P(rb) || ((rb)->rb_info & RB_FLAG_RED) == 0)+#define	RB_MARK_RED(rb) 	((void)((rb)->rb_info |= RB_FLAG_RED))+#define	RB_MARK_BLACK(rb) 	((void)((rb)->rb_info &= ~RB_FLAG_RED))+#define	RB_INVERT_COLOR(rb) 	((void)((rb)->rb_info ^= RB_FLAG_RED))+#define	RB_ROOT_P(rbt, rb)	((rbt)->rbt_root == (rb))+#define	RB_SET_POSITION(rb, position) \+    ((void)((position) ? ((rb)->rb_info |= RB_FLAG_POSITION) : \+    ((rb)->rb_info &= ~RB_FLAG_POSITION)))+#define	RB_ZERO_PROPERTIES(rb)	((void)((rb)->rb_info &= ~RB_FLAG_MASK))+#define	RB_COPY_PROPERTIES(dst, src) \+    ((void)((dst)->rb_info ^= ((dst)->rb_info ^ (src)->rb_info) & RB_FLAG_MASK))+#define RB_SWAP_PROPERTIES(a, b) do { \+    uintptr_t xorinfo = ((a)->rb_info ^ (b)->rb_info) & RB_FLAG_MASK; \+    (a)->rb_info ^= xorinfo; \+    (b)->rb_info ^= xorinfo; \+  } while (/*CONSTCOND*/ 0)++static void __archive_rb_tree_insert_rebalance(struct archive_rb_tree *,+    struct archive_rb_node *);+static void __archive_rb_tree_removal_rebalance(struct archive_rb_tree *,+    struct archive_rb_node *, unsigned int);++#define	RB_SENTINEL_NODE	NULL++#define T	1+#define	F	0++void+__archive_rb_tree_init(struct archive_rb_tree *rbt,+    const struct archive_rb_tree_ops *ops)+{+	rbt->rbt_ops = ops;+	*((struct archive_rb_node **)&rbt->rbt_root) = RB_SENTINEL_NODE;+}++struct archive_rb_node *+__archive_rb_tree_find_node(struct archive_rb_tree *rbt, const void *key)+{+	archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;+	struct archive_rb_node *parent = rbt->rbt_root;++	while (!RB_SENTINEL_P(parent)) {+		const signed int diff = (*compare_key)(parent, key);+		if (diff == 0)+			return parent;+		parent = parent->rb_nodes[diff > 0];+	}++	return NULL;+}+ +struct archive_rb_node *+__archive_rb_tree_find_node_geq(struct archive_rb_tree *rbt, const void *key)+{+	archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;+	struct archive_rb_node *parent = rbt->rbt_root;+	struct archive_rb_node *last = NULL;++	while (!RB_SENTINEL_P(parent)) {+		const signed int diff = (*compare_key)(parent, key);+		if (diff == 0)+			return parent;+		if (diff < 0)+			last = parent;+		parent = parent->rb_nodes[diff > 0];+	}++	return last;+}+ +struct archive_rb_node *+__archive_rb_tree_find_node_leq(struct archive_rb_tree *rbt, const void *key)+{+	archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;+	struct archive_rb_node *parent = rbt->rbt_root;+	struct archive_rb_node *last = NULL;++	while (!RB_SENTINEL_P(parent)) {+		const signed int diff = (*compare_key)(parent, key);+		if (diff == 0)+			return parent;+		if (diff > 0)+			last = parent;+		parent = parent->rb_nodes[diff > 0];+	}++	return last;+}++int+__archive_rb_tree_insert_node(struct archive_rb_tree *rbt,+    struct archive_rb_node *self)+{+	archive_rbto_compare_nodes_fn compare_nodes = rbt->rbt_ops->rbto_compare_nodes;+	struct archive_rb_node *parent, *tmp;+	unsigned int position;+	int rebalance;++	tmp = rbt->rbt_root;+	/*+	 * This is a hack.  Because rbt->rbt_root is just a+	 * struct archive_rb_node *, just like rb_node->rb_nodes[RB_DIR_LEFT],+	 * we can use this fact to avoid a lot of tests for root and know+	 * that even at root, updating+	 * RB_FATHER(rb_node)->rb_nodes[RB_POSITION(rb_node)] will+	 * update rbt->rbt_root.+	 */+	parent = (struct archive_rb_node *)(void *)&rbt->rbt_root;+	position = RB_DIR_LEFT;++	/*+	 * Find out where to place this new leaf.+	 */+	while (!RB_SENTINEL_P(tmp)) {+		const signed int diff = (*compare_nodes)(tmp, self);+		if (diff == 0) {+			/*+			 * Node already exists; don't insert.+			 */+			return F;+		}+		parent = tmp;+		position = (diff > 0);+		tmp = parent->rb_nodes[position];+	}++	/*+	 * Initialize the node and insert as a leaf into the tree.+	 */+	RB_SET_FATHER(self, parent);+	RB_SET_POSITION(self, position);+	if (parent == (struct archive_rb_node *)(void *)&rbt->rbt_root) {+		RB_MARK_BLACK(self);		/* root is always black */+		rebalance = F;+	} else {+		/*+		 * All new nodes are colored red.  We only need to rebalance+		 * if our parent is also red.+		 */+		RB_MARK_RED(self);+		rebalance = RB_RED_P(parent);+	}+	self->rb_left = parent->rb_nodes[position];+	self->rb_right = parent->rb_nodes[position];+	parent->rb_nodes[position] = self;++	/*+	 * Rebalance tree after insertion+	 */+	if (rebalance)+		__archive_rb_tree_insert_rebalance(rbt, self);++	return T;+}++/*+ * Swap the location and colors of 'self' and its child @ which.  The child+ * can not be a sentinel node.  This is our rotation function.  However,+ * since it preserves coloring, it great simplifies both insertion and+ * removal since rotation almost always involves the exchanging of colors+ * as a separate step.+ */+/*ARGSUSED*/+static void+__archive_rb_tree_reparent_nodes(+    struct archive_rb_node *old_father, const unsigned int which)+{+	const unsigned int other = which ^ RB_DIR_OTHER;+	struct archive_rb_node * const grandpa = RB_FATHER(old_father);+	struct archive_rb_node * const old_child = old_father->rb_nodes[which];+	struct archive_rb_node * const new_father = old_child;+	struct archive_rb_node * const new_child = old_father;++	if (new_father == NULL)+		return;+	/*+	 * Exchange descendant linkages.+	 */+	grandpa->rb_nodes[RB_POSITION(old_father)] = new_father;+	new_child->rb_nodes[which] = old_child->rb_nodes[other];+	new_father->rb_nodes[other] = new_child;++	/*+	 * Update ancestor linkages+	 */+	RB_SET_FATHER(new_father, grandpa);+	RB_SET_FATHER(new_child, new_father);++	/*+	 * Exchange properties between new_father and new_child.  The only+	 * change is that new_child's position is now on the other side.+	 */+	RB_SWAP_PROPERTIES(new_father, new_child);+	RB_SET_POSITION(new_child, other);++	/*+	 * Make sure to reparent the new child to ourself.+	 */+	if (!RB_SENTINEL_P(new_child->rb_nodes[which])) {+		RB_SET_FATHER(new_child->rb_nodes[which], new_child);+		RB_SET_POSITION(new_child->rb_nodes[which], which);+	}++}++static void+__archive_rb_tree_insert_rebalance(struct archive_rb_tree *rbt,+    struct archive_rb_node *self)+{+	struct archive_rb_node * father = RB_FATHER(self);+	struct archive_rb_node * grandpa;+	struct archive_rb_node * uncle;+	unsigned int which;+	unsigned int other;++	for (;;) {+		/*+		 * We are red and our parent is red, therefore we must have a+		 * grandfather and he must be black.+		 */+		grandpa = RB_FATHER(father);+		which = (father == grandpa->rb_right);+		other = which ^ RB_DIR_OTHER;+		uncle = grandpa->rb_nodes[other];++		if (RB_BLACK_P(uncle))+			break;++		/*+		 * Case 1: our uncle is red+		 *   Simply invert the colors of our parent and+		 *   uncle and make our grandparent red.  And+		 *   then solve the problem up at his level.+		 */+		RB_MARK_BLACK(uncle);+		RB_MARK_BLACK(father);+		if (RB_ROOT_P(rbt, grandpa)) {+			/*+			 * If our grandpa is root, don't bother+			 * setting him to red, just return.+			 */+			return;+		}+		RB_MARK_RED(grandpa);+		self = grandpa;+		father = RB_FATHER(self);+		if (RB_BLACK_P(father)) {+			/*+			 * If our great-grandpa is black, we're done.+			 */+			return;+		}+	}++	/*+	 * Case 2&3: our uncle is black.+	 */+	if (self == father->rb_nodes[other]) {+		/*+		 * Case 2: we are on the same side as our uncle+		 *   Swap ourselves with our parent so this case+		 *   becomes case 3.  Basically our parent becomes our+		 *   child.+		 */+		__archive_rb_tree_reparent_nodes(father, other);+	}+	/*+	 * Case 3: we are opposite a child of a black uncle.+	 *   Swap our parent and grandparent.  Since our grandfather+	 *   is black, our father will become black and our new sibling+	 *   (former grandparent) will become red.+	 */+	__archive_rb_tree_reparent_nodes(grandpa, which);++	/*+	 * Final step: Set the root to black.+	 */+	RB_MARK_BLACK(rbt->rbt_root);+}++static void+__archive_rb_tree_prune_node(struct archive_rb_tree *rbt,+    struct archive_rb_node *self, int rebalance)+{+	const unsigned int which = RB_POSITION(self);+	struct archive_rb_node *father = RB_FATHER(self);++	/*+	 * Since we are childless, we know that self->rb_left is pointing+	 * to the sentinel node.+	 */+	father->rb_nodes[which] = self->rb_left;++	/*+	 * Rebalance if requested.+	 */+	if (rebalance)+		__archive_rb_tree_removal_rebalance(rbt, father, which);+}++/*+ * When deleting an interior node+ */+static void+__archive_rb_tree_swap_prune_and_rebalance(struct archive_rb_tree *rbt,+    struct archive_rb_node *self, struct archive_rb_node *standin)+{+	const unsigned int standin_which = RB_POSITION(standin);+	unsigned int standin_other = standin_which ^ RB_DIR_OTHER;+	struct archive_rb_node *standin_son;+	struct archive_rb_node *standin_father = RB_FATHER(standin);+	int rebalance = RB_BLACK_P(standin);++	if (standin_father == self) {+		/*+		 * As a child of self, any children would be opposite of+		 * our parent.+		 */+		standin_son = standin->rb_nodes[standin_which];+	} else {+		/*+		 * Since we aren't a child of self, any children would be+		 * on the same side as our parent.+		 */+		standin_son = standin->rb_nodes[standin_other];+	}++	if (RB_RED_P(standin_son)) {+		/*+		 * We know we have a red child so if we flip it to black+		 * we don't have to rebalance.+		 */+		RB_MARK_BLACK(standin_son);+		rebalance = F;++		if (standin_father != self) {+			/*+			 * Change the son's parentage to point to his grandpa.+			 */+			RB_SET_FATHER(standin_son, standin_father);+			RB_SET_POSITION(standin_son, standin_which);+		}+	}++	if (standin_father == self) {+		/*+		 * If we are about to delete the standin's father, then when+		 * we call rebalance, we need to use ourselves as our father.+		 * Otherwise remember our original father.  Also, since we are+		 * our standin's father we only need to reparent the standin's+		 * brother.+		 *+		 * |    R      -->     S    |+		 * |  Q   S    -->   Q   T  |+		 * |        t  -->          |+		 *+		 * Have our son/standin adopt his brother as his new son.+		 */+		standin_father = standin;+	} else {+		/*+		 * |    R          -->    S       .  |+		 * |   / \  |   T  -->   / \  |  /   |+		 * |  ..... | S    -->  ..... | T    |+		 *+		 * Sever standin's connection to his father.+		 */+		standin_father->rb_nodes[standin_which] = standin_son;+		/*+		 * Adopt the far son.+		 */+		standin->rb_nodes[standin_other] = self->rb_nodes[standin_other];+		RB_SET_FATHER(standin->rb_nodes[standin_other], standin);+		/*+		 * Use standin_other because we need to preserve standin_which+		 * for the removal_rebalance.+		 */+		standin_other = standin_which;+	}++	/*+	 * Move the only remaining son to our standin.  If our standin is our+	 * son, this will be the only son needed to be moved.+	 */+	standin->rb_nodes[standin_other] = self->rb_nodes[standin_other];+	RB_SET_FATHER(standin->rb_nodes[standin_other], standin);++	/*+	 * Now copy the result of self to standin and then replace+	 * self with standin in the tree.+	 */+	RB_COPY_PROPERTIES(standin, self);+	RB_SET_FATHER(standin, RB_FATHER(self));+	RB_FATHER(standin)->rb_nodes[RB_POSITION(standin)] = standin;++	if (rebalance)+		__archive_rb_tree_removal_rebalance(rbt, standin_father, standin_which);+}++/*+ * We could do this by doing+ *	__archive_rb_tree_node_swap(rbt, self, which);+ *	__archive_rb_tree_prune_node(rbt, self, F);+ *+ * But it's more efficient to just evaluate and recolor the child.+ */+static void+__archive_rb_tree_prune_blackred_branch(+    struct archive_rb_node *self, unsigned int which)+{+	struct archive_rb_node *father = RB_FATHER(self);+	struct archive_rb_node *son = self->rb_nodes[which];++	/*+	 * Remove ourselves from the tree and give our former child our+	 * properties (position, color, root).+	 */+	RB_COPY_PROPERTIES(son, self);+	father->rb_nodes[RB_POSITION(son)] = son;+	RB_SET_FATHER(son, father);+}+/*+ *+ */+void+__archive_rb_tree_remove_node(struct archive_rb_tree *rbt,+    struct archive_rb_node *self)+{+	struct archive_rb_node *standin;+	unsigned int which;++	/*+	 * In the following diagrams, we (the node to be removed) are S.  Red+	 * nodes are lowercase.  T could be either red or black.+	 *+	 * Remember the major axiom of the red-black tree: the number of+	 * black nodes from the root to each leaf is constant across all+	 * leaves, only the number of red nodes varies.+	 *+	 * Thus removing a red leaf doesn't require any other changes to a+	 * red-black tree.  So if we must remove a node, attempt to rearrange+	 * the tree so we can remove a red node.+	 *+	 * The simplest case is a childless red node or a childless root node:+	 *+	 * |    T  -->    T  |    or    |  R  -->  *  |+	 * |  s    -->  *    |+	 */+	if (RB_CHILDLESS_P(self)) {+		const int rebalance = RB_BLACK_P(self) && !RB_ROOT_P(rbt, self);+		__archive_rb_tree_prune_node(rbt, self, rebalance);+		return;+	}+	if (!RB_TWOCHILDREN_P(self)) {+		/*+		 * The next simplest case is the node we are deleting is+		 * black and has one red child.+		 *+		 * |      T  -->      T  -->      T  |+		 * |    S    -->  R      -->  R      |+		 * |  r      -->    s    -->    *    |+		 */+		which = RB_LEFT_SENTINEL_P(self) ? RB_DIR_RIGHT : RB_DIR_LEFT;+		__archive_rb_tree_prune_blackred_branch(self, which);+		return;+	}++	/*+	 * We invert these because we prefer to remove from the inside of+	 * the tree.+	 */+	which = RB_POSITION(self) ^ RB_DIR_OTHER;++	/*+	 * Let's find the node closes to us opposite of our parent+	 * Now swap it with ourself, "prune" it, and rebalance, if needed.+	 */+	standin = __archive_rb_tree_iterate(rbt, self, which);+	__archive_rb_tree_swap_prune_and_rebalance(rbt, self, standin);+}++static void+__archive_rb_tree_removal_rebalance(struct archive_rb_tree *rbt,+    struct archive_rb_node *parent, unsigned int which)+{++	while (RB_BLACK_P(parent->rb_nodes[which])) {+		unsigned int other = which ^ RB_DIR_OTHER;+		struct archive_rb_node *brother = parent->rb_nodes[other];++		if (brother == NULL)+			return;/* The tree may be broken. */+		/*+		 * For cases 1, 2a, and 2b, our brother's children must+		 * be black and our father must be black+		 */+		if (RB_BLACK_P(parent)+		    && RB_BLACK_P(brother->rb_left)+		    && RB_BLACK_P(brother->rb_right)) {+			if (RB_RED_P(brother)) {+				/*+				 * Case 1: Our brother is red, swap its+				 * position (and colors) with our parent. +				 * This should now be case 2b (unless C or E+				 * has a red child which is case 3; thus no+				 * explicit branch to case 2b).+				 *+				 *    B         ->        D+				 *  A     d     ->    b     E+				 *      C   E   ->  A   C+				 */+				__archive_rb_tree_reparent_nodes(parent, other);+				brother = parent->rb_nodes[other];+				if (brother == NULL)+					return;/* The tree may be broken. */+			} else {+				/*+				 * Both our parent and brother are black.+				 * Change our brother to red, advance up rank+				 * and go through the loop again.+				 *+				 *    B         ->   *B+				 * *A     D     ->  A     d+				 *      C   E   ->      C   E+				 */+				RB_MARK_RED(brother);+				if (RB_ROOT_P(rbt, parent))+					return;	/* root == parent == black */+				which = RB_POSITION(parent);+				parent = RB_FATHER(parent);+				continue;+			}+		}+		/*+		 * Avoid an else here so that case 2a above can hit either+		 * case 2b, 3, or 4.+		 */+		if (RB_RED_P(parent)+		    && RB_BLACK_P(brother)+		    && RB_BLACK_P(brother->rb_left)+		    && RB_BLACK_P(brother->rb_right)) {+			/*+			 * We are black, our father is red, our brother and+			 * both nephews are black.  Simply invert/exchange the+			 * colors of our father and brother (to black and red+			 * respectively).+			 *+			 *	|    f        -->    F        |+			 *	|  *     B    -->  *     b    |+			 *	|      N   N  -->      N   N  |+			 */+			RB_MARK_BLACK(parent);+			RB_MARK_RED(brother);+			break;		/* We're done! */+		} else {+			/*+			 * Our brother must be black and have at least one+			 * red child (it may have two).+			 */+			if (RB_BLACK_P(brother->rb_nodes[other])) {+				/*+				 * Case 3: our brother is black, our near+				 * nephew is red, and our far nephew is black.+				 * Swap our brother with our near nephew.  +				 * This result in a tree that matches case 4.+				 * (Our father could be red or black).+				 *+				 *	|    F      -->    F      |+				 *	|  x     B  -->  x   B    |+				 *	|      n    -->        n  |+				 */+				__archive_rb_tree_reparent_nodes(brother, which);+				brother = parent->rb_nodes[other];+			}+			/*+			 * Case 4: our brother is black and our far nephew+			 * is red.  Swap our father and brother locations and+			 * change our far nephew to black.  (these can be+			 * done in either order so we change the color first).+			 * The result is a valid red-black tree and is a+			 * terminal case.  (again we don't care about the+			 * father's color)+			 *+			 * If the father is red, we will get a red-black-black+			 * tree:+			 *	|  f      ->  f      -->    b    |+			 *	|    B    ->    B    -->  F   N  |+			 *	|      n  ->      N  -->         |+			 *+			 * If the father is black, we will get an all black+			 * tree:+			 *	|  F      ->  F      -->    B    |+			 *	|    B    ->    B    -->  F   N  |+			 *	|      n  ->      N  -->         |+			 *+			 * If we had two red nephews, then after the swap,+			 * our former father would have a red grandson. +			 */+			if (brother->rb_nodes[other] == NULL)+				return;/* The tree may be broken. */+			RB_MARK_BLACK(brother->rb_nodes[other]);+			__archive_rb_tree_reparent_nodes(parent, other);+			break;		/* We're done! */+		}+	}+}++struct archive_rb_node *+__archive_rb_tree_iterate(struct archive_rb_tree *rbt,+    struct archive_rb_node *self, const unsigned int direction)+{+	const unsigned int other = direction ^ RB_DIR_OTHER;++	if (self == NULL) {+		self = rbt->rbt_root;+		if (RB_SENTINEL_P(self))+			return NULL;+		while (!RB_SENTINEL_P(self->rb_nodes[direction]))+			self = self->rb_nodes[direction];+		return self;+	}+	/*+	 * We can't go any further in this direction.  We proceed up in the+	 * opposite direction until our parent is in direction we want to go.+	 */+	if (RB_SENTINEL_P(self->rb_nodes[direction])) {+		while (!RB_ROOT_P(rbt, self)) {+			if (other == (unsigned int)RB_POSITION(self))+				return RB_FATHER(self);+			self = RB_FATHER(self);+		}+		return NULL;+	}++	/*+	 * Advance down one in current direction and go down as far as possible+	 * in the opposite direction.+	 */+	self = self->rb_nodes[direction];+	while (!RB_SENTINEL_P(self->rb_nodes[other]))+		self = self->rb_nodes[other];+	return self;+}
+ c/archive_rb.h view
@@ -0,0 +1,113 @@+/*-+ * Copyright (c) 2001 The NetBSD Foundation, Inc.+ * All rights reserved.+ *+ * This code is derived from software contributed to The NetBSD Foundation+ * by Matt Thomas <matt@3am-software.com>.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+ * POSSIBILITY OF SUCH DAMAGE.+ *+ * Based on NetBSD: rb.h,v 1.13 2009/08/16 10:57:01 yamt Exp+ */++#ifndef ARCHIVE_RB_H_INCLUDED+#define	ARCHIVE_RB_H_INCLUDED++struct archive_rb_node {+	struct archive_rb_node *rb_nodes[2];+	/*+	 * rb_info contains the two flags and the parent back pointer.+	 * We put the two flags in the low two bits since we know that+	 * rb_node will have an alignment of 4 or 8 bytes.+	 */+	uintptr_t rb_info;+};++#define	ARCHIVE_RB_DIR_LEFT		0+#define	ARCHIVE_RB_DIR_RIGHT		1++#define ARCHIVE_RB_TREE_MIN(T) \+    __archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_LEFT)+#define ARCHIVE_RB_TREE_MAX(T) \+    __archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_RIGHT)+#define ARCHIVE_RB_TREE_NEXT(T, N) \+    __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_RIGHT)+#define ARCHIVE_RB_TREE_PREV(T, N) \+    __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_LEFT)+#define ARCHIVE_RB_TREE_FOREACH(N, T) \+    for ((N) = ARCHIVE_RB_TREE_MIN(T); (N); \+	(N) = ARCHIVE_RB_TREE_NEXT((T), (N)))+#define ARCHIVE_RB_TREE_FOREACH_REVERSE(N, T) \+    for ((N) = ARCHIVE_RB_TREE_MAX(T); (N); \+	(N) = ARCHIVE_RB_TREE_PREV((T), (N)))+#define ARCHIVE_RB_TREE_FOREACH_SAFE(N, T, S) \+    for ((N) = ARCHIVE_RB_TREE_MIN(T); \+	(N) && ((S) = ARCHIVE_RB_TREE_NEXT((T), (N)), 1); \+	(N) = (S))+#define ARCHIVE_RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \+    for ((N) = ARCHIVE_RB_TREE_MAX(T); \+        (N) && ((S) = ARCHIVE_RB_TREE_PREV((T), (N)), 1); \+        (N) = (S))++/*+ * archive_rbto_compare_nodes_fn:+ *	return a positive value if the first node < the second node.+ *	return a negative value if the first node > the second node.+ *	return 0 if they are considered same.+ *+ * archive_rbto_compare_key_fn:+ *	return a positive value if the node < the key.+ *	return a negative value if the node > the key.+ *	return 0 if they are considered same.+ */++typedef signed int (*const archive_rbto_compare_nodes_fn)(const struct archive_rb_node *,+    const struct archive_rb_node *);+typedef signed int (*const archive_rbto_compare_key_fn)(const struct archive_rb_node *,+    const void *);++struct archive_rb_tree_ops {+	archive_rbto_compare_nodes_fn rbto_compare_nodes;+	archive_rbto_compare_key_fn rbto_compare_key;+};++struct archive_rb_tree {+	struct archive_rb_node *rbt_root;+	const struct archive_rb_tree_ops *rbt_ops;+};++void	__archive_rb_tree_init(struct archive_rb_tree *,+    const struct archive_rb_tree_ops *);+int	__archive_rb_tree_insert_node(struct archive_rb_tree *,+    struct archive_rb_node *);+struct archive_rb_node	*+	__archive_rb_tree_find_node(struct archive_rb_tree *, const void *);+struct archive_rb_node	*+	__archive_rb_tree_find_node_geq(struct archive_rb_tree *, const void *);+struct archive_rb_node	*+	__archive_rb_tree_find_node_leq(struct archive_rb_tree *, const void *);+void	__archive_rb_tree_remove_node(struct archive_rb_tree *, struct archive_rb_node *);+struct archive_rb_node *+	__archive_rb_tree_iterate(struct archive_rb_tree *,+	struct archive_rb_node *, const unsigned int);++#endif	/* ARCHIVE_RB_H_*/
+ c/archive_read.c view
@@ -0,0 +1,1752 @@+/*-+ * Copyright (c) 2003-2011 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++/*+ * This file contains the "essential" portions of the read API, that+ * is, stuff that will probably always be used by any client that+ * actually needs to read an archive.  Optional pieces have been, as+ * far as possible, separated out into separate files to avoid+ * needlessly bloating statically-linked clients.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read.c 201157 2009-12-29 05:30:23Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_private.h"++#define minimum(a, b) (a < b ? a : b)++static int	choose_filters(struct archive_read *);+static int	choose_format(struct archive_read *);+static int	close_filters(struct archive_read *);+static struct archive_vtable *archive_read_vtable(void);+static int64_t	_archive_filter_bytes(struct archive *, int);+static int	_archive_filter_code(struct archive *, int);+static const char *_archive_filter_name(struct archive *, int);+static int  _archive_filter_count(struct archive *);+static int	_archive_read_close(struct archive *);+static int	_archive_read_data_block(struct archive *,+		    const void **, size_t *, int64_t *);+static int	_archive_read_free(struct archive *);+static int	_archive_read_next_header(struct archive *,+		    struct archive_entry **);+static int	_archive_read_next_header2(struct archive *,+		    struct archive_entry *);+static int64_t  advance_file_pointer(struct archive_read_filter *, int64_t);++static struct archive_vtable *+archive_read_vtable(void)+{+	static struct archive_vtable av;+	static int inited = 0;++	if (!inited) {+		av.archive_filter_bytes = _archive_filter_bytes;+		av.archive_filter_code = _archive_filter_code;+		av.archive_filter_name = _archive_filter_name;+		av.archive_filter_count = _archive_filter_count;+		av.archive_read_data_block = _archive_read_data_block;+		av.archive_read_next_header = _archive_read_next_header;+		av.archive_read_next_header2 = _archive_read_next_header2;+		av.archive_free = _archive_read_free;+		av.archive_close = _archive_read_close;+		inited = 1;+	}+	return (&av);+}++/*+ * Allocate, initialize and return a struct archive object.+ */+struct archive *+archive_read_new(void)+{+	struct archive_read *a;++	a = (struct archive_read *)calloc(1, sizeof(*a));+	if (a == NULL)+		return (NULL);+	a->archive.magic = ARCHIVE_READ_MAGIC;++	a->archive.state = ARCHIVE_STATE_NEW;+	a->entry = archive_entry_new2(&a->archive);+	a->archive.vtable = archive_read_vtable();++	a->passphrases.last = &a->passphrases.first;++	return (&a->archive);+}++/*+ * Record the do-not-extract-to file. This belongs in archive_read_extract.c.+ */+void+archive_read_extract_set_skip_file(struct archive *_a, la_int64_t d,+    la_int64_t i)+{+	struct archive_read *a = (struct archive_read *)_a;++	if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_MAGIC,+		ARCHIVE_STATE_ANY, "archive_read_extract_set_skip_file"))+		return;+	a->skip_file_set = 1;+	a->skip_file_dev = d;+	a->skip_file_ino = i;+}++/*+ * Open the archive+ */+int+archive_read_open(struct archive *a, void *client_data,+    archive_open_callback *client_opener, archive_read_callback *client_reader,+    archive_close_callback *client_closer)+{+	/* Old archive_read_open() is just a thin shell around+	 * archive_read_open1. */+	archive_read_set_open_callback(a, client_opener);+	archive_read_set_read_callback(a, client_reader);+	archive_read_set_close_callback(a, client_closer);+	archive_read_set_callback_data(a, client_data);+	return archive_read_open1(a);+}+++int+archive_read_open2(struct archive *a, void *client_data,+    archive_open_callback *client_opener,+    archive_read_callback *client_reader,+    archive_skip_callback *client_skipper,+    archive_close_callback *client_closer)+{+	/* Old archive_read_open2() is just a thin shell around+	 * archive_read_open1. */+	archive_read_set_callback_data(a, client_data);+	archive_read_set_open_callback(a, client_opener);+	archive_read_set_read_callback(a, client_reader);+	archive_read_set_skip_callback(a, client_skipper);+	archive_read_set_close_callback(a, client_closer);+	return archive_read_open1(a);+}++static ssize_t+client_read_proxy(struct archive_read_filter *self, const void **buff)+{+	ssize_t r;+	r = (self->archive->client.reader)(&self->archive->archive,+	    self->data, buff);+	return (r);+}++static int64_t+client_skip_proxy(struct archive_read_filter *self, int64_t request)+{+	if (request < 0)+		__archive_errx(1, "Negative skip requested.");+	if (request == 0)+		return 0;++	if (self->archive->client.skipper != NULL) {+		/* Seek requests over 1GiB are broken down into+		 * multiple seeks.  This avoids overflows when the+		 * requests get passed through 32-bit arguments. */+		int64_t skip_limit = (int64_t)1 << 30;+		int64_t total = 0;+		for (;;) {+			int64_t get, ask = request;+			if (ask > skip_limit)+				ask = skip_limit;+			get = (self->archive->client.skipper)+				(&self->archive->archive, self->data, ask);+			total += get;+			if (get == 0 || get == request)+				return (total);+			if (get > request)+				return ARCHIVE_FATAL;+			request -= get;+		}+	} else if (self->archive->client.seeker != NULL+		&& request > 64 * 1024) {+		/* If the client provided a seeker but not a skipper,+		 * we can use the seeker to skip forward.+		 *+		 * Note: This isn't always a good idea.  The client+		 * skipper is allowed to skip by less than requested+		 * if it needs to maintain block alignment.  The+		 * seeker is not allowed to play such games, so using+		 * the seeker here may be a performance loss compared+		 * to just reading and discarding.  That's why we+		 * only do this for skips of over 64k.+		 */+		int64_t before = self->position;+		int64_t after = (self->archive->client.seeker)+		    (&self->archive->archive, self->data, request, SEEK_CUR);+		if (after != before + request)+			return ARCHIVE_FATAL;+		return after - before;+	}+	return 0;+}++static int64_t+client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence)+{+	/* DO NOT use the skipper here!  If we transparently handled+	 * forward seek here by using the skipper, that will break+	 * other libarchive code that assumes a successful forward+	 * seek means it can also seek backwards.+	 */+	if (self->archive->client.seeker == NULL) {+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+		    "Current client reader does not support seeking a device");+		return (ARCHIVE_FAILED);+	}+	return (self->archive->client.seeker)(&self->archive->archive,+	    self->data, offset, whence);+}++static int+client_close_proxy(struct archive_read_filter *self)+{+	int r = ARCHIVE_OK, r2;+	unsigned int i;++	if (self->archive->client.closer == NULL)+		return (r);+	for (i = 0; i < self->archive->client.nodes; i++)+	{+		r2 = (self->archive->client.closer)+			((struct archive *)self->archive,+				self->archive->client.dataset[i].data);+		if (r > r2)+			r = r2;+	}+	return (r);+}++static int+client_open_proxy(struct archive_read_filter *self)+{+  int r = ARCHIVE_OK;+	if (self->archive->client.opener != NULL)+		r = (self->archive->client.opener)(+		    (struct archive *)self->archive, self->data);+	return (r);+}++static int+client_switch_proxy(struct archive_read_filter *self, unsigned int iindex)+{+  int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK;+	void *data2 = NULL;++	/* Don't do anything if already in the specified data node */+	if (self->archive->client.cursor == iindex)+		return (ARCHIVE_OK);++	self->archive->client.cursor = iindex;+	data2 = self->archive->client.dataset[self->archive->client.cursor].data;+	if (self->archive->client.switcher != NULL)+	{+		r1 = r2 = (self->archive->client.switcher)+			((struct archive *)self->archive, self->data, data2);+		self->data = data2;+	}+	else+	{+		/* Attempt to call close and open instead */+		if (self->archive->client.closer != NULL)+			r1 = (self->archive->client.closer)+				((struct archive *)self->archive, self->data);+		self->data = data2;+		if (self->archive->client.opener != NULL)+			r2 = (self->archive->client.opener)+				((struct archive *)self->archive, self->data);+	}+	return (r1 < r2) ? r1 : r2;+}++int+archive_read_set_open_callback(struct archive *_a,+    archive_open_callback *client_opener)+{+	struct archive_read *a = (struct archive_read *)_a;+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+	    "archive_read_set_open_callback");+	a->client.opener = client_opener;+	return ARCHIVE_OK;+}++int+archive_read_set_read_callback(struct archive *_a,+    archive_read_callback *client_reader)+{+	struct archive_read *a = (struct archive_read *)_a;+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+	    "archive_read_set_read_callback");+	a->client.reader = client_reader;+	return ARCHIVE_OK;+}++int+archive_read_set_skip_callback(struct archive *_a,+    archive_skip_callback *client_skipper)+{+	struct archive_read *a = (struct archive_read *)_a;+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+	    "archive_read_set_skip_callback");+	a->client.skipper = client_skipper;+	return ARCHIVE_OK;+}++int+archive_read_set_seek_callback(struct archive *_a,+    archive_seek_callback *client_seeker)+{+	struct archive_read *a = (struct archive_read *)_a;+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+	    "archive_read_set_seek_callback");+	a->client.seeker = client_seeker;+	return ARCHIVE_OK;+}++int+archive_read_set_close_callback(struct archive *_a,+    archive_close_callback *client_closer)+{+	struct archive_read *a = (struct archive_read *)_a;+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+	    "archive_read_set_close_callback");+	a->client.closer = client_closer;+	return ARCHIVE_OK;+}++int+archive_read_set_switch_callback(struct archive *_a,+    archive_switch_callback *client_switcher)+{+	struct archive_read *a = (struct archive_read *)_a;+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+	    "archive_read_set_switch_callback");+	a->client.switcher = client_switcher;+	return ARCHIVE_OK;+}++int+archive_read_set_callback_data(struct archive *_a, void *client_data)+{+	return archive_read_set_callback_data2(_a, client_data, 0);+}++int+archive_read_set_callback_data2(struct archive *_a, void *client_data,+    unsigned int iindex)+{+	struct archive_read *a = (struct archive_read *)_a;+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+	    "archive_read_set_callback_data2");++	if (a->client.nodes == 0)+	{+		a->client.dataset = (struct archive_read_data_node *)+		    calloc(1, sizeof(*a->client.dataset));+		if (a->client.dataset == NULL)+		{+			archive_set_error(&a->archive, ENOMEM,+				"No memory.");+			return ARCHIVE_FATAL;+		}+		a->client.nodes = 1;+	}++	if (iindex > a->client.nodes - 1)+	{+		archive_set_error(&a->archive, EINVAL,+			"Invalid index specified.");+		return ARCHIVE_FATAL;+	}+	a->client.dataset[iindex].data = client_data;+	a->client.dataset[iindex].begin_position = -1;+	a->client.dataset[iindex].total_size = -1;+	return ARCHIVE_OK;+}++int+archive_read_add_callback_data(struct archive *_a, void *client_data,+    unsigned int iindex)+{+	struct archive_read *a = (struct archive_read *)_a;+	void *p;+	unsigned int i;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+	    "archive_read_add_callback_data");+	if (iindex > a->client.nodes) {+		archive_set_error(&a->archive, EINVAL,+			"Invalid index specified.");+		return ARCHIVE_FATAL;+	}+	p = realloc(a->client.dataset, sizeof(*a->client.dataset)+		* (++(a->client.nodes)));+	if (p == NULL) {+		archive_set_error(&a->archive, ENOMEM,+			"No memory.");+		return ARCHIVE_FATAL;+	}+	a->client.dataset = (struct archive_read_data_node *)p;+	for (i = a->client.nodes - 1; i > iindex; i--) {+		a->client.dataset[i].data = a->client.dataset[i-1].data;+		a->client.dataset[i].begin_position = -1;+		a->client.dataset[i].total_size = -1;+	}+	a->client.dataset[iindex].data = client_data;+	a->client.dataset[iindex].begin_position = -1;+	a->client.dataset[iindex].total_size = -1;+	return ARCHIVE_OK;+}++int+archive_read_append_callback_data(struct archive *_a, void *client_data)+{+	struct archive_read *a = (struct archive_read *)_a;+	return archive_read_add_callback_data(_a, client_data, a->client.nodes);+}++int+archive_read_prepend_callback_data(struct archive *_a, void *client_data)+{+	return archive_read_add_callback_data(_a, client_data, 0);+}++int+archive_read_open1(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter *filter, *tmp;+	int slot, e = ARCHIVE_OK;+	unsigned int i;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+	    "archive_read_open");+	archive_clear_error(&a->archive);++	if (a->client.reader == NULL) {+		archive_set_error(&a->archive, EINVAL,+		    "No reader function provided to archive_read_open");+		a->archive.state = ARCHIVE_STATE_FATAL;+		return (ARCHIVE_FATAL);+	}++	/* Open data source. */+	if (a->client.opener != NULL) {+		e = (a->client.opener)(&a->archive, a->client.dataset[0].data);+		if (e != 0) {+			/* If the open failed, call the closer to clean up. */+			if (a->client.closer) {+				for (i = 0; i < a->client.nodes; i++)+					(a->client.closer)(&a->archive,+					    a->client.dataset[i].data);+			}+			return (e);+		}+	}++	filter = calloc(1, sizeof(*filter));+	if (filter == NULL)+		return (ARCHIVE_FATAL);+	filter->bidder = NULL;+	filter->upstream = NULL;+	filter->archive = a;+	filter->data = a->client.dataset[0].data;+	filter->open = client_open_proxy;+	filter->read = client_read_proxy;+	filter->skip = client_skip_proxy;+	filter->seek = client_seek_proxy;+	filter->close = client_close_proxy;+	filter->sswitch = client_switch_proxy;+	filter->name = "none";+	filter->code = ARCHIVE_FILTER_NONE;++	a->client.dataset[0].begin_position = 0;+	if (!a->filter || !a->bypass_filter_bidding)+	{+		a->filter = filter;+		/* Build out the input pipeline. */+		e = choose_filters(a);+		if (e < ARCHIVE_WARN) {+			a->archive.state = ARCHIVE_STATE_FATAL;+			return (ARCHIVE_FATAL);+		}+	}+	else+	{+		/* Need to add "NONE" type filter at the end of the filter chain */+		tmp = a->filter;+		while (tmp->upstream)+			tmp = tmp->upstream;+		tmp->upstream = filter;+	}++	if (!a->format)+	{+		slot = choose_format(a);+		if (slot < 0) {+			close_filters(a);+			a->archive.state = ARCHIVE_STATE_FATAL;+			return (ARCHIVE_FATAL);+		}+		a->format = &(a->formats[slot]);+	}++	a->archive.state = ARCHIVE_STATE_HEADER;++	/* Ensure libarchive starts from the first node in a multivolume set */+	client_switch_proxy(a->filter, 0);+	return (e);+}++/*+ * Allow each registered stream transform to bid on whether+ * it wants to handle this stream.  Repeat until we've finished+ * building the pipeline.+ */++/* We won't build a filter pipeline with more stages than this. */+#define MAX_NUMBER_FILTERS 25++static int+choose_filters(struct archive_read *a)+{+	int number_bidders, i, bid, best_bid, number_filters;+	struct archive_read_filter_bidder *bidder, *best_bidder;+	struct archive_read_filter *filter;+	ssize_t avail;+	int r;++	for (number_filters = 0; number_filters < MAX_NUMBER_FILTERS; ++number_filters) {+		number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);++		best_bid = 0;+		best_bidder = NULL;++		bidder = a->bidders;+		for (i = 0; i < number_bidders; i++, bidder++) {+			if (bidder->bid != NULL) {+				bid = (bidder->bid)(bidder, a->filter);+				if (bid > best_bid) {+					best_bid = bid;+					best_bidder = bidder;+				}+			}+		}++		/* If no bidder, we're done. */+		if (best_bidder == NULL) {+			/* Verify the filter by asking it for some data. */+			__archive_read_filter_ahead(a->filter, 1, &avail);+			if (avail < 0) {+				__archive_read_free_filters(a);+				return (ARCHIVE_FATAL);+			}+			a->archive.compression_name = a->filter->name;+			a->archive.compression_code = a->filter->code;+			return (ARCHIVE_OK);+		}++		filter+		    = (struct archive_read_filter *)calloc(1, sizeof(*filter));+		if (filter == NULL)+			return (ARCHIVE_FATAL);+		filter->bidder = best_bidder;+		filter->archive = a;+		filter->upstream = a->filter;+		a->filter = filter;+		r = (best_bidder->init)(a->filter);+		if (r != ARCHIVE_OK) {+			__archive_read_free_filters(a);+			return (ARCHIVE_FATAL);+		}+	}+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Input requires too many filters for decoding");+	return (ARCHIVE_FATAL);+}++int+__archive_read_header(struct archive_read *a, struct archive_entry *entry)+{+	if (a->filter->read_header)+		return a->filter->read_header(a->filter, entry);+	else+		return (ARCHIVE_OK);+}++/*+ * Read header of next entry.+ */+static int+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)+{+	struct archive_read *a = (struct archive_read *)_a;+	int r1 = ARCHIVE_OK, r2;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_read_next_header");++	archive_entry_clear(entry);+	archive_clear_error(&a->archive);++	/*+	 * If client didn't consume entire data, skip any remainder+	 * (This is especially important for GNU incremental directories.)+	 */+	if (a->archive.state == ARCHIVE_STATE_DATA) {+		r1 = archive_read_data_skip(&a->archive);+		if (r1 == ARCHIVE_EOF)+			archive_set_error(&a->archive, EIO,+			    "Premature end-of-file.");+		if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) {+			a->archive.state = ARCHIVE_STATE_FATAL;+			return (ARCHIVE_FATAL);+		}+	}++	/* Record start-of-header offset in uncompressed stream. */+	a->header_position = a->filter->position;++	++_a->file_count;+	r2 = (a->format->read_header)(a, entry);++	/*+	 * EOF and FATAL are persistent at this layer.  By+	 * modifying the state, we guarantee that future calls to+	 * read a header or read data will fail.+	 */+	switch (r2) {+	case ARCHIVE_EOF:+		a->archive.state = ARCHIVE_STATE_EOF;+		--_a->file_count;/* Revert a file counter. */+		break;+	case ARCHIVE_OK:+		a->archive.state = ARCHIVE_STATE_DATA;+		break;+	case ARCHIVE_WARN:+		a->archive.state = ARCHIVE_STATE_DATA;+		break;+	case ARCHIVE_RETRY:+		break;+	case ARCHIVE_FATAL:+		a->archive.state = ARCHIVE_STATE_FATAL;+		break;+	}++	__archive_reset_read_data(&a->archive);++	a->data_start_node = a->client.cursor;+	/* EOF always wins; otherwise return the worst error. */+	return (r2 < r1 || r2 == ARCHIVE_EOF) ? r2 : r1;+}++static int+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)+{+	int ret;+	struct archive_read *a = (struct archive_read *)_a;+	*entryp = NULL;+	ret = _archive_read_next_header2(_a, a->entry);+	*entryp = a->entry;+	return ret;+}++/*+ * Allow each registered format to bid on whether it wants to handle+ * the next entry.  Return index of winning bidder.+ */+static int+choose_format(struct archive_read *a)+{+	int slots;+	int i;+	int bid, best_bid;+	int best_bid_slot;++	slots = sizeof(a->formats) / sizeof(a->formats[0]);+	best_bid = -1;+	best_bid_slot = -1;++	/* Set up a->format for convenience of bidders. */+	a->format = &(a->formats[0]);+	for (i = 0; i < slots; i++, a->format++) {+		if (a->format->bid) {+			bid = (a->format->bid)(a, best_bid);+			if (bid == ARCHIVE_FATAL)+				return (ARCHIVE_FATAL);+			if (a->filter->position != 0)+				__archive_read_seek(a, 0, SEEK_SET);+			if ((bid > best_bid) || (best_bid_slot < 0)) {+				best_bid = bid;+				best_bid_slot = i;+			}+		}+	}++	/*+	 * There were no bidders; this is a serious programmer error+	 * and demands a quick and definitive abort.+	 */+	if (best_bid_slot < 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "No formats registered");+		return (ARCHIVE_FATAL);+	}++	/*+	 * There were bidders, but no non-zero bids; this means we+	 * can't support this stream.+	 */+	if (best_bid < 1) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unrecognized archive format");+		return (ARCHIVE_FATAL);+	}++	return (best_bid_slot);+}++/*+ * Return the file offset (within the uncompressed data stream) where+ * the last header started.+ */+la_int64_t+archive_read_header_position(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_header_position");+	return (a->header_position);+}++/*+ * Returns 1 if the archive contains at least one encrypted entry.+ * If the archive format not support encryption at all+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.+ * If for any other reason (e.g. not enough data read so far)+ * we cannot say whether there are encrypted entries, then+ * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.+ * In general, this function will return values below zero when the+ * reader is uncertain or totally incapable of encryption support.+ * When this function returns 0 you can be sure that the reader+ * supports encryption detection but no encrypted entries have+ * been found yet.+ *+ * NOTE: If the metadata/header of an archive is also encrypted, you+ * cannot rely on the number of encrypted entries. That is why this+ * function does not return the number of encrypted entries but#+ * just shows that there are some.+ */+int+archive_read_has_encrypted_entries(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	int format_supports_encryption = archive_read_format_capabilities(_a)+			& (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);++	if (!_a || !format_supports_encryption) {+		/* Format in general doesn't support encryption */+		return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED;+	}++	/* A reader potentially has read enough data now. */+	if (a->format && a->format->has_encrypted_entries) {+		return (a->format->has_encrypted_entries)(a);+	}++	/* For any other reason we cannot say how many entries are there. */+	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;+}++/*+ * Returns a bitmask of capabilities that are supported by the archive format reader.+ * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned.+ */+int+archive_read_format_capabilities(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	if (a && a->format && a->format->format_capabilties) {+		return (a->format->format_capabilties)(a);+	}+	return ARCHIVE_READ_FORMAT_CAPS_NONE;+}++/*+ * Read data from an archive entry, using a read(2)-style interface.+ * This is a convenience routine that just calls+ * archive_read_data_block and copies the results into the client+ * buffer, filling any gaps with zero bytes.  Clients using this+ * API can be completely ignorant of sparse-file issues; sparse files+ * will simply be padded with nulls.+ *+ * DO NOT intermingle calls to this function and archive_read_data_block+ * to read a single entry body.+ */+la_ssize_t+archive_read_data(struct archive *_a, void *buff, size_t s)+{+	struct archive *a = (struct archive *)_a;+	char	*dest;+	const void *read_buf;+	size_t	 bytes_read;+	size_t	 len;+	int	 r;++	bytes_read = 0;+	dest = (char *)buff;++	while (s > 0) {+		if (a->read_data_offset == a->read_data_output_offset &&+		    a->read_data_remaining == 0) {+			read_buf = a->read_data_block;+			a->read_data_is_posix_read = 1;+			a->read_data_requested = s;+			r = archive_read_data_block(a, &read_buf,+			    &a->read_data_remaining, &a->read_data_offset);+			a->read_data_block = read_buf;+			if (r == ARCHIVE_EOF)+				return (bytes_read);+			/*+			 * Error codes are all negative, so the status+			 * return here cannot be confused with a valid+			 * byte count.  (ARCHIVE_OK is zero.)+			 */+			if (r < ARCHIVE_OK)+				return (r);+		}++		if (a->read_data_offset < a->read_data_output_offset) {+			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,+			    "Encountered out-of-order sparse blocks");+			return (ARCHIVE_RETRY);+		}++		/* Compute the amount of zero padding needed. */+		if (a->read_data_output_offset + (int64_t)s <+		    a->read_data_offset) {+			len = s;+		} else if (a->read_data_output_offset <+		    a->read_data_offset) {+			len = (size_t)(a->read_data_offset -+			    a->read_data_output_offset);+		} else+			len = 0;++		/* Add zeroes. */+		memset(dest, 0, len);+		s -= len;+		a->read_data_output_offset += len;+		dest += len;+		bytes_read += len;++		/* Copy data if there is any space left. */+		if (s > 0) {+			len = a->read_data_remaining;+			if (len > s)+				len = s;+			if (len) {+				memcpy(dest, a->read_data_block, len);+				s -= len;+				a->read_data_block += len;+				a->read_data_remaining -= len;+				a->read_data_output_offset += len;+				a->read_data_offset += len;+				dest += len;+				bytes_read += len;+			}+		}+	}+	a->read_data_is_posix_read = 0;+	a->read_data_requested = 0;+	return (bytes_read);+}++/*+ * Reset the read_data_* variables, used for starting a new entry.+ */+void __archive_reset_read_data(struct archive * a)+{+	a->read_data_output_offset = 0;+	a->read_data_remaining = 0;+	a->read_data_is_posix_read = 0;+	a->read_data_requested = 0;++   /* extra resets, from rar.c */+   a->read_data_block = NULL;+   a->read_data_offset = 0;+}++/*+ * Skip over all remaining data in this entry.+ */+int+archive_read_data_skip(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	int r;+	const void *buff;+	size_t size;+	int64_t offset;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_read_data_skip");++	if (a->format->read_data_skip != NULL)+		r = (a->format->read_data_skip)(a);+	else {+		while ((r = archive_read_data_block(&a->archive,+			    &buff, &size, &offset))+		    == ARCHIVE_OK)+			;+	}++	if (r == ARCHIVE_EOF)+		r = ARCHIVE_OK;++	a->archive.state = ARCHIVE_STATE_HEADER;+	return (r);+}++la_int64_t+archive_seek_data(struct archive *_a, int64_t offset, int whence)+{+	struct archive_read *a = (struct archive_read *)_a;+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_seek_data_block");++	if (a->format->seek_data == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+		    "Internal error: "+		    "No format_seek_data_block function registered");+		return (ARCHIVE_FATAL);+	}++	return (a->format->seek_data)(a, offset, whence);+}++/*+ * Read the next block of entry data from the archive.+ * This is a zero-copy interface; the client receives a pointer,+ * size, and file offset of the next available block of data.+ *+ * Returns ARCHIVE_OK if the operation is successful, ARCHIVE_EOF if+ * the end of entry is encountered.+ */+static int+_archive_read_data_block(struct archive *_a,+    const void **buff, size_t *size, int64_t *offset)+{+	struct archive_read *a = (struct archive_read *)_a;+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_read_data_block");++	if (a->format->read_data == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+		    "Internal error: "+		    "No format->read_data function registered");+		return (ARCHIVE_FATAL);+	}++	return (a->format->read_data)(a, buff, size, offset);+}++static int+close_filters(struct archive_read *a)+{+	struct archive_read_filter *f = a->filter;+	int r = ARCHIVE_OK;+	/* Close each filter in the pipeline. */+	while (f != NULL) {+		struct archive_read_filter *t = f->upstream;+		if (!f->closed && f->close != NULL) {+			int r1 = (f->close)(f);+			f->closed = 1;+			if (r1 < r)+				r = r1;+		}+		free(f->buffer);+		f->buffer = NULL;+		f = t;+	}+	return r;+}++void+__archive_read_free_filters(struct archive_read *a)+{+	/* Make sure filters are closed and their buffers are freed */+	close_filters(a);++	while (a->filter != NULL) {+		struct archive_read_filter *t = a->filter->upstream;+		free(a->filter);+		a->filter = t;+	}+}++/*+ * return the count of # of filters in use+ */+static int+_archive_filter_count(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter *p = a->filter;+	int count = 0;+	while(p) {+		count++;+		p = p->upstream;+	}+	return count;+}++/*+ * Close the file and all I/O.+ */+static int+_archive_read_close(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	int r = ARCHIVE_OK, r1 = ARCHIVE_OK;++	archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");+	if (a->archive.state == ARCHIVE_STATE_CLOSED)+		return (ARCHIVE_OK);+	archive_clear_error(&a->archive);+	a->archive.state = ARCHIVE_STATE_CLOSED;++	/* TODO: Clean up the formatters. */++	/* Release the filter objects. */+	r1 = close_filters(a);+	if (r1 < r)+		r = r1;++	return (r);+}++/*+ * Release memory and other resources.+ */+static int+_archive_read_free(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_passphrase *p;+	int i, n;+	int slots;+	int r = ARCHIVE_OK;++	if (_a == NULL)+		return (ARCHIVE_OK);+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");+	if (a->archive.state != ARCHIVE_STATE_CLOSED+	    && a->archive.state != ARCHIVE_STATE_FATAL)+		r = archive_read_close(&a->archive);++	/* Call cleanup functions registered by optional components. */+	if (a->cleanup_archive_extract != NULL)+		r = (a->cleanup_archive_extract)(a);++	/* Cleanup format-specific data. */+	slots = sizeof(a->formats) / sizeof(a->formats[0]);+	for (i = 0; i < slots; i++) {+		a->format = &(a->formats[i]);+		if (a->formats[i].cleanup)+			(a->formats[i].cleanup)(a);+	}++	/* Free the filters */+	__archive_read_free_filters(a);++	/* Release the bidder objects. */+	n = sizeof(a->bidders)/sizeof(a->bidders[0]);+	for (i = 0; i < n; i++) {+		if (a->bidders[i].free != NULL) {+			int r1 = (a->bidders[i].free)(&a->bidders[i]);+			if (r1 < r)+				r = r1;+		}+	}++	/* Release passphrase list. */+	p = a->passphrases.first;+	while (p != NULL) {+		struct archive_read_passphrase *np = p->next;++		/* A passphrase should be cleaned. */+		memset(p->passphrase, 0, strlen(p->passphrase));+		free(p->passphrase);+		free(p);+		p = np;+	}++	archive_string_free(&a->archive.error_string);+	archive_entry_free(a->entry);+	a->archive.magic = 0;+	__archive_clean(&a->archive);+	free(a->client.dataset);+	free(a);+	return (r);+}++static struct archive_read_filter *+get_filter(struct archive *_a, int n)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter *f = a->filter;+	/* We use n == -1 for 'the last filter', which is always the+	 * client proxy. */+	if (n == -1 && f != NULL) {+		struct archive_read_filter *last = f;+		f = f->upstream;+		while (f != NULL) {+			last = f;+			f = f->upstream;+		}+		return (last);+	}+	if (n < 0)+		return NULL;+	while (n > 0 && f != NULL) {+		f = f->upstream;+		--n;+	}+	return (f);+}++static int+_archive_filter_code(struct archive *_a, int n)+{+	struct archive_read_filter *f = get_filter(_a, n);+	return f == NULL ? -1 : f->code;+}++static const char *+_archive_filter_name(struct archive *_a, int n)+{+	struct archive_read_filter *f = get_filter(_a, n);+	return f != NULL ? f->name : NULL;+}++static int64_t+_archive_filter_bytes(struct archive *_a, int n)+{+	struct archive_read_filter *f = get_filter(_a, n);+	return f == NULL ? -1 : f->position;+}++/*+ * Used internally by read format handlers to register their bid and+ * initialization functions.+ */+int+__archive_read_register_format(struct archive_read *a,+    void *format_data,+    const char *name,+    int (*bid)(struct archive_read *, int),+    int (*options)(struct archive_read *, const char *, const char *),+    int (*read_header)(struct archive_read *, struct archive_entry *),+    int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *),+    int (*read_data_skip)(struct archive_read *),+    int64_t (*seek_data)(struct archive_read *, int64_t, int),+    int (*cleanup)(struct archive_read *),+    int (*format_capabilities)(struct archive_read *),+    int (*has_encrypted_entries)(struct archive_read *))+{+	int i, number_slots;++	archive_check_magic(&a->archive,+	    ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+	    "__archive_read_register_format");++	number_slots = sizeof(a->formats) / sizeof(a->formats[0]);++	for (i = 0; i < number_slots; i++) {+		if (a->formats[i].bid == bid)+			return (ARCHIVE_WARN); /* We've already installed */+		if (a->formats[i].bid == NULL) {+			a->formats[i].bid = bid;+			a->formats[i].options = options;+			a->formats[i].read_header = read_header;+			a->formats[i].read_data = read_data;+			a->formats[i].read_data_skip = read_data_skip;+			a->formats[i].seek_data = seek_data;+			a->formats[i].cleanup = cleanup;+			a->formats[i].data = format_data;+			a->formats[i].name = name;+			a->formats[i].format_capabilties = format_capabilities;+			a->formats[i].has_encrypted_entries = has_encrypted_entries;+			return (ARCHIVE_OK);+		}+	}++	archive_set_error(&a->archive, ENOMEM,+	    "Not enough slots for format registration");+	return (ARCHIVE_FATAL);+}++/*+ * Used internally by decompression routines to register their bid and+ * initialization functions.+ */+int+__archive_read_get_bidder(struct archive_read *a,+    struct archive_read_filter_bidder **bidder)+{+	int i, number_slots;++	number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]);++	for (i = 0; i < number_slots; i++) {+		if (a->bidders[i].bid == NULL) {+			memset(a->bidders + i, 0, sizeof(a->bidders[0]));+			*bidder = (a->bidders + i);+			return (ARCHIVE_OK);+		}+	}++	archive_set_error(&a->archive, ENOMEM,+	    "Not enough slots for filter registration");+	return (ARCHIVE_FATAL);+}++/*+ * The next section implements the peek/consume internal I/O+ * system used by archive readers.  This system allows simple+ * read-ahead for consumers while preserving zero-copy operation+ * most of the time.+ *+ * The two key operations:+ *  * The read-ahead function returns a pointer to a block of data+ *    that satisfies a minimum request.+ *  * The consume function advances the file pointer.+ *+ * In the ideal case, filters generate blocks of data+ * and __archive_read_ahead() just returns pointers directly into+ * those blocks.  Then __archive_read_consume() just bumps those+ * pointers.  Only if your request would span blocks does the I/O+ * layer use a copy buffer to provide you with a contiguous block of+ * data.+ *+ * A couple of useful idioms:+ *  * "I just want some data."  Ask for 1 byte and pay attention to+ *    the "number of bytes available" from __archive_read_ahead().+ *    Consume whatever you actually use.+ *  * "I want to output a large block of data."  As above, ask for 1 byte,+ *    emit all that's available (up to whatever limit you have), consume+ *    it all, then repeat until you're done.  This effectively means that+ *    you're passing along the blocks that came from your provider.+ *  * "I want to peek ahead by a large amount."  Ask for 4k or so, then+ *    double and repeat until you get an error or have enough.  Note+ *    that the I/O layer will likely end up expanding its copy buffer+ *    to fit your request, so use this technique cautiously.  This+ *    technique is used, for example, by some of the format tasting+ *    code that has uncertain look-ahead needs.+ */++/*+ * Looks ahead in the input stream:+ *  * If 'avail' pointer is provided, that returns number of bytes available+ *    in the current buffer, which may be much larger than requested.+ *  * If end-of-file, *avail gets set to zero.+ *  * If error, *avail gets error code.+ *  * If request can be met, returns pointer to data.+ *  * If minimum request cannot be met, returns NULL.+ *+ * Note: If you just want "some data", ask for 1 byte and pay attention+ * to *avail, which will have the actual amount available.  If you+ * know exactly how many bytes you need, just ask for that and treat+ * a NULL return as an error.+ *+ * Important:  This does NOT move the file pointer.  See+ * __archive_read_consume() below.+ */+const void *+__archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)+{+	return (__archive_read_filter_ahead(a->filter, min, avail));+}++const void *+__archive_read_filter_ahead(struct archive_read_filter *filter,+    size_t min, ssize_t *avail)+{+	ssize_t bytes_read;+	size_t tocopy;++	if (filter->fatal) {+		if (avail)+			*avail = ARCHIVE_FATAL;+		return (NULL);+	}++	/*+	 * Keep pulling more data until we can satisfy the request.+	 */+	for (;;) {++		/*+		 * If we can satisfy from the copy buffer (and the+		 * copy buffer isn't empty), we're done.  In particular,+		 * note that min == 0 is a perfectly well-defined+		 * request.+		 */+		if (filter->avail >= min && filter->avail > 0) {+			if (avail != NULL)+				*avail = filter->avail;+			return (filter->next);+		}++		/*+		 * We can satisfy directly from client buffer if everything+		 * currently in the copy buffer is still in the client buffer.+		 */+		if (filter->client_total >= filter->client_avail + filter->avail+		    && filter->client_avail + filter->avail >= min) {+			/* "Roll back" to client buffer. */+			filter->client_avail += filter->avail;+			filter->client_next -= filter->avail;+			/* Copy buffer is now empty. */+			filter->avail = 0;+			filter->next = filter->buffer;+			/* Return data from client buffer. */+			if (avail != NULL)+				*avail = filter->client_avail;+			return (filter->client_next);+		}++		/* Move data forward in copy buffer if necessary. */+		if (filter->next > filter->buffer &&+		    filter->next + min > filter->buffer + filter->buffer_size) {+			if (filter->avail > 0)+				memmove(filter->buffer, filter->next,+				    filter->avail);+			filter->next = filter->buffer;+		}++		/* If we've used up the client data, get more. */+		if (filter->client_avail <= 0) {+			if (filter->end_of_file) {+				if (avail != NULL)+					*avail = 0;+				return (NULL);+			}+			bytes_read = (filter->read)(filter,+			    &filter->client_buff);+			if (bytes_read < 0) {		/* Read error. */+				filter->client_total = filter->client_avail = 0;+				filter->client_next =+				    filter->client_buff = NULL;+				filter->fatal = 1;+				if (avail != NULL)+					*avail = ARCHIVE_FATAL;+				return (NULL);+			}+			if (bytes_read == 0) {+				/* Check for another client object first */+				if (filter->archive->client.cursor !=+				      filter->archive->client.nodes - 1) {+					if (client_switch_proxy(filter,+					    filter->archive->client.cursor + 1)+					    == ARCHIVE_OK)+						continue;+				}+				/* Premature end-of-file. */+				filter->client_total = filter->client_avail = 0;+				filter->client_next =+				    filter->client_buff = NULL;+				filter->end_of_file = 1;+				/* Return whatever we do have. */+				if (avail != NULL)+					*avail = filter->avail;+				return (NULL);+			}+			filter->client_total = bytes_read;+			filter->client_avail = filter->client_total;+			filter->client_next = filter->client_buff;+		} else {+			/*+			 * We can't satisfy the request from the copy+			 * buffer or the existing client data, so we+			 * need to copy more client data over to the+			 * copy buffer.+			 */++			/* Ensure the buffer is big enough. */+			if (min > filter->buffer_size) {+				size_t s, t;+				char *p;++				/* Double the buffer; watch for overflow. */+				s = t = filter->buffer_size;+				if (s == 0)+					s = min;+				while (s < min) {+					t *= 2;+					if (t <= s) { /* Integer overflow! */+						archive_set_error(+						    &filter->archive->archive,+						    ENOMEM,+						    "Unable to allocate copy"+						    " buffer");+						filter->fatal = 1;+						if (avail != NULL)+							*avail = ARCHIVE_FATAL;+						return (NULL);+					}+					s = t;+				}+				/* Now s >= min, so allocate a new buffer. */+				p = (char *)malloc(s);+				if (p == NULL) {+					archive_set_error(+						&filter->archive->archive,+						ENOMEM,+					    "Unable to allocate copy buffer");+					filter->fatal = 1;+					if (avail != NULL)+						*avail = ARCHIVE_FATAL;+					return (NULL);+				}+				/* Move data into newly-enlarged buffer. */+				if (filter->avail > 0)+					memmove(p, filter->next, filter->avail);+				free(filter->buffer);+				filter->next = filter->buffer = p;+				filter->buffer_size = s;+			}++			/* We can add client data to copy buffer. */+			/* First estimate: copy to fill rest of buffer. */+			tocopy = (filter->buffer + filter->buffer_size)+			    - (filter->next + filter->avail);+			/* Don't waste time buffering more than we need to. */+			if (tocopy + filter->avail > min)+				tocopy = min - filter->avail;+			/* Don't copy more than is available. */+			if (tocopy > filter->client_avail)+				tocopy = filter->client_avail;++			memcpy(filter->next + filter->avail,+			    filter->client_next, tocopy);+			/* Remove this data from client buffer. */+			filter->client_next += tocopy;+			filter->client_avail -= tocopy;+			/* add it to copy buffer. */+			filter->avail += tocopy;+		}+	}+}++/*+ * Move the file pointer forward.+ */+int64_t+__archive_read_consume(struct archive_read *a, int64_t request)+{+	return (__archive_read_filter_consume(a->filter, request));+}++int64_t+__archive_read_filter_consume(struct archive_read_filter * filter,+    int64_t request)+{+	int64_t skipped;++	if (request < 0)+		return ARCHIVE_FATAL;+	if (request == 0)+		return 0;++	skipped = advance_file_pointer(filter, request);+	if (skipped == request)+		return (skipped);+	/* We hit EOF before we satisfied the skip request. */+	if (skipped < 0)  /* Map error code to 0 for error message below. */+		skipped = 0;+	archive_set_error(&filter->archive->archive,+	    ARCHIVE_ERRNO_MISC,+	    "Truncated input file (needed %jd bytes, only %jd available)",+	    (intmax_t)request, (intmax_t)skipped);+	return (ARCHIVE_FATAL);+}++/*+ * Advance the file pointer by the amount requested.+ * Returns the amount actually advanced, which may be less than the+ * request if EOF is encountered first.+ * Returns a negative value if there's an I/O error.+ */+static int64_t+advance_file_pointer(struct archive_read_filter *filter, int64_t request)+{+	int64_t bytes_skipped, total_bytes_skipped = 0;+	ssize_t bytes_read;+	size_t min;++	if (filter->fatal)+		return (-1);++	/* Use up the copy buffer first. */+	if (filter->avail > 0) {+		min = (size_t)minimum(request, (int64_t)filter->avail);+		filter->next += min;+		filter->avail -= min;+		request -= min;+		filter->position += min;+		total_bytes_skipped += min;+	}++	/* Then use up the client buffer. */+	if (filter->client_avail > 0) {+		min = (size_t)minimum(request, (int64_t)filter->client_avail);+		filter->client_next += min;+		filter->client_avail -= min;+		request -= min;+		filter->position += min;+		total_bytes_skipped += min;+	}+	if (request == 0)+		return (total_bytes_skipped);++	/* If there's an optimized skip function, use it. */+	if (filter->skip != NULL) {+		bytes_skipped = (filter->skip)(filter, request);+		if (bytes_skipped < 0) {	/* error */+			filter->fatal = 1;+			return (bytes_skipped);+		}+		filter->position += bytes_skipped;+		total_bytes_skipped += bytes_skipped;+		request -= bytes_skipped;+		if (request == 0)+			return (total_bytes_skipped);+	}++	/* Use ordinary reads as necessary to complete the request. */+	for (;;) {+		bytes_read = (filter->read)(filter, &filter->client_buff);+		if (bytes_read < 0) {+			filter->client_buff = NULL;+			filter->fatal = 1;+			return (bytes_read);+		}++		if (bytes_read == 0) {+			if (filter->archive->client.cursor !=+			      filter->archive->client.nodes - 1) {+				if (client_switch_proxy(filter,+				    filter->archive->client.cursor + 1)+				    == ARCHIVE_OK)+					continue;+			}+			filter->client_buff = NULL;+			filter->end_of_file = 1;+			return (total_bytes_skipped);+		}++		if (bytes_read >= request) {+			filter->client_next =+			    ((const char *)filter->client_buff) + request;+			filter->client_avail = (size_t)(bytes_read - request);+			filter->client_total = bytes_read;+			total_bytes_skipped += request;+			filter->position += request;+			return (total_bytes_skipped);+		}++		filter->position += bytes_read;+		total_bytes_skipped += bytes_read;+		request -= bytes_read;+	}+}++/**+ * Returns ARCHIVE_FAILED if seeking isn't supported.+ */+int64_t+__archive_read_seek(struct archive_read *a, int64_t offset, int whence)+{+	return __archive_read_filter_seek(a->filter, offset, whence);+}++int64_t+__archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,+    int whence)+{+	struct archive_read_client *client;+	int64_t r;+	unsigned int cursor;++	if (filter->closed || filter->fatal)+		return (ARCHIVE_FATAL);+	if (filter->seek == NULL)+		return (ARCHIVE_FAILED);++	client = &(filter->archive->client);+	switch (whence) {+	case SEEK_CUR:+		/* Adjust the offset and use SEEK_SET instead */+		offset += filter->position;+		__LA_FALLTHROUGH;+	case SEEK_SET:+		cursor = 0;+		while (1)+		{+			if (client->dataset[cursor].begin_position < 0 ||+			    client->dataset[cursor].total_size < 0 ||+			    client->dataset[cursor].begin_position ++			      client->dataset[cursor].total_size - 1 > offset ||+			    cursor + 1 >= client->nodes)+				break;+			r = client->dataset[cursor].begin_position ++				client->dataset[cursor].total_size;+			client->dataset[++cursor].begin_position = r;+		}+		while (1) {+			r = client_switch_proxy(filter, cursor);+			if (r != ARCHIVE_OK)+				return r;+			if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)+				return r;+			client->dataset[cursor].total_size = r;+			if (client->dataset[cursor].begin_position ++			    client->dataset[cursor].total_size - 1 > offset ||+			    cursor + 1 >= client->nodes)+				break;+			r = client->dataset[cursor].begin_position ++				client->dataset[cursor].total_size;+			client->dataset[++cursor].begin_position = r;+		}+		offset -= client->dataset[cursor].begin_position;+		if (offset < 0+		    || offset > client->dataset[cursor].total_size)+			return ARCHIVE_FATAL;+		if ((r = client_seek_proxy(filter, offset, SEEK_SET)) < 0)+			return r;+		break;++	case SEEK_END:+		cursor = 0;+		while (1) {+			if (client->dataset[cursor].begin_position < 0 ||+			    client->dataset[cursor].total_size < 0 ||+			    cursor + 1 >= client->nodes)+				break;+			r = client->dataset[cursor].begin_position ++				client->dataset[cursor].total_size;+			client->dataset[++cursor].begin_position = r;+		}+		while (1) {+			r = client_switch_proxy(filter, cursor);+			if (r != ARCHIVE_OK)+				return r;+			if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)+				return r;+			client->dataset[cursor].total_size = r;+			r = client->dataset[cursor].begin_position ++				client->dataset[cursor].total_size;+			if (cursor + 1 >= client->nodes)+				break;+			client->dataset[++cursor].begin_position = r;+		}+		while (1) {+			if (r + offset >=+			    client->dataset[cursor].begin_position)+				break;+			offset += client->dataset[cursor].total_size;+			if (cursor == 0)+				break;+			cursor--;+			r = client->dataset[cursor].begin_position ++				client->dataset[cursor].total_size;+		}+		offset = (r + offset) - client->dataset[cursor].begin_position;+		if ((r = client_switch_proxy(filter, cursor)) != ARCHIVE_OK)+			return r;+		r = client_seek_proxy(filter, offset, SEEK_SET);+		if (r < ARCHIVE_OK)+			return r;+		break;++	default:+		return (ARCHIVE_FATAL);+	}+	r += client->dataset[cursor].begin_position;++	if (r >= 0) {+		/*+		 * Ouch.  Clearing the buffer like this hurts, especially+		 * at bid time.  A lot of our efficiency at bid time comes+		 * from having bidders reuse the data we've already read.+		 *+		 * TODO: If the seek request is in data we already+		 * have, then don't call the seek callback.+		 *+		 * TODO: Zip seeks to end-of-file at bid time.  If+		 * other formats also start doing this, we may need to+		 * find a way for clients to fudge the seek offset to+		 * a block boundary.+		 *+		 * Hmmm... If whence was SEEK_END, we know the file+		 * size is (r - offset).  Can we use that to simplify+		 * the TODO items above?+		 */+		filter->avail = filter->client_avail = 0;+		filter->next = filter->buffer;+		filter->position = r;+		filter->end_of_file = 0;+	}+	return r;+}
+ c/archive_read_add_passphrase.c view
@@ -0,0 +1,190 @@+/*-+ * Copyright (c) 2014 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include "archive_read_private.h"++static void+add_passphrase_to_tail(struct archive_read *a,+    struct archive_read_passphrase *p)+{+	*a->passphrases.last = p;+	a->passphrases.last = &p->next;+	p->next = NULL;+}++static struct archive_read_passphrase *+remove_passphrases_from_head(struct archive_read *a)+{+	struct archive_read_passphrase *p;++	p = a->passphrases.first;+	if (p != NULL)+		a->passphrases.first = p->next;+	return (p);+}++static void+insert_passphrase_to_head(struct archive_read *a,+    struct archive_read_passphrase *p)+{+	p->next = a->passphrases.first;+	a->passphrases.first = p;+	if (&a->passphrases.first == a->passphrases.last) {+		a->passphrases.last = &p->next;+		p->next = NULL;+	}+}++static struct archive_read_passphrase *+new_read_passphrase(struct archive_read *a, const char *passphrase)+{+	struct archive_read_passphrase *p;++	p = malloc(sizeof(*p));+	if (p == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (NULL);+	}+	p->passphrase = strdup(passphrase);+	if (p->passphrase == NULL) {+		free(p);+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (NULL);+	}+	return (p);+}++int+archive_read_add_passphrase(struct archive *_a, const char *passphrase)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_passphrase *p;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+		"archive_read_add_passphrase");++	if (passphrase == NULL || passphrase[0] == '\0') {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Empty passphrase is unacceptable");+		return (ARCHIVE_FAILED);+	}++	p = new_read_passphrase(a, passphrase);+	if (p == NULL)+		return (ARCHIVE_FATAL);+	add_passphrase_to_tail(a, p);++	return (ARCHIVE_OK);+}++int+archive_read_set_passphrase_callback(struct archive *_a, void *client_data,+    archive_passphrase_callback *cb)+{+	struct archive_read *a = (struct archive_read *)_a;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+		"archive_read_set_passphrase_callback");++	a->passphrases.callback = cb;+	a->passphrases.client_data = client_data;+	return (ARCHIVE_OK);+}++/*+ * Call this in advance when you start to get a passphrase for decryption+ * for a entry.+ */+void+__archive_read_reset_passphrase(struct archive_read *a)+{++	a->passphrases.candidate = -1;+}++/*+ * Get a passphrase for decryption.+ */+const char *+__archive_read_next_passphrase(struct archive_read *a)+{+	struct archive_read_passphrase *p;+	const char *passphrase;++	if (a->passphrases.candidate < 0) {+		/* Count out how many passphrases we have. */+		int cnt = 0;++		for (p = a->passphrases.first; p != NULL; p = p->next)+			cnt++;+		a->passphrases.candidate = cnt;+		p = a->passphrases.first;+	} else if (a->passphrases.candidate > 1) {+		/* Rotate a passphrase list. */+		a->passphrases.candidate--;+		p = remove_passphrases_from_head(a);+		add_passphrase_to_tail(a, p);+		/* Pick a new passphrase candidate up. */+		p = a->passphrases.first;+	} else if (a->passphrases.candidate == 1) {+		/* This case is that all candidates failed to decrypt. */+		a->passphrases.candidate = 0;+		if (a->passphrases.first->next != NULL) {+			/* Rotate a passphrase list. */+			p = remove_passphrases_from_head(a);+			add_passphrase_to_tail(a, p);+		}+		p = NULL;+	} else  /* There is no passphrase candidate. */+		p = NULL;++	if (p != NULL)+		passphrase = p->passphrase;+	else if (a->passphrases.callback != NULL) {+		/* Get a passphrase through a call-back function+		 * since we tried all passphrases out or we don't+		 * have it. */+		passphrase = a->passphrases.callback(&a->archive,+		    a->passphrases.client_data);+		if (passphrase != NULL) {+			p = new_read_passphrase(a, passphrase);+			if (p == NULL)+				return (NULL);+			insert_passphrase_to_head(a, p);+			a->passphrases.candidate = 1;+		}+	} else+		passphrase = NULL;++	return (passphrase);+}
+ c/archive_read_append_filter.c view
@@ -0,0 +1,204 @@+/*-+ * Copyright (c) 2003-2012 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_read_private.h"++int+archive_read_append_filter(struct archive *_a, int code)+{+  int r1, r2, number_bidders, i;+  char str[20];+  struct archive_read_filter_bidder *bidder;+  struct archive_read_filter *filter;+  struct archive_read *a = (struct archive_read *)_a;++  r2 = (ARCHIVE_OK);+  switch (code)+  {+    case ARCHIVE_FILTER_NONE:+      /* No filter to add, so do nothing.+       * NOTE: An initial "NONE" type filter is always set at the end of the+       * filter chain.+       */+      r1 = (ARCHIVE_OK);+      break;+    case ARCHIVE_FILTER_GZIP:+      strcpy(str, "gzip");+      r1 = archive_read_support_filter_gzip(_a);+      break;+    case ARCHIVE_FILTER_BZIP2:+      strcpy(str, "bzip2");+      r1 = archive_read_support_filter_bzip2(_a);+      break;+    case ARCHIVE_FILTER_COMPRESS:+      strcpy(str, "compress (.Z)");+      r1 = archive_read_support_filter_compress(_a);+      break;+    case ARCHIVE_FILTER_PROGRAM:+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+          "Cannot append program filter using archive_read_append_filter");+      return (ARCHIVE_FATAL);+    case ARCHIVE_FILTER_LZMA:+      strcpy(str, "lzma");+      r1 = archive_read_support_filter_lzma(_a);+      break;+    case ARCHIVE_FILTER_XZ:+      strcpy(str, "xz");+      r1 = archive_read_support_filter_xz(_a);+      break;+    case ARCHIVE_FILTER_UU:+      strcpy(str, "uu");+      r1 = archive_read_support_filter_uu(_a);+      break;+    case ARCHIVE_FILTER_RPM:+      strcpy(str, "rpm");+      r1 = archive_read_support_filter_rpm(_a);+      break;+    case ARCHIVE_FILTER_LZ4:+      strcpy(str, "lz4");+      r1 = archive_read_support_filter_lz4(_a);+      break;+    case ARCHIVE_FILTER_ZSTD:+      strcpy(str, "zstd");+      r1 = archive_read_support_filter_zstd(_a);+      break;+    case ARCHIVE_FILTER_LZIP:+      strcpy(str, "lzip");+      r1 = archive_read_support_filter_lzip(_a);+      break;+    case ARCHIVE_FILTER_LRZIP:+      strcpy(str, "lrzip");+      r1 = archive_read_support_filter_lrzip(_a);+      break;+    default:+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+          "Invalid filter code specified");+      return (ARCHIVE_FATAL);+  }++  if (code != ARCHIVE_FILTER_NONE)+  {+    number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);++    bidder = a->bidders;+    for (i = 0; i < number_bidders; i++, bidder++)+    {+      if (!bidder->name || !strcmp(bidder->name, str))+        break;+    }+    if (!bidder->name || strcmp(bidder->name, str))+    {+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+          "Internal error: Unable to append filter");+      return (ARCHIVE_FATAL);+    }++    filter+        = (struct archive_read_filter *)calloc(1, sizeof(*filter));+    if (filter == NULL)+    {+      archive_set_error(&a->archive, ENOMEM, "Out of memory");+      return (ARCHIVE_FATAL);+    }+    filter->bidder = bidder;+    filter->archive = a;+    filter->upstream = a->filter;+    a->filter = filter;+    r2 = (bidder->init)(a->filter);+    if (r2 != ARCHIVE_OK) {+      __archive_read_free_filters(a);+      return (ARCHIVE_FATAL);+    }+  }++  a->bypass_filter_bidding = 1;+  return (r1 < r2) ? r1 : r2;+}++int+archive_read_append_filter_program(struct archive *_a, const char *cmd)+{+  return (archive_read_append_filter_program_signature(_a, cmd, NULL, 0));+}++int+archive_read_append_filter_program_signature(struct archive *_a,+  const char *cmd, const void *signature, size_t signature_len)+{+  int r, number_bidders, i;+  struct archive_read_filter_bidder *bidder;+  struct archive_read_filter *filter;+  struct archive_read *a = (struct archive_read *)_a;++  if (archive_read_support_filter_program_signature(_a, cmd, signature,+    signature_len) != (ARCHIVE_OK))+    return (ARCHIVE_FATAL);++  number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);++  bidder = a->bidders;+  for (i = 0; i < number_bidders; i++, bidder++)+  {+    /* Program bidder name set to filter name after initialization */+    if (bidder->data && !bidder->name)+      break;+  }+  if (!bidder->data)+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+        "Internal error: Unable to append program filter");+    return (ARCHIVE_FATAL);+  }++  filter+      = (struct archive_read_filter *)calloc(1, sizeof(*filter));+  if (filter == NULL)+  {+    archive_set_error(&a->archive, ENOMEM, "Out of memory");+    return (ARCHIVE_FATAL);+  }+  filter->bidder = bidder;+  filter->archive = a;+  filter->upstream = a->filter;+  a->filter = filter;+  r = (bidder->init)(a->filter);+  if (r != ARCHIVE_OK) {+    __archive_read_free_filters(a);+    return (ARCHIVE_FATAL);+  }+  bidder->name = a->filter->name;++  a->bypass_filter_bidding = 1;+  return r;+}
+ c/archive_read_data_into_fd.c view
@@ -0,0 +1,139 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_fd.c,v 1.16 2008/05/23 05:01:29 cperciva Exp $");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"+#include "archive_private.h"++/* Maximum amount of data to write at one time. */+#define	MAX_WRITE	(1024 * 1024)++/*+ * This implementation minimizes copying of data and is sparse-file aware.+ */+static int+pad_to(struct archive *a, int fd, int can_lseek,+    size_t nulls_size, const char *nulls,+    int64_t target_offset, int64_t actual_offset)+{+	size_t to_write;+	ssize_t bytes_written;++	if (can_lseek) {+		actual_offset = lseek(fd,+		    target_offset - actual_offset, SEEK_CUR);+		if (actual_offset != target_offset) {+			archive_set_error(a, errno, "Seek error");+			return (ARCHIVE_FATAL);+		}+		return (ARCHIVE_OK);+	}+	while (target_offset > actual_offset) {+		to_write = nulls_size;+		if (target_offset < actual_offset + (int64_t)nulls_size)+			to_write = (size_t)(target_offset - actual_offset);+		bytes_written = write(fd, nulls, to_write);+		if (bytes_written < 0) {+			archive_set_error(a, errno, "Write error");+			return (ARCHIVE_FATAL);+		}+		actual_offset += bytes_written;+	}+	return (ARCHIVE_OK);+}+++int+archive_read_data_into_fd(struct archive *a, int fd)+{+	struct stat st;+	int r, r2;+	const void *buff;+	size_t size, bytes_to_write;+	ssize_t bytes_written;+	int64_t target_offset;+	int64_t actual_offset = 0;+	int can_lseek;+	char *nulls = NULL;+	size_t nulls_size = 16384;++	archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_read_data_into_fd");++	can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode);+	if (!can_lseek)+		nulls = calloc(1, nulls_size);++	while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) ==+	    ARCHIVE_OK) {+		const char *p = buff;+		if (target_offset > actual_offset) {+			r = pad_to(a, fd, can_lseek, nulls_size, nulls,+			    target_offset, actual_offset);+			if (r != ARCHIVE_OK)+				break;+			actual_offset = target_offset;+		}+		while (size > 0) {+			bytes_to_write = size;+			if (bytes_to_write > MAX_WRITE)+				bytes_to_write = MAX_WRITE;+			bytes_written = write(fd, p, bytes_to_write);+			if (bytes_written < 0) {+				archive_set_error(a, errno, "Write error");+				r = ARCHIVE_FATAL;+				goto cleanup;+			}+			actual_offset += bytes_written;+			p += bytes_written;+			size -= bytes_written;+		}+	}++	if (r == ARCHIVE_EOF && target_offset > actual_offset) {+		r2 = pad_to(a, fd, can_lseek, nulls_size, nulls,+		    target_offset, actual_offset);+		if (r2 != ARCHIVE_OK)+			r = r2;+	}++cleanup:+	free(nulls);+	if (r != ARCHIVE_EOF)+		return (r);+	return (ARCHIVE_OK);+}
+ c/archive_read_disk_entry_from_file.c view
@@ -0,0 +1,1044 @@+/*-+ * Copyright (c) 2003-2009 Tim Kientzle+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA+ * Copyright (c) 2016 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD");++/* This is the tree-walking code for POSIX systems. */+#if !defined(_WIN32) || defined(__CYGWIN__)++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_SYS_EXTATTR_H+#include <sys/extattr.h>+#endif+#ifdef HAVE_SYS_IOCTL_H+#include <sys/ioctl.h>+#endif+#ifdef HAVE_SYS_PARAM_H+#include <sys/param.h>+#endif+#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#if defined(HAVE_SYS_XATTR_H)+#include <sys/xattr.h>+#elif defined(HAVE_ATTR_XATTR_H)+#include <attr/xattr.h>+#endif+#ifdef HAVE_SYS_EA_H+#include <sys/ea.h>+#endif+#ifdef HAVE_COPYFILE_H+#include <copyfile.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_LINUX_TYPES_H+#include <linux/types.h>+#endif+#ifdef HAVE_LINUX_FIEMAP_H+#include <linux/fiemap.h>+#endif+#ifdef HAVE_LINUX_FS_H+#include <linux/fs.h>+#endif+/*+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.+ * As the include guards don't agree, the order of include is important.+ */+#ifdef HAVE_LINUX_EXT2_FS_H+#include <linux/ext2_fs.h>      /* for Linux file flags */+#endif+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)+#include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */+#endif+#ifdef HAVE_PATHS_H+#include <paths.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_disk_private.h"++#ifndef O_CLOEXEC+#define O_CLOEXEC	0+#endif++static int setup_mac_metadata(struct archive_read_disk *,+    struct archive_entry *, int *fd);+static int setup_xattrs(struct archive_read_disk *,+    struct archive_entry *, int *fd);+static int setup_sparse(struct archive_read_disk *,+    struct archive_entry *, int *fd);+#if defined(HAVE_LINUX_FIEMAP_H)+static int setup_sparse_fiemap(struct archive_read_disk *,+    struct archive_entry *, int *fd);+#endif++#if !ARCHIVE_ACL_SUPPORT+int+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	(void)a;      /* UNUSED */+	(void)entry;  /* UNUSED */+	(void)fd;     /* UNUSED */+	return (ARCHIVE_OK);+}+#endif++/*+ * Enter working directory and return working pathname of archive_entry.+ * If a pointer to an integer is provided and its value is below zero+ * open a file descriptor on this pathname.+ */+const char *+archive_read_disk_entry_setup_path(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	const char *path;++	path = archive_entry_sourcepath(entry);++	if (path == NULL || (a->tree != NULL &&+	    a->tree_enter_working_dir(a->tree) != 0))+		path = archive_entry_pathname(entry);+	if (path == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		   "Couldn't determine path");+	} else if (fd != NULL && *fd < 0 && a->tree != NULL &&+	    (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)) {+		*fd = a->open_on_current_dir(a->tree, path,+		    O_RDONLY | O_NONBLOCK);+	}+	return (path);+}++int+archive_read_disk_entry_from_file(struct archive *_a,+    struct archive_entry *entry,+    int fd,+    const struct stat *st)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	const char *path, *name;+	struct stat s;+	int initial_fd = fd;+	int r, r1;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,+		"archive_read_disk_entry_from_file");++	archive_clear_error(_a);+	path = archive_entry_sourcepath(entry);+	if (path == NULL)+		path = archive_entry_pathname(entry);++	if (a->tree == NULL) {+		if (st == NULL) {+#if HAVE_FSTAT+			if (fd >= 0) {+				if (fstat(fd, &s) != 0) {+					archive_set_error(&a->archive, errno,+					    "Can't fstat");+					return (ARCHIVE_FAILED);+				}+			} else+#endif+#if HAVE_LSTAT+			if (!a->follow_symlinks) {+				if (lstat(path, &s) != 0) {+					archive_set_error(&a->archive, errno,+					    "Can't lstat %s", path);+					return (ARCHIVE_FAILED);+				}+			} else+#endif+			if (la_stat(path, &s) != 0) {+				archive_set_error(&a->archive, errno,+				    "Can't stat %s", path);+				return (ARCHIVE_FAILED);+			}+			st = &s;+		}+		archive_entry_copy_stat(entry, st);+	}++	/* Lookup uname/gname */+	name = archive_read_disk_uname(_a, archive_entry_uid(entry));+	if (name != NULL)+		archive_entry_copy_uname(entry, name);+	name = archive_read_disk_gname(_a, archive_entry_gid(entry));+	if (name != NULL)+		archive_entry_copy_gname(entry, name);++#ifdef HAVE_STRUCT_STAT_ST_FLAGS+	/* On FreeBSD, we get flags for free with the stat. */+	/* TODO: Does this belong in copy_stat()? */+	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && st->st_flags != 0)+		archive_entry_set_fflags(entry, st->st_flags, 0);+#endif++#if (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \+    (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))+	/* Linux requires an extra ioctl to pull the flags.  Although+	 * this is an extra step, it has a nice side-effect: We get an+	 * open file descriptor which we can use in the subsequent lookups. */+	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 &&+	    (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {+		if (fd < 0) {+			if (a->tree != NULL)+				fd = a->open_on_current_dir(a->tree, path,+					O_RDONLY | O_NONBLOCK | O_CLOEXEC);+			else+				fd = open(path, O_RDONLY | O_NONBLOCK |+						O_CLOEXEC);+			__archive_ensure_cloexec_flag(fd);+		}+		if (fd >= 0) {+			int stflags;+			r = ioctl(fd,+#if defined(FS_IOC_GETFLAGS)+			    FS_IOC_GETFLAGS,+#else+			    EXT2_IOC_GETFLAGS,+#endif+			    &stflags);+			if (r == 0 && stflags != 0)+				archive_entry_set_fflags(entry, stflags, 0);+		}+	}+#endif++#if defined(HAVE_READLINK) || defined(HAVE_READLINKAT)+	if (S_ISLNK(st->st_mode)) {+		size_t linkbuffer_len = st->st_size;+		char *linkbuffer;+		int lnklen;++		linkbuffer = malloc(linkbuffer_len + 1);+		if (linkbuffer == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Couldn't read link data");+			return (ARCHIVE_FAILED);+		}+		if (a->tree != NULL) {+#ifdef HAVE_READLINKAT+			lnklen = readlinkat(a->tree_current_dir_fd(a->tree),+			    path, linkbuffer, linkbuffer_len);+#else+			if (a->tree_enter_working_dir(a->tree) != 0) {+				archive_set_error(&a->archive, errno,+				    "Couldn't read link data");+				free(linkbuffer);+				return (ARCHIVE_FAILED);+			}+			lnklen = readlink(path, linkbuffer, linkbuffer_len);+#endif /* HAVE_READLINKAT */+		} else+			lnklen = readlink(path, linkbuffer, linkbuffer_len);+		if (lnklen < 0) {+			archive_set_error(&a->archive, errno,+			    "Couldn't read link data");+			free(linkbuffer);+			return (ARCHIVE_FAILED);+		}+		linkbuffer[lnklen] = '\0';+		archive_entry_set_symlink(entry, linkbuffer);+		free(linkbuffer);+	}+#endif /* HAVE_READLINK || HAVE_READLINKAT */++	r = 0;+	if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0)+		r = archive_read_disk_entry_setup_acls(a, entry, &fd);+	if ((a->flags & ARCHIVE_READDISK_NO_XATTR) == 0) {+		r1 = setup_xattrs(a, entry, &fd);+		if (r1 < r)+			r = r1;+	}+	if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {+		r1 = setup_mac_metadata(a, entry, &fd);+		if (r1 < r)+			r = r1;+	}+	r1 = setup_sparse(a, entry, &fd);+	if (r1 < r)+		r = r1;++	/* If we opened the file earlier in this function, close it. */+	if (initial_fd != fd)+		close(fd);+	return (r);+}++#if defined(__APPLE__) && defined(HAVE_COPYFILE_H)+/*+ * The Mac OS "copyfile()" API copies the extended metadata for a+ * file into a separate file in AppleDouble format (see RFC 1740).+ *+ * Mac OS tar and cpio implementations store this extended+ * metadata as a separate entry just before the regular entry+ * with a "._" prefix added to the filename.+ *+ * Note that this is currently done unconditionally; the tar program has+ * an option to discard this information before the archive is written.+ *+ * TODO: If there's a failure, report it and return ARCHIVE_WARN.+ */+static int+setup_mac_metadata(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	int tempfd = -1;+	int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR;+	struct stat copyfile_stat;+	int ret = ARCHIVE_OK;+	void *buff = NULL;+	int have_attrs;+	const char *name, *tempdir;+	struct archive_string tempfile;++	(void)fd; /* UNUSED */++	name = archive_read_disk_entry_setup_path(a, entry, NULL);+	if (name == NULL)+		return (ARCHIVE_WARN);++	/* Short-circuit if there's nothing to do. */+	have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);+	if (have_attrs == -1) {+		archive_set_error(&a->archive, errno,+			"Could not check extended attributes");+		return (ARCHIVE_WARN);+	}+	if (have_attrs == 0)+		return (ARCHIVE_OK);++	tempdir = NULL;+	if (issetugid() == 0)+		tempdir = getenv("TMPDIR");+	if (tempdir == NULL)+		tempdir = _PATH_TMP;+	archive_string_init(&tempfile);+	archive_strcpy(&tempfile, tempdir);+	archive_strcat(&tempfile, "tar.md.XXXXXX");+	tempfd = mkstemp(tempfile.s);+	if (tempfd < 0) {+		archive_set_error(&a->archive, errno,+		    "Could not open extended attribute file");+		ret = ARCHIVE_WARN;+		goto cleanup;+	}+	__archive_ensure_cloexec_flag(tempfd);++	/* XXX I wish copyfile() could pack directly to a memory+	 * buffer; that would avoid the temp file here.  For that+	 * matter, it would be nice if fcopyfile() actually worked,+	 * that would reduce the many open/close races here. */+	if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) {+		archive_set_error(&a->archive, errno,+		    "Could not pack extended attributes");+		ret = ARCHIVE_WARN;+		goto cleanup;+	}+	if (fstat(tempfd, &copyfile_stat)) {+		archive_set_error(&a->archive, errno,+		    "Could not check size of extended attributes");+		ret = ARCHIVE_WARN;+		goto cleanup;+	}+	buff = malloc(copyfile_stat.st_size);+	if (buff == NULL) {+		archive_set_error(&a->archive, errno,+		    "Could not allocate memory for extended attributes");+		ret = ARCHIVE_WARN;+		goto cleanup;+	}+	if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) {+		archive_set_error(&a->archive, errno,+		    "Could not read extended attributes into memory");+		ret = ARCHIVE_WARN;+		goto cleanup;+	}+	archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size);++cleanup:+	if (tempfd >= 0) {+		close(tempfd);+		unlink(tempfile.s);+	}+	archive_string_free(&tempfile);+	free(buff);+	return (ret);+}++#else++/*+ * Stub implementation for non-Mac systems.+ */+static int+setup_mac_metadata(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	(void)a; /* UNUSED */+	(void)entry; /* UNUSED */+	(void)fd; /* UNUSED */+	return (ARCHIVE_OK);+}+#endif++#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX++/*+ * Linux, Darwin and AIX extended attribute support.+ *+ * TODO:  By using a stack-allocated buffer for the first+ * call to getxattr(), we might be able to avoid the second+ * call entirely.  We only need the second call if the+ * stack-allocated buffer is too small.  But a modest buffer+ * of 1024 bytes or so will often be big enough.  Same applies+ * to listxattr().+ */+++static int+setup_xattr(struct archive_read_disk *a,+    struct archive_entry *entry, const char *name, int fd, const char *accpath)+{+	ssize_t size;+	void *value = NULL;+++	if (fd >= 0) {+#if ARCHIVE_XATTR_LINUX+		size = fgetxattr(fd, name, NULL, 0);+#elif ARCHIVE_XATTR_DARWIN+		size = fgetxattr(fd, name, NULL, 0, 0, 0);+#elif ARCHIVE_XATTR_AIX+		size = fgetea(fd, name, NULL, 0);+#endif+	} else if (!a->follow_symlinks) {+#if ARCHIVE_XATTR_LINUX+		size = lgetxattr(accpath, name, NULL, 0);+#elif ARCHIVE_XATTR_DARWIN+		size = getxattr(accpath, name, NULL, 0, 0, XATTR_NOFOLLOW);+#elif ARCHIVE_XATTR_AIX+		size = lgetea(accpath, name, NULL, 0);+#endif+	} else {+#if ARCHIVE_XATTR_LINUX+		size = getxattr(accpath, name, NULL, 0);+#elif ARCHIVE_XATTR_DARWIN+		size = getxattr(accpath, name, NULL, 0, 0, 0);+#elif ARCHIVE_XATTR_AIX+		size = getea(accpath, name, NULL, 0);+#endif+	}++	if (size == -1) {+		archive_set_error(&a->archive, errno,+		    "Couldn't query extended attribute");+		return (ARCHIVE_WARN);+	}++	if (size > 0 && (value = malloc(size)) == NULL) {+		archive_set_error(&a->archive, errno, "Out of memory");+		return (ARCHIVE_FATAL);+	}+++	if (fd >= 0) {+#if ARCHIVE_XATTR_LINUX+		size = fgetxattr(fd, name, value, size);+#elif ARCHIVE_XATTR_DARWIN+		size = fgetxattr(fd, name, value, size, 0, 0);+#elif ARCHIVE_XATTR_AIX+		size = fgetea(fd, name, value, size);+#endif+	} else if (!a->follow_symlinks) {+#if ARCHIVE_XATTR_LINUX+		size = lgetxattr(accpath, name, value, size);+#elif ARCHIVE_XATTR_DARWIN+		size = getxattr(accpath, name, value, size, 0, XATTR_NOFOLLOW);+#elif ARCHIVE_XATTR_AIX+		size = lgetea(accpath, name, value, size);+#endif+	} else {+#if ARCHIVE_XATTR_LINUX+		size = getxattr(accpath, name, value, size);+#elif ARCHIVE_XATTR_DARWIN+		size = getxattr(accpath, name, value, size, 0, 0);+#elif ARCHIVE_XATTR_AIX+		size = getea(accpath, name, value, size);+#endif+	}++	if (size == -1) {+		archive_set_error(&a->archive, errno,+		    "Couldn't read extended attribute");+		return (ARCHIVE_WARN);+	}++	archive_entry_xattr_add_entry(entry, name, value, size);++	free(value);+	return (ARCHIVE_OK);+}++static int+setup_xattrs(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	char *list, *p;+	const char *path;+	ssize_t list_size;++	path = NULL;++	if (*fd < 0) {+		path = archive_read_disk_entry_setup_path(a, entry, fd);+		if (path == NULL)+			return (ARCHIVE_WARN);+	}++	if (*fd >= 0) {+#if ARCHIVE_XATTR_LINUX+		list_size = flistxattr(*fd, NULL, 0);+#elif ARCHIVE_XATTR_DARWIN+		list_size = flistxattr(*fd, NULL, 0, 0);+#elif ARCHIVE_XATTR_AIX+		list_size = flistea(*fd, NULL, 0);+#endif+	} else if (!a->follow_symlinks) {+#if ARCHIVE_XATTR_LINUX+		list_size = llistxattr(path, NULL, 0);+#elif ARCHIVE_XATTR_DARWIN+		list_size = listxattr(path, NULL, 0, XATTR_NOFOLLOW);+#elif ARCHIVE_XATTR_AIX+		list_size = llistea(path, NULL, 0);+#endif+	} else {+#if ARCHIVE_XATTR_LINUX+		list_size = listxattr(path, NULL, 0);+#elif ARCHIVE_XATTR_DARWIN+		list_size = listxattr(path, NULL, 0, 0);+#elif ARCHIVE_XATTR_AIX+		list_size = listea(path, NULL, 0);+#endif+	}++	if (list_size == -1) {+		if (errno == ENOTSUP || errno == ENOSYS)+			return (ARCHIVE_OK);+		archive_set_error(&a->archive, errno,+			"Couldn't list extended attributes");+		return (ARCHIVE_WARN);+	}++	if (list_size == 0)+		return (ARCHIVE_OK);++	if ((list = malloc(list_size)) == NULL) {+		archive_set_error(&a->archive, errno, "Out of memory");+		return (ARCHIVE_FATAL);+	}++	if (*fd >= 0) {+#if ARCHIVE_XATTR_LINUX+		list_size = flistxattr(*fd, list, list_size);+#elif ARCHIVE_XATTR_DARWIN+		list_size = flistxattr(*fd, list, list_size, 0);+#elif ARCHIVE_XATTR_AIX+		list_size = flistea(*fd, list, list_size);+#endif+	} else if (!a->follow_symlinks) {+#if ARCHIVE_XATTR_LINUX+		list_size = llistxattr(path, list, list_size);+#elif ARCHIVE_XATTR_DARWIN+		list_size = listxattr(path, list, list_size, XATTR_NOFOLLOW);+#elif ARCHIVE_XATTR_AIX+		list_size = llistea(path, list, list_size);+#endif+	} else {+#if ARCHIVE_XATTR_LINUX+		list_size = listxattr(path, list, list_size);+#elif ARCHIVE_XATTR_DARWIN+		list_size = listxattr(path, list, list_size, 0);+#elif ARCHIVE_XATTR_AIX+		list_size = listea(path, list, list_size);+#endif+	}++	if (list_size == -1) {+		archive_set_error(&a->archive, errno,+			"Couldn't retrieve extended attributes");+		free(list);+		return (ARCHIVE_WARN);+	}++	for (p = list; (p - list) < list_size; p += strlen(p) + 1) {+#if ARCHIVE_XATTR_LINUX+		/* Linux: skip POSIX.1e ACL extended attributes */+		if (strncmp(p, "system.", 7) == 0 &&+		   (strcmp(p + 7, "posix_acl_access") == 0 ||+		    strcmp(p + 7, "posix_acl_default") == 0))+			continue;+		if (strncmp(p, "trusted.SGI_", 12) == 0 &&+		   (strcmp(p + 12, "ACL_DEFAULT") == 0 ||+		    strcmp(p + 12, "ACL_FILE") == 0))+			continue;++		/* Linux: xfsroot namespace is obsolete and unsupported */+		if (strncmp(p, "xfsroot.", 8) == 0)+			continue;+#endif+		setup_xattr(a, entry, p, *fd, path);+	}++	free(list);+	return (ARCHIVE_OK);+}++#elif ARCHIVE_XATTR_FREEBSD++/*+ * FreeBSD extattr interface.+ */++/* TODO: Implement this.  Follow the Linux model above, but+ * with FreeBSD-specific system calls, of course.  Be careful+ * to not include the system extattrs that hold ACLs; we handle+ * those separately.+ */+static int+setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,+    int namespace, const char *name, const char *fullname, int fd,+    const char *path);++static int+setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,+    int namespace, const char *name, const char *fullname, int fd,+    const char *accpath)+{+	ssize_t size;+	void *value = NULL;++	if (fd >= 0)+		size = extattr_get_fd(fd, namespace, name, NULL, 0);+	else if (!a->follow_symlinks)+		size = extattr_get_link(accpath, namespace, name, NULL, 0);+	else+		size = extattr_get_file(accpath, namespace, name, NULL, 0);++	if (size == -1) {+		archive_set_error(&a->archive, errno,+		    "Couldn't query extended attribute");+		return (ARCHIVE_WARN);+	}++	if (size > 0 && (value = malloc(size)) == NULL) {+		archive_set_error(&a->archive, errno, "Out of memory");+		return (ARCHIVE_FATAL);+	}++	if (fd >= 0)+		size = extattr_get_fd(fd, namespace, name, value, size);+	else if (!a->follow_symlinks)+		size = extattr_get_link(accpath, namespace, name, value, size);+	else+		size = extattr_get_file(accpath, namespace, name, value, size);++	if (size == -1) {+		free(value);+		archive_set_error(&a->archive, errno,+		    "Couldn't read extended attribute");+		return (ARCHIVE_WARN);+	}++	archive_entry_xattr_add_entry(entry, fullname, value, size);++	free(value);+	return (ARCHIVE_OK);+}++static int+setup_xattrs(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	char buff[512];+	char *list, *p;+	ssize_t list_size;+	const char *path;+	int namespace = EXTATTR_NAMESPACE_USER;++	path = NULL;++	if (*fd < 0) {+		path = archive_read_disk_entry_setup_path(a, entry, fd);+		if (path == NULL)+			return (ARCHIVE_WARN);+	}++	if (*fd >= 0)+		list_size = extattr_list_fd(*fd, namespace, NULL, 0);+	else if (!a->follow_symlinks)+		list_size = extattr_list_link(path, namespace, NULL, 0);+	else+		list_size = extattr_list_file(path, namespace, NULL, 0);++	if (list_size == -1 && errno == EOPNOTSUPP)+		return (ARCHIVE_OK);+	if (list_size == -1) {+		archive_set_error(&a->archive, errno,+			"Couldn't list extended attributes");+		return (ARCHIVE_WARN);+	}++	if (list_size == 0)+		return (ARCHIVE_OK);++	if ((list = malloc(list_size)) == NULL) {+		archive_set_error(&a->archive, errno, "Out of memory");+		return (ARCHIVE_FATAL);+	}++	if (*fd >= 0)+		list_size = extattr_list_fd(*fd, namespace, list, list_size);+	else if (!a->follow_symlinks)+		list_size = extattr_list_link(path, namespace, list, list_size);+	else+		list_size = extattr_list_file(path, namespace, list, list_size);++	if (list_size == -1) {+		archive_set_error(&a->archive, errno,+			"Couldn't retrieve extended attributes");+		free(list);+		return (ARCHIVE_WARN);+	}++	p = list;+	while ((p - list) < list_size) {+		size_t len = 255 & (int)*p;+		char *name;++		strcpy(buff, "user.");+		name = buff + strlen(buff);+		memcpy(name, p + 1, len);+		name[len] = '\0';+		setup_xattr(a, entry, namespace, name, buff, *fd, path);+		p += 1 + len;+	}++	free(list);+	return (ARCHIVE_OK);+}++#else++/*+ * Generic (stub) extended attribute support.+ */+static int+setup_xattrs(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	(void)a;     /* UNUSED */+	(void)entry; /* UNUSED */+	(void)fd;    /* UNUSED */+	return (ARCHIVE_OK);+}++#endif++#if defined(HAVE_LINUX_FIEMAP_H)++/*+ * Linux FIEMAP sparse interface.+ *+ * The FIEMAP ioctl returns an "extent" for each physical allocation+ * on disk.  We need to process those to generate a more compact list+ * of logical file blocks.  We also need to be very careful to use+ * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes+ * does not report allocations for newly-written data that hasn't+ * been synced to disk.+ *+ * It's important to return a minimal sparse file list because we want+ * to not trigger sparse file extensions if we don't have to, since+ * not all readers support them.+ */++static int+setup_sparse_fiemap(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	char buff[4096];+	struct fiemap *fm;+	struct fiemap_extent *fe;+	int64_t size;+	int count, do_fiemap, iters;+	int exit_sts = ARCHIVE_OK;+	const char *path;++	if (archive_entry_filetype(entry) != AE_IFREG+	    || archive_entry_size(entry) <= 0+	    || archive_entry_hardlink(entry) != NULL)+		return (ARCHIVE_OK);++	if (*fd < 0) {+		path = archive_read_disk_entry_setup_path(a, entry, NULL);+		if (path == NULL)+			return (ARCHIVE_FAILED);++		if (a->tree != NULL)+			*fd = a->open_on_current_dir(a->tree, path,+				O_RDONLY | O_NONBLOCK | O_CLOEXEC);+		else+			*fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);+		if (*fd < 0) {+			archive_set_error(&a->archive, errno,+			    "Can't open `%s'", path);+			return (ARCHIVE_FAILED);+		}+		__archive_ensure_cloexec_flag(*fd);+	}++	/* Initialize buffer to avoid the error valgrind complains about. */+	memset(buff, 0, sizeof(buff));+	count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe);+	fm = (struct fiemap *)buff;+	fm->fm_start = 0;+	fm->fm_length = ~0ULL;;+	fm->fm_flags = FIEMAP_FLAG_SYNC;+	fm->fm_extent_count = count;+	do_fiemap = 1;+	size = archive_entry_size(entry);+	for (iters = 0; ; ++iters) {+		int i, r;++		r = ioctl(*fd, FS_IOC_FIEMAP, fm); +		if (r < 0) {+			/* When something error happens, it is better we+			 * should return ARCHIVE_OK because an earlier+			 * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */+			goto exit_setup_sparse_fiemap;+		}+		if (fm->fm_mapped_extents == 0) {+			if (iters == 0) {+				/* Fully sparse file; insert a zero-length "data" entry */+				archive_entry_sparse_add_entry(entry, 0, 0);+			}+			break;+		}+		fe = fm->fm_extents;+		for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) {+			if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {+				/* The fe_length of the last block does not+				 * adjust itself to its size files. */+				int64_t length = fe->fe_length;+				if (fe->fe_logical + length > (uint64_t)size)+					length -= fe->fe_logical + length - size;+				if (fe->fe_logical == 0 && length == size) {+					/* This is not sparse. */+					do_fiemap = 0;+					break;+				}+				if (length > 0)+					archive_entry_sparse_add_entry(entry,+					    fe->fe_logical, length);+			}+			if (fe->fe_flags & FIEMAP_EXTENT_LAST)+				do_fiemap = 0;+		}+		if (do_fiemap) {+			fe = fm->fm_extents + fm->fm_mapped_extents -1;+			fm->fm_start = fe->fe_logical + fe->fe_length;+		} else+			break;+	}+exit_setup_sparse_fiemap:+	return (exit_sts);+}++#if !defined(SEEK_HOLE) || !defined(SEEK_DATA)+static int+setup_sparse(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	return setup_sparse_fiemap(a, entry, fd);+}+#endif+#endif	/* defined(HAVE_LINUX_FIEMAP_H) */++#if defined(SEEK_HOLE) && defined(SEEK_DATA)++/*+ * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris)+ */++static int+setup_sparse(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	int64_t size;+	off_t initial_off;+	off_t off_s, off_e;+	int exit_sts = ARCHIVE_OK;+	int check_fully_sparse = 0;+	const char *path;++	if (archive_entry_filetype(entry) != AE_IFREG+	    || archive_entry_size(entry) <= 0+	    || archive_entry_hardlink(entry) != NULL)+		return (ARCHIVE_OK);++	/* Does filesystem support the reporting of hole ? */+	if (*fd < 0)+		path = archive_read_disk_entry_setup_path(a, entry, fd);+	else+		path = NULL;++	if (*fd >= 0) {+#ifdef _PC_MIN_HOLE_SIZE+		if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0)+			return (ARCHIVE_OK);+#endif+		initial_off = lseek(*fd, 0, SEEK_CUR);+		if (initial_off != 0)+			lseek(*fd, 0, SEEK_SET);+	} else {+		if (path == NULL)+			return (ARCHIVE_FAILED);+#ifdef _PC_MIN_HOLE_SIZE+		if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)+			return (ARCHIVE_OK);+#endif+		*fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);+		if (*fd < 0) {+			archive_set_error(&a->archive, errno,+			    "Can't open `%s'", path);+			return (ARCHIVE_FAILED);+		}+		__archive_ensure_cloexec_flag(*fd);+		initial_off = 0;+	}++#ifndef _PC_MIN_HOLE_SIZE+	/* Check if the underlying filesystem supports seek hole */+	off_s = lseek(*fd, 0, SEEK_HOLE);+	if (off_s < 0)+#if defined(HAVE_LINUX_FIEMAP_H)+		return setup_sparse_fiemap(a, entry, fd);+#else+		goto exit_setup_sparse;+#endif+	else if (off_s > 0)+		lseek(*fd, 0, SEEK_SET);+#endif++	off_s = 0;+	size = archive_entry_size(entry);+	while (off_s < size) {+		off_s = lseek(*fd, off_s, SEEK_DATA);+		if (off_s == (off_t)-1) {+			if (errno == ENXIO) {+				/* no more hole */+				if (archive_entry_sparse_count(entry) == 0) {+					/* Potentially a fully-sparse file. */+					check_fully_sparse = 1;+				}+				break;+			}+			archive_set_error(&a->archive, errno,+			    "lseek(SEEK_HOLE) failed");+			exit_sts = ARCHIVE_FAILED;+			goto exit_setup_sparse;+		}+		off_e = lseek(*fd, off_s, SEEK_HOLE);+		if (off_e == (off_t)-1) {+			if (errno == ENXIO) {+				off_e = lseek(*fd, 0, SEEK_END);+				if (off_e != (off_t)-1)+					break;/* no more data */+			}+			archive_set_error(&a->archive, errno,+			    "lseek(SEEK_DATA) failed");+			exit_sts = ARCHIVE_FAILED;+			goto exit_setup_sparse;+		}+		if (off_s == 0 && off_e == size)+			break;/* This is not sparse. */+		archive_entry_sparse_add_entry(entry, off_s,+			off_e - off_s);+		off_s = off_e;+	}++	if (check_fully_sparse) {+		if (lseek(*fd, 0, SEEK_HOLE) == 0 &&+			lseek(*fd, 0, SEEK_END) == size) {+			/* Fully sparse file; insert a zero-length "data" entry */+			archive_entry_sparse_add_entry(entry, 0, 0);+		}+	}+exit_setup_sparse:+	lseek(*fd, initial_off, SEEK_SET);+	return (exit_sts);+}++#elif !defined(HAVE_LINUX_FIEMAP_H)++/*+ * Generic (stub) sparse support.+ */+static int+setup_sparse(struct archive_read_disk *a,+    struct archive_entry *entry, int *fd)+{+	(void)a;     /* UNUSED */+	(void)entry; /* UNUSED */+	(void)fd;    /* UNUSED */+	return (ARCHIVE_OK);+}++#endif++#endif /* !defined(_WIN32) || defined(__CYGWIN__) */+
+ c/archive_read_disk_posix.c view
@@ -0,0 +1,2722 @@+/*-+ * Copyright (c) 2003-2009 Tim Kientzle+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer+ *    in this position and unchanged.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++/* This is the tree-walking code for POSIX systems. */+#if !defined(_WIN32) || defined(__CYGWIN__)++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_SYS_PARAM_H+#include <sys/param.h>+#endif+#ifdef HAVE_SYS_MOUNT_H+#include <sys/mount.h>+#endif+#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_SYS_STATFS_H+#include <sys/statfs.h>+#endif+#ifdef HAVE_SYS_STATVFS_H+#include <sys/statvfs.h>+#endif+#ifdef HAVE_SYS_TIME_H+#include <sys/time.h>+#endif+#ifdef HAVE_LINUX_MAGIC_H+#include <linux/magic.h>+#endif+#ifdef HAVE_LINUX_FS_H+#include <linux/fs.h>+#endif+/*+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.+ * As the include guards don't agree, the order of include is important.+ */+#ifdef HAVE_LINUX_EXT2_FS_H+#include <linux/ext2_fs.h>      /* for Linux file flags */+#endif+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)+#include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */+#endif+#ifdef HAVE_DIRECT_H+#include <direct.h>+#endif+#ifdef HAVE_DIRENT_H+#include <dirent.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#ifdef HAVE_SYS_IOCTL_H+#include <sys/ioctl.h>+#endif++#include "archive.h"+#include "archive_string.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_disk_private.h"++#ifndef HAVE_FCHDIR+#error fchdir function required.+#endif+#ifndef O_BINARY+#define O_BINARY	0+#endif+#ifndef O_CLOEXEC+#define O_CLOEXEC	0+#endif++/*-+ * This is a new directory-walking system that addresses a number+ * of problems I've had with fts(3).  In particular, it has no+ * pathname-length limits (other than the size of 'int'), handles+ * deep logical traversals, uses considerably less memory, and has+ * an opaque interface (easier to modify in the future).+ *+ * Internally, it keeps a single list of "tree_entry" items that+ * represent filesystem objects that require further attention.+ * Non-directories are not kept in memory: they are pulled from+ * readdir(), returned to the client, then freed as soon as possible.+ * Any directory entry to be traversed gets pushed onto the stack.+ *+ * There is surprisingly little information that needs to be kept for+ * each item on the stack.  Just the name, depth (represented here as the+ * string length of the parent directory's pathname), and some markers+ * indicating how to get back to the parent (via chdir("..") for a+ * regular dir or via fchdir(2) for a symlink).+ */+/*+ * TODO:+ *    1) Loop checking.+ *    3) Arbitrary logical traversals by closing/reopening intermediate fds.+ */++struct restore_time {+	const char		*name;+	time_t			 mtime;+	long			 mtime_nsec;+	time_t			 atime;+	long			 atime_nsec;+	mode_t			 filetype;+	int			 noatime;+};++struct tree_entry {+	int			 depth;+	struct tree_entry	*next;+	struct tree_entry	*parent;+	struct archive_string	 name;+	size_t			 dirname_length;+	int64_t			 dev;+	int64_t			 ino;+	int			 flags;+	int			 filesystem_id;+	/* How to return back to the parent of a symlink. */+	int			 symlink_parent_fd;+	/* How to restore time of a directory. */+	struct restore_time	 restore_time;+};++struct filesystem {+	int64_t		dev;+	int		synthetic;+	int		remote;+	int		noatime;+#if defined(USE_READDIR_R)+	size_t		name_max;+#endif+	long		incr_xfer_size;+	long		max_xfer_size;+	long		min_xfer_size;+	long		xfer_align;++	/*+	 * Buffer used for reading file contents.+	 */+	/* Exactly allocated memory pointer. */+	unsigned char	*allocation_ptr;+	/* Pointer adjusted to the filesystem alignment . */+	unsigned char	*buff;+	size_t		 buff_size;+};++/* Definitions for tree_entry.flags bitmap. */+#define	isDir		1  /* This entry is a regular directory. */+#define	isDirLink	2  /* This entry is a symbolic link to a directory. */+#define	needsFirstVisit	4  /* This is an initial entry. */+#define	needsDescent	8  /* This entry needs to be previsited. */+#define	needsOpen	16 /* This is a directory that needs to be opened. */+#define	needsAscent	32 /* This entry needs to be postvisited. */++/*+ * Local data for this package.+ */+struct tree {+	struct tree_entry	*stack;+	struct tree_entry	*current;+	DIR			*d;+#define	INVALID_DIR_HANDLE NULL+	struct dirent		*de;+#if defined(USE_READDIR_R)+	struct dirent		*dirent;+	size_t			 dirent_allocated;+#endif+	int			 flags;+	int			 visit_type;+	/* Error code from last failed operation. */+	int			 tree_errno;++	/* Dynamically-sized buffer for holding path */+	struct archive_string	 path;++	/* Last path element */+	const char		*basename;+	/* Leading dir length */+	size_t			 dirname_length;++	int			 depth;+	int			 openCount;+	int			 maxOpenCount;+	int			 initial_dir_fd;+	int			 working_dir_fd;++	struct stat		 lst;+	struct stat		 st;+	int			 descend;+	int			 nlink;+	/* How to restore time of a file. */+	struct restore_time	 restore_time;++	struct entry_sparse {+		int64_t		 length;+		int64_t		 offset;+	}			*sparse_list, *current_sparse;+	int			 sparse_count;+	int			 sparse_list_size;++	char			 initial_symlink_mode;+	char			 symlink_mode;+	struct filesystem	*current_filesystem;+	struct filesystem	*filesystem_table;+	int			 initial_filesystem_id;+	int			 current_filesystem_id;+	int			 max_filesystem_id;+	int			 allocated_filesystem;++	int			 entry_fd;+	int			 entry_eof;+	int64_t			 entry_remaining_bytes;+	int64_t			 entry_total;+	unsigned char		*entry_buff;+	size_t			 entry_buff_size;+};++/* Definitions for tree.flags bitmap. */+#define	hasStat		16 /* The st entry is valid. */+#define	hasLstat	32 /* The lst entry is valid. */+#define	onWorkingDir	64 /* We are on the working dir where we are+			    * reading directory entry at this time. */+#define	needsRestoreTimes 128+#define	onInitialDir	256 /* We are on the initial dir. */++static int+tree_dir_next_posix(struct tree *t);++#ifdef HAVE_DIRENT_D_NAMLEN+/* BSD extension; avoids need for a strlen() call. */+#define	D_NAMELEN(dp)	(dp)->d_namlen+#else+#define	D_NAMELEN(dp)	(strlen((dp)->d_name))+#endif++/* Initiate/terminate a tree traversal. */+static struct tree *tree_open(const char *, int, int);+static struct tree *tree_reopen(struct tree *, const char *, int);+static void tree_close(struct tree *);+static void tree_free(struct tree *);+static void tree_push(struct tree *, const char *, int, int64_t, int64_t,+		struct restore_time *);+static int tree_enter_initial_dir(struct tree *);+static int tree_enter_working_dir(struct tree *);+static int tree_current_dir_fd(struct tree *);++/*+ * tree_next() returns Zero if there is no next entry, non-zero if+ * there is.  Note that directories are visited three times.+ * Directories are always visited first as part of enumerating their+ * parent; that is a "regular" visit.  If tree_descend() is invoked at+ * that time, the directory is added to a work list and will+ * subsequently be visited two more times: once just after descending+ * into the directory ("postdescent") and again just after ascending+ * back to the parent ("postascent").+ *+ * TREE_ERROR_DIR is returned if the descent failed (because the+ * directory couldn't be opened, for instance).  This is returned+ * instead of TREE_POSTDESCENT/TREE_POSTASCENT.  TREE_ERROR_DIR is not a+ * fatal error, but it does imply that the relevant subtree won't be+ * visited.  TREE_ERROR_FATAL is returned for an error that left the+ * traversal completely hosed.  Right now, this is only returned for+ * chdir() failures during ascent.+ */+#define	TREE_REGULAR		1+#define	TREE_POSTDESCENT	2+#define	TREE_POSTASCENT		3+#define	TREE_ERROR_DIR		-1+#define	TREE_ERROR_FATAL	-2++static int tree_next(struct tree *);++/*+ * Return information about the current entry.+ */++/*+ * The current full pathname, length of the full pathname, and a name+ * that can be used to access the file.  Because tree does use chdir+ * extensively, the access path is almost never the same as the full+ * current path.+ *+ * TODO: On platforms that support it, use openat()-style operations+ * to eliminate the chdir() operations entirely while still supporting+ * arbitrarily deep traversals.  This makes access_path troublesome to+ * support, of course, which means we'll need a rich enough interface+ * that clients can function without it.  (In particular, we'll need+ * tree_current_open() that returns an open file descriptor.)+ *+ */+static const char *tree_current_path(struct tree *);+static const char *tree_current_access_path(struct tree *);++/*+ * Request the lstat() or stat() data for the current path.  Since the+ * tree package needs to do some of this anyway, and caches the+ * results, you should take advantage of it here if you need it rather+ * than make a redundant stat() or lstat() call of your own.+ */+static const struct stat *tree_current_stat(struct tree *);+static const struct stat *tree_current_lstat(struct tree *);+static int	tree_current_is_symblic_link_target(struct tree *);++/* The following functions use tricks to avoid a certain number of+ * stat()/lstat() calls. */+/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */+static int tree_current_is_physical_dir(struct tree *);+/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */+static int tree_current_is_dir(struct tree *);+static int update_current_filesystem(struct archive_read_disk *a,+		    int64_t dev);+static int setup_current_filesystem(struct archive_read_disk *);+static int tree_target_is_same_as_parent(struct tree *, const struct stat *);++static int	_archive_read_disk_open(struct archive *, const char *);+static int	_archive_read_free(struct archive *);+static int	_archive_read_close(struct archive *);+static int	_archive_read_data_block(struct archive *,+		    const void **, size_t *, int64_t *);+static int	_archive_read_next_header(struct archive *,+		    struct archive_entry **);+static int	_archive_read_next_header2(struct archive *,+		    struct archive_entry *);+static const char *trivial_lookup_gname(void *, int64_t gid);+static const char *trivial_lookup_uname(void *, int64_t uid);+static int	setup_sparse(struct archive_read_disk *, struct archive_entry *);+static int	close_and_restore_time(int fd, struct tree *,+		    struct restore_time *);+static int	open_on_current_dir(struct tree *, const char *, int);+static int	tree_dup(int);+++static struct archive_vtable *+archive_read_disk_vtable(void)+{+	static struct archive_vtable av;+	static int inited = 0;++	if (!inited) {+		av.archive_free = _archive_read_free;+		av.archive_close = _archive_read_close;+		av.archive_read_data_block = _archive_read_data_block;+		av.archive_read_next_header = _archive_read_next_header;+		av.archive_read_next_header2 = _archive_read_next_header2;+		inited = 1;+	}+	return (&av);+}++const char *+archive_read_disk_gname(struct archive *_a, la_int64_t gid)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+		ARCHIVE_STATE_ANY, "archive_read_disk_gname"))+		return (NULL);+	if (a->lookup_gname == NULL)+		return (NULL);+	return ((*a->lookup_gname)(a->lookup_gname_data, gid));+}++const char *+archive_read_disk_uname(struct archive *_a, la_int64_t uid)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+		ARCHIVE_STATE_ANY, "archive_read_disk_uname"))+		return (NULL);+	if (a->lookup_uname == NULL)+		return (NULL);+	return ((*a->lookup_uname)(a->lookup_uname_data, uid));+}++int+archive_read_disk_set_gname_lookup(struct archive *_a,+    void *private_data,+    const char * (*lookup_gname)(void *private, la_int64_t gid),+    void (*cleanup_gname)(void *private))+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup");++	if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)+		(a->cleanup_gname)(a->lookup_gname_data);++	a->lookup_gname = lookup_gname;+	a->cleanup_gname = cleanup_gname;+	a->lookup_gname_data = private_data;+	return (ARCHIVE_OK);+}++int+archive_read_disk_set_uname_lookup(struct archive *_a,+    void *private_data,+    const char * (*lookup_uname)(void *private, la_int64_t uid),+    void (*cleanup_uname)(void *private))+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup");++	if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)+		(a->cleanup_uname)(a->lookup_uname_data);++	a->lookup_uname = lookup_uname;+	a->cleanup_uname = cleanup_uname;+	a->lookup_uname_data = private_data;+	return (ARCHIVE_OK);+}++/*+ * Create a new archive_read_disk object and initialize it with global state.+ */+struct archive *+archive_read_disk_new(void)+{+	struct archive_read_disk *a;++	a = (struct archive_read_disk *)calloc(1, sizeof(*a));+	if (a == NULL)+		return (NULL);+	a->archive.magic = ARCHIVE_READ_DISK_MAGIC;+	a->archive.state = ARCHIVE_STATE_NEW;+	a->archive.vtable = archive_read_disk_vtable();+	a->entry = archive_entry_new2(&a->archive);+	a->lookup_uname = trivial_lookup_uname;+	a->lookup_gname = trivial_lookup_gname;+	a->flags = ARCHIVE_READDISK_MAC_COPYFILE;+	a->open_on_current_dir = open_on_current_dir;+	a->tree_current_dir_fd = tree_current_dir_fd;+	a->tree_enter_working_dir = tree_enter_working_dir;+	return (&a->archive);+}++static int+_archive_read_free(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	int r;++	if (_a == NULL)+		return (ARCHIVE_OK);+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");++	if (a->archive.state != ARCHIVE_STATE_CLOSED)+		r = _archive_read_close(&a->archive);+	else+		r = ARCHIVE_OK;++	tree_free(a->tree);+	if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)+		(a->cleanup_gname)(a->lookup_gname_data);+	if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)+		(a->cleanup_uname)(a->lookup_uname_data);+	archive_string_free(&a->archive.error_string);+	archive_entry_free(a->entry);+	a->archive.magic = 0;+	__archive_clean(&a->archive);+	free(a);+	return (r);+}++static int+_archive_read_close(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");++	if (a->archive.state != ARCHIVE_STATE_FATAL)+		a->archive.state = ARCHIVE_STATE_CLOSED;++	tree_close(a->tree);++	return (ARCHIVE_OK);+}++static void+setup_symlink_mode(struct archive_read_disk *a, char symlink_mode,+    int follow_symlinks)+{+	a->symlink_mode = symlink_mode;+	a->follow_symlinks = follow_symlinks;+	if (a->tree != NULL) {+		a->tree->initial_symlink_mode = a->symlink_mode;+		a->tree->symlink_mode = a->symlink_mode;+	}+}++int+archive_read_disk_set_symlink_logical(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical");+	setup_symlink_mode(a, 'L', 1);+	return (ARCHIVE_OK);+}++int+archive_read_disk_set_symlink_physical(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical");+	setup_symlink_mode(a, 'P', 0);+	return (ARCHIVE_OK);+}++int+archive_read_disk_set_symlink_hybrid(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid");+	setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */+	return (ARCHIVE_OK);+}++int+archive_read_disk_set_atime_restored(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime");+#ifdef HAVE_UTIMES+	a->flags |= ARCHIVE_READDISK_RESTORE_ATIME;+	if (a->tree != NULL)+		a->tree->flags |= needsRestoreTimes;+	return (ARCHIVE_OK);+#else+	/* Display warning and unset flag */+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+	    "Cannot restore access time on this system");+	a->flags &= ~ARCHIVE_READDISK_RESTORE_ATIME;+	return (ARCHIVE_WARN);+#endif+}++int+archive_read_disk_set_behavior(struct archive *_a, int flags)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	int r = ARCHIVE_OK;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");++	a->flags = flags;++	if (flags & ARCHIVE_READDISK_RESTORE_ATIME)+		r = archive_read_disk_set_atime_restored(_a);+	else {+		if (a->tree != NULL)+			a->tree->flags &= ~needsRestoreTimes;+	}+	return (r);+}++/*+ * Trivial implementations of gname/uname lookup functions.+ * These are normally overridden by the client, but these stub+ * versions ensure that we always have something that works.+ */+static const char *+trivial_lookup_gname(void *private_data, int64_t gid)+{+	(void)private_data; /* UNUSED */+	(void)gid; /* UNUSED */+	return (NULL);+}++static const char *+trivial_lookup_uname(void *private_data, int64_t uid)+{+	(void)private_data; /* UNUSED */+	(void)uid; /* UNUSED */+	return (NULL);+}++/*+ * Allocate memory for the reading buffer adjusted to the filesystem+ * alignment.+ */+static int+setup_suitable_read_buffer(struct archive_read_disk *a)+{+	struct tree *t = a->tree;+	struct filesystem *cf = t->current_filesystem;+	size_t asize;+	size_t s;++	if (cf->allocation_ptr == NULL) {+		/* If we couldn't get a filesystem alignment,+		 * we use 4096 as default value but we won't use+		 * O_DIRECT to open() and openat() operations. */+		long xfer_align = (cf->xfer_align == -1)?4096:cf->xfer_align;++		if (cf->max_xfer_size != -1)+			asize = cf->max_xfer_size + xfer_align;+		else {+			long incr = cf->incr_xfer_size;+			/* Some platform does not set a proper value to+			 * incr_xfer_size.*/+			if (incr < 0)+				incr = cf->min_xfer_size;+			if (cf->min_xfer_size < 0) {+				incr = xfer_align;+				asize = xfer_align;+			} else+				asize = cf->min_xfer_size;++			/* Increase a buffer size up to 64K bytes in+			 * a proper increment size. */+			while (asize < 1024*64)+				asize += incr;+			/* Take a margin to adjust to the filesystem+			 * alignment. */+			asize += xfer_align;+		}+		cf->allocation_ptr = malloc(asize);+		if (cf->allocation_ptr == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Couldn't allocate memory");+			a->archive.state = ARCHIVE_STATE_FATAL;+			return (ARCHIVE_FATAL);+		}++		/*+		 * Calculate proper address for the filesystem.+		 */+		s = (uintptr_t)cf->allocation_ptr;+		s %= xfer_align;+		if (s > 0)+			s = xfer_align - s;++		/*+		 * Set a read buffer pointer in the proper alignment of+		 * the current filesystem.+		 */+		cf->buff = cf->allocation_ptr + s;+		cf->buff_size = asize - xfer_align;+	}+	return (ARCHIVE_OK);+}++static int+_archive_read_data_block(struct archive *_a, const void **buff,+    size_t *size, int64_t *offset)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	struct tree *t = a->tree;+	int r;+	ssize_t bytes;+	int64_t sparse_bytes;+	size_t buffbytes;+	int empty_sparse_region = 0;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_read_data_block");++	if (t->entry_eof || t->entry_remaining_bytes <= 0) {+		r = ARCHIVE_EOF;+		goto abort_read_data;+	}++	/*+	 * Open the current file.+	 */+	if (t->entry_fd < 0) {+		int flags = O_RDONLY | O_BINARY | O_CLOEXEC;++		/*+		 * Eliminate or reduce cache effects if we can.+		 *+		 * Carefully consider this to be enabled.+		 */+#if defined(O_DIRECT) && 0/* Disabled for now */+		if (t->current_filesystem->xfer_align != -1 &&+		    t->nlink == 1)+			flags |= O_DIRECT;+#endif+#if defined(O_NOATIME)+		/*+		 * Linux has O_NOATIME flag; use it if we need.+		 */+		if ((t->flags & needsRestoreTimes) != 0 &&+		    t->restore_time.noatime == 0)+			flags |= O_NOATIME;+#endif+		t->entry_fd = open_on_current_dir(t,+		    tree_current_access_path(t), flags);+		__archive_ensure_cloexec_flag(t->entry_fd);+#if defined(O_NOATIME)+		/*+		 * When we did open the file with O_NOATIME flag,+		 * if successful, set 1 to t->restore_time.noatime+		 * not to restore an atime of the file later.+		 * if failed by EPERM, retry it without O_NOATIME flag.+		 */+		if (flags & O_NOATIME) {+			if (t->entry_fd >= 0)+				t->restore_time.noatime = 1;+			else if (errno == EPERM)+				flags &= ~O_NOATIME;+		}+#endif+		if (t->entry_fd < 0) {+			archive_set_error(&a->archive, errno,+			    "Couldn't open %s", tree_current_path(t));+			r = ARCHIVE_FAILED;+			tree_enter_initial_dir(t);+			goto abort_read_data;+		}+		tree_enter_initial_dir(t);+	}++	/*+	 * Allocate read buffer if not allocated.+	 */+	if (t->current_filesystem->allocation_ptr == NULL) {+		r = setup_suitable_read_buffer(a);+		if (r != ARCHIVE_OK) {+			a->archive.state = ARCHIVE_STATE_FATAL;+			goto abort_read_data;+		}+	}+	t->entry_buff = t->current_filesystem->buff;+	t->entry_buff_size = t->current_filesystem->buff_size;++	buffbytes = t->entry_buff_size;+	if ((int64_t)buffbytes > t->current_sparse->length)+		buffbytes = t->current_sparse->length;++	if (t->current_sparse->length == 0)+		empty_sparse_region = 1;++	/*+	 * Skip hole.+	 * TODO: Should we consider t->current_filesystem->xfer_align?+	 */+	if (t->current_sparse->offset > t->entry_total) {+		if (lseek(t->entry_fd,+		    (off_t)t->current_sparse->offset, SEEK_SET) < 0) {+			archive_set_error(&a->archive, errno, "Seek error");+			r = ARCHIVE_FATAL;+			a->archive.state = ARCHIVE_STATE_FATAL;+			goto abort_read_data;+		}+		sparse_bytes = t->current_sparse->offset - t->entry_total;+		t->entry_remaining_bytes -= sparse_bytes;+		t->entry_total += sparse_bytes;+	}++	/*+	 * Read file contents.+	 */+	if (buffbytes > 0) {+		bytes = read(t->entry_fd, t->entry_buff, buffbytes);+		if (bytes < 0) {+			archive_set_error(&a->archive, errno, "Read error");+			r = ARCHIVE_FATAL;+			a->archive.state = ARCHIVE_STATE_FATAL;+			goto abort_read_data;+		}+	} else+		bytes = 0;+	/*+	 * Return an EOF unless we've read a leading empty sparse region, which+	 * is used to represent fully-sparse files.+	*/+	if (bytes == 0 && !empty_sparse_region) {+		/* Get EOF */+		t->entry_eof = 1;+		r = ARCHIVE_EOF;+		goto abort_read_data;+	}+	*buff = t->entry_buff;+	*size = bytes;+	*offset = t->entry_total;+	t->entry_total += bytes;+	t->entry_remaining_bytes -= bytes;+	if (t->entry_remaining_bytes == 0) {+		/* Close the current file descriptor */+		close_and_restore_time(t->entry_fd, t, &t->restore_time);+		t->entry_fd = -1;+		t->entry_eof = 1;+	}+	t->current_sparse->offset += bytes;+	t->current_sparse->length -= bytes;+	if (t->current_sparse->length == 0 && !t->entry_eof)+		t->current_sparse++;+	return (ARCHIVE_OK);++abort_read_data:+	*buff = NULL;+	*size = 0;+	*offset = t->entry_total;+	if (t->entry_fd >= 0) {+		/* Close the current file descriptor */+		close_and_restore_time(t->entry_fd, t, &t->restore_time);+		t->entry_fd = -1;+	}+	return (r);+}++static int+next_entry(struct archive_read_disk *a, struct tree *t,+    struct archive_entry *entry)+{+	const struct stat *st; /* info to use for this entry */+	const struct stat *lst;/* lstat() information */+	const char *name;+	int delayed, delayed_errno, descend, r;+	struct archive_string delayed_str;++	delayed = ARCHIVE_OK;+	delayed_errno = 0;+	archive_string_init(&delayed_str);++	st = NULL;+	lst = NULL;+	t->descend = 0;+	do {+		switch (tree_next(t)) {+		case TREE_ERROR_FATAL:+			archive_set_error(&a->archive, t->tree_errno,+			    "%s: Unable to continue traversing directory tree",+			    tree_current_path(t));+			a->archive.state = ARCHIVE_STATE_FATAL;+			tree_enter_initial_dir(t);+			return (ARCHIVE_FATAL);+		case TREE_ERROR_DIR:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "%s: Couldn't visit directory",+			    tree_current_path(t));+			tree_enter_initial_dir(t);+			return (ARCHIVE_FAILED);+		case 0:+			tree_enter_initial_dir(t);+			return (ARCHIVE_EOF);+		case TREE_POSTDESCENT:+		case TREE_POSTASCENT:+			break;+		case TREE_REGULAR:+			lst = tree_current_lstat(t);+			if (lst == NULL) {+			    if (errno == ENOENT && t->depth > 0) {+				delayed = ARCHIVE_WARN;+				delayed_errno = errno;+				if (delayed_str.length == 0) {+					archive_string_sprintf(&delayed_str,+					    "%s", tree_current_path(t));+				} else {+					archive_string_sprintf(&delayed_str,+					    " %s", tree_current_path(t));+				}+			    } else {+				archive_set_error(&a->archive, errno,+				    "%s: Cannot stat",+				    tree_current_path(t));+				tree_enter_initial_dir(t);+				return (ARCHIVE_FAILED);+			    }+			}+			break;+		}+	} while (lst == NULL);++#ifdef __APPLE__+	if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {+		/* If we're using copyfile(), ignore "._XXX" files. */+		const char *bname = strrchr(tree_current_path(t), '/');+		if (bname == NULL)+			bname = tree_current_path(t);+		else+			++bname;+		if (bname[0] == '.' && bname[1] == '_')+			return (ARCHIVE_RETRY);+	}+#endif++	archive_entry_copy_pathname(entry, tree_current_path(t));+	/*+	 * Perform path matching.+	 */+	if (a->matching) {+		r = archive_match_path_excluded(a->matching, entry);+		if (r < 0) {+			archive_set_error(&(a->archive), errno,+			    "Failed : %s", archive_error_string(a->matching));+			return (r);+		}+		if (r) {+			if (a->excluded_cb_func)+				a->excluded_cb_func(&(a->archive),+				    a->excluded_cb_data, entry);+			return (ARCHIVE_RETRY);+		}+	}++	/*+	 * Distinguish 'L'/'P'/'H' symlink following.+	 */+	switch(t->symlink_mode) {+	case 'H':+		/* 'H': After the first item, rest like 'P'. */+		t->symlink_mode = 'P';+		/* 'H': First item (from command line) like 'L'. */+		/* FALLTHROUGH */+	case 'L':+		/* 'L': Do descend through a symlink to dir. */+		descend = tree_current_is_dir(t);+		/* 'L': Follow symlinks to files. */+		a->symlink_mode = 'L';+		a->follow_symlinks = 1;+		/* 'L': Archive symlinks as targets, if we can. */+		st = tree_current_stat(t);+		if (st != NULL && !tree_target_is_same_as_parent(t, st))+			break;+		/* If stat fails, we have a broken symlink;+		 * in that case, don't follow the link. */+		/* FALLTHROUGH */+	default:+		/* 'P': Don't descend through a symlink to dir. */+		descend = tree_current_is_physical_dir(t);+		/* 'P': Don't follow symlinks to files. */+		a->symlink_mode = 'P';+		a->follow_symlinks = 0;+		/* 'P': Archive symlinks as symlinks. */+		st = lst;+		break;+	}++	if (update_current_filesystem(a, st->st_dev) != ARCHIVE_OK) {+		a->archive.state = ARCHIVE_STATE_FATAL;+		tree_enter_initial_dir(t);+		return (ARCHIVE_FATAL);+	}+	if (t->initial_filesystem_id == -1)+		t->initial_filesystem_id = t->current_filesystem_id;+	if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) {+		if (t->initial_filesystem_id != t->current_filesystem_id)+			descend = 0;+	}+	t->descend = descend;++	/*+	 * Honor nodump flag.+	 * If the file is marked with nodump flag, do not return this entry.+	 */+	if (a->flags & ARCHIVE_READDISK_HONOR_NODUMP) {+#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)+		if (st->st_flags & UF_NODUMP)+			return (ARCHIVE_RETRY);+#elif (defined(FS_IOC_GETFLAGS) && defined(FS_NODUMP_FL) && \+       defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \+      (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) && \+       defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))+		if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) {+			int stflags;++			t->entry_fd = open_on_current_dir(t,+			    tree_current_access_path(t),+			    O_RDONLY | O_NONBLOCK | O_CLOEXEC);+			__archive_ensure_cloexec_flag(t->entry_fd);+			if (t->entry_fd >= 0) {+				r = ioctl(t->entry_fd,+#ifdef FS_IOC_GETFLAGS+				FS_IOC_GETFLAGS,+#else+				EXT2_IOC_GETFLAGS,+#endif+					&stflags);+#ifdef FS_NODUMP_FL+				if (r == 0 && (stflags & FS_NODUMP_FL) != 0)+#else+				if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0)+#endif+					return (ARCHIVE_RETRY);+			}+		}+#endif+	}++	archive_entry_copy_stat(entry, st);++	/* Save the times to be restored. This must be in before+	 * calling archive_read_disk_descend() or any chance of it,+	 * especially, invoking a callback. */+	t->restore_time.mtime = archive_entry_mtime(entry);+	t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry);+	t->restore_time.atime = archive_entry_atime(entry);+	t->restore_time.atime_nsec = archive_entry_atime_nsec(entry);+	t->restore_time.filetype = archive_entry_filetype(entry);+	t->restore_time.noatime = t->current_filesystem->noatime;++	/*+	 * Perform time matching.+	 */+	if (a->matching) {+		r = archive_match_time_excluded(a->matching, entry);+		if (r < 0) {+			archive_set_error(&(a->archive), errno,+			    "Failed : %s", archive_error_string(a->matching));+			return (r);+		}+		if (r) {+			if (a->excluded_cb_func)+				a->excluded_cb_func(&(a->archive),+				    a->excluded_cb_data, entry);+			return (ARCHIVE_RETRY);+		}+	}++	/* Lookup uname/gname */+	name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry));+	if (name != NULL)+		archive_entry_copy_uname(entry, name);+	name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry));+	if (name != NULL)+		archive_entry_copy_gname(entry, name);++	/*+	 * Perform owner matching.+	 */+	if (a->matching) {+		r = archive_match_owner_excluded(a->matching, entry);+		if (r < 0) {+			archive_set_error(&(a->archive), errno,+			    "Failed : %s", archive_error_string(a->matching));+			return (r);+		}+		if (r) {+			if (a->excluded_cb_func)+				a->excluded_cb_func(&(a->archive),+				    a->excluded_cb_data, entry);+			return (ARCHIVE_RETRY);+		}+	}++	/*+	 * Invoke a meta data filter callback.+	 */+	if (a->metadata_filter_func) {+		if (!a->metadata_filter_func(&(a->archive),+		    a->metadata_filter_data, entry))+			return (ARCHIVE_RETRY);+	}++	/*+	 * Populate the archive_entry with metadata from the disk.+	 */+	archive_entry_copy_sourcepath(entry, tree_current_access_path(t));+	r = archive_read_disk_entry_from_file(&(a->archive), entry,+		t->entry_fd, st);++	if (r == ARCHIVE_OK) {+		r = delayed;+		if (r != ARCHIVE_OK) {+			archive_string_sprintf(&delayed_str, ": %s",+			    "File removed before we read it");+			archive_set_error(&(a->archive), delayed_errno,+			    "%s", delayed_str.s);+		}+	}+	archive_string_free(&delayed_str);++	return (r);+}++static int+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)+{+	int ret;+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	*entryp = NULL;+	ret = _archive_read_next_header2(_a, a->entry);+	*entryp = a->entry;+	return ret;+}++static int+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	struct tree *t;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_read_next_header2");++	t = a->tree;+	if (t->entry_fd >= 0) {+		close_and_restore_time(t->entry_fd, t, &t->restore_time);+		t->entry_fd = -1;+	}++	archive_entry_clear(entry);++	for (;;) {+		r = next_entry(a, t, entry);+		if (t->entry_fd >= 0) {+			close(t->entry_fd);+			t->entry_fd = -1;+		}++		if (r == ARCHIVE_RETRY) {+			archive_entry_clear(entry);+			continue;+		}+		break;+	}++	/* Return to the initial directory. */+	tree_enter_initial_dir(t);++	/*+	 * EOF and FATAL are persistent at this layer.  By+	 * modifying the state, we guarantee that future calls to+	 * read a header or read data will fail.+	 */+	switch (r) {+	case ARCHIVE_EOF:+		a->archive.state = ARCHIVE_STATE_EOF;+		break;+	case ARCHIVE_OK:+	case ARCHIVE_WARN:+		/* Overwrite the sourcepath based on the initial directory. */+		archive_entry_copy_sourcepath(entry, tree_current_path(t));+		t->entry_total = 0;+		if (archive_entry_filetype(entry) == AE_IFREG) {+			t->nlink = archive_entry_nlink(entry);+			t->entry_remaining_bytes = archive_entry_size(entry);+			t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0;+			if (!t->entry_eof &&+			    setup_sparse(a, entry) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		} else {+			t->entry_remaining_bytes = 0;+			t->entry_eof = 1;+		}+		a->archive.state = ARCHIVE_STATE_DATA;+		break;+	case ARCHIVE_RETRY:+		break;+	case ARCHIVE_FATAL:+		a->archive.state = ARCHIVE_STATE_FATAL;+		break;+	}++	__archive_reset_read_data(&a->archive);+	return (r);+}++static int+setup_sparse(struct archive_read_disk *a, struct archive_entry *entry)+{+	struct tree *t = a->tree;+	int64_t length, offset;+	int i;++	t->sparse_count = archive_entry_sparse_reset(entry);+	if (t->sparse_count+1 > t->sparse_list_size) {+		free(t->sparse_list);+		t->sparse_list_size = t->sparse_count + 1;+		t->sparse_list = malloc(sizeof(t->sparse_list[0]) *+		    t->sparse_list_size);+		if (t->sparse_list == NULL) {+			t->sparse_list_size = 0;+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate data");+			a->archive.state = ARCHIVE_STATE_FATAL;+			return (ARCHIVE_FATAL);+		}+	}+	for (i = 0; i < t->sparse_count; i++) {+		archive_entry_sparse_next(entry, &offset, &length);+		t->sparse_list[i].offset = offset;+		t->sparse_list[i].length = length;+	}+	if (i == 0) {+		t->sparse_list[i].offset = 0;+		t->sparse_list[i].length = archive_entry_size(entry);+	} else {+		t->sparse_list[i].offset = archive_entry_size(entry);+		t->sparse_list[i].length = 0;+	}+	t->current_sparse = t->sparse_list;++	return (ARCHIVE_OK);+}++int+archive_read_disk_set_matching(struct archive *_a, struct archive *_ma,+    void (*_excluded_func)(struct archive *, void *, struct archive_entry *),+    void *_client_data)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_matching");+	a->matching = _ma;+	a->excluded_cb_func = _excluded_func;+	a->excluded_cb_data = _client_data;+	return (ARCHIVE_OK);+}++int+archive_read_disk_set_metadata_filter_callback(struct archive *_a,+    int (*_metadata_filter_func)(struct archive *, void *,+    struct archive_entry *), void *_client_data)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,+	    "archive_read_disk_set_metadata_filter_callback");++	a->metadata_filter_func = _metadata_filter_func;+	a->metadata_filter_data = _client_data;+	return (ARCHIVE_OK);+}++int+archive_read_disk_can_descend(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	struct tree *t = a->tree;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_read_disk_can_descend");++	return (t->visit_type == TREE_REGULAR && t->descend);+}++/*+ * Called by the client to mark the directory just returned from+ * tree_next() as needing to be visited.+ */+int+archive_read_disk_descend(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	struct tree *t = a->tree;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_read_disk_descend");++	if (t->visit_type != TREE_REGULAR || !t->descend)+		return (ARCHIVE_OK);++	/*+	 * We must not treat the initial specified path as a physical dir,+	 * because if we do then we will try and ascend out of it by opening+	 * ".." which is (a) wrong and (b) causes spurious permissions errors+	 * if ".." is not readable by us. Instead, treat it as if it were a+	 * symlink. (This uses an extra fd, but it can only happen once at the+	 * top level of a traverse.) But we can't necessarily assume t->st is+	 * valid here (though t->lst is), which complicates the logic a+	 * little.+	 */+	if (tree_current_is_physical_dir(t)) {+		tree_push(t, t->basename, t->current_filesystem_id,+		    t->lst.st_dev, t->lst.st_ino, &t->restore_time);+		if (t->stack->parent->parent != NULL)+			t->stack->flags |= isDir;+		else+			t->stack->flags |= isDirLink;+	} else if (tree_current_is_dir(t)) {+		tree_push(t, t->basename, t->current_filesystem_id,+		    t->st.st_dev, t->st.st_ino, &t->restore_time);+		t->stack->flags |= isDirLink;+	}+	t->descend = 0;+	return (ARCHIVE_OK);+}++int+archive_read_disk_open(struct archive *_a, const char *pathname)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,+	    "archive_read_disk_open");+	archive_clear_error(&a->archive);++	return (_archive_read_disk_open(_a, pathname));+}++int+archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	struct archive_string path;+	int ret;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,+	    "archive_read_disk_open_w");+	archive_clear_error(&a->archive);++	/* Make a char string from a wchar_t string. */+	archive_string_init(&path);+	if (archive_string_append_from_wcs(&path, pathname,+	    wcslen(pathname)) != 0) {+		if (errno == ENOMEM)+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory");+		else+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Can't convert a path to a char string");+		a->archive.state = ARCHIVE_STATE_FATAL;+		ret = ARCHIVE_FATAL;+	} else+		ret = _archive_read_disk_open(_a, path.s);++	archive_string_free(&path);+	return (ret);+}++static int+_archive_read_disk_open(struct archive *_a, const char *pathname)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	if (a->tree != NULL)+		a->tree = tree_reopen(a->tree, pathname,+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);+	else+		a->tree = tree_open(pathname, a->symlink_mode,+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);+	if (a->tree == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate tar data");+		a->archive.state = ARCHIVE_STATE_FATAL;+		return (ARCHIVE_FATAL);+	}+	a->archive.state = ARCHIVE_STATE_HEADER;++	return (ARCHIVE_OK);+}++/*+ * Return a current filesystem ID which is index of the filesystem entry+ * you've visited through archive_read_disk.+ */+int+archive_read_disk_current_filesystem(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_read_disk_current_filesystem");++	return (a->tree->current_filesystem_id);+}++static int+update_current_filesystem(struct archive_read_disk *a, int64_t dev)+{+	struct tree *t = a->tree;+	int i, fid;++	if (t->current_filesystem != NULL &&+	    t->current_filesystem->dev == dev)+		return (ARCHIVE_OK);++	for (i = 0; i < t->max_filesystem_id; i++) {+		if (t->filesystem_table[i].dev == dev) {+			/* There is the filesystem ID we've already generated. */+			t->current_filesystem_id = i;+			t->current_filesystem = &(t->filesystem_table[i]);+			return (ARCHIVE_OK);+		}+	}++	/*+	 * This is the new filesystem which we have to generate a new ID for.+	 */+	fid = t->max_filesystem_id++;+	if (t->max_filesystem_id > t->allocated_filesystem) {+		size_t s;+		void *p;++		s = t->max_filesystem_id * 2;+		p = realloc(t->filesystem_table,+		        s * sizeof(*t->filesystem_table));+		if (p == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate tar data");+			return (ARCHIVE_FATAL);+		}+		t->filesystem_table = (struct filesystem *)p;+		t->allocated_filesystem = s;+	}+	t->current_filesystem_id = fid;+	t->current_filesystem = &(t->filesystem_table[fid]);+	t->current_filesystem->dev = dev;+	t->current_filesystem->allocation_ptr = NULL;+	t->current_filesystem->buff = NULL;++	/* Setup the current filesystem properties which depend on+	 * platform specific. */+	return (setup_current_filesystem(a));+}++/*+ * Returns 1 if current filesystem is generated filesystem, 0 if it is not+ * or -1 if it is unknown.+ */+int+archive_read_disk_current_filesystem_is_synthetic(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_read_disk_current_filesystem");++	return (a->tree->current_filesystem->synthetic);+}++/*+ * Returns 1 if current filesystem is remote filesystem, 0 if it is not+ * or -1 if it is unknown.+ */+int+archive_read_disk_current_filesystem_is_remote(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_read_disk_current_filesystem");++	return (a->tree->current_filesystem->remote);+}++#if defined(_PC_REC_INCR_XFER_SIZE) && defined(_PC_REC_MAX_XFER_SIZE) &&\+	defined(_PC_REC_MIN_XFER_SIZE) && defined(_PC_REC_XFER_ALIGN)+static int+get_xfer_size(struct tree *t, int fd, const char *path)+{+	t->current_filesystem->xfer_align = -1;+	errno = 0;+	if (fd >= 0) {+		t->current_filesystem->incr_xfer_size =+		    fpathconf(fd, _PC_REC_INCR_XFER_SIZE);+		t->current_filesystem->max_xfer_size =+		    fpathconf(fd, _PC_REC_MAX_XFER_SIZE);+		t->current_filesystem->min_xfer_size =+		    fpathconf(fd, _PC_REC_MIN_XFER_SIZE);+		t->current_filesystem->xfer_align =+		    fpathconf(fd, _PC_REC_XFER_ALIGN);+	} else if (path != NULL) {+		t->current_filesystem->incr_xfer_size =+		    pathconf(path, _PC_REC_INCR_XFER_SIZE);+		t->current_filesystem->max_xfer_size =+		    pathconf(path, _PC_REC_MAX_XFER_SIZE);+		t->current_filesystem->min_xfer_size =+		    pathconf(path, _PC_REC_MIN_XFER_SIZE);+		t->current_filesystem->xfer_align =+		    pathconf(path, _PC_REC_XFER_ALIGN);+	}+	/* At least we need an alignment size. */+	if (t->current_filesystem->xfer_align == -1)+		return ((errno == EINVAL)?1:-1);+	else+		return (0);+}+#else+static int+get_xfer_size(struct tree *t, int fd, const char *path)+{+	(void)t; /* UNUSED */+	(void)fd; /* UNUSED */+	(void)path; /* UNUSED */+	return (1);/* Not supported */+}+#endif++#if defined(HAVE_STATFS) && defined(HAVE_FSTATFS) && defined(MNT_LOCAL) \+	&& !defined(ST_LOCAL)++/*+ * Gather current filesystem properties on FreeBSD, OpenBSD and Mac OS X.+ */+static int+setup_current_filesystem(struct archive_read_disk *a)+{+	struct tree *t = a->tree;+	struct statfs sfs;+#if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC)+/* TODO: configure should set GETVFSBYNAME_ARG_TYPE to make+ * this accurate; some platforms have both and we need the one that's+ * used by getvfsbyname()+ *+ * Then the following would become:+ *  #if defined(GETVFSBYNAME_ARG_TYPE)+ *   GETVFSBYNAME_ARG_TYPE vfc;+ *  #endif+ */+#  if defined(HAVE_STRUCT_XVFSCONF)+	struct xvfsconf vfc;+#  else+	struct vfsconf vfc;+#  endif+#endif+	int r, xr = 0;+#if !defined(HAVE_STRUCT_STATFS_F_NAMEMAX)+	long nm;+#endif++	t->current_filesystem->synthetic = -1;+	t->current_filesystem->remote = -1;+	if (tree_current_is_symblic_link_target(t)) {+#if defined(HAVE_OPENAT)+		/*+		 * Get file system statistics on any directory+		 * where current is.+		 */+		int fd = openat(tree_current_dir_fd(t),+		    tree_current_access_path(t), O_RDONLY | O_CLOEXEC);+		__archive_ensure_cloexec_flag(fd);+		if (fd < 0) {+			archive_set_error(&a->archive, errno,+			    "openat failed");+			return (ARCHIVE_FAILED);+		}+		r = fstatfs(fd, &sfs);+		if (r == 0)+			xr = get_xfer_size(t, fd, NULL);+		close(fd);+#else+		if (tree_enter_working_dir(t) != 0) {+			archive_set_error(&a->archive, errno, "fchdir failed");+			return (ARCHIVE_FAILED);+		}+		r = statfs(tree_current_access_path(t), &sfs);+		if (r == 0)+			xr = get_xfer_size(t, -1, tree_current_access_path(t));+#endif+	} else {+		r = fstatfs(tree_current_dir_fd(t), &sfs);+		if (r == 0)+			xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);+	}+	if (r == -1 || xr == -1) {+		archive_set_error(&a->archive, errno, "statfs failed");+		return (ARCHIVE_FAILED);+	} else if (xr == 1) {+		/* pathconf(_PC_REX_*) operations are not supported. */+		t->current_filesystem->xfer_align = sfs.f_bsize;+		t->current_filesystem->max_xfer_size = -1;+		t->current_filesystem->min_xfer_size = sfs.f_iosize;+		t->current_filesystem->incr_xfer_size = sfs.f_iosize;+	}+	if (sfs.f_flags & MNT_LOCAL)+		t->current_filesystem->remote = 0;+	else+		t->current_filesystem->remote = 1;++#if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC)+	r = getvfsbyname(sfs.f_fstypename, &vfc);+	if (r == -1) {+		archive_set_error(&a->archive, errno, "getvfsbyname failed");+		return (ARCHIVE_FAILED);+	}+	if (vfc.vfc_flags & VFCF_SYNTHETIC)+		t->current_filesystem->synthetic = 1;+	else+		t->current_filesystem->synthetic = 0;+#endif++#if defined(MNT_NOATIME)+	if (sfs.f_flags & MNT_NOATIME)+		t->current_filesystem->noatime = 1;+	else+#endif+		t->current_filesystem->noatime = 0;++#if defined(USE_READDIR_R)+	/* Set maximum filename length. */+#if defined(HAVE_STRUCT_STATFS_F_NAMEMAX)+	t->current_filesystem->name_max = sfs.f_namemax;+#else+# if defined(_PC_NAME_MAX)+	/* Mac OS X does not have f_namemax in struct statfs. */+	if (tree_current_is_symblic_link_target(t)) {+		if (tree_enter_working_dir(t) != 0) {+			archive_set_error(&a->archive, errno, "fchdir failed");+			return (ARCHIVE_FAILED);+		}+		nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX);+	} else+		nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX);+# else+	nm = -1;+# endif+	if (nm == -1)+		t->current_filesystem->name_max = NAME_MAX;+	else+		t->current_filesystem->name_max = nm;+#endif+#endif /* USE_READDIR_R */+	return (ARCHIVE_OK);+}++#elif (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) && defined(ST_LOCAL)++/*+ * Gather current filesystem properties on NetBSD+ */+static int+setup_current_filesystem(struct archive_read_disk *a)+{+	struct tree *t = a->tree;+	struct statvfs svfs;+	int r, xr = 0;++	t->current_filesystem->synthetic = -1;+	if (tree_enter_working_dir(t) != 0) {+		archive_set_error(&a->archive, errno, "fchdir failed");+		return (ARCHIVE_FAILED);+	}+	if (tree_current_is_symblic_link_target(t)) {+		r = statvfs(tree_current_access_path(t), &svfs);+		if (r == 0)+			xr = get_xfer_size(t, -1, tree_current_access_path(t));+	} else {+#ifdef HAVE_FSTATVFS+		r = fstatvfs(tree_current_dir_fd(t), &svfs);+		if (r == 0)+			xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);+#else+		r = statvfs(".", &svfs);+		if (r == 0)+			xr = get_xfer_size(t, -1, ".");+#endif+	}+	if (r == -1 || xr == -1) {+		t->current_filesystem->remote = -1;+		archive_set_error(&a->archive, errno, "statvfs failed");+		return (ARCHIVE_FAILED);+	} else if (xr == 1) {+		/* Usually come here unless NetBSD supports _PC_REC_XFER_ALIGN+		 * for pathconf() function. */+		t->current_filesystem->xfer_align = svfs.f_frsize;+		t->current_filesystem->max_xfer_size = -1;+#if defined(HAVE_STRUCT_STATVFS_F_IOSIZE)+		t->current_filesystem->min_xfer_size = svfs.f_iosize;+		t->current_filesystem->incr_xfer_size = svfs.f_iosize;+#else+		t->current_filesystem->min_xfer_size = svfs.f_bsize;+		t->current_filesystem->incr_xfer_size = svfs.f_bsize;+#endif+	}+	if (svfs.f_flag & ST_LOCAL)+		t->current_filesystem->remote = 0;+	else+		t->current_filesystem->remote = 1;++#if defined(ST_NOATIME)+	if (svfs.f_flag & ST_NOATIME)+		t->current_filesystem->noatime = 1;+	else+#endif+		t->current_filesystem->noatime = 0;++	/* Set maximum filename length. */+	t->current_filesystem->name_max = svfs.f_namemax;+	return (ARCHIVE_OK);+}++#elif defined(HAVE_SYS_STATFS_H) && defined(HAVE_LINUX_MAGIC_H) &&\+	defined(HAVE_STATFS) && defined(HAVE_FSTATFS)+/*+ * Note: statfs is deprecated since LSB 3.2+ */++#ifndef CIFS_SUPER_MAGIC+#define CIFS_SUPER_MAGIC 0xFF534D42+#endif+#ifndef DEVFS_SUPER_MAGIC+#define DEVFS_SUPER_MAGIC 0x1373+#endif++/*+ * Gather current filesystem properties on Linux+ */+static int+setup_current_filesystem(struct archive_read_disk *a)+{+	struct tree *t = a->tree;+	struct statfs sfs;+#if defined(HAVE_STATVFS)+	struct statvfs svfs;+#endif+	int r, vr = 0, xr = 0;++	if (tree_current_is_symblic_link_target(t)) {+#if defined(HAVE_OPENAT)+		/*+		 * Get file system statistics on any directory+		 * where current is.+		 */+		int fd = openat(tree_current_dir_fd(t),+		    tree_current_access_path(t), O_RDONLY | O_CLOEXEC);+		__archive_ensure_cloexec_flag(fd);+		if (fd < 0) {+			archive_set_error(&a->archive, errno,+			    "openat failed");+			return (ARCHIVE_FAILED);+		}+#if defined(HAVE_FSTATVFS)+		vr = fstatvfs(fd, &svfs);/* for f_flag, mount flags */+#endif+		r = fstatfs(fd, &sfs);+		if (r == 0)+			xr = get_xfer_size(t, fd, NULL);+		close(fd);+#else+		if (tree_enter_working_dir(t) != 0) {+			archive_set_error(&a->archive, errno, "fchdir failed");+			return (ARCHIVE_FAILED);+		}+#if defined(HAVE_STATVFS)+		vr = statvfs(tree_current_access_path(t), &svfs);+#endif+		r = statfs(tree_current_access_path(t), &sfs);+		if (r == 0)+			xr = get_xfer_size(t, -1, tree_current_access_path(t));+#endif+	} else {+#ifdef HAVE_FSTATFS+#if defined(HAVE_FSTATVFS)+		vr = fstatvfs(tree_current_dir_fd(t), &svfs);+#endif+		r = fstatfs(tree_current_dir_fd(t), &sfs);+		if (r == 0)+			xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);+#else+		if (tree_enter_working_dir(t) != 0) {+			archive_set_error(&a->archive, errno, "fchdir failed");+			return (ARCHIVE_FAILED);+		}+#if defined(HAVE_STATVFS)+		vr = statvfs(".", &svfs);+#endif+		r = statfs(".", &sfs);+		if (r == 0)+			xr = get_xfer_size(t, -1, ".");+#endif+	}+	if (r == -1 || xr == -1 || vr == -1) {+		t->current_filesystem->synthetic = -1;+		t->current_filesystem->remote = -1;+		archive_set_error(&a->archive, errno, "statfs failed");+		return (ARCHIVE_FAILED);+	} else if (xr == 1) {+		/* pathconf(_PC_REX_*) operations are not supported. */+#if defined(HAVE_STATVFS)+		t->current_filesystem->xfer_align = svfs.f_frsize;+		t->current_filesystem->max_xfer_size = -1;+		t->current_filesystem->min_xfer_size = svfs.f_bsize;+		t->current_filesystem->incr_xfer_size = svfs.f_bsize;+#else+		t->current_filesystem->xfer_align = sfs.f_frsize;+		t->current_filesystem->max_xfer_size = -1;+		t->current_filesystem->min_xfer_size = sfs.f_bsize;+		t->current_filesystem->incr_xfer_size = sfs.f_bsize;+#endif+	}+	switch (sfs.f_type) {+	case AFS_SUPER_MAGIC:+	case CIFS_SUPER_MAGIC:+	case CODA_SUPER_MAGIC:+	case NCP_SUPER_MAGIC:/* NetWare */+	case NFS_SUPER_MAGIC:+	case SMB_SUPER_MAGIC:+		t->current_filesystem->remote = 1;+		t->current_filesystem->synthetic = 0;+		break;+	case DEVFS_SUPER_MAGIC:+	case PROC_SUPER_MAGIC:+	case USBDEVICE_SUPER_MAGIC:+		t->current_filesystem->remote = 0;+		t->current_filesystem->synthetic = 1;+		break;+	default:+		t->current_filesystem->remote = 0;+		t->current_filesystem->synthetic = 0;+		break;+	}++#if defined(ST_NOATIME)+#if defined(HAVE_STATVFS)+	if (svfs.f_flag & ST_NOATIME)+#else+	if (sfs.f_flags & ST_NOATIME)+#endif+		t->current_filesystem->noatime = 1;+	else+#endif+		t->current_filesystem->noatime = 0;++#if defined(USE_READDIR_R)+	/* Set maximum filename length. */+	t->current_filesystem->name_max = sfs.f_namelen;+#endif+	return (ARCHIVE_OK);+}++#elif defined(HAVE_SYS_STATVFS_H) &&\+	(defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS))++/*+ * Gather current filesystem properties on other posix platform.+ */+static int+setup_current_filesystem(struct archive_read_disk *a)+{+	struct tree *t = a->tree;+	struct statvfs svfs;+	int r, xr = 0;++	t->current_filesystem->synthetic = -1;/* Not supported */+	t->current_filesystem->remote = -1;/* Not supported */+	if (tree_current_is_symblic_link_target(t)) {+#if defined(HAVE_OPENAT)+		/*+		 * Get file system statistics on any directory+		 * where current is.+		 */+		int fd = openat(tree_current_dir_fd(t),+		    tree_current_access_path(t), O_RDONLY | O_CLOEXEC);+		__archive_ensure_cloexec_flag(fd);+		if (fd < 0) {+			archive_set_error(&a->archive, errno,+			    "openat failed");+			return (ARCHIVE_FAILED);+		}+		r = fstatvfs(fd, &svfs);+		if (r == 0)+			xr = get_xfer_size(t, fd, NULL);+		close(fd);+#else+		if (tree_enter_working_dir(t) != 0) {+			archive_set_error(&a->archive, errno, "fchdir failed");+			return (ARCHIVE_FAILED);+		}+		r = statvfs(tree_current_access_path(t), &svfs);+		if (r == 0)+			xr = get_xfer_size(t, -1, tree_current_access_path(t));+#endif+	} else {+#ifdef HAVE_FSTATVFS+		r = fstatvfs(tree_current_dir_fd(t), &svfs);+		if (r == 0)+			xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);+#else+		if (tree_enter_working_dir(t) != 0) {+			archive_set_error(&a->archive, errno, "fchdir failed");+			return (ARCHIVE_FAILED);+		}+		r = statvfs(".", &svfs);+		if (r == 0)+			xr = get_xfer_size(t, -1, ".");+#endif+	}+	if (r == -1 || xr == -1) {+		t->current_filesystem->synthetic = -1;+		t->current_filesystem->remote = -1;+		archive_set_error(&a->archive, errno, "statvfs failed");+		return (ARCHIVE_FAILED);+	} else if (xr == 1) {+		/* pathconf(_PC_REX_*) operations are not supported. */+		t->current_filesystem->xfer_align = svfs.f_frsize;+		t->current_filesystem->max_xfer_size = -1;+		t->current_filesystem->min_xfer_size = svfs.f_bsize;+		t->current_filesystem->incr_xfer_size = svfs.f_bsize;+	}++#if defined(ST_NOATIME)+	if (svfs.f_flag & ST_NOATIME)+		t->current_filesystem->noatime = 1;+	else+#endif+		t->current_filesystem->noatime = 0;++#if defined(USE_READDIR_R)+	/* Set maximum filename length. */+	t->current_filesystem->name_max = svfs.f_namemax;+#endif+	return (ARCHIVE_OK);+}++#else++/*+ * Generic: Gather current filesystem properties.+ * TODO: Is this generic function really needed?+ */+static int+setup_current_filesystem(struct archive_read_disk *a)+{+	struct tree *t = a->tree;+#if defined(_PC_NAME_MAX) && defined(USE_READDIR_R)+	long nm;+#endif+	t->current_filesystem->synthetic = -1;/* Not supported */+	t->current_filesystem->remote = -1;/* Not supported */+	t->current_filesystem->noatime = 0;+	(void)get_xfer_size(t, -1, ".");/* Dummy call to avoid build error. */+	t->current_filesystem->xfer_align = -1;/* Unknown */+	t->current_filesystem->max_xfer_size = -1;+	t->current_filesystem->min_xfer_size = -1;+	t->current_filesystem->incr_xfer_size = -1;++#if defined(USE_READDIR_R)+	/* Set maximum filename length. */+#  if defined(_PC_NAME_MAX)+	if (tree_current_is_symblic_link_target(t)) {+		if (tree_enter_working_dir(t) != 0) {+			archive_set_error(&a->archive, errno, "fchdir failed");+			return (ARCHIVE_FAILED);+		}+		nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX);+	} else+		nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX);+	if (nm == -1)+#  endif /* _PC_NAME_MAX */+		/*+		 * Some systems (HP-UX or others?) incorrectly defined+		 * NAME_MAX macro to be a smaller value.+		 */+#  if defined(NAME_MAX) && NAME_MAX >= 255+		t->current_filesystem->name_max = NAME_MAX;+#  else+		/* No way to get a trusted value of maximum filename+		 * length. */+		t->current_filesystem->name_max = PATH_MAX;+#  endif /* NAME_MAX */+#  if defined(_PC_NAME_MAX)+	else+		t->current_filesystem->name_max = nm;+#  endif /* _PC_NAME_MAX */+#endif /* USE_READDIR_R */+	return (ARCHIVE_OK);+}++#endif++static int+close_and_restore_time(int fd, struct tree *t, struct restore_time *rt)+{+#ifndef HAVE_UTIMES+	(void)t; /* UNUSED */+	(void)rt; /* UNUSED */+	return (close(fd));+#else+#if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__)+	struct timespec timespecs[2];+#endif+	struct timeval times[2];++	if ((t->flags & needsRestoreTimes) == 0 || rt->noatime) {+		if (fd >= 0)+			return (close(fd));+		else+			return (0);+	}++#if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__)+	timespecs[1].tv_sec = rt->mtime;+	timespecs[1].tv_nsec = rt->mtime_nsec;++	timespecs[0].tv_sec = rt->atime;+	timespecs[0].tv_nsec = rt->atime_nsec;+	/* futimens() is defined in POSIX.1-2008. */+	if (futimens(fd, timespecs) == 0)+		return (close(fd));+#endif++	times[1].tv_sec = rt->mtime;+	times[1].tv_usec = rt->mtime_nsec / 1000;++	times[0].tv_sec = rt->atime;+	times[0].tv_usec = rt->atime_nsec / 1000;++#if !defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES) && !defined(__CYGWIN__)+	if (futimes(fd, times) == 0)+		return (close(fd));+#endif+	close(fd);+#if defined(HAVE_FUTIMESAT)+	if (futimesat(tree_current_dir_fd(t), rt->name, times) == 0)+		return (0);+#endif+#ifdef HAVE_LUTIMES+	if (lutimes(rt->name, times) != 0)+#else+	if (AE_IFLNK != rt->filetype && utimes(rt->name, times) != 0)+#endif+		return (-1);+#endif+	return (0);+}++static int+open_on_current_dir(struct tree *t, const char *path, int flags)+{+#ifdef HAVE_OPENAT+	return (openat(tree_current_dir_fd(t), path, flags));+#else+	if (tree_enter_working_dir(t) != 0)+		return (-1);+	return (open(path, flags));+#endif+}++static int+tree_dup(int fd)+{+	int new_fd;+#ifdef F_DUPFD_CLOEXEC+	static volatile int can_dupfd_cloexec = 1;++	if (can_dupfd_cloexec) {+		new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);+		if (new_fd != -1)+			return (new_fd);+		/* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC,+		 * but it cannot be used. So we have to try dup(). */+		/* We won't try F_DUPFD_CLOEXEC. */+		can_dupfd_cloexec = 0;+	}+#endif /* F_DUPFD_CLOEXEC */+	new_fd = dup(fd);+	__archive_ensure_cloexec_flag(new_fd);+	return (new_fd);+}++/*+ * Add a directory path to the current stack.+ */+static void+tree_push(struct tree *t, const char *path, int filesystem_id,+    int64_t dev, int64_t ino, struct restore_time *rt)+{+	struct tree_entry *te;++	te = calloc(1, sizeof(*te));+	te->next = t->stack;+	te->parent = t->current;+	if (te->parent)+		te->depth = te->parent->depth + 1;+	t->stack = te;+	archive_string_init(&te->name);+	te->symlink_parent_fd = -1;+	archive_strcpy(&te->name, path);+	te->flags = needsDescent | needsOpen | needsAscent;+	te->filesystem_id = filesystem_id;+	te->dev = dev;+	te->ino = ino;+	te->dirname_length = t->dirname_length;+	te->restore_time.name = te->name.s;+	if (rt != NULL) {+		te->restore_time.mtime = rt->mtime;+		te->restore_time.mtime_nsec = rt->mtime_nsec;+		te->restore_time.atime = rt->atime;+		te->restore_time.atime_nsec = rt->atime_nsec;+		te->restore_time.filetype = rt->filetype;+		te->restore_time.noatime = rt->noatime;+	}+}++/*+ * Append a name to the current dir path.+ */+static void+tree_append(struct tree *t, const char *name, size_t name_length)+{+	size_t size_needed;++	t->path.s[t->dirname_length] = '\0';+	t->path.length = t->dirname_length;+	/* Strip trailing '/' from name, unless entire name is "/". */+	while (name_length > 1 && name[name_length - 1] == '/')+		name_length--;++	/* Resize pathname buffer as needed. */+	size_needed = name_length + t->dirname_length + 2;+	archive_string_ensure(&t->path, size_needed);+	/* Add a separating '/' if it's needed. */+	if (t->dirname_length > 0 && t->path.s[archive_strlen(&t->path)-1] != '/')+		archive_strappend_char(&t->path, '/');+	t->basename = t->path.s + archive_strlen(&t->path);+	archive_strncat(&t->path, name, name_length);+	t->restore_time.name = t->basename;+}++/*+ * Open a directory tree for traversal.+ */+static struct tree *+tree_open(const char *path, int symlink_mode, int restore_time)+{+	struct tree *t;++	if ((t = calloc(1, sizeof(*t))) == NULL)+		return (NULL);+	archive_string_init(&t->path);+	archive_string_ensure(&t->path, 31);+	t->initial_symlink_mode = symlink_mode;+	return (tree_reopen(t, path, restore_time));+}++static struct tree *+tree_reopen(struct tree *t, const char *path, int restore_time)+{+#if defined(O_PATH)+	/* Linux */+	const int o_flag = O_PATH;+#elif defined(O_SEARCH)+	/* SunOS */+	const int o_flag = O_SEARCH;+#elif defined(__FreeBSD__) && defined(O_EXEC)+	/* FreeBSD */+	const int o_flag = O_EXEC;+#endif++	t->flags = (restore_time != 0)?needsRestoreTimes:0;+	t->flags |= onInitialDir;+	t->visit_type = 0;+	t->tree_errno = 0;+	t->dirname_length = 0;+	t->depth = 0;+	t->descend = 0;+	t->current = NULL;+	t->d = INVALID_DIR_HANDLE;+	t->symlink_mode = t->initial_symlink_mode;+	archive_string_empty(&t->path);+	t->entry_fd = -1;+	t->entry_eof = 0;+	t->entry_remaining_bytes = 0;+	t->initial_filesystem_id = -1;++	/* First item is set up a lot like a symlink traversal. */+	tree_push(t, path, 0, 0, 0, NULL);+	t->stack->flags = needsFirstVisit;+	t->maxOpenCount = t->openCount = 1;+	t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC);+#if defined(O_PATH) || defined(O_SEARCH) || \+ (defined(__FreeBSD__) && defined(O_EXEC))+	/*+	 * Most likely reason to fail opening "." is that it's not readable,+	 * so try again for execute. The consequences of not opening this are+	 * unhelpful and unnecessary errors later.+	 */+	if (t->initial_dir_fd < 0)+		t->initial_dir_fd = open(".", o_flag | O_CLOEXEC);+#endif+	__archive_ensure_cloexec_flag(t->initial_dir_fd);+	t->working_dir_fd = tree_dup(t->initial_dir_fd);+	return (t);+}++static int+tree_descent(struct tree *t)+{+	int flag, new_fd, r = 0;++	t->dirname_length = archive_strlen(&t->path);+	flag = O_RDONLY | O_CLOEXEC;+#if defined(O_DIRECTORY)+	flag |= O_DIRECTORY;+#endif+	new_fd = open_on_current_dir(t, t->stack->name.s, flag);+	__archive_ensure_cloexec_flag(new_fd);+	if (new_fd < 0) {+		t->tree_errno = errno;+		r = TREE_ERROR_DIR;+	} else {+		t->depth++;+		/* If it is a link, set up fd for the ascent. */+		if (t->stack->flags & isDirLink) {+			t->stack->symlink_parent_fd = t->working_dir_fd;+			t->openCount++;+			if (t->openCount > t->maxOpenCount)+				t->maxOpenCount = t->openCount;+		} else+			close(t->working_dir_fd);+		/* Renew the current working directory. */+		t->working_dir_fd = new_fd;+		t->flags &= ~onWorkingDir;+	}+	return (r);+}++/*+ * We've finished a directory; ascend back to the parent.+ */+static int+tree_ascend(struct tree *t)+{+	struct tree_entry *te;+	int new_fd, r = 0, prev_dir_fd;++	te = t->stack;+	prev_dir_fd = t->working_dir_fd;+	if (te->flags & isDirLink)+		new_fd = te->symlink_parent_fd;+	else {+		new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC);+		__archive_ensure_cloexec_flag(new_fd);+	}+	if (new_fd < 0) {+		t->tree_errno = errno;+		r = TREE_ERROR_FATAL;+	} else {+		/* Renew the current working directory. */+		t->working_dir_fd = new_fd;+		t->flags &= ~onWorkingDir;+		/* Current directory has been changed, we should+		 * close an fd of previous working directory. */+		close_and_restore_time(prev_dir_fd, t, &te->restore_time);+		if (te->flags & isDirLink) {+			t->openCount--;+			te->symlink_parent_fd = -1;+		}+		t->depth--;+	}+	return (r);+}++/*+ * Return to the initial directory where tree_open() was performed.+ */+static int+tree_enter_initial_dir(struct tree *t)+{+	int r = 0;++	if ((t->flags & onInitialDir) == 0) {+		r = fchdir(t->initial_dir_fd);+		if (r == 0) {+			t->flags &= ~onWorkingDir;+			t->flags |= onInitialDir;+		}+	}+	return (r);+}++/*+ * Restore working directory of directory traversals.+ */+static int+tree_enter_working_dir(struct tree *t)+{+	int r = 0;++	/*+	 * Change the current directory if really needed.+	 * Sometimes this is unneeded when we did not do+	 * descent.+	 */+	if (t->depth > 0 && (t->flags & onWorkingDir) == 0) {+		r = fchdir(t->working_dir_fd);+		if (r == 0) {+			t->flags &= ~onInitialDir;+			t->flags |= onWorkingDir;+		}+	}+	return (r);+}++static int+tree_current_dir_fd(struct tree *t)+{+	return (t->working_dir_fd);+}++/*+ * Pop the working stack.+ */+static void+tree_pop(struct tree *t)+{+	struct tree_entry *te;++	t->path.s[t->dirname_length] = '\0';+	t->path.length = t->dirname_length;+	if (t->stack == t->current && t->current != NULL)+		t->current = t->current->parent;+	te = t->stack;+	t->stack = te->next;+	t->dirname_length = te->dirname_length;+	t->basename = t->path.s + t->dirname_length;+	while (t->basename[0] == '/')+		t->basename++;+	archive_string_free(&te->name);+	free(te);+}++/*+ * Get the next item in the tree traversal.+ */+static int+tree_next(struct tree *t)+{+	int r;++	while (t->stack != NULL) {+		/* If there's an open dir, get the next entry from there. */+		if (t->d != INVALID_DIR_HANDLE) {+			r = tree_dir_next_posix(t);+			if (r == 0)+				continue;+			return (r);+		}++		if (t->stack->flags & needsFirstVisit) {+			/* Top stack item needs a regular visit. */+			t->current = t->stack;+			tree_append(t, t->stack->name.s,+			    archive_strlen(&(t->stack->name)));+			/* t->dirname_length = t->path_length; */+			/* tree_pop(t); */+			t->stack->flags &= ~needsFirstVisit;+			return (t->visit_type = TREE_REGULAR);+		} else if (t->stack->flags & needsDescent) {+			/* Top stack item is dir to descend into. */+			t->current = t->stack;+			tree_append(t, t->stack->name.s,+			    archive_strlen(&(t->stack->name)));+			t->stack->flags &= ~needsDescent;+			r = tree_descent(t);+			if (r != 0) {+				tree_pop(t);+				t->visit_type = r;+			} else+				t->visit_type = TREE_POSTDESCENT;+			return (t->visit_type);+		} else if (t->stack->flags & needsOpen) {+			t->stack->flags &= ~needsOpen;+			r = tree_dir_next_posix(t);+			if (r == 0)+				continue;+			return (r);+		} else if (t->stack->flags & needsAscent) {+		        /* Top stack item is dir and we're done with it. */+			r = tree_ascend(t);+			tree_pop(t);+			t->visit_type = r != 0 ? r : TREE_POSTASCENT;+			return (t->visit_type);+		} else {+			/* Top item on stack is dead. */+			tree_pop(t);+			t->flags &= ~hasLstat;+			t->flags &= ~hasStat;+		}+	}+	return (t->visit_type = 0);+}++static int+tree_dir_next_posix(struct tree *t)+{+	int r;+	const char *name;+	size_t namelen;++	if (t->d == NULL) {+#if defined(USE_READDIR_R)+		size_t dirent_size;+#endif++#if defined(HAVE_FDOPENDIR)+		t->d = fdopendir(tree_dup(t->working_dir_fd));+#else /* HAVE_FDOPENDIR */+		if (tree_enter_working_dir(t) == 0) {+			t->d = opendir(".");+#if HAVE_DIRFD || defined(dirfd)+			__archive_ensure_cloexec_flag(dirfd(t->d));+#endif+		}+#endif /* HAVE_FDOPENDIR */+		if (t->d == NULL) {+			r = tree_ascend(t); /* Undo "chdir" */+			tree_pop(t);+			t->tree_errno = errno;+			t->visit_type = r != 0 ? r : TREE_ERROR_DIR;+			return (t->visit_type);+		}+#if defined(USE_READDIR_R)+		dirent_size = offsetof(struct dirent, d_name) ++		  t->filesystem_table[t->current->filesystem_id].name_max + 1;+		if (t->dirent == NULL || t->dirent_allocated < dirent_size) {+			free(t->dirent);+			t->dirent = malloc(dirent_size);+			if (t->dirent == NULL) {+				closedir(t->d);+				t->d = INVALID_DIR_HANDLE;+				(void)tree_ascend(t);+				tree_pop(t);+				t->tree_errno = ENOMEM;+				t->visit_type = TREE_ERROR_DIR;+				return (t->visit_type);+			}+			t->dirent_allocated = dirent_size;+		}+#endif /* USE_READDIR_R */+	}+	for (;;) {+		errno = 0;+#if defined(USE_READDIR_R)+		r = readdir_r(t->d, t->dirent, &t->de);+#ifdef _AIX+		/* Note: According to the man page, return value 9 indicates+		 * that the readdir_r was not successful and the error code+		 * is set to the global errno variable. And then if the end+		 * of directory entries was reached, the return value is 9+		 * and the third parameter is set to NULL and errno is+		 * unchanged. */+		if (r == 9)+			r = errno;+#endif /* _AIX */+		if (r != 0 || t->de == NULL) {+#else+		t->de = readdir(t->d);+		if (t->de == NULL) {+			r = errno;+#endif+			closedir(t->d);+			t->d = INVALID_DIR_HANDLE;+			if (r != 0) {+				t->tree_errno = r;+				t->visit_type = TREE_ERROR_DIR;+				return (t->visit_type);+			} else+				return (0);+		}+		name = t->de->d_name;+		namelen = D_NAMELEN(t->de);+		t->flags &= ~hasLstat;+		t->flags &= ~hasStat;+		if (name[0] == '.' && name[1] == '\0')+			continue;+		if (name[0] == '.' && name[1] == '.' && name[2] == '\0')+			continue;+		tree_append(t, name, namelen);+		return (t->visit_type = TREE_REGULAR);+	}+}+++/*+ * Get the stat() data for the entry just returned from tree_next().+ */+static const struct stat *+tree_current_stat(struct tree *t)+{+	if (!(t->flags & hasStat)) {+#ifdef HAVE_FSTATAT+		if (fstatat(tree_current_dir_fd(t),+		    tree_current_access_path(t), &t->st, 0) != 0)+#else+		if (tree_enter_working_dir(t) != 0)+			return NULL;+		if (la_stat(tree_current_access_path(t), &t->st) != 0)+#endif+			return NULL;+		t->flags |= hasStat;+	}+	return (&t->st);+}++/*+ * Get the lstat() data for the entry just returned from tree_next().+ */+static const struct stat *+tree_current_lstat(struct tree *t)+{+	if (!(t->flags & hasLstat)) {+#ifdef HAVE_FSTATAT+		if (fstatat(tree_current_dir_fd(t),+		    tree_current_access_path(t), &t->lst,+		    AT_SYMLINK_NOFOLLOW) != 0)+#else+		if (tree_enter_working_dir(t) != 0)+			return NULL;+		if (lstat(tree_current_access_path(t), &t->lst) != 0)+#endif+			return NULL;+		t->flags |= hasLstat;+	}+	return (&t->lst);+}++/*+ * Test whether current entry is a dir or link to a dir.+ */+static int+tree_current_is_dir(struct tree *t)+{+	const struct stat *st;+	/*+	 * If we already have lstat() info, then try some+	 * cheap tests to determine if this is a dir.+	 */+	if (t->flags & hasLstat) {+		/* If lstat() says it's a dir, it must be a dir. */+		st = tree_current_lstat(t);+		if (st == NULL)+			return 0;+		if (S_ISDIR(st->st_mode))+			return 1;+		/* Not a dir; might be a link to a dir. */+		/* If it's not a link, then it's not a link to a dir. */+		if (!S_ISLNK(st->st_mode))+			return 0;+		/*+		 * It's a link, but we don't know what it's a link to,+		 * so we'll have to use stat().+		 */+	}++	st = tree_current_stat(t);+	/* If we can't stat it, it's not a dir. */+	if (st == NULL)+		return 0;+	/* Use the definitive test.  Hopefully this is cached. */+	return (S_ISDIR(st->st_mode));+}++/*+ * Test whether current entry is a physical directory.  Usually, we+ * already have at least one of stat() or lstat() in memory, so we+ * use tricks to try to avoid an extra trip to the disk.+ */+static int+tree_current_is_physical_dir(struct tree *t)+{+	const struct stat *st;++	/*+	 * If stat() says it isn't a dir, then it's not a dir.+	 * If stat() data is cached, this check is free, so do it first.+	 */+	if (t->flags & hasStat) {+		st = tree_current_stat(t);+		if (st == NULL)+			return (0);+		if (!S_ISDIR(st->st_mode))+			return (0);+	}++	/*+	 * Either stat() said it was a dir (in which case, we have+	 * to determine whether it's really a link to a dir) or+	 * stat() info wasn't available.  So we use lstat(), which+	 * hopefully is already cached.+	 */++	st = tree_current_lstat(t);+	/* If we can't stat it, it's not a dir. */+	if (st == NULL)+		return 0;+	/* Use the definitive test.  Hopefully this is cached. */+	return (S_ISDIR(st->st_mode));+}++/*+ * Test whether the same file has been in the tree as its parent.+ */+static int+tree_target_is_same_as_parent(struct tree *t, const struct stat *st)+{+	struct tree_entry *te;++	for (te = t->current->parent; te != NULL; te = te->parent) {+		if (te->dev == (int64_t)st->st_dev &&+		    te->ino == (int64_t)st->st_ino)+			return (1);+	}+	return (0);+}++/*+ * Test whether the current file is symbolic link target and+ * on the other filesystem.+ */+static int+tree_current_is_symblic_link_target(struct tree *t)+{+	static const struct stat *lst, *st;++	lst = tree_current_lstat(t);+	st = tree_current_stat(t);+	return (st != NULL && lst != NULL &&+	    (int64_t)st->st_dev == t->current_filesystem->dev &&+	    st->st_dev != lst->st_dev);+}++/*+ * Return the access path for the entry just returned from tree_next().+ */+static const char *+tree_current_access_path(struct tree *t)+{+	return (t->basename);+}++/*+ * Return the full path for the entry just returned from tree_next().+ */+static const char *+tree_current_path(struct tree *t)+{+	return (t->path.s);+}++/*+ * Terminate the traversal.+ */+static void+tree_close(struct tree *t)+{++	if (t == NULL)+		return;+	if (t->entry_fd >= 0) {+		close_and_restore_time(t->entry_fd, t, &t->restore_time);+		t->entry_fd = -1;+	}+	/* Close the handle of readdir(). */+	if (t->d != INVALID_DIR_HANDLE) {+		closedir(t->d);+		t->d = INVALID_DIR_HANDLE;+	}+	/* Release anything remaining in the stack. */+	while (t->stack != NULL) {+		if (t->stack->flags & isDirLink)+			close(t->stack->symlink_parent_fd);+		tree_pop(t);+	}+	if (t->working_dir_fd >= 0) {+		close(t->working_dir_fd);+		t->working_dir_fd = -1;+	}+	if (t->initial_dir_fd >= 0) {+		close(t->initial_dir_fd);+		t->initial_dir_fd = -1;+	}+}++/*+ * Release any resources.+ */+static void+tree_free(struct tree *t)+{+	int i;++	if (t == NULL)+		return;+	archive_string_free(&t->path);+#if defined(USE_READDIR_R)+	free(t->dirent);+#endif+	free(t->sparse_list);+	for (i = 0; i < t->max_filesystem_id; i++)+		free(t->filesystem_table[i].allocation_ptr);+	free(t->filesystem_table);+	free(t);+}++#endif
+ c/archive_read_disk_private.h view
@@ -0,0 +1,98 @@+/*-+ * Copyright (c) 2003-2009 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer+ *    in this position and unchanged.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/archive_read_disk_private.h 201105 2009-12-28 03:20:54Z kientzle $+ */++#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED+#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++#include "archive_platform_acl.h"++struct tree;+struct archive_entry;++struct archive_read_disk {+	struct archive	archive;++	/* Reused by archive_read_next_header() */+	struct archive_entry *entry;++	/*+	 * Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid,+	 * following an old BSD convention.  'L' follows all symlinks,+	 * 'P' follows none, 'H' follows symlinks only for the first+	 * item.+	 */+	char	symlink_mode;++	/*+	 * Since symlink interaction changes, we need to track whether+	 * we're following symlinks for the current item.  'L' mode above+	 * sets this true, 'P' sets it false, 'H' changes it as we traverse.+	 */+	char	follow_symlinks;  /* Either 'L' or 'P'. */++	/* Directory traversals. */+	struct tree *tree;+	int	(*open_on_current_dir)(struct tree*, const char *, int);+	int	(*tree_current_dir_fd)(struct tree*);+	int	(*tree_enter_working_dir)(struct tree*);++	/* Bitfield with ARCHIVE_READDISK_* tunables */+	int	flags;++	const char * (*lookup_gname)(void *private, int64_t gid);+	void	(*cleanup_gname)(void *private);+	void	 *lookup_gname_data;+	const char * (*lookup_uname)(void *private, int64_t uid);+	void	(*cleanup_uname)(void *private);+	void	 *lookup_uname_data;++	int	(*metadata_filter_func)(struct archive *, void *,+			struct archive_entry *);+	void	*metadata_filter_data;++	/* ARCHIVE_MATCH object. */+	struct archive	*matching;+	/* Callback function, this will be invoked when ARCHIVE_MATCH+	 * archive_match_*_excluded_ae return true. */+	void	(*excluded_cb_func)(struct archive *, void *,+			 struct archive_entry *);+	void	*excluded_cb_data;+};++const char *+archive_read_disk_entry_setup_path(struct archive_read_disk *,+    struct archive_entry *, int *);++int+archive_read_disk_entry_setup_acls(struct archive_read_disk *,+    struct archive_entry *, int *);+#endif
+ c/archive_read_disk_set_standard_lookup.c view
@@ -0,0 +1,313 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_set_standard_lookup.c 201109 2009-12-28 03:30:31Z kientzle $");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_GRP_H+#include <grp.h>+#endif+#ifdef HAVE_PWD_H+#include <pwd.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"++#if defined(_WIN32) && !defined(__CYGWIN__)+int+archive_read_disk_set_standard_lookup(struct archive *a)+{+	archive_set_error(a, -1, "Standard lookups not available on Windows");+	return (ARCHIVE_FATAL);+}+#else /* ! (_WIN32 && !__CYGWIN__) */+#define	name_cache_size 127++static const char * const NO_NAME = "(noname)";++struct name_cache {+	struct archive *archive;+	char   *buff;+	size_t  buff_size;+	int	probes;+	int	hits;+	size_t	size;+	struct {+		id_t id;+		const char *name;+	} cache[name_cache_size];+};++static const char *	lookup_gname(void *, int64_t);+static const char *	lookup_uname(void *, int64_t);+static void	cleanup(void *);+static const char *	lookup_gname_helper(struct name_cache *, id_t gid);+static const char *	lookup_uname_helper(struct name_cache *, id_t uid);++/*+ * Installs functions that use getpwuid()/getgrgid()---along with+ * a simple cache to accelerate such lookups---into the archive_read_disk+ * object.  This is in a separate file because getpwuid()/getgrgid()+ * can pull in a LOT of library code (including NIS/LDAP functions, which+ * pull in DNS resolvers, etc).  This can easily top 500kB, which makes+ * it inappropriate for some space-constrained applications.+ *+ * Applications that are size-sensitive may want to just use the+ * real default functions (defined in archive_read_disk.c) that just+ * use the uid/gid without the lookup.  Or define your own custom functions+ * if you prefer.+ */+int+archive_read_disk_set_standard_lookup(struct archive *a)+{+	struct name_cache *ucache = malloc(sizeof(struct name_cache));+	struct name_cache *gcache = malloc(sizeof(struct name_cache));++	if (ucache == NULL || gcache == NULL) {+		archive_set_error(a, ENOMEM,+		    "Can't allocate uname/gname lookup cache");+		free(ucache);+		free(gcache);+		return (ARCHIVE_FATAL);+	}++	memset(ucache, 0, sizeof(*ucache));+	ucache->archive = a;+	ucache->size = name_cache_size;+	memset(gcache, 0, sizeof(*gcache));+	gcache->archive = a;+	gcache->size = name_cache_size;++	archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup);+	archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup);++	return (ARCHIVE_OK);+}++static void+cleanup(void *data)+{+	struct name_cache *cache = (struct name_cache *)data;+	size_t i;++	if (cache != NULL) {+		for (i = 0; i < cache->size; i++) {+			if (cache->cache[i].name != NULL &&+			    cache->cache[i].name != NO_NAME)+				free((void *)(uintptr_t)cache->cache[i].name);+		}+		free(cache->buff);+		free(cache);+	}+}++/*+ * Lookup uid/gid from uname/gname, return NULL if no match.+ */+static const char *+lookup_name(struct name_cache *cache,+    const char * (*lookup_fn)(struct name_cache *, id_t), id_t id)+{+	const char *name;+	int slot;+++	cache->probes++;++	slot = id % cache->size;+	if (cache->cache[slot].name != NULL) {+		if (cache->cache[slot].id == id) {+			cache->hits++;+			if (cache->cache[slot].name == NO_NAME)+				return (NULL);+			return (cache->cache[slot].name);+		}+		if (cache->cache[slot].name != NO_NAME)+			free((void *)(uintptr_t)cache->cache[slot].name);+		cache->cache[slot].name = NULL;+	}++	name = (lookup_fn)(cache, id);+	if (name == NULL) {+		/* Cache and return the negative response. */+		cache->cache[slot].name = NO_NAME;+		cache->cache[slot].id = id;+		return (NULL);+	}++	cache->cache[slot].name = name;+	cache->cache[slot].id = id;+	return (cache->cache[slot].name);+}++static const char *+lookup_uname(void *data, int64_t uid)+{+	struct name_cache *uname_cache = (struct name_cache *)data;+	return (lookup_name(uname_cache,+		    &lookup_uname_helper, (id_t)uid));+}++#if HAVE_GETPWUID_R+static const char *+lookup_uname_helper(struct name_cache *cache, id_t id)+{+	struct passwd	pwent, *result;+	char * nbuff;+	size_t nbuff_size;+	int r;++	if (cache->buff_size == 0) {+		cache->buff_size = 256;+		cache->buff = malloc(cache->buff_size);+	}+	if (cache->buff == NULL)+		return (NULL);+	for (;;) {+		result = &pwent; /* Old getpwuid_r ignores last arg. */+		r = getpwuid_r((uid_t)id, &pwent,+			       cache->buff, cache->buff_size, &result);+		if (r == 0)+			break;+		if (r != ERANGE)+			break;+		/* ERANGE means our buffer was too small, but POSIX+		 * doesn't tell us how big the buffer should be, so+		 * we just double it and try again.  Because the buffer+		 * is kept around in the cache object, we shouldn't+		 * have to do this very often. */+		nbuff_size = cache->buff_size * 2;+		nbuff = realloc(cache->buff, nbuff_size);+		if (nbuff == NULL)+			break;+		cache->buff = nbuff;+		cache->buff_size = nbuff_size;+	}+	if (r != 0) {+		archive_set_error(cache->archive, errno,+		    "Can't lookup user for id %d", (int)id);+		return (NULL);+	}+	if (result == NULL)+		return (NULL);++	return strdup(result->pw_name);+}+#else+static const char *+lookup_uname_helper(struct name_cache *cache, id_t id)+{+	struct passwd	*result;+	(void)cache; /* UNUSED */++	result = getpwuid((uid_t)id);++	if (result == NULL)+		return (NULL);++	return strdup(result->pw_name);+}+#endif++static const char *+lookup_gname(void *data, int64_t gid)+{+	struct name_cache *gname_cache = (struct name_cache *)data;+	return (lookup_name(gname_cache,+		    &lookup_gname_helper, (id_t)gid));+}++#if HAVE_GETGRGID_R+static const char *+lookup_gname_helper(struct name_cache *cache, id_t id)+{+	struct group	grent, *result;+	char * nbuff;+	size_t nbuff_size;+	int r;++	if (cache->buff_size == 0) {+		cache->buff_size = 256;+		cache->buff = malloc(cache->buff_size);+	}+	if (cache->buff == NULL)+		return (NULL);+	for (;;) {+		result = &grent; /* Old getgrgid_r ignores last arg. */+		r = getgrgid_r((gid_t)id, &grent,+			       cache->buff, cache->buff_size, &result);+		if (r == 0)+			break;+		if (r != ERANGE)+			break;+		/* ERANGE means our buffer was too small, but POSIX+		 * doesn't tell us how big the buffer should be, so+		 * we just double it and try again. */+		nbuff_size = cache->buff_size * 2;+		nbuff = realloc(cache->buff, nbuff_size);+		if (nbuff == NULL)+			break;+		cache->buff = nbuff;+		cache->buff_size = nbuff_size;+	}+	if (r != 0) {+		archive_set_error(cache->archive, errno,+		    "Can't lookup group for id %d", (int)id);+		return (NULL);+	}+	if (result == NULL)+		return (NULL);++	return strdup(result->gr_name);+}+#else+static const char *+lookup_gname_helper(struct name_cache *cache, id_t id)+{+	struct group	*result;+	(void)cache; /* UNUSED */++	result = getgrgid((gid_t)id);++	if (result == NULL)+		return (NULL);++	return strdup(result->gr_name);+}+#endif++#endif /* ! (_WIN32 && !__CYGWIN__) */
+ c/archive_read_disk_windows.c view
@@ -0,0 +1,2477 @@+/*-+ * Copyright (c) 2003-2009 Tim Kientzle+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer+ *    in this position and unchanged.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */+#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#if defined(_WIN32) && !defined(__CYGWIN__)++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#include <winioctl.h>++#include "archive.h"+#include "archive_string.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_disk_private.h"++#ifndef O_BINARY+#define O_BINARY	0+#endif+#ifndef IO_REPARSE_TAG_SYMLINK+/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */+#define	IO_REPARSE_TAG_SYMLINK 0xA000000CL+#endif++/*-+ * This is a new directory-walking system that addresses a number+ * of problems I've had with fts(3).  In particular, it has no+ * pathname-length limits (other than the size of 'int'), handles+ * deep logical traversals, uses considerably less memory, and has+ * an opaque interface (easier to modify in the future).+ *+ * Internally, it keeps a single list of "tree_entry" items that+ * represent filesystem objects that require further attention.+ * Non-directories are not kept in memory: they are pulled from+ * readdir(), returned to the client, then freed as soon as possible.+ * Any directory entry to be traversed gets pushed onto the stack.+ *+ * There is surprisingly little information that needs to be kept for+ * each item on the stack.  Just the name, depth (represented here as the+ * string length of the parent directory's pathname), and some markers+ * indicating how to get back to the parent (via chdir("..") for a+ * regular dir or via fchdir(2) for a symlink).+ */++struct restore_time {+	const wchar_t		*full_path;+	FILETIME		 lastWriteTime;+	FILETIME		 lastAccessTime;+	mode_t			 filetype;+};++struct tree_entry {+	int			 depth;+	struct tree_entry	*next;+	struct tree_entry	*parent;+	size_t			 full_path_dir_length;+	struct archive_wstring	 name;+	struct archive_wstring	 full_path;+	size_t			 dirname_length;+	int64_t			 dev;+	int64_t			 ino;+	int			 flags;+	int			 filesystem_id;+	/* How to restore time of a directory. */+	struct restore_time	 restore_time;+};++struct filesystem {+	int64_t		dev;+	int		synthetic;+	int		remote;+	DWORD		bytesPerSector;+};++/* Definitions for tree_entry.flags bitmap. */+#define	isDir		1  /* This entry is a regular directory. */+#define	isDirLink	2  /* This entry is a symbolic link to a directory. */+#define	needsFirstVisit	4  /* This is an initial entry. */+#define	needsDescent	8  /* This entry needs to be previsited. */+#define	needsOpen	16 /* This is a directory that needs to be opened. */+#define	needsAscent	32 /* This entry needs to be postvisited. */++/*+ * On Windows, "first visit" is handled as a pattern to be handed to+ * _findfirst().  This is consistent with Windows conventions that+ * file patterns are handled within the application.  On Posix,+ * "first visit" is just returned to the client.+ */++#define MAX_OVERLAPPED	8+#define READ_BUFFER_SIZE	(1024 * 64) /* Default to 64KB per https://technet.microsoft.com/en-us/library/cc938632.aspx */+#define DIRECT_IO	0/* Disabled */+#define ASYNC_IO	1/* Enabled */++/*+ * Local data for this package.+ */+struct tree {+	struct tree_entry	*stack;+	struct tree_entry	*current;+	HANDLE d;+	WIN32_FIND_DATAW	_findData;+	WIN32_FIND_DATAW	*findData;+	int			 flags;+	int			 visit_type;+	/* Error code from last failed operation. */+	int			 tree_errno;++	/* A full path with "\\?\" prefix. */+	struct archive_wstring	 full_path;+	size_t			 full_path_dir_length;+	/* Dynamically-sized buffer for holding path */+	struct archive_wstring	 path;++	/* Last path element */+	const wchar_t		*basename;+	/* Leading dir length */+	size_t			 dirname_length;++	int	 depth;++	BY_HANDLE_FILE_INFORMATION	lst;+	BY_HANDLE_FILE_INFORMATION	st;+	int			 descend;+	/* How to restore time of a file. */+	struct restore_time	restore_time;++	struct entry_sparse {+		int64_t		 length;+		int64_t		 offset;+	}			*sparse_list, *current_sparse;+	int			 sparse_count;+	int			 sparse_list_size;++	char			 initial_symlink_mode;+	char			 symlink_mode;+	struct filesystem	*current_filesystem;+	struct filesystem	*filesystem_table;+	int			 initial_filesystem_id;+	int			 current_filesystem_id;+	int			 max_filesystem_id;+	int			 allocated_filesystem;++	HANDLE			 entry_fh;+	int			 entry_eof;+	int64_t			 entry_remaining_bytes;+	int64_t			 entry_total;++	int			 ol_idx_doing;+	int			 ol_idx_done;+	int			 ol_num_doing;+	int			 ol_num_done;+	int64_t			 ol_remaining_bytes;+	int64_t			 ol_total;+	struct la_overlapped {+		OVERLAPPED	 ol;+		struct archive * _a;+		unsigned char	*buff;+		size_t		 buff_size;+		int64_t		 offset;+		size_t		 bytes_expected;+		size_t		 bytes_transferred;+	}			 ol[MAX_OVERLAPPED];+	int			 direct_io;+	int			 async_io;+};++#define bhfi_dev(bhfi)	((bhfi)->dwVolumeSerialNumber)+/* Treat FileIndex as i-node. We should remove a sequence number+ * which is high-16-bits of nFileIndexHigh. */+#define bhfi_ino(bhfi)	\+	((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \+    + (bhfi)->nFileIndexLow)++/* Definitions for tree.flags bitmap. */+#define	hasStat		16 /* The st entry is valid. */+#define	hasLstat	32 /* The lst entry is valid. */+#define	needsRestoreTimes 128++static int+tree_dir_next_windows(struct tree *t, const wchar_t *pattern);++/* Initiate/terminate a tree traversal. */+static struct tree *tree_open(const wchar_t *, int, int);+static struct tree *tree_reopen(struct tree *, const wchar_t *, int);+static void tree_close(struct tree *);+static void tree_free(struct tree *);+static void tree_push(struct tree *, const wchar_t *, const wchar_t *,+		int, int64_t, int64_t, struct restore_time *);++/*+ * tree_next() returns Zero if there is no next entry, non-zero if+ * there is.  Note that directories are visited three times.+ * Directories are always visited first as part of enumerating their+ * parent; that is a "regular" visit.  If tree_descend() is invoked at+ * that time, the directory is added to a work list and will+ * subsequently be visited two more times: once just after descending+ * into the directory ("postdescent") and again just after ascending+ * back to the parent ("postascent").+ *+ * TREE_ERROR_DIR is returned if the descent failed (because the+ * directory couldn't be opened, for instance).  This is returned+ * instead of TREE_POSTDESCENT/TREE_POSTASCENT.  TREE_ERROR_DIR is not a+ * fatal error, but it does imply that the relevant subtree won't be+ * visited.  TREE_ERROR_FATAL is returned for an error that left the+ * traversal completely hosed.  Right now, this is only returned for+ * chdir() failures during ascent.+ */+#define	TREE_REGULAR		1+#define	TREE_POSTDESCENT	2+#define	TREE_POSTASCENT		3+#define	TREE_ERROR_DIR		-1+#define	TREE_ERROR_FATAL	-2++static int tree_next(struct tree *);++/*+ * Return information about the current entry.+ */++/*+ * The current full pathname, length of the full pathname, and a name+ * that can be used to access the file.  Because tree does use chdir+ * extensively, the access path is almost never the same as the full+ * current path.+ *+ */+static const wchar_t *tree_current_path(struct tree *);+static const wchar_t *tree_current_access_path(struct tree *);++/*+ * Request the lstat() or stat() data for the current path.  Since the+ * tree package needs to do some of this anyway, and caches the+ * results, you should take advantage of it here if you need it rather+ * than make a redundant stat() or lstat() call of your own.+ */+static const BY_HANDLE_FILE_INFORMATION *tree_current_stat(struct tree *);+static const BY_HANDLE_FILE_INFORMATION *tree_current_lstat(struct tree *);++/* The following functions use tricks to avoid a certain number of+ * stat()/lstat() calls. */+/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */+static int tree_current_is_physical_dir(struct tree *);+/* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */+static int tree_current_is_physical_link(struct tree *);+/* Instead of archive_entry_copy_stat for BY_HANDLE_FILE_INFORMATION */+static void tree_archive_entry_copy_bhfi(struct archive_entry *,+		    struct tree *, const BY_HANDLE_FILE_INFORMATION *);+/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */+static int tree_current_is_dir(struct tree *);+static int update_current_filesystem(struct archive_read_disk *a,+		    int64_t dev);+static int setup_current_filesystem(struct archive_read_disk *);+static int tree_target_is_same_as_parent(struct tree *,+		    const BY_HANDLE_FILE_INFORMATION *);++static int	_archive_read_disk_open_w(struct archive *, const wchar_t *);+static int	_archive_read_free(struct archive *);+static int	_archive_read_close(struct archive *);+static int	_archive_read_data_block(struct archive *,+		    const void **, size_t *, int64_t *);+static int	_archive_read_next_header(struct archive *,+		    struct archive_entry **);+static int	_archive_read_next_header2(struct archive *,+		    struct archive_entry *);+static const char *trivial_lookup_gname(void *, int64_t gid);+static const char *trivial_lookup_uname(void *, int64_t uid);+static int	setup_sparse(struct archive_read_disk *, struct archive_entry *);+static int	close_and_restore_time(HANDLE, struct tree *,+		    struct restore_time *);+static int	setup_sparse_from_disk(struct archive_read_disk *,+		    struct archive_entry *, HANDLE);+static int	la_linkname_from_handle(HANDLE, wchar_t **, int *);+static int	la_linkname_from_pathw(const wchar_t *, wchar_t **, int *);+static void	entry_symlink_from_pathw(struct archive_entry *,+		    const wchar_t *path);++typedef struct _REPARSE_DATA_BUFFER {+	ULONG	ReparseTag;+	USHORT ReparseDataLength;+	USHORT	Reserved;+	union {+		struct {+			USHORT	SubstituteNameOffset;+			USHORT	SubstituteNameLength;+			USHORT	PrintNameOffset;+			USHORT	PrintNameLength;+			ULONG	Flags;+			WCHAR	PathBuffer[1];+		} SymbolicLinkReparseBuffer;+		struct {+			USHORT	SubstituteNameOffset;+			USHORT	SubstituteNameLength;+			USHORT	PrintNameOffset;+			USHORT	PrintNameLength;+			WCHAR	PathBuffer[1];+		} MountPointReparseBuffer;+		struct {+			UCHAR	DataBuffer[1];+		} GenericReparseBuffer;+	} DUMMYUNIONNAME;+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;++/*+ * Reads the target of a symbolic link+ *+ * Returns 0 on success and -1 on failure+ * outbuf is allocated in the function+ */+static int+la_linkname_from_handle(HANDLE h, wchar_t **linkname, int *linktype)+{+	DWORD inbytes;+	REPARSE_DATA_BUFFER *buf;+	BY_HANDLE_FILE_INFORMATION st;+	size_t len;+	BOOL ret;+	BYTE *indata;+	wchar_t *tbuf;++	ret = GetFileInformationByHandle(h, &st);+	if (ret == 0 ||+	    (st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {+		return (-1);+	}++	indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);+	ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata,+	    1024, &inbytes, NULL);+	if (ret == 0) {+		la_dosmaperr(GetLastError());+		free(indata);+		return (-1);+	}++	buf = (REPARSE_DATA_BUFFER *) indata;+	if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) {+		free(indata);+		/* File is not a symbolic link */+		errno = EINVAL;+		return (-1);+	}++	len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength;+	if (len <= 0) {+		free(indata);+		return (-1);+	}++	tbuf = malloc(len + 1 * sizeof(wchar_t));+	if (tbuf == NULL) {+		free(indata);+		return (-1);+	}++	memcpy(tbuf, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer)+	    [buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len);+	free(indata);++	tbuf[len / sizeof(wchar_t)] = L'\0';++	*linkname = tbuf;++	/*+	 * Translate backslashes to slashes for libarchive internal use+	 */+	while(*tbuf != L'\0') {+		if (*tbuf == L'\\')+			*tbuf = L'/';+		tbuf++;+	}++	if ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)+		*linktype = AE_SYMLINK_TYPE_FILE;+	else+		*linktype = AE_SYMLINK_TYPE_DIRECTORY;++	return (0);+}++/*+ * Returns AE_SYMLINK_TYPE_FILE, AE_SYMLINK_TYPE_DIRECTORY or -1 on error+ */+static int+la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype)+{+	HANDLE h;+	const DWORD flag = FILE_FLAG_BACKUP_SEMANTICS |+	    FILE_FLAG_OPEN_REPARSE_POINT;+	int ret;++	h = CreateFileW(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag,+	    NULL);+	if (h == INVALID_HANDLE_VALUE) {+		la_dosmaperr(GetLastError());+		return (-1);+	}++	ret = la_linkname_from_handle(h, outbuf, linktype);+	CloseHandle(h);++	return (ret);+}++static void+entry_symlink_from_pathw(struct archive_entry *entry, const wchar_t *path)+{+	wchar_t *linkname = NULL;+	int ret, linktype;++	ret = la_linkname_from_pathw(path, &linkname, &linktype);+	if (ret != 0)+		return;+	if (linktype >= 0) {+		archive_entry_copy_symlink_w(entry, linkname);+		archive_entry_set_symlink_type(entry, linktype);+	}+	free(linkname);++	return;+}++static struct archive_vtable *+archive_read_disk_vtable(void)+{+	static struct archive_vtable av;+	static int inited = 0;++	if (!inited) {+		av.archive_free = _archive_read_free;+		av.archive_close = _archive_read_close;+		av.archive_read_data_block = _archive_read_data_block;+		av.archive_read_next_header = _archive_read_next_header;+		av.archive_read_next_header2 = _archive_read_next_header2;+		inited = 1;+	}+	return (&av);+}++const char *+archive_read_disk_gname(struct archive *_a, la_int64_t gid)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+		ARCHIVE_STATE_ANY, "archive_read_disk_gname"))+		return (NULL);+	if (a->lookup_gname == NULL)+		return (NULL);+	return ((*a->lookup_gname)(a->lookup_gname_data, gid));+}++const char *+archive_read_disk_uname(struct archive *_a, la_int64_t uid)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+		ARCHIVE_STATE_ANY, "archive_read_disk_uname"))+		return (NULL);+	if (a->lookup_uname == NULL)+		return (NULL);+	return ((*a->lookup_uname)(a->lookup_uname_data, uid));+}++int+archive_read_disk_set_gname_lookup(struct archive *_a,+    void *private_data,+    const char * (*lookup_gname)(void *private, la_int64_t gid),+    void (*cleanup_gname)(void *private))+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup");++	if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)+		(a->cleanup_gname)(a->lookup_gname_data);++	a->lookup_gname = lookup_gname;+	a->cleanup_gname = cleanup_gname;+	a->lookup_gname_data = private_data;+	return (ARCHIVE_OK);+}++int+archive_read_disk_set_uname_lookup(struct archive *_a,+    void *private_data,+    const char * (*lookup_uname)(void *private, int64_t uid),+    void (*cleanup_uname)(void *private))+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup");++	if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)+		(a->cleanup_uname)(a->lookup_uname_data);++	a->lookup_uname = lookup_uname;+	a->cleanup_uname = cleanup_uname;+	a->lookup_uname_data = private_data;+	return (ARCHIVE_OK);+}++/*+ * Create a new archive_read_disk object and initialize it with global state.+ */+struct archive *+archive_read_disk_new(void)+{+	struct archive_read_disk *a;++	a = (struct archive_read_disk *)calloc(1, sizeof(*a));+	if (a == NULL)+		return (NULL);+	a->archive.magic = ARCHIVE_READ_DISK_MAGIC;+	a->archive.state = ARCHIVE_STATE_NEW;+	a->archive.vtable = archive_read_disk_vtable();+	a->entry = archive_entry_new2(&a->archive);+	a->lookup_uname = trivial_lookup_uname;+	a->lookup_gname = trivial_lookup_gname;+	a->flags = ARCHIVE_READDISK_MAC_COPYFILE;+	return (&a->archive);+}++static int+_archive_read_free(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	int r;++	if (_a == NULL)+		return (ARCHIVE_OK);+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");++	if (a->archive.state != ARCHIVE_STATE_CLOSED)+		r = _archive_read_close(&a->archive);+	else+		r = ARCHIVE_OK;++	tree_free(a->tree);+	if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)+		(a->cleanup_gname)(a->lookup_gname_data);+	if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)+		(a->cleanup_uname)(a->lookup_uname_data);+	archive_string_free(&a->archive.error_string);+	archive_entry_free(a->entry);+	a->archive.magic = 0;+	free(a);+	return (r);+}++static int+_archive_read_close(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");++	if (a->archive.state != ARCHIVE_STATE_FATAL)+		a->archive.state = ARCHIVE_STATE_CLOSED;++	tree_close(a->tree);++	return (ARCHIVE_OK);+}++static void+setup_symlink_mode(struct archive_read_disk *a, char symlink_mode, +    int follow_symlinks)+{+	a->symlink_mode = symlink_mode;+	a->follow_symlinks = follow_symlinks;+	if (a->tree != NULL) {+		a->tree->initial_symlink_mode = a->symlink_mode;+		a->tree->symlink_mode = a->symlink_mode;+	}+}++int+archive_read_disk_set_symlink_logical(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical");+	setup_symlink_mode(a, 'L', 1);+	return (ARCHIVE_OK);+}++int+archive_read_disk_set_symlink_physical(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical");+	setup_symlink_mode(a, 'P', 0);+	return (ARCHIVE_OK);+}++int+archive_read_disk_set_symlink_hybrid(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid");+	setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */+	return (ARCHIVE_OK);+}++int+archive_read_disk_set_atime_restored(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime");+	a->flags |= ARCHIVE_READDISK_RESTORE_ATIME;+	if (a->tree != NULL)+		a->tree->flags |= needsRestoreTimes;+	return (ARCHIVE_OK);+}++int+archive_read_disk_set_behavior(struct archive *_a, int flags)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	int r = ARCHIVE_OK;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");++	a->flags = flags;++	if (flags & ARCHIVE_READDISK_RESTORE_ATIME)+		r = archive_read_disk_set_atime_restored(_a);+	else {+		if (a->tree != NULL)+			a->tree->flags &= ~needsRestoreTimes;+	}+	return (r);+}++/*+ * Trivial implementations of gname/uname lookup functions.+ * These are normally overridden by the client, but these stub+ * versions ensure that we always have something that works.+ */+static const char *+trivial_lookup_gname(void *private_data, int64_t gid)+{+	(void)private_data; /* UNUSED */+	(void)gid; /* UNUSED */+	return (NULL);+}++static const char *+trivial_lookup_uname(void *private_data, int64_t uid)+{+	(void)private_data; /* UNUSED */+	(void)uid; /* UNUSED */+	return (NULL);+}++static int64_t+align_num_per_sector(struct tree *t, int64_t size)+{+	int64_t surplus;++	size += t->current_filesystem->bytesPerSector -1;+	surplus = size % t->current_filesystem->bytesPerSector;+	size -= surplus;+	return (size);+}++static int+start_next_async_read(struct archive_read_disk *a, struct tree *t)+{+	struct la_overlapped *olp;+	DWORD buffbytes, rbytes;++	if (t->ol_remaining_bytes == 0)+		return (ARCHIVE_EOF);++	olp = &(t->ol[t->ol_idx_doing]);+	t->ol_idx_doing = (t->ol_idx_doing + 1) % MAX_OVERLAPPED;++	/* Allocate read buffer. */+	if (olp->buff == NULL) {+		void *p;+		size_t s = (size_t)align_num_per_sector(t, READ_BUFFER_SIZE);+		p = VirtualAlloc(NULL, s, MEM_COMMIT, PAGE_READWRITE);+		if (p == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Couldn't allocate memory");+			a->archive.state = ARCHIVE_STATE_FATAL;+			return (ARCHIVE_FATAL);+		}+		olp->buff = p;+		olp->buff_size = s;+		olp->_a = &a->archive;+		olp->ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);+		if (olp->ol.hEvent == NULL) {+			la_dosmaperr(GetLastError());+			archive_set_error(&a->archive, errno,+			    "CreateEvent failed");+			a->archive.state = ARCHIVE_STATE_FATAL;+			return (ARCHIVE_FATAL);+		}+	} else+		ResetEvent(olp->ol.hEvent);++	buffbytes = (DWORD)olp->buff_size;+	if (buffbytes > t->current_sparse->length)+		buffbytes = (DWORD)t->current_sparse->length;++	/* Skip hole. */+	if (t->current_sparse->offset > t->ol_total) {+		t->ol_remaining_bytes -=+			t->current_sparse->offset - t->ol_total;+	}++	olp->offset = t->current_sparse->offset;+	olp->ol.Offset = (DWORD)(olp->offset & 0xffffffff);+	olp->ol.OffsetHigh = (DWORD)(olp->offset >> 32);++	if (t->ol_remaining_bytes > buffbytes) {+		olp->bytes_expected = buffbytes;+		t->ol_remaining_bytes -= buffbytes;+	} else {+		olp->bytes_expected = (size_t)t->ol_remaining_bytes;+		t->ol_remaining_bytes = 0;+	}+	olp->bytes_transferred = 0;+	t->current_sparse->offset += buffbytes;+	t->current_sparse->length -= buffbytes;+	t->ol_total = t->current_sparse->offset;+	if (t->current_sparse->length == 0 && t->ol_remaining_bytes > 0)+		t->current_sparse++;++	if (!ReadFile(t->entry_fh, olp->buff, buffbytes, &rbytes, &(olp->ol))) {+		DWORD lasterr;++		lasterr = GetLastError();+		if (lasterr == ERROR_HANDLE_EOF) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Reading file truncated");+			a->archive.state = ARCHIVE_STATE_FATAL;+			return (ARCHIVE_FATAL);+		} else if (lasterr != ERROR_IO_PENDING) {+			if (lasterr == ERROR_NO_DATA)+				errno = EAGAIN;+			else if (lasterr == ERROR_ACCESS_DENIED)+				errno = EBADF;+			else+				la_dosmaperr(lasterr);+			archive_set_error(&a->archive, errno, "Read error");+			a->archive.state = ARCHIVE_STATE_FATAL;+			return (ARCHIVE_FATAL);+		}+	} else+		olp->bytes_transferred = rbytes;+	t->ol_num_doing++;++	return (t->ol_remaining_bytes == 0)? ARCHIVE_EOF: ARCHIVE_OK;+}++static void+cancel_async(struct tree *t)+{+	if (t->ol_num_doing != t->ol_num_done) {+		CancelIo(t->entry_fh);+		t->ol_num_doing = t->ol_num_done = 0;+	}+}++static int+_archive_read_data_block(struct archive *_a, const void **buff,+    size_t *size, int64_t *offset)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	struct tree *t = a->tree;+	struct la_overlapped *olp;+	DWORD bytes_transferred;+	int r = ARCHIVE_FATAL;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_read_data_block");++	if (t->entry_eof || t->entry_remaining_bytes <= 0) {+		r = ARCHIVE_EOF;+		goto abort_read_data;+	}++	/*+	 * Make a request to read the file in asynchronous.+	 */+	if (t->ol_num_doing == 0) {+		do {+			r = start_next_async_read(a, t);+			if (r == ARCHIVE_FATAL)+				goto abort_read_data;+			if (!t->async_io)+				break;+		} while (r == ARCHIVE_OK && t->ol_num_doing < MAX_OVERLAPPED);+	} else {+		if ((r = start_next_async_read(a, t)) == ARCHIVE_FATAL)+			goto abort_read_data;+	}++	olp = &(t->ol[t->ol_idx_done]);+	t->ol_idx_done = (t->ol_idx_done + 1) % MAX_OVERLAPPED;+	if (olp->bytes_transferred)+		bytes_transferred = (DWORD)olp->bytes_transferred;+	else if (!GetOverlappedResult(t->entry_fh, &(olp->ol),+	    &bytes_transferred, TRUE)) {+		la_dosmaperr(GetLastError());+		archive_set_error(&a->archive, errno,+		    "GetOverlappedResult failed");+		a->archive.state = ARCHIVE_STATE_FATAL;+		r = ARCHIVE_FATAL;+		goto abort_read_data;+	}+	t->ol_num_done++;++	if (bytes_transferred == 0 ||+	    olp->bytes_expected != bytes_transferred) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Reading file truncated");+		a->archive.state = ARCHIVE_STATE_FATAL;+		r = ARCHIVE_FATAL;+		goto abort_read_data;+	}++	*buff = olp->buff;+	*size = bytes_transferred;+	*offset = olp->offset;+	if (olp->offset > t->entry_total)+		t->entry_remaining_bytes -= olp->offset - t->entry_total;+	t->entry_total = olp->offset + *size;+	t->entry_remaining_bytes -= *size;+	if (t->entry_remaining_bytes == 0) {+		/* Close the current file descriptor */+		close_and_restore_time(t->entry_fh, t, &t->restore_time);+		t->entry_fh = INVALID_HANDLE_VALUE;+		t->entry_eof = 1;+	}+	return (ARCHIVE_OK);++abort_read_data:+	*buff = NULL;+	*size = 0;+	*offset = t->entry_total;+	if (t->entry_fh != INVALID_HANDLE_VALUE) {+		cancel_async(t);+		/* Close the current file descriptor */+		close_and_restore_time(t->entry_fh, t, &t->restore_time);+		t->entry_fh = INVALID_HANDLE_VALUE;+	}+	return (r);+}++static int+next_entry(struct archive_read_disk *a, struct tree *t,+    struct archive_entry *entry)+{+	const BY_HANDLE_FILE_INFORMATION *st;+	const BY_HANDLE_FILE_INFORMATION *lst;+	const char*name;+	int descend, r;++	st = NULL;+	lst = NULL;+	t->descend = 0;+	do {+		switch (tree_next(t)) {+		case TREE_ERROR_FATAL:+			archive_set_error(&a->archive, t->tree_errno,+			    "%ls: Unable to continue traversing directory tree",+			    tree_current_path(t));+			a->archive.state = ARCHIVE_STATE_FATAL;+			return (ARCHIVE_FATAL);+		case TREE_ERROR_DIR:+			archive_set_error(&a->archive, t->tree_errno,+			    "%ls: Couldn't visit directory",+			    tree_current_path(t));+			return (ARCHIVE_FAILED);+		case 0:+			return (ARCHIVE_EOF);+		case TREE_POSTDESCENT:+		case TREE_POSTASCENT:+			break;+		case TREE_REGULAR:+			lst = tree_current_lstat(t);+			if (lst == NULL) {+				archive_set_error(&a->archive, t->tree_errno,+				    "%ls: Cannot stat",+				    tree_current_path(t));+				return (ARCHIVE_FAILED);+			}+			break;+		}	+	} while (lst == NULL);++	archive_entry_copy_pathname_w(entry, tree_current_path(t));++	/*+	 * Perform path matching.+	 */+	if (a->matching) {+		r = archive_match_path_excluded(a->matching, entry);+		if (r < 0) {+			archive_set_error(&(a->archive), errno,+			    "Failed : %s", archive_error_string(a->matching));+			return (r);+		}+		if (r) {+			if (a->excluded_cb_func)+				a->excluded_cb_func(&(a->archive),+				    a->excluded_cb_data, entry);+			return (ARCHIVE_RETRY);+		}+	}++	/*+	 * Distinguish 'L'/'P'/'H' symlink following.+	 */+	switch(t->symlink_mode) {+	case 'H':+		/* 'H': After the first item, rest like 'P'. */+		t->symlink_mode = 'P';+		/* 'H': First item (from command line) like 'L'. */+		/* FALLTHROUGH */+	case 'L':+		/* 'L': Do descend through a symlink to dir. */+		descend = tree_current_is_dir(t);+		/* 'L': Follow symlinks to files. */+		a->symlink_mode = 'L';+		a->follow_symlinks = 1;+		/* 'L': Archive symlinks as targets, if we can. */+		st = tree_current_stat(t);+		if (st != NULL && !tree_target_is_same_as_parent(t, st))+			break;+		/* If stat fails, we have a broken symlink;+		 * in that case, don't follow the link. */+		/* FALLTHROUGH */+	default:+		/* 'P': Don't descend through a symlink to dir. */+		descend = tree_current_is_physical_dir(t);+		/* 'P': Don't follow symlinks to files. */+		a->symlink_mode = 'P';+		a->follow_symlinks = 0;+		/* 'P': Archive symlinks as symlinks. */+		st = lst;+		break;+	}++	if (update_current_filesystem(a, bhfi_dev(st)) != ARCHIVE_OK) {+		a->archive.state = ARCHIVE_STATE_FATAL;+		return (ARCHIVE_FATAL);+	}+	if (t->initial_filesystem_id == -1)+		t->initial_filesystem_id = t->current_filesystem_id;+	if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) {+		if (t->initial_filesystem_id != t->current_filesystem_id)+			return (ARCHIVE_RETRY);+	}+	t->descend = descend;++	tree_archive_entry_copy_bhfi(entry, t, st);++	/* Save the times to be restored. This must be in before+	 * calling archive_read_disk_descend() or any chance of it,+	 * especially, invoking a callback. */+	t->restore_time.lastWriteTime = st->ftLastWriteTime;+	t->restore_time.lastAccessTime = st->ftLastAccessTime;+	t->restore_time.filetype = archive_entry_filetype(entry);++	/*+	 * Perform time matching.+	 */+	if (a->matching) {+		r = archive_match_time_excluded(a->matching, entry);+		if (r < 0) {+			archive_set_error(&(a->archive), errno,+			    "Failed : %s", archive_error_string(a->matching));+			return (r);+		}+		if (r) {+			if (a->excluded_cb_func)+				a->excluded_cb_func(&(a->archive),+				    a->excluded_cb_data, entry);+			return (ARCHIVE_RETRY);+		}+	}++	/* Lookup uname/gname */+	name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry));+	if (name != NULL)+		archive_entry_copy_uname(entry, name);+	name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry));+	if (name != NULL)+		archive_entry_copy_gname(entry, name);++	/*+	 * Perform owner matching.+	 */+	if (a->matching) {+		r = archive_match_owner_excluded(a->matching, entry);+		if (r < 0) {+			archive_set_error(&(a->archive), errno,+			    "Failed : %s", archive_error_string(a->matching));+			return (r);+		}+		if (r) {+			if (a->excluded_cb_func)+				a->excluded_cb_func(&(a->archive),+				    a->excluded_cb_data, entry);+			return (ARCHIVE_RETRY);+		}+	}++	/*+	 * File attributes+	 */+	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {+		const int supported_attrs =+		    FILE_ATTRIBUTE_READONLY |+		    FILE_ATTRIBUTE_HIDDEN |+		    FILE_ATTRIBUTE_SYSTEM;+		DWORD file_attrs = st->dwFileAttributes & supported_attrs;+		if (file_attrs != 0)+			archive_entry_set_fflags(entry, file_attrs, 0);+	}++	/*+	 * Invoke a meta data filter callback.+	 */+	if (a->metadata_filter_func) {+		if (!a->metadata_filter_func(&(a->archive),+		    a->metadata_filter_data, entry))+			return (ARCHIVE_RETRY);+	}++	archive_entry_copy_sourcepath_w(entry, tree_current_access_path(t));++	r = ARCHIVE_OK;+	if (archive_entry_filetype(entry) == AE_IFREG &&+	    archive_entry_size(entry) > 0) {+		DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;+		if (t->async_io)+			flags |= FILE_FLAG_OVERLAPPED;+		if (t->direct_io)+			flags |= FILE_FLAG_NO_BUFFERING;+		else+			flags |= FILE_FLAG_SEQUENTIAL_SCAN;+		t->entry_fh = CreateFileW(tree_current_access_path(t),+		    GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, flags, NULL);+		if (t->entry_fh == INVALID_HANDLE_VALUE) {+			la_dosmaperr(GetLastError());+			archive_set_error(&a->archive, errno,+			    "Couldn't open %ls", tree_current_path(a->tree));+			return (ARCHIVE_FAILED);+		}++		/* Find sparse data from the disk. */+		if (archive_entry_hardlink(entry) == NULL &&+		    (st->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0)+			r = setup_sparse_from_disk(a, entry, t->entry_fh);+	}+	return (r);+}++static int+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)+{+       int ret;+       struct archive_read_disk *a = (struct archive_read_disk *)_a;+       *entryp = NULL;+       ret = _archive_read_next_header2(_a, a->entry);+       *entryp = a->entry;+       return ret;+}++static int+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	struct tree *t;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_read_next_header2");++	t = a->tree;+	if (t->entry_fh != INVALID_HANDLE_VALUE) {+		cancel_async(t);+		close_and_restore_time(t->entry_fh, t, &t->restore_time);+		t->entry_fh = INVALID_HANDLE_VALUE;+	}++	archive_entry_clear(entry);++	while ((r = next_entry(a, t, entry)) == ARCHIVE_RETRY)+		archive_entry_clear(entry);++	/*+	 * EOF and FATAL are persistent at this layer.  By+	 * modifying the state, we guarantee that future calls to+	 * read a header or read data will fail.+	 */+	switch (r) {+	case ARCHIVE_EOF:+		a->archive.state = ARCHIVE_STATE_EOF;+		break;+	case ARCHIVE_OK:+	case ARCHIVE_WARN:+		t->entry_total = 0;+		if (archive_entry_filetype(entry) == AE_IFREG) {+			t->entry_remaining_bytes = archive_entry_size(entry);+			t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0;+			if (!t->entry_eof &&+			    setup_sparse(a, entry) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		} else {+			t->entry_remaining_bytes = 0;+			t->entry_eof = 1;+		}+		t->ol_idx_doing = t->ol_idx_done = 0;+		t->ol_num_doing = t->ol_num_done = 0;+		t->ol_remaining_bytes = t->entry_remaining_bytes;+		t->ol_total = 0;+		a->archive.state = ARCHIVE_STATE_DATA;+		break;+	case ARCHIVE_RETRY:+		break;+	case ARCHIVE_FATAL:+		a->archive.state = ARCHIVE_STATE_FATAL;+		break;+	}++	__archive_reset_read_data(&a->archive);+	return (r);+}++static int+setup_sparse(struct archive_read_disk *a, struct archive_entry *entry)+{+	struct tree *t = a->tree;+	int64_t aligned, length, offset;+	int i;++	t->sparse_count = archive_entry_sparse_reset(entry);+	if (t->sparse_count+1 > t->sparse_list_size) {+		free(t->sparse_list);+		t->sparse_list_size = t->sparse_count + 1;+		t->sparse_list = malloc(sizeof(t->sparse_list[0]) *+		    t->sparse_list_size);+		if (t->sparse_list == NULL) {+			t->sparse_list_size = 0;+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate data");+			a->archive.state = ARCHIVE_STATE_FATAL;+			return (ARCHIVE_FATAL);+		}+	}+	/*+	 * Get sparse list and make sure those offsets and lengths are+	 * aligned by a sector size.+	 */+	for (i = 0; i < t->sparse_count; i++) {+		archive_entry_sparse_next(entry, &offset, &length);+		aligned = align_num_per_sector(t, offset);+		if (aligned != offset) {+			aligned -= t->current_filesystem->bytesPerSector;+			length += offset - aligned;+		}+		t->sparse_list[i].offset = aligned;+		aligned = align_num_per_sector(t, length);+		t->sparse_list[i].length = aligned;+	}++	aligned = align_num_per_sector(t, archive_entry_size(entry));+	if (i == 0) {+		t->sparse_list[i].offset = 0;+		t->sparse_list[i].length = aligned;+	} else {+		int j, last = i;++		t->sparse_list[i].offset = aligned;+		t->sparse_list[i].length = 0;+		for (i = 0; i < last; i++) {+			if ((t->sparse_list[i].offset ++			       t->sparse_list[i].length) <= +					t->sparse_list[i+1].offset)+				continue;+			/*+			 * Now sparse_list[i+1] is overlapped by sparse_list[i].+			 * Merge those two.+			 */+			length = t->sparse_list[i+1].offset -+					t->sparse_list[i].offset;+			t->sparse_list[i+1].offset = t->sparse_list[i].offset;+			t->sparse_list[i+1].length += length;+			/* Remove sparse_list[i]. */+			for (j = i; j < last; j++) {+				t->sparse_list[j].offset =+				    t->sparse_list[j+1].offset;+				t->sparse_list[j].length =+				    t->sparse_list[j+1].length;+			}+			last--;+		}+	}+	t->current_sparse = t->sparse_list;++	return (ARCHIVE_OK);+}++int+archive_read_disk_set_matching(struct archive *_a, struct archive *_ma,+    void (*_excluded_func)(struct archive *, void *, struct archive_entry *),+    void *_client_data)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_matching");+	a->matching = _ma;+	a->excluded_cb_func = _excluded_func;+	a->excluded_cb_data = _client_data;+	return (ARCHIVE_OK);+}++int+archive_read_disk_set_metadata_filter_callback(struct archive *_a,+    int (*_metadata_filter_func)(struct archive *, void *,+    struct archive_entry *), void *_client_data)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,+	    "archive_read_disk_set_metadata_filter_callback");++	a->metadata_filter_func = _metadata_filter_func;+	a->metadata_filter_data = _client_data;+	return (ARCHIVE_OK);+}++int+archive_read_disk_can_descend(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	struct tree *t = a->tree;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_read_disk_can_descend");++	return (t->visit_type == TREE_REGULAR && t->descend);+}++/*+ * Called by the client to mark the directory just returned from+ * tree_next() as needing to be visited.+ */+int+archive_read_disk_descend(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	struct tree *t = a->tree;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_read_disk_descend");++	if (t->visit_type != TREE_REGULAR || !t->descend)+		return (ARCHIVE_OK);++	if (tree_current_is_physical_dir(t)) {+		tree_push(t, t->basename, t->full_path.s,+		    t->current_filesystem_id,+		    bhfi_dev(&(t->lst)), bhfi_ino(&(t->lst)),+		    &t->restore_time);+		t->stack->flags |= isDir;+	} else if (tree_current_is_dir(t)) {+		tree_push(t, t->basename, t->full_path.s,+		    t->current_filesystem_id,+		    bhfi_dev(&(t->st)), bhfi_ino(&(t->st)),+		    &t->restore_time);+		t->stack->flags |= isDirLink;+	}+	t->descend = 0;+	return (ARCHIVE_OK);+}++int+archive_read_disk_open(struct archive *_a, const char *pathname)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	struct archive_wstring wpath;+	int ret;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,+	    "archive_read_disk_open");+	archive_clear_error(&a->archive);++	/* Make a wchar_t string from a char string. */+	archive_string_init(&wpath);+	if (archive_wstring_append_from_mbs(&wpath, pathname,+	    strlen(pathname)) != 0) {+		if (errno == ENOMEM)+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory");+		else+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Can't convert a path to a wchar_t string");+		a->archive.state = ARCHIVE_STATE_FATAL;+		ret = ARCHIVE_FATAL;+	} else+		ret = _archive_read_disk_open_w(_a, wpath.s);++	archive_wstring_free(&wpath);+	return (ret);+}++int+archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,+	    ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,+	    "archive_read_disk_open_w");+	archive_clear_error(&a->archive);++	return (_archive_read_disk_open_w(_a, pathname));+}++static int+_archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	if (a->tree != NULL)+		a->tree = tree_reopen(a->tree, pathname,+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);+	else+		a->tree = tree_open(pathname, a->symlink_mode,+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);+	if (a->tree == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate directory traversal data");+		a->archive.state = ARCHIVE_STATE_FATAL;+		return (ARCHIVE_FATAL);+	}+	a->archive.state = ARCHIVE_STATE_HEADER;++	return (ARCHIVE_OK);+}++/*+ * Return a current filesystem ID which is index of the filesystem entry+ * you've visited through archive_read_disk.+ */+int+archive_read_disk_current_filesystem(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_read_disk_current_filesystem");++	return (a->tree->current_filesystem_id);+}++static int+update_current_filesystem(struct archive_read_disk *a, int64_t dev)+{+	struct tree *t = a->tree;+	int i, fid;++	if (t->current_filesystem != NULL &&+	    t->current_filesystem->dev == dev)+		return (ARCHIVE_OK);++	for (i = 0; i < t->max_filesystem_id; i++) {+		if (t->filesystem_table[i].dev == dev) {+			/* There is the filesystem ID we've already generated. */+			t->current_filesystem_id = i;+			t->current_filesystem = &(t->filesystem_table[i]);+			return (ARCHIVE_OK);+		}+	}++	/*+	 * There is a new filesystem, we generate a new ID for.+	 */+	fid = t->max_filesystem_id++;+	if (t->max_filesystem_id > t->allocated_filesystem) {+		size_t s;+		void *p;++		s = t->max_filesystem_id * 2;+		p = realloc(t->filesystem_table,+			s * sizeof(*t->filesystem_table));+		if (p == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate tar data");+			return (ARCHIVE_FATAL);+		}+		t->filesystem_table = (struct filesystem *)p;+		t->allocated_filesystem = (int)s;+	}+	t->current_filesystem_id = fid;+	t->current_filesystem = &(t->filesystem_table[fid]);+	t->current_filesystem->dev = dev;++	return (setup_current_filesystem(a));+}++/*+ * Returns 1 if current filesystem is generated filesystem, 0 if it is not+ * or -1 if it is unknown.+ */+int+archive_read_disk_current_filesystem_is_synthetic(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_read_disk_current_filesystem");++	return (a->tree->current_filesystem->synthetic);+}++/*+ * Returns 1 if current filesystem is remote filesystem, 0 if it is not+ * or -1 if it is unknown.+ */+int+archive_read_disk_current_filesystem_is_remote(struct archive *_a)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;++	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,+	    "archive_read_disk_current_filesystem");++	return (a->tree->current_filesystem->remote);+}++/*+ * If symlink is broken, statfs or statvfs will fail.+ * Use its directory path instead.+ */+static wchar_t *+safe_path_for_statfs(struct tree *t)+{+	const wchar_t *path;+	wchar_t *cp, *p = NULL;++	path = tree_current_access_path(t);+	if (tree_current_stat(t) == NULL) {+		p = _wcsdup(path);+		cp = wcsrchr(p, '/');+		if (cp != NULL && wcslen(cp) >= 2) {+			cp[1] = '.';+			cp[2] = '\0';+			path = p;+		}+	} else+		p = _wcsdup(path);+	return (p);+}++/*+ * Get conditions of synthetic and remote on Windows+ */+static int+setup_current_filesystem(struct archive_read_disk *a)+{+	struct tree *t = a->tree;+	wchar_t vol[256];+	wchar_t *path;++	t->current_filesystem->synthetic = -1;/* Not supported */+	path = safe_path_for_statfs(t);+	if (!GetVolumePathNameW(path, vol, sizeof(vol)/sizeof(vol[0]))) {+		free(path);+		t->current_filesystem->remote = -1;+		t->current_filesystem->bytesPerSector = 0;+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+                        "GetVolumePathName failed: %d", (int)GetLastError());+		return (ARCHIVE_FAILED);+	}+	free(path);+	switch (GetDriveTypeW(vol)) {+	case DRIVE_UNKNOWN:+	case DRIVE_NO_ROOT_DIR:+		t->current_filesystem->remote = -1;+		break;+	case DRIVE_REMOTE:+		t->current_filesystem->remote = 1;+		break;+	default:+		t->current_filesystem->remote = 0;+		break;+	}++	if (!GetDiskFreeSpaceW(vol, NULL,+	    &(t->current_filesystem->bytesPerSector), NULL, NULL)) {+		t->current_filesystem->bytesPerSector = 0;+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+                        "GetDiskFreeSpace failed: %d", (int)GetLastError());+		return (ARCHIVE_FAILED);+	}++	return (ARCHIVE_OK);+}++static int+close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt)+{+	HANDLE handle;+	int r = 0;++	if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype)+		return (0);++	/* Close a file descriptor.+	 * It will not be used for SetFileTime() because it has been opened+	 * by a read only mode.+	 */+	if (h != INVALID_HANDLE_VALUE)+		CloseHandle(h);+	if ((t->flags & needsRestoreTimes) == 0)+		return (r);++	handle = CreateFileW(rt->full_path, FILE_WRITE_ATTRIBUTES,+		    0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);+	if (handle == INVALID_HANDLE_VALUE) {+		errno = EINVAL;+		return (-1);+	}++	if (SetFileTime(handle, NULL, &rt->lastAccessTime,+	    &rt->lastWriteTime) == 0) {+		errno = EINVAL;+		r = -1;+	} else+		r = 0;+	CloseHandle(handle);+	return (r);+}++/*+ * Add a directory path to the current stack.+ */+static void+tree_push(struct tree *t, const wchar_t *path, const wchar_t *full_path,+    int filesystem_id, int64_t dev, int64_t ino, struct restore_time *rt)+{+	struct tree_entry *te;++	te = calloc(1, sizeof(*te));+	te->next = t->stack;+	te->parent = t->current;+	if (te->parent)+		te->depth = te->parent->depth + 1;+	t->stack = te;+	archive_string_init(&te->name);+	archive_wstrcpy(&te->name, path);+	archive_string_init(&te->full_path);+	archive_wstrcpy(&te->full_path, full_path);+	te->flags = needsDescent | needsOpen | needsAscent;+	te->filesystem_id = filesystem_id;+	te->dev = dev;+	te->ino = ino;+	te->dirname_length = t->dirname_length;+	te->full_path_dir_length = t->full_path_dir_length;+	te->restore_time.full_path = te->full_path.s;+	if (rt != NULL) {+		te->restore_time.lastWriteTime = rt->lastWriteTime;+		te->restore_time.lastAccessTime = rt->lastAccessTime;+		te->restore_time.filetype = rt->filetype;+	}+}++/*+ * Append a name to the current dir path.+ */+static void+tree_append(struct tree *t, const wchar_t *name, size_t name_length)+{+	size_t size_needed;++	t->path.s[t->dirname_length] = L'\0';+	t->path.length = t->dirname_length;+	/* Strip trailing '/' from name, unless entire name is "/". */+	while (name_length > 1 && name[name_length - 1] == L'/')+		name_length--;++	/* Resize pathname buffer as needed. */+	size_needed = name_length + t->dirname_length + 2;+	archive_wstring_ensure(&t->path, size_needed);+	/* Add a separating '/' if it's needed. */+	if (t->dirname_length > 0 &&+	    t->path.s[archive_strlen(&t->path)-1] != L'/')+		archive_wstrappend_wchar(&t->path, L'/');+	t->basename = t->path.s + archive_strlen(&t->path);+	archive_wstrncat(&t->path, name, name_length);+	t->restore_time.full_path = t->basename;+	if (t->full_path_dir_length > 0) {+		t->full_path.s[t->full_path_dir_length] = L'\0';+		t->full_path.length = t->full_path_dir_length;+		size_needed = name_length + t->full_path_dir_length + 2;+		archive_wstring_ensure(&t->full_path, size_needed);+		/* Add a separating '\' if it's needed. */+		if (t->full_path.s[archive_strlen(&t->full_path)-1] != L'\\')+			archive_wstrappend_wchar(&t->full_path, L'\\');+		archive_wstrncat(&t->full_path, name, name_length);+		t->restore_time.full_path = t->full_path.s;+	}+}++/*+ * Open a directory tree for traversal.+ */+static struct tree *+tree_open(const wchar_t *path, int symlink_mode, int restore_time)+{+	struct tree *t;++	t = calloc(1, sizeof(*t));+	archive_string_init(&(t->full_path));+	archive_string_init(&t->path);+	archive_wstring_ensure(&t->path, 15);+	t->initial_symlink_mode = symlink_mode;+	return (tree_reopen(t, path, restore_time));+}++static struct tree *+tree_reopen(struct tree *t, const wchar_t *path, int restore_time)+{+	struct archive_wstring ws;+	wchar_t *pathname, *p, *base;++	t->flags = (restore_time != 0)?needsRestoreTimes:0;+	t->visit_type = 0;+	t->tree_errno = 0;+	t->full_path_dir_length = 0;+	t->dirname_length = 0;+	t->depth = 0;+	t->descend = 0;+	t->current = NULL;+	t->d = INVALID_HANDLE_VALUE;+	t->symlink_mode = t->initial_symlink_mode;+	archive_string_empty(&(t->full_path));+	archive_string_empty(&t->path);+	t->entry_fh = INVALID_HANDLE_VALUE;+	t->entry_eof = 0;+	t->entry_remaining_bytes = 0;+	t->initial_filesystem_id = -1;++	/* Get wchar_t strings from char strings. */+	archive_string_init(&ws);+	archive_wstrcpy(&ws, path);+	pathname = ws.s;+	/* Get a full-path-name. */+	p = __la_win_permissive_name_w(pathname);+	if (p == NULL)+		goto failed;+	archive_wstrcpy(&(t->full_path), p);+	free(p);++	/* Convert path separators from '\' to '/' */+	for (p = pathname; *p != L'\0'; ++p) {+		if (*p == L'\\')+			*p = L'/';+	}+	base = pathname;++	/* First item is set up a lot like a symlink traversal. */+	/* printf("Looking for wildcard in %s\n", path); */+	if ((base[0] == L'/' && base[1] == L'/' &&+	     base[2] == L'?' && base[3] == L'/' &&+	     (wcschr(base+4, L'*') || wcschr(base+4, L'?'))) ||+	    (!(base[0] == L'/' && base[1] == L'/' &&+	       base[2] == L'?' && base[3] == L'/') &&+	       (wcschr(base, L'*') || wcschr(base, L'?')))) {+		// It has a wildcard in it...+		// Separate the last element.+		p = wcsrchr(base, L'/');+		if (p != NULL) {+			*p = L'\0';+			tree_append(t, base, p - base);+			t->dirname_length = archive_strlen(&t->path);+			base = p + 1;+		}+		p = wcsrchr(t->full_path.s, L'\\');+		if (p != NULL) {+			*p = L'\0';+			t->full_path.length = wcslen(t->full_path.s);+			t->full_path_dir_length = archive_strlen(&t->full_path);+		}+	}+	tree_push(t, base, t->full_path.s, 0, 0, 0, NULL);+	archive_wstring_free(&ws);+	t->stack->flags = needsFirstVisit;+	/*+	 * Debug flag for Direct IO(No buffering) or Async IO.+	 * Those dependent on environment variable switches+	 * will be removed until next release.+	 */+	{+		const char *e;+		if ((e = getenv("LIBARCHIVE_DIRECT_IO")) != NULL) {+			if (e[0] == '0')+				t->direct_io = 0;+			else+				t->direct_io = 1;+			fprintf(stderr, "LIBARCHIVE_DIRECT_IO=%s\n",+				(t->direct_io)?"Enabled":"Disabled");+		} else+			t->direct_io = DIRECT_IO;+		if ((e = getenv("LIBARCHIVE_ASYNC_IO")) != NULL) {+			if (e[0] == '0')+				t->async_io = 0;+			else+				t->async_io = 1;+			fprintf(stderr, "LIBARCHIVE_ASYNC_IO=%s\n",+			    (t->async_io)?"Enabled":"Disabled");+		} else+			t->async_io = ASYNC_IO;+	}+	return (t);+failed:+	archive_wstring_free(&ws);+	tree_free(t);+	return (NULL);+}++static int+tree_descent(struct tree *t)+{+	t->dirname_length = archive_strlen(&t->path);+	t->full_path_dir_length = archive_strlen(&t->full_path);+	t->depth++;+	return (0);+}++/*+ * We've finished a directory; ascend back to the parent.+ */+static int+tree_ascend(struct tree *t)+{+	struct tree_entry *te;++	te = t->stack;+	t->depth--;+	close_and_restore_time(INVALID_HANDLE_VALUE, t, &te->restore_time);+	return (0);+}++/*+ * Pop the working stack.+ */+static void+tree_pop(struct tree *t)+{+	struct tree_entry *te;++	t->full_path.s[t->full_path_dir_length] = L'\0';+	t->full_path.length = t->full_path_dir_length;+	t->path.s[t->dirname_length] = L'\0';+	t->path.length = t->dirname_length;+	if (t->stack == t->current && t->current != NULL)+		t->current = t->current->parent;+	te = t->stack;+	t->stack = te->next;+	t->dirname_length = te->dirname_length;+	t->basename = t->path.s + t->dirname_length;+	t->full_path_dir_length = te->full_path_dir_length;+	while (t->basename[0] == L'/')+		t->basename++;+	archive_wstring_free(&te->name);+	archive_wstring_free(&te->full_path);+	free(te);+}++/*+ * Get the next item in the tree traversal.+ */+static int+tree_next(struct tree *t)+{+	int r;++	while (t->stack != NULL) {+		/* If there's an open dir, get the next entry from there. */+		if (t->d != INVALID_HANDLE_VALUE) {+			r = tree_dir_next_windows(t, NULL);+			if (r == 0)+				continue;+			return (r);+		}++		if (t->stack->flags & needsFirstVisit) {+			wchar_t *d = t->stack->name.s;+			t->stack->flags &= ~needsFirstVisit;+			if (!(d[0] == L'/' && d[1] == L'/' &&+			      d[2] == L'?' && d[3] == L'/') &&+			    (wcschr(d, L'*') || wcschr(d, L'?'))) {+				r = tree_dir_next_windows(t, d);+				if (r == 0)+					continue;+				return (r);+			} else {+				HANDLE h = FindFirstFileW(d, &t->_findData);+				if (h == INVALID_HANDLE_VALUE) {+					la_dosmaperr(GetLastError());+					t->tree_errno = errno;+					t->visit_type = TREE_ERROR_DIR;+					return (t->visit_type);+				}+				t->findData = &t->_findData;+				FindClose(h);+			}+			/* Top stack item needs a regular visit. */+			t->current = t->stack;+			tree_append(t, t->stack->name.s,+			    archive_strlen(&(t->stack->name)));+			//t->dirname_length = t->path_length;+			//tree_pop(t);+			t->stack->flags &= ~needsFirstVisit;+			return (t->visit_type = TREE_REGULAR);+		} else if (t->stack->flags & needsDescent) {+			/* Top stack item is dir to descend into. */+			t->current = t->stack;+			tree_append(t, t->stack->name.s,+			    archive_strlen(&(t->stack->name)));+			t->stack->flags &= ~needsDescent;+			r = tree_descent(t);+			if (r != 0) {+				tree_pop(t);+				t->visit_type = r;+			} else+				t->visit_type = TREE_POSTDESCENT;+			return (t->visit_type);+		} else if (t->stack->flags & needsOpen) {+			t->stack->flags &= ~needsOpen;+			r = tree_dir_next_windows(t, L"*");+			if (r == 0)+				continue;+			return (r);+		} else if (t->stack->flags & needsAscent) {+		        /* Top stack item is dir and we're done with it. */+			r = tree_ascend(t);+			tree_pop(t);+			t->visit_type = r != 0 ? r : TREE_POSTASCENT;+			return (t->visit_type);+		} else {+			/* Top item on stack is dead. */+			tree_pop(t);+			t->flags &= ~hasLstat;+			t->flags &= ~hasStat;+		}+	}+	return (t->visit_type = 0);+}++static int+tree_dir_next_windows(struct tree *t, const wchar_t *pattern)+{+	const wchar_t *name;+	size_t namelen;+	int r;++	for (;;) {+		if (pattern != NULL) {+			struct archive_wstring pt;++			archive_string_init(&pt);+			archive_wstring_ensure(&pt,+			    archive_strlen(&(t->full_path))+			      + 2 + wcslen(pattern));+			archive_wstring_copy(&pt, &(t->full_path));+			archive_wstrappend_wchar(&pt, L'\\');+			archive_wstrcat(&pt, pattern);+			t->d = FindFirstFileW(pt.s, &t->_findData);+			archive_wstring_free(&pt);+			if (t->d == INVALID_HANDLE_VALUE) {+				la_dosmaperr(GetLastError());+				t->tree_errno = errno;+				r = tree_ascend(t); /* Undo "chdir" */+				tree_pop(t);+				t->visit_type = r != 0 ? r : TREE_ERROR_DIR;+				return (t->visit_type);+			}+			t->findData = &t->_findData;+			pattern = NULL;+		} else if (!FindNextFileW(t->d, &t->_findData)) {+			FindClose(t->d);+			t->d = INVALID_HANDLE_VALUE;+			t->findData = NULL;+			return (0);+		}+		name = t->findData->cFileName;+		namelen = wcslen(name);+		t->flags &= ~hasLstat;+		t->flags &= ~hasStat;+		if (name[0] == L'.' && name[1] == L'\0')+			continue;+		if (name[0] == L'.' && name[1] == L'.' && name[2] == L'\0')+			continue;+		tree_append(t, name, namelen);+		return (t->visit_type = TREE_REGULAR);+	}+}++#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)+static void+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)+{+	ULARGE_INTEGER utc;++	utc.HighPart = filetime->dwHighDateTime;+	utc.LowPart  = filetime->dwLowDateTime;+	if (utc.QuadPart >= EPOC_TIME) {+		utc.QuadPart -= EPOC_TIME;+		/* milli seconds base */+		*t = (time_t)(utc.QuadPart / 10000000);+		/* nano seconds base */+		*ns = (long)(utc.QuadPart % 10000000) * 100;+	} else {+		*t = 0;+		*ns = 0;+	}+}++static void+entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path,+	const WIN32_FIND_DATAW *findData,+	const BY_HANDLE_FILE_INFORMATION *bhfi)+{+	time_t secs;+	long nsecs;+	mode_t mode;++	fileTimeToUtc(&bhfi->ftLastAccessTime, &secs, &nsecs);+	archive_entry_set_atime(entry, secs, nsecs);+	fileTimeToUtc(&bhfi->ftLastWriteTime, &secs, &nsecs);+	archive_entry_set_mtime(entry, secs, nsecs);+	fileTimeToUtc(&bhfi->ftCreationTime, &secs, &nsecs);+	archive_entry_set_birthtime(entry, secs, nsecs);+	archive_entry_set_ctime(entry, secs, nsecs);+	archive_entry_set_dev(entry, bhfi_dev(bhfi));+	archive_entry_set_ino64(entry, bhfi_ino(bhfi));+	if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)+		archive_entry_set_nlink(entry, bhfi->nNumberOfLinks + 1);+	else+		archive_entry_set_nlink(entry, bhfi->nNumberOfLinks);+	archive_entry_set_size(entry,+	    (((int64_t)bhfi->nFileSizeHigh) << 32)+	    + bhfi->nFileSizeLow);+	archive_entry_set_uid(entry, 0);+	archive_entry_set_gid(entry, 0);+	archive_entry_set_rdev(entry, 0);++	mode = S_IRUSR | S_IRGRP | S_IROTH;+	if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)+		mode |= S_IWUSR | S_IWGRP | S_IWOTH;+	if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&+	    findData != NULL &&+	    findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) {+		mode |= S_IFLNK;+		entry_symlink_from_pathw(entry, path);+	} else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)+		mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;+	else {+		const wchar_t *p;++		mode |= S_IFREG;+		p = wcsrchr(path, L'.');+		if (p != NULL && wcslen(p) == 4) {+			switch (p[1]) {+			case L'B': case L'b':+				if ((p[2] == L'A' || p[2] == L'a' ) &&+				    (p[3] == L'T' || p[3] == L't' ))+					mode |= S_IXUSR | S_IXGRP | S_IXOTH;+				break;+			case L'C': case L'c':+				if (((p[2] == L'M' || p[2] == L'm' ) &&+				    (p[3] == L'D' || p[3] == L'd' )))+					mode |= S_IXUSR | S_IXGRP | S_IXOTH;+				break;+			case L'E': case L'e':+				if ((p[2] == L'X' || p[2] == L'x' ) &&+				    (p[3] == L'E' || p[3] == L'e' ))+					mode |= S_IXUSR | S_IXGRP | S_IXOTH;+				break;+			default:+				break;+			}+		}+	}+	archive_entry_set_mode(entry, mode);+}++static void+tree_archive_entry_copy_bhfi(struct archive_entry *entry, struct tree *t,+	const BY_HANDLE_FILE_INFORMATION *bhfi)+{+	entry_copy_bhfi(entry, tree_current_path(t), t->findData, bhfi);+}++static int+tree_current_file_information(struct tree *t, BY_HANDLE_FILE_INFORMATION *st,+ int sim_lstat)+{+	HANDLE h;+	int r;+	DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;+	+	if (sim_lstat && tree_current_is_physical_link(t))+		flag |= FILE_FLAG_OPEN_REPARSE_POINT;+	h = CreateFileW(tree_current_access_path(t), 0, FILE_SHARE_READ, NULL,+	    OPEN_EXISTING, flag, NULL);+	if (h == INVALID_HANDLE_VALUE) {+		la_dosmaperr(GetLastError());+		t->tree_errno = errno;+		return (0);+	}+	r = GetFileInformationByHandle(h, st);+	CloseHandle(h);+	return (r);+}++/*+ * Get the stat() data for the entry just returned from tree_next().+ */+static const BY_HANDLE_FILE_INFORMATION *+tree_current_stat(struct tree *t)+{+	if (!(t->flags & hasStat)) {+		if (!tree_current_file_information(t, &t->st, 0))+			return NULL;+		t->flags |= hasStat;+	}+	return (&t->st);+}++/*+ * Get the lstat() data for the entry just returned from tree_next().+ */+static const BY_HANDLE_FILE_INFORMATION *+tree_current_lstat(struct tree *t)+{+	if (!(t->flags & hasLstat)) {+		if (!tree_current_file_information(t, &t->lst, 1))+			return NULL;+		t->flags |= hasLstat;+	}+	return (&t->lst);+}++/*+ * Test whether current entry is a dir or link to a dir.+ */+static int+tree_current_is_dir(struct tree *t)+{+	if (t->findData)+		return (t->findData->dwFileAttributes+		    & FILE_ATTRIBUTE_DIRECTORY);+	return (0);+}++/*+ * Test whether current entry is a physical directory.  Usually, we+ * already have at least one of stat() or lstat() in memory, so we+ * use tricks to try to avoid an extra trip to the disk.+ */+static int+tree_current_is_physical_dir(struct tree *t)+{+	if (tree_current_is_physical_link(t))+		return (0);+	return (tree_current_is_dir(t));+}++/*+ * Test whether current entry is a symbolic link.+ */+static int+tree_current_is_physical_link(struct tree *t)+{+	if (t->findData)+		return ((t->findData->dwFileAttributes+			        & FILE_ATTRIBUTE_REPARSE_POINT) &&+			(t->findData->dwReserved0+			    == IO_REPARSE_TAG_SYMLINK));+	return (0);+}++/*+ * Test whether the same file has been in the tree as its parent.+ */+static int+tree_target_is_same_as_parent(struct tree *t,+    const BY_HANDLE_FILE_INFORMATION *st)+{+	struct tree_entry *te;+	int64_t dev = bhfi_dev(st);+	int64_t ino = bhfi_ino(st);++	for (te = t->current->parent; te != NULL; te = te->parent) {+		if (te->dev == dev && te->ino == ino)+			return (1);+	}+	return (0);+}++/*+ * Return the access path for the entry just returned from tree_next().+ */+static const wchar_t *+tree_current_access_path(struct tree *t)+{+	return (t->full_path.s);+}++/*+ * Return the full path for the entry just returned from tree_next().+ */+static const wchar_t *+tree_current_path(struct tree *t)+{+	return (t->path.s);+}++/*+ * Terminate the traversal.+ */+static void+tree_close(struct tree *t)+{++	if (t == NULL)+		return;+	if (t->entry_fh != INVALID_HANDLE_VALUE) {+		cancel_async(t);+		close_and_restore_time(t->entry_fh, t, &t->restore_time);+		t->entry_fh = INVALID_HANDLE_VALUE;+	}+	/* Close the handle of FindFirstFileW */+	if (t->d != INVALID_HANDLE_VALUE) {+		FindClose(t->d);+		t->d = INVALID_HANDLE_VALUE;+		t->findData = NULL;+	}+	/* Release anything remaining in the stack. */+	while (t->stack != NULL)+		tree_pop(t);+}++/*+ * Release any resources.+ */+static void+tree_free(struct tree *t)+{+	int i;++	if (t == NULL)+		return;+	archive_wstring_free(&t->path);+	archive_wstring_free(&t->full_path);+	free(t->sparse_list);+	free(t->filesystem_table);+	for (i = 0; i < MAX_OVERLAPPED; i++) {+		if (t->ol[i].buff)+			VirtualFree(t->ol[i].buff, 0, MEM_RELEASE);+		CloseHandle(t->ol[i].ol.hEvent);+	}+	free(t);+}+++/*+ * Populate the archive_entry with metadata from the disk.+ */+int+archive_read_disk_entry_from_file(struct archive *_a,+    struct archive_entry *entry, int fd, const struct stat *st)+{+	struct archive_read_disk *a = (struct archive_read_disk *)_a;+	const wchar_t *path;+	const wchar_t *wname;+	const char *name;+	HANDLE h;+	BY_HANDLE_FILE_INFORMATION bhfi;+	DWORD fileAttributes = 0;+	int r;++	archive_clear_error(_a);+	wname = archive_entry_sourcepath_w(entry);+	if (wname == NULL)+		wname = archive_entry_pathname_w(entry);+	if (wname == NULL) {+		archive_set_error(&a->archive, EINVAL,+		    "Can't get a wide character version of the path");+		return (ARCHIVE_FAILED);+	}+	path = __la_win_permissive_name_w(wname);++	if (st == NULL) {+		/*+		 * Get metadata through GetFileInformationByHandle().+		 */+		if (fd >= 0) {+			h = (HANDLE)_get_osfhandle(fd);+			r = GetFileInformationByHandle(h, &bhfi);+			if (r == 0) {+				la_dosmaperr(GetLastError());+				archive_set_error(&a->archive, errno,+				    "Can't GetFileInformationByHandle");+				return (ARCHIVE_FAILED);+			}+			entry_copy_bhfi(entry, path, NULL, &bhfi);+		} else {+			WIN32_FIND_DATAW findData;+			DWORD flag, desiredAccess;+	+			h = FindFirstFileW(path, &findData);+			if (h == INVALID_HANDLE_VALUE) {+				la_dosmaperr(GetLastError());+				archive_set_error(&a->archive, errno,+				    "Can't FindFirstFileW");+				return (ARCHIVE_FAILED);+			}+			FindClose(h);++			flag = FILE_FLAG_BACKUP_SEMANTICS;+			if (!a->follow_symlinks &&+			    (findData.dwFileAttributes+			      & FILE_ATTRIBUTE_REPARSE_POINT) &&+				  (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {+				flag |= FILE_FLAG_OPEN_REPARSE_POINT;+				desiredAccess = 0;+			} else if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {+				desiredAccess = 0;+			} else+				desiredAccess = GENERIC_READ;++			h = CreateFileW(path, desiredAccess, FILE_SHARE_READ, NULL,+			    OPEN_EXISTING, flag, NULL);+			if (h == INVALID_HANDLE_VALUE) {+				la_dosmaperr(GetLastError());+				archive_set_error(&a->archive, errno,+				    "Can't CreateFileW");+				return (ARCHIVE_FAILED);+			}+			r = GetFileInformationByHandle(h, &bhfi);+			if (r == 0) {+				la_dosmaperr(GetLastError());+				archive_set_error(&a->archive, errno,+				    "Can't GetFileInformationByHandle");+				CloseHandle(h);+				return (ARCHIVE_FAILED);+			}+			entry_copy_bhfi(entry, path, &findData, &bhfi);+		}+		fileAttributes = bhfi.dwFileAttributes;+	} else {+		archive_entry_copy_stat(entry, st);+		if (st->st_mode & S_IFLNK)+			entry_symlink_from_pathw(entry, path);+		h = INVALID_HANDLE_VALUE;+	}++	/* Lookup uname/gname */+	name = archive_read_disk_uname(_a, archive_entry_uid(entry));+	if (name != NULL)+		archive_entry_copy_uname(entry, name);+	name = archive_read_disk_gname(_a, archive_entry_gid(entry));+	if (name != NULL)+		archive_entry_copy_gname(entry, name);++	/*+	 * File attributes+	 */+	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {+		const int supported_attrs =+		    FILE_ATTRIBUTE_READONLY |+		    FILE_ATTRIBUTE_HIDDEN |+		    FILE_ATTRIBUTE_SYSTEM;+		DWORD file_attrs = fileAttributes & supported_attrs;+		if (file_attrs != 0)+			archive_entry_set_fflags(entry, file_attrs, 0);+	}++	/*+	 * Can this file be sparse file ?+	 */+	if (archive_entry_filetype(entry) != AE_IFREG+	    || archive_entry_size(entry) <= 0+		|| archive_entry_hardlink(entry) != NULL) {+		if (h != INVALID_HANDLE_VALUE && fd < 0)+			CloseHandle(h);+		return (ARCHIVE_OK);+	}++	if (h == INVALID_HANDLE_VALUE) {+		if (fd >= 0) {+			h = (HANDLE)_get_osfhandle(fd);+		} else {+			h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL,+			    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);+			if (h == INVALID_HANDLE_VALUE) {+				la_dosmaperr(GetLastError());+				archive_set_error(&a->archive, errno,+				    "Can't CreateFileW");+				return (ARCHIVE_FAILED);+			}+		}+		r = GetFileInformationByHandle(h, &bhfi);+		if (r == 0) {+			la_dosmaperr(GetLastError());+			archive_set_error(&a->archive, errno,+			    "Can't GetFileInformationByHandle");+			if (h != INVALID_HANDLE_VALUE && fd < 0)+				CloseHandle(h);+			return (ARCHIVE_FAILED);+		}+		fileAttributes = bhfi.dwFileAttributes;+	}++	/* Sparse file must be set a mark, FILE_ATTRIBUTE_SPARSE_FILE */+	if ((fileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {+		if (fd < 0)+			CloseHandle(h);+		return (ARCHIVE_OK);+	}++	r = setup_sparse_from_disk(a, entry, h);+	if (fd < 0)+		CloseHandle(h);++	return (r);+}++/*+ * Windows sparse interface.+ */+#if defined(__MINGW32__) && !defined(FSCTL_QUERY_ALLOCATED_RANGES)+#define FSCTL_QUERY_ALLOCATED_RANGES 0x940CF+typedef struct {+	LARGE_INTEGER FileOffset;+	LARGE_INTEGER Length;+} FILE_ALLOCATED_RANGE_BUFFER;+#endif++static int+setup_sparse_from_disk(struct archive_read_disk *a,+    struct archive_entry *entry, HANDLE handle)+{+	FILE_ALLOCATED_RANGE_BUFFER range, *outranges = NULL;+	size_t outranges_size;+	int64_t entry_size = archive_entry_size(entry);+	int exit_sts = ARCHIVE_OK;++	range.FileOffset.QuadPart = 0;+	range.Length.QuadPart = entry_size;+	outranges_size = 2048;+	outranges = (FILE_ALLOCATED_RANGE_BUFFER *)malloc(outranges_size);+	if (outranges == NULL) {+		archive_set_error(&a->archive, ENOMEM,+			"Couldn't allocate memory");+		exit_sts = ARCHIVE_FATAL;+		goto exit_setup_sparse;+	}++	for (;;) {+		DWORD retbytes;+		BOOL ret;++		for (;;) {+			ret = DeviceIoControl(handle,+			    FSCTL_QUERY_ALLOCATED_RANGES,+			    &range, sizeof(range), outranges,+			    (DWORD)outranges_size, &retbytes, NULL);+			if (ret == 0 && GetLastError() == ERROR_MORE_DATA) {+				free(outranges);+				outranges_size *= 2;+				outranges = (FILE_ALLOCATED_RANGE_BUFFER *)+				    malloc(outranges_size);+				if (outranges == NULL) {+					archive_set_error(&a->archive, ENOMEM,+					    "Couldn't allocate memory");+					exit_sts = ARCHIVE_FATAL;+					goto exit_setup_sparse;+				}+				continue;+			} else+				break;+		}+		if (ret != 0) {+			if (retbytes > 0) {+				DWORD i, n;++				n = retbytes / sizeof(outranges[0]);+				if (n == 1 &&+				    outranges[0].FileOffset.QuadPart == 0 &&+				    outranges[0].Length.QuadPart == entry_size)+					break;/* This is not sparse. */+				for (i = 0; i < n; i++)+					archive_entry_sparse_add_entry(entry,+					    outranges[i].FileOffset.QuadPart,+						outranges[i].Length.QuadPart);+				range.FileOffset.QuadPart =+				    outranges[n-1].FileOffset.QuadPart+				    + outranges[n-1].Length.QuadPart;+				range.Length.QuadPart =+				    entry_size - range.FileOffset.QuadPart;+				if (range.Length.QuadPart > 0)+					continue;+			} else {+				/* The entire file is a hole. Add one data block of size 0 at the end. */+				archive_entry_sparse_add_entry(entry,+				    entry_size,+				    0);+			}+			break;+		} else {+			la_dosmaperr(GetLastError());+			archive_set_error(&a->archive, errno,+			    "DeviceIoControl Failed: %lu", GetLastError());+			exit_sts = ARCHIVE_FAILED;+			goto exit_setup_sparse;+		}+	}+exit_setup_sparse:+	free(outranges);++	return (exit_sts);+}++#endif
+ c/archive_read_extract.c view
@@ -0,0 +1,60 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.61 2008/05/26 17:00:22 kientzle Exp $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_private.h"++int+archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags)+{+	struct archive_read_extract *extract;+	struct archive_read * a = (struct archive_read *)_a;++	extract = __archive_read_get_extract(a);+	if (extract == NULL)+		return (ARCHIVE_FATAL);++	/* If we haven't initialized the archive_write_disk object, do it now. */+	if (extract->ad == NULL) {+		extract->ad = archive_write_disk_new();+		if (extract->ad == NULL) {+			archive_set_error(&a->archive, ENOMEM, "Can't extract");+			return (ARCHIVE_FATAL);+		}+		archive_write_disk_set_standard_lookup(extract->ad);+	}++	archive_write_disk_set_options(extract->ad, flags);+	return (archive_read_extract2(&a->archive, entry, extract->ad));+}
+ c/archive_read_extract2.c view
@@ -0,0 +1,155 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.61 2008/05/26 17:00:22 kientzle Exp $");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_private.h"++static int	copy_data(struct archive *ar, struct archive *aw);+static int	archive_read_extract_cleanup(struct archive_read *);+++/* Retrieve an extract object without initialising the associated+ * archive_write_disk object.+ */+struct archive_read_extract *+__archive_read_get_extract(struct archive_read *a)+{+	if (a->extract == NULL) {+		a->extract = (struct archive_read_extract *)calloc(1, sizeof(*a->extract));+		if (a->extract == NULL) {+			archive_set_error(&a->archive, ENOMEM, "Can't extract");+			return (NULL);+		}+		a->cleanup_archive_extract = archive_read_extract_cleanup;+	}+	return (a->extract);+}++/*+ * Cleanup function for archive_extract.+ */+static int+archive_read_extract_cleanup(struct archive_read *a)+{+	int ret = ARCHIVE_OK;++	if (a->extract->ad != NULL) {+		ret = archive_write_free(a->extract->ad);+	}+	free(a->extract);+	a->extract = NULL;+	return (ret);+}++int+archive_read_extract2(struct archive *_a, struct archive_entry *entry,+    struct archive *ad)+{+	struct archive_read *a = (struct archive_read *)_a;+	int r, r2;++	/* Set up for this particular entry. */+	if (a->skip_file_set)+		archive_write_disk_set_skip_file(ad,+		    a->skip_file_dev, a->skip_file_ino);+	r = archive_write_header(ad, entry);+	if (r < ARCHIVE_WARN)+		r = ARCHIVE_WARN;+	if (r != ARCHIVE_OK)+		/* If _write_header failed, copy the error. */+ 		archive_copy_error(&a->archive, ad);+	else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0)+		/* Otherwise, pour data into the entry. */+		r = copy_data(_a, ad);+	r2 = archive_write_finish_entry(ad);+	if (r2 < ARCHIVE_WARN)+		r2 = ARCHIVE_WARN;+	/* Use the first message. */+	if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)+		archive_copy_error(&a->archive, ad);+	/* Use the worst error return. */+	if (r2 < r)+		r = r2;+	return (r);+}++void+archive_read_extract_set_progress_callback(struct archive *_a,+    void (*progress_func)(void *), void *user_data)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_extract *extract = __archive_read_get_extract(a);+	if (extract != NULL) {+		extract->extract_progress = progress_func;+		extract->extract_progress_user_data = user_data;+	}+}++static int+copy_data(struct archive *ar, struct archive *aw)+{+	int64_t offset;+	const void *buff;+	struct archive_read_extract *extract;+	size_t size;+	int r;++	extract = __archive_read_get_extract((struct archive_read *)ar);+	if (extract == NULL)+		return (ARCHIVE_FATAL);+	for (;;) {+		r = archive_read_data_block(ar, &buff, &size, &offset);+		if (r == ARCHIVE_EOF)+			return (ARCHIVE_OK);+		if (r != ARCHIVE_OK)+			return (r);+		r = (int)archive_write_data_block(aw, buff, size, offset);+		if (r < ARCHIVE_WARN)+			r = ARCHIVE_WARN;+		if (r < ARCHIVE_OK) {+			archive_set_error(ar, archive_errno(aw),+			    "%s", archive_error_string(aw));+			return (r);+		}+		if (extract->extract_progress)+			(extract->extract_progress)+			    (extract->extract_progress_user_data);+	}+}
+ c/archive_read_open_fd.c view
@@ -0,0 +1,211 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_fd.c 201103 2009-12-28 03:13:49Z kientzle $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_IO_H+#include <io.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"++struct read_fd_data {+	int	 fd;+	size_t	 block_size;+	char	 use_lseek;+	void	*buffer;+};++static int	file_close(struct archive *, void *);+static ssize_t	file_read(struct archive *, void *, const void **buff);+static int64_t	file_seek(struct archive *, void *, int64_t request, int);+static int64_t	file_skip(struct archive *, void *, int64_t request);++int+archive_read_open_fd(struct archive *a, int fd, size_t block_size)+{+	struct stat st;+	struct read_fd_data *mine;+	void *b;++	archive_clear_error(a);+	if (fstat(fd, &st) != 0) {+		archive_set_error(a, errno, "Can't stat fd %d", fd);+		return (ARCHIVE_FATAL);+	}++	mine = (struct read_fd_data *)calloc(1, sizeof(*mine));+	b = malloc(block_size);+	if (mine == NULL || b == NULL) {+		archive_set_error(a, ENOMEM, "No memory");+		free(mine);+		free(b);+		return (ARCHIVE_FATAL);+	}+	mine->block_size = block_size;+	mine->buffer = b;+	mine->fd = fd;+	/*+	 * Skip support is a performance optimization for anything+	 * that supports lseek().  On FreeBSD, only regular files and+	 * raw disk devices support lseek() and there's no portable+	 * way to determine if a device is a raw disk device, so we+	 * only enable this optimization for regular files.+	 */+	if (S_ISREG(st.st_mode)) {+		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);+		mine->use_lseek = 1;+	}+#if defined(__CYGWIN__) || defined(_WIN32)+	setmode(mine->fd, O_BINARY);+#endif++	archive_read_set_read_callback(a, file_read);+	archive_read_set_skip_callback(a, file_skip);+	archive_read_set_seek_callback(a, file_seek);+	archive_read_set_close_callback(a, file_close);+	archive_read_set_callback_data(a, mine);+	return (archive_read_open1(a));+}++static ssize_t+file_read(struct archive *a, void *client_data, const void **buff)+{+	struct read_fd_data *mine = (struct read_fd_data *)client_data;+	ssize_t bytes_read;++	*buff = mine->buffer;+	for (;;) {+		bytes_read = read(mine->fd, mine->buffer, mine->block_size);+		if (bytes_read < 0) {+			if (errno == EINTR)+				continue;+			archive_set_error(a, errno, "Error reading fd %d",+			    mine->fd);+		}+		return (bytes_read);+	}+}++static int64_t+file_skip(struct archive *a, void *client_data, int64_t request)+{+	struct read_fd_data *mine = (struct read_fd_data *)client_data;+	int64_t skip = request;+	int64_t old_offset, new_offset;+	int skip_bits = sizeof(skip) * 8 - 1;  /* off_t is a signed type. */++	if (!mine->use_lseek)+		return (0);++	/* Reduce a request that would overflow the 'skip' variable. */+	if (sizeof(request) > sizeof(skip)) {+		int64_t max_skip =+		    (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;+		if (request > max_skip)+			skip = max_skip;+	}++	/* Reduce request to the next smallest multiple of block_size */+	request = (request / mine->block_size) * mine->block_size;+	if (request == 0)+		return (0);++	if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0) &&+	    ((new_offset = lseek(mine->fd, skip, SEEK_CUR)) >= 0))+		return (new_offset - old_offset);++	/* If seek failed once, it will probably fail again. */+	mine->use_lseek = 0;++	/* Let libarchive recover with read+discard. */+	if (errno == ESPIPE)+		return (0);++	/*+	 * There's been an error other than ESPIPE. This is most+	 * likely caused by a programmer error (too large request)+	 * or a corrupted archive file.+	 */+	archive_set_error(a, errno, "Error seeking");+	return (-1);+}++/*+ * TODO: Store the offset and use it in the read callback.+ */+static int64_t+file_seek(struct archive *a, void *client_data, int64_t request, int whence)+{+	struct read_fd_data *mine = (struct read_fd_data *)client_data;+	int64_t r;++	/* We use off_t here because lseek() is declared that way. */+	/* See above for notes about when off_t is less than 64 bits. */+	r = lseek(mine->fd, request, whence);+	if (r >= 0)+		return r;++	if (errno == ESPIPE) {+		archive_set_error(a, errno,+		    "A file descriptor(%d) is not seekable(PIPE)", mine->fd);+		return (ARCHIVE_FAILED);+	} else {+		/* If the input is corrupted or truncated, fail. */+		archive_set_error(a, errno,+		    "Error seeking in a file descriptor(%d)", mine->fd);+		return (ARCHIVE_FATAL);+	}+}++static int+file_close(struct archive *a, void *client_data)+{+	struct read_fd_data *mine = (struct read_fd_data *)client_data;++	(void)a; /* UNUSED */+	free(mine->buffer);+	free(mine);+	return (ARCHIVE_OK);+}
+ c/archive_read_open_file.c view
@@ -0,0 +1,180 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_file.c 201093 2009-12-28 02:28:44Z kientzle $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_IO_H+#include <io.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"++struct read_FILE_data {+	FILE    *f;+	size_t	 block_size;+	void	*buffer;+	char	 can_skip;+};++static int	file_close(struct archive *, void *);+static ssize_t	file_read(struct archive *, void *, const void **buff);+static int64_t	file_skip(struct archive *, void *, int64_t request);++int+archive_read_open_FILE(struct archive *a, FILE *f)+{+	struct stat st;+	struct read_FILE_data *mine;+	size_t block_size = 128 * 1024;+	void *b;++	archive_clear_error(a);+	mine = (struct read_FILE_data *)malloc(sizeof(*mine));+	b = malloc(block_size);+	if (mine == NULL || b == NULL) {+		archive_set_error(a, ENOMEM, "No memory");+		free(mine);+		free(b);+		return (ARCHIVE_FATAL);+	}+	mine->block_size = block_size;+	mine->buffer = b;+	mine->f = f;+	/*+	 * If we can't fstat() the file, it may just be that it's not+	 * a file.  (On some platforms, FILE * objects can wrap I/O+	 * streams that don't support fileno()).  As a result, fileno()+	 * should be used cautiously.)+	 */+	if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) {+		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);+		/* Enable the seek optimization only for regular files. */+		mine->can_skip = 1;+	} else+		mine->can_skip = 0;++#if defined(__CYGWIN__) || defined(_WIN32)+	setmode(fileno(mine->f), O_BINARY);+#endif++	archive_read_set_read_callback(a, file_read);+	archive_read_set_skip_callback(a, file_skip);+	archive_read_set_close_callback(a, file_close);+	archive_read_set_callback_data(a, mine);+	return (archive_read_open1(a));+}++static ssize_t+file_read(struct archive *a, void *client_data, const void **buff)+{+	struct read_FILE_data *mine = (struct read_FILE_data *)client_data;+	size_t bytes_read;++	*buff = mine->buffer;+	bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f);+	if (bytes_read < mine->block_size && ferror(mine->f)) {+		archive_set_error(a, errno, "Error reading file");+	}+	return (bytes_read);+}++static int64_t+file_skip(struct archive *a, void *client_data, int64_t request)+{+	struct read_FILE_data *mine = (struct read_FILE_data *)client_data;+#if HAVE_FSEEKO+	off_t skip = (off_t)request;+#elif HAVE__FSEEKI64+	int64_t skip = request;+#else+	long skip = (long)request;+#endif+	int skip_bits = sizeof(skip) * 8 - 1;++	(void)a; /* UNUSED */++	/*+	 * If we can't skip, return 0 as the amount we did step and+	 * the caller will work around by reading and discarding.+	 */+	if (!mine->can_skip)+		return (0);+	if (request == 0)+		return (0);++	/* If request is too big for a long or an off_t, reduce it. */+	if (sizeof(request) > sizeof(skip)) {+		int64_t max_skip =+		    (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;+		if (request > max_skip)+			skip = max_skip;+	}++#ifdef __ANDROID__+        /* fileno() isn't safe on all platforms ... see above. */+	if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0)+#elif HAVE_FSEEKO+	if (fseeko(mine->f, skip, SEEK_CUR) != 0)+#elif HAVE__FSEEKI64+	if (_fseeki64(mine->f, skip, SEEK_CUR) != 0)+#else+	if (fseek(mine->f, skip, SEEK_CUR) != 0)+#endif+	{+		mine->can_skip = 0;+		return (0);+	}+	return (request);+}++static int+file_close(struct archive *a, void *client_data)+{+	struct read_FILE_data *mine = (struct read_FILE_data *)client_data;++	(void)a; /* UNUSED */+	free(mine->buffer);+	free(mine);+	return (ARCHIVE_OK);+}
+ c/archive_read_open_filename.c view
@@ -0,0 +1,586 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_filename.c 201093 2009-12-28 02:28:44Z kientzle $");++#ifdef HAVE_SYS_IOCTL_H+#include <sys/ioctl.h>+#endif+#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_IO_H+#include <io.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)+#include <sys/disk.h>+#elif defined(__NetBSD__) || defined(__OpenBSD__)+#include <sys/disklabel.h>+#include <sys/dkio.h>+#elif defined(__DragonFly__)+#include <sys/diskslice.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_string.h"++#ifndef O_BINARY+#define O_BINARY 0+#endif+#ifndef O_CLOEXEC+#define O_CLOEXEC	0+#endif++struct read_file_data {+	int	 fd;+	size_t	 block_size;+	void	*buffer;+	mode_t	 st_mode;  /* Mode bits for opened file. */+	char	 use_lseek;+	enum fnt_e { FNT_STDIN, FNT_MBS, FNT_WCS } filename_type;+	union {+		char	 m[1];/* MBS filename. */+		wchar_t	 w[1];/* WCS filename. */+	} filename; /* Must be last! */+};++static int	file_open(struct archive *, void *);+static int	file_close(struct archive *, void *);+static int file_close2(struct archive *, void *);+static int file_switch(struct archive *, void *, void *);+static ssize_t	file_read(struct archive *, void *, const void **buff);+static int64_t	file_seek(struct archive *, void *, int64_t request, int);+static int64_t	file_skip(struct archive *, void *, int64_t request);+static int64_t	file_skip_lseek(struct archive *, void *, int64_t request);++int+archive_read_open_file(struct archive *a, const char *filename,+    size_t block_size)+{+	return (archive_read_open_filename(a, filename, block_size));+}++int+archive_read_open_filename(struct archive *a, const char *filename,+    size_t block_size)+{+	const char *filenames[2];+	filenames[0] = filename;+	filenames[1] = NULL;+	return archive_read_open_filenames(a, filenames, block_size);+}++int+archive_read_open_filenames(struct archive *a, const char **filenames,+    size_t block_size)+{+	struct read_file_data *mine;+	const char *filename = NULL;+	if (filenames)+		filename = *(filenames++);++	archive_clear_error(a);+	do+	{+		if (filename == NULL)+			filename = "";+		mine = (struct read_file_data *)calloc(1,+			sizeof(*mine) + strlen(filename));+		if (mine == NULL)+			goto no_memory;+		strcpy(mine->filename.m, filename);+		mine->block_size = block_size;+		mine->fd = -1;+		mine->buffer = NULL;+		mine->st_mode = mine->use_lseek = 0;+		if (filename == NULL || filename[0] == '\0') {+			mine->filename_type = FNT_STDIN;+		} else+			mine->filename_type = FNT_MBS;+		if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK))+			return (ARCHIVE_FATAL);+		if (filenames == NULL)+			break;+		filename = *(filenames++);+	} while (filename != NULL && filename[0] != '\0');+	archive_read_set_open_callback(a, file_open);+	archive_read_set_read_callback(a, file_read);+	archive_read_set_skip_callback(a, file_skip);+	archive_read_set_close_callback(a, file_close);+	archive_read_set_switch_callback(a, file_switch);+	archive_read_set_seek_callback(a, file_seek);++	return (archive_read_open1(a));+no_memory:+	archive_set_error(a, ENOMEM, "No memory");+	return (ARCHIVE_FATAL);+}++int+archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename,+    size_t block_size)+{+	struct read_file_data *mine = (struct read_file_data *)calloc(1,+		sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t));+	if (!mine)+	{+		archive_set_error(a, ENOMEM, "No memory");+		return (ARCHIVE_FATAL);+	}+	mine->fd = -1;+	mine->block_size = block_size;++	if (wfilename == NULL || wfilename[0] == L'\0') {+		mine->filename_type = FNT_STDIN;+	} else {+#if defined(_WIN32) && !defined(__CYGWIN__)+		mine->filename_type = FNT_WCS;+		wcscpy(mine->filename.w, wfilename);+#else+		/*+		 * POSIX system does not support a wchar_t interface for+		 * open() system call, so we have to translate a wchar_t+		 * filename to multi-byte one and use it.+		 */+		struct archive_string fn;++		archive_string_init(&fn);+		if (archive_string_append_from_wcs(&fn, wfilename,+		    wcslen(wfilename)) != 0) {+			if (errno == ENOMEM)+				archive_set_error(a, errno,+				    "Can't allocate memory");+			else+				archive_set_error(a, EINVAL,+				    "Failed to convert a wide-character"+				    " filename to a multi-byte filename");+			archive_string_free(&fn);+			free(mine);+			return (ARCHIVE_FATAL);+		}+		mine->filename_type = FNT_MBS;+		strcpy(mine->filename.m, fn.s);+		archive_string_free(&fn);+#endif+	}+	if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK))+		return (ARCHIVE_FATAL);+	archive_read_set_open_callback(a, file_open);+	archive_read_set_read_callback(a, file_read);+	archive_read_set_skip_callback(a, file_skip);+	archive_read_set_close_callback(a, file_close);+	archive_read_set_switch_callback(a, file_switch);+	archive_read_set_seek_callback(a, file_seek);++	return (archive_read_open1(a));+}++static int+file_open(struct archive *a, void *client_data)+{+	struct stat st;+	struct read_file_data *mine = (struct read_file_data *)client_data;+	void *buffer;+	const char *filename = NULL;+#if defined(_WIN32) && !defined(__CYGWIN__)+	const wchar_t *wfilename = NULL;+#endif+	int fd = -1;+	int is_disk_like = 0;+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)+	off_t mediasize = 0; /* FreeBSD-specific, so off_t okay here. */+#elif defined(__NetBSD__) || defined(__OpenBSD__)+	struct disklabel dl;+#elif defined(__DragonFly__)+	struct partinfo pi;+#endif++	archive_clear_error(a);+	if (mine->filename_type == FNT_STDIN) {+		/* We used to delegate stdin support by+		 * directly calling archive_read_open_fd(a,0,block_size)+		 * here, but that doesn't (and shouldn't) handle the+		 * end-of-file flush when reading stdout from a pipe.+		 * Basically, read_open_fd() is intended for folks who+		 * are willing to handle such details themselves.  This+		 * API is intended to be a little smarter for folks who+		 * want easy handling of the common case.+		 */+		fd = 0;+#if defined(__CYGWIN__) || defined(_WIN32)+		setmode(0, O_BINARY);+#endif+		filename = "";+	} else if (mine->filename_type == FNT_MBS) {+		filename = mine->filename.m;+		fd = open(filename, O_RDONLY | O_BINARY | O_CLOEXEC);+		__archive_ensure_cloexec_flag(fd);+		if (fd < 0) {+			archive_set_error(a, errno,+			    "Failed to open '%s'", filename);+			return (ARCHIVE_FATAL);+		}+	} else {+#if defined(_WIN32) && !defined(__CYGWIN__)+		wfilename = mine->filename.w;+		fd = _wopen(wfilename, O_RDONLY | O_BINARY);+		if (fd < 0 && errno == ENOENT) {+			wchar_t *fullpath;+			fullpath = __la_win_permissive_name_w(wfilename);+			if (fullpath != NULL) {+				fd = _wopen(fullpath, O_RDONLY | O_BINARY);+				free(fullpath);+			}+		}+		if (fd < 0) {+			archive_set_error(a, errno,+			    "Failed to open '%S'", wfilename);+			return (ARCHIVE_FATAL);+		}+#else+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Unexpedted operation in archive_read_open_filename");+		goto fail;+#endif+	}+	if (fstat(fd, &st) != 0) {+#if defined(_WIN32) && !defined(__CYGWIN__)+		if (mine->filename_type == FNT_WCS)+			archive_set_error(a, errno, "Can't stat '%S'",+			    wfilename);+		else+#endif+			archive_set_error(a, errno, "Can't stat '%s'",+			    filename);+		goto fail;+	}++	/*+	 * Determine whether the input looks like a disk device or a+	 * tape device.  The results are used below to select an I/O+	 * strategy:+	 *  = "disk-like" devices support arbitrary lseek() and will+	 *    support I/O requests of any size.  So we get easy skipping+	 *    and can cheat on block sizes to get better performance.+	 *  = "tape-like" devices require strict blocking and use+	 *    specialized ioctls for seeking.+	 *  = "socket-like" devices cannot seek at all but can improve+	 *    performance by using nonblocking I/O to read "whatever is+	 *    available right now".+	 *+	 * Right now, we only specially recognize disk-like devices,+	 * but it should be straightforward to add probes and strategy+	 * here for tape-like and socket-like devices.+	 */+	if (S_ISREG(st.st_mode)) {+		/* Safety:  Tell the extractor not to overwrite the input. */+		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);+		/* Regular files act like disks. */+		is_disk_like = 1;+	}+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)+	/* FreeBSD: if it supports DIOCGMEDIASIZE ioctl, it's disk-like. */+	else if (S_ISCHR(st.st_mode) &&+	    ioctl(fd, DIOCGMEDIASIZE, &mediasize) == 0 &&+	    mediasize > 0) {+		is_disk_like = 1;+	}+#elif defined(__NetBSD__) || defined(__OpenBSD__)+	/* Net/OpenBSD: if it supports DIOCGDINFO ioctl, it's disk-like. */+	else if ((S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) &&+	    ioctl(fd, DIOCGDINFO, &dl) == 0 &&+	    dl.d_partitions[DISKPART(st.st_rdev)].p_size > 0) {+		is_disk_like = 1;+	}+#elif defined(__DragonFly__)+	/* DragonFly BSD:  if it supports DIOCGPART ioctl, it's disk-like. */+	else if (S_ISCHR(st.st_mode) &&+	    ioctl(fd, DIOCGPART, &pi) == 0 &&+	    pi.media_size > 0) {+		is_disk_like = 1;+	}+#elif defined(__linux__)+	/* Linux:  All block devices are disk-like. */+	else if (S_ISBLK(st.st_mode) &&+	    lseek(fd, 0, SEEK_CUR) == 0 &&+	    lseek(fd, 0, SEEK_SET) == 0 &&+	    lseek(fd, 0, SEEK_END) > 0 &&+	    lseek(fd, 0, SEEK_SET) == 0) {+		is_disk_like = 1;+	}+#endif+	/* TODO: Add an "is_tape_like" variable and appropriate tests. */++	/* Disk-like devices prefer power-of-two block sizes.  */+	/* Use provided block_size as a guide so users have some control. */+	if (is_disk_like) {+		size_t new_block_size = 64 * 1024;+		while (new_block_size < mine->block_size+		    && new_block_size < 64 * 1024 * 1024)+			new_block_size *= 2;+		mine->block_size = new_block_size;+	}+	buffer = malloc(mine->block_size);+	if (buffer == NULL) {+		archive_set_error(a, ENOMEM, "No memory");+		goto fail;+	}+	mine->buffer = buffer;+	mine->fd = fd;+	/* Remember mode so close can decide whether to flush. */+	mine->st_mode = st.st_mode;++	/* Disk-like inputs can use lseek(). */+	if (is_disk_like)+		mine->use_lseek = 1;++	return (ARCHIVE_OK);+fail:+	/*+	 * Don't close file descriptors not opened or ones pointing referring+	 * to `FNT_STDIN`.+	 */+	if (fd != -1 && fd != 0)+		close(fd);+	return (ARCHIVE_FATAL);+}++static ssize_t+file_read(struct archive *a, void *client_data, const void **buff)+{+	struct read_file_data *mine = (struct read_file_data *)client_data;+	ssize_t bytes_read;++	/* TODO: If a recent lseek() operation has left us+	 * mis-aligned, read and return a short block to try to get+	 * us back in alignment. */++	/* TODO: Someday, try mmap() here; if that succeeds, give+	 * the entire file to libarchive as a single block.  That+	 * could be a lot faster than block-by-block manual I/O. */++	/* TODO: We might be able to improve performance on pipes and+	 * sockets by setting non-blocking I/O and just accepting+	 * whatever we get here instead of waiting for a full block+	 * worth of data. */++	*buff = mine->buffer;+	for (;;) {+		bytes_read = read(mine->fd, mine->buffer, mine->block_size);+		if (bytes_read < 0) {+			if (errno == EINTR)+				continue;+			else if (mine->filename_type == FNT_STDIN)+				archive_set_error(a, errno,+				    "Error reading stdin");+			else if (mine->filename_type == FNT_MBS)+				archive_set_error(a, errno,+				    "Error reading '%s'", mine->filename.m);+			else+				archive_set_error(a, errno,+				    "Error reading '%S'", mine->filename.w);+		}+		return (bytes_read);+	}+}++/*+ * Regular files and disk-like block devices can use simple lseek+ * without needing to round the request to the block size.+ *+ * TODO: This can leave future reads mis-aligned.  Since we know the+ * offset here, we should store it and use it in file_read() above+ * to determine whether we should perform a short read to get back+ * into alignment.  Long series of mis-aligned reads can negatively+ * impact disk throughput.  (Of course, the performance impact should+ * be carefully tested; extra code complexity is only worthwhile if+ * it does provide measurable improvement.)+ *+ * TODO: Be lazy about the actual seek.  There are a few pathological+ * cases where libarchive makes a bunch of seek requests in a row+ * without any intervening reads.  This isn't a huge performance+ * problem, since the kernel handles seeks lazily already, but+ * it would be very slightly faster if we simply remembered the+ * seek request here and then actually performed the seek at the+ * top of the read callback above.+ */+static int64_t+file_skip_lseek(struct archive *a, void *client_data, int64_t request)+{+	struct read_file_data *mine = (struct read_file_data *)client_data;+#if defined(_WIN32) && !defined(__CYGWIN__)+	/* We use _lseeki64() on Windows. */+	int64_t old_offset, new_offset;+#else+	off_t old_offset, new_offset;+#endif++	/* We use off_t here because lseek() is declared that way. */++	/* TODO: Deal with case where off_t isn't 64 bits.+	 * This shouldn't be a problem on Linux or other POSIX+	 * systems, since the configuration logic for libarchive+	 * tries to obtain a 64-bit off_t.+	 */+	if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0 &&+	    (new_offset = lseek(mine->fd, request, SEEK_CUR)) >= 0)+		return (new_offset - old_offset);++	/* If lseek() fails, don't bother trying again. */+	mine->use_lseek = 0;++	/* Let libarchive recover with read+discard */+	if (errno == ESPIPE)+		return (0);++	/* If the input is corrupted or truncated, fail. */+	if (mine->filename_type == FNT_STDIN)+		archive_set_error(a, errno, "Error seeking in stdin");+	else if (mine->filename_type == FNT_MBS)+		archive_set_error(a, errno, "Error seeking in '%s'",+		    mine->filename.m);+	else+		archive_set_error(a, errno, "Error seeking in '%S'",+		    mine->filename.w);+	return (-1);+}+++/*+ * TODO: Implement another file_skip_XXXX that uses MTIO ioctls to+ * accelerate operation on tape drives.+ */++static int64_t+file_skip(struct archive *a, void *client_data, int64_t request)+{+	struct read_file_data *mine = (struct read_file_data *)client_data;++	/* Delegate skip requests. */+	if (mine->use_lseek)+		return (file_skip_lseek(a, client_data, request));++	/* If we can't skip, return 0; libarchive will read+discard instead. */+	return (0);+}++/*+ * TODO: Store the offset and use it in the read callback.+ */+static int64_t+file_seek(struct archive *a, void *client_data, int64_t request, int whence)+{+	struct read_file_data *mine = (struct read_file_data *)client_data;+	int64_t r;++	/* We use off_t here because lseek() is declared that way. */+	/* See above for notes about when off_t is less than 64 bits. */+	r = lseek(mine->fd, request, whence);+	if (r >= 0)+		return r;++	/* If the input is corrupted or truncated, fail. */+	if (mine->filename_type == FNT_STDIN)+		archive_set_error(a, errno, "Error seeking in stdin");+	else if (mine->filename_type == FNT_MBS)+		archive_set_error(a, errno, "Error seeking in '%s'",+		    mine->filename.m);+	else+		archive_set_error(a, errno, "Error seeking in '%S'",+		    mine->filename.w);+	return (ARCHIVE_FATAL);+}++static int+file_close2(struct archive *a, void *client_data)+{+	struct read_file_data *mine = (struct read_file_data *)client_data;++	(void)a; /* UNUSED */++	/* Only flush and close if open succeeded. */+	if (mine->fd >= 0) {+		/*+		 * Sometimes, we should flush the input before closing.+		 *   Regular files: faster to just close without flush.+		 *   Disk-like devices:  Ditto.+		 *   Tapes: must not flush (user might need to+		 *      read the "next" item on a non-rewind device).+		 *   Pipes and sockets:  must flush (otherwise, the+		 *      program feeding the pipe or socket may complain).+		 * Here, I flush everything except for regular files and+		 * device nodes.+		 */+		if (!S_ISREG(mine->st_mode)+		    && !S_ISCHR(mine->st_mode)+		    && !S_ISBLK(mine->st_mode)) {+			ssize_t bytesRead;+			do {+				bytesRead = read(mine->fd, mine->buffer,+				    mine->block_size);+			} while (bytesRead > 0);+		}+		/* If a named file was opened, then it needs to be closed. */+		if (mine->filename_type != FNT_STDIN)+			close(mine->fd);+	}+	free(mine->buffer);+	mine->buffer = NULL;+	mine->fd = -1;+	return (ARCHIVE_OK);+}++static int+file_close(struct archive *a, void *client_data)+{+	struct read_file_data *mine = (struct read_file_data *)client_data;+	file_close2(a, client_data);+	free(mine);+	return (ARCHIVE_OK);+}++static int+file_switch(struct archive *a, void *client_data1, void *client_data2)+{+	file_close2(a, client_data1);+	return file_open(a, client_data2);+}
+ c/archive_read_open_memory.c view
@@ -0,0 +1,186 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_memory.c,v 1.6 2007/07/06 15:51:59 kientzle Exp $");++#include <errno.h>+#include <stdlib.h>+#include <string.h>++#include "archive.h"++/*+ * Glue to read an archive from a block of memory.+ *+ * This is mostly a huge help in building test harnesses;+ * test programs can build archives in memory and read them+ * back again without having to mess with files on disk.+ */++struct read_memory_data {+	const unsigned char	*start;+	const unsigned char	*p;+	const unsigned char	*end;+	ssize_t	 read_size;+};++static int	memory_read_close(struct archive *, void *);+static int	memory_read_open(struct archive *, void *);+static int64_t	memory_read_seek(struct archive *, void *, int64_t offset, int whence);+static int64_t	memory_read_skip(struct archive *, void *, int64_t request);+static ssize_t	memory_read(struct archive *, void *, const void **buff);++int+archive_read_open_memory(struct archive *a, const void *buff, size_t size)+{+	return archive_read_open_memory2(a, buff, size, size);+}++/*+ * Don't use _open_memory2() in production code; the archive_read_open_memory()+ * version is the one you really want.  This is just here so that+ * test harnesses can exercise block operations inside the library.+ */+int+archive_read_open_memory2(struct archive *a, const void *buff,+    size_t size, size_t read_size)+{+	struct read_memory_data *mine;++	mine = (struct read_memory_data *)calloc(1, sizeof(*mine));+	if (mine == NULL) {+		archive_set_error(a, ENOMEM, "No memory");+		return (ARCHIVE_FATAL);+	}+	mine->start = mine->p = (const unsigned char *)buff;+	mine->end = mine->start + size;+	mine->read_size = read_size;+	archive_read_set_open_callback(a, memory_read_open);+	archive_read_set_read_callback(a, memory_read);+	archive_read_set_seek_callback(a, memory_read_seek);+	archive_read_set_skip_callback(a, memory_read_skip);+	archive_read_set_close_callback(a, memory_read_close);+	archive_read_set_callback_data(a, mine);+	return (archive_read_open1(a));+}++/*+ * There's nothing to open.+ */+static int+memory_read_open(struct archive *a, void *client_data)+{+	(void)a; /* UNUSED */+	(void)client_data; /* UNUSED */+	return (ARCHIVE_OK);+}++/*+ * This is scary simple:  Just advance a pointer.  Limiting+ * to read_size is not technically necessary, but it exercises+ * more of the internal logic when used with a small block size+ * in a test harness.  Production use should not specify a block+ * size; then this is much faster.+ */+static ssize_t+memory_read(struct archive *a, void *client_data, const void **buff)+{+	struct read_memory_data *mine = (struct read_memory_data *)client_data;+	ssize_t size;++	(void)a; /* UNUSED */+	*buff = mine->p;+	size = mine->end - mine->p;+	if (size > mine->read_size)+		size = mine->read_size;+        mine->p += size;+	return (size);+}++/*+ * Advancing is just as simple.  Again, this is doing more than+ * necessary in order to better exercise internal code when used+ * as a test harness.+ */+static int64_t+memory_read_skip(struct archive *a, void *client_data, int64_t skip)+{+	struct read_memory_data *mine = (struct read_memory_data *)client_data;++	(void)a; /* UNUSED */+	if ((int64_t)skip > (int64_t)(mine->end - mine->p))+		skip = mine->end - mine->p;+	/* Round down to block size. */+	skip /= mine->read_size;+	skip *= mine->read_size;+	mine->p += skip;+	return (skip);+}++/*+ * Seeking.+ */+static int64_t+memory_read_seek(struct archive *a, void *client_data, int64_t offset, int whence)+{+	struct read_memory_data *mine = (struct read_memory_data *)client_data;++	(void)a; /* UNUSED */+	switch (whence) {+	case SEEK_SET:+		mine->p = mine->start + offset;+		break;+	case SEEK_CUR:+		mine->p += offset;+		break;+	case SEEK_END:+		mine->p = mine->end + offset;+		break;+	default:+		return ARCHIVE_FATAL;+	}+	if (mine->p < mine->start) {+		mine->p = mine->start;+		return ARCHIVE_FAILED;+	}+	if (mine->p > mine->end) {+		mine->p = mine->end;+		return ARCHIVE_FAILED;+	}+	return (mine->p - mine->start);+}++/*+ * Close is just cleaning up our one small bit of data.+ */+static int+memory_read_close(struct archive *a, void *client_data)+{+	struct read_memory_data *mine = (struct read_memory_data *)client_data;+	(void)a; /* UNUSED */+	free(mine);+	return (ARCHIVE_OK);+}
+ c/archive_read_private.h view
@@ -0,0 +1,266 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/archive_read_private.h 201088 2009-12-28 02:18:55Z kientzle $+ */++#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED+#define ARCHIVE_READ_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#ifndef __LIBARCHIVE_TEST+#error This header is only to be used internally to libarchive.+#endif+#endif++#include "archive.h"+#include "archive_string.h"+#include "archive_private.h"++struct archive_read;+struct archive_read_filter_bidder;+struct archive_read_filter;++/*+ * How bidding works for filters:+ *   * The bid manager initializes the client-provided reader as the+ *     first filter.+ *   * It invokes the bidder for each registered filter with the+ *     current head filter.+ *   * The bidders can use archive_read_filter_ahead() to peek ahead+ *     at the incoming data to compose their bids.+ *   * The bid manager creates a new filter structure for the winning+ *     bidder and gives the winning bidder a chance to initialize it.+ *   * The new filter becomes the new top filter and we repeat the+ *     process.+ * This ends only when no bidder provides a non-zero bid.  Then+ * we perform a similar dance with the registered format handlers.+ */+struct archive_read_filter_bidder {+	/* Configuration data for the bidder. */+	void *data;+	/* Name of the filter */+	const char *name;+	/* Taste the upstream filter to see if we handle this. */+	int (*bid)(struct archive_read_filter_bidder *,+	    struct archive_read_filter *);+	/* Initialize a newly-created filter. */+	int (*init)(struct archive_read_filter *);+	/* Set an option for the filter bidder. */+	int (*options)(struct archive_read_filter_bidder *,+	    const char *key, const char *value);+	/* Release the bidder's configuration data. */+	int (*free)(struct archive_read_filter_bidder *);+};++/*+ * This structure is allocated within the archive_read core+ * and initialized by archive_read and the init() method of the+ * corresponding bidder above.+ */+struct archive_read_filter {+	int64_t position;+	/* Essentially all filters will need these values, so+	 * just declare them here. */+	struct archive_read_filter_bidder *bidder; /* My bidder. */+	struct archive_read_filter *upstream; /* Who I read from. */+	struct archive_read *archive; /* Associated archive. */+	/* Open a block for reading */+	int (*open)(struct archive_read_filter *self);+	/* Return next block. */+	ssize_t (*read)(struct archive_read_filter *, const void **);+	/* Skip forward this many bytes. */+	int64_t (*skip)(struct archive_read_filter *self, int64_t request);+	/* Seek to an absolute location. */+	int64_t (*seek)(struct archive_read_filter *self, int64_t offset, int whence);+	/* Close (just this filter) and free(self). */+	int (*close)(struct archive_read_filter *self);+	/* Function that handles switching from reading one block to the next/prev */+	int (*sswitch)(struct archive_read_filter *self, unsigned int iindex);+	/* Read any header metadata if available. */+	int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry);+	/* My private data. */+	void *data;++	const char	*name;+	int		 code;++	/* Used by reblocking logic. */+	char		*buffer;+	size_t		 buffer_size;+	char		*next;		/* Current read location. */+	size_t		 avail;		/* Bytes in my buffer. */+	const void	*client_buff;	/* Client buffer information. */+	size_t		 client_total;+	const char	*client_next;+	size_t		 client_avail;+	char		 end_of_file;+	char		 closed;+	char		 fatal;+};++/*+ * The client looks a lot like a filter, so we just wrap it here.+ *+ * TODO: Make archive_read_filter and archive_read_client identical so+ * that users of the library can easily register their own+ * transformation filters.  This will probably break the API/ABI and+ * so should be deferred at least until libarchive 3.0.+ */+struct archive_read_data_node {+	int64_t begin_position;+	int64_t total_size;+	void *data;+};+struct archive_read_client {+	archive_open_callback	*opener;+	archive_read_callback	*reader;+	archive_skip_callback	*skipper;+	archive_seek_callback	*seeker;+	archive_close_callback	*closer;+	archive_switch_callback *switcher;+	unsigned int nodes;+	unsigned int cursor;+	int64_t position;+	struct archive_read_data_node *dataset;+};+struct archive_read_passphrase {+	char	*passphrase;+	struct archive_read_passphrase *next;+};++struct archive_read_extract {+	struct archive *ad; /* archive_write_disk object */++	/* Progress function invoked during extract. */+	void			(*extract_progress)(void *);+	void			 *extract_progress_user_data;+};++struct archive_read {+	struct archive	archive;++	struct archive_entry	*entry;++	/* Dev/ino of the archive being read/written. */+	int		  skip_file_set;+	int64_t		  skip_file_dev;+	int64_t		  skip_file_ino;++	/* Callbacks to open/read/write/close client archive streams. */+	struct archive_read_client client;++	/* Registered filter bidders. */+	struct archive_read_filter_bidder bidders[16];++	/* Last filter in chain */+	struct archive_read_filter *filter;++	/* Whether to bypass filter bidding process */+	int bypass_filter_bidding;++	/* File offset of beginning of most recently-read header. */+	int64_t		  header_position;++	/* Nodes and offsets of compressed data block */+	unsigned int data_start_node;+	unsigned int data_end_node;++	/*+	 * Format detection is mostly the same as compression+	 * detection, with one significant difference: The bidders+	 * use the read_ahead calls above to examine the stream rather+	 * than having the supervisor hand them a block of data to+	 * examine.+	 */++	struct archive_format_descriptor {+		void	 *data;+		const char *name;+		int	(*bid)(struct archive_read *, int best_bid);+		int	(*options)(struct archive_read *, const char *key,+		    const char *value);+		int	(*read_header)(struct archive_read *, struct archive_entry *);+		int	(*read_data)(struct archive_read *, const void **, size_t *, int64_t *);+		int	(*read_data_skip)(struct archive_read *);+		int64_t	(*seek_data)(struct archive_read *, int64_t, int);+		int	(*cleanup)(struct archive_read *);+		int	(*format_capabilties)(struct archive_read *);+		int	(*has_encrypted_entries)(struct archive_read *);+	}	formats[16];+	struct archive_format_descriptor	*format; /* Active format. */++	/*+	 * Various information needed by archive_extract.+	 */+	struct archive_read_extract		*extract;+	int			(*cleanup_archive_extract)(struct archive_read *);++	/*+	 * Decryption passphrase.+	 */+	struct {+		struct archive_read_passphrase *first;+		struct archive_read_passphrase **last;+		int candidate;+		archive_passphrase_callback *callback;+		void *client_data;+	}		passphrases;+};++int	__archive_read_register_format(struct archive_read *a,+		void *format_data,+		const char *name,+		int (*bid)(struct archive_read *, int),+		int (*options)(struct archive_read *, const char *, const char *),+		int (*read_header)(struct archive_read *, struct archive_entry *),+		int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *),+		int (*read_data_skip)(struct archive_read *),+		int64_t (*seek_data)(struct archive_read *, int64_t, int),+		int (*cleanup)(struct archive_read *),+		int (*format_capabilities)(struct archive_read *),+		int (*has_encrypted_entries)(struct archive_read *));++int __archive_read_get_bidder(struct archive_read *a,+    struct archive_read_filter_bidder **bidder);++const void *__archive_read_ahead(struct archive_read *, size_t, ssize_t *);+const void *__archive_read_filter_ahead(struct archive_read_filter *,+    size_t, ssize_t *);+int64_t	__archive_read_seek(struct archive_read*, int64_t, int);+int64_t	__archive_read_filter_seek(struct archive_read_filter *, int64_t, int);+int64_t	__archive_read_consume(struct archive_read *, int64_t);+int64_t	__archive_read_filter_consume(struct archive_read_filter *, int64_t);+int __archive_read_header(struct archive_read *, struct archive_entry *);+int __archive_read_program(struct archive_read_filter *, const char *);+void __archive_read_free_filters(struct archive_read *);+struct archive_read_extract *__archive_read_get_extract(struct archive_read *);+++/*+ * Get a decryption passphrase.+ */+void __archive_read_reset_passphrase(struct archive_read *a);+const char * __archive_read_next_passphrase(struct archive_read *a);+#endif
+ c/archive_read_set_format.c view
@@ -0,0 +1,108 @@+/*-+ * Copyright (c) 2003-2012 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_read_private.h"++int+archive_read_set_format(struct archive *_a, int code)+{+  int r1, r2, slots, i;+  char str[10];+  struct archive_read *a = (struct archive_read *)_a;++  if ((r1 = archive_read_support_format_by_code(_a, code)) < (ARCHIVE_OK))+    return r1;++  r1 = r2 = (ARCHIVE_OK);+  if (a->format)+    r2 = (ARCHIVE_WARN);+  switch (code & ARCHIVE_FORMAT_BASE_MASK)+  {+    case ARCHIVE_FORMAT_7ZIP:+      strcpy(str, "7zip");+      break;+    case ARCHIVE_FORMAT_AR:+      strcpy(str, "ar");+      break;+    case ARCHIVE_FORMAT_CAB:+      strcpy(str, "cab");+      break;+    case ARCHIVE_FORMAT_CPIO:+      strcpy(str, "cpio");+      break;+    case ARCHIVE_FORMAT_ISO9660:+      strcpy(str, "iso9660");+      break;+    case ARCHIVE_FORMAT_LHA:+      strcpy(str, "lha");+      break;+    case ARCHIVE_FORMAT_MTREE:+      strcpy(str, "mtree");+      break;+    case ARCHIVE_FORMAT_RAR:+      strcpy(str, "rar");+      break;+    case ARCHIVE_FORMAT_RAR_V5:+      strcpy(str, "rar5");+      break;+    case ARCHIVE_FORMAT_TAR:+      strcpy(str, "tar");+      break;+    case ARCHIVE_FORMAT_XAR:+      strcpy(str, "xar");+      break;+    case ARCHIVE_FORMAT_ZIP:+      strcpy(str, "zip");+      break;+    default:+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+          "Invalid format code specified");+      return (ARCHIVE_FATAL);+  }++  slots = sizeof(a->formats) / sizeof(a->formats[0]);+  a->format = &(a->formats[0]);+  for (i = 0; i < slots; i++, a->format++) {+    if (!a->format->name || !strcmp(a->format->name, str))+      break;+  }+  if (!a->format->name || strcmp(a->format->name, str))+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+        "Internal error: Unable to set format");+    r1 = (ARCHIVE_FATAL);+  }++  return (r1 < r2) ? r1 : r2;+}
+ c/archive_read_set_options.c view
@@ -0,0 +1,155 @@+/*-+ * Copyright (c) 2011 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#include "archive_read_private.h"+#include "archive_options_private.h"++static int	archive_set_format_option(struct archive *a,+		    const char *m, const char *o, const char *v);+static int	archive_set_filter_option(struct archive *a,+		    const char *m, const char *o, const char *v);+static int	archive_set_option(struct archive *a,+		    const char *m, const char *o, const char *v);++int+archive_read_set_format_option(struct archive *a, const char *m, const char *o,+    const char *v)+{+	return _archive_set_option(a, m, o, v,+	    ARCHIVE_READ_MAGIC, "archive_read_set_format_option",+	    archive_set_format_option);+}++int+archive_read_set_filter_option(struct archive *a, const char *m, const char *o,+    const char *v)+{+	return _archive_set_option(a, m, o, v,+	    ARCHIVE_READ_MAGIC, "archive_read_set_filter_option",+	    archive_set_filter_option);+}++int+archive_read_set_option(struct archive *a, const char *m, const char *o,+    const char *v)+{+	return _archive_set_option(a, m, o, v,+	    ARCHIVE_READ_MAGIC, "archive_read_set_option",+	    archive_set_option);+}++int+archive_read_set_options(struct archive *a, const char *options)+{+	return _archive_set_options(a, options,+	    ARCHIVE_READ_MAGIC, "archive_read_set_options",+	    archive_set_option);+}++static int+archive_set_format_option(struct archive *_a, const char *m, const char *o,+    const char *v)+{+	struct archive_read *a = (struct archive_read *)_a;+	size_t i;+	int r, rv = ARCHIVE_WARN, matched_modules = 0;++	for (i = 0; i < sizeof(a->formats)/sizeof(a->formats[0]); i++) {+		struct archive_format_descriptor *format = &a->formats[i];++		if (format->options == NULL || format->name == NULL)+			/* This format does not support option. */+			continue;+		if (m != NULL) {+			if (strcmp(format->name, m) != 0)+				continue;+			++matched_modules;+		}++		a->format = format;+		r = format->options(a, o, v);+		a->format = NULL;++		if (r == ARCHIVE_FATAL)+			return (ARCHIVE_FATAL);++		if (r == ARCHIVE_OK)+			rv = ARCHIVE_OK;+	}+	/* If the format name didn't match, return a special code for+	 * _archive_set_option[s]. */+	if (m != NULL && matched_modules == 0)+		return ARCHIVE_WARN - 1;+	return (rv);+}++static int+archive_set_filter_option(struct archive *_a, const char *m, const char *o,+    const char *v)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter *filter;+	struct archive_read_filter_bidder *bidder;+	int r, rv = ARCHIVE_WARN, matched_modules = 0;++	for (filter = a->filter; filter != NULL; filter = filter->upstream) {+		bidder = filter->bidder;+		if (bidder == NULL)+			continue;+		if (bidder->options == NULL)+			/* This bidder does not support option */+			continue;+		if (m != NULL) {+			if (strcmp(filter->name, m) != 0)+				continue;+			++matched_modules;+		}++		r = bidder->options(bidder, o, v);++		if (r == ARCHIVE_FATAL)+			return (ARCHIVE_FATAL);++		if (r == ARCHIVE_OK)+			rv = ARCHIVE_OK;+	}+	/* If the filter name didn't match, return a special code for+	 * _archive_set_option[s]. */+	if (m != NULL && matched_modules == 0)+		return ARCHIVE_WARN - 1;+	return (rv);+}++static int+archive_set_option(struct archive *a, const char *m, const char *o,+    const char *v)+{+	return _archive_set_either_option(a, m, o, v,+	    archive_set_format_option,+	    archive_set_filter_option);+}
+ c/archive_read_support_filter_all.c view
@@ -0,0 +1,85 @@+/*-+ * Copyright (c) 2003-2011 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#include "archive.h"+#include "archive_private.h"++#if ARCHIVE_VERSION_NUMBER < 4000000+/* Deprecated; remove in libarchive 4.0 */+int+archive_read_support_compression_all(struct archive *a)+{+	return archive_read_support_filter_all(a);+}+#endif++int+archive_read_support_filter_all(struct archive *a)+{+	archive_check_magic(a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_all");++	/* Bzip falls back to "bunzip2" command-line */+	archive_read_support_filter_bzip2(a);+	/* The decompress code doesn't use an outside library. */+	archive_read_support_filter_compress(a);+	/* Gzip decompress falls back to "gzip -d" command-line. */+	archive_read_support_filter_gzip(a);+	/* Lzip falls back to "unlzip" command-line program. */+	archive_read_support_filter_lzip(a);+	/* The LZMA file format has a very weak signature, so it+	 * may not be feasible to keep this here, but we'll try.+	 * This will come back out if there are problems. */+	/* Lzma falls back to "unlzma" command-line program. */+	archive_read_support_filter_lzma(a);+	/* Xz falls back to "unxz" command-line program. */+	archive_read_support_filter_xz(a);+	/* The decode code doesn't use an outside library. */+	archive_read_support_filter_uu(a);+	/* The decode code doesn't use an outside library. */+	archive_read_support_filter_rpm(a);+	/* The decode code always uses "lrzip -q -d" command-line. */+	archive_read_support_filter_lrzip(a);+	/* Lzop decompress falls back to "lzop -d" command-line. */+	archive_read_support_filter_lzop(a);+	/* The decode code always uses "grzip -d" command-line. */+	archive_read_support_filter_grzip(a);+	/* Lz4 falls back to "lz4 -d" command-line program. */+	archive_read_support_filter_lz4(a);+	/* Zstd falls back to "zstd -d" command-line program. */+	archive_read_support_filter_zstd(a);++	/* Note: We always return ARCHIVE_OK here, even if some of the+	 * above return ARCHIVE_WARN.  The intent here is to enable+	 * "as much as possible."  Clients who need specific+	 * compression should enable those individually so they can+	 * verify the level of support. */+	/* Clear any warning messages set by the above functions. */+	archive_clear_error(a);+	return (ARCHIVE_OK);+}
+ c/archive_read_support_filter_bzip2.c view
@@ -0,0 +1,371 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#ifdef HAVE_BZLIB_H+#include <bzlib.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_read_private.h"++#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+struct private_data {+	bz_stream	 stream;+	char		*out_block;+	size_t		 out_block_size;+	char		 valid; /* True = decompressor is initialized */+	char		 eof; /* True = found end of compressed data. */+};++/* Bzip2 filter */+static ssize_t	bzip2_filter_read(struct archive_read_filter *, const void **);+static int	bzip2_filter_close(struct archive_read_filter *);+#endif++/*+ * Note that we can detect bzip2 archives even if we can't decompress+ * them.  (In fact, we like detecting them because we can give better+ * error messages.)  So the bid framework here gets compiled even+ * if bzlib is unavailable.+ */+static int	bzip2_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);+static int	bzip2_reader_init(struct archive_read_filter *);+static int	bzip2_reader_free(struct archive_read_filter_bidder *);++#if ARCHIVE_VERSION_NUMBER < 4000000+/* Deprecated; remove in libarchive 4.0 */+int+archive_read_support_compression_bzip2(struct archive *a)+{+	return archive_read_support_filter_bzip2(a);+}+#endif++int+archive_read_support_filter_bzip2(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *reader;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_bzip2");++	if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	reader->data = NULL;+	reader->name = "bzip2";+	reader->bid = bzip2_reader_bid;+	reader->init = bzip2_reader_init;+	reader->options = NULL;+	reader->free = bzip2_reader_free;+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+	return (ARCHIVE_OK);+#else+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external bzip2 program");+	return (ARCHIVE_WARN);+#endif+}++static int+bzip2_reader_free(struct archive_read_filter_bidder *self){+	(void)self; /* UNUSED */+	return (ARCHIVE_OK);+}++/*+ * Test whether we can handle this data.+ *+ * This logic returns zero if any part of the signature fails.  It+ * also tries to Do The Right Thing if a very short buffer prevents us+ * from verifying as much as we would like.+ */+static int+bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter)+{+	const unsigned char *buffer;+	ssize_t avail;+	int bits_checked;++	(void)self; /* UNUSED */++	/* Minimal bzip2 archive is 14 bytes. */+	buffer = __archive_read_filter_ahead(filter, 14, &avail);+	if (buffer == NULL)+		return (0);++	/* First three bytes must be "BZh" */+	bits_checked = 0;+	if (memcmp(buffer, "BZh", 3) != 0)+		return (0);+	bits_checked += 24;++	/* Next follows a compression flag which must be an ASCII digit. */+	if (buffer[3] < '1' || buffer[3] > '9')+		return (0);+	bits_checked += 5;++	/* After BZh[1-9], there must be either a data block+	 * which begins with 0x314159265359 or an end-of-data+	 * marker of 0x177245385090. */+	if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0)+		bits_checked += 48;+	else if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", 6) == 0)+		bits_checked += 48;+	else+		return (0);++	return (bits_checked);+}++#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)++/*+ * If we don't have the library on this system, we can't actually do the+ * decompression.  We can, however, still detect compressed archives+ * and emit a useful message.+ */+static int+bzip2_reader_init(struct archive_read_filter *self)+{+	int r;++	r = __archive_read_program(self, "bzip2 -d");+	/* Note: We set the format here even if __archive_read_program()+	 * above fails.  We do, after all, know what the format is+	 * even if we weren't able to read it. */+	self->code = ARCHIVE_FILTER_BZIP2;+	self->name = "bzip2";+	return (r);+}+++#else++/*+ * Setup the callbacks.+ */+static int+bzip2_reader_init(struct archive_read_filter *self)+{+	static const size_t out_block_size = 64 * 1024;+	void *out_block;+	struct private_data *state;++	self->code = ARCHIVE_FILTER_BZIP2;+	self->name = "bzip2";++	state = (struct private_data *)calloc(sizeof(*state), 1);+	out_block = (unsigned char *)malloc(out_block_size);+	if (state == NULL || out_block == NULL) {+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Can't allocate data for bzip2 decompression");+		free(out_block);+		free(state);+		return (ARCHIVE_FATAL);+	}++	self->data = state;+	state->out_block_size = out_block_size;+	state->out_block = out_block;+	self->read = bzip2_filter_read;+	self->skip = NULL; /* not supported */+	self->close = bzip2_filter_close;++	return (ARCHIVE_OK);+}++/*+ * Return the next block of decompressed data.+ */+static ssize_t+bzip2_filter_read(struct archive_read_filter *self, const void **p)+{+	struct private_data *state;+	size_t decompressed;+	const char *read_buf;+	ssize_t ret;++	state = (struct private_data *)self->data;++	if (state->eof) {+		*p = NULL;+		return (0);+	}++	/* Empty our output buffer. */+	state->stream.next_out = state->out_block;+	state->stream.avail_out = state->out_block_size;++	/* Try to fill the output buffer. */+	for (;;) {+		if (!state->valid) {+			if (bzip2_reader_bid(self->bidder, self->upstream) == 0) {+				state->eof = 1;+				*p = state->out_block;+				decompressed = state->stream.next_out+				    - state->out_block;+				return (decompressed);+			}+			/* Initialize compression library. */+			ret = BZ2_bzDecompressInit(&(state->stream),+					   0 /* library verbosity */,+					   0 /* don't use low-mem algorithm */);++			/* If init fails, try low-memory algorithm instead. */+			if (ret == BZ_MEM_ERROR)+				ret = BZ2_bzDecompressInit(&(state->stream),+					   0 /* library verbosity */,+					   1 /* do use low-mem algo */);++			if (ret != BZ_OK) {+				const char *detail = NULL;+				int err = ARCHIVE_ERRNO_MISC;+				switch (ret) {+				case BZ_PARAM_ERROR:+					detail = "invalid setup parameter";+					break;+				case BZ_MEM_ERROR:+					err = ENOMEM;+					detail = "out of memory";+					break;+				case BZ_CONFIG_ERROR:+					detail = "mis-compiled library";+					break;+				}+				archive_set_error(&self->archive->archive, err,+				    "Internal error initializing decompressor%s%s",+				    detail == NULL ? "" : ": ",+				    detail);+				return (ARCHIVE_FATAL);+			}+			state->valid = 1;+		}++		/* stream.next_in is really const, but bzlib+		 * doesn't declare it so. <sigh> */+		read_buf =+		    __archive_read_filter_ahead(self->upstream, 1, &ret);+		if (read_buf == NULL) {+			archive_set_error(&self->archive->archive,+			    ARCHIVE_ERRNO_MISC,+			    "truncated bzip2 input");+			return (ARCHIVE_FATAL);+		}+		state->stream.next_in = (char *)(uintptr_t)read_buf;+		state->stream.avail_in = ret;+		/* There is no more data, return whatever we have. */+		if (ret == 0) {+			state->eof = 1;+			*p = state->out_block;+			decompressed = state->stream.next_out+			    - state->out_block;+			return (decompressed);+		}++		/* Decompress as much as we can in one pass. */+		ret = BZ2_bzDecompress(&(state->stream));+		__archive_read_filter_consume(self->upstream,+		    state->stream.next_in - read_buf);++		switch (ret) {+		case BZ_STREAM_END: /* Found end of stream. */+			switch (BZ2_bzDecompressEnd(&(state->stream))) {+			case BZ_OK:+				break;+			default:+				archive_set_error(&(self->archive->archive),+					  ARCHIVE_ERRNO_MISC,+					  "Failed to clean up decompressor");+				return (ARCHIVE_FATAL);+			}+			state->valid = 0;+			/* FALLTHROUGH */+		case BZ_OK: /* Decompressor made some progress. */+			/* If we filled our buffer, update stats and return. */+			if (state->stream.avail_out == 0) {+				*p = state->out_block;+				decompressed = state->stream.next_out+				    - state->out_block;+				return (decompressed);+			}+			break;+		default: /* Return an error. */+			archive_set_error(&self->archive->archive,+			    ARCHIVE_ERRNO_MISC, "bzip decompression failed");+			return (ARCHIVE_FATAL);+		}+	}+}++/*+ * Clean up the decompressor.+ */+static int+bzip2_filter_close(struct archive_read_filter *self)+{+	struct private_data *state;+	int ret = ARCHIVE_OK;++	state = (struct private_data *)self->data;++	if (state->valid) {+		switch (BZ2_bzDecompressEnd(&state->stream)) {+		case BZ_OK:+			break;+		default:+			archive_set_error(&self->archive->archive,+					  ARCHIVE_ERRNO_MISC,+					  "Failed to clean up decompressor");+			ret = ARCHIVE_FATAL;+		}+		state->valid = 0;+	}++	free(state->out_block);+	free(state);+	return (ret);+}++#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
+ c/archive_read_support_filter_compress.c view
@@ -0,0 +1,465 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++/*+ * This code borrows heavily from "compress" source code, which is+ * protected by the following copyright.  (Clause 3 dropped by request+ * of the Regents.)+ */++/*-+ * Copyright (c) 1985, 1986, 1992, 1993+ *	The Regents of the University of California.  All rights reserved.+ *+ * This code is derived from software contributed to Berkeley by+ * Diomidis Spinellis and James A. Woods, derived from original+ * work by Spencer Thomas and Joseph Orost.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ * 4. Neither the name of the University nor the names of its contributors+ *    may be used to endorse or promote products derived from this software+ *    without specific prior written permission.+ *+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+ * SUCH DAMAGE.+ */+++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_read_private.h"++/*+ * Because LZW decompression is pretty simple, I've just implemented+ * the whole decompressor here (cribbing from "compress" source code,+ * of course), rather than relying on an external library.  I have+ * made an effort to clarify and simplify the algorithm, so the+ * names and structure here don't exactly match those used by compress.+ */++struct private_data {+	/* Input variables. */+	const unsigned char	*next_in;+	size_t			 avail_in;+	size_t			 consume_unnotified;+	int			 bit_buffer;+	int			 bits_avail;+	size_t			 bytes_in_section;++	/* Output variables. */+	size_t			 out_block_size;+	void			*out_block;++	/* Decompression status variables. */+	int			 use_reset_code;+	int			 end_of_stream;	/* EOF status. */+	int			 maxcode;	/* Largest code. */+	int			 maxcode_bits;	/* Length of largest code. */+	int			 section_end_code; /* When to increase bits. */+	int			 bits;		/* Current code length. */+	int			 oldcode;	/* Previous code. */+	int			 finbyte;	/* Last byte of prev code. */++	/* Dictionary. */+	int			 free_ent;       /* Next dictionary entry. */+	unsigned char		 suffix[65536];+	uint16_t		 prefix[65536];++	/*+	 * Scratch area for expanding dictionary entries.  Note:+	 * "worst" case here comes from compressing /dev/zero: the+	 * last code in the dictionary will code a sequence of+	 * 65536-256 zero bytes.  Thus, we need stack space to expand+	 * a 65280-byte dictionary entry.  (Of course, 32640:1+	 * compression could also be considered the "best" case. ;-)+	 */+	unsigned char		*stackp;+	unsigned char		 stack[65300];+};++static int	compress_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);+static int	compress_bidder_init(struct archive_read_filter *);+static int	compress_bidder_free(struct archive_read_filter_bidder *);++static ssize_t	compress_filter_read(struct archive_read_filter *, const void **);+static int	compress_filter_close(struct archive_read_filter *);++static int	getbits(struct archive_read_filter *, int n);+static int	next_code(struct archive_read_filter *);++#if ARCHIVE_VERSION_NUMBER < 4000000+/* Deprecated; remove in libarchive 4.0 */+int+archive_read_support_compression_compress(struct archive *a)+{+	return archive_read_support_filter_compress(a);+}+#endif++int+archive_read_support_filter_compress(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *bidder;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_compress");++	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	bidder->data = NULL;+	bidder->name = "compress (.Z)";+	bidder->bid = compress_bidder_bid;+	bidder->init = compress_bidder_init;+	bidder->options = NULL;+	bidder->free = compress_bidder_free;+	return (ARCHIVE_OK);+}++/*+ * Test whether we can handle this data.+ * This logic returns zero if any part of the signature fails.+ */+static int+compress_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{+	const unsigned char *buffer;+	ssize_t avail;+	int bits_checked;++	(void)self; /* UNUSED */++	/* Shortest valid compress file is 3 bytes. */+	buffer = __archive_read_filter_ahead(filter, 3, &avail);++	if (buffer == NULL)+		return (0);++	bits_checked = 0;+	/* First two bytes are the magic value */+	if (buffer[0] != 0x1F || buffer[1] != 0x9D)+		return (0);+	/* Third byte holds compression parameters. */+	if (buffer[2] & 0x20) /* Reserved bit, must be zero. */+		return (0);+	if (buffer[2] & 0x40) /* Reserved bit, must be zero. */+		return (0);+	bits_checked += 18;++	return (bits_checked);+}++/*+ * Setup the callbacks.+ */+static int+compress_bidder_init(struct archive_read_filter *self)+{+	struct private_data *state;+	static const size_t out_block_size = 64 * 1024;+	void *out_block;+	int code;++	self->code = ARCHIVE_FILTER_COMPRESS;+	self->name = "compress (.Z)";++	state = (struct private_data *)calloc(sizeof(*state), 1);+	out_block = malloc(out_block_size);+	if (state == NULL || out_block == NULL) {+		free(out_block);+		free(state);+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Can't allocate data for %s decompression",+		    self->name);+		return (ARCHIVE_FATAL);+	}++	self->data = state;+	state->out_block_size = out_block_size;+	state->out_block = out_block;+	self->read = compress_filter_read;+	self->skip = NULL; /* not supported */+	self->close = compress_filter_close;++	/* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */++	(void)getbits(self, 8); /* Skip first signature byte. */+	(void)getbits(self, 8); /* Skip second signature byte. */++	/* Get compression parameters. */+	code = getbits(self, 8);+	if ((code & 0x1f) > 16) {+		archive_set_error(&self->archive->archive, -1,+		    "Invalid compressed data");+		return (ARCHIVE_FATAL);+	}+	state->maxcode_bits = code & 0x1f;+	state->maxcode = (1 << state->maxcode_bits);+	state->use_reset_code = code & 0x80;++	/* Initialize decompressor. */+	state->free_ent = 256;+	state->stackp = state->stack;+	if (state->use_reset_code)+		state->free_ent++;+	state->bits = 9;+	state->section_end_code = (1<<state->bits) - 1;+	state->oldcode = -1;+	for (code = 255; code >= 0; code--) {+		state->prefix[code] = 0;+		state->suffix[code] = code;+	}+	next_code(self);++	return (ARCHIVE_OK);+}++/*+ * Return a block of data from the decompression buffer.  Decompress more+ * as necessary.+ */+static ssize_t+compress_filter_read(struct archive_read_filter *self, const void **pblock)+{+	struct private_data *state;+	unsigned char *p, *start, *end;+	int ret;++	state = (struct private_data *)self->data;+	if (state->end_of_stream) {+		*pblock = NULL;+		return (0);+	}+	p = start = (unsigned char *)state->out_block;+	end = start + state->out_block_size;++	while (p < end && !state->end_of_stream) {+		if (state->stackp > state->stack) {+			*p++ = *--state->stackp;+		} else {+			ret = next_code(self);+			if (ret == -1)+				state->end_of_stream = ret;+			else if (ret != ARCHIVE_OK)+				return (ret);+		}+	}++	*pblock = start;+	return (p - start);+}++/*+ * Clean up the reader.+ */+static int+compress_bidder_free(struct archive_read_filter_bidder *self)+{+	self->data = NULL;+	return (ARCHIVE_OK);+}++/*+ * Close and release the filter.+ */+static int+compress_filter_close(struct archive_read_filter *self)+{+	struct private_data *state = (struct private_data *)self->data;++	free(state->out_block);+	free(state);+	return (ARCHIVE_OK);+}++/*+ * Process the next code and fill the stack with the expansion+ * of the code.  Returns ARCHIVE_FATAL if there is a fatal I/O or+ * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise.+ */+static int+next_code(struct archive_read_filter *self)+{+	struct private_data *state = (struct private_data *)self->data;+	int code, newcode;++	static int debug_buff[1024];+	static unsigned debug_index;++	code = newcode = getbits(self, state->bits);+	if (code < 0)+		return (code);++	debug_buff[debug_index++] = code;+	if (debug_index >= sizeof(debug_buff)/sizeof(debug_buff[0]))+		debug_index = 0;++	/* If it's a reset code, reset the dictionary. */+	if ((code == 256) && state->use_reset_code) {+		/*+		 * The original 'compress' implementation blocked its+		 * I/O in a manner that resulted in junk bytes being+		 * inserted after every reset.  The next section skips+		 * this junk.  (Yes, the number of *bytes* to skip is+		 * a function of the current *bit* length.)+		 */+		int skip_bytes =  state->bits -+		    (state->bytes_in_section % state->bits);+		skip_bytes %= state->bits;+		state->bits_avail = 0; /* Discard rest of this byte. */+		while (skip_bytes-- > 0) {+			code = getbits(self, 8);+			if (code < 0)+				return (code);+		}+		/* Now, actually do the reset. */+		state->bytes_in_section = 0;+		state->bits = 9;+		state->section_end_code = (1 << state->bits) - 1;+		state->free_ent = 257;+		state->oldcode = -1;+		return (next_code(self));+	}++	if (code > state->free_ent+	    || (code == state->free_ent && state->oldcode < 0)) {+		/* An invalid code is a fatal error. */+		archive_set_error(&(self->archive->archive), -1,+		    "Invalid compressed data");+		return (ARCHIVE_FATAL);+	}++	/* Special case for KwKwK string. */+	if (code >= state->free_ent) {+		*state->stackp++ = state->finbyte;+		code = state->oldcode;+	}++	/* Generate output characters in reverse order. */+	while (code >= 256) {+		*state->stackp++ = state->suffix[code];+		code = state->prefix[code];+	}+	*state->stackp++ = state->finbyte = code;++	/* Generate the new entry. */+	code = state->free_ent;+	if (code < state->maxcode && state->oldcode >= 0) {+		state->prefix[code] = state->oldcode;+		state->suffix[code] = state->finbyte;+		++state->free_ent;+	}+	if (state->free_ent > state->section_end_code) {+		state->bits++;+		state->bytes_in_section = 0;+		if (state->bits == state->maxcode_bits)+			state->section_end_code = state->maxcode;+		else+			state->section_end_code = (1 << state->bits) - 1;+	}++	/* Remember previous code. */+	state->oldcode = newcode;+	return (ARCHIVE_OK);+}++/*+ * Return next 'n' bits from stream.+ *+ * -1 indicates end of available data.+ */+static int+getbits(struct archive_read_filter *self, int n)+{+	struct private_data *state = (struct private_data *)self->data;+	int code;+	ssize_t ret;+	static const int mask[] = {+		0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff,+		0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff+	};++	while (state->bits_avail < n) {+		if (state->avail_in <= 0) {+			if (state->consume_unnotified) {+				__archive_read_filter_consume(self->upstream,+					state->consume_unnotified);+				state->consume_unnotified = 0;+			}+			state->next_in+			    = __archive_read_filter_ahead(self->upstream,+				1, &ret);+			if (ret == 0)+				return (-1);+			if (ret < 0 || state->next_in == NULL)+				return (ARCHIVE_FATAL);+			state->consume_unnotified = state->avail_in = ret;+		}+		state->bit_buffer |= *state->next_in++ << state->bits_avail;+		state->avail_in--;+		state->bits_avail += 8;+		state->bytes_in_section++;+	}++	code = state->bit_buffer;+	state->bit_buffer >>= n;+	state->bits_avail -= n;++	return (code & mask[n]);+}
+ c/archive_read_support_filter_grzip.c view
@@ -0,0 +1,121 @@+/*-+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");+++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_read_private.h"++static const unsigned char grzip_magic[] = {+	0x47, 0x52, 0x5a, 0x69, 0x70, 0x49, 0x49, 0x00,+	0x02, 0x04, 0x3a, 0x29 };++static int	grzip_bidder_bid(struct archive_read_filter_bidder *,+		    struct archive_read_filter *);+static int	grzip_bidder_init(struct archive_read_filter *);+++static int+grzip_reader_free(struct archive_read_filter_bidder *self)+{+	(void)self; /* UNUSED */+	return (ARCHIVE_OK);+}++int+archive_read_support_filter_grzip(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *reader;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_grzip");++	if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	reader->data = NULL;+	reader->bid = grzip_bidder_bid;+	reader->init = grzip_bidder_init;+	reader->options = NULL;+	reader->free = grzip_reader_free;+	/* This filter always uses an external program. */+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external grzip program for grzip decompression");+	return (ARCHIVE_WARN);+}++/*+ * Bidder just verifies the header and returns the number of verified bits.+ */+static int+grzip_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{+	const unsigned char *p;+	ssize_t avail;++	(void)self; /* UNUSED */++	p = __archive_read_filter_ahead(filter, sizeof(grzip_magic), &avail);+	if (p == NULL || avail == 0)+		return (0);++	if (memcmp(p, grzip_magic, sizeof(grzip_magic)))+		return (0);++	return (sizeof(grzip_magic) * 8);+}++static int+grzip_bidder_init(struct archive_read_filter *self)+{+	int r;++	r = __archive_read_program(self, "grzip -d");+	/* Note: We set the format here even if __archive_read_program()+	 * above fails.  We do, after all, know what the format is+	 * even if we weren't able to read it. */+	self->code = ARCHIVE_FILTER_GRZIP;+	self->name = "grzip";+	return (r);+}
+ c/archive_read_support_filter_gzip.c view
@@ -0,0 +1,535 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");+++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_endian.h"+#include "archive_private.h"+#include "archive_read_private.h"++#ifdef HAVE_ZLIB_H+struct private_data {+	z_stream	 stream;+	char		 in_stream;+	unsigned char	*out_block;+	size_t		 out_block_size;+	int64_t		 total_out;+	unsigned long	 crc;+	uint32_t	 mtime;+	char		*name;+	char		 eof; /* True = found end of compressed data. */+};++/* Gzip Filter. */+static ssize_t	gzip_filter_read(struct archive_read_filter *, const void **);+static int	gzip_filter_close(struct archive_read_filter *);+#endif++/*+ * Note that we can detect gzip archives even if we can't decompress+ * them.  (In fact, we like detecting them because we can give better+ * error messages.)  So the bid framework here gets compiled even+ * if zlib is unavailable.+ *+ * TODO: If zlib is unavailable, gzip_bidder_init() should+ * use the compress_program framework to try to fire up an external+ * gzip program.+ */+static int	gzip_bidder_bid(struct archive_read_filter_bidder *,+		    struct archive_read_filter *);+static int	gzip_bidder_init(struct archive_read_filter *);++#if ARCHIVE_VERSION_NUMBER < 4000000+/* Deprecated; remove in libarchive 4.0 */+int+archive_read_support_compression_gzip(struct archive *a)+{+	return archive_read_support_filter_gzip(a);+}+#endif++int+archive_read_support_filter_gzip(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *bidder;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_gzip");++	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	bidder->data = NULL;+	bidder->name = "gzip";+	bidder->bid = gzip_bidder_bid;+	bidder->init = gzip_bidder_init;+	bidder->options = NULL;+	bidder->free = NULL; /* No data, so no cleanup necessary. */+	/* Signal the extent of gzip support with the return value here. */+#if HAVE_ZLIB_H+	return (ARCHIVE_OK);+#else+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external gzip program");+	return (ARCHIVE_WARN);+#endif+}++/*+ * Read and verify the header.+ *+ * Returns zero if the header couldn't be validated, else returns+ * number of bytes in header.  If pbits is non-NULL, it receives a+ * count of bits verified, suitable for use by bidder.+ */+static ssize_t+peek_at_header(struct archive_read_filter *filter, int *pbits,+#ifdef HAVE_ZLIB_H+	       struct private_data *state+#else+	       void *state+#endif+	      )+{+	const unsigned char *p;+	ssize_t avail, len;+	int bits = 0;+	int header_flags;+#ifndef HAVE_ZLIB_H+	(void)state; /* UNUSED */+#endif++	/* Start by looking at the first ten bytes of the header, which+	 * is all fixed layout. */+	len = 10;+	p = __archive_read_filter_ahead(filter, len, &avail);+	if (p == NULL || avail == 0)+		return (0);+	/* We only support deflation- third byte must be 0x08. */+	if (memcmp(p, "\x1F\x8B\x08", 3) != 0)+		return (0);+	bits += 24;+	if ((p[3] & 0xE0)!= 0)	/* No reserved flags set. */+		return (0);+	bits += 3;+	header_flags = p[3];+	/* Bytes 4-7 are mod time in little endian. */+#ifdef HAVE_ZLIB_H+	if (state)+		state->mtime = archive_le32dec(p + 4);+#endif+	/* Byte 8 is deflate flags. */+	/* XXXX TODO: return deflate flags back to consume_header for use+	   in initializing the decompressor. */+	/* Byte 9 is OS. */++	/* Optional extra data:  2 byte length plus variable body. */+	if (header_flags & 4) {+		p = __archive_read_filter_ahead(filter, len + 2, &avail);+		if (p == NULL)+			return (0);+		len += ((int)p[len + 1] << 8) | (int)p[len];+		len += 2;+	}++	/* Null-terminated optional filename. */+	if (header_flags & 8) {+#ifdef HAVE_ZLIB_H+		ssize_t file_start = len;+#endif+		do {+			++len;+			if (avail < len)+				p = __archive_read_filter_ahead(filter,+				    len, &avail);+			if (p == NULL)+				return (0);+		} while (p[len - 1] != 0);++#ifdef HAVE_ZLIB_H+		if (state) {+			/* Reset the name in case of repeat header reads. */+			free(state->name);+			state->name = strdup((const char *)&p[file_start]);+		}+#endif+	}++	/* Null-terminated optional comment. */+	if (header_flags & 16) {+		do {+			++len;+			if (avail < len)+				p = __archive_read_filter_ahead(filter,+				    len, &avail);+			if (p == NULL)+				return (0);+		} while (p[len - 1] != 0);+	}++	/* Optional header CRC */+	if ((header_flags & 2)) {+		p = __archive_read_filter_ahead(filter, len + 2, &avail);+		if (p == NULL)+			return (0);+#if 0+	int hcrc = ((int)p[len + 1] << 8) | (int)p[len];+	int crc = /* XXX TODO: Compute header CRC. */;+	if (crc != hcrc)+		return (0);+	bits += 16;+#endif+		len += 2;+	}++	if (pbits != NULL)+		*pbits = bits;+	return (len);+}++/*+ * Bidder just verifies the header and returns the number of verified bits.+ */+static int+gzip_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{+	int bits_checked;++	(void)self; /* UNUSED */++	if (peek_at_header(filter, &bits_checked, NULL))+		return (bits_checked);+	return (0);+}++#ifndef HAVE_ZLIB_H++/*+ * If we don't have the library on this system, we can't do the+ * decompression directly.  We can, however, try to run "gzip -d"+ * in case that's available.+ */+static int+gzip_bidder_init(struct archive_read_filter *self)+{+	int r;++	r = __archive_read_program(self, "gzip -d");+	/* Note: We set the format here even if __archive_read_program()+	 * above fails.  We do, after all, know what the format is+	 * even if we weren't able to read it. */+	self->code = ARCHIVE_FILTER_GZIP;+	self->name = "gzip";+	return (r);+}++#else++static int+gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry)+{+	struct private_data *state;++	state = (struct private_data *)self->data;++	/* A mtime of 0 is considered invalid/missing. */+	if (state->mtime != 0)+		archive_entry_set_mtime(entry, state->mtime, 0);++	/* If the name is available, extract it. */+	if (state->name)+		archive_entry_set_pathname(entry, state->name);++	return (ARCHIVE_OK);+}++/*+ * Initialize the filter object.+ */+static int+gzip_bidder_init(struct archive_read_filter *self)+{+	struct private_data *state;+	static const size_t out_block_size = 64 * 1024;+	void *out_block;++	self->code = ARCHIVE_FILTER_GZIP;+	self->name = "gzip";++	state = (struct private_data *)calloc(sizeof(*state), 1);+	out_block = (unsigned char *)malloc(out_block_size);+	if (state == NULL || out_block == NULL) {+		free(out_block);+		free(state);+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Can't allocate data for gzip decompression");+		return (ARCHIVE_FATAL);+	}++	self->data = state;+	state->out_block_size = out_block_size;+	state->out_block = out_block;+	self->read = gzip_filter_read;+	self->skip = NULL; /* not supported */+	self->close = gzip_filter_close;+#ifdef HAVE_ZLIB_H+	self->read_header = gzip_read_header;+#endif++	state->in_stream = 0; /* We're not actually within a stream yet. */++	return (ARCHIVE_OK);+}++static int+consume_header(struct archive_read_filter *self)+{+	struct private_data *state;+	ssize_t avail;+	size_t len;+	int ret;++	state = (struct private_data *)self->data;++	/* If this is a real header, consume it. */+	len = peek_at_header(self->upstream, NULL, state);+	if (len == 0)+		return (ARCHIVE_EOF);+	__archive_read_filter_consume(self->upstream, len);++	/* Initialize CRC accumulator. */+	state->crc = crc32(0L, NULL, 0);++	/* Initialize compression library. */+	state->stream.next_in = (unsigned char *)(uintptr_t)+	    __archive_read_filter_ahead(self->upstream, 1, &avail);+	state->stream.avail_in = (uInt)avail;+	ret = inflateInit2(&(state->stream),+	    -15 /* Don't check for zlib header */);++	/* Decipher the error code. */+	switch (ret) {+	case Z_OK:+		state->in_stream = 1;+		return (ARCHIVE_OK);+	case Z_STREAM_ERROR:+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library: "+		    "invalid setup parameter");+		break;+	case Z_MEM_ERROR:+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Internal error initializing compression library: "+		    "out of memory");+		break;+	case Z_VERSION_ERROR:+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library: "+		    "invalid library version");+		break;+	default:+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library: "+		    " Zlib error %d", ret);+		break;+	}+	return (ARCHIVE_FATAL);+}++static int+consume_trailer(struct archive_read_filter *self)+{+	struct private_data *state;+	const unsigned char *p;+	ssize_t avail;++	state = (struct private_data *)self->data;++	state->in_stream = 0;+	switch (inflateEnd(&(state->stream))) {+	case Z_OK:+		break;+	default:+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Failed to clean up gzip decompressor");+		return (ARCHIVE_FATAL);+	}++	/* GZip trailer is a fixed 8 byte structure. */+	p = __archive_read_filter_ahead(self->upstream, 8, &avail);+	if (p == NULL || avail == 0)+		return (ARCHIVE_FATAL);++	/* XXX TODO: Verify the length and CRC. */++	/* We've verified the trailer, so consume it now. */+	__archive_read_filter_consume(self->upstream, 8);++	return (ARCHIVE_OK);+}++static ssize_t+gzip_filter_read(struct archive_read_filter *self, const void **p)+{+	struct private_data *state;+	size_t decompressed;+	ssize_t avail_in, max_in;+	int ret;++	state = (struct private_data *)self->data;++	/* Empty our output buffer. */+	state->stream.next_out = state->out_block;+	state->stream.avail_out = (uInt)state->out_block_size;++	/* Try to fill the output buffer. */+	while (state->stream.avail_out > 0 && !state->eof) {+		/* If we're not in a stream, read a header+		 * and initialize the decompression library. */+		if (!state->in_stream) {+			ret = consume_header(self);+			if (ret == ARCHIVE_EOF) {+				state->eof = 1;+				break;+			}+			if (ret < ARCHIVE_OK)+				return (ret);+		}++		/* Peek at the next available data. */+		/* ZLib treats stream.next_in as const but doesn't declare+		 * it so, hence this ugly cast. */+		state->stream.next_in = (unsigned char *)(uintptr_t)+		    __archive_read_filter_ahead(self->upstream, 1, &avail_in);+		if (state->stream.next_in == NULL) {+			archive_set_error(&self->archive->archive,+			    ARCHIVE_ERRNO_MISC,+			    "truncated gzip input");+			return (ARCHIVE_FATAL);+		}+		if (UINT_MAX >= SSIZE_MAX)+			max_in = SSIZE_MAX;+		else+			max_in = UINT_MAX;+		if (avail_in > max_in)+			avail_in = max_in;+		state->stream.avail_in = (uInt)avail_in;++		/* Decompress and consume some of that data. */+		ret = inflate(&(state->stream), 0);+		switch (ret) {+		case Z_OK: /* Decompressor made some progress. */+			__archive_read_filter_consume(self->upstream,+			    avail_in - state->stream.avail_in);+			break;+		case Z_STREAM_END: /* Found end of stream. */+			__archive_read_filter_consume(self->upstream,+			    avail_in - state->stream.avail_in);+			/* Consume the stream trailer; release the+			 * decompression library. */+			ret = consume_trailer(self);+			if (ret < ARCHIVE_OK)+				return (ret);+			break;+		default:+			/* Return an error. */+			archive_set_error(&self->archive->archive,+			    ARCHIVE_ERRNO_MISC,+			    "gzip decompression failed");+			return (ARCHIVE_FATAL);+		}+	}++	/* We've read as much as we can. */+	decompressed = state->stream.next_out - state->out_block;+	state->total_out += decompressed;+	if (decompressed == 0)+		*p = NULL;+	else+		*p = state->out_block;+	return (decompressed);+}++/*+ * Clean up the decompressor.+ */+static int+gzip_filter_close(struct archive_read_filter *self)+{+	struct private_data *state;+	int ret;++	state = (struct private_data *)self->data;+	ret = ARCHIVE_OK;++	if (state->in_stream) {+		switch (inflateEnd(&(state->stream))) {+		case Z_OK:+			break;+		default:+			archive_set_error(&(self->archive->archive),+			    ARCHIVE_ERRNO_MISC,+			    "Failed to clean up gzip compressor");+			ret = ARCHIVE_FATAL;+		}+	}++	free(state->name);+	free(state->out_block);+	free(state);+	return (ret);+}++#endif /* HAVE_ZLIB_H */
+ c/archive_read_support_filter_lrzip.c view
@@ -0,0 +1,132 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");+++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_read_private.h"++#define LRZIP_HEADER_MAGIC "LRZI"+#define LRZIP_HEADER_MAGIC_LEN 4++static int	lrzip_bidder_bid(struct archive_read_filter_bidder *,+		    struct archive_read_filter *);+static int	lrzip_bidder_init(struct archive_read_filter *);+++static int+lrzip_reader_free(struct archive_read_filter_bidder *self)+{+	(void)self; /* UNUSED */+	return (ARCHIVE_OK);+}++int+archive_read_support_filter_lrzip(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *reader;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_lrzip");++	if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	reader->data = NULL;+	reader->name = "lrzip";+	reader->bid = lrzip_bidder_bid;+	reader->init = lrzip_bidder_init;+	reader->options = NULL;+	reader->free = lrzip_reader_free;+	/* This filter always uses an external program. */+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external lrzip program for lrzip decompression");+	return (ARCHIVE_WARN);+}++/*+ * Bidder just verifies the header and returns the number of verified bits.+ */+static int+lrzip_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{+	const unsigned char *p;+	ssize_t avail, len;+	int i;++	(void)self; /* UNUSED */+	/* Start by looking at the first six bytes of the header, which+	 * is all fixed layout. */+	len = 6;+	p = __archive_read_filter_ahead(filter, len, &avail);+	if (p == NULL || avail == 0)+		return (0);++	if (memcmp(p, LRZIP_HEADER_MAGIC, LRZIP_HEADER_MAGIC_LEN))+		return (0);++	/* current major version is always 0, verify this */+	if (p[LRZIP_HEADER_MAGIC_LEN])+		return 0;+	/* support only v0.6+ lrzip for sanity */+	i = p[LRZIP_HEADER_MAGIC_LEN + 1];+	if ((i < 6) || (i > 10))+		return 0;++	return (int)len;+}++static int+lrzip_bidder_init(struct archive_read_filter *self)+{+	int r;++	r = __archive_read_program(self, "lrzip -d -q");+	/* Note: We set the format here even if __archive_read_program()+	 * above fails.  We do, after all, know what the format is+	 * even if we weren't able to read it. */+	self->code = ARCHIVE_FILTER_LRZIP;+	self->name = "lrzip";+	return (r);+}
+ c/archive_read_support_filter_lz4.c view
@@ -0,0 +1,742 @@+/*-+ * Copyright (c) 2014 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#ifdef HAVE_LZ4_H+#include <lz4.h>+#endif++#include "archive.h"+#include "archive_endian.h"+#include "archive_private.h"+#include "archive_read_private.h"+#include "archive_xxhash.h"++#define LZ4_MAGICNUMBER		0x184d2204+#define LZ4_SKIPPABLED		0x184d2a50+#define LZ4_LEGACY		0x184c2102++#if defined(HAVE_LIBLZ4)+struct private_data {+	enum {  SELECT_STREAM,+		READ_DEFAULT_STREAM,+		READ_DEFAULT_BLOCK,+		READ_LEGACY_STREAM,+		READ_LEGACY_BLOCK,+	}		stage;+	struct {+		unsigned block_independence:1;+		unsigned block_checksum:3;+		unsigned stream_size:1;+		unsigned stream_checksum:1;+		unsigned preset_dictionary:1;+		int	 block_maximum_size;+	} flags;+	int64_t		 stream_size;+	uint32_t	 dict_id;+	char		*out_block;+	size_t		 out_block_size;++	/* Bytes read but not yet consumed via __archive_read_consume() */+	size_t		 unconsumed;+	size_t		 decoded_size;+	void		*xxh32_state;++	char		 valid; /* True = decompressor is initialized */+	char		 eof; /* True = found end of compressed data. */+};++#define LEGACY_BLOCK_SIZE	(8 * 1024 * 1024)++/* Lz4 filter */+static ssize_t	lz4_filter_read(struct archive_read_filter *, const void **);+static int	lz4_filter_close(struct archive_read_filter *);+#endif++/*+ * Note that we can detect lz4 archives even if we can't decompress+ * them.  (In fact, we like detecting them because we can give better+ * error messages.)  So the bid framework here gets compiled even+ * if liblz4 is unavailable.+ */+static int	lz4_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);+static int	lz4_reader_init(struct archive_read_filter *);+static int	lz4_reader_free(struct archive_read_filter_bidder *);+#if defined(HAVE_LIBLZ4)+static ssize_t  lz4_filter_read_default_stream(struct archive_read_filter *,+		    const void **);+static ssize_t  lz4_filter_read_legacy_stream(struct archive_read_filter *,+		    const void **);+#endif++int+archive_read_support_filter_lz4(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *reader;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_lz4");++	if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	reader->data = NULL;+	reader->name = "lz4";+	reader->bid = lz4_reader_bid;+	reader->init = lz4_reader_init;+	reader->options = NULL;+	reader->free = lz4_reader_free;+#if defined(HAVE_LIBLZ4)+	return (ARCHIVE_OK);+#else+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external lz4 program");+	return (ARCHIVE_WARN);+#endif+}++static int+lz4_reader_free(struct archive_read_filter_bidder *self){+	(void)self; /* UNUSED */+	return (ARCHIVE_OK);+}++/*+ * Test whether we can handle this data.+ *+ * This logic returns zero if any part of the signature fails.  It+ * also tries to Do The Right Thing if a very short buffer prevents us+ * from verifying as much as we would like.+ */+static int+lz4_reader_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{+	const unsigned char *buffer;+	ssize_t avail;+	int bits_checked;+	uint32_t number;++	(void)self; /* UNUSED */++	/* Minimal lz4 archive is 11 bytes. */+	buffer = __archive_read_filter_ahead(filter, 11, &avail);+	if (buffer == NULL)+		return (0);++	/* First four bytes must be LZ4 magic numbers. */+	bits_checked = 0;+	if ((number = archive_le32dec(buffer)) == LZ4_MAGICNUMBER) {+		unsigned char flag, BD;++		bits_checked += 32;+		/* Next follows a stream descriptor. */+		/* Descriptor Flags. */+		flag = buffer[4];+		/* A version number must be "01". */+		if (((flag & 0xc0) >> 6) != 1)+			return (0);+		/* A reserved bit must be "0". */+		if (flag & 2)+			return (0);+		bits_checked += 8;+		BD = buffer[5];+		/* A block maximum size should be more than 3. */+		if (((BD & 0x70) >> 4) < 4)+			return (0);+		/* Reserved bits must be "0". */+		if (BD & ~0x70)+			return (0);+		bits_checked += 8;+	} else if (number == LZ4_LEGACY) {+		bits_checked += 32;+	}+	+	return (bits_checked);+}++#if !defined(HAVE_LIBLZ4)++/*+ * If we don't have the library on this system, we can't actually do the+ * decompression.  We can, however, still detect compressed archives+ * and emit a useful message.+ */+static int+lz4_reader_init(struct archive_read_filter *self)+{+	int r;++	r = __archive_read_program(self, "lz4 -d -q");+	/* Note: We set the format here even if __archive_read_program()+	 * above fails.  We do, after all, know what the format is+	 * even if we weren't able to read it. */+	self->code = ARCHIVE_FILTER_LZ4;+	self->name = "lz4";+	return (r);+}+++#else++/*+ * Setup the callbacks.+ */+static int+lz4_reader_init(struct archive_read_filter *self)+{+	struct private_data *state;++	self->code = ARCHIVE_FILTER_LZ4;+	self->name = "lz4";++	state = (struct private_data *)calloc(sizeof(*state), 1);+	if (state == NULL) {+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Can't allocate data for lz4 decompression");+		return (ARCHIVE_FATAL);+	}++	self->data = state;+	state->stage = SELECT_STREAM;+	self->read = lz4_filter_read;+	self->skip = NULL; /* not supported */+	self->close = lz4_filter_close;++	return (ARCHIVE_OK);+}++static int+lz4_allocate_out_block(struct archive_read_filter *self)+{+	struct private_data *state = (struct private_data *)self->data;+	size_t out_block_size = state->flags.block_maximum_size;+	void *out_block;++	if (!state->flags.block_independence)+		out_block_size += 64 * 1024;+	if (state->out_block_size < out_block_size) {+		free(state->out_block);+		out_block = (unsigned char *)malloc(out_block_size);+		state->out_block_size = out_block_size;+		if (out_block == NULL) {+			archive_set_error(&self->archive->archive, ENOMEM,+			    "Can't allocate data for lz4 decompression");+			return (ARCHIVE_FATAL);+		}+		state->out_block = out_block;+	}+	if (!state->flags.block_independence)+		memset(state->out_block, 0, 64 * 1024);+	return (ARCHIVE_OK);+}++static int+lz4_allocate_out_block_for_legacy(struct archive_read_filter *self)+{+	struct private_data *state = (struct private_data *)self->data;+	size_t out_block_size = LEGACY_BLOCK_SIZE;+	void *out_block;++	if (state->out_block_size < out_block_size) {+		free(state->out_block);+		out_block = (unsigned char *)malloc(out_block_size);+		state->out_block_size = out_block_size;+		if (out_block == NULL) {+			archive_set_error(&self->archive->archive, ENOMEM,+			    "Can't allocate data for lz4 decompression");+			return (ARCHIVE_FATAL);+		}+		state->out_block = out_block;+	}+	return (ARCHIVE_OK);+}++/*+ * Return the next block of decompressed data.+ */+static ssize_t+lz4_filter_read(struct archive_read_filter *self, const void **p)+{+	struct private_data *state = (struct private_data *)self->data;+	ssize_t ret;++	if (state->eof) {+		*p = NULL;+		return (0);+	}++	__archive_read_filter_consume(self->upstream, state->unconsumed);+	state->unconsumed = 0;++	switch (state->stage) {+	case SELECT_STREAM:+		break;+	case READ_DEFAULT_STREAM:+	case READ_LEGACY_STREAM:+		/* Reading a lz4 stream already failed. */+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC, "Invalid sequence.");+		return (ARCHIVE_FATAL);+	case READ_DEFAULT_BLOCK:+		ret = lz4_filter_read_default_stream(self, p);+		if (ret != 0 || state->stage != SELECT_STREAM)+			return ret;+		break;+	case READ_LEGACY_BLOCK:+		ret = lz4_filter_read_legacy_stream(self, p);+		if (ret != 0 || state->stage != SELECT_STREAM)+			return ret;+		break;+	default:+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC, "Program error.");+		return (ARCHIVE_FATAL);+		break;+	}++	while (state->stage == SELECT_STREAM) {+		const char *read_buf;++		/* Read a magic number. */+		read_buf = __archive_read_filter_ahead(self->upstream, 4,+				NULL);+		if (read_buf == NULL) {+			state->eof = 1;+			*p = NULL;+			return (0);+		}+		uint32_t number = archive_le32dec(read_buf);+		__archive_read_filter_consume(self->upstream, 4);+		if (number == LZ4_MAGICNUMBER)+			return lz4_filter_read_default_stream(self, p);+		else if (number == LZ4_LEGACY)+			return lz4_filter_read_legacy_stream(self, p);+		else if ((number & ~0xF) == LZ4_SKIPPABLED) {+			read_buf = __archive_read_filter_ahead(+				self->upstream, 4, NULL);+			if (read_buf == NULL) {+				archive_set_error(+				    &self->archive->archive,+		    		    ARCHIVE_ERRNO_MISC,+				    "Malformed lz4 data");+				return (ARCHIVE_FATAL);+			}+			uint32_t skip_bytes = archive_le32dec(read_buf);+			__archive_read_filter_consume(self->upstream,+				4 + skip_bytes);+		} else {+			/* Ignore following unrecognized data. */+			state->eof = 1;+			*p = NULL;+			return (0);+		}+	}+	state->eof = 1;+	*p = NULL;+	return (0);+}++static int+lz4_filter_read_descriptor(struct archive_read_filter *self)+{+	struct private_data *state = (struct private_data *)self->data;+	const char *read_buf;+	ssize_t bytes_remaining;+	ssize_t descriptor_bytes;+	unsigned char flag, bd;+	unsigned int chsum, chsum_verifier;++	/* Make sure we have 2 bytes for flags. */+	read_buf = __archive_read_filter_ahead(self->upstream, 2,+	    &bytes_remaining);+	if (read_buf == NULL) {+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC,+		    "truncated lz4 input");+		return (ARCHIVE_FATAL);+	}++	/*+	   Parse flags.+	 */+	flag = (unsigned char)read_buf[0];+	/* Verify version number. */+	if ((flag & 0xc0) != 1<<6)+		goto malformed_error;+	/* A reserved bit must be zero. */+	if (flag & 0x02)+		goto malformed_error;+	state->flags.block_independence = (flag & 0x20) != 0;+	state->flags.block_checksum = (flag & 0x10)?4:0;+	state->flags.stream_size = (flag & 0x08) != 0;+	state->flags.stream_checksum = (flag & 0x04) != 0;+	state->flags.preset_dictionary = (flag & 0x01) != 0;++	/* BD */+	bd = (unsigned char)read_buf[1];+	/* Reserved bits must be zero. */+	if (bd & 0x8f)+		goto malformed_error;+	/* Get a maximum block size. */+	switch (read_buf[1] >> 4) {+	case 4: /* 64 KB */+		state->flags.block_maximum_size = 64 * 1024;+		break;+	case 5: /* 256 KB */+		state->flags.block_maximum_size = 256 * 1024;+		break;+	case 6: /* 1 MB */+		state->flags.block_maximum_size = 1024 * 1024;+		break;+	case 7: /* 4 MB */+		state->flags.block_maximum_size = 4 * 1024 * 1024;+		break;+	default:+		goto malformed_error;+	}++	/* Read the whole descriptor in a stream block. */+	descriptor_bytes = 3;+	if (state->flags.stream_size)+		descriptor_bytes += 8;+	if (state->flags.preset_dictionary)+		descriptor_bytes += 4;+	if (bytes_remaining < descriptor_bytes) {+		read_buf = __archive_read_filter_ahead(self->upstream,+		    descriptor_bytes, &bytes_remaining);+		if (read_buf == NULL) {+			archive_set_error(&self->archive->archive,+			    ARCHIVE_ERRNO_MISC,+			    "truncated lz4 input");+			return (ARCHIVE_FATAL);+		}+	}+	/* Check if a descriptor is corrupted */+	chsum = __archive_xxhash.XXH32(read_buf, (int)descriptor_bytes -1, 0);+	chsum = (chsum >> 8) & 0xff;+	chsum_verifier = read_buf[descriptor_bytes-1] & 0xff;+	if (chsum != chsum_verifier)+		goto malformed_error;++	__archive_read_filter_consume(self->upstream, descriptor_bytes);++	/* Make sure we have a large enough buffer for uncompressed data. */+	if (lz4_allocate_out_block(self) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	if (state->flags.stream_checksum)+		state->xxh32_state = __archive_xxhash.XXH32_init(0);++	state->decoded_size = 0;+	/* Success */+	return (ARCHIVE_OK);+malformed_error:+	archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+	    "malformed lz4 data");+	return (ARCHIVE_FATAL);+}++static ssize_t+lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)+{+	struct private_data *state = (struct private_data *)self->data;+	ssize_t compressed_size;+	const char *read_buf;+	ssize_t bytes_remaining;+	int checksum_size;+	ssize_t uncompressed_size;+	size_t prefix64k;++	*p = NULL;++	/* Make sure we have 4 bytes for a block size. */+	read_buf = __archive_read_filter_ahead(self->upstream, 4,+	    &bytes_remaining);+	if (read_buf == NULL)+		goto truncated_error;+	compressed_size = archive_le32dec(read_buf);+	if ((compressed_size & 0x7fffffff) > state->flags.block_maximum_size)+		goto malformed_error;+	/* A compressed size == 0 means the end of stream blocks. */+	if (compressed_size == 0) {+		__archive_read_filter_consume(self->upstream, 4);+		return 0;+	}++	checksum_size = state->flags.block_checksum;+	/* Check if the block is uncompressed. */+	if (compressed_size & 0x80000000U) {+		compressed_size &= 0x7fffffff;+		uncompressed_size = compressed_size;+	} else+		uncompressed_size = 0;/* Unknown yet. */++	/*+	  Unfortunately, lz4 decompression API requires a whole block+	  for its decompression speed, so we read a whole block and allocate+	  a huge buffer used for decoded data.+	*/+	read_buf = __archive_read_filter_ahead(self->upstream,+	    4 + compressed_size + checksum_size, &bytes_remaining);+	if (read_buf == NULL)+		goto truncated_error;++	/* Optional processing, checking a block sum. */+	if (checksum_size) {+		unsigned int chsum = __archive_xxhash.XXH32(+			read_buf + 4, (int)compressed_size, 0);+		unsigned int chsum_block =+		    archive_le32dec(read_buf + 4 + compressed_size);+		if (chsum != chsum_block)+			goto malformed_error;+	}+++	/* If the block is uncompressed, there is nothing to do. */+	if (uncompressed_size) {+		/* Prepare a prefix 64k block for next block. */+		if (!state->flags.block_independence) {+			prefix64k = 64 * 1024;+			if (uncompressed_size < (ssize_t)prefix64k) {+				memcpy(state->out_block+					+ prefix64k - uncompressed_size,+				    read_buf + 4,+				    uncompressed_size);+				memset(state->out_block, 0,+				    prefix64k - uncompressed_size);+			} else {+				memcpy(state->out_block,+				    read_buf + 4+					+ uncompressed_size - prefix64k,+				    prefix64k);+			}+			state->decoded_size = 0;+		}+		state->unconsumed = 4 + uncompressed_size + checksum_size;+		*p = read_buf + 4;+		return uncompressed_size;+	}++	/*+	   Decompress a block data.+	 */+	if (state->flags.block_independence) {+		prefix64k = 0;+		uncompressed_size = LZ4_decompress_safe(read_buf + 4,+		    state->out_block, (int)compressed_size,+		    state->flags.block_maximum_size);+	} else {+		prefix64k = 64 * 1024;+		if (state->decoded_size) {+			if (state->decoded_size < prefix64k) {+				memmove(state->out_block+					+ prefix64k - state->decoded_size,+				    state->out_block + prefix64k,+				    state->decoded_size);+				memset(state->out_block, 0,+				    prefix64k - state->decoded_size);+			} else {+				memmove(state->out_block,+				    state->out_block + state->decoded_size,+				    prefix64k);+			}+		}+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7+		uncompressed_size = LZ4_decompress_safe_usingDict(+		    read_buf + 4,+		    state->out_block + prefix64k, (int)compressed_size,+		    state->flags.block_maximum_size,+		    state->out_block,+		    prefix64k);+#else+		uncompressed_size = LZ4_decompress_safe_withPrefix64k(+		    read_buf + 4,+		    state->out_block + prefix64k, (int)compressed_size,+		    state->flags.block_maximum_size);+#endif+	}++	/* Check if an error occurred in the decompression process. */+	if (uncompressed_size < 0) {+		archive_set_error(&(self->archive->archive),+		    ARCHIVE_ERRNO_MISC, "lz4 decompression failed");+		return (ARCHIVE_FATAL);+	}++	state->unconsumed = 4 + compressed_size + checksum_size;+	*p = state->out_block + prefix64k;+	state->decoded_size = uncompressed_size;+	return uncompressed_size;++malformed_error:+	archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+	    "malformed lz4 data");+	return (ARCHIVE_FATAL);+truncated_error:+	archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+	    "truncated lz4 input");+	return (ARCHIVE_FATAL);+}++static ssize_t+lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p)+{+	struct private_data *state = (struct private_data *)self->data;+	const char *read_buf;+	ssize_t bytes_remaining;+	ssize_t ret;++	if (state->stage == SELECT_STREAM) {+		state->stage = READ_DEFAULT_STREAM;+		/* First, read a descriptor. */+		if((ret = lz4_filter_read_descriptor(self)) != ARCHIVE_OK)+			return (ret);+		state->stage = READ_DEFAULT_BLOCK;+	}+	/* Decompress a block. */+	ret = lz4_filter_read_data_block(self, p);++	/* If the end of block is detected, change the filter status+	   to read next stream. */+	if (ret == 0 && *p == NULL)+		state->stage = SELECT_STREAM;++	/* Optional processing, checking a stream sum. */+	if (state->flags.stream_checksum) {+		if (state->stage == SELECT_STREAM) {+			unsigned int checksum;+			unsigned int checksum_stream;+			read_buf = __archive_read_filter_ahead(self->upstream,+			    4, &bytes_remaining);+			if (read_buf == NULL) {+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_MISC, "truncated lz4 input");+				return (ARCHIVE_FATAL);+			}+			checksum = archive_le32dec(read_buf);+			__archive_read_filter_consume(self->upstream, 4);+			checksum_stream = __archive_xxhash.XXH32_digest(+			    state->xxh32_state);+			state->xxh32_state = NULL;+			if (checksum != checksum_stream) {+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_MISC,+				    "lz4 stream checksum error");+				return (ARCHIVE_FATAL);+			}+		} else if (ret > 0)+			__archive_xxhash.XXH32_update(state->xxh32_state,+			    *p, (int)ret);+	}+	return (ret);+}++static ssize_t+lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p)+{+	struct private_data *state = (struct private_data *)self->data;+	uint32_t compressed;+	const char *read_buf;+	ssize_t ret;++	*p = NULL;+	ret = lz4_allocate_out_block_for_legacy(self);+	if (ret != ARCHIVE_OK)+		return ret;++	/* Make sure we have 4 bytes for a block size. */+	read_buf = __archive_read_filter_ahead(self->upstream, 4, NULL);+	if (read_buf == NULL) {+		if (state->stage == SELECT_STREAM) {+			state->stage = READ_LEGACY_STREAM;+			archive_set_error(&self->archive->archive,+			    ARCHIVE_ERRNO_MISC,+			    "truncated lz4 input");+			return (ARCHIVE_FATAL);+		}+		state->stage = SELECT_STREAM;+		return 0;+	}+	state->stage = READ_LEGACY_BLOCK;+	compressed = archive_le32dec(read_buf);+	if (compressed > LZ4_COMPRESSBOUND(LEGACY_BLOCK_SIZE)) {+		state->stage = SELECT_STREAM;+		return 0;+	}++	/* Make sure we have a whole block. */+	read_buf = __archive_read_filter_ahead(self->upstream,+	    4 + compressed, NULL);+	if (read_buf == NULL) {+		archive_set_error(&(self->archive->archive),+		    ARCHIVE_ERRNO_MISC, "truncated lz4 input");+		return (ARCHIVE_FATAL);+	}+	ret = LZ4_decompress_safe(read_buf + 4, state->out_block,+	    compressed, (int)state->out_block_size);+	if (ret < 0) {+		archive_set_error(&(self->archive->archive),+		    ARCHIVE_ERRNO_MISC, "lz4 decompression failed");+		return (ARCHIVE_FATAL);+	}+	*p = state->out_block;+	state->unconsumed = 4 + compressed;+	return ret;+}++/*+ * Clean up the decompressor.+ */+static int+lz4_filter_close(struct archive_read_filter *self)+{+	struct private_data *state;+	int ret = ARCHIVE_OK;++	state = (struct private_data *)self->data;+	free(state->xxh32_state);+	free(state->out_block);+	free(state);+	return (ret);+}++#endif /* HAVE_LIBLZ4 */
+ c/archive_read_support_filter_lzop.c view
@@ -0,0 +1,494 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");++#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#ifdef HAVE_LZO_LZOCONF_H+#include <lzo/lzoconf.h>+#endif+#ifdef HAVE_LZO_LZO1X_H+#include <lzo/lzo1x.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h> /* for crc32 and adler32 */+#endif++#include "archive.h"+#if !defined(HAVE_ZLIB_H) &&\+     defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)+#include "archive_crc32.h"+#endif+#include "archive_endian.h"+#include "archive_private.h"+#include "archive_read_private.h"++#ifndef HAVE_ZLIB_H+#define adler32	lzo_adler32+#endif++#define LZOP_HEADER_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a"+#define LZOP_HEADER_MAGIC_LEN 9++#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)+struct read_lzop {+	unsigned char	*out_block;+	size_t		 out_block_size;+	int64_t		 total_out;+	int		 flags;+	uint32_t	 compressed_cksum;+	uint32_t	 uncompressed_cksum;+	size_t		 compressed_size;+	size_t		 uncompressed_size;+	size_t		 unconsumed_bytes;+	char		 in_stream;+	char		 eof; /* True = found end of compressed data. */+};++#define FILTER			0x0800+#define CRC32_HEADER		0x1000+#define EXTRA_FIELD		0x0040+#define ADLER32_UNCOMPRESSED	0x0001+#define ADLER32_COMPRESSED	0x0002+#define CRC32_UNCOMPRESSED	0x0100+#define CRC32_COMPRESSED	0x0200+#define MAX_BLOCK_SIZE		(64 * 1024 * 1024)++static ssize_t  lzop_filter_read(struct archive_read_filter *, const void **);+static int	lzop_filter_close(struct archive_read_filter *);+#endif++static int lzop_bidder_bid(struct archive_read_filter_bidder *,+    struct archive_read_filter *);+static int lzop_bidder_init(struct archive_read_filter *);++int+archive_read_support_filter_lzop(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *reader;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_lzop");++	if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	reader->data = NULL;+	reader->bid = lzop_bidder_bid;+	reader->init = lzop_bidder_init;+	reader->options = NULL;+	reader->free = NULL;+	/* Signal the extent of lzop support with the return value here. */+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)+	return (ARCHIVE_OK);+#else+	/* Return ARCHIVE_WARN since this always uses an external program. */+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external lzop program for lzop decompression");+	return (ARCHIVE_WARN);+#endif+}++/*+ * Bidder just verifies the header and returns the number of verified bits.+ */+static int+lzop_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{+	const unsigned char *p;+	ssize_t avail;++	(void)self; /* UNUSED */++	p = __archive_read_filter_ahead(filter, LZOP_HEADER_MAGIC_LEN, &avail);+	if (p == NULL || avail == 0)+		return (0);++	if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN))+		return (0);++	return (LZOP_HEADER_MAGIC_LEN * 8);+}++#if !defined(HAVE_LZO_LZOCONF_H) || !defined(HAVE_LZO_LZO1X_H)+/*+ * If we don't have the library on this system, we can't do the+ * decompression directly.  We can, however, try to run "lzop -d"+ * in case that's available.+ */+static int+lzop_bidder_init(struct archive_read_filter *self)+{+	int r;++	r = __archive_read_program(self, "lzop -d");+	/* Note: We set the format here even if __archive_read_program()+	 * above fails.  We do, after all, know what the format is+	 * even if we weren't able to read it. */+	self->code = ARCHIVE_FILTER_LZOP;+	self->name = "lzop";+	return (r);+}+#else+/*+ * Initialize the filter object.+ */+static int+lzop_bidder_init(struct archive_read_filter *self)+{+	struct read_lzop *state;++	self->code = ARCHIVE_FILTER_LZOP;+	self->name = "lzop";++	state = (struct read_lzop *)calloc(sizeof(*state), 1);+	if (state == NULL) {+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Can't allocate data for lzop decompression");+		return (ARCHIVE_FATAL);+	}++	self->data = state;+	self->read = lzop_filter_read;+	self->skip = NULL; /* not supported */+	self->close = lzop_filter_close;++	return (ARCHIVE_OK);+}++static int+consume_header(struct archive_read_filter *self)+{+	struct read_lzop *state = (struct read_lzop *)self->data;+	const unsigned char *p, *_p;+	unsigned checksum, flags, len, method, version;++	/*+	 * Check LZOP magic code.+	 */+	p = __archive_read_filter_ahead(self->upstream,+		LZOP_HEADER_MAGIC_LEN, NULL);+	if (p == NULL)+		return (ARCHIVE_EOF);++	if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN))+		return (ARCHIVE_EOF);+	__archive_read_filter_consume(self->upstream,+	    LZOP_HEADER_MAGIC_LEN);++	p = __archive_read_filter_ahead(self->upstream, 29, NULL);+	if (p == NULL)+		goto truncated;+	_p = p;+	version = archive_be16dec(p);+	p += 4;/* version(2 bytes) + library version(2 bytes) */++	if (version >= 0x940) {+		unsigned reqversion = archive_be16dec(p); p += 2;+		if (reqversion < 0x900) {+			archive_set_error(&self->archive->archive,+			    ARCHIVE_ERRNO_MISC, "Invalid required version");+			return (ARCHIVE_FAILED);+		}+	}++	method = *p++;+	if (method < 1 || method > 3) {+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+		    "Unsupported method");+		return (ARCHIVE_FAILED);+	}++	if (version >= 0x940) {+		unsigned level = *p++;+#if 0+		unsigned default_level[] = {0, 3, 1, 9};+#endif+		if (level == 0)+			/* Method is 1..3 here due to check above. */+#if 0	/* Avoid an error Clang Static Analyzer claims+	  "Value stored to 'level' is never read". */+			level = default_level[method];+#else+			;/* NOP */+#endif+		else if (level > 9) {+			archive_set_error(&self->archive->archive,+			    ARCHIVE_ERRNO_MISC, "Invalid level");+			return (ARCHIVE_FAILED);+		}+	}++	flags = archive_be32dec(p); p += 4;++	if (flags & FILTER)+		p += 4; /* Skip filter */+	p += 4; /* Skip mode */+	if (version >= 0x940)+		p += 8; /* Skip mtime */+	else+		p += 4; /* Skip mtime */+	len = *p++; /* Read filename length */+	len += p - _p;+	/* Make sure we have all bytes we need to calculate checksum. */+	p = __archive_read_filter_ahead(self->upstream, len + 4, NULL);+	if (p == NULL)+		goto truncated;+	if (flags & CRC32_HEADER)+		checksum = crc32(crc32(0, NULL, 0), p, len);+	else+		checksum = adler32(adler32(0, NULL, 0), p, len);+	if (archive_be32dec(p + len) != checksum)+		goto corrupted;+	__archive_read_filter_consume(self->upstream, len + 4);+	if (flags & EXTRA_FIELD) {+		/* Skip extra field */+		p = __archive_read_filter_ahead(self->upstream, 4, NULL);+		if (p == NULL)+			goto truncated;+		len = archive_be32dec(p);+		__archive_read_filter_consume(self->upstream, len + 4 + 4);+	}+	state->flags = flags;+	state->in_stream = 1;+	return (ARCHIVE_OK);+truncated:+	archive_set_error(&self->archive->archive,+	    ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");+	return (ARCHIVE_FAILED);+corrupted:+	archive_set_error(&self->archive->archive,+	    ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");+	return (ARCHIVE_FAILED);+}++static int+consume_block_info(struct archive_read_filter *self)+{+	struct read_lzop *state = (struct read_lzop *)self->data;+	const unsigned char *p;+	unsigned flags = state->flags;++	p = __archive_read_filter_ahead(self->upstream, 4, NULL);+	if (p == NULL)+		goto truncated;+	state->uncompressed_size = archive_be32dec(p);+	__archive_read_filter_consume(self->upstream, 4);+	if (state->uncompressed_size == 0)+		return (ARCHIVE_EOF);+	if (state->uncompressed_size > MAX_BLOCK_SIZE)+		goto corrupted;++	p = __archive_read_filter_ahead(self->upstream, 4, NULL);+	if (p == NULL)+		goto truncated;+	state->compressed_size = archive_be32dec(p);+	__archive_read_filter_consume(self->upstream, 4);+	if (state->compressed_size > state->uncompressed_size)+		goto corrupted;++	if (flags & (CRC32_UNCOMPRESSED | ADLER32_UNCOMPRESSED)) {+		p = __archive_read_filter_ahead(self->upstream, 4, NULL);+		if (p == NULL)+			goto truncated;+		state->compressed_cksum = state->uncompressed_cksum =+		    archive_be32dec(p);+		__archive_read_filter_consume(self->upstream, 4);+	}+	if ((flags & (CRC32_COMPRESSED | ADLER32_COMPRESSED)) &&+	    state->compressed_size < state->uncompressed_size) {+		p = __archive_read_filter_ahead(self->upstream, 4, NULL);+		if (p == NULL)+			goto truncated;+		state->compressed_cksum = archive_be32dec(p);+		__archive_read_filter_consume(self->upstream, 4);+	}+	return (ARCHIVE_OK);+truncated:+	archive_set_error(&self->archive->archive,+	    ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");+	return (ARCHIVE_FAILED);+corrupted:+	archive_set_error(&self->archive->archive,+	    ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");+	return (ARCHIVE_FAILED);+}++static ssize_t+lzop_filter_read(struct archive_read_filter *self, const void **p)+{+	struct read_lzop *state = (struct read_lzop *)self->data;+	const void *b;+	lzo_uint out_size;+	uint32_t cksum;+	int ret, r;++	if (state->unconsumed_bytes) {+		__archive_read_filter_consume(self->upstream,+		    state->unconsumed_bytes);+		state->unconsumed_bytes = 0;+	}+	if (state->eof)+		return (0);++	for (;;) {+		if (!state->in_stream) {+			ret = consume_header(self);+			if (ret < ARCHIVE_OK)+				return (ret);+			if (ret == ARCHIVE_EOF) {+				state->eof = 1;+				return (0);+			}+		}+		ret = consume_block_info(self);+		if (ret < ARCHIVE_OK)+			return (ret);+		if (ret == ARCHIVE_EOF)+			state->in_stream = 0;+		else+			break;+	}++	if (state->out_block == NULL ||+	    state->out_block_size < state->uncompressed_size) {+		void *new_block;++		new_block = realloc(state->out_block, state->uncompressed_size);+		if (new_block == NULL) {+			archive_set_error(&self->archive->archive, ENOMEM,+			    "Can't allocate data for lzop decompression");+			return (ARCHIVE_FATAL);+		}+		state->out_block = new_block;+		state->out_block_size = state->uncompressed_size;+	}++	b = __archive_read_filter_ahead(self->upstream,+		state->compressed_size, NULL);+	if (b == NULL) {+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");+		return (ARCHIVE_FATAL);+	}+	if (state->flags & CRC32_COMPRESSED)+		cksum = crc32(crc32(0, NULL, 0), b, state->compressed_size);+	else if (state->flags & ADLER32_COMPRESSED)+		cksum = adler32(adler32(0, NULL, 0), b, state->compressed_size);+	else+		cksum = state->compressed_cksum;+	if (cksum != state->compressed_cksum) {+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC, "Corrupted data");+		return (ARCHIVE_FATAL);+	}++	/*+	 * If the both uncompressed size and compressed size are the same,+	 * we do not decompress this block.+	 */+	if (state->uncompressed_size == state->compressed_size) {+		*p = b;+		state->total_out += state->compressed_size;+		state->unconsumed_bytes = state->compressed_size;+		return ((ssize_t)state->uncompressed_size);+	}++	/*+	 * Drive lzo uncompression.+	 */+	out_size = (lzo_uint)state->uncompressed_size;+	r = lzo1x_decompress_safe(b, (lzo_uint)state->compressed_size,+		state->out_block, &out_size, NULL);+	switch (r) {+	case LZO_E_OK:+		if (out_size == state->uncompressed_size)+			break;+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC, "Corrupted data");+		return (ARCHIVE_FATAL);+	case LZO_E_OUT_OF_MEMORY:+		archive_set_error(&self->archive->archive, ENOMEM,+		    "lzop decompression failed: out of memory");+		return (ARCHIVE_FATAL);+	default:+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+		    "lzop decompression failed: %d", r);+		return (ARCHIVE_FATAL);+	}++	if (state->flags & CRC32_UNCOMPRESSED)+		cksum = crc32(crc32(0, NULL, 0), state->out_block,+		    state->uncompressed_size);+	else if (state->flags & ADLER32_UNCOMPRESSED)+		cksum = adler32(adler32(0, NULL, 0), state->out_block,+		    state->uncompressed_size);+	else+		cksum = state->uncompressed_cksum;+	if (cksum != state->uncompressed_cksum) {+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC, "Corrupted data");+		return (ARCHIVE_FATAL);+	}++	__archive_read_filter_consume(self->upstream, state->compressed_size);+	*p = state->out_block;+	state->total_out += out_size;+	return ((ssize_t)out_size);+}++/*+ * Clean up the decompressor.+ */+static int+lzop_filter_close(struct archive_read_filter *self)+{+	struct read_lzop *state = (struct read_lzop *)self->data;++	free(state->out_block);+	free(state);+	return (ARCHIVE_OK);+}++#endif
+ c/archive_read_support_filter_none.c view
@@ -0,0 +1,52 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#include "archive.h"+#include "archive_private.h"++#if ARCHIVE_VERSION_NUMBER < 4000000+/* Deprecated; remove in libarchive 4.0 */+int+archive_read_support_compression_none(struct archive *a)+{+	return archive_read_support_filter_none(a);+}+#endif++/*+ * Uncompressed streams are handled implicitly by the read core,+ * so this is now a no-op.+ */+int+archive_read_support_filter_none(struct archive *a)+{+	archive_check_magic(a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_none");++	return (ARCHIVE_OK);+}
+ c/archive_read_support_filter_program.c view
@@ -0,0 +1,503 @@+/*-+ * Copyright (c) 2007 Joerg Sonnenberger+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_SYS_WAIT_H+#  include <sys/wait.h>+#endif+#ifdef HAVE_ERRNO_H+#  include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#  include <fcntl.h>+#endif+#ifdef HAVE_LIMITS_H+#  include <limits.h>+#endif+#ifdef HAVE_SIGNAL_H+#  include <signal.h>+#endif+#ifdef HAVE_STDLIB_H+#  include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#  include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#  include <unistd.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_string.h"+#include "archive_read_private.h"+#include "filter_fork.h"+++#if ARCHIVE_VERSION_NUMBER < 4000000+/* Deprecated; remove in libarchive 4.0 */+int+archive_read_support_compression_program(struct archive *a, const char *cmd)+{+	return archive_read_support_filter_program(a, cmd);+}++int+archive_read_support_compression_program_signature(struct archive *a,+    const char *cmd, const void *signature, size_t signature_len)+{+	return archive_read_support_filter_program_signature(a,+	    cmd, signature, signature_len);+}+#endif++int+archive_read_support_filter_program(struct archive *a, const char *cmd)+{+	return (archive_read_support_filter_program_signature(a, cmd, NULL, 0));+}++/*+ * The bidder object stores the command and the signature to watch for.+ * The 'inhibit' entry here is used to ensure that unchecked filters never+ * bid twice in the same pipeline.+ */+struct program_bidder {+	char *description;+	char *cmd;+	void *signature;+	size_t signature_len;+	int inhibit;+};++static int	program_bidder_bid(struct archive_read_filter_bidder *,+		    struct archive_read_filter *upstream);+static int	program_bidder_init(struct archive_read_filter *);+static int	program_bidder_free(struct archive_read_filter_bidder *);++/*+ * The actual filter needs to track input and output data.+ */+struct program_filter {+	struct archive_string description;+#if defined(_WIN32) && !defined(__CYGWIN__)+	HANDLE		 child;+#else+	pid_t		 child;+#endif+	int		 exit_status;+	int		 waitpid_return;+	int		 child_stdin, child_stdout;++	char		*out_buf;+	size_t		 out_buf_len;+};++static ssize_t	program_filter_read(struct archive_read_filter *,+		    const void **);+static int	program_filter_close(struct archive_read_filter *);+static void	free_state(struct program_bidder *);++static int+set_bidder_signature(struct archive_read_filter_bidder *bidder,+    struct program_bidder *state, const void *signature, size_t signature_len)+{++	if (signature != NULL && signature_len > 0) {+		state->signature_len = signature_len;+		state->signature = malloc(signature_len);+		memcpy(state->signature, signature, signature_len);+	}++	/*+	 * Fill in the bidder object.+	 */+	bidder->data = state;+	bidder->bid = program_bidder_bid;+	bidder->init = program_bidder_init;+	bidder->options = NULL;+	bidder->free = program_bidder_free;+	return (ARCHIVE_OK);+}++int+archive_read_support_filter_program_signature(struct archive *_a,+    const char *cmd, const void *signature, size_t signature_len)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *bidder;+	struct program_bidder *state;++	/*+	 * Get a bidder object from the read core.+	 */+	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/*+	 * Allocate our private state.+	 */+	state = (struct program_bidder *)calloc(1, sizeof (*state));+	if (state == NULL)+		goto memerr;+	state->cmd = strdup(cmd);+	if (state->cmd == NULL)+		goto memerr;++	return set_bidder_signature(bidder, state, signature, signature_len);+memerr:+	free_state(state);+	archive_set_error(_a, ENOMEM, "Can't allocate memory");+	return (ARCHIVE_FATAL);+}++static int+program_bidder_free(struct archive_read_filter_bidder *self)+{+	struct program_bidder *state = (struct program_bidder *)self->data;++	free_state(state);+	return (ARCHIVE_OK);+}++static void+free_state(struct program_bidder *state)+{++	if (state) {+		free(state->cmd);+		free(state->signature);+		free(state);+	}+}++/*+ * If we do have a signature, bid only if that matches.+ *+ * If there's no signature, we bid INT_MAX the first time+ * we're called, then never bid again.+ */+static int+program_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *upstream)+{+	struct program_bidder *state = self->data;+	const char *p;++	/* If we have a signature, use that to match. */+	if (state->signature_len > 0) {+		p = __archive_read_filter_ahead(upstream,+		    state->signature_len, NULL);+		if (p == NULL)+			return (0);+		/* No match, so don't bid. */+		if (memcmp(p, state->signature, state->signature_len) != 0)+			return (0);+		return ((int)state->signature_len * 8);+	}++	/* Otherwise, bid once and then never bid again. */+	if (state->inhibit)+		return (0);+	state->inhibit = 1;+	return (INT_MAX);+}++/*+ * Shut down the child, return ARCHIVE_OK if it exited normally.+ *+ * Note that the return value is sticky; if we're called again,+ * we won't reap the child again, but we will return the same status+ * (including error message if the child came to a bad end).+ */+static int+child_stop(struct archive_read_filter *self, struct program_filter *state)+{+	/* Close our side of the I/O with the child. */+	if (state->child_stdin != -1) {+		close(state->child_stdin);+		state->child_stdin = -1;+	}+	if (state->child_stdout != -1) {+		close(state->child_stdout);+		state->child_stdout = -1;+	}++	if (state->child != 0) {+		/* Reap the child. */+		do {+			state->waitpid_return+			    = waitpid(state->child, &state->exit_status, 0);+		} while (state->waitpid_return == -1 && errno == EINTR);+#if defined(_WIN32) && !defined(__CYGWIN__)+		CloseHandle(state->child);+#endif+		state->child = 0;+	}++	if (state->waitpid_return < 0) {+		/* waitpid() failed?  This is ugly. */+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+		    "Child process exited badly");+		return (ARCHIVE_WARN);+	}++#if !defined(_WIN32) || defined(__CYGWIN__)+	if (WIFSIGNALED(state->exit_status)) {+#ifdef SIGPIPE+		/* If the child died because we stopped reading before+		 * it was done, that's okay.  Some archive formats+		 * have padding at the end that we routinely ignore. */+		/* The alternative to this would be to add a step+		 * before close(child_stdout) above to read from the+		 * child until the child has no more to write. */+		if (WTERMSIG(state->exit_status) == SIGPIPE)+			return (ARCHIVE_OK);+#endif+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+		    "Child process exited with signal %d",+		    WTERMSIG(state->exit_status));+		return (ARCHIVE_WARN);+	}+#endif /* !_WIN32 || __CYGWIN__ */++	if (WIFEXITED(state->exit_status)) {+		if (WEXITSTATUS(state->exit_status) == 0)+			return (ARCHIVE_OK);++		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Child process exited with status %d",+		    WEXITSTATUS(state->exit_status));+		return (ARCHIVE_WARN);+	}++	return (ARCHIVE_WARN);+}++/*+ * Use select() to decide whether the child is ready for read or write.+ */+static ssize_t+child_read(struct archive_read_filter *self, char *buf, size_t buf_len)+{+	struct program_filter *state = self->data;+	ssize_t ret, requested, avail;+	const char *p;+#if defined(_WIN32) && !defined(__CYGWIN__)+	HANDLE handle = (HANDLE)_get_osfhandle(state->child_stdout);+#endif++	requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len;++	for (;;) {+		do {+#if defined(_WIN32) && !defined(__CYGWIN__)+			/* Avoid infinity wait.+			 * Note: If there is no data in the pipe, ReadFile()+			 * called in read() never returns and so we won't+			 * write remaining encoded data to the pipe.+			 * Note: This way may cause performance problem.+			 * we are looking forward to great code to resolve+			 * this.  */+			DWORD pipe_avail = -1;+			int cnt = 2;++			while (PeekNamedPipe(handle, NULL, 0, NULL,+			    &pipe_avail, NULL) != 0 && pipe_avail == 0 &&+			    cnt--)+				Sleep(5);+			if (pipe_avail == 0) {+				ret = -1;+				errno = EAGAIN;+				break;+			}+#endif+			ret = read(state->child_stdout, buf, requested);+		} while (ret == -1 && errno == EINTR);++		if (ret > 0)+			return (ret);+		if (ret == 0 || (ret == -1 && errno == EPIPE))+			/* Child has closed its output; reap the child+			 * and return the status. */+			return (child_stop(self, state));+		if (ret == -1 && errno != EAGAIN)+			return (-1);++		if (state->child_stdin == -1) {+			/* Block until child has some I/O ready. */+			__archive_check_child(state->child_stdin,+			    state->child_stdout);+			continue;+		}++		/* Get some more data from upstream. */+		p = __archive_read_filter_ahead(self->upstream, 1, &avail);+		if (p == NULL) {+			close(state->child_stdin);+			state->child_stdin = -1;+			fcntl(state->child_stdout, F_SETFL, 0);+			if (avail < 0)+				return (avail);+			continue;+		}++		do {+			ret = write(state->child_stdin, p, avail);+		} while (ret == -1 && errno == EINTR);++		if (ret > 0) {+			/* Consume whatever we managed to write. */+			__archive_read_filter_consume(self->upstream, ret);+		} else if (ret == -1 && errno == EAGAIN) {+			/* Block until child has some I/O ready. */+			__archive_check_child(state->child_stdin,+			    state->child_stdout);+		} else {+			/* Write failed. */+			close(state->child_stdin);+			state->child_stdin = -1;+			fcntl(state->child_stdout, F_SETFL, 0);+			/* If it was a bad error, we're done; otherwise+			 * it was EPIPE or EOF, and we can still read+			 * from the child. */+			if (ret == -1 && errno != EPIPE)+				return (-1);+		}+	}+}++int+__archive_read_program(struct archive_read_filter *self, const char *cmd)+{+	struct program_filter	*state;+	static const size_t out_buf_len = 65536;+	char *out_buf;+	const char *prefix = "Program: ";+	int ret;+	size_t l;++	l = strlen(prefix) + strlen(cmd) + 1;+	state = (struct program_filter *)calloc(1, sizeof(*state));+	out_buf = (char *)malloc(out_buf_len);+	if (state == NULL || out_buf == NULL ||+	    archive_string_ensure(&state->description, l) == NULL) {+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Can't allocate input data");+		if (state != NULL) {+			archive_string_free(&state->description);+			free(state);+		}+		free(out_buf);+		return (ARCHIVE_FATAL);+	}+	archive_strcpy(&state->description, prefix);+	archive_strcat(&state->description, cmd);++	self->code = ARCHIVE_FILTER_PROGRAM;+	self->name = state->description.s;++	state->out_buf = out_buf;+	state->out_buf_len = out_buf_len;++	ret = __archive_create_child(cmd, &state->child_stdin,+	    &state->child_stdout, &state->child);+	if (ret != ARCHIVE_OK) {+		free(state->out_buf);+		archive_string_free(&state->description);+		free(state);+		archive_set_error(&self->archive->archive, EINVAL,+		    "Can't initialize filter; unable to run program \"%s\"",+		    cmd);+		return (ARCHIVE_FATAL);+	}++	self->data = state;+	self->read = program_filter_read;+	self->skip = NULL;+	self->close = program_filter_close;++	/* XXX Check that we can read at least one byte? */+	return (ARCHIVE_OK);+}++static int+program_bidder_init(struct archive_read_filter *self)+{+	struct program_bidder   *bidder_state;++	bidder_state = (struct program_bidder *)self->bidder->data;+	return (__archive_read_program(self, bidder_state->cmd));+}++static ssize_t+program_filter_read(struct archive_read_filter *self, const void **buff)+{+	struct program_filter *state;+	ssize_t bytes;+	size_t total;+	char *p;++	state = (struct program_filter *)self->data;++	total = 0;+	p = state->out_buf;+	while (state->child_stdout != -1 && total < state->out_buf_len) {+		bytes = child_read(self, p, state->out_buf_len - total);+		if (bytes < 0)+			/* No recovery is possible if we can no longer+			 * read from the child. */+			return (ARCHIVE_FATAL);+		if (bytes == 0)+			/* We got EOF from the child. */+			break;+		total += bytes;+		p += bytes;+	}++	*buff = state->out_buf;+	return (total);+}++static int+program_filter_close(struct archive_read_filter *self)+{+	struct program_filter	*state;+	int e;++	state = (struct program_filter *)self->data;+	e = child_stop(self, state);++	/* Release our private data. */+	free(state->out_buf);+	archive_string_free(&state->description);+	free(state);++	return (e);+}
+ c/archive_read_support_filter_rpm.c view
@@ -0,0 +1,289 @@+/*-+ * Copyright (c) 2009 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif++#include "archive.h"+#include "archive_endian.h"+#include "archive_private.h"+#include "archive_read_private.h"++struct rpm {+	int64_t		 total_in;+	size_t		 hpos;+	size_t		 hlen;+	unsigned char	 header[16];+	enum {+		ST_LEAD,	/* Skipping 'Lead' section. */+		ST_HEADER,	/* Reading 'Header' section;+				 * first 16 bytes. */+		ST_HEADER_DATA,	/* Skipping 'Header' section. */+		ST_PADDING,	/* Skipping padding data after the+				 * 'Header' section. */+		ST_ARCHIVE	/* Reading 'Archive' section. */+	}		 state;+	int		 first_header;+};+#define RPM_LEAD_SIZE	96	/* Size of 'Lead' section. */++static int	rpm_bidder_bid(struct archive_read_filter_bidder *,+		    struct archive_read_filter *);+static int	rpm_bidder_init(struct archive_read_filter *);++static ssize_t	rpm_filter_read(struct archive_read_filter *,+		    const void **);+static int	rpm_filter_close(struct archive_read_filter *);++#if ARCHIVE_VERSION_NUMBER < 4000000+/* Deprecated; remove in libarchive 4.0 */+int+archive_read_support_compression_rpm(struct archive *a)+{+	return archive_read_support_filter_rpm(a);+}+#endif++int+archive_read_support_filter_rpm(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *bidder;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_rpm");++	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	bidder->data = NULL;+	bidder->name = "rpm";+	bidder->bid = rpm_bidder_bid;+	bidder->init = rpm_bidder_init;+	bidder->options = NULL;+	bidder->free = NULL;+	return (ARCHIVE_OK);+}++static int+rpm_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{+	const unsigned char *b;+	ssize_t avail;+	int bits_checked;++	(void)self; /* UNUSED */++	b = __archive_read_filter_ahead(filter, 8, &avail);+	if (b == NULL)+		return (0);++	bits_checked = 0;+	/*+	 * Verify Header Magic Bytes : 0XED 0XAB 0XEE 0XDB+	 */+	if (memcmp(b, "\xED\xAB\xEE\xDB", 4) != 0)+		return (0);+	bits_checked += 32;+	/*+	 * Check major version.+	 */+	if (b[4] != 3 && b[4] != 4)+		return (0);+	bits_checked += 8;+	/*+	 * Check package type; binary or source.+	 */+	if (b[6] != 0)+		return (0);+	bits_checked += 8;+	if (b[7] != 0 && b[7] != 1)+		return (0);+	bits_checked += 8;++	return (bits_checked);+}++static int+rpm_bidder_init(struct archive_read_filter *self)+{+	struct rpm   *rpm;++	self->code = ARCHIVE_FILTER_RPM;+	self->name = "rpm";+	self->read = rpm_filter_read;+	self->skip = NULL; /* not supported */+	self->close = rpm_filter_close;++	rpm = (struct rpm *)calloc(sizeof(*rpm), 1);+	if (rpm == NULL) {+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Can't allocate data for rpm");+		return (ARCHIVE_FATAL);+	}++	self->data = rpm;+	rpm->state = ST_LEAD;++	return (ARCHIVE_OK);+}++static ssize_t+rpm_filter_read(struct archive_read_filter *self, const void **buff)+{+	struct rpm *rpm;+	const unsigned char *b;+	ssize_t avail_in, total;+	size_t used, n;+	uint32_t section;+	uint32_t bytes;++	rpm = (struct rpm *)self->data;+	*buff = NULL;+	total = avail_in = 0;+	b = NULL;+	used = 0;+	do {+		if (b == NULL) {+			b = __archive_read_filter_ahead(self->upstream, 1,+			    &avail_in);+			if (b == NULL) {+				if (avail_in < 0)+					return (ARCHIVE_FATAL);+				else+					break;+			}+		}++		switch (rpm->state) {+		case ST_LEAD:+			if (rpm->total_in + avail_in < RPM_LEAD_SIZE)+				used += avail_in;+			else {+				n = (size_t)(RPM_LEAD_SIZE - rpm->total_in);+				used += n;+				b += n;+				rpm->state = ST_HEADER;+				rpm->hpos = 0;+				rpm->hlen = 0;+				rpm->first_header = 1;+			}+			break;+		case ST_HEADER:+			n = 16 - rpm->hpos;+			if (n > avail_in - used)+				n = avail_in - used;+			memcpy(rpm->header+rpm->hpos, b, n);+			b += n;+			used += n;+			rpm->hpos += n;++			if (rpm->hpos == 16) {+				if (rpm->header[0] != 0x8e ||+				    rpm->header[1] != 0xad ||+				    rpm->header[2] != 0xe8 ||+				    rpm->header[3] != 0x01) {+					if (rpm->first_header) {+						archive_set_error(+						    &self->archive->archive,+						    ARCHIVE_ERRNO_FILE_FORMAT,+						    "Unrecoginized rpm header");+						return (ARCHIVE_FATAL);+					}+					rpm->state = ST_ARCHIVE;+					*buff = rpm->header;+					total = rpm->hpos;+					break;+				}+				/* Calculate 'Header' length. */+				section = archive_be32dec(rpm->header+8);+				bytes = archive_be32dec(rpm->header+12);+				rpm->hlen = 16 + section * 16 + bytes;+				rpm->state = ST_HEADER_DATA;+				rpm->first_header = 0;+			}+			break;+		case ST_HEADER_DATA:+			n = rpm->hlen - rpm->hpos;+			if (n > avail_in - used)+				n = avail_in - used;+			b += n;+			used += n;+			rpm->hpos += n;+			if (rpm->hpos == rpm->hlen)+				rpm->state = ST_PADDING;+			break;+		case ST_PADDING:+			while (used < (size_t)avail_in) {+				if (*b != 0) {+					/* Read next header. */+					rpm->state = ST_HEADER;+					rpm->hpos = 0;+					rpm->hlen = 0;+					break;+				}+				b++;+				used++;+			}+			break;+		case ST_ARCHIVE:+			*buff = b;+			total = avail_in;+			used = avail_in;+			break;+		}+		if (used == (size_t)avail_in) {+			rpm->total_in += used;+			__archive_read_filter_consume(self->upstream, used);+			b = NULL;+			used = 0;+		}+	} while (total == 0 && avail_in > 0);++	if (used > 0 && b != NULL) {+		rpm->total_in += used;+		__archive_read_filter_consume(self->upstream, used);+	}+	return (total);+}++static int+rpm_filter_close(struct archive_read_filter *self)+{+	struct rpm *rpm;++	rpm = (struct rpm *)self->data;+	free(rpm);++	return (ARCHIVE_OK);+}+
+ c/archive_read_support_filter_uu.c view
@@ -0,0 +1,685 @@+/*-+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_read_private.h"++/* Maximum lookahead during bid phase */+#define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */++struct uudecode {+	int64_t		 total;+	unsigned char	*in_buff;+#define IN_BUFF_SIZE	(1024)+	int		 in_cnt;+	size_t		 in_allocated;+	unsigned char	*out_buff;+#define OUT_BUFF_SIZE	(64 * 1024)+	int		 state;+#define ST_FIND_HEAD	0+#define ST_READ_UU	1+#define ST_UUEND	2+#define ST_READ_BASE64	3+#define ST_IGNORE	4+};++static int	uudecode_bidder_bid(struct archive_read_filter_bidder *,+		    struct archive_read_filter *filter);+static int	uudecode_bidder_init(struct archive_read_filter *);++static ssize_t	uudecode_filter_read(struct archive_read_filter *,+		    const void **);+static int	uudecode_filter_close(struct archive_read_filter *);++#if ARCHIVE_VERSION_NUMBER < 4000000+/* Deprecated; remove in libarchive 4.0 */+int+archive_read_support_compression_uu(struct archive *a)+{+	return archive_read_support_filter_uu(a);+}+#endif++int+archive_read_support_filter_uu(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *bidder;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_uu");++	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	bidder->data = NULL;+	bidder->name = "uu";+	bidder->bid = uudecode_bidder_bid;+	bidder->init = uudecode_bidder_init;+	bidder->options = NULL;+	bidder->free = NULL;+	return (ARCHIVE_OK);+}++static const unsigned char ascii[256] = {+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */+};++static const unsigned char uuchar[256] = {+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */+};++static const unsigned char base64[256] = {+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */+	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */+	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */+};++static const int base64num[128] = {+	 0,  0,  0,  0,  0,  0,  0,  0,+	 0,  0,  0,  0,  0,  0,  0,  0, /* 00 - 0F */+	 0,  0,  0,  0,  0,  0,  0,  0,+	 0,  0,  0,  0,  0,  0,  0,  0, /* 10 - 1F */+	 0,  0,  0,  0,  0,  0,  0,  0,+	 0,  0,  0, 62,  0,  0,  0, 63, /* 20 - 2F */+	52, 53, 54, 55, 56, 57, 58, 59,+	60, 61,  0,  0,  0,  0,  0,  0, /* 30 - 3F */+	 0,  0,  1,  2,  3,  4,  5,  6,+	 7,  8,  9, 10, 11, 12, 13, 14, /* 40 - 4F */+	15, 16, 17, 18, 19, 20, 21, 22,+	23, 24, 25,  0,  0,  0,  0,  0, /* 50 - 5F */+	 0, 26, 27, 28, 29, 30, 31, 32,+	33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */+	41, 42, 43, 44, 45, 46, 47, 48,+	49, 50, 51,  0,  0,  0,  0,  0, /* 70 - 7F */+};++static ssize_t+get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)+{+	ssize_t len;++	len = 0;+	while (len < avail) {+		switch (ascii[*b]) {+		case 0:	/* Non-ascii character or control character. */+			if (nlsize != NULL)+				*nlsize = 0;+			return (-1);+		case '\r':+			if (avail-len > 1 && b[1] == '\n') {+				if (nlsize != NULL)+					*nlsize = 2;+				return (len+2);+			}+			/* FALL THROUGH */+		case '\n':+			if (nlsize != NULL)+				*nlsize = 1;+			return (len+1);+		case 1:+			b++;+			len++;+			break;+		}+	}+	if (nlsize != NULL)+		*nlsize = 0;+	return (avail);+}++static ssize_t+bid_get_line(struct archive_read_filter *filter,+    const unsigned char **b, ssize_t *avail, ssize_t *ravail,+    ssize_t *nl, size_t* nbytes_read)+{+	ssize_t len;+	int quit;+	+	quit = 0;+	if (*avail == 0) {+		*nl = 0;+		len = 0;+	} else+		len = get_line(*b, *avail, nl);++	/*+	 * Read bytes more while it does not reach the end of line.+	 */+	while (*nl == 0 && len == *avail && !quit &&+	    *nbytes_read < UUENCODE_BID_MAX_READ) {+		ssize_t diff = *ravail - *avail;+		size_t nbytes_req = (*ravail+1023) & ~1023U;+		ssize_t tested;++		/* Increase reading bytes if it is not enough to at least+		 * new two lines. */+		if (nbytes_req < (size_t)*ravail + 160)+			nbytes_req <<= 1;++		*b = __archive_read_filter_ahead(filter, nbytes_req, avail);+		if (*b == NULL) {+			if (*ravail >= *avail)+				return (0);+			/* Reading bytes reaches the end of a stream. */+			*b = __archive_read_filter_ahead(filter, *avail, avail);+			quit = 1;+		}+		*nbytes_read = *avail;+		*ravail = *avail;+		*b += diff;+		*avail -= diff;+		tested = len;/* Skip some bytes we already determinated. */+		len = get_line(*b + tested, *avail - tested, nl);+		if (len >= 0)+			len += tested;+	}+	return (len);+}++#define UUDECODE(c) (((c) - 0x20) & 0x3f)++static int+uudecode_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{+	const unsigned char *b;+	ssize_t avail, ravail;+	ssize_t len, nl;+	int l;+	int firstline;+	size_t nbytes_read;++	(void)self; /* UNUSED */++	b = __archive_read_filter_ahead(filter, 1, &avail);+	if (b == NULL)+		return (0);++	firstline = 20;+	ravail = avail;+	nbytes_read = avail;+	for (;;) {+		len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);+		if (len < 0 || nl == 0)+			return (0); /* No match found. */+		if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)+			l = 6;+		else if (len -nl >= 18 && memcmp(b, "begin-base64 ", 13) == 0)+			l = 13;+		else+			l = 0;++		if (l > 0 && (b[l] < '0' || b[l] > '7' ||+		    b[l+1] < '0' || b[l+1] > '7' ||+		    b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))+			l = 0;++		b += len;+		avail -= len;+		if (l)+			break;+		firstline = 0;++		/* Do not read more than UUENCODE_BID_MAX_READ bytes */+		if (nbytes_read >= UUENCODE_BID_MAX_READ)+			return (0);+	}+	if (!avail)+		return (0);+	len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);+	if (len < 0 || nl == 0)+		return (0);/* There are non-ascii characters. */+	avail -= len;++	if (l == 6) {+		/* "begin " */+		if (!uuchar[*b])+			return (0);+		/* Get a length of decoded bytes. */+		l = UUDECODE(*b++); len--;+		if (l > 45)+			/* Normally, maximum length is 45(character 'M'). */+			return (0);+		if (l > len - nl)+			return (0); /* Line too short. */+		while (l) {+			if (!uuchar[*b++])+				return (0);+			--len;+			--l;+		}+		if (len-nl == 1 &&+		    (uuchar[*b] ||		 /* Check sum. */+		     (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */+			++b;+			--len;+		}+		b += nl;+		if (avail && uuchar[*b])+			return (firstline+30);+	} else if (l == 13) {+		/* "begin-base64 " */+		while (len-nl > 0) {+			if (!base64[*b++])+				return (0);+			--len;+		}+		b += nl;++		if (avail >= 5 && memcmp(b, "====\n", 5) == 0)+			return (firstline+40);+		if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)+			return (firstline+40);+		if (avail > 0 && base64[*b])+			return (firstline+30);+	}++	return (0);+}++static int+uudecode_bidder_init(struct archive_read_filter *self)+{+	struct uudecode   *uudecode;+	void *out_buff;+	void *in_buff;++	self->code = ARCHIVE_FILTER_UU;+	self->name = "uu";+	self->read = uudecode_filter_read;+	self->skip = NULL; /* not supported */+	self->close = uudecode_filter_close;++	uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1);+	out_buff = malloc(OUT_BUFF_SIZE);+	in_buff = malloc(IN_BUFF_SIZE);+	if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Can't allocate data for uudecode");+		free(uudecode);+		free(out_buff);+		free(in_buff);+		return (ARCHIVE_FATAL);+	}++	self->data = uudecode;+	uudecode->in_buff = in_buff;+	uudecode->in_cnt = 0;+	uudecode->in_allocated = IN_BUFF_SIZE;+	uudecode->out_buff = out_buff;+	uudecode->state = ST_FIND_HEAD;++	return (ARCHIVE_OK);+}++static int+ensure_in_buff_size(struct archive_read_filter *self,+    struct uudecode *uudecode, size_t size)+{++	if (size > uudecode->in_allocated) {+		unsigned char *ptr;+		size_t newsize;++		/*+		 * Calculate a new buffer size for in_buff.+		 * Increase its value until it has enough size we need.+		 */+		newsize = uudecode->in_allocated;+		do {+			if (newsize < IN_BUFF_SIZE*32)+				newsize <<= 1;+			else+				newsize += IN_BUFF_SIZE;+		} while (size > newsize);+		/* Allocate the new buffer. */+		ptr = malloc(newsize);+		if (ptr == NULL) {+			free(ptr);+			archive_set_error(&self->archive->archive,+			    ENOMEM,+    			    "Can't allocate data for uudecode");+			return (ARCHIVE_FATAL);+		}+		/* Move the remaining data in in_buff into the new buffer. */+		if (uudecode->in_cnt)+			memmove(ptr, uudecode->in_buff, uudecode->in_cnt);+		/* Replace in_buff with the new buffer. */+		free(uudecode->in_buff);+		uudecode->in_buff = ptr;+		uudecode->in_allocated = newsize;+	}+	return (ARCHIVE_OK);+}++static ssize_t+uudecode_filter_read(struct archive_read_filter *self, const void **buff)+{+	struct uudecode *uudecode;+	const unsigned char *b, *d;+	unsigned char *out;+	ssize_t avail_in, ravail;+	ssize_t used;+	ssize_t total;+	ssize_t len, llen, nl;++	uudecode = (struct uudecode *)self->data;++read_more:+	d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);+	if (d == NULL && avail_in < 0)+		return (ARCHIVE_FATAL);+	/* Quiet a code analyzer; make sure avail_in must be zero+	 * when d is NULL. */+	if (d == NULL)+		avail_in = 0;+	used = 0;+	total = 0;+	out = uudecode->out_buff;+	ravail = avail_in;+	if (uudecode->state == ST_IGNORE) {+		used = avail_in;+		goto finish;+	}+	if (uudecode->in_cnt) {+		/*+		 * If there is remaining data which is saved by+		 * previous calling, use it first.+		 */+		if (ensure_in_buff_size(self, uudecode,+		    avail_in + uudecode->in_cnt) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		memcpy(uudecode->in_buff + uudecode->in_cnt,+		    d, avail_in);+		d = uudecode->in_buff;+		avail_in += uudecode->in_cnt;+		uudecode->in_cnt = 0;+	}+	for (;used < avail_in; d += llen, used += llen) {+		int64_t l, body;++		b = d;+		len = get_line(b, avail_in - used, &nl);+		if (len < 0) {+			/* Non-ascii character is found. */+			if (uudecode->state == ST_FIND_HEAD &&+			    (uudecode->total > 0 || total > 0)) {+				uudecode->state = ST_IGNORE;+				used = avail_in;+				goto finish;+			}+			archive_set_error(&self->archive->archive,+			    ARCHIVE_ERRNO_MISC,+			    "Insufficient compressed data");+			return (ARCHIVE_FATAL);+		}+		llen = len;+		if ((nl == 0) && (uudecode->state != ST_UUEND)) {+			if (total == 0 && ravail <= 0) {+				/* There is nothing more to read, fail */+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Missing format data");+				return (ARCHIVE_FATAL);+			}+			/*+			 * Save remaining data which does not contain+			 * NL('\n','\r').+			 */+			if (ensure_in_buff_size(self, uudecode, len)+			    != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			if (uudecode->in_buff != b)+				memmove(uudecode->in_buff, b, len);+			uudecode->in_cnt = (int)len;+			if (total == 0) {+				/* Do not return 0; it means end-of-file.+				 * We should try to read bytes more. */+				__archive_read_filter_consume(+				    self->upstream, ravail);+				goto read_more;+			}+			used += len;+			break;+		}+		switch (uudecode->state) {+		default:+		case ST_FIND_HEAD:+			/* Do not read more than UUENCODE_BID_MAX_READ bytes */+			if (total + len >= UUENCODE_BID_MAX_READ) {+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Invalid format data");+				return (ARCHIVE_FATAL);+			}+			if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)+				l = 6;+			else if (len - nl >= 18 &&+			    memcmp(b, "begin-base64 ", 13) == 0)+				l = 13;+			else+				l = 0;+			if (l != 0 && b[l] >= '0' && b[l] <= '7' &&+			    b[l+1] >= '0' && b[l+1] <= '7' &&+			    b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {+				if (l == 6)+					uudecode->state = ST_READ_UU;+				else+					uudecode->state = ST_READ_BASE64;+			}+			break;+		case ST_READ_UU:+			if (total + len * 2 > OUT_BUFF_SIZE)+				goto finish;+			body = len - nl;+			if (!uuchar[*b] || body <= 0) {+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Insufficient compressed data");+				return (ARCHIVE_FATAL);+			}+			/* Get length of undecoded bytes of current line. */+			l = UUDECODE(*b++);+			body--;+			if (l > body) {+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Insufficient compressed data");+				return (ARCHIVE_FATAL);+			}+			if (l == 0) {+				uudecode->state = ST_UUEND;+				break;+			}+			while (l > 0) {+				int n = 0;++				if (!uuchar[b[0]] || !uuchar[b[1]])+					break;+				n = UUDECODE(*b++) << 18;+				n |= UUDECODE(*b++) << 12;+				*out++ = n >> 16; total++;+				--l;++				if (l > 0) {+					if (!uuchar[b[0]])+						break;+					n |= UUDECODE(*b++) << 6;+					*out++ = (n >> 8) & 0xFF; total++;+					--l;+				}+				if (l > 0) {+					if (!uuchar[b[0]])+						break;+					n |= UUDECODE(*b++);+					*out++ = n & 0xFF; total++;+					--l;+				}+			}+			if (l) {+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Insufficient compressed data");+				return (ARCHIVE_FATAL);+			}+			break;+		case ST_UUEND:+			if (len - nl == 3 && memcmp(b, "end ", 3) == 0)+				uudecode->state = ST_FIND_HEAD;+			else {+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Insufficient compressed data");+				return (ARCHIVE_FATAL);+			}+			break;+		case ST_READ_BASE64:+			if (total + len * 2 > OUT_BUFF_SIZE)+				goto finish;+			l = len - nl;+			if (l >= 3 && b[0] == '=' && b[1] == '=' &&+			    b[2] == '=') {+				uudecode->state = ST_FIND_HEAD;+				break;+			}+			while (l > 0) {+				int n = 0;++				if (!base64[b[0]] || !base64[b[1]])+					break;+				n = base64num[*b++] << 18;+				n |= base64num[*b++] << 12;+				*out++ = n >> 16; total++;+				l -= 2;++				if (l > 0) {+					if (*b == '=')+						break;+					if (!base64[*b])+						break;+					n |= base64num[*b++] << 6;+					*out++ = (n >> 8) & 0xFF; total++;+					--l;+				}+				if (l > 0) {+					if (*b == '=')+						break;+					if (!base64[*b])+						break;+					n |= base64num[*b++];+					*out++ = n & 0xFF; total++;+					--l;+				}+			}+			if (l && *b != '=') {+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Insufficient compressed data");+				return (ARCHIVE_FATAL);+			}+			break;+		}+	}+finish:+	if (ravail < avail_in)+		used -= avail_in - ravail;+	__archive_read_filter_consume(self->upstream, used);++	*buff = uudecode->out_buff;+	uudecode->total += total;+	return (total);+}++static int+uudecode_filter_close(struct archive_read_filter *self)+{+	struct uudecode *uudecode;++	uudecode = (struct uudecode *)self->data;+	free(uudecode->in_buff);+	free(uudecode->out_buff);+	free(uudecode);++	return (ARCHIVE_OK);+}+
+ c/archive_read_support_filter_xz.c view
@@ -0,0 +1,796 @@+/*-+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA+ * Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#if HAVE_LZMA_H+#include <lzma.h>+#endif++#include "archive.h"+#include "archive_endian.h"+#include "archive_private.h"+#include "archive_read_private.h"++#if HAVE_LZMA_H && HAVE_LIBLZMA++struct private_data {+	lzma_stream	 stream;+	unsigned char	*out_block;+	size_t		 out_block_size;+	int64_t		 total_out;+	char		 eof; /* True = found end of compressed data. */+	char		 in_stream;++	/* Following variables are used for lzip only. */+	char		 lzip_ver;+	uint32_t	 crc32;+	int64_t		 member_in;+	int64_t		 member_out;+};++#if LZMA_VERSION_MAJOR >= 5+/* Effectively disable the limiter. */+#define LZMA_MEMLIMIT	UINT64_MAX+#else+/* NOTE: This needs to check memory size which running system has. */+#define LZMA_MEMLIMIT	(1U << 30)+#endif++/* Combined lzip/lzma/xz filter */+static ssize_t	xz_filter_read(struct archive_read_filter *, const void **);+static int	xz_filter_close(struct archive_read_filter *);+static int	xz_lzma_bidder_init(struct archive_read_filter *);++#endif++/*+ * Note that we can detect xz and lzma compressed files even if we+ * can't decompress them.  (In fact, we like detecting them because we+ * can give better error messages.)  So the bid framework here gets+ * compiled even if no lzma library is available.+ */+static int	xz_bidder_bid(struct archive_read_filter_bidder *,+		    struct archive_read_filter *);+static int	xz_bidder_init(struct archive_read_filter *);+static int	lzma_bidder_bid(struct archive_read_filter_bidder *,+		    struct archive_read_filter *);+static int	lzma_bidder_init(struct archive_read_filter *);+static int	lzip_has_member(struct archive_read_filter *);+static int	lzip_bidder_bid(struct archive_read_filter_bidder *,+		    struct archive_read_filter *);+static int	lzip_bidder_init(struct archive_read_filter *);++#if ARCHIVE_VERSION_NUMBER < 4000000+/* Deprecated; remove in libarchive 4.0 */+int+archive_read_support_compression_xz(struct archive *a)+{+	return archive_read_support_filter_xz(a);+}+#endif++int+archive_read_support_filter_xz(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *bidder;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_xz");++	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	bidder->data = NULL;+	bidder->name = "xz";+	bidder->bid = xz_bidder_bid;+	bidder->init = xz_bidder_init;+	bidder->options = NULL;+	bidder->free = NULL;+#if HAVE_LZMA_H && HAVE_LIBLZMA+	return (ARCHIVE_OK);+#else+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external xz program for xz decompression");+	return (ARCHIVE_WARN);+#endif+}++#if ARCHIVE_VERSION_NUMBER < 4000000+int+archive_read_support_compression_lzma(struct archive *a)+{+	return archive_read_support_filter_lzma(a);+}+#endif++int+archive_read_support_filter_lzma(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *bidder;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_lzma");++	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	bidder->data = NULL;+	bidder->name = "lzma";+	bidder->bid = lzma_bidder_bid;+	bidder->init = lzma_bidder_init;+	bidder->options = NULL;+	bidder->free = NULL;+#if HAVE_LZMA_H && HAVE_LIBLZMA+	return (ARCHIVE_OK);+#else+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external lzma program for lzma decompression");+	return (ARCHIVE_WARN);+#endif+}+++#if ARCHIVE_VERSION_NUMBER < 4000000+int+archive_read_support_compression_lzip(struct archive *a)+{+	return archive_read_support_filter_lzip(a);+}+#endif++int+archive_read_support_filter_lzip(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *bidder;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_lzip");++	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	bidder->data = NULL;+	bidder->name = "lzip";+	bidder->bid = lzip_bidder_bid;+	bidder->init = lzip_bidder_init;+	bidder->options = NULL;+	bidder->free = NULL;+#if HAVE_LZMA_H && HAVE_LIBLZMA+	return (ARCHIVE_OK);+#else+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external lzip program for lzip decompression");+	return (ARCHIVE_WARN);+#endif+}++/*+ * Test whether we can handle this data.+ */+static int+xz_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{+	const unsigned char *buffer;+	ssize_t avail;++	(void)self; /* UNUSED */++	buffer = __archive_read_filter_ahead(filter, 6, &avail);+	if (buffer == NULL)+		return (0);++	/*+	 * Verify Header Magic Bytes : FD 37 7A 58 5A 00+	 */+	if (memcmp(buffer, "\xFD\x37\x7A\x58\x5A\x00", 6) != 0)+		return (0);++	return (48);+}++/*+ * Test whether we can handle this data.+ *+ * <sigh> LZMA has a rather poor file signature.  Zeros do not+ * make good signature bytes as a rule, and the only non-zero byte+ * here is an ASCII character.  For example, an uncompressed tar+ * archive whose first file is ']' would satisfy this check.  It may+ * be necessary to exclude LZMA from compression_all() because of+ * this.  Clients of libarchive would then have to explicitly enable+ * LZMA checking instead of (or in addition to) compression_all() when+ * they have other evidence (file name, command-line option) to go on.+ */+static int+lzma_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{+	const unsigned char *buffer;+	ssize_t avail;+	uint32_t dicsize;+	uint64_t uncompressed_size;+	int bits_checked;++	(void)self; /* UNUSED */++	buffer = __archive_read_filter_ahead(filter, 14, &avail);+	if (buffer == NULL)+		return (0);++	/* First byte of raw LZMA stream is commonly 0x5d.+	 * The first byte is a special number, which consists of+	 * three parameters of LZMA compression, a number of literal+	 * context bits(which is from 0 to 8, default is 3), a number+	 * of literal pos bits(which is from 0 to 4, default is 0),+	 * a number of pos bits(which is from 0 to 4, default is 2).+	 * The first byte is made by+	 * (pos bits * 5 + literal pos bit) * 9 + * literal contest bit,+	 * and so the default value in this field is+	 * (2 * 5 + 0) * 9 + 3 = 0x5d.+	 * lzma of LZMA SDK has options to change those parameters.+	 * It means a range of this field is from 0 to 224. And lzma of+	 * XZ Utils with option -e records 0x5e in this field. */+	/* NOTE: If this checking of the first byte increases false+	 * recognition, we should allow only 0x5d and 0x5e for the first+	 * byte of LZMA stream. */+	bits_checked = 0;+	if (buffer[0] > (4 * 5 + 4) * 9 + 8)+		return (0);+	/* Most likely value in the first byte of LZMA stream. */+	if (buffer[0] == 0x5d || buffer[0] == 0x5e)+		bits_checked += 8;++	/* Sixth through fourteenth bytes are uncompressed size,+	 * stored in little-endian order. `-1' means uncompressed+	 * size is unknown and lzma of XZ Utils always records `-1'+	 * in this field. */+	uncompressed_size = archive_le64dec(buffer+5);+	if (uncompressed_size == (uint64_t)ARCHIVE_LITERAL_LL(-1))+		bits_checked += 64;++	/* Second through fifth bytes are dictionary size, stored in+	 * little-endian order. The minimum dictionary size is+	 * 1 << 12(4KiB) which the lzma of LZMA SDK uses with option+	 * -d12 and the maximum dictionary size is 1 << 27(128MiB)+	 * which the one uses with option -d27.+	 * NOTE: A comment of LZMA SDK source code says this dictionary+	 * range is from 1 << 12 to 1 << 30. */+	dicsize = archive_le32dec(buffer+1);+	switch (dicsize) {+	case 0x00001000:/* lzma of LZMA SDK option -d12. */+	case 0x00002000:/* lzma of LZMA SDK option -d13. */+	case 0x00004000:/* lzma of LZMA SDK option -d14. */+	case 0x00008000:/* lzma of LZMA SDK option -d15. */+	case 0x00010000:/* lzma of XZ Utils option -0 and -1.+			 * lzma of LZMA SDK option -d16. */+	case 0x00020000:/* lzma of LZMA SDK option -d17. */+	case 0x00040000:/* lzma of LZMA SDK option -d18. */+	case 0x00080000:/* lzma of XZ Utils option -2.+			 * lzma of LZMA SDK option -d19. */+	case 0x00100000:/* lzma of XZ Utils option -3.+			 * lzma of LZMA SDK option -d20. */+	case 0x00200000:/* lzma of XZ Utils option -4.+			 * lzma of LZMA SDK option -d21. */+	case 0x00400000:/* lzma of XZ Utils option -5.+			 * lzma of LZMA SDK option -d22. */+	case 0x00800000:/* lzma of XZ Utils option -6.+			 * lzma of LZMA SDK option -d23. */+	case 0x01000000:/* lzma of XZ Utils option -7.+			 * lzma of LZMA SDK option -d24. */+	case 0x02000000:/* lzma of XZ Utils option -8.+			 * lzma of LZMA SDK option -d25. */+	case 0x04000000:/* lzma of XZ Utils option -9.+			 * lzma of LZMA SDK option -d26. */+	case 0x08000000:/* lzma of LZMA SDK option -d27. */+		bits_checked += 32;+		break;+	default:+		/* If a memory usage for encoding was not enough on+		 * the platform where LZMA stream was made, lzma of+		 * XZ Utils automatically decreased the dictionary+		 * size to enough memory for encoding by 1Mi bytes+		 * (1 << 20).*/+		if (dicsize <= 0x03F00000 && dicsize >= 0x00300000 &&+		    (dicsize & ((1 << 20)-1)) == 0 &&+		    bits_checked == 8 + 64) {+			bits_checked += 32;+			break;+		}+		/* Otherwise dictionary size is unlikely. But it is+		 * possible that someone makes lzma stream with+		 * liblzma/LZMA SDK in one's dictionary size. */+		return (0);+	}++	/* TODO: The above test is still very weak.  It would be+	 * good to do better. */++	return (bits_checked);+}++static int+lzip_has_member(struct archive_read_filter *filter)+{+	const unsigned char *buffer;+	ssize_t avail;+	int bits_checked;+	int log2dic;++	buffer = __archive_read_filter_ahead(filter, 6, &avail);+	if (buffer == NULL)+		return (0);++	/*+	 * Verify Header Magic Bytes : 4C 5A 49 50 (`LZIP')+	 */+	bits_checked = 0;+	if (memcmp(buffer, "LZIP", 4) != 0)+		return (0);+	bits_checked += 32;++	/* A version number must be 0 or 1 */+	if (buffer[4] != 0 && buffer[4] != 1)+		return (0);+	bits_checked += 8;++	/* Dictionary size. */+	log2dic = buffer[5] & 0x1f;+	if (log2dic < 12 || log2dic > 27)+		return (0);+	bits_checked += 8;++	return (bits_checked);+}++static int+lzip_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{++	(void)self; /* UNUSED */+	return (lzip_has_member(filter));+}++#if HAVE_LZMA_H && HAVE_LIBLZMA++/*+ * liblzma 4.999.7 and later support both lzma and xz streams.+ */+static int+xz_bidder_init(struct archive_read_filter *self)+{+	self->code = ARCHIVE_FILTER_XZ;+	self->name = "xz";+	return (xz_lzma_bidder_init(self));+}++static int+lzma_bidder_init(struct archive_read_filter *self)+{+	self->code = ARCHIVE_FILTER_LZMA;+	self->name = "lzma";+	return (xz_lzma_bidder_init(self));+}++static int+lzip_bidder_init(struct archive_read_filter *self)+{+	self->code = ARCHIVE_FILTER_LZIP;+	self->name = "lzip";+	return (xz_lzma_bidder_init(self));+}++/*+ * Set an error code and choose an error message+ */+static void+set_error(struct archive_read_filter *self, int ret)+{++	switch (ret) {+	case LZMA_STREAM_END: /* Found end of stream. */+	case LZMA_OK: /* Decompressor made some progress. */+		break;+	case LZMA_MEM_ERROR:+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Lzma library error: Cannot allocate memory");+		break;+	case LZMA_MEMLIMIT_ERROR:+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Lzma library error: Out of memory");+		break;+	case LZMA_FORMAT_ERROR:+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Lzma library error: format not recognized");+		break;+	case LZMA_OPTIONS_ERROR:+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Lzma library error: Invalid options");+		break;+	case LZMA_DATA_ERROR:+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Lzma library error: Corrupted input data");+		break;+	case LZMA_BUF_ERROR:+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Lzma library error:  No progress is possible");+		break;+	default:+		/* Return an error. */+		archive_set_error(&self->archive->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Lzma decompression failed:  Unknown error");+		break;+	}+}++/*+ * Setup the callbacks.+ */+static int+xz_lzma_bidder_init(struct archive_read_filter *self)+{+	static const size_t out_block_size = 64 * 1024;+	void *out_block;+	struct private_data *state;+	int ret;++	state = (struct private_data *)calloc(sizeof(*state), 1);+	out_block = (unsigned char *)malloc(out_block_size);+	if (state == NULL || out_block == NULL) {+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Can't allocate data for xz decompression");+		free(out_block);+		free(state);+		return (ARCHIVE_FATAL);+	}++	self->data = state;+	state->out_block_size = out_block_size;+	state->out_block = out_block;+	self->read = xz_filter_read;+	self->skip = NULL; /* not supported */+	self->close = xz_filter_close;++	state->stream.avail_in = 0;++	state->stream.next_out = state->out_block;+	state->stream.avail_out = state->out_block_size;++	state->crc32 = 0;+	if (self->code == ARCHIVE_FILTER_LZIP) {+		/*+		 * We have to read a lzip header and use it to initialize+		 * compression library, thus we cannot initialize the+		 * library for lzip here.+		 */+		state->in_stream = 0;+		return (ARCHIVE_OK);+	} else+		state->in_stream = 1;++	/* Initialize compression library. */+	if (self->code == ARCHIVE_FILTER_XZ)+		ret = lzma_stream_decoder(&(state->stream),+		    LZMA_MEMLIMIT,/* memlimit */+		    LZMA_CONCATENATED);+	else+		ret = lzma_alone_decoder(&(state->stream),+		    LZMA_MEMLIMIT);/* memlimit */++	if (ret == LZMA_OK)+		return (ARCHIVE_OK);++	/* Library setup failed: Choose an error message and clean up. */+	set_error(self, ret);++	free(state->out_block);+	free(state);+	self->data = NULL;+	return (ARCHIVE_FATAL);+}++static int+lzip_init(struct archive_read_filter *self)+{+	struct private_data *state;+	const unsigned char *h;+	lzma_filter filters[2];+	unsigned char props[5];+	ssize_t avail_in;+	uint32_t dicsize;+	int log2dic, ret;++	state = (struct private_data *)self->data;+	h = __archive_read_filter_ahead(self->upstream, 6, &avail_in);+	if (h == NULL)+		return (ARCHIVE_FATAL);++	/* Get a version number. */+	state->lzip_ver = h[4];++	/*+	 * Setup lzma property.+	 */+	props[0] = 0x5d;++	/* Get dictionary size. */+	log2dic = h[5] & 0x1f;+	if (log2dic < 12 || log2dic > 27)+		return (ARCHIVE_FATAL);+	dicsize = 1U << log2dic;+	if (log2dic > 12)+		dicsize -= (dicsize / 16) * (h[5] >> 5);+	archive_le32enc(props+1, dicsize);++	/* Consume lzip header. */+	__archive_read_filter_consume(self->upstream, 6);+	state->member_in = 6;++	filters[0].id = LZMA_FILTER_LZMA1;+	filters[0].options = NULL;+	filters[1].id = LZMA_VLI_UNKNOWN;+	filters[1].options = NULL;++	ret = lzma_properties_decode(&filters[0], NULL, props, sizeof(props));+	if (ret != LZMA_OK) {+		set_error(self, ret);+		return (ARCHIVE_FATAL);+	}+	ret = lzma_raw_decoder(&(state->stream), filters);+	free(filters[0].options);+	if (ret != LZMA_OK) {+		set_error(self, ret);+		return (ARCHIVE_FATAL);+	}+	return (ARCHIVE_OK);+}++static int+lzip_tail(struct archive_read_filter *self)+{+	struct private_data *state;+	const unsigned char *f;+	ssize_t avail_in;+	int tail;++	state = (struct private_data *)self->data;+	if (state->lzip_ver == 0)+		tail = 12;+	else+		tail = 20;+	f = __archive_read_filter_ahead(self->upstream, tail, &avail_in);+	if (f == NULL && avail_in < 0)+		return (ARCHIVE_FATAL);+	if (f == NULL || avail_in < tail) {+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+		    "Lzip: Remaining data is less bytes");+		return (ARCHIVE_FAILED);+	}++	/* Check the crc32 value of the uncompressed data of the current+	 * member */+	if (state->crc32 != archive_le32dec(f)) {+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+		    "Lzip: CRC32 error");+		return (ARCHIVE_FAILED);+	}++	/* Check the uncompressed size of the current member */+	if ((uint64_t)state->member_out != archive_le64dec(f + 4)) {+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+		    "Lzip: Uncompressed size error");+		return (ARCHIVE_FAILED);+	}++	/* Check the total size of the current member */+	if (state->lzip_ver == 1 &&+	    (uint64_t)state->member_in + tail != archive_le64dec(f + 12)) {+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,+		    "Lzip: Member size error");+		return (ARCHIVE_FAILED);+	}+	__archive_read_filter_consume(self->upstream, tail);++	/* If current lzip data consists of multi member, try decompressing+	 * a next member. */+	if (lzip_has_member(self->upstream) != 0) {+		state->in_stream = 0;+		state->crc32 = 0;+		state->member_out = 0;+		state->member_in = 0;+		state->eof = 0;+	}+	return (ARCHIVE_OK);+}++/*+ * Return the next block of decompressed data.+ */+static ssize_t+xz_filter_read(struct archive_read_filter *self, const void **p)+{+	struct private_data *state;+	size_t decompressed;+	ssize_t avail_in;+	int ret;++	state = (struct private_data *)self->data;++	/* Empty our output buffer. */+	state->stream.next_out = state->out_block;+	state->stream.avail_out = state->out_block_size;++	/* Try to fill the output buffer. */+	while (state->stream.avail_out > 0 && !state->eof) {+		if (!state->in_stream) {+			/*+			 * Initialize liblzma for lzip+			 */+			ret = lzip_init(self);+			if (ret != ARCHIVE_OK)+				return (ret);+			state->in_stream = 1;+		}+		state->stream.next_in =+		    __archive_read_filter_ahead(self->upstream, 1, &avail_in);+		if (state->stream.next_in == NULL && avail_in < 0) {+			archive_set_error(&self->archive->archive,+			    ARCHIVE_ERRNO_MISC,+			    "truncated input");+			return (ARCHIVE_FATAL);+		}+		state->stream.avail_in = avail_in;++		/* Decompress as much as we can in one pass. */+		ret = lzma_code(&(state->stream),+		    (state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN);+		switch (ret) {+		case LZMA_STREAM_END: /* Found end of stream. */+			state->eof = 1;+			/* FALL THROUGH */+		case LZMA_OK: /* Decompressor made some progress. */+			__archive_read_filter_consume(self->upstream,+			    avail_in - state->stream.avail_in);+			state->member_in +=+			    avail_in - state->stream.avail_in;+			break;+		default:+			set_error(self, ret);+			return (ARCHIVE_FATAL);+		}+	}++	decompressed = state->stream.next_out - state->out_block;+	state->total_out += decompressed;+	state->member_out += decompressed;+	if (decompressed == 0)+		*p = NULL;+	else {+		*p = state->out_block;+		if (self->code == ARCHIVE_FILTER_LZIP) {+			state->crc32 = lzma_crc32(state->out_block,+			    decompressed, state->crc32);+			if (state->eof) {+				ret = lzip_tail(self);+				if (ret != ARCHIVE_OK)+					return (ret);+			}+		}+	}+	return (decompressed);+}++/*+ * Clean up the decompressor.+ */+static int+xz_filter_close(struct archive_read_filter *self)+{+	struct private_data *state;++	state = (struct private_data *)self->data;+	lzma_end(&(state->stream));+	free(state->out_block);+	free(state);+	return (ARCHIVE_OK);+}++#else++/*+ *+ * If we have no suitable library on this system, we can't actually do+ * the decompression.  We can, however, still detect compressed+ * archives and emit a useful message.+ *+ */+static int+lzma_bidder_init(struct archive_read_filter *self)+{+	int r;++	r = __archive_read_program(self, "lzma -d -qq");+	/* Note: We set the format here even if __archive_read_program()+	 * above fails.  We do, after all, know what the format is+	 * even if we weren't able to read it. */+	self->code = ARCHIVE_FILTER_LZMA;+	self->name = "lzma";+	return (r);+}++static int+xz_bidder_init(struct archive_read_filter *self)+{+	int r;++	r = __archive_read_program(self, "xz -d -qq");+	/* Note: We set the format here even if __archive_read_program()+	 * above fails.  We do, after all, know what the format is+	 * even if we weren't able to read it. */+	self->code = ARCHIVE_FILTER_XZ;+	self->name = "xz";+	return (r);+}++static int+lzip_bidder_init(struct archive_read_filter *self)+{+	int r;++	r = __archive_read_program(self, "lzip -d -q");+	/* Note: We set the format here even if __archive_read_program()+	 * above fails.  We do, after all, know what the format is+	 * even if we weren't able to read it. */+	self->code = ARCHIVE_FILTER_LZIP;+	self->name = "lzip";+	return (r);+}++#endif /* HAVE_LZMA_H */
+ c/archive_read_support_filter_zstd.c view
@@ -0,0 +1,296 @@+/*-+ * Copyright (c) 2009-2011 Sean Purcell+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#if HAVE_ZSTD_H+#include <zstd.h>+#endif++#include "archive.h"+#include "archive_endian.h"+#include "archive_private.h"+#include "archive_read_private.h"++#if HAVE_ZSTD_H && HAVE_LIBZSTD++struct private_data {+	ZSTD_DStream	*dstream;+	unsigned char	*out_block;+	size_t		 out_block_size;+	int64_t		 total_out;+	char		 in_frame; /* True = in the middle of a zstd frame. */+	char		 eof; /* True = found end of compressed data. */+};++/* Zstd Filter. */+static ssize_t	zstd_filter_read(struct archive_read_filter *, const void**);+static int	zstd_filter_close(struct archive_read_filter *);+#endif++/*+ * Note that we can detect zstd compressed files even if we can't decompress+ * them.  (In fact, we like detecting them because we can give better error+ * messages.)  So the bid framework here gets compiled even if no zstd library+ * is available.+ */+static int	zstd_bidder_bid(struct archive_read_filter_bidder *,+		    struct archive_read_filter *);+static int	zstd_bidder_init(struct archive_read_filter *);++int+archive_read_support_filter_zstd(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct archive_read_filter_bidder *bidder;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_zstd");++	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	bidder->data = NULL;+	bidder->name = "zstd";+	bidder->bid = zstd_bidder_bid;+	bidder->init = zstd_bidder_init;+	bidder->options = NULL;+	bidder->free = NULL;+#if HAVE_ZSTD_H && HAVE_LIBZSTD+	return (ARCHIVE_OK);+#else+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external zstd program for zstd decompression");+	return (ARCHIVE_WARN);+#endif+}++/*+ * Test whether we can handle this data.+ */+static int+zstd_bidder_bid(struct archive_read_filter_bidder *self,+    struct archive_read_filter *filter)+{+	const unsigned char *buffer;+	ssize_t avail;+	unsigned prefix;++	/* Zstd frame magic values */+	const unsigned zstd_magic = 0xFD2FB528U;+	const unsigned zstd_magic_skippable_start = 0x184D2A50U;+	const unsigned zstd_magic_skippable_mask = 0xFFFFFFF0;++	(void) self; /* UNUSED */++	buffer = __archive_read_filter_ahead(filter, 4, &avail);+	if (buffer == NULL)+		return (0);++	prefix = archive_le32dec(buffer);+	if (prefix == zstd_magic)+		return (32);+	if ((prefix & zstd_magic_skippable_mask) == zstd_magic_skippable_start)+		return (32);++	return (0);+}++#if !(HAVE_ZSTD_H && HAVE_LIBZSTD)++/*+ * If we don't have the library on this system, we can't do the+ * decompression directly.  We can, however, try to run "zstd -d"+ * in case that's available.+ */+static int+zstd_bidder_init(struct archive_read_filter *self)+{+	int r;++	r = __archive_read_program(self, "zstd -d -qq");+	/* Note: We set the format here even if __archive_read_program()+	 * above fails.  We do, after all, know what the format is+	 * even if we weren't able to read it. */+	self->code = ARCHIVE_FILTER_ZSTD;+	self->name = "zstd";+	return (r);+}++#else++/*+ * Initialize the filter object+ */+static int+zstd_bidder_init(struct archive_read_filter *self)+{+	struct private_data *state;+	const size_t out_block_size = ZSTD_DStreamOutSize();+	void *out_block;+	ZSTD_DStream *dstream;++	self->code = ARCHIVE_FILTER_ZSTD;+	self->name = "zstd";++	state = (struct private_data *)calloc(sizeof(*state), 1);+	out_block = (unsigned char *)malloc(out_block_size);+	dstream = ZSTD_createDStream();++	if (state == NULL || out_block == NULL || dstream == NULL) {+		free(out_block);+		free(state);+		ZSTD_freeDStream(dstream); /* supports free on NULL */+		archive_set_error(&self->archive->archive, ENOMEM,+		    "Can't allocate data for zstd decompression");+		return (ARCHIVE_FATAL);+	}++	self->data = state;++	state->out_block_size = out_block_size;+	state->out_block = out_block;+	state->dstream = dstream;+	self->read = zstd_filter_read;+	self->skip = NULL; /* not supported */+	self->close = zstd_filter_close;++	state->eof = 0;+	state->in_frame = 0;++	return (ARCHIVE_OK);+}++static ssize_t+zstd_filter_read(struct archive_read_filter *self, const void **p)+{+	struct private_data *state;+	size_t decompressed;+	ssize_t avail_in;+	ZSTD_outBuffer out;+	ZSTD_inBuffer in;++	state = (struct private_data *)self->data;++	out = (ZSTD_outBuffer) { state->out_block, state->out_block_size, 0 };++	/* Try to fill the output buffer. */+	while (out.pos < out.size && !state->eof) {+		if (!state->in_frame) {+			const size_t ret = ZSTD_initDStream(state->dstream);+			if (ZSTD_isError(ret)) {+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Error initializing zstd decompressor: %s",+				    ZSTD_getErrorName(ret));+				return (ARCHIVE_FATAL);+			}+		}+		in.src = __archive_read_filter_ahead(self->upstream, 1,+		    &avail_in);+		if (avail_in < 0) {+			return avail_in;+		}+		if (in.src == NULL && avail_in == 0) {+			if (!state->in_frame) {+				/* end of stream */+				state->eof = 1;+				break;+			} else {+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Truncated zstd input");+				return (ARCHIVE_FATAL);+			}+		}+		in.size = avail_in;+		in.pos = 0;++		{+			const size_t ret =+			    ZSTD_decompressStream(state->dstream, &out, &in);++			if (ZSTD_isError(ret)) {+				archive_set_error(&self->archive->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Zstd decompression failed: %s",+				    ZSTD_getErrorName(ret));+				return (ARCHIVE_FATAL);+			}++			/* Decompressor made some progress */+			__archive_read_filter_consume(self->upstream, in.pos);++			/* ret guaranteed to be > 0 if frame isn't done yet */+			state->in_frame = (ret != 0);+		}+	}++	decompressed = out.pos;+	state->total_out += decompressed;+	if (decompressed == 0)+		*p = NULL;+	else+		*p = state->out_block;+	return (decompressed);+}++/*+ * Clean up the decompressor.+ */+static int+zstd_filter_close(struct archive_read_filter *self)+{+	struct private_data *state;++	state = (struct private_data *)self->data;++	ZSTD_freeDStream(state->dstream);+	free(state->out_block);+	free(state);++	return (ARCHIVE_OK);+}++#endif /* HAVE_ZLIB_H && HAVE_LIBZSTD */
+ c/archive_read_support_format_7zip.c view
@@ -0,0 +1,3873 @@+/*-+ * Copyright (c) 2011 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_BZLIB_H+#include <bzlib.h>+#endif+#ifdef HAVE_LZMA_H+#include <lzma.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_ppmd7_private.h"+#include "archive_private.h"+#include "archive_read_private.h"+#include "archive_endian.h"++#ifndef HAVE_ZLIB_H+#include "archive_crc32.h"+#endif++#define _7ZIP_SIGNATURE	"7z\xBC\xAF\x27\x1C"+#define SFX_MIN_ADDR	0x27000+#define SFX_MAX_ADDR	0x60000+++/*+ * Codec ID+ */+#define _7Z_COPY	0+#define _7Z_LZMA	0x030101+#define _7Z_LZMA2	0x21+#define _7Z_DEFLATE	0x040108+#define _7Z_BZ2		0x040202+#define _7Z_PPMD	0x030401+#define _7Z_DELTA	0x03+#define _7Z_CRYPTO_MAIN_ZIP			0x06F10101 /* Main Zip crypto algo */+#define _7Z_CRYPTO_RAR_29			0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */+#define _7Z_CRYPTO_AES_256_SHA_256	0x06F10701 /* AES-256 + SHA-256 */+++#define _7Z_X86		0x03030103+#define _7Z_X86_BCJ2	0x0303011B+#define _7Z_POWERPC	0x03030205+#define _7Z_IA64	0x03030401+#define _7Z_ARM		0x03030501+#define _7Z_ARMTHUMB	0x03030701+#define _7Z_SPARC	0x03030805++/*+ * 7-Zip header property IDs.+ */+#define kEnd			0x00+#define kHeader			0x01+#define kArchiveProperties	0x02+#define kAdditionalStreamsInfo	0x03+#define kMainStreamsInfo	0x04+#define kFilesInfo		0x05+#define kPackInfo		0x06+#define kUnPackInfo		0x07+#define kSubStreamsInfo		0x08+#define kSize			0x09+#define kCRC			0x0A+#define kFolder			0x0B+#define kCodersUnPackSize	0x0C+#define kNumUnPackStream	0x0D+#define kEmptyStream		0x0E+#define kEmptyFile		0x0F+#define kAnti			0x10+#define kName			0x11+#define kCTime			0x12+#define kATime			0x13+#define kMTime			0x14+#define kAttributes		0x15+#define kEncodedHeader		0x17+#define kDummy			0x19++struct _7z_digests {+	unsigned char	*defineds;+	uint32_t	*digests;+};+++struct _7z_folder {+	uint64_t		 numCoders;+	struct _7z_coder {+		unsigned long	 codec;+		uint64_t	 numInStreams;+		uint64_t	 numOutStreams;+		uint64_t	 propertiesSize;+		unsigned char	*properties;+	} *coders;+	uint64_t		 numBindPairs;+	struct {+		uint64_t	 inIndex;+		uint64_t	 outIndex;+	} *bindPairs;+	uint64_t		 numPackedStreams;+	uint64_t		*packedStreams;+	uint64_t		 numInStreams;+	uint64_t		 numOutStreams;+	uint64_t		*unPackSize;+	unsigned char		 digest_defined;+	uint32_t		 digest;+	uint64_t		 numUnpackStreams;+	uint32_t		 packIndex;+	/* Unoperated bytes. */+	uint64_t		 skipped_bytes;+};++struct _7z_coders_info {+	uint64_t		 numFolders;+	struct _7z_folder	*folders;+	uint64_t		 dataStreamIndex;+};++struct _7z_pack_info {+	uint64_t		 pos;+	uint64_t		 numPackStreams;+	uint64_t		*sizes;+	struct _7z_digests	 digest;+	/* Calculated from pos and numPackStreams. */+	uint64_t		*positions;+};++struct _7z_substream_info {+	size_t			 unpack_streams;+	uint64_t		*unpackSizes;+	unsigned char		*digestsDefined;+	uint32_t		*digests;+};++struct _7z_stream_info {+	struct _7z_pack_info	 pi;+	struct _7z_coders_info	 ci;+	struct _7z_substream_info ss;+};++struct _7z_header_info {+	uint64_t		 dataIndex;++	unsigned char		*emptyStreamBools;+	unsigned char		*emptyFileBools;+	unsigned char		*antiBools;+	unsigned char		*attrBools;+};++struct _7zip_entry {+	size_t			 name_len;+	unsigned char		*utf16name;+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)+	const wchar_t		*wname;+#endif+	uint32_t		 folderIndex;+	uint32_t		 ssIndex;+	unsigned		 flg;+#define MTIME_IS_SET	(1<<0)+#define ATIME_IS_SET	(1<<1)+#define CTIME_IS_SET	(1<<2)+#define CRC32_IS_SET	(1<<3)+#define HAS_STREAM	(1<<4)++	time_t			 mtime;+	time_t			 atime;+	time_t			 ctime;+	long			 mtime_ns;+	long			 atime_ns;+	long			 ctime_ns;+	uint32_t		 mode;+	uint32_t		 attr;+};++struct _7zip {+	/* Structural information about the archive. */+	struct _7z_stream_info	 si;++	int			 header_is_being_read;+	int			 header_is_encoded;+	uint64_t		 header_bytes_remaining;+	unsigned long		 header_crc32;+	/* Header offset to check that reading points of the file contents+	 * will not exceed the header. */+	uint64_t		 header_offset;+	/* Base offset of the archive file for a seek in case reading SFX. */+	uint64_t		 seek_base;++	/* List of entries */+	size_t			 entries_remaining;+	uint64_t		 numFiles;+	struct _7zip_entry	*entries;+	struct _7zip_entry	*entry;+	unsigned char		*entry_names;++	/* entry_bytes_remaining is the number of bytes we expect. */+	int64_t			 entry_offset;+	uint64_t		 entry_bytes_remaining;++	/* Running CRC32 of the decompressed data */+	unsigned long		 entry_crc32;++	/* Flags to mark progress of decompression. */+	char			 end_of_entry;++	/* Uncompressed buffer control.  */+#define UBUFF_SIZE	(64 * 1024)+	unsigned char 		*uncompressed_buffer;+	unsigned char 		*uncompressed_buffer_pointer;+	size_t 			 uncompressed_buffer_size;+	size_t			 uncompressed_buffer_bytes_remaining;++	/* Offset of the compressed data. */+	int64_t			 stream_offset;++	/*+	 * Decompressing control data.+	 */+	unsigned		 folder_index;+	uint64_t		 folder_outbytes_remaining;+	unsigned		 pack_stream_index;+	unsigned		 pack_stream_remaining;+	uint64_t		 pack_stream_inbytes_remaining;+	size_t			 pack_stream_bytes_unconsumed;++	/* The codec information of a folder. */+	unsigned long		 codec;+	unsigned long		 codec2;++	/*+	 * Decompressor controllers.+	 */+	/* Decoding LZMA1 and LZMA2 data. */+#ifdef HAVE_LZMA_H+	lzma_stream		 lzstream;+	int			 lzstream_valid;+#endif+	/* Decoding bzip2 data. */+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+	bz_stream		 bzstream;+	int			 bzstream_valid;+#endif+	/* Decoding deflate data. */+#ifdef HAVE_ZLIB_H+	z_stream		 stream;+	int			 stream_valid;+#endif+	/* Decoding PPMd data. */+	int			 ppmd7_stat;+	CPpmd7			 ppmd7_context;+	CPpmd7z_RangeDec	 range_dec;+	IByteIn			 bytein;+	struct {+		const unsigned char	*next_in;+		int64_t			 avail_in;+		int64_t			 total_in;+		unsigned char		*next_out;+		int64_t			 avail_out;+		int64_t			 total_out;+		int			 overconsumed;+	} ppstream;+	int			 ppmd7_valid;++	/* Decoding BCJ and BCJ2 data. */+	uint32_t		 bcj_state;+	size_t			 odd_bcj_size;+	unsigned char		 odd_bcj[4];+	/* Decoding BCJ data. */+	size_t			 bcj_prevPosT;+	uint32_t		 bcj_prevMask;+	uint32_t		 bcj_ip;++	/* Decoding BCJ2 data. */+	size_t			 main_stream_bytes_remaining;+	unsigned char		*sub_stream_buff[3];+	size_t			 sub_stream_size[3];+	size_t			 sub_stream_bytes_remaining[3];+	unsigned char		*tmp_stream_buff;+	size_t			 tmp_stream_buff_size;+	size_t			 tmp_stream_bytes_avail;+	size_t			 tmp_stream_bytes_remaining;+#ifdef _LZMA_PROB32+#define CProb uint32_t+#else+#define CProb uint16_t+#endif+	CProb			 bcj2_p[256 + 2];+	uint8_t			 bcj2_prevByte;+	uint32_t		 bcj2_range;+	uint32_t		 bcj2_code;+	uint64_t		 bcj2_outPos;++	/* Filename character-set conversion data. */+	struct archive_string_conv *sconv;++	char			 format_name[64];++	/* Custom value that is non-zero if this archive contains encrypted entries. */+	int			 has_encrypted_entries;+};++/* Maximum entry size. This limitation prevents reading intentional+ * corrupted 7-zip files on assuming there are not so many entries in+ * the files. */+#define UMAX_ENTRY	ARCHIVE_LITERAL_ULL(100000000)++static int	archive_read_format_7zip_has_encrypted_entries(struct archive_read *);+static int	archive_read_support_format_7zip_capabilities(struct archive_read *a);+static int	archive_read_format_7zip_bid(struct archive_read *, int);+static int	archive_read_format_7zip_cleanup(struct archive_read *);+static int	archive_read_format_7zip_read_data(struct archive_read *,+		    const void **, size_t *, int64_t *);+static int	archive_read_format_7zip_read_data_skip(struct archive_read *);+static int	archive_read_format_7zip_read_header(struct archive_read *,+		    struct archive_entry *);+static int	check_7zip_header_in_sfx(const char *);+static unsigned long decode_codec_id(const unsigned char *, size_t);+static int	decode_encoded_header_info(struct archive_read *,+		    struct _7z_stream_info *);+static int	decompress(struct archive_read *, struct _7zip *,+		    void *, size_t *, const void *, size_t *);+static ssize_t	extract_pack_stream(struct archive_read *, size_t);+static void	fileTimeToUtc(uint64_t, time_t *, long *);+static uint64_t folder_uncompressed_size(struct _7z_folder *);+static void	free_CodersInfo(struct _7z_coders_info *);+static void	free_Digest(struct _7z_digests *);+static void	free_Folder(struct _7z_folder *);+static void	free_Header(struct _7z_header_info *);+static void	free_PackInfo(struct _7z_pack_info *);+static void	free_StreamsInfo(struct _7z_stream_info *);+static void	free_SubStreamsInfo(struct _7z_substream_info *);+static int	free_decompression(struct archive_read *, struct _7zip *);+static ssize_t	get_uncompressed_data(struct archive_read *, const void **,+		    size_t, size_t);+static const unsigned char * header_bytes(struct archive_read *, size_t);+static int	init_decompression(struct archive_read *, struct _7zip *,+		    const struct _7z_coder *, const struct _7z_coder *);+static int	parse_7zip_uint64(struct archive_read *, uint64_t *);+static int	read_Bools(struct archive_read *, unsigned char *, size_t);+static int	read_CodersInfo(struct archive_read *,+		    struct _7z_coders_info *);+static int	read_Digests(struct archive_read *, struct _7z_digests *,+		    size_t);+static int	read_Folder(struct archive_read *, struct _7z_folder *);+static int	read_Header(struct archive_read *, struct _7z_header_info *,+		    int);+static int	read_PackInfo(struct archive_read *, struct _7z_pack_info *);+static int	read_StreamsInfo(struct archive_read *,+		    struct _7z_stream_info *);+static int	read_SubStreamsInfo(struct archive_read *,+		    struct _7z_substream_info *, struct _7z_folder *, size_t);+static int	read_Times(struct archive_read *, struct _7z_header_info *,+		    int);+static void	read_consume(struct archive_read *);+static ssize_t	read_stream(struct archive_read *, const void **, size_t,+		    size_t);+static int	seek_pack(struct archive_read *);+static int64_t	skip_stream(struct archive_read *, size_t);+static int	skip_sfx(struct archive_read *, ssize_t);+static int	slurp_central_directory(struct archive_read *, struct _7zip *,+		    struct _7z_header_info *);+static int	setup_decode_folder(struct archive_read *, struct _7z_folder *,+		    int);+static void	x86_Init(struct _7zip *);+static size_t	x86_Convert(struct _7zip *, uint8_t *, size_t);+static ssize_t		Bcj2_Decode(struct _7zip *, uint8_t *, size_t);+++int+archive_read_support_format_7zip(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct _7zip *zip;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_7zip");++	zip = calloc(1, sizeof(*zip));+	if (zip == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate 7zip data");+		return (ARCHIVE_FATAL);+	}++	/*+	 * Until enough data has been read, we cannot tell about+	 * any encrypted entries yet.+	 */+	zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;+++	r = __archive_read_register_format(a,+	    zip,+	    "7zip",+	    archive_read_format_7zip_bid,+	    NULL,+	    archive_read_format_7zip_read_header,+	    archive_read_format_7zip_read_data,+	    archive_read_format_7zip_read_data_skip,+	    NULL,+	    archive_read_format_7zip_cleanup,+	    archive_read_support_format_7zip_capabilities,+	    archive_read_format_7zip_has_encrypted_entries);++	if (r != ARCHIVE_OK)+		free(zip);+	return (ARCHIVE_OK);+}++static int+archive_read_support_format_7zip_capabilities(struct archive_read * a)+{+	(void)a; /* UNUSED */+	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |+			ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);+}+++static int+archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a)+{+	if (_a && _a->format) {+		struct _7zip * zip = (struct _7zip *)_a->format->data;+		if (zip) {+			return zip->has_encrypted_entries;+		}+	}+	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;+}++static int+archive_read_format_7zip_bid(struct archive_read *a, int best_bid)+{+	const char *p;++	/* If someone has already bid more than 32, then avoid+	   trashing the look-ahead buffers with a seek. */+	if (best_bid > 32)+		return (-1);++	if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)+		return (0);++	/* If first six bytes are the 7-Zip signature,+	 * return the bid right now. */+	if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0)+		return (48);++	/*+	 * It may a 7-Zip SFX archive file. If first two bytes are+	 * 'M' and 'Z' available on Windows or first four bytes are+	 * "\x7F\x45LF" available on posix like system, seek the 7-Zip+	 * signature. Although we will perform a seek when reading+	 * a header, what we do not use __archive_read_seek() here is+	 * due to a bidding performance.+	 */+	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {+		ssize_t offset = SFX_MIN_ADDR;+		ssize_t window = 4096;+		ssize_t bytes_avail;+		while (offset + window <= (SFX_MAX_ADDR)) {+			const char *buff = __archive_read_ahead(a,+					offset + window, &bytes_avail);+			if (buff == NULL) {+				/* Remaining bytes are less than window. */+				window >>= 1;+				if (window < 0x40)+					return (0);+				continue;+			}+			p = buff + offset;+			while (p + 32 < buff + bytes_avail) {+				int step = check_7zip_header_in_sfx(p);+				if (step == 0)+					return (48);+				p += step;+			}+			offset = p - buff;+		}+	}+	return (0);+}++static int+check_7zip_header_in_sfx(const char *p)+{+	switch ((unsigned char)p[5]) {+	case 0x1C:+		if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0)+			return (6);+		/*+		 * Test the CRC because its extraction code has 7-Zip+		 * Magic Code, so we should do this in order not to+		 * make a mis-detection.+		 */+		if (crc32(0, (const unsigned char *)p + 12, 20)+			!= archive_le32dec(p + 8))+			return (6);+		/* Hit the header! */+		return (0);+	case 0x37: return (5);+	case 0x7A: return (4);+	case 0xBC: return (3);+	case 0xAF: return (2);+	case 0x27: return (1);+	default: return (6);+	}+}++static int+skip_sfx(struct archive_read *a, ssize_t bytes_avail)+{+	const void *h;+	const char *p, *q;+	size_t skip, offset;+	ssize_t bytes, window;++	/*+	 * If bytes_avail > SFX_MIN_ADDR we do not have to call+	 * __archive_read_seek() at this time since we have+	 * already had enough data.+	 */+	if (bytes_avail > SFX_MIN_ADDR)+		__archive_read_consume(a, SFX_MIN_ADDR);+	else if (__archive_read_seek(a, SFX_MIN_ADDR, SEEK_SET) < 0)+		return (ARCHIVE_FATAL);++	offset = 0;+	window = 1;+	while (offset + window <= SFX_MAX_ADDR - SFX_MIN_ADDR) {+		h = __archive_read_ahead(a, window, &bytes);+		if (h == NULL) {+			/* Remaining bytes are less than window. */+			window >>= 1;+			if (window < 0x40)+				goto fatal;+			continue;+		}+		if (bytes < 6) {+			/* This case might happen when window == 1. */+			window = 4096;+			continue;+		}+		p = (const char *)h;+		q = p + bytes;++		/*+		 * Scan ahead until we find something that looks+		 * like the 7-Zip header.+		 */+		while (p + 32 < q) {+			int step = check_7zip_header_in_sfx(p);+			if (step == 0) {+				struct _7zip *zip =+				    (struct _7zip *)a->format->data;+				skip = p - (const char *)h;+				__archive_read_consume(a, skip);+				zip->seek_base = SFX_MIN_ADDR + offset + skip;+				return (ARCHIVE_OK);+			}+			p += step;+		}+		skip = p - (const char *)h;+		__archive_read_consume(a, skip);+		offset += skip;+		if (window == 1)+			window = 4096;+	}+fatal:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Couldn't find out 7-Zip header");+	return (ARCHIVE_FATAL);+}++static int+archive_read_format_7zip_read_header(struct archive_read *a,+	struct archive_entry *entry)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	struct _7zip_entry *zip_entry;+	int r, ret = ARCHIVE_OK;+	struct _7z_folder *folder = 0;+	uint64_t fidx = 0;++	/*+	 * It should be sufficient to call archive_read_next_header() for+	 * a reader to determine if an entry is encrypted or not. If the+	 * encryption of an entry is only detectable when calling+	 * archive_read_data(), so be it. We'll do the same check there+	 * as well.+	 */+	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {+		zip->has_encrypted_entries = 0;+	}++	a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;+	if (a->archive.archive_format_name == NULL)+		a->archive.archive_format_name = "7-Zip";++	if (zip->entries == NULL) {+		struct _7z_header_info header;++		memset(&header, 0, sizeof(header));+		r = slurp_central_directory(a, zip, &header);+		free_Header(&header);+		if (r != ARCHIVE_OK)+			return (r);+		zip->entries_remaining = (size_t)zip->numFiles;+		zip->entry = zip->entries;+	} else {+		++zip->entry;+	}+	zip_entry = zip->entry;++	if (zip->entries_remaining <= 0 || zip_entry == NULL)+		return ARCHIVE_EOF;+	--zip->entries_remaining;++	zip->entry_offset = 0;+	zip->end_of_entry = 0;+	zip->entry_crc32 = crc32(0, NULL, 0);++	/* Setup a string conversion for a filename. */+	if (zip->sconv == NULL) {+		zip->sconv = archive_string_conversion_from_charset(+		    &a->archive, "UTF-16LE", 1);+		if (zip->sconv == NULL)+			return (ARCHIVE_FATAL);+	}++	/* Figure out if the entry is encrypted by looking at the folder+	   that is associated to the current 7zip entry. If the folder+	   has a coder with a _7Z_CRYPTO codec then the folder is encrypted.+	   Hence the entry must also be encrypted. */+	if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) {+		folder = &(zip->si.ci.folders[zip_entry->folderIndex]);+		for (fidx=0; folder && fidx<folder->numCoders; fidx++) {+			switch(folder->coders[fidx].codec) {+				case _7Z_CRYPTO_MAIN_ZIP:+				case _7Z_CRYPTO_RAR_29:+				case _7Z_CRYPTO_AES_256_SHA_256: {+					archive_entry_set_is_data_encrypted(entry, 1);+					zip->has_encrypted_entries = 1;+					break;+				}+			}+		}+	}++	/* Now that we've checked for encryption, if there were still no+	 * encrypted entries found we can say for sure that there are none.+	 */+	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {+		zip->has_encrypted_entries = 0;+	}++	if (archive_entry_copy_pathname_l(entry,+	    (const char *)zip_entry->utf16name,+	    zip_entry->name_len, zip->sconv) != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Pathname cannot be converted "+		    "from %s to current locale.",+		    archive_string_conversion_charset_name(zip->sconv));+		ret = ARCHIVE_WARN;+	}++	/* Populate some additional entry fields: */+	archive_entry_set_mode(entry, zip_entry->mode);+	if (zip_entry->flg & MTIME_IS_SET)+		archive_entry_set_mtime(entry, zip_entry->mtime,+			zip_entry->mtime_ns);+	if (zip_entry->flg & CTIME_IS_SET)+		archive_entry_set_ctime(entry, zip_entry->ctime,+		    zip_entry->ctime_ns);+	if (zip_entry->flg & ATIME_IS_SET)+		archive_entry_set_atime(entry, zip_entry->atime,+		    zip_entry->atime_ns);+	if (zip_entry->ssIndex != (uint32_t)-1) {+		zip->entry_bytes_remaining =+		    zip->si.ss.unpackSizes[zip_entry->ssIndex];+		archive_entry_set_size(entry, zip->entry_bytes_remaining);+	} else {+		zip->entry_bytes_remaining = 0;+		archive_entry_set_size(entry, 0);+	}++	/* If there's no body, force read_data() to return EOF immediately. */+	if (zip->entry_bytes_remaining < 1)+		zip->end_of_entry = 1;++	if ((zip_entry->mode & AE_IFMT) == AE_IFLNK) {+		unsigned char *symname = NULL;+		size_t symsize = 0;++		/*+		 * Symbolic-name is recorded as its contents. We have to+		 * read the contents at this time.+		 */+		while (zip->entry_bytes_remaining > 0) {+			const void *buff;+			unsigned char *mem;+			size_t size;+			int64_t offset;++			r = archive_read_format_7zip_read_data(a, &buff,+				&size, &offset);+			if (r < ARCHIVE_WARN) {+				free(symname);+				return (r);+			}+			mem = realloc(symname, symsize + size + 1);+			if (mem == NULL) {+				free(symname);+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for Symname");+				return (ARCHIVE_FATAL);+			}+			symname = mem;+			memcpy(symname+symsize, buff, size);+			symsize += size;+		}+		if (symsize == 0) {+			/* If there is no symname, handle it as a regular+			 * file. */+			zip_entry->mode &= ~AE_IFMT;+			zip_entry->mode |= AE_IFREG;+			archive_entry_set_mode(entry, zip_entry->mode);+		} else {+			symname[symsize] = '\0';+			archive_entry_copy_symlink(entry,+			    (const char *)symname);+		}+		free(symname);+		archive_entry_set_size(entry, 0);+	}++	/* Set up a more descriptive format name. */+	sprintf(zip->format_name, "7-Zip");+	a->archive.archive_format_name = zip->format_name;++	return (ret);+}++static int+archive_read_format_7zip_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	struct _7zip *zip;+	ssize_t bytes;+	int ret = ARCHIVE_OK;++	zip = (struct _7zip *)(a->format->data);++	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {+		zip->has_encrypted_entries = 0;+	}++	if (zip->pack_stream_bytes_unconsumed)+		read_consume(a);++	*offset = zip->entry_offset;+	*size = 0;+	*buff = NULL;+	/*+	 * If we hit end-of-entry last time, clean up and return+	 * ARCHIVE_EOF this time.+	 */+	if (zip->end_of_entry)+		return (ARCHIVE_EOF);++	bytes = read_stream(a, buff,+		(size_t)zip->entry_bytes_remaining, 0);+	if (bytes < 0)+		return ((int)bytes);+	if (bytes == 0) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated 7-Zip file body");+		return (ARCHIVE_FATAL);+	}+	zip->entry_bytes_remaining -= bytes;+	if (zip->entry_bytes_remaining == 0)+		zip->end_of_entry = 1;++	/* Update checksum */+	if ((zip->entry->flg & CRC32_IS_SET) && bytes)+		zip->entry_crc32 = crc32(zip->entry_crc32, *buff,+		    (unsigned)bytes);++	/* If we hit the end, swallow any end-of-data marker. */+	if (zip->end_of_entry) {+		/* Check computed CRC against file contents. */+		if ((zip->entry->flg & CRC32_IS_SET) &&+			zip->si.ss.digests[zip->entry->ssIndex] !=+		    zip->entry_crc32) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "7-Zip bad CRC: 0x%lx should be 0x%lx",+			    (unsigned long)zip->entry_crc32,+			    (unsigned long)zip->si.ss.digests[+			    		zip->entry->ssIndex]);+			ret = ARCHIVE_WARN;+		}+	}++	*size = bytes;+	*offset = zip->entry_offset;+	zip->entry_offset += bytes;++	return (ret);+}++static int+archive_read_format_7zip_read_data_skip(struct archive_read *a)+{+	struct _7zip *zip;+	int64_t bytes_skipped;++	zip = (struct _7zip *)(a->format->data);++	if (zip->pack_stream_bytes_unconsumed)+		read_consume(a);++	/* If we've already read to end of data, we're done. */+	if (zip->end_of_entry)+		return (ARCHIVE_OK);++	/*+	 * If the length is at the beginning, we can skip the+	 * compressed data much more quickly.+	 */+	bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining);+	if (bytes_skipped < 0)+		return (ARCHIVE_FATAL);+	zip->entry_bytes_remaining = 0;++	/* This entry is finished and done. */+	zip->end_of_entry = 1;+	return (ARCHIVE_OK);+}++static int+archive_read_format_7zip_cleanup(struct archive_read *a)+{+	struct _7zip *zip;++	zip = (struct _7zip *)(a->format->data);+	free_StreamsInfo(&(zip->si));+	free(zip->entries);+	free(zip->entry_names);+	free_decompression(a, zip);+	free(zip->uncompressed_buffer);+	free(zip->sub_stream_buff[0]);+	free(zip->sub_stream_buff[1]);+	free(zip->sub_stream_buff[2]);+	free(zip->tmp_stream_buff);+	free(zip);+	(a->format->data) = NULL;+	return (ARCHIVE_OK);+}++static void+read_consume(struct archive_read *a)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;++	if (zip->pack_stream_bytes_unconsumed) {+		__archive_read_consume(a, zip->pack_stream_bytes_unconsumed);+		zip->stream_offset += zip->pack_stream_bytes_unconsumed;+		zip->pack_stream_bytes_unconsumed = 0;+	}+}++#ifdef HAVE_LZMA_H++/*+ * Set an error code and choose an error message for liblzma.+ */+static void+set_error(struct archive_read *a, int ret)+{++	switch (ret) {+	case LZMA_STREAM_END: /* Found end of stream. */+	case LZMA_OK: /* Decompressor made some progress. */+		break;+	case LZMA_MEM_ERROR:+		archive_set_error(&a->archive, ENOMEM,+		    "Lzma library error: Cannot allocate memory");+		break;+	case LZMA_MEMLIMIT_ERROR:+		archive_set_error(&a->archive, ENOMEM,+		    "Lzma library error: Out of memory");+		break;+	case LZMA_FORMAT_ERROR:+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Lzma library error: format not recognized");+		break;+	case LZMA_OPTIONS_ERROR:+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Lzma library error: Invalid options");+		break;+	case LZMA_DATA_ERROR:+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Lzma library error: Corrupted input data");+		break;+	case LZMA_BUF_ERROR:+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Lzma library error:  No progress is possible");+		break;+	default:+		/* Return an error. */+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "Lzma decompression failed:  Unknown error");+		break;+	}+}++#endif++static unsigned long+decode_codec_id(const unsigned char *codecId, size_t id_size)+{+	unsigned i;+	unsigned long id = 0;++	for (i = 0; i < id_size; i++) {+		id <<= 8;+		id += codecId[i];+	}+	return (id);+}++static Byte+ppmd_read(void *p)+{+	struct archive_read *a = ((IByteIn*)p)->a;+	struct _7zip *zip = (struct _7zip *)(a->format->data);+	Byte b;++	if (zip->ppstream.avail_in == 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated RAR file data");+		zip->ppstream.overconsumed = 1;+		return (0);+	}+	b = *zip->ppstream.next_in++;+	zip->ppstream.avail_in--;+	zip->ppstream.total_in++;+	return (b);+}++static int+init_decompression(struct archive_read *a, struct _7zip *zip,+    const struct _7z_coder *coder1, const struct _7z_coder *coder2)+{+	int r;++	zip->codec = coder1->codec;+	zip->codec2 = -1;++	switch (zip->codec) {+	case _7Z_COPY:+	case _7Z_BZ2:+	case _7Z_DEFLATE:+	case _7Z_PPMD:+		if (coder2 != NULL) {+			if (coder2->codec != _7Z_X86 &&+			    coder2->codec != _7Z_X86_BCJ2) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Unsupported filter %lx for %lx",+				    coder2->codec, coder1->codec);+				return (ARCHIVE_FAILED);+			}+			zip->codec2 = coder2->codec;+			zip->bcj_state = 0;+			if (coder2->codec == _7Z_X86)+				x86_Init(zip);+		}+		break;+	default:+		break;+	}++	switch (zip->codec) {+	case _7Z_COPY:+		break;++	case _7Z_LZMA: case _7Z_LZMA2:+#ifdef HAVE_LZMA_H+#if LZMA_VERSION_MAJOR >= 5+/* Effectively disable the limiter. */+#define LZMA_MEMLIMIT   UINT64_MAX+#else+/* NOTE: This needs to check memory size which running system has. */+#define LZMA_MEMLIMIT   (1U << 30)+#endif+	{+		lzma_options_delta delta_opt;+		lzma_filter filters[LZMA_FILTERS_MAX], *ff;+		int fi = 0;++		if (zip->lzstream_valid) {+			lzma_end(&(zip->lzstream));+			zip->lzstream_valid = 0;+		}++		/*+		 * NOTE: liblzma incompletely handle the BCJ+LZMA compressed+		 * data made by 7-Zip because 7-Zip does not add End-Of-+		 * Payload Marker(EOPM) at the end of LZMA compressed data,+		 * and so liblzma cannot know the end of the compressed data+		 * without EOPM. So consequently liblzma will not return last+		 * three or four bytes of uncompressed data because+		 * LZMA_FILTER_X86 filter does not handle input data if its+		 * data size is less than five bytes. If liblzma detect EOPM+		 * or know the uncompressed data size, liblzma will flush out+		 * the remaining that three or four bytes of uncompressed+		 * data. That is why we have to use our converting program+		 * for BCJ+LZMA. If we were able to tell the uncompressed+		 * size to liblzma when using lzma_raw_decoder() liblzma+		 * could correctly deal with BCJ+LZMA. But unfortunately+		 * there is no way to do that.+		 * Discussion about this can be found at XZ Utils forum.+		 */+		if (coder2 != NULL) {+			zip->codec2 = coder2->codec;++			filters[fi].options = NULL;+			switch (zip->codec2) {+			case _7Z_X86:+				if (zip->codec == _7Z_LZMA2) {+					filters[fi].id = LZMA_FILTER_X86;+					fi++;+				} else+					/* Use our filter. */+					x86_Init(zip);+				break;+			case _7Z_X86_BCJ2:+				/* Use our filter. */+				zip->bcj_state = 0;+				break;+			case _7Z_DELTA:+				if (coder2->propertiesSize != 1) {+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_MISC,+					    "Invalid Delta parameter");+					return (ARCHIVE_FAILED);+				}+				filters[fi].id = LZMA_FILTER_DELTA;+				memset(&delta_opt, 0, sizeof(delta_opt));+				delta_opt.type = LZMA_DELTA_TYPE_BYTE;+				delta_opt.dist =+				    (uint32_t)coder2->properties[0] + 1;+				filters[fi].options = &delta_opt;+				fi++;+				break;+			/* Following filters have not been tested yet. */+			case _7Z_POWERPC:+				filters[fi].id = LZMA_FILTER_POWERPC;+				fi++;+				break;+			case _7Z_IA64:+				filters[fi].id = LZMA_FILTER_IA64;+				fi++;+				break;+			case _7Z_ARM:+				filters[fi].id = LZMA_FILTER_ARM;+				fi++;+				break;+			case _7Z_ARMTHUMB:+				filters[fi].id = LZMA_FILTER_ARMTHUMB;+				fi++;+				break;+			case _7Z_SPARC:+				filters[fi].id = LZMA_FILTER_SPARC;+				fi++;+				break;+			default:+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Unexpected codec ID: %lX", zip->codec2);+				return (ARCHIVE_FAILED);+			}+		}++		if (zip->codec == _7Z_LZMA2)+			filters[fi].id = LZMA_FILTER_LZMA2;+		else+			filters[fi].id = LZMA_FILTER_LZMA1;+		filters[fi].options = NULL;+		ff = &filters[fi];+		r = lzma_properties_decode(&filters[fi], NULL,+		    coder1->properties, (size_t)coder1->propertiesSize);+		if (r != LZMA_OK) {+			set_error(a, r);+			return (ARCHIVE_FAILED);+		}+		fi++;++		filters[fi].id = LZMA_VLI_UNKNOWN;+		filters[fi].options = NULL;+		r = lzma_raw_decoder(&(zip->lzstream), filters);+		free(ff->options);+		if (r != LZMA_OK) {+			set_error(a, r);+			return (ARCHIVE_FAILED);+		}+		zip->lzstream_valid = 1;+		zip->lzstream.total_in = 0;+		zip->lzstream.total_out = 0;+		break;+	}+#else+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "LZMA codec is unsupported");+		return (ARCHIVE_FAILED);+#endif+	case _7Z_BZ2:+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+		if (zip->bzstream_valid) {+			BZ2_bzDecompressEnd(&(zip->bzstream));+			zip->bzstream_valid = 0;+		}+		r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 0);+		if (r == BZ_MEM_ERROR)+			r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 1);+		if (r != BZ_OK) {+			int err = ARCHIVE_ERRNO_MISC;+			const char *detail = NULL;+			switch (r) {+			case BZ_PARAM_ERROR:+				detail = "invalid setup parameter";+				break;+			case BZ_MEM_ERROR:+				err = ENOMEM;+				detail = "out of memory";+				break;+			case BZ_CONFIG_ERROR:+				detail = "mis-compiled library";+				break;+			}+			archive_set_error(&a->archive, err,+			    "Internal error initializing decompressor: %s",+			    detail != NULL ? detail : "??");+			zip->bzstream_valid = 0;+			return (ARCHIVE_FAILED);+		}+		zip->bzstream_valid = 1;+		zip->bzstream.total_in_lo32 = 0;+		zip->bzstream.total_in_hi32 = 0;+		zip->bzstream.total_out_lo32 = 0;+		zip->bzstream.total_out_hi32 = 0;+		break;+#else+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "BZ2 codec is unsupported");+		return (ARCHIVE_FAILED);+#endif+	case _7Z_DEFLATE:+#ifdef HAVE_ZLIB_H+		if (zip->stream_valid)+			r = inflateReset(&(zip->stream));+		else+			r = inflateInit2(&(zip->stream),+			    -15 /* Don't check for zlib header */);+		if (r != Z_OK) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Couldn't initialize zlib stream.");+			return (ARCHIVE_FAILED);+		}+		zip->stream_valid = 1;+		zip->stream.total_in = 0;+		zip->stream.total_out = 0;+		break;+#else+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "DEFLATE codec is unsupported");+		return (ARCHIVE_FAILED);+#endif+	case _7Z_PPMD:+	{+		unsigned order;+		uint32_t msize;++		if (zip->ppmd7_valid) {+			__archive_ppmd7_functions.Ppmd7_Free(+			    &zip->ppmd7_context);+			zip->ppmd7_valid = 0;+		}++		if (coder1->propertiesSize < 5) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Malformed PPMd parameter");+			return (ARCHIVE_FAILED);+		}+		order = coder1->properties[0];+		msize = archive_le32dec(&(coder1->properties[1]));+		if (order < PPMD7_MIN_ORDER || order > PPMD7_MAX_ORDER ||+		    msize < PPMD7_MIN_MEM_SIZE || msize > PPMD7_MAX_MEM_SIZE) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Malformed PPMd parameter");+			return (ARCHIVE_FAILED);+		}+		__archive_ppmd7_functions.Ppmd7_Construct(&zip->ppmd7_context);+		r = __archive_ppmd7_functions.Ppmd7_Alloc(+			&zip->ppmd7_context, msize);+		if (r == 0) {+			archive_set_error(&a->archive, ENOMEM,+			    "Coludn't allocate memory for PPMd");+			return (ARCHIVE_FATAL);+		}+		__archive_ppmd7_functions.Ppmd7_Init(+			&zip->ppmd7_context, order);+		__archive_ppmd7_functions.Ppmd7z_RangeDec_CreateVTable(+			&zip->range_dec);+		zip->ppmd7_valid = 1;+		zip->ppmd7_stat = 0;+		zip->ppstream.overconsumed = 0;+		zip->ppstream.total_in = 0;+		zip->ppstream.total_out = 0;+		break;+	}+	case _7Z_X86:+	case _7Z_X86_BCJ2:+	case _7Z_POWERPC:+	case _7Z_IA64:+	case _7Z_ARM:+	case _7Z_ARMTHUMB:+	case _7Z_SPARC:+	case _7Z_DELTA:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Unexpected codec ID: %lX", zip->codec);+		return (ARCHIVE_FAILED);+	case _7Z_CRYPTO_MAIN_ZIP:+	case _7Z_CRYPTO_RAR_29:+	case _7Z_CRYPTO_AES_256_SHA_256:+		if (a->entry) {+			archive_entry_set_is_metadata_encrypted(a->entry, 1);+			archive_entry_set_is_data_encrypted(a->entry, 1);+			zip->has_encrypted_entries = 1;+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Crypto codec not supported yet (ID: 0x%lX)", zip->codec);+		return (ARCHIVE_FAILED);+	default:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Unknown codec ID: %lX", zip->codec);+		return (ARCHIVE_FAILED);+	}++	return (ARCHIVE_OK);+}++static int+decompress(struct archive_read *a, struct _7zip *zip,+    void *buff, size_t *outbytes, const void *b, size_t *used)+{+	const uint8_t *t_next_in;+	uint8_t *t_next_out;+	size_t o_avail_in, o_avail_out;+	size_t t_avail_in, t_avail_out;+	uint8_t *bcj2_next_out;+	size_t bcj2_avail_out;+	int r, ret = ARCHIVE_OK;++	t_avail_in = o_avail_in = *used;+	t_avail_out = o_avail_out = *outbytes;+	t_next_in = b;+	t_next_out = buff;++	if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) {+		int i;++		/* Do not copy out the BCJ remaining bytes when the output+		 * buffer size is less than five bytes. */+		if (o_avail_in != 0 && t_avail_out < 5 && zip->odd_bcj_size) {+			*used = 0;+			*outbytes = 0;+			return (ret);+		}+		for (i = 0; zip->odd_bcj_size > 0 && t_avail_out; i++) {+			*t_next_out++ = zip->odd_bcj[i];+			t_avail_out--;+			zip->odd_bcj_size--;+		}+		if (o_avail_in == 0 || t_avail_out == 0) {+			*used = o_avail_in - t_avail_in;+			*outbytes = o_avail_out - t_avail_out;+			if (o_avail_in == 0)+				ret = ARCHIVE_EOF;+			return (ret);+		}+	}++	bcj2_next_out = t_next_out;+	bcj2_avail_out = t_avail_out;+	if (zip->codec2 == _7Z_X86_BCJ2) {+		/*+		 * Decord a remaining decompressed main stream for BCJ2.+		 */+		if (zip->tmp_stream_bytes_remaining) {+			ssize_t bytes;+			size_t remaining = zip->tmp_stream_bytes_remaining;+			bytes = Bcj2_Decode(zip, t_next_out, t_avail_out);+			if (bytes < 0) {+				archive_set_error(&(a->archive),+				    ARCHIVE_ERRNO_MISC,+				    "BCJ2 conversion Failed");+				return (ARCHIVE_FAILED);+			}+			zip->main_stream_bytes_remaining -=+			    remaining - zip->tmp_stream_bytes_remaining;+			t_avail_out -= bytes;+			if (o_avail_in == 0 || t_avail_out == 0) {+				*used = 0;+				*outbytes = o_avail_out - t_avail_out;+				if (o_avail_in == 0 &&+				    zip->tmp_stream_bytes_remaining)+					ret = ARCHIVE_EOF;+				return (ret);+			}+			t_next_out += bytes;+			bcj2_next_out = t_next_out;+			bcj2_avail_out = t_avail_out;+		}+		t_next_out = zip->tmp_stream_buff;+		t_avail_out = zip->tmp_stream_buff_size;+	}++	switch (zip->codec) {+	case _7Z_COPY:+	{+		size_t bytes =+		    (t_avail_in > t_avail_out)?t_avail_out:t_avail_in;++		memcpy(t_next_out, t_next_in, bytes);+		t_avail_in -= bytes;+		t_avail_out -= bytes;+		if (o_avail_in == 0)+			ret = ARCHIVE_EOF;+		break;+	}+#ifdef HAVE_LZMA_H+	case _7Z_LZMA: case _7Z_LZMA2:+		zip->lzstream.next_in = t_next_in;+		zip->lzstream.avail_in = t_avail_in;+		zip->lzstream.next_out = t_next_out;+		zip->lzstream.avail_out = t_avail_out;++		r = lzma_code(&(zip->lzstream), LZMA_RUN);+		switch (r) {+		case LZMA_STREAM_END: /* Found end of stream. */+			lzma_end(&(zip->lzstream));+			zip->lzstream_valid = 0;+			ret = ARCHIVE_EOF;+			break;+		case LZMA_OK: /* Decompressor made some progress. */+			break;+		default:+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+				"Decompression failed(%d)",+			    r);+			return (ARCHIVE_FAILED);+		}+		t_avail_in = zip->lzstream.avail_in;+		t_avail_out = zip->lzstream.avail_out;+		break;+#endif+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+	case _7Z_BZ2:+		zip->bzstream.next_in = (char *)(uintptr_t)t_next_in;+		zip->bzstream.avail_in = t_avail_in;+		zip->bzstream.next_out = (char *)(uintptr_t)t_next_out;+		zip->bzstream.avail_out = t_avail_out;+		r = BZ2_bzDecompress(&(zip->bzstream));+		switch (r) {+		case BZ_STREAM_END: /* Found end of stream. */+			switch (BZ2_bzDecompressEnd(&(zip->bzstream))) {+			case BZ_OK:+				break;+			default:+				archive_set_error(&(a->archive),+				    ARCHIVE_ERRNO_MISC,+				    "Failed to clean up decompressor");+				return (ARCHIVE_FAILED);+			}+			zip->bzstream_valid = 0;+			ret = ARCHIVE_EOF;+			break;+		case BZ_OK: /* Decompressor made some progress. */+			break;+		default:+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "bzip decompression failed");+			return (ARCHIVE_FAILED);+		}+		t_avail_in = zip->bzstream.avail_in;+		t_avail_out = zip->bzstream.avail_out;+		break;+#endif+#ifdef HAVE_ZLIB_H+	case _7Z_DEFLATE:+		zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in;+		zip->stream.avail_in = (uInt)t_avail_in;+		zip->stream.next_out = t_next_out;+		zip->stream.avail_out = (uInt)t_avail_out;+		r = inflate(&(zip->stream), 0);+		switch (r) {+		case Z_STREAM_END: /* Found end of stream. */+			ret = ARCHIVE_EOF;+			break;+		case Z_OK: /* Decompressor made some progress.*/+			break;+		default:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "File decompression failed (%d)", r);+			return (ARCHIVE_FAILED);+		}+		t_avail_in = zip->stream.avail_in;+		t_avail_out = zip->stream.avail_out;+		break;+#endif+	case _7Z_PPMD:+	{+		uint64_t flush_bytes;++		if (!zip->ppmd7_valid || zip->ppmd7_stat < 0 ||+		    t_avail_out <= 0) {+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "Decompression internal error");+			return (ARCHIVE_FAILED);+		}+		zip->ppstream.next_in = t_next_in;+		zip->ppstream.avail_in = t_avail_in;+		zip->ppstream.next_out = t_next_out;+		zip->ppstream.avail_out = t_avail_out;+		if (zip->ppmd7_stat == 0) {+			zip->bytein.a = a;+			zip->bytein.Read = &ppmd_read;+			zip->range_dec.Stream = &zip->bytein;+			r = __archive_ppmd7_functions.Ppmd7z_RangeDec_Init(+				&(zip->range_dec));+			if (r == 0) {+				zip->ppmd7_stat = -1;+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Failed to initialize PPMd range decorder");+				return (ARCHIVE_FAILED);+			}+			if (zip->ppstream.overconsumed) {+				zip->ppmd7_stat = -1;+				return (ARCHIVE_FAILED);+			}+			zip->ppmd7_stat = 1;+		}++		if (t_avail_in == 0)+			/* XXX Flush out remaining decoded data XXX */+			flush_bytes = zip->folder_outbytes_remaining;+		else+			flush_bytes = 0;++		do {+			int sym;++			sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(+				&(zip->ppmd7_context), &(zip->range_dec.p));+			if (sym < 0) {+				zip->ppmd7_stat = -1;+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Failed to decode PPMd");+				return (ARCHIVE_FAILED);+			}+			if (zip->ppstream.overconsumed) {+				zip->ppmd7_stat = -1;+				return (ARCHIVE_FAILED);+			}+			*zip->ppstream.next_out++ = (unsigned char)sym;+			zip->ppstream.avail_out--;+			zip->ppstream.total_out++;+			if (flush_bytes)+				flush_bytes--;+		} while (zip->ppstream.avail_out &&+			(zip->ppstream.avail_in || flush_bytes));++		t_avail_in = (size_t)zip->ppstream.avail_in;+		t_avail_out = (size_t)zip->ppstream.avail_out;+		break;+	}+	default:+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,+		    "Decompression internal error");+		return (ARCHIVE_FAILED);+	}+	if (ret != ARCHIVE_OK && ret != ARCHIVE_EOF)+		return (ret);++	*used = o_avail_in - t_avail_in;+	*outbytes = o_avail_out - t_avail_out;++	/*+	 * Decord BCJ.+	 */+	if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) {+		size_t l = x86_Convert(zip, buff, *outbytes);+		zip->odd_bcj_size = *outbytes - l;+		if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 &&+		    o_avail_in && ret != ARCHIVE_EOF) {+			memcpy(zip->odd_bcj, ((unsigned char *)buff) + l,+			    zip->odd_bcj_size);+			*outbytes = l;+		} else+			zip->odd_bcj_size = 0;+	}++	/*+	 * Decord BCJ2 with a decompressed main stream.+	 */+	if (zip->codec2 == _7Z_X86_BCJ2) {+		ssize_t bytes;++		zip->tmp_stream_bytes_avail =+		    zip->tmp_stream_buff_size - t_avail_out;+		if (zip->tmp_stream_bytes_avail >+		      zip->main_stream_bytes_remaining)+			zip->tmp_stream_bytes_avail =+			    zip->main_stream_bytes_remaining;+		zip->tmp_stream_bytes_remaining = zip->tmp_stream_bytes_avail;+		bytes = Bcj2_Decode(zip, bcj2_next_out, bcj2_avail_out);+		if (bytes < 0) {+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed");+			return (ARCHIVE_FAILED);+		}+		zip->main_stream_bytes_remaining -=+		    zip->tmp_stream_bytes_avail+		      - zip->tmp_stream_bytes_remaining;+		bcj2_avail_out -= bytes;+		*outbytes = o_avail_out - bcj2_avail_out;+	}++	return (ret);+}++static int+free_decompression(struct archive_read *a, struct _7zip *zip)+{+	int r = ARCHIVE_OK;++#if !defined(HAVE_ZLIB_H) &&\+	!(defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR))+	(void)a;/* UNUSED */+#endif+#ifdef HAVE_LZMA_H+	if (zip->lzstream_valid)+		lzma_end(&(zip->lzstream));+#endif+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+	if (zip->bzstream_valid) {+		if (BZ2_bzDecompressEnd(&(zip->bzstream)) != BZ_OK) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "Failed to clean up bzip2 decompressor");+			r = ARCHIVE_FATAL;+		}+		zip->bzstream_valid = 0;+	}+#endif+#ifdef HAVE_ZLIB_H+	if (zip->stream_valid) {+		if (inflateEnd(&(zip->stream)) != Z_OK) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "Failed to clean up zlib decompressor");+			r = ARCHIVE_FATAL;+		}+		zip->stream_valid = 0;+	}+#endif+	if (zip->ppmd7_valid) {+		__archive_ppmd7_functions.Ppmd7_Free(+			&zip->ppmd7_context);+		zip->ppmd7_valid = 0;+	}+	return (r);+}++static int+parse_7zip_uint64(struct archive_read *a, uint64_t *val)+{+	const unsigned char *p;+	unsigned char avail, mask;+	int i;++	if ((p = header_bytes(a, 1)) == NULL)+		return (-1);+	avail = *p;+	mask = 0x80;+	*val = 0;+	for (i = 0; i < 8; i++) {+		if (avail & mask) {+			if ((p = header_bytes(a, 1)) == NULL)+				return (-1);+			*val |= ((uint64_t)*p) << (8 * i);+			mask >>= 1;+			continue;+		}+		*val += ((uint64_t)(avail & (mask -1))) << (8 * i);+		break;+	}+	return (0);+}++static int+read_Bools(struct archive_read *a, unsigned char *data, size_t num)+{+	const unsigned char *p;+	unsigned i, mask = 0, avail = 0;++	for (i = 0; i < num; i++) {+		if (mask == 0) {+			if ((p = header_bytes(a, 1)) == NULL)+				return (-1);+			avail = *p;+			mask = 0x80;+		}+		data[i] = (avail & mask)?1:0;+		mask >>= 1;+	}+	return (0);+}++static void+free_Digest(struct _7z_digests *d)+{+	free(d->defineds);+	free(d->digests);+}++static int+read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num)+{+	const unsigned char *p;+	unsigned i;++	if (num == 0)+		return (-1);+	memset(d, 0, sizeof(*d));++	d->defineds = malloc(num);+	if (d->defineds == NULL)+		return (-1);+	/*+	 * Read Bools.+	 */+	if ((p = header_bytes(a, 1)) == NULL)+		return (-1);+	if (*p == 0) {+		if (read_Bools(a, d->defineds, num) < 0)+			return (-1);+	} else+		/* All are defined */+		memset(d->defineds, 1, num);++	d->digests = calloc(num, sizeof(*d->digests));+	if (d->digests == NULL)+		return (-1);+	for (i = 0; i < num; i++) {+		if (d->defineds[i]) {+			if ((p = header_bytes(a, 4)) == NULL)+				return (-1);+			d->digests[i] = archive_le32dec(p);+		}+	}++	return (0);+}++static void+free_PackInfo(struct _7z_pack_info *pi)+{+	free(pi->sizes);+	free(pi->positions);+	free_Digest(&(pi->digest));+}++static int+read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)+{+	const unsigned char *p;+	unsigned i;++	memset(pi, 0, sizeof(*pi));++	/*+	 * Read PackPos.+	 */+	if (parse_7zip_uint64(a, &(pi->pos)) < 0)+		return (-1);++	/*+	 * Read NumPackStreams.+	 */+	if (parse_7zip_uint64(a, &(pi->numPackStreams)) < 0)+		return (-1);+	if (pi->numPackStreams == 0)+		return (-1);+	if (UMAX_ENTRY < pi->numPackStreams)+		return (-1);++	/*+	 * Read PackSizes[num]+	 */+	if ((p = header_bytes(a, 1)) == NULL)+		return (-1);+	if (*p == kEnd)+		/* PackSizes[num] are not present. */+		return (0);+	if (*p != kSize)+		return (-1);+	pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));+	pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));+	if (pi->sizes == NULL || pi->positions == NULL)+		return (-1);++	for (i = 0; i < pi->numPackStreams; i++) {+		if (parse_7zip_uint64(a, &(pi->sizes[i])) < 0)+			return (-1);+	}++	/*+	 * Read PackStreamDigests[num]+	 */+	if ((p = header_bytes(a, 1)) == NULL)+		return (-1);+	if (*p == kEnd) {+		/* PackStreamDigests[num] are not present. */+		pi->digest.defineds =+		    calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds));+		pi->digest.digests =+		    calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests));+		if (pi->digest.defineds == NULL || pi->digest.digests == NULL)+			return (-1);+		return (0);+	}++	if (*p != kCRC)+		return (-1);++	if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0)+		return (-1);++	/*+	 *  Must be marked by kEnd.+	 */+	if ((p = header_bytes(a, 1)) == NULL)+		return (-1);+	if (*p != kEnd)+		return (-1);+	return (0);+}++static void+free_Folder(struct _7z_folder *f)+{+	unsigned i;++	if (f->coders) {+		for (i = 0; i< f->numCoders; i++) {+			free(f->coders[i].properties);+		}+		free(f->coders);+	}+	free(f->bindPairs);+	free(f->packedStreams);+	free(f->unPackSize);+}++static int+read_Folder(struct archive_read *a, struct _7z_folder *f)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	const unsigned char *p;+	uint64_t numInStreamsTotal = 0;+	uint64_t numOutStreamsTotal = 0;+	unsigned i;++	memset(f, 0, sizeof(*f));++	/*+	 * Read NumCoders.+	 */+	if (parse_7zip_uint64(a, &(f->numCoders)) < 0)+		return (-1);+	if (f->numCoders > 4)+		/* Too many coders. */+		return (-1);++	f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders));+	if (f->coders == NULL)+		return (-1);+	for (i = 0; i< f->numCoders; i++) {+		size_t codec_size;+		int simple, attr;++		if ((p = header_bytes(a, 1)) == NULL)+			return (-1);+		/*+		 * 0:3 CodecIdSize+		 * 4:  0 - IsSimple+		 *     1 - Is not Simple+		 * 5:  0 - No Attributes+		 *     1 - There are Attributes;+		 * 7:  Must be zero.+		 */+		codec_size = *p & 0xf;+		simple = (*p & 0x10)?0:1;+		attr = *p & 0x20;+		if (*p & 0x80)+			return (-1);/* Not supported. */++		/*+		 * Read Decompression Method IDs.+		 */+		if ((p = header_bytes(a, codec_size)) == NULL)+			return (-1);++		f->coders[i].codec = decode_codec_id(p, codec_size);++		if (simple) {+			f->coders[i].numInStreams = 1;+			f->coders[i].numOutStreams = 1;+		} else {+			if (parse_7zip_uint64(+			    a, &(f->coders[i].numInStreams)) < 0)+				return (-1);+			if (UMAX_ENTRY < f->coders[i].numInStreams)+				return (-1);+			if (parse_7zip_uint64(+			    a, &(f->coders[i].numOutStreams)) < 0)+				return (-1);+			if (UMAX_ENTRY < f->coders[i].numOutStreams)+				return (-1);+		}++		if (attr) {+			if (parse_7zip_uint64(+			    a, &(f->coders[i].propertiesSize)) < 0)+				return (-1);+			if ((p = header_bytes(+			    a, (size_t)f->coders[i].propertiesSize)) == NULL)+				return (-1);+			f->coders[i].properties =+			    malloc((size_t)f->coders[i].propertiesSize);+			if (f->coders[i].properties == NULL)+				return (-1);+			memcpy(f->coders[i].properties, p,+			    (size_t)f->coders[i].propertiesSize);+		}++		numInStreamsTotal += f->coders[i].numInStreams;+		numOutStreamsTotal += f->coders[i].numOutStreams;+	}++	if (numOutStreamsTotal == 0 ||+	    numInStreamsTotal < numOutStreamsTotal-1)+		return (-1);++	f->numBindPairs = numOutStreamsTotal - 1;+	if (zip->header_bytes_remaining < f->numBindPairs)+			return (-1);+	if (f->numBindPairs > 0) {+		f->bindPairs =+			calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs));+		if (f->bindPairs == NULL)+			return (-1);+	} else+		f->bindPairs = NULL;+	for (i = 0; i < f->numBindPairs; i++) {+		if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0)+			return (-1);+		if (UMAX_ENTRY < f->bindPairs[i].inIndex)+			return (-1);+		if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0)+			return (-1);+		if (UMAX_ENTRY < f->bindPairs[i].outIndex)+			return (-1);+	}++	f->numPackedStreams = numInStreamsTotal - f->numBindPairs;+	f->packedStreams =+	    calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams));+	if (f->packedStreams == NULL)+		return (-1);+	if (f->numPackedStreams == 1) {+		for (i = 0; i < numInStreamsTotal; i++) {+			unsigned j;+			for (j = 0; j < f->numBindPairs; j++) {+				if (f->bindPairs[j].inIndex == i)+					break;+			}+			if (j == f->numBindPairs)+				break;+		}+		if (i == numInStreamsTotal)+			return (-1);+		f->packedStreams[0] = i;+	} else {+		for (i = 0; i < f->numPackedStreams; i++) {+			if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0)+				return (-1);+			if (UMAX_ENTRY < f->packedStreams[i])+				return (-1);+		}+	}+	f->numInStreams = numInStreamsTotal;+	f->numOutStreams = numOutStreamsTotal;++	return (0);+}++static void+free_CodersInfo(struct _7z_coders_info *ci)+{+	unsigned i;++	if (ci->folders) {+		for (i = 0; i < ci->numFolders; i++)+			free_Folder(&(ci->folders[i]));+		free(ci->folders);+	}+}++static int+read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)+{+	const unsigned char *p;+	struct _7z_digests digest;+	unsigned i;++	memset(ci, 0, sizeof(*ci));+	memset(&digest, 0, sizeof(digest));++	if ((p = header_bytes(a, 1)) == NULL)+		goto failed;+	if (*p != kFolder)+		goto failed;++	/*+	 * Read NumFolders.+	 */+	if (parse_7zip_uint64(a, &(ci->numFolders)) < 0)+		goto failed;+	if (UMAX_ENTRY < ci->numFolders)+		return (-1);++	/*+	 * Read External.+	 */+	if ((p = header_bytes(a, 1)) == NULL)+		goto failed;+	switch (*p) {+	case 0:+		ci->folders =+			calloc((size_t)ci->numFolders, sizeof(*ci->folders));+		if (ci->folders == NULL)+			return (-1);+		for (i = 0; i < ci->numFolders; i++) {+			if (read_Folder(a, &(ci->folders[i])) < 0)+				goto failed;+		}+		break;+	case 1:+		if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0)+			return (-1);+		if (UMAX_ENTRY < ci->dataStreamIndex)+			return (-1);+		if (ci->numFolders > 0) {+			archive_set_error(&a->archive, -1,+			    "Malformed 7-Zip archive");+			goto failed;+		}+		break;+	default:+		archive_set_error(&a->archive, -1,+		    "Malformed 7-Zip archive");+		goto failed;+	}++	if ((p = header_bytes(a, 1)) == NULL)+		goto failed;+	if (*p != kCodersUnPackSize)+		goto failed;++	for (i = 0; i < ci->numFolders; i++) {+		struct _7z_folder *folder = &(ci->folders[i]);+		unsigned j;++		folder->unPackSize =+		    calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize));+		if (folder->unPackSize == NULL)+			goto failed;+		for (j = 0; j < folder->numOutStreams; j++) {+			if (parse_7zip_uint64(a, &(folder->unPackSize[j])) < 0)+				goto failed;+		}+	}++	/*+	 * Read CRCs.+	 */+	if ((p = header_bytes(a, 1)) == NULL)+		goto failed;+	if (*p == kEnd)+		return (0);+	if (*p != kCRC)+		goto failed;+	if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0)+		goto failed;+	for (i = 0; i < ci->numFolders; i++) {+		ci->folders[i].digest_defined = digest.defineds[i];+		ci->folders[i].digest = digest.digests[i];+	}++	/*+	 *  Must be kEnd.+	 */+	if ((p = header_bytes(a, 1)) == NULL)+		goto failed;+	if (*p != kEnd)+		goto failed;+	free_Digest(&digest);+	return (0);+failed:+	free_Digest(&digest);+	return (-1);+}++static uint64_t+folder_uncompressed_size(struct _7z_folder *f)+{+	int n = (int)f->numOutStreams;+	unsigned pairs = (unsigned)f->numBindPairs;++	while (--n >= 0) {+		unsigned i;+		for (i = 0; i < pairs; i++) {+			if (f->bindPairs[i].outIndex == (uint64_t)n)+				break;+		}+		if (i >= pairs)+			return (f->unPackSize[n]);+	}+	return (0);+}++static void+free_SubStreamsInfo(struct _7z_substream_info *ss)+{+	free(ss->unpackSizes);+	free(ss->digestsDefined);+	free(ss->digests);+}++static int+read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,+    struct _7z_folder *f, size_t numFolders)+{+	const unsigned char *p;+	uint64_t *usizes;+	size_t unpack_streams;+	int type;+	unsigned i;+	uint32_t numDigests;++	memset(ss, 0, sizeof(*ss));++	for (i = 0; i < numFolders; i++)+		f[i].numUnpackStreams = 1;++	if ((p = header_bytes(a, 1)) == NULL)+		return (-1);+	type = *p;++	if (type == kNumUnPackStream) {+		unpack_streams = 0;+		for (i = 0; i < numFolders; i++) {+			if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0)+				return (-1);+			if (UMAX_ENTRY < f[i].numUnpackStreams)+				return (-1);+			if (unpack_streams > SIZE_MAX - UMAX_ENTRY) {+				return (-1);+			}+			unpack_streams += (size_t)f[i].numUnpackStreams;+		}+		if ((p = header_bytes(a, 1)) == NULL)+			return (-1);+		type = *p;+	} else+		unpack_streams = numFolders;++	ss->unpack_streams = unpack_streams;+	if (unpack_streams) {+		ss->unpackSizes = calloc(unpack_streams,+		    sizeof(*ss->unpackSizes));+		ss->digestsDefined = calloc(unpack_streams,+		    sizeof(*ss->digestsDefined));+		ss->digests = calloc(unpack_streams,+		    sizeof(*ss->digests));+		if (ss->unpackSizes == NULL || ss->digestsDefined == NULL ||+		    ss->digests == NULL)+			return (-1);+	}++	usizes = ss->unpackSizes;+	for (i = 0; i < numFolders; i++) {+		unsigned pack;+		uint64_t sum;++		if (f[i].numUnpackStreams == 0)+			continue;++		sum = 0;+		if (type == kSize) {+			for (pack = 1; pack < f[i].numUnpackStreams; pack++) {+				if (parse_7zip_uint64(a, usizes) < 0)+					return (-1);+				sum += *usizes++;+			}+		}+		*usizes++ = folder_uncompressed_size(&f[i]) - sum;+	}++	if (type == kSize) {+		if ((p = header_bytes(a, 1)) == NULL)+			return (-1);+		type = *p;+	}++	for (i = 0; i < unpack_streams; i++) {+		ss->digestsDefined[i] = 0;+		ss->digests[i] = 0;+	}++	numDigests = 0;+	for (i = 0; i < numFolders; i++) {+		if (f[i].numUnpackStreams != 1 || !f[i].digest_defined)+			numDigests += (uint32_t)f[i].numUnpackStreams;+	}++	if (type == kCRC) {+		struct _7z_digests tmpDigests;+		unsigned char *digestsDefined = ss->digestsDefined;+		uint32_t * digests = ss->digests;+		int di = 0;++		memset(&tmpDigests, 0, sizeof(tmpDigests));+		if (read_Digests(a, &(tmpDigests), numDigests) < 0) {+			free_Digest(&tmpDigests);+			return (-1);+		}+		for (i = 0; i < numFolders; i++) {+			if (f[i].numUnpackStreams == 1 && f[i].digest_defined) {+				*digestsDefined++ = 1;+				*digests++ = f[i].digest;+			} else {+				unsigned j;++				for (j = 0; j < f[i].numUnpackStreams;+				    j++, di++) {+					*digestsDefined++ =+					    tmpDigests.defineds[di];+					*digests++ =+					    tmpDigests.digests[di];+				}+			}+		}+		free_Digest(&tmpDigests);+		if ((p = header_bytes(a, 1)) == NULL)+			return (-1);+		type = *p;+	}++	/*+	 *  Must be kEnd.+	 */+	if (type != kEnd)+		return (-1);+	return (0);+}++static void+free_StreamsInfo(struct _7z_stream_info *si)+{+	free_PackInfo(&(si->pi));+	free_CodersInfo(&(si->ci));+	free_SubStreamsInfo(&(si->ss));+}++static int+read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	const unsigned char *p;+	unsigned i;++	memset(si, 0, sizeof(*si));++	if ((p = header_bytes(a, 1)) == NULL)+		return (-1);+	if (*p == kPackInfo) {+		uint64_t packPos;++		if (read_PackInfo(a, &(si->pi)) < 0)+			return (-1);++		if (si->pi.positions == NULL || si->pi.sizes == NULL)+			return (-1);+		/*+		 * Calculate packed stream positions.+		 */+		packPos = si->pi.pos;+		for (i = 0; i < si->pi.numPackStreams; i++) {+			si->pi.positions[i] = packPos;+			packPos += si->pi.sizes[i];+			if (packPos > zip->header_offset)+				return (-1);+		}+		if ((p = header_bytes(a, 1)) == NULL)+			return (-1);+	}+	if (*p == kUnPackInfo) {+		uint32_t packIndex;+		struct _7z_folder *f;++		if (read_CodersInfo(a, &(si->ci)) < 0)+			return (-1);++		/*+		 * Calculate packed stream indexes.+		 */+		packIndex = 0;+		f = si->ci.folders;+		for (i = 0; i < si->ci.numFolders; i++) {+			f[i].packIndex = packIndex;+			packIndex += (uint32_t)f[i].numPackedStreams;+			if (packIndex > si->pi.numPackStreams)+				return (-1);+		}+		if ((p = header_bytes(a, 1)) == NULL)+			return (-1);+	}++	if (*p == kSubStreamsInfo) {+		if (read_SubStreamsInfo(a, &(si->ss),+		    si->ci.folders, (size_t)si->ci.numFolders) < 0)+			return (-1);+		if ((p = header_bytes(a, 1)) == NULL)+			return (-1);+	}++	/*+	 *  Must be kEnd.+	 */+	if (*p != kEnd)+		return (-1);+	return (0);+}++static void+free_Header(struct _7z_header_info *h)+{+	free(h->emptyStreamBools);+	free(h->emptyFileBools);+	free(h->antiBools);+	free(h->attrBools);+}++static int+read_Header(struct archive_read *a, struct _7z_header_info *h,+    int check_header_id)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	const unsigned char *p;+	struct _7z_folder *folders;+	struct _7z_stream_info *si = &(zip->si);+	struct _7zip_entry *entries;+	uint32_t folderIndex, indexInFolder;+	unsigned i;+	int eindex, empty_streams, sindex;++	if (check_header_id) {+		/*+		 * Read Header.+		 */+		if ((p = header_bytes(a, 1)) == NULL)+			return (-1);+		if (*p != kHeader)+			return (-1);+	}++	/*+	 * Read ArchiveProperties.+	 */+	if ((p = header_bytes(a, 1)) == NULL)+		return (-1);+	if (*p == kArchiveProperties) {+		for (;;) {+			uint64_t size;+			if ((p = header_bytes(a, 1)) == NULL)+				return (-1);+			if (*p == 0)+				break;+			if (parse_7zip_uint64(a, &size) < 0)+				return (-1);+		}+		if ((p = header_bytes(a, 1)) == NULL)+			return (-1);+	}++	/*+	 * Read MainStreamsInfo.+	 */+	if (*p == kMainStreamsInfo) {+		if (read_StreamsInfo(a, &(zip->si)) < 0)+			return (-1);+		if ((p = header_bytes(a, 1)) == NULL)+			return (-1);+	}+	if (*p == kEnd)+		return (0);++	/*+	 * Read FilesInfo.+	 */+	if (*p != kFilesInfo)+		return (-1);++	if (parse_7zip_uint64(a, &(zip->numFiles)) < 0)+		return (-1);+	if (UMAX_ENTRY < zip->numFiles)+		return (-1);++	zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries));+	if (zip->entries == NULL)+		return (-1);+	entries = zip->entries;++	empty_streams = 0;+	for (;;) {+		int type;+		uint64_t size;+		size_t ll;++		if ((p = header_bytes(a, 1)) == NULL)+			return (-1);+		type = *p;+		if (type == kEnd)+			break;++		if (parse_7zip_uint64(a, &size) < 0)+			return (-1);+		if (zip->header_bytes_remaining < size)+			return (-1);+		ll = (size_t)size;++		switch (type) {+		case kEmptyStream:+			if (h->emptyStreamBools != NULL)+				return (-1);+			h->emptyStreamBools = calloc((size_t)zip->numFiles,+			    sizeof(*h->emptyStreamBools));+			if (h->emptyStreamBools == NULL)+				return (-1);+			if (read_Bools(+			    a, h->emptyStreamBools, (size_t)zip->numFiles) < 0)+				return (-1);+			empty_streams = 0;+			for (i = 0; i < zip->numFiles; i++) {+				if (h->emptyStreamBools[i])+					empty_streams++;+			}+			break;+		case kEmptyFile:+			if (empty_streams <= 0) {+				/* Unexcepted sequence. Skip this. */+				if (header_bytes(a, ll) == NULL)+					return (-1);+				break;+			}+			if (h->emptyFileBools != NULL)+				return (-1);+			h->emptyFileBools = calloc(empty_streams,+			    sizeof(*h->emptyFileBools));+			if (h->emptyFileBools == NULL)+				return (-1);+			if (read_Bools(a, h->emptyFileBools, empty_streams) < 0)+				return (-1);+			break;+		case kAnti:+			if (empty_streams <= 0) {+				/* Unexcepted sequence. Skip this. */+				if (header_bytes(a, ll) == NULL)+					return (-1);+				break;+			}+			if (h->antiBools != NULL)+				return (-1);+			h->antiBools = calloc(empty_streams,+			    sizeof(*h->antiBools));+			if (h->antiBools == NULL)+				return (-1);+			if (read_Bools(a, h->antiBools, empty_streams) < 0)+				return (-1);+			break;+		case kCTime:+		case kATime:+		case kMTime:+			if (read_Times(a, h, type) < 0)+				return (-1);+			break;+		case kName:+		{+			unsigned char *np;+			size_t nl, nb;++			/* Skip one byte. */+			if ((p = header_bytes(a, 1)) == NULL)+				return (-1);+			ll--;++			if ((ll & 1) || ll < zip->numFiles * 4)+				return (-1);++			if (zip->entry_names != NULL)+				return (-1);+			zip->entry_names = malloc(ll);+			if (zip->entry_names == NULL)+				return (-1);+			np = zip->entry_names;+			nb = ll;+			/*+			 * Copy whole file names.+			 * NOTE: This loop prevents from expanding+			 * the uncompressed buffer in order not to+			 * use extra memory resource.+			 */+			while (nb) {+				size_t b;+				if (nb > UBUFF_SIZE)+					b = UBUFF_SIZE;+				else+					b = nb;+				if ((p = header_bytes(a, b)) == NULL)+					return (-1);+				memcpy(np, p, b);+				np += b;+				nb -= b;+			}+			np = zip->entry_names;+			nl = ll;++			for (i = 0; i < zip->numFiles; i++) {+				entries[i].utf16name = np;+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)+				entries[i].wname = (wchar_t *)np;+#endif++				/* Find a terminator. */+				while (nl >= 2 && (np[0] || np[1])) {+					np += 2;+					nl -= 2;+				}+				if (nl < 2)+					return (-1);/* Terminator not found */+				entries[i].name_len = np - entries[i].utf16name;+				np += 2;+				nl -= 2;+			}+			break;+		}+		case kAttributes:+		{+			int allAreDefined;++			if ((p = header_bytes(a, 2)) == NULL)+				return (-1);+			allAreDefined = *p;+			if (h->attrBools != NULL)+				return (-1);+			h->attrBools = calloc((size_t)zip->numFiles,+			    sizeof(*h->attrBools));+			if (h->attrBools == NULL)+				return (-1);+			if (allAreDefined)+				memset(h->attrBools, 1, (size_t)zip->numFiles);+			else {+				if (read_Bools(a, h->attrBools,+				      (size_t)zip->numFiles) < 0)+					return (-1);+			}+			for (i = 0; i < zip->numFiles; i++) {+				if (h->attrBools[i]) {+					if ((p = header_bytes(a, 4)) == NULL)+						return (-1);+					entries[i].attr = archive_le32dec(p);+				}+			}+			break;+		}+		case kDummy:+			if (ll == 0)+				break;+			__LA_FALLTHROUGH;+		default:+			if (header_bytes(a, ll) == NULL)+				return (-1);+			break;+		}+	}++	/*+	 * Set up entry's attributes.+	 */+	folders = si->ci.folders;+	eindex = sindex = 0;+	folderIndex = indexInFolder = 0;+	for (i = 0; i < zip->numFiles; i++) {+		if (h->emptyStreamBools == NULL || h->emptyStreamBools[i] == 0)+			entries[i].flg |= HAS_STREAM;+		/* The high 16 bits of attributes is a posix file mode. */+		entries[i].mode = entries[i].attr >> 16;+		if (entries[i].flg & HAS_STREAM) {+			if ((size_t)sindex >= si->ss.unpack_streams)+				return (-1);+			if (entries[i].mode == 0)+				entries[i].mode = AE_IFREG | 0666;+			if (si->ss.digestsDefined[sindex])+				entries[i].flg |= CRC32_IS_SET;+			entries[i].ssIndex = sindex;+			sindex++;+		} else {+			int dir;+			if (h->emptyFileBools == NULL)+				dir = 1;+			else {+				if (h->emptyFileBools[eindex])+					dir = 0;+				else+					dir = 1;+				eindex++;+			}+			if (entries[i].mode == 0) {+				if (dir)+					entries[i].mode = AE_IFDIR | 0777;+				else+					entries[i].mode = AE_IFREG | 0666;+			} else if (dir &&+			    (entries[i].mode & AE_IFMT) != AE_IFDIR) {+				entries[i].mode &= ~AE_IFMT;+				entries[i].mode |= AE_IFDIR;+			}+			if ((entries[i].mode & AE_IFMT) == AE_IFDIR &&+			    entries[i].name_len >= 2 &&+			    (entries[i].utf16name[entries[i].name_len-2] != '/' ||+			     entries[i].utf16name[entries[i].name_len-1] != 0)) {+				entries[i].utf16name[entries[i].name_len] = '/';+				entries[i].utf16name[entries[i].name_len+1] = 0;+				entries[i].name_len += 2;+			}+			entries[i].ssIndex = -1;+		}+		if (entries[i].attr & 0x01)+			entries[i].mode &= ~0222;/* Read only. */++		if ((entries[i].flg & HAS_STREAM) == 0 && indexInFolder == 0) {+			/*+			 * The entry is an empty file or a directory file,+			 * those both have no contents.+			 */+			entries[i].folderIndex = -1;+			continue;+		}+		if (indexInFolder == 0) {+			for (;;) {+				if (folderIndex >= si->ci.numFolders)+					return (-1);+				if (folders[folderIndex].numUnpackStreams)+					break;+				folderIndex++;+			}+		}+		entries[i].folderIndex = folderIndex;+		if ((entries[i].flg & HAS_STREAM) == 0)+			continue;+		indexInFolder++;+		if (indexInFolder >= folders[folderIndex].numUnpackStreams) {+			folderIndex++;+			indexInFolder = 0;+		}+	}++	return (0);+}++#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)+static void+fileTimeToUtc(uint64_t fileTime, time_t *timep, long *ns)+{++	if (fileTime >= EPOC_TIME) {+		fileTime -= EPOC_TIME;+		/* milli seconds base */+		*timep = (time_t)(fileTime / 10000000);+		/* nano seconds base */+		*ns = (long)(fileTime % 10000000) * 100;+	} else {+		*timep = 0;+		*ns = 0;+	}+}++static int+read_Times(struct archive_read *a, struct _7z_header_info *h, int type)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	const unsigned char *p;+	struct _7zip_entry *entries = zip->entries;+	unsigned char *timeBools;+	int allAreDefined;+	unsigned i;++	timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools));+	if (timeBools == NULL)+		return (-1);++	/* Read allAreDefined. */+	if ((p = header_bytes(a, 1)) == NULL)+		goto failed;+	allAreDefined = *p;+	if (allAreDefined)+		memset(timeBools, 1, (size_t)zip->numFiles);+	else {+		if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0)+			goto failed;+	}++	/* Read external. */+	if ((p = header_bytes(a, 1)) == NULL)+		goto failed;+	if (*p) {+		if (parse_7zip_uint64(a, &(h->dataIndex)) < 0)+			goto failed;+		if (UMAX_ENTRY < h->dataIndex)+			goto failed;+	}++	for (i = 0; i < zip->numFiles; i++) {+		if (!timeBools[i])+			continue;+		if ((p = header_bytes(a, 8)) == NULL)+			goto failed;+		switch (type) {+		case kCTime:+			fileTimeToUtc(archive_le64dec(p),+			    &(entries[i].ctime),+			    &(entries[i].ctime_ns));+			entries[i].flg |= CTIME_IS_SET;+			break;+		case kATime:+			fileTimeToUtc(archive_le64dec(p),+			    &(entries[i].atime),+			    &(entries[i].atime_ns));+			entries[i].flg |= ATIME_IS_SET;+			break;+		case kMTime:+			fileTimeToUtc(archive_le64dec(p),+			    &(entries[i].mtime),+			    &(entries[i].mtime_ns));+			entries[i].flg |= MTIME_IS_SET;+			break;+		}+	}++	free(timeBools);+	return (0);+failed:+	free(timeBools);+	return (-1);+}++static int+decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;++	errno = 0;+	if (read_StreamsInfo(a, si) < 0) {+		if (errno == ENOMEM)+			archive_set_error(&a->archive, -1,+			    "Couldn't allocate memory");+		else+			archive_set_error(&a->archive, -1,+			    "Malformed 7-Zip archive");+		return (ARCHIVE_FATAL);+	}++	if (si->pi.numPackStreams == 0 || si->ci.numFolders == 0) {+		archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");+		return (ARCHIVE_FATAL);+	}++	if (zip->header_offset < si->pi.pos + si->pi.sizes[0] ||+	    (int64_t)(si->pi.pos + si->pi.sizes[0]) < 0 ||+	    si->pi.sizes[0] == 0 || (int64_t)si->pi.pos < 0) {+		archive_set_error(&a->archive, -1, "Malformed Header offset");+		return (ARCHIVE_FATAL);+	}++	return (ARCHIVE_OK);+}++static const unsigned char *+header_bytes(struct archive_read *a, size_t rbytes)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	const unsigned char *p;++	if (zip->header_bytes_remaining < rbytes)+		return (NULL);+	if (zip->pack_stream_bytes_unconsumed)+		read_consume(a);++	if (zip->header_is_encoded == 0) {+		p = __archive_read_ahead(a, rbytes, NULL);+		if (p == NULL)+			return (NULL);+		zip->header_bytes_remaining -= rbytes;+		zip->pack_stream_bytes_unconsumed = rbytes;+	} else {+		const void *buff;+		ssize_t bytes;++		bytes = read_stream(a, &buff, rbytes, rbytes);+		if (bytes <= 0)+			return (NULL);+		zip->header_bytes_remaining -= bytes;+		p = buff;+	}++	/* Update checksum */+	zip->header_crc32 = crc32(zip->header_crc32, p, (unsigned)rbytes);+	return (p);+}++static int+slurp_central_directory(struct archive_read *a, struct _7zip *zip,+    struct _7z_header_info *header)+{+	const unsigned char *p;+	uint64_t next_header_offset;+	uint64_t next_header_size;+	uint32_t next_header_crc;+	ssize_t bytes_avail;+	int check_header_crc, r;++	if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL)+		return (ARCHIVE_FATAL);++	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {+		/* This is an executable ? Must be self-extracting... */+		r = skip_sfx(a, bytes_avail);+		if (r < ARCHIVE_WARN)+			return (r);+		if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL)+			return (ARCHIVE_FATAL);+	}+	zip->seek_base += 32;++	if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) {+		archive_set_error(&a->archive, -1, "Not 7-Zip archive file");+		return (ARCHIVE_FATAL);+	}++	/* CRC check. */+	if (crc32(0, (const unsigned char *)p + 12, 20)+	    != archive_le32dec(p + 8)) {+		archive_set_error(&a->archive, -1, "Header CRC error");+		return (ARCHIVE_FATAL);+	}++	next_header_offset = archive_le64dec(p + 12);+	next_header_size = archive_le64dec(p + 20);+	next_header_crc = archive_le32dec(p + 28);++	if (next_header_size == 0)+		/* There is no entry in an archive file. */+		return (ARCHIVE_EOF);++	if (((int64_t)next_header_offset) < 0) {+		archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");+		return (ARCHIVE_FATAL);+	}+	__archive_read_consume(a, 32);+	if (next_header_offset != 0) {+		if (bytes_avail >= (ssize_t)next_header_offset)+			__archive_read_consume(a, next_header_offset);+		else if (__archive_read_seek(a,+		    next_header_offset + zip->seek_base, SEEK_SET) < 0)+			return (ARCHIVE_FATAL);+	}+	zip->stream_offset = next_header_offset;+	zip->header_offset = next_header_offset;+	zip->header_bytes_remaining = next_header_size;+	zip->header_crc32 = 0;+	zip->header_is_encoded = 0;+	zip->header_is_being_read = 1;+	zip->has_encrypted_entries = 0;+	check_header_crc = 1;++	if ((p = header_bytes(a, 1)) == NULL) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated 7-Zip file body");+		return (ARCHIVE_FATAL);+	}+	/* Parse ArchiveProperties. */+	switch (p[0]) {+	case kEncodedHeader:+		/*+		 * The archive has an encoded header and we have to decode it+		 * in order to parse the header correctly.+		 */+		r = decode_encoded_header_info(a, &(zip->si));++		/* Check the EncodedHeader CRC.*/+		if (r == 0 && zip->header_crc32 != next_header_crc) {+			archive_set_error(&a->archive, -1,+			    "Damaged 7-Zip archive");+			r = -1;+		}+		if (r == 0) {+			if (zip->si.ci.folders[0].digest_defined)+				next_header_crc = zip->si.ci.folders[0].digest;+			else+				check_header_crc = 0;+			if (zip->pack_stream_bytes_unconsumed)+				read_consume(a);+			r = setup_decode_folder(a, zip->si.ci.folders, 1);+			if (r == 0) {+				zip->header_bytes_remaining =+					zip->folder_outbytes_remaining;+				r = seek_pack(a);+			}+		}+		/* Clean up StreamsInfo. */+		free_StreamsInfo(&(zip->si));+		memset(&(zip->si), 0, sizeof(zip->si));+		if (r < 0)+			return (ARCHIVE_FATAL);+		zip->header_is_encoded = 1;+		zip->header_crc32 = 0;+		/* FALL THROUGH */+	case kHeader:+		/*+		 * Parse the header.+		 */+		errno = 0;+		r = read_Header(a, header, zip->header_is_encoded);+		if (r < 0) {+			if (errno == ENOMEM)+				archive_set_error(&a->archive, -1,+				    "Couldn't allocate memory");+			else+				archive_set_error(&a->archive, -1,+				    "Damaged 7-Zip archive");+			return (ARCHIVE_FATAL);+		}++		/*+		 *  Must be kEnd.+		 */+		if ((p = header_bytes(a, 1)) == NULL ||*p != kEnd) {+			archive_set_error(&a->archive, -1,+			    "Malformed 7-Zip archive");+			return (ARCHIVE_FATAL);+		}++		/* Check the Header CRC.*/+		if (check_header_crc && zip->header_crc32 != next_header_crc) {+			archive_set_error(&a->archive, -1,+			    "Malformed 7-Zip archive");+			return (ARCHIVE_FATAL);+		}+		break;+	default:+		archive_set_error(&a->archive, -1,+		    "Unexpected Property ID = %X", p[0]);+		return (ARCHIVE_FATAL);+	}++	/* Clean up variables be used for decoding the archive header */+	zip->pack_stream_remaining = 0;+	zip->pack_stream_index = 0;+	zip->folder_outbytes_remaining = 0;+	zip->uncompressed_buffer_bytes_remaining = 0;+	zip->pack_stream_bytes_unconsumed = 0;+	zip->header_is_being_read = 0;++	return (ARCHIVE_OK);+}++static ssize_t+get_uncompressed_data(struct archive_read *a, const void **buff, size_t size,+    size_t minimum)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	ssize_t bytes_avail;++	if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {+		/* Copy mode. */++		*buff = __archive_read_ahead(a, minimum, &bytes_avail);+		if (bytes_avail <= 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated 7-Zip file data");+			return (ARCHIVE_FATAL);+		}+		if ((size_t)bytes_avail >+		    zip->uncompressed_buffer_bytes_remaining)+			bytes_avail = (ssize_t)+			    zip->uncompressed_buffer_bytes_remaining;+		if ((size_t)bytes_avail > size)+			bytes_avail = (ssize_t)size;++		zip->pack_stream_bytes_unconsumed = bytes_avail;+	} else if (zip->uncompressed_buffer_pointer == NULL) {+		/* Decompression has failed. */+		archive_set_error(&(a->archive),+		    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");+		return (ARCHIVE_FATAL);+	} else {+		/* Packed mode. */+		if (minimum > zip->uncompressed_buffer_bytes_remaining) {+			/*+			 * If remaining uncompressed data size is less than+			 * the minimum size, fill the buffer up to the+			 * minimum size.+			 */+			if (extract_pack_stream(a, minimum) < 0)+				return (ARCHIVE_FATAL);+		}+		if (size > zip->uncompressed_buffer_bytes_remaining)+			bytes_avail = (ssize_t)+			    zip->uncompressed_buffer_bytes_remaining;+		else+			bytes_avail = (ssize_t)size;+		*buff = zip->uncompressed_buffer_pointer;+		zip->uncompressed_buffer_pointer += bytes_avail;+	}+	zip->uncompressed_buffer_bytes_remaining -= bytes_avail;+	return (bytes_avail);+}++static ssize_t+extract_pack_stream(struct archive_read *a, size_t minimum)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	ssize_t bytes_avail;+	int r;++	if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {+		if (minimum == 0)+			minimum = 1;+		if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL+		    || bytes_avail <= 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated 7-Zip file body");+			return (ARCHIVE_FATAL);+		}+		if (bytes_avail > (ssize_t)zip->pack_stream_inbytes_remaining)+			bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining;+		zip->pack_stream_inbytes_remaining -= bytes_avail;+		if (bytes_avail > (ssize_t)zip->folder_outbytes_remaining)+			bytes_avail = (ssize_t)zip->folder_outbytes_remaining;+		zip->folder_outbytes_remaining -= bytes_avail;+		zip->uncompressed_buffer_bytes_remaining = bytes_avail;+		return (ARCHIVE_OK);+	}++	/* If the buffer hasn't been allocated, allocate it now. */+	if (zip->uncompressed_buffer == NULL) {+		zip->uncompressed_buffer_size = UBUFF_SIZE;+		if (zip->uncompressed_buffer_size < minimum) {+			zip->uncompressed_buffer_size = minimum + 1023;+			zip->uncompressed_buffer_size &= ~0x3ff;+		}+		zip->uncompressed_buffer =+		    malloc(zip->uncompressed_buffer_size);+		if (zip->uncompressed_buffer == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "No memory for 7-Zip decompression");+			return (ARCHIVE_FATAL);+		}+		zip->uncompressed_buffer_bytes_remaining = 0;+	} else if (zip->uncompressed_buffer_size < minimum ||+	    zip->uncompressed_buffer_bytes_remaining < minimum) {+		/*+		 * Make sure the uncompressed buffer can have bytes+		 * at least `minimum' bytes.+		 * NOTE: This case happen when reading the header.+		 */+		size_t used;+		if (zip->uncompressed_buffer_pointer != 0)+			used = zip->uncompressed_buffer_pointer -+				zip->uncompressed_buffer;+		else+			used = 0;+		if (zip->uncompressed_buffer_size < minimum) {+			/*+			 * Expand the uncompressed buffer up to+			 * the minimum size.+			 */+			void *p;+			size_t new_size;++			new_size = minimum + 1023;+			new_size &= ~0x3ff;+			p = realloc(zip->uncompressed_buffer, new_size);+			if (p == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "No memory for 7-Zip decompression");+				return (ARCHIVE_FATAL);+			}+			zip->uncompressed_buffer = (unsigned char *)p;+			zip->uncompressed_buffer_size = new_size;+		}+		/*+		 * Move unconsumed bytes to the head.+		 */+		if (used) {+			memmove(zip->uncompressed_buffer,+				zip->uncompressed_buffer + used,+				zip->uncompressed_buffer_bytes_remaining);+		}+	} else+		zip->uncompressed_buffer_bytes_remaining = 0;+	zip->uncompressed_buffer_pointer = NULL;+	for (;;) {+		size_t bytes_in, bytes_out;+		const void *buff_in;+		unsigned char *buff_out;+		int end_of_data;++		/*+		 * Note: '1' here is a performance optimization.+		 * Recall that the decompression layer returns a count of+		 * available bytes; asking for more than that forces the+		 * decompressor to combine reads by copying data.+		 */+		buff_in = __archive_read_ahead(a, 1, &bytes_avail);+		if (bytes_avail <= 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated 7-Zip file body");+			return (ARCHIVE_FATAL);+		}++		buff_out = zip->uncompressed_buffer+			+ zip->uncompressed_buffer_bytes_remaining;+		bytes_out = zip->uncompressed_buffer_size+			- zip->uncompressed_buffer_bytes_remaining;+		bytes_in = bytes_avail;+		if (bytes_in > zip->pack_stream_inbytes_remaining)+			bytes_in = (size_t)zip->pack_stream_inbytes_remaining;+		/* Drive decompression. */+		r = decompress(a, zip, buff_out, &bytes_out,+			buff_in, &bytes_in);+		switch (r) {+		case ARCHIVE_OK:+			end_of_data = 0;+			break;+		case ARCHIVE_EOF:+			end_of_data = 1;+			break;+		default:+			return (ARCHIVE_FATAL);+		}+		zip->pack_stream_inbytes_remaining -= bytes_in;+		if (bytes_out > zip->folder_outbytes_remaining)+			bytes_out = (size_t)zip->folder_outbytes_remaining;+		zip->folder_outbytes_remaining -= bytes_out;+		zip->uncompressed_buffer_bytes_remaining += bytes_out;+		zip->pack_stream_bytes_unconsumed = bytes_in;++		/*+		 * Continue decompression until uncompressed_buffer is full.+		 */+		if (zip->uncompressed_buffer_bytes_remaining ==+		    zip->uncompressed_buffer_size)+			break;+		if (zip->codec2 == _7Z_X86 && zip->odd_bcj_size &&+		    zip->uncompressed_buffer_bytes_remaining + 5 >+		    zip->uncompressed_buffer_size)+			break;+		if (zip->pack_stream_inbytes_remaining == 0 &&+		    zip->folder_outbytes_remaining == 0)+			break;+		if (end_of_data || (bytes_in == 0 && bytes_out == 0)) {+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");+			return (ARCHIVE_FATAL);+		}+		read_consume(a);+	}+	if (zip->uncompressed_buffer_bytes_remaining < minimum) {+		archive_set_error(&(a->archive),+		    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");+		return (ARCHIVE_FATAL);+	}+	zip->uncompressed_buffer_pointer = zip->uncompressed_buffer;+	return (ARCHIVE_OK);+}++static int+seek_pack(struct archive_read *a)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	int64_t pack_offset;++	if (zip->pack_stream_remaining <= 0) {+		archive_set_error(&(a->archive),+		    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");+		return (ARCHIVE_FATAL);+	}+	zip->pack_stream_inbytes_remaining =+	    zip->si.pi.sizes[zip->pack_stream_index];+	pack_offset = zip->si.pi.positions[zip->pack_stream_index];+	if (zip->stream_offset != pack_offset) {+		if (0 > __archive_read_seek(a, pack_offset + zip->seek_base,+		    SEEK_SET))+			return (ARCHIVE_FATAL);+		zip->stream_offset = pack_offset;+	}+	zip->pack_stream_index++;+	zip->pack_stream_remaining--;+	return (ARCHIVE_OK);+}++static ssize_t+read_stream(struct archive_read *a, const void **buff, size_t size,+    size_t minimum)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	uint64_t skip_bytes = 0;+	ssize_t r;++	if (zip->uncompressed_buffer_bytes_remaining == 0) {+		if (zip->pack_stream_inbytes_remaining > 0) {+			r = extract_pack_stream(a, 0);+			if (r < 0)+				return (r);+			return (get_uncompressed_data(a, buff, size, minimum));+		} else if (zip->folder_outbytes_remaining > 0) {+			/* Extract a remaining pack stream. */+			r = extract_pack_stream(a, 0);+			if (r < 0)+				return (r);+			return (get_uncompressed_data(a, buff, size, minimum));+		}+	} else+		return (get_uncompressed_data(a, buff, size, minimum));++	/*+	 * Current pack stream has been consumed.+	 */+	if (zip->pack_stream_remaining == 0) {+		if (zip->header_is_being_read) {+			/* Invalid sequence. This might happen when+			 * reading a malformed archive. */+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC, "Malformed 7-Zip archive");+			return (ARCHIVE_FATAL);+		}++		/*+		 * All current folder's pack streams have been+		 * consumed. Switch to next folder.+		 */+		if (zip->folder_index == 0 &&+		    (zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes+		     || zip->folder_index != zip->entry->folderIndex)) {+			zip->folder_index = zip->entry->folderIndex;+			skip_bytes =+			    zip->si.ci.folders[zip->folder_index].skipped_bytes;+		}++		if (zip->folder_index >= zip->si.ci.numFolders) {+			/*+			 * We have consumed all folders and its pack streams.+			 */+			*buff = NULL;+			return (0);+		}+		r = setup_decode_folder(a,+			&(zip->si.ci.folders[zip->folder_index]), 0);+		if (r != ARCHIVE_OK)+			return (ARCHIVE_FATAL);++		zip->folder_index++;+	}++	/*+	 * Switch to next pack stream.+	 */+	r = seek_pack(a);+	if (r < 0)+		return (r);++	/* Extract a new pack stream. */+	r = extract_pack_stream(a, 0);+	if (r < 0)+		return (r);++	/*+	 * Skip the bytes we already has skipped in skip_stream().+	 */+	while (skip_bytes) {+		ssize_t skipped;++		if (zip->uncompressed_buffer_bytes_remaining == 0) {+			if (zip->pack_stream_inbytes_remaining > 0) {+				r = extract_pack_stream(a, 0);+				if (r < 0)+					return (r);+			} else if (zip->folder_outbytes_remaining > 0) {+				/* Extract a remaining pack stream. */+				r = extract_pack_stream(a, 0);+				if (r < 0)+					return (r);+			} else {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Truncated 7-Zip file body");+				return (ARCHIVE_FATAL);+			}+		}+		skipped = get_uncompressed_data(+			a, buff, (size_t)skip_bytes, 0);+		if (skipped < 0)+			return (skipped);+		skip_bytes -= skipped;+		if (zip->pack_stream_bytes_unconsumed)+			read_consume(a);+	}++	return (get_uncompressed_data(a, buff, size, minimum));+}++static int+setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,+    int header)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	const struct _7z_coder *coder1, *coder2;+	const char *cname = (header)?"archive header":"file content";+	unsigned i;+	int r, found_bcj2 = 0;++	/*+	 * Release the memory which the previous folder used for BCJ2.+	 */+	for (i = 0; i < 3; i++) {+		free(zip->sub_stream_buff[i]);+		zip->sub_stream_buff[i] = NULL;+	}++	/*+	 * Initialize a stream reader.+	 */+	zip->pack_stream_remaining = (unsigned)folder->numPackedStreams;+	zip->pack_stream_index = (unsigned)folder->packIndex;+	zip->folder_outbytes_remaining = folder_uncompressed_size(folder);+	zip->uncompressed_buffer_bytes_remaining = 0;++	/*+	 * Check coder types.+	 */+	for (i = 0; i < folder->numCoders; i++) {+		switch(folder->coders[i].codec) {+			case _7Z_CRYPTO_MAIN_ZIP:+			case _7Z_CRYPTO_RAR_29:+			case _7Z_CRYPTO_AES_256_SHA_256: {+				/* For entry that is associated with this folder, mark+				   it as encrypted (data+metadata). */+				zip->has_encrypted_entries = 1;+				if (a->entry) {+					archive_entry_set_is_data_encrypted(a->entry, 1);+					archive_entry_set_is_metadata_encrypted(a->entry, 1);+				}+				archive_set_error(&(a->archive),+					ARCHIVE_ERRNO_MISC,+					"The %s is encrypted, "+					"but currently not supported", cname);+				return (ARCHIVE_FATAL);+			}+			case _7Z_X86_BCJ2: {+				found_bcj2++;+				break;+			}+		}+	}+	/* Now that we've checked for encryption, if there were still no+	 * encrypted entries found we can say for sure that there are none.+	 */+	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {+		zip->has_encrypted_entries = 0;+	}++	if ((folder->numCoders > 2 && !found_bcj2) || found_bcj2 > 1) {+		archive_set_error(&(a->archive),+		    ARCHIVE_ERRNO_MISC,+		    "The %s is encoded with many filters, "+		    "but currently not supported", cname);+		return (ARCHIVE_FATAL);+	}+	coder1 = &(folder->coders[0]);+	if (folder->numCoders == 2)+		coder2 = &(folder->coders[1]);+	else+		coder2 = NULL;++	if (found_bcj2) {+		/*+		 * Preparation to decode BCJ2.+		 * Decoding BCJ2 requires four sources. Those are at least,+		 * as far as I know, two types of the storage form.+		 */+		const struct _7z_coder *fc = folder->coders;+		static const struct _7z_coder coder_copy = {0, 1, 1, 0, NULL};+		const struct _7z_coder *scoder[3] =+			{&coder_copy, &coder_copy, &coder_copy};+		const void *buff;+		ssize_t bytes;+		unsigned char *b[3] = {NULL, NULL, NULL};+		uint64_t sunpack[3] ={-1, -1, -1};+		size_t s[3] = {0, 0, 0};+		int idx[3] = {0, 1, 2};++		if (folder->numCoders == 4 && fc[3].codec == _7Z_X86_BCJ2 &&+		    folder->numInStreams == 7 && folder->numOutStreams == 4 &&+		    zip->pack_stream_remaining == 4) {+			/* Source type 1 made by 7zr or 7z with -m options. */+			if (folder->bindPairs[0].inIndex == 5) {+				/* The form made by 7zr */+				idx[0] = 1; idx[1] = 2; idx[2] = 0;+				scoder[1] = &(fc[1]);+				scoder[2] = &(fc[0]);+				sunpack[1] = folder->unPackSize[1];+				sunpack[2] = folder->unPackSize[0];+				coder1 = &(fc[2]);+			} else {+				/*+				 * NOTE: Some patterns do not work.+				 * work:+				 *  7z a -m0=BCJ2 -m1=COPY -m2=COPY+				 *       -m3=(any)+				 *  7z a -m0=BCJ2 -m1=COPY -m2=(any)+				 *       -m3=COPY+				 *  7z a -m0=BCJ2 -m1=(any) -m2=COPY+				 *       -m3=COPY+				 * not work:+				 *  other patterns.+				 *+				 * We have to handle this like `pipe' or+				 * our libarchive7s filter frame work,+				 * decoding the BCJ2 main stream sequentially,+				 * m3 -> m2 -> m1 -> BCJ2.+				 *+				 */+				if (fc[0].codec == _7Z_COPY &&+				    fc[1].codec == _7Z_COPY)+					coder1 = &(folder->coders[2]);+				else if (fc[0].codec == _7Z_COPY &&+				    fc[2].codec == _7Z_COPY)+					coder1 = &(folder->coders[1]);+				else if (fc[1].codec == _7Z_COPY &&+				    fc[2].codec == _7Z_COPY)+					coder1 = &(folder->coders[0]);+				else {+					archive_set_error(&(a->archive),+					    ARCHIVE_ERRNO_MISC,+					    "Unsupported form of "+					    "BCJ2 streams");+					return (ARCHIVE_FATAL);+				}+			}+			coder2 = &(fc[3]);+			zip->main_stream_bytes_remaining =+				(size_t)folder->unPackSize[2];+		} else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 &&+		    zip->pack_stream_remaining == 4 &&+		    folder->numInStreams == 5 && folder->numOutStreams == 2) {+			/* Source type 0 made by 7z */+			zip->main_stream_bytes_remaining =+				(size_t)folder->unPackSize[0];+		} else {+			/* We got an unexpected form. */+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "Unsupported form of BCJ2 streams");+			return (ARCHIVE_FATAL);+		}++		/* Skip the main stream at this time. */+		if ((r = seek_pack(a)) < 0)+			return (r);+		zip->pack_stream_bytes_unconsumed =+		    (size_t)zip->pack_stream_inbytes_remaining;+		read_consume(a);++		/* Read following three sub streams. */+		for (i = 0; i < 3; i++) {+			const struct _7z_coder *coder = scoder[i];++			if ((r = seek_pack(a)) < 0) {+				free(b[0]); free(b[1]); free(b[2]);+				return (r);+			}++			if (sunpack[i] == (uint64_t)-1)+				zip->folder_outbytes_remaining =+				    zip->pack_stream_inbytes_remaining;+			else+				zip->folder_outbytes_remaining = sunpack[i];++			r = init_decompression(a, zip, coder, NULL);+			if (r != ARCHIVE_OK) {+				free(b[0]); free(b[1]); free(b[2]);+				return (ARCHIVE_FATAL);+			}++			/* Allocate memory for the decoded data of a sub+			 * stream. */+			b[i] = malloc((size_t)zip->folder_outbytes_remaining);+			if (b[i] == NULL) {+				free(b[0]); free(b[1]); free(b[2]);+				archive_set_error(&a->archive, ENOMEM,+				    "No memory for 7-Zip decompression");+				return (ARCHIVE_FATAL);+			}++			/* Extract a sub stream. */+			while (zip->pack_stream_inbytes_remaining > 0) {+				r = (int)extract_pack_stream(a, 0);+				if (r < 0) {+					free(b[0]); free(b[1]); free(b[2]);+					return (r);+				}+				bytes = get_uncompressed_data(a, &buff,+				    zip->uncompressed_buffer_bytes_remaining,+				    0);+				if (bytes < 0) {+					free(b[0]); free(b[1]); free(b[2]);+					return ((int)bytes);+				}+				memcpy(b[i]+s[i], buff, bytes);+				s[i] += bytes;+				if (zip->pack_stream_bytes_unconsumed)+					read_consume(a);+			}+		}++		/* Set the sub streams to the right place. */+		for (i = 0; i < 3; i++) {+			zip->sub_stream_buff[i] = b[idx[i]];+			zip->sub_stream_size[i] = s[idx[i]];+			zip->sub_stream_bytes_remaining[i] = s[idx[i]];+		}++		/* Allocate memory used for decoded main stream bytes. */+		if (zip->tmp_stream_buff == NULL) {+			zip->tmp_stream_buff_size = 32 * 1024;+			zip->tmp_stream_buff =+			    malloc(zip->tmp_stream_buff_size);+			if (zip->tmp_stream_buff == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "No memory for 7-Zip decompression");+				return (ARCHIVE_FATAL);+			}+		}+		zip->tmp_stream_bytes_avail = 0;+		zip->tmp_stream_bytes_remaining = 0;+		zip->odd_bcj_size = 0;+		zip->bcj2_outPos = 0;++		/*+		 * Reset a stream reader in order to read the main stream+		 * of BCJ2.+		 */+		zip->pack_stream_remaining = 1;+		zip->pack_stream_index = (unsigned)folder->packIndex;+		zip->folder_outbytes_remaining =+		    folder_uncompressed_size(folder);+		zip->uncompressed_buffer_bytes_remaining = 0;+	}++	/*+	 * Initialize the decompressor for the new folder's pack streams.+	 */+	r = init_decompression(a, zip, coder1, coder2);+	if (r != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	return (ARCHIVE_OK);+}++static int64_t+skip_stream(struct archive_read *a, size_t skip_bytes)+{+	struct _7zip *zip = (struct _7zip *)a->format->data;+	const void *p;+	int64_t skipped_bytes;+	size_t bytes = skip_bytes;++	if (zip->folder_index == 0) {+		/*+		 * Optimization for a list mode.+		 * Avoid unnecessary decoding operations.+		 */+		zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes+		    += skip_bytes;+		return (skip_bytes);+	}++	while (bytes) {+		skipped_bytes = read_stream(a, &p, bytes, 0);+		if (skipped_bytes < 0)+			return (skipped_bytes);+		if (skipped_bytes == 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated 7-Zip file body");+			return (ARCHIVE_FATAL);+		}+		bytes -= (size_t)skipped_bytes;+		if (zip->pack_stream_bytes_unconsumed)+			read_consume(a);+	}+	return (skip_bytes);+}++/*+ * Brought from LZMA SDK.+ *+ * Bra86.c -- Converter for x86 code (BCJ)+ * 2008-10-04 : Igor Pavlov : Public domain+ *+ */++#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF)++static void+x86_Init(struct _7zip *zip)+{+	zip->bcj_state = 0;+	zip->bcj_prevPosT = (size_t)0 - 1;+	zip->bcj_prevMask = 0;+	zip->bcj_ip = 5;+}++static size_t+x86_Convert(struct _7zip *zip, uint8_t *data, size_t size)+{+	static const uint8_t kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0};+	static const uint8_t kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3};+	size_t bufferPos, prevPosT;+	uint32_t ip, prevMask;++	if (size < 5)+		return 0;++	bufferPos = 0;+	prevPosT = zip->bcj_prevPosT;+	prevMask = zip->bcj_prevMask;+	ip = zip->bcj_ip;++	for (;;) {+		uint8_t *p = data + bufferPos;+		uint8_t *limit = data + size - 4;++		for (; p < limit; p++)+			if ((*p & 0xFE) == 0xE8)+				break;+		bufferPos = (size_t)(p - data);+		if (p >= limit)+			break;+		prevPosT = bufferPos - prevPosT;+		if (prevPosT > 3)+			prevMask = 0;+		else {+			prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7;+			if (prevMask != 0) {+				unsigned char b =+					p[4 - kMaskToBitNumber[prevMask]];+				if (!kMaskToAllowedStatus[prevMask] ||+				    Test86MSByte(b)) {+					prevPosT = bufferPos;+					prevMask = ((prevMask << 1) & 0x7) | 1;+					bufferPos++;+					continue;+				}+			}+		}+		prevPosT = bufferPos;++		if (Test86MSByte(p[4])) {+			uint32_t src = ((uint32_t)p[4] << 24) |+				((uint32_t)p[3] << 16) | ((uint32_t)p[2] << 8) |+				((uint32_t)p[1]);+			uint32_t dest;+			for (;;) {+				uint8_t b;+				int b_index;++				dest = src - (ip + (uint32_t)bufferPos);+				if (prevMask == 0)+					break;+				b_index = kMaskToBitNumber[prevMask] * 8;+				b = (uint8_t)(dest >> (24 - b_index));+				if (!Test86MSByte(b))+					break;+				src = dest ^ ((1 << (32 - b_index)) - 1);+			}+			p[4] = (uint8_t)(~(((dest >> 24) & 1) - 1));+			p[3] = (uint8_t)(dest >> 16);+			p[2] = (uint8_t)(dest >> 8);+			p[1] = (uint8_t)dest;+			bufferPos += 5;+		} else {+			prevMask = ((prevMask << 1) & 0x7) | 1;+			bufferPos++;+		}+	}+	zip->bcj_prevPosT = prevPosT;+	zip->bcj_prevMask = prevMask;+	zip->bcj_ip += (uint32_t)bufferPos;+	return (bufferPos);+}++/*+ * Brought from LZMA SDK.+ *+ * Bcj2.c -- Converter for x86 code (BCJ2)+ * 2008-10-04 : Igor Pavlov : Public domain+ *+ */++#define SZ_ERROR_DATA	 ARCHIVE_FAILED++#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80)+#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1))++#define kNumTopBits 24+#define kTopValue ((uint32_t)1 << kNumTopBits)++#define kNumBitModelTotalBits 11+#define kBitModelTotal (1 << kNumBitModelTotalBits)+#define kNumMoveBits 5++#define RC_READ_BYTE (*buffer++)+#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; }+#define RC_INIT2 zip->bcj2_code = 0; zip->bcj2_range = 0xFFFFFFFF; \+  { int ii; for (ii = 0; ii < 5; ii++) { RC_TEST; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }}++#define NORMALIZE if (zip->bcj2_range < kTopValue) { RC_TEST; zip->bcj2_range <<= 8; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }++#define IF_BIT_0(p) ttt = *(p); bound = (zip->bcj2_range >> kNumBitModelTotalBits) * ttt; if (zip->bcj2_code < bound)+#define UPDATE_0(p) zip->bcj2_range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE;+#define UPDATE_1(p) zip->bcj2_range -= bound; zip->bcj2_code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE;++static ssize_t+Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize)+{+	size_t inPos = 0, outPos = 0;+	const uint8_t *buf0, *buf1, *buf2, *buf3;+	size_t size0, size1, size2, size3;+	const uint8_t *buffer, *bufferLim;+	unsigned int i, j;++	size0 = zip->tmp_stream_bytes_remaining;+	buf0 = zip->tmp_stream_buff + zip->tmp_stream_bytes_avail - size0;+	size1 = zip->sub_stream_bytes_remaining[0];+	buf1 = zip->sub_stream_buff[0] + zip->sub_stream_size[0] - size1;+	size2 = zip->sub_stream_bytes_remaining[1];+	buf2 = zip->sub_stream_buff[1] + zip->sub_stream_size[1] - size2;+	size3 = zip->sub_stream_bytes_remaining[2];+	buf3 = zip->sub_stream_buff[2] + zip->sub_stream_size[2] - size3;++	buffer = buf3;+	bufferLim = buffer + size3;++	if (zip->bcj_state == 0) {+		/*+		 * Initialize.+		 */+		zip->bcj2_prevByte = 0;+		for (i = 0;+		    i < sizeof(zip->bcj2_p) / sizeof(zip->bcj2_p[0]); i++)+			zip->bcj2_p[i] = kBitModelTotal >> 1;+		RC_INIT2;+		zip->bcj_state = 1;+	}++	/*+	 * Gather the odd bytes of a previous call.+	 */+	for (i = 0; zip->odd_bcj_size > 0 && outPos < outSize; i++) {+		outBuf[outPos++] = zip->odd_bcj[i];+		zip->odd_bcj_size--;+	}++	if (outSize == 0) {+		zip->bcj2_outPos += outPos;+		return (outPos);+	}++	for (;;) {+		uint8_t b;+		CProb *prob;+		uint32_t bound;+		uint32_t ttt;++		size_t limit = size0 - inPos;+		if (outSize - outPos < limit)+			limit = outSize - outPos;++		if (zip->bcj_state == 1) {+			while (limit != 0) {+				uint8_t bb = buf0[inPos];+				outBuf[outPos++] = bb;+				if (IsJ(zip->bcj2_prevByte, bb)) {+					zip->bcj_state = 2;+					break;+				}+				inPos++;+				zip->bcj2_prevByte = bb;+				limit--;+			}+		}++		if (limit == 0 || outPos == outSize)+			break;+		zip->bcj_state = 1;++		b = buf0[inPos++];++		if (b == 0xE8)+			prob = zip->bcj2_p + zip->bcj2_prevByte;+		else if (b == 0xE9)+			prob = zip->bcj2_p + 256;+		else+			prob = zip->bcj2_p + 257;++		IF_BIT_0(prob) {+			UPDATE_0(prob)+			zip->bcj2_prevByte = b;+		} else {+			uint32_t dest;+			const uint8_t *v;+			uint8_t out[4];++			UPDATE_1(prob)+			if (b == 0xE8) {+				v = buf1;+				if (size1 < 4)+					return SZ_ERROR_DATA;+				buf1 += 4;+				size1 -= 4;+			} else {+				v = buf2;+				if (size2 < 4)+					return SZ_ERROR_DATA;+				buf2 += 4;+				size2 -= 4;+			}+			dest = (((uint32_t)v[0] << 24) |+			    ((uint32_t)v[1] << 16) |+			    ((uint32_t)v[2] << 8) |+			    ((uint32_t)v[3])) -+			    ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4);+			out[0] = (uint8_t)dest;+			out[1] = (uint8_t)(dest >> 8);+			out[2] = (uint8_t)(dest >> 16);+			out[3] = zip->bcj2_prevByte = (uint8_t)(dest >> 24);++			for (i = 0; i < 4 && outPos < outSize; i++)+				outBuf[outPos++] = out[i];+			if (i < 4) {+				/*+				 * Save odd bytes which we could not add into+				 * the output buffer because of out of space.+				 */+				zip->odd_bcj_size = 4 -i;+				for (; i < 4; i++) {+					j = i - 4 + (unsigned)zip->odd_bcj_size;+					zip->odd_bcj[j] = out[i];+				}+				break;+			}+		}+	}+	zip->tmp_stream_bytes_remaining -= inPos;+	zip->sub_stream_bytes_remaining[0] = size1;+	zip->sub_stream_bytes_remaining[1] = size2;+	zip->sub_stream_bytes_remaining[2] = bufferLim - buffer;+	zip->bcj2_outPos += outPos;++	return ((ssize_t)outPos);+}+
+ c/archive_read_support_format_all.c view
@@ -0,0 +1,89 @@+/*-+ * Copyright (c) 2003-2011 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_all.c 174991 2007-12-30 04:58:22Z kientzle $");++#include "archive.h"+#include "archive_private.h"++int+archive_read_support_format_all(struct archive *a)+{+	archive_check_magic(a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_all");++	/* TODO: It would be nice to compute the ordering+	 * here automatically so that people who enable just+	 * a few formats can still get the benefits.  That+	 * may just require the format registration to include+	 * a "maximum read-ahead" value (anything that uses seek+	 * would be essentially infinite read-ahead).  The core+	 * bid management can then sort the bidders before calling+	 * them.+	 *+	 * If you implement the above, please return the list below+	 * to alphabetic order.+	 */++	/*+	 * These bidders are all pretty cheap; they just examine a+	 * small initial part of the archive.  If one of these bids+	 * high, we can maybe avoid running any of the more expensive+	 * bidders below.+	 */+	archive_read_support_format_ar(a);+	archive_read_support_format_cpio(a);+	archive_read_support_format_empty(a);+	archive_read_support_format_lha(a);+	archive_read_support_format_mtree(a);+	archive_read_support_format_tar(a);+	archive_read_support_format_xar(a);+	archive_read_support_format_warc(a);++	/*+	 * Install expensive bidders last.  By doing them last, we+	 * increase the chance that a high bid from someone else will+	 * make it unnecessary for these to do anything at all.+	 */+	/* These three have potentially large look-ahead. */+	archive_read_support_format_7zip(a);+	archive_read_support_format_cab(a);+	archive_read_support_format_rar(a);+	archive_read_support_format_rar5(a);+	archive_read_support_format_iso9660(a);+	/* Seek is really bad, since it forces the read-ahead+	 * logic to discard buffered data. */+	archive_read_support_format_zip(a);++	/* Note: We always return ARCHIVE_OK here, even if some of the+	 * above return ARCHIVE_WARN.  The intent here is to enable+	 * "as much as possible."  Clients who need specific+	 * compression should enable those individually so they can+	 * verify the level of support. */+	/* Clear any warning messages set by the above functions. */+	archive_clear_error(a);+	return (ARCHIVE_OK);+}
+ c/archive_read_support_format_ar.c view
@@ -0,0 +1,638 @@+/*-+ * Copyright (c) 2007 Kai Wang+ * Copyright (c) 2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer+ *    in this position and unchanged.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_ar.c 201101 2009-12-28 03:06:27Z kientzle $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_private.h"++struct ar {+	int64_t	 entry_bytes_remaining;+	/* unconsumed is purely to track data we've gotten from readahead,+	 * but haven't yet marked as consumed.  Must be paired with+	 * entry_bytes_remaining usage/modification.+	 */+	size_t   entry_bytes_unconsumed;+	int64_t	 entry_offset;+	int64_t	 entry_padding;+	char	*strtab;+	size_t	 strtab_size;+	char	 read_global_header;+};++/*+ * Define structure of the "ar" header.+ */+#define AR_name_offset 0+#define AR_name_size 16+#define AR_date_offset 16+#define AR_date_size 12+#define AR_uid_offset 28+#define AR_uid_size 6+#define AR_gid_offset 34+#define AR_gid_size 6+#define AR_mode_offset 40+#define AR_mode_size 8+#define AR_size_offset 48+#define AR_size_size 10+#define AR_fmag_offset 58+#define AR_fmag_size 2++static int	archive_read_format_ar_bid(struct archive_read *a, int);+static int	archive_read_format_ar_cleanup(struct archive_read *a);+static int	archive_read_format_ar_read_data(struct archive_read *a,+		    const void **buff, size_t *size, int64_t *offset);+static int	archive_read_format_ar_skip(struct archive_read *a);+static int	archive_read_format_ar_read_header(struct archive_read *a,+		    struct archive_entry *e);+static uint64_t	ar_atol8(const char *p, unsigned char_cnt);+static uint64_t	ar_atol10(const char *p, unsigned char_cnt);+static int	ar_parse_gnu_filename_table(struct archive_read *a);+static int	ar_parse_common_header(struct ar *ar, struct archive_entry *,+		    const char *h);++int+archive_read_support_format_ar(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct ar *ar;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_ar");++	ar = (struct ar *)calloc(1, sizeof(*ar));+	if (ar == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate ar data");+		return (ARCHIVE_FATAL);+	}+	ar->strtab = NULL;++	r = __archive_read_register_format(a,+	    ar,+	    "ar",+	    archive_read_format_ar_bid,+	    NULL,+	    archive_read_format_ar_read_header,+	    archive_read_format_ar_read_data,+	    archive_read_format_ar_skip,+	    NULL,+	    archive_read_format_ar_cleanup,+	    NULL,+	    NULL);++	if (r != ARCHIVE_OK) {+		free(ar);+		return (r);+	}+	return (ARCHIVE_OK);+}++static int+archive_read_format_ar_cleanup(struct archive_read *a)+{+	struct ar *ar;++	ar = (struct ar *)(a->format->data);+	free(ar->strtab);+	free(ar);+	(a->format->data) = NULL;+	return (ARCHIVE_OK);+}++static int+archive_read_format_ar_bid(struct archive_read *a, int best_bid)+{+	const void *h;++	(void)best_bid; /* UNUSED */++	/*+	 * Verify the 8-byte file signature.+	 * TODO: Do we need to check more than this?+	 */+	if ((h = __archive_read_ahead(a, 8, NULL)) == NULL)+		return (-1);+	if (memcmp(h, "!<arch>\n", 8) == 0) {+		return (64);+	}+	return (-1);+}++static int+_ar_read_header(struct archive_read *a, struct archive_entry *entry,+	struct ar *ar, const char *h, size_t *unconsumed)+{+	char filename[AR_name_size + 1];+	uint64_t number; /* Used to hold parsed numbers before validation. */+	size_t bsd_name_length, entry_size;+	char *p, *st;+	const void *b;+	int r;++	/* Verify the magic signature on the file header. */+	if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) {+		archive_set_error(&a->archive, EINVAL,+		    "Incorrect file header signature");+		return (ARCHIVE_FATAL);+	}++	/* Copy filename into work buffer. */+	strncpy(filename, h + AR_name_offset, AR_name_size);+	filename[AR_name_size] = '\0';++	/*+	 * Guess the format variant based on the filename.+	 */+	if (a->archive.archive_format == ARCHIVE_FORMAT_AR) {+		/* We don't already know the variant, so let's guess. */+		/*+		 * Biggest clue is presence of '/': GNU starts special+		 * filenames with '/', appends '/' as terminator to+		 * non-special names, so anything with '/' should be+		 * GNU except for BSD long filenames.+		 */+		if (strncmp(filename, "#1/", 3) == 0)+			a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;+		else if (strchr(filename, '/') != NULL)+			a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;+		else if (strncmp(filename, "__.SYMDEF", 9) == 0)+			a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;+		/*+		 * XXX Do GNU/SVR4 'ar' programs ever omit trailing '/'+		 * if name exactly fills 16-byte field?  If so, we+		 * can't assume entries without '/' are BSD. XXX+		 */+	}++	/* Update format name from the code. */+	if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU)+		a->archive.archive_format_name = "ar (GNU/SVR4)";+	else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD)+		a->archive.archive_format_name = "ar (BSD)";+	else+		a->archive.archive_format_name = "ar";++	/*+	 * Remove trailing spaces from the filename.  GNU and BSD+	 * variants both pad filename area out with spaces.+	 * This will only be wrong if GNU/SVR4 'ar' implementations+	 * omit trailing '/' for 16-char filenames and we have+	 * a 16-char filename that ends in ' '.+	 */+	p = filename + AR_name_size - 1;+	while (p >= filename && *p == ' ') {+		*p = '\0';+		p--;+	}++	/*+	 * Remove trailing slash unless first character is '/'.+	 * (BSD entries never end in '/', so this will only trim+	 * GNU-format entries.  GNU special entries start with '/'+	 * and are not terminated in '/', so we don't trim anything+	 * that starts with '/'.)+	 */+	if (filename[0] != '/' && p > filename && *p == '/') {+		*p = '\0';+	}++	if (p < filename) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Found entry with empty filename");+		return (ARCHIVE_FATAL);+	}++	/*+	 * '//' is the GNU filename table.+	 * Later entries can refer to names in this table.+	 */+	if (strcmp(filename, "//") == 0) {+		/* This must come before any call to _read_ahead. */+		ar_parse_common_header(ar, entry, h);+		archive_entry_copy_pathname(entry, filename);+		archive_entry_set_filetype(entry, AE_IFREG);+		/* Get the size of the filename table. */+		number = ar_atol10(h + AR_size_offset, AR_size_size);+		if (number > SIZE_MAX || number > 1024 * 1024 * 1024) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Filename table too large");+			return (ARCHIVE_FATAL);+		}+		entry_size = (size_t)number;+		if (entry_size == 0) {+			archive_set_error(&a->archive, EINVAL,+			    "Invalid string table");+			return (ARCHIVE_FATAL);+		}+		if (ar->strtab != NULL) {+			archive_set_error(&a->archive, EINVAL,+			    "More than one string tables exist");+			return (ARCHIVE_FATAL);+		}++		/* Read the filename table into memory. */+		st = malloc(entry_size);+		if (st == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate filename table buffer");+			return (ARCHIVE_FATAL);+		}+		ar->strtab = st;+		ar->strtab_size = entry_size;++		if (*unconsumed) {+			__archive_read_consume(a, *unconsumed);+			*unconsumed = 0;+		}++		if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL)+			return (ARCHIVE_FATAL);+		memcpy(st, b, entry_size);+		__archive_read_consume(a, entry_size);+		/* All contents are consumed. */+		ar->entry_bytes_remaining = 0;+		archive_entry_set_size(entry, ar->entry_bytes_remaining);++		/* Parse the filename table. */+		return (ar_parse_gnu_filename_table(a));+	}++	/*+	 * GNU variant handles long filenames by storing /<number>+	 * to indicate a name stored in the filename table.+	 * XXX TODO: Verify that it's all digits... Don't be fooled+	 * by "/9xyz" XXX+	 */+	if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') {+		number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1);+		/*+		 * If we can't look up the real name, warn and return+		 * the entry with the wrong name.+		 */+		if (ar->strtab == NULL || number >= ar->strtab_size) {+			archive_set_error(&a->archive, EINVAL,+			    "Can't find long filename for GNU/SVR4 archive entry");+			archive_entry_copy_pathname(entry, filename);+			/* Parse the time, owner, mode, size fields. */+			ar_parse_common_header(ar, entry, h);+			return (ARCHIVE_FATAL);+		}++		archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]);+		/* Parse the time, owner, mode, size fields. */+		return (ar_parse_common_header(ar, entry, h));+	}++	/*+	 * BSD handles long filenames by storing "#1/" followed by the+	 * length of filename as a decimal number, then prepends the+	 * the filename to the file contents.+	 */+	if (strncmp(filename, "#1/", 3) == 0) {+		/* Parse the time, owner, mode, size fields. */+		/* This must occur before _read_ahead is called again. */+		ar_parse_common_header(ar, entry, h);++		/* Parse the size of the name, adjust the file size. */+		number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3);+		/* Sanity check the filename length:+		 *   = Must be <= SIZE_MAX - 1+		 *   = Must be <= 1MB+		 *   = Cannot be bigger than the entire entry+		 */+		if (number > SIZE_MAX - 1+		    || number > 1024 * 1024+		    || (int64_t)number > ar->entry_bytes_remaining) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Bad input file size");+			return (ARCHIVE_FATAL);+		}+		bsd_name_length = (size_t)number;+		ar->entry_bytes_remaining -= bsd_name_length;+		/* Adjust file size reported to client. */+		archive_entry_set_size(entry, ar->entry_bytes_remaining);++		if (*unconsumed) {+			__archive_read_consume(a, *unconsumed);+			*unconsumed = 0;+		}++		/* Read the long name into memory. */+		if ((b = __archive_read_ahead(a, bsd_name_length, NULL)) == NULL) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Truncated input file");+			return (ARCHIVE_FATAL);+		}+		/* Store it in the entry. */+		p = (char *)malloc(bsd_name_length + 1);+		if (p == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate fname buffer");+			return (ARCHIVE_FATAL);+		}+		strncpy(p, b, bsd_name_length);+		p[bsd_name_length] = '\0';++		__archive_read_consume(a, bsd_name_length);++		archive_entry_copy_pathname(entry, p);+		free(p);+		return (ARCHIVE_OK);+	}++	/*+	 * "/" is the SVR4/GNU archive symbol table.+	 * "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table.+	 */+	if (strcmp(filename, "/") == 0 || strcmp(filename, "/SYM64/") == 0) {+		archive_entry_copy_pathname(entry, filename);+		/* Parse the time, owner, mode, size fields. */+		r = ar_parse_common_header(ar, entry, h);+		/* Force the file type to a regular file. */+		archive_entry_set_filetype(entry, AE_IFREG);+		return (r);+	}++	/*+	 * "__.SYMDEF" is a BSD archive symbol table.+	 */+	if (strcmp(filename, "__.SYMDEF") == 0) {+		archive_entry_copy_pathname(entry, filename);+		/* Parse the time, owner, mode, size fields. */+		return (ar_parse_common_header(ar, entry, h));+	}++	/*+	 * Otherwise, this is a standard entry.  The filename+	 * has already been trimmed as much as possible, based+	 * on our current knowledge of the format.+	 */+	archive_entry_copy_pathname(entry, filename);+	return (ar_parse_common_header(ar, entry, h));+}++static int+archive_read_format_ar_read_header(struct archive_read *a,+    struct archive_entry *entry)+{+	struct ar *ar = (struct ar*)(a->format->data);+	size_t unconsumed;+	const void *header_data;+	int ret;++	if (!ar->read_global_header) {+		/*+		 * We are now at the beginning of the archive,+		 * so we need first consume the ar global header.+		 */+		__archive_read_consume(a, 8);+		ar->read_global_header = 1;+		/* Set a default format code for now. */+		a->archive.archive_format = ARCHIVE_FORMAT_AR;+	}++	/* Read the header for the next file entry. */+	if ((header_data = __archive_read_ahead(a, 60, NULL)) == NULL)+		/* Broken header. */+		return (ARCHIVE_EOF);+	+	unconsumed = 60;+	+	ret = _ar_read_header(a, entry, ar, (const char *)header_data, &unconsumed);++	if (unconsumed)+		__archive_read_consume(a, unconsumed);++	return ret;+}+++static int+ar_parse_common_header(struct ar *ar, struct archive_entry *entry,+    const char *h)+{+	uint64_t n;++	/* Copy remaining header */+	archive_entry_set_filetype(entry, AE_IFREG);+	archive_entry_set_mtime(entry,+	    (time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L);+	archive_entry_set_uid(entry,+	    (uid_t)ar_atol10(h + AR_uid_offset, AR_uid_size));+	archive_entry_set_gid(entry,+	    (gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size));+	archive_entry_set_mode(entry,+	    (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size));+	n = ar_atol10(h + AR_size_offset, AR_size_size);++	ar->entry_offset = 0;+	ar->entry_padding = n % 2;+	archive_entry_set_size(entry, n);+	ar->entry_bytes_remaining = n;+	return (ARCHIVE_OK);+}++static int+archive_read_format_ar_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	ssize_t bytes_read;+	struct ar *ar;++	ar = (struct ar *)(a->format->data);++	if (ar->entry_bytes_unconsumed) {+		__archive_read_consume(a, ar->entry_bytes_unconsumed);+		ar->entry_bytes_unconsumed = 0;+	}++	if (ar->entry_bytes_remaining > 0) {+		*buff = __archive_read_ahead(a, 1, &bytes_read);+		if (bytes_read == 0) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Truncated ar archive");+			return (ARCHIVE_FATAL);+		}+		if (bytes_read < 0)+			return (ARCHIVE_FATAL);+		if (bytes_read > ar->entry_bytes_remaining)+			bytes_read = (ssize_t)ar->entry_bytes_remaining;+		*size = bytes_read;+		ar->entry_bytes_unconsumed = bytes_read;+		*offset = ar->entry_offset;+		ar->entry_offset += bytes_read;+		ar->entry_bytes_remaining -= bytes_read;+		return (ARCHIVE_OK);+	} else {+		int64_t skipped = __archive_read_consume(a, ar->entry_padding);+		if (skipped >= 0) {+			ar->entry_padding -= skipped;+		}+		if (ar->entry_padding) {+			if (skipped >= 0) {+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+					"Truncated ar archive- failed consuming padding");+			}+			return (ARCHIVE_FATAL);+		}+		*buff = NULL;+		*size = 0;+		*offset = ar->entry_offset;+		return (ARCHIVE_EOF);+	}+}++static int+archive_read_format_ar_skip(struct archive_read *a)+{+	int64_t bytes_skipped;+	struct ar* ar;++	ar = (struct ar *)(a->format->data);++	bytes_skipped = __archive_read_consume(a,+	    ar->entry_bytes_remaining + ar->entry_padding+	    + ar->entry_bytes_unconsumed);+	if (bytes_skipped < 0)+		return (ARCHIVE_FATAL);++	ar->entry_bytes_remaining = 0;+	ar->entry_bytes_unconsumed = 0;+	ar->entry_padding = 0;++	return (ARCHIVE_OK);+}++static int+ar_parse_gnu_filename_table(struct archive_read *a)+{+	struct ar *ar;+	char *p;+	size_t size;++	ar = (struct ar*)(a->format->data);+	size = ar->strtab_size;++	for (p = ar->strtab; p < ar->strtab + size - 1; ++p) {+		if (*p == '/') {+			*p++ = '\0';+			if (*p != '\n')+				goto bad_string_table;+			*p = '\0';+		}+	}+	/*+	 * GNU ar always pads the table to an even size.+	 * The pad character is either '\n' or '`'.+	 */+	if (p != ar->strtab + size && *p != '\n' && *p != '`')+		goto bad_string_table;++	/* Enforce zero termination. */+	ar->strtab[size - 1] = '\0';++	return (ARCHIVE_OK);++bad_string_table:+	archive_set_error(&a->archive, EINVAL,+	    "Invalid string table");+	free(ar->strtab);+	ar->strtab = NULL;+	return (ARCHIVE_FATAL);+}++static uint64_t+ar_atol8(const char *p, unsigned char_cnt)+{+	uint64_t l, limit, last_digit_limit;+	unsigned int digit, base;++	base = 8;+	limit = UINT64_MAX / base;+	last_digit_limit = UINT64_MAX % base;++	while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)+		p++;++	l = 0;+	digit = *p - '0';+	while (*p >= '0' && digit < base  && char_cnt-- > 0) {+		if (l>limit || (l == limit && digit > last_digit_limit)) {+			l = UINT64_MAX; /* Truncate on overflow. */+			break;+		}+		l = (l * base) + digit;+		digit = *++p - '0';+	}+	return (l);+}++static uint64_t+ar_atol10(const char *p, unsigned char_cnt)+{+	uint64_t l, limit, last_digit_limit;+	unsigned int base, digit;++	base = 10;+	limit = UINT64_MAX / base;+	last_digit_limit = UINT64_MAX % base;++	while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)+		p++;+	l = 0;+	digit = *p - '0';+	while (*p >= '0' && digit < base  && char_cnt-- > 0) {+		if (l > limit || (l == limit && digit > last_digit_limit)) {+			l = UINT64_MAX; /* Truncate on overflow. */+			break;+		}+		l = (l * base) + digit;+		digit = *++p - '0';+	}+	return (l);+}
+ c/archive_read_support_format_by_code.c view
@@ -0,0 +1,77 @@+/*-+ * Copyright (c) 2003-2011 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#include "archive.h"+#include "archive_private.h"++int+archive_read_support_format_by_code(struct archive *a, int format_code)+{+	archive_check_magic(a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_by_code");++	switch (format_code & ARCHIVE_FORMAT_BASE_MASK) {+	case ARCHIVE_FORMAT_7ZIP:+		return archive_read_support_format_7zip(a);+		break;+	case ARCHIVE_FORMAT_AR:+		return archive_read_support_format_ar(a);+		break;+	case ARCHIVE_FORMAT_CAB:+		return archive_read_support_format_cab(a);+		break;+	case ARCHIVE_FORMAT_CPIO:+		return archive_read_support_format_cpio(a);+		break;+	case ARCHIVE_FORMAT_ISO9660:+		return archive_read_support_format_iso9660(a);+		break;+	case ARCHIVE_FORMAT_LHA:+		return archive_read_support_format_lha(a);+		break;+	case ARCHIVE_FORMAT_MTREE:+		return archive_read_support_format_mtree(a);+		break;+	case ARCHIVE_FORMAT_RAR:+		return archive_read_support_format_rar(a);+		break;+	case ARCHIVE_FORMAT_RAR_V5:+		return archive_read_support_format_rar5(a);+		break;+	case ARCHIVE_FORMAT_TAR:+		return archive_read_support_format_tar(a);+		break;+	case ARCHIVE_FORMAT_XAR:+		return archive_read_support_format_xar(a);+		break;+	case ARCHIVE_FORMAT_ZIP:+		return archive_read_support_format_zip(a);+		break;+	}+	return (ARCHIVE_FATAL);+}
+ c/archive_read_support_format_cab.c view
@@ -0,0 +1,3227 @@+/*-+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_read_private.h"+#include "archive_endian.h"+++struct lzx_dec {+	/* Decoding status. */+	int     		 state;++	/*+	 * Window to see last decoded data, from 32KBi to 2MBi.+	 */+	int			 w_size;+	int			 w_mask;+	/* Window buffer, which is a loop buffer. */+	unsigned char		*w_buff;+	/* The insert position to the window. */+	int			 w_pos;+	/* The position where we can copy decoded code from the window. */+	int     		 copy_pos;+	/* The length how many bytes we can copy decoded code from+	 * the window. */+	int     		 copy_len;+	/* Translation reversal for x86 processor CALL byte sequence(E8).+	 * This is used for LZX only. */+	uint32_t		 translation_size;+	char			 translation;+	char			 block_type;+#define VERBATIM_BLOCK		1+#define ALIGNED_OFFSET_BLOCK	2+#define UNCOMPRESSED_BLOCK	3+	size_t			 block_size;+	size_t			 block_bytes_avail;+	/* Repeated offset. */+	int			 r0, r1, r2;+	unsigned char		 rbytes[4];+	int			 rbytes_avail;+	int			 length_header;+	int			 position_slot;+	int			 offset_bits;++	struct lzx_pos_tbl {+		int		 base;+		int		 footer_bits;+	}			*pos_tbl;+	/*+	 * Bit stream reader.+	 */+	struct lzx_br {+#define CACHE_TYPE		uint64_t+#define CACHE_BITS		(8 * sizeof(CACHE_TYPE))+	 	/* Cache buffer. */+		CACHE_TYPE	 cache_buffer;+		/* Indicates how many bits avail in cache_buffer. */+		int		 cache_avail;+		unsigned char	 odd;+		char		 have_odd;+	} br;++	/*+	 * Huffman coding.+	 */+	struct huffman {+		int		 len_size;+		int		 freq[17];+		unsigned char	*bitlen;++		/*+		 * Use a index table. It's faster than searching a huffman+		 * coding tree, which is a binary tree. But a use of a large+		 * index table causes L1 cache read miss many times.+		 */+		int		 max_bits;+		int		 tbl_bits;+		int		 tree_used;+		/* Direct access table. */+		uint16_t	*tbl;+	}			 at, lt, mt, pt;++	int			 loop;+	int			 error;+};++static const int slots[] = {+	30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290+};+#define SLOT_BASE	15+#define SLOT_MAX	21/*->25*/++struct lzx_stream {+	const unsigned char	*next_in;+	int64_t			 avail_in;+	int64_t			 total_in;+	unsigned char		*next_out;+	int64_t			 avail_out;+	int64_t			 total_out;+	struct lzx_dec		*ds;+};++/*+ * Cabinet file definitions.+ */+/* CFHEADER offset */+#define CFHEADER_signature	0+#define CFHEADER_cbCabinet	8+#define CFHEADER_coffFiles	16+#define CFHEADER_versionMinor	24+#define CFHEADER_versionMajor	25+#define CFHEADER_cFolders	26+#define CFHEADER_cFiles		28+#define CFHEADER_flags		30+#define CFHEADER_setID		32+#define CFHEADER_iCabinet	34+#define CFHEADER_cbCFHeader	36+#define CFHEADER_cbCFFolder	38+#define CFHEADER_cbCFData	39++/* CFFOLDER offset */+#define CFFOLDER_coffCabStart	0+#define CFFOLDER_cCFData	4+#define CFFOLDER_typeCompress	6+#define CFFOLDER_abReserve	8++/* CFFILE offset */+#define CFFILE_cbFile		0+#define CFFILE_uoffFolderStart	4+#define CFFILE_iFolder		8+#define CFFILE_date_time	10+#define CFFILE_attribs		14++/* CFDATA offset */+#define CFDATA_csum		0+#define CFDATA_cbData		4+#define CFDATA_cbUncomp		6++static const char * const compression_name[] = {+	"NONE",+	"MSZIP",+	"Quantum",+	"LZX",+};++struct cfdata {+	/* Sum value of this CFDATA. */+	uint32_t		 sum;+	uint16_t		 compressed_size;+	uint16_t		 compressed_bytes_remaining;+	uint16_t		 uncompressed_size;+	uint16_t		 uncompressed_bytes_remaining;+	/* To know how many bytes we have decompressed. */+	uint16_t		 uncompressed_avail;+	/* Offset from the beginning of compressed data of this CFDATA */+	uint16_t		 read_offset;+	int64_t			 unconsumed;+	/* To keep memory image of this CFDATA to compute the sum. */+	size_t			 memimage_size;+	unsigned char		*memimage;+	/* Result of calculation of sum. */+	uint32_t		 sum_calculated;+	unsigned char		 sum_extra[4];+	int			 sum_extra_avail;+	const void		*sum_ptr;+};++struct cffolder {+	uint32_t		 cfdata_offset_in_cab;+	uint16_t		 cfdata_count;+	uint16_t		 comptype;+#define COMPTYPE_NONE		0x0000+#define COMPTYPE_MSZIP		0x0001+#define COMPTYPE_QUANTUM	0x0002+#define COMPTYPE_LZX		0x0003+	uint16_t		 compdata;+	const char		*compname;+	/* At the time reading CFDATA */+	struct cfdata		 cfdata;+	int			 cfdata_index;+	/* Flags to mark progress of decompression. */+	char			 decompress_init;+};++struct cffile {+	uint32_t		 uncompressed_size;+	uint32_t		 offset;+	time_t			 mtime;+	uint16_t	 	 folder;+#define iFoldCONTINUED_FROM_PREV	0xFFFD+#define iFoldCONTINUED_TO_NEXT		0xFFFE+#define iFoldCONTINUED_PREV_AND_NEXT	0xFFFF+	unsigned char		 attr;+#define ATTR_RDONLY		0x01+#define ATTR_NAME_IS_UTF	0x80+	struct archive_string 	 pathname;+};++struct cfheader {+	/* Total bytes of all file size in a Cabinet. */+	uint32_t		 total_bytes;+	uint32_t		 files_offset;+	uint16_t		 folder_count;+	uint16_t		 file_count;+	uint16_t		 flags;+#define PREV_CABINET	0x0001+#define NEXT_CABINET	0x0002+#define RESERVE_PRESENT	0x0004+	uint16_t		 setid;+	uint16_t		 cabinet;+	/* Version number. */+	unsigned char		 major;+	unsigned char		 minor;+	unsigned char		 cffolder;+	unsigned char		 cfdata;+	/* All folders in a cabinet. */+	struct cffolder		*folder_array;+	/* All files in a cabinet. */+	struct cffile		*file_array;+	int			 file_index;+};++struct cab {+	/* entry_bytes_remaining is the number of bytes we expect.	    */+	int64_t			 entry_offset;+	int64_t			 entry_bytes_remaining;+	int64_t			 entry_unconsumed;+	int64_t			 entry_compressed_bytes_read;+	int64_t			 entry_uncompressed_bytes_read;+	struct cffolder		*entry_cffolder;+	struct cffile		*entry_cffile;+	struct cfdata		*entry_cfdata;++	/* Offset from beginning of a cabinet file. */+	int64_t			 cab_offset;+	struct cfheader		 cfheader;+	struct archive_wstring	 ws;++	/* Flag to mark progress that an archive was read their first header.*/+	char			 found_header;+	char			 end_of_archive;+	char			 end_of_entry;+	char			 end_of_entry_cleanup;+	char			 read_data_invoked;+	int64_t			 bytes_skipped;++	unsigned char		*uncompressed_buffer;+	size_t			 uncompressed_buffer_size;++	int			 init_default_conversion;+	struct archive_string_conv *sconv;+	struct archive_string_conv *sconv_default;+	struct archive_string_conv *sconv_utf8;+	char			 format_name[64];++#ifdef HAVE_ZLIB_H+	z_stream		 stream;+	char			 stream_valid;+#endif+	struct lzx_stream	 xstrm;+};++static int	archive_read_format_cab_bid(struct archive_read *, int);+static int	archive_read_format_cab_options(struct archive_read *,+		    const char *, const char *);+static int	archive_read_format_cab_read_header(struct archive_read *,+		    struct archive_entry *);+static int	archive_read_format_cab_read_data(struct archive_read *,+		    const void **, size_t *, int64_t *);+static int	archive_read_format_cab_read_data_skip(struct archive_read *);+static int	archive_read_format_cab_cleanup(struct archive_read *);++static int	cab_skip_sfx(struct archive_read *);+static time_t	cab_dos_time(const unsigned char *);+static int	cab_read_data(struct archive_read *, const void **,+		    size_t *, int64_t *);+static int	cab_read_header(struct archive_read *);+static uint32_t cab_checksum_cfdata_4(const void *, size_t bytes, uint32_t);+static uint32_t cab_checksum_cfdata(const void *, size_t bytes, uint32_t);+static void	cab_checksum_update(struct archive_read *, size_t);+static int	cab_checksum_finish(struct archive_read *);+static int	cab_next_cfdata(struct archive_read *);+static const void *cab_read_ahead_cfdata(struct archive_read *, ssize_t *);+static const void *cab_read_ahead_cfdata_none(struct archive_read *, ssize_t *);+static const void *cab_read_ahead_cfdata_deflate(struct archive_read *,+		    ssize_t *);+static const void *cab_read_ahead_cfdata_lzx(struct archive_read *,+		    ssize_t *);+static int64_t	cab_consume_cfdata(struct archive_read *, int64_t);+static int64_t	cab_minimum_consume_cfdata(struct archive_read *, int64_t);+static int	lzx_decode_init(struct lzx_stream *, int);+static int	lzx_read_blocks(struct lzx_stream *, int);+static int	lzx_decode_blocks(struct lzx_stream *, int);+static void	lzx_decode_free(struct lzx_stream *);+static void	lzx_translation(struct lzx_stream *, void *, size_t, uint32_t);+static void	lzx_cleanup_bitstream(struct lzx_stream *);+static int	lzx_decode(struct lzx_stream *, int);+static int	lzx_read_pre_tree(struct lzx_stream *);+static int	lzx_read_bitlen(struct lzx_stream *, struct huffman *, int);+static int	lzx_huffman_init(struct huffman *, size_t, int);+static void	lzx_huffman_free(struct huffman *);+static int	lzx_make_huffman_table(struct huffman *);+static inline int lzx_decode_huffman(struct huffman *, unsigned);+++int+archive_read_support_format_cab(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct cab *cab;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_cab");++	cab = (struct cab *)calloc(1, sizeof(*cab));+	if (cab == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate CAB data");+		return (ARCHIVE_FATAL);+	}+	archive_string_init(&cab->ws);+	archive_wstring_ensure(&cab->ws, 256);++	r = __archive_read_register_format(a,+	    cab,+	    "cab",+	    archive_read_format_cab_bid,+	    archive_read_format_cab_options,+	    archive_read_format_cab_read_header,+	    archive_read_format_cab_read_data,+	    archive_read_format_cab_read_data_skip,+	    NULL,+	    archive_read_format_cab_cleanup,+	    NULL,+	    NULL);++	if (r != ARCHIVE_OK)+		free(cab);+	return (ARCHIVE_OK);+}++static int+find_cab_magic(const char *p)+{+	switch (p[4]) {+	case 0:+		/*+		 * Note: Self-Extraction program has 'MSCF' string in their+		 * program. If we were finding 'MSCF' string only, we got+		 * wrong place for Cabinet header, thus, we have to check+		 * following four bytes which are reserved and must be set+		 * to zero.+		 */+		if (memcmp(p, "MSCF\0\0\0\0", 8) == 0)+			return 0;+		return 5;+	case 'F': return 1;+	case 'C': return 2;+	case 'S': return 3;+	case 'M': return 4;+	default:  return 5;+	}+}++static int+archive_read_format_cab_bid(struct archive_read *a, int best_bid)+{+	const char *p;+	ssize_t bytes_avail, offset, window;++	/* If there's already a better bid than we can ever+	   make, don't bother testing. */+	if (best_bid > 64)+		return (-1);++	if ((p = __archive_read_ahead(a, 8, NULL)) == NULL)+		return (-1);++	if (memcmp(p, "MSCF\0\0\0\0", 8) == 0)+		return (64);++	/*+	 * Attempt to handle self-extracting archives+	 * by noting a PE header and searching forward+	 * up to 128k for a 'MSCF' marker.+	 */+	if (p[0] == 'M' && p[1] == 'Z') {+		offset = 0;+		window = 4096;+		while (offset < (1024 * 128)) {+			const char *h = __archive_read_ahead(a, offset + window,+			    &bytes_avail);+			if (h == NULL) {+				/* Remaining bytes are less than window. */+				window >>= 1;+				if (window < 128)+					return (0);+				continue;+			}+			p = h + offset;+			while (p + 8 < h + bytes_avail) {+				int next;+				if ((next = find_cab_magic(p)) == 0)+					return (64);+				p += next;+			}+			offset = p - h;+		}+	}+	return (0);+}++static int+archive_read_format_cab_options(struct archive_read *a,+    const char *key, const char *val)+{+	struct cab *cab;+	int ret = ARCHIVE_FAILED;++	cab = (struct cab *)(a->format->data);+	if (strcmp(key, "hdrcharset")  == 0) {+		if (val == NULL || val[0] == 0)+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "cab: hdrcharset option needs a character-set name");+		else {+			cab->sconv = archive_string_conversion_from_charset(+			    &a->archive, val, 0);+			if (cab->sconv != NULL)+				ret = ARCHIVE_OK;+			else+				ret = ARCHIVE_FATAL;+		}+		return (ret);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+cab_skip_sfx(struct archive_read *a)+{+	const char *p, *q;+	size_t skip;+	ssize_t bytes, window;++	window = 4096;+	for (;;) {+		const char *h = __archive_read_ahead(a, window, &bytes);+		if (h == NULL) {+			/* Remaining size are less than window. */+			window >>= 1;+			if (window < 128) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Couldn't find out CAB header");+				return (ARCHIVE_FATAL);+			}+			continue;+		}+		p = h;+		q = p + bytes;++		/*+		 * Scan ahead until we find something that looks+		 * like the cab header.+		 */+		while (p + 8 < q) {+			int next;+			if ((next = find_cab_magic(p)) == 0) {+				skip = p - h;+				__archive_read_consume(a, skip);+				return (ARCHIVE_OK);+			}+			p += next;+		}+		skip = p - h;+		__archive_read_consume(a, skip);+	}+}++static int+truncated_error(struct archive_read *a)+{+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Truncated CAB header");+	return (ARCHIVE_FATAL);+}++static ssize_t+cab_strnlen(const unsigned char *p, size_t maxlen)+{+	size_t i;++	for (i = 0; i <= maxlen; i++) {+		if (p[i] == 0)+			break;+	}+	if (i > maxlen)+		return (-1);/* invalid */+	return ((ssize_t)i);+}++/* Read bytes as much as remaining. */+static const void *+cab_read_ahead_remaining(struct archive_read *a, size_t min, ssize_t *avail)+{+	const void *p;++	while (min > 0) {+		p = __archive_read_ahead(a, min, avail);+		if (p != NULL)+			return (p);+		min--;+	}+	return (NULL);+}++/* Convert a path separator '\' -> '/' */+static int+cab_convert_path_separator_1(struct archive_string *fn, unsigned char attr)+{+	size_t i;+	int mb;++	/* Easy check if we have '\' in multi-byte string. */+	mb = 0;+	for (i = 0; i < archive_strlen(fn); i++) {+		if (fn->s[i] == '\\') {+			if (mb) {+				/* This may be second byte of multi-byte+				 * character. */+				break;+			}+			fn->s[i] = '/';+			mb = 0;+		} else if ((fn->s[i] & 0x80) && !(attr & ATTR_NAME_IS_UTF))+			mb = 1;+		else+			mb = 0;+	}+	if (i == archive_strlen(fn))+		return (0);+	return (-1);+}++/*+ * Replace a character '\' with '/' in wide character.+ */+static void+cab_convert_path_separator_2(struct cab *cab, struct archive_entry *entry)+{+	const wchar_t *wp;+	size_t i;++	/* If a conversion to wide character failed, force the replacement. */+	if ((wp = archive_entry_pathname_w(entry)) != NULL) {+		archive_wstrcpy(&(cab->ws), wp);+		for (i = 0; i < archive_strlen(&(cab->ws)); i++) {+			if (cab->ws.s[i] == L'\\')+				cab->ws.s[i] = L'/';+		}+		archive_entry_copy_pathname_w(entry, cab->ws.s);+	}+}++/*+ * Read CFHEADER, CFFOLDER and CFFILE.+ */+static int+cab_read_header(struct archive_read *a)+{+	const unsigned char *p;+	struct cab *cab;+	struct cfheader *hd;+	size_t bytes, used;+	ssize_t len;+	int64_t skip;+	int err, i;+	int cur_folder, prev_folder;+	uint32_t offset32;+	+	a->archive.archive_format = ARCHIVE_FORMAT_CAB;+	if (a->archive.archive_format_name == NULL)+		a->archive.archive_format_name = "CAB";++	if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)+		return (truncated_error(a));++	cab = (struct cab *)(a->format->data);+	if (cab->found_header == 0 &&+	    p[0] == 'M' && p[1] == 'Z') {+		/* This is an executable?  Must be self-extracting... */+		err = cab_skip_sfx(a);+		if (err < ARCHIVE_WARN)+			return (err);++		/* Re-read header after processing the SFX. */+		if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)+			return (truncated_error(a));+	}++	cab->cab_offset = 0;+	/*+	 * Read CFHEADER.+	 */+	hd = &cab->cfheader;+	if (p[CFHEADER_signature+0] != 'M' || p[CFHEADER_signature+1] != 'S' ||+	    p[CFHEADER_signature+2] != 'C' || p[CFHEADER_signature+3] != 'F') {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Couldn't find out CAB header");+		return (ARCHIVE_FATAL);+	}+	hd->total_bytes = archive_le32dec(p + CFHEADER_cbCabinet);+	hd->files_offset = archive_le32dec(p + CFHEADER_coffFiles);+	hd->minor = p[CFHEADER_versionMinor];+	hd->major = p[CFHEADER_versionMajor];+	hd->folder_count = archive_le16dec(p + CFHEADER_cFolders);+	if (hd->folder_count == 0)+		goto invalid;+	hd->file_count = archive_le16dec(p + CFHEADER_cFiles);+	if (hd->file_count == 0)+		goto invalid;+	hd->flags = archive_le16dec(p + CFHEADER_flags);+	hd->setid = archive_le16dec(p + CFHEADER_setID);+	hd->cabinet = archive_le16dec(p + CFHEADER_iCabinet);+	used = CFHEADER_iCabinet + 2;+	if (hd->flags & RESERVE_PRESENT) {+		uint16_t cfheader;+		cfheader = archive_le16dec(p + CFHEADER_cbCFHeader);+		if (cfheader > 60000U)+			goto invalid;+		hd->cffolder = p[CFHEADER_cbCFFolder];+		hd->cfdata = p[CFHEADER_cbCFData];+		used += 4;/* cbCFHeader, cbCFFolder and cbCFData */+		used += cfheader;/* abReserve */+	} else+		hd->cffolder = 0;/* Avoid compiling warning. */+	if (hd->flags & PREV_CABINET) {+		/* How many bytes are used for szCabinetPrev. */+		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)+			return (truncated_error(a));+		if ((len = cab_strnlen(p + used, 255)) <= 0)+			goto invalid;+		used += len + 1;+		/* How many bytes are used for szDiskPrev. */+		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)+			return (truncated_error(a));+		if ((len = cab_strnlen(p + used, 255)) <= 0)+			goto invalid;+		used += len + 1;+	}+	if (hd->flags & NEXT_CABINET) {+		/* How many bytes are used for szCabinetNext. */+		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)+			return (truncated_error(a));+		if ((len = cab_strnlen(p + used, 255)) <= 0)+			goto invalid;+		used += len + 1;+		/* How many bytes are used for szDiskNext. */+		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)+			return (truncated_error(a));+		if ((len = cab_strnlen(p + used, 255)) <= 0)+			goto invalid;+		used += len + 1;+	}+	__archive_read_consume(a, used);+	cab->cab_offset += used;+	used = 0;++	/*+	 * Read CFFOLDER.+	 */+	hd->folder_array = (struct cffolder *)calloc(+	    hd->folder_count, sizeof(struct cffolder));+	if (hd->folder_array == NULL)+		goto nomem;+	+	bytes = 8;+	if (hd->flags & RESERVE_PRESENT)+		bytes += hd->cffolder;+	bytes *= hd->folder_count;+	if ((p = __archive_read_ahead(a, bytes, NULL)) == NULL)+		return (truncated_error(a));+	offset32 = 0;+	for (i = 0; i < hd->folder_count; i++) {+		struct cffolder *folder = &(hd->folder_array[i]);+		folder->cfdata_offset_in_cab =+		    archive_le32dec(p + CFFOLDER_coffCabStart);+		folder->cfdata_count = archive_le16dec(p+CFFOLDER_cCFData);+		folder->comptype =+		    archive_le16dec(p+CFFOLDER_typeCompress) & 0x0F;+		folder->compdata =+		    archive_le16dec(p+CFFOLDER_typeCompress) >> 8;+		/* Get a compression name. */+		if (folder->comptype <+		    sizeof(compression_name) / sizeof(compression_name[0]))+			folder->compname = compression_name[folder->comptype];+		else+			folder->compname = "UNKNOWN";+		p += 8;+		used += 8;+		if (hd->flags & RESERVE_PRESENT) {+			p += hd->cffolder;/* abReserve */+			used += hd->cffolder;+		}+		/*+		 * Sanity check if each data is acceptable.+		 */+		if (offset32 >= folder->cfdata_offset_in_cab)+			goto invalid;+		offset32 = folder->cfdata_offset_in_cab;++		/* Set a request to initialize zlib for the CFDATA of+		 * this folder. */+		folder->decompress_init = 0;+	}+	__archive_read_consume(a, used);+	cab->cab_offset += used;++	/*+	 * Read CFFILE.+	 */+	/* Seek read pointer to the offset of CFFILE if needed. */+	skip = (int64_t)hd->files_offset - cab->cab_offset;+	if (skip <  0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Invalid offset of CFFILE %jd < %jd",+		    (intmax_t)hd->files_offset, (intmax_t)cab->cab_offset);+		return (ARCHIVE_FATAL);+	}+	if (skip) {+		__archive_read_consume(a, skip);+		cab->cab_offset += skip;+	}+	/* Allocate memory for CFDATA */+	hd->file_array = (struct cffile *)calloc(+	    hd->file_count, sizeof(struct cffile));+	if (hd->file_array == NULL)+		goto nomem;++	prev_folder = -1;+	for (i = 0; i < hd->file_count; i++) {+		struct cffile *file = &(hd->file_array[i]);+		ssize_t avail;++		if ((p = __archive_read_ahead(a, 16, NULL)) == NULL)+			return (truncated_error(a));+		file->uncompressed_size = archive_le32dec(p + CFFILE_cbFile);+		file->offset = archive_le32dec(p + CFFILE_uoffFolderStart);+		file->folder = archive_le16dec(p + CFFILE_iFolder);+		file->mtime = cab_dos_time(p + CFFILE_date_time);+		file->attr = (uint8_t)archive_le16dec(p + CFFILE_attribs);+		__archive_read_consume(a, 16);++		cab->cab_offset += 16;+		if ((p = cab_read_ahead_remaining(a, 256, &avail)) == NULL)+			return (truncated_error(a));+		if ((len = cab_strnlen(p, avail-1)) <= 0)+			goto invalid;++		/* Copy a pathname.  */+		archive_string_init(&(file->pathname));+		archive_strncpy(&(file->pathname), p, len);+		__archive_read_consume(a, len + 1);+		cab->cab_offset += len + 1;++		/*+		 * Sanity check if each data is acceptable.+		 */+		if (file->uncompressed_size > 0x7FFF8000)+			goto invalid;/* Too large */+		if ((int64_t)file->offset + (int64_t)file->uncompressed_size+		    > ARCHIVE_LITERAL_LL(0x7FFF8000))+			goto invalid;/* Too large */+		switch (file->folder) {+		case iFoldCONTINUED_TO_NEXT:+			/* This must be last file in a folder. */+			if (i != hd->file_count -1)+				goto invalid;+			cur_folder = hd->folder_count -1;+			break;+		case iFoldCONTINUED_PREV_AND_NEXT:+			/* This must be only one file in a folder. */+			if (hd->file_count != 1)+				goto invalid;+			/* FALL THROUGH */+		case iFoldCONTINUED_FROM_PREV:+			/* This must be first file in a folder. */+			if (i != 0)+				goto invalid;+			prev_folder = cur_folder = 0;+			offset32 = file->offset;+			break;+		default:+			if (file->folder >= hd->folder_count)+				goto invalid;+			cur_folder = file->folder;+			break;+		}+		/* Dot not back track. */+		if (cur_folder < prev_folder)+			goto invalid;+		if (cur_folder != prev_folder)+			offset32 = 0;+		prev_folder = cur_folder;++		/* Make sure there are not any blanks from last file+		 * contents. */+		if (offset32 != file->offset)+			goto invalid;+		offset32 += file->uncompressed_size;++		/* CFDATA is available for file contents. */+		if (file->uncompressed_size > 0 &&+		    hd->folder_array[cur_folder].cfdata_count == 0)+			goto invalid;+	}++	if (hd->cabinet != 0 || hd->flags & (PREV_CABINET | NEXT_CABINET)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Multivolume cabinet file is unsupported");+		return (ARCHIVE_WARN);+	}+	return (ARCHIVE_OK);+invalid:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Invalid CAB header");+	return (ARCHIVE_FATAL);+nomem:+	archive_set_error(&a->archive, ENOMEM,+	    "Can't allocate memory for CAB data");+	return (ARCHIVE_FATAL);+}++static int+archive_read_format_cab_read_header(struct archive_read *a,+    struct archive_entry *entry)+{+	struct cab *cab;+	struct cfheader *hd;+	struct cffolder *prev_folder;+	struct cffile *file;+	struct archive_string_conv *sconv;+	int err = ARCHIVE_OK, r;+	+	cab = (struct cab *)(a->format->data);+	if (cab->found_header == 0) {+		err = cab_read_header(a); +		if (err < ARCHIVE_WARN)+			return (err);+		/* We've found the header. */+		cab->found_header = 1;+	}+	hd = &cab->cfheader;++	if (hd->file_index >= hd->file_count) {+		cab->end_of_archive = 1;+		return (ARCHIVE_EOF);+	}+	file = &hd->file_array[hd->file_index++];++	cab->end_of_entry = 0;+	cab->end_of_entry_cleanup = 0;+	cab->entry_compressed_bytes_read = 0;+	cab->entry_uncompressed_bytes_read = 0;+	cab->entry_unconsumed = 0;+	cab->entry_cffile = file;++	/*+	 * Choose a proper folder.+	 */+	prev_folder = cab->entry_cffolder;+	switch (file->folder) {+	case iFoldCONTINUED_FROM_PREV:+	case iFoldCONTINUED_PREV_AND_NEXT:+		cab->entry_cffolder = &hd->folder_array[0];+		break;+	case iFoldCONTINUED_TO_NEXT:+		cab->entry_cffolder = &hd->folder_array[hd->folder_count-1];+		break;+	default:+		cab->entry_cffolder = &hd->folder_array[file->folder];+		break;+	}+	/* If a cffolder of this file is changed, reset a cfdata to read+	 * file contents from next cfdata. */+	if (prev_folder != cab->entry_cffolder)+		cab->entry_cfdata = NULL;++	/* If a pathname is UTF-8, prepare a string conversion object+	 * for UTF-8 and use it. */+	if (file->attr & ATTR_NAME_IS_UTF) {+		if (cab->sconv_utf8 == NULL) {+			cab->sconv_utf8 =+			    archive_string_conversion_from_charset(+				&(a->archive), "UTF-8", 1);+			if (cab->sconv_utf8 == NULL)+				return (ARCHIVE_FATAL);+		}+		sconv = cab->sconv_utf8;+	} else if (cab->sconv != NULL) {+		/* Choose the conversion specified by the option. */+		sconv = cab->sconv;+	} else {+		/* Choose the default conversion. */+		if (!cab->init_default_conversion) {+			cab->sconv_default =+			    archive_string_default_conversion_for_read(+			      &(a->archive));+			cab->init_default_conversion = 1;+		}+		sconv = cab->sconv_default;+	}++	/*+	 * Set a default value and common data+	 */+	r = cab_convert_path_separator_1(&(file->pathname), file->attr);+	if (archive_entry_copy_pathname_l(entry, file->pathname.s,+	    archive_strlen(&(file->pathname)), sconv) != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Pathname cannot be converted "+		    "from %s to current locale.",+		    archive_string_conversion_charset_name(sconv));+		err = ARCHIVE_WARN;+	}+	if (r < 0) {+		/* Convert a path separator '\' -> '/' */+		cab_convert_path_separator_2(cab, entry);+	}++	archive_entry_set_size(entry, file->uncompressed_size);+	if (file->attr & ATTR_RDONLY)+		archive_entry_set_mode(entry, AE_IFREG | 0555);+	else+		archive_entry_set_mode(entry, AE_IFREG | 0666);+	archive_entry_set_mtime(entry, file->mtime, 0);++	cab->entry_bytes_remaining = file->uncompressed_size;+	cab->entry_offset = 0;+	/* We don't need compress data. */+	if (file->uncompressed_size == 0)+		cab->end_of_entry_cleanup = cab->end_of_entry = 1;++	/* Set up a more descriptive format name. */+	sprintf(cab->format_name, "CAB %d.%d (%s)",+	    hd->major, hd->minor, cab->entry_cffolder->compname);+	a->archive.archive_format_name = cab->format_name;++	return (err);+}++static int+archive_read_format_cab_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	struct cab *cab = (struct cab *)(a->format->data);+	int r;++	switch (cab->entry_cffile->folder) {+	case iFoldCONTINUED_FROM_PREV:+	case iFoldCONTINUED_TO_NEXT:+	case iFoldCONTINUED_PREV_AND_NEXT:+		*buff = NULL;+		*size = 0;+		*offset = 0;+		archive_clear_error(&a->archive);+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Cannot restore this file split in multivolume.");+		return (ARCHIVE_FAILED);+	default:+		break;+	}+	if (cab->read_data_invoked == 0) {+		if (cab->bytes_skipped) {+			if (cab->entry_cfdata == NULL) {+				r = cab_next_cfdata(a);+				if (r < 0)+					return (r);+			}+			if (cab_consume_cfdata(a, cab->bytes_skipped) < 0)+				return (ARCHIVE_FATAL);+			cab->bytes_skipped = 0;+		}+		cab->read_data_invoked = 1;+	}+	if (cab->entry_unconsumed) {+		/* Consume as much as the compressor actually used. */+		r = (int)cab_consume_cfdata(a, cab->entry_unconsumed);+		cab->entry_unconsumed = 0;+		if (r < 0)+			return (r);+	}+	if (cab->end_of_archive || cab->end_of_entry) {+		if (!cab->end_of_entry_cleanup) {+			/* End-of-entry cleanup done. */+			cab->end_of_entry_cleanup = 1;+		}+		*offset = cab->entry_offset;+		*size = 0;+		*buff = NULL;+		return (ARCHIVE_EOF);+	}++	return (cab_read_data(a, buff, size, offset));+}++static uint32_t+cab_checksum_cfdata_4(const void *p, size_t bytes, uint32_t seed)+{+	const unsigned char *b;+	unsigned u32num;+	uint32_t sum;++	u32num = (unsigned)bytes / 4;+	sum = seed;+	b = p;+	for (;u32num > 0; --u32num) {+		sum ^= archive_le32dec(b);+		b += 4;+	}+	return (sum);+}++static uint32_t+cab_checksum_cfdata(const void *p, size_t bytes, uint32_t seed)+{+	const unsigned char *b;+	uint32_t sum;+	uint32_t t;++	sum = cab_checksum_cfdata_4(p, bytes, seed);+	b = p;+	b += bytes & ~3;+	t = 0;+	switch (bytes & 3) {+	case 3:+		t |= ((uint32_t)(*b++)) << 16;+		/* FALL THROUGH */+	case 2:+		t |= ((uint32_t)(*b++)) << 8;+		/* FALL THROUGH */+	case 1:+		t |= *b;+		/* FALL THROUGH */+	default:+		break;+	}+	sum ^= t;++	return (sum);+}++static void+cab_checksum_update(struct archive_read *a, size_t bytes)+{+	struct cab *cab = (struct cab *)(a->format->data);+	struct cfdata *cfdata = cab->entry_cfdata;+	const unsigned char *p;+	size_t sumbytes;++	if (cfdata->sum == 0 || cfdata->sum_ptr == NULL)+		return;+	/*+	 * Calculate the sum of this CFDATA.+	 * Make sure CFDATA must be calculated in four bytes.+	 */+	p = cfdata->sum_ptr;+	sumbytes = bytes;+	if (cfdata->sum_extra_avail) {+		while (cfdata->sum_extra_avail < 4 && sumbytes > 0) {+			cfdata->sum_extra[+			    cfdata->sum_extra_avail++] = *p++;+			sumbytes--;+		}+		if (cfdata->sum_extra_avail == 4) {+			cfdata->sum_calculated = cab_checksum_cfdata_4(+			    cfdata->sum_extra, 4, cfdata->sum_calculated);+			cfdata->sum_extra_avail = 0;+		}+	}+	if (sumbytes) {+		int odd = sumbytes & 3;+		if (sumbytes - odd > 0)+			cfdata->sum_calculated = cab_checksum_cfdata_4(+			    p, sumbytes - odd, cfdata->sum_calculated);+		if (odd)+			memcpy(cfdata->sum_extra, p + sumbytes - odd, odd);+		cfdata->sum_extra_avail = odd;+	}+	cfdata->sum_ptr = NULL;+}++static int+cab_checksum_finish(struct archive_read *a)+{+	struct cab *cab = (struct cab *)(a->format->data);+	struct cfdata *cfdata = cab->entry_cfdata;+	int l;++	/* Do not need to compute a sum. */+	if (cfdata->sum == 0)+		return (ARCHIVE_OK);++	/*+	 * Calculate the sum of remaining CFDATA.+	 */+	if (cfdata->sum_extra_avail) {+		cfdata->sum_calculated =+		    cab_checksum_cfdata(cfdata->sum_extra,+		       cfdata->sum_extra_avail, cfdata->sum_calculated);+		cfdata->sum_extra_avail = 0;+	}++	l = 4;+	if (cab->cfheader.flags & RESERVE_PRESENT)+		l += cab->cfheader.cfdata;+	cfdata->sum_calculated = cab_checksum_cfdata(+	    cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated);+	if (cfdata->sum_calculated != cfdata->sum) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Checksum error CFDATA[%d] %x:%x in %d bytes",+		    cab->entry_cffolder->cfdata_index -1,+		    cfdata->sum, cfdata->sum_calculated,+		    cfdata->compressed_size);+		return (ARCHIVE_FAILED);+	}+	return (ARCHIVE_OK);+}++/*+ * Read CFDATA if needed.+ */+static int+cab_next_cfdata(struct archive_read *a)+{+	struct cab *cab = (struct cab *)(a->format->data);+	struct cfdata *cfdata = cab->entry_cfdata;++	/* There are remaining bytes in current CFDATA, use it first. */+	if (cfdata != NULL && cfdata->uncompressed_bytes_remaining > 0)+		return (ARCHIVE_OK);++	if (cfdata == NULL) {+		int64_t skip;++		cab->entry_cffolder->cfdata_index = 0;++		/* Seek read pointer to the offset of CFDATA if needed. */+		skip = cab->entry_cffolder->cfdata_offset_in_cab+			- cab->cab_offset;+		if (skip < 0) {+			int folder_index;+			switch (cab->entry_cffile->folder) {+			case iFoldCONTINUED_FROM_PREV:+			case iFoldCONTINUED_PREV_AND_NEXT:+				folder_index = 0;+				break;+			case iFoldCONTINUED_TO_NEXT:+				folder_index = cab->cfheader.folder_count-1;+				break;+			default:+				folder_index = cab->entry_cffile->folder;+				break;+			}+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Invalid offset of CFDATA in folder(%d) %jd < %jd",+			    folder_index,+			    (intmax_t)cab->entry_cffolder->cfdata_offset_in_cab,+			    (intmax_t)cab->cab_offset);+			return (ARCHIVE_FATAL);+		}+		if (skip > 0) {+			if (__archive_read_consume(a, skip) < 0)+				return (ARCHIVE_FATAL);+			cab->cab_offset =+			    cab->entry_cffolder->cfdata_offset_in_cab;+		}+	}++	/*+	 * Read a CFDATA.+	 */+	if (cab->entry_cffolder->cfdata_index <+	    cab->entry_cffolder->cfdata_count) {+		const unsigned char *p;+		int l;++		cfdata = &(cab->entry_cffolder->cfdata);+		cab->entry_cffolder->cfdata_index++;+		cab->entry_cfdata = cfdata;+		cfdata->sum_calculated = 0;+		cfdata->sum_extra_avail = 0;+		cfdata->sum_ptr = NULL;+		l = 8;+		if (cab->cfheader.flags & RESERVE_PRESENT)+			l += cab->cfheader.cfdata;+		if ((p = __archive_read_ahead(a, l, NULL)) == NULL)+			return (truncated_error(a));+		cfdata->sum = archive_le32dec(p + CFDATA_csum);+		cfdata->compressed_size = archive_le16dec(p + CFDATA_cbData);+		cfdata->compressed_bytes_remaining = cfdata->compressed_size;+		cfdata->uncompressed_size =+		    archive_le16dec(p + CFDATA_cbUncomp);+		cfdata->uncompressed_bytes_remaining =+		    cfdata->uncompressed_size;+		cfdata->uncompressed_avail = 0;+		cfdata->read_offset = 0;+		cfdata->unconsumed = 0;++		/*+		 * Sanity check if data size is acceptable.+		 */+		if (cfdata->compressed_size == 0 ||+		    cfdata->compressed_size > (0x8000+6144))+			goto invalid;+		if (cfdata->uncompressed_size > 0x8000)+			goto invalid;+		if (cfdata->uncompressed_size == 0) {+			switch (cab->entry_cffile->folder) {+			case iFoldCONTINUED_PREV_AND_NEXT:+			case iFoldCONTINUED_TO_NEXT:+				break;+			case iFoldCONTINUED_FROM_PREV:+			default:+				goto invalid;+			}+		}+		/* If CFDATA is not last in a folder, an uncompressed+		 * size must be 0x8000(32KBi) */+		if ((cab->entry_cffolder->cfdata_index <+		     cab->entry_cffolder->cfdata_count) &&+		       cfdata->uncompressed_size != 0x8000)+			goto invalid;++		/* A compressed data size and an uncompressed data size must+		 * be the same in no compression mode. */+		if (cab->entry_cffolder->comptype == COMPTYPE_NONE &&+		    cfdata->compressed_size != cfdata->uncompressed_size)+			goto invalid;++		/*+		 * Save CFDATA image for sum check.+		 */+		if (cfdata->memimage_size < (size_t)l) {+			free(cfdata->memimage);+			cfdata->memimage = malloc(l);+			if (cfdata->memimage == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for CAB data");+				return (ARCHIVE_FATAL);+			}+			cfdata->memimage_size = l;+		}+		memcpy(cfdata->memimage, p, l);++		/* Consume bytes as much as we used. */+		__archive_read_consume(a, l);+		cab->cab_offset += l;+	} else if (cab->entry_cffolder->cfdata_count > 0) {+		/* Run out of all CFDATA in a folder. */+		cfdata->compressed_size = 0;+		cfdata->uncompressed_size = 0;+		cfdata->compressed_bytes_remaining = 0;+		cfdata->uncompressed_bytes_remaining = 0;+	} else {+		/* Current folder does not have any CFDATA. */+		cfdata = &(cab->entry_cffolder->cfdata);+		cab->entry_cfdata = cfdata;+		memset(cfdata, 0, sizeof(*cfdata));+	}+	return (ARCHIVE_OK);+invalid:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Invalid CFDATA");+	return (ARCHIVE_FATAL);+}++/*+ * Read ahead CFDATA.+ */+static const void *+cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail)+{+	struct cab *cab = (struct cab *)(a->format->data);+	int err;++	err = cab_next_cfdata(a);+	if (err < ARCHIVE_OK) {+		*avail = err;+		return (NULL);+	}++	switch (cab->entry_cffolder->comptype) {+	case COMPTYPE_NONE:+		return (cab_read_ahead_cfdata_none(a, avail));+	case COMPTYPE_MSZIP:+		return (cab_read_ahead_cfdata_deflate(a, avail));+	case COMPTYPE_LZX:+		return (cab_read_ahead_cfdata_lzx(a, avail));+	default: /* Unsupported compression. */+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unsupported CAB compression : %s",+		    cab->entry_cffolder->compname);+		*avail = ARCHIVE_FAILED;+		return (NULL);+	}+}++/*+ * Read ahead CFDATA as uncompressed data.+ */+static const void *+cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail)+{+	struct cab *cab = (struct cab *)(a->format->data);+	struct cfdata *cfdata;+	const void *d;++	cfdata = cab->entry_cfdata;++	/*+	 * Note: '1' here is a performance optimization.+	 * Recall that the decompression layer returns a count of+	 * available bytes; asking for more than that forces the+	 * decompressor to combine reads by copying data.+	 */+	d = __archive_read_ahead(a, 1, avail);+	if (*avail <= 0) {+		*avail = truncated_error(a);+		return (NULL);+	}+	if (*avail > cfdata->uncompressed_bytes_remaining)+		*avail = cfdata->uncompressed_bytes_remaining;+	cfdata->uncompressed_avail = cfdata->uncompressed_size;+	cfdata->unconsumed = *avail;+	cfdata->sum_ptr = d;+	return (d);+}++/*+ * Read ahead CFDATA as deflate data.+ */+#ifdef HAVE_ZLIB_H+static const void *+cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)+{+	struct cab *cab = (struct cab *)(a->format->data);+	struct cfdata *cfdata;+	const void *d;+	int r, mszip;+	uint16_t uavail;+	char eod = 0;++	cfdata = cab->entry_cfdata;+	/* If the buffer hasn't been allocated, allocate it now. */+	if (cab->uncompressed_buffer == NULL) {+		cab->uncompressed_buffer_size = 0x8000;+		cab->uncompressed_buffer+		    = (unsigned char *)malloc(cab->uncompressed_buffer_size);+		if (cab->uncompressed_buffer == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "No memory for CAB reader");+			*avail = ARCHIVE_FATAL;+			return (NULL);+		}+	}++	uavail = cfdata->uncompressed_avail;+	if (uavail == cfdata->uncompressed_size) {+		d = cab->uncompressed_buffer + cfdata->read_offset;+		*avail = uavail - cfdata->read_offset;+		return (d);+	}++	if (!cab->entry_cffolder->decompress_init) {+		cab->stream.next_in = NULL;+		cab->stream.avail_in = 0;+		cab->stream.total_in = 0;+		cab->stream.next_out = NULL;+		cab->stream.avail_out = 0;+		cab->stream.total_out = 0;+		if (cab->stream_valid)+			r = inflateReset(&cab->stream);+		else+			r = inflateInit2(&cab->stream,+			    -15 /* Don't check for zlib header */);+		if (r != Z_OK) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Can't initialize deflate decompression.");+			*avail = ARCHIVE_FATAL;+			return (NULL);+		}+		/* Stream structure has been set up. */+		cab->stream_valid = 1;+		/* We've initialized decompression for this stream. */+		cab->entry_cffolder->decompress_init = 1;+	}++	if (cfdata->compressed_bytes_remaining == cfdata->compressed_size)+		mszip = 2;+	else+		mszip = 0;+	eod = 0;+	cab->stream.total_out = uavail;+	/*+	 * We always uncompress all data in current CFDATA.+	 */+	while (!eod && cab->stream.total_out < cfdata->uncompressed_size) {+		ssize_t bytes_avail;++		cab->stream.next_out =+		    cab->uncompressed_buffer + cab->stream.total_out;+		cab->stream.avail_out =+		    cfdata->uncompressed_size - cab->stream.total_out;++		d = __archive_read_ahead(a, 1, &bytes_avail);+		if (bytes_avail <= 0) {+			*avail = truncated_error(a);+			return (NULL);+		}+		if (bytes_avail > cfdata->compressed_bytes_remaining)+			bytes_avail = cfdata->compressed_bytes_remaining;+		/*+		 * A bug in zlib.h: stream.next_in should be marked 'const'+		 * but isn't (the library never alters data through the+		 * next_in pointer, only reads it).  The result: this ugly+		 * cast to remove 'const'.+		 */+		cab->stream.next_in = (Bytef *)(uintptr_t)d;+		cab->stream.avail_in = (uInt)bytes_avail;+		cab->stream.total_in = 0;++		/* Cut out a tow-byte MSZIP signature(0x43, 0x4b). */+		if (mszip > 0) {+			if (bytes_avail <= 0)+				goto nomszip;+			if (bytes_avail <= mszip) {+				if (mszip == 2) {+					if (cab->stream.next_in[0] != 0x43)+						goto nomszip;+					if (bytes_avail > 1 &&+					    cab->stream.next_in[1] != 0x4b)+						goto nomszip;+				} else if (cab->stream.next_in[0] != 0x4b)+					goto nomszip;+				cfdata->unconsumed = bytes_avail;+				cfdata->sum_ptr = d;+				if (cab_minimum_consume_cfdata(+				    a, cfdata->unconsumed) < 0) {+					*avail = ARCHIVE_FATAL;+					return (NULL);+				}+				mszip -= (int)bytes_avail;+				continue;+			}+			if (mszip == 1 && cab->stream.next_in[0] != 0x4b)+				goto nomszip;+			else if (mszip == 2 && (cab->stream.next_in[0] != 0x43 ||+			    cab->stream.next_in[1] != 0x4b))+				goto nomszip;+			cab->stream.next_in += mszip;+			cab->stream.avail_in -= mszip;+			cab->stream.total_in += mszip;+			mszip = 0;+		}++		r = inflate(&cab->stream, 0);+		switch (r) {+		case Z_OK:+			break;+		case Z_STREAM_END:+			eod = 1;+			break;+		default:+			goto zlibfailed;+		}+		cfdata->unconsumed = cab->stream.total_in;+		cfdata->sum_ptr = d;+		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {+			*avail = ARCHIVE_FATAL;+			return (NULL);+		}+	}+	uavail = (uint16_t)cab->stream.total_out;++	if (uavail < cfdata->uncompressed_size) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Invalid uncompressed size (%d < %d)",+		    uavail, cfdata->uncompressed_size);+		*avail = ARCHIVE_FATAL;+		return (NULL);+	}++	/*+	 * Note: I suspect there is a bug in makecab.exe because, in rare+	 * case, compressed bytes are still remaining regardless we have+	 * gotten all uncompressed bytes, which size is recorded in CFDATA,+	 * as much as we need, and we have to use the garbage so as to+	 * correctly compute the sum of CFDATA accordingly.+	 */+	if (cfdata->compressed_bytes_remaining > 0) {+		ssize_t bytes_avail;++		d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining,+		    &bytes_avail);+		if (bytes_avail <= 0) {+			*avail = truncated_error(a);+			return (NULL);+		}+		cfdata->unconsumed = cfdata->compressed_bytes_remaining;+		cfdata->sum_ptr = d;+		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {+			*avail = ARCHIVE_FATAL;+			return (NULL);+		}+	}++	/*+	 * Set dictionary data for decompressing of next CFDATA, which+	 * in the same folder. This is why we always do decompress CFDATA+	 * even if beginning CFDATA or some of CFDATA are not used in+	 * skipping file data.+	 */+	if (cab->entry_cffolder->cfdata_index <+	    cab->entry_cffolder->cfdata_count) {+		r = inflateReset(&cab->stream);+		if (r != Z_OK)+			goto zlibfailed;+		r = inflateSetDictionary(&cab->stream,+		    cab->uncompressed_buffer, cfdata->uncompressed_size);+		if (r != Z_OK)+			goto zlibfailed;+	}++	d = cab->uncompressed_buffer + cfdata->read_offset;+	*avail = uavail - cfdata->read_offset;+	cfdata->uncompressed_avail = uavail;++	return (d);++zlibfailed:+	switch (r) {+	case Z_MEM_ERROR:+		archive_set_error(&a->archive, ENOMEM,+		    "Out of memory for deflate decompression");+		break;+	default:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Deflate decompression failed (%d)", r);+		break;+	}+	*avail = ARCHIVE_FATAL;+	return (NULL);+nomszip:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+	    "CFDATA incorrect(no MSZIP signature)");+	*avail = ARCHIVE_FATAL;+	return (NULL);+}++#else /* HAVE_ZLIB_H */++static const void *+cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)+{+	*avail = ARCHIVE_FATAL;+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+	    "libarchive compiled without deflate support (no libz)");+	return (NULL);+}++#endif /* HAVE_ZLIB_H */++static const void *+cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)+{+	struct cab *cab = (struct cab *)(a->format->data);+	struct cfdata *cfdata;+	const void *d;+	int r;+	uint16_t uavail;++	cfdata = cab->entry_cfdata;+	/* If the buffer hasn't been allocated, allocate it now. */+	if (cab->uncompressed_buffer == NULL) {+		cab->uncompressed_buffer_size = 0x8000;+		cab->uncompressed_buffer+		    = (unsigned char *)malloc(cab->uncompressed_buffer_size);+		if (cab->uncompressed_buffer == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "No memory for CAB reader");+			*avail = ARCHIVE_FATAL;+			return (NULL);+		}+	}++	uavail = cfdata->uncompressed_avail;+	if (uavail == cfdata->uncompressed_size) {+		d = cab->uncompressed_buffer + cfdata->read_offset;+		*avail = uavail - cfdata->read_offset;+		return (d);+	}++	if (!cab->entry_cffolder->decompress_init) {+		r = lzx_decode_init(&cab->xstrm,+		    cab->entry_cffolder->compdata);+		if (r != ARCHIVE_OK) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Can't initialize LZX decompression.");+			*avail = ARCHIVE_FATAL;+			return (NULL);+		}+		/* We've initialized decompression for this stream. */+		cab->entry_cffolder->decompress_init = 1;+	}++	/* Clean up remaining bits of previous CFDATA. */+	lzx_cleanup_bitstream(&cab->xstrm);+	cab->xstrm.total_out = uavail;+	while (cab->xstrm.total_out < cfdata->uncompressed_size) {+		ssize_t bytes_avail;++		cab->xstrm.next_out =+		    cab->uncompressed_buffer + cab->xstrm.total_out;+		cab->xstrm.avail_out =+		    cfdata->uncompressed_size - cab->xstrm.total_out;++		d = __archive_read_ahead(a, 1, &bytes_avail);+		if (bytes_avail <= 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated CAB file data");+			*avail = ARCHIVE_FATAL;+			return (NULL);+		}+		if (bytes_avail > cfdata->compressed_bytes_remaining)+			bytes_avail = cfdata->compressed_bytes_remaining;++		cab->xstrm.next_in = d;+		cab->xstrm.avail_in = bytes_avail;+		cab->xstrm.total_in = 0;+		r = lzx_decode(&cab->xstrm,+		    cfdata->compressed_bytes_remaining == bytes_avail);+		switch (r) {+		case ARCHIVE_OK:+		case ARCHIVE_EOF:+			break;+		default:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "LZX decompression failed (%d)", r);+			*avail = ARCHIVE_FATAL;+			return (NULL);+		}+		cfdata->unconsumed = cab->xstrm.total_in;+		cfdata->sum_ptr = d;+		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {+			*avail = ARCHIVE_FATAL;+			return (NULL);+		}+	}++	uavail = (uint16_t)cab->xstrm.total_out;+	/*+	 * Make sure a read pointer advances to next CFDATA.+	 */+	if (cfdata->compressed_bytes_remaining > 0) {+		ssize_t bytes_avail;++		d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining,+		    &bytes_avail);+		if (bytes_avail <= 0) {+			*avail = truncated_error(a);+			return (NULL);+		}+		cfdata->unconsumed = cfdata->compressed_bytes_remaining;+		cfdata->sum_ptr = d;+		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {+			*avail = ARCHIVE_FATAL;+			return (NULL);+		}+	}++	/*+	 * Translation reversal of x86 processor CALL byte sequence(E8).+	 */+	lzx_translation(&cab->xstrm, cab->uncompressed_buffer,+	    cfdata->uncompressed_size,+	    (cab->entry_cffolder->cfdata_index-1) * 0x8000);++	d = cab->uncompressed_buffer + cfdata->read_offset;+	*avail = uavail - cfdata->read_offset;+	cfdata->uncompressed_avail = uavail;++	return (d);+}++/*+ * Consume CFDATA.+ * We always decompress CFDATA to consume CFDATA as much as we need+ * in uncompressed bytes because all CFDATA in a folder are related+ * so we do not skip any CFDATA without decompressing.+ * Note: If the folder of a CFFILE is iFoldCONTINUED_PREV_AND_NEXT or+ * iFoldCONTINUED_FROM_PREV, we won't decompress because a CFDATA for+ * the CFFILE is remaining bytes of previous Multivolume CAB file.+ */+static int64_t+cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)+{+	struct cab *cab = (struct cab *)(a->format->data);+	struct cfdata *cfdata;+	int64_t cbytes, rbytes;+	int err;++	rbytes = cab_minimum_consume_cfdata(a, consumed_bytes);+	if (rbytes < 0)+		return (ARCHIVE_FATAL);++	cfdata = cab->entry_cfdata;+	while (rbytes > 0) {+		ssize_t avail;++		if (cfdata->compressed_size == 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Invalid CFDATA");+			return (ARCHIVE_FATAL);+		}+		cbytes = cfdata->uncompressed_bytes_remaining;+		if (cbytes > rbytes)+			cbytes = rbytes;+		rbytes -= cbytes;++		if (cfdata->uncompressed_avail == 0 &&+		   (cab->entry_cffile->folder == iFoldCONTINUED_PREV_AND_NEXT ||+		    cab->entry_cffile->folder == iFoldCONTINUED_FROM_PREV)) {+			/* We have not read any data yet. */+			if (cbytes == cfdata->uncompressed_bytes_remaining) {+				/* Skip whole current CFDATA. */+				__archive_read_consume(a,+				    cfdata->compressed_size);+				cab->cab_offset += cfdata->compressed_size;+				cfdata->compressed_bytes_remaining = 0;+				cfdata->uncompressed_bytes_remaining = 0;+				err = cab_next_cfdata(a);+				if (err < 0)+					return (err);+				cfdata = cab->entry_cfdata;+				if (cfdata->uncompressed_size == 0) {+					switch (cab->entry_cffile->folder) {+					case iFoldCONTINUED_PREV_AND_NEXT:+					case iFoldCONTINUED_TO_NEXT:+					case iFoldCONTINUED_FROM_PREV:+						rbytes = 0;+						break;+					default:+						break;+					}+				}+				continue;+			}+			cfdata->read_offset += (uint16_t)cbytes;+			cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;+			break;+		} else if (cbytes == 0) {+			err = cab_next_cfdata(a);+			if (err < 0)+				return (err);+			cfdata = cab->entry_cfdata;+			if (cfdata->uncompressed_size == 0) {+				switch (cab->entry_cffile->folder) {+				case iFoldCONTINUED_PREV_AND_NEXT:+				case iFoldCONTINUED_TO_NEXT:+				case iFoldCONTINUED_FROM_PREV:+					return (ARCHIVE_FATAL);+				default:+					break;+				}+			}+			continue;+		}+		while (cbytes > 0) {+			(void)cab_read_ahead_cfdata(a, &avail);+			if (avail <= 0)+				return (ARCHIVE_FATAL);+			if (avail > cbytes)+				avail = (ssize_t)cbytes;+			if (cab_minimum_consume_cfdata(a, avail) < 0)+				return (ARCHIVE_FATAL);+			cbytes -= avail;+		}+	}+	return (consumed_bytes);+}++/*+ * Consume CFDATA as much as we have already gotten and+ * compute the sum of CFDATA.+ */+static int64_t+cab_minimum_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)+{+	struct cab *cab = (struct cab *)(a->format->data);+	struct cfdata *cfdata;+	int64_t cbytes, rbytes;+	int err;++	cfdata = cab->entry_cfdata;+	rbytes = consumed_bytes;+	if (cab->entry_cffolder->comptype == COMPTYPE_NONE) {+		if (consumed_bytes < cfdata->unconsumed)+			cbytes = consumed_bytes;+		else+			cbytes = cfdata->unconsumed;+		rbytes -= cbytes; +		cfdata->read_offset += (uint16_t)cbytes;+		cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;+		cfdata->unconsumed -= cbytes;+	} else {+		cbytes = cfdata->uncompressed_avail - cfdata->read_offset;+		if (cbytes > 0) {+			if (consumed_bytes < cbytes)+				cbytes = consumed_bytes;+			rbytes -= cbytes;+			cfdata->read_offset += (uint16_t)cbytes;+			cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;+		}++		if (cfdata->unconsumed) {+			cbytes = cfdata->unconsumed;+			cfdata->unconsumed = 0;+		} else+			cbytes = 0;+	}+	if (cbytes) {+		/* Compute the sum. */+		cab_checksum_update(a, (size_t)cbytes);++		/* Consume as much as the compressor actually used. */+		__archive_read_consume(a, cbytes);+		cab->cab_offset += cbytes;+		cfdata->compressed_bytes_remaining -= (uint16_t)cbytes;+		if (cfdata->compressed_bytes_remaining == 0) {+			err = cab_checksum_finish(a);+			if (err < 0)+				return (err);+		}+	}+	return (rbytes);+}++/*+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets+ * cab->end_of_entry if it consumes all of the data.+ */+static int+cab_read_data(struct archive_read *a, const void **buff,+    size_t *size, int64_t *offset)+{+	struct cab *cab = (struct cab *)(a->format->data);+	ssize_t bytes_avail;++	if (cab->entry_bytes_remaining == 0) {+		*buff = NULL;+		*size = 0;+		*offset = cab->entry_offset;+		cab->end_of_entry = 1;+		return (ARCHIVE_OK);+	}++	*buff = cab_read_ahead_cfdata(a, &bytes_avail);+	if (bytes_avail <= 0) {+		*buff = NULL;+		*size = 0;+		*offset = 0;+		if (bytes_avail == 0 &&+		    cab->entry_cfdata->uncompressed_size == 0) {+			/* All of CFDATA in a folder has been handled. */+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA");+			return (ARCHIVE_FATAL);+		} else+			return ((int)bytes_avail);+	}+	if (bytes_avail > cab->entry_bytes_remaining)+		bytes_avail = (ssize_t)cab->entry_bytes_remaining;++	*size = bytes_avail;+	*offset = cab->entry_offset;+	cab->entry_offset += bytes_avail;+	cab->entry_bytes_remaining -= bytes_avail;+	if (cab->entry_bytes_remaining == 0)+		cab->end_of_entry = 1;+	cab->entry_unconsumed = bytes_avail;+	if (cab->entry_cffolder->comptype == COMPTYPE_NONE) {+		/* Don't consume more than current entry used. */+		if (cab->entry_cfdata->unconsumed > cab->entry_unconsumed)+			cab->entry_cfdata->unconsumed = cab->entry_unconsumed;+	}+	return (ARCHIVE_OK);+}++static int+archive_read_format_cab_read_data_skip(struct archive_read *a)+{+	struct cab *cab;+	int64_t bytes_skipped;+	int r;++	cab = (struct cab *)(a->format->data);++	if (cab->end_of_archive)+		return (ARCHIVE_EOF);++	if (!cab->read_data_invoked) {+		cab->bytes_skipped += cab->entry_bytes_remaining;+		cab->entry_bytes_remaining = 0;+		/* This entry is finished and done. */+		cab->end_of_entry_cleanup = cab->end_of_entry = 1;+		return (ARCHIVE_OK);+	}++	if (cab->entry_unconsumed) {+		/* Consume as much as the compressor actually used. */+		r = (int)cab_consume_cfdata(a, cab->entry_unconsumed);+		cab->entry_unconsumed = 0;+		if (r < 0)+			return (r);+	} else if (cab->entry_cfdata == NULL) {+		r = cab_next_cfdata(a);+		if (r < 0)+			return (r);+	}++	/* if we've already read to end of data, we're done. */+	if (cab->end_of_entry_cleanup)+		return (ARCHIVE_OK);++	/*+	 * If the length is at the beginning, we can skip the+	 * compressed data much more quickly.+	 */+	bytes_skipped = cab_consume_cfdata(a, cab->entry_bytes_remaining);+	if (bytes_skipped < 0)+		return (ARCHIVE_FATAL);++	/* If the compression type is none(uncompressed), we've already+	 * consumed data as much as the current entry size. */+	if (cab->entry_cffolder->comptype == COMPTYPE_NONE &&+	    cab->entry_cfdata != NULL)+		cab->entry_cfdata->unconsumed = 0;++	/* This entry is finished and done. */+	cab->end_of_entry_cleanup = cab->end_of_entry = 1;+	return (ARCHIVE_OK);+}++static int+archive_read_format_cab_cleanup(struct archive_read *a)+{+	struct cab *cab = (struct cab *)(a->format->data);+	struct cfheader *hd = &cab->cfheader;+	int i;++	if (hd->folder_array != NULL) {+		for (i = 0; i < hd->folder_count; i++)+			free(hd->folder_array[i].cfdata.memimage);+		free(hd->folder_array);+	}+	if (hd->file_array != NULL) {+		for (i = 0; i < cab->cfheader.file_count; i++)+			archive_string_free(&(hd->file_array[i].pathname));+		free(hd->file_array);+	}+#ifdef HAVE_ZLIB_H+	if (cab->stream_valid)+		inflateEnd(&cab->stream);+#endif+	lzx_decode_free(&cab->xstrm);+	archive_wstring_free(&cab->ws);+	free(cab->uncompressed_buffer);+	free(cab);+	(a->format->data) = NULL;+	return (ARCHIVE_OK);+}++/* Convert an MSDOS-style date/time into Unix-style time. */+static time_t+cab_dos_time(const unsigned char *p)+{+	int msTime, msDate;+	struct tm ts;++	msDate = archive_le16dec(p);+	msTime = archive_le16dec(p+2);++	memset(&ts, 0, sizeof(ts));+	ts.tm_year = ((msDate >> 9) & 0x7f) + 80;   /* Years since 1900. */+	ts.tm_mon = ((msDate >> 5) & 0x0f) - 1;     /* Month number.     */+	ts.tm_mday = msDate & 0x1f;		    /* Day of month.     */+	ts.tm_hour = (msTime >> 11) & 0x1f;+	ts.tm_min = (msTime >> 5) & 0x3f;+	ts.tm_sec = (msTime << 1) & 0x3e;+	ts.tm_isdst = -1;+	return (mktime(&ts));+}++/*****************************************************************+ *+ * LZX decompression code.+ *+ *****************************************************************/++/*+ * Initialize LZX decoder.+ *+ * Returns ARCHIVE_OK if initialization was successful.+ * Returns ARCHIVE_FAILED if w_bits has unsupported value.+ * Returns ARCHIVE_FATAL if initialization failed; memory allocation+ * error occurred.+ */+static int+lzx_decode_init(struct lzx_stream *strm, int w_bits)+{+	struct lzx_dec *ds;+	int slot, w_size, w_slot;+	int base, footer;+	int base_inc[18];++	if (strm->ds == NULL) {+		strm->ds = calloc(1, sizeof(*strm->ds));+		if (strm->ds == NULL)+			return (ARCHIVE_FATAL);+	}+	ds = strm->ds;+	ds->error = ARCHIVE_FAILED;++	/* Allow bits from 15(32KBi) up to 21(2MBi) */+	if (w_bits < SLOT_BASE || w_bits > SLOT_MAX)+		return (ARCHIVE_FAILED);++	ds->error = ARCHIVE_FATAL;++	/*+	 * Alloc window+	 */+	w_size = ds->w_size;+	w_slot = slots[w_bits - SLOT_BASE];+	ds->w_size = 1U << w_bits;+	ds->w_mask = ds->w_size -1;+	if (ds->w_buff == NULL || w_size != ds->w_size) {+		free(ds->w_buff);+		ds->w_buff = malloc(ds->w_size);+		if (ds->w_buff == NULL)+			return (ARCHIVE_FATAL);+		free(ds->pos_tbl);+		ds->pos_tbl = malloc(sizeof(ds->pos_tbl[0]) * w_slot);+		if (ds->pos_tbl == NULL)+			return (ARCHIVE_FATAL);+		lzx_huffman_free(&(ds->mt));+	}++	for (footer = 0; footer < 18; footer++)+		base_inc[footer] = 1 << footer;+	base = footer = 0;+	for (slot = 0; slot < w_slot; slot++) {+		int n;+		if (footer == 0)+			base = slot;+		else+			base += base_inc[footer];+		if (footer < 17) {+			footer = -2;+			for (n = base; n; n >>= 1)+				footer++;+			if (footer <= 0)+				footer = 0;+		}+		ds->pos_tbl[slot].base = base;+		ds->pos_tbl[slot].footer_bits = footer;+	}++	ds->w_pos = 0;+	ds->state = 0;+	ds->br.cache_buffer = 0;+	ds->br.cache_avail = 0;+	ds->r0 = ds->r1 = ds->r2 = 1;++	/* Initialize aligned offset tree. */+	if (lzx_huffman_init(&(ds->at), 8, 8) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Initialize pre-tree. */+	if (lzx_huffman_init(&(ds->pt), 20, 10) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Initialize Main tree. */+	if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16)+	    != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Initialize Length tree. */+	if (lzx_huffman_init(&(ds->lt), 249, 16) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	ds->error = 0;++	return (ARCHIVE_OK);+}++/*+ * Release LZX decoder.+ */+static void+lzx_decode_free(struct lzx_stream *strm)+{++	if (strm->ds == NULL)+		return;+	free(strm->ds->w_buff);+	free(strm->ds->pos_tbl);+	lzx_huffman_free(&(strm->ds->at));+	lzx_huffman_free(&(strm->ds->pt));+	lzx_huffman_free(&(strm->ds->mt));+	lzx_huffman_free(&(strm->ds->lt));+	free(strm->ds);+	strm->ds = NULL;+}++/*+ * E8 Call Translation reversal.+ */+static void+lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset)+{+	struct lzx_dec *ds = strm->ds;+	unsigned char *b, *end;++	if (!ds->translation || size <= 10)+		return;+	b = p;+	end = b + size - 10;+	while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) {+		size_t i = b - (unsigned char *)p;+		int32_t cp, displacement, value;++		cp = (int32_t)(offset + (uint32_t)i);+		value = archive_le32dec(&b[1]);+		if (value >= -cp && value < (int32_t)ds->translation_size) {+			if (value >= 0)+				displacement = value - cp;+			else+				displacement = value + ds->translation_size;+			archive_le32enc(&b[1], (uint32_t)displacement);+		}+		b += 5;+	}+}++/*+ * Bit stream reader.+ */+/* Check that the cache buffer has enough bits. */+#define lzx_br_has(br, n)	((br)->cache_avail >= n)+/* Get compressed data by bit. */+#define lzx_br_bits(br, n)				\+	(((uint32_t)((br)->cache_buffer >>		\+		((br)->cache_avail - (n)))) & cache_masks[n])+#define lzx_br_bits_forced(br, n)			\+	(((uint32_t)((br)->cache_buffer <<		\+		((n) - (br)->cache_avail))) & cache_masks[n])+/* Read ahead to make sure the cache buffer has enough compressed data we+ * will use.+ *  True  : completed, there is enough data in the cache buffer.+ *  False : we met that strm->next_in is empty, we have to get following+ *          bytes. */+#define lzx_br_read_ahead_0(strm, br, n)	\+	(lzx_br_has((br), (n)) || lzx_br_fillup(strm, br))+/*  True  : the cache buffer has some bits as much as we need.+ *  False : there are no enough bits in the cache buffer to be used,+ *          we have to get following bytes if we could. */+#define lzx_br_read_ahead(strm, br, n)	\+	(lzx_br_read_ahead_0((strm), (br), (n)) || lzx_br_has((br), (n)))++/* Notify how many bits we consumed. */+#define lzx_br_consume(br, n)	((br)->cache_avail -= (n))+#define lzx_br_consume_unaligned_bits(br) ((br)->cache_avail &= ~0x0f)++#define lzx_br_is_unaligned(br)	((br)->cache_avail & 0x0f)++static const uint32_t cache_masks[] = {+	0x00000000, 0x00000001, 0x00000003, 0x00000007,+	0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,+	0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,+	0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,+	0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,+	0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,+	0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,+	0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,+	0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF+};++/*+ * Shift away used bits in the cache data and fill it up with following bits.+ * Call this when cache buffer does not have enough bits you need.+ *+ * Returns 1 if the cache buffer is full.+ * Returns 0 if the cache buffer is not full; input buffer is empty.+ */+static int+lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)+{+/*+ * x86 processor family can read misaligned data without an access error.+ */+	int n = CACHE_BITS - br->cache_avail;++	for (;;) {+		switch (n >> 4) {+		case 4:+			if (strm->avail_in >= 8) {+				br->cache_buffer =+				    ((uint64_t)strm->next_in[1]) << 56 |+				    ((uint64_t)strm->next_in[0]) << 48 |+				    ((uint64_t)strm->next_in[3]) << 40 |+				    ((uint64_t)strm->next_in[2]) << 32 |+				    ((uint32_t)strm->next_in[5]) << 24 |+				    ((uint32_t)strm->next_in[4]) << 16 |+				    ((uint32_t)strm->next_in[7]) << 8 |+				     (uint32_t)strm->next_in[6];+				strm->next_in += 8;+				strm->avail_in -= 8;+				br->cache_avail += 8 * 8;+				return (1);+			}+			break;+		case 3:+			if (strm->avail_in >= 6) {+				br->cache_buffer =+		 		   (br->cache_buffer << 48) |+				    ((uint64_t)strm->next_in[1]) << 40 |+				    ((uint64_t)strm->next_in[0]) << 32 |+				    ((uint32_t)strm->next_in[3]) << 24 |+				    ((uint32_t)strm->next_in[2]) << 16 |+				    ((uint32_t)strm->next_in[5]) << 8 |+				     (uint32_t)strm->next_in[4];+				strm->next_in += 6;+				strm->avail_in -= 6;+				br->cache_avail += 6 * 8;+				return (1);+			}+			break;+		case 0:+			/* We have enough compressed data in+			 * the cache buffer.*/+			return (1);+		default:+			break;+		}+		if (strm->avail_in < 2) {+			/* There is not enough compressed data to+			 * fill up the cache buffer. */+			if (strm->avail_in == 1) {+				br->odd = *strm->next_in++;+				strm->avail_in--;+				br->have_odd = 1;+			}+			return (0);+		}+		br->cache_buffer =+		   (br->cache_buffer << 16) |+		    archive_le16dec(strm->next_in);+		strm->next_in += 2;+		strm->avail_in -= 2;+		br->cache_avail += 16;+		n -= 16;+	}+}++static void+lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br)+{+	int n = CACHE_BITS - br->cache_avail;++	if (br->have_odd && n >= 16 && strm->avail_in > 0) {+		br->cache_buffer =+		   (br->cache_buffer << 16) |+		   ((uint16_t)(*strm->next_in)) << 8 | br->odd;+		strm->next_in++;+		strm->avail_in--;+		br->cache_avail += 16;+		br->have_odd = 0;+	}+}++static void+lzx_cleanup_bitstream(struct lzx_stream *strm)+{+	strm->ds->br.cache_avail = 0;+	strm->ds->br.have_odd = 0;+}++/*+ * Decode LZX.+ *+ * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty.+ *    Please set available buffer and call this function again.+ * 2. Returns ARCHIVE_EOF if decompression has been completed.+ * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data+ *    is broken or you do not set 'last' flag properly.+ */+#define ST_RD_TRANSLATION	0+#define ST_RD_TRANSLATION_SIZE	1+#define ST_RD_BLOCK_TYPE	2+#define ST_RD_BLOCK_SIZE	3+#define ST_RD_ALIGNMENT		4+#define ST_RD_R0		5+#define ST_RD_R1		6+#define ST_RD_R2		7+#define ST_COPY_UNCOMP1		8+#define ST_COPY_UNCOMP2		9+#define ST_RD_ALIGNED_OFFSET	10+#define ST_RD_VERBATIM		11+#define ST_RD_PRE_MAIN_TREE_256	12+#define ST_MAIN_TREE_256	13+#define ST_RD_PRE_MAIN_TREE_REM	14+#define ST_MAIN_TREE_REM	15+#define ST_RD_PRE_LENGTH_TREE	16+#define ST_LENGTH_TREE		17+#define ST_MAIN			18+#define ST_LENGTH		19+#define ST_OFFSET		20+#define ST_REAL_POS		21+#define ST_COPY			22++static int+lzx_decode(struct lzx_stream *strm, int last)+{+	struct lzx_dec *ds = strm->ds;+	int64_t avail_in;+	int r;++	if (ds->error)+		return (ds->error);++	avail_in = strm->avail_in;+	lzx_br_fixup(strm, &(ds->br));+	do {+		if (ds->state < ST_MAIN)+			r = lzx_read_blocks(strm, last);+		else {+			int64_t bytes_written = strm->avail_out;+			r = lzx_decode_blocks(strm, last);+			bytes_written -= strm->avail_out;+			strm->next_out += bytes_written;+			strm->total_out += bytes_written;+		}+	} while (r == 100);+	strm->total_in += avail_in - strm->avail_in;+	return (r);+}++static int+lzx_read_blocks(struct lzx_stream *strm, int last)+{+	struct lzx_dec *ds = strm->ds;+	struct lzx_br *br = &(ds->br);+	int i, r;++	for (;;) {+		switch (ds->state) {+		case ST_RD_TRANSLATION:+			if (!lzx_br_read_ahead(strm, br, 1)) {+				ds->state = ST_RD_TRANSLATION;+				if (last)+					goto failed;+				return (ARCHIVE_OK);+			}+			ds->translation = lzx_br_bits(br, 1);+			lzx_br_consume(br, 1);+			/* FALL THROUGH */+		case ST_RD_TRANSLATION_SIZE:+			if (ds->translation) {+				if (!lzx_br_read_ahead(strm, br, 32)) {+					ds->state = ST_RD_TRANSLATION_SIZE;+					if (last)+						goto failed;+					return (ARCHIVE_OK);+				}+				ds->translation_size = lzx_br_bits(br, 16);+				lzx_br_consume(br, 16);+				ds->translation_size <<= 16;+				ds->translation_size |= lzx_br_bits(br, 16);+				lzx_br_consume(br, 16);+			}+			/* FALL THROUGH */+		case ST_RD_BLOCK_TYPE:+			if (!lzx_br_read_ahead(strm, br, 3)) {+				ds->state = ST_RD_BLOCK_TYPE;+				if (last)+					goto failed;+				return (ARCHIVE_OK);+			}+			ds->block_type = lzx_br_bits(br, 3);+			lzx_br_consume(br, 3);+			/* Check a block type. */+			switch (ds->block_type) {+			case VERBATIM_BLOCK:+			case ALIGNED_OFFSET_BLOCK:+			case UNCOMPRESSED_BLOCK:+				break;+			default:+				goto failed;/* Invalid */+			}+			/* FALL THROUGH */+		case ST_RD_BLOCK_SIZE:+			if (!lzx_br_read_ahead(strm, br, 24)) {+				ds->state = ST_RD_BLOCK_SIZE;+				if (last)+					goto failed;+				return (ARCHIVE_OK);+			}+			ds->block_size = lzx_br_bits(br, 8);+			lzx_br_consume(br, 8);+			ds->block_size <<= 16;+			ds->block_size |= lzx_br_bits(br, 16);+			lzx_br_consume(br, 16);+			if (ds->block_size == 0)+				goto failed;+			ds->block_bytes_avail = ds->block_size;+			if (ds->block_type != UNCOMPRESSED_BLOCK) {+				if (ds->block_type == VERBATIM_BLOCK)+					ds->state = ST_RD_VERBATIM;+				else+					ds->state = ST_RD_ALIGNED_OFFSET;+				break;+			}+			/* FALL THROUGH */+		case ST_RD_ALIGNMENT:+			/*+			 * Handle an Uncompressed Block.+			 */+			/* Skip padding to align following field on+			 * 16-bit boundary. */+			if (lzx_br_is_unaligned(br))+				lzx_br_consume_unaligned_bits(br);+			else {+				if (lzx_br_read_ahead(strm, br, 16))+					lzx_br_consume(br, 16);+				else {+					ds->state = ST_RD_ALIGNMENT;+					if (last)+						goto failed;+					return (ARCHIVE_OK);+				}+			}+			/* Preparation to read repeated offsets R0,R1 and R2. */+			ds->rbytes_avail = 0;+			ds->state = ST_RD_R0;+			/* FALL THROUGH */+		case ST_RD_R0:+		case ST_RD_R1:+		case ST_RD_R2:+			do {+				uint16_t u16;+				/* Drain bits in the cache buffer of+				 * bit-stream. */+				if (lzx_br_has(br, 32)) {+					u16 = lzx_br_bits(br, 16);+					lzx_br_consume(br, 16);+					archive_le16enc(ds->rbytes, u16);+					u16 = lzx_br_bits(br, 16);+					lzx_br_consume(br, 16);+					archive_le16enc(ds->rbytes+2, u16);+					ds->rbytes_avail = 4;+				} else if (lzx_br_has(br, 16)) {+					u16 = lzx_br_bits(br, 16);+					lzx_br_consume(br, 16);+					archive_le16enc(ds->rbytes, u16);+					ds->rbytes_avail = 2;+				}+				if (ds->rbytes_avail < 4 && ds->br.have_odd) {+					ds->rbytes[ds->rbytes_avail++] =+					    ds->br.odd;+					ds->br.have_odd = 0;+				}+				while (ds->rbytes_avail < 4) {+					if (strm->avail_in <= 0) {+						if (last)+							goto failed;+						return (ARCHIVE_OK);+					}+					ds->rbytes[ds->rbytes_avail++] =+					    *strm->next_in++;+					strm->avail_in--;+				}+				ds->rbytes_avail = 0;+				if (ds->state == ST_RD_R0) {+					ds->r0 = archive_le32dec(ds->rbytes);+					if (ds->r0 < 0)+						goto failed;+					ds->state = ST_RD_R1;+				} else if (ds->state == ST_RD_R1) {+					ds->r1 = archive_le32dec(ds->rbytes);+					if (ds->r1 < 0)+						goto failed;+					ds->state = ST_RD_R2;+				} else if (ds->state == ST_RD_R2) {+					ds->r2 = archive_le32dec(ds->rbytes);+					if (ds->r2 < 0)+						goto failed;+					/* We've gotten all repeated offsets. */+					ds->state = ST_COPY_UNCOMP1;+				}+			} while (ds->state != ST_COPY_UNCOMP1);+			/* FALL THROUGH */+		case ST_COPY_UNCOMP1:+			/*+			 * Copy bytes form next_in to next_out directly.+			 */+			while (ds->block_bytes_avail) {+				int l;++				if (strm->avail_out <= 0)+					/* Output buffer is empty. */+					return (ARCHIVE_OK);+				if (strm->avail_in <= 0) {+					/* Input buffer is empty. */+					if (last)+						goto failed;+					return (ARCHIVE_OK);+				}+				l = (int)ds->block_bytes_avail;+				if (l > ds->w_size - ds->w_pos)+					l = ds->w_size - ds->w_pos;+				if (l > strm->avail_out)+					l = (int)strm->avail_out;+				if (l > strm->avail_in)+					l = (int)strm->avail_in;+				memcpy(strm->next_out, strm->next_in, l);+				memcpy(&(ds->w_buff[ds->w_pos]),+				    strm->next_in, l);+				strm->next_in += l;+				strm->avail_in -= l;+				strm->next_out += l;+				strm->avail_out -= l;+				strm->total_out += l;+				ds->w_pos = (ds->w_pos + l) & ds->w_mask;+				ds->block_bytes_avail -= l;+			}+			/* FALL THROUGH */+		case ST_COPY_UNCOMP2:+			/* Re-align; skip padding byte. */+			if (ds->block_size & 1) {+				if (strm->avail_in <= 0) {+					/* Input buffer is empty. */+					ds->state = ST_COPY_UNCOMP2;+					if (last)+						goto failed;+					return (ARCHIVE_OK);+				}+				strm->next_in++;+				strm->avail_in --;+			}+			/* This block ended. */+			ds->state = ST_RD_BLOCK_TYPE;+			return (ARCHIVE_EOF);+			/********************/+		case ST_RD_ALIGNED_OFFSET:+			/*+			 * Read Aligned offset tree.+			 */+			if (!lzx_br_read_ahead(strm, br, 3 * ds->at.len_size)) {+				ds->state = ST_RD_ALIGNED_OFFSET;+				if (last)+					goto failed;+				return (ARCHIVE_OK);+			}+			memset(ds->at.freq, 0, sizeof(ds->at.freq));+			for (i = 0; i < ds->at.len_size; i++) {+				ds->at.bitlen[i] = lzx_br_bits(br, 3);+				ds->at.freq[ds->at.bitlen[i]]++;+				lzx_br_consume(br, 3);+			}+			if (!lzx_make_huffman_table(&ds->at))+				goto failed;+			/* FALL THROUGH */+		case ST_RD_VERBATIM:+			ds->loop = 0;+			/* FALL THROUGH */+		case ST_RD_PRE_MAIN_TREE_256:+			/*+			 * Read Pre-tree for first 256 elements of main tree.+			 */+			if (!lzx_read_pre_tree(strm)) {+				ds->state = ST_RD_PRE_MAIN_TREE_256;+				if (last)+					goto failed;+				return (ARCHIVE_OK);+			}+			if (!lzx_make_huffman_table(&ds->pt))+				goto failed;+			ds->loop = 0;+			/* FALL THROUGH */+		case ST_MAIN_TREE_256:+			/*+			 * Get path lengths of first 256 elements of main tree.+			 */+			r = lzx_read_bitlen(strm, &ds->mt, 256);+			if (r < 0)+				goto failed;+			else if (!r) {+				ds->state = ST_MAIN_TREE_256;+				if (last)+					goto failed;+				return (ARCHIVE_OK);+			}+			ds->loop = 0;+			/* FALL THROUGH */+		case ST_RD_PRE_MAIN_TREE_REM:+			/*+			 * Read Pre-tree for remaining elements of main tree.+			 */+			if (!lzx_read_pre_tree(strm)) {+				ds->state = ST_RD_PRE_MAIN_TREE_REM;+				if (last)+					goto failed;+				return (ARCHIVE_OK);+			}+			if (!lzx_make_huffman_table(&ds->pt))+				goto failed;+			ds->loop = 256;+			/* FALL THROUGH */+		case ST_MAIN_TREE_REM:+			/*+			 * Get path lengths of remaining elements of main tree.+			 */+			r = lzx_read_bitlen(strm, &ds->mt, -1);+			if (r < 0)+				goto failed;+			else if (!r) {+				ds->state = ST_MAIN_TREE_REM;+				if (last)+					goto failed;+				return (ARCHIVE_OK);+			}+			if (!lzx_make_huffman_table(&ds->mt))+				goto failed;+			ds->loop = 0;+			/* FALL THROUGH */+		case ST_RD_PRE_LENGTH_TREE:+			/*+			 * Read Pre-tree for remaining elements of main tree.+			 */+			if (!lzx_read_pre_tree(strm)) {+				ds->state = ST_RD_PRE_LENGTH_TREE;+				if (last)+					goto failed;+				return (ARCHIVE_OK);+			}+			if (!lzx_make_huffman_table(&ds->pt))+				goto failed;+			ds->loop = 0;+			/* FALL THROUGH */+		case ST_LENGTH_TREE:+			/*+			 * Get path lengths of remaining elements of main tree.+			 */+			r = lzx_read_bitlen(strm, &ds->lt, -1);+			if (r < 0)+				goto failed;+			else if (!r) {+				ds->state = ST_LENGTH_TREE;+				if (last)+					goto failed;+				return (ARCHIVE_OK);+			}+			if (!lzx_make_huffman_table(&ds->lt))+				goto failed;+			ds->state = ST_MAIN;+			return (100);+		}+	}+failed:+	return (ds->error = ARCHIVE_FAILED);+}++static int+lzx_decode_blocks(struct lzx_stream *strm, int last)+{+	struct lzx_dec *ds = strm->ds;+	struct lzx_br bre = ds->br;+	struct huffman *at = &(ds->at), *lt = &(ds->lt), *mt = &(ds->mt);+	const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl;+	unsigned char *noutp = strm->next_out;+	unsigned char *endp = noutp + strm->avail_out;+	unsigned char *w_buff = ds->w_buff;+	unsigned char *at_bitlen = at->bitlen;+	unsigned char *lt_bitlen = lt->bitlen;+	unsigned char *mt_bitlen = mt->bitlen;+	size_t block_bytes_avail = ds->block_bytes_avail;+	int at_max_bits = at->max_bits;+	int lt_max_bits = lt->max_bits;+	int mt_max_bits = mt->max_bits;+	int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos;+	int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;+	int length_header = ds->length_header;+	int offset_bits = ds->offset_bits;+	int position_slot = ds->position_slot;+	int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2;+	int state = ds->state;+	char block_type = ds->block_type;++	for (;;) {+		switch (state) {+		case ST_MAIN:+			for (;;) {+				if (block_bytes_avail == 0) {+					/* This block ended. */+					ds->state = ST_RD_BLOCK_TYPE;+					ds->br = bre;+					ds->block_bytes_avail =+					    block_bytes_avail;+					ds->copy_len = copy_len;+					ds->copy_pos = copy_pos;+					ds->length_header = length_header;+					ds->position_slot = position_slot;+					ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;+					ds->w_pos = w_pos;+					strm->avail_out = endp - noutp;+					return (ARCHIVE_EOF);+				}+				if (noutp >= endp)+					/* Output buffer is empty. */+					goto next_data;++				if (!lzx_br_read_ahead(strm, &bre,+				    mt_max_bits)) {+					if (!last)+						goto next_data;+					/* Remaining bits are less than+					 * maximum bits(mt.max_bits) but maybe+					 * it still remains as much as we need,+					 * so we should try to use it with+					 * dummy bits. */+					c = lzx_decode_huffman(mt,+					      lzx_br_bits_forced(+				 	        &bre, mt_max_bits));+					lzx_br_consume(&bre, mt_bitlen[c]);+					if (!lzx_br_has(&bre, 0))+						goto failed;/* Over read. */+				} else {+					c = lzx_decode_huffman(mt,+					      lzx_br_bits(&bre, mt_max_bits));+					lzx_br_consume(&bre, mt_bitlen[c]);+				}+				if (c > UCHAR_MAX)+					break;+				/*+				 * 'c' is exactly literal code.+				 */+				/* Save a decoded code to reference it+				 * afterward. */+				w_buff[w_pos] = c;+				w_pos = (w_pos + 1) & w_mask;+				/* Store the decoded code to output buffer. */+				*noutp++ = c;+				block_bytes_avail--;+			}+			/*+			 * Get a match code, its length and offset.+			 */+			c -= UCHAR_MAX + 1;+			length_header = c & 7;+			position_slot = c >> 3;+			/* FALL THROUGH */+		case ST_LENGTH:+			/*+			 * Get a length.+			 */+			if (length_header == 7) {+				if (!lzx_br_read_ahead(strm, &bre,+				    lt_max_bits)) {+					if (!last) {+						state = ST_LENGTH;+						goto next_data;+					}+					c = lzx_decode_huffman(lt,+					      lzx_br_bits_forced(+					        &bre, lt_max_bits));+					lzx_br_consume(&bre, lt_bitlen[c]);+					if (!lzx_br_has(&bre, 0))+						goto failed;/* Over read. */+				} else {+					c = lzx_decode_huffman(lt,+					    lzx_br_bits(&bre, lt_max_bits));+					lzx_br_consume(&bre, lt_bitlen[c]);+				}+				copy_len = c + 7 + 2;+			} else+				copy_len = length_header + 2;+			if ((size_t)copy_len > block_bytes_avail)+				goto failed;+			/*+			 * Get an offset.+			 */+			switch (position_slot) {+			case 0: /* Use repeated offset 0. */+				copy_pos = r0;+				state = ST_REAL_POS;+				continue;+			case 1: /* Use repeated offset 1. */+				copy_pos = r1;+				/* Swap repeated offset. */+				r1 = r0;+				r0 = copy_pos;+				state = ST_REAL_POS;+				continue;+			case 2: /* Use repeated offset 2. */+				copy_pos = r2;+				/* Swap repeated offset. */+				r2 = r0;+				r0 = copy_pos;+				state = ST_REAL_POS;+				continue;+			default:+				offset_bits =+				    pos_tbl[position_slot].footer_bits;+				break;+			}+			/* FALL THROUGH */+		case ST_OFFSET:+			/*+			 * Get the offset, which is a distance from+			 * current window position.+			 */+			if (block_type == ALIGNED_OFFSET_BLOCK &&+			    offset_bits >= 3) {+				int offbits = offset_bits - 3;++				if (!lzx_br_read_ahead(strm, &bre, offbits)) {+					state = ST_OFFSET;+					if (last)+						goto failed;+					goto next_data;+				}+				copy_pos = lzx_br_bits(&bre, offbits) << 3;++				/* Get an aligned number. */+				if (!lzx_br_read_ahead(strm, &bre,+				    offbits + at_max_bits)) {+					if (!last) {+						state = ST_OFFSET;+						goto next_data;+					}+					lzx_br_consume(&bre, offbits);+					c = lzx_decode_huffman(at,+					      lzx_br_bits_forced(&bre,+					        at_max_bits));+					lzx_br_consume(&bre, at_bitlen[c]);+					if (!lzx_br_has(&bre, 0))+						goto failed;/* Over read. */+				} else {+					lzx_br_consume(&bre, offbits);+					c = lzx_decode_huffman(at,+					      lzx_br_bits(&bre, at_max_bits));+					lzx_br_consume(&bre, at_bitlen[c]);+				}+				/* Add an aligned number. */+				copy_pos += c;+			} else {+				if (!lzx_br_read_ahead(strm, &bre,+				    offset_bits)) {+					state = ST_OFFSET;+					if (last)+						goto failed;+					goto next_data;+				}+				copy_pos = lzx_br_bits(&bre, offset_bits);+				lzx_br_consume(&bre, offset_bits);+			}+			copy_pos += pos_tbl[position_slot].base -2;++			/* Update repeated offset LRU queue. */+			r2 = r1;+			r1 = r0;+			r0 = copy_pos;+			/* FALL THROUGH */+		case ST_REAL_POS:+			/*+			 * Compute a real position in window.+			 */+			copy_pos = (w_pos - copy_pos) & w_mask;+			/* FALL THROUGH */+		case ST_COPY:+			/*+			 * Copy several bytes as extracted data from the window+			 * into the output buffer.+			 */+			for (;;) {+				const unsigned char *s;+				int l;++				l = copy_len;+				if (copy_pos > w_pos) {+					if (l > w_size - copy_pos)+						l = w_size - copy_pos;+				} else {+					if (l > w_size - w_pos)+						l = w_size - w_pos;+				}+				if (noutp + l >= endp)+					l = (int)(endp - noutp);+				s = w_buff + copy_pos;+				if (l >= 8 && ((copy_pos + l < w_pos)+				  || (w_pos + l < copy_pos))) {+					memcpy(w_buff + w_pos, s, l);+					memcpy(noutp, s, l);+				} else {+					unsigned char *d;+					int li;++					d = w_buff + w_pos;+					for (li = 0; li < l; li++)+						noutp[li] = d[li] = s[li];+				}+				noutp += l;+				copy_pos = (copy_pos + l) & w_mask;+				w_pos = (w_pos + l) & w_mask;+				block_bytes_avail -= l;+				if (copy_len <= l)+					/* A copy of current pattern ended. */+					break;+				copy_len -= l;+				if (noutp >= endp) {+					/* Output buffer is empty. */+					state = ST_COPY;+					goto next_data;+				}+			}+			state = ST_MAIN;+			break;+		}+	}+failed:+	return (ds->error = ARCHIVE_FAILED);+next_data:+	ds->br = bre;+	ds->block_bytes_avail = block_bytes_avail;+	ds->copy_len = copy_len;+	ds->copy_pos = copy_pos;+	ds->length_header = length_header;+	ds->offset_bits = offset_bits;+	ds->position_slot = position_slot;+	ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;+	ds->state = state;+	ds->w_pos = w_pos;+	strm->avail_out = endp - noutp;+	return (ARCHIVE_OK);+}++static int+lzx_read_pre_tree(struct lzx_stream *strm)+{+	struct lzx_dec *ds = strm->ds;+	struct lzx_br *br = &(ds->br);+	int i;++	if (ds->loop == 0)+		memset(ds->pt.freq, 0, sizeof(ds->pt.freq));+	for (i = ds->loop; i < ds->pt.len_size; i++) {+		if (!lzx_br_read_ahead(strm, br, 4)) {+			ds->loop = i;+			return (0);+		}+		ds->pt.bitlen[i] = lzx_br_bits(br, 4);+		ds->pt.freq[ds->pt.bitlen[i]]++;+		lzx_br_consume(br, 4);+	}+	ds->loop = i;+	return (1);+}++/*+ * Read a bunch of bit-lengths from pre-tree.+ */+static int+lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end)+{+	struct lzx_dec *ds = strm->ds;+	struct lzx_br *br = &(ds->br);+	int c, i, j, ret, same;+	unsigned rbits;++	i = ds->loop;+	if (i == 0)+		memset(d->freq, 0, sizeof(d->freq));+	ret = 0;+	if (end < 0)+		end = d->len_size;+	while (i < end) {+		ds->loop = i;+		if (!lzx_br_read_ahead(strm, br, ds->pt.max_bits))+			goto getdata;+		rbits = lzx_br_bits(br, ds->pt.max_bits);+		c = lzx_decode_huffman(&(ds->pt), rbits);+		switch (c) {+		case 17:/* several zero lengths, from 4 to 19. */+			if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+4))+				goto getdata;+			lzx_br_consume(br, ds->pt.bitlen[c]);+			same = lzx_br_bits(br, 4) + 4;+			if (i + same > end)+				return (-1);/* Invalid */+			lzx_br_consume(br, 4);+			for (j = 0; j < same; j++)+				d->bitlen[i++] = 0;+			break;+		case 18:/* many zero lengths, from 20 to 51. */+			if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+5))+				goto getdata;+			lzx_br_consume(br, ds->pt.bitlen[c]);+			same = lzx_br_bits(br, 5) + 20;+			if (i + same > end)+				return (-1);/* Invalid */+			lzx_br_consume(br, 5);+			memset(d->bitlen + i, 0, same);+			i += same;+			break;+		case 19:/* a few same lengths. */+			if (!lzx_br_read_ahead(strm, br,+			    ds->pt.bitlen[c]+1+ds->pt.max_bits))+				goto getdata;+			lzx_br_consume(br, ds->pt.bitlen[c]);+			same = lzx_br_bits(br, 1) + 4;+			if (i + same > end)+				return (-1);+			lzx_br_consume(br, 1);+			rbits = lzx_br_bits(br, ds->pt.max_bits);+			c = lzx_decode_huffman(&(ds->pt), rbits);+			lzx_br_consume(br, ds->pt.bitlen[c]);+			c = (d->bitlen[i] - c + 17) % 17;+			if (c < 0)+				return (-1);/* Invalid */+			for (j = 0; j < same; j++)+				d->bitlen[i++] = c;+			d->freq[c] += same;+			break;+		default:+			lzx_br_consume(br, ds->pt.bitlen[c]);+			c = (d->bitlen[i] - c + 17) % 17;+			if (c < 0)+				return (-1);/* Invalid */+			d->freq[c]++;+			d->bitlen[i++] = c;+			break;+		}+	}+	ret = 1;+getdata:+	ds->loop = i;+	return (ret);+}++static int+lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)+{++	if (hf->bitlen == NULL || hf->len_size != (int)len_size) {+		free(hf->bitlen);+		hf->bitlen = calloc(len_size,  sizeof(hf->bitlen[0]));+		if (hf->bitlen == NULL)+			return (ARCHIVE_FATAL);+		hf->len_size = (int)len_size;+	} else+		memset(hf->bitlen, 0, len_size *  sizeof(hf->bitlen[0]));+	if (hf->tbl == NULL) {+		hf->tbl = malloc(((size_t)1 << tbl_bits) * sizeof(hf->tbl[0]));+		if (hf->tbl == NULL)+			return (ARCHIVE_FATAL);+		hf->tbl_bits = tbl_bits;+	}+	return (ARCHIVE_OK);+}++static void+lzx_huffman_free(struct huffman *hf)+{+	free(hf->bitlen);+	free(hf->tbl);+}++/*+ * Make a huffman coding table.+ */+static int+lzx_make_huffman_table(struct huffman *hf)+{+	uint16_t *tbl;+	const unsigned char *bitlen;+	int bitptn[17], weight[17];+	int i, maxbits = 0, ptn, tbl_size, w;+	int len_avail;++	/*+	 * Initialize bit patterns.+	 */+	ptn = 0;+	for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {+		bitptn[i] = ptn;+		weight[i] = w;+		if (hf->freq[i]) {+			ptn += hf->freq[i] * w;+			maxbits = i;+		}+	}+	if ((ptn & 0xffff) != 0 || maxbits > hf->tbl_bits)+		return (0);/* Invalid */++	hf->max_bits = maxbits;++	/*+	 * Cut out extra bits which we won't house in the table.+	 * This preparation reduces the same calculation in the for-loop+	 * making the table.+	 */+	if (maxbits < 16) {+		int ebits = 16 - maxbits;+		for (i = 1; i <= maxbits; i++) {+			bitptn[i] >>= ebits;+			weight[i] >>= ebits;+		}+	}++	/*+	 * Make the table.+	 */+	tbl_size = 1 << hf->tbl_bits;+	tbl = hf->tbl;+	bitlen = hf->bitlen;+	len_avail = hf->len_size;+	hf->tree_used = 0;+	for (i = 0; i < len_avail; i++) {+		uint16_t *p;+		int len, cnt;++		if (bitlen[i] == 0)+			continue;+		/* Get a bit pattern */+		len = bitlen[i];+		if (len > tbl_size)+			return (0);+		ptn = bitptn[len];+		cnt = weight[len];+		/* Calculate next bit pattern */+		if ((bitptn[len] = ptn + cnt) > tbl_size)+			return (0);/* Invalid */+		/* Update the table */+		p = &(tbl[ptn]);+		while (--cnt >= 0)+			p[cnt] = (uint16_t)i;+	}+	return (1);+}++static inline int+lzx_decode_huffman(struct huffman *hf, unsigned rbits)+{+	int c;+	c = hf->tbl[rbits];+	if (c < hf->len_size)+		return (c);+	return (0);+}
+ c/archive_read_support_format_cpio.c view
@@ -0,0 +1,1086 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_cpio.c 201163 2009-12-29 05:50:34Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+/* #include <stdint.h> */ /* See archive_platform.h */+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_read_private.h"++#define	bin_magic_offset 0+#define	bin_magic_size 2+#define	bin_dev_offset 2+#define	bin_dev_size 2+#define	bin_ino_offset 4+#define	bin_ino_size 2+#define	bin_mode_offset 6+#define	bin_mode_size 2+#define	bin_uid_offset 8+#define	bin_uid_size 2+#define	bin_gid_offset 10+#define	bin_gid_size 2+#define	bin_nlink_offset 12+#define	bin_nlink_size 2+#define	bin_rdev_offset 14+#define	bin_rdev_size 2+#define	bin_mtime_offset 16+#define	bin_mtime_size 4+#define	bin_namesize_offset 20+#define	bin_namesize_size 2+#define	bin_filesize_offset 22+#define	bin_filesize_size 4+#define	bin_header_size 26++#define	odc_magic_offset 0+#define	odc_magic_size 6+#define	odc_dev_offset 6+#define	odc_dev_size 6+#define	odc_ino_offset 12+#define	odc_ino_size 6+#define	odc_mode_offset 18+#define	odc_mode_size 6+#define	odc_uid_offset 24+#define	odc_uid_size 6+#define	odc_gid_offset 30+#define	odc_gid_size 6+#define	odc_nlink_offset 36+#define	odc_nlink_size 6+#define	odc_rdev_offset 42+#define	odc_rdev_size 6+#define	odc_mtime_offset 48+#define	odc_mtime_size 11+#define	odc_namesize_offset 59+#define	odc_namesize_size 6+#define	odc_filesize_offset 65+#define	odc_filesize_size 11+#define	odc_header_size 76++#define	newc_magic_offset 0+#define	newc_magic_size 6+#define	newc_ino_offset 6+#define	newc_ino_size 8+#define	newc_mode_offset 14+#define	newc_mode_size 8+#define	newc_uid_offset 22+#define	newc_uid_size 8+#define	newc_gid_offset 30+#define	newc_gid_size 8+#define	newc_nlink_offset 38+#define	newc_nlink_size 8+#define	newc_mtime_offset 46+#define	newc_mtime_size 8+#define	newc_filesize_offset 54+#define	newc_filesize_size 8+#define	newc_devmajor_offset 62+#define	newc_devmajor_size 8+#define	newc_devminor_offset 70+#define	newc_devminor_size 8+#define	newc_rdevmajor_offset 78+#define	newc_rdevmajor_size 8+#define	newc_rdevminor_offset 86+#define	newc_rdevminor_size 8+#define	newc_namesize_offset 94+#define	newc_namesize_size 8+#define	newc_checksum_offset 102+#define	newc_checksum_size 8+#define	newc_header_size 110++/*+ * An afio large ASCII header, which they named itself.+ * afio utility uses this header, if a file size is larger than 2G bytes+ * or inode/uid/gid is bigger than 65535(0xFFFF) or mtime is bigger than+ * 0x7fffffff, which we cannot record to odc header because of its limit.+ * If not, uses odc header.+ */+#define	afiol_magic_offset 0+#define	afiol_magic_size 6+#define	afiol_dev_offset 6+#define	afiol_dev_size 8	/* hex */+#define	afiol_ino_offset 14+#define	afiol_ino_size 16	/* hex */+#define	afiol_ino_m_offset 30	/* 'm' */+#define	afiol_mode_offset 31+#define	afiol_mode_size 6	/* oct */+#define	afiol_uid_offset 37+#define	afiol_uid_size 8	/* hex */+#define	afiol_gid_offset 45+#define	afiol_gid_size 8	/* hex */+#define	afiol_nlink_offset 53+#define	afiol_nlink_size 8	/* hex */+#define	afiol_rdev_offset 61+#define	afiol_rdev_size 8	/* hex */+#define	afiol_mtime_offset 69+#define	afiol_mtime_size 16	/* hex */+#define	afiol_mtime_n_offset 85	/* 'n' */+#define	afiol_namesize_offset 86+#define	afiol_namesize_size 4	/* hex */+#define	afiol_flag_offset 90+#define	afiol_flag_size 4	/* hex */+#define	afiol_xsize_offset 94+#define	afiol_xsize_size 4	/* hex */+#define	afiol_xsize_s_offset 98	/* 's' */+#define	afiol_filesize_offset 99+#define	afiol_filesize_size 16	/* hex */+#define	afiol_filesize_c_offset 115	/* ':' */+#define afiol_header_size 116+++struct links_entry {+        struct links_entry      *next;+        struct links_entry      *previous;+        unsigned int             links;+        dev_t                    dev;+        int64_t                  ino;+        char                    *name;+};++#define	CPIO_MAGIC   0x13141516+struct cpio {+	int			  magic;+	int			(*read_header)(struct archive_read *, struct cpio *,+				     struct archive_entry *, size_t *, size_t *);+	struct links_entry	 *links_head;+	int64_t			  entry_bytes_remaining;+	int64_t			  entry_bytes_unconsumed;+	int64_t			  entry_offset;+	int64_t			  entry_padding;++	struct archive_string_conv *opt_sconv;+	struct archive_string_conv *sconv_default;+	int			  init_default_conversion;+};++static int64_t	atol16(const char *, unsigned);+static int64_t	atol8(const char *, unsigned);+static int	archive_read_format_cpio_bid(struct archive_read *, int);+static int	archive_read_format_cpio_options(struct archive_read *,+		    const char *, const char *);+static int	archive_read_format_cpio_cleanup(struct archive_read *);+static int	archive_read_format_cpio_read_data(struct archive_read *,+		    const void **, size_t *, int64_t *);+static int	archive_read_format_cpio_read_header(struct archive_read *,+		    struct archive_entry *);+static int	archive_read_format_cpio_skip(struct archive_read *);+static int64_t	be4(const unsigned char *);+static int	find_odc_header(struct archive_read *);+static int	find_newc_header(struct archive_read *);+static int	header_bin_be(struct archive_read *, struct cpio *,+		    struct archive_entry *, size_t *, size_t *);+static int	header_bin_le(struct archive_read *, struct cpio *,+		    struct archive_entry *, size_t *, size_t *);+static int	header_newc(struct archive_read *, struct cpio *,+		    struct archive_entry *, size_t *, size_t *);+static int	header_odc(struct archive_read *, struct cpio *,+		    struct archive_entry *, size_t *, size_t *);+static int	header_afiol(struct archive_read *, struct cpio *,+		    struct archive_entry *, size_t *, size_t *);+static int	is_octal(const char *, size_t);+static int	is_hex(const char *, size_t);+static int64_t	le4(const unsigned char *);+static int	record_hardlink(struct archive_read *a,+		    struct cpio *cpio, struct archive_entry *entry);++int+archive_read_support_format_cpio(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct cpio *cpio;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_cpio");++	cpio = (struct cpio *)calloc(1, sizeof(*cpio));+	if (cpio == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");+		return (ARCHIVE_FATAL);+	}+	cpio->magic = CPIO_MAGIC;++	r = __archive_read_register_format(a,+	    cpio,+	    "cpio",+	    archive_read_format_cpio_bid,+	    archive_read_format_cpio_options,+	    archive_read_format_cpio_read_header,+	    archive_read_format_cpio_read_data,+	    archive_read_format_cpio_skip,+	    NULL,+	    archive_read_format_cpio_cleanup,+	    NULL,+	    NULL);++	if (r != ARCHIVE_OK)+		free(cpio);+	return (ARCHIVE_OK);+}+++static int+archive_read_format_cpio_bid(struct archive_read *a, int best_bid)+{+	const unsigned char *p;+	struct cpio *cpio;+	int bid;++	(void)best_bid; /* UNUSED */++	cpio = (struct cpio *)(a->format->data);++	if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)+		return (-1);++	bid = 0;+	if (memcmp(p, "070707", 6) == 0) {+		/* ASCII cpio archive (odc, POSIX.1) */+		cpio->read_header = header_odc;+		bid += 48;+		/*+		 * XXX TODO:  More verification; Could check that only octal+		 * digits appear in appropriate header locations. XXX+		 */+	} else if (memcmp(p, "070727", 6) == 0) {+		/* afio large ASCII cpio archive */+		cpio->read_header = header_odc;+		bid += 48;+		/*+		 * XXX TODO:  More verification; Could check that almost hex+		 * digits appear in appropriate header locations. XXX+		 */+	} else if (memcmp(p, "070701", 6) == 0) {+		/* ASCII cpio archive (SVR4 without CRC) */+		cpio->read_header = header_newc;+		bid += 48;+		/*+		 * XXX TODO:  More verification; Could check that only hex+		 * digits appear in appropriate header locations. XXX+		 */+	} else if (memcmp(p, "070702", 6) == 0) {+		/* ASCII cpio archive (SVR4 with CRC) */+		/* XXX TODO: Flag that we should check the CRC. XXX */+		cpio->read_header = header_newc;+		bid += 48;+		/*+		 * XXX TODO:  More verification; Could check that only hex+		 * digits appear in appropriate header locations. XXX+		 */+	} else if (p[0] * 256 + p[1] == 070707) {+		/* big-endian binary cpio archives */+		cpio->read_header = header_bin_be;+		bid += 16;+		/* Is more verification possible here? */+	} else if (p[0] + p[1] * 256 == 070707) {+		/* little-endian binary cpio archives */+		cpio->read_header = header_bin_le;+		bid += 16;+		/* Is more verification possible here? */+	} else+		return (ARCHIVE_WARN);++	return (bid);+}++static int+archive_read_format_cpio_options(struct archive_read *a,+    const char *key, const char *val)+{+	struct cpio *cpio;+	int ret = ARCHIVE_FAILED;++	cpio = (struct cpio *)(a->format->data);+	if (strcmp(key, "compat-2x")  == 0) {+		/* Handle filenames as libarchive 2.x */+		cpio->init_default_conversion = (val != NULL)?1:0;+		return (ARCHIVE_OK);+	} else if (strcmp(key, "hdrcharset")  == 0) {+		if (val == NULL || val[0] == 0)+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "cpio: hdrcharset option needs a character-set name");+		else {+			cpio->opt_sconv =+			    archive_string_conversion_from_charset(+				&a->archive, val, 0);+			if (cpio->opt_sconv != NULL)+				ret = ARCHIVE_OK;+			else+				ret = ARCHIVE_FATAL;+		}+		return (ret);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+archive_read_format_cpio_read_header(struct archive_read *a,+    struct archive_entry *entry)+{+	struct cpio *cpio;+	const void *h, *hl;+	struct archive_string_conv *sconv;+	size_t namelength;+	size_t name_pad;+	int r;++	cpio = (struct cpio *)(a->format->data);+	sconv = cpio->opt_sconv;+	if (sconv == NULL) {+		if (!cpio->init_default_conversion) {+			cpio->sconv_default =+			    archive_string_default_conversion_for_read(+			      &(a->archive));+			cpio->init_default_conversion = 1;+		}+		sconv = cpio->sconv_default;+	}+	+	r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad));++	if (r < ARCHIVE_WARN)+		return (r);++	/* Read name from buffer. */+	h = __archive_read_ahead(a, namelength + name_pad, NULL);+	if (h == NULL)+	    return (ARCHIVE_FATAL);+	if (archive_entry_copy_pathname_l(entry,+	    (const char *)h, namelength, sconv) != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Pathname can't be converted from %s to current locale.",+		    archive_string_conversion_charset_name(sconv));+		r = ARCHIVE_WARN;+	}+	cpio->entry_offset = 0;++	__archive_read_consume(a, namelength + name_pad);++	/* If this is a symlink, read the link contents. */+	if (archive_entry_filetype(entry) == AE_IFLNK) {+		if (cpio->entry_bytes_remaining > 1024 * 1024) {+			archive_set_error(&a->archive, ENOMEM,+			    "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte");+			return (ARCHIVE_FATAL);+		}+		hl = __archive_read_ahead(a,+			(size_t)cpio->entry_bytes_remaining, NULL);+		if (hl == NULL)+			return (ARCHIVE_FATAL);+		if (archive_entry_copy_symlink_l(entry, (const char *)hl,+		    (size_t)cpio->entry_bytes_remaining, sconv) != 0) {+			if (errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for Linkname");+				return (ARCHIVE_FATAL);+			}+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Linkname can't be converted from %s to "+			    "current locale.",+			    archive_string_conversion_charset_name(sconv));+			r = ARCHIVE_WARN;+		}+		__archive_read_consume(a, cpio->entry_bytes_remaining);+		cpio->entry_bytes_remaining = 0;+	}++	/* XXX TODO: If the full mode is 0160200, then this is a Solaris+	 * ACL description for the following entry.  Read this body+	 * and parse it as a Solaris-style ACL, then read the next+	 * header.  XXX */++	/* Compare name to "TRAILER!!!" to test for end-of-archive. */+	if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!",+	    11) == 0) {+		/* TODO: Store file location of start of block. */+		archive_clear_error(&a->archive);+		return (ARCHIVE_EOF);+	}++	/* Detect and record hardlinks to previously-extracted entries. */+	if (record_hardlink(a, cpio, entry) != ARCHIVE_OK) {+		return (ARCHIVE_FATAL);+	}++	return (r);+}++static int+archive_read_format_cpio_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	ssize_t bytes_read;+	struct cpio *cpio;++	cpio = (struct cpio *)(a->format->data);++	if (cpio->entry_bytes_unconsumed) {+		__archive_read_consume(a, cpio->entry_bytes_unconsumed);+		cpio->entry_bytes_unconsumed = 0;+	}++	if (cpio->entry_bytes_remaining > 0) {+		*buff = __archive_read_ahead(a, 1, &bytes_read);+		if (bytes_read <= 0)+			return (ARCHIVE_FATAL);+		if (bytes_read > cpio->entry_bytes_remaining)+			bytes_read = (ssize_t)cpio->entry_bytes_remaining;+		*size = bytes_read;+		cpio->entry_bytes_unconsumed = bytes_read;+		*offset = cpio->entry_offset;+		cpio->entry_offset += bytes_read;+		cpio->entry_bytes_remaining -= bytes_read;+		return (ARCHIVE_OK);+	} else {+		if (cpio->entry_padding !=+			__archive_read_consume(a, cpio->entry_padding)) {+			return (ARCHIVE_FATAL);+		}+		cpio->entry_padding = 0;+		*buff = NULL;+		*size = 0;+		*offset = cpio->entry_offset;+		return (ARCHIVE_EOF);+	}+}++static int+archive_read_format_cpio_skip(struct archive_read *a)+{+	struct cpio *cpio = (struct cpio *)(a->format->data);+	int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding ++		cpio->entry_bytes_unconsumed;++	if (to_skip != __archive_read_consume(a, to_skip)) {+		return (ARCHIVE_FATAL);+	}+	cpio->entry_bytes_remaining = 0;+	cpio->entry_padding = 0;+	cpio->entry_bytes_unconsumed = 0;+	return (ARCHIVE_OK);+}++/*+ * Skip forward to the next cpio newc header by searching for the+ * 07070[12] string.  This should be generalized and merged with+ * find_odc_header below.+ */+static int+is_hex(const char *p, size_t len)+{+	while (len-- > 0) {+		if ((*p >= '0' && *p <= '9')+		    || (*p >= 'a' && *p <= 'f')+		    || (*p >= 'A' && *p <= 'F'))+			++p;+		else+			return (0);+	}+	return (1);+}++static int+find_newc_header(struct archive_read *a)+{+	const void *h;+	const char *p, *q;+	size_t skip, skipped = 0;+	ssize_t bytes;++	for (;;) {+		h = __archive_read_ahead(a, newc_header_size, &bytes);+		if (h == NULL)+			return (ARCHIVE_FATAL);+		p = h;+		q = p + bytes;++		/* Try the typical case first, then go into the slow search.*/+		if (memcmp("07070", p, 5) == 0+		    && (p[5] == '1' || p[5] == '2')+		    && is_hex(p, newc_header_size))+			return (ARCHIVE_OK);++		/*+		 * Scan ahead until we find something that looks+		 * like a newc header.+		 */+		while (p + newc_header_size <= q) {+			switch (p[5]) {+			case '1':+			case '2':+				if (memcmp("07070", p, 5) == 0+				    && is_hex(p, newc_header_size)) {+					skip = p - (const char *)h;+					__archive_read_consume(a, skip);+					skipped += skip;+					if (skipped > 0) {+						archive_set_error(&a->archive,+						    0,+						    "Skipped %d bytes before "+						    "finding valid header",+						    (int)skipped);+						return (ARCHIVE_WARN);+					}+					return (ARCHIVE_OK);+				}+				p += 2;+				break;+			case '0':+				p++;+				break;+			default:+				p += 6;+				break;+			}+		}+		skip = p - (const char *)h;+		__archive_read_consume(a, skip);+		skipped += skip;+	}+}++static int+header_newc(struct archive_read *a, struct cpio *cpio,+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)+{+	const void *h;+	const char *header;+	int r;++	r = find_newc_header(a);+	if (r < ARCHIVE_WARN)+		return (r);++	/* Read fixed-size portion of header. */+	h = __archive_read_ahead(a, newc_header_size, NULL);+	if (h == NULL)+	    return (ARCHIVE_FATAL);++	/* Parse out hex fields. */+	header = (const char *)h;++	if (memcmp(header + newc_magic_offset, "070701", 6) == 0) {+		a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;+		a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)";+	} else if (memcmp(header + newc_magic_offset, "070702", 6) == 0) {+		a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC;+		a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)";+	} else {+		/* TODO: Abort here? */+	}++	archive_entry_set_devmajor(entry,+		(dev_t)atol16(header + newc_devmajor_offset, newc_devmajor_size));+	archive_entry_set_devminor(entry, +		(dev_t)atol16(header + newc_devminor_offset, newc_devminor_size));+	archive_entry_set_ino(entry, atol16(header + newc_ino_offset, newc_ino_size));+	archive_entry_set_mode(entry, +		(mode_t)atol16(header + newc_mode_offset, newc_mode_size));+	archive_entry_set_uid(entry, atol16(header + newc_uid_offset, newc_uid_size));+	archive_entry_set_gid(entry, atol16(header + newc_gid_offset, newc_gid_size));+	archive_entry_set_nlink(entry,+		(unsigned int)atol16(header + newc_nlink_offset, newc_nlink_size));+	archive_entry_set_rdevmajor(entry,+		(dev_t)atol16(header + newc_rdevmajor_offset, newc_rdevmajor_size));+	archive_entry_set_rdevminor(entry,+		(dev_t)atol16(header + newc_rdevminor_offset, newc_rdevminor_size));+	archive_entry_set_mtime(entry, atol16(header + newc_mtime_offset, newc_mtime_size), 0);+	*namelength = (size_t)atol16(header + newc_namesize_offset, newc_namesize_size);+	/* Pad name to 2 more than a multiple of 4. */+	*name_pad = (2 - *namelength) & 3;++	/* Make sure that the padded name length fits into size_t. */+	if (*name_pad > SIZE_MAX - *namelength) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "cpio archive has invalid namelength");+		return (ARCHIVE_FATAL);+	}++	/*+	 * Note: entry_bytes_remaining is at least 64 bits and+	 * therefore guaranteed to be big enough for a 33-bit file+	 * size.+	 */+	cpio->entry_bytes_remaining =+	    atol16(header + newc_filesize_offset, newc_filesize_size);+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);+	/* Pad file contents to a multiple of 4. */+	cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;+	__archive_read_consume(a, newc_header_size);+	return (r);+}++/*+ * Skip forward to the next cpio odc header by searching for the+ * 070707 string.  This is a hand-optimized search that could+ * probably be easily generalized to handle all character-based+ * cpio variants.+ */+static int+is_octal(const char *p, size_t len)+{+	while (len-- > 0) {+		if (*p < '0' || *p > '7')+			return (0);+	        ++p;+	}+	return (1);+}++static int+is_afio_large(const char *h, size_t len)+{+	if (len < afiol_header_size)+		return (0);+	if (h[afiol_ino_m_offset] != 'm'+	    || h[afiol_mtime_n_offset] != 'n'+	    || h[afiol_xsize_s_offset] != 's'+	    || h[afiol_filesize_c_offset] != ':')+		return (0);+	if (!is_hex(h + afiol_dev_offset, afiol_ino_m_offset - afiol_dev_offset))+		return (0);+	if (!is_hex(h + afiol_mode_offset, afiol_mtime_n_offset - afiol_mode_offset))+		return (0);+	if (!is_hex(h + afiol_namesize_offset, afiol_xsize_s_offset - afiol_namesize_offset))+		return (0);+	if (!is_hex(h + afiol_filesize_offset, afiol_filesize_size))+		return (0);+	return (1);+}++static int+find_odc_header(struct archive_read *a)+{+	const void *h;+	const char *p, *q;+	size_t skip, skipped = 0;+	ssize_t bytes;++	for (;;) {+		h = __archive_read_ahead(a, odc_header_size, &bytes);+		if (h == NULL)+			return (ARCHIVE_FATAL);+		p = h;+		q = p + bytes;++		/* Try the typical case first, then go into the slow search.*/+		if (memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size))+			return (ARCHIVE_OK);+		if (memcmp("070727", p, 6) == 0 && is_afio_large(p, bytes)) {+			a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE;+			return (ARCHIVE_OK);+		}++		/*+		 * Scan ahead until we find something that looks+		 * like an odc header.+		 */+		while (p + odc_header_size <= q) {+			switch (p[5]) {+			case '7':+				if ((memcmp("070707", p, 6) == 0+				    && is_octal(p, odc_header_size))+				    || (memcmp("070727", p, 6) == 0+				        && is_afio_large(p, q - p))) {+					skip = p - (const char *)h;+					__archive_read_consume(a, skip);+					skipped += skip;+					if (p[4] == '2')+						a->archive.archive_format =+						    ARCHIVE_FORMAT_CPIO_AFIO_LARGE;+					if (skipped > 0) {+						archive_set_error(&a->archive,+						    0,+						    "Skipped %d bytes before "+						    "finding valid header",+						    (int)skipped);+						return (ARCHIVE_WARN);+					}+					return (ARCHIVE_OK);+				}+				p += 2;+				break;+			case '0':+				p++;+				break;+			default:+				p += 6;+				break;+			}+		}+		skip = p - (const char *)h;+		__archive_read_consume(a, skip);+		skipped += skip;+	}+}++static int+header_odc(struct archive_read *a, struct cpio *cpio,+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)+{+	const void *h;+	int r;+	const char *header;++	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;+	a->archive.archive_format_name = "POSIX octet-oriented cpio";++	/* Find the start of the next header. */+	r = find_odc_header(a);+	if (r < ARCHIVE_WARN)+		return (r);++	if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_AFIO_LARGE) {+		int r2 = (header_afiol(a, cpio, entry, namelength, name_pad));+		if (r2 == ARCHIVE_OK)+			return (r);+		else+			return (r2);+	}++	/* Read fixed-size portion of header. */+	h = __archive_read_ahead(a, odc_header_size, NULL);+	if (h == NULL)+	    return (ARCHIVE_FATAL);++	/* Parse out octal fields. */+	header = (const char *)h;++	archive_entry_set_dev(entry, +		(dev_t)atol8(header + odc_dev_offset, odc_dev_size));+	archive_entry_set_ino(entry, atol8(header + odc_ino_offset, odc_ino_size));+	archive_entry_set_mode(entry, +		(mode_t)atol8(header + odc_mode_offset, odc_mode_size));+	archive_entry_set_uid(entry, atol8(header + odc_uid_offset, odc_uid_size));+	archive_entry_set_gid(entry, atol8(header + odc_gid_offset, odc_gid_size));+	archive_entry_set_nlink(entry, +		(unsigned int)atol8(header + odc_nlink_offset, odc_nlink_size));+	archive_entry_set_rdev(entry,+		(dev_t)atol8(header + odc_rdev_offset, odc_rdev_size));+	archive_entry_set_mtime(entry, atol8(header + odc_mtime_offset, odc_mtime_size), 0);+	*namelength = (size_t)atol8(header + odc_namesize_offset, odc_namesize_size);+	*name_pad = 0; /* No padding of filename. */++	/*+	 * Note: entry_bytes_remaining is at least 64 bits and+	 * therefore guaranteed to be big enough for a 33-bit file+	 * size.+	 */+	cpio->entry_bytes_remaining =+	    atol8(header + odc_filesize_offset, odc_filesize_size);+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);+	cpio->entry_padding = 0;+	__archive_read_consume(a, odc_header_size);+	return (r);+}++/*+ * NOTE: if a filename suffix is ".z", it is the file gziped by afio.+ * it would be nice that we can show uncompressed file size and we can+ * uncompressed file contents automatically, unfortunately we have nothing+ * to get a uncompressed file size while reading each header. It means+ * we also cannot uncompress file contents under our framework.+ */+static int+header_afiol(struct archive_read *a, struct cpio *cpio,+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)+{+	const void *h;+	const char *header;++	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE;+	a->archive.archive_format_name = "afio large ASCII";++	/* Read fixed-size portion of header. */+	h = __archive_read_ahead(a, afiol_header_size, NULL);+	if (h == NULL)+	    return (ARCHIVE_FATAL);++	/* Parse out octal fields. */+	header = (const char *)h;++	archive_entry_set_dev(entry, +		(dev_t)atol16(header + afiol_dev_offset, afiol_dev_size));+	archive_entry_set_ino(entry, atol16(header + afiol_ino_offset, afiol_ino_size));+	archive_entry_set_mode(entry,+		(mode_t)atol8(header + afiol_mode_offset, afiol_mode_size));+	archive_entry_set_uid(entry, atol16(header + afiol_uid_offset, afiol_uid_size));+	archive_entry_set_gid(entry, atol16(header + afiol_gid_offset, afiol_gid_size));+	archive_entry_set_nlink(entry,+		(unsigned int)atol16(header + afiol_nlink_offset, afiol_nlink_size));+	archive_entry_set_rdev(entry,+		(dev_t)atol16(header + afiol_rdev_offset, afiol_rdev_size));+	archive_entry_set_mtime(entry, atol16(header + afiol_mtime_offset, afiol_mtime_size), 0);+	*namelength = (size_t)atol16(header + afiol_namesize_offset, afiol_namesize_size);+	*name_pad = 0; /* No padding of filename. */++	cpio->entry_bytes_remaining =+	    atol16(header + afiol_filesize_offset, afiol_filesize_size);+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);+	cpio->entry_padding = 0;+	__archive_read_consume(a, afiol_header_size);+	return (ARCHIVE_OK);+}+++static int+header_bin_le(struct archive_read *a, struct cpio *cpio,+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)+{+	const void *h;+	const unsigned char *header;++	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE;+	a->archive.archive_format_name = "cpio (little-endian binary)";++	/* Read fixed-size portion of header. */+	h = __archive_read_ahead(a, bin_header_size, NULL);+	if (h == NULL) {+	    archive_set_error(&a->archive, 0,+		"End of file trying to read next cpio header");+	    return (ARCHIVE_FATAL);+	}++	/* Parse out binary fields. */+	header = (const unsigned char *)h;++	archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256);+	archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256);+	archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256);+	archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256);+	archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256);+	archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256);+	archive_entry_set_rdev(entry, header[bin_rdev_offset] + header[bin_rdev_offset + 1] * 256);+	archive_entry_set_mtime(entry, le4(header + bin_mtime_offset), 0);+	*namelength = header[bin_namesize_offset] + header[bin_namesize_offset + 1] * 256;+	*name_pad = *namelength & 1; /* Pad to even. */++	cpio->entry_bytes_remaining = le4(header + bin_filesize_offset);+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);+	cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */+	__archive_read_consume(a, bin_header_size);+	return (ARCHIVE_OK);+}++static int+header_bin_be(struct archive_read *a, struct cpio *cpio,+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)+{+	const void *h;+	const unsigned char *header;++	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE;+	a->archive.archive_format_name = "cpio (big-endian binary)";++	/* Read fixed-size portion of header. */+	h = __archive_read_ahead(a, bin_header_size, NULL);+	if (h == NULL) {+	    archive_set_error(&a->archive, 0,+		"End of file trying to read next cpio header");+	    return (ARCHIVE_FATAL);+	}++	/* Parse out binary fields. */+	header = (const unsigned char *)h;++	archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]);+	archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]);+	archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]);+	archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]);+	archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]);+	archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]);+	archive_entry_set_rdev(entry, header[bin_rdev_offset] * 256 + header[bin_rdev_offset + 1]);+	archive_entry_set_mtime(entry, be4(header + bin_mtime_offset), 0);+	*namelength = header[bin_namesize_offset] * 256 + header[bin_namesize_offset + 1];+	*name_pad = *namelength & 1; /* Pad to even. */++	cpio->entry_bytes_remaining = be4(header + bin_filesize_offset);+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);+	cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */+	    __archive_read_consume(a, bin_header_size);+	return (ARCHIVE_OK);+}++static int+archive_read_format_cpio_cleanup(struct archive_read *a)+{+	struct cpio *cpio;++	cpio = (struct cpio *)(a->format->data);+        /* Free inode->name map */+        while (cpio->links_head != NULL) {+                struct links_entry *lp = cpio->links_head->next;++                free(cpio->links_head->name);+                free(cpio->links_head);+                cpio->links_head = lp;+        }+	free(cpio);+	(a->format->data) = NULL;+	return (ARCHIVE_OK);+}++static int64_t+le4(const unsigned char *p)+{+	return ((p[0] << 16) + (((int64_t)p[1]) << 24) + (p[2] << 0) + (p[3] << 8));+}+++static int64_t+be4(const unsigned char *p)+{+	return ((((int64_t)p[0]) << 24) + (p[1] << 16) + (p[2] << 8) + (p[3]));+}++/*+ * Note that this implementation does not (and should not!) obey+ * locale settings; you cannot simply substitute strtol here, since+ * it does obey locale.+ */+static int64_t+atol8(const char *p, unsigned char_cnt)+{+	int64_t l;+	int digit;++	l = 0;+	while (char_cnt-- > 0) {+		if (*p >= '0' && *p <= '7')+			digit = *p - '0';+		else+			return (l);+		p++;+		l <<= 3;+		l |= digit;+	}+	return (l);+}++static int64_t+atol16(const char *p, unsigned char_cnt)+{+	int64_t l;+	int digit;++	l = 0;+	while (char_cnt-- > 0) {+		if (*p >= 'a' && *p <= 'f')+			digit = *p - 'a' + 10;+		else if (*p >= 'A' && *p <= 'F')+			digit = *p - 'A' + 10;+		else if (*p >= '0' && *p <= '9')+			digit = *p - '0';+		else+			return (l);+		p++;+		l <<= 4;+		l |= digit;+	}+	return (l);+}++static int+record_hardlink(struct archive_read *a,+    struct cpio *cpio, struct archive_entry *entry)+{+	struct links_entry      *le;+	dev_t dev;+	int64_t ino;++	if (archive_entry_nlink(entry) <= 1)+		return (ARCHIVE_OK);++	dev = archive_entry_dev(entry);+	ino = archive_entry_ino64(entry);++	/*+	 * First look in the list of multiply-linked files.  If we've+	 * already dumped it, convert this entry to a hard link entry.+	 */+	for (le = cpio->links_head; le; le = le->next) {+		if (le->dev == dev && le->ino == ino) {+			archive_entry_copy_hardlink(entry, le->name);++			if (--le->links <= 0) {+				if (le->previous != NULL)+					le->previous->next = le->next;+				if (le->next != NULL)+					le->next->previous = le->previous;+				if (cpio->links_head == le)+					cpio->links_head = le->next;+				free(le->name);+				free(le);+			}++			return (ARCHIVE_OK);+		}+	}++	le = (struct links_entry *)malloc(sizeof(struct links_entry));+	if (le == NULL) {+		archive_set_error(&a->archive,+		    ENOMEM, "Out of memory adding file to list");+		return (ARCHIVE_FATAL);+	}+	if (cpio->links_head != NULL)+		cpio->links_head->previous = le;+	le->next = cpio->links_head;+	le->previous = NULL;+	cpio->links_head = le;+	le->dev = dev;+	le->ino = ino;+	le->links = archive_entry_nlink(entry) - 1;+	le->name = strdup(archive_entry_pathname(entry));+	if (le->name == NULL) {+		archive_set_error(&a->archive,+		    ENOMEM, "Out of memory adding file to list");+		return (ARCHIVE_FATAL);+	}++	return (ARCHIVE_OK);+}
+ c/archive_read_support_format_empty.c view
@@ -0,0 +1,96 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_empty.c 191524 2009-04-26 18:24:14Z kientzle $");++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_private.h"++static int	archive_read_format_empty_bid(struct archive_read *, int);+static int	archive_read_format_empty_read_data(struct archive_read *,+		    const void **, size_t *, int64_t *);+static int	archive_read_format_empty_read_header(struct archive_read *,+		    struct archive_entry *);+int+archive_read_support_format_empty(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_empty");++	r = __archive_read_register_format(a,+	    NULL,+	    NULL,+	    archive_read_format_empty_bid,+	    NULL,+	    archive_read_format_empty_read_header,+	    archive_read_format_empty_read_data,+	    NULL,+	    NULL,+	    NULL,+	    NULL,+	    NULL);++	return (r);+}+++static int+archive_read_format_empty_bid(struct archive_read *a, int best_bid)+{+	if (best_bid < 1 && __archive_read_ahead(a, 1, NULL) == NULL)+		return (1);+	return (-1);+}++static int+archive_read_format_empty_read_header(struct archive_read *a,+    struct archive_entry *entry)+{+	(void)a; /* UNUSED */+	(void)entry; /* UNUSED */++	a->archive.archive_format = ARCHIVE_FORMAT_EMPTY;+	a->archive.archive_format_name = "Empty file";++	return (ARCHIVE_EOF);+}++static int+archive_read_format_empty_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	(void)a; /* UNUSED */+	(void)buff; /* UNUSED */+	(void)size; /* UNUSED */+	(void)offset; /* UNUSED */++	return (ARCHIVE_EOF);+}
+ c/archive_read_support_format_iso9660.c view
@@ -0,0 +1,3278 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2009 Andreas Henriksson <andreas@fatal.se>+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_iso9660.c 201246 2009-12-30 05:30:35Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+/* #include <stdint.h> */ /* See archive_platform.h */+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#include <time.h>+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif++#include "archive.h"+#include "archive_endian.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_read_private.h"+#include "archive_string.h"++/*+ * An overview of ISO 9660 format:+ *+ * Each disk is laid out as follows:+ *   * 32k reserved for private use+ *   * Volume descriptor table.  Each volume descriptor+ *     is 2k and specifies basic format information.+ *     The "Primary Volume Descriptor" (PVD) is defined by the+ *     standard and should always be present; other volume+ *     descriptors include various vendor-specific extensions.+ *   * Files and directories.  Each file/dir is specified by+ *     an "extent" (starting sector and length in bytes).+ *     Dirs are just files with directory records packed one+ *     after another.  The PVD contains a single dir entry+ *     specifying the location of the root directory.  Everything+ *     else follows from there.+ *+ * This module works by first reading the volume descriptors, then+ * building a list of directory entries, sorted by starting+ * sector.  At each step, I look for the earliest dir entry that+ * hasn't yet been read, seek forward to that location and read+ * that entry.  If it's a dir, I slurp in the new dir entries and+ * add them to the heap; if it's a regular file, I return the+ * corresponding archive_entry and wait for the client to request+ * the file body.  This strategy allows us to read most compliant+ * CDs with a single pass through the data, as required by libarchive.+ */+#define	LOGICAL_BLOCK_SIZE	2048+#define	SYSTEM_AREA_BLOCK	16++/* Structure of on-disk primary volume descriptor. */+#define PVD_type_offset 0+#define PVD_type_size 1+#define PVD_id_offset (PVD_type_offset + PVD_type_size)+#define PVD_id_size 5+#define PVD_version_offset (PVD_id_offset + PVD_id_size)+#define PVD_version_size 1+#define PVD_reserved1_offset (PVD_version_offset + PVD_version_size)+#define PVD_reserved1_size 1+#define PVD_system_id_offset (PVD_reserved1_offset + PVD_reserved1_size)+#define PVD_system_id_size 32+#define PVD_volume_id_offset (PVD_system_id_offset + PVD_system_id_size)+#define PVD_volume_id_size 32+#define PVD_reserved2_offset (PVD_volume_id_offset + PVD_volume_id_size)+#define PVD_reserved2_size 8+#define PVD_volume_space_size_offset (PVD_reserved2_offset + PVD_reserved2_size)+#define PVD_volume_space_size_size 8+#define PVD_reserved3_offset (PVD_volume_space_size_offset + PVD_volume_space_size_size)+#define PVD_reserved3_size 32+#define PVD_volume_set_size_offset (PVD_reserved3_offset + PVD_reserved3_size)+#define PVD_volume_set_size_size 4+#define PVD_volume_sequence_number_offset (PVD_volume_set_size_offset + PVD_volume_set_size_size)+#define PVD_volume_sequence_number_size 4+#define PVD_logical_block_size_offset (PVD_volume_sequence_number_offset + PVD_volume_sequence_number_size)+#define PVD_logical_block_size_size 4+#define PVD_path_table_size_offset (PVD_logical_block_size_offset + PVD_logical_block_size_size)+#define PVD_path_table_size_size 8+#define PVD_type_1_path_table_offset (PVD_path_table_size_offset + PVD_path_table_size_size)+#define PVD_type_1_path_table_size 4+#define PVD_opt_type_1_path_table_offset (PVD_type_1_path_table_offset + PVD_type_1_path_table_size)+#define PVD_opt_type_1_path_table_size 4+#define PVD_type_m_path_table_offset (PVD_opt_type_1_path_table_offset + PVD_opt_type_1_path_table_size)+#define PVD_type_m_path_table_size 4+#define PVD_opt_type_m_path_table_offset (PVD_type_m_path_table_offset + PVD_type_m_path_table_size)+#define PVD_opt_type_m_path_table_size 4+#define PVD_root_directory_record_offset (PVD_opt_type_m_path_table_offset + PVD_opt_type_m_path_table_size)+#define PVD_root_directory_record_size 34+#define PVD_volume_set_id_offset (PVD_root_directory_record_offset + PVD_root_directory_record_size)+#define PVD_volume_set_id_size 128+#define PVD_publisher_id_offset (PVD_volume_set_id_offset + PVD_volume_set_id_size)+#define PVD_publisher_id_size 128+#define PVD_preparer_id_offset (PVD_publisher_id_offset + PVD_publisher_id_size)+#define PVD_preparer_id_size 128+#define PVD_application_id_offset (PVD_preparer_id_offset + PVD_preparer_id_size)+#define PVD_application_id_size 128+#define PVD_copyright_file_id_offset (PVD_application_id_offset + PVD_application_id_size)+#define PVD_copyright_file_id_size 37+#define PVD_abstract_file_id_offset (PVD_copyright_file_id_offset + PVD_copyright_file_id_size)+#define PVD_abstract_file_id_size 37+#define PVD_bibliographic_file_id_offset (PVD_abstract_file_id_offset + PVD_abstract_file_id_size)+#define PVD_bibliographic_file_id_size 37+#define PVD_creation_date_offset (PVD_bibliographic_file_id_offset + PVD_bibliographic_file_id_size)+#define PVD_creation_date_size 17+#define PVD_modification_date_offset (PVD_creation_date_offset + PVD_creation_date_size)+#define PVD_modification_date_size 17+#define PVD_expiration_date_offset (PVD_modification_date_offset + PVD_modification_date_size)+#define PVD_expiration_date_size 17+#define PVD_effective_date_offset (PVD_expiration_date_offset + PVD_expiration_date_size)+#define PVD_effective_date_size 17+#define PVD_file_structure_version_offset (PVD_effective_date_offset + PVD_effective_date_size)+#define PVD_file_structure_version_size 1+#define PVD_reserved4_offset (PVD_file_structure_version_offset + PVD_file_structure_version_size)+#define PVD_reserved4_size 1+#define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size)+#define PVD_application_data_size 512+#define PVD_reserved5_offset (PVD_application_data_offset + PVD_application_data_size)+#define PVD_reserved5_size (2048 - PVD_reserved5_offset)++/* TODO: It would make future maintenance easier to just hardcode the+ * above values.  In particular, ECMA119 states the offsets as part of+ * the standard.  That would eliminate the need for the following check.*/+#if PVD_reserved5_offset != 1395+#error PVD offset and size definitions are wrong.+#endif+++/* Structure of optional on-disk supplementary volume descriptor. */+#define SVD_type_offset 0+#define SVD_type_size 1+#define SVD_id_offset (SVD_type_offset + SVD_type_size)+#define SVD_id_size 5+#define SVD_version_offset (SVD_id_offset + SVD_id_size)+#define SVD_version_size 1+/* ... */+#define SVD_reserved1_offset	72+#define SVD_reserved1_size	8+#define SVD_volume_space_size_offset 80+#define SVD_volume_space_size_size 8+#define SVD_escape_sequences_offset (SVD_volume_space_size_offset + SVD_volume_space_size_size)+#define SVD_escape_sequences_size 32+/* ... */+#define SVD_logical_block_size_offset 128+#define SVD_logical_block_size_size 4+#define SVD_type_L_path_table_offset 140+#define SVD_type_M_path_table_offset 148+/* ... */+#define SVD_root_directory_record_offset 156+#define SVD_root_directory_record_size 34+#define SVD_file_structure_version_offset 881+#define SVD_reserved2_offset	882+#define SVD_reserved2_size	1+#define SVD_reserved3_offset	1395+#define SVD_reserved3_size	653+/* ... */+/* FIXME: validate correctness of last SVD entry offset. */++/* Structure of an on-disk directory record. */+/* Note:  ISO9660 stores each multi-byte integer twice, once in+ * each byte order.  The sizes here are the size of just one+ * of the two integers.  (This is why the offset of a field isn't+ * the same as the offset+size of the previous field.) */+#define DR_length_offset 0+#define DR_length_size 1+#define DR_ext_attr_length_offset 1+#define DR_ext_attr_length_size 1+#define DR_extent_offset 2+#define DR_extent_size 4+#define DR_size_offset 10+#define DR_size_size 4+#define DR_date_offset 18+#define DR_date_size 7+#define DR_flags_offset 25+#define DR_flags_size 1+#define DR_file_unit_size_offset 26+#define DR_file_unit_size_size 1+#define DR_interleave_offset 27+#define DR_interleave_size 1+#define DR_volume_sequence_number_offset 28+#define DR_volume_sequence_number_size 2+#define DR_name_len_offset 32+#define DR_name_len_size 1+#define DR_name_offset 33++#ifdef HAVE_ZLIB_H+static const unsigned char zisofs_magic[8] = {+	0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07+};++struct zisofs {+	/* Set 1 if this file compressed by paged zlib */+	int		 pz;+	int		 pz_log2_bs; /* Log2 of block size */+	uint64_t	 pz_uncompressed_size;++	int		 initialized;+	unsigned char	*uncompressed_buffer;+	size_t		 uncompressed_buffer_size;++	uint32_t	 pz_offset;+	unsigned char	 header[16];+	size_t		 header_avail;+	int		 header_passed;+	unsigned char	*block_pointers;+	size_t		 block_pointers_alloc;+	size_t		 block_pointers_size;+	size_t		 block_pointers_avail;+	size_t		 block_off;+	uint32_t	 block_avail;++	z_stream	 stream;+	int		 stream_valid;+};+#else+struct zisofs {+	/* Set 1 if this file compressed by paged zlib */+	int		 pz;+};+#endif++struct content {+	uint64_t	 offset;/* Offset on disk.		*/+	uint64_t	 size;	/* File size in bytes.		*/+	struct content	*next;+};++/* In-memory storage for a directory record. */+struct file_info {+	struct file_info	*use_next;+	struct file_info	*parent;+	struct file_info	*next;+	struct file_info	*re_next;+	int		 subdirs;+	uint64_t	 key;		/* Heap Key.			*/+	uint64_t	 offset;	/* Offset on disk.		*/+	uint64_t	 size;		/* File size in bytes.		*/+	uint32_t	 ce_offset;	/* Offset of CE.		*/+	uint32_t	 ce_size;	/* Size of CE.			*/+	char		 rr_moved;	/* Flag to rr_moved.		*/+	char		 rr_moved_has_re_only;+	char		 re;		/* Having RRIP "RE" extension.	*/+	char		 re_descendant;+	uint64_t	 cl_offset;	/* Having RRIP "CL" extension.	*/+	int		 birthtime_is_set;+	time_t		 birthtime;	/* File created time.		*/+	time_t		 mtime;		/* File last modified time.	*/+	time_t		 atime;		/* File last accessed time.	*/+	time_t		 ctime;		/* File attribute change time.	*/+	uint64_t	 rdev;		/* Device number.		*/+	mode_t		 mode;+	uid_t		 uid;+	gid_t		 gid;+	int64_t		 number;+	int		 nlinks;+	struct archive_string name; /* Pathname */+	unsigned char	*utf16be_name;+	size_t		 utf16be_bytes;+	char		 name_continues; /* Non-zero if name continues */+	struct archive_string symlink;+	char		 symlink_continues; /* Non-zero if link continues */+	/* Set 1 if this file compressed by paged zlib(zisofs) */+	int		 pz;+	int		 pz_log2_bs; /* Log2 of block size */+	uint64_t	 pz_uncompressed_size;+	/* Set 1 if this file is multi extent. */+	int		 multi_extent;+	struct {+		struct content	*first;+		struct content	**last;+	} contents;+	struct {+		struct file_info	*first;+		struct file_info	**last;+	} rede_files;+};++struct heap_queue {+	struct file_info **files;+	int		 allocated;+	int		 used;+};++struct iso9660 {+	int	magic;+#define ISO9660_MAGIC   0x96609660++	int opt_support_joliet;+	int opt_support_rockridge;++	struct archive_string pathname;+	char	seenRockridge;	/* Set true if RR extensions are used. */+	char	seenSUSP;	/* Set true if SUSP is being used. */+	char	seenJoliet;++	unsigned char	suspOffset;+	struct file_info *rr_moved;+	struct read_ce_queue {+		struct read_ce_req {+			uint64_t	 offset;/* Offset of CE on disk. */+			struct file_info *file;+		}		*reqs;+		int		 cnt;+		int		 allocated;+	}	read_ce_req;++	int64_t		previous_number;+	struct archive_string previous_pathname;++	struct file_info		*use_files;+	struct heap_queue		 pending_files;+	struct {+		struct file_info	*first;+		struct file_info	**last;+	}	cache_files;+	struct {+		struct file_info	*first;+		struct file_info	**last;+	}	re_files;++	uint64_t current_position;+	ssize_t	logical_block_size;+	uint64_t volume_size; /* Total size of volume in bytes. */+	int32_t  volume_block;/* Total size of volume in logical blocks. */++	struct vd {+		int		location;	/* Location of Extent.	*/+		uint32_t	size;+	} primary, joliet;++	int64_t	entry_sparse_offset;+	int64_t	entry_bytes_remaining;+	size_t  entry_bytes_unconsumed;+	struct zisofs	 entry_zisofs;+	struct content	*entry_content;+	struct archive_string_conv *sconv_utf16be;+	/*+	 * Buffers for a full pathname in UTF-16BE in Joliet extensions.+	 */+#define UTF16_NAME_MAX	1024+	unsigned char *utf16be_path;+	size_t		 utf16be_path_len;+	unsigned char *utf16be_previous_path;+	size_t		 utf16be_previous_path_len;+	/* Null buffer used in bidder to improve its performance. */+	unsigned char	 null[2048];+};++static int	archive_read_format_iso9660_bid(struct archive_read *, int);+static int	archive_read_format_iso9660_options(struct archive_read *,+		    const char *, const char *);+static int	archive_read_format_iso9660_cleanup(struct archive_read *);+static int	archive_read_format_iso9660_read_data(struct archive_read *,+		    const void **, size_t *, int64_t *);+static int	archive_read_format_iso9660_read_data_skip(struct archive_read *);+static int	archive_read_format_iso9660_read_header(struct archive_read *,+		    struct archive_entry *);+static const char *build_pathname(struct archive_string *, struct file_info *, int);+static int	build_pathname_utf16be(unsigned char *, size_t, size_t *,+		    struct file_info *);+#if DEBUG+static void	dump_isodirrec(FILE *, const unsigned char *isodirrec);+#endif+static time_t	time_from_tm(struct tm *);+static time_t	isodate17(const unsigned char *);+static time_t	isodate7(const unsigned char *);+static int	isBootRecord(struct iso9660 *, const unsigned char *);+static int	isVolumePartition(struct iso9660 *, const unsigned char *);+static int	isVDSetTerminator(struct iso9660 *, const unsigned char *);+static int	isJolietSVD(struct iso9660 *, const unsigned char *);+static int	isSVD(struct iso9660 *, const unsigned char *);+static int	isEVD(struct iso9660 *, const unsigned char *);+static int	isPVD(struct iso9660 *, const unsigned char *);+static int	next_cache_entry(struct archive_read *, struct iso9660 *,+		    struct file_info **);+static int	next_entry_seek(struct archive_read *, struct iso9660 *,+		    struct file_info **);+static struct file_info *+		parse_file_info(struct archive_read *a,+		    struct file_info *parent, const unsigned char *isodirrec,+		    size_t reclen);+static int	parse_rockridge(struct archive_read *a,+		    struct file_info *file, const unsigned char *start,+		    const unsigned char *end);+static int	register_CE(struct archive_read *a, int32_t location,+		    struct file_info *file);+static int	read_CE(struct archive_read *a, struct iso9660 *iso9660);+static void	parse_rockridge_NM1(struct file_info *,+		    const unsigned char *, int);+static void	parse_rockridge_SL1(struct file_info *,+		    const unsigned char *, int);+static void	parse_rockridge_TF1(struct file_info *,+		    const unsigned char *, int);+static void	parse_rockridge_ZF1(struct file_info *,+		    const unsigned char *, int);+static void	register_file(struct iso9660 *, struct file_info *);+static void	release_files(struct iso9660 *);+static unsigned	toi(const void *p, int n);+static inline void re_add_entry(struct iso9660 *, struct file_info *);+static inline struct file_info * re_get_entry(struct iso9660 *);+static inline int rede_add_entry(struct file_info *);+static inline struct file_info * rede_get_entry(struct file_info *);+static inline void cache_add_entry(struct iso9660 *iso9660,+		    struct file_info *file);+static inline struct file_info *cache_get_entry(struct iso9660 *iso9660);+static int	heap_add_entry(struct archive_read *a, struct heap_queue *heap,+		    struct file_info *file, uint64_t key);+static struct file_info *heap_get_entry(struct heap_queue *heap);++#define add_entry(arch, iso9660, file)	\+	heap_add_entry(arch, &((iso9660)->pending_files), file, file->offset)+#define next_entry(iso9660)		\+	heap_get_entry(&((iso9660)->pending_files))++int+archive_read_support_format_iso9660(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct iso9660 *iso9660;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_iso9660");++	iso9660 = (struct iso9660 *)calloc(1, sizeof(*iso9660));+	if (iso9660 == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate iso9660 data");+		return (ARCHIVE_FATAL);+	}+	iso9660->magic = ISO9660_MAGIC;+	iso9660->cache_files.first = NULL;+	iso9660->cache_files.last = &(iso9660->cache_files.first);+	iso9660->re_files.first = NULL;+	iso9660->re_files.last = &(iso9660->re_files.first);+	/* Enable to support Joliet extensions by default.	*/+	iso9660->opt_support_joliet = 1;+	/* Enable to support Rock Ridge extensions by default.	*/+	iso9660->opt_support_rockridge = 1;++	r = __archive_read_register_format(a,+	    iso9660,+	    "iso9660",+	    archive_read_format_iso9660_bid,+	    archive_read_format_iso9660_options,+	    archive_read_format_iso9660_read_header,+	    archive_read_format_iso9660_read_data,+	    archive_read_format_iso9660_read_data_skip,+	    NULL,+	    archive_read_format_iso9660_cleanup,+	    NULL,+	    NULL);++	if (r != ARCHIVE_OK) {+		free(iso9660);+		return (r);+	}+	return (ARCHIVE_OK);+}+++static int+archive_read_format_iso9660_bid(struct archive_read *a, int best_bid)+{+	struct iso9660 *iso9660;+	ssize_t bytes_read;+	const unsigned char *p;+	int seenTerminator;++	/* If there's already a better bid than we can ever+	   make, don't bother testing. */+	if (best_bid > 48)+		return (-1);++	iso9660 = (struct iso9660 *)(a->format->data);++	/*+	 * Skip the first 32k (reserved area) and get the first+	 * 8 sectors of the volume descriptor table.  Of course,+	 * if the I/O layer gives us more, we'll take it.+	 */+#define RESERVED_AREA	(SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE)+	p = __archive_read_ahead(a,+	    RESERVED_AREA + 8 * LOGICAL_BLOCK_SIZE,+	    &bytes_read);+	if (p == NULL)+	    return (-1);++	/* Skip the reserved area. */+	bytes_read -= RESERVED_AREA;+	p += RESERVED_AREA;++	/* Check each volume descriptor. */+	seenTerminator = 0;+	for (; bytes_read > LOGICAL_BLOCK_SIZE;+	    bytes_read -= LOGICAL_BLOCK_SIZE, p += LOGICAL_BLOCK_SIZE) {+		/* Do not handle undefined Volume Descriptor Type. */+		if (p[0] >= 4 && p[0] <= 254)+			return (0);+		/* Standard Identifier must be "CD001" */+		if (memcmp(p + 1, "CD001", 5) != 0)+			return (0);+		if (isPVD(iso9660, p))+			continue;+		if (!iso9660->joliet.location) {+			if (isJolietSVD(iso9660, p))+				continue;+		}+		if (isBootRecord(iso9660, p))+			continue;+		if (isEVD(iso9660, p))+			continue;+		if (isSVD(iso9660, p))+			continue;+		if (isVolumePartition(iso9660, p))+			continue;+		if (isVDSetTerminator(iso9660, p)) {+			seenTerminator = 1;+			break;+		}+		return (0);+	}+	/*+	 * ISO 9660 format must have Primary Volume Descriptor and+	 * Volume Descriptor Set Terminator.+	 */+	if (seenTerminator && iso9660->primary.location > 16)+		return (48);++	/* We didn't find a valid PVD; return a bid of zero. */+	return (0);+}++static int+archive_read_format_iso9660_options(struct archive_read *a,+		const char *key, const char *val)+{+	struct iso9660 *iso9660;++	iso9660 = (struct iso9660 *)(a->format->data);++	if (strcmp(key, "joliet") == 0) {+		if (val == NULL || strcmp(val, "off") == 0 ||+				strcmp(val, "ignore") == 0 ||+				strcmp(val, "disable") == 0 ||+				strcmp(val, "0") == 0)+			iso9660->opt_support_joliet = 0;+		else+			iso9660->opt_support_joliet = 1;+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "rockridge") == 0 ||+	    strcmp(key, "Rockridge") == 0) {+		iso9660->opt_support_rockridge = val != NULL;+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+isNull(struct iso9660 *iso9660, const unsigned char *h, unsigned offset,+unsigned bytes)+{++	while (bytes >= sizeof(iso9660->null)) {+		if (!memcmp(iso9660->null, h + offset, sizeof(iso9660->null)))+			return (0);+		offset += sizeof(iso9660->null);+		bytes -= sizeof(iso9660->null);+	}+	if (bytes)+		return memcmp(iso9660->null, h + offset, bytes) == 0;+	else+		return (1);+}++static int+isBootRecord(struct iso9660 *iso9660, const unsigned char *h)+{+	(void)iso9660; /* UNUSED */++	/* Type of the Volume Descriptor Boot Record must be 0. */+	if (h[0] != 0)+		return (0);++	/* Volume Descriptor Version must be 1. */+	if (h[6] != 1)+		return (0);++	return (1);+}++static int+isVolumePartition(struct iso9660 *iso9660, const unsigned char *h)+{+	int32_t location;++	/* Type of the Volume Partition Descriptor must be 3. */+	if (h[0] != 3)+		return (0);++	/* Volume Descriptor Version must be 1. */+	if (h[6] != 1)+		return (0);+	/* Unused Field */+	if (h[7] != 0)+		return (0);++	location = archive_le32dec(h + 72);+	if (location <= SYSTEM_AREA_BLOCK ||+	    location >= iso9660->volume_block)+		return (0);+	if ((uint32_t)location != archive_be32dec(h + 76))+		return (0);++	return (1);+}++static int+isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h)+{+	(void)iso9660; /* UNUSED */++	/* Type of the Volume Descriptor Set Terminator must be 255. */+	if (h[0] != 255)+		return (0);++	/* Volume Descriptor Version must be 1. */+	if (h[6] != 1)+		return (0);++	/* Reserved field must be 0. */+	if (!isNull(iso9660, h, 7, 2048-7))+		return (0);++	return (1);+}++static int+isJolietSVD(struct iso9660 *iso9660, const unsigned char *h)+{+	const unsigned char *p;+	ssize_t logical_block_size;+	int32_t volume_block;++	/* Check if current sector is a kind of Supplementary Volume+	 * Descriptor. */+	if (!isSVD(iso9660, h))+		return (0);++	/* FIXME: do more validations according to joliet spec. */++	/* check if this SVD contains joliet extension! */+	p = h + SVD_escape_sequences_offset;+	/* N.B. Joliet spec says p[1] == '\\', but.... */+	if (p[0] == '%' && p[1] == '/') {+		int level = 0;++		if (p[2] == '@')+			level = 1;+		else if (p[2] == 'C')+			level = 2;+		else if (p[2] == 'E')+			level = 3;+		else /* not joliet */+			return (0);++		iso9660->seenJoliet = level;++	} else /* not joliet */+		return (0);++	logical_block_size =+	    archive_le16dec(h + SVD_logical_block_size_offset);+	volume_block = archive_le32dec(h + SVD_volume_space_size_offset);++	iso9660->logical_block_size = logical_block_size;+	iso9660->volume_block = volume_block;+	iso9660->volume_size = logical_block_size * (uint64_t)volume_block;+	/* Read Root Directory Record in Volume Descriptor. */+	p = h + SVD_root_directory_record_offset;+	iso9660->joliet.location = archive_le32dec(p + DR_extent_offset);+	iso9660->joliet.size = archive_le32dec(p + DR_size_offset);++	return (48);+}++static int+isSVD(struct iso9660 *iso9660, const unsigned char *h)+{+	const unsigned char *p;+	ssize_t logical_block_size;+	int32_t volume_block;+	int32_t location;++	(void)iso9660; /* UNUSED */++	/* Type 2 means it's a SVD. */+	if (h[SVD_type_offset] != 2)+		return (0);++	/* Reserved field must be 0. */+	if (!isNull(iso9660, h, SVD_reserved1_offset, SVD_reserved1_size))+		return (0);+	if (!isNull(iso9660, h, SVD_reserved2_offset, SVD_reserved2_size))+		return (0);+	if (!isNull(iso9660, h, SVD_reserved3_offset, SVD_reserved3_size))+		return (0);++	/* File structure version must be 1 for ISO9660/ECMA119. */+	if (h[SVD_file_structure_version_offset] != 1)+		return (0);++	logical_block_size =+	    archive_le16dec(h + SVD_logical_block_size_offset);+	if (logical_block_size <= 0)+		return (0);++	volume_block = archive_le32dec(h + SVD_volume_space_size_offset);+	if (volume_block <= SYSTEM_AREA_BLOCK+4)+		return (0);++	/* Location of Occurrence of Type L Path Table must be+	 * available location,+	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */+	location = archive_le32dec(h+SVD_type_L_path_table_offset);+	if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)+		return (0);++	/* The Type M Path Table must be at a valid location (WinISO+	 * and probably other programs omit this, so we allow zero)+	 *+	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */+	location = archive_be32dec(h+SVD_type_M_path_table_offset);+	if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)+	    || location >= volume_block)+		return (0);++	/* Read Root Directory Record in Volume Descriptor. */+	p = h + SVD_root_directory_record_offset;+	if (p[DR_length_offset] != 34)+		return (0);++	return (48);+}++static int+isEVD(struct iso9660 *iso9660, const unsigned char *h)+{+	const unsigned char *p;+	ssize_t logical_block_size;+	int32_t volume_block;+	int32_t location;++	(void)iso9660; /* UNUSED */++	/* Type of the Enhanced Volume Descriptor must be 2. */+	if (h[PVD_type_offset] != 2)+		return (0);++	/* EVD version must be 2. */+	if (h[PVD_version_offset] != 2)+		return (0);++	/* Reserved field must be 0. */+	if (h[PVD_reserved1_offset] != 0)+		return (0);++	/* Reserved field must be 0. */+	if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))+		return (0);++	/* Reserved field must be 0. */+	if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))+		return (0);++	/* Logical block size must be > 0. */+	/* I've looked at Ecma 119 and can't find any stronger+	 * restriction on this field. */+	logical_block_size =+	    archive_le16dec(h + PVD_logical_block_size_offset);+	if (logical_block_size <= 0)+		return (0);++	volume_block =+	    archive_le32dec(h + PVD_volume_space_size_offset);+	if (volume_block <= SYSTEM_AREA_BLOCK+4)+		return (0);++	/* File structure version must be 2 for ISO9660:1999. */+	if (h[PVD_file_structure_version_offset] != 2)+		return (0);++	/* Location of Occurrence of Type L Path Table must be+	 * available location,+	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */+	location = archive_le32dec(h+PVD_type_1_path_table_offset);+	if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)+		return (0);++	/* Location of Occurrence of Type M Path Table must be+	 * available location,+	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */+	location = archive_be32dec(h+PVD_type_m_path_table_offset);+	if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)+	    || location >= volume_block)+		return (0);++	/* Reserved field must be 0. */+	if (!isNull(iso9660, h, PVD_reserved4_offset, PVD_reserved4_size))+		return (0);++	/* Reserved field must be 0. */+	if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))+		return (0);++	/* Read Root Directory Record in Volume Descriptor. */+	p = h + PVD_root_directory_record_offset;+	if (p[DR_length_offset] != 34)+		return (0);++	return (48);+}++static int+isPVD(struct iso9660 *iso9660, const unsigned char *h)+{+	const unsigned char *p;+	ssize_t logical_block_size;+	int32_t volume_block;+	int32_t location;+	int i;++	/* Type of the Primary Volume Descriptor must be 1. */+	if (h[PVD_type_offset] != 1)+		return (0);++	/* PVD version must be 1. */+	if (h[PVD_version_offset] != 1)+		return (0);++	/* Reserved field must be 0. */+	if (h[PVD_reserved1_offset] != 0)+		return (0);++	/* Reserved field must be 0. */+	if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))+		return (0);++	/* Reserved field must be 0. */+	if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))+		return (0);++	/* Logical block size must be > 0. */+	/* I've looked at Ecma 119 and can't find any stronger+	 * restriction on this field. */+	logical_block_size =+	    archive_le16dec(h + PVD_logical_block_size_offset);+	if (logical_block_size <= 0)+		return (0);++	volume_block = archive_le32dec(h + PVD_volume_space_size_offset);+	if (volume_block <= SYSTEM_AREA_BLOCK+4)+		return (0);++	/* File structure version must be 1 for ISO9660/ECMA119. */+	if (h[PVD_file_structure_version_offset] != 1)+		return (0);++	/* Location of Occurrence of Type L Path Table must be+	 * available location,+	 * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */+	location = archive_le32dec(h+PVD_type_1_path_table_offset);+	if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)+		return (0);++	/* The Type M Path Table must also be at a valid location+	 * (although ECMA 119 requires a Type M Path Table, WinISO and+	 * probably other programs omit it, so we permit a zero here)+	 *+	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */+	location = archive_be32dec(h+PVD_type_m_path_table_offset);+	if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)+	    || location >= volume_block)+		return (0);++	/* Reserved field must be 0. */+	/* But accept NetBSD/FreeBSD "makefs" images with 0x20 here. */+	for (i = 0; i < PVD_reserved4_size; ++i)+		if (h[PVD_reserved4_offset + i] != 0+		    && h[PVD_reserved4_offset + i] != 0x20)+			return (0);++	/* Reserved field must be 0. */+	if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))+		return (0);++	/* XXX TODO: Check other values for sanity; reject more+	 * malformed PVDs. XXX */++	/* Read Root Directory Record in Volume Descriptor. */+	p = h + PVD_root_directory_record_offset;+	if (p[DR_length_offset] != 34)+		return (0);++	if (!iso9660->primary.location) {+		iso9660->logical_block_size = logical_block_size;+		iso9660->volume_block = volume_block;+		iso9660->volume_size =+		    logical_block_size * (uint64_t)volume_block;+		iso9660->primary.location =+		    archive_le32dec(p + DR_extent_offset);+		iso9660->primary.size = archive_le32dec(p + DR_size_offset);+	}++	return (48);+}++static int+read_children(struct archive_read *a, struct file_info *parent)+{+	struct iso9660 *iso9660;+	const unsigned char *b, *p;+	struct file_info *multi;+	size_t step, skip_size;++	iso9660 = (struct iso9660 *)(a->format->data);+	/* flush any remaining bytes from the last round to ensure+	 * we're positioned */+	if (iso9660->entry_bytes_unconsumed) {+		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);+		iso9660->entry_bytes_unconsumed = 0;+	}+	if (iso9660->current_position > parent->offset) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Ignoring out-of-order directory (%s) %jd > %jd",+		    parent->name.s,+		    (intmax_t)iso9660->current_position,+		    (intmax_t)parent->offset);+		return (ARCHIVE_WARN);+	}+	if (parent->offset + parent->size > iso9660->volume_size) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Directory is beyond end-of-media: %s",+		    parent->name.s);+		return (ARCHIVE_WARN);+	}+	if (iso9660->current_position < parent->offset) {+		int64_t skipsize;++		skipsize = parent->offset - iso9660->current_position;+		skipsize = __archive_read_consume(a, skipsize);+		if (skipsize < 0)+			return ((int)skipsize);+		iso9660->current_position = parent->offset;+	}++	step = (size_t)(((parent->size + iso9660->logical_block_size -1) /+	    iso9660->logical_block_size) * iso9660->logical_block_size);+	b = __archive_read_ahead(a, step, NULL);+	if (b == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Failed to read full block when scanning "+		    "ISO9660 directory list");+		return (ARCHIVE_FATAL);+	}+	iso9660->current_position += step;+	multi = NULL;+	skip_size = step;+	while (step) {+		p = b;+		b += iso9660->logical_block_size;+		step -= iso9660->logical_block_size;+		for (; *p != 0 && p < b && p + *p <= b; p += *p) {+			struct file_info *child;++			/* N.B.: these special directory identifiers+			 * are 8 bit "values" even on a+			 * Joliet CD with UCS-2 (16bit) encoding.+			 */++			/* Skip '.' entry. */+			if (*(p + DR_name_len_offset) == 1+			    && *(p + DR_name_offset) == '\0')+				continue;+			/* Skip '..' entry. */+			if (*(p + DR_name_len_offset) == 1+			    && *(p + DR_name_offset) == '\001')+				continue;+			child = parse_file_info(a, parent, p, b - p);+			if (child == NULL) {+				__archive_read_consume(a, skip_size);+				return (ARCHIVE_FATAL);+			}+			if (child->cl_offset == 0 &&+			    (child->multi_extent || multi != NULL)) {+				struct content *con;++				if (multi == NULL) {+					multi = child;+					multi->contents.first = NULL;+					multi->contents.last =+					    &(multi->contents.first);+				}+				con = malloc(sizeof(struct content));+				if (con == NULL) {+					archive_set_error(+					    &a->archive, ENOMEM,+					    "No memory for multi extent");+					__archive_read_consume(a, skip_size);+					return (ARCHIVE_FATAL);+				}+				con->offset = child->offset;+				con->size = child->size;+				con->next = NULL;+				*multi->contents.last = con;+				multi->contents.last = &(con->next);+				if (multi == child) {+					if (add_entry(a, iso9660, child)+					    != ARCHIVE_OK)+						return (ARCHIVE_FATAL);+				} else {+					multi->size += child->size;+					if (!child->multi_extent)+						multi = NULL;+				}+			} else+				if (add_entry(a, iso9660, child) != ARCHIVE_OK)+					return (ARCHIVE_FATAL);+		}+	}++	__archive_read_consume(a, skip_size);++	/* Read data which recorded by RRIP "CE" extension. */+	if (read_CE(a, iso9660) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	return (ARCHIVE_OK);+}++static int+choose_volume(struct archive_read *a, struct iso9660 *iso9660)+{+	struct file_info *file;+	int64_t skipsize;+	struct vd *vd;+	const void *block;+	char seenJoliet;++	vd = &(iso9660->primary);+	if (!iso9660->opt_support_joliet)+		iso9660->seenJoliet = 0;+	if (iso9660->seenJoliet &&+		vd->location > iso9660->joliet.location)+		/* This condition is unlikely; by way of caution. */+		vd = &(iso9660->joliet);++	skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location;+	skipsize = __archive_read_consume(a, skipsize);+	if (skipsize < 0)+		return ((int)skipsize);+	iso9660->current_position = skipsize;++	block = __archive_read_ahead(a, vd->size, NULL);+	if (block == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Failed to read full block when scanning "+		    "ISO9660 directory list");+		return (ARCHIVE_FATAL);+	}++	/*+	 * While reading Root Directory, flag seenJoliet must be zero to+	 * avoid converting special name 0x00(Current Directory) and+	 * next byte to UCS2.+	 */+	seenJoliet = iso9660->seenJoliet;/* Save flag. */+	iso9660->seenJoliet = 0;+	file = parse_file_info(a, NULL, block, vd->size);+	if (file == NULL)+		return (ARCHIVE_FATAL);+	iso9660->seenJoliet = seenJoliet;++	/*+	 * If the iso image has both RockRidge and Joliet, we preferentially+	 * use RockRidge Extensions rather than Joliet ones.+	 */+	if (vd == &(iso9660->primary) && iso9660->seenRockridge+	    && iso9660->seenJoliet)+		iso9660->seenJoliet = 0;++	if (vd == &(iso9660->primary) && !iso9660->seenRockridge+	    && iso9660->seenJoliet) {+		/* Switch reading data from primary to joliet. */+		vd = &(iso9660->joliet);+		skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location;+		skipsize -= iso9660->current_position;+		skipsize = __archive_read_consume(a, skipsize);+		if (skipsize < 0)+			return ((int)skipsize);+		iso9660->current_position += skipsize;++		block = __archive_read_ahead(a, vd->size, NULL);+		if (block == NULL) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Failed to read full block when scanning "+			    "ISO9660 directory list");+			return (ARCHIVE_FATAL);+		}+		iso9660->seenJoliet = 0;+		file = parse_file_info(a, NULL, block, vd->size);+		if (file == NULL)+			return (ARCHIVE_FATAL);+		iso9660->seenJoliet = seenJoliet;+	}++	/* Store the root directory in the pending list. */+	if (add_entry(a, iso9660, file) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	if (iso9660->seenRockridge) {+		a->archive.archive_format = ARCHIVE_FORMAT_ISO9660_ROCKRIDGE;+		a->archive.archive_format_name =+		    "ISO9660 with Rockridge extensions";+	}++	return (ARCHIVE_OK);+}++static int+archive_read_format_iso9660_read_header(struct archive_read *a,+    struct archive_entry *entry)+{+	struct iso9660 *iso9660;+	struct file_info *file;+	int r, rd_r = ARCHIVE_OK;++	iso9660 = (struct iso9660 *)(a->format->data);++	if (!a->archive.archive_format) {+		a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;+		a->archive.archive_format_name = "ISO9660";+	}++	if (iso9660->current_position == 0) {+		r = choose_volume(a, iso9660);+		if (r != ARCHIVE_OK)+			return (r);+	}++	file = NULL;/* Eliminate a warning. */+	/* Get the next entry that appears after the current offset. */+	r = next_entry_seek(a, iso9660, &file);+	if (r != ARCHIVE_OK)+		return (r);++	if (iso9660->seenJoliet) {+		/*+		 * Convert UTF-16BE of a filename to local locale MBS+		 * and store the result into a filename field.+		 */+		if (iso9660->sconv_utf16be == NULL) {+			iso9660->sconv_utf16be =+			    archive_string_conversion_from_charset(+				&(a->archive), "UTF-16BE", 1);+			if (iso9660->sconv_utf16be == NULL)+				/* Couldn't allocate memory */+				return (ARCHIVE_FATAL);+		}+		if (iso9660->utf16be_path == NULL) {+			iso9660->utf16be_path = malloc(UTF16_NAME_MAX);+			if (iso9660->utf16be_path == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "No memory");+				return (ARCHIVE_FATAL);+			}+		}+		if (iso9660->utf16be_previous_path == NULL) {+			iso9660->utf16be_previous_path = malloc(UTF16_NAME_MAX);+			if (iso9660->utf16be_previous_path == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "No memory");+				return (ARCHIVE_FATAL);+			}+		}++		iso9660->utf16be_path_len = 0;+		if (build_pathname_utf16be(iso9660->utf16be_path,+		    UTF16_NAME_MAX, &(iso9660->utf16be_path_len), file) != 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Pathname is too long");+			return (ARCHIVE_FATAL);+		}++		r = archive_entry_copy_pathname_l(entry,+		    (const char *)iso9660->utf16be_path,+		    iso9660->utf16be_path_len,+		    iso9660->sconv_utf16be);+		if (r != 0) {+			if (errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "No memory for Pathname");+				return (ARCHIVE_FATAL);+			}+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Pathname cannot be converted "+			    "from %s to current locale.",+			    archive_string_conversion_charset_name(+			      iso9660->sconv_utf16be));++			rd_r = ARCHIVE_WARN;+		}+	} else {+		const char *path = build_pathname(&iso9660->pathname, file, 0);+		if (path == NULL) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Pathname is too long");+			return (ARCHIVE_FATAL);+		} else {+			archive_string_empty(&iso9660->pathname);+			archive_entry_set_pathname(entry, path);+		}+	}++	iso9660->entry_bytes_remaining = file->size;+	/* Offset for sparse-file-aware clients. */+	iso9660->entry_sparse_offset = 0;++	if (file->offset + file->size > iso9660->volume_size) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "File is beyond end-of-media: %s",+		    archive_entry_pathname(entry));+		iso9660->entry_bytes_remaining = 0;+		return (ARCHIVE_WARN);+	}++	/* Set up the entry structure with information about this entry. */+	archive_entry_set_mode(entry, file->mode);+	archive_entry_set_uid(entry, file->uid);+	archive_entry_set_gid(entry, file->gid);+	archive_entry_set_nlink(entry, file->nlinks);+	if (file->birthtime_is_set)+		archive_entry_set_birthtime(entry, file->birthtime, 0);+	else+		archive_entry_unset_birthtime(entry);+	archive_entry_set_mtime(entry, file->mtime, 0);+	archive_entry_set_ctime(entry, file->ctime, 0);+	archive_entry_set_atime(entry, file->atime, 0);+	/* N.B.: Rock Ridge supports 64-bit device numbers. */+	archive_entry_set_rdev(entry, (dev_t)file->rdev);+	archive_entry_set_size(entry, iso9660->entry_bytes_remaining);+	if (file->symlink.s != NULL)+		archive_entry_copy_symlink(entry, file->symlink.s);++	/* Note: If the input isn't seekable, we can't rewind to+	 * return the same body again, so if the next entry refers to+	 * the same data, we have to return it as a hardlink to the+	 * original entry. */+	if (file->number != -1 &&+	    file->number == iso9660->previous_number) {+		if (iso9660->seenJoliet) {+			r = archive_entry_copy_hardlink_l(entry,+			    (const char *)iso9660->utf16be_previous_path,+			    iso9660->utf16be_previous_path_len,+			    iso9660->sconv_utf16be);+			if (r != 0) {+				if (errno == ENOMEM) {+					archive_set_error(&a->archive, ENOMEM,+					    "No memory for Linkname");+					return (ARCHIVE_FATAL);+				}+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Linkname cannot be converted "+				    "from %s to current locale.",+				    archive_string_conversion_charset_name(+				      iso9660->sconv_utf16be));+				rd_r = ARCHIVE_WARN;+			}+		} else+			archive_entry_set_hardlink(entry,+			    iso9660->previous_pathname.s);+		archive_entry_unset_size(entry);+		iso9660->entry_bytes_remaining = 0;+		return (rd_r);+	}++	if ((file->mode & AE_IFMT) != AE_IFDIR &&+	    file->offset < iso9660->current_position) {+		int64_t r64;++		r64 = __archive_read_seek(a, file->offset, SEEK_SET);+		if (r64 != (int64_t)file->offset) {+			/* We can't seek backwards to extract it, so issue+			 * a warning.  Note that this can only happen if+			 * this entry was added to the heap after we passed+			 * this offset, that is, only if the directory+			 * mentioning this entry is later than the body of+			 * the entry. Such layouts are very unusual; most+			 * ISO9660 writers lay out and record all directory+			 * information first, then store all file bodies. */+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Ignoring out-of-order file @%jx (%s) %jd < %jd",+			    (intmax_t)file->number,+			    iso9660->pathname.s,+			    (intmax_t)file->offset,+			    (intmax_t)iso9660->current_position);+			iso9660->entry_bytes_remaining = 0;+			return (ARCHIVE_WARN);+		}+		iso9660->current_position = (uint64_t)r64;+	}++	/* Initialize zisofs variables. */+	iso9660->entry_zisofs.pz = file->pz;+	if (file->pz) {+#ifdef HAVE_ZLIB_H+		struct zisofs  *zisofs;++		zisofs = &iso9660->entry_zisofs;+		zisofs->initialized = 0;+		zisofs->pz_log2_bs = file->pz_log2_bs;+		zisofs->pz_uncompressed_size = file->pz_uncompressed_size;+		zisofs->pz_offset = 0;+		zisofs->header_avail = 0;+		zisofs->header_passed = 0;+		zisofs->block_pointers_avail = 0;+#endif+		archive_entry_set_size(entry, file->pz_uncompressed_size);+	}++	iso9660->previous_number = file->number;+	if (iso9660->seenJoliet) {+		memcpy(iso9660->utf16be_previous_path, iso9660->utf16be_path,+		    iso9660->utf16be_path_len);+		iso9660->utf16be_previous_path_len = iso9660->utf16be_path_len;+	} else+		archive_strcpy(+		    &iso9660->previous_pathname, iso9660->pathname.s);++	/* Reset entry_bytes_remaining if the file is multi extent. */+	iso9660->entry_content = file->contents.first;+	if (iso9660->entry_content != NULL)+		iso9660->entry_bytes_remaining = iso9660->entry_content->size;++	if (archive_entry_filetype(entry) == AE_IFDIR) {+		/* Overwrite nlinks by proper link number which is+		 * calculated from number of sub directories. */+		archive_entry_set_nlink(entry, 2 + file->subdirs);+		/* Directory data has been read completely. */+		iso9660->entry_bytes_remaining = 0;+	}++	if (rd_r != ARCHIVE_OK)+		return (rd_r);+	return (ARCHIVE_OK);+}++static int+archive_read_format_iso9660_read_data_skip(struct archive_read *a)+{+	/* Because read_next_header always does an explicit skip+	 * to the next entry, we don't need to do anything here. */+	(void)a; /* UNUSED */+	return (ARCHIVE_OK);+}++#ifdef HAVE_ZLIB_H++static int+zisofs_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	struct iso9660 *iso9660;+	struct zisofs  *zisofs;+	const unsigned char *p;+	size_t avail;+	ssize_t bytes_read;+	size_t uncompressed_size;+	int r;++	iso9660 = (struct iso9660 *)(a->format->data);+	zisofs = &iso9660->entry_zisofs;++	p = __archive_read_ahead(a, 1, &bytes_read);+	if (bytes_read <= 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated zisofs file body");+		return (ARCHIVE_FATAL);+	}+	if (bytes_read > iso9660->entry_bytes_remaining)+		bytes_read = (ssize_t)iso9660->entry_bytes_remaining;+	avail = bytes_read;+	uncompressed_size = 0;++	if (!zisofs->initialized) {+		size_t ceil, xsize;++		/* Allocate block pointers buffer. */+		ceil = (size_t)((zisofs->pz_uncompressed_size ++			(((int64_t)1) << zisofs->pz_log2_bs) - 1)+			>> zisofs->pz_log2_bs);+		xsize = (ceil + 1) * 4;+		if (zisofs->block_pointers_alloc < xsize) {+			size_t alloc;++			if (zisofs->block_pointers != NULL)+				free(zisofs->block_pointers);+			alloc = ((xsize >> 10) + 1) << 10;+			zisofs->block_pointers = malloc(alloc);+			if (zisofs->block_pointers == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "No memory for zisofs decompression");+				return (ARCHIVE_FATAL);+			}+			zisofs->block_pointers_alloc = alloc;+		}+		zisofs->block_pointers_size = xsize;++		/* Allocate uncompressed data buffer. */+		xsize = (size_t)1UL << zisofs->pz_log2_bs;+		if (zisofs->uncompressed_buffer_size < xsize) {+			if (zisofs->uncompressed_buffer != NULL)+				free(zisofs->uncompressed_buffer);+			zisofs->uncompressed_buffer = malloc(xsize);+			if (zisofs->uncompressed_buffer == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "No memory for zisofs decompression");+				return (ARCHIVE_FATAL);+			}+		}+		zisofs->uncompressed_buffer_size = xsize;++		/*+		 * Read the file header, and check the magic code of zisofs.+		 */+		if (zisofs->header_avail < sizeof(zisofs->header)) {+			xsize = sizeof(zisofs->header) - zisofs->header_avail;+			if (avail < xsize)+				xsize = avail;+			memcpy(zisofs->header + zisofs->header_avail, p, xsize);+			zisofs->header_avail += xsize;+			avail -= xsize;+			p += xsize;+		}+		if (!zisofs->header_passed &&+		    zisofs->header_avail == sizeof(zisofs->header)) {+			int err = 0;++			if (memcmp(zisofs->header, zisofs_magic,+			    sizeof(zisofs_magic)) != 0)+				err = 1;+			if (archive_le32dec(zisofs->header + 8)+			    != zisofs->pz_uncompressed_size)+				err = 1;+			if (zisofs->header[12] != 4)+				err = 1;+			if (zisofs->header[13] != zisofs->pz_log2_bs)+				err = 1;+			if (err) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Illegal zisofs file body");+				return (ARCHIVE_FATAL);+			}+			zisofs->header_passed = 1;+		}+		/*+		 * Read block pointers.+		 */+		if (zisofs->header_passed &&+		    zisofs->block_pointers_avail < zisofs->block_pointers_size) {+			xsize = zisofs->block_pointers_size+			    - zisofs->block_pointers_avail;+			if (avail < xsize)+				xsize = avail;+			memcpy(zisofs->block_pointers+			    + zisofs->block_pointers_avail, p, xsize);+			zisofs->block_pointers_avail += xsize;+			avail -= xsize;+			p += xsize;+		    	if (zisofs->block_pointers_avail+			    == zisofs->block_pointers_size) {+				/* We've got all block pointers and initialize+				 * related variables.	*/+				zisofs->block_off = 0;+				zisofs->block_avail = 0;+				/* Complete a initialization */+				zisofs->initialized = 1;+			}+		}++		if (!zisofs->initialized)+			goto next_data; /* We need more data. */+	}++	/*+	 * Get block offsets from block pointers.+	 */+	if (zisofs->block_avail == 0) {+		uint32_t bst, bed;++		if (zisofs->block_off + 4 >= zisofs->block_pointers_size) {+			/* There isn't a pair of offsets. */+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Illegal zisofs block pointers");+			return (ARCHIVE_FATAL);+		}+		bst = archive_le32dec(+		    zisofs->block_pointers + zisofs->block_off);+		if (bst != zisofs->pz_offset + (bytes_read - avail)) {+			/* TODO: Should we seek offset of current file+			 * by bst ? */+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Illegal zisofs block pointers(cannot seek)");+			return (ARCHIVE_FATAL);+		}+		bed = archive_le32dec(+		    zisofs->block_pointers + zisofs->block_off + 4);+		if (bed < bst) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Illegal zisofs block pointers");+			return (ARCHIVE_FATAL);+		}+		zisofs->block_avail = bed - bst;+		zisofs->block_off += 4;++		/* Initialize compression library for new block. */+		if (zisofs->stream_valid)+			r = inflateReset(&zisofs->stream);+		else+			r = inflateInit(&zisofs->stream);+		if (r != Z_OK) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Can't initialize zisofs decompression.");+			return (ARCHIVE_FATAL);+		}+		zisofs->stream_valid = 1;+		zisofs->stream.total_in = 0;+		zisofs->stream.total_out = 0;+	}++	/*+	 * Make uncompressed data.+	 */+	if (zisofs->block_avail == 0) {+		memset(zisofs->uncompressed_buffer, 0,+		    zisofs->uncompressed_buffer_size);+		uncompressed_size = zisofs->uncompressed_buffer_size;+	} else {+		zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p;+		if (avail > zisofs->block_avail)+			zisofs->stream.avail_in = zisofs->block_avail;+		else+			zisofs->stream.avail_in = (uInt)avail;+		zisofs->stream.next_out = zisofs->uncompressed_buffer;+		zisofs->stream.avail_out =+		    (uInt)zisofs->uncompressed_buffer_size;++		r = inflate(&zisofs->stream, 0);+		switch (r) {+		case Z_OK: /* Decompressor made some progress.*/+		case Z_STREAM_END: /* Found end of stream. */+			break;+		default:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "zisofs decompression failed (%d)", r);+			return (ARCHIVE_FATAL);+		}+		uncompressed_size =+		    zisofs->uncompressed_buffer_size - zisofs->stream.avail_out;+		avail -= zisofs->stream.next_in - p;+		zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p);+	}+next_data:+	bytes_read -= avail;+	*buff = zisofs->uncompressed_buffer;+	*size = uncompressed_size;+	*offset = iso9660->entry_sparse_offset;+	iso9660->entry_sparse_offset += uncompressed_size;+	iso9660->entry_bytes_remaining -= bytes_read;+	iso9660->current_position += bytes_read;+	zisofs->pz_offset += (uint32_t)bytes_read;+	iso9660->entry_bytes_unconsumed += bytes_read;++	return (ARCHIVE_OK);+}++#else /* HAVE_ZLIB_H */++static int+zisofs_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{++	(void)buff;/* UNUSED */+	(void)size;/* UNUSED */+	(void)offset;/* UNUSED */+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "zisofs is not supported on this platform.");+	return (ARCHIVE_FAILED);+}++#endif /* HAVE_ZLIB_H */++static int+archive_read_format_iso9660_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	ssize_t bytes_read;+	struct iso9660 *iso9660;++	iso9660 = (struct iso9660 *)(a->format->data);++	if (iso9660->entry_bytes_unconsumed) {+		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);+		iso9660->entry_bytes_unconsumed = 0;+	}++	if (iso9660->entry_bytes_remaining <= 0) {+		if (iso9660->entry_content != NULL)+			iso9660->entry_content = iso9660->entry_content->next;+		if (iso9660->entry_content == NULL) {+			*buff = NULL;+			*size = 0;+			*offset = iso9660->entry_sparse_offset;+			return (ARCHIVE_EOF);+		}+		/* Seek forward to the start of the entry. */+		if (iso9660->current_position < iso9660->entry_content->offset) {+			int64_t step;++			step = iso9660->entry_content->offset -+			    iso9660->current_position;+			step = __archive_read_consume(a, step);+			if (step < 0)+				return ((int)step);+			iso9660->current_position =+			    iso9660->entry_content->offset;+		}+		if (iso9660->entry_content->offset < iso9660->current_position) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Ignoring out-of-order file (%s) %jd < %jd",+			    iso9660->pathname.s,+			    (intmax_t)iso9660->entry_content->offset,+			    (intmax_t)iso9660->current_position);+			*buff = NULL;+			*size = 0;+			*offset = iso9660->entry_sparse_offset;+			return (ARCHIVE_WARN);+		}+		iso9660->entry_bytes_remaining = iso9660->entry_content->size;+	}+	if (iso9660->entry_zisofs.pz)+		return (zisofs_read_data(a, buff, size, offset));++	*buff = __archive_read_ahead(a, 1, &bytes_read);+	if (bytes_read == 0)+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Truncated input file");+	if (*buff == NULL)+		return (ARCHIVE_FATAL);+	if (bytes_read > iso9660->entry_bytes_remaining)+		bytes_read = (ssize_t)iso9660->entry_bytes_remaining;+	*size = bytes_read;+	*offset = iso9660->entry_sparse_offset;+	iso9660->entry_sparse_offset += bytes_read;+	iso9660->entry_bytes_remaining -= bytes_read;+	iso9660->entry_bytes_unconsumed = bytes_read;+	iso9660->current_position += bytes_read;+	return (ARCHIVE_OK);+}++static int+archive_read_format_iso9660_cleanup(struct archive_read *a)+{+	struct iso9660 *iso9660;+	int r = ARCHIVE_OK;++	iso9660 = (struct iso9660 *)(a->format->data);+	release_files(iso9660);+	free(iso9660->read_ce_req.reqs);+	archive_string_free(&iso9660->pathname);+	archive_string_free(&iso9660->previous_pathname);+	free(iso9660->pending_files.files);+#ifdef HAVE_ZLIB_H+	free(iso9660->entry_zisofs.uncompressed_buffer);+	free(iso9660->entry_zisofs.block_pointers);+	if (iso9660->entry_zisofs.stream_valid) {+		if (inflateEnd(&iso9660->entry_zisofs.stream) != Z_OK) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Failed to clean up zlib decompressor");+			r = ARCHIVE_FATAL;+		}+	}+#endif+	free(iso9660->utf16be_path);+	free(iso9660->utf16be_previous_path);+	free(iso9660);+	(a->format->data) = NULL;+	return (r);+}++/*+ * This routine parses a single ISO directory record, makes sense+ * of any extensions, and stores the result in memory.+ */+static struct file_info *+parse_file_info(struct archive_read *a, struct file_info *parent,+    const unsigned char *isodirrec, size_t reclen)+{+	struct iso9660 *iso9660;+	struct file_info *file, *filep;+	size_t name_len;+	const unsigned char *rr_start, *rr_end;+	const unsigned char *p;+	size_t dr_len;+	uint64_t fsize, offset;+	int32_t location;+	int flags;++	iso9660 = (struct iso9660 *)(a->format->data);++	if (reclen != 0)+		dr_len = (size_t)isodirrec[DR_length_offset];+	/*+	 * Sanity check that reclen is not zero and dr_len is greater than+	 * reclen but at least 34+	 */+	if (reclen == 0 || reclen < dr_len || dr_len < 34) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			"Invalid length of directory record");+		return (NULL);+	}+	name_len = (size_t)isodirrec[DR_name_len_offset];+	location = archive_le32dec(isodirrec + DR_extent_offset);+	fsize = toi(isodirrec + DR_size_offset, DR_size_size);+	/* Sanity check that name_len doesn't exceed dr_len. */+	if (dr_len - 33 < name_len || name_len == 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Invalid length of file identifier");+		return (NULL);+	}+	/* Sanity check that location doesn't exceed volume block.+	 * Don't check lower limit of location; it's possibility+	 * the location has negative value when file type is symbolic+	 * link or file size is zero. As far as I know latest mkisofs+	 * do that.+	 */+	if (location > 0 &&+	    (location + ((fsize + iso9660->logical_block_size -1)+	       / iso9660->logical_block_size))+			> (uint32_t)iso9660->volume_block) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Invalid location of extent of file");+		return (NULL);+	}+	/* Sanity check that location doesn't have a negative value+	 * when the file is not empty. it's too large. */+	if (fsize != 0 && location < 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Invalid location of extent of file");+		return (NULL);+	}++	/* Sanity check that this entry does not create a cycle. */+	offset = iso9660->logical_block_size * (uint64_t)location;+	for (filep = parent; filep != NULL; filep = filep->parent) {+		if (filep->offset == offset) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+			    "Directory structure contains loop");+			return (NULL);+		}+	}++	/* Create a new file entry and copy data from the ISO dir record. */+	file = (struct file_info *)calloc(1, sizeof(*file));+	if (file == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "No memory for file entry");+		return (NULL);+	}+	file->parent = parent;+	file->offset = offset;+	file->size = fsize;+	file->mtime = isodate7(isodirrec + DR_date_offset);+	file->ctime = file->atime = file->mtime;+	file->rede_files.first = NULL;+	file->rede_files.last = &(file->rede_files.first);++	p = isodirrec + DR_name_offset;+	/* Rockridge extensions (if any) follow name.  Compute this+	 * before fidgeting the name_len below. */+	rr_start = p + name_len + (name_len & 1 ? 0 : 1);+	rr_end = isodirrec + dr_len;++	if (iso9660->seenJoliet) {+		/* Joliet names are max 64 chars (128 bytes) according to spec,+		 * but genisoimage/mkisofs allows recording longer Joliet+		 * names which are 103 UCS2 characters(206 bytes) by their+		 * option '-joliet-long'.+		 */+		if (name_len > 206)+			name_len = 206;+		name_len &= ~1;++		/* trim trailing first version and dot from filename.+		 *+		 * Remember we were in UTF-16BE land!+		 * SEPARATOR 1 (.) and SEPARATOR 2 (;) are both+		 * 16 bits big endian characters on Joliet.+		 *+		 * TODO: sanitize filename?+		 *       Joliet allows any UCS-2 char except:+		 *       *, /, :, ;, ? and \.+		 */+		/* Chop off trailing ';1' from files. */+		if (name_len > 4 && p[name_len-4] == 0 && p[name_len-3] == ';'+		    && p[name_len-2] == 0 && p[name_len-1] == '1')+			name_len -= 4;+#if 0 /* XXX: this somehow manages to strip of single-character file extensions, like '.c'. */+		/* Chop off trailing '.' from filenames. */+		if (name_len > 2 && p[name_len-2] == 0 && p[name_len-1] == '.')+			name_len -= 2;+#endif+		if ((file->utf16be_name = malloc(name_len)) == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "No memory for file name");+			goto fail;+		}+		memcpy(file->utf16be_name, p, name_len);+		file->utf16be_bytes = name_len;+	} else {+		/* Chop off trailing ';1' from files. */+		if (name_len > 2 && p[name_len - 2] == ';' &&+				p[name_len - 1] == '1')+			name_len -= 2;+		/* Chop off trailing '.' from filenames. */+		if (name_len > 1 && p[name_len - 1] == '.')+			--name_len;++		archive_strncpy(&file->name, (const char *)p, name_len);+	}++	flags = isodirrec[DR_flags_offset];+	if (flags & 0x02)+		file->mode = AE_IFDIR | 0700;+	else+		file->mode = AE_IFREG | 0400;+	if (flags & 0x80)+		file->multi_extent = 1;+	else+		file->multi_extent = 0;+	/*+	 * Use a location for the file number, which is treated as an inode+	 * number to find out hardlink target. If Rockridge extensions is+	 * being used, the file number will be overwritten by FILE SERIAL+	 * NUMBER of RRIP "PX" extension.+	 * Note: Old mkisofs did not record that FILE SERIAL NUMBER+	 * in ISO images.+	 * Note2: xorriso set 0 to the location of a symlink file. +	 */+	if (file->size == 0 && location >= 0) {+		/* If file->size is zero, its location points wrong place,+		 * and so we should not use it for the file number.+		 * When the location has negative value, it can be used+		 * for the file number.+		 */+		file->number = -1;+		/* Do not appear before any directory entries. */+		file->offset = -1;+	} else+		file->number = (int64_t)(uint32_t)location;++	/* Rockridge extensions overwrite information from above. */+	if (iso9660->opt_support_rockridge) {+		if (parent == NULL && rr_end - rr_start >= 7) {+			p = rr_start;+			if (memcmp(p, "SP\x07\x01\xbe\xef", 6) == 0) {+				/*+				 * SP extension stores the suspOffset+				 * (Number of bytes to skip between+				 * filename and SUSP records.)+				 * It is mandatory by the SUSP standard+				 * (IEEE 1281).+				 *+				 * It allows SUSP to coexist with+				 * non-SUSP uses of the System+				 * Use Area by placing non-SUSP data+				 * before SUSP data.+				 *+				 * SP extension must be in the root+				 * directory entry, disable all SUSP+				 * processing if not found.+				 */+				iso9660->suspOffset = p[6];+				iso9660->seenSUSP = 1;+				rr_start += 7;+			}+		}+		if (iso9660->seenSUSP) {+			int r;++			file->name_continues = 0;+			file->symlink_continues = 0;+			rr_start += iso9660->suspOffset;+			r = parse_rockridge(a, file, rr_start, rr_end);+			if (r != ARCHIVE_OK)+				goto fail;+			/*+			 * A file size of symbolic link files in ISO images+			 * made by makefs is not zero and its location is+			 * the same as those of next regular file. That is+			 * the same as hard like file and it causes unexpected+			 * error. +			 */+			if (file->size > 0 &&+			    (file->mode & AE_IFMT) == AE_IFLNK) {+				file->size = 0;+				file->number = -1;+				file->offset = -1;+			}+		} else+			/* If there isn't SUSP, disable parsing+			 * rock ridge extensions. */+			iso9660->opt_support_rockridge = 0;+	}++	file->nlinks = 1;/* Reset nlink. we'll calculate it later. */+	/* Tell file's parent how many children that parent has. */+	if (parent != NULL && (flags & 0x02))+		parent->subdirs++;++	if (iso9660->seenRockridge) {+		if (parent != NULL && parent->parent == NULL &&+		    (flags & 0x02) && iso9660->rr_moved == NULL &&+		    file->name.s &&+		    (strcmp(file->name.s, "rr_moved") == 0 ||+		     strcmp(file->name.s, ".rr_moved") == 0)) {+			iso9660->rr_moved = file;+			file->rr_moved = 1;+			file->rr_moved_has_re_only = 1;+			file->re = 0;+			parent->subdirs--;+		} else if (file->re) {+			/*+			 * Sanity check: file's parent is rr_moved.+			 */+			if (parent == NULL || parent->rr_moved == 0) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Invalid Rockridge RE");+				goto fail;+			}+			/*+			 * Sanity check: file does not have "CL" extension.+			 */+			if (file->cl_offset) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Invalid Rockridge RE and CL");+				goto fail;+			}+			/*+			 * Sanity check: The file type must be a directory.+			 */+			if ((flags & 0x02) == 0) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Invalid Rockridge RE");+				goto fail;+			}+		} else if (parent != NULL && parent->rr_moved)+			file->rr_moved_has_re_only = 0;+		else if (parent != NULL && (flags & 0x02) &&+		    (parent->re || parent->re_descendant))+			file->re_descendant = 1;+		if (file->cl_offset) {+			struct file_info *r;++			if (parent == NULL || parent->parent == NULL) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Invalid Rockridge CL");+				goto fail;+			}+			/*+			 * Sanity check: The file type must be a regular file.+			 */+			if ((flags & 0x02) != 0) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Invalid Rockridge CL");+				goto fail;+			}+			parent->subdirs++;+			/* Overwrite an offset and a number of this "CL" entry+			 * to appear before other dirs. "+1" to those is to+			 * make sure to appear after "RE" entry which this+			 * "CL" entry should be connected with. */+			file->offset = file->number = file->cl_offset + 1;++			/*+			 * Sanity check: cl_offset does not point at its+			 * the parents or itself.+			 */+			for (r = parent; r; r = r->parent) {+				if (r->offset == file->cl_offset) {+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_MISC,+					    "Invalid Rockridge CL");+					goto fail;+				}+			}+			if (file->cl_offset == file->offset ||+			    parent->rr_moved) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Invalid Rockridge CL");+				goto fail;+			}+		}+	}++#if DEBUG+	/* DEBUGGING: Warn about attributes I don't yet fully support. */+	if ((flags & ~0x02) != 0) {+		fprintf(stderr, "\n ** Unrecognized flag: ");+		dump_isodirrec(stderr, isodirrec);+		fprintf(stderr, "\n");+	} else if (toi(isodirrec + DR_volume_sequence_number_offset, 2) != 1) {+		fprintf(stderr, "\n ** Unrecognized sequence number: ");+		dump_isodirrec(stderr, isodirrec);+		fprintf(stderr, "\n");+	} else if (*(isodirrec + DR_file_unit_size_offset) != 0) {+		fprintf(stderr, "\n ** Unexpected file unit size: ");+		dump_isodirrec(stderr, isodirrec);+		fprintf(stderr, "\n");+	} else if (*(isodirrec + DR_interleave_offset) != 0) {+		fprintf(stderr, "\n ** Unexpected interleave: ");+		dump_isodirrec(stderr, isodirrec);+		fprintf(stderr, "\n");+	} else if (*(isodirrec + DR_ext_attr_length_offset) != 0) {+		fprintf(stderr, "\n ** Unexpected extended attribute length: ");+		dump_isodirrec(stderr, isodirrec);+		fprintf(stderr, "\n");+	}+#endif+	register_file(iso9660, file);+	return (file);+fail:+	archive_string_free(&file->name);+	free(file);+	return (NULL);+}++static int+parse_rockridge(struct archive_read *a, struct file_info *file,+    const unsigned char *p, const unsigned char *end)+{+	struct iso9660 *iso9660;+	int entry_seen = 0;++	iso9660 = (struct iso9660 *)(a->format->data);++	while (p + 4 <= end  /* Enough space for another entry. */+	    && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */+	    && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */+	    && p[2] >= 4 /* Sanity-check length. */+	    && p + p[2] <= end) { /* Sanity-check length. */+		const unsigned char *data = p + 4;+		int data_length = p[2] - 4;+		int version = p[3];++		switch(p[0]) {+		case 'C':+			if (p[1] == 'E') {+				if (version == 1 && data_length == 24) {+					/*+					 * CE extension comprises:+					 *   8 byte sector containing extension+					 *   8 byte offset w/in above sector+					 *   8 byte length of continuation+					 */+					int32_t location =+					    archive_le32dec(data);+					file->ce_offset =+					    archive_le32dec(data+8);+					file->ce_size =+					    archive_le32dec(data+16);+					if (register_CE(a, location, file)+					    != ARCHIVE_OK)+						return (ARCHIVE_FATAL);+				}+			}+			else if (p[1] == 'L') {+				if (version == 1 && data_length == 8) {+					file->cl_offset = (uint64_t)+					    iso9660->logical_block_size *+					    (uint64_t)archive_le32dec(data);+					iso9660->seenRockridge = 1;+				}+			}+			break;+		case 'N':+			if (p[1] == 'M') {+				if (version == 1) {+					parse_rockridge_NM1(file,+					    data, data_length);+					iso9660->seenRockridge = 1;+				}+			}+			break;+		case 'P':+			/*+			 * PD extension is padding;+			 * contents are always ignored.+			 *+			 * PL extension won't appear;+			 * contents are always ignored.+			 */+			if (p[1] == 'N') {+				if (version == 1 && data_length == 16) {+					file->rdev = toi(data,4);+					file->rdev <<= 32;+					file->rdev |= toi(data + 8, 4);+					iso9660->seenRockridge = 1;+				}+			}+			else if (p[1] == 'X') {+				/*+				 * PX extension comprises:+				 *   8 bytes for mode,+				 *   8 bytes for nlinks,+				 *   8 bytes for uid,+				 *   8 bytes for gid,+				 *   8 bytes for inode.+				 */+				if (version == 1) {+					if (data_length >= 8)+						file->mode+						    = toi(data, 4);+					if (data_length >= 16)+						file->nlinks+						    = toi(data + 8, 4);+					if (data_length >= 24)+						file->uid+						    = toi(data + 16, 4);+					if (data_length >= 32)+						file->gid+						    = toi(data + 24, 4);+					if (data_length >= 40)+						file->number+						    = toi(data + 32, 4);+					iso9660->seenRockridge = 1;+				}+			}+			break;+		case 'R':+			if (p[1] == 'E' && version == 1) {+				file->re = 1;+				iso9660->seenRockridge = 1;+			}+			else if (p[1] == 'R' && version == 1) {+				/*+				 * RR extension comprises:+				 *    one byte flag value+				 * This extension is obsolete,+				 * so contents are always ignored.+				 */+			}+			break;+		case 'S':+			if (p[1] == 'L') {+				if (version == 1) {+					parse_rockridge_SL1(file,+					    data, data_length);+					iso9660->seenRockridge = 1;+				}+			}+			else if (p[1] == 'T'+			    && data_length == 0 && version == 1) {+				/*+				 * ST extension marks end of this+				 * block of SUSP entries.+				 *+				 * It allows SUSP to coexist with+				 * non-SUSP uses of the System+				 * Use Area by placing non-SUSP data+				 * after SUSP data.+				 */+				iso9660->seenSUSP = 0;+				iso9660->seenRockridge = 0;+				return (ARCHIVE_OK);+			}+			break;+		case 'T':+			if (p[1] == 'F') {+				if (version == 1) {+					parse_rockridge_TF1(file,+					    data, data_length);+					iso9660->seenRockridge = 1;+				}+			}+			break;+		case 'Z':+			if (p[1] == 'F') {+				if (version == 1)+					parse_rockridge_ZF1(file,+					    data, data_length);+			}+			break;+		default:+			break;+		}++		p += p[2];+		entry_seen = 1;+	}++	if (entry_seen)+		return (ARCHIVE_OK);+	else {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+				  "Tried to parse Rockridge extensions, but none found");+		return (ARCHIVE_WARN);+	}+}++static int+register_CE(struct archive_read *a, int32_t location,+    struct file_info *file)+{+	struct iso9660 *iso9660;+	struct read_ce_queue *heap;+	struct read_ce_req *p;+	uint64_t offset, parent_offset;+	int hole, parent;++	iso9660 = (struct iso9660 *)(a->format->data);+	offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size;+	if (((file->mode & AE_IFMT) == AE_IFREG &&+	    offset >= file->offset) ||+	    offset < iso9660->current_position ||+	    (((uint64_t)file->ce_offset) + file->ce_size)+	      > (uint64_t)iso9660->logical_block_size ||+	    offset + file->ce_offset + file->ce_size+		  > iso9660->volume_size) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Invalid parameter in SUSP \"CE\" extension");+		return (ARCHIVE_FATAL);+	}++	/* Expand our CE list as necessary. */+	heap = &(iso9660->read_ce_req);+	if (heap->cnt >= heap->allocated) {+		int new_size;++		if (heap->allocated < 16)+			new_size = 16;+		else+			new_size = heap->allocated * 2;+		/* Overflow might keep us from growing the list. */+		if (new_size <= heap->allocated) {+			archive_set_error(&a->archive, ENOMEM, "Out of memory");+			return (ARCHIVE_FATAL);+		}+		p = calloc(new_size, sizeof(p[0]));+		if (p == NULL) {+			archive_set_error(&a->archive, ENOMEM, "Out of memory");+			return (ARCHIVE_FATAL);+		}+		if (heap->reqs != NULL) {+			memcpy(p, heap->reqs, heap->cnt * sizeof(*p));+			free(heap->reqs);+		}+		heap->reqs = p;+		heap->allocated = new_size;+	}++	/*+	 * Start with hole at end, walk it up tree to find insertion point.+	 */+	hole = heap->cnt++;+	while (hole > 0) {+		parent = (hole - 1)/2;+		parent_offset = heap->reqs[parent].offset;+		if (offset >= parent_offset) {+			heap->reqs[hole].offset = offset;+			heap->reqs[hole].file = file;+			return (ARCHIVE_OK);+		}+		/* Move parent into hole <==> move hole up tree. */+		heap->reqs[hole] = heap->reqs[parent];+		hole = parent;+	}+	heap->reqs[0].offset = offset;+	heap->reqs[0].file = file;+	return (ARCHIVE_OK);+}++static void+next_CE(struct read_ce_queue *heap)+{+	uint64_t a_offset, b_offset, c_offset;+	int a, b, c;+	struct read_ce_req tmp;++	if (heap->cnt < 1)+		return;++	/*+	 * Move the last item in the heap to the root of the tree+	 */+	heap->reqs[0] = heap->reqs[--(heap->cnt)];++	/*+	 * Rebalance the heap.+	 */+	a = 0; /* Starting element and its offset */+	a_offset = heap->reqs[a].offset;+	for (;;) {+		b = a + a + 1; /* First child */+		if (b >= heap->cnt)+			return;+		b_offset = heap->reqs[b].offset;+		c = b + 1; /* Use second child if it is smaller. */+		if (c < heap->cnt) {+			c_offset = heap->reqs[c].offset;+			if (c_offset < b_offset) {+				b = c;+				b_offset = c_offset;+			}+		}+		if (a_offset <= b_offset)+			return;+		tmp = heap->reqs[a];+		heap->reqs[a] = heap->reqs[b];+		heap->reqs[b] = tmp;+		a = b;+	}+}+++static int+read_CE(struct archive_read *a, struct iso9660 *iso9660)+{+	struct read_ce_queue *heap;+	const unsigned char *b, *p, *end;+	struct file_info *file;+	size_t step;+	int r;++	/* Read data which RRIP "CE" extension points. */+	heap = &(iso9660->read_ce_req);+	step = iso9660->logical_block_size;+	while (heap->cnt &&+	    heap->reqs[0].offset == iso9660->current_position) {+		b = __archive_read_ahead(a, step, NULL);+		if (b == NULL) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "Failed to read full block when scanning "+			    "ISO9660 directory list");+			return (ARCHIVE_FATAL);+		}+		do {+			file = heap->reqs[0].file;+			if (file->ce_offset + file->ce_size > step) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Malformed CE information");+				return (ARCHIVE_FATAL);+			}+			p = b + file->ce_offset;+			end = p + file->ce_size;+			next_CE(heap);+			r = parse_rockridge(a, file, p, end);+			if (r != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		} while (heap->cnt &&+		    heap->reqs[0].offset == iso9660->current_position);+		/* NOTE: Do not move this consume's code to front of+		 * do-while loop. Registration of nested CE extension+		 * might cause error because of current position. */+		__archive_read_consume(a, step);+		iso9660->current_position += step;+	}+	return (ARCHIVE_OK);+}++static void+parse_rockridge_NM1(struct file_info *file,+		    const unsigned char *data, int data_length)+{+	if (!file->name_continues)+		archive_string_empty(&file->name);+	file->name_continues = 0;+	if (data_length < 1)+		return;+	/*+	 * NM version 1 extension comprises:+	 *   1 byte flag, value is one of:+	 *     = 0: remainder is name+	 *     = 1: remainder is name, next NM entry continues name+	 *     = 2: "."+	 *     = 4: ".."+	 *     = 32: Implementation specific+	 *     All other values are reserved.+	 */+	switch(data[0]) {+	case 0:+		if (data_length < 2)+			return;+		archive_strncat(&file->name,+		    (const char *)data + 1, data_length - 1);+		break;+	case 1:+		if (data_length < 2)+			return;+		archive_strncat(&file->name,+		    (const char *)data + 1, data_length - 1);+		file->name_continues = 1;+		break;+	case 2:+		archive_strcat(&file->name, ".");+		break;+	case 4:+		archive_strcat(&file->name, "..");+		break;+	default:+		return;+	}++}++static void+parse_rockridge_TF1(struct file_info *file, const unsigned char *data,+    int data_length)+{+	char flag;+	/*+	 * TF extension comprises:+	 *   one byte flag+	 *   create time (optional)+	 *   modify time (optional)+	 *   access time (optional)+	 *   attribute time (optional)+	 *  Time format and presence of fields+	 *  is controlled by flag bits.+	 */+	if (data_length < 1)+		return;+	flag = data[0];+	++data;+	--data_length;+	if (flag & 0x80) {+		/* Use 17-byte time format. */+		if ((flag & 1) && data_length >= 17) {+			/* Create time. */+			file->birthtime_is_set = 1;+			file->birthtime = isodate17(data);+			data += 17;+			data_length -= 17;+		}+		if ((flag & 2) && data_length >= 17) {+			/* Modify time. */+			file->mtime = isodate17(data);+			data += 17;+			data_length -= 17;+		}+		if ((flag & 4) && data_length >= 17) {+			/* Access time. */+			file->atime = isodate17(data);+			data += 17;+			data_length -= 17;+		}+		if ((flag & 8) && data_length >= 17) {+			/* Attribute change time. */+			file->ctime = isodate17(data);+		}+	} else {+		/* Use 7-byte time format. */+		if ((flag & 1) && data_length >= 7) {+			/* Create time. */+			file->birthtime_is_set = 1;+			file->birthtime = isodate7(data);+			data += 7;+			data_length -= 7;+		}+		if ((flag & 2) && data_length >= 7) {+			/* Modify time. */+			file->mtime = isodate7(data);+			data += 7;+			data_length -= 7;+		}+		if ((flag & 4) && data_length >= 7) {+			/* Access time. */+			file->atime = isodate7(data);+			data += 7;+			data_length -= 7;+		}+		if ((flag & 8) && data_length >= 7) {+			/* Attribute change time. */+			file->ctime = isodate7(data);+		}+	}+}++static void+parse_rockridge_SL1(struct file_info *file, const unsigned char *data,+    int data_length)+{+	const char *separator = "";++	if (!file->symlink_continues || file->symlink.length < 1)+		archive_string_empty(&file->symlink);+	file->symlink_continues = 0;++	/*+	 * Defined flag values:+	 *  0: This is the last SL record for this symbolic link+	 *  1: this symbolic link field continues in next SL entry+	 *  All other values are reserved.+	 */+	if (data_length < 1)+		return;+	switch(*data) {+	case 0:+		break;+	case 1:+		file->symlink_continues = 1;+		break;+	default:+		return;+	}+	++data;  /* Skip flag byte. */+	--data_length;++	/*+	 * SL extension body stores "components".+	 * Basically, this is a complicated way of storing+	 * a POSIX path.  It also interferes with using+	 * symlinks for storing non-path data. <sigh>+	 *+	 * Each component is 2 bytes (flag and length)+	 * possibly followed by name data.+	 */+	while (data_length >= 2) {+		unsigned char flag = *data++;+		unsigned char nlen = *data++;+		data_length -= 2;++		archive_strcat(&file->symlink, separator);+		separator = "/";++		switch(flag) {+		case 0: /* Usual case, this is text. */+			if (data_length < nlen)+				return;+			archive_strncat(&file->symlink,+			    (const char *)data, nlen);+			break;+		case 0x01: /* Text continues in next component. */+			if (data_length < nlen)+				return;+			archive_strncat(&file->symlink,+			    (const char *)data, nlen);+			separator = "";+			break;+		case 0x02: /* Current dir. */+			archive_strcat(&file->symlink, ".");+			break;+		case 0x04: /* Parent dir. */+			archive_strcat(&file->symlink, "..");+			break;+		case 0x08: /* Root of filesystem. */+			archive_strcat(&file->symlink, "/");+			separator = "";+			break;+		case 0x10: /* Undefined (historically "volume root" */+			archive_string_empty(&file->symlink);+			archive_strcat(&file->symlink, "ROOT");+			break;+		case 0x20: /* Undefined (historically "hostname") */+			archive_strcat(&file->symlink, "hostname");+			break;+		default:+			/* TODO: issue a warning ? */+			return;+		}+		data += nlen;+		data_length -= nlen;+	}+}++static void+parse_rockridge_ZF1(struct file_info *file, const unsigned char *data,+    int data_length)+{++	if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) {+		/* paged zlib */+		file->pz = 1;+		file->pz_log2_bs = data[3];+		file->pz_uncompressed_size = archive_le32dec(&data[4]);+	}+}++static void+register_file(struct iso9660 *iso9660, struct file_info *file)+{++	file->use_next = iso9660->use_files;+	iso9660->use_files = file;+}++static void+release_files(struct iso9660 *iso9660)+{+	struct content *con, *connext;+	struct file_info *file;++	file = iso9660->use_files;+	while (file != NULL) {+		struct file_info *next = file->use_next;++		archive_string_free(&file->name);+		archive_string_free(&file->symlink);+		free(file->utf16be_name);+		con = file->contents.first;+		while (con != NULL) {+			connext = con->next;+			free(con);+			con = connext;+		}+		free(file);+		file = next;+	}+}++static int+next_entry_seek(struct archive_read *a, struct iso9660 *iso9660,+    struct file_info **pfile)+{+	struct file_info *file;+	int r;++	r = next_cache_entry(a, iso9660, pfile);+	if (r != ARCHIVE_OK)+		return (r);+	file = *pfile;++	/* Don't waste time seeking for zero-length bodies. */+	if (file->size == 0)+		file->offset = iso9660->current_position;++	/* flush any remaining bytes from the last round to ensure+	 * we're positioned */+	if (iso9660->entry_bytes_unconsumed) {+		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);+		iso9660->entry_bytes_unconsumed = 0;+	}++	/* Seek forward to the start of the entry. */+	if (iso9660->current_position < file->offset) {+		int64_t step;++		step = file->offset - iso9660->current_position;+		step = __archive_read_consume(a, step);+		if (step < 0)+			return ((int)step);+		iso9660->current_position = file->offset;+	}++	/* We found body of file; handle it now. */+	return (ARCHIVE_OK);+}++static int+next_cache_entry(struct archive_read *a, struct iso9660 *iso9660,+    struct file_info **pfile)+{+	struct file_info *file;+	struct {+		struct file_info	*first;+		struct file_info	**last;+	}	empty_files;+	int64_t number;+	int count;++	file = cache_get_entry(iso9660);+	if (file != NULL) {+		*pfile = file;+		return (ARCHIVE_OK);+	}++	for (;;) {+		struct file_info *re, *d;++		*pfile = file = next_entry(iso9660);+		if (file == NULL) {+			/*+			 * If directory entries all which are descendant of+			 * rr_moved are still remaining, expose their.+			 */+			if (iso9660->re_files.first != NULL && +			    iso9660->rr_moved != NULL &&+			    iso9660->rr_moved->rr_moved_has_re_only)+				/* Expose "rr_moved" entry. */+				cache_add_entry(iso9660, iso9660->rr_moved);+			while ((re = re_get_entry(iso9660)) != NULL) {+				/* Expose its descendant dirs. */+				while ((d = rede_get_entry(re)) != NULL)+					cache_add_entry(iso9660, d);+			}+			if (iso9660->cache_files.first != NULL)+				return (next_cache_entry(a, iso9660, pfile));+			return (ARCHIVE_EOF);+		}++		if (file->cl_offset) {+			struct file_info *first_re = NULL;+			int nexted_re = 0;++			/*+			 * Find "RE" dir for the current file, which+			 * has "CL" flag.+			 */+			while ((re = re_get_entry(iso9660))+			    != first_re) {+				if (first_re == NULL)+					first_re = re;+				if (re->offset == file->cl_offset) {+					re->parent->subdirs--;+					re->parent = file->parent;+					re->re = 0;+					if (re->parent->re_descendant) {+						nexted_re = 1;+						re->re_descendant = 1;+						if (rede_add_entry(re) < 0)+							goto fatal_rr;+						/* Move a list of descendants+						 * to a new ancestor. */+						while ((d = rede_get_entry(+						    re)) != NULL)+							if (rede_add_entry(d)+							    < 0)+								goto fatal_rr;+						break;+					}+					/* Replace the current file+					 * with "RE" dir */+					*pfile = file = re;+					/* Expose its descendant */+					while ((d = rede_get_entry(+					    file)) != NULL)+						cache_add_entry(+						    iso9660, d);+					break;+				} else+					re_add_entry(iso9660, re);+			}+			if (nexted_re) {+				/*+				 * Do not expose this at this time+				 * because we have not gotten its full-path+				 * name yet.+				 */+				continue;+			}+		} else if ((file->mode & AE_IFMT) == AE_IFDIR) {+			int r;++			/* Read file entries in this dir. */+			r = read_children(a, file);+			if (r != ARCHIVE_OK)+				return (r);++			/*+			 * Handle a special dir of Rockridge extensions,+			 * "rr_moved".+			 */+			if (file->rr_moved) {+				/*+				 * If this has only the subdirectories which+				 * have "RE" flags, do not expose at this time.+				 */+				if (file->rr_moved_has_re_only)+					continue;+				/* Otherwise expose "rr_moved" entry. */+			} else if (file->re) {+				/*+				 * Do not expose this at this time+				 * because we have not gotten its full-path+				 * name yet.+				 */+				re_add_entry(iso9660, file);+				continue;+			} else if (file->re_descendant) {+				/*+				 * If the top level "RE" entry of this entry+				 * is not exposed, we, accordingly, should not+				 * expose this entry at this time because+				 * we cannot make its proper full-path name.+				 */+				if (rede_add_entry(file) == 0)+					continue;+				/* Otherwise we can expose this entry because+				 * it seems its top level "RE" has already been+				 * exposed. */+			}+		}+		break;+	}++	if ((file->mode & AE_IFMT) != AE_IFREG || file->number == -1)+		return (ARCHIVE_OK);++	count = 0;+	number = file->number;+	iso9660->cache_files.first = NULL;+	iso9660->cache_files.last = &(iso9660->cache_files.first);+	empty_files.first = NULL;+	empty_files.last = &empty_files.first;+	/* Collect files which has the same file serial number.+	 * Peek pending_files so that file which number is different+	 * is not put back. */+	while (iso9660->pending_files.used > 0 &&+	    (iso9660->pending_files.files[0]->number == -1 ||+	     iso9660->pending_files.files[0]->number == number)) {+		if (file->number == -1) {+			/* This file has the same offset+			 * but it's wrong offset which empty files+			 * and symlink files have.+			 * NOTE: This wrong offset was recorded by+			 * old mkisofs utility. If ISO images is+			 * created by latest mkisofs, this does not+			 * happen.+			 */+			file->next = NULL;+			*empty_files.last = file;+			empty_files.last = &(file->next);+		} else {+			count++;+			cache_add_entry(iso9660, file);+		}+		file = next_entry(iso9660);+	}++	if (count == 0) {+		*pfile = file;+		return ((file == NULL)?ARCHIVE_EOF:ARCHIVE_OK);+	}+	if (file->number == -1) {+		file->next = NULL;+		*empty_files.last = file;+		empty_files.last = &(file->next);+	} else {+		count++;+		cache_add_entry(iso9660, file);+	}++	if (count > 1) {+		/* The count is the same as number of hardlink,+		 * so much so that each nlinks of files in cache_file+		 * is overwritten by value of the count.+		 */+		for (file = iso9660->cache_files.first;+		    file != NULL; file = file->next)+			file->nlinks = count;+	}+	/* If there are empty files, that files are added+	 * to the tail of the cache_files. */+	if (empty_files.first != NULL) {+		*iso9660->cache_files.last = empty_files.first;+		iso9660->cache_files.last = empty_files.last;+	}+	*pfile = cache_get_entry(iso9660);+	return ((*pfile == NULL)?ARCHIVE_EOF:ARCHIVE_OK);++fatal_rr:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+	    "Failed to connect 'CL' pointer to 'RE' rr_moved pointer of "+	    "Rockridge extensions: current position = %jd, CL offset = %jd",+	    (intmax_t)iso9660->current_position, (intmax_t)file->cl_offset);+	return (ARCHIVE_FATAL);+}++static inline void+re_add_entry(struct iso9660 *iso9660, struct file_info *file)+{+	file->re_next = NULL;+	*iso9660->re_files.last = file;+	iso9660->re_files.last = &(file->re_next);+}++static inline struct file_info *+re_get_entry(struct iso9660 *iso9660)+{+	struct file_info *file;++	if ((file = iso9660->re_files.first) != NULL) {+		iso9660->re_files.first = file->re_next;+		if (iso9660->re_files.first == NULL)+			iso9660->re_files.last =+			    &(iso9660->re_files.first);+	}+	return (file);+}++static inline int+rede_add_entry(struct file_info *file)+{+	struct file_info *re;++	/*+	 * Find "RE" entry.+	 */+	re = file->parent;+	while (re != NULL && !re->re)+		re = re->parent;+	if (re == NULL)+		return (-1);++	file->re_next = NULL;+	*re->rede_files.last = file;+	re->rede_files.last = &(file->re_next);+	return (0);+}++static inline struct file_info *+rede_get_entry(struct file_info *re)+{+	struct file_info *file;++	if ((file = re->rede_files.first) != NULL) {+		re->rede_files.first = file->re_next;+		if (re->rede_files.first == NULL)+			re->rede_files.last =+			    &(re->rede_files.first);+	}+	return (file);+}++static inline void+cache_add_entry(struct iso9660 *iso9660, struct file_info *file)+{+	file->next = NULL;+	*iso9660->cache_files.last = file;+	iso9660->cache_files.last = &(file->next);+}++static inline struct file_info *+cache_get_entry(struct iso9660 *iso9660)+{+	struct file_info *file;++	if ((file = iso9660->cache_files.first) != NULL) {+		iso9660->cache_files.first = file->next;+		if (iso9660->cache_files.first == NULL)+			iso9660->cache_files.last =+			    &(iso9660->cache_files.first);+	}+	return (file);+}++static int+heap_add_entry(struct archive_read *a, struct heap_queue *heap,+    struct file_info *file, uint64_t key)+{+	uint64_t file_key, parent_key;+	int hole, parent;++	/* Expand our pending files list as necessary. */+	if (heap->used >= heap->allocated) {+		struct file_info **new_pending_files;+		int new_size = heap->allocated * 2;++		if (heap->allocated < 1024)+			new_size = 1024;+		/* Overflow might keep us from growing the list. */+		if (new_size <= heap->allocated) {+			archive_set_error(&a->archive,+			    ENOMEM, "Out of memory");+			return (ARCHIVE_FATAL);+		}+		new_pending_files = (struct file_info **)+		    malloc(new_size * sizeof(new_pending_files[0]));+		if (new_pending_files == NULL) {+			archive_set_error(&a->archive,+			    ENOMEM, "Out of memory");+			return (ARCHIVE_FATAL);+		}+		if (heap->allocated)+			memcpy(new_pending_files, heap->files,+			    heap->allocated * sizeof(new_pending_files[0]));+		free(heap->files);+		heap->files = new_pending_files;+		heap->allocated = new_size;+	}++	file_key = file->key = key;++	/*+	 * Start with hole at end, walk it up tree to find insertion point.+	 */+	hole = heap->used++;+	while (hole > 0) {+		parent = (hole - 1)/2;+		parent_key = heap->files[parent]->key;+		if (file_key >= parent_key) {+			heap->files[hole] = file;+			return (ARCHIVE_OK);+		}+		/* Move parent into hole <==> move hole up tree. */+		heap->files[hole] = heap->files[parent];+		hole = parent;+	}+	heap->files[0] = file;++	return (ARCHIVE_OK);+}++static struct file_info *+heap_get_entry(struct heap_queue *heap)+{+	uint64_t a_key, b_key, c_key;+	int a, b, c;+	struct file_info *r, *tmp;++	if (heap->used < 1)+		return (NULL);++	/*+	 * The first file in the list is the earliest; we'll return this.+	 */+	r = heap->files[0];++	/*+	 * Move the last item in the heap to the root of the tree+	 */+	heap->files[0] = heap->files[--(heap->used)];++	/*+	 * Rebalance the heap.+	 */+	a = 0; /* Starting element and its heap key */+	a_key = heap->files[a]->key;+	for (;;) {+		b = a + a + 1; /* First child */+		if (b >= heap->used)+			return (r);+		b_key = heap->files[b]->key;+		c = b + 1; /* Use second child if it is smaller. */+		if (c < heap->used) {+			c_key = heap->files[c]->key;+			if (c_key < b_key) {+				b = c;+				b_key = c_key;+			}+		}+		if (a_key <= b_key)+			return (r);+		tmp = heap->files[a];+		heap->files[a] = heap->files[b];+		heap->files[b] = tmp;+		a = b;+	}+}++static unsigned int+toi(const void *p, int n)+{+	const unsigned char *v = (const unsigned char *)p;+	if (n > 1)+		return v[0] + 256 * toi(v + 1, n - 1);+	if (n == 1)+		return v[0];+	return (0);+}++static time_t+isodate7(const unsigned char *v)+{+	struct tm tm;+	int offset;+	time_t t;++	memset(&tm, 0, sizeof(tm));+	tm.tm_year = v[0];+	tm.tm_mon = v[1] - 1;+	tm.tm_mday = v[2];+	tm.tm_hour = v[3];+	tm.tm_min = v[4];+	tm.tm_sec = v[5];+	/* v[6] is the signed timezone offset, in 1/4-hour increments. */+	offset = ((const signed char *)v)[6];+	if (offset > -48 && offset < 52) {+		tm.tm_hour -= offset / 4;+		tm.tm_min -= (offset % 4) * 15;+	}+	t = time_from_tm(&tm);+	if (t == (time_t)-1)+		return ((time_t)0);+	return (t);+}++static time_t+isodate17(const unsigned char *v)+{+	struct tm tm;+	int offset;+	time_t t;++	memset(&tm, 0, sizeof(tm));+	tm.tm_year = (v[0] - '0') * 1000 + (v[1] - '0') * 100+	    + (v[2] - '0') * 10 + (v[3] - '0')+	    - 1900;+	tm.tm_mon = (v[4] - '0') * 10 + (v[5] - '0');+	tm.tm_mday = (v[6] - '0') * 10 + (v[7] - '0');+	tm.tm_hour = (v[8] - '0') * 10 + (v[9] - '0');+	tm.tm_min = (v[10] - '0') * 10 + (v[11] - '0');+	tm.tm_sec = (v[12] - '0') * 10 + (v[13] - '0');+	/* v[16] is the signed timezone offset, in 1/4-hour increments. */+	offset = ((const signed char *)v)[16];+	if (offset > -48 && offset < 52) {+		tm.tm_hour -= offset / 4;+		tm.tm_min -= (offset % 4) * 15;+	}+	t = time_from_tm(&tm);+	if (t == (time_t)-1)+		return ((time_t)0);+	return (t);+}++static time_t+time_from_tm(struct tm *t)+{+#if HAVE_TIMEGM+        /* Use platform timegm() if available. */+        return (timegm(t));+#elif HAVE__MKGMTIME64+        return (_mkgmtime64(t));+#else+        /* Else use direct calculation using POSIX assumptions. */+        /* First, fix up tm_yday based on the year/month/day. */+        if (mktime(t) == (time_t)-1)+                return ((time_t)-1);+        /* Then we can compute timegm() from first principles. */+        return (t->tm_sec+            + t->tm_min * 60+            + t->tm_hour * 3600+            + t->tm_yday * 86400+            + (t->tm_year - 70) * 31536000+            + ((t->tm_year - 69) / 4) * 86400+            - ((t->tm_year - 1) / 100) * 86400+            + ((t->tm_year + 299) / 400) * 86400);+#endif+}++static const char *+build_pathname(struct archive_string *as, struct file_info *file, int depth)+{+	// Plain ISO9660 only allows 8 dir levels; if we get+	// to 1000, then something is very, very wrong.+	if (depth > 1000) {+		return NULL;+	}+	if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) {+		if (build_pathname(as, file->parent, depth + 1) == NULL) {+			return NULL;+		}+		archive_strcat(as, "/");+	}+	if (archive_strlen(&file->name) == 0)+		archive_strcat(as, ".");+	else+		archive_string_concat(as, &file->name);+	return (as->s);+}++static int+build_pathname_utf16be(unsigned char *p, size_t max, size_t *len,+    struct file_info *file)+{+	if (file->parent != NULL && file->parent->utf16be_bytes > 0) {+		if (build_pathname_utf16be(p, max, len, file->parent) != 0)+			return (-1);+		p[*len] = 0;+		p[*len + 1] = '/';+		*len += 2;+	}+	if (file->utf16be_bytes == 0) {+		if (*len + 2 > max)+			return (-1);/* Path is too long! */+		p[*len] = 0;+		p[*len + 1] = '.';+		*len += 2;+	} else {+		if (*len + file->utf16be_bytes > max)+			return (-1);/* Path is too long! */+		memcpy(p + *len, file->utf16be_name, file->utf16be_bytes);+		*len += file->utf16be_bytes;+	}+	return (0);+}++#if DEBUG+static void+dump_isodirrec(FILE *out, const unsigned char *isodirrec)+{+	fprintf(out, " l %d,",+	    toi(isodirrec + DR_length_offset, DR_length_size));+	fprintf(out, " a %d,",+	    toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size));+	fprintf(out, " ext 0x%x,",+	    toi(isodirrec + DR_extent_offset, DR_extent_size));+	fprintf(out, " s %d,",+	    toi(isodirrec + DR_size_offset, DR_extent_size));+	fprintf(out, " f 0x%x,",+	    toi(isodirrec + DR_flags_offset, DR_flags_size));+	fprintf(out, " u %d,",+	    toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size));+	fprintf(out, " ilv %d,",+	    toi(isodirrec + DR_interleave_offset, DR_interleave_size));+	fprintf(out, " seq %d,",+	    toi(isodirrec + DR_volume_sequence_number_offset,+		DR_volume_sequence_number_size));+	fprintf(out, " nl %d:",+	    toi(isodirrec + DR_name_len_offset, DR_name_len_size));+	fprintf(out, " `%.*s'",+	    toi(isodirrec + DR_name_len_offset, DR_name_len_size),+		isodirrec + DR_name_offset);+}+#endif
+ c/archive_read_support_format_lha.c view
@@ -0,0 +1,2912 @@+/*-+ * Copyright (c) 2008-2014 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_read_private.h"+#include "archive_endian.h"+++#define MAXMATCH		256	/* Maximum match length. */+#define MINMATCH		3	/* Minimum match length. */+/*+ * Literal table format:+ * +0              +256                      +510+ * +---------------+-------------------------++ * | literal code  |       match length      |+ * |   0 ... 255   |  MINMATCH ... MAXMATCH  |+ * +---------------+-------------------------++ *  <---          LT_BITLEN_SIZE         --->+ */+/* Literal table size. */+#define LT_BITLEN_SIZE		(UCHAR_MAX + 1 + MAXMATCH - MINMATCH + 1)+/* Position table size.+ * Note: this used for both position table and pre literal table.*/+#define PT_BITLEN_SIZE		(3 + 16)++struct lzh_dec {+	/* Decoding status. */+	int     		 state;++	/*+	 * Window to see last 8Ki(lh5),32Ki(lh6),64Ki(lh7) bytes of decoded+	 * data.+	 */+	int			 w_size;+	int			 w_mask;+	/* Window buffer, which is a loop buffer. */+	unsigned char		*w_buff;+	/* The insert position to the window. */+	int			 w_pos;+	/* The position where we can copy decoded code from the window. */+	int     		 copy_pos;+	/* The length how many bytes we can copy decoded code from+	 * the window. */+	int     		 copy_len;++	/*+	 * Bit stream reader.+	 */+	struct lzh_br {+#define CACHE_TYPE		uint64_t+#define CACHE_BITS		(8 * sizeof(CACHE_TYPE))+	 	/* Cache buffer. */+		CACHE_TYPE	 cache_buffer;+		/* Indicates how many bits avail in cache_buffer. */+		int		 cache_avail;+	} br;++	/*+	 * Huffman coding.+	 */+	struct huffman {+		int		 len_size;+		int		 len_avail;+		int		 len_bits;+		int		 freq[17];+		unsigned char	*bitlen;++		/*+		 * Use a index table. It's faster than searching a huffman+		 * coding tree, which is a binary tree. But a use of a large+		 * index table causes L1 cache read miss many times.+		 */+#define HTBL_BITS	10+		int		 max_bits;+		int		 shift_bits;+		int		 tbl_bits;+		int		 tree_used;+		int		 tree_avail;+		/* Direct access table. */+		uint16_t	*tbl;+		/* Binary tree table for extra bits over the direct access. */+		struct htree_t {+			uint16_t left;+			uint16_t right;+		}		*tree;+	}			 lt, pt;++	int			 blocks_avail;+	int			 pos_pt_len_size;+	int			 pos_pt_len_bits;+	int			 literal_pt_len_size;+	int			 literal_pt_len_bits;+	int			 reading_position;+	int			 loop;+	int			 error;+};++struct lzh_stream {+	const unsigned char	*next_in;+	int			 avail_in;+	int64_t			 total_in;+	const unsigned char	*ref_ptr;+	int			 avail_out;+	int64_t			 total_out;+	struct lzh_dec		*ds;+};++struct lha {+	/* entry_bytes_remaining is the number of bytes we expect.	    */+	int64_t                  entry_offset;+	int64_t                  entry_bytes_remaining;+	int64_t			 entry_unconsumed;+	uint16_t		 entry_crc_calculated;+ +	size_t			 header_size;	/* header size		    */+	unsigned char		 level;		/* header level		    */+	char			 method[3];	/* compress type	    */+	int64_t			 compsize;	/* compressed data size	    */+	int64_t			 origsize;	/* original file size	    */+	int			 setflag;+#define BIRTHTIME_IS_SET	1+#define ATIME_IS_SET		2+#define UNIX_MODE_IS_SET	4+#define CRC_IS_SET		8+	time_t			 birthtime;+	long			 birthtime_tv_nsec;+	time_t			 mtime;+	long			 mtime_tv_nsec;+	time_t			 atime;+	long			 atime_tv_nsec;+	mode_t			 mode;+	int64_t			 uid;+	int64_t			 gid;+	struct archive_string 	 uname;+	struct archive_string 	 gname;+	uint16_t		 header_crc;+	uint16_t		 crc;+	/* dirname and filename could be in different codepages */+	struct archive_string_conv *sconv_dir;+	struct archive_string_conv *sconv_fname;+	struct archive_string_conv *opt_sconv;++	struct archive_string 	 dirname;+	struct archive_string 	 filename;+	struct archive_wstring	 ws;++	unsigned char		 dos_attr;++	/* Flag to mark progress that an archive was read their first header.*/+	char			 found_first_header;+	/* Flag to mark that indicates an empty directory. */+	char			 directory;++	/* Flags to mark progress of decompression. */+	char			 decompress_init;+	char			 end_of_entry;+	char			 end_of_entry_cleanup;+	char			 entry_is_compressed;++	char			 format_name[64];++	struct lzh_stream	 strm;+};++/*+ * LHA header common member offset.+ */+#define H_METHOD_OFFSET	2	/* Compress type. */+#define H_ATTR_OFFSET	19	/* DOS attribute. */+#define H_LEVEL_OFFSET	20	/* Header Level.  */+#define H_SIZE		22	/* Minimum header size. */++static int      archive_read_format_lha_bid(struct archive_read *, int);+static int      archive_read_format_lha_options(struct archive_read *,+		    const char *, const char *);+static int	archive_read_format_lha_read_header(struct archive_read *,+		    struct archive_entry *);+static int	archive_read_format_lha_read_data(struct archive_read *,+		    const void **, size_t *, int64_t *);+static int	archive_read_format_lha_read_data_skip(struct archive_read *);+static int	archive_read_format_lha_cleanup(struct archive_read *);++static void	lha_replace_path_separator(struct lha *,+		    struct archive_entry *);+static int	lha_read_file_header_0(struct archive_read *, struct lha *);+static int	lha_read_file_header_1(struct archive_read *, struct lha *);+static int	lha_read_file_header_2(struct archive_read *, struct lha *);+static int	lha_read_file_header_3(struct archive_read *, struct lha *);+static int	lha_read_file_extended_header(struct archive_read *,+		    struct lha *, uint16_t *, int, size_t, size_t *);+static size_t	lha_check_header_format(const void *);+static int	lha_skip_sfx(struct archive_read *);+static time_t	lha_dos_time(const unsigned char *);+static time_t	lha_win_time(uint64_t, long *);+static unsigned char	lha_calcsum(unsigned char, const void *,+		    int, size_t);+static int	lha_parse_linkname(struct archive_wstring *,+		    struct archive_wstring *);+static int	lha_read_data_none(struct archive_read *, const void **,+		    size_t *, int64_t *);+static int	lha_read_data_lzh(struct archive_read *, const void **,+		    size_t *, int64_t *);+static void	lha_crc16_init(void);+static uint16_t lha_crc16(uint16_t, const void *, size_t);+static int	lzh_decode_init(struct lzh_stream *, const char *);+static void	lzh_decode_free(struct lzh_stream *);+static int	lzh_decode(struct lzh_stream *, int);+static int	lzh_br_fillup(struct lzh_stream *, struct lzh_br *);+static int	lzh_huffman_init(struct huffman *, size_t, int);+static void	lzh_huffman_free(struct huffman *);+static int	lzh_read_pt_bitlen(struct lzh_stream *, int start, int end);+static int	lzh_make_fake_table(struct huffman *, uint16_t);+static int	lzh_make_huffman_table(struct huffman *);+static inline int lzh_decode_huffman(struct huffman *, unsigned);+static int	lzh_decode_huffman_tree(struct huffman *, unsigned, int);+++int+archive_read_support_format_lha(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct lha *lha;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_lha");++	lha = (struct lha *)calloc(1, sizeof(*lha));+	if (lha == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate lha data");+		return (ARCHIVE_FATAL);+	}+	archive_string_init(&lha->ws);++	r = __archive_read_register_format(a,+	    lha,+	    "lha",+	    archive_read_format_lha_bid,+	    archive_read_format_lha_options,+	    archive_read_format_lha_read_header,+	    archive_read_format_lha_read_data,+	    archive_read_format_lha_read_data_skip,+	    NULL,+	    archive_read_format_lha_cleanup,+	    NULL,+	    NULL);++	if (r != ARCHIVE_OK)+		free(lha);+	return (ARCHIVE_OK);+}++static size_t+lha_check_header_format(const void *h)+{+	const unsigned char *p = h;+	size_t next_skip_bytes;++	switch (p[H_METHOD_OFFSET+3]) {+	/*+	 * "-lh0-" ... "-lh7-" "-lhd-"+	 * "-lzs-" "-lz5-"+	 */+	case '0': case '1': case '2': case '3':+	case '4': case '5': case '6': case '7':+	case 'd':+	case 's':+		next_skip_bytes = 4;++		/* b0 == 0 means the end of an LHa archive file.	*/+		if (p[0] == 0)+			break;+		if (p[H_METHOD_OFFSET] != '-' || p[H_METHOD_OFFSET+1] != 'l'+		    ||  p[H_METHOD_OFFSET+4] != '-')+			break;++		if (p[H_METHOD_OFFSET+2] == 'h') {+			/* "-lh?-" */+			if (p[H_METHOD_OFFSET+3] == 's')+				break;+			if (p[H_LEVEL_OFFSET] == 0)+				return (0);+			if (p[H_LEVEL_OFFSET] <= 3 && p[H_ATTR_OFFSET] == 0x20)+				return (0);+		}+		if (p[H_METHOD_OFFSET+2] == 'z') {+			/* LArc extensions: -lzs-,-lz4- and -lz5- */+			if (p[H_LEVEL_OFFSET] != 0)+				break;+			if (p[H_METHOD_OFFSET+3] == 's'+			    || p[H_METHOD_OFFSET+3] == '4'+			    || p[H_METHOD_OFFSET+3] == '5')+				return (0);+		}+		break;+	case 'h': next_skip_bytes = 1; break;+	case 'z': next_skip_bytes = 1; break;+	case 'l': next_skip_bytes = 2; break;+	case '-': next_skip_bytes = 3; break;+	default : next_skip_bytes = 4; break;+	}++	return (next_skip_bytes);+}++static int+archive_read_format_lha_bid(struct archive_read *a, int best_bid)+{+	const char *p;+	const void *buff;+	ssize_t bytes_avail, offset, window;+	size_t next;++	/* If there's already a better bid than we can ever+	   make, don't bother testing. */+	if (best_bid > 30)+		return (-1);++	if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL)+		return (-1);++	if (lha_check_header_format(p) == 0)+		return (30);++	if (p[0] == 'M' && p[1] == 'Z') {+		/* PE file */+		offset = 0;+		window = 4096;+		while (offset < (1024 * 20)) {+			buff = __archive_read_ahead(a, offset + window,+			    &bytes_avail);+			if (buff == NULL) {+				/* Remaining bytes are less than window. */+				window >>= 1;+				if (window < (H_SIZE + 3))+					return (0);+				continue;+			}+			p = (const char *)buff + offset;+			while (p + H_SIZE < (const char *)buff + bytes_avail) {+				if ((next = lha_check_header_format(p)) == 0)+					return (30);+				p += next;+			}+			offset = p - (const char *)buff;+		}+	}+	return (0);+}++static int+archive_read_format_lha_options(struct archive_read *a,+    const char *key, const char *val)+{+	struct lha *lha;+	int ret = ARCHIVE_FAILED;++	lha = (struct lha *)(a->format->data);+	if (strcmp(key, "hdrcharset")  == 0) {+		if (val == NULL || val[0] == 0)+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "lha: hdrcharset option needs a character-set name");+		else {+			lha->opt_sconv =+			    archive_string_conversion_from_charset(+				&a->archive, val, 0);+			if (lha->opt_sconv != NULL)+				ret = ARCHIVE_OK;+			else+				ret = ARCHIVE_FATAL;+		}+		return (ret);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+lha_skip_sfx(struct archive_read *a)+{+	const void *h;+	const char *p, *q;+	size_t next, skip;+	ssize_t bytes, window;++	window = 4096;+	for (;;) {+		h = __archive_read_ahead(a, window, &bytes);+		if (h == NULL) {+			/* Remaining bytes are less than window. */+			window >>= 1;+			if (window < (H_SIZE + 3))+				goto fatal;+			continue;+		}+		if (bytes < H_SIZE)+			goto fatal;+		p = h;+		q = p + bytes;++		/*+		 * Scan ahead until we find something that looks+		 * like the lha header.+		 */+		while (p + H_SIZE < q) {+			if ((next = lha_check_header_format(p)) == 0) {+				skip = p - (const char *)h;+				__archive_read_consume(a, skip);+				return (ARCHIVE_OK);+			}+			p += next;+		}+		skip = p - (const char *)h;+		__archive_read_consume(a, skip);+	}+fatal:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Couldn't find out LHa header");+	return (ARCHIVE_FATAL);+}++static int+truncated_error(struct archive_read *a)+{+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Truncated LHa header");+	return (ARCHIVE_FATAL);+}++static int+archive_read_format_lha_read_header(struct archive_read *a,+    struct archive_entry *entry)+{+	struct archive_wstring linkname;+	struct archive_wstring pathname;+	struct lha *lha;+	const unsigned char *p;+	const char *signature;+	int err;+	struct archive_mstring conv_buffer;+	const wchar_t *conv_buffer_p;++	lha_crc16_init();++	a->archive.archive_format = ARCHIVE_FORMAT_LHA;+	if (a->archive.archive_format_name == NULL)+		a->archive.archive_format_name = "lha";++	lha = (struct lha *)(a->format->data);+	lha->decompress_init = 0;+	lha->end_of_entry = 0;+	lha->end_of_entry_cleanup = 0;+	lha->entry_unconsumed = 0;++	if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) {+		/*+		 * LHa archiver added 0 to the tail of its archive file as+		 * the mark of the end of the archive.+		 */+		signature = __archive_read_ahead(a, sizeof(signature[0]), NULL);+		if (signature == NULL || signature[0] == 0)+			return (ARCHIVE_EOF);+		return (truncated_error(a));+	}++	signature = (const char *)p;+	if (lha->found_first_header == 0 &&+	    signature[0] == 'M' && signature[1] == 'Z') {+                /* This is an executable?  Must be self-extracting... 	*/+		err = lha_skip_sfx(a);+		if (err < ARCHIVE_WARN)+			return (err);++		if ((p = __archive_read_ahead(a, sizeof(*p), NULL)) == NULL)+			return (truncated_error(a));+		signature = (const char *)p;+	}+	/* signature[0] == 0 means the end of an LHa archive file. */+	if (signature[0] == 0)+		return (ARCHIVE_EOF);++	/*+	 * Check the header format and method type.+	 */+	if (lha_check_header_format(p) != 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Bad LHa file");+		return (ARCHIVE_FATAL);+	}++	/* We've found the first header. */+	lha->found_first_header = 1;+	/* Set a default value and common data */+	lha->header_size = 0;+	lha->level = p[H_LEVEL_OFFSET];+	lha->method[0] = p[H_METHOD_OFFSET+1];+	lha->method[1] = p[H_METHOD_OFFSET+2];+	lha->method[2] = p[H_METHOD_OFFSET+3];+	if (memcmp(lha->method, "lhd", 3) == 0)+		lha->directory = 1;+	else+		lha->directory = 0;+	if (memcmp(lha->method, "lh0", 3) == 0 ||+	    memcmp(lha->method, "lz4", 3) == 0)+		lha->entry_is_compressed = 0;+	else+		lha->entry_is_compressed = 1;++	lha->compsize = 0;+	lha->origsize = 0;+	lha->setflag = 0;+	lha->birthtime = 0;+	lha->birthtime_tv_nsec = 0;+	lha->mtime = 0;+	lha->mtime_tv_nsec = 0;+	lha->atime = 0;+	lha->atime_tv_nsec = 0;+	lha->mode = (lha->directory)? 0777 : 0666;+	lha->uid = 0;+	lha->gid = 0;+	archive_string_empty(&lha->dirname);+	archive_string_empty(&lha->filename);+	lha->dos_attr = 0;+	if (lha->opt_sconv != NULL) {+		lha->sconv_dir = lha->opt_sconv;+		lha->sconv_fname = lha->opt_sconv;+	} else {+		lha->sconv_dir = NULL;+		lha->sconv_fname = NULL;+	}++	switch (p[H_LEVEL_OFFSET]) {+	case 0:+		err = lha_read_file_header_0(a, lha);+		break;+	case 1:+		err = lha_read_file_header_1(a, lha);+		break;+	case 2:+		err = lha_read_file_header_2(a, lha);+		break;+	case 3:+		err = lha_read_file_header_3(a, lha);+		break;+	default:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unsupported LHa header level %d", p[H_LEVEL_OFFSET]);+		err = ARCHIVE_FATAL;+		break;+	}+	if (err < ARCHIVE_WARN)+		return (err);+++	if (!lha->directory && archive_strlen(&lha->filename) == 0)+		/* The filename has not been set */+		return (truncated_error(a));++	/*+	 * Make a pathname from a dirname and a filename, after converting to Unicode.+	 * This is because codepages might differ between dirname and filename.+	*/+	archive_string_init(&pathname);+	archive_string_init(&linkname);+	archive_string_init(&conv_buffer.aes_mbs);+	archive_string_init(&conv_buffer.aes_mbs_in_locale);+	archive_string_init(&conv_buffer.aes_utf8);+	archive_string_init(&conv_buffer.aes_wcs);+	if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->dirname.s, lha->dirname.length, lha->sconv_dir)) {+		archive_set_error(&a->archive,+			ARCHIVE_ERRNO_FILE_FORMAT,+			"Pathname cannot be converted "+			"from %s to Unicode.",+			archive_string_conversion_charset_name(lha->sconv_dir));+		err = ARCHIVE_FATAL;+	} else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p))+		err = ARCHIVE_FATAL;+	if (err == ARCHIVE_FATAL) {+		archive_mstring_clean(&conv_buffer);+		archive_wstring_free(&pathname);+		archive_wstring_free(&linkname);+		return (err);+	}+	archive_wstring_copy(&pathname, &conv_buffer.aes_wcs);++	archive_string_empty(&conv_buffer.aes_mbs);+	archive_string_empty(&conv_buffer.aes_mbs_in_locale);+	archive_string_empty(&conv_buffer.aes_utf8);+	archive_wstring_empty(&conv_buffer.aes_wcs);+	if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->filename.s, lha->filename.length, lha->sconv_fname)) {+		archive_set_error(&a->archive,+			ARCHIVE_ERRNO_FILE_FORMAT,+			"Pathname cannot be converted "+			"from %s to Unicode.",+			archive_string_conversion_charset_name(lha->sconv_fname));+		err = ARCHIVE_FATAL;+	}+	else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p))+		err = ARCHIVE_FATAL;+	if (err == ARCHIVE_FATAL) {+		archive_mstring_clean(&conv_buffer);+		archive_wstring_free(&pathname);+		archive_wstring_free(&linkname);+		return (err);+	}+	archive_wstring_concat(&pathname, &conv_buffer.aes_wcs);+	archive_mstring_clean(&conv_buffer);++	if ((lha->mode & AE_IFMT) == AE_IFLNK) {+		/*+	 	 * Extract the symlink-name if it's included in the pathname.+	 	 */+		if (!lha_parse_linkname(&linkname, &pathname)) {+			/* We couldn't get the symlink-name. */+			archive_set_error(&a->archive,+		    	    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Unknown symlink-name");+			archive_wstring_free(&pathname);+			archive_wstring_free(&linkname);+			return (ARCHIVE_FAILED);+		}+	} else {+		/*+		 * Make sure a file-type is set.+		 * The mode has been overridden if it is in the extended data.+		 */+		lha->mode = (lha->mode & ~AE_IFMT) |+		    ((lha->directory)? AE_IFDIR: AE_IFREG);+	}+	if ((lha->setflag & UNIX_MODE_IS_SET) == 0 &&+	    (lha->dos_attr & 1) != 0)+		lha->mode &= ~(0222);/* read only. */++	/*+	 * Set basic file parameters.+	 */+	archive_entry_copy_pathname_w(entry, pathname.s);+	archive_wstring_free(&pathname);+	if (archive_strlen(&linkname) > 0) {+		archive_entry_copy_symlink_w(entry, linkname.s);+	} else+		archive_entry_set_symlink(entry, NULL);+	archive_wstring_free(&linkname);+	/*+	 * When a header level is 0, there is a possibility that+	 * a pathname and a symlink has '\' character, a directory+	 * separator in DOS/Windows. So we should convert it to '/'.+	 */+	if (p[H_LEVEL_OFFSET] == 0)+		lha_replace_path_separator(lha, entry);++	archive_entry_set_mode(entry, lha->mode);+	archive_entry_set_uid(entry, lha->uid);+	archive_entry_set_gid(entry, lha->gid);+	if (archive_strlen(&lha->uname) > 0)+		archive_entry_set_uname(entry, lha->uname.s);+	if (archive_strlen(&lha->gname) > 0)+		archive_entry_set_gname(entry, lha->gname.s);+	if (lha->setflag & BIRTHTIME_IS_SET) {+		archive_entry_set_birthtime(entry, lha->birthtime,+		    lha->birthtime_tv_nsec);+		archive_entry_set_ctime(entry, lha->birthtime,+		    lha->birthtime_tv_nsec);+	} else {+		archive_entry_unset_birthtime(entry);+		archive_entry_unset_ctime(entry);+	}+	archive_entry_set_mtime(entry, lha->mtime, lha->mtime_tv_nsec);+	if (lha->setflag & ATIME_IS_SET)+		archive_entry_set_atime(entry, lha->atime,+		    lha->atime_tv_nsec);+	else+		archive_entry_unset_atime(entry);+	if (lha->directory || archive_entry_symlink(entry) != NULL)+		archive_entry_unset_size(entry);+	else+		archive_entry_set_size(entry, lha->origsize);++	/*+	 * Prepare variables used to read a file content.+	 */+	lha->entry_bytes_remaining = lha->compsize;+	if (lha->entry_bytes_remaining < 0) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Invalid LHa entry size");+		return (ARCHIVE_FATAL);+	}+	lha->entry_offset = 0;+	lha->entry_crc_calculated = 0;++	/*+	 * This file does not have a content.+	 */+	if (lha->directory || lha->compsize == 0)+		lha->end_of_entry = 1;++	sprintf(lha->format_name, "lha -%c%c%c-",+	    lha->method[0], lha->method[1], lha->method[2]);+	a->archive.archive_format_name = lha->format_name;++	return (err);+}++/*+ * Replace a DOS path separator '\' by a character '/'.+ * Some multi-byte character set have  a character '\' in its second byte.+ */+static void+lha_replace_path_separator(struct lha *lha, struct archive_entry *entry)+{+	const wchar_t *wp;+	size_t i;++	if ((wp = archive_entry_pathname_w(entry)) != NULL) {+		archive_wstrcpy(&(lha->ws), wp);+		for (i = 0; i < archive_strlen(&(lha->ws)); i++) {+			if (lha->ws.s[i] == L'\\')+				lha->ws.s[i] = L'/';+		}+		archive_entry_copy_pathname_w(entry, lha->ws.s);+	}++	if ((wp = archive_entry_symlink_w(entry)) != NULL) {+		archive_wstrcpy(&(lha->ws), wp);+		for (i = 0; i < archive_strlen(&(lha->ws)); i++) {+			if (lha->ws.s[i] == L'\\')+				lha->ws.s[i] = L'/';+		}+		archive_entry_copy_symlink_w(entry, lha->ws.s);+	}+}++/*+ * Header 0 format+ *+ * +0              +1         +2               +7                  +11+ * +---------------+----------+----------------+-------------------++ * |header size(*1)|header sum|compression type|compressed size(*2)|+ * +---------------+----------+----------------+-------------------++ *                             <---------------------(*1)----------*+ *+ * +11               +15       +17       +19            +20              +21+ * +-----------------+---------+---------+--------------+----------------++ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=0)|+ * +-----------------+---------+---------+--------------+----------------++ * *--------------------------------(*1)---------------------------------*+ *+ * +21             +22       +22+(*3)   +22+(*3)+2       +22+(*3)+2+(*4)+ * +---------------+---------+----------+----------------+------------------++ * |name length(*3)|file name|file CRC16|extra header(*4)|  compressed data |+ * +---------------+---------+----------+----------------+------------------++ *                  <--(*3)->                             <------(*2)------>+ * *----------------------(*1)-------------------------->+ *+ */+#define H0_HEADER_SIZE_OFFSET	0+#define H0_HEADER_SUM_OFFSET	1+#define H0_COMP_SIZE_OFFSET	7+#define H0_ORIG_SIZE_OFFSET	11+#define H0_DOS_TIME_OFFSET	15+#define H0_NAME_LEN_OFFSET	21+#define H0_FILE_NAME_OFFSET	22+#define H0_FIXED_SIZE		24+static int+lha_read_file_header_0(struct archive_read *a, struct lha *lha)+{+	const unsigned char *p;+	int extdsize, namelen;+	unsigned char headersum, sum_calculated;++	if ((p = __archive_read_ahead(a, H0_FIXED_SIZE, NULL)) == NULL)+		return (truncated_error(a));+	lha->header_size = p[H0_HEADER_SIZE_OFFSET] + 2;+	headersum = p[H0_HEADER_SUM_OFFSET];+	lha->compsize = archive_le32dec(p + H0_COMP_SIZE_OFFSET);+	lha->origsize = archive_le32dec(p + H0_ORIG_SIZE_OFFSET);+	lha->mtime = lha_dos_time(p + H0_DOS_TIME_OFFSET);+	namelen = p[H0_NAME_LEN_OFFSET];+	extdsize = (int)lha->header_size - H0_FIXED_SIZE - namelen;+	if ((namelen > 221 || extdsize < 0) && extdsize != -2) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Invalid LHa header");+		return (ARCHIVE_FATAL);+	}+	if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)+		return (truncated_error(a));++	archive_strncpy(&lha->filename, p + H0_FILE_NAME_OFFSET, namelen);+	/* When extdsize == -2, A CRC16 value is not present in the header. */+	if (extdsize >= 0) {+		lha->crc = archive_le16dec(p + H0_FILE_NAME_OFFSET + namelen);+		lha->setflag |= CRC_IS_SET;+	}+	sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2);++	/* Read an extended header */+	if (extdsize > 0) {+		/* This extended data is set by 'LHa for UNIX' only.+		 * Maybe fixed size.+		 */+		p += H0_FILE_NAME_OFFSET + namelen + 2;+		if (p[0] == 'U' && extdsize == 12) {+			/* p[1] is a minor version. */+			lha->mtime = archive_le32dec(&p[2]);+			lha->mode = archive_le16dec(&p[6]);+			lha->uid = archive_le16dec(&p[8]);+			lha->gid = archive_le16dec(&p[10]);+			lha->setflag |= UNIX_MODE_IS_SET;+		}+	}+	__archive_read_consume(a, lha->header_size);++	if (sum_calculated != headersum) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "LHa header sum error");+		return (ARCHIVE_FATAL);+	}++	return (ARCHIVE_OK);+}++/*+ * Header 1 format+ *+ * +0              +1         +2               +7            +11+ * +---------------+----------+----------------+-------------++ * |header size(*1)|header sum|compression type|skip size(*2)|+ * +---------------+----------+----------------+-------------++ *                             <---------------(*1)----------*+ *+ * +11               +15       +17       +19            +20              +21+ * +-----------------+---------+---------+--------------+----------------++ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=1)|+ * +-----------------+---------+---------+--------------+----------------++ * *-------------------------------(*1)----------------------------------*+ *+ * +21             +22       +22+(*3)   +22+(*3)+2  +22+(*3)+3  +22+(*3)+3+(*4)+ * +---------------+---------+----------+-----------+-----------++ * |name length(*3)|file name|file CRC16|  creator  |padding(*4)|+ * +---------------+---------+----------+-----------+-----------++ *                  <--(*3)->+ * *----------------------------(*1)----------------------------*+ *+ * +22+(*3)+3+(*4)  +22+(*3)+3+(*4)+2     +22+(*3)+3+(*4)+2+(*5)+ * +----------------+---------------------+------------------------++ * |next header size| extended header(*5) |     compressed data    |+ * +----------------+---------------------+------------------------++ * *------(*1)-----> <--------------------(*2)-------------------->+ */+#define H1_HEADER_SIZE_OFFSET	0+#define H1_HEADER_SUM_OFFSET	1+#define H1_COMP_SIZE_OFFSET	7+#define H1_ORIG_SIZE_OFFSET	11+#define H1_DOS_TIME_OFFSET	15+#define H1_NAME_LEN_OFFSET	21+#define H1_FILE_NAME_OFFSET	22+#define H1_FIXED_SIZE		27+static int+lha_read_file_header_1(struct archive_read *a, struct lha *lha)+{+	const unsigned char *p;+	size_t extdsize;+	int i, err, err2;+	int namelen, padding;+	unsigned char headersum, sum_calculated;++	err = ARCHIVE_OK;++	if ((p = __archive_read_ahead(a, H1_FIXED_SIZE, NULL)) == NULL)+		return (truncated_error(a));++	lha->header_size = p[H1_HEADER_SIZE_OFFSET] + 2;+	headersum = p[H1_HEADER_SUM_OFFSET];+	/* Note: An extended header size is included in a compsize. */+	lha->compsize = archive_le32dec(p + H1_COMP_SIZE_OFFSET);+	lha->origsize = archive_le32dec(p + H1_ORIG_SIZE_OFFSET);+	lha->mtime = lha_dos_time(p + H1_DOS_TIME_OFFSET);+	namelen = p[H1_NAME_LEN_OFFSET];+	/* Calculate a padding size. The result will be normally 0 only(?) */+	padding = ((int)lha->header_size) - H1_FIXED_SIZE - namelen;++	if (namelen > 230 || padding < 0)+		goto invalid;++	if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)+		return (truncated_error(a));++	for (i = 0; i < namelen; i++) {+		if (p[i + H1_FILE_NAME_OFFSET] == 0xff)+			goto invalid;/* Invalid filename. */+	}+	archive_strncpy(&lha->filename, p + H1_FILE_NAME_OFFSET, namelen);+	lha->crc = archive_le16dec(p + H1_FILE_NAME_OFFSET + namelen);+	lha->setflag |= CRC_IS_SET;++	sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2);+	/* Consume used bytes but not include `next header size' data+	 * since it will be consumed in lha_read_file_extended_header(). */+	__archive_read_consume(a, lha->header_size - 2);++	/* Read extended headers */+	err2 = lha_read_file_extended_header(a, lha, NULL, 2,+	    (size_t)(lha->compsize + 2), &extdsize);+	if (err2 < ARCHIVE_WARN)+		return (err2);+	if (err2 < err)+		err = err2;+	/* Get a real compressed file size. */+	lha->compsize -= extdsize - 2;++	if (lha->compsize < 0)+		goto invalid;	/* Invalid compressed file size */++	if (sum_calculated != headersum) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "LHa header sum error");+		return (ARCHIVE_FATAL);+	}+	return (err);+invalid:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Invalid LHa header");+	return (ARCHIVE_FATAL);+}++/*+ * Header 2 format+ *+ * +0              +2               +7                  +11               +15+ * +---------------+----------------+-------------------+-----------------++ * |header size(*1)|compression type|compressed size(*2)|uncompressed size|+ * +---------------+----------------+-------------------+-----------------++ *  <--------------------------------(*1)---------------------------------*+ *+ * +15               +19          +20              +21        +23         +24+ * +-----------------+------------+----------------+----------+-----------++ * |data/time(time_t)| 0x20 fixed |header level(=2)|file CRC16|  creator  |+ * +-----------------+------------+----------------+----------+-----------++ * *---------------------------------(*1)---------------------------------*+ *+ * +24              +26                 +26+(*3)      +26+(*3)+(*4)+ * +----------------+-------------------+-------------+-------------------++ * |next header size|extended header(*3)| padding(*4) |  compressed data  |+ * +----------------+-------------------+-------------+-------------------++ * *--------------------------(*1)-------------------> <------(*2)------->+ *+ */+#define H2_HEADER_SIZE_OFFSET	0+#define H2_COMP_SIZE_OFFSET	7+#define H2_ORIG_SIZE_OFFSET	11+#define H2_TIME_OFFSET		15+#define H2_CRC_OFFSET		21+#define H2_FIXED_SIZE		24+static int+lha_read_file_header_2(struct archive_read *a, struct lha *lha)+{+	const unsigned char *p;+	size_t extdsize;+	int err, padding;+	uint16_t header_crc;++	if ((p = __archive_read_ahead(a, H2_FIXED_SIZE, NULL)) == NULL)+		return (truncated_error(a));++	lha->header_size =archive_le16dec(p + H2_HEADER_SIZE_OFFSET);+	lha->compsize = archive_le32dec(p + H2_COMP_SIZE_OFFSET);+	lha->origsize = archive_le32dec(p + H2_ORIG_SIZE_OFFSET);+	lha->mtime = archive_le32dec(p + H2_TIME_OFFSET);+	lha->crc = archive_le16dec(p + H2_CRC_OFFSET);+	lha->setflag |= CRC_IS_SET;++	if (lha->header_size < H2_FIXED_SIZE) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Invalid LHa header size");+		return (ARCHIVE_FATAL);+	}++	header_crc = lha_crc16(0, p, H2_FIXED_SIZE);+	__archive_read_consume(a, H2_FIXED_SIZE);++	/* Read extended headers */+	err = lha_read_file_extended_header(a, lha, &header_crc, 2,+		  lha->header_size - H2_FIXED_SIZE, &extdsize);+	if (err < ARCHIVE_WARN)+		return (err);++	/* Calculate a padding size. The result will be normally 0 or 1. */+	padding = (int)lha->header_size - (int)(H2_FIXED_SIZE + extdsize);+	if (padding > 0) {+		if ((p = __archive_read_ahead(a, padding, NULL)) == NULL)+			return (truncated_error(a));+		header_crc = lha_crc16(header_crc, p, padding);+		__archive_read_consume(a, padding);+	}++	if (header_crc != lha->header_crc) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "LHa header CRC error");+		return (ARCHIVE_FATAL);+	}+	return (err);+}++/*+ * Header 3 format+ *+ * +0           +2               +7                  +11               +15+ * +------------+----------------+-------------------+-----------------++ * | 0x04 fixed |compression type|compressed size(*2)|uncompressed size|+ * +------------+----------------+-------------------+-----------------++ *  <-------------------------------(*1)-------------------------------*+ *+ * +15               +19          +20              +21        +23         +24+ * +-----------------+------------+----------------+----------+-----------++ * |date/time(time_t)| 0x20 fixed |header level(=3)|file CRC16|  creator  |+ * +-----------------+------------+----------------+----------+-----------++ * *--------------------------------(*1)----------------------------------*+ *+ * +24             +28              +32                 +32+(*3)+ * +---------------+----------------+-------------------+-----------------++ * |header size(*1)|next header size|extended header(*3)| compressed data |+ * +---------------+----------------+-------------------+-----------------++ * *------------------------(*1)-----------------------> <------(*2)----->+ *+ */+#define H3_FIELD_LEN_OFFSET	0+#define H3_COMP_SIZE_OFFSET	7+#define H3_ORIG_SIZE_OFFSET	11+#define H3_TIME_OFFSET		15+#define H3_CRC_OFFSET		21+#define H3_HEADER_SIZE_OFFSET	24+#define H3_FIXED_SIZE		28+static int+lha_read_file_header_3(struct archive_read *a, struct lha *lha)+{+	const unsigned char *p;+	size_t extdsize;+	int err;+	uint16_t header_crc;++	if ((p = __archive_read_ahead(a, H3_FIXED_SIZE, NULL)) == NULL)+		return (truncated_error(a));++	if (archive_le16dec(p + H3_FIELD_LEN_OFFSET) != 4)+		goto invalid;+	lha->header_size =archive_le32dec(p + H3_HEADER_SIZE_OFFSET);+	lha->compsize = archive_le32dec(p + H3_COMP_SIZE_OFFSET);+	lha->origsize = archive_le32dec(p + H3_ORIG_SIZE_OFFSET);+	lha->mtime = archive_le32dec(p + H3_TIME_OFFSET);+	lha->crc = archive_le16dec(p + H3_CRC_OFFSET);+	lha->setflag |= CRC_IS_SET;++	if (lha->header_size < H3_FIXED_SIZE + 4)+		goto invalid;+	header_crc = lha_crc16(0, p, H3_FIXED_SIZE);+	__archive_read_consume(a, H3_FIXED_SIZE);++	/* Read extended headers */+	err = lha_read_file_extended_header(a, lha, &header_crc, 4,+		  lha->header_size - H3_FIXED_SIZE, &extdsize);+	if (err < ARCHIVE_WARN)+		return (err);++	if (header_crc != lha->header_crc) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "LHa header CRC error");+		return (ARCHIVE_FATAL);+	}+	return (err);+invalid:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Invalid LHa header");+	return (ARCHIVE_FATAL);+}++/*+ * Extended header format+ *+ * +0             +2        +3  -- used in header 1 and 2+ * +0             +4        +5  -- used in header 3+ * +--------------+---------+-------------------+--------------+--+ * |ex-header size|header id|        data       |ex-header size| .......+ * +--------------+---------+-------------------+--------------+--+ *  <-------------( ex-header size)------------> <-- next extended header --*+ *+ * If the ex-header size is zero, it is the make of the end of extended+ * headers.+ *+ */+static int+lha_read_file_extended_header(struct archive_read *a, struct lha *lha,+    uint16_t *crc, int sizefield_length, size_t limitsize, size_t *total_size)+{+	const void *h;+	const unsigned char *extdheader;+	size_t	extdsize;+	size_t	datasize;+	unsigned int i;+	unsigned char extdtype;++#define EXT_HEADER_CRC		0x00		/* Header CRC and information*/+#define EXT_FILENAME		0x01		/* Filename 		    */+#define EXT_DIRECTORY		0x02		/* Directory name	    */+#define EXT_DOS_ATTR		0x40		/* MS-DOS attribute	    */+#define EXT_TIMESTAMP		0x41		/* Windows time stamp	    */+#define EXT_FILESIZE		0x42		/* Large file size	    */+#define EXT_TIMEZONE		0x43		/* Time zone		    */+#define EXT_UTF16_FILENAME	0x44		/* UTF-16 filename 	    */+#define EXT_UTF16_DIRECTORY	0x45		/* UTF-16 directory name    */+#define EXT_CODEPAGE		0x46		/* Codepage		    */+#define EXT_UNIX_MODE		0x50		/* File permission	    */+#define EXT_UNIX_GID_UID	0x51		/* gid,uid		    */+#define EXT_UNIX_GNAME		0x52		/* Group name		    */+#define EXT_UNIX_UNAME		0x53		/* User name		    */+#define EXT_UNIX_MTIME		0x54		/* Modified time	    */+#define EXT_OS2_NEW_ATTR	0x7f		/* new attribute(OS/2 only) */+#define EXT_NEW_ATTR		0xff		/* new attribute	    */++	*total_size = sizefield_length;++	for (;;) {+		/* Read an extended header size. */+		if ((h =+		    __archive_read_ahead(a, sizefield_length, NULL)) == NULL)+			return (truncated_error(a));+		/* Check if the size is the zero indicates the end of the+		 * extended header. */+		if (sizefield_length == sizeof(uint16_t))+			extdsize = archive_le16dec(h);+		else+			extdsize = archive_le32dec(h);+		if (extdsize == 0) {+			/* End of extended header */+			if (crc != NULL)+				*crc = lha_crc16(*crc, h, sizefield_length);+			__archive_read_consume(a, sizefield_length);+			return (ARCHIVE_OK);+		}++		/* Sanity check to the extended header size. */+		if (((uint64_t)*total_size + extdsize) >+				    (uint64_t)limitsize ||+		    extdsize <= (size_t)sizefield_length)+			goto invalid;++		/* Read the extended header. */+		if ((h = __archive_read_ahead(a, extdsize, NULL)) == NULL)+			return (truncated_error(a));+		*total_size += extdsize;++		extdheader = (const unsigned char *)h;+		/* Get the extended header type. */+		extdtype = extdheader[sizefield_length];+		/* Calculate an extended data size. */+		datasize = extdsize - (1 + sizefield_length);+		/* Skip an extended header size field and type field. */+		extdheader += sizefield_length + 1;++		if (crc != NULL && extdtype != EXT_HEADER_CRC)+			*crc = lha_crc16(*crc, h, extdsize);+		switch (extdtype) {+		case EXT_HEADER_CRC:+			/* We only use a header CRC. Following data will not+			 * be used. */+			if (datasize >= 2) {+				lha->header_crc = archive_le16dec(extdheader);+				if (crc != NULL) {+					static const char zeros[2] = {0, 0};+					*crc = lha_crc16(*crc, h,+					    extdsize - datasize);+					/* CRC value itself as zero */+					*crc = lha_crc16(*crc, zeros, 2);+					*crc = lha_crc16(*crc,+					    extdheader+2, datasize - 2);+				}+			}+			break;+		case EXT_FILENAME:+			if (datasize == 0) {+				/* maybe directory header */+				archive_string_empty(&lha->filename);+				break;+			}+			if (extdheader[0] == '\0')+				goto invalid;+			archive_strncpy(&lha->filename,+			    (const char *)extdheader, datasize);+			break;+		case EXT_UTF16_FILENAME:+			if (datasize == 0) {+				/* maybe directory header */+				archive_string_empty(&lha->filename);+				break;+			} else if (datasize & 1) {+				/* UTF-16 characters take always 2 or 4 bytes */+				goto invalid;+			}+			if (extdheader[0] == '\0')+				goto invalid;+			archive_string_empty(&lha->filename);+			archive_array_append(&lha->filename,+				(const char *)extdheader, datasize);+			/* Setup a string conversion for a filename. */+			lha->sconv_fname =+			    archive_string_conversion_from_charset(&a->archive,+			        "UTF-16LE", 1);+			if (lha->sconv_fname == NULL)+				return (ARCHIVE_FATAL);+			break;+		case EXT_DIRECTORY:+			if (datasize == 0 || extdheader[0] == '\0')+				/* no directory name data. exit this case. */+				goto invalid;++			archive_strncpy(&lha->dirname,+		  	    (const char *)extdheader, datasize);+			/*+			 * Convert directory delimiter from 0xFF+			 * to '/' for local system.+	 		 */+			for (i = 0; i < lha->dirname.length; i++) {+				if ((unsigned char)lha->dirname.s[i] == 0xFF)+					lha->dirname.s[i] = '/';+			}+			/* Is last character directory separator? */+			if (lha->dirname.s[lha->dirname.length-1] != '/')+				/* invalid directory data */+				goto invalid;+			break;+		case EXT_UTF16_DIRECTORY:+			/* UTF-16 characters take always 2 or 4 bytes */+			if (datasize == 0 || (datasize & 1) ||+			    extdheader[0] == '\0') {+				/* no directory name data. exit this case. */+				goto invalid;+			}++			archive_string_empty(&lha->dirname);+			archive_array_append(&lha->dirname,+				(const char *)extdheader, datasize);+			lha->sconv_dir =+			    archive_string_conversion_from_charset(&a->archive,+			        "UTF-16LE", 1);+			if (lha->sconv_dir == NULL)+				return (ARCHIVE_FATAL);+			else {+				/*+				 * Convert directory delimiter from 0xFFFF+				 * to '/' for local system.+				 */+				uint16_t dirSep;+				uint16_t d = 1;+				if (archive_be16dec(&d) == 1)+					dirSep = 0x2F00;+				else+					dirSep = 0x002F;++				/* UTF-16LE character */+				uint16_t *utf16name =+				    (uint16_t *)lha->dirname.s;+				for (i = 0; i < lha->dirname.length / 2; i++) {+					if (utf16name[i] == 0xFFFF) {+						utf16name[i] = dirSep;+					}+				}+				/* Is last character directory separator? */+				if (utf16name[lha->dirname.length / 2 - 1] !=+				    dirSep) {+					/* invalid directory data */+					goto invalid;+				}+			}+			break;+		case EXT_DOS_ATTR:+			if (datasize == 2)+				lha->dos_attr = (unsigned char)+				    (archive_le16dec(extdheader) & 0xff);+			break;+		case EXT_TIMESTAMP:+			if (datasize == (sizeof(uint64_t) * 3)) {+				lha->birthtime = lha_win_time(+				    archive_le64dec(extdheader),+				    &lha->birthtime_tv_nsec);+				extdheader += sizeof(uint64_t);+				lha->mtime = lha_win_time(+				    archive_le64dec(extdheader),+				    &lha->mtime_tv_nsec);+				extdheader += sizeof(uint64_t);+				lha->atime = lha_win_time(+				    archive_le64dec(extdheader),+				    &lha->atime_tv_nsec);+				lha->setflag |= BIRTHTIME_IS_SET |+				    ATIME_IS_SET;+			}+			break;+		case EXT_FILESIZE:+			if (datasize == sizeof(uint64_t) * 2) {+				lha->compsize = archive_le64dec(extdheader);+				extdheader += sizeof(uint64_t);+				lha->origsize = archive_le64dec(extdheader);+			}+			break;+		case EXT_CODEPAGE:+			/* Get an archived filename charset from codepage.+			 * This overwrites the charset specified by+			 * hdrcharset option. */+			if (datasize == sizeof(uint32_t)) {+				struct archive_string cp;+				const char *charset;++				archive_string_init(&cp);+				switch (archive_le32dec(extdheader)) {+				case 65001: /* UTF-8 */+					charset = "UTF-8";+					break;+				default:+					archive_string_sprintf(&cp, "CP%d",+					    (int)archive_le32dec(extdheader));+					charset = cp.s;+					break;+				}+				lha->sconv_dir =+				    archive_string_conversion_from_charset(+					&(a->archive), charset, 1);+				lha->sconv_fname =+				    archive_string_conversion_from_charset(+					&(a->archive), charset, 1);+				archive_string_free(&cp);+				if (lha->sconv_dir == NULL)+					return (ARCHIVE_FATAL);+				if (lha->sconv_fname == NULL)+					return (ARCHIVE_FATAL);+			}+			break;+		case EXT_UNIX_MODE:+			if (datasize == sizeof(uint16_t)) {+				lha->mode = archive_le16dec(extdheader);+				lha->setflag |= UNIX_MODE_IS_SET;+			}+			break;+		case EXT_UNIX_GID_UID:+			if (datasize == (sizeof(uint16_t) * 2)) {+				lha->gid = archive_le16dec(extdheader);+				lha->uid = archive_le16dec(extdheader+2);+			}+			break;+		case EXT_UNIX_GNAME:+			if (datasize > 0)+				archive_strncpy(&lha->gname,+				    (const char *)extdheader, datasize);+			break;+		case EXT_UNIX_UNAME:+			if (datasize > 0)+				archive_strncpy(&lha->uname,+				    (const char *)extdheader, datasize);+			break;+		case EXT_UNIX_MTIME:+			if (datasize == sizeof(uint32_t))+				lha->mtime = archive_le32dec(extdheader);+			break;+		case EXT_OS2_NEW_ATTR:+			/* This extended header is OS/2 depend. */+			if (datasize == 16) {+				lha->dos_attr = (unsigned char)+				    (archive_le16dec(extdheader) & 0xff);+				lha->mode = archive_le16dec(extdheader+2);+				lha->gid = archive_le16dec(extdheader+4);+				lha->uid = archive_le16dec(extdheader+6);+				lha->birthtime = archive_le32dec(extdheader+8);+				lha->atime = archive_le32dec(extdheader+12);+				lha->setflag |= UNIX_MODE_IS_SET+				    | BIRTHTIME_IS_SET | ATIME_IS_SET;+			}+			break;+		case EXT_NEW_ATTR:+			if (datasize == 20) {+				lha->mode = (mode_t)archive_le32dec(extdheader);+				lha->gid = archive_le32dec(extdheader+4);+				lha->uid = archive_le32dec(extdheader+8);+				lha->birthtime = archive_le32dec(extdheader+12);+				lha->atime = archive_le32dec(extdheader+16);+				lha->setflag |= UNIX_MODE_IS_SET+				    | BIRTHTIME_IS_SET | ATIME_IS_SET;+			}+			break;+		case EXT_TIMEZONE:		/* Not supported */+			break;+		default:+			break;+		}++		__archive_read_consume(a, extdsize);+	}+invalid:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Invalid extended LHa header");+	return (ARCHIVE_FATAL);+}++static int+lha_end_of_entry(struct archive_read *a)+{+	struct lha *lha = (struct lha *)(a->format->data);+	int r = ARCHIVE_EOF;++	if (!lha->end_of_entry_cleanup) {+		if ((lha->setflag & CRC_IS_SET) &&+		    lha->crc != lha->entry_crc_calculated) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "LHa data CRC error");+			r = ARCHIVE_WARN;+		}++		/* End-of-entry cleanup done. */+		lha->end_of_entry_cleanup = 1;+	}+	return (r);+}++static int+archive_read_format_lha_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	struct lha *lha = (struct lha *)(a->format->data);+	int r;++	if (lha->entry_unconsumed) {+		/* Consume as much as the decompressor actually used. */+		__archive_read_consume(a, lha->entry_unconsumed);+		lha->entry_unconsumed = 0;+	}+	if (lha->end_of_entry) {+		*offset = lha->entry_offset;+		*size = 0;+		*buff = NULL;+		return (lha_end_of_entry(a));+	}++	if (lha->entry_is_compressed)+		r =  lha_read_data_lzh(a, buff, size, offset);+	else+		/* No compression. */+		r =  lha_read_data_none(a, buff, size, offset);+	return (r);+}++/*+ * Read a file content in no compression.+ *+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets+ * lha->end_of_entry if it consumes all of the data.+ */+static int+lha_read_data_none(struct archive_read *a, const void **buff,+    size_t *size, int64_t *offset)+{+	struct lha *lha = (struct lha *)(a->format->data);+	ssize_t bytes_avail;++	if (lha->entry_bytes_remaining == 0) {+		*buff = NULL;+		*size = 0;+		*offset = lha->entry_offset;+		lha->end_of_entry = 1;+		return (ARCHIVE_OK);+	}+	/*+	 * Note: '1' here is a performance optimization.+	 * Recall that the decompression layer returns a count of+	 * available bytes; asking for more than that forces the+	 * decompressor to combine reads by copying data.+	 */+	*buff = __archive_read_ahead(a, 1, &bytes_avail);+	if (bytes_avail <= 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated LHa file data");+		return (ARCHIVE_FATAL);+	}+	if (bytes_avail > lha->entry_bytes_remaining)+		bytes_avail = (ssize_t)lha->entry_bytes_remaining;+	lha->entry_crc_calculated =+	    lha_crc16(lha->entry_crc_calculated, *buff, bytes_avail);+	*size = bytes_avail;+	*offset = lha->entry_offset;+	lha->entry_offset += bytes_avail;+	lha->entry_bytes_remaining -= bytes_avail;+	if (lha->entry_bytes_remaining == 0)+		lha->end_of_entry = 1;+	lha->entry_unconsumed = bytes_avail;+	return (ARCHIVE_OK);+}++/*+ * Read a file content in LZHUFF encoding.+ *+ * Returns ARCHIVE_OK if successful, returns ARCHIVE_WARN if compression is+ * unsupported, ARCHIVE_FATAL otherwise, sets lha->end_of_entry if it consumes+ * all of the data.+ */+static int+lha_read_data_lzh(struct archive_read *a, const void **buff,+    size_t *size, int64_t *offset)+{+	struct lha *lha = (struct lha *)(a->format->data);+	ssize_t bytes_avail;+	int r;++	/* If we haven't yet read any data, initialize the decompressor. */+	if (!lha->decompress_init) {+		r = lzh_decode_init(&(lha->strm), lha->method);+		switch (r) {+		case ARCHIVE_OK:+			break;+		case ARCHIVE_FAILED:+        		/* Unsupported compression. */+			*buff = NULL;+			*size = 0;+			*offset = 0;+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Unsupported lzh compression method -%c%c%c-",+			    lha->method[0], lha->method[1], lha->method[2]);+			/* We know compressed size; just skip it. */+			archive_read_format_lha_read_data_skip(a);+			return (ARCHIVE_WARN);+		default:+			archive_set_error(&a->archive, ENOMEM,+			    "Couldn't allocate memory "+			    "for lzh decompression");+			return (ARCHIVE_FATAL);+		}+		/* We've initialized decompression for this stream. */+		lha->decompress_init = 1;+		lha->strm.avail_out = 0;+		lha->strm.total_out = 0;+	}++	/*+	 * Note: '1' here is a performance optimization.+	 * Recall that the decompression layer returns a count of+	 * available bytes; asking for more than that forces the+	 * decompressor to combine reads by copying data.+	 */+	lha->strm.next_in = __archive_read_ahead(a, 1, &bytes_avail);+	if (bytes_avail <= 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated LHa file body");+		return (ARCHIVE_FATAL);+	}+	if (bytes_avail > lha->entry_bytes_remaining)+		bytes_avail = (ssize_t)lha->entry_bytes_remaining;++	lha->strm.avail_in = (int)bytes_avail;+	lha->strm.total_in = 0;+	lha->strm.avail_out = 0;++	r = lzh_decode(&(lha->strm), bytes_avail == lha->entry_bytes_remaining);+	switch (r) {+	case ARCHIVE_OK:+		break;+	case ARCHIVE_EOF:+		lha->end_of_entry = 1;+		break;+	default:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Bad lzh data");+		return (ARCHIVE_FAILED);+	}+	lha->entry_unconsumed = lha->strm.total_in;+	lha->entry_bytes_remaining -= lha->strm.total_in;++	if (lha->strm.avail_out) {+		*offset = lha->entry_offset;+		*size = lha->strm.avail_out;+		*buff = lha->strm.ref_ptr;+		lha->entry_crc_calculated =+		    lha_crc16(lha->entry_crc_calculated, *buff, *size);+		lha->entry_offset += *size;+	} else {+		*offset = lha->entry_offset;+		*size = 0;+		*buff = NULL;+		if (lha->end_of_entry)+			return (lha_end_of_entry(a));+	}+	return (ARCHIVE_OK);+}++/*+ * Skip a file content.+ */+static int+archive_read_format_lha_read_data_skip(struct archive_read *a)+{+	struct lha *lha;+	int64_t bytes_skipped;++	lha = (struct lha *)(a->format->data);++	if (lha->entry_unconsumed) {+		/* Consume as much as the decompressor actually used. */+		__archive_read_consume(a, lha->entry_unconsumed);+		lha->entry_unconsumed = 0;+	}++	/* if we've already read to end of data, we're done. */+	if (lha->end_of_entry_cleanup)+		return (ARCHIVE_OK);++	/*+	 * If the length is at the beginning, we can skip the+	 * compressed data much more quickly.+	 */+	bytes_skipped = __archive_read_consume(a, lha->entry_bytes_remaining);+	if (bytes_skipped < 0)+		return (ARCHIVE_FATAL);++	/* This entry is finished and done. */+	lha->end_of_entry_cleanup = lha->end_of_entry = 1;+	return (ARCHIVE_OK);+}++static int+archive_read_format_lha_cleanup(struct archive_read *a)+{+	struct lha *lha = (struct lha *)(a->format->data);++	lzh_decode_free(&(lha->strm));+	archive_string_free(&(lha->dirname));+	archive_string_free(&(lha->filename));+	archive_string_free(&(lha->uname));+	archive_string_free(&(lha->gname));+	archive_wstring_free(&(lha->ws));+	free(lha);+	(a->format->data) = NULL;+	return (ARCHIVE_OK);+}++/*+ * 'LHa for UNIX' utility has archived a symbolic-link name after+ * a pathname with '|' character.+ * This function extracts the symbolic-link name from the pathname.+ *+ * example.+ *   1. a symbolic-name is 'aaa/bb/cc'+ *   2. a filename is 'xxx/bbb'+ *  then a archived pathname is 'xxx/bbb|aaa/bb/cc'+ */+static int+lha_parse_linkname(struct archive_wstring *linkname,+    struct archive_wstring *pathname)+{+	wchar_t *	linkptr;+	size_t 	symlen;++	linkptr = wcschr(pathname->s, L'|');+	if (linkptr != NULL) {+		symlen = wcslen(linkptr + 1);+		archive_wstrncpy(linkname, linkptr+1, symlen);++		*linkptr = 0;+		pathname->length = wcslen(pathname->s);++		return (1);+	}+	return (0);+}++/* Convert an MSDOS-style date/time into Unix-style time. */+static time_t+lha_dos_time(const unsigned char *p)+{+	int msTime, msDate;+	struct tm ts;++	msTime = archive_le16dec(p);+	msDate = archive_le16dec(p+2);++	memset(&ts, 0, sizeof(ts));+	ts.tm_year = ((msDate >> 9) & 0x7f) + 80;   /* Years since 1900. */+	ts.tm_mon = ((msDate >> 5) & 0x0f) - 1;     /* Month number.     */+	ts.tm_mday = msDate & 0x1f;		    /* Day of month.     */+	ts.tm_hour = (msTime >> 11) & 0x1f;+	ts.tm_min = (msTime >> 5) & 0x3f;+	ts.tm_sec = (msTime << 1) & 0x3e;+	ts.tm_isdst = -1;+	return (mktime(&ts));+}++/* Convert an MS-Windows-style date/time into Unix-style time. */+static time_t+lha_win_time(uint64_t wintime, long *ns)+{+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)++	if (wintime >= EPOC_TIME) {+		wintime -= EPOC_TIME;	/* 1970-01-01 00:00:00 (UTC) */+		if (ns != NULL)+			*ns = (long)(wintime % 10000000) * 100;+		return (wintime / 10000000);+	} else {+		if (ns != NULL)+			*ns = 0;+		return (0);+	}+}++static unsigned char+lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size)+{+	unsigned char const *p = (unsigned char const *)pp;++	p += offset;+	for (;size > 0; --size)+		sum += *p++;+	return (sum);+}++static uint16_t crc16tbl[2][256];+static void+lha_crc16_init(void)+{+	unsigned int i;+	static int crc16init = 0;++	if (crc16init)+		return;+	crc16init = 1;++	for (i = 0; i < 256; i++) {+		unsigned int j;+		uint16_t crc = (uint16_t)i;+		for (j = 8; j; j--)+			crc = (crc >> 1) ^ ((crc & 1) * 0xA001);+		crc16tbl[0][i] = crc;+	}++	for (i = 0; i < 256; i++) {+		crc16tbl[1][i] = (crc16tbl[0][i] >> 8)+			^ crc16tbl[0][crc16tbl[0][i] & 0xff];+	}+}++static uint16_t+lha_crc16(uint16_t crc, const void *pp, size_t len)+{+	const unsigned char *p = (const unsigned char *)pp;+	const uint16_t *buff;+	const union {+		uint32_t i;+		char c[4];+	} u = { 0x01020304 };++	if (len == 0)+		return crc;++	/* Process unaligned address. */+	if (((uintptr_t)p) & (uintptr_t)0x1) {+		crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff];+		len--;+	}+	buff = (const uint16_t *)p;+	/*+	 * Modern C compiler such as GCC does not unroll automatically yet+	 * without unrolling pragma, and Clang is so. So we should+	 * unroll this loop for its performance.+	 */+	for (;len >= 8; len -= 8) {+		/* This if statement expects compiler optimization will+		 * remove the statement which will not be executed. */+#undef bswap16+#if defined(_MSC_VER) && _MSC_VER >= 1400  /* Visual Studio */+#  define bswap16(x) _byteswap_ushort(x)+#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4)+/* GCC 4.8 and later has __builtin_bswap16() */+#  define bswap16(x) __builtin_bswap16(x)+#elif defined(__clang__)+/* All clang versions have __builtin_bswap16() */+#  define bswap16(x) __builtin_bswap16(x)+#else+#  define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8))+#endif+#define CRC16W	do { 	\+		if(u.c[0] == 1) { /* Big endian */		\+			crc ^= bswap16(*buff); buff++;		\+		} else						\+			crc ^= *buff++;				\+		crc = crc16tbl[1][crc & 0xff] ^ crc16tbl[0][crc >> 8];\+} while (0)+		CRC16W;+		CRC16W;+		CRC16W;+		CRC16W;+#undef CRC16W+#undef bswap16+	}++	p = (const unsigned char *)buff;+	for (;len; len--) {+		crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff];+	}+	return crc;+}++/*+ * Initialize LZHUF decoder.+ *+ * Returns ARCHIVE_OK if initialization was successful.+ * Returns ARCHIVE_FAILED if method is unsupported.+ * Returns ARCHIVE_FATAL if initialization failed; memory allocation+ * error occurred.+ */+static int+lzh_decode_init(struct lzh_stream *strm, const char *method)+{+	struct lzh_dec *ds;+	int w_bits, w_size;++	if (strm->ds == NULL) {+		strm->ds = calloc(1, sizeof(*strm->ds));+		if (strm->ds == NULL)+			return (ARCHIVE_FATAL);+	}+	ds = strm->ds;+	ds->error = ARCHIVE_FAILED;+	if (method == NULL || method[0] != 'l' || method[1] != 'h')+		return (ARCHIVE_FAILED);+	switch (method[2]) {+	case '5':+		w_bits = 13;/* 8KiB for window */+		break;+	case '6':+		w_bits = 15;/* 32KiB for window */+		break;+	case '7':+		w_bits = 16;/* 64KiB for window */+		break;+	default:+		return (ARCHIVE_FAILED);/* Not supported. */+	}+	ds->error = ARCHIVE_FATAL;+	/* Expand a window size up to 128 KiB for decompressing process+	 * performance whatever its original window size is. */+	ds->w_size = 1U << 17;+	ds->w_mask = ds->w_size -1;+	if (ds->w_buff == NULL) {+		ds->w_buff = malloc(ds->w_size);+		if (ds->w_buff == NULL)+			return (ARCHIVE_FATAL);+	}+	w_size = 1U << w_bits;+	memset(ds->w_buff + ds->w_size - w_size, 0x20, w_size);+	ds->w_pos = 0;+	ds->state = 0;+	ds->pos_pt_len_size = w_bits + 1;+	ds->pos_pt_len_bits = (w_bits == 15 || w_bits == 16)? 5: 4;+	ds->literal_pt_len_size = PT_BITLEN_SIZE;+	ds->literal_pt_len_bits = 5;+	ds->br.cache_buffer = 0;+	ds->br.cache_avail = 0;++	if (lzh_huffman_init(&(ds->lt), LT_BITLEN_SIZE, 16)+	    != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	ds->lt.len_bits = 9;+	if (lzh_huffman_init(&(ds->pt), PT_BITLEN_SIZE, 16)+	    != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	ds->error = 0;++	return (ARCHIVE_OK);+}++/*+ * Release LZHUF decoder.+ */+static void+lzh_decode_free(struct lzh_stream *strm)+{++	if (strm->ds == NULL)+		return;+	free(strm->ds->w_buff);+	lzh_huffman_free(&(strm->ds->lt));+	lzh_huffman_free(&(strm->ds->pt));+	free(strm->ds);+	strm->ds = NULL;+}++/*+ * Bit stream reader.+ */+/* Check that the cache buffer has enough bits. */+#define lzh_br_has(br, n)	((br)->cache_avail >= n)+/* Get compressed data by bit. */+#define lzh_br_bits(br, n)				\+	(((uint16_t)((br)->cache_buffer >>		\+		((br)->cache_avail - (n)))) & cache_masks[n])+#define lzh_br_bits_forced(br, n)			\+	(((uint16_t)((br)->cache_buffer <<		\+		((n) - (br)->cache_avail))) & cache_masks[n])+/* Read ahead to make sure the cache buffer has enough compressed data we+ * will use.+ *  True  : completed, there is enough data in the cache buffer.+ *  False : we met that strm->next_in is empty, we have to get following+ *          bytes. */+#define lzh_br_read_ahead_0(strm, br, n)	\+	(lzh_br_has(br, (n)) || lzh_br_fillup(strm, br))+/*  True  : the cache buffer has some bits as much as we need.+ *  False : there are no enough bits in the cache buffer to be used,+ *          we have to get following bytes if we could. */+#define lzh_br_read_ahead(strm, br, n)	\+	(lzh_br_read_ahead_0((strm), (br), (n)) || lzh_br_has((br), (n)))++/* Notify how many bits we consumed. */+#define lzh_br_consume(br, n)	((br)->cache_avail -= (n))+#define lzh_br_unconsume(br, n)	((br)->cache_avail += (n))++static const uint16_t cache_masks[] = {+	0x0000, 0x0001, 0x0003, 0x0007,+	0x000F, 0x001F, 0x003F, 0x007F,+	0x00FF, 0x01FF, 0x03FF, 0x07FF,+	0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,+	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF+};++/*+ * Shift away used bits in the cache data and fill it up with following bits.+ * Call this when cache buffer does not have enough bits you need.+ *+ * Returns 1 if the cache buffer is full.+ * Returns 0 if the cache buffer is not full; input buffer is empty.+ */+static int+lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br)+{+	int n = CACHE_BITS - br->cache_avail;++	for (;;) {+		const int x = n >> 3;+		if (strm->avail_in >= x) {+			switch (x) {+			case 8:+				br->cache_buffer =+				    ((uint64_t)strm->next_in[0]) << 56 |+				    ((uint64_t)strm->next_in[1]) << 48 |+				    ((uint64_t)strm->next_in[2]) << 40 |+				    ((uint64_t)strm->next_in[3]) << 32 |+				    ((uint32_t)strm->next_in[4]) << 24 |+				    ((uint32_t)strm->next_in[5]) << 16 |+				    ((uint32_t)strm->next_in[6]) << 8 |+				     (uint32_t)strm->next_in[7];+				strm->next_in += 8;+				strm->avail_in -= 8;+				br->cache_avail += 8 * 8;+				return (1);+			case 7:+				br->cache_buffer =+		 		   (br->cache_buffer << 56) |+				    ((uint64_t)strm->next_in[0]) << 48 |+				    ((uint64_t)strm->next_in[1]) << 40 |+				    ((uint64_t)strm->next_in[2]) << 32 |+				    ((uint32_t)strm->next_in[3]) << 24 |+				    ((uint32_t)strm->next_in[4]) << 16 |+				    ((uint32_t)strm->next_in[5]) << 8 |+				     (uint32_t)strm->next_in[6];+				strm->next_in += 7;+				strm->avail_in -= 7;+				br->cache_avail += 7 * 8;+				return (1);+			case 6:+				br->cache_buffer =+		 		   (br->cache_buffer << 48) |+				    ((uint64_t)strm->next_in[0]) << 40 |+				    ((uint64_t)strm->next_in[1]) << 32 |+				    ((uint32_t)strm->next_in[2]) << 24 |+				    ((uint32_t)strm->next_in[3]) << 16 |+				    ((uint32_t)strm->next_in[4]) << 8 |+				     (uint32_t)strm->next_in[5];+				strm->next_in += 6;+				strm->avail_in -= 6;+				br->cache_avail += 6 * 8;+				return (1);+			case 0:+				/* We have enough compressed data in+				 * the cache buffer.*/+				return (1);+			default:+				break;+			}+		}+		if (strm->avail_in == 0) {+			/* There is not enough compressed data to fill up the+			 * cache buffer. */+			return (0);+		}+		br->cache_buffer =+		   (br->cache_buffer << 8) | *strm->next_in++;+		strm->avail_in--;+		br->cache_avail += 8;+		n -= 8;+	}+}++/*+ * Decode LZHUF.+ *+ * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty.+ *    Please set available buffer and call this function again.+ * 2. Returns ARCHIVE_EOF if decompression has been completed.+ * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data+ *    is broken or you do not set 'last' flag properly.+ * 4. 'last' flag is very important, you must set 1 to the flag if there+ *    is no input data. The lha compressed data format does not provide how+ *    to know the compressed data is really finished.+ *    Note: lha command utility check if the total size of output bytes is+ *    reached the uncompressed size recorded in its header. it does not mind+ *    that the decoding process is properly finished.+ *    GNU ZIP can decompress another compressed file made by SCO LZH compress.+ *    it handles EOF as null to fill read buffer with zero until the decoding+ *    process meet 2 bytes of zeros at reading a size of a next chunk, so the+ *    zeros are treated as the mark of the end of the data although the zeros+ *    is dummy, not the file data.+ */+static int	lzh_read_blocks(struct lzh_stream *, int);+static int	lzh_decode_blocks(struct lzh_stream *, int);+#define ST_RD_BLOCK		0+#define ST_RD_PT_1		1+#define ST_RD_PT_2		2+#define ST_RD_PT_3		3+#define ST_RD_PT_4		4+#define ST_RD_LITERAL_1		5+#define ST_RD_LITERAL_2		6+#define ST_RD_LITERAL_3		7+#define ST_RD_POS_DATA_1	8+#define ST_GET_LITERAL		9+#define ST_GET_POS_1		10+#define ST_GET_POS_2		11+#define ST_COPY_DATA		12++static int+lzh_decode(struct lzh_stream *strm, int last)+{+	struct lzh_dec *ds = strm->ds;+	int avail_in;+	int r;++	if (ds->error)+		return (ds->error);++	avail_in = strm->avail_in;+	do {+		if (ds->state < ST_GET_LITERAL)+			r = lzh_read_blocks(strm, last);+		else+			r = lzh_decode_blocks(strm, last);+	} while (r == 100);+	strm->total_in += avail_in - strm->avail_in;+	return (r);+}++static void+lzh_emit_window(struct lzh_stream *strm, size_t s)+{+	strm->ref_ptr = strm->ds->w_buff;+	strm->avail_out = (int)s;+	strm->total_out += s;+}++static int+lzh_read_blocks(struct lzh_stream *strm, int last)+{+	struct lzh_dec *ds = strm->ds;+	struct lzh_br *br = &(ds->br);+	int c = 0, i;+	unsigned rbits;++	for (;;) {+		switch (ds->state) {+		case ST_RD_BLOCK:+			/*+			 * Read a block number indicates how many blocks+			 * we will handle. The block is composed of a+			 * literal and a match, sometimes a literal only+			 * in particular, there are no reference data at+			 * the beginning of the decompression.+			 */+			if (!lzh_br_read_ahead_0(strm, br, 16)) {+				if (!last)+					/* We need following data. */+					return (ARCHIVE_OK);+				if (lzh_br_has(br, 8)) {+					/*+					 * It seems there are extra bits.+					 *  1. Compressed data is broken.+					 *  2. `last' flag does not properly+					 *     set.+					 */+					goto failed;+				}+				if (ds->w_pos > 0) {+					lzh_emit_window(strm, ds->w_pos);+					ds->w_pos = 0;+					return (ARCHIVE_OK);+				}+				/* End of compressed data; we have completely+				 * handled all compressed data. */+				return (ARCHIVE_EOF);+			}+			ds->blocks_avail = lzh_br_bits(br, 16);+			if (ds->blocks_avail == 0)+				goto failed;+			lzh_br_consume(br, 16);+			/*+			 * Read a literal table compressed in huffman+			 * coding.+			 */+			ds->pt.len_size = ds->literal_pt_len_size;+			ds->pt.len_bits = ds->literal_pt_len_bits;+			ds->reading_position = 0;+			/* FALL THROUGH */+		case ST_RD_PT_1:+			/* Note: ST_RD_PT_1, ST_RD_PT_2 and ST_RD_PT_4 are+			 * used in reading both a literal table and a+			 * position table. */+			if (!lzh_br_read_ahead(strm, br, ds->pt.len_bits)) {+				if (last)+					goto failed;/* Truncated data. */+				ds->state = ST_RD_PT_1;+				return (ARCHIVE_OK);+			}+			ds->pt.len_avail = lzh_br_bits(br, ds->pt.len_bits);+			lzh_br_consume(br, ds->pt.len_bits);+			/* FALL THROUGH */+		case ST_RD_PT_2:+			if (ds->pt.len_avail == 0) {+				/* There is no bitlen. */+				if (!lzh_br_read_ahead(strm, br,+				    ds->pt.len_bits)) {+					if (last)+						goto failed;/* Truncated data.*/+					ds->state = ST_RD_PT_2;+					return (ARCHIVE_OK);+				}+				if (!lzh_make_fake_table(&(ds->pt),+				    lzh_br_bits(br, ds->pt.len_bits)))+					goto failed;/* Invalid data. */+				lzh_br_consume(br, ds->pt.len_bits);+				if (ds->reading_position)+					ds->state = ST_GET_LITERAL;+				else+					ds->state = ST_RD_LITERAL_1;+				break;+			} else if (ds->pt.len_avail > ds->pt.len_size)+				goto failed;/* Invalid data. */+			ds->loop = 0;+			memset(ds->pt.freq, 0, sizeof(ds->pt.freq));+			if (ds->pt.len_avail < 3 ||+			    ds->pt.len_size == ds->pos_pt_len_size) {+				ds->state = ST_RD_PT_4;+				break;+			}+			/* FALL THROUGH */+		case ST_RD_PT_3:+			ds->loop = lzh_read_pt_bitlen(strm, ds->loop, 3);+			if (ds->loop < 3) {+				if (ds->loop < 0 || last)+					goto failed;/* Invalid data. */+				/* Not completed, get following data. */+				ds->state = ST_RD_PT_3;+				return (ARCHIVE_OK);+			}+			/* There are some null in bitlen of the literal. */+			if (!lzh_br_read_ahead(strm, br, 2)) {+				if (last)+					goto failed;/* Truncated data. */+				ds->state = ST_RD_PT_3;+				return (ARCHIVE_OK);+			}+			c = lzh_br_bits(br, 2);+			lzh_br_consume(br, 2);+			if (c > ds->pt.len_avail - 3)+				goto failed;/* Invalid data. */+			for (i = 3; c-- > 0 ;)+				ds->pt.bitlen[i++] = 0;+			ds->loop = i;+			/* FALL THROUGH */+		case ST_RD_PT_4:+			ds->loop = lzh_read_pt_bitlen(strm, ds->loop,+			    ds->pt.len_avail);+			if (ds->loop < ds->pt.len_avail) {+				if (ds->loop < 0 || last)+					goto failed;/* Invalid data. */+				/* Not completed, get following data. */+				ds->state = ST_RD_PT_4;+				return (ARCHIVE_OK);+			}+			if (!lzh_make_huffman_table(&(ds->pt)))+				goto failed;/* Invalid data */+			if (ds->reading_position) {+				ds->state = ST_GET_LITERAL;+				break;+			}+			/* FALL THROUGH */+		case ST_RD_LITERAL_1:+			if (!lzh_br_read_ahead(strm, br, ds->lt.len_bits)) {+				if (last)+					goto failed;/* Truncated data. */+				ds->state = ST_RD_LITERAL_1;+				return (ARCHIVE_OK);+			}+			ds->lt.len_avail = lzh_br_bits(br, ds->lt.len_bits);+			lzh_br_consume(br, ds->lt.len_bits);+			/* FALL THROUGH */+		case ST_RD_LITERAL_2:+			if (ds->lt.len_avail == 0) {+				/* There is no bitlen. */+				if (!lzh_br_read_ahead(strm, br,+				    ds->lt.len_bits)) {+					if (last)+						goto failed;/* Truncated data.*/+					ds->state = ST_RD_LITERAL_2;+					return (ARCHIVE_OK);+				}+				if (!lzh_make_fake_table(&(ds->lt),+				    lzh_br_bits(br, ds->lt.len_bits)))+					goto failed;/* Invalid data */+				lzh_br_consume(br, ds->lt.len_bits);+				ds->state = ST_RD_POS_DATA_1;+				break;+			} else if (ds->lt.len_avail > ds->lt.len_size)+				goto failed;/* Invalid data */+			ds->loop = 0;+			memset(ds->lt.freq, 0, sizeof(ds->lt.freq));+			/* FALL THROUGH */+		case ST_RD_LITERAL_3:+			i = ds->loop;+			while (i < ds->lt.len_avail) {+				if (!lzh_br_read_ahead(strm, br,+				    ds->pt.max_bits)) {+					if (last)+						goto failed;/* Truncated data.*/+					ds->loop = i;+					ds->state = ST_RD_LITERAL_3;+					return (ARCHIVE_OK);+				}+				rbits = lzh_br_bits(br, ds->pt.max_bits);+				c = lzh_decode_huffman(&(ds->pt), rbits);+				if (c > 2) {+					/* Note: 'c' will never be more than+					 * eighteen since it's limited by+					 * PT_BITLEN_SIZE, which is being set+					 * to ds->pt.len_size through+					 * ds->literal_pt_len_size. */+					lzh_br_consume(br, ds->pt.bitlen[c]);+					c -= 2;+					ds->lt.freq[c]++;+					ds->lt.bitlen[i++] = c;+				} else if (c == 0) {+					lzh_br_consume(br, ds->pt.bitlen[c]);+					ds->lt.bitlen[i++] = 0;+				} else {+					/* c == 1 or c == 2 */+					int n = (c == 1)?4:9;+					if (!lzh_br_read_ahead(strm, br,+					     ds->pt.bitlen[c] + n)) {+						if (last) /* Truncated data. */+							goto failed;+						ds->loop = i;+						ds->state = ST_RD_LITERAL_3;+						return (ARCHIVE_OK);+					}+					lzh_br_consume(br, ds->pt.bitlen[c]);+					c = lzh_br_bits(br, n);+					lzh_br_consume(br, n);+					c += (n == 4)?3:20;+					if (i + c > ds->lt.len_avail)+						goto failed;/* Invalid data */+					memset(&(ds->lt.bitlen[i]), 0, c);+					i += c;+				}+			}+			if (i > ds->lt.len_avail ||+			    !lzh_make_huffman_table(&(ds->lt)))+				goto failed;/* Invalid data */+			/* FALL THROUGH */+		case ST_RD_POS_DATA_1:+			/*+			 * Read a position table compressed in huffman+			 * coding.+			 */+			ds->pt.len_size = ds->pos_pt_len_size;+			ds->pt.len_bits = ds->pos_pt_len_bits;+			ds->reading_position = 1;+			ds->state = ST_RD_PT_1;+			break;+		case ST_GET_LITERAL:+			return (100);+		}+	}+failed:+	return (ds->error = ARCHIVE_FAILED);+}++static int+lzh_decode_blocks(struct lzh_stream *strm, int last)+{+	struct lzh_dec *ds = strm->ds;+	struct lzh_br bre = ds->br;+	struct huffman *lt = &(ds->lt);+	struct huffman *pt = &(ds->pt);+	unsigned char *w_buff = ds->w_buff;+	unsigned char *lt_bitlen = lt->bitlen;+	unsigned char *pt_bitlen = pt->bitlen;+	int blocks_avail = ds->blocks_avail, c = 0;+	int copy_len = ds->copy_len, copy_pos = ds->copy_pos;+	int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;+	int lt_max_bits = lt->max_bits, pt_max_bits = pt->max_bits;+	int state = ds->state;++	for (;;) {+		switch (state) {+		case ST_GET_LITERAL:+			for (;;) {+				if (blocks_avail == 0) {+					/* We have decoded all blocks.+					 * Let's handle next blocks. */+					ds->state = ST_RD_BLOCK;+					ds->br = bre;+					ds->blocks_avail = 0;+					ds->w_pos = w_pos;+					ds->copy_pos = 0;+					return (100);+				}++				/* lzh_br_read_ahead() always try to fill the+				 * cache buffer up. In specific situation we+				 * are close to the end of the data, the cache+				 * buffer will not be full and thus we have to+				 * determine if the cache buffer has some bits+				 * as much as we need after lzh_br_read_ahead()+				 * failed. */+				if (!lzh_br_read_ahead(strm, &bre,+				    lt_max_bits)) {+					if (!last)+						goto next_data;+					/* Remaining bits are less than+					 * maximum bits(lt.max_bits) but maybe+					 * it still remains as much as we need,+					 * so we should try to use it with+					 * dummy bits. */+					c = lzh_decode_huffman(lt,+					      lzh_br_bits_forced(&bre,+					        lt_max_bits));+					lzh_br_consume(&bre, lt_bitlen[c]);+					if (!lzh_br_has(&bre, 0))+						goto failed;/* Over read. */+				} else {+					c = lzh_decode_huffman(lt,+					      lzh_br_bits(&bre, lt_max_bits));+					lzh_br_consume(&bre, lt_bitlen[c]);+				}+				blocks_avail--;+				if (c > UCHAR_MAX)+					/* Current block is a match data. */+					break;+				/*+				 * 'c' is exactly a literal code.+				 */+				/* Save a decoded code to reference it+				 * afterward. */+				w_buff[w_pos] = c;+				if (++w_pos >= w_size) {+					w_pos = 0;+					lzh_emit_window(strm, w_size);+					goto next_data;+				}+			}+			/* 'c' is the length of a match pattern we have+			 * already extracted, which has be stored in+			 * window(ds->w_buff). */+			copy_len = c - (UCHAR_MAX + 1) + MINMATCH;+			/* FALL THROUGH */+		case ST_GET_POS_1:+			/*+			 * Get a reference position. +			 */+			if (!lzh_br_read_ahead(strm, &bre, pt_max_bits)) {+				if (!last) {+					state = ST_GET_POS_1;+					ds->copy_len = copy_len;+					goto next_data;+				}+				copy_pos = lzh_decode_huffman(pt,+				    lzh_br_bits_forced(&bre, pt_max_bits));+				lzh_br_consume(&bre, pt_bitlen[copy_pos]);+				if (!lzh_br_has(&bre, 0))+					goto failed;/* Over read. */+			} else {+				copy_pos = lzh_decode_huffman(pt,+				    lzh_br_bits(&bre, pt_max_bits));+				lzh_br_consume(&bre, pt_bitlen[copy_pos]);+			}+			/* FALL THROUGH */+		case ST_GET_POS_2:+			if (copy_pos > 1) {+				/* We need an additional adjustment number to+				 * the position. */+				int p = copy_pos - 1;+				if (!lzh_br_read_ahead(strm, &bre, p)) {+					if (last)+						goto failed;/* Truncated data.*/+					state = ST_GET_POS_2;+					ds->copy_len = copy_len;+					ds->copy_pos = copy_pos;+					goto next_data;+				}+				copy_pos = (1 << p) + lzh_br_bits(&bre, p);+				lzh_br_consume(&bre, p);+			}+			/* The position is actually a distance from the last+			 * code we had extracted and thus we have to convert+			 * it to a position of the window. */+			copy_pos = (w_pos - copy_pos - 1) & w_mask;+			/* FALL THROUGH */+		case ST_COPY_DATA:+			/*+			 * Copy `copy_len' bytes as extracted data from+			 * the window into the output buffer.+			 */+			for (;;) {+				int l;++				l = copy_len;+				if (copy_pos > w_pos) {+					if (l > w_size - copy_pos)+						l = w_size - copy_pos;+				} else {+					if (l > w_size - w_pos)+						l = w_size - w_pos;+				}+				if ((copy_pos + l < w_pos)+				    || (w_pos + l < copy_pos)) {+					/* No overlap. */+					memcpy(w_buff + w_pos,+					    w_buff + copy_pos, l);+				} else {+					const unsigned char *s;+					unsigned char *d;+					int li;++					d = w_buff + w_pos;+					s = w_buff + copy_pos;+					for (li = 0; li < l-1;) {+						d[li] = s[li];li++;+						d[li] = s[li];li++;+					}+					if (li < l)+						d[li] = s[li];+				}+				w_pos += l;+				if (w_pos == w_size) {+					w_pos = 0;+					lzh_emit_window(strm, w_size);+					if (copy_len <= l)+						state = ST_GET_LITERAL;+					else {+						state = ST_COPY_DATA;+						ds->copy_len = copy_len - l;+						ds->copy_pos =+						    (copy_pos + l) & w_mask;+					}+					goto next_data;+				}+				if (copy_len <= l)+					/* A copy of current pattern ended. */+					break;+				copy_len -= l;+				copy_pos = (copy_pos + l) & w_mask;+			}+			state = ST_GET_LITERAL;+			break;+		}+	}+failed:+	return (ds->error = ARCHIVE_FAILED);+next_data:+	ds->br = bre;+	ds->blocks_avail = blocks_avail;+	ds->state = state;+	ds->w_pos = w_pos;+	return (ARCHIVE_OK);+}++static int+lzh_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)+{+	int bits;++	if (hf->bitlen == NULL) {+		hf->bitlen = malloc(len_size * sizeof(hf->bitlen[0]));+		if (hf->bitlen == NULL)+			return (ARCHIVE_FATAL);+	}+	if (hf->tbl == NULL) {+		if (tbl_bits < HTBL_BITS)+			bits = tbl_bits;+		else+			bits = HTBL_BITS;+		hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0]));+		if (hf->tbl == NULL)+			return (ARCHIVE_FATAL);+	}+	if (hf->tree == NULL && tbl_bits > HTBL_BITS) {+		hf->tree_avail = 1 << (tbl_bits - HTBL_BITS + 4);+		hf->tree = malloc(hf->tree_avail * sizeof(hf->tree[0]));+		if (hf->tree == NULL)+			return (ARCHIVE_FATAL);+	}+	hf->len_size = (int)len_size;+	hf->tbl_bits = tbl_bits;+	return (ARCHIVE_OK);+}++static void+lzh_huffman_free(struct huffman *hf)+{+	free(hf->bitlen);+	free(hf->tbl);+	free(hf->tree);+}++static const char bitlen_tbl[0x400] = {+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,+	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,+	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,+	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,+	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,+	11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,+	11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,+	12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,+	13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 16,  0+};+static int+lzh_read_pt_bitlen(struct lzh_stream *strm, int start, int end)+{+	struct lzh_dec *ds = strm->ds;+	struct lzh_br *br = &(ds->br);+	int c, i;++	for (i = start; i < end; ) {+		/*+		 *  bit pattern     the number we need+		 *     000           ->  0+		 *     001           ->  1+		 *     010           ->  2+		 *     ...+		 *     110           ->  6+		 *     1110          ->  7+		 *     11110         ->  8+		 *     ...+		 *     1111111111110 ->  16+		 */+		if (!lzh_br_read_ahead(strm, br, 3))+			return (i);+		if ((c = lzh_br_bits(br, 3)) == 7) {+			if (!lzh_br_read_ahead(strm, br, 13))+				return (i);+			c = bitlen_tbl[lzh_br_bits(br, 13) & 0x3FF];+			if (c)+				lzh_br_consume(br, c - 3);+			else+				return (-1);/* Invalid data. */+		} else+			lzh_br_consume(br, 3);+		ds->pt.bitlen[i++] = c;+		ds->pt.freq[c]++;+	}+	return (i);+}++static int+lzh_make_fake_table(struct huffman *hf, uint16_t c)+{+	if (c >= hf->len_size)+		return (0);+	hf->tbl[0] = c;+	hf->max_bits = 0;+	hf->shift_bits = 0;+	hf->bitlen[hf->tbl[0]] = 0;+	return (1);+}++/*+ * Make a huffman coding table.+ */+static int+lzh_make_huffman_table(struct huffman *hf)+{+	uint16_t *tbl;+	const unsigned char *bitlen;+	int bitptn[17], weight[17];+	int i, maxbits = 0, ptn, tbl_size, w;+	int diffbits, len_avail;++	/*+	 * Initialize bit patterns.+	 */+	ptn = 0;+	for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {+		bitptn[i] = ptn;+		weight[i] = w;+		if (hf->freq[i]) {+			ptn += hf->freq[i] * w;+			maxbits = i;+		}+	}+	if (ptn != 0x10000 || maxbits > hf->tbl_bits)+		return (0);/* Invalid */++	hf->max_bits = maxbits;++	/*+	 * Cut out extra bits which we won't house in the table.+	 * This preparation reduces the same calculation in the for-loop+	 * making the table.+	 */+	if (maxbits < 16) {+		int ebits = 16 - maxbits;+		for (i = 1; i <= maxbits; i++) {+			bitptn[i] >>= ebits;+			weight[i] >>= ebits;+		}+	}+	if (maxbits > HTBL_BITS) {+		unsigned htbl_max;+		uint16_t *p;++		diffbits = maxbits - HTBL_BITS;+		for (i = 1; i <= HTBL_BITS; i++) {+			bitptn[i] >>= diffbits;+			weight[i] >>= diffbits;+		}+		htbl_max = bitptn[HTBL_BITS] ++		    weight[HTBL_BITS] * hf->freq[HTBL_BITS];+		p = &(hf->tbl[htbl_max]);+		while (p < &hf->tbl[1U<<HTBL_BITS])+			*p++ = 0;+	} else+		diffbits = 0;+	hf->shift_bits = diffbits;++	/*+	 * Make the table.+	 */+	tbl_size = 1 << HTBL_BITS;+	tbl = hf->tbl;+	bitlen = hf->bitlen;+	len_avail = hf->len_avail;+	hf->tree_used = 0;+	for (i = 0; i < len_avail; i++) {+		uint16_t *p;+		int len, cnt;+		uint16_t bit;+		int extlen;+		struct htree_t *ht;++		if (bitlen[i] == 0)+			continue;+		/* Get a bit pattern */+		len = bitlen[i];+		ptn = bitptn[len];+		cnt = weight[len];+		if (len <= HTBL_BITS) {+			/* Calculate next bit pattern */+			if ((bitptn[len] = ptn + cnt) > tbl_size)+				return (0);/* Invalid */+			/* Update the table */+			p = &(tbl[ptn]);+			if (cnt > 7) {+				uint16_t *pc;++				cnt -= 8;+				pc = &p[cnt];+				pc[0] = (uint16_t)i;+				pc[1] = (uint16_t)i;+				pc[2] = (uint16_t)i;+				pc[3] = (uint16_t)i;+				pc[4] = (uint16_t)i;+				pc[5] = (uint16_t)i;+				pc[6] = (uint16_t)i;+				pc[7] = (uint16_t)i;+				if (cnt > 7) {+					cnt -= 8;+					memcpy(&p[cnt], pc,+						8 * sizeof(uint16_t));+					pc = &p[cnt];+					while (cnt > 15) {+						cnt -= 16;+						memcpy(&p[cnt], pc,+							16 * sizeof(uint16_t));+					}+				}+				if (cnt)+					memcpy(p, pc, cnt * sizeof(uint16_t));+			} else {+				while (cnt > 1) {+					p[--cnt] = (uint16_t)i;+					p[--cnt] = (uint16_t)i;+				}+				if (cnt)+					p[--cnt] = (uint16_t)i;+			}+			continue;+		}++		/*+		 * A bit length is too big to be housed to a direct table,+		 * so we use a tree model for its extra bits.+		 */+		bitptn[len] = ptn + cnt;+		bit = 1U << (diffbits -1);+		extlen = len - HTBL_BITS;+		+		p = &(tbl[ptn >> diffbits]);+		if (*p == 0) {+			*p = len_avail + hf->tree_used;+			ht = &(hf->tree[hf->tree_used++]);+			if (hf->tree_used > hf->tree_avail)+				return (0);/* Invalid */+			ht->left = 0;+			ht->right = 0;+		} else {+			if (*p < len_avail ||+			    *p >= (len_avail + hf->tree_used))+				return (0);/* Invalid */+			ht = &(hf->tree[*p - len_avail]);+		}+		while (--extlen > 0) {+			if (ptn & bit) {+				if (ht->left < len_avail) {+					ht->left = len_avail + hf->tree_used;+					ht = &(hf->tree[hf->tree_used++]);+					if (hf->tree_used > hf->tree_avail)+						return (0);/* Invalid */+					ht->left = 0;+					ht->right = 0;+				} else {+					ht = &(hf->tree[ht->left - len_avail]);+				}+			} else {+				if (ht->right < len_avail) {+					ht->right = len_avail + hf->tree_used;+					ht = &(hf->tree[hf->tree_used++]);+					if (hf->tree_used > hf->tree_avail)+						return (0);/* Invalid */+					ht->left = 0;+					ht->right = 0;+				} else {+					ht = &(hf->tree[ht->right - len_avail]);+				}+			}+			bit >>= 1;+		}+		if (ptn & bit) {+			if (ht->left != 0)+				return (0);/* Invalid */+			ht->left = (uint16_t)i;+		} else {+			if (ht->right != 0)+				return (0);/* Invalid */+			ht->right = (uint16_t)i;+		}+	}+	return (1);+}++static int+lzh_decode_huffman_tree(struct huffman *hf, unsigned rbits, int c)+{+	struct htree_t *ht;+	int extlen;++	ht = hf->tree;+	extlen = hf->shift_bits;+	while (c >= hf->len_avail) {+		c -= hf->len_avail;+		if (extlen-- <= 0 || c >= hf->tree_used)+			return (0);+		if (rbits & (1U << extlen))+			c = ht[c].left;+		else+			c = ht[c].right;+	}+	return (c);+}++static inline int+lzh_decode_huffman(struct huffman *hf, unsigned rbits)+{+	int c;+	/*+	 * At first search an index table for a bit pattern.+	 * If it fails, search a huffman tree for.+	 */+	c = hf->tbl[rbits >> hf->shift_bits];+	if (c < hf->len_avail || hf->len_avail == 0)+		return (c);+	/* This bit pattern needs to be found out at a huffman tree. */+	return (lzh_decode_huffman_tree(hf, rbits, c));+}+
+ c/archive_read_support_format_mtree.c view
@@ -0,0 +1,2025 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2008 Joerg Sonnenberger+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 201165 2009-12-29 05:52:13Z kientzle $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#include <stddef.h>+/* #include <stdint.h> */ /* See archive_platform.h */+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_CTYPE_H+#include <ctype.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_rb.h"+#include "archive_read_private.h"+#include "archive_string.h"+#include "archive_pack_dev.h"++#ifndef O_BINARY+#define	O_BINARY 0+#endif+#ifndef O_CLOEXEC+#define O_CLOEXEC	0+#endif++#define	MTREE_HAS_DEVICE	0x0001+#define	MTREE_HAS_FFLAGS	0x0002+#define	MTREE_HAS_GID		0x0004+#define	MTREE_HAS_GNAME		0x0008+#define	MTREE_HAS_MTIME		0x0010+#define	MTREE_HAS_NLINK		0x0020+#define	MTREE_HAS_PERM		0x0040+#define	MTREE_HAS_SIZE		0x0080+#define	MTREE_HAS_TYPE		0x0100+#define	MTREE_HAS_UID		0x0200+#define	MTREE_HAS_UNAME		0x0400++#define	MTREE_HAS_OPTIONAL	0x0800+#define	MTREE_HAS_NOCHANGE	0x1000 /* FreeBSD specific */++#define	MAX_LINE_LEN		(1024 * 1024)++struct mtree_option {+	struct mtree_option *next;+	char *value;+};++struct mtree_entry {+	struct archive_rb_node rbnode;+	struct mtree_entry *next_dup;+	struct mtree_entry *next;+	struct mtree_option *options;+	char *name;+	char full;+	char used;+};++struct mtree {+	struct archive_string	 line;+	size_t			 buffsize;+	char			*buff;+	int64_t			 offset;+	int			 fd;+	int			 archive_format;+	const char		*archive_format_name;+	struct mtree_entry	*entries;+	struct mtree_entry	*this_entry;+	struct archive_rb_tree	 entry_rbtree;+	struct archive_string	 current_dir;+	struct archive_string	 contents_name;++	struct archive_entry_linkresolver *resolver;+	struct archive_rb_tree rbtree;++	int64_t			 cur_size;+	char checkfs;+};++static int	bid_keycmp(const char *, const char *, ssize_t);+static int	cleanup(struct archive_read *);+static int	detect_form(struct archive_read *, int *);+static int	mtree_bid(struct archive_read *, int);+static int	parse_file(struct archive_read *, struct archive_entry *,+		    struct mtree *, struct mtree_entry *, int *);+static void	parse_escapes(char *, struct mtree_entry *);+static int	parse_line(struct archive_read *, struct archive_entry *,+		    struct mtree *, struct mtree_entry *, int *);+static int	parse_keyword(struct archive_read *, struct mtree *,+		    struct archive_entry *, struct mtree_option *, int *);+static int	read_data(struct archive_read *a,+		    const void **buff, size_t *size, int64_t *offset);+static ssize_t	readline(struct archive_read *, struct mtree *, char **, ssize_t);+static int	skip(struct archive_read *a);+static int	read_header(struct archive_read *,+		    struct archive_entry *);+static int64_t	mtree_atol(char **, int base);++/*+ * There's no standard for TIME_T_MAX/TIME_T_MIN.  So we compute them+ * here.  TODO: Move this to configure time, but be careful+ * about cross-compile environments.+ */+static int64_t+get_time_t_max(void)+{+#if defined(TIME_T_MAX)+	return TIME_T_MAX;+#else+	/* ISO C allows time_t to be a floating-point type,+	   but POSIX requires an integer type.  The following+	   should work on any system that follows the POSIX+	   conventions. */+	if (((time_t)0) < ((time_t)-1)) {+		/* Time_t is unsigned */+		return (~(time_t)0);+	} else {+		/* Time_t is signed. */+		/* Assume it's the same as int64_t or int32_t */+		if (sizeof(time_t) == sizeof(int64_t)) {+			return (time_t)INT64_MAX;+		} else {+			return (time_t)INT32_MAX;+		}+	}+#endif+}++static int64_t+get_time_t_min(void)+{+#if defined(TIME_T_MIN)+	return TIME_T_MIN;+#else+	if (((time_t)0) < ((time_t)-1)) {+		/* Time_t is unsigned */+		return (time_t)0;+	} else {+		/* Time_t is signed. */+		if (sizeof(time_t) == sizeof(int64_t)) {+			return (time_t)INT64_MIN;+		} else {+			return (time_t)INT32_MIN;+		}+	}+#endif+}++static int+archive_read_format_mtree_options(struct archive_read *a,+    const char *key, const char *val)+{+	struct mtree *mtree;++	mtree = (struct mtree *)(a->format->data);+	if (strcmp(key, "checkfs")  == 0) {+		/* Allows to read information missing from the mtree from the file system */+		if (val == NULL || val[0] == 0) {+			mtree->checkfs = 0;+		} else {+			mtree->checkfs = 1;+		}+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static void+free_options(struct mtree_option *head)+{+	struct mtree_option *next;++	for (; head != NULL; head = next) {+		next = head->next;+		free(head->value);+		free(head);+	}+}++static int+mtree_cmp_node(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	const struct mtree_entry *e1 = (const struct mtree_entry *)n1;+	const struct mtree_entry *e2 = (const struct mtree_entry *)n2;++	return (strcmp(e1->name, e2->name));+}++static int+mtree_cmp_key(const struct archive_rb_node *n, const void *key)+{+	const struct mtree_entry *e = (const struct mtree_entry *)n;++	return (strcmp(e->name, key));+}++int+archive_read_support_format_mtree(struct archive *_a)+{+	static const struct archive_rb_tree_ops rb_ops = {+		mtree_cmp_node, mtree_cmp_key,+	};+	struct archive_read *a = (struct archive_read *)_a;+	struct mtree *mtree;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_mtree");++	mtree = (struct mtree *)calloc(1, sizeof(*mtree));+	if (mtree == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate mtree data");+		return (ARCHIVE_FATAL);+	}+	mtree->checkfs = 0;+	mtree->fd = -1;++	__archive_rb_tree_init(&mtree->rbtree, &rb_ops);++	r = __archive_read_register_format(a, mtree, "mtree",+           mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL);++	if (r != ARCHIVE_OK)+		free(mtree);+	return (ARCHIVE_OK);+}++static int+cleanup(struct archive_read *a)+{+	struct mtree *mtree;+	struct mtree_entry *p, *q;++	mtree = (struct mtree *)(a->format->data);++	p = mtree->entries;+	while (p != NULL) {+		q = p->next;+		free(p->name);+		free_options(p->options);+		free(p);+		p = q;+	}+	archive_string_free(&mtree->line);+	archive_string_free(&mtree->current_dir);+	archive_string_free(&mtree->contents_name);+	archive_entry_linkresolver_free(mtree->resolver);++	free(mtree->buff);+	free(mtree);+	(a->format->data) = NULL;+	return (ARCHIVE_OK);+}++static ssize_t+get_line_size(const char *b, ssize_t avail, ssize_t *nlsize)+{+	ssize_t len;++	len = 0;+	while (len < avail) {+		switch (*b) {+		case '\0':/* Non-ascii character or control character. */+			if (nlsize != NULL)+				*nlsize = 0;+			return (-1);+		case '\r':+			if (avail-len > 1 && b[1] == '\n') {+				if (nlsize != NULL)+					*nlsize = 2;+				return (len+2);+			}+			/* FALL THROUGH */+		case '\n':+			if (nlsize != NULL)+				*nlsize = 1;+			return (len+1);+		default:+			b++;+			len++;+			break;+		}+	}+	if (nlsize != NULL)+		*nlsize = 0;+	return (avail);+}++/*+ *  <---------------- ravail --------------------->+ *  <-- diff ------> <---  avail ----------------->+ *                   <---- len ----------->+ * | Previous lines | line being parsed  nl extra |+ *                  ^+ *                  b+ *+ */+static ssize_t+next_line(struct archive_read *a,+    const char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl)+{+	ssize_t len;+	int quit;+	+	quit = 0;+	if (*avail == 0) {+		*nl = 0;+		len = 0;+	} else+		len = get_line_size(*b, *avail, nl);+	/*+	 * Read bytes more while it does not reach the end of line.+	 */+	while (*nl == 0 && len == *avail && !quit) {+		ssize_t diff = *ravail - *avail;+		size_t nbytes_req = (*ravail+1023) & ~1023U;+		ssize_t tested;++		/*+		 * Place an arbitrary limit on the line length.+		 * mtree is almost free-form input and without line length limits,+		 * it can consume a lot of memory.+		 */+		if (len >= MAX_LINE_LEN)+			return (-1);++		/* Increase reading bytes if it is not enough to at least+		 * new two lines. */+		if (nbytes_req < (size_t)*ravail + 160)+			nbytes_req <<= 1;++		*b = __archive_read_ahead(a, nbytes_req, avail);+		if (*b == NULL) {+			if (*ravail >= *avail)+				return (0);+			/* Reading bytes reaches the end of file. */+			*b = __archive_read_ahead(a, *avail, avail);+			quit = 1;+		}+		*ravail = *avail;+		*b += diff;+		*avail -= diff;+		tested = len;/* Skip some bytes we already determinated. */+		len = get_line_size(*b + len, *avail - len, nl);+		if (len >= 0)+			len += tested;+	}+	return (len);+}++/*+ * Compare characters with a mtree keyword.+ * Returns the length of a mtree keyword if matched.+ * Returns 0 if not matched.+ */+static int+bid_keycmp(const char *p, const char *key, ssize_t len)+{+	int match_len = 0;++	while (len > 0 && *p && *key) {+		if (*p == *key) {+			--len;+			++p;+			++key;+			++match_len;+			continue;+		}+		return (0);/* Not match */+	}+	if (*key != '\0')+		return (0);/* Not match */++	/* A following character should be specified characters */+	if (p[0] == '=' || p[0] == ' ' || p[0] == '\t' ||+	    p[0] == '\n' || p[0] == '\r' ||+	   (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r')))+		return (match_len);+	return (0);/* Not match */+}++/*+ * Test whether the characters 'p' has is mtree keyword.+ * Returns the length of a detected keyword.+ * Returns 0 if any keywords were not found.+ */+static int+bid_keyword(const char *p,  ssize_t len)+{+	static const char * const keys_c[] = {+		"content", "contents", "cksum", NULL+	};+	static const char * const keys_df[] = {+		"device", "flags", NULL+	};+	static const char * const keys_g[] = {+		"gid", "gname", NULL+	};+	static const char * const keys_il[] = {+		"ignore", "inode", "link", NULL+	};+	static const char * const keys_m[] = {+		"md5", "md5digest", "mode", NULL+	};+	static const char * const keys_no[] = {+		"nlink", "nochange", "optional", NULL+	};+	static const char * const keys_r[] = {+		"resdevice", "rmd160", "rmd160digest", NULL+	};+	static const char * const keys_s[] = {+		"sha1", "sha1digest",+		"sha256", "sha256digest",+		"sha384", "sha384digest",+		"sha512", "sha512digest",+		"size", NULL+	};+	static const char * const keys_t[] = {+		"tags", "time", "type", NULL+	};+	static const char * const keys_u[] = {+		"uid", "uname",	NULL+	};+	const char * const *keys;+	int i;++	switch (*p) {+	case 'c': keys = keys_c; break;+	case 'd': case 'f': keys = keys_df; break;+	case 'g': keys = keys_g; break;+	case 'i': case 'l': keys = keys_il; break;+	case 'm': keys = keys_m; break;+	case 'n': case 'o': keys = keys_no; break;+	case 'r': keys = keys_r; break;+	case 's': keys = keys_s; break;+	case 't': keys = keys_t; break;+	case 'u': keys = keys_u; break;+	default: return (0);/* Unknown key */+	}++	for (i = 0; keys[i] != NULL; i++) {+		int l = bid_keycmp(p, keys[i], len);+		if (l > 0)+			return (l);+	}+	return (0);/* Unknown key */+}++/*+ * Test whether there is a set of mtree keywords.+ * Returns the number of keyword.+ * Returns -1 if we got incorrect sequence.+ * This function expects a set of "<space characters>keyword=value".+ * When "unset" is specified, expects a set of "<space characters>keyword".+ */+static int+bid_keyword_list(const char *p,  ssize_t len, int unset, int last_is_path)+{+	int l;+	int keycnt = 0;++	while (len > 0 && *p) {+		int blank = 0;++		/* Test whether there are blank characters in the line. */+		while (len >0 && (*p == ' ' || *p == '\t')) {+			++p;+			--len;+			blank = 1;+		}+		if (*p == '\n' || *p == '\r')+			break;+		if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))+			break;+		if (!blank && !last_is_path) /* No blank character. */+			return (-1);+		if (last_is_path && len == 0)+				return (keycnt);++		if (unset) {+			l = bid_keycmp(p, "all", len);+			if (l > 0)+				return (1);+		}+		/* Test whether there is a correct key in the line. */+		l = bid_keyword(p, len);+		if (l == 0)+			return (-1);/* Unknown keyword was found. */+		p += l;+		len -= l;+		keycnt++;++		/* Skip value */+		if (*p == '=') {+			int value = 0;+			++p;+			--len;+			while (len > 0 && *p != ' ' && *p != '\t') {+				++p;+				--len;+				value = 1;+			}+			/* A keyword should have a its value unless+			 * "/unset" operation. */ +			if (!unset && value == 0)+				return (-1);+		}+	}+	return (keycnt);+}++static int+bid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path)+{+	int f = 0;+	static const unsigned char safe_char[256] = {+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */+		/* !"$%&'()*+,-./  EXCLUSION:( )(#) */+		0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */+		/* 0123456789:;<>?  EXCLUSION:(=) */+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */+		/* @ABCDEFGHIJKLMNO */+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */+		/* PQRSTUVWXYZ[\]^_  */+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */+		/* `abcdefghijklmno */+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */+		/* pqrstuvwxyz{|}~ */+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */+	};+	ssize_t ll;+	const char *pp = p;+	const char * const pp_end = pp + len;++	*last_is_path = 0;+	/*+	 * Skip the path-name which is quoted.+	 */+	for (;pp < pp_end; ++pp) {+		if (!safe_char[*(const unsigned char *)pp]) {+			if (*pp != ' ' && *pp != '\t' && *pp != '\r'+			    && *pp != '\n')+				f = 0;+			break;+		}+		f = 1;+	}+	ll = pp_end - pp;++	/* If a path-name was not found at the first, try to check+	 * a mtree format(a.k.a form D) ``NetBSD's mtree -D'' creates,+	 * which places the path-name at the last. */+	if (f == 0) {+		const char *pb = p + len - nl;+		int name_len = 0;+		int slash;++		/* The form D accepts only a single line for an entry. */+		if (pb-2 >= p &&+		    pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t'))+			return (-1);+		if (pb-1 >= p && pb[-1] == '\\')+			return (-1);++		slash = 0;+		while (p <= --pb && *pb != ' ' && *pb != '\t') {+			if (!safe_char[*(const unsigned char *)pb])+				return (-1);+			name_len++;+			/* The pathname should have a slash in this+			 * format. */+			if (*pb == '/')+				slash = 1;+		}+		if (name_len == 0 || slash == 0)+			return (-1);+		/* If '/' is placed at the first in this field, this is not+		 * a valid filename. */+		if (pb[1] == '/')+			return (-1);+		ll = len - nl - name_len;+		pp = p;+		*last_is_path = 1;+	}++	return (bid_keyword_list(pp, ll, 0, *last_is_path));+}++#define MAX_BID_ENTRY	3++static int+mtree_bid(struct archive_read *a, int best_bid)+{+	const char *signature = "#mtree";+	const char *p;++	(void)best_bid; /* UNUSED */++	/* Now let's look at the actual header and see if it matches. */+	p = __archive_read_ahead(a, strlen(signature), NULL);+	if (p == NULL)+		return (-1);++	if (memcmp(p, signature, strlen(signature)) == 0)+		return (8 * (int)strlen(signature));++	/*+	 * There is not a mtree signature. Let's try to detect mtree format.+	 */+	return (detect_form(a, NULL));+}++static int+detect_form(struct archive_read *a, int *is_form_d)+{+	const char *p;+	ssize_t avail, ravail;+	ssize_t detected_bytes = 0, len, nl;+	int entry_cnt = 0, multiline = 0;+	int form_D = 0;/* The archive is generated by `NetBSD mtree -D'+			* (In this source we call it `form D') . */++	if (is_form_d != NULL)+		*is_form_d = 0;+	p = __archive_read_ahead(a, 1, &avail);+	if (p == NULL)+		return (-1);+	ravail = avail;+	for (;;) {+		len = next_line(a, &p, &avail, &ravail, &nl);+		/* The terminal character of the line should be+		 * a new line character, '\r\n' or '\n'. */+		if (len <= 0 || nl == 0)+			break;+		if (!multiline) {+			/* Leading whitespace is never significant,+			 * ignore it. */+			while (len > 0 && (*p == ' ' || *p == '\t')) {+				++p;+				--avail;+				--len;+			}+			/* Skip comment or empty line. */ +			if (p[0] == '#' || p[0] == '\n' || p[0] == '\r') {+				p += len;+				avail -= len;+				continue;+			}+		} else {+			/* A continuance line; the terminal+			 * character of previous line was '\' character. */+			if (bid_keyword_list(p, len, 0, 0) <= 0)+				break;+			if (multiline == 1)+				detected_bytes += len;+			if (p[len-nl-1] != '\\') {+				if (multiline == 1 &&+				    ++entry_cnt >= MAX_BID_ENTRY)+					break;+				multiline = 0;+			}+			p += len;+			avail -= len;+			continue;+		}+		if (p[0] != '/') {+			int last_is_path, keywords;++			keywords = bid_entry(p, len, nl, &last_is_path);+			if (keywords >= 0) {+				detected_bytes += len;+				if (form_D == 0) {+					if (last_is_path)+						form_D = 1;+					else if (keywords > 0)+						/* This line is not `form D'. */+						form_D = -1;+				} else if (form_D == 1) {+					if (!last_is_path && keywords > 0)+						/* This this is not `form D'+						 * and We cannot accept mixed+						 * format. */+						break;+				}+				if (!last_is_path && p[len-nl-1] == '\\')+					/* This line continues. */+					multiline = 1;+				else {+					/* We've got plenty of correct lines+					 * to assume that this file is a mtree+					 * format. */+					if (++entry_cnt >= MAX_BID_ENTRY)+						break;+				}+			} else+				break;+		} else if (len > 4 && strncmp(p, "/set", 4) == 0) {+			if (bid_keyword_list(p+4, len-4, 0, 0) <= 0)+				break;+			/* This line continues. */+			if (p[len-nl-1] == '\\')+				multiline = 2;+		} else if (len > 6 && strncmp(p, "/unset", 6) == 0) {+			if (bid_keyword_list(p+6, len-6, 1, 0) <= 0)+				break;+			/* This line continues. */+			if (p[len-nl-1] == '\\')+				multiline = 2;+		} else+			break;++		/* Test next line. */+		p += len;+		avail -= len;+	}+	if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) {+		if (is_form_d != NULL) {+			if (form_D == 1)+				*is_form_d = 1;+		}+		return (32);+	}++	return (0);+}++/*+ * The extended mtree format permits multiple lines specifying+ * attributes for each file.  For those entries, only the last line+ * is actually used.  Practically speaking, that means we have+ * to read the entire mtree file into memory up front.+ *+ * The parsing is done in two steps.  First, it is decided if a line+ * changes the global defaults and if it is, processed accordingly.+ * Otherwise, the options of the line are merged with the current+ * global options.+ */+static int+add_option(struct archive_read *a, struct mtree_option **global,+    const char *value, size_t len)+{+	struct mtree_option *opt;++	if ((opt = malloc(sizeof(*opt))) == NULL) {+		archive_set_error(&a->archive, errno, "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	if ((opt->value = malloc(len + 1)) == NULL) {+		free(opt);+		archive_set_error(&a->archive, errno, "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	memcpy(opt->value, value, len);+	opt->value[len] = '\0';+	opt->next = *global;+	*global = opt;+	return (ARCHIVE_OK);+}++static void+remove_option(struct mtree_option **global, const char *value, size_t len)+{+	struct mtree_option *iter, *last;++	last = NULL;+	for (iter = *global; iter != NULL; last = iter, iter = iter->next) {+		if (strncmp(iter->value, value, len) == 0 &&+		    (iter->value[len] == '\0' ||+		     iter->value[len] == '='))+			break;+	}+	if (iter == NULL)+		return;+	if (last == NULL)+		*global = iter->next;+	else+		last->next = iter->next;++	free(iter->value);+	free(iter);+}++static int+process_global_set(struct archive_read *a,+    struct mtree_option **global, const char *line)+{+	const char *next, *eq;+	size_t len;+	int r;++	line += 4;+	for (;;) {+		next = line + strspn(line, " \t\r\n");+		if (*next == '\0')+			return (ARCHIVE_OK);+		line = next;+		next = line + strcspn(line, " \t\r\n");+		eq = strchr(line, '=');+		if (eq > next)+			len = next - line;+		else+			len = eq - line;++		remove_option(global, line, len);+		r = add_option(a, global, line, next - line);+		if (r != ARCHIVE_OK)+			return (r);+		line = next;+	}+}++static int+process_global_unset(struct archive_read *a,+    struct mtree_option **global, const char *line)+{+	const char *next;+	size_t len;++	line += 6;+	if (strchr(line, '=') != NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "/unset shall not contain `='");+		return ARCHIVE_FATAL;+	}++	for (;;) {+		next = line + strspn(line, " \t\r\n");+		if (*next == '\0')+			return (ARCHIVE_OK);+		line = next;+		len = strcspn(line, " \t\r\n");++		if (len == 3 && strncmp(line, "all", 3) == 0) {+			free_options(*global);+			*global = NULL;+		} else {+			remove_option(global, line, len);+		}++		line += len;+	}+}++static int+process_add_entry(struct archive_read *a, struct mtree *mtree,+    struct mtree_option **global, const char *line, ssize_t line_len,+    struct mtree_entry **last_entry, int is_form_d)+{+	struct mtree_entry *entry;+	struct mtree_option *iter;+	const char *next, *eq, *name, *end;+	size_t name_len, len;+	int r, i;++	if ((entry = malloc(sizeof(*entry))) == NULL) {+		archive_set_error(&a->archive, errno, "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	entry->next = NULL;+	entry->options = NULL;+	entry->name = NULL;+	entry->used = 0;+	entry->full = 0;++	/* Add this entry to list. */+	if (*last_entry == NULL)+		mtree->entries = entry;+	else+		(*last_entry)->next = entry;+	*last_entry = entry;++	if (is_form_d) {+		/* Filename is last item on line. */+		/* Adjust line_len to trim trailing whitespace */+		while (line_len > 0) {+			char last_character = line[line_len - 1];+			if (last_character == '\r'+			    || last_character == '\n'+			    || last_character == '\t'+			    || last_character == ' ') {+				line_len--;+			} else {+				break;+			}+		}+		/* Name starts after the last whitespace separator */+		name = line;+		for (i = 0; i < line_len; i++) {+			if (line[i] == '\r'+			    || line[i] == '\n'+			    || line[i] == '\t'+			    || line[i] == ' ') {+				name = line + i + 1;+			}+		}+		name_len = line + line_len - name;+		end = name;+	} else {+		/* Filename is first item on line */+		name_len = strcspn(line, " \t\r\n");+		name = line;+		line += name_len;+		end = line + line_len;+	}+	/* name/name_len is the name within the line. */+	/* line..end brackets the entire line except the name */++	if ((entry->name = malloc(name_len + 1)) == NULL) {+		archive_set_error(&a->archive, errno, "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}++	memcpy(entry->name, name, name_len);+	entry->name[name_len] = '\0';+	parse_escapes(entry->name, entry);++	entry->next_dup = NULL;+	if (entry->full) {+		if (!__archive_rb_tree_insert_node(&mtree->rbtree, &entry->rbnode)) {+			struct mtree_entry *alt;+			alt = (struct mtree_entry *)__archive_rb_tree_find_node(+			    &mtree->rbtree, entry->name);+			while (alt->next_dup)+				alt = alt->next_dup;+			alt->next_dup = entry;+		}+	}++	for (iter = *global; iter != NULL; iter = iter->next) {+		r = add_option(a, &entry->options, iter->value,+		    strlen(iter->value));+		if (r != ARCHIVE_OK)+			return (r);+	}++	for (;;) {+		next = line + strspn(line, " \t\r\n");+		if (*next == '\0')+			return (ARCHIVE_OK);+		if (next >= end)+			return (ARCHIVE_OK);+		line = next;+		next = line + strcspn(line, " \t\r\n");+		eq = strchr(line, '=');+		if (eq == NULL || eq > next)+			len = next - line;+		else+			len = eq - line;++		remove_option(&entry->options, line, len);+		r = add_option(a, &entry->options, line, next - line);+		if (r != ARCHIVE_OK)+			return (r);+		line = next;+	}+}++static int+read_mtree(struct archive_read *a, struct mtree *mtree)+{+	ssize_t len;+	uintmax_t counter;+	char *p, *s;+	struct mtree_option *global;+	struct mtree_entry *last_entry;+	int r, is_form_d;++	mtree->archive_format = ARCHIVE_FORMAT_MTREE;+	mtree->archive_format_name = "mtree";++	global = NULL;+	last_entry = NULL;++	(void)detect_form(a, &is_form_d);++	for (counter = 1; ; ++counter) {+		r = ARCHIVE_OK;+		len = readline(a, mtree, &p, 65536);+		if (len == 0) {+			mtree->this_entry = mtree->entries;+			free_options(global);+			return (ARCHIVE_OK);+		}+		if (len < 0) {+			free_options(global);+			return ((int)len);+		}+		/* Leading whitespace is never significant, ignore it. */+		while (*p == ' ' || *p == '\t') {+			++p;+			--len;+		}+		/* Skip content lines and blank lines. */+		if (*p == '#')+			continue;+		if (*p == '\r' || *p == '\n' || *p == '\0')+			continue;+		/* Non-printable characters are not allowed */+		for (s = p;s < p + len - 1; s++) {+			if (!isprint(*s)) {+				r = ARCHIVE_FATAL;+				break;+			}+		}+		if (r != ARCHIVE_OK)+			break;+		if (*p != '/') {+			r = process_add_entry(a, mtree, &global, p, len,+			    &last_entry, is_form_d);+		} else if (len > 4 && strncmp(p, "/set", 4) == 0) {+			if (p[4] != ' ' && p[4] != '\t')+				break;+			r = process_global_set(a, &global, p);+		} else if (len > 6 && strncmp(p, "/unset", 6) == 0) {+			if (p[6] != ' ' && p[6] != '\t')+				break;+			r = process_global_unset(a, &global, p);+		} else+			break;++		if (r != ARCHIVE_OK) {+			free_options(global);+			return r;+		}+	}++	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Can't parse line %ju", counter);+	free_options(global);+	return (ARCHIVE_FATAL);+}++/*+ * Read in the entire mtree file into memory on the first request.+ * Then use the next unused file to satisfy each header request.+ */+static int+read_header(struct archive_read *a, struct archive_entry *entry)+{+	struct mtree *mtree;+	char *p;+	int r, use_next;++	mtree = (struct mtree *)(a->format->data);++	if (mtree->fd >= 0) {+		close(mtree->fd);+		mtree->fd = -1;+	}++	if (mtree->entries == NULL) {+		mtree->resolver = archive_entry_linkresolver_new();+		if (mtree->resolver == NULL)+			return ARCHIVE_FATAL;+		archive_entry_linkresolver_set_strategy(mtree->resolver,+		    ARCHIVE_FORMAT_MTREE);+		r = read_mtree(a, mtree);+		if (r != ARCHIVE_OK)+			return (r);+	}++	a->archive.archive_format = mtree->archive_format;+	a->archive.archive_format_name = mtree->archive_format_name;++	for (;;) {+		if (mtree->this_entry == NULL)+			return (ARCHIVE_EOF);+		if (strcmp(mtree->this_entry->name, "..") == 0) {+			mtree->this_entry->used = 1;+			if (archive_strlen(&mtree->current_dir) > 0) {+				/* Roll back current path. */+				p = mtree->current_dir.s+				    + mtree->current_dir.length - 1;+				while (p >= mtree->current_dir.s && *p != '/')+					--p;+				if (p >= mtree->current_dir.s)+					--p;+				mtree->current_dir.length+				    = p - mtree->current_dir.s + 1;+			}+		}+		if (!mtree->this_entry->used) {+			use_next = 0;+			r = parse_file(a, entry, mtree, mtree->this_entry,+				&use_next);+			if (use_next == 0)+				return (r);+		}+		mtree->this_entry = mtree->this_entry->next;+	}+}++/*+ * A single file can have multiple lines contribute specifications.+ * Parse as many lines as necessary, then pull additional information+ * from a backing file on disk as necessary.+ */+static int+parse_file(struct archive_read *a, struct archive_entry *entry,+    struct mtree *mtree, struct mtree_entry *mentry, int *use_next)+{+	const char *path;+	struct stat st_storage, *st;+	struct mtree_entry *mp;+	struct archive_entry *sparse_entry;+	int r = ARCHIVE_OK, r1, parsed_kws;++	mentry->used = 1;++	/* Initialize reasonable defaults. */+	archive_entry_set_filetype(entry, AE_IFREG);+	archive_entry_set_size(entry, 0);+	archive_string_empty(&mtree->contents_name);++	/* Parse options from this line. */+	parsed_kws = 0;+	r = parse_line(a, entry, mtree, mentry, &parsed_kws);++	if (mentry->full) {+		archive_entry_copy_pathname(entry, mentry->name);+		/*+		 * "Full" entries are allowed to have multiple lines+		 * and those lines aren't required to be adjacent.  We+		 * don't support multiple lines for "relative" entries+		 * nor do we make any attempt to merge data from+		 * separate "relative" and "full" entries.  (Merging+		 * "relative" and "full" entries would require dealing+		 * with pathname canonicalization, which is a very+		 * tricky subject.)+		 */+		mp = (struct mtree_entry *)__archive_rb_tree_find_node(+		    &mtree->rbtree, mentry->name);+		for (; mp; mp = mp->next_dup) {+			if (mp->full && !mp->used) {+				/* Later lines override earlier ones. */+				mp->used = 1;+				r1 = parse_line(a, entry, mtree, mp, &parsed_kws);+				if (r1 < r)+					r = r1;+			}+		}+	} else {+		/*+		 * Relative entries require us to construct+		 * the full path and possibly update the+		 * current directory.+		 */+		size_t n = archive_strlen(&mtree->current_dir);+		if (n > 0)+			archive_strcat(&mtree->current_dir, "/");+		archive_strcat(&mtree->current_dir, mentry->name);+		archive_entry_copy_pathname(entry, mtree->current_dir.s);+		if (archive_entry_filetype(entry) != AE_IFDIR)+			mtree->current_dir.length = n;+	}++	if (mtree->checkfs) {+		/*+		 * Try to open and stat the file to get the real size+		 * and other file info.  It would be nice to avoid+		 * this here so that getting a listing of an mtree+		 * wouldn't require opening every referenced contents+		 * file.  But then we wouldn't know the actual+		 * contents size, so I don't see a really viable way+		 * around this.  (Also, we may want to someday pull+		 * other unspecified info from the contents file on+		 * disk.)+		 */+		mtree->fd = -1;+		if (archive_strlen(&mtree->contents_name) > 0)+			path = mtree->contents_name.s;+		else+			path = archive_entry_pathname(entry);++		if (archive_entry_filetype(entry) == AE_IFREG ||+				archive_entry_filetype(entry) == AE_IFDIR) {+			mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC);+			__archive_ensure_cloexec_flag(mtree->fd);+			if (mtree->fd == -1 &&+				(errno != ENOENT ||+				 archive_strlen(&mtree->contents_name) > 0)) {+				archive_set_error(&a->archive, errno,+						"Can't open %s", path);+				r = ARCHIVE_WARN;+			}+		}++		st = &st_storage;+		if (mtree->fd >= 0) {+			if (fstat(mtree->fd, st) == -1) {+				archive_set_error(&a->archive, errno,+						"Could not fstat %s", path);+				r = ARCHIVE_WARN;+				/* If we can't stat it, don't keep it open. */+				close(mtree->fd);+				mtree->fd = -1;+				st = NULL;+			}+		} else if (lstat(path, st) == -1) {+			st = NULL;+		}++		/*+		 * Check for a mismatch between the type in the specification+		 * and the type of the contents object on disk.+		 */+		if (st != NULL) {+			if (((st->st_mode & S_IFMT) == S_IFREG &&+			      archive_entry_filetype(entry) == AE_IFREG)+#ifdef S_IFLNK+			  ||((st->st_mode & S_IFMT) == S_IFLNK &&+			      archive_entry_filetype(entry) == AE_IFLNK)+#endif+#ifdef S_IFSOCK+			  ||((st->st_mode & S_IFSOCK) == S_IFSOCK &&+			      archive_entry_filetype(entry) == AE_IFSOCK)+#endif+#ifdef S_IFCHR+			  ||((st->st_mode & S_IFMT) == S_IFCHR &&+			      archive_entry_filetype(entry) == AE_IFCHR)+#endif+#ifdef S_IFBLK+			  ||((st->st_mode & S_IFMT) == S_IFBLK &&+			      archive_entry_filetype(entry) == AE_IFBLK)+#endif+			  ||((st->st_mode & S_IFMT) == S_IFDIR &&+			      archive_entry_filetype(entry) == AE_IFDIR)+#ifdef S_IFIFO+			  ||((st->st_mode & S_IFMT) == S_IFIFO &&+			      archive_entry_filetype(entry) == AE_IFIFO)+#endif+			) {+				/* Types match. */+			} else {+				/* Types don't match; bail out gracefully. */+				if (mtree->fd >= 0)+					close(mtree->fd);+				mtree->fd = -1;+				if (parsed_kws & MTREE_HAS_OPTIONAL) {+					/* It's not an error for an optional+					 * entry to not match disk. */+					*use_next = 1;+				} else if (r == ARCHIVE_OK) {+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_MISC,+					    "mtree specification has different"+					    " type for %s",+					    archive_entry_pathname(entry));+					r = ARCHIVE_WARN;+				}+				return (r);+			}+		}++		/*+		 * If there is a contents file on disk, pick some of the+		 * metadata from that file.  For most of these, we only+		 * set it from the contents if it wasn't already parsed+		 * from the specification.+		 */+		if (st != NULL) {+			if (((parsed_kws & MTREE_HAS_DEVICE) == 0 ||+				(parsed_kws & MTREE_HAS_NOCHANGE) != 0) &&+				(archive_entry_filetype(entry) == AE_IFCHR ||+				 archive_entry_filetype(entry) == AE_IFBLK))+				archive_entry_set_rdev(entry, st->st_rdev);+			if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME))+				== 0 ||+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0)+				archive_entry_set_gid(entry, st->st_gid);+			if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME))+				== 0 ||+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0)+				archive_entry_set_uid(entry, st->st_uid);+			if ((parsed_kws & MTREE_HAS_MTIME) == 0 ||+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0) {+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC+				archive_entry_set_mtime(entry, st->st_mtime,+						st->st_mtimespec.tv_nsec);+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC+				archive_entry_set_mtime(entry, st->st_mtime,+						st->st_mtim.tv_nsec);+#elif HAVE_STRUCT_STAT_ST_MTIME_N+				archive_entry_set_mtime(entry, st->st_mtime,+						st->st_mtime_n);+#elif HAVE_STRUCT_STAT_ST_UMTIME+				archive_entry_set_mtime(entry, st->st_mtime,+						st->st_umtime*1000);+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC+				archive_entry_set_mtime(entry, st->st_mtime,+						st->st_mtime_usec*1000);+#else+				archive_entry_set_mtime(entry, st->st_mtime, 0);+#endif+			}+			if ((parsed_kws & MTREE_HAS_NLINK) == 0 ||+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0)+				archive_entry_set_nlink(entry, st->st_nlink);+			if ((parsed_kws & MTREE_HAS_PERM) == 0 ||+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0)+				archive_entry_set_perm(entry, st->st_mode);+			if ((parsed_kws & MTREE_HAS_SIZE) == 0 ||+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0)+				archive_entry_set_size(entry, st->st_size);+			archive_entry_set_ino(entry, st->st_ino);+			archive_entry_set_dev(entry, st->st_dev);++			archive_entry_linkify(mtree->resolver, &entry,+				&sparse_entry);+		} else if (parsed_kws & MTREE_HAS_OPTIONAL) {+			/*+			 * Couldn't open the entry, stat it or the on-disk type+			 * didn't match.  If this entry is optional, just+			 * ignore it and read the next header entry.+			 */+			*use_next = 1;+			return ARCHIVE_OK;+		}+	}++	mtree->cur_size = archive_entry_size(entry);+	mtree->offset = 0;++	return r;+}++/*+ * Each line contains a sequence of keywords.+ */+static int+parse_line(struct archive_read *a, struct archive_entry *entry,+    struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws)+{+	struct mtree_option *iter;+	int r = ARCHIVE_OK, r1;++	for (iter = mp->options; iter != NULL; iter = iter->next) {+		r1 = parse_keyword(a, mtree, entry, iter, parsed_kws);+		if (r1 < r)+			r = r1;+	}+	if (r == ARCHIVE_OK && (*parsed_kws & MTREE_HAS_TYPE) == 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Missing type keyword in mtree specification");+		return (ARCHIVE_WARN);+	}+	return (r);+}++/*+ * Device entries have one of the following forms:+ *  - raw dev_t+ *  - format,major,minor[,subdevice]+ * When parsing succeeded, `pdev' will contain the appropriate dev_t value.+ */++/* strsep() is not in C90, but strcspn() is. */+/* Taken from http://unixpapa.com/incnote/string.html */+static char *+la_strsep(char **sp, const char *sep)+{+	char *p, *s;+	if (sp == NULL || *sp == NULL || **sp == '\0')+		return(NULL);+	s = *sp;+	p = s + strcspn(s, sep);+	if (*p != '\0')+		*p++ = '\0';+	*sp = p;+	return(s);+}++static int+parse_device(dev_t *pdev, struct archive *a, char *val)+{+#define MAX_PACK_ARGS 3+	unsigned long numbers[MAX_PACK_ARGS];+	char *p, *dev;+	int argc;+	pack_t *pack;+	dev_t result;+	const char *error = NULL;++	memset(pdev, 0, sizeof(*pdev));+	if ((dev = strchr(val, ',')) != NULL) {+		/*+		 * Device's major/minor are given in a specified format.+		 * Decode and pack it accordingly.+		 */+		*dev++ = '\0';+		if ((pack = pack_find(val)) == NULL) {+			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,+			    "Unknown format `%s'", val);+			return ARCHIVE_WARN;+		}+		argc = 0;+		while ((p = la_strsep(&dev, ",")) != NULL) {+			if (*p == '\0') {+				archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,+				    "Missing number");+				return ARCHIVE_WARN;+			}+			if (argc >= MAX_PACK_ARGS) {+				archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,+				    "Too many arguments");+				return ARCHIVE_WARN;+			}+			numbers[argc++] = (unsigned long)mtree_atol(&p, 0);+		}+		if (argc < 2) {+			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,+			    "Not enough arguments");+			return ARCHIVE_WARN;+		}+		result = (*pack)(argc, numbers, &error);+		if (error != NULL) {+			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,+			    "%s", error);+			return ARCHIVE_WARN;+		}+	} else {+		/* file system raw value. */+		result = (dev_t)mtree_atol(&val, 0);+	}+	*pdev = result;+	return ARCHIVE_OK;+#undef MAX_PACK_ARGS+}++/*+ * Parse a single keyword and its value.+ */+static int+parse_keyword(struct archive_read *a, struct mtree *mtree,+    struct archive_entry *entry, struct mtree_option *opt, int *parsed_kws)+{+	char *val, *key;++	key = opt->value;++	if (*key == '\0')+		return (ARCHIVE_OK);++	if (strcmp(key, "nochange") == 0) {+		*parsed_kws |= MTREE_HAS_NOCHANGE;+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "optional") == 0) {+		*parsed_kws |= MTREE_HAS_OPTIONAL;+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "ignore") == 0) {+		/*+		 * The mtree processing is not recursive, so+		 * recursion will only happen for explicitly listed+		 * entries.+		 */+		return (ARCHIVE_OK);+	}++	val = strchr(key, '=');+	if (val == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Malformed attribute \"%s\" (%d)", key, key[0]);+		return (ARCHIVE_WARN);+	}++	*val = '\0';+	++val;++	switch (key[0]) {+	case 'c':+		if (strcmp(key, "content") == 0+		    || strcmp(key, "contents") == 0) {+			parse_escapes(val, NULL);+			archive_strcpy(&mtree->contents_name, val);+			break;+		}+		if (strcmp(key, "cksum") == 0)+			break;+		__LA_FALLTHROUGH;+	case 'd':+		if (strcmp(key, "device") == 0) {+			/* stat(2) st_rdev field, e.g. the major/minor IDs+			 * of a char/block special file */+			int r;+			dev_t dev;++			*parsed_kws |= MTREE_HAS_DEVICE;+			r = parse_device(&dev, &a->archive, val);+			if (r == ARCHIVE_OK)+				archive_entry_set_rdev(entry, dev);+			return r;+		}+		__LA_FALLTHROUGH;+	case 'f':+		if (strcmp(key, "flags") == 0) {+			*parsed_kws |= MTREE_HAS_FFLAGS;+			archive_entry_copy_fflags_text(entry, val);+			break;+		}+		__LA_FALLTHROUGH;+	case 'g':+		if (strcmp(key, "gid") == 0) {+			*parsed_kws |= MTREE_HAS_GID;+			archive_entry_set_gid(entry, mtree_atol(&val, 10));+			break;+		}+		if (strcmp(key, "gname") == 0) {+			*parsed_kws |= MTREE_HAS_GNAME;+			archive_entry_copy_gname(entry, val);+			break;+		}+		__LA_FALLTHROUGH;+	case 'i':+		if (strcmp(key, "inode") == 0) {+			archive_entry_set_ino(entry, mtree_atol(&val, 10));+			break;+		}+		__LA_FALLTHROUGH;+	case 'l':+		if (strcmp(key, "link") == 0) {+			archive_entry_copy_symlink(entry, val);+			break;+		}+		__LA_FALLTHROUGH;+	case 'm':+		if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0)+			break;+		if (strcmp(key, "mode") == 0) {+			if (val[0] >= '0' && val[0] <= '7') {+				*parsed_kws |= MTREE_HAS_PERM;+				archive_entry_set_perm(entry,+				    (mode_t)mtree_atol(&val, 8));+			} else {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Symbolic or non-octal mode \"%s\" unsupported", val);+				return ARCHIVE_WARN;+			}+			break;+		}+		__LA_FALLTHROUGH;+	case 'n':+		if (strcmp(key, "nlink") == 0) {+			*parsed_kws |= MTREE_HAS_NLINK;+			archive_entry_set_nlink(entry,+				(unsigned int)mtree_atol(&val, 10));+			break;+		}+		__LA_FALLTHROUGH;+	case 'r':+		if (strcmp(key, "resdevice") == 0) {+			/* stat(2) st_dev field, e.g. the device ID where the+			 * inode resides */+			int r;+			dev_t dev;++			r = parse_device(&dev, &a->archive, val);+			if (r == ARCHIVE_OK)+				archive_entry_set_dev(entry, dev);+			return r;+		}+		if (strcmp(key, "rmd160") == 0 ||+		    strcmp(key, "rmd160digest") == 0)+			break;+		__LA_FALLTHROUGH;+	case 's':+		if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0)+			break;+		if (strcmp(key, "sha256") == 0 ||+		    strcmp(key, "sha256digest") == 0)+			break;+		if (strcmp(key, "sha384") == 0 ||+		    strcmp(key, "sha384digest") == 0)+			break;+		if (strcmp(key, "sha512") == 0 ||+		    strcmp(key, "sha512digest") == 0)+			break;+		if (strcmp(key, "size") == 0) {+			archive_entry_set_size(entry, mtree_atol(&val, 10));+			break;+		}+		__LA_FALLTHROUGH;+	case 't':+		if (strcmp(key, "tags") == 0) {+			/*+			 * Comma delimited list of tags.+			 * Ignore the tags for now, but the interface+			 * should be extended to allow inclusion/exclusion.+			 */+			break;+		}+		if (strcmp(key, "time") == 0) {+			int64_t m;+			int64_t my_time_t_max = get_time_t_max();+			int64_t my_time_t_min = get_time_t_min();+			long ns = 0;++			*parsed_kws |= MTREE_HAS_MTIME;+			m = mtree_atol(&val, 10);+			/* Replicate an old mtree bug:+			 * 123456789.1 represents 123456789+			 * seconds and 1 nanosecond. */+			if (*val == '.') {+				++val;+				ns = (long)mtree_atol(&val, 10);+				if (ns < 0)+					ns = 0;+				else if (ns > 999999999)+					ns = 999999999;+			}+			if (m > my_time_t_max)+				m = my_time_t_max;+			else if (m < my_time_t_min)+				m = my_time_t_min;+			archive_entry_set_mtime(entry, (time_t)m, ns);+			break;+		}+		if (strcmp(key, "type") == 0) {+			switch (val[0]) {+			case 'b':+				if (strcmp(val, "block") == 0) {+					archive_entry_set_filetype(entry, AE_IFBLK);+					break;+				}+				__LA_FALLTHROUGH;+			case 'c':+				if (strcmp(val, "char") == 0) {+					archive_entry_set_filetype(entry,+						AE_IFCHR);+					break;+				}+				__LA_FALLTHROUGH;+			case 'd':+				if (strcmp(val, "dir") == 0) {+					archive_entry_set_filetype(entry,+						AE_IFDIR);+					break;+				}+				__LA_FALLTHROUGH;+			case 'f':+				if (strcmp(val, "fifo") == 0) {+					archive_entry_set_filetype(entry,+						AE_IFIFO);+					break;+				}+				if (strcmp(val, "file") == 0) {+					archive_entry_set_filetype(entry,+						AE_IFREG);+					break;+				}+				__LA_FALLTHROUGH;+			case 'l':+				if (strcmp(val, "link") == 0) {+					archive_entry_set_filetype(entry,+						AE_IFLNK);+					break;+				}+				__LA_FALLTHROUGH;+			default:+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Unrecognized file type \"%s\"; "+				    "assuming \"file\"", val);+				archive_entry_set_filetype(entry, AE_IFREG);+				return (ARCHIVE_WARN);+			}+			*parsed_kws |= MTREE_HAS_TYPE;+			break;+		}+		__LA_FALLTHROUGH;+	case 'u':+		if (strcmp(key, "uid") == 0) {+			*parsed_kws |= MTREE_HAS_UID;+			archive_entry_set_uid(entry, mtree_atol(&val, 10));+			break;+		}+		if (strcmp(key, "uname") == 0) {+			*parsed_kws |= MTREE_HAS_UNAME;+			archive_entry_copy_uname(entry, val);+			break;+		}+		__LA_FALLTHROUGH;+	default:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unrecognized key %s=%s", key, val);+		return (ARCHIVE_WARN);+	}+	return (ARCHIVE_OK);+}++static int+read_data(struct archive_read *a, const void **buff, size_t *size,+    int64_t *offset)+{+	size_t bytes_to_read;+	ssize_t bytes_read;+	struct mtree *mtree;++	mtree = (struct mtree *)(a->format->data);+	if (mtree->fd < 0) {+		*buff = NULL;+		*offset = 0;+		*size = 0;+		return (ARCHIVE_EOF);+	}+	if (mtree->buff == NULL) {+		mtree->buffsize = 64 * 1024;+		mtree->buff = malloc(mtree->buffsize);+		if (mtree->buff == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory");+			return (ARCHIVE_FATAL);+		}+	}++	*buff = mtree->buff;+	*offset = mtree->offset;+	if ((int64_t)mtree->buffsize > mtree->cur_size - mtree->offset)+		bytes_to_read = (size_t)(mtree->cur_size - mtree->offset);+	else+		bytes_to_read = mtree->buffsize;+	bytes_read = read(mtree->fd, mtree->buff, bytes_to_read);+	if (bytes_read < 0) {+		archive_set_error(&a->archive, errno, "Can't read");+		return (ARCHIVE_WARN);+	}+	if (bytes_read == 0) {+		*size = 0;+		return (ARCHIVE_EOF);+	}+	mtree->offset += bytes_read;+	*size = bytes_read;+	return (ARCHIVE_OK);+}++/* Skip does nothing except possibly close the contents file. */+static int+skip(struct archive_read *a)+{+	struct mtree *mtree;++	mtree = (struct mtree *)(a->format->data);+	if (mtree->fd >= 0) {+		close(mtree->fd);+		mtree->fd = -1;+	}+	return (ARCHIVE_OK);+}++/*+ * Since parsing backslash sequences always makes strings shorter,+ * we can always do this conversion in-place.+ */+static void+parse_escapes(char *src, struct mtree_entry *mentry)+{+	char *dest = src;+	char c;++	if (mentry != NULL && strcmp(src, ".") == 0)+		mentry->full = 1;++	while (*src != '\0') {+		c = *src++;+		if (c == '/' && mentry != NULL)+			mentry->full = 1;+		if (c == '\\') {+			switch (src[0]) {+			case '0':+				if (src[1] < '0' || src[1] > '7') {+					c = 0;+					++src;+					break;+				}+				/* FALLTHROUGH */+			case '1':+			case '2':+			case '3':+				if (src[1] >= '0' && src[1] <= '7' &&+				    src[2] >= '0' && src[2] <= '7') {+					c = (src[0] - '0') << 6;+					c |= (src[1] - '0') << 3;+					c |= (src[2] - '0');+					src += 3;+				}+				break;+			case 'a':+				c = '\a';+				++src;+				break;+			case 'b':+				c = '\b';+				++src;+				break;+			case 'f':+				c = '\f';+				++src;+				break;+			case 'n':+				c = '\n';+				++src;+				break;+			case 'r':+				c = '\r';+				++src;+				break;+			case 's':+				c = ' ';+				++src;+				break;+			case 't':+				c = '\t';+				++src;+				break;+			case 'v':+				c = '\v';+				++src;+				break;+			case '\\':+				c = '\\';+				++src;+				break;+			}+		}+		*dest++ = c;+	}+	*dest = '\0';+}++/* Parse a hex digit. */+static int+parsedigit(char c)+{+	if (c >= '0' && c <= '9')+		return c - '0';+	else if (c >= 'a' && c <= 'f')+		return c - 'a';+	else if (c >= 'A' && c <= 'F')+		return c - 'A';+	else+		return -1;+}++/*+ * Note that this implementation does not (and should not!) obey+ * locale settings; you cannot simply substitute strtol here, since+ * it does obey locale.+ */+static int64_t+mtree_atol(char **p, int base)+{+	int64_t l, limit;+	int digit, last_digit_limit;++	if (base == 0) {+		if (**p != '0')+			base = 10;+		else if ((*p)[1] == 'x' || (*p)[1] == 'X') {+			*p += 2;+			base = 16;+		} else {+			base = 8;+		}+	}++	if (**p == '-') {+		limit = INT64_MIN / base;+		last_digit_limit = INT64_MIN % base;+		++(*p);++		l = 0;+		digit = parsedigit(**p);+		while (digit >= 0 && digit < base) {+			if (l < limit || (l == limit && digit > last_digit_limit))+				return INT64_MIN;+			l = (l * base) - digit;+			digit = parsedigit(*++(*p));+		}+		return l;+	} else {+		limit = INT64_MAX / base;+		last_digit_limit = INT64_MAX % base;++		l = 0;+		digit = parsedigit(**p);+		while (digit >= 0 && digit < base) {+			if (l > limit || (l == limit && digit > last_digit_limit))+				return INT64_MAX;+			l = (l * base) + digit;+			digit = parsedigit(*++(*p));+		}+		return l;+	}+}++/*+ * Returns length of line (including trailing newline)+ * or negative on error.  'start' argument is updated to+ * point to first character of line.+ */+static ssize_t+readline(struct archive_read *a, struct mtree *mtree, char **start,+    ssize_t limit)+{+	ssize_t bytes_read;+	ssize_t total_size = 0;+	ssize_t find_off = 0;+	const void *t;+	void *nl;+	char *u;++	/* Accumulate line in a line buffer. */+	for (;;) {+		/* Read some more. */+		t = __archive_read_ahead(a, 1, &bytes_read);+		if (t == NULL)+			return (0);+		if (bytes_read < 0)+			return (ARCHIVE_FATAL);+		nl = memchr(t, '\n', bytes_read);+		/* If we found '\n', trim the read to end exactly there. */+		if (nl != NULL) {+			bytes_read = ((const char *)nl) - ((const char *)t) + 1;+		}+		if (total_size + bytes_read + 1 > limit) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Line too long");+			return (ARCHIVE_FATAL);+		}+		if (archive_string_ensure(&mtree->line,+			total_size + bytes_read + 1) == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate working buffer");+			return (ARCHIVE_FATAL);+		}+		/* Append new bytes to string. */+		memcpy(mtree->line.s + total_size, t, bytes_read);+		__archive_read_consume(a, bytes_read);+		total_size += bytes_read;+		mtree->line.s[total_size] = '\0';++		for (u = mtree->line.s + find_off; *u; ++u) {+			if (u[0] == '\n') {+				/* Ends with unescaped newline. */+				*start = mtree->line.s;+				return total_size;+			} else if (u[0] == '#') {+				/* Ends with comment sequence #...\n */+				if (nl == NULL) {+					/* But we've not found the \n yet */+					break;+				}+			} else if (u[0] == '\\') {+				if (u[1] == '\n') {+					/* Trim escaped newline. */+					total_size -= 2;+					mtree->line.s[total_size] = '\0';+					break;+				} else if (u[1] != '\0') {+					/* Skip the two-char escape sequence */+					++u;+				}+			}+		}+		find_off = u - mtree->line.s;+	}+}
+ c/archive_read_support_format_rar.c view
@@ -0,0 +1,2999 @@+/*-+* Copyright (c) 2003-2007 Tim Kientzle+* Copyright (c) 2011 Andres Mejia+* All rights reserved.+*+* Redistribution and use in source and binary forms, with or without+* modification, are permitted provided that the following conditions+* are met:+* 1. Redistributions of source code must retain the above copyright+*    notice, this list of conditions and the following disclaimer.+* 2. Redistributions in binary form must reproduce the above copyright+*    notice, this list of conditions and the following disclaimer in the+*    documentation and/or other materials provided with the distribution.+*+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*/++#include "archive_platform.h"++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <time.h>+#include <limits.h>+#ifdef HAVE_ZLIB_H+#include <zlib.h> /* crc32 */+#endif++#include "archive.h"+#ifndef HAVE_ZLIB_H+#include "archive_crc32.h"+#endif+#include "archive_endian.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_ppmd7_private.h"+#include "archive_private.h"+#include "archive_read_private.h"++/* RAR signature, also known as the mark header */+#define RAR_SIGNATURE "\x52\x61\x72\x21\x1A\x07\x00"++/* Header types */+#define MARK_HEAD    0x72+#define MAIN_HEAD    0x73+#define FILE_HEAD    0x74+#define COMM_HEAD    0x75+#define AV_HEAD      0x76+#define SUB_HEAD     0x77+#define PROTECT_HEAD 0x78+#define SIGN_HEAD    0x79+#define NEWSUB_HEAD  0x7a+#define ENDARC_HEAD  0x7b++/* Main Header Flags */+#define MHD_VOLUME       0x0001+#define MHD_COMMENT      0x0002+#define MHD_LOCK         0x0004+#define MHD_SOLID        0x0008+#define MHD_NEWNUMBERING 0x0010+#define MHD_AV           0x0020+#define MHD_PROTECT      0x0040+#define MHD_PASSWORD     0x0080+#define MHD_FIRSTVOLUME  0x0100+#define MHD_ENCRYPTVER   0x0200++/* Flags common to all headers */+#define HD_MARKDELETION     0x4000+#define HD_ADD_SIZE_PRESENT 0x8000++/* File Header Flags */+#define FHD_SPLIT_BEFORE 0x0001+#define FHD_SPLIT_AFTER  0x0002+#define FHD_PASSWORD     0x0004+#define FHD_COMMENT      0x0008+#define FHD_SOLID        0x0010+#define FHD_LARGE        0x0100+#define FHD_UNICODE      0x0200+#define FHD_SALT         0x0400+#define FHD_VERSION      0x0800+#define FHD_EXTTIME      0x1000+#define FHD_EXTFLAGS     0x2000++/* File dictionary sizes */+#define DICTIONARY_SIZE_64   0x00+#define DICTIONARY_SIZE_128  0x20+#define DICTIONARY_SIZE_256  0x40+#define DICTIONARY_SIZE_512  0x60+#define DICTIONARY_SIZE_1024 0x80+#define DICTIONARY_SIZE_2048 0xA0+#define DICTIONARY_SIZE_4096 0xC0+#define FILE_IS_DIRECTORY    0xE0+#define DICTIONARY_MASK      FILE_IS_DIRECTORY++/* OS Flags */+#define OS_MSDOS  0+#define OS_OS2    1+#define OS_WIN32  2+#define OS_UNIX   3+#define OS_MAC_OS 4+#define OS_BEOS   5++/* Compression Methods */+#define COMPRESS_METHOD_STORE   0x30+/* LZSS */+#define COMPRESS_METHOD_FASTEST 0x31+#define COMPRESS_METHOD_FAST    0x32+#define COMPRESS_METHOD_NORMAL  0x33+/* PPMd Variant H */+#define COMPRESS_METHOD_GOOD    0x34+#define COMPRESS_METHOD_BEST    0x35++#define CRC_POLYNOMIAL 0xEDB88320++#define NS_UNIT 10000000++#define DICTIONARY_MAX_SIZE 0x400000++#define MAINCODE_SIZE      299+#define OFFSETCODE_SIZE    60+#define LOWOFFSETCODE_SIZE 17+#define LENGTHCODE_SIZE    28+#define HUFFMAN_TABLE_SIZE \+  MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE + LENGTHCODE_SIZE++#define MAX_SYMBOL_LENGTH 0xF+#define MAX_SYMBOLS       20++/*+ * Considering L1,L2 cache miss and a calling of write system-call,+ * the best size of the output buffer(uncompressed buffer) is 128K.+ * If the structure of extracting process is changed, this value+ * might be researched again.+ */+#define UNP_BUFFER_SIZE   (128 * 1024)++/* Define this here for non-Windows platforms */+#if !((defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__))+#define FILE_ATTRIBUTE_DIRECTORY 0x10+#endif++#undef minimum+#define minimum(a, b)	((a)<(b)?(a):(b))++/* Fields common to all headers */+struct rar_header+{+  char crc[2];+  char type;+  char flags[2];+  char size[2];+};++/* Fields common to all file headers */+struct rar_file_header+{+  char pack_size[4];+  char unp_size[4];+  char host_os;+  char file_crc[4];+  char file_time[4];+  char unp_ver;+  char method;+  char name_size[2];+  char file_attr[4];+};++struct huffman_tree_node+{+  int branches[2];+};++struct huffman_table_entry+{+  unsigned int length;+  int value;+};++struct huffman_code+{+  struct huffman_tree_node *tree;+  int numentries;+  int numallocatedentries;+  int minlength;+  int maxlength;+  int tablesize;+  struct huffman_table_entry *table;+};++struct lzss+{+  unsigned char *window;+  int mask;+  int64_t position;+};++struct data_block_offsets+{+  int64_t header_size;+  int64_t start_offset;+  int64_t end_offset;+};++struct rar+{+  /* Entries from main RAR header */+  unsigned main_flags;+  unsigned long file_crc;+  char reserved1[2];+  char reserved2[4];+  char encryptver;++  /* File header entries */+  char compression_method;+  unsigned file_flags;+  int64_t packed_size;+  int64_t unp_size;+  time_t mtime;+  long mnsec;+  mode_t mode;+  char *filename;+  char *filename_save;+  size_t filename_save_size;+  size_t filename_allocated;++  /* File header optional entries */+  char salt[8];+  time_t atime;+  long ansec;+  time_t ctime;+  long cnsec;+  time_t arctime;+  long arcnsec;++  /* Fields to help with tracking decompression of files. */+  int64_t bytes_unconsumed;+  int64_t bytes_remaining;+  int64_t bytes_uncopied;+  int64_t offset;+  int64_t offset_outgoing;+  int64_t offset_seek;+  char valid;+  unsigned int unp_offset;+  unsigned int unp_buffer_size;+  unsigned char *unp_buffer;+  unsigned int dictionary_size;+  char start_new_block;+  char entry_eof;+  unsigned long crc_calculated;+  int found_first_header;+  char has_endarc_header;+  struct data_block_offsets *dbo;+  unsigned int cursor;+  unsigned int nodes;+  char filename_must_match;++  /* LZSS members */+  struct huffman_code maincode;+  struct huffman_code offsetcode;+  struct huffman_code lowoffsetcode;+  struct huffman_code lengthcode;+  unsigned char lengthtable[HUFFMAN_TABLE_SIZE];+  struct lzss lzss;+  char output_last_match;+  unsigned int lastlength;+  unsigned int lastoffset;+  unsigned int oldoffset[4];+  unsigned int lastlowoffset;+  unsigned int numlowoffsetrepeats;+  int64_t filterstart;+  char start_new_table;++  /* PPMd Variant H members */+  char ppmd_valid;+  char ppmd_eod;+  char is_ppmd_block;+  int ppmd_escape;+  CPpmd7 ppmd7_context;+  CPpmd7z_RangeDec range_dec;+  IByteIn bytein;++  /*+   * String conversion object.+   */+  int init_default_conversion;+  struct archive_string_conv *sconv_default;+  struct archive_string_conv *opt_sconv;+  struct archive_string_conv *sconv_utf8;+  struct archive_string_conv *sconv_utf16be;++  /*+   * Bit stream reader.+   */+  struct rar_br {+#define CACHE_TYPE	uint64_t+#define CACHE_BITS	(8 * sizeof(CACHE_TYPE))+    /* Cache buffer. */+    CACHE_TYPE		 cache_buffer;+    /* Indicates how many bits avail in cache_buffer. */+    int			 cache_avail;+    ssize_t		 avail_in;+    const unsigned char *next_in;+  } br;++  /*+   * Custom field to denote that this archive contains encrypted entries+   */+  int has_encrypted_entries;+};++static int archive_read_support_format_rar_capabilities(struct archive_read *);+static int archive_read_format_rar_has_encrypted_entries(struct archive_read *);+static int archive_read_format_rar_bid(struct archive_read *, int);+static int archive_read_format_rar_options(struct archive_read *,+    const char *, const char *);+static int archive_read_format_rar_read_header(struct archive_read *,+    struct archive_entry *);+static int archive_read_format_rar_read_data(struct archive_read *,+    const void **, size_t *, int64_t *);+static int archive_read_format_rar_read_data_skip(struct archive_read *a);+static int64_t archive_read_format_rar_seek_data(struct archive_read *, int64_t,+    int);+static int archive_read_format_rar_cleanup(struct archive_read *);++/* Support functions */+static int read_header(struct archive_read *, struct archive_entry *, char);+static time_t get_time(int);+static int read_exttime(const char *, struct rar *, const char *);+static int read_symlink_stored(struct archive_read *, struct archive_entry *,+                               struct archive_string_conv *);+static int read_data_stored(struct archive_read *, const void **, size_t *,+                            int64_t *);+static int read_data_compressed(struct archive_read *, const void **, size_t *,+                          int64_t *);+static int rar_br_preparation(struct archive_read *, struct rar_br *);+static int parse_codes(struct archive_read *);+static void free_codes(struct archive_read *);+static int read_next_symbol(struct archive_read *, struct huffman_code *);+static int create_code(struct archive_read *, struct huffman_code *,+                        unsigned char *, int, char);+static int add_value(struct archive_read *, struct huffman_code *, int, int,+                     int);+static int new_node(struct huffman_code *);+static int make_table(struct archive_read *, struct huffman_code *);+static int make_table_recurse(struct archive_read *, struct huffman_code *, int,+                              struct huffman_table_entry *, int, int);+static int64_t expand(struct archive_read *, int64_t);+static int copy_from_lzss_window(struct archive_read *, const void **,+                                   int64_t, int);+static const void *rar_read_ahead(struct archive_read *, size_t, ssize_t *);++/*+ * Bit stream reader.+ */+/* Check that the cache buffer has enough bits. */+#define rar_br_has(br, n) ((br)->cache_avail >= n)+/* Get compressed data by bit. */+#define rar_br_bits(br, n)        \+  (((uint32_t)((br)->cache_buffer >>    \+    ((br)->cache_avail - (n)))) & cache_masks[n])+#define rar_br_bits_forced(br, n)     \+  (((uint32_t)((br)->cache_buffer <<    \+    ((n) - (br)->cache_avail))) & cache_masks[n])+/* Read ahead to make sure the cache buffer has enough compressed data we+ * will use.+ *  True  : completed, there is enough data in the cache buffer.+ *  False : there is no data in the stream. */+#define rar_br_read_ahead(a, br, n) \+  ((rar_br_has(br, (n)) || rar_br_fillup(a, br)) || rar_br_has(br, (n)))+/* Notify how many bits we consumed. */+#define rar_br_consume(br, n) ((br)->cache_avail -= (n))+#define rar_br_consume_unalined_bits(br) ((br)->cache_avail &= ~7)++static const uint32_t cache_masks[] = {+  0x00000000, 0x00000001, 0x00000003, 0x00000007,+  0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,+  0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,+  0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,+  0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,+  0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,+  0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,+  0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,+  0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF+};++/*+ * Shift away used bits in the cache data and fill it up with following bits.+ * Call this when cache buffer does not have enough bits you need.+ *+ * Returns 1 if the cache buffer is full.+ * Returns 0 if the cache buffer is not full; input buffer is empty.+ */+static int+rar_br_fillup(struct archive_read *a, struct rar_br *br)+{+  struct rar *rar = (struct rar *)(a->format->data);+  int n = CACHE_BITS - br->cache_avail;++  for (;;) {+    switch (n >> 3) {+    case 8:+      if (br->avail_in >= 8) {+        br->cache_buffer =+            ((uint64_t)br->next_in[0]) << 56 |+            ((uint64_t)br->next_in[1]) << 48 |+            ((uint64_t)br->next_in[2]) << 40 |+            ((uint64_t)br->next_in[3]) << 32 |+            ((uint32_t)br->next_in[4]) << 24 |+            ((uint32_t)br->next_in[5]) << 16 |+            ((uint32_t)br->next_in[6]) << 8 |+             (uint32_t)br->next_in[7];+        br->next_in += 8;+        br->avail_in -= 8;+        br->cache_avail += 8 * 8;+        rar->bytes_unconsumed += 8;+        rar->bytes_remaining -= 8;+        return (1);+      }+      break;+    case 7:+      if (br->avail_in >= 7) {+        br->cache_buffer =+           (br->cache_buffer << 56) |+            ((uint64_t)br->next_in[0]) << 48 |+            ((uint64_t)br->next_in[1]) << 40 |+            ((uint64_t)br->next_in[2]) << 32 |+            ((uint32_t)br->next_in[3]) << 24 |+            ((uint32_t)br->next_in[4]) << 16 |+            ((uint32_t)br->next_in[5]) << 8 |+             (uint32_t)br->next_in[6];+        br->next_in += 7;+        br->avail_in -= 7;+        br->cache_avail += 7 * 8;+        rar->bytes_unconsumed += 7;+        rar->bytes_remaining -= 7;+        return (1);+      }+      break;+    case 6:+      if (br->avail_in >= 6) {+        br->cache_buffer =+           (br->cache_buffer << 48) |+            ((uint64_t)br->next_in[0]) << 40 |+            ((uint64_t)br->next_in[1]) << 32 |+            ((uint32_t)br->next_in[2]) << 24 |+            ((uint32_t)br->next_in[3]) << 16 |+            ((uint32_t)br->next_in[4]) << 8 |+             (uint32_t)br->next_in[5];+        br->next_in += 6;+        br->avail_in -= 6;+        br->cache_avail += 6 * 8;+        rar->bytes_unconsumed += 6;+        rar->bytes_remaining -= 6;+        return (1);+      }+      break;+    case 0:+      /* We have enough compressed data in+       * the cache buffer.*/+      return (1);+    default:+      break;+    }+    if (br->avail_in <= 0) {++      if (rar->bytes_unconsumed > 0) {+        /* Consume as much as the decompressor+         * actually used. */+        __archive_read_consume(a, rar->bytes_unconsumed);+        rar->bytes_unconsumed = 0;+      }+      br->next_in = rar_read_ahead(a, 1, &(br->avail_in));+      if (br->next_in == NULL)+        return (0);+      if (br->avail_in == 0)+        return (0);+    }+    br->cache_buffer =+       (br->cache_buffer << 8) | *br->next_in++;+    br->avail_in--;+    br->cache_avail += 8;+    n -= 8;+    rar->bytes_unconsumed++;+    rar->bytes_remaining--;+  }+}++static int+rar_br_preparation(struct archive_read *a, struct rar_br *br)+{+  struct rar *rar = (struct rar *)(a->format->data);++  if (rar->bytes_remaining > 0) {+    br->next_in = rar_read_ahead(a, 1, &(br->avail_in));+    if (br->next_in == NULL) {+      archive_set_error(&a->archive,+          ARCHIVE_ERRNO_FILE_FORMAT,+          "Truncated RAR file data");+      return (ARCHIVE_FATAL);+    }+    if (br->cache_avail == 0)+      (void)rar_br_fillup(a, br);+  }+  return (ARCHIVE_OK);+}++/* Find last bit set */+static inline int+rar_fls(unsigned int word)+{+  word |= (word >>  1);+  word |= (word >>  2);+  word |= (word >>  4);+  word |= (word >>  8);+  word |= (word >> 16);+  return word - (word >> 1);+}++/* LZSS functions */+static inline int64_t+lzss_position(struct lzss *lzss)+{+  return lzss->position;+}++static inline int+lzss_mask(struct lzss *lzss)+{+  return lzss->mask;+}++static inline int+lzss_size(struct lzss *lzss)+{+  return lzss->mask + 1;+}++static inline int+lzss_offset_for_position(struct lzss *lzss, int64_t pos)+{+  return (int)(pos & lzss->mask);+}++static inline unsigned char *+lzss_pointer_for_position(struct lzss *lzss, int64_t pos)+{+  return &lzss->window[lzss_offset_for_position(lzss, pos)];+}++static inline int+lzss_current_offset(struct lzss *lzss)+{+  return lzss_offset_for_position(lzss, lzss->position);+}++static inline uint8_t *+lzss_current_pointer(struct lzss *lzss)+{+  return lzss_pointer_for_position(lzss, lzss->position);+}++static inline void+lzss_emit_literal(struct rar *rar, uint8_t literal)+{+  *lzss_current_pointer(&rar->lzss) = literal;+  rar->lzss.position++;+}++static inline void+lzss_emit_match(struct rar *rar, int offset, int length)+{+  int dstoffs = lzss_current_offset(&rar->lzss);+  int srcoffs = (dstoffs - offset) & lzss_mask(&rar->lzss);+  int l, li, remaining;+  unsigned char *d, *s;++  remaining = length;+  while (remaining > 0) {+    l = remaining;+    if (dstoffs > srcoffs) {+      if (l > lzss_size(&rar->lzss) - dstoffs)+        l = lzss_size(&rar->lzss) - dstoffs;+    } else {+      if (l > lzss_size(&rar->lzss) - srcoffs)+        l = lzss_size(&rar->lzss) - srcoffs;+    }+    d = &(rar->lzss.window[dstoffs]);+    s = &(rar->lzss.window[srcoffs]);+    if ((dstoffs + l < srcoffs) || (srcoffs + l < dstoffs))+      memcpy(d, s, l);+    else {+      for (li = 0; li < l; li++)+        d[li] = s[li];+    }+    remaining -= l;+    dstoffs = (dstoffs + l) & lzss_mask(&(rar->lzss));+    srcoffs = (srcoffs + l) & lzss_mask(&(rar->lzss));+  }+  rar->lzss.position += length;+}++static Byte+ppmd_read(void *p)+{+  struct archive_read *a = ((IByteIn*)p)->a;+  struct rar *rar = (struct rar *)(a->format->data);+  struct rar_br *br = &(rar->br);+  Byte b;+  if (!rar_br_read_ahead(a, br, 8))+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Truncated RAR file data");+    rar->valid = 0;+    return 0;+  }+  b = rar_br_bits(br, 8);+  rar_br_consume(br, 8);+  return b;+}++int+archive_read_support_format_rar(struct archive *_a)+{+  struct archive_read *a = (struct archive_read *)_a;+  struct rar *rar;+  int r;++  archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+                      "archive_read_support_format_rar");++  rar = (struct rar *)calloc(sizeof(*rar), 1);+  if (rar == NULL)+  {+    archive_set_error(&a->archive, ENOMEM, "Can't allocate rar data");+    return (ARCHIVE_FATAL);+  }++	/*+	 * Until enough data has been read, we cannot tell about+	 * any encrypted entries yet.+	 */+	rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;++  r = __archive_read_register_format(a,+                                     rar,+                                     "rar",+                                     archive_read_format_rar_bid,+                                     archive_read_format_rar_options,+                                     archive_read_format_rar_read_header,+                                     archive_read_format_rar_read_data,+                                     archive_read_format_rar_read_data_skip,+                                     archive_read_format_rar_seek_data,+                                     archive_read_format_rar_cleanup,+                                     archive_read_support_format_rar_capabilities,+                                     archive_read_format_rar_has_encrypted_entries);++  if (r != ARCHIVE_OK)+    free(rar);+  return (r);+}++static int+archive_read_support_format_rar_capabilities(struct archive_read * a)+{+	(void)a; /* UNUSED */+	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA+			| ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);+}++static int+archive_read_format_rar_has_encrypted_entries(struct archive_read *_a)+{+	if (_a && _a->format) {+		struct rar * rar = (struct rar *)_a->format->data;+		if (rar) {+			return rar->has_encrypted_entries;+		}+	}+	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;+}+++static int+archive_read_format_rar_bid(struct archive_read *a, int best_bid)+{+  const char *p;++  /* If there's already a bid > 30, we'll never win. */+  if (best_bid > 30)+	  return (-1);++  if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)+    return (-1);++  if (memcmp(p, RAR_SIGNATURE, 7) == 0)+    return (30);++  if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {+    /* This is a PE file */+    ssize_t offset = 0x10000;+    ssize_t window = 4096;+    ssize_t bytes_avail;+    while (offset + window <= (1024 * 128)) {+      const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail);+      if (buff == NULL) {+        /* Remaining bytes are less than window. */+        window >>= 1;+        if (window < 0x40)+          return (0);+        continue;+      }+      p = buff + offset;+      while (p + 7 < buff + bytes_avail) {+        if (memcmp(p, RAR_SIGNATURE, 7) == 0)+          return (30);+        p += 0x10;+      }+      offset = p - buff;+    }+  }+  return (0);+}++static int+skip_sfx(struct archive_read *a)+{+  const void *h;+  const char *p, *q;+  size_t skip, total;+  ssize_t bytes, window;++  total = 0;+  window = 4096;+  while (total + window <= (1024 * 128)) {+    h = __archive_read_ahead(a, window, &bytes);+    if (h == NULL) {+      /* Remaining bytes are less than window. */+      window >>= 1;+      if (window < 0x40)+      	goto fatal;+      continue;+    }+    if (bytes < 0x40)+      goto fatal;+    p = h;+    q = p + bytes;++    /*+     * Scan ahead until we find something that looks+     * like the RAR header.+     */+    while (p + 7 < q) {+      if (memcmp(p, RAR_SIGNATURE, 7) == 0) {+      	skip = p - (const char *)h;+      	__archive_read_consume(a, skip);+      	return (ARCHIVE_OK);+      }+      p += 0x10;+    }+    skip = p - (const char *)h;+    __archive_read_consume(a, skip);+	total += skip;+  }+fatal:+  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+      "Couldn't find out RAR header");+  return (ARCHIVE_FATAL);+}++static int+archive_read_format_rar_options(struct archive_read *a,+    const char *key, const char *val)+{+  struct rar *rar;+  int ret = ARCHIVE_FAILED;++  rar = (struct rar *)(a->format->data);+  if (strcmp(key, "hdrcharset")  == 0) {+    if (val == NULL || val[0] == 0)+      archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+          "rar: hdrcharset option needs a character-set name");+    else {+      rar->opt_sconv =+          archive_string_conversion_from_charset(+              &a->archive, val, 0);+      if (rar->opt_sconv != NULL)+        ret = ARCHIVE_OK;+      else+        ret = ARCHIVE_FATAL;+    }+    return (ret);+  }++  /* Note: The "warn" return is just to inform the options+   * supervisor that we didn't handle it.  It will generate+   * a suitable error if no one used this option. */+  return (ARCHIVE_WARN);+}++static int+archive_read_format_rar_read_header(struct archive_read *a,+                                    struct archive_entry *entry)+{+  const void *h;+  const char *p;+  struct rar *rar;+  size_t skip;+  char head_type;+  int ret;+  unsigned flags;+  unsigned long crc32_expected;++  a->archive.archive_format = ARCHIVE_FORMAT_RAR;+  if (a->archive.archive_format_name == NULL)+    a->archive.archive_format_name = "RAR";++  rar = (struct rar *)(a->format->data);++  /*+   * It should be sufficient to call archive_read_next_header() for+   * a reader to determine if an entry is encrypted or not. If the+   * encryption of an entry is only detectable when calling+   * archive_read_data(), so be it. We'll do the same check there+   * as well.+   */+  if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {+	  rar->has_encrypted_entries = 0;+  }++  /* RAR files can be generated without EOF headers, so return ARCHIVE_EOF if+  * this fails.+  */+  if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)+    return (ARCHIVE_EOF);++  p = h;+  if (rar->found_first_header == 0 &&+     ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)) {+    /* This is an executable ? Must be self-extracting... */+    ret = skip_sfx(a);+    if (ret < ARCHIVE_WARN)+      return (ret);+  }+  rar->found_first_header = 1;++  while (1)+  {+    unsigned long crc32_val;++    if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)+      return (ARCHIVE_FATAL);+    p = h;++    head_type = p[2];+    switch(head_type)+    {+    case MARK_HEAD:+      if (memcmp(p, RAR_SIGNATURE, 7) != 0) {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+          "Invalid marker header");+        return (ARCHIVE_FATAL);+      }+      __archive_read_consume(a, 7);+      break;++    case MAIN_HEAD:+      rar->main_flags = archive_le16dec(p + 3);+      skip = archive_le16dec(p + 5);+      if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)) {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+          "Invalid header size");+        return (ARCHIVE_FATAL);+      }+      if ((h = __archive_read_ahead(a, skip, NULL)) == NULL)+        return (ARCHIVE_FATAL);+      p = h;+      memcpy(rar->reserved1, p + 7, sizeof(rar->reserved1));+      memcpy(rar->reserved2, p + 7 + sizeof(rar->reserved1),+             sizeof(rar->reserved2));+      if (rar->main_flags & MHD_ENCRYPTVER) {+        if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)+1) {+          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+            "Invalid header size");+          return (ARCHIVE_FATAL);+        }+        rar->encryptver = *(p + 7 + sizeof(rar->reserved1) ++                            sizeof(rar->reserved2));+      }++      /* Main header is password encrypted, so we cannot read any+         file names or any other info about files from the header. */+      if (rar->main_flags & MHD_PASSWORD)+      {+        archive_entry_set_is_metadata_encrypted(entry, 1);+        archive_entry_set_is_data_encrypted(entry, 1);+        rar->has_encrypted_entries = 1;+         archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                          "RAR encryption support unavailable.");+        return (ARCHIVE_FATAL);+      }++      crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2);+      if ((crc32_val & 0xffff) != archive_le16dec(p)) {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+          "Header CRC error");+        return (ARCHIVE_FATAL);+      }+      __archive_read_consume(a, skip);+      break;++    case FILE_HEAD:+      return read_header(a, entry, head_type);++    case COMM_HEAD:+    case AV_HEAD:+    case SUB_HEAD:+    case PROTECT_HEAD:+    case SIGN_HEAD:+    case ENDARC_HEAD:+      flags = archive_le16dec(p + 3);+      skip = archive_le16dec(p + 5);+      if (skip < 7) {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+          "Invalid header size too small");+        return (ARCHIVE_FATAL);+      }+      if (flags & HD_ADD_SIZE_PRESENT)+      {+        if (skip < 7 + 4) {+          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+            "Invalid header size too small");+          return (ARCHIVE_FATAL);+        }+        if ((h = __archive_read_ahead(a, skip, NULL)) == NULL)+          return (ARCHIVE_FATAL);+        p = h;+        skip += archive_le32dec(p + 7);+      }++      /* Skip over the 2-byte CRC at the beginning of the header. */+      crc32_expected = archive_le16dec(p);+      __archive_read_consume(a, 2);+      skip -= 2;++      /* Skim the entire header and compute the CRC. */+      crc32_val = 0;+      while (skip > 0) {+	      size_t to_read = skip;+	      ssize_t did_read;+	      if (to_read > 32 * 1024) {+		      to_read = 32 * 1024;+	      }+	      if ((h = __archive_read_ahead(a, to_read, &did_read)) == NULL) {+		      return (ARCHIVE_FATAL);+	      }+	      p = h;+	      crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned)did_read);+	      __archive_read_consume(a, did_read);+	      skip -= did_read;+      }+      if ((crc32_val & 0xffff) != crc32_expected) {+	      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		  "Header CRC error");+	      return (ARCHIVE_FATAL);+      }+      if (head_type == ENDARC_HEAD)+	      return (ARCHIVE_EOF);+      break;++    case NEWSUB_HEAD:+      if ((ret = read_header(a, entry, head_type)) < ARCHIVE_WARN)+        return ret;+      break;++    default:+      archive_set_error(&a->archive,  ARCHIVE_ERRNO_FILE_FORMAT,+                        "Bad RAR file");+      return (ARCHIVE_FATAL);+    }+  }+}++static int+archive_read_format_rar_read_data(struct archive_read *a, const void **buff,+                                  size_t *size, int64_t *offset)+{+  struct rar *rar = (struct rar *)(a->format->data);+  int ret;++  if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {+	  rar->has_encrypted_entries = 0;+  }++  if (rar->bytes_unconsumed > 0) {+      /* Consume as much as the decompressor actually used. */+      __archive_read_consume(a, rar->bytes_unconsumed);+      rar->bytes_unconsumed = 0;+  }++  *buff = NULL;+  if (rar->entry_eof || rar->offset_seek >= rar->unp_size) {+    *size = 0;+    *offset = rar->offset;+    if (*offset < rar->unp_size)+      *offset = rar->unp_size;+    return (ARCHIVE_EOF);+  }++  switch (rar->compression_method)+  {+  case COMPRESS_METHOD_STORE:+    ret = read_data_stored(a, buff, size, offset);+    break;++  case COMPRESS_METHOD_FASTEST:+  case COMPRESS_METHOD_FAST:+  case COMPRESS_METHOD_NORMAL:+  case COMPRESS_METHOD_GOOD:+  case COMPRESS_METHOD_BEST:+    ret = read_data_compressed(a, buff, size, offset);+    if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) {+      __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);+      rar->start_new_table = 1;+      rar->ppmd_valid = 0;+    }+    break;++  default:+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Unsupported compression method for RAR file.");+    ret = ARCHIVE_FATAL;+    break;+  }+  return (ret);+}++static int+archive_read_format_rar_read_data_skip(struct archive_read *a)+{+  struct rar *rar;+  int64_t bytes_skipped;+  int ret;++  rar = (struct rar *)(a->format->data);++  if (rar->bytes_unconsumed > 0) {+      /* Consume as much as the decompressor actually used. */+      __archive_read_consume(a, rar->bytes_unconsumed);+      rar->bytes_unconsumed = 0;+  }++  if (rar->bytes_remaining > 0) {+    bytes_skipped = __archive_read_consume(a, rar->bytes_remaining);+    if (bytes_skipped < 0)+      return (ARCHIVE_FATAL);+  }++  /* Compressed data to skip must be read from each header in a multivolume+   * archive.+   */+  if (rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER)+  {+    ret = archive_read_format_rar_read_header(a, a->entry);+    if (ret == (ARCHIVE_EOF))+      ret = archive_read_format_rar_read_header(a, a->entry);+    if (ret != (ARCHIVE_OK))+      return ret;+    return archive_read_format_rar_read_data_skip(a);+  }++  return (ARCHIVE_OK);+}++static int64_t+archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset,+    int whence)+{+  int64_t client_offset, ret;+  unsigned int i;+  struct rar *rar = (struct rar *)(a->format->data);++  if (rar->compression_method == COMPRESS_METHOD_STORE)+  {+    /* Modify the offset for use with SEEK_SET */+    switch (whence)+    {+      case SEEK_CUR:+        client_offset = rar->offset_seek;+        break;+      case SEEK_END:+        client_offset = rar->unp_size;+        break;+      case SEEK_SET:+      default:+        client_offset = 0;+    }+    client_offset += offset;+    if (client_offset < 0)+    {+      /* Can't seek past beginning of data block */+      return -1;+    }+    else if (client_offset > rar->unp_size)+    {+      /*+       * Set the returned offset but only seek to the end of+       * the data block.+       */+      rar->offset_seek = client_offset;+      client_offset = rar->unp_size;+    }++    client_offset += rar->dbo[0].start_offset;+    i = 0;+    while (i < rar->cursor)+    {+      i++;+      client_offset += rar->dbo[i].start_offset - rar->dbo[i-1].end_offset;+    }+    if (rar->main_flags & MHD_VOLUME)+    {+      /* Find the appropriate offset among the multivolume archive */+      while (1)+      {+        if (client_offset < rar->dbo[rar->cursor].start_offset &&+          rar->file_flags & FHD_SPLIT_BEFORE)+        {+          /* Search backwards for the correct data block */+          if (rar->cursor == 0)+          {+            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+              "Attempt to seek past beginning of RAR data block");+            return (ARCHIVE_FAILED);+          }+          rar->cursor--;+          client_offset -= rar->dbo[rar->cursor+1].start_offset -+            rar->dbo[rar->cursor].end_offset;+          if (client_offset < rar->dbo[rar->cursor].start_offset)+            continue;+          ret = __archive_read_seek(a, rar->dbo[rar->cursor].start_offset -+            rar->dbo[rar->cursor].header_size, SEEK_SET);+          if (ret < (ARCHIVE_OK))+            return ret;+          ret = archive_read_format_rar_read_header(a, a->entry);+          if (ret != (ARCHIVE_OK))+          {+            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+              "Error during seek of RAR file");+            return (ARCHIVE_FAILED);+          }+          rar->cursor--;+          break;+        }+        else if (client_offset > rar->dbo[rar->cursor].end_offset &&+          rar->file_flags & FHD_SPLIT_AFTER)+        {+          /* Search forward for the correct data block */+          rar->cursor++;+          if (rar->cursor < rar->nodes &&+            client_offset > rar->dbo[rar->cursor].end_offset)+          {+            client_offset += rar->dbo[rar->cursor].start_offset -+              rar->dbo[rar->cursor-1].end_offset;+            continue;+          }+          rar->cursor--;+          ret = __archive_read_seek(a, rar->dbo[rar->cursor].end_offset,+                                    SEEK_SET);+          if (ret < (ARCHIVE_OK))+            return ret;+          ret = archive_read_format_rar_read_header(a, a->entry);+          if (ret == (ARCHIVE_EOF))+          {+            rar->has_endarc_header = 1;+            ret = archive_read_format_rar_read_header(a, a->entry);+          }+          if (ret != (ARCHIVE_OK))+          {+            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+              "Error during seek of RAR file");+            return (ARCHIVE_FAILED);+          }+          client_offset += rar->dbo[rar->cursor].start_offset -+            rar->dbo[rar->cursor-1].end_offset;+          continue;+        }+        break;+      }+    }++    ret = __archive_read_seek(a, client_offset, SEEK_SET);+    if (ret < (ARCHIVE_OK))+      return ret;+    rar->bytes_remaining = rar->dbo[rar->cursor].end_offset - ret;+    i = rar->cursor;+    while (i > 0)+    {+      i--;+      ret -= rar->dbo[i+1].start_offset - rar->dbo[i].end_offset;+    }+    ret -= rar->dbo[0].start_offset;++    /* Always restart reading the file after a seek */+    __archive_reset_read_data(&a->archive);++    rar->bytes_unconsumed = 0;+    rar->offset = 0;++    /*+     * If a seek past the end of file was requested, return the requested+     * offset.+     */+    if (ret == rar->unp_size && rar->offset_seek > rar->unp_size)+      return rar->offset_seek;++    /* Return the new offset */+    rar->offset_seek = ret;+    return rar->offset_seek;+  }+  else+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+      "Seeking of compressed RAR files is unsupported");+  }+  return (ARCHIVE_FAILED);+}++static int+archive_read_format_rar_cleanup(struct archive_read *a)+{+  struct rar *rar;++  rar = (struct rar *)(a->format->data);+  free_codes(a);+  free(rar->filename);+  free(rar->filename_save);+  free(rar->dbo);+  free(rar->unp_buffer);+  free(rar->lzss.window);+  __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);+  free(rar);+  (a->format->data) = NULL;+  return (ARCHIVE_OK);+}++static int+read_header(struct archive_read *a, struct archive_entry *entry,+            char head_type)+{+  const void *h;+  const char *p, *endp;+  struct rar *rar;+  struct rar_header rar_header;+  struct rar_file_header file_header;+  int64_t header_size;+  unsigned filename_size, end;+  char *filename;+  char *strp;+  char packed_size[8];+  char unp_size[8];+  int ttime;+  struct archive_string_conv *sconv, *fn_sconv;+  unsigned long crc32_val;+  int ret = (ARCHIVE_OK), ret2;++  rar = (struct rar *)(a->format->data);++  /* Setup a string conversion object for non-rar-unicode filenames. */+  sconv = rar->opt_sconv;+  if (sconv == NULL) {+    if (!rar->init_default_conversion) {+      rar->sconv_default =+          archive_string_default_conversion_for_read(+            &(a->archive));+      rar->init_default_conversion = 1;+    }+    sconv = rar->sconv_default;+  }+++  if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)+    return (ARCHIVE_FATAL);+  p = h;+  memcpy(&rar_header, p, sizeof(rar_header));+  rar->file_flags = archive_le16dec(rar_header.flags);+  header_size = archive_le16dec(rar_header.size);+  if (header_size < (int64_t)sizeof(file_header) + 7) {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+      "Invalid header size");+    return (ARCHIVE_FATAL);+  }+  crc32_val = crc32(0, (const unsigned char *)p + 2, 7 - 2);+  __archive_read_consume(a, 7);++  if (!(rar->file_flags & FHD_SOLID))+  {+    rar->compression_method = 0;+    rar->packed_size = 0;+    rar->unp_size = 0;+    rar->mtime = 0;+    rar->ctime = 0;+    rar->atime = 0;+    rar->arctime = 0;+    rar->mode = 0;+    memset(&rar->salt, 0, sizeof(rar->salt));+    rar->atime = 0;+    rar->ansec = 0;+    rar->ctime = 0;+    rar->cnsec = 0;+    rar->mtime = 0;+    rar->mnsec = 0;+    rar->arctime = 0;+    rar->arcnsec = 0;+  }+  else+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "RAR solid archive support unavailable.");+    return (ARCHIVE_FATAL);+  }++  if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)+    return (ARCHIVE_FATAL);++  /* File Header CRC check. */+  crc32_val = crc32(crc32_val, h, (unsigned)(header_size - 7));+  if ((crc32_val & 0xffff) != archive_le16dec(rar_header.crc)) {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+      "Header CRC error");+    return (ARCHIVE_FATAL);+  }+  /* If no CRC error, Go on parsing File Header. */+  p = h;+  endp = p + header_size - 7;+  memcpy(&file_header, p, sizeof(file_header));+  p += sizeof(file_header);++  rar->compression_method = file_header.method;++  ttime = archive_le32dec(file_header.file_time);+  rar->mtime = get_time(ttime);++  rar->file_crc = archive_le32dec(file_header.file_crc);++  if (rar->file_flags & FHD_PASSWORD)+  {+	archive_entry_set_is_data_encrypted(entry, 1);+	rar->has_encrypted_entries = 1;+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "RAR encryption support unavailable.");+    /* Since it is only the data part itself that is encrypted we can at least+       extract information about the currently processed entry and don't need+       to return ARCHIVE_FATAL here. */+    /*return (ARCHIVE_FATAL);*/+  }++  if (rar->file_flags & FHD_LARGE)+  {+    memcpy(packed_size, file_header.pack_size, 4);+    memcpy(packed_size + 4, p, 4); /* High pack size */+    p += 4;+    memcpy(unp_size, file_header.unp_size, 4);+    memcpy(unp_size + 4, p, 4); /* High unpack size */+    p += 4;+    rar->packed_size = archive_le64dec(&packed_size);+    rar->unp_size = archive_le64dec(&unp_size);+  }+  else+  {+    rar->packed_size = archive_le32dec(file_header.pack_size);+    rar->unp_size = archive_le32dec(file_header.unp_size);+  }++  if (rar->packed_size < 0 || rar->unp_size < 0)+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Invalid sizes specified.");+    return (ARCHIVE_FATAL);+  }++  rar->bytes_remaining = rar->packed_size;++  /* TODO: RARv3 subblocks contain comments. For now the complete block is+   * consumed at the end.+   */+  if (head_type == NEWSUB_HEAD) {+    size_t distance = p - (const char *)h;+    header_size += rar->packed_size;+    /* Make sure we have the extended data. */+    if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)+        return (ARCHIVE_FATAL);+    p = h;+    endp = p + header_size - 7;+    p += distance;+  }++  filename_size = archive_le16dec(file_header.name_size);+  if (p + filename_size > endp) {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+      "Invalid filename size");+    return (ARCHIVE_FATAL);+  }+  if (rar->filename_allocated < filename_size * 2 + 2) {+    char *newptr;+    size_t newsize = filename_size * 2 + 2;+    newptr = realloc(rar->filename, newsize);+    if (newptr == NULL) {+      archive_set_error(&a->archive, ENOMEM,+                        "Couldn't allocate memory.");+      return (ARCHIVE_FATAL);+    }+    rar->filename = newptr;+    rar->filename_allocated = newsize;+  }+  filename = rar->filename;+  memcpy(filename, p, filename_size);+  filename[filename_size] = '\0';+  if (rar->file_flags & FHD_UNICODE)+  {+    if (filename_size != strlen(filename))+    {+      unsigned char highbyte, flagbits, flagbyte;+      unsigned fn_end, offset;++      end = filename_size;+      fn_end = filename_size * 2;+      filename_size = 0;+      offset = (unsigned)strlen(filename) + 1;+      highbyte = *(p + offset++);+      flagbits = 0;+      flagbyte = 0;+      while (offset < end && filename_size < fn_end)+      {+        if (!flagbits)+        {+          flagbyte = *(p + offset++);+          flagbits = 8;+        }++        flagbits -= 2;+        switch((flagbyte >> flagbits) & 3)+        {+          case 0:+            filename[filename_size++] = '\0';+            filename[filename_size++] = *(p + offset++);+            break;+          case 1:+            filename[filename_size++] = highbyte;+            filename[filename_size++] = *(p + offset++);+            break;+          case 2:+            filename[filename_size++] = *(p + offset + 1);+            filename[filename_size++] = *(p + offset);+            offset += 2;+            break;+          case 3:+          {+            char extra, high;+            uint8_t length = *(p + offset++);++            if (length & 0x80) {+              extra = *(p + offset++);+              high = (char)highbyte;+            } else+              extra = high = 0;+            length = (length & 0x7f) + 2;+            while (length && filename_size < fn_end) {+              unsigned cp = filename_size >> 1;+              filename[filename_size++] = high;+              filename[filename_size++] = p[cp] + extra;+              length--;+            }+          }+          break;+        }+      }+      if (filename_size > fn_end) {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+          "Invalid filename");+        return (ARCHIVE_FATAL);+      }+      filename[filename_size++] = '\0';+      /*+       * Do not increment filename_size here as the computations below+       * add the space for the terminating NUL explicitly.+       */+      filename[filename_size] = '\0';++      /* Decoded unicode form is UTF-16BE, so we have to update a string+       * conversion object for it. */+      if (rar->sconv_utf16be == NULL) {+        rar->sconv_utf16be = archive_string_conversion_from_charset(+           &a->archive, "UTF-16BE", 1);+        if (rar->sconv_utf16be == NULL)+          return (ARCHIVE_FATAL);+      }+      fn_sconv = rar->sconv_utf16be;++      strp = filename;+      while (memcmp(strp, "\x00\x00", 2))+      {+        if (!memcmp(strp, "\x00\\", 2))+          *(strp + 1) = '/';+        strp += 2;+      }+      p += offset;+    } else {+      /*+       * If FHD_UNICODE is set but no unicode data, this file name form+       * is UTF-8, so we have to update a string conversion object for+       * it accordingly.+       */+      if (rar->sconv_utf8 == NULL) {+        rar->sconv_utf8 = archive_string_conversion_from_charset(+           &a->archive, "UTF-8", 1);+        if (rar->sconv_utf8 == NULL)+          return (ARCHIVE_FATAL);+      }+      fn_sconv = rar->sconv_utf8;+      while ((strp = strchr(filename, '\\')) != NULL)+        *strp = '/';+      p += filename_size;+    }+  }+  else+  {+    fn_sconv = sconv;+    while ((strp = strchr(filename, '\\')) != NULL)+      *strp = '/';+    p += filename_size;+  }++  /* Split file in multivolume RAR. No more need to process header. */+  if (rar->filename_save &&+    filename_size == rar->filename_save_size &&+    !memcmp(rar->filename, rar->filename_save, filename_size + 1))+  {+    __archive_read_consume(a, header_size - 7);+    rar->cursor++;+    if (rar->cursor >= rar->nodes)+    {+      rar->nodes++;+      if ((rar->dbo =+        realloc(rar->dbo, sizeof(*rar->dbo) * rar->nodes)) == NULL)+      {+        archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");+        return (ARCHIVE_FATAL);+      }+      rar->dbo[rar->cursor].header_size = header_size;+      rar->dbo[rar->cursor].start_offset = -1;+      rar->dbo[rar->cursor].end_offset = -1;+    }+    if (rar->dbo[rar->cursor].start_offset < 0)+    {+      rar->dbo[rar->cursor].start_offset = a->filter->position;+      rar->dbo[rar->cursor].end_offset = rar->dbo[rar->cursor].start_offset ++        rar->packed_size;+    }+    return ret;+  }+  else if (rar->filename_must_match)+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+      "Mismatch of file parts split across multi-volume archive");+    return (ARCHIVE_FATAL);+  }++  rar->filename_save = (char*)realloc(rar->filename_save,+                                      filename_size + 1);+  memcpy(rar->filename_save, rar->filename, filename_size + 1);+  rar->filename_save_size = filename_size;++  /* Set info for seeking */+  free(rar->dbo);+  if ((rar->dbo = calloc(1, sizeof(*rar->dbo))) == NULL)+  {+    archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");+    return (ARCHIVE_FATAL);+  }+  rar->dbo[0].header_size = header_size;+  rar->dbo[0].start_offset = -1;+  rar->dbo[0].end_offset = -1;+  rar->cursor = 0;+  rar->nodes = 1;++  if (rar->file_flags & FHD_SALT)+  {+    if (p + 8 > endp) {+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+        "Invalid header size");+      return (ARCHIVE_FATAL);+    }+    memcpy(rar->salt, p, 8);+    p += 8;+  }++  if (rar->file_flags & FHD_EXTTIME) {+    if (read_exttime(p, rar, endp) < 0) {+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+        "Invalid header size");+      return (ARCHIVE_FATAL);+    }+  }++  __archive_read_consume(a, header_size - 7);+  rar->dbo[0].start_offset = a->filter->position;+  rar->dbo[0].end_offset = rar->dbo[0].start_offset + rar->packed_size;++  switch(file_header.host_os)+  {+  case OS_MSDOS:+  case OS_OS2:+  case OS_WIN32:+    rar->mode = archive_le32dec(file_header.file_attr);+    if (rar->mode & FILE_ATTRIBUTE_DIRECTORY)+      rar->mode = AE_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;+    else+      rar->mode = AE_IFREG;+    rar->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;+    break;++  case OS_UNIX:+  case OS_MAC_OS:+  case OS_BEOS:+    rar->mode = archive_le32dec(file_header.file_attr);+    break;++  default:+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Unknown file attributes from RAR file's host OS");+    return (ARCHIVE_FATAL);+  }++  rar->bytes_uncopied = rar->bytes_unconsumed = 0;+  rar->lzss.position = rar->offset = 0;+  rar->offset_seek = 0;+  rar->dictionary_size = 0;+  rar->offset_outgoing = 0;+  rar->br.cache_avail = 0;+  rar->br.avail_in = 0;+  rar->crc_calculated = 0;+  rar->entry_eof = 0;+  rar->valid = 1;+  rar->is_ppmd_block = 0;+  rar->start_new_table = 1;+  free(rar->unp_buffer);+  rar->unp_buffer = NULL;+  rar->unp_offset = 0;+  rar->unp_buffer_size = UNP_BUFFER_SIZE;+  memset(rar->lengthtable, 0, sizeof(rar->lengthtable));+  __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);+  rar->ppmd_valid = rar->ppmd_eod = 0;++  /* Don't set any archive entries for non-file header types */+  if (head_type == NEWSUB_HEAD)+    return ret;++  archive_entry_set_mtime(entry, rar->mtime, rar->mnsec);+  archive_entry_set_ctime(entry, rar->ctime, rar->cnsec);+  archive_entry_set_atime(entry, rar->atime, rar->ansec);+  archive_entry_set_size(entry, rar->unp_size);+  archive_entry_set_mode(entry, rar->mode);++  if (archive_entry_copy_pathname_l(entry, filename, filename_size, fn_sconv))+  {+    if (errno == ENOMEM)+    {+      archive_set_error(&a->archive, ENOMEM,+                        "Can't allocate memory for Pathname");+      return (ARCHIVE_FATAL);+    }+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Pathname cannot be converted from %s to current locale.",+                      archive_string_conversion_charset_name(fn_sconv));+    ret = (ARCHIVE_WARN);+  }++  if (((rar->mode) & AE_IFMT) == AE_IFLNK)+  {+    /* Make sure a symbolic-link file does not have its body. */+    rar->bytes_remaining = 0;+    archive_entry_set_size(entry, 0);++    /* Read a symbolic-link name. */+    if ((ret2 = read_symlink_stored(a, entry, sconv)) < (ARCHIVE_WARN))+      return ret2;+    if (ret > ret2)+      ret = ret2;+  }++  if (rar->bytes_remaining == 0)+    rar->entry_eof = 1;++  return ret;+}++static time_t+get_time(int ttime)+{+  struct tm tm;+  tm.tm_sec = 2 * (ttime & 0x1f);+  tm.tm_min = (ttime >> 5) & 0x3f;+  tm.tm_hour = (ttime >> 11) & 0x1f;+  tm.tm_mday = (ttime >> 16) & 0x1f;+  tm.tm_mon = ((ttime >> 21) & 0x0f) - 1;+  tm.tm_year = ((ttime >> 25) & 0x7f) + 80;+  tm.tm_isdst = -1;+  return mktime(&tm);+}++static int+read_exttime(const char *p, struct rar *rar, const char *endp)+{+  unsigned rmode, flags, rem, j, count;+  int ttime, i;+  struct tm *tm;+  time_t t;+  long nsec;+#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)+  struct tm tmbuf;+#endif+#if defined(HAVE__LOCALTIME64_S)+  errno_t terr;+  __time64_t tmptime;+#endif++  if (p + 2 > endp)+    return (-1);+  flags = archive_le16dec(p);+  p += 2;++  for (i = 3; i >= 0; i--)+  {+    t = 0;+    if (i == 3)+      t = rar->mtime;+    rmode = flags >> i * 4;+    if (rmode & 8)+    {+      if (!t)+      {+        if (p + 4 > endp)+          return (-1);+        ttime = archive_le32dec(p);+        t = get_time(ttime);+        p += 4;+      }+      rem = 0;+      count = rmode & 3;+      if (p + count > endp)+        return (-1);+      for (j = 0; j < count; j++)+      {+        rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8);+        p++;+      }+#if defined(HAVE_LOCALTIME_R)+      tm = localtime_r(&t, &tmbuf);+#elif defined(HAVE__LOCALTIME64_S)+      tmptime = t;+      terr = _localtime64_s(&tmbuf, &tmptime);+      if (terr)+        tm = NULL;+      else+        tm = &tmbuf;+#else+      tm = localtime(&t);+#endif+      nsec = tm->tm_sec + rem / NS_UNIT;+      if (rmode & 4)+      {+        tm->tm_sec++;+        t = mktime(tm);+      }+      if (i == 3)+      {+        rar->mtime = t;+        rar->mnsec = nsec;+      }+      else if (i == 2)+      {+        rar->ctime = t;+        rar->cnsec = nsec;+      }+      else if (i == 1)+      {+        rar->atime = t;+        rar->ansec = nsec;+      }+      else+      {+        rar->arctime = t;+        rar->arcnsec = nsec;+      }+    }+  }+  return (0);+}++static int+read_symlink_stored(struct archive_read *a, struct archive_entry *entry,+                    struct archive_string_conv *sconv)+{+  const void *h;+  const char *p;+  struct rar *rar;+  int ret = (ARCHIVE_OK);++  rar = (struct rar *)(a->format->data);+  if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL)+    return (ARCHIVE_FATAL);+  p = h;++  if (archive_entry_copy_symlink_l(entry,+      p, (size_t)rar->packed_size, sconv))+  {+    if (errno == ENOMEM)+    {+      archive_set_error(&a->archive, ENOMEM,+                        "Can't allocate memory for link");+      return (ARCHIVE_FATAL);+    }+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "link cannot be converted from %s to current locale.",+                      archive_string_conversion_charset_name(sconv));+    ret = (ARCHIVE_WARN);+  }+  __archive_read_consume(a, rar->packed_size);+  return ret;+}++static int+read_data_stored(struct archive_read *a, const void **buff, size_t *size,+                 int64_t *offset)+{+  struct rar *rar;+  ssize_t bytes_avail;++  rar = (struct rar *)(a->format->data);+  if (rar->bytes_remaining == 0 &&+    !(rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER))+  {+    *buff = NULL;+    *size = 0;+    *offset = rar->offset;+    if (rar->file_crc != rar->crc_calculated) {+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                        "File CRC error");+      return (ARCHIVE_FATAL);+    }+    rar->entry_eof = 1;+    return (ARCHIVE_EOF);+  }++  *buff = rar_read_ahead(a, 1, &bytes_avail);+  if (bytes_avail <= 0)+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Truncated RAR file data");+    return (ARCHIVE_FATAL);+  }++  *size = bytes_avail;+  *offset = rar->offset;+  rar->offset += bytes_avail;+  rar->offset_seek += bytes_avail;+  rar->bytes_remaining -= bytes_avail;+  rar->bytes_unconsumed = bytes_avail;+  /* Calculate File CRC. */+  rar->crc_calculated = crc32(rar->crc_calculated, *buff,+    (unsigned)bytes_avail);+  return (ARCHIVE_OK);+}++static int+read_data_compressed(struct archive_read *a, const void **buff, size_t *size,+               int64_t *offset)+{+  struct rar *rar;+  int64_t start, end, actualend;+  size_t bs;+  int ret = (ARCHIVE_OK), sym, code, lzss_offset, length, i;++  rar = (struct rar *)(a->format->data);++  do {+    if (!rar->valid)+      return (ARCHIVE_FATAL);+    if (rar->ppmd_eod ||+       (rar->dictionary_size && rar->offset >= rar->unp_size))+    {+      if (rar->unp_offset > 0) {+        /*+         * We have unprocessed extracted data. write it out.+         */+        *buff = rar->unp_buffer;+        *size = rar->unp_offset;+        *offset = rar->offset_outgoing;+        rar->offset_outgoing += *size;+        /* Calculate File CRC. */+        rar->crc_calculated = crc32(rar->crc_calculated, *buff,+          (unsigned)*size);+        rar->unp_offset = 0;+        return (ARCHIVE_OK);+      }+      *buff = NULL;+      *size = 0;+      *offset = rar->offset;+      if (rar->file_crc != rar->crc_calculated) {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                          "File CRC error");+        return (ARCHIVE_FATAL);+      }+      rar->entry_eof = 1;+      return (ARCHIVE_EOF);+    }++    if (!rar->is_ppmd_block && rar->dictionary_size && rar->bytes_uncopied > 0)+    {+      if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset))+        bs = rar->unp_buffer_size - rar->unp_offset;+      else+        bs = (size_t)rar->bytes_uncopied;+      ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs);+      if (ret != ARCHIVE_OK)+        return (ret);+      rar->offset += bs;+      rar->bytes_uncopied -= bs;+      if (*buff != NULL) {+        rar->unp_offset = 0;+        *size = rar->unp_buffer_size;+        *offset = rar->offset_outgoing;+        rar->offset_outgoing += *size;+        /* Calculate File CRC. */+        rar->crc_calculated = crc32(rar->crc_calculated, *buff,+          (unsigned)*size);+        return (ret);+      }+      continue;+    }++    if (!rar->br.next_in &&+      (ret = rar_br_preparation(a, &(rar->br))) < ARCHIVE_WARN)+      return (ret);+    if (rar->start_new_table && ((ret = parse_codes(a)) < (ARCHIVE_WARN)))+      return (ret);++    if (rar->is_ppmd_block)+    {+      if ((sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(+        &rar->ppmd7_context, &rar->range_dec.p)) < 0)+      {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                          "Invalid symbol");+        return (ARCHIVE_FATAL);+      }+      if(sym != rar->ppmd_escape)+      {+        lzss_emit_literal(rar, sym);+        rar->bytes_uncopied++;+      }+      else+      {+        if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(+          &rar->ppmd7_context, &rar->range_dec.p)) < 0)+        {+          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                            "Invalid symbol");+          return (ARCHIVE_FATAL);+        }++        switch(code)+        {+          case 0:+            rar->start_new_table = 1;+            return read_data_compressed(a, buff, size, offset);++          case 2:+            rar->ppmd_eod = 1;/* End Of ppmd Data. */+            continue;++          case 3:+            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+                              "Parsing filters is unsupported.");+            return (ARCHIVE_FAILED);++          case 4:+            lzss_offset = 0;+            for (i = 2; i >= 0; i--)+            {+              if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(+                &rar->ppmd7_context, &rar->range_dec.p)) < 0)+              {+                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                                  "Invalid symbol");+                return (ARCHIVE_FATAL);+              }+              lzss_offset |= code << (i * 8);+            }+            if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(+              &rar->ppmd7_context, &rar->range_dec.p)) < 0)+            {+              archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                                "Invalid symbol");+              return (ARCHIVE_FATAL);+            }+            lzss_emit_match(rar, lzss_offset + 2, length + 32);+            rar->bytes_uncopied += length + 32;+            break;++          case 5:+            if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(+              &rar->ppmd7_context, &rar->range_dec.p)) < 0)+            {+              archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                                "Invalid symbol");+              return (ARCHIVE_FATAL);+            }+            lzss_emit_match(rar, 1, length + 4);+            rar->bytes_uncopied += length + 4;+            break;++         default:+           lzss_emit_literal(rar, sym);+           rar->bytes_uncopied++;+        }+      }+    }+    else+    {+      start = rar->offset;+      end = start + rar->dictionary_size;+      rar->filterstart = INT64_MAX;++      if ((actualend = expand(a, end)) < 0)+        return ((int)actualend);++      rar->bytes_uncopied = actualend - start;+      if (rar->bytes_uncopied == 0) {+          /* Broken RAR files cause this case.+          * NOTE: If this case were possible on a normal RAR file+          * we would find out where it was actually bad and+          * what we would do to solve it. */+          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                            "Internal error extracting RAR file");+          return (ARCHIVE_FATAL);+      }+    }+    if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset))+      bs = rar->unp_buffer_size - rar->unp_offset;+    else+      bs = (size_t)rar->bytes_uncopied;+    ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs);+    if (ret != ARCHIVE_OK)+      return (ret);+    rar->offset += bs;+    rar->bytes_uncopied -= bs;+    /*+     * If *buff is NULL, it means unp_buffer is not full.+     * So we have to continue extracting a RAR file.+     */+  } while (*buff == NULL);++  rar->unp_offset = 0;+  *size = rar->unp_buffer_size;+  *offset = rar->offset_outgoing;+  rar->offset_outgoing += *size;+  /* Calculate File CRC. */+  rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size);+  return ret;+}++static int+parse_codes(struct archive_read *a)+{+  int i, j, val, n, r;+  unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags;+  unsigned int maxorder;+  struct huffman_code precode;+  struct rar *rar = (struct rar *)(a->format->data);+  struct rar_br *br = &(rar->br);++  free_codes(a);++  /* Skip to the next byte */+  rar_br_consume_unalined_bits(br);++  /* PPMd block flag */+  if (!rar_br_read_ahead(a, br, 1))+    goto truncated_data;+  if ((rar->is_ppmd_block = rar_br_bits(br, 1)) != 0)+  {+    rar_br_consume(br, 1);+    if (!rar_br_read_ahead(a, br, 7))+      goto truncated_data;+    ppmd_flags = rar_br_bits(br, 7);+    rar_br_consume(br, 7);++    /* Memory is allocated in MB */+    if (ppmd_flags & 0x20)+    {+      if (!rar_br_read_ahead(a, br, 8))+        goto truncated_data;+      rar->dictionary_size = (rar_br_bits(br, 8) + 1) << 20;+      rar_br_consume(br, 8);+    }++    if (ppmd_flags & 0x40)+    {+      if (!rar_br_read_ahead(a, br, 8))+        goto truncated_data;+      rar->ppmd_escape = rar->ppmd7_context.InitEsc = rar_br_bits(br, 8);+      rar_br_consume(br, 8);+    }+    else+      rar->ppmd_escape = 2;++    if (ppmd_flags & 0x20)+    {+      maxorder = (ppmd_flags & 0x1F) + 1;+      if(maxorder > 16)+        maxorder = 16 + (maxorder - 16) * 3;++      if (maxorder == 1)+      {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                          "Truncated RAR file data");+        return (ARCHIVE_FATAL);+      }++      /* Make sure ppmd7_contest is freed before Ppmd7_Construct+       * because reading a broken file cause this abnormal sequence. */+      __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);++      rar->bytein.a = a;+      rar->bytein.Read = &ppmd_read;+      __archive_ppmd7_functions.PpmdRAR_RangeDec_CreateVTable(&rar->range_dec);+      rar->range_dec.Stream = &rar->bytein;+      __archive_ppmd7_functions.Ppmd7_Construct(&rar->ppmd7_context);++      if (rar->dictionary_size == 0) {+	      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                          "Invalid zero dictionary size");+	      return (ARCHIVE_FATAL);+      }++      if (!__archive_ppmd7_functions.Ppmd7_Alloc(&rar->ppmd7_context,+        rar->dictionary_size))+      {+        archive_set_error(&a->archive, ENOMEM,+                          "Out of memory");+        return (ARCHIVE_FATAL);+      }+      if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec))+      {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                          "Unable to initialize PPMd range decoder");+        return (ARCHIVE_FATAL);+      }+      __archive_ppmd7_functions.Ppmd7_Init(&rar->ppmd7_context, maxorder);+      rar->ppmd_valid = 1;+    }+    else+    {+      if (!rar->ppmd_valid) {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                          "Invalid PPMd sequence");+        return (ARCHIVE_FATAL);+      }+      if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec))+      {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                          "Unable to initialize PPMd range decoder");+        return (ARCHIVE_FATAL);+      }+    }+  }+  else+  {+    rar_br_consume(br, 1);++    /* Keep existing table flag */+    if (!rar_br_read_ahead(a, br, 1))+      goto truncated_data;+    if (!rar_br_bits(br, 1))+      memset(rar->lengthtable, 0, sizeof(rar->lengthtable));+    rar_br_consume(br, 1);++    memset(&bitlengths, 0, sizeof(bitlengths));+    for (i = 0; i < MAX_SYMBOLS;)+    {+      if (!rar_br_read_ahead(a, br, 4))+        goto truncated_data;+      bitlengths[i++] = rar_br_bits(br, 4);+      rar_br_consume(br, 4);+      if (bitlengths[i-1] == 0xF)+      {+        if (!rar_br_read_ahead(a, br, 4))+          goto truncated_data;+        zerocount = rar_br_bits(br, 4);+        rar_br_consume(br, 4);+        if (zerocount)+        {+          i--;+          for (j = 0; j < zerocount + 2 && i < MAX_SYMBOLS; j++)+            bitlengths[i++] = 0;+        }+      }+    }++    memset(&precode, 0, sizeof(precode));+    r = create_code(a, &precode, bitlengths, MAX_SYMBOLS, MAX_SYMBOL_LENGTH);+    if (r != ARCHIVE_OK) {+      free(precode.tree);+      free(precode.table);+      return (r);+    }++    for (i = 0; i < HUFFMAN_TABLE_SIZE;)+    {+      if ((val = read_next_symbol(a, &precode)) < 0) {+        free(precode.tree);+        free(precode.table);+        return (ARCHIVE_FATAL);+      }+      if (val < 16)+      {+        rar->lengthtable[i] = (rar->lengthtable[i] + val) & 0xF;+        i++;+      }+      else if (val < 18)+      {+        if (i == 0)+        {+          free(precode.tree);+          free(precode.table);+          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                            "Internal error extracting RAR file.");+          return (ARCHIVE_FATAL);+        }++        if(val == 16) {+          if (!rar_br_read_ahead(a, br, 3)) {+            free(precode.tree);+            free(precode.table);+            goto truncated_data;+          }+          n = rar_br_bits(br, 3) + 3;+          rar_br_consume(br, 3);+        } else {+          if (!rar_br_read_ahead(a, br, 7)) {+            free(precode.tree);+            free(precode.table);+            goto truncated_data;+          }+          n = rar_br_bits(br, 7) + 11;+          rar_br_consume(br, 7);+        }++        for (j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++)+        {+          rar->lengthtable[i] = rar->lengthtable[i-1];+          i++;+        }+      }+      else+      {+        if(val == 18) {+          if (!rar_br_read_ahead(a, br, 3)) {+            free(precode.tree);+            free(precode.table);+            goto truncated_data;+          }+          n = rar_br_bits(br, 3) + 3;+          rar_br_consume(br, 3);+        } else {+          if (!rar_br_read_ahead(a, br, 7)) {+            free(precode.tree);+            free(precode.table);+            goto truncated_data;+          }+          n = rar_br_bits(br, 7) + 11;+          rar_br_consume(br, 7);+        }++        for(j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++)+          rar->lengthtable[i++] = 0;+      }+    }+    free(precode.tree);+    free(precode.table);++    r = create_code(a, &rar->maincode, &rar->lengthtable[0], MAINCODE_SIZE,+                MAX_SYMBOL_LENGTH);+    if (r != ARCHIVE_OK)+      return (r);+    r = create_code(a, &rar->offsetcode, &rar->lengthtable[MAINCODE_SIZE],+                OFFSETCODE_SIZE, MAX_SYMBOL_LENGTH);+    if (r != ARCHIVE_OK)+      return (r);+    r = create_code(a, &rar->lowoffsetcode,+                &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE],+                LOWOFFSETCODE_SIZE, MAX_SYMBOL_LENGTH);+    if (r != ARCHIVE_OK)+      return (r);+    r = create_code(a, &rar->lengthcode,+                &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE ++                LOWOFFSETCODE_SIZE], LENGTHCODE_SIZE, MAX_SYMBOL_LENGTH);+    if (r != ARCHIVE_OK)+      return (r);+  }++  if (!rar->dictionary_size || !rar->lzss.window)+  {+    /* Seems as though dictionary sizes are not used. Even so, minimize+     * memory usage as much as possible.+     */+    void *new_window;+    unsigned int new_size;++    if (rar->unp_size >= DICTIONARY_MAX_SIZE)+      new_size = DICTIONARY_MAX_SIZE;+    else+      new_size = rar_fls((unsigned int)rar->unp_size) << 1;+    if (new_size == 0) {+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                        "Zero window size is invalid.");+      return (ARCHIVE_FATAL);+    }+    new_window = realloc(rar->lzss.window, new_size);+    if (new_window == NULL) {+      archive_set_error(&a->archive, ENOMEM,+                        "Unable to allocate memory for uncompressed data.");+      return (ARCHIVE_FATAL);+    }+    rar->lzss.window = (unsigned char *)new_window;+    rar->dictionary_size = new_size;+    memset(rar->lzss.window, 0, rar->dictionary_size);+    rar->lzss.mask = rar->dictionary_size - 1;+  }++  rar->start_new_table = 0;+  return (ARCHIVE_OK);+truncated_data:+  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                    "Truncated RAR file data");+  rar->valid = 0;+  return (ARCHIVE_FATAL);+}++static void+free_codes(struct archive_read *a)+{+  struct rar *rar = (struct rar *)(a->format->data);+  free(rar->maincode.tree);+  free(rar->offsetcode.tree);+  free(rar->lowoffsetcode.tree);+  free(rar->lengthcode.tree);+  free(rar->maincode.table);+  free(rar->offsetcode.table);+  free(rar->lowoffsetcode.table);+  free(rar->lengthcode.table);+  memset(&rar->maincode, 0, sizeof(rar->maincode));+  memset(&rar->offsetcode, 0, sizeof(rar->offsetcode));+  memset(&rar->lowoffsetcode, 0, sizeof(rar->lowoffsetcode));+  memset(&rar->lengthcode, 0, sizeof(rar->lengthcode));+}+++static int+read_next_symbol(struct archive_read *a, struct huffman_code *code)+{+  unsigned char bit;+  unsigned int bits;+  int length, value, node;+  struct rar *rar;+  struct rar_br *br;++  if (!code->table)+  {+    if (make_table(a, code) != (ARCHIVE_OK))+      return -1;+  }++  rar = (struct rar *)(a->format->data);+  br = &(rar->br);++  /* Look ahead (peek) at bits */+  if (!rar_br_read_ahead(a, br, code->tablesize)) {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Truncated RAR file data");+    rar->valid = 0;+    return -1;+  }+  bits = rar_br_bits(br, code->tablesize);++  length = code->table[bits].length;+  value = code->table[bits].value;++  if (length < 0)+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Invalid prefix code in bitstream");+    return -1;+  }++  if (length <= code->tablesize)+  {+    /* Skip length bits */+    rar_br_consume(br, length);+    return value;+  }++  /* Skip tablesize bits */+  rar_br_consume(br, code->tablesize);++  node = value;+  while (!(code->tree[node].branches[0] ==+    code->tree[node].branches[1]))+  {+    if (!rar_br_read_ahead(a, br, 1)) {+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                        "Truncated RAR file data");+      rar->valid = 0;+      return -1;+    }+    bit = rar_br_bits(br, 1);+    rar_br_consume(br, 1);++    if (code->tree[node].branches[bit] < 0)+    {+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                        "Invalid prefix code in bitstream");+      return -1;+    }+    node = code->tree[node].branches[bit];+  }++  return code->tree[node].branches[0];+}++static int+create_code(struct archive_read *a, struct huffman_code *code,+            unsigned char *lengths, int numsymbols, char maxlength)+{+  int i, j, codebits = 0, symbolsleft = numsymbols;++  code->numentries = 0;+  code->numallocatedentries = 0;+  if (new_node(code) < 0) {+    archive_set_error(&a->archive, ENOMEM,+                      "Unable to allocate memory for node data.");+    return (ARCHIVE_FATAL);+  }+  code->numentries = 1;+  code->minlength = INT_MAX;+  code->maxlength = INT_MIN;+  codebits = 0;+  for(i = 1; i <= maxlength; i++)+  {+    for(j = 0; j < numsymbols; j++)+    {+      if (lengths[j] != i) continue;+      if (add_value(a, code, j, codebits, i) != ARCHIVE_OK)+        return (ARCHIVE_FATAL);+      codebits++;+      if (--symbolsleft <= 0)+        break;+    }+    if (symbolsleft <= 0)+      break;+    codebits <<= 1;+  }+  return (ARCHIVE_OK);+}++static int+add_value(struct archive_read *a, struct huffman_code *code, int value,+          int codebits, int length)+{+  int lastnode, bitpos, bit;+  /* int repeatpos, repeatnode, nextnode; */++  free(code->table);+  code->table = NULL;++  if(length > code->maxlength)+    code->maxlength = length;+  if(length < code->minlength)+    code->minlength = length;++  /*+   * Dead code, repeatpos was is -1+   *+  repeatpos = -1;+  if (repeatpos == 0 || (repeatpos >= 0+    && (((codebits >> (repeatpos - 1)) & 3) == 0+    || ((codebits >> (repeatpos - 1)) & 3) == 3)))+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Invalid repeat position");+    return (ARCHIVE_FATAL);+  }+  */++  lastnode = 0;+  for (bitpos = length - 1; bitpos >= 0; bitpos--)+  {+    bit = (codebits >> bitpos) & 1;++    /* Leaf node check */+    if (code->tree[lastnode].branches[0] ==+      code->tree[lastnode].branches[1])+    {+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                        "Prefix found");+      return (ARCHIVE_FATAL);+    }++    /*+     * Dead code, repeatpos was -1, bitpos >=0+     *+    if (bitpos == repeatpos)+    {+      * Open branch check *+      if (!(code->tree[lastnode].branches[bit] < 0))+      {+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                          "Invalid repeating code");+        return (ARCHIVE_FATAL);+      }++      if ((repeatnode = new_node(code)) < 0) {+        archive_set_error(&a->archive, ENOMEM,+                          "Unable to allocate memory for node data.");+        return (ARCHIVE_FATAL);+      }+      if ((nextnode = new_node(code)) < 0) {+        archive_set_error(&a->archive, ENOMEM,+                          "Unable to allocate memory for node data.");+        return (ARCHIVE_FATAL);+      }++      * Set branches *+      code->tree[lastnode].branches[bit] = repeatnode;+      code->tree[repeatnode].branches[bit] = repeatnode;+      code->tree[repeatnode].branches[bit^1] = nextnode;+      lastnode = nextnode;++      bitpos++; * terminating bit already handled, skip it *+    }+    else+    {+    */+      /* Open branch check */+      if (code->tree[lastnode].branches[bit] < 0)+      {+        if (new_node(code) < 0) {+          archive_set_error(&a->archive, ENOMEM,+                            "Unable to allocate memory for node data.");+          return (ARCHIVE_FATAL);+        }+        code->tree[lastnode].branches[bit] = code->numentries++;+      }++      /* set to branch */+      lastnode = code->tree[lastnode].branches[bit];+ /* } */+  }++  if (!(code->tree[lastnode].branches[0] == -1+    && code->tree[lastnode].branches[1] == -2))+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Prefix found");+    return (ARCHIVE_FATAL);+  }++  /* Set leaf value */+  code->tree[lastnode].branches[0] = value;+  code->tree[lastnode].branches[1] = value;++  return (ARCHIVE_OK);+}++static int+new_node(struct huffman_code *code)+{+  void *new_tree;+  if (code->numallocatedentries == code->numentries) {+    int new_num_entries = 256;+    if (code->numentries > 0) {+        new_num_entries = code->numentries * 2;+    }+    new_tree = realloc(code->tree, new_num_entries * sizeof(*code->tree));+    if (new_tree == NULL)+        return (-1);+    code->tree = (struct huffman_tree_node *)new_tree;+    code->numallocatedentries = new_num_entries;+  }+  code->tree[code->numentries].branches[0] = -1;+  code->tree[code->numentries].branches[1] = -2;+  return 1;+}++static int+make_table(struct archive_read *a, struct huffman_code *code)+{+  if (code->maxlength < code->minlength || code->maxlength > 10)+    code->tablesize = 10;+  else+    code->tablesize = code->maxlength;++  code->table =+    (struct huffman_table_entry *)calloc(1, sizeof(*code->table)+    * ((size_t)1 << code->tablesize));++  return make_table_recurse(a, code, 0, code->table, 0, code->tablesize);+}++static int+make_table_recurse(struct archive_read *a, struct huffman_code *code, int node,+                   struct huffman_table_entry *table, int depth,+                   int maxdepth)+{+  int currtablesize, i, ret = (ARCHIVE_OK);++  if (!code->tree)+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Huffman tree was not created.");+    return (ARCHIVE_FATAL);+  }+  if (node < 0 || node >= code->numentries)+  {+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                      "Invalid location to Huffman tree specified.");+    return (ARCHIVE_FATAL);+  }++  currtablesize = 1 << (maxdepth - depth);++  if (code->tree[node].branches[0] ==+    code->tree[node].branches[1])+  {+    for(i = 0; i < currtablesize; i++)+    {+      table[i].length = depth;+      table[i].value = code->tree[node].branches[0];+    }+  }+  /*+   * Dead code, node >= 0+   *+  else if (node < 0)+  {+    for(i = 0; i < currtablesize; i++)+      table[i].length = -1;+  }+   */+  else+  {+    if(depth == maxdepth)+    {+      table[0].length = maxdepth + 1;+      table[0].value = node;+    }+    else+    {+      ret |= make_table_recurse(a, code, code->tree[node].branches[0], table,+                                depth + 1, maxdepth);+      ret |= make_table_recurse(a, code, code->tree[node].branches[1],+                         table + currtablesize / 2, depth + 1, maxdepth);+    }+  }+  return ret;+}++static int64_t+expand(struct archive_read *a, int64_t end)+{+  static const unsigned char lengthbases[] =+    {   0,   1,   2,   3,   4,   5,   6,+        7,   8,  10,  12,  14,  16,  20,+       24,  28,  32,  40,  48,  56,  64,+       80,  96, 112, 128, 160, 192, 224 };+  static const unsigned char lengthbits[] =+    { 0, 0, 0, 0, 0, 0, 0,+      0, 1, 1, 1, 1, 2, 2,+      2, 2, 3, 3, 3, 3, 4,+      4, 4, 4, 5, 5, 5, 5 };+  static const int lengthb_min = minimum(+    (int)(sizeof(lengthbases)/sizeof(lengthbases[0])),+    (int)(sizeof(lengthbits)/sizeof(lengthbits[0]))+  );+  static const unsigned int offsetbases[] =+    {       0,       1,       2,       3,       4,       6,+            8,      12,      16,      24,      32,      48,+           64,      96,     128,     192,     256,     384,+          512,     768,    1024,    1536,    2048,    3072,+         4096,    6144,    8192,   12288,   16384,   24576,+        32768,   49152,   65536,   98304,  131072,  196608,+       262144,  327680,  393216,  458752,  524288,  589824,+       655360,  720896,  786432,  851968,  917504,  983040,+      1048576, 1310720, 1572864, 1835008, 2097152, 2359296,+      2621440, 2883584, 3145728, 3407872, 3670016, 3932160 };+  static const unsigned char offsetbits[] =+    {  0,  0,  0,  0,  1,  1,  2,  2,  3,  3,  4,  4,+       5,  5,  6,  6,  7,  7,  8,  8,  9,  9, 10, 10,+      11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,+      16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,+      18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 };+  static const int offsetb_min = minimum(+    (int)(sizeof(offsetbases)/sizeof(offsetbases[0])),+    (int)(sizeof(offsetbits)/sizeof(offsetbits[0]))+  );+  static const unsigned char shortbases[] =+    { 0, 4, 8, 16, 32, 64, 128, 192 };+  static const unsigned char shortbits[] =+    { 2, 2, 3, 4, 5, 6, 6, 6 };++  int symbol, offs, len, offsindex, lensymbol, i, offssymbol, lowoffsetsymbol;+  unsigned char newfile;+  struct rar *rar = (struct rar *)(a->format->data);+  struct rar_br *br = &(rar->br);++  if (rar->filterstart < end)+    end = rar->filterstart;++  while (1)+  {+    if (rar->output_last_match &&+      lzss_position(&rar->lzss) + rar->lastlength <= end)+    {+      lzss_emit_match(rar, rar->lastoffset, rar->lastlength);+      rar->output_last_match = 0;+    }++    if(rar->is_ppmd_block || rar->output_last_match ||+      lzss_position(&rar->lzss) >= end)+      return lzss_position(&rar->lzss);++    if ((symbol = read_next_symbol(a, &rar->maincode)) < 0)+      return (ARCHIVE_FATAL);+    rar->output_last_match = 0;++    if (symbol < 256)+    {+      lzss_emit_literal(rar, symbol);+      continue;+    }+    else if (symbol == 256)+    {+      if (!rar_br_read_ahead(a, br, 1))+        goto truncated_data;+      newfile = !rar_br_bits(br, 1);+      rar_br_consume(br, 1);++      if(newfile)+      {+        rar->start_new_block = 1;+        if (!rar_br_read_ahead(a, br, 1))+          goto truncated_data;+        rar->start_new_table = rar_br_bits(br, 1);+        rar_br_consume(br, 1);+        return lzss_position(&rar->lzss);+      }+      else+      {+        if (parse_codes(a) != ARCHIVE_OK)+          return (ARCHIVE_FATAL);+        continue;+      }+    }+    else if(symbol==257)+    {+      archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+                        "Parsing filters is unsupported.");+      return (ARCHIVE_FAILED);+    }+    else if(symbol==258)+    {+      if(rar->lastlength == 0)+        continue;++      offs = rar->lastoffset;+      len = rar->lastlength;+    }+    else if (symbol <= 262)+    {+      offsindex = symbol - 259;+      offs = rar->oldoffset[offsindex];++      if ((lensymbol = read_next_symbol(a, &rar->lengthcode)) < 0)+        goto bad_data;+      if (lensymbol > lengthb_min)+        goto bad_data;+      len = lengthbases[lensymbol] + 2;+      if (lengthbits[lensymbol] > 0) {+        if (!rar_br_read_ahead(a, br, lengthbits[lensymbol]))+          goto truncated_data;+        len += rar_br_bits(br, lengthbits[lensymbol]);+        rar_br_consume(br, lengthbits[lensymbol]);+      }++      for (i = offsindex; i > 0; i--)+        rar->oldoffset[i] = rar->oldoffset[i-1];+      rar->oldoffset[0] = offs;+    }+    else if(symbol<=270)+    {+      offs = shortbases[symbol-263] + 1;+      if(shortbits[symbol-263] > 0) {+        if (!rar_br_read_ahead(a, br, shortbits[symbol-263]))+          goto truncated_data;+        offs += rar_br_bits(br, shortbits[symbol-263]);+        rar_br_consume(br, shortbits[symbol-263]);+      }++      len = 2;++      for(i = 3; i > 0; i--)+        rar->oldoffset[i] = rar->oldoffset[i-1];+      rar->oldoffset[0] = offs;+    }+    else+    {+      if (symbol-271 > lengthb_min)+        goto bad_data;+      len = lengthbases[symbol-271]+3;+      if(lengthbits[symbol-271] > 0) {+        if (!rar_br_read_ahead(a, br, lengthbits[symbol-271]))+          goto truncated_data;+        len += rar_br_bits(br, lengthbits[symbol-271]);+        rar_br_consume(br, lengthbits[symbol-271]);+      }++      if ((offssymbol = read_next_symbol(a, &rar->offsetcode)) < 0)+        goto bad_data;+      if (offssymbol > offsetb_min)+        goto bad_data;+      offs = offsetbases[offssymbol]+1;+      if(offsetbits[offssymbol] > 0)+      {+        if(offssymbol > 9)+        {+          if(offsetbits[offssymbol] > 4) {+            if (!rar_br_read_ahead(a, br, offsetbits[offssymbol] - 4))+              goto truncated_data;+            offs += rar_br_bits(br, offsetbits[offssymbol] - 4) << 4;+            rar_br_consume(br, offsetbits[offssymbol] - 4);+	  }++          if(rar->numlowoffsetrepeats > 0)+          {+            rar->numlowoffsetrepeats--;+            offs += rar->lastlowoffset;+          }+          else+          {+            if ((lowoffsetsymbol =+              read_next_symbol(a, &rar->lowoffsetcode)) < 0)+              return (ARCHIVE_FATAL);+            if(lowoffsetsymbol == 16)+            {+              rar->numlowoffsetrepeats = 15;+              offs += rar->lastlowoffset;+            }+            else+            {+              offs += lowoffsetsymbol;+              rar->lastlowoffset = lowoffsetsymbol;+            }+          }+        }+        else {+          if (!rar_br_read_ahead(a, br, offsetbits[offssymbol]))+            goto truncated_data;+          offs += rar_br_bits(br, offsetbits[offssymbol]);+          rar_br_consume(br, offsetbits[offssymbol]);+        }+      }++      if (offs >= 0x40000)+        len++;+      if (offs >= 0x2000)+        len++;++      for(i = 3; i > 0; i--)+        rar->oldoffset[i] = rar->oldoffset[i-1];+      rar->oldoffset[0] = offs;+    }++    rar->lastoffset = offs;+    rar->lastlength = len;+    rar->output_last_match = 1;+  }+truncated_data:+  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                    "Truncated RAR file data");+  rar->valid = 0;+  return (ARCHIVE_FATAL);+bad_data:+  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                    "Bad RAR file data");+  return (ARCHIVE_FATAL);+}++static int+copy_from_lzss_window(struct archive_read *a, const void **buffer,+                        int64_t startpos, int length)+{+  int windowoffs, firstpart;+  struct rar *rar = (struct rar *)(a->format->data);++  if (!rar->unp_buffer)+  {+    if ((rar->unp_buffer = malloc(rar->unp_buffer_size)) == NULL)+    {+      archive_set_error(&a->archive, ENOMEM,+                        "Unable to allocate memory for uncompressed data.");+      return (ARCHIVE_FATAL);+    }+  }++  windowoffs = lzss_offset_for_position(&rar->lzss, startpos);+  if(windowoffs + length <= lzss_size(&rar->lzss)) {+    memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs],+           length);+  } else if (length <= lzss_size(&rar->lzss)) {+    firstpart = lzss_size(&rar->lzss) - windowoffs;+    if (firstpart < 0) {+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                        "Bad RAR file data");+      return (ARCHIVE_FATAL);+    }+    if (firstpart < length) {+      memcpy(&rar->unp_buffer[rar->unp_offset],+             &rar->lzss.window[windowoffs], firstpart);+      memcpy(&rar->unp_buffer[rar->unp_offset + firstpart],+             &rar->lzss.window[0], length - firstpart);+    } else {+      memcpy(&rar->unp_buffer[rar->unp_offset],+             &rar->lzss.window[windowoffs], length);+    }+  } else {+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+                        "Bad RAR file data");+      return (ARCHIVE_FATAL);+  }+  rar->unp_offset += length;+  if (rar->unp_offset >= rar->unp_buffer_size)+    *buffer = rar->unp_buffer;+  else+    *buffer = NULL;+  return (ARCHIVE_OK);+}++static const void *+rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)+{+  struct rar *rar = (struct rar *)(a->format->data);+  const void *h = __archive_read_ahead(a, min, avail);+  int ret;+  if (avail)+  {+    if (a->archive.read_data_is_posix_read && *avail > (ssize_t)a->archive.read_data_requested)+      *avail = a->archive.read_data_requested;+    if (*avail > rar->bytes_remaining)+      *avail = (ssize_t)rar->bytes_remaining;+    if (*avail < 0)+      return NULL;+    else if (*avail == 0 && rar->main_flags & MHD_VOLUME &&+      rar->file_flags & FHD_SPLIT_AFTER)+    {+      rar->filename_must_match = 1;+      ret = archive_read_format_rar_read_header(a, a->entry);+      if (ret == (ARCHIVE_EOF))+      {+        rar->has_endarc_header = 1;+        ret = archive_read_format_rar_read_header(a, a->entry);+      }+      rar->filename_must_match = 0;+      if (ret != (ARCHIVE_OK))+        return NULL;+      return rar_read_ahead(a, min, avail);+    }+  }+  return h;+}
+ c/archive_read_support_format_rar5.c view
@@ -0,0 +1,4097 @@+/*-+* Copyright (c) 2018 Grzegorz Antoniak (http://antoniak.org)+* All rights reserved.+*+* Redistribution and use in source and binary forms, with or without+* modification, are permitted provided that the following conditions+* are met:+* 1. Redistributions of source code must retain the above copyright+*    notice, this list of conditions and the following disclaimer.+* 2. Redistributions in binary form must reproduce the above copyright+*    notice, this list of conditions and the following disclaimer in the+*    documentation and/or other materials provided with the distribution.+*+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*/++#include "archive_platform.h"+#include "archive_endian.h"++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <time.h>+#ifdef HAVE_ZLIB_H+#include <zlib.h> /* crc32 */+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif++#include "archive.h"+#ifndef HAVE_ZLIB_H+#include "archive_crc32.h"+#endif++#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_ppmd7_private.h"+#include "archive_entry_private.h"++#ifdef HAVE_BLAKE2_H+#include <blake2.h>+#else+#include "archive_blake2.h"+#endif++/*#define CHECK_CRC_ON_SOLID_SKIP*/+/*#define DONT_FAIL_ON_CRC_ERROR*/+/*#define DEBUG*/++#define rar5_min(a, b) (((a) > (b)) ? (b) : (a))+#define rar5_max(a, b) (((a) > (b)) ? (a) : (b))+#define rar5_countof(X) ((const ssize_t) (sizeof(X) / sizeof(*X)))++#if defined DEBUG+#define DEBUG_CODE if(1)+#define LOG(...) do { printf("rar5: " __VA_ARGS__); puts(""); } while(0)+#else+#define DEBUG_CODE if(0)+#endif++/* Real RAR5 magic number is:+ *+ * 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00+ * "Rar!→•☺·\x00"+ *+ * Retrieved with `rar5_signature()` by XOR'ing it with 0xA1, because I don't+ * want to put this magic sequence in each binary that uses libarchive, so+ * applications that scan through the file for this marker won't trigger on+ * this "false" one.+ *+ * The array itself is decrypted in `rar5_init` function. */++static unsigned char rar5_signature_xor[] = { 243, 192, 211, 128, 187, 166, 160, 161 };+static const size_t g_unpack_window_size = 0x20000;++/* These could have been static const's, but they aren't, because of+ * Visual Studio. */+#define MAX_NAME_IN_CHARS 2048+#define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS)++struct file_header {+	ssize_t bytes_remaining;+	ssize_t unpacked_size;+	int64_t last_offset;         /* Used in sanity checks. */+	int64_t last_size;           /* Used in sanity checks. */++	uint8_t solid : 1;           /* Is this a solid stream? */+	uint8_t service : 1;         /* Is this file a service data? */+	uint8_t eof : 1;             /* Did we finish unpacking the file? */+	uint8_t dir : 1;             /* Is this file entry a directory? */++	/* Optional time fields. */+	uint64_t e_mtime;+	uint64_t e_ctime;+	uint64_t e_atime;+	uint32_t e_unix_ns;++	/* Optional hash fields. */+	uint32_t stored_crc32;+	uint32_t calculated_crc32;+	uint8_t blake2sp[32];+	blake2sp_state b2state;+	char has_blake2;++	/* Optional redir fields */+	uint64_t redir_type;+	uint64_t redir_flags;++	ssize_t solid_window_size; /* Used in file format check. */+};++enum EXTRA {+	EX_CRYPT = 0x01,+	EX_HASH = 0x02,+	EX_HTIME = 0x03,+	EX_VERSION = 0x04,+	EX_REDIR = 0x05,+	EX_UOWNER = 0x06,+	EX_SUBDATA = 0x07+};++#define REDIR_SYMLINK_IS_DIR	1++enum REDIR_TYPE {+	REDIR_TYPE_NONE = 0,+	REDIR_TYPE_UNIXSYMLINK = 1,+	REDIR_TYPE_WINSYMLINK = 2,+	REDIR_TYPE_JUNCTION = 3,+	REDIR_TYPE_HARDLINK = 4,+	REDIR_TYPE_FILECOPY = 5,+};++#define	OWNER_USER_NAME		0x01+#define	OWNER_GROUP_NAME	0x02+#define	OWNER_USER_UID		0x04+#define	OWNER_GROUP_GID		0x08+#define	OWNER_MAXNAMELEN	256++enum FILTER_TYPE {+	FILTER_DELTA = 0,   /* Generic pattern. */+	FILTER_E8    = 1,   /* Intel x86 code. */+	FILTER_E8E9  = 2,   /* Intel x86 code. */+	FILTER_ARM   = 3,   /* ARM code. */+	FILTER_AUDIO = 4,   /* Audio filter, not used in RARv5. */+	FILTER_RGB   = 5,   /* Color palette, not used in RARv5. */+	FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */+	FILTER_PPM   = 7,   /* Predictive pattern matching, not used in+			       RARv5. */+	FILTER_NONE  = 8,+};++struct filter_info {+	int type;+	int channels;+	int pos_r;++	int64_t block_start;+	ssize_t block_length;+	uint16_t width;+};++struct data_ready {+	char used;+	const uint8_t* buf;+	size_t size;+	int64_t offset;+};++struct cdeque {+	uint16_t beg_pos;+	uint16_t end_pos;+	uint16_t cap_mask;+	uint16_t size;+	size_t* arr;+};++struct decode_table {+	uint32_t size;+	int32_t decode_len[16];+	uint32_t decode_pos[16];+	uint32_t quick_bits;+	uint8_t quick_len[1 << 10];+	uint16_t quick_num[1 << 10];+	uint16_t decode_num[306];+};++struct comp_state {+	/* Flag used to specify if unpacker needs to reinitialize the+	   uncompression context. */+	uint8_t initialized : 1;++	/* Flag used when applying filters. */+	uint8_t all_filters_applied : 1;++	/* Flag used to skip file context reinitialization, used when unpacker+	   is skipping through different multivolume archives. */+	uint8_t switch_multivolume : 1;++	/* Flag used to specify if unpacker has processed the whole data block+	   or just a part of it. */+	uint8_t block_parsing_finished : 1;++	signed int notused : 4;++	int flags;                   /* Uncompression flags. */+	int method;                  /* Uncompression algorithm method. */+	int version;                 /* Uncompression algorithm version. */+	ssize_t window_size;         /* Size of window_buf. */+	uint8_t* window_buf;         /* Circular buffer used during+	                                decompression. */+	uint8_t* filtered_buf;       /* Buffer used when applying filters. */+	const uint8_t* block_buf;    /* Buffer used when merging blocks. */+	size_t window_mask;          /* Convenience field; window_size - 1. */+	int64_t write_ptr;           /* This amount of data has been unpacked+					in the window buffer. */+	int64_t last_write_ptr;      /* This amount of data has been stored in+	                                the output file. */+	int64_t last_unstore_ptr;    /* Counter of bytes extracted during+	                                unstoring. This is separate from+	                                last_write_ptr because of how SERVICE+	                                base blocks are handled during skipping+	                                in solid multiarchive archives. */+	int64_t solid_offset;        /* Additional offset inside the window+	                                buffer, used in unpacking solid+	                                archives. */+	ssize_t cur_block_size;      /* Size of current data block. */+	int last_len;                /* Flag used in lzss decompression. */++	/* Decode tables used during lzss uncompression. */++#define HUFF_BC 20+	struct decode_table bd;      /* huffman bit lengths */+#define HUFF_NC 306+	struct decode_table ld;      /* literals */+#define HUFF_DC 64+	struct decode_table dd;      /* distances */+#define HUFF_LDC 16+	struct decode_table ldd;     /* lower bits of distances */+#define HUFF_RC 44+	struct decode_table rd;      /* repeating distances */+#define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC)++	/* Circular deque for storing filters. */+	struct cdeque filters;+	int64_t last_block_start;    /* Used for sanity checking. */+	ssize_t last_block_length;   /* Used for sanity checking. */++	/* Distance cache used during lzss uncompression. */+	int dist_cache[4];++	/* Data buffer stack. */+	struct data_ready dready[2];+};++/* Bit reader state. */+struct bit_reader {+	int8_t bit_addr;    /* Current bit pointer inside current byte. */+	int in_addr;        /* Current byte pointer. */+};++/* RARv5 block header structure. Use bf_* functions to get values from+ * block_flags_u8 field. I.e. bf_byte_count, etc. */+struct compressed_block_header {+	/* block_flags_u8 contain fields encoded in little-endian bitfield:+	 *+	 * - table present flag (shr 7, and 1),+	 * - last block flag    (shr 6, and 1),+	 * - byte_count         (shr 3, and 7),+	 * - bit_size           (shr 0, and 7).+	 */+	uint8_t block_flags_u8;+	uint8_t block_cksum;+};++/* RARv5 main header structure. */+struct main_header {+	/* Does the archive contain solid streams? */+	uint8_t solid : 1;++	/* If this a multi-file archive? */+	uint8_t volume : 1;+	uint8_t endarc : 1;+	uint8_t notused : 5;++	unsigned int vol_no;+};++struct generic_header {+	uint8_t split_after : 1;+	uint8_t split_before : 1;+	uint8_t padding : 6;+	int size;+	int last_header_id;+};++struct multivolume {+	unsigned int expected_vol_no;+	uint8_t* push_buf;+};++/* Main context structure. */+struct rar5 {+	int header_initialized;++	/* Set to 1 if current file is positioned AFTER the magic value+	 * of the archive file. This is used in header reading functions. */+	int skipped_magic;++	/* Set to not zero if we're in skip mode (either by calling+	 * rar5_data_skip function or when skipping over solid streams).+	 * Set to 0 when in * extraction mode. This is used during checksum+	 * calculation functions. */+	int skip_mode;++	/* Set to not zero if we're in block merging mode (i.e. when switching+	 * to another file in multivolume archive, last block from 1st archive+	 * needs to be merged with 1st block from 2nd archive). This flag+	 * guards against recursive use of the merging function, which doesn't+	 * support recursive calls. */+	int merge_mode;++	/* An offset to QuickOpen list. This is not supported by this unpacker,+	 * because we're focusing on streaming interface. QuickOpen is designed+	 * to make things quicker for non-stream interfaces, so it's not our+	 * use case. */+	uint64_t qlist_offset;++	/* An offset to additional Recovery data. This is not supported by this+	 * unpacker. Recovery data are additional Reed-Solomon codes that could+	 * be used to calculate bytes that are missing in archive or are+	 * corrupted. */+	uint64_t rr_offset;++	/* Various context variables grouped to different structures. */+	struct generic_header generic;+	struct main_header main;+	struct comp_state cstate;+	struct file_header file;+	struct bit_reader bits;+	struct multivolume vol;++	/* The header of currently processed RARv5 block. Used in main+	 * decompression logic loop. */+	struct compressed_block_header last_block_hdr;+};++/* Forward function declarations. */++static void rar5_signature(char *buf);+static int verify_global_checksums(struct archive_read* a);+static int rar5_read_data_skip(struct archive_read *a);+static int push_data_ready(struct archive_read* a, struct rar5* rar,+	const uint8_t* buf, size_t size, int64_t offset);++/* CDE_xxx = Circular Double Ended (Queue) return values. */+enum CDE_RETURN_VALUES {+	CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS,+};++/* Clears the contents of this circular deque. */+static void cdeque_clear(struct cdeque* d) {+	d->size = 0;+	d->beg_pos = 0;+	d->end_pos = 0;+}++/* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32,+ * 64, 256, etc. When the user will add another item above current capacity,+ * the circular deque will overwrite the oldest entry. */+static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) {+	if(d == NULL || max_capacity_power_of_2 == 0)+		return CDE_PARAM;++	d->cap_mask = max_capacity_power_of_2 - 1;+	d->arr = NULL;++	if((max_capacity_power_of_2 & d->cap_mask) != 0)+		return CDE_PARAM;++	cdeque_clear(d);+	d->arr = malloc(sizeof(void*) * max_capacity_power_of_2);++	return d->arr ? CDE_OK : CDE_ALLOC;+}++/* Return the current size (not capacity) of circular deque `d`. */+static size_t cdeque_size(struct cdeque* d) {+	return d->size;+}++/* Returns the first element of current circular deque. Note that this function+ * doesn't perform any bounds checking. If you need bounds checking, use+ * `cdeque_front()` function instead. */+static void cdeque_front_fast(struct cdeque* d, void** value) {+	*value = (void*) d->arr[d->beg_pos];+}++/* Returns the first element of current circular deque. This function+ * performs bounds checking. */+static int cdeque_front(struct cdeque* d, void** value) {+	if(d->size > 0) {+		cdeque_front_fast(d, value);+		return CDE_OK;+	} else+		return CDE_OUT_OF_BOUNDS;+}++/* Pushes a new element into the end of this circular deque object. If current+ * size will exceed capacity, the oldest element will be overwritten. */+static int cdeque_push_back(struct cdeque* d, void* item) {+	if(d == NULL)+		return CDE_PARAM;++	if(d->size == d->cap_mask + 1)+		return CDE_OUT_OF_BOUNDS;++	d->arr[d->end_pos] = (size_t) item;+	d->end_pos = (d->end_pos + 1) & d->cap_mask;+	d->size++;++	return CDE_OK;+}++/* Pops a front element of this circular deque object and returns its value.+ * This function doesn't perform any bounds checking. */+static void cdeque_pop_front_fast(struct cdeque* d, void** value) {+	*value = (void*) d->arr[d->beg_pos];+	d->beg_pos = (d->beg_pos + 1) & d->cap_mask;+	d->size--;+}++/* Pops a front element of this circular deque object and returns its value.+ * This function performs bounds checking. */+static int cdeque_pop_front(struct cdeque* d, void** value) {+	if(!d || !value)+		return CDE_PARAM;++	if(d->size == 0)+		return CDE_OUT_OF_BOUNDS;++	cdeque_pop_front_fast(d, value);+	return CDE_OK;+}++/* Convenience function to cast filter_info** to void **. */+static void** cdeque_filter_p(struct filter_info** f) {+	return (void**) (size_t) f;+}++/* Convenience function to cast filter_info* to void *. */+static void* cdeque_filter(struct filter_info* f) {+	return (void**) (size_t) f;+}++/* Destroys this circular deque object. Deallocates the memory of the+ * collection buffer, but doesn't deallocate the memory of any pointer passed+ * to this deque as a value. */+static void cdeque_free(struct cdeque* d) {+	if(!d)+		return;++	if(!d->arr)+		return;++	free(d->arr);++	d->arr = NULL;+	d->beg_pos = -1;+	d->end_pos = -1;+	d->cap_mask = 0;+}++static inline+uint8_t bf_bit_size(const struct compressed_block_header* hdr) {+	return hdr->block_flags_u8 & 7;+}++static inline+uint8_t bf_byte_count(const struct compressed_block_header* hdr) {+	return (hdr->block_flags_u8 >> 3) & 7;+}++static inline+uint8_t bf_is_table_present(const struct compressed_block_header* hdr) {+	return (hdr->block_flags_u8 >> 7) & 1;+}++static inline struct rar5* get_context(struct archive_read* a) {+	return (struct rar5*) a->format->data;+}++/* Convenience functions used by filter implementations. */+static void circular_memcpy(uint8_t* dst, uint8_t* window, const uint64_t mask,+    int64_t start, int64_t end)+{+	if((start & mask) > (end & mask)) {+		ssize_t len1 = mask + 1 - (start & mask);+		ssize_t len2 = end & mask;++		memcpy(dst, &window[start & mask], len1);+		memcpy(dst + len1, window, len2);+	} else {+		memcpy(dst, &window[start & mask], (size_t) (end - start));+	}+}++static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) {+	uint8_t linear_buf[4];+	circular_memcpy(linear_buf, rar->cstate.window_buf,+	    rar->cstate.window_mask, offset, offset + 4);+	return archive_le32dec(linear_buf);+}++static void write_filter_data(struct rar5* rar, uint32_t offset,+    uint32_t value)+{+	archive_le32enc(&rar->cstate.filtered_buf[offset], value);+}++/* Allocates a new filter descriptor and adds it to the filter array. */+static struct filter_info* add_new_filter(struct rar5* rar) {+	struct filter_info* f =+		(struct filter_info*) calloc(1, sizeof(struct filter_info));++	if(!f) {+		return NULL;+	}++	cdeque_push_back(&rar->cstate.filters, cdeque_filter(f));+	return f;+}++static int run_delta_filter(struct rar5* rar, struct filter_info* flt) {+	int i;+	ssize_t dest_pos, src_pos = 0;++	for(i = 0; i < flt->channels; i++) {+		uint8_t prev_byte = 0;+		for(dest_pos = i;+				dest_pos < flt->block_length;+				dest_pos += flt->channels)+		{+			uint8_t byte;++			byte = rar->cstate.window_buf[+			    (rar->cstate.solid_offset + flt->block_start ++			    src_pos) & rar->cstate.window_mask];++			prev_byte -= byte;+			rar->cstate.filtered_buf[dest_pos] = prev_byte;+			src_pos++;+		}+	}++	return ARCHIVE_OK;+}++static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt,+		int extended)+{+	const uint32_t file_size = 0x1000000;+	ssize_t i;++	circular_memcpy(rar->cstate.filtered_buf,+	    rar->cstate.window_buf, rar->cstate.window_mask,+	    rar->cstate.solid_offset + flt->block_start,+	    rar->cstate.solid_offset + flt->block_start + flt->block_length);++	for(i = 0; i < flt->block_length - 4;) {+		uint8_t b = rar->cstate.window_buf[+		    (rar->cstate.solid_offset + flt->block_start ++		    i++) & rar->cstate.window_mask];++		/*+		 * 0xE8 = x86's call <relative_addr_uint32> (function call)+		 * 0xE9 = x86's jmp <relative_addr_uint32> (unconditional jump)+		 */+		if(b == 0xE8 || (extended && b == 0xE9)) {++			uint32_t addr;+			uint32_t offset = (i + flt->block_start) % file_size;++			addr = read_filter_data(rar,+			    (uint32_t)(rar->cstate.solid_offset ++			    flt->block_start + i) & rar->cstate.window_mask);++			if(addr & 0x80000000) {+				if(((addr + offset) & 0x80000000) == 0) {+					write_filter_data(rar, (uint32_t)i,+					    addr + file_size);+				}+			} else {+				if((addr - file_size) & 0x80000000) {+					uint32_t naddr = addr - offset;+					write_filter_data(rar, (uint32_t)i,+					    naddr);+				}+			}++			i += 4;+		}+	}++	return ARCHIVE_OK;+}++static int run_arm_filter(struct rar5* rar, struct filter_info* flt) {+	ssize_t i = 0;+	uint32_t offset;++	circular_memcpy(rar->cstate.filtered_buf,+	    rar->cstate.window_buf, rar->cstate.window_mask,+	    rar->cstate.solid_offset + flt->block_start,+	    rar->cstate.solid_offset + flt->block_start + flt->block_length);++	for(i = 0; i < flt->block_length - 3; i += 4) {+		uint8_t* b = &rar->cstate.window_buf[+		    (rar->cstate.solid_offset ++		    flt->block_start + i + 3) & rar->cstate.window_mask];++		if(*b == 0xEB) {+			/* 0xEB = ARM's BL (branch + link) instruction. */+			offset = read_filter_data(rar,+			    (rar->cstate.solid_offset + flt->block_start + i) &+			     rar->cstate.window_mask) & 0x00ffffff;++			offset -= (uint32_t) ((i + flt->block_start) / 4);+			offset = (offset & 0x00ffffff) | 0xeb000000;+			write_filter_data(rar, (uint32_t)i, offset);+		}+	}++	return ARCHIVE_OK;+}++static int run_filter(struct archive_read* a, struct filter_info* flt) {+	int ret;+	struct rar5* rar = get_context(a);++	free(rar->cstate.filtered_buf);++	rar->cstate.filtered_buf = malloc(flt->block_length);+	if(!rar->cstate.filtered_buf) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory for filter data.");+		return ARCHIVE_FATAL;+	}++	switch(flt->type) {+		case FILTER_DELTA:+			ret = run_delta_filter(rar, flt);+			break;++		case FILTER_E8:+			/* fallthrough */+		case FILTER_E8E9:+			ret = run_e8e9_filter(rar, flt,+			    flt->type == FILTER_E8E9);+			break;++		case FILTER_ARM:+			ret = run_arm_filter(rar, flt);+			break;++		default:+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Unsupported filter type: 0x%x", flt->type);+			return ARCHIVE_FATAL;+	}++	if(ret != ARCHIVE_OK) {+		/* Filter has failed. */+		return ret;+	}++	if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf,+	    flt->block_length, rar->cstate.last_write_ptr))+	{+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+		    "Stack overflow when submitting unpacked data");++		return ARCHIVE_FATAL;+	}++	rar->cstate.last_write_ptr += flt->block_length;+	return ARCHIVE_OK;+}++/* The `push_data` function submits the selected data range to the user.+ * Next call of `use_data` will use the pointer, size and offset arguments+ * that are specified here. These arguments are pushed to the FIFO stack here,+ * and popped from the stack by the `use_data` function. */+static void push_data(struct archive_read* a, struct rar5* rar,+    const uint8_t* buf, int64_t idx_begin, int64_t idx_end)+{+	const uint64_t wmask = rar->cstate.window_mask;+	const ssize_t solid_write_ptr = (rar->cstate.solid_offset ++	    rar->cstate.last_write_ptr) & wmask;++	idx_begin += rar->cstate.solid_offset;+	idx_end += rar->cstate.solid_offset;++	/* Check if our unpacked data is wrapped inside the window circular+	 * buffer.  If it's not wrapped, it can be copied out by using+	 * a single memcpy, but when it's wrapped, we need to copy the first+	 * part with one memcpy, and the second part with another memcpy. */++	if((idx_begin & wmask) > (idx_end & wmask)) {+		/* The data is wrapped (begin offset sis bigger than end+		 * offset). */+		const ssize_t frag1_size = rar->cstate.window_size -+		    (idx_begin & wmask);+		const ssize_t frag2_size = idx_end & wmask;++		/* Copy the first part of the buffer first. */+		push_data_ready(a, rar, buf + solid_write_ptr, frag1_size,+		    rar->cstate.last_write_ptr);++		/* Copy the second part of the buffer. */+		push_data_ready(a, rar, buf, frag2_size,+		    rar->cstate.last_write_ptr + frag1_size);++		rar->cstate.last_write_ptr += frag1_size + frag2_size;+	} else {+		/* Data is not wrapped, so we can just use one call to copy the+		 * data. */+		push_data_ready(a, rar,+		    buf + solid_write_ptr, (idx_end - idx_begin) & wmask,+		    rar->cstate.last_write_ptr);++		rar->cstate.last_write_ptr += idx_end - idx_begin;+	}+}++/* Convenience function that submits the data to the user. It uses the+ * unpack window buffer as a source location. */+static void push_window_data(struct archive_read* a, struct rar5* rar,+    int64_t idx_begin, int64_t idx_end)+{+	push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end);+}++static int apply_filters(struct archive_read* a) {+	struct filter_info* flt;+	struct rar5* rar = get_context(a);+	int ret;++	rar->cstate.all_filters_applied = 0;++	/* Get the first filter that can be applied to our data. The data+	 * needs to be fully unpacked before the filter can be run. */+	if(CDE_OK == cdeque_front(&rar->cstate.filters,+	    cdeque_filter_p(&flt))) {+		/* Check if our unpacked data fully covers this filter's+		 * range. */+		if(rar->cstate.write_ptr > flt->block_start &&+		    rar->cstate.write_ptr >= flt->block_start ++		    flt->block_length) {+			/* Check if we have some data pending to be written+			 * right before the filter's start offset. */+			if(rar->cstate.last_write_ptr == flt->block_start) {+				/* Run the filter specified by descriptor+				 * `flt`. */+				ret = run_filter(a, flt);+				if(ret != ARCHIVE_OK) {+					/* Filter failure, return error. */+					return ret;+				}++				/* Filter descriptor won't be needed anymore+				 * after it's used, * so remove it from the+				 * filter list and free its memory. */+				(void) cdeque_pop_front(&rar->cstate.filters,+				    cdeque_filter_p(&flt));++				free(flt);+			} else {+				/* We can't run filters yet, dump the memory+				 * right before the filter. */+				push_window_data(a, rar,+				    rar->cstate.last_write_ptr,+				    flt->block_start);+			}++			/* Return 'filter applied or not needed' state to the+			 * caller. */+			return ARCHIVE_RETRY;+		}+	}++	rar->cstate.all_filters_applied = 1;+	return ARCHIVE_OK;+}++static void dist_cache_push(struct rar5* rar, int value) {+	int* q = rar->cstate.dist_cache;++	q[3] = q[2];+	q[2] = q[1];+	q[1] = q[0];+	q[0] = value;+}++static int dist_cache_touch(struct rar5* rar, int idx) {+	int* q = rar->cstate.dist_cache;+	int i, dist = q[idx];++	for(i = idx; i > 0; i--)+		q[i] = q[i - 1];++	q[0] = dist;+	return dist;+}++static void free_filters(struct rar5* rar) {+	struct cdeque* d = &rar->cstate.filters;++	/* Free any remaining filters. All filters should be naturally+	 * consumed by the unpacking function, so remaining filters after+	 * unpacking normally mean that unpacking wasn't successful.+	 * But still of course we shouldn't leak memory in such case. */++	/* cdeque_size() is a fast operation, so we can use it as a loop+	 * expression. */+	while(cdeque_size(d) > 0) {+		struct filter_info* f = NULL;++		/* Pop_front will also decrease the collection's size. */+		if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f)))+			free(f);+	}++	cdeque_clear(d);++	/* Also clear out the variables needed for sanity checking. */+	rar->cstate.last_block_start = 0;+	rar->cstate.last_block_length = 0;+}++static void reset_file_context(struct rar5* rar) {+	memset(&rar->file, 0, sizeof(rar->file));+	blake2sp_init(&rar->file.b2state, 32);++	if(rar->main.solid) {+		rar->cstate.solid_offset += rar->cstate.write_ptr;+	} else {+		rar->cstate.solid_offset = 0;+	}++	rar->cstate.write_ptr = 0;+	rar->cstate.last_write_ptr = 0;+	rar->cstate.last_unstore_ptr = 0;++	rar->file.redir_type = REDIR_TYPE_NONE;+	rar->file.redir_flags = 0;++	free_filters(rar);+}++static inline int get_archive_read(struct archive* a,+    struct archive_read** ar)+{+	*ar = (struct archive_read*) a;+	archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,+	    "archive_read_support_format_rar5");++	return ARCHIVE_OK;+}++static int read_ahead(struct archive_read* a, size_t how_many,+    const uint8_t** ptr)+{+	ssize_t avail = -1;+	if(!ptr)+		return 0;++	*ptr = __archive_read_ahead(a, how_many, &avail);+	if(*ptr == NULL) {+		return 0;+	}++	return 1;+}++static int consume(struct archive_read* a, int64_t how_many) {+	int ret;++	ret = how_many == __archive_read_consume(a, how_many)+		? ARCHIVE_OK+		: ARCHIVE_FATAL;++	return ret;+}++/**+ * Read a RAR5 variable sized numeric value. This value will be stored in+ * `pvalue`. The `pvalue_len` argument points to a variable that will receive+ * the byte count that was consumed in order to decode the `pvalue` value, plus+ * one.+ *+ * pvalue_len is optional and can be NULL.+ *+ * NOTE: if `pvalue_len` is NOT NULL, the caller needs to manually consume+ * the number of bytes that `pvalue_len` value contains. If the `pvalue_len`+ * is NULL, this consuming operation is done automatically.+ *+ * Returns 1 if *pvalue was successfully read.+ * Returns 0 if there was an error. In this case, *pvalue contains an+ *           invalid value.+ */++static int read_var(struct archive_read* a, uint64_t* pvalue,+    uint64_t* pvalue_len)+{+	uint64_t result = 0;+	size_t shift, i;+	const uint8_t* p;+	uint8_t b;++	/* We will read maximum of 8 bytes. We don't have to handle the+	 * situation to read the RAR5 variable-sized value stored at the end of+	 * the file, because such situation will never happen. */+	if(!read_ahead(a, 8, &p))+		return 0;++	for(shift = 0, i = 0; i < 8; i++, shift += 7) {+		b = p[i];++		/* Strip the MSB from the input byte and add the resulting+		 * number to the `result`. */+		result += (b & (uint64_t)0x7F) << shift;++		/* MSB set to 1 means we need to continue decoding process.+		 * MSB set to 0 means we're done.+		 *+		 * This conditional checks for the second case. */+		if((b & 0x80) == 0) {+			if(pvalue) {+				*pvalue = result;+			}++			/* If the caller has passed the `pvalue_len` pointer,+			 * store the number of consumed bytes in it and do NOT+			 * consume those bytes, since the caller has all the+			 * information it needs to perform */+			if(pvalue_len) {+				*pvalue_len = 1 + i;+			} else {+				/* If the caller did not provide the+				 * `pvalue_len` pointer, it will not have the+				 * possibility to advance the file pointer,+				 * because it will not know how many bytes it+				 * needs to consume. This is why we handle+				 * such situation here automatically. */+				if(ARCHIVE_OK != consume(a, 1 + i)) {+					return 0;+				}+			}++			/* End of decoding process, return success. */+			return 1;+		}+	}++	/* The decoded value takes the maximum number of 8 bytes.+	 * It's a maximum number of bytes, so end decoding process here+	 * even if the first bit of last byte is 1. */+	if(pvalue) {+		*pvalue = result;+	}++	if(pvalue_len) {+		*pvalue_len = 9;+	} else {+		if(ARCHIVE_OK != consume(a, 9)) {+			return 0;+		}+	}++	return 1;+}++static int read_var_sized(struct archive_read* a, size_t* pvalue,+    size_t* pvalue_len)+{+	uint64_t v;+	uint64_t v_size = 0;++	const int ret = pvalue_len ? read_var(a, &v, &v_size)+				   : read_var(a, &v, NULL);++	if(ret == 1 && pvalue) {+		*pvalue = (size_t) v;+	}++	if(pvalue_len) {+		/* Possible data truncation should be safe. */+		*pvalue_len = (size_t) v_size;+	}++	return ret;+}++static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) {+	uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24;+	bits |= p[rar->bits.in_addr + 1] << 16;+	bits |= p[rar->bits.in_addr + 2] << 8;+	bits |= p[rar->bits.in_addr + 3];+	bits <<= rar->bits.bit_addr;+	bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr);+	*value = bits;+	return ARCHIVE_OK;+}++static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) {+	int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16;+	bits |= (int) p[rar->bits.in_addr + 1] << 8;+	bits |= (int) p[rar->bits.in_addr + 2];+	bits >>= (8 - rar->bits.bit_addr);+	*value = bits & 0xffff;+	return ARCHIVE_OK;+}++static void skip_bits(struct rar5* rar, int bits) {+	const int new_bits = rar->bits.bit_addr + bits;+	rar->bits.in_addr += new_bits >> 3;+	rar->bits.bit_addr = new_bits & 7;+}++/* n = up to 16 */+static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n,+    int* value)+{+	uint16_t v;+	int ret, num;++	if(n == 0 || n > 16) {+		/* This is a programmer error and should never happen+		 * in runtime. */+		return ARCHIVE_FATAL;+	}++	ret = read_bits_16(rar, p, &v);+	if(ret != ARCHIVE_OK)+		return ret;++	num = (int) v;+	num >>= 16 - n;++	skip_bits(rar, n);++	if(value)+		*value = num;++	return ARCHIVE_OK;+}++static int read_u32(struct archive_read* a, uint32_t* pvalue) {+	const uint8_t* p;+	if(!read_ahead(a, 4, &p))+		return 0;++	*pvalue = archive_le32dec(p);+	return ARCHIVE_OK == consume(a, 4) ? 1 : 0;+}++static int read_u64(struct archive_read* a, uint64_t* pvalue) {+	const uint8_t* p;+	if(!read_ahead(a, 8, &p))+		return 0;++	*pvalue = archive_le64dec(p);+	return ARCHIVE_OK == consume(a, 8) ? 1 : 0;+}++static int bid_standard(struct archive_read* a) {+	const uint8_t* p;+	char signature[sizeof(rar5_signature_xor)];++	rar5_signature(signature);++	if(!read_ahead(a, sizeof(rar5_signature_xor), &p))+		return -1;++	if(!memcmp(signature, p, sizeof(rar5_signature_xor)))+		return 30;++	return -1;+}++static int rar5_bid(struct archive_read* a, int best_bid) {+	int my_bid;++	if(best_bid > 30)+		return -1;++	my_bid = bid_standard(a);+	if(my_bid > -1) {+		return my_bid;+	}++	return -1;+}++static int rar5_options(struct archive_read *a, const char *key,+    const char *val) {+	(void) a;+	(void) key;+	(void) val;++	/* No options supported in this version. Return the ARCHIVE_WARN code+	 * to signal the options supervisor that the unpacker didn't handle+	 * setting this option. */++	return ARCHIVE_WARN;+}++static void init_header(struct archive_read* a) {+	a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5;+	a->archive.archive_format_name = "RAR5";+}++static void init_window_mask(struct rar5* rar) {+	if (rar->cstate.window_size)+		rar->cstate.window_mask = rar->cstate.window_size - 1;+	else+		rar->cstate.window_mask = 0;+}++enum HEADER_FLAGS {+	HFL_EXTRA_DATA = 0x0001,+	HFL_DATA = 0x0002,+	HFL_SKIP_IF_UNKNOWN = 0x0004,+	HFL_SPLIT_BEFORE = 0x0008,+	HFL_SPLIT_AFTER = 0x0010,+	HFL_CHILD = 0x0020,+	HFL_INHERITED = 0x0040+};++static int process_main_locator_extra_block(struct archive_read* a,+    struct rar5* rar)+{+	uint64_t locator_flags;++	enum LOCATOR_FLAGS {+		QLIST = 0x01, RECOVERY = 0x02,+	};++	if(!read_var(a, &locator_flags, NULL)) {+		return ARCHIVE_EOF;+	}++	if(locator_flags & QLIST) {+		if(!read_var(a, &rar->qlist_offset, NULL)) {+			return ARCHIVE_EOF;+		}++		/* qlist is not used */+	}++	if(locator_flags & RECOVERY) {+		if(!read_var(a, &rar->rr_offset, NULL)) {+			return ARCHIVE_EOF;+		}++		/* rr is not used */+	}++	return ARCHIVE_OK;+}++static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar,+    ssize_t* extra_data_size)+{+	size_t hash_type = 0;+	size_t value_len;++	enum HASH_TYPE {+		BLAKE2sp = 0x00+	};++	if(!read_var_sized(a, &hash_type, &value_len))+		return ARCHIVE_EOF;++	*extra_data_size -= value_len;+	if(ARCHIVE_OK != consume(a, value_len)) {+		return ARCHIVE_EOF;+	}++	/* The file uses BLAKE2sp checksum algorithm instead of plain old+	 * CRC32. */+	if(hash_type == BLAKE2sp) {+		const uint8_t* p;+		const int hash_size = sizeof(rar->file.blake2sp);++		if(!read_ahead(a, hash_size, &p))+			return ARCHIVE_EOF;++		rar->file.has_blake2 = 1;+		memcpy(&rar->file.blake2sp, p, hash_size);++		if(ARCHIVE_OK != consume(a, hash_size)) {+			return ARCHIVE_EOF;+		}++		*extra_data_size -= hash_size;+	} else {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unsupported hash type (0x%x)", (int) hash_type);+		return ARCHIVE_FATAL;+	}++	return ARCHIVE_OK;+}++static uint64_t time_win_to_unix(uint64_t win_time) {+	const size_t ns_in_sec = 10000000;+	const uint64_t sec_to_unix = 11644473600LL;+	return win_time / ns_in_sec - sec_to_unix;+}++static int parse_htime_item(struct archive_read* a, char unix_time,+    uint64_t* where, ssize_t* extra_data_size)+{+	if(unix_time) {+		uint32_t time_val;+		if(!read_u32(a, &time_val))+			return ARCHIVE_EOF;++		*extra_data_size -= 4;+		*where = (uint64_t) time_val;+	} else {+		uint64_t windows_time;+		if(!read_u64(a, &windows_time))+			return ARCHIVE_EOF;++		*where = time_win_to_unix(windows_time);+		*extra_data_size -= 8;+	}++	return ARCHIVE_OK;+}++static int parse_file_extra_version(struct archive_read* a,+    struct archive_entry* e, ssize_t* extra_data_size)+{+	size_t flags = 0;+	size_t version = 0;+	size_t value_len = 0;+	struct archive_string version_string;+	struct archive_string name_utf8_string;+	const char* cur_filename;++	/* Flags are ignored. */+	if(!read_var_sized(a, &flags, &value_len))+		return ARCHIVE_EOF;++	*extra_data_size -= value_len;+	if(ARCHIVE_OK != consume(a, value_len))+		return ARCHIVE_EOF;++	if(!read_var_sized(a, &version, &value_len))+		return ARCHIVE_EOF;++	*extra_data_size -= value_len;+	if(ARCHIVE_OK != consume(a, value_len))+		return ARCHIVE_EOF;++	/* extra_data_size should be zero here. */++	cur_filename = archive_entry_pathname_utf8(e);+	if(cur_filename == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+		    "Version entry without file name");+		return ARCHIVE_FATAL;+	}++	archive_string_init(&version_string);+	archive_string_init(&name_utf8_string);++	/* Prepare a ;123 suffix for the filename, where '123' is the version+	 * value of this file. */+	archive_string_sprintf(&version_string, ";%zu", version);++	/* Build the new filename. */+	archive_strcat(&name_utf8_string, cur_filename);+	archive_strcat(&name_utf8_string, version_string.s);++	/* Apply the new filename into this file's context. */+	archive_entry_update_pathname_utf8(e, name_utf8_string.s);++	/* Free buffers. */+	archive_string_free(&version_string);+	archive_string_free(&name_utf8_string);+	return ARCHIVE_OK;+}++static int parse_file_extra_htime(struct archive_read* a,+    struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size)+{+	char unix_time = 0;+	size_t flags = 0;+	size_t value_len;++	enum HTIME_FLAGS {+		IS_UNIX       = 0x01,+		HAS_MTIME     = 0x02,+		HAS_CTIME     = 0x04,+		HAS_ATIME     = 0x08,+		HAS_UNIX_NS   = 0x10,+	};++	if(!read_var_sized(a, &flags, &value_len))+		return ARCHIVE_EOF;++	*extra_data_size -= value_len;+	if(ARCHIVE_OK != consume(a, value_len)) {+		return ARCHIVE_EOF;+	}++	unix_time = flags & IS_UNIX;++	if(flags & HAS_MTIME) {+		parse_htime_item(a, unix_time, &rar->file.e_mtime,+		    extra_data_size);+		archive_entry_set_mtime(e, rar->file.e_mtime, 0);+	}++	if(flags & HAS_CTIME) {+		parse_htime_item(a, unix_time, &rar->file.e_ctime,+		    extra_data_size);+		archive_entry_set_ctime(e, rar->file.e_ctime, 0);+	}++	if(flags & HAS_ATIME) {+		parse_htime_item(a, unix_time, &rar->file.e_atime,+		    extra_data_size);+		archive_entry_set_atime(e, rar->file.e_atime, 0);+	}++	if(flags & HAS_UNIX_NS) {+		if(!read_u32(a, &rar->file.e_unix_ns))+			return ARCHIVE_EOF;++		*extra_data_size -= 4;+	}++	return ARCHIVE_OK;+}++static int parse_file_extra_redir(struct archive_read* a,+    struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size)+{+	uint64_t value_size = 0;+	size_t target_size = 0;+	char target_utf8_buf[MAX_NAME_IN_BYTES];+	const uint8_t* p;++	if(!read_var(a, &rar->file.redir_type, &value_size))+		return ARCHIVE_EOF;+	if(ARCHIVE_OK != consume(a, (int64_t)value_size))+		return ARCHIVE_EOF;+	*extra_data_size -= value_size;++	if(!read_var(a, &rar->file.redir_flags, &value_size))+		return ARCHIVE_EOF;+	if(ARCHIVE_OK != consume(a, (int64_t)value_size))+		return ARCHIVE_EOF;+	*extra_data_size -= value_size;++	if(!read_var_sized(a, &target_size, NULL))+		return ARCHIVE_EOF;+	*extra_data_size -= target_size + 1;++	if(!read_ahead(a, target_size, &p))+		return ARCHIVE_EOF;++	if(target_size > (MAX_NAME_IN_CHARS - 1)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Link target is too long");+		return ARCHIVE_FATAL;+	}++	if(target_size == 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "No link target specified");+		return ARCHIVE_FATAL;+	}++	memcpy(target_utf8_buf, p, target_size);+	target_utf8_buf[target_size] = 0;++	if(ARCHIVE_OK != consume(a, (int64_t)target_size))+		return ARCHIVE_EOF;++	switch(rar->file.redir_type) {+		case REDIR_TYPE_UNIXSYMLINK:+		case REDIR_TYPE_WINSYMLINK:+			archive_entry_set_filetype(e, AE_IFLNK);+			archive_entry_update_symlink_utf8(e, target_utf8_buf);+			if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) {+				archive_entry_set_symlink_type(e,+					AE_SYMLINK_TYPE_DIRECTORY);+			} else {+				archive_entry_set_symlink_type(e,+				AE_SYMLINK_TYPE_FILE);+			}+			break;++		case REDIR_TYPE_HARDLINK:+			archive_entry_set_filetype(e, AE_IFREG);+			archive_entry_update_hardlink_utf8(e, target_utf8_buf);+			break;++		default:+			/* Unknown redir type, skip it. */+			break;+	}+	return ARCHIVE_OK;+}++static int parse_file_extra_owner(struct archive_read* a,+    struct archive_entry* e, ssize_t* extra_data_size)+{+	uint64_t flags = 0;+	uint64_t value_size = 0;+	uint64_t id = 0;+	size_t name_len = 0;+	size_t name_size = 0;+	char namebuf[OWNER_MAXNAMELEN];+	const uint8_t* p;++	if(!read_var(a, &flags, &value_size))+		return ARCHIVE_EOF;+	if(ARCHIVE_OK != consume(a, (int64_t)value_size))+		return ARCHIVE_EOF;+	*extra_data_size -= value_size;++	if ((flags & OWNER_USER_NAME) != 0) {+		if(!read_var_sized(a, &name_size, NULL))+			return ARCHIVE_EOF;+		*extra_data_size -= name_size + 1;++		if(!read_ahead(a, name_size, &p))+			return ARCHIVE_EOF;++		if (name_size >= OWNER_MAXNAMELEN) {+			name_len = OWNER_MAXNAMELEN - 1;+		} else {+			name_len = name_size;+		}++		memcpy(namebuf, p, name_len);+		namebuf[name_len] = 0;+		if(ARCHIVE_OK != consume(a, (int64_t)name_size))+			return ARCHIVE_EOF;++		archive_entry_set_uname(e, namebuf);+	}+	if ((flags & OWNER_GROUP_NAME) != 0) {+		if(!read_var_sized(a, &name_size, NULL))+			return ARCHIVE_EOF;+		*extra_data_size -= name_size + 1;++		if(!read_ahead(a, name_size, &p))+			return ARCHIVE_EOF;++		if (name_size >= OWNER_MAXNAMELEN) {+			name_len = OWNER_MAXNAMELEN - 1;+		} else {+			name_len = name_size;+		}++		memcpy(namebuf, p, name_len);+		namebuf[name_len] = 0;+		if(ARCHIVE_OK != consume(a, (int64_t)name_size))+			return ARCHIVE_EOF;++		archive_entry_set_gname(e, namebuf);+	}+	if ((flags & OWNER_USER_UID) != 0) {+		if(!read_var(a, &id, &value_size))+			return ARCHIVE_EOF;+		if(ARCHIVE_OK != consume(a, (int64_t)value_size))+			return ARCHIVE_EOF;+		*extra_data_size -= value_size;++		archive_entry_set_uid(e, (la_int64_t)id);+	}+	if ((flags & OWNER_GROUP_GID) != 0) {+		if(!read_var(a, &id, &value_size))+			return ARCHIVE_EOF;+		if(ARCHIVE_OK != consume(a, (int64_t)value_size))+			return ARCHIVE_EOF;+		*extra_data_size -= value_size;++		archive_entry_set_gid(e, (la_int64_t)id);+	}+	return ARCHIVE_OK;+}++static int process_head_file_extra(struct archive_read* a,+    struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size)+{+	size_t extra_field_size;+	size_t extra_field_id = 0;+	int ret = ARCHIVE_FATAL;+	size_t var_size;++	while(extra_data_size > 0) {+		if(!read_var_sized(a, &extra_field_size, &var_size))+			return ARCHIVE_EOF;++		extra_data_size -= var_size;+		if(ARCHIVE_OK != consume(a, var_size)) {+			return ARCHIVE_EOF;+		}++		if(!read_var_sized(a, &extra_field_id, &var_size))+			return ARCHIVE_EOF;++		extra_data_size -= var_size;+		if(ARCHIVE_OK != consume(a, var_size)) {+			return ARCHIVE_EOF;+		}++		switch(extra_field_id) {+			case EX_HASH:+				ret = parse_file_extra_hash(a, rar,+				    &extra_data_size);+				break;+			case EX_HTIME:+				ret = parse_file_extra_htime(a, e, rar,+				    &extra_data_size);+				break;+			case EX_REDIR:+				ret = parse_file_extra_redir(a, e, rar,+				    &extra_data_size);+				break;+			case EX_UOWNER:+				ret = parse_file_extra_owner(a, e,+				    &extra_data_size);+				break;+			case EX_VERSION:+				ret = parse_file_extra_version(a, e,+				    &extra_data_size);+				break;+			case EX_CRYPT:+				/* fallthrough */+			case EX_SUBDATA:+				/* fallthrough */+			default:+				/* Skip unsupported entry. */+				return consume(a, extra_data_size);+		}+	}++	if(ret != ARCHIVE_OK) {+		/* Attribute not implemented. */+		return ret;+	}++	return ARCHIVE_OK;+}++static int process_head_file(struct archive_read* a, struct rar5* rar,+    struct archive_entry* entry, size_t block_flags)+{+	ssize_t extra_data_size = 0;+	size_t data_size = 0;+	size_t file_flags = 0;+	size_t file_attr = 0;+	size_t compression_info = 0;+	size_t host_os = 0;+	size_t name_size = 0;+	uint64_t unpacked_size, window_size;+	uint32_t mtime = 0, crc = 0;+	int c_method = 0, c_version = 0;+	char name_utf8_buf[MAX_NAME_IN_BYTES];+	const uint8_t* p;++	enum FILE_FLAGS {+		DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004,+		UNKNOWN_UNPACKED_SIZE = 0x0008,+	};++	enum FILE_ATTRS {+		ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4,+		ATTR_DIRECTORY = 0x10,+	};++	enum COMP_INFO_FLAGS {+		SOLID = 0x0040,+	};++	enum HOST_OS {+		HOST_WINDOWS = 0,+		HOST_UNIX = 1,+	};++	archive_entry_clear(entry);++	/* Do not reset file context if we're switching archives. */+	if(!rar->cstate.switch_multivolume) {+		reset_file_context(rar);+	}++	if(block_flags & HFL_EXTRA_DATA) {+		size_t edata_size = 0;+		if(!read_var_sized(a, &edata_size, NULL))+			return ARCHIVE_EOF;++		/* Intentional type cast from unsigned to signed. */+		extra_data_size = (ssize_t) edata_size;+	}++	if(block_flags & HFL_DATA) {+		if(!read_var_sized(a, &data_size, NULL))+			return ARCHIVE_EOF;++		rar->file.bytes_remaining = data_size;+	} else {+		rar->file.bytes_remaining = 0;++		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+				"no data found in file/service block");+		return ARCHIVE_FATAL;+	}++	if(!read_var_sized(a, &file_flags, NULL))+		return ARCHIVE_EOF;++	if(!read_var(a, &unpacked_size, NULL))+		return ARCHIVE_EOF;++	if(file_flags & UNKNOWN_UNPACKED_SIZE) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+		    "Files with unknown unpacked size are not supported");+		return ARCHIVE_FATAL;+	}++	rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0);++	if(!read_var_sized(a, &file_attr, NULL))+		return ARCHIVE_EOF;++	if(file_flags & UTIME) {+		if(!read_u32(a, &mtime))+			return ARCHIVE_EOF;+	}++	if(file_flags & CRC32) {+		if(!read_u32(a, &crc))+			return ARCHIVE_EOF;+	}++	if(!read_var_sized(a, &compression_info, NULL))+		return ARCHIVE_EOF;++	c_method = (int) (compression_info >> 7) & 0x7;+	c_version = (int) (compression_info & 0x3f);++	/* RAR5 seems to limit the dictionary size to 64MB. */+	window_size = (rar->file.dir > 0) ?+		0 :+		g_unpack_window_size << ((compression_info >> 10) & 15);+	rar->cstate.method = c_method;+	rar->cstate.version = c_version + 50;+	rar->file.solid = (compression_info & SOLID) > 0;++	/* Archives which declare solid files without initializing the window+	 * buffer first are invalid. */++	if(rar->file.solid > 0 && rar->cstate.window_buf == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+				  "Declared solid file, but no window buffer "+				  "initialized yet.");+		return ARCHIVE_FATAL;+	}++	/* Check if window_size is a sane value. Also, if the file is not+	 * declared as a directory, disallow window_size == 0. */+	if(window_size > (64 * 1024 * 1024) ||+	    (rar->file.dir == 0 && window_size == 0))+	{+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Declared dictionary size is not supported.");+		return ARCHIVE_FATAL;+	}++	if(rar->file.solid > 0) {+		/* Re-check if current window size is the same as previous+		 * window size (for solid files only). */+		if(rar->file.solid_window_size > 0 &&+		    rar->file.solid_window_size != (ssize_t) window_size)+		{+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+			    "Window size for this solid file doesn't match "+			    "the window size used in previous solid file. ");+			return ARCHIVE_FATAL;+		}+	}++	/* If we're currently switching volumes, ignore the new definition of+	 * window_size. */+	if(rar->cstate.switch_multivolume == 0) {+		/* Values up to 64M should fit into ssize_t on every+		 * architecture. */+		rar->cstate.window_size = (ssize_t) window_size;+	}++	if(rar->file.solid > 0 && rar->file.solid_window_size == 0) {+		/* Solid files have to have the same window_size across+		   whole archive. Remember the window_size parameter+		   for first solid file found. */+		rar->file.solid_window_size = rar->cstate.window_size;+	}++	init_window_mask(rar);++	rar->file.service = 0;++	if(!read_var_sized(a, &host_os, NULL))+		return ARCHIVE_EOF;++	if(host_os == HOST_WINDOWS) {+		/* Host OS is Windows */++		__LA_MODE_T mode;++		if(file_attr & ATTR_DIRECTORY) {+			if (file_attr & ATTR_READONLY) {+				mode = 0555 | AE_IFDIR;+			} else {+				mode = 0755 | AE_IFDIR;+			}+		} else {+			if (file_attr & ATTR_READONLY) {+				mode = 0444 | AE_IFREG;+			} else {+				mode = 0644 | AE_IFREG;+			}+		}++		archive_entry_set_mode(entry, mode);++		if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) {+			char *fflags_text, *ptr;+			/* allocate for "rdonly,hidden,system," */+			fflags_text = malloc(22 * sizeof(char));+			if (fflags_text != NULL) {+				ptr = fflags_text;+				if (file_attr & ATTR_READONLY) {+					strcpy(ptr, "rdonly,");+					ptr = ptr + 7;+				}+				if (file_attr & ATTR_HIDDEN) {+					strcpy(ptr, "hidden,");+					ptr = ptr + 7;+				}+				if (file_attr & ATTR_SYSTEM) {+					strcpy(ptr, "system,");+					ptr = ptr + 7;+				}+				if (ptr > fflags_text) {+					/* Delete trailing comma */+					*(ptr - 1) = '\0';+					archive_entry_copy_fflags_text(entry,+					    fflags_text);+				}+				free(fflags_text);+			}+		}+	} else if(host_os == HOST_UNIX) {+		/* Host OS is Unix */+		archive_entry_set_mode(entry, (__LA_MODE_T) file_attr);+	} else {+		/* Unknown host OS */+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+				"Unsupported Host OS: 0x%x", (int) host_os);++		return ARCHIVE_FATAL;+	}++	if(!read_var_sized(a, &name_size, NULL))+		return ARCHIVE_EOF;++	if(!read_ahead(a, name_size, &p))+		return ARCHIVE_EOF;++	if(name_size > (MAX_NAME_IN_CHARS - 1)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+				"Filename is too long");++		return ARCHIVE_FATAL;+	}++	if(name_size == 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+				"No filename specified");++		return ARCHIVE_FATAL;+	}++	memcpy(name_utf8_buf, p, name_size);+	name_utf8_buf[name_size] = 0;+	if(ARCHIVE_OK != consume(a, name_size)) {+		return ARCHIVE_EOF;+	}++	archive_entry_update_pathname_utf8(entry, name_utf8_buf);++	if(extra_data_size > 0) {+		int ret = process_head_file_extra(a, entry, rar,+		    extra_data_size);++		/*+		 * TODO: rewrite or remove useless sanity check+		 *       as extra_data_size is not passed as a pointer+		 *+		if(extra_data_size < 0) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+			    "File extra data size is not zero");+			return ARCHIVE_FATAL;+		}+		 */++		if(ret != ARCHIVE_OK)+			return ret;+	}++	if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) {+		rar->file.unpacked_size = (ssize_t) unpacked_size;+		if(rar->file.redir_type == REDIR_TYPE_NONE)+			archive_entry_set_size(entry, unpacked_size);+	}++	if(file_flags & UTIME) {+		archive_entry_set_mtime(entry, (time_t) mtime, 0);+	}++	if(file_flags & CRC32) {+		rar->file.stored_crc32 = crc;+	}++	if(!rar->cstate.switch_multivolume) {+		/* Do not reinitialize unpacking state if we're switching+		 * archives. */+		rar->cstate.block_parsing_finished = 1;+		rar->cstate.all_filters_applied = 1;+		rar->cstate.initialized = 0;+	}++	if(rar->generic.split_before > 0) {+		/* If now we're standing on a header that has a 'split before'+		 * mark, it means we're standing on a 'continuation' file+		 * header. Signal the caller that if it wants to move to+		 * another file, it must call rar5_read_header() function+		 * again. */++		return ARCHIVE_RETRY;+	} else {+		return ARCHIVE_OK;+	}+}++static int process_head_service(struct archive_read* a, struct rar5* rar,+    struct archive_entry* entry, size_t block_flags)+{+	/* Process this SERVICE block the same way as FILE blocks. */+	int ret = process_head_file(a, rar, entry, block_flags);+	if(ret != ARCHIVE_OK)+		return ret;++	rar->file.service = 1;++	/* But skip the data part automatically. It's no use for the user+	 * anyway.  It contains only service data, not even needed to+	 * properly unpack the file. */+	ret = rar5_read_data_skip(a);+	if(ret != ARCHIVE_OK)+		return ret;++	/* After skipping, try parsing another block automatically. */+	return ARCHIVE_RETRY;+}++static int process_head_main(struct archive_read* a, struct rar5* rar,+    struct archive_entry* entry, size_t block_flags)+{+	int ret;+	size_t extra_data_size = 0;+	size_t extra_field_size = 0;+	size_t extra_field_id = 0;+	size_t archive_flags = 0;++	enum MAIN_FLAGS {+		VOLUME = 0x0001,         /* multi-volume archive */+		VOLUME_NUMBER = 0x0002,  /* volume number, first vol doesn't+					  * have it */+		SOLID = 0x0004,          /* solid archive */+		PROTECT = 0x0008,        /* contains Recovery info */+		LOCK = 0x0010,           /* readonly flag, not used */+	};++	enum MAIN_EXTRA {+		// Just one attribute here.+		LOCATOR = 0x01,+	};++	(void) entry;++	if(block_flags & HFL_EXTRA_DATA) {+		if(!read_var_sized(a, &extra_data_size, NULL))+			return ARCHIVE_EOF;+	} else {+		extra_data_size = 0;+	}++	if(!read_var_sized(a, &archive_flags, NULL)) {+		return ARCHIVE_EOF;+	}++	rar->main.volume = (archive_flags & VOLUME) > 0;+	rar->main.solid = (archive_flags & SOLID) > 0;++	if(archive_flags & VOLUME_NUMBER) {+		size_t v = 0;+		if(!read_var_sized(a, &v, NULL)) {+			return ARCHIVE_EOF;+		}++		if (v > UINT_MAX) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Invalid volume number");+			return ARCHIVE_FATAL;+		}++		rar->main.vol_no = (unsigned int) v;+	} else {+		rar->main.vol_no = 0;+	}++	if(rar->vol.expected_vol_no > 0 &&+		rar->main.vol_no != rar->vol.expected_vol_no)+	{+		/* Returning EOF instead of FATAL because of strange+		 * libarchive behavior. When opening multiple files via+		 * archive_read_open_filenames(), after reading up the whole+		 * last file, the __archive_read_ahead function wraps up to+		 * the first archive instead of returning EOF. */+		return ARCHIVE_EOF;+	}++	if(extra_data_size == 0) {+		/* Early return. */+		return ARCHIVE_OK;+	}++	if(!read_var_sized(a, &extra_field_size, NULL)) {+		return ARCHIVE_EOF;+	}++	if(!read_var_sized(a, &extra_field_id, NULL)) {+		return ARCHIVE_EOF;+	}++	if(extra_field_size == 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Invalid extra field size");+		return ARCHIVE_FATAL;+	}++	switch(extra_field_id) {+		case LOCATOR:+			ret = process_main_locator_extra_block(a, rar);+			if(ret != ARCHIVE_OK) {+				/* Error while parsing main locator extra+				 * block. */+				return ret;+			}++			break;+		default:+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Unsupported extra type (0x%x)",+			    (int) extra_field_id);+			return ARCHIVE_FATAL;+	}++	return ARCHIVE_OK;+}++static int skip_unprocessed_bytes(struct archive_read* a) {+	struct rar5* rar = get_context(a);+	int ret;++	if(rar->file.bytes_remaining) {+		/* Use different skipping method in block merging mode than in+		 * normal mode. If merge mode is active, rar5_read_data_skip+		 * can't be used, because it could allow recursive use of+		 * merge_block() * function, and this function doesn't support+		 * recursive use. */+		if(rar->merge_mode) {+			/* Discard whole merged block. This is valid in solid+			 * mode as well, because the code will discard blocks+			 * only if those blocks are safe to discard (i.e.+			 * they're not FILE blocks).  */+			ret = consume(a, rar->file.bytes_remaining);+			if(ret != ARCHIVE_OK) {+				return ret;+			}+			rar->file.bytes_remaining = 0;+		} else {+			/* If we're not in merge mode, use safe skipping code.+			 * This will ensure we'll handle solid archives+			 * properly. */+			ret = rar5_read_data_skip(a);+			if(ret != ARCHIVE_OK) {+				return ret;+			}+		}+	}++	return ARCHIVE_OK;+}++static int scan_for_signature(struct archive_read* a);++/* Base block processing function. A 'base block' is a RARv5 header block+ * that tells the reader what kind of data is stored inside the block.+ *+ * From the birds-eye view a RAR file looks file this:+ *+ * <magic><base_block_1><base_block_2>...<base_block_n>+ *+ * There are a few types of base blocks. Those types are specified inside+ * the 'switch' statement in this function. For example purposes, I'll write+ * how a standard RARv5 file could look like here:+ *+ * <magic><MAIN><FILE><FILE><FILE><SERVICE><ENDARC>+ *+ * The structure above could describe an archive file with 3 files in it,+ * one service "QuickOpen" block (that is ignored by this parser), and an+ * end of file base block marker.+ *+ * If the file is stored in multiple archive files ("multiarchive"), it might+ * look like this:+ *+ * .part01.rar: <magic><MAIN><FILE><ENDARC>+ * .part02.rar: <magic><MAIN><FILE><ENDARC>+ * .part03.rar: <magic><MAIN><FILE><ENDARC>+ *+ * This example could describe 3 RAR files that contain ONE archived file.+ * Or it could describe 3 RAR files that contain 3 different files. Or 3+ * RAR files than contain 2 files. It all depends what metadata is stored in+ * the headers of <FILE> blocks.+ *+ * Each <FILE> block contains info about its size, the name of the file it's+ * storing inside, and whether this FILE block is a continuation block of+ * previous archive ('split before'), and is this FILE block should be+ * continued in another archive ('split after'). By parsing the 'split before'+ * and 'split after' flags, we're able to tell if multiple <FILE> base blocks+ * are describing one file, or multiple files (with the same filename, for+ * example).+ *+ * One thing to note is that if we're parsing the first <FILE> block, and+ * we see 'split after' flag, then we need to jump over to another <FILE>+ * block to be able to decompress rest of the data. To do this, we need+ * to skip the <ENDARC> block, then switch to another file, then skip the+ * <magic> block, <MAIN> block, and then we're standing on the proper+ * <FILE> block.+ */++static int process_base_block(struct archive_read* a,+    struct archive_entry* entry)+{+	const size_t SMALLEST_RAR5_BLOCK_SIZE = 3;++	struct rar5* rar = get_context(a);+	uint32_t hdr_crc, computed_crc;+	size_t raw_hdr_size = 0, hdr_size_len, hdr_size;+	size_t header_id = 0;+	size_t header_flags = 0;+	const uint8_t* p;+	int ret;++	enum HEADER_TYPE {+		HEAD_MARK    = 0x00, HEAD_MAIN  = 0x01, HEAD_FILE   = 0x02,+		HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05,+		HEAD_UNKNOWN = 0xff,+	};++	/* Skip any unprocessed data for this file. */+	ret = skip_unprocessed_bytes(a);+	if(ret != ARCHIVE_OK)+		return ret;++	/* Read the expected CRC32 checksum. */+	if(!read_u32(a, &hdr_crc)) {+		return ARCHIVE_EOF;+	}++	/* Read header size. */+	if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) {+		return ARCHIVE_EOF;+	}++	hdr_size = raw_hdr_size + hdr_size_len;++	/* Sanity check, maximum header size for RAR5 is 2MB. */+	if(hdr_size > (2 * 1024 * 1024)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Base block header is too large");++		return ARCHIVE_FATAL;+	}++	/* Additional sanity checks to weed out invalid files. */+	if(raw_hdr_size == 0 || hdr_size_len == 0 ||+		hdr_size < SMALLEST_RAR5_BLOCK_SIZE)+	{+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Too small block encountered (%zu bytes)",+		    raw_hdr_size);++		return ARCHIVE_FATAL;+	}++	/* Read the whole header data into memory, maximum memory use here is+	 * 2MB. */+	if(!read_ahead(a, hdr_size, &p)) {+		return ARCHIVE_EOF;+	}++	/* Verify the CRC32 of the header data. */+	computed_crc = (uint32_t) crc32(0, p, (int) hdr_size);+	if(computed_crc != hdr_crc) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Header CRC error");++		return ARCHIVE_FATAL;+	}++	/* If the checksum is OK, we proceed with parsing. */+	if(ARCHIVE_OK != consume(a, hdr_size_len)) {+		return ARCHIVE_EOF;+	}++	if(!read_var_sized(a, &header_id, NULL))+		return ARCHIVE_EOF;++	if(!read_var_sized(a, &header_flags, NULL))+		return ARCHIVE_EOF;++	rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0;+	rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0;+	rar->generic.size = (int)hdr_size;+	rar->generic.last_header_id = (int)header_id;+	rar->main.endarc = 0;++	/* Those are possible header ids in RARv5. */+	switch(header_id) {+		case HEAD_MAIN:+			ret = process_head_main(a, rar, entry, header_flags);++			/* Main header doesn't have any files in it, so it's+			 * pointless to return to the caller. Retry to next+			 * header, which should be HEAD_FILE/HEAD_SERVICE. */+			if(ret == ARCHIVE_OK)+				return ARCHIVE_RETRY;++			return ret;+		case HEAD_SERVICE:+			ret = process_head_service(a, rar, entry, header_flags);+			return ret;+		case HEAD_FILE:+			ret = process_head_file(a, rar, entry, header_flags);+			return ret;+		case HEAD_CRYPT:+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Encryption is not supported");+			return ARCHIVE_FATAL;+		case HEAD_ENDARC:+			rar->main.endarc = 1;++			/* After encountering an end of file marker, we need+			 * to take into consideration if this archive is+			 * continued in another file (i.e. is it part01.rar:+			 * is there a part02.rar?) */+			if(rar->main.volume) {+				/* In case there is part02.rar, position the+				 * read pointer in a proper place, so we can+				 * resume parsing. */+				ret = scan_for_signature(a);+				if(ret == ARCHIVE_FATAL) {+					return ARCHIVE_EOF;+				} else {+					if(rar->vol.expected_vol_no ==+					    UINT_MAX) {+						archive_set_error(&a->archive,+						    ARCHIVE_ERRNO_FILE_FORMAT,+						    "Header error");+							return ARCHIVE_FATAL;+					}++					rar->vol.expected_vol_no =+					    rar->main.vol_no + 1;+					return ARCHIVE_OK;+				}+			} else {+				return ARCHIVE_EOF;+			}+		case HEAD_MARK:+			return ARCHIVE_EOF;+		default:+			if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Header type error");+				return ARCHIVE_FATAL;+			} else {+				/* If the block is marked as 'skip if unknown',+				 * do as the flag says: skip the block+				 * instead on failing on it. */+				return ARCHIVE_RETRY;+			}+	}++#if !defined WIN32+	// Not reached.+	archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+	    "Internal unpacker error");+	return ARCHIVE_FATAL;+#endif+}++static int skip_base_block(struct archive_read* a) {+	int ret;+	struct rar5* rar = get_context(a);++	/* Create a new local archive_entry structure that will be operated on+	 * by header reader; operations on this archive_entry will be discarded.+	 */+	struct archive_entry* entry = archive_entry_new();+	ret = process_base_block(a, entry);++	/* Discard operations on this archive_entry structure. */+	archive_entry_free(entry);+	if(ret == ARCHIVE_FATAL)+		return ret;++	if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0)+		return ARCHIVE_OK;++	if(ret == ARCHIVE_OK)+		return ARCHIVE_RETRY;+	else+		return ret;+}++static int rar5_read_header(struct archive_read *a,+    struct archive_entry *entry)+{+	struct rar5* rar = get_context(a);+	int ret;++	if(rar->header_initialized == 0) {+		init_header(a);+		rar->header_initialized = 1;+	}++	if(rar->skipped_magic == 0) {+		if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) {+			return ARCHIVE_EOF;+		}++		rar->skipped_magic = 1;+	}++	do {+		ret = process_base_block(a, entry);+	} while(ret == ARCHIVE_RETRY ||+			(rar->main.endarc > 0 && ret == ARCHIVE_OK));++	return ret;+}++static void init_unpack(struct rar5* rar) {+	rar->file.calculated_crc32 = 0;+	init_window_mask(rar);++	free(rar->cstate.window_buf);+	free(rar->cstate.filtered_buf);++	if(rar->cstate.window_size > 0) {+		rar->cstate.window_buf = calloc(1, rar->cstate.window_size);+		rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size);+	} else {+		rar->cstate.window_buf = NULL;+		rar->cstate.filtered_buf = NULL;+	}++	rar->cstate.write_ptr = 0;+	rar->cstate.last_write_ptr = 0;++	memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd));+	memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld));+	memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd));+	memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd));+	memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd));+}++static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) {+    int verify_crc;++	if(rar->skip_mode) {+#if defined CHECK_CRC_ON_SOLID_SKIP+		verify_crc = 1;+#else+		verify_crc = 0;+#endif+	} else+		verify_crc = 1;++	if(verify_crc) {+		/* Don't update CRC32 if the file doesn't have the+		 * `stored_crc32` info filled in. */+		if(rar->file.stored_crc32 > 0) {+			rar->file.calculated_crc32 =+				crc32(rar->file.calculated_crc32, p, to_read);+		}++		/* Check if the file uses an optional BLAKE2sp checksum+		 * algorithm. */+		if(rar->file.has_blake2 > 0) {+			/* Return value of the `update` function is always 0,+			 * so we can explicitly ignore it here. */+			(void) blake2sp_update(&rar->file.b2state, p, to_read);+		}+	}+}++static int create_decode_tables(uint8_t* bit_length,+    struct decode_table* table, int size)+{+	int code, upper_limit = 0, i, lc[16];+	uint32_t decode_pos_clone[rar5_countof(table->decode_pos)];+	ssize_t cur_len, quick_data_size;++	memset(&lc, 0, sizeof(lc));+	memset(table->decode_num, 0, sizeof(table->decode_num));+	table->size = size;+	table->quick_bits = size == HUFF_NC ? 10 : 7;++	for(i = 0; i < size; i++) {+		lc[bit_length[i] & 15]++;+	}++	lc[0] = 0;+	table->decode_pos[0] = 0;+	table->decode_len[0] = 0;++	for(i = 1; i < 16; i++) {+		upper_limit += lc[i];++		table->decode_len[i] = upper_limit << (16 - i);+		table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1];++		upper_limit <<= 1;+	}++	memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone));++	for(i = 0; i < size; i++) {+		uint8_t clen = bit_length[i] & 15;+		if(clen > 0) {+			int last_pos = decode_pos_clone[clen];+			table->decode_num[last_pos] = i;+			decode_pos_clone[clen]++;+		}+	}++	quick_data_size = (int64_t)1 << table->quick_bits;+	cur_len = 1;+	for(code = 0; code < quick_data_size; code++) {+		int bit_field = code << (16 - table->quick_bits);+		int dist, pos;++		while(cur_len < rar5_countof(table->decode_len) &&+				bit_field >= table->decode_len[cur_len]) {+			cur_len++;+		}++		table->quick_len[code] = (uint8_t) cur_len;++		dist = bit_field - table->decode_len[cur_len - 1];+		dist >>= (16 - cur_len);++		pos = table->decode_pos[cur_len & 15] + dist;+		if(cur_len < rar5_countof(table->decode_pos) && pos < size) {+			table->quick_num[code] = table->decode_num[pos];+		} else {+			table->quick_num[code] = 0;+		}+	}++	return ARCHIVE_OK;+}++static int decode_number(struct archive_read* a, struct decode_table* table,+    const uint8_t* p, uint16_t* num)+{+	int i, bits, dist;+	uint16_t bitfield;+	uint32_t pos;+	struct rar5* rar = get_context(a);++	if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) {+		return ARCHIVE_EOF;+	}++	bitfield &= 0xfffe;++	if(bitfield < table->decode_len[table->quick_bits]) {+		int code = bitfield >> (16 - table->quick_bits);+		skip_bits(rar, table->quick_len[code]);+		*num = table->quick_num[code];+		return ARCHIVE_OK;+	}++	bits = 15;++	for(i = table->quick_bits + 1; i < 15; i++) {+		if(bitfield < table->decode_len[i]) {+			bits = i;+			break;+		}+	}++	skip_bits(rar, bits);++	dist = bitfield - table->decode_len[bits - 1];+	dist >>= (16 - bits);+	pos = table->decode_pos[bits] + dist;++	if(pos >= table->size)+		pos = 0;++	*num = table->decode_num[pos];+	return ARCHIVE_OK;+}++/* Reads and parses Huffman tables from the beginning of the block. */+static int parse_tables(struct archive_read* a, struct rar5* rar,+    const uint8_t* p)+{+	int ret, value, i, w, idx = 0;+	uint8_t bit_length[HUFF_BC],+		table[HUFF_TABLE_SIZE],+		nibble_mask = 0xF0,+		nibble_shift = 4;++	enum { ESCAPE = 15 };++	/* The data for table generation is compressed using a simple RLE-like+	 * algorithm when storing zeroes, so we need to unpack it first. */+	for(w = 0, i = 0; w < HUFF_BC;) {+		if(i >= rar->cstate.cur_block_size) {+			/* Truncated data, can't continue. */+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated data in huffman tables");+			return ARCHIVE_FATAL;+		}++		value = (p[i] & nibble_mask) >> nibble_shift;++		if(nibble_mask == 0x0F)+			++i;++		nibble_mask ^= 0xFF;+		nibble_shift ^= 4;++		/* Values smaller than 15 is data, so we write it directly.+		 * Value 15 is a flag telling us that we need to unpack more+		 * bytes. */+		if(value == ESCAPE) {+			value = (p[i] & nibble_mask) >> nibble_shift;+			if(nibble_mask == 0x0F)+				++i;+			nibble_mask ^= 0xFF;+			nibble_shift ^= 4;++			if(value == 0) {+				/* We sometimes need to write the actual value+				 * of 15, so this case handles that. */+				bit_length[w++] = ESCAPE;+			} else {+				int k;++				/* Fill zeroes. */+				for(k = 0; (k < value + 2) && (w < HUFF_BC);+				    k++) {+					bit_length[w++] = 0;+				}+			}+		} else {+			bit_length[w++] = value;+		}+	}++	rar->bits.in_addr = i;+	rar->bits.bit_addr = nibble_shift ^ 4;++	ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC);+	if(ret != ARCHIVE_OK) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Decoding huffman tables failed");+		return ARCHIVE_FATAL;+	}++	for(i = 0; i < HUFF_TABLE_SIZE;) {+		uint16_t num;++		if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) {+			/* Truncated data, can't continue. */+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated data in huffman tables (#2)");+			return ARCHIVE_FATAL;+		}++		ret = decode_number(a, &rar->cstate.bd, p, &num);+		if(ret != ARCHIVE_OK) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Decoding huffman tables failed");+			return ARCHIVE_FATAL;+		}++		if(num < 16) {+			/* 0..15: store directly */+			table[i] = (uint8_t) num;+			i++;+		} else if(num < 18) {+			/* 16..17: repeat previous code */+			uint16_t n;++			if(ARCHIVE_OK != read_bits_16(rar, p, &n))+				return ARCHIVE_EOF;++			if(num == 16) {+				n >>= 13;+				n += 3;+				skip_bits(rar, 3);+			} else {+				n >>= 9;+				n += 11;+				skip_bits(rar, 7);+			}++			if(i > 0) {+				while(n-- > 0 && i < HUFF_TABLE_SIZE) {+					table[i] = table[i - 1];+					i++;+				}+			} else {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Unexpected error when decoding "+				    "huffman tables");+				return ARCHIVE_FATAL;+			}+		} else {+			/* other codes: fill with zeroes `n` times */+			uint16_t n;++			if(ARCHIVE_OK != read_bits_16(rar, p, &n))+				return ARCHIVE_EOF;++			if(num == 18) {+				n >>= 13;+				n += 3;+				skip_bits(rar, 3);+			} else {+				n >>= 9;+				n += 11;+				skip_bits(rar, 7);+			}++			while(n-- > 0 && i < HUFF_TABLE_SIZE)+				table[i++] = 0;+		}+	}++	ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC);+	if(ret != ARCHIVE_OK) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		     "Failed to create literal table");+		return ARCHIVE_FATAL;+	}++	idx += HUFF_NC;++	ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC);+	if(ret != ARCHIVE_OK) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Failed to create distance table");+		return ARCHIVE_FATAL;+	}++	idx += HUFF_DC;++	ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC);+	if(ret != ARCHIVE_OK) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Failed to create lower bits of distances table");+		return ARCHIVE_FATAL;+	}++	idx += HUFF_LDC;++	ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC);+	if(ret != ARCHIVE_OK) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Failed to create repeating distances table");+		return ARCHIVE_FATAL;+	}++	return ARCHIVE_OK;+}++/* Parses the block header, verifies its CRC byte, and saves the header+ * fields inside the `hdr` pointer. */+static int parse_block_header(struct archive_read* a, const uint8_t* p,+    ssize_t* block_size, struct compressed_block_header* hdr)+{+	uint8_t calculated_cksum;+	memcpy(hdr, p, sizeof(struct compressed_block_header));++	if(bf_byte_count(hdr) > 2) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unsupported block header size (was %d, max is 2)",+		    bf_byte_count(hdr));+		return ARCHIVE_FATAL;+	}++	/* This should probably use bit reader interface in order to be more+	 * future-proof. */+	*block_size = 0;+	switch(bf_byte_count(hdr)) {+		/* 1-byte block size */+		case 0:+			*block_size = *(const uint8_t*) &p[2];+			break;++		/* 2-byte block size */+		case 1:+			*block_size = archive_le16dec(&p[2]);+			break;++		/* 3-byte block size */+		case 2:+			*block_size = archive_le32dec(&p[2]);+			*block_size &= 0x00FFFFFF;+			break;++		/* Other block sizes are not supported. This case is not+		 * reached, because we have an 'if' guard before the switch+		 * that makes sure of it. */+		default:+			return ARCHIVE_FATAL;+	}++	/* Verify the block header checksum. 0x5A is a magic value and is+	 * always * constant. */+	calculated_cksum = 0x5A+	    ^ (uint8_t) hdr->block_flags_u8+	    ^ (uint8_t) *block_size+	    ^ (uint8_t) (*block_size >> 8)+	    ^ (uint8_t) (*block_size >> 16);++	if(calculated_cksum != hdr->block_cksum) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Block checksum error: got 0x%x, expected 0x%x",+		    hdr->block_cksum, calculated_cksum);++		return ARCHIVE_FATAL;+	}++	return ARCHIVE_OK;+}++/* Convenience function used during filter processing. */+static int parse_filter_data(struct rar5* rar, const uint8_t* p,+    uint32_t* filter_data)+{+	int i, bytes;+	uint32_t data = 0;++	if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes))+		return ARCHIVE_EOF;++	bytes++;++	for(i = 0; i < bytes; i++) {+		uint16_t byte;++		if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) {+			return ARCHIVE_EOF;+		}++		/* Cast to uint32_t will ensure the shift operation will not+		 * produce undefined result. */+		data += ((uint32_t) byte >> 8) << (i * 8);+		skip_bits(rar, 8);+	}++	*filter_data = data;+	return ARCHIVE_OK;+}++/* Function is used during sanity checking. */+static int is_valid_filter_block_start(struct rar5* rar,+    uint32_t start)+{+	const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr;+	const int64_t last_bs = rar->cstate.last_block_start;+	const ssize_t last_bl = rar->cstate.last_block_length;++	if(last_bs == 0 || last_bl == 0) {+		/* We didn't have any filters yet, so accept this offset. */+		return 1;+	}++	if(block_start >= last_bs + last_bl) {+		/* Current offset is bigger than last block's end offset, so+		 * accept current offset. */+		return 1;+	}++	/* Any other case is not a normal situation and we should fail. */+	return 0;+}++/* The function will create a new filter, read its parameters from the input+ * stream and add it to the filter collection. */+static int parse_filter(struct archive_read* ar, const uint8_t* p) {+	uint32_t block_start, block_length;+	uint16_t filter_type;+	struct filter_info* filt = NULL;+	struct rar5* rar = get_context(ar);++	/* Read the parameters from the input stream. */+	if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start))+		return ARCHIVE_EOF;++	if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length))+		return ARCHIVE_EOF;++	if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type))+		return ARCHIVE_EOF;++	filter_type >>= 13;+	skip_bits(rar, 3);++	/* Perform some sanity checks on this filter parameters. Note that we+	 * allow only DELTA, E8/E9 and ARM filters here, because rest of+	 * filters are not used in RARv5. */++	if(block_length < 4 ||+	    block_length > 0x400000 ||+	    filter_type > FILTER_ARM ||+	    !is_valid_filter_block_start(rar, block_start))+	{+		archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Invalid filter encountered");+		return ARCHIVE_FATAL;+	}++	/* Allocate a new filter. */+	filt = add_new_filter(rar);+	if(filt == NULL) {+		archive_set_error(&ar->archive, ENOMEM,+		    "Can't allocate memory for a filter descriptor.");+		return ARCHIVE_FATAL;+	}++	filt->type = filter_type;+	filt->block_start = rar->cstate.write_ptr + block_start;+	filt->block_length = block_length;++	rar->cstate.last_block_start = filt->block_start;+	rar->cstate.last_block_length = filt->block_length;++	/* Read some more data in case this is a DELTA filter. Other filter+	 * types don't require any additional data over what was already+	 * read. */+	if(filter_type == FILTER_DELTA) {+		int channels;++		if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels))+			return ARCHIVE_EOF;++		filt->channels = channels + 1;+	}++	return ARCHIVE_OK;+}++static int decode_code_length(struct rar5* rar, const uint8_t* p,+    uint16_t code)+{+	int lbits, length = 2;+	if(code < 8) {+		lbits = 0;+		length += code;+	} else {+		lbits = code / 4 - 1;+		length += (4 | (code & 3)) << lbits;+	}++	if(lbits > 0) {+		int add;++		if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add))+			return -1;++		length += add;+	}++	return length;+}++static int copy_string(struct archive_read* a, int len, int dist) {+	struct rar5* rar = get_context(a);+	const uint64_t cmask = rar->cstate.window_mask;+	const uint64_t write_ptr = rar->cstate.write_ptr ++	    rar->cstate.solid_offset;+	int i;++	if (rar->cstate.window_buf == NULL)+		return ARCHIVE_FATAL;++	/* The unpacker spends most of the time in this function. It would be+	 * a good idea to introduce some optimizations here.+	 *+	 * Just remember that this loop treats buffers that overlap differently+	 * than buffers that do not overlap. This is why a simple memcpy(3)+	 * call will not be enough. */++	for(i = 0; i < len; i++) {+		const ssize_t write_idx = (write_ptr + i) & cmask;+		const ssize_t read_idx = (write_ptr + i - dist) & cmask;+		rar->cstate.window_buf[write_idx] =+		    rar->cstate.window_buf[read_idx];+	}++	rar->cstate.write_ptr += len;+	return ARCHIVE_OK;+}++static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {+	struct rar5* rar = get_context(a);+	uint16_t num;+	int ret;++	const uint64_t cmask = rar->cstate.window_mask;+	const struct compressed_block_header* hdr = &rar->last_block_hdr;+	const uint8_t bit_size = 1 + bf_bit_size(hdr);++	while(1) {+		if(rar->cstate.write_ptr - rar->cstate.last_write_ptr >+		    (rar->cstate.window_size >> 1)) {+			/* Don't allow growing data by more than half of the+			 * window size at a time. In such case, break the loop;+			 *  next call to this function will continue processing+			 *  from this moment. */+			break;+		}++		if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 ||+		    (rar->bits.in_addr == rar->cstate.cur_block_size - 1 &&+		    rar->bits.bit_addr >= bit_size))+		{+			/* If the program counter is here, it means the+			 * function has finished processing the block. */+			rar->cstate.block_parsing_finished = 1;+			break;+		}++		/* Decode the next literal. */+		if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) {+			return ARCHIVE_EOF;+		}++		/* Num holds a decompression literal, or 'command code'.+		 *+		 * - Values lower than 256 are just bytes. Those codes+		 *   can be stored in the output buffer directly.+		 *+		 * - Code 256 defines a new filter, which is later used to+		 *   ransform the data block accordingly to the filter type.+		 *   The data block needs to be fully uncompressed first.+		 *+		 * - Code bigger than 257 and smaller than 262 define+		 *   a repetition pattern that should be copied from+		 *   an already uncompressed chunk of data.+		 */++		if(num < 256) {+			/* Directly store the byte. */+			int64_t write_idx = rar->cstate.solid_offset ++			    rar->cstate.write_ptr++;++			rar->cstate.window_buf[write_idx & cmask] =+			    (uint8_t) num;+			continue;+		} else if(num >= 262) {+			uint16_t dist_slot;+			int len = decode_code_length(rar, p, num - 262),+				dbits,+				dist = 1;++			if(len == -1) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_PROGRAMMER,+				    "Failed to decode the code length");++				return ARCHIVE_FATAL;+			}++			if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p,+			    &dist_slot))+			{+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_PROGRAMMER,+				    "Failed to decode the distance slot");++				return ARCHIVE_FATAL;+			}++			if(dist_slot < 4) {+				dbits = 0;+				dist += dist_slot;+			} else {+				dbits = dist_slot / 2 - 1;++				/* Cast to uint32_t will make sure the shift+				 * left operation won't produce undefined+				 * result. Then, the uint32_t type will+				 * be implicitly casted to int. */+				dist += (uint32_t) (2 |+				    (dist_slot & 1)) << dbits;+			}++			if(dbits > 0) {+				if(dbits >= 4) {+					uint32_t add = 0;+					uint16_t low_dist;++					if(dbits > 4) {+						if(ARCHIVE_OK != read_bits_32(+						    rar, p, &add)) {+							/* Return EOF if we+							 * can't read more+							 * data. */+							return ARCHIVE_EOF;+						}++						skip_bits(rar, dbits - 4);+						add = (add >> (+						    36 - dbits)) << 4;+						dist += add;+					}++					if(ARCHIVE_OK != decode_number(a,+					    &rar->cstate.ldd, p, &low_dist))+					{+						archive_set_error(&a->archive,+						    ARCHIVE_ERRNO_PROGRAMMER,+						    "Failed to decode the "+						    "distance slot");++						return ARCHIVE_FATAL;+					}++					if(dist >= INT_MAX - low_dist - 1) {+						/* This only happens in+						 * invalid archives. */+						archive_set_error(&a->archive,+						    ARCHIVE_ERRNO_FILE_FORMAT,+						    "Distance pointer "+						    "overflow");+						return ARCHIVE_FATAL;+					}++					dist += low_dist;+				} else {+					/* dbits is one of [0,1,2,3] */+					int add;++					if(ARCHIVE_OK != read_consume_bits(rar,+					     p, dbits, &add)) {+						/* Return EOF if we can't read+						 * more data. */+						return ARCHIVE_EOF;+					}++					dist += add;+				}+			}++			if(dist > 0x100) {+				len++;++				if(dist > 0x2000) {+					len++;++					if(dist > 0x40000) {+						len++;+					}+				}+			}++			dist_cache_push(rar, dist);+			rar->cstate.last_len = len;++			if(ARCHIVE_OK != copy_string(a, len, dist))+				return ARCHIVE_FATAL;++			continue;+		} else if(num == 256) {+			/* Create a filter. */+			ret = parse_filter(a, p);+			if(ret != ARCHIVE_OK)+				return ret;++			continue;+		} else if(num == 257) {+			if(rar->cstate.last_len != 0) {+				if(ARCHIVE_OK != copy_string(a,+				    rar->cstate.last_len,+				    rar->cstate.dist_cache[0]))+				{+					return ARCHIVE_FATAL;+				}+			}++			continue;+		} else {+			/* num < 262 */+			const int idx = num - 258;+			const int dist = dist_cache_touch(rar, idx);++			uint16_t len_slot;+			int len;++			if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p,+			    &len_slot)) {+				return ARCHIVE_FATAL;+			}++			len = decode_code_length(rar, p, len_slot);+			rar->cstate.last_len = len;++			if(ARCHIVE_OK != copy_string(a, len, dist))+				return ARCHIVE_FATAL;++			continue;+		}+	}++	return ARCHIVE_OK;+}++/* Binary search for the RARv5 signature. */+static int scan_for_signature(struct archive_read* a) {+	const uint8_t* p;+	const int chunk_size = 512;+	ssize_t i;+	char signature[sizeof(rar5_signature_xor)];++	/* If we're here, it means we're on an 'unknown territory' data.+	 * There's no indication what kind of data we're reading here.+	 * It could be some text comment, any kind of binary data,+	 * digital sign, dragons, etc.+	 *+	 * We want to find a valid RARv5 magic header inside this unknown+	 * data. */++	/* Is it possible in libarchive to just skip everything until the+	 * end of the file? If so, it would be a better approach than the+	 * current implementation of this function. */++	rar5_signature(signature);++	while(1) {+		if(!read_ahead(a, chunk_size, &p))+			return ARCHIVE_EOF;++		for(i = 0; i < chunk_size - (int)sizeof(rar5_signature_xor);+		    i++) {+			if(memcmp(&p[i], signature,+			    sizeof(rar5_signature_xor)) == 0) {+				/* Consume the number of bytes we've used to+				 * search for the signature, as well as the+				 * number of bytes used by the signature+				 * itself. After this we should be standing+				 * on a valid base block header. */+				(void) consume(a,+				    i + sizeof(rar5_signature_xor));+				return ARCHIVE_OK;+			}+		}++		consume(a, chunk_size);+	}++	return ARCHIVE_FATAL;+}++/* This function will switch the multivolume archive file to another file,+ * i.e. from part03 to part 04. */+static int advance_multivolume(struct archive_read* a) {+	int lret;+	struct rar5* rar = get_context(a);++	/* A small state machine that will skip unnecessary data, needed to+	 * switch from one multivolume to another. Such skipping is needed if+	 * we want to be an stream-oriented (instead of file-oriented)+	 * unpacker.+	 *+	 * The state machine starts with `rar->main.endarc` == 0. It also+	 * assumes that current stream pointer points to some base block+	 * header.+	 *+	 * The `endarc` field is being set when the base block parsing+	 * function encounters the 'end of archive' marker.+	 */++	while(1) {+		if(rar->main.endarc == 1) {+			int looping = 1;++			rar->main.endarc = 0;++			while(looping) {+				lret = skip_base_block(a);+				switch(lret) {+					case ARCHIVE_RETRY:+						/* Continue looping. */+						break;+					case ARCHIVE_OK:+						/* Break loop. */+						looping = 0;+						break;+					default:+						/* Forward any errors to the+						 * caller. */+						return lret;+				}+			}++			break;+		} else {+			/* Skip current base block. In order to properly skip+			 * it, we really need to simply parse it and discard+			 * the results. */++			lret = skip_base_block(a);+			if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED)+				return lret;++			/* The `skip_base_block` function tells us if we+			 * should continue with skipping, or we should stop+			 * skipping. We're trying to skip everything up to+			 * a base FILE block. */++			if(lret != ARCHIVE_RETRY) {+				/* If there was an error during skipping, or we+				 * have just skipped a FILE base block... */++				if(rar->main.endarc == 0) {+					return lret;+				} else {+					continue;+				}+			}+		}+	}++	return ARCHIVE_OK;+}++/* Merges the partial block from the first multivolume archive file, and+ * partial block from the second multivolume archive file. The result is+ * a chunk of memory containing the whole block, and the stream pointer+ * is advanced to the next block in the second multivolume archive file. */+static int merge_block(struct archive_read* a, ssize_t block_size,+    const uint8_t** p)+{+	struct rar5* rar = get_context(a);+	ssize_t cur_block_size, partial_offset = 0;+	const uint8_t* lp;+	int ret;++	if(rar->merge_mode) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+		    "Recursive merge is not allowed");++		return ARCHIVE_FATAL;+	}++	/* Set a flag that we're in the switching mode. */+	rar->cstate.switch_multivolume = 1;++	/* Reallocate the memory which will hold the whole block. */+	if(rar->vol.push_buf)+		free((void*) rar->vol.push_buf);++	/* Increasing the allocation block by 8 is due to bit reading functions,+	 * which are using additional 2 or 4 bytes. Allocating the block size+	 * by exact value would make bit reader perform reads from invalid+	 * memory block when reading the last byte from the buffer. */+	rar->vol.push_buf = malloc(block_size + 8);+	if(!rar->vol.push_buf) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory for a merge block buffer.");+		return ARCHIVE_FATAL;+	}++	/* Valgrind complains if the extension block for bit reader is not+	 * initialized, so initialize it. */+	memset(&rar->vol.push_buf[block_size], 0, 8);++	/* A single block can span across multiple multivolume archive files,+	 * so we use a loop here. This loop will consume enough multivolume+	 * archive files until the whole block is read. */++	while(1) {+		/* Get the size of current block chunk in this multivolume+		 * archive file and read it. */+		cur_block_size = rar5_min(rar->file.bytes_remaining,+		    block_size - partial_offset);++		if(cur_block_size == 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Encountered block size == 0 during block merge");+			return ARCHIVE_FATAL;+		}++		if(!read_ahead(a, cur_block_size, &lp))+			return ARCHIVE_EOF;++		/* Sanity check; there should never be a situation where this+		 * function reads more data than the block's size. */+		if(partial_offset + cur_block_size > block_size) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_PROGRAMMER,+			    "Consumed too much data when merging blocks.");+			return ARCHIVE_FATAL;+		}++		/* Merge previous block chunk with current block chunk,+		 * or create first block chunk if this is our first+		 * iteration. */+		memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size);++		/* Advance the stream read pointer by this block chunk size. */+		if(ARCHIVE_OK != consume(a, cur_block_size))+			return ARCHIVE_EOF;++		/* Update the pointers. `partial_offset` contains information+		 * about the sum of merged block chunks. */+		partial_offset += cur_block_size;+		rar->file.bytes_remaining -= cur_block_size;++		/* If `partial_offset` is the same as `block_size`, this means+		 * we've merged all block chunks and we have a valid full+		 * block. */+		if(partial_offset == block_size) {+			break;+		}++		/* If we don't have any bytes to read, this means we should+		 * switch to another multivolume archive file. */+		if(rar->file.bytes_remaining == 0) {+			rar->merge_mode++;+			ret = advance_multivolume(a);+			rar->merge_mode--;+			if(ret != ARCHIVE_OK) {+				return ret;+			}+		}+	}++	*p = rar->vol.push_buf;++	/* If we're here, we can resume unpacking by processing the block+	 * pointed to by the `*p` memory pointer. */++	return ARCHIVE_OK;+}++static int process_block(struct archive_read* a) {+	const uint8_t* p;+	struct rar5* rar = get_context(a);+	int ret;++	/* If we don't have any data to be processed, this most probably means+	 * we need to switch to the next volume. */+	if(rar->main.volume && rar->file.bytes_remaining == 0) {+		ret = advance_multivolume(a);+		if(ret != ARCHIVE_OK)+			return ret;+	}++	if(rar->cstate.block_parsing_finished) {+		ssize_t block_size;+		ssize_t to_skip;+		ssize_t cur_block_size;++		/* The header size won't be bigger than 6 bytes. */+		if(!read_ahead(a, 6, &p)) {+			/* Failed to prefetch data block header. */+			return ARCHIVE_EOF;+		}++		/*+		 * Read block_size by parsing block header. Validate the header+		 * by calculating CRC byte stored inside the header. Size of+		 * the header is not constant (block size can be stored either+		 * in 1 or 2 bytes), that's why block size is left out from the+		 * `compressed_block_header` structure and returned by+		 * `parse_block_header` as the second argument. */++		ret = parse_block_header(a, p, &block_size,+		    &rar->last_block_hdr);+		if(ret != ARCHIVE_OK) {+			return ret;+		}++		/* Skip block header. Next data is huffman tables,+		 * if present. */+		to_skip = sizeof(struct compressed_block_header) ++			bf_byte_count(&rar->last_block_hdr) + 1;++		if(ARCHIVE_OK != consume(a, to_skip))+			return ARCHIVE_EOF;++		rar->file.bytes_remaining -= to_skip;++		/* The block size gives information about the whole block size,+		 * but the block could be stored in split form when using+		 * multi-volume archives. In this case, the block size will be+		 * bigger than the actual data stored in this file. Remaining+		 * part of the data will be in another file. */++		cur_block_size =+			rar5_min(rar->file.bytes_remaining, block_size);++		if(block_size > rar->file.bytes_remaining) {+			/* If current blocks' size is bigger than our data+			 * size, this means we have a multivolume archive.+			 * In this case, skip all base headers until the end+			 * of the file, proceed to next "partXXX.rar" volume,+			 * find its signature, skip all headers up to the first+			 * FILE base header, and continue from there.+			 *+			 * Note that `merge_block` will update the `rar`+			 * context structure quite extensively. */++			ret = merge_block(a, block_size, &p);+			if(ret != ARCHIVE_OK) {+				return ret;+			}++			cur_block_size = block_size;++			/* Current stream pointer should be now directly+			 * *after* the block that spanned through multiple+			 * archive files. `p` pointer should have the data of+			 * the *whole* block (merged from partial blocks+			 * stored in multiple archives files). */+		} else {+			rar->cstate.switch_multivolume = 0;++			/* Read the whole block size into memory. This can take+			 * up to  8 megabytes of memory in theoretical cases.+			 * Might be worth to optimize this and use a standard+			 * chunk of 4kb's. */+			if(!read_ahead(a, 4 + cur_block_size, &p)) {+				/* Failed to prefetch block data. */+				return ARCHIVE_EOF;+			}+		}++		rar->cstate.block_buf = p;+		rar->cstate.cur_block_size = cur_block_size;+		rar->cstate.block_parsing_finished = 0;++		rar->bits.in_addr = 0;+		rar->bits.bit_addr = 0;++		if(bf_is_table_present(&rar->last_block_hdr)) {+			/* Load Huffman tables. */+			ret = parse_tables(a, rar, p);+			if(ret != ARCHIVE_OK) {+				/* Error during decompression of Huffman+				 * tables. */+				return ret;+			}+		}+	} else {+		/* Block parsing not finished, reuse previous memory buffer. */+		p = rar->cstate.block_buf;+	}++	/* Uncompress the block, or a part of it, depending on how many bytes+	 * will be generated by uncompressing the block.+	 *+	 * In case too many bytes will be generated, calling this function+	 * again will resume the uncompression operation. */+	ret = do_uncompress_block(a, p);+	if(ret != ARCHIVE_OK) {+		return ret;+	}++	if(rar->cstate.block_parsing_finished &&+	    rar->cstate.switch_multivolume == 0 &&+	    rar->cstate.cur_block_size > 0)+	{+		/* If we're processing a normal block, consume the whole+		 * block. We can do this because we've already read the whole+		 * block to memory. */+		if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size))+			return ARCHIVE_FATAL;++		rar->file.bytes_remaining -= rar->cstate.cur_block_size;+	} else if(rar->cstate.switch_multivolume) {+		/* Don't consume the block if we're doing multivolume+		 * processing. The volume switching function will consume+		 * the proper count of bytes instead. */+		rar->cstate.switch_multivolume = 0;+	}++	return ARCHIVE_OK;+}++/* Pops the `buf`, `size` and `offset` from the "data ready" stack.+ *+ * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY+ * when there is no data on the stack. */+static int use_data(struct rar5* rar, const void** buf, size_t* size,+    int64_t* offset)+{+	int i;++	for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {+		struct data_ready *d = &rar->cstate.dready[i];++		if(d->used) {+			if(buf)    *buf = d->buf;+			if(size)   *size = d->size;+			if(offset) *offset = d->offset;++			d->used = 0;+			return ARCHIVE_OK;+		}+	}++	return ARCHIVE_RETRY;+}++/* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready+ * FIFO stack. Those values will be popped from this stack by the `use_data`+ * function. */+static int push_data_ready(struct archive_read* a, struct rar5* rar,+    const uint8_t* buf, size_t size, int64_t offset)+{+	int i;++	/* Don't push if we're in skip mode. This is needed because solid+	 * streams need full processing even if we're skipping data. After+	 * fully processing the stream, we need to discard the generated bytes,+	 * because we're interested only in the side effect: building up the+	 * internal window circular buffer. This window buffer will be used+	 * later during unpacking of requested data. */+	if(rar->skip_mode)+		return ARCHIVE_OK;++	/* Sanity check. */+	if(offset != rar->file.last_offset + rar->file.last_size) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+		    "Sanity check error: output stream is not continuous");+		return ARCHIVE_FATAL;+	}++	for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {+		struct data_ready* d = &rar->cstate.dready[i];+		if(!d->used) {+			d->used = 1;+			d->buf = buf;+			d->size = size;+			d->offset = offset;++			/* These fields are used only in sanity checking. */+			rar->file.last_offset = offset;+			rar->file.last_size = size;++			/* Calculate the checksum of this new block before+			 * submitting data to libarchive's engine. */+			update_crc(rar, d->buf, d->size);++			return ARCHIVE_OK;+		}+	}++	/* Program counter will reach this code if the `rar->cstate.data_ready`+	 * stack will be filled up so that no new entries will be allowed. The+	 * code shouldn't allow such situation to occur. So we treat this case+	 * as an internal error. */++	archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+	    "Error: premature end of data_ready stack");+	return ARCHIVE_FATAL;+}++/* This function uncompresses the data that is stored in the <FILE> base+ * block.+ *+ * The FILE base block looks like this:+ *+ * <header><huffman tables><block_1><block_2>...<block_n>+ *+ * The <header> is a block header, that is parsed in parse_block_header().+ * It's a "compressed_block_header" structure, containing metadata needed+ * to know when we should stop looking for more <block_n> blocks.+ *+ * <huffman tables> contain data needed to set up the huffman tables, needed+ * for the actual decompression.+ *+ * Each <block_n> consists of series of literals:+ *+ * <literal><literal><literal>...<literal>+ *+ * Those literals generate the uncompression data. They operate on a circular+ * buffer, sometimes writing raw data into it, sometimes referencing+ * some previous data inside this buffer, and sometimes declaring a filter+ * that will need to be executed on the data stored in the circular buffer.+ * It all depends on the literal that is used.+ *+ * Sometimes blocks produce output data, sometimes they don't. For example, for+ * some huge files that use lots of filters, sometimes a block is filled with+ * only filter declaration literals. Such blocks won't produce any data in the+ * circular buffer.+ *+ * Sometimes blocks will produce 4 bytes of data, and sometimes 1 megabyte,+ * because a literal can reference previously decompressed data. For example,+ * there can be a literal that says: 'append a byte 0xFE here', and after+ * it another literal can say 'append 1 megabyte of data from circular buffer+ * offset 0x12345'. This is how RAR format handles compressing repeated+ * patterns.+ *+ * The RAR compressor creates those literals and the actual efficiency of+ * compression depends on what those literals are. The literals can also+ * be seen as a kind of a non-turing-complete virtual machine that simply+ * tells the decompressor what it should do.+ * */++static int do_uncompress_file(struct archive_read* a) {+	struct rar5* rar = get_context(a);+	int ret;+	int64_t max_end_pos;++	if(!rar->cstate.initialized) {+		/* Don't perform full context reinitialization if we're+		 * processing a solid archive. */+		if(!rar->main.solid || !rar->cstate.window_buf) {+			init_unpack(rar);+		}++		rar->cstate.initialized = 1;+	}++	if(rar->cstate.all_filters_applied == 1) {+		/* We use while(1) here, but standard case allows for just 1+		 * iteration. The loop will iterate if process_block() didn't+		 * generate any data at all. This can happen if the block+		 * contains only filter definitions (this is common in big+		 * files). */+		while(1) {+			ret = process_block(a);+			if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL)+				return ret;++			if(rar->cstate.last_write_ptr ==+			    rar->cstate.write_ptr) {+				/* The block didn't generate any new data,+				 * so just process a new block. */+				continue;+			}++			/* The block has generated some new data, so break+			 * the loop. */+			break;+		}+	}++	/* Try to run filters. If filters won't be applied, it means that+	 * insufficient data was generated. */+	ret = apply_filters(a);+	if(ret == ARCHIVE_RETRY) {+		return ARCHIVE_OK;+	} else if(ret == ARCHIVE_FATAL) {+		return ARCHIVE_FATAL;+	}++	/* If apply_filters() will return ARCHIVE_OK, we can continue here. */++	if(cdeque_size(&rar->cstate.filters) > 0) {+		/* Check if we can write something before hitting first+		 * filter. */+		struct filter_info* flt;++		/* Get the block_start offset from the first filter. */+		if(CDE_OK != cdeque_front(&rar->cstate.filters,+		    cdeque_filter_p(&flt)))+		{+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_PROGRAMMER,+			    "Can't read first filter");+			return ARCHIVE_FATAL;+		}++		max_end_pos = rar5_min(flt->block_start,+		    rar->cstate.write_ptr);+	} else {+		/* There are no filters defined, or all filters were applied.+		 * This means we can just store the data without any+		 * postprocessing. */+		max_end_pos = rar->cstate.write_ptr;+	}++	if(max_end_pos == rar->cstate.last_write_ptr) {+		/* We can't write anything yet. The block uncompression+		 * function did not generate enough data, and no filter can be+		 * applied. At the same time we don't have any data that can be+		 *  stored without filter postprocessing. This means we need to+		 *  wait for more data to be generated, so we can apply the+		 * filters.+		 *+		 * Signal the caller that we need more data to be able to do+		 * anything.+		 */+		return ARCHIVE_RETRY;+	} else {+		/* We can write the data before hitting the first filter.+		 * So let's do it. The push_window_data() function will+		 * effectively return the selected data block to the user+		 * application. */+		push_window_data(a, rar, rar->cstate.last_write_ptr,+		    max_end_pos);+		rar->cstate.last_write_ptr = max_end_pos;+	}++	return ARCHIVE_OK;+}++static int uncompress_file(struct archive_read* a) {+	int ret;++	while(1) {+		/* Sometimes the uncompression function will return a+		 * 'retry' signal. If this will happen, we have to retry+		 * the function. */+		ret = do_uncompress_file(a);+		if(ret != ARCHIVE_RETRY)+			return ret;+	}+}+++static int do_unstore_file(struct archive_read* a,+    struct rar5* rar, const void** buf, size_t* size, int64_t* offset)+{+	size_t to_read;+	const uint8_t* p;++	if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 &&+	    rar->generic.split_after > 0)+	{+		int ret;++		rar->cstate.switch_multivolume = 1;+		ret = advance_multivolume(a);+		rar->cstate.switch_multivolume = 0;++		if(ret != ARCHIVE_OK) {+			/* Failed to advance to next multivolume archive+			 * file. */+			return ret;+		}+	}++	to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024);+	if(to_read == 0) {+		return ARCHIVE_EOF;+	}++	if(!read_ahead(a, to_read, &p)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "I/O error when unstoring file");+		return ARCHIVE_FATAL;+	}++	if(ARCHIVE_OK != consume(a, to_read)) {+		return ARCHIVE_EOF;+	}++	if(buf)    *buf = p;+	if(size)   *size = to_read;+	if(offset) *offset = rar->cstate.last_unstore_ptr;++	rar->file.bytes_remaining -= to_read;+	rar->cstate.last_unstore_ptr += to_read;++	update_crc(rar, p, to_read);+	return ARCHIVE_OK;+}++static int do_unpack(struct archive_read* a, struct rar5* rar,+    const void** buf, size_t* size, int64_t* offset)+{+	enum COMPRESSION_METHOD {+		STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4,+		BEST = 5+	};++	if(rar->file.service > 0) {+		return do_unstore_file(a, rar, buf, size, offset);+	} else {+		switch(rar->cstate.method) {+			case STORE:+				return do_unstore_file(a, rar, buf, size,+				    offset);+			case FASTEST:+				/* fallthrough */+			case FAST:+				/* fallthrough */+			case NORMAL:+				/* fallthrough */+			case GOOD:+				/* fallthrough */+			case BEST:+				return uncompress_file(a);+			default:+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Compression method not supported: 0x%x",+				    rar->cstate.method);++				return ARCHIVE_FATAL;+		}+	}++#if !defined WIN32+	/* Not reached. */+	return ARCHIVE_OK;+#endif+}++static int verify_checksums(struct archive_read* a) {+	int verify_crc;+	struct rar5* rar = get_context(a);++	/* Check checksums only when actually unpacking the data. There's no+	 * need to calculate checksum when we're skipping data in solid archives+	 * (skipping in solid archives is the same thing as unpacking compressed+	 * data and discarding the result). */++	if(!rar->skip_mode) {+		/* Always check checksums if we're not in skip mode */+		verify_crc = 1;+	} else {+		/* We can override the logic above with a compile-time option+		 * NO_CRC_ON_SOLID_SKIP. This option is used during debugging,+		 * and it will check checksums of unpacked data even when+		 * we're skipping it. */++#if defined CHECK_CRC_ON_SOLID_SKIP+		/* Debug case */+		verify_crc = 1;+#else+		/* Normal case */+		verify_crc = 0;+#endif+	}++	if(verify_crc) {+		/* During unpacking, on each unpacked block we're calling the+		 * update_crc() function. Since we are here, the unpacking+		 * process is already over and we can check if calculated+		 * checksum (CRC32 or BLAKE2sp) is the same as what is stored+		 * in the archive. */+		if(rar->file.stored_crc32 > 0) {+			/* Check CRC32 only when the file contains a CRC32+			 * value for this file. */++			if(rar->file.calculated_crc32 !=+			    rar->file.stored_crc32) {+				/* Checksums do not match; the unpacked file+				 * is corrupted. */++				DEBUG_CODE {+					printf("Checksum error: CRC32 "+					    "(was: %08x, expected: %08x)\n",+					    rar->file.calculated_crc32,+					    rar->file.stored_crc32);+				}++#ifndef DONT_FAIL_ON_CRC_ERROR+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Checksum error: CRC32");+				return ARCHIVE_FATAL;+#endif+			} else {+				DEBUG_CODE {+					printf("Checksum OK: CRC32 "+					    "(%08x/%08x)\n",+					    rar->file.stored_crc32,+					    rar->file.calculated_crc32);+				}+			}+		}++		if(rar->file.has_blake2 > 0) {+			/* BLAKE2sp is an optional checksum algorithm that is+			 * added to RARv5 archives when using the `-htb` switch+			 *  during creation of archive.+			 *+			 * We now finalize the hash calculation by calling the+			 * `final` function. This will generate the final hash+			 * value we can use to compare it with the BLAKE2sp+			 * checksum that is stored in the archive.+			 *+			 * The return value of this `final` function is not+			 * very helpful, as it guards only against improper use.+ 			 * This is why we're explicitly ignoring it. */++			uint8_t b2_buf[32];+			(void) blake2sp_final(&rar->file.b2state, b2_buf, 32);++			if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) {+#ifndef DONT_FAIL_ON_CRC_ERROR+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Checksum error: BLAKE2");++				return ARCHIVE_FATAL;+#endif+			}+		}+	}++	/* Finalization for this file has been successfully completed. */+	return ARCHIVE_OK;+}++static int verify_global_checksums(struct archive_read* a) {+	return verify_checksums(a);+}++/*+ * Decryption function for the magic signature pattern. Check the comment near+ * the `rar5_signature_xor` symbol to read the rationale behind this.+ */+static void rar5_signature(char *buf) {+		size_t i;++		for(i = 0; i < sizeof(rar5_signature_xor); i++) {+			buf[i] = rar5_signature_xor[i] ^ 0xA1;+		}+}++static int rar5_read_data(struct archive_read *a, const void **buff,+    size_t *size, int64_t *offset) {+	int ret;+	struct rar5* rar = get_context(a);++	if(rar->file.dir > 0) {+		/* Don't process any data if this file entry was declared+		 * as a directory. This is needed, because entries marked as+		 * directory doesn't have any dictionary buffer allocated, so+		 * it's impossible to perform any decompression. */+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't decompress an entry marked as a directory");+		return ARCHIVE_FAILED;+	}++	if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+		    "Unpacker has written too many bytes");+		return ARCHIVE_FATAL;+	}++	ret = use_data(rar, buff, size, offset);+	if(ret == ARCHIVE_OK) {+		return ret;+	}++	if(rar->file.eof == 1) {+		return ARCHIVE_EOF;+	}++	ret = do_unpack(a, rar, buff, size, offset);+	if(ret != ARCHIVE_OK) {+		return ret;+	}++	if(rar->file.bytes_remaining == 0 &&+			rar->cstate.last_write_ptr == rar->file.unpacked_size)+	{+		/* If all bytes of current file were processed, run+		 * finalization.+		 *+		 * Finalization will check checksum against proper values. If+		 * some of the checksums will not match, we'll return an error+		 * value in the last `archive_read_data` call to signal an error+		 * to the user. */++		rar->file.eof = 1;+		return verify_global_checksums(a);+	}++	return ARCHIVE_OK;+}++static int rar5_read_data_skip(struct archive_read *a) {+	struct rar5* rar = get_context(a);++	if(rar->main.solid) {+		/* In solid archives, instead of skipping the data, we need to+		 * extract it, and dispose the result. The side effect of this+		 * operation will be setting up the initial window buffer state+		 * needed to be able to extract the selected file. */++		int ret;++		/* Make sure to process all blocks in the compressed stream. */+		while(rar->file.bytes_remaining > 0) {+			/* Setting the "skip mode" will allow us to skip+			 * checksum checks during data skipping. Checking the+			 * checksum of skipped data isn't really necessary and+			 * it's only slowing things down.+			 *+			 * This is incremented instead of setting to 1 because+			 * this data skipping function can be called+			 * recursively. */+			rar->skip_mode++;++			/* We're disposing 1 block of data, so we use triple+			 * NULLs in arguments. */+			ret = rar5_read_data(a, NULL, NULL, NULL);++			/* Turn off "skip mode". */+			rar->skip_mode--;++			if(ret < 0 || ret == ARCHIVE_EOF) {+				/* Propagate any potential error conditions+				 * to the caller. */+				return ret;+			}+		}+	} else {+		/* In standard archives, we can just jump over the compressed+		 * stream. Each file in non-solid archives starts from an empty+		 * window buffer. */++		if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) {+			return ARCHIVE_FATAL;+		}++		rar->file.bytes_remaining = 0;+	}++	return ARCHIVE_OK;+}++static int64_t rar5_seek_data(struct archive_read *a, int64_t offset,+    int whence)+{+	(void) a;+	(void) offset;+	(void) whence;++	/* We're a streaming unpacker, and we don't support seeking. */++	return ARCHIVE_FATAL;+}++static int rar5_cleanup(struct archive_read *a) {+	struct rar5* rar = get_context(a);++	free(rar->cstate.window_buf);+	free(rar->cstate.filtered_buf);++	free(rar->vol.push_buf);++	free_filters(rar);+	cdeque_free(&rar->cstate.filters);++	free(rar);+	a->format->data = NULL;++	return ARCHIVE_OK;+}++static int rar5_capabilities(struct archive_read * a) {+	(void) a;+	return 0;+}++static int rar5_has_encrypted_entries(struct archive_read *_a) {+	(void) _a;++	/* Unsupported for now. */+	return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED;+}++static int rar5_init(struct rar5* rar) {+	memset(rar, 0, sizeof(struct rar5));++	if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192))+		return ARCHIVE_FATAL;++	return ARCHIVE_OK;+}++int archive_read_support_format_rar5(struct archive *_a) {+	struct archive_read* ar;+	int ret;+	struct rar5* rar;++	if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar)))+		return ret;++	rar = malloc(sizeof(*rar));+	if(rar == NULL) {+		archive_set_error(&ar->archive, ENOMEM,+		    "Can't allocate rar5 data");+		return ARCHIVE_FATAL;+	}++	if(ARCHIVE_OK != rar5_init(rar)) {+		archive_set_error(&ar->archive, ENOMEM,+		    "Can't allocate rar5 filter buffer");+		return ARCHIVE_FATAL;+	}++	ret = __archive_read_register_format(ar,+	    rar,+	    "rar5",+	    rar5_bid,+	    rar5_options,+	    rar5_read_header,+	    rar5_read_data,+	    rar5_read_data_skip,+	    rar5_seek_data,+	    rar5_cleanup,+	    rar5_capabilities,+	    rar5_has_encrypted_entries);++	if(ret != ARCHIVE_OK) {+		(void) rar5_cleanup(ar);+	}++	return ret;+}
+ c/archive_read_support_format_raw.c view
@@ -0,0 +1,192 @@+/*-+ * Copyright (c) 2003-2009 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */+#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_raw.c 201107 2009-12-28 03:25:33Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_private.h"++struct raw_info {+	int64_t offset; /* Current position in the file. */+	int64_t unconsumed;+	int     end_of_file;+};++static int	archive_read_format_raw_bid(struct archive_read *, int);+static int	archive_read_format_raw_cleanup(struct archive_read *);+static int	archive_read_format_raw_read_data(struct archive_read *,+		    const void **, size_t *, int64_t *);+static int	archive_read_format_raw_read_data_skip(struct archive_read *);+static int	archive_read_format_raw_read_header(struct archive_read *,+		    struct archive_entry *);++int+archive_read_support_format_raw(struct archive *_a)+{+	struct raw_info *info;+	struct archive_read *a = (struct archive_read *)_a;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_raw");++	info = (struct raw_info *)calloc(1, sizeof(*info));+	if (info == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate raw_info data");+		return (ARCHIVE_FATAL);+	}++	r = __archive_read_register_format(a,+	    info,+	    "raw",+	    archive_read_format_raw_bid,+	    NULL,+	    archive_read_format_raw_read_header,+	    archive_read_format_raw_read_data,+	    archive_read_format_raw_read_data_skip,+	    NULL,+	    archive_read_format_raw_cleanup,+	    NULL,+	    NULL);+	if (r != ARCHIVE_OK)+		free(info);+	return (r);+}++/*+ * Bid 1 if this is a non-empty file.  Anyone who can really support+ * this should outbid us, so it should generally be safe to use "raw"+ * in conjunction with other formats.  But, this could really confuse+ * folks if there are bid errors or minor file damage, so we don't+ * include "raw" as part of support_format_all().+ */+static int+archive_read_format_raw_bid(struct archive_read *a, int best_bid)+{+	if (best_bid < 1 && __archive_read_ahead(a, 1, NULL) != NULL)+		return (1);+	return (-1);+}++/*+ * Mock up a fake header.+ */+static int+archive_read_format_raw_read_header(struct archive_read *a,+    struct archive_entry *entry)+{+	struct raw_info *info;++	info = (struct raw_info *)(a->format->data);+	if (info->end_of_file)+		return (ARCHIVE_EOF);++	a->archive.archive_format = ARCHIVE_FORMAT_RAW;+	a->archive.archive_format_name = "raw";+	archive_entry_set_pathname(entry, "data");+	archive_entry_set_filetype(entry, AE_IFREG);+	archive_entry_set_perm(entry, 0644);+	/* I'm deliberately leaving most fields unset here. */++	/* Let the filter fill out any fields it might have. */+	return __archive_read_header(a, entry);+}++static int+archive_read_format_raw_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	struct raw_info *info;+	ssize_t avail;++	info = (struct raw_info *)(a->format->data);++	/* Consume the bytes we read last time. */+	if (info->unconsumed) {+		__archive_read_consume(a, info->unconsumed);+		info->unconsumed = 0;+	}++	if (info->end_of_file)+		return (ARCHIVE_EOF);++	/* Get whatever bytes are immediately available. */+	*buff = __archive_read_ahead(a, 1, &avail);+	if (avail > 0) {+		/* Return the bytes we just read */+		*size = avail;+		*offset = info->offset;+		info->offset += *size;+		info->unconsumed = avail;+		return (ARCHIVE_OK);+	} else if (0 == avail) {+		/* Record and return end-of-file. */+		info->end_of_file = 1;+		*size = 0;+		*offset = info->offset;+		return (ARCHIVE_EOF);+	} else {+		/* Record and return an error. */+		*size = 0;+		*offset = info->offset;+		return ((int)avail);+	}+}++static int+archive_read_format_raw_read_data_skip(struct archive_read *a)+{+	struct raw_info *info = (struct raw_info *)(a->format->data);++	/* Consume the bytes we read last time. */+	if (info->unconsumed) {+		__archive_read_consume(a, info->unconsumed);+		info->unconsumed = 0;+	}+	info->end_of_file = 1;+	return (ARCHIVE_OK);+}++static int+archive_read_format_raw_cleanup(struct archive_read *a)+{+	struct raw_info *info;++	info = (struct raw_info *)(a->format->data);+	free(info);+	a->format->data = NULL;+	return (ARCHIVE_OK);+}
+ c/archive_read_support_format_tar.c view
@@ -0,0 +1,2922 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA+ * Copyright (c) 2016 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_tar.c 201161 2009-12-29 05:44:39Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stddef.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_acl_private.h" /* For ACL parsing routines. */+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_read_private.h"++#define tar_min(a,b) ((a) < (b) ? (a) : (b))++/*+ * Layout of POSIX 'ustar' tar header.+ */+struct archive_entry_header_ustar {+	char	name[100];+	char	mode[8];+	char	uid[8];+	char	gid[8];+	char	size[12];+	char	mtime[12];+	char	checksum[8];+	char	typeflag[1];+	char	linkname[100];	/* "old format" header ends here */+	char	magic[6];	/* For POSIX: "ustar\0" */+	char	version[2];	/* For POSIX: "00" */+	char	uname[32];+	char	gname[32];+	char	rdevmajor[8];+	char	rdevminor[8];+	char	prefix[155];+};++/*+ * Structure of GNU tar header+ */+struct gnu_sparse {+	char	offset[12];+	char	numbytes[12];+};++struct archive_entry_header_gnutar {+	char	name[100];+	char	mode[8];+	char	uid[8];+	char	gid[8];+	char	size[12];+	char	mtime[12];+	char	checksum[8];+	char	typeflag[1];+	char	linkname[100];+	char	magic[8];  /* "ustar  \0" (note blank/blank/null at end) */+	char	uname[32];+	char	gname[32];+	char	rdevmajor[8];+	char	rdevminor[8];+	char	atime[12];+	char	ctime[12];+	char	offset[12];+	char	longnames[4];+	char	unused[1];+	struct gnu_sparse sparse[4];+	char	isextended[1];+	char	realsize[12];+	/*+	 * Old GNU format doesn't use POSIX 'prefix' field; they use+	 * the 'L' (longname) entry instead.+	 */+};++/*+ * Data specific to this format.+ */+struct sparse_block {+	struct sparse_block	*next;+	int64_t	offset;+	int64_t	remaining;+	int hole;+};++struct tar {+	struct archive_string	 acl_text;+	struct archive_string	 entry_pathname;+	/* For "GNU.sparse.name" and other similar path extensions. */+	struct archive_string	 entry_pathname_override;+	struct archive_string	 entry_linkpath;+	struct archive_string	 entry_uname;+	struct archive_string	 entry_gname;+	struct archive_string	 longlink;+	struct archive_string	 longname;+	struct archive_string	 pax_header;+	struct archive_string	 pax_global;+	struct archive_string	 line;+	int			 pax_hdrcharset_binary;+	int			 header_recursion_depth;+	int64_t			 entry_bytes_remaining;+	int64_t			 entry_offset;+	int64_t			 entry_padding;+	int64_t 		 entry_bytes_unconsumed;+	int64_t			 realsize;+	int			 sparse_allowed;+	struct sparse_block	*sparse_list;+	struct sparse_block	*sparse_last;+	int64_t			 sparse_offset;+	int64_t			 sparse_numbytes;+	int			 sparse_gnu_major;+	int			 sparse_gnu_minor;+	char			 sparse_gnu_pending;++	struct archive_string	 localname;+	struct archive_string_conv *opt_sconv;+	struct archive_string_conv *sconv;+	struct archive_string_conv *sconv_acl;+	struct archive_string_conv *sconv_default;+	int			 init_default_conversion;+	int			 compat_2x;+	int			 process_mac_extensions;+	int			 read_concatenated_archives;+	int			 realsize_override;+};++static int	archive_block_is_null(const char *p);+static char	*base64_decode(const char *, size_t, size_t *);+static int	gnu_add_sparse_entry(struct archive_read *, struct tar *,+		    int64_t offset, int64_t remaining);++static void	gnu_clear_sparse_list(struct tar *);+static int	gnu_sparse_old_read(struct archive_read *, struct tar *,+		    const struct archive_entry_header_gnutar *header, size_t *);+static int	gnu_sparse_old_parse(struct archive_read *, struct tar *,+		    const struct gnu_sparse *sparse, int length);+static int	gnu_sparse_01_parse(struct archive_read *, struct tar *,+		    const char *);+static ssize_t	gnu_sparse_10_read(struct archive_read *, struct tar *,+			size_t *);+static int	header_Solaris_ACL(struct archive_read *,  struct tar *,+		    struct archive_entry *, const void *, size_t *);+static int	header_common(struct archive_read *,  struct tar *,+		    struct archive_entry *, const void *);+static int	header_old_tar(struct archive_read *, struct tar *,+		    struct archive_entry *, const void *);+static int	header_pax_extensions(struct archive_read *, struct tar *,+		    struct archive_entry *, const void *, size_t *);+static int	header_pax_global(struct archive_read *, struct tar *,+		    struct archive_entry *, const void *h, size_t *);+static int	header_longlink(struct archive_read *, struct tar *,+		    struct archive_entry *, const void *h, size_t *);+static int	header_longname(struct archive_read *, struct tar *,+		    struct archive_entry *, const void *h, size_t *);+static int	read_mac_metadata_blob(struct archive_read *, struct tar *,+		    struct archive_entry *, const void *h, size_t *);+static int	header_volume(struct archive_read *, struct tar *,+		    struct archive_entry *, const void *h, size_t *);+static int	header_ustar(struct archive_read *, struct tar *,+		    struct archive_entry *, const void *h);+static int	header_gnutar(struct archive_read *, struct tar *,+		    struct archive_entry *, const void *h, size_t *);+static int	archive_read_format_tar_bid(struct archive_read *, int);+static int	archive_read_format_tar_options(struct archive_read *,+		    const char *, const char *);+static int	archive_read_format_tar_cleanup(struct archive_read *);+static int	archive_read_format_tar_read_data(struct archive_read *a,+		    const void **buff, size_t *size, int64_t *offset);+static int	archive_read_format_tar_skip(struct archive_read *a);+static int	archive_read_format_tar_read_header(struct archive_read *,+		    struct archive_entry *);+static int	checksum(struct archive_read *, const void *);+static int 	pax_attribute(struct archive_read *, struct tar *,+		    struct archive_entry *, const char *key, const char *value,+		    size_t value_length);+static int	pax_attribute_acl(struct archive_read *, struct tar *,+		    struct archive_entry *, const char *, int);+static int	pax_attribute_xattr(struct archive_entry *, const char *,+		    const char *);+static int 	pax_header(struct archive_read *, struct tar *,+		    struct archive_entry *, struct archive_string *);+static void	pax_time(const char *, int64_t *sec, long *nanos);+static ssize_t	readline(struct archive_read *, struct tar *, const char **,+		    ssize_t limit, size_t *);+static int	read_body_to_string(struct archive_read *, struct tar *,+		    struct archive_string *, const void *h, size_t *);+static int	solaris_sparse_parse(struct archive_read *, struct tar *,+		    struct archive_entry *, const char *);+static int64_t	tar_atol(const char *, size_t);+static int64_t	tar_atol10(const char *, size_t);+static int64_t	tar_atol256(const char *, size_t);+static int64_t	tar_atol8(const char *, size_t);+static int	tar_read_header(struct archive_read *, struct tar *,+		    struct archive_entry *, size_t *);+static int	tohex(int c);+static char	*url_decode(const char *);+static void	tar_flush_unconsumed(struct archive_read *, size_t *);+++int+archive_read_support_format_gnutar(struct archive *a)+{+	archive_check_magic(a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_gnutar");+	return (archive_read_support_format_tar(a));+}+++int+archive_read_support_format_tar(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct tar *tar;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_tar");++	tar = (struct tar *)calloc(1, sizeof(*tar));+	if (tar == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate tar data");+		return (ARCHIVE_FATAL);+	}+#ifdef HAVE_COPYFILE_H+	/* Set this by default on Mac OS. */+	tar->process_mac_extensions = 1;+#endif++	r = __archive_read_register_format(a, tar, "tar",+	    archive_read_format_tar_bid,+	    archive_read_format_tar_options,+	    archive_read_format_tar_read_header,+	    archive_read_format_tar_read_data,+	    archive_read_format_tar_skip,+	    NULL,+	    archive_read_format_tar_cleanup,+	    NULL,+	    NULL);++	if (r != ARCHIVE_OK)+		free(tar);+	return (ARCHIVE_OK);+}++static int+archive_read_format_tar_cleanup(struct archive_read *a)+{+	struct tar *tar;++	tar = (struct tar *)(a->format->data);+	gnu_clear_sparse_list(tar);+	archive_string_free(&tar->acl_text);+	archive_string_free(&tar->entry_pathname);+	archive_string_free(&tar->entry_pathname_override);+	archive_string_free(&tar->entry_linkpath);+	archive_string_free(&tar->entry_uname);+	archive_string_free(&tar->entry_gname);+	archive_string_free(&tar->line);+	archive_string_free(&tar->pax_global);+	archive_string_free(&tar->pax_header);+	archive_string_free(&tar->longname);+	archive_string_free(&tar->longlink);+	archive_string_free(&tar->localname);+	free(tar);+	(a->format->data) = NULL;+	return (ARCHIVE_OK);+}++/*+ * Validate number field+ *+ * This has to be pretty lenient in order to accommodate the enormous+ * variety of tar writers in the world:+ *  = POSIX (IEEE Std 1003.1-1988) ustar requires octal values with leading+ *    zeros and allows fields to be terminated with space or null characters+ *  = Many writers use different termination (in particular, libarchive+ *    omits terminator bytes to squeeze one or two more digits)+ *  = Many writers pad with space and omit leading zeros+ *  = GNU tar and star write base-256 values if numbers are too+ *    big to be represented in octal+ *+ *  Examples of specific tar headers that we should support:+ *  = Perl Archive::Tar terminates uid, gid, devminor and devmajor with two+ *    null bytes, pads size with spaces and other numeric fields with zeroes+ *  = plexus-archiver prior to 2.6.3 (before switching to commons-compress)+ *    may have uid and gid fields filled with spaces without any octal digits+ *    at all and pads all numeric fields with spaces+ *+ * This should tolerate all variants in use.  It will reject a field+ * where the writer just left garbage after a trailing NUL.+ */+static int+validate_number_field(const char* p_field, size_t i_size)+{+	unsigned char marker = (unsigned char)p_field[0];+	if (marker == 128 || marker == 255 || marker == 0) {+		/* Base-256 marker, there's nothing we can check. */+		return 1;+	} else {+		/* Must be octal */+		size_t i = 0;+		/* Skip any leading spaces */+		while (i < i_size && p_field[i] == ' ') {+			++i;+		}+		/* Skip octal digits. */+		while (i < i_size && p_field[i] >= '0' && p_field[i] <= '7') {+			++i;+		}+		/* Any remaining characters must be space or NUL padding. */+		while (i < i_size) {+			if (p_field[i] != ' ' && p_field[i] != 0) {+				return 0;+			}+			++i;+		}+		return 1;+	}+}++static int+archive_read_format_tar_bid(struct archive_read *a, int best_bid)+{+	int bid;+	const char *h;+	const struct archive_entry_header_ustar *header;++	(void)best_bid; /* UNUSED */++	bid = 0;++	/* Now let's look at the actual header and see if it matches. */+	h = __archive_read_ahead(a, 512, NULL);+	if (h == NULL)+		return (-1);++	/* If it's an end-of-archive mark, we can handle it. */+	if (h[0] == 0 && archive_block_is_null(h)) {+		/*+		 * Usually, I bid the number of bits verified, but+		 * in this case, 4096 seems excessive so I picked 10 as+		 * an arbitrary but reasonable-seeming value.+		 */+		return (10);+	}++	/* If it's not an end-of-archive mark, it must have a valid checksum.*/+	if (!checksum(a, h))+		return (0);+	bid += 48;  /* Checksum is usually 6 octal digits. */++	header = (const struct archive_entry_header_ustar *)h;++	/* Recognize POSIX formats. */+	if ((memcmp(header->magic, "ustar\0", 6) == 0)+	    && (memcmp(header->version, "00", 2) == 0))+		bid += 56;++	/* Recognize GNU tar format. */+	if ((memcmp(header->magic, "ustar ", 6) == 0)+	    && (memcmp(header->version, " \0", 2) == 0))+		bid += 56;++	/* Type flag must be null, digit or A-Z, a-z. */+	if (header->typeflag[0] != 0 &&+	    !( header->typeflag[0] >= '0' && header->typeflag[0] <= '9') &&+	    !( header->typeflag[0] >= 'A' && header->typeflag[0] <= 'Z') &&+	    !( header->typeflag[0] >= 'a' && header->typeflag[0] <= 'z') )+		return (0);+	bid += 2;  /* 6 bits of variation in an 8-bit field leaves 2 bits. */++	/*+	 * Check format of mode/uid/gid/mtime/size/rdevmajor/rdevminor fields.+	 */+	if (bid > 0 && (+	    validate_number_field(header->mode, sizeof(header->mode)) == 0+	    || validate_number_field(header->uid, sizeof(header->uid)) == 0+	    || validate_number_field(header->gid, sizeof(header->gid)) == 0+	    || validate_number_field(header->mtime, sizeof(header->mtime)) == 0+	    || validate_number_field(header->size, sizeof(header->size)) == 0+	    || validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0+	    || validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0)) {+		bid = 0;+	}++	return (bid);+}++static int+archive_read_format_tar_options(struct archive_read *a,+    const char *key, const char *val)+{+	struct tar *tar;+	int ret = ARCHIVE_FAILED;++	tar = (struct tar *)(a->format->data);+	if (strcmp(key, "compat-2x")  == 0) {+		/* Handle UTF-8 filenames as libarchive 2.x */+		tar->compat_2x = (val != NULL && val[0] != 0);+		tar->init_default_conversion = tar->compat_2x;+		return (ARCHIVE_OK);+	} else if (strcmp(key, "hdrcharset")  == 0) {+		if (val == NULL || val[0] == 0)+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "tar: hdrcharset option needs a character-set name");+		else {+			tar->opt_sconv =+			    archive_string_conversion_from_charset(+				&a->archive, val, 0);+			if (tar->opt_sconv != NULL)+				ret = ARCHIVE_OK;+			else+				ret = ARCHIVE_FATAL;+		}+		return (ret);+	} else if (strcmp(key, "mac-ext") == 0) {+		tar->process_mac_extensions = (val != NULL && val[0] != 0);+		return (ARCHIVE_OK);+	} else if (strcmp(key, "read_concatenated_archives") == 0) {+		tar->read_concatenated_archives = (val != NULL && val[0] != 0);+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++/* utility function- this exists to centralize the logic of tracking+ * how much unconsumed data we have floating around, and to consume+ * anything outstanding since we're going to do read_aheads+ */+static void+tar_flush_unconsumed(struct archive_read *a, size_t *unconsumed)+{+	if (*unconsumed) {+/*+		void *data = (void *)__archive_read_ahead(a, *unconsumed, NULL);+		 * this block of code is to poison claimed unconsumed space, ensuring+		 * things break if it is in use still.+		 * currently it WILL break things, so enable it only for debugging this issue+		if (data) {+			memset(data, 0xff, *unconsumed);+		}+*/+		__archive_read_consume(a, *unconsumed);+		*unconsumed = 0;+	}+}++/*+ * The function invoked by archive_read_next_header().  This+ * just sets up a few things and then calls the internal+ * tar_read_header() function below.+ */+static int+archive_read_format_tar_read_header(struct archive_read *a,+    struct archive_entry *entry)+{+	/*+	 * When converting tar archives to cpio archives, it is+	 * essential that each distinct file have a distinct inode+	 * number.  To simplify this, we keep a static count here to+	 * assign fake dev/inode numbers to each tar entry.  Note that+	 * pax format archives may overwrite this with something more+	 * useful.+	 *+	 * Ideally, we would track every file read from the archive so+	 * that we could assign the same dev/ino pair to hardlinks,+	 * but the memory required to store a complete lookup table is+	 * probably not worthwhile just to support the relatively+	 * obscure tar->cpio conversion case.+	 */+	static int default_inode;+	static int default_dev;+	struct tar *tar;+	const char *p;+	const wchar_t *wp;+	int r;+	size_t l, unconsumed = 0;++	/* Assign default device/inode values. */+	archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */+	archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */+	/* Limit generated st_ino number to 16 bits. */+	if (default_inode >= 0xffff) {+		++default_dev;+		default_inode = 0;+	}++	tar = (struct tar *)(a->format->data);+	tar->entry_offset = 0;+	gnu_clear_sparse_list(tar);+	tar->realsize = -1; /* Mark this as "unset" */+	tar->realsize_override = 0;++	/* Setup default string conversion. */+	tar->sconv = tar->opt_sconv;+	if (tar->sconv == NULL) {+		if (!tar->init_default_conversion) {+			tar->sconv_default =+			    archive_string_default_conversion_for_read(&(a->archive));+			tar->init_default_conversion = 1;+		}+		tar->sconv = tar->sconv_default;+	}++	r = tar_read_header(a, tar, entry, &unconsumed);++	tar_flush_unconsumed(a, &unconsumed);++	/*+	 * "non-sparse" files are really just sparse files with+	 * a single block.+	 */+	if (tar->sparse_list == NULL) {+		if (gnu_add_sparse_entry(a, tar, 0, tar->entry_bytes_remaining)+		    != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	} else {+		struct sparse_block *sb;++		for (sb = tar->sparse_list; sb != NULL; sb = sb->next) {+			if (!sb->hole)+				archive_entry_sparse_add_entry(entry,+				    sb->offset, sb->remaining);+		}+	}++	if (r == ARCHIVE_OK && archive_entry_filetype(entry) == AE_IFREG) {+		/*+		 * "Regular" entry with trailing '/' is really+		 * directory: This is needed for certain old tar+		 * variants and even for some broken newer ones.+		 */+		if ((wp = archive_entry_pathname_w(entry)) != NULL) {+			l = wcslen(wp);+			if (l > 0 && wp[l - 1] == L'/') {+				archive_entry_set_filetype(entry, AE_IFDIR);+			}+		} else if ((p = archive_entry_pathname(entry)) != NULL) {+			l = strlen(p);+			if (l > 0 && p[l - 1] == '/') {+				archive_entry_set_filetype(entry, AE_IFDIR);+			}+		}+	}+	return (r);+}++static int+archive_read_format_tar_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	ssize_t bytes_read;+	struct tar *tar;+	struct sparse_block *p;++	tar = (struct tar *)(a->format->data);++	for (;;) {+		/* Remove exhausted entries from sparse list. */+		while (tar->sparse_list != NULL &&+		    tar->sparse_list->remaining == 0) {+			p = tar->sparse_list;+			tar->sparse_list = p->next;+			free(p);+		}++		if (tar->entry_bytes_unconsumed) {+			__archive_read_consume(a, tar->entry_bytes_unconsumed);+			tar->entry_bytes_unconsumed = 0;+		}++		/* If we're at end of file, return EOF. */+		if (tar->sparse_list == NULL ||+		    tar->entry_bytes_remaining == 0) {+			if (__archive_read_consume(a, tar->entry_padding) < 0)+				return (ARCHIVE_FATAL);+			tar->entry_padding = 0;+			*buff = NULL;+			*size = 0;+			*offset = tar->realsize;+			return (ARCHIVE_EOF);+		}++		*buff = __archive_read_ahead(a, 1, &bytes_read);+		if (bytes_read < 0)+			return (ARCHIVE_FATAL);+		if (*buff == NULL) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Truncated tar archive");+			return (ARCHIVE_FATAL);+		}+		if (bytes_read > tar->entry_bytes_remaining)+			bytes_read = (ssize_t)tar->entry_bytes_remaining;+		/* Don't read more than is available in the+		 * current sparse block. */+		if (tar->sparse_list->remaining < bytes_read)+			bytes_read = (ssize_t)tar->sparse_list->remaining;+		*size = bytes_read;+		*offset = tar->sparse_list->offset;+		tar->sparse_list->remaining -= bytes_read;+		tar->sparse_list->offset += bytes_read;+		tar->entry_bytes_remaining -= bytes_read;+		tar->entry_bytes_unconsumed = bytes_read;++		if (!tar->sparse_list->hole)+			return (ARCHIVE_OK);+		/* Current is hole data and skip this. */+	}+}++static int+archive_read_format_tar_skip(struct archive_read *a)+{+	int64_t bytes_skipped;+	int64_t request;+	struct sparse_block *p;+	struct tar* tar;++	tar = (struct tar *)(a->format->data);++	/* Do not consume the hole of a sparse file. */+	request = 0;+	for (p = tar->sparse_list; p != NULL; p = p->next) {+		if (!p->hole) {+			if (p->remaining >= INT64_MAX - request) {+				return ARCHIVE_FATAL;+			}+			request += p->remaining;+		}+	}+	if (request > tar->entry_bytes_remaining)+		request = tar->entry_bytes_remaining;+	request += tar->entry_padding + tar->entry_bytes_unconsumed;++	bytes_skipped = __archive_read_consume(a, request);+	if (bytes_skipped < 0)+		return (ARCHIVE_FATAL);++	tar->entry_bytes_remaining = 0;+	tar->entry_bytes_unconsumed = 0;+	tar->entry_padding = 0;++	/* Free the sparse list. */+	gnu_clear_sparse_list(tar);++	return (ARCHIVE_OK);+}++/*+ * This function recursively interprets all of the headers associated+ * with a single entry.+ */+static int+tar_read_header(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, size_t *unconsumed)+{+	ssize_t bytes;+	int err, eof_vol_header;+	const char *h;+	const struct archive_entry_header_ustar *header;+	const struct archive_entry_header_gnutar *gnuheader;++	eof_vol_header = 0;++	/* Loop until we find a workable header record. */+	for (;;) {+		tar_flush_unconsumed(a, unconsumed);++		/* Read 512-byte header record */+		h = __archive_read_ahead(a, 512, &bytes);+		if (bytes < 0)+			return ((int)bytes);+		if (bytes == 0) { /* EOF at a block boundary. */+			/* Some writers do omit the block of nulls. <sigh> */+			return (ARCHIVE_EOF);+		}+		if (bytes < 512) {  /* Short block at EOF; this is bad. */+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated tar archive");+			return (ARCHIVE_FATAL);+		}+		*unconsumed = 512;++		/* Header is workable if it's not an end-of-archive mark. */+		if (h[0] != 0 || !archive_block_is_null(h))+			break;++		/* Ensure format is set for archives with only null blocks. */+		if (a->archive.archive_format_name == NULL) {+			a->archive.archive_format = ARCHIVE_FORMAT_TAR;+			a->archive.archive_format_name = "tar";+		}++		if (!tar->read_concatenated_archives) {+			/* Try to consume a second all-null record, as well. */+			tar_flush_unconsumed(a, unconsumed);+			h = __archive_read_ahead(a, 512, NULL);+			if (h != NULL && h[0] == 0 && archive_block_is_null(h))+				__archive_read_consume(a, 512);+			archive_clear_error(&a->archive);+			return (ARCHIVE_EOF);+		}++		/*+		 * We're reading concatenated archives, ignore this block and+		 * loop to get the next.+		 */+	}++	/*+	 * Note: If the checksum fails and we return ARCHIVE_RETRY,+	 * then the client is likely to just retry.  This is a very+	 * crude way to search for the next valid header!+	 *+	 * TODO: Improve this by implementing a real header scan.+	 */+	if (!checksum(a, h)) {+		tar_flush_unconsumed(a, unconsumed);+		archive_set_error(&a->archive, EINVAL, "Damaged tar archive");+		return (ARCHIVE_RETRY); /* Retryable: Invalid header */+	}++	if (++tar->header_recursion_depth > 32) {+		tar_flush_unconsumed(a, unconsumed);+		archive_set_error(&a->archive, EINVAL, "Too many special headers");+		return (ARCHIVE_WARN);+	}++	/* Determine the format variant. */+	header = (const struct archive_entry_header_ustar *)h;++	switch(header->typeflag[0]) {+	case 'A': /* Solaris tar ACL */+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;+		a->archive.archive_format_name = "Solaris tar";+		err = header_Solaris_ACL(a, tar, entry, h, unconsumed);+		break;+	case 'g': /* POSIX-standard 'g' header. */+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;+		a->archive.archive_format_name = "POSIX pax interchange format";+		err = header_pax_global(a, tar, entry, h, unconsumed);+		if (err == ARCHIVE_EOF)+			return (err);+		break;+	case 'K': /* Long link name (GNU tar, others) */+		err = header_longlink(a, tar, entry, h, unconsumed);+		break;+	case 'L': /* Long filename (GNU tar, others) */+		err = header_longname(a, tar, entry, h, unconsumed);+		break;+	case 'V': /* GNU volume header */+		err = header_volume(a, tar, entry, h, unconsumed);+		if (err == ARCHIVE_EOF)+			eof_vol_header = 1;+		break;+	case 'X': /* Used by SUN tar; same as 'x'. */+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;+		a->archive.archive_format_name =+		    "POSIX pax interchange format (Sun variant)";+		err = header_pax_extensions(a, tar, entry, h, unconsumed);+		break;+	case 'x': /* POSIX-standard 'x' header. */+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;+		a->archive.archive_format_name = "POSIX pax interchange format";+		err = header_pax_extensions(a, tar, entry, h, unconsumed);+		break;+	default:+		gnuheader = (const struct archive_entry_header_gnutar *)h;+		if (memcmp(gnuheader->magic, "ustar  \0", 8) == 0) {+			a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;+			a->archive.archive_format_name = "GNU tar format";+			err = header_gnutar(a, tar, entry, h, unconsumed);+		} else if (memcmp(header->magic, "ustar", 5) == 0) {+			if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {+				a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;+				a->archive.archive_format_name = "POSIX ustar format";+			}+			err = header_ustar(a, tar, entry, h);+		} else {+			a->archive.archive_format = ARCHIVE_FORMAT_TAR;+			a->archive.archive_format_name = "tar (non-POSIX)";+			err = header_old_tar(a, tar, entry, h);+		}+	}+	if (err == ARCHIVE_FATAL)+		return (err);++	tar_flush_unconsumed(a, unconsumed);++	h = NULL;+	header = NULL;++	--tar->header_recursion_depth;+	/* Yuck.  Apple's design here ends up storing long pathname+	 * extensions for both the AppleDouble extension entry and the+	 * regular entry.+	 */+	if ((err == ARCHIVE_WARN || err == ARCHIVE_OK) &&+	    tar->header_recursion_depth == 0 &&+	    tar->process_mac_extensions) {+		int err2 = read_mac_metadata_blob(a, tar, entry, h, unconsumed);+		if (err2 < err)+			err = err2;+	}++	/* We return warnings or success as-is.  Anything else is fatal. */+	if (err == ARCHIVE_WARN || err == ARCHIVE_OK) {+		if (tar->sparse_gnu_pending) {+			if (tar->sparse_gnu_major == 1 &&+			    tar->sparse_gnu_minor == 0) {+				ssize_t bytes_read;++				tar->sparse_gnu_pending = 0;+				/* Read initial sparse map. */+				bytes_read = gnu_sparse_10_read(a, tar, unconsumed);+				if (bytes_read < 0)+					return ((int)bytes_read);+				tar->entry_bytes_remaining -= bytes_read;+			} else {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Unrecognized GNU sparse file format");+				return (ARCHIVE_WARN);+			}+			tar->sparse_gnu_pending = 0;+		}+		return (err);+	}+	if (err == ARCHIVE_EOF) {+		if (!eof_vol_header) {+			/* EOF when recursively reading a header is bad. */+			archive_set_error(&a->archive, EINVAL,+			    "Damaged tar archive");+		} else {+			/* If we encounter just a GNU volume header treat+			 * this situation as an empty archive */+			return (ARCHIVE_EOF);+		}+	}+	return (ARCHIVE_FATAL);+}++/*+ * Return true if block checksum is correct.+ */+static int+checksum(struct archive_read *a, const void *h)+{+	const unsigned char *bytes;+	const struct archive_entry_header_ustar	*header;+	int check, sum;+	size_t i;++	(void)a; /* UNUSED */+	bytes = (const unsigned char *)h;+	header = (const struct archive_entry_header_ustar *)h;++	/* Checksum field must hold an octal number */+	for (i = 0; i < sizeof(header->checksum); ++i) {+		char c = header->checksum[i];+		if (c != ' ' && c != '\0' && (c < '0' || c > '7'))+			return 0;+	}++	/*+	 * Test the checksum.  Note that POSIX specifies _unsigned_+	 * bytes for this calculation.+	 */+	sum = (int)tar_atol(header->checksum, sizeof(header->checksum));+	check = 0;+	for (i = 0; i < 148; i++)+		check += (unsigned char)bytes[i];+	for (; i < 156; i++)+		check += 32;+	for (; i < 512; i++)+		check += (unsigned char)bytes[i];+	if (sum == check)+		return (1);++	/*+	 * Repeat test with _signed_ bytes, just in case this archive+	 * was created by an old BSD, Solaris, or HP-UX tar with a+	 * broken checksum calculation.+	 */+	check = 0;+	for (i = 0; i < 148; i++)+		check += (signed char)bytes[i];+	for (; i < 156; i++)+		check += 32;+	for (; i < 512; i++)+		check += (signed char)bytes[i];+	if (sum == check)+		return (1);++	return (0);+}++/*+ * Return true if this block contains only nulls.+ */+static int+archive_block_is_null(const char *p)+{+	unsigned i;++	for (i = 0; i < 512; i++)+		if (*p++)+			return (0);+	return (1);+}++/*+ * Interpret 'A' Solaris ACL header+ */+static int+header_Solaris_ACL(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const void *h, size_t *unconsumed)+{+	const struct archive_entry_header_ustar *header;+	size_t size;+	int err, acl_type;+	int64_t type;+	char *acl, *p;++	/*+	 * read_body_to_string adds a NUL terminator, but we need a little+	 * more to make sure that we don't overrun acl_text later.+	 */+	header = (const struct archive_entry_header_ustar *)h;+	size = (size_t)tar_atol(header->size, sizeof(header->size));+	err = read_body_to_string(a, tar, &(tar->acl_text), h, unconsumed);+	if (err != ARCHIVE_OK)+		return (err);++	/* Recursively read next header */+	err = tar_read_header(a, tar, entry, unconsumed);+	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))+		return (err);++	/* TODO: Examine the first characters to see if this+	 * is an AIX ACL descriptor.  We'll likely never support+	 * them, but it would be polite to recognize and warn when+	 * we do see them. */++	/* Leading octal number indicates ACL type and number of entries. */+	p = acl = tar->acl_text.s;+	type = 0;+	while (*p != '\0' && p < acl + size) {+		if (*p < '0' || *p > '7') {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Malformed Solaris ACL attribute (invalid digit)");+			return(ARCHIVE_WARN);+		}+		type <<= 3;+		type += *p - '0';+		if (type > 077777777) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Malformed Solaris ACL attribute (count too large)");+			return (ARCHIVE_WARN);+		}+		p++;+	}+	switch ((int)type & ~0777777) {+	case 01000000:+		/* POSIX.1e ACL */+		acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;+		break;+	case 03000000:+		/* NFSv4 ACL */+		acl_type = ARCHIVE_ENTRY_ACL_TYPE_NFS4;+		break;+	default:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Malformed Solaris ACL attribute (unsupported type %o)",+		    (int)type);+		return (ARCHIVE_WARN);+	}+	p++;++	if (p >= acl + size) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Malformed Solaris ACL attribute (body overflow)");+		return(ARCHIVE_WARN);+	}++	/* ACL text is null-terminated; find the end. */+	size -= (p - acl);+	acl = p;++	while (*p != '\0' && p < acl + size)+		p++;++	if (tar->sconv_acl == NULL) {+		tar->sconv_acl = archive_string_conversion_from_charset(+		    &(a->archive), "UTF-8", 1);+		if (tar->sconv_acl == NULL)+			return (ARCHIVE_FATAL);+	}+	archive_strncpy(&(tar->localname), acl, p - acl);+	err = archive_acl_from_text_l(archive_entry_acl(entry),+	    tar->localname.s, acl_type, tar->sconv_acl);+	if (err != ARCHIVE_OK) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for ACL");+		} else+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Malformed Solaris ACL attribute (unparsable)");+	}+	return (err);+}++/*+ * Interpret 'K' long linkname header.+ */+static int+header_longlink(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const void *h, size_t *unconsumed)+{+	int err;++	err = read_body_to_string(a, tar, &(tar->longlink), h, unconsumed);+	if (err != ARCHIVE_OK)+		return (err);+	err = tar_read_header(a, tar, entry, unconsumed);+	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))+		return (err);+	/* Set symlink if symlink already set, else hardlink. */+	archive_entry_copy_link(entry, tar->longlink.s);+	return (ARCHIVE_OK);+}++static int+set_conversion_failed_error(struct archive_read *a,+    struct archive_string_conv *sconv, const char *name)+{+	if (errno == ENOMEM) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory for %s", name);+		return (ARCHIVE_FATAL);+	}+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "%s can't be converted from %s to current locale.",+	    name, archive_string_conversion_charset_name(sconv));+	return (ARCHIVE_WARN);+}++/*+ * Interpret 'L' long filename header.+ */+static int+header_longname(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const void *h, size_t *unconsumed)+{+	int err;++	err = read_body_to_string(a, tar, &(tar->longname), h, unconsumed);+	if (err != ARCHIVE_OK)+		return (err);+	/* Read and parse "real" header, then override name. */+	err = tar_read_header(a, tar, entry, unconsumed);+	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))+		return (err);+	if (archive_entry_copy_pathname_l(entry, tar->longname.s,+	    archive_strlen(&(tar->longname)), tar->sconv) != 0)+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");+	return (err);+}+++/*+ * Interpret 'V' GNU tar volume header.+ */+static int+header_volume(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const void *h, size_t *unconsumed)+{+	(void)h;++	/* Just skip this and read the next header. */+	return (tar_read_header(a, tar, entry, unconsumed));+}++/*+ * Read body of an archive entry into an archive_string object.+ */+static int+read_body_to_string(struct archive_read *a, struct tar *tar,+    struct archive_string *as, const void *h, size_t *unconsumed)+{+	int64_t size;+	const struct archive_entry_header_ustar *header;+	const void *src;++	(void)tar; /* UNUSED */+	header = (const struct archive_entry_header_ustar *)h;+	size  = tar_atol(header->size, sizeof(header->size));+	if ((size > 1048576) || (size < 0)) {+		archive_set_error(&a->archive, EINVAL,+		    "Special header too large");+		return (ARCHIVE_FATAL);+	}++	/* Fail if we can't make our buffer big enough. */+	if (archive_string_ensure(as, (size_t)size+1) == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "No memory");+		return (ARCHIVE_FATAL);+	}++	tar_flush_unconsumed(a, unconsumed);++	/* Read the body into the string. */+	*unconsumed = (size_t)((size + 511) & ~ 511);+	src = __archive_read_ahead(a, *unconsumed, NULL);+	if (src == NULL) {+		*unconsumed = 0;+		return (ARCHIVE_FATAL);+	}+	memcpy(as->s, src, (size_t)size);+	as->s[size] = '\0';+	as->length = (size_t)size;+	return (ARCHIVE_OK);+}++/*+ * Parse out common header elements.+ *+ * This would be the same as header_old_tar, except that the+ * filename is handled slightly differently for old and POSIX+ * entries  (POSIX entries support a 'prefix').  This factoring+ * allows header_old_tar and header_ustar+ * to handle filenames differently, while still putting most of the+ * common parsing into one place.+ */+static int+header_common(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const void *h)+{+	const struct archive_entry_header_ustar	*header;+	char	tartype;+	int     err = ARCHIVE_OK;++	header = (const struct archive_entry_header_ustar *)h;+	if (header->linkname[0])+		archive_strncpy(&(tar->entry_linkpath),+		    header->linkname, sizeof(header->linkname));+	else+		archive_string_empty(&(tar->entry_linkpath));++	/* Parse out the numeric fields (all are octal) */+	archive_entry_set_mode(entry,+		(mode_t)tar_atol(header->mode, sizeof(header->mode)));+	archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid)));+	archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid)));+	tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size));+	if (tar->entry_bytes_remaining < 0) {+		tar->entry_bytes_remaining = 0;+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Tar entry has negative size");+		return (ARCHIVE_FATAL);+	}+	if (tar->entry_bytes_remaining == INT64_MAX) {+		/* Note: tar_atol returns INT64_MAX on overflow */+		tar->entry_bytes_remaining = 0;+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Tar entry size overflow");+		return (ARCHIVE_FATAL);+	}+	tar->realsize = tar->entry_bytes_remaining;+	archive_entry_set_size(entry, tar->entry_bytes_remaining);+	archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0);++	/* Handle the tar type flag appropriately. */+	tartype = header->typeflag[0];++	switch (tartype) {+	case '1': /* Hard link */+		if (archive_entry_copy_hardlink_l(entry, tar->entry_linkpath.s,+		    archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) {+			err = set_conversion_failed_error(a, tar->sconv,+			    "Linkname");+			if (err == ARCHIVE_FATAL)+				return (err);+		}+		/*+		 * The following may seem odd, but: Technically, tar+		 * does not store the file type for a "hard link"+		 * entry, only the fact that it is a hard link.  So, I+		 * leave the type zero normally.  But, pax interchange+		 * format allows hard links to have data, which+		 * implies that the underlying entry is a regular+		 * file.+		 */+		if (archive_entry_size(entry) > 0)+			archive_entry_set_filetype(entry, AE_IFREG);++		/*+		 * A tricky point: Traditionally, tar readers have+		 * ignored the size field when reading hardlink+		 * entries, and some writers put non-zero sizes even+		 * though the body is empty.  POSIX blessed this+		 * convention in the 1988 standard, but broke with+		 * this tradition in 2001 by permitting hardlink+		 * entries to store valid bodies in pax interchange+		 * format, but not in ustar format.  Since there is no+		 * hard and fast way to distinguish pax interchange+		 * from earlier archives (the 'x' and 'g' entries are+		 * optional, after all), we need a heuristic.+		 */+		if (archive_entry_size(entry) == 0) {+			/* If the size is already zero, we're done. */+		}  else if (a->archive.archive_format+		    == ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {+			/* Definitely pax extended; must obey hardlink size. */+		} else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR+		    || a->archive.archive_format == ARCHIVE_FORMAT_TAR_GNUTAR)+		{+			/* Old-style or GNU tar: we must ignore the size. */+			archive_entry_set_size(entry, 0);+			tar->entry_bytes_remaining = 0;+		} else if (archive_read_format_tar_bid(a, 50) > 50) {+			/*+			 * We don't know if it's pax: If the bid+			 * function sees a valid ustar header+			 * immediately following, then let's ignore+			 * the hardlink size.+			 */+			archive_entry_set_size(entry, 0);+			tar->entry_bytes_remaining = 0;+		}+		/*+		 * TODO: There are still two cases I'd like to handle:+		 *   = a ustar non-pax archive with a hardlink entry at+		 *     end-of-archive.  (Look for block of nulls following?)+		 *   = a pax archive that has not seen any pax headers+		 *     and has an entry which is a hardlink entry storing+		 *     a body containing an uncompressed tar archive.+		 * The first is worth addressing; I don't see any reliable+		 * way to deal with the second possibility.+		 */+		break;+	case '2': /* Symlink */+		archive_entry_set_filetype(entry, AE_IFLNK);+		archive_entry_set_size(entry, 0);+		tar->entry_bytes_remaining = 0;+		if (archive_entry_copy_symlink_l(entry, tar->entry_linkpath.s,+		    archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) {+			err = set_conversion_failed_error(a, tar->sconv,+			    "Linkname");+			if (err == ARCHIVE_FATAL)+				return (err);+		}+		break;+	case '3': /* Character device */+		archive_entry_set_filetype(entry, AE_IFCHR);+		archive_entry_set_size(entry, 0);+		tar->entry_bytes_remaining = 0;+		break;+	case '4': /* Block device */+		archive_entry_set_filetype(entry, AE_IFBLK);+		archive_entry_set_size(entry, 0);+		tar->entry_bytes_remaining = 0;+		break;+	case '5': /* Dir */+		archive_entry_set_filetype(entry, AE_IFDIR);+		archive_entry_set_size(entry, 0);+		tar->entry_bytes_remaining = 0;+		break;+	case '6': /* FIFO device */+		archive_entry_set_filetype(entry, AE_IFIFO);+		archive_entry_set_size(entry, 0);+		tar->entry_bytes_remaining = 0;+		break;+	case 'D': /* GNU incremental directory type */+		/*+		 * No special handling is actually required here.+		 * It might be nice someday to preprocess the file list and+		 * provide it to the client, though.+		 */+		archive_entry_set_filetype(entry, AE_IFDIR);+		break;+	case 'M': /* GNU "Multi-volume" (remainder of file from last archive)*/+		/*+		 * As far as I can tell, this is just like a regular file+		 * entry, except that the contents should be _appended_ to+		 * the indicated file at the indicated offset.  This may+		 * require some API work to fully support.+		 */+		break;+	case 'N': /* Old GNU "long filename" entry. */+		/* The body of this entry is a script for renaming+		 * previously-extracted entries.  Ugh.  It will never+		 * be supported by libarchive. */+		archive_entry_set_filetype(entry, AE_IFREG);+		break;+	case 'S': /* GNU sparse files */+		/*+		 * Sparse files are really just regular files with+		 * sparse information in the extended area.+		 */+		/* FALLTHROUGH */+	case '0':+		/*+		 * Enable sparse file "read" support only for regular+		 * files and explicit GNU sparse files.  However, we+		 * don't allow non-standard file types to be sparse.+		 */+		tar->sparse_allowed = 1;+		/* FALLTHROUGH */+	default: /* Regular file  and non-standard types */+		/*+		 * Per POSIX: non-recognized types should always be+		 * treated as regular files.+		 */+		archive_entry_set_filetype(entry, AE_IFREG);+		break;+	}+	return (err);+}++/*+ * Parse out header elements for "old-style" tar archives.+ */+static int+header_old_tar(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const void *h)+{+	const struct archive_entry_header_ustar	*header;+	int err = ARCHIVE_OK, err2;++	/* Copy filename over (to ensure null termination). */+	header = (const struct archive_entry_header_ustar *)h;+	if (archive_entry_copy_pathname_l(entry,+	    header->name, sizeof(header->name), tar->sconv) != 0) {+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");+		if (err == ARCHIVE_FATAL)+			return (err);+	}++	/* Grab rest of common fields */+	err2 = header_common(a, tar, entry, h);+	if (err > err2)+		err = err2;++	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);+	return (err);+}++/*+ * Read a Mac AppleDouble-encoded blob of file metadata,+ * if there is one.+ */+static int+read_mac_metadata_blob(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const void *h, size_t *unconsumed)+{+	int64_t size;+	const void *data;+	const char *p, *name;+	const wchar_t *wp, *wname;++	(void)h; /* UNUSED */++	wname = wp = archive_entry_pathname_w(entry);+	if (wp != NULL) {+		/* Find the last path element. */+		for (; *wp != L'\0'; ++wp) {+			if (wp[0] == '/' && wp[1] != L'\0')+				wname = wp + 1;+		}+		/*+		 * If last path element starts with "._", then+		 * this is a Mac extension.+		 */+		if (wname[0] != L'.' || wname[1] != L'_' || wname[2] == L'\0')+			return ARCHIVE_OK;+	} else {+		/* Find the last path element. */+		name = p = archive_entry_pathname(entry);+		if (p == NULL)+			return (ARCHIVE_FAILED);+		for (; *p != '\0'; ++p) {+			if (p[0] == '/' && p[1] != '\0')+				name = p + 1;+		}+		/*+		 * If last path element starts with "._", then+		 * this is a Mac extension.+		 */+		if (name[0] != '.' || name[1] != '_' || name[2] == '\0')+			return ARCHIVE_OK;+	}++ 	/* Read the body as a Mac OS metadata blob. */+	size = archive_entry_size(entry);++	/*+	 * TODO: Look beyond the body here to peek at the next header.+	 * If it's a regular header (not an extension header)+	 * that has the wrong name, just return the current+	 * entry as-is, without consuming the body here.+	 * That would reduce the risk of us mis-identifying+	 * an ordinary file that just happened to have+	 * a name starting with "._".+	 *+	 * Q: Is the above idea really possible?  Even+	 * when there are GNU or pax extension entries?+	 */+	data = __archive_read_ahead(a, (size_t)size, NULL);+	if (data == NULL) {+		*unconsumed = 0;+		return (ARCHIVE_FATAL);+	}+	archive_entry_copy_mac_metadata(entry, data, (size_t)size);+	*unconsumed = (size_t)((size + 511) & ~ 511);+	tar_flush_unconsumed(a, unconsumed);+	return (tar_read_header(a, tar, entry, unconsumed));+}++/*+ * Parse a file header for a pax extended archive entry.+ */+static int+header_pax_global(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const void *h, size_t *unconsumed)+{+	int err;++	err = read_body_to_string(a, tar, &(tar->pax_global), h, unconsumed);+	if (err != ARCHIVE_OK)+		return (err);+	err = tar_read_header(a, tar, entry, unconsumed);+	return (err);+}++static int+header_pax_extensions(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const void *h, size_t *unconsumed)+{+	int err, err2;++	err = read_body_to_string(a, tar, &(tar->pax_header), h, unconsumed);+	if (err != ARCHIVE_OK)+		return (err);++	/* Parse the next header. */+	err = tar_read_header(a, tar, entry, unconsumed);+	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))+		return (err);++	/*+	 * TODO: Parse global/default options into 'entry' struct here+	 * before handling file-specific options.+	 *+	 * This design (parse standard header, then overwrite with pax+	 * extended attribute data) usually works well, but isn't ideal;+	 * it would be better to parse the pax extended attributes first+	 * and then skip any fields in the standard header that were+	 * defined in the pax header.+	 */+	err2 = pax_header(a, tar, entry, &tar->pax_header);+	err =  err_combine(err, err2);+	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);+	return (err);+}+++/*+ * Parse a file header for a Posix "ustar" archive entry.  This also+ * handles "pax" or "extended ustar" entries.+ */+static int+header_ustar(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const void *h)+{+	const struct archive_entry_header_ustar	*header;+	struct archive_string *as;+	int err = ARCHIVE_OK, r;++	header = (const struct archive_entry_header_ustar *)h;++	/* Copy name into an internal buffer to ensure null-termination. */+	as = &(tar->entry_pathname);+	if (header->prefix[0]) {+		archive_strncpy(as, header->prefix, sizeof(header->prefix));+		if (as->s[archive_strlen(as) - 1] != '/')+			archive_strappend_char(as, '/');+		archive_strncat(as, header->name, sizeof(header->name));+	} else {+		archive_strncpy(as, header->name, sizeof(header->name));+	}+	if (archive_entry_copy_pathname_l(entry, as->s, archive_strlen(as),+	    tar->sconv) != 0) {+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");+		if (err == ARCHIVE_FATAL)+			return (err);+	}++	/* Handle rest of common fields. */+	r = header_common(a, tar, entry, h);+	if (r == ARCHIVE_FATAL)+		return (r);+	if (r < err)+		err = r;++	/* Handle POSIX ustar fields. */+	if (archive_entry_copy_uname_l(entry,+	    header->uname, sizeof(header->uname), tar->sconv) != 0) {+		err = set_conversion_failed_error(a, tar->sconv, "Uname");+		if (err == ARCHIVE_FATAL)+			return (err);+	}++	if (archive_entry_copy_gname_l(entry,+	    header->gname, sizeof(header->gname), tar->sconv) != 0) {+		err = set_conversion_failed_error(a, tar->sconv, "Gname");+		if (err == ARCHIVE_FATAL)+			return (err);+	}++	/* Parse out device numbers only for char and block specials. */+	if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {+		archive_entry_set_rdevmajor(entry, (dev_t)+		    tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));+		archive_entry_set_rdevminor(entry, (dev_t)+		    tar_atol(header->rdevminor, sizeof(header->rdevminor)));+	}++	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);++	return (err);+}+++/*+ * Parse the pax extended attributes record.+ *+ * Returns non-zero if there's an error in the data.+ */+static int+pax_header(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, struct archive_string *in_as)+{+	size_t attr_length, l, line_length, value_length;+	char *p;+	char *key, *value;+	struct archive_string *as;+	struct archive_string_conv *sconv;+	int err, err2;+	char *attr = in_as->s;++	attr_length = in_as->length;+	tar->pax_hdrcharset_binary = 0;+	archive_string_empty(&(tar->entry_gname));+	archive_string_empty(&(tar->entry_linkpath));+	archive_string_empty(&(tar->entry_pathname));+	archive_string_empty(&(tar->entry_pathname_override));+	archive_string_empty(&(tar->entry_uname));+	err = ARCHIVE_OK;+	while (attr_length > 0) {+		/* Parse decimal length field at start of line. */+		line_length = 0;+		l = attr_length;+		p = attr; /* Record start of line. */+		while (l>0) {+			if (*p == ' ') {+				p++;+				l--;+				break;+			}+			if (*p < '0' || *p > '9') {+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+				    "Ignoring malformed pax extended attributes");+				return (ARCHIVE_WARN);+			}+			line_length *= 10;+			line_length += *p - '0';+			if (line_length > 999999) {+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+				    "Rejecting pax extended attribute > 1MB");+				return (ARCHIVE_WARN);+			}+			p++;+			l--;+		}++		/*+		 * Parsed length must be no bigger than available data,+		 * at least 1, and the last character of the line must+		 * be '\n'.+		 */+		if (line_length > attr_length+		    || line_length < 1+		    || attr[line_length - 1] != '\n')+		{+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Ignoring malformed pax extended attribute");+			return (ARCHIVE_WARN);+		}++		/* Null-terminate the line. */+		attr[line_length - 1] = '\0';++		/* Find end of key and null terminate it. */+		key = p;+		if (key[0] == '=')+			return (-1);+		while (*p && *p != '=')+			++p;+		if (*p == '\0') {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Invalid pax extended attributes");+			return (ARCHIVE_WARN);+		}+		*p = '\0';++		value = p + 1;++		/* Some values may be binary data */+		value_length = attr + line_length - 1 - value;++		/* Identify this attribute and set it in the entry. */+		err2 = pax_attribute(a, tar, entry, key, value, value_length);+		if (err2 == ARCHIVE_FATAL)+			return (err2);+		err = err_combine(err, err2);++		/* Skip to next line */+		attr += line_length;+		attr_length -= line_length;+	}++	/*+	 * PAX format uses UTF-8 as default charset for its metadata+	 * unless hdrcharset=BINARY is present in its header.+	 * We apply the charset specified by the hdrcharset option only+	 * when the hdrcharset attribute(in PAX header) is BINARY because+	 * we respect the charset described in PAX header and BINARY also+	 * means that metadata(filename,uname and gname) character-set+	 * is unknown.+	 */+	if (tar->pax_hdrcharset_binary)+		sconv = tar->opt_sconv;+	else {+		sconv = archive_string_conversion_from_charset(+		    &(a->archive), "UTF-8", 1);+		if (sconv == NULL)+			return (ARCHIVE_FATAL);+		if (tar->compat_2x)+			archive_string_conversion_set_opt(sconv,+			    SCONV_SET_OPT_UTF8_LIBARCHIVE2X);+	}++	if (archive_strlen(&(tar->entry_gname)) > 0) {+		if (archive_entry_copy_gname_l(entry, tar->entry_gname.s,+		    archive_strlen(&(tar->entry_gname)), sconv) != 0) {+			err = set_conversion_failed_error(a, sconv, "Gname");+			if (err == ARCHIVE_FATAL)+				return (err);+			/* Use a converted an original name. */+			archive_entry_copy_gname(entry, tar->entry_gname.s);+		}+	}+	if (archive_strlen(&(tar->entry_linkpath)) > 0) {+		if (archive_entry_copy_link_l(entry, tar->entry_linkpath.s,+		    archive_strlen(&(tar->entry_linkpath)), sconv) != 0) {+			err = set_conversion_failed_error(a, sconv, "Linkname");+			if (err == ARCHIVE_FATAL)+				return (err);+			/* Use a converted an original name. */+			archive_entry_copy_link(entry, tar->entry_linkpath.s);+		}+	}+	/*+	 * Some extensions (such as the GNU sparse file extensions)+	 * deliberately store a synthetic name under the regular 'path'+	 * attribute and the real file name under a different attribute.+	 * Since we're supposed to not care about the order, we+	 * have no choice but to store all of the various filenames+	 * we find and figure it all out afterwards.  This is the+	 * figuring out part.+	 */+	as = NULL;+	if (archive_strlen(&(tar->entry_pathname_override)) > 0)+		as = &(tar->entry_pathname_override);+	else if (archive_strlen(&(tar->entry_pathname)) > 0)+		as = &(tar->entry_pathname);+	if (as != NULL) {+		if (archive_entry_copy_pathname_l(entry, as->s,+		    archive_strlen(as), sconv) != 0) {+			err = set_conversion_failed_error(a, sconv, "Pathname");+			if (err == ARCHIVE_FATAL)+				return (err);+			/* Use a converted an original name. */+			archive_entry_copy_pathname(entry, as->s);+		}+	}+	if (archive_strlen(&(tar->entry_uname)) > 0) {+		if (archive_entry_copy_uname_l(entry, tar->entry_uname.s,+		    archive_strlen(&(tar->entry_uname)), sconv) != 0) {+			err = set_conversion_failed_error(a, sconv, "Uname");+			if (err == ARCHIVE_FATAL)+				return (err);+			/* Use a converted an original name. */+			archive_entry_copy_uname(entry, tar->entry_uname.s);+		}+	}+	return (err);+}++static int+pax_attribute_xattr(struct archive_entry *entry,+	const char *name, const char *value)+{+	char *name_decoded;+	void *value_decoded;+	size_t value_len;++	if (strlen(name) < 18 || (memcmp(name, "LIBARCHIVE.xattr.", 17)) != 0)+		return 3;++	name += 17;++	/* URL-decode name */+	name_decoded = url_decode(name);+	if (name_decoded == NULL)+		return 2;++	/* Base-64 decode value */+	value_decoded = base64_decode(value, strlen(value), &value_len);+	if (value_decoded == NULL) {+		free(name_decoded);+		return 1;+	}++	archive_entry_xattr_add_entry(entry, name_decoded,+		value_decoded, value_len);++	free(name_decoded);+	free(value_decoded);+	return 0;+}++static int+pax_attribute_schily_xattr(struct archive_entry *entry,+	const char *name, const char *value, size_t value_length)+{+	if (strlen(name) < 14 || (memcmp(name, "SCHILY.xattr.", 13)) != 0)+		return 1;++	name += 13;++	archive_entry_xattr_add_entry(entry, name, value, value_length);++	return 0;+}++static int+pax_attribute_rht_security_selinux(struct archive_entry *entry,+	const char *value, size_t value_length)+{+	archive_entry_xattr_add_entry(entry, "security.selinux",+            value, value_length);++	return 0;+}++static int+pax_attribute_acl(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const char *value, int type)+{+	int r;+	const char* errstr;++	switch (type) {+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:+		errstr = "SCHILY.acl.access";+		break;+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:+		errstr = "SCHILY.acl.default";+		break;+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:+		errstr = "SCHILY.acl.ace";+		break;+	default:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Unknown ACL type: %d", type);+		return(ARCHIVE_FATAL);+	}++	if (tar->sconv_acl == NULL) {+		tar->sconv_acl =+		    archive_string_conversion_from_charset(+			&(a->archive), "UTF-8", 1);+		if (tar->sconv_acl == NULL)+			return (ARCHIVE_FATAL);+	}++	r = archive_acl_from_text_l(archive_entry_acl(entry), value, type,+	    tar->sconv_acl);+	if (r != ARCHIVE_OK) {+		if (r == ARCHIVE_FATAL) {+			archive_set_error(&a->archive, ENOMEM,+			    "%s %s", "Can't allocate memory for ",+			    errstr);+			return (r);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC, "%s %s", "Parse error: ", errstr);+	}+	return (r);+}++/*+ * Parse a single key=value attribute.  key/value pointers are+ * assumed to point into reasonably long-lived storage.+ *+ * Note that POSIX reserves all-lowercase keywords.  Vendor-specific+ * extensions should always have keywords of the form "VENDOR.attribute"+ * In particular, it's quite feasible to support many different+ * vendor extensions here.  I'm using "LIBARCHIVE" for extensions+ * unique to this library.+ *+ * Investigate other vendor-specific extensions and see if+ * any of them look useful.+ */+static int+pax_attribute(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const char *key, const char *value, size_t value_length)+{+	int64_t s;+	long n;+	int err = ARCHIVE_OK, r;++	if (value == NULL)+		value = "";	/* Disable compiler warning; do not pass+				 * NULL pointer to strlen().  */+	switch (key[0]) {+	case 'G':+		/* Reject GNU.sparse.* headers on non-regular files. */+		if (strncmp(key, "GNU.sparse", 10) == 0 &&+		    !tar->sparse_allowed) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Non-regular file cannot be sparse");+			return (ARCHIVE_FATAL);+		}++		/* GNU "0.0" sparse pax format. */+		if (strcmp(key, "GNU.sparse.numblocks") == 0) {+			tar->sparse_offset = -1;+			tar->sparse_numbytes = -1;+			tar->sparse_gnu_major = 0;+			tar->sparse_gnu_minor = 0;+		}+		if (strcmp(key, "GNU.sparse.offset") == 0) {+			tar->sparse_offset = tar_atol10(value, strlen(value));+			if (tar->sparse_numbytes != -1) {+				if (gnu_add_sparse_entry(a, tar,+				    tar->sparse_offset, tar->sparse_numbytes)+				    != ARCHIVE_OK)+					return (ARCHIVE_FATAL);+				tar->sparse_offset = -1;+				tar->sparse_numbytes = -1;+			}+		}+		if (strcmp(key, "GNU.sparse.numbytes") == 0) {+			tar->sparse_numbytes = tar_atol10(value, strlen(value));+			if (tar->sparse_numbytes != -1) {+				if (gnu_add_sparse_entry(a, tar,+				    tar->sparse_offset, tar->sparse_numbytes)+				    != ARCHIVE_OK)+					return (ARCHIVE_FATAL);+				tar->sparse_offset = -1;+				tar->sparse_numbytes = -1;+			}+		}+		if (strcmp(key, "GNU.sparse.size") == 0) {+			tar->realsize = tar_atol10(value, strlen(value));+			archive_entry_set_size(entry, tar->realsize);+			tar->realsize_override = 1;+		}++		/* GNU "0.1" sparse pax format. */+		if (strcmp(key, "GNU.sparse.map") == 0) {+			tar->sparse_gnu_major = 0;+			tar->sparse_gnu_minor = 1;+			if (gnu_sparse_01_parse(a, tar, value) != ARCHIVE_OK)+				return (ARCHIVE_WARN);+		}++		/* GNU "1.0" sparse pax format */+		if (strcmp(key, "GNU.sparse.major") == 0) {+			tar->sparse_gnu_major = (int)tar_atol10(value, strlen(value));+			tar->sparse_gnu_pending = 1;+		}+		if (strcmp(key, "GNU.sparse.minor") == 0) {+			tar->sparse_gnu_minor = (int)tar_atol10(value, strlen(value));+			tar->sparse_gnu_pending = 1;+		}+		if (strcmp(key, "GNU.sparse.name") == 0) {+			/*+			 * The real filename; when storing sparse+			 * files, GNU tar puts a synthesized name into+			 * the regular 'path' attribute in an attempt+			 * to limit confusion. ;-)+			 */+			archive_strcpy(&(tar->entry_pathname_override), value);+		}+		if (strcmp(key, "GNU.sparse.realsize") == 0) {+			tar->realsize = tar_atol10(value, strlen(value));+			archive_entry_set_size(entry, tar->realsize);+			tar->realsize_override = 1;+		}+		break;+	case 'L':+		/* Our extensions */+/* TODO: Handle arbitrary extended attributes... */+/*+		if (strcmp(key, "LIBARCHIVE.xxxxxxx") == 0)+			archive_entry_set_xxxxxx(entry, value);+*/+		if (strcmp(key, "LIBARCHIVE.creationtime") == 0) {+			pax_time(value, &s, &n);+			archive_entry_set_birthtime(entry, s, n);+		}+		if (strcmp(key, "LIBARCHIVE.symlinktype") == 0) {+			if (strcmp(value, "file") == 0) {+				archive_entry_set_symlink_type(entry,+				    AE_SYMLINK_TYPE_FILE);+			} else if (strcmp(value, "dir") == 0) {+				archive_entry_set_symlink_type(entry,+				    AE_SYMLINK_TYPE_DIRECTORY);+			}+		}+		if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0)+			pax_attribute_xattr(entry, key, value);+		break;+	case 'R':+		/* GNU tar uses RHT.security header to store SELinux xattrs+		 * SCHILY.xattr.security.selinux == RHT.security.selinux */+		if (strcmp(key, "RHT.security.selinux") == 0) {+			pax_attribute_rht_security_selinux(entry, value,+			    value_length);+			}+		break;+	case 'S':+		/* We support some keys used by the "star" archiver */+		if (strcmp(key, "SCHILY.acl.access") == 0) {+			r = pax_attribute_acl(a, tar, entry, value,+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);+			if (r == ARCHIVE_FATAL)+				return (r);+		} else if (strcmp(key, "SCHILY.acl.default") == 0) {+			r = pax_attribute_acl(a, tar, entry, value,+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);+			if (r == ARCHIVE_FATAL)+				return (r);+		} else if (strcmp(key, "SCHILY.acl.ace") == 0) {+			r = pax_attribute_acl(a, tar, entry, value,+			    ARCHIVE_ENTRY_ACL_TYPE_NFS4);+			if (r == ARCHIVE_FATAL)+				return (r);+		} else if (strcmp(key, "SCHILY.devmajor") == 0) {+			archive_entry_set_rdevmajor(entry,+			    (dev_t)tar_atol10(value, strlen(value)));+		} else if (strcmp(key, "SCHILY.devminor") == 0) {+			archive_entry_set_rdevminor(entry,+			    (dev_t)tar_atol10(value, strlen(value)));+		} else if (strcmp(key, "SCHILY.fflags") == 0) {+			archive_entry_copy_fflags_text(entry, value);+		} else if (strcmp(key, "SCHILY.dev") == 0) {+			archive_entry_set_dev(entry,+			    (dev_t)tar_atol10(value, strlen(value)));+		} else if (strcmp(key, "SCHILY.ino") == 0) {+			archive_entry_set_ino(entry,+			    tar_atol10(value, strlen(value)));+		} else if (strcmp(key, "SCHILY.nlink") == 0) {+			archive_entry_set_nlink(entry, (unsigned)+			    tar_atol10(value, strlen(value)));+		} else if (strcmp(key, "SCHILY.realsize") == 0) {+			tar->realsize = tar_atol10(value, strlen(value));+			tar->realsize_override = 1;+			archive_entry_set_size(entry, tar->realsize);+		} else if (strncmp(key, "SCHILY.xattr.", 13) == 0) {+			pax_attribute_schily_xattr(entry, key, value,+			    value_length);+		} else if (strcmp(key, "SUN.holesdata") == 0) {+			/* A Solaris extension for sparse. */+			r = solaris_sparse_parse(a, tar, entry, value);+			if (r < err) {+				if (r == ARCHIVE_FATAL)+					return (r);+				err = r;+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Parse error: SUN.holesdata");+			}+		}+		break;+	case 'a':+		if (strcmp(key, "atime") == 0) {+			pax_time(value, &s, &n);+			archive_entry_set_atime(entry, s, n);+		}+		break;+	case 'c':+		if (strcmp(key, "ctime") == 0) {+			pax_time(value, &s, &n);+			archive_entry_set_ctime(entry, s, n);+		} else if (strcmp(key, "charset") == 0) {+			/* TODO: Publish charset information in entry. */+		} else if (strcmp(key, "comment") == 0) {+			/* TODO: Publish comment in entry. */+		}+		break;+	case 'g':+		if (strcmp(key, "gid") == 0) {+			archive_entry_set_gid(entry,+			    tar_atol10(value, strlen(value)));+		} else if (strcmp(key, "gname") == 0) {+			archive_strcpy(&(tar->entry_gname), value);+		}+		break;+	case 'h':+		if (strcmp(key, "hdrcharset") == 0) {+			if (strcmp(value, "BINARY") == 0)+				/* Binary  mode. */+				tar->pax_hdrcharset_binary = 1;+			else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0)+				tar->pax_hdrcharset_binary = 0;+		}+		break;+	case 'l':+		/* pax interchange doesn't distinguish hardlink vs. symlink. */+		if (strcmp(key, "linkpath") == 0) {+			archive_strcpy(&(tar->entry_linkpath), value);+		}+		break;+	case 'm':+		if (strcmp(key, "mtime") == 0) {+			pax_time(value, &s, &n);+			archive_entry_set_mtime(entry, s, n);+		}+		break;+	case 'p':+		if (strcmp(key, "path") == 0) {+			archive_strcpy(&(tar->entry_pathname), value);+		}+		break;+	case 'r':+		/* POSIX has reserved 'realtime.*' */+		break;+	case 's':+		/* POSIX has reserved 'security.*' */+		/* Someday: if (strcmp(key, "security.acl") == 0) { ... } */+		if (strcmp(key, "size") == 0) {+			/* "size" is the size of the data in the entry. */+			tar->entry_bytes_remaining+			    = tar_atol10(value, strlen(value));+			/*+			 * The "size" pax header keyword always overrides the+			 * "size" field in the tar header.+			 * GNU.sparse.realsize, GNU.sparse.size and+			 * SCHILY.realsize override this value.+			 */+			if (!tar->realsize_override) {+				archive_entry_set_size(entry,+				    tar->entry_bytes_remaining);+				tar->realsize+				    = tar->entry_bytes_remaining;+			}+		}+		break;+	case 'u':+		if (strcmp(key, "uid") == 0) {+			archive_entry_set_uid(entry,+			    tar_atol10(value, strlen(value)));+		} else if (strcmp(key, "uname") == 0) {+			archive_strcpy(&(tar->entry_uname), value);+		}+		break;+	}+	return (err);+}++++/*+ * parse a decimal time value, which may include a fractional portion+ */+static void+pax_time(const char *p, int64_t *ps, long *pn)+{+	char digit;+	int64_t	s;+	unsigned long l;+	int sign;+	int64_t limit, last_digit_limit;++	limit = INT64_MAX / 10;+	last_digit_limit = INT64_MAX % 10;++	s = 0;+	sign = 1;+	if (*p == '-') {+		sign = -1;+		p++;+	}+	while (*p >= '0' && *p <= '9') {+		digit = *p - '0';+		if (s > limit ||+		    (s == limit && digit > last_digit_limit)) {+			s = INT64_MAX;+			break;+		}+		s = (s * 10) + digit;+		++p;+	}++	*ps = s * sign;++	/* Calculate nanoseconds. */+	*pn = 0;++	if (*p != '.')+		return;++	l = 100000000UL;+	do {+		++p;+		if (*p >= '0' && *p <= '9')+			*pn += (*p - '0') * l;+		else+			break;+	} while (l /= 10);+}++/*+ * Parse GNU tar header+ */+static int+header_gnutar(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const void *h, size_t *unconsumed)+{+	const struct archive_entry_header_gnutar *header;+	int64_t t;+	int err = ARCHIVE_OK;++	/*+	 * GNU header is like POSIX ustar, except 'prefix' is+	 * replaced with some other fields. This also means the+	 * filename is stored as in old-style archives.+	 */++	/* Grab fields common to all tar variants. */+	err = header_common(a, tar, entry, h);+	if (err == ARCHIVE_FATAL)+		return (err);++	/* Copy filename over (to ensure null termination). */+	header = (const struct archive_entry_header_gnutar *)h;+	if (archive_entry_copy_pathname_l(entry,+	    header->name, sizeof(header->name), tar->sconv) != 0) {+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");+		if (err == ARCHIVE_FATAL)+			return (err);+	}++	/* Fields common to ustar and GNU */+	/* XXX Can the following be factored out since it's common+	 * to ustar and gnu tar?  Is it okay to move it down into+	 * header_common, perhaps?  */+	if (archive_entry_copy_uname_l(entry,+	    header->uname, sizeof(header->uname), tar->sconv) != 0) {+		err = set_conversion_failed_error(a, tar->sconv, "Uname");+		if (err == ARCHIVE_FATAL)+			return (err);+	}++	if (archive_entry_copy_gname_l(entry,+	    header->gname, sizeof(header->gname), tar->sconv) != 0) {+		err = set_conversion_failed_error(a, tar->sconv, "Gname");+		if (err == ARCHIVE_FATAL)+			return (err);+	}++	/* Parse out device numbers only for char and block specials */+	if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {+		archive_entry_set_rdevmajor(entry, (dev_t)+		    tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));+		archive_entry_set_rdevminor(entry, (dev_t)+		    tar_atol(header->rdevminor, sizeof(header->rdevminor)));+	} else+		archive_entry_set_rdev(entry, 0);++	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);++	/* Grab GNU-specific fields. */+	t = tar_atol(header->atime, sizeof(header->atime));+	if (t > 0)+		archive_entry_set_atime(entry, t, 0);+	t = tar_atol(header->ctime, sizeof(header->ctime));+	if (t > 0)+		archive_entry_set_ctime(entry, t, 0);++	if (header->realsize[0] != 0) {+		tar->realsize+		    = tar_atol(header->realsize, sizeof(header->realsize));+		archive_entry_set_size(entry, tar->realsize);+		tar->realsize_override = 1;+	}++	if (header->sparse[0].offset[0] != 0) {+		if (gnu_sparse_old_read(a, tar, header, unconsumed)+		    != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	} else {+		if (header->isextended[0] != 0) {+			/* XXX WTF? XXX */+		}+	}++	return (err);+}++static int+gnu_add_sparse_entry(struct archive_read *a, struct tar *tar,+    int64_t offset, int64_t remaining)+{+	struct sparse_block *p;++	p = (struct sparse_block *)calloc(1, sizeof(*p));+	if (p == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	if (tar->sparse_last != NULL)+		tar->sparse_last->next = p;+	else+		tar->sparse_list = p;+	tar->sparse_last = p;+	if (remaining < 0 || offset < 0 || offset > INT64_MAX - remaining) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed sparse map data");+		return (ARCHIVE_FATAL);+	}+	p->offset = offset;+	p->remaining = remaining;+	return (ARCHIVE_OK);+}++static void+gnu_clear_sparse_list(struct tar *tar)+{+	struct sparse_block *p;++	while (tar->sparse_list != NULL) {+		p = tar->sparse_list;+		tar->sparse_list = p->next;+		free(p);+	}+	tar->sparse_last = NULL;+}++/*+ * GNU tar old-format sparse data.+ *+ * GNU old-format sparse data is stored in a fixed-field+ * format.  Offset/size values are 11-byte octal fields (same+ * format as 'size' field in ustart header).  These are+ * stored in the header, allocating subsequent header blocks+ * as needed.  Extending the header in this way is a pretty+ * severe POSIX violation; this design has earned GNU tar a+ * lot of criticism.+ */++static int+gnu_sparse_old_read(struct archive_read *a, struct tar *tar,+    const struct archive_entry_header_gnutar *header, size_t *unconsumed)+{+	ssize_t bytes_read;+	const void *data;+	struct extended {+		struct gnu_sparse sparse[21];+		char	isextended[1];+		char	padding[7];+	};+	const struct extended *ext;++	if (gnu_sparse_old_parse(a, tar, header->sparse, 4) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	if (header->isextended[0] == 0)+		return (ARCHIVE_OK);++	do {+		tar_flush_unconsumed(a, unconsumed);+		data = __archive_read_ahead(a, 512, &bytes_read);+		if (bytes_read < 0)+			return (ARCHIVE_FATAL);+		if (bytes_read < 512) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated tar archive "+			    "detected while reading sparse file data");+			return (ARCHIVE_FATAL);+		}+		*unconsumed = 512;+		ext = (const struct extended *)data;+		if (gnu_sparse_old_parse(a, tar, ext->sparse, 21) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	} while (ext->isextended[0] != 0);+	if (tar->sparse_list != NULL)+		tar->entry_offset = tar->sparse_list->offset;+	return (ARCHIVE_OK);+}++static int+gnu_sparse_old_parse(struct archive_read *a, struct tar *tar,+    const struct gnu_sparse *sparse, int length)+{+	while (length > 0 && sparse->offset[0] != 0) {+		if (gnu_add_sparse_entry(a, tar,+		    tar_atol(sparse->offset, sizeof(sparse->offset)),+		    tar_atol(sparse->numbytes, sizeof(sparse->numbytes)))+		    != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		sparse++;+		length--;+	}+	return (ARCHIVE_OK);+}++/*+ * GNU tar sparse format 0.0+ *+ * Beginning with GNU tar 1.15, sparse files are stored using+ * information in the pax extended header.  The GNU tar maintainers+ * have gone through a number of variations in the process of working+ * out this scheme; fortunately, they're all numbered.+ *+ * Sparse format 0.0 uses attribute GNU.sparse.numblocks to store the+ * number of blocks, and GNU.sparse.offset/GNU.sparse.numbytes to+ * store offset/size for each block.  The repeated instances of these+ * latter fields violate the pax specification (which frowns on+ * duplicate keys), so this format was quickly replaced.+ */++/*+ * GNU tar sparse format 0.1+ *+ * This version replaced the offset/numbytes attributes with+ * a single "map" attribute that stored a list of integers.  This+ * format had two problems: First, the "map" attribute could be very+ * long, which caused problems for some implementations.  More+ * importantly, the sparse data was lost when extracted by archivers+ * that didn't recognize this extension.+ */++static int+gnu_sparse_01_parse(struct archive_read *a, struct tar *tar, const char *p)+{+	const char *e;+	int64_t offset = -1, size = -1;++	for (;;) {+		e = p;+		while (*e != '\0' && *e != ',') {+			if (*e < '0' || *e > '9')+				return (ARCHIVE_WARN);+			e++;+		}+		if (offset < 0) {+			offset = tar_atol10(p, e - p);+			if (offset < 0)+				return (ARCHIVE_WARN);+		} else {+			size = tar_atol10(p, e - p);+			if (size < 0)+				return (ARCHIVE_WARN);+			if (gnu_add_sparse_entry(a, tar, offset, size)+			    != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			offset = -1;+		}+		if (*e == '\0')+			return (ARCHIVE_OK);+		p = e + 1;+	}+}++/*+ * GNU tar sparse format 1.0+ *+ * The idea: The offset/size data is stored as a series of base-10+ * ASCII numbers prepended to the file data, so that dearchivers that+ * don't support this format will extract the block map along with the+ * data and a separate post-process can restore the sparseness.+ *+ * Unfortunately, GNU tar 1.16 had a bug that added unnecessary+ * padding to the body of the file when using this format.  GNU tar+ * 1.17 corrected this bug without bumping the version number, so+ * it's not possible to support both variants.  This code supports+ * the later variant at the expense of not supporting the former.+ *+ * This variant also replaced GNU.sparse.size with GNU.sparse.realsize+ * and introduced the GNU.sparse.major/GNU.sparse.minor attributes.+ */++/*+ * Read the next line from the input, and parse it as a decimal+ * integer followed by '\n'.  Returns positive integer value or+ * negative on error.+ */+static int64_t+gnu_sparse_10_atol(struct archive_read *a, struct tar *tar,+    int64_t *remaining, size_t *unconsumed)+{+	int64_t l, limit, last_digit_limit;+	const char *p;+	ssize_t bytes_read;+	int base, digit;++	base = 10;+	limit = INT64_MAX / base;+	last_digit_limit = INT64_MAX % base;++	/*+	 * Skip any lines starting with '#'; GNU tar specs+	 * don't require this, but they should.+	 */+	do {+		bytes_read = readline(a, tar, &p,+			(ssize_t)tar_min(*remaining, 100), unconsumed);+		if (bytes_read <= 0)+			return (ARCHIVE_FATAL);+		*remaining -= bytes_read;+	} while (p[0] == '#');++	l = 0;+	while (bytes_read > 0) {+		if (*p == '\n')+			return (l);+		if (*p < '0' || *p >= '0' + base)+			return (ARCHIVE_WARN);+		digit = *p - '0';+		if (l > limit || (l == limit && digit > last_digit_limit))+			l = INT64_MAX; /* Truncate on overflow. */+		else+			l = (l * base) + digit;+		p++;+		bytes_read--;+	}+	/* TODO: Error message. */+	return (ARCHIVE_WARN);+}++/*+ * Returns length (in bytes) of the sparse data description+ * that was read.+ */+static ssize_t+gnu_sparse_10_read(struct archive_read *a, struct tar *tar, size_t *unconsumed)+{+	ssize_t bytes_read;+	int entries;+	int64_t offset, size, to_skip, remaining;++	/* Clear out the existing sparse list. */+	gnu_clear_sparse_list(tar);++	remaining = tar->entry_bytes_remaining;++	/* Parse entries. */+	entries = (int)gnu_sparse_10_atol(a, tar, &remaining, unconsumed);+	if (entries < 0)+		return (ARCHIVE_FATAL);+	/* Parse the individual entries. */+	while (entries-- > 0) {+		/* Parse offset/size */+		offset = gnu_sparse_10_atol(a, tar, &remaining, unconsumed);+		if (offset < 0)+			return (ARCHIVE_FATAL);+		size = gnu_sparse_10_atol(a, tar, &remaining, unconsumed);+		if (size < 0)+			return (ARCHIVE_FATAL);+		/* Add a new sparse entry. */+		if (gnu_add_sparse_entry(a, tar, offset, size) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}+	/* Skip rest of block... */+	tar_flush_unconsumed(a, unconsumed);+	bytes_read = (ssize_t)(tar->entry_bytes_remaining - remaining);+	to_skip = 0x1ff & -bytes_read;+	/* Fail if tar->entry_bytes_remaing would get negative */+	if (to_skip > remaining)+		return (ARCHIVE_FATAL);+	if (to_skip != __archive_read_consume(a, to_skip))+		return (ARCHIVE_FATAL);+	return ((ssize_t)(bytes_read + to_skip));+}++/*+ * Solaris pax extension for a sparse file. This is recorded with the+ * data and hole pairs. The way recording sparse information by Solaris'+ * pax simply indicates where data and sparse are, so the stored contents+ * consist of both data and hole.+ */+static int+solaris_sparse_parse(struct archive_read *a, struct tar *tar,+    struct archive_entry *entry, const char *p)+{+	const char *e;+	int64_t start, end;+	int hole = 1;++	(void)entry; /* UNUSED */++	end = 0;+	if (*p == ' ')+		p++;+	else+		return (ARCHIVE_WARN);+	for (;;) {+		e = p;+		while (*e != '\0' && *e != ' ') {+			if (*e < '0' || *e > '9')+				return (ARCHIVE_WARN);+			e++;+		}+		start = end;+		end = tar_atol10(p, e - p);+		if (end < 0)+			return (ARCHIVE_WARN);+		if (start < end) {+			if (gnu_add_sparse_entry(a, tar, start,+			    end - start) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			tar->sparse_last->hole = hole;+		}+		if (*e == '\0')+			return (ARCHIVE_OK);+		p = e + 1;+		hole = hole == 0;+	}+}++/*-+ * Convert text->integer.+ *+ * Traditional tar formats (including POSIX) specify base-8 for+ * all of the standard numeric fields.  This is a significant limitation+ * in practice:+ *   = file size is limited to 8GB+ *   = rdevmajor and rdevminor are limited to 21 bits+ *   = uid/gid are limited to 21 bits+ *+ * There are two workarounds for this:+ *   = pax extended headers, which use variable-length string fields+ *   = GNU tar and STAR both allow either base-8 or base-256 in+ *      most fields.  The high bit is set to indicate base-256.+ *+ * On read, this implementation supports both extensions.+ */+static int64_t+tar_atol(const char *p, size_t char_cnt)+{+	/*+	 * Technically, GNU tar considers a field to be in base-256+	 * only if the first byte is 0xff or 0x80.+	 */+	if (*p & 0x80)+		return (tar_atol256(p, char_cnt));+	return (tar_atol8(p, char_cnt));+}++/*+ * Note that this implementation does not (and should not!) obey+ * locale settings; you cannot simply substitute strtol here, since+ * it does obey locale.+ */+static int64_t+tar_atol_base_n(const char *p, size_t char_cnt, int base)+{+	int64_t	l, maxval, limit, last_digit_limit;+	int digit, sign;++	maxval = INT64_MAX;+	limit = INT64_MAX / base;+	last_digit_limit = INT64_MAX % base;++	/* the pointer will not be dereferenced if char_cnt is zero+	 * due to the way the && operator is evaluated.+	 */+	while (char_cnt != 0 && (*p == ' ' || *p == '\t')) {+		p++;+		char_cnt--;+	}++	sign = 1;+	if (char_cnt != 0 && *p == '-') {+		sign = -1;+		p++;+		char_cnt--;++		maxval = INT64_MIN;+		limit = -(INT64_MIN / base);+		last_digit_limit = INT64_MIN % base;+	}++	l = 0;+	if (char_cnt != 0) {+		digit = *p - '0';+		while (digit >= 0 && digit < base  && char_cnt != 0) {+			if (l>limit || (l == limit && digit > last_digit_limit)) {+				return maxval; /* Truncate on overflow. */+			}+			l = (l * base) + digit;+			digit = *++p - '0';+			char_cnt--;+		}+	}+	return (sign < 0) ? -l : l;+}++static int64_t+tar_atol8(const char *p, size_t char_cnt)+{+	return tar_atol_base_n(p, char_cnt, 8);+}++static int64_t+tar_atol10(const char *p, size_t char_cnt)+{+	return tar_atol_base_n(p, char_cnt, 10);+}++/*+ * Parse a base-256 integer.  This is just a variable-length+ * twos-complement signed binary value in big-endian order, except+ * that the high-order bit is ignored.  The values here can be up to+ * 12 bytes, so we need to be careful about overflowing 64-bit+ * (8-byte) integers.+ *+ * This code unashamedly assumes that the local machine uses 8-bit+ * bytes and twos-complement arithmetic.+ */+static int64_t+tar_atol256(const char *_p, size_t char_cnt)+{+	uint64_t l;+	const unsigned char *p = (const unsigned char *)_p;+	unsigned char c, neg;++	/* Extend 7-bit 2s-comp to 8-bit 2s-comp, decide sign. */+	c = *p;+	if (c & 0x40) {+		neg = 0xff;+		c |= 0x80;+		l = ~ARCHIVE_LITERAL_ULL(0);+	} else {+		neg = 0;+		c &= 0x7f;+		l = 0;+	}++	/* If more than 8 bytes, check that we can ignore+	 * high-order bits without overflow. */+	while (char_cnt > sizeof(int64_t)) {+		--char_cnt;+		if (c != neg)+			return neg ? INT64_MIN : INT64_MAX;+		c = *++p;+	}++	/* c is first byte that fits; if sign mismatch, return overflow */+	if ((c ^ neg) & 0x80) {+		return neg ? INT64_MIN : INT64_MAX;+	}++	/* Accumulate remaining bytes. */+	while (--char_cnt > 0) {+		l = (l << 8) | c;+		c = *++p;+	}+	l = (l << 8) | c;+	/* Return signed twos-complement value. */+	return (int64_t)(l);+}++/*+ * Returns length of line (including trailing newline)+ * or negative on error.  'start' argument is updated to+ * point to first character of line.  This avoids copying+ * when possible.+ */+static ssize_t+readline(struct archive_read *a, struct tar *tar, const char **start,+    ssize_t limit, size_t *unconsumed)+{+	ssize_t bytes_read;+	ssize_t total_size = 0;+	const void *t;+	const char *s;+	void *p;++	tar_flush_unconsumed(a, unconsumed);++	t = __archive_read_ahead(a, 1, &bytes_read);+	if (bytes_read <= 0)+		return (ARCHIVE_FATAL);+	s = t;  /* Start of line? */+	p = memchr(t, '\n', bytes_read);+	/* If we found '\n' in the read buffer, return pointer to that. */+	if (p != NULL) {+		bytes_read = 1 + ((const char *)p) - s;+		if (bytes_read > limit) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Line too long");+			return (ARCHIVE_FATAL);+		}+		*unconsumed = bytes_read;+		*start = s;+		return (bytes_read);+	}+	*unconsumed = bytes_read;+	/* Otherwise, we need to accumulate in a line buffer. */+	for (;;) {+		if (total_size + bytes_read > limit) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Line too long");+			return (ARCHIVE_FATAL);+		}+		if (archive_string_ensure(&tar->line, total_size + bytes_read) == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate working buffer");+			return (ARCHIVE_FATAL);+		}+		memcpy(tar->line.s + total_size, t, bytes_read);+		tar_flush_unconsumed(a, unconsumed);+		total_size += bytes_read;+		/* If we found '\n', clean up and return. */+		if (p != NULL) {+			*start = tar->line.s;+			return (total_size);+		}+		/* Read some more. */+		t = __archive_read_ahead(a, 1, &bytes_read);+		if (bytes_read <= 0)+			return (ARCHIVE_FATAL);+		s = t;  /* Start of line? */+		p = memchr(t, '\n', bytes_read);+		/* If we found '\n', trim the read. */+		if (p != NULL) {+			bytes_read = 1 + ((const char *)p) - s;+		}+		*unconsumed = bytes_read;+	}+}++/*+ * base64_decode - Base64 decode+ *+ * This accepts most variations of base-64 encoding, including:+ *    * with or without line breaks+ *    * with or without the final group padded with '=' or '_' characters+ * (The most economical Base-64 variant does not pad the last group and+ * omits line breaks; RFC1341 used for MIME requires both.)+ */+static char *+base64_decode(const char *s, size_t len, size_t *out_len)+{+	static const unsigned char digits[64] = {+		'A','B','C','D','E','F','G','H','I','J','K','L','M','N',+		'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b',+		'c','d','e','f','g','h','i','j','k','l','m','n','o','p',+		'q','r','s','t','u','v','w','x','y','z','0','1','2','3',+		'4','5','6','7','8','9','+','/' };+	static unsigned char decode_table[128];+	char *out, *d;+	const unsigned char *src = (const unsigned char *)s;++	/* If the decode table is not yet initialized, prepare it. */+	if (decode_table[digits[1]] != 1) {+		unsigned i;+		memset(decode_table, 0xff, sizeof(decode_table));+		for (i = 0; i < sizeof(digits); i++)+			decode_table[digits[i]] = i;+	}++	/* Allocate enough space to hold the entire output. */+	/* Note that we may not use all of this... */+	out = (char *)malloc(len - len / 4 + 1);+	if (out == NULL) {+		*out_len = 0;+		return (NULL);+	}+	d = out;++	while (len > 0) {+		/* Collect the next group of (up to) four characters. */+		int v = 0;+		int group_size = 0;+		while (group_size < 4 && len > 0) {+			/* '=' or '_' padding indicates final group. */+			if (*src == '=' || *src == '_') {+				len = 0;+				break;+			}+			/* Skip illegal characters (including line breaks) */+			if (*src > 127 || *src < 32+			    || decode_table[*src] == 0xff) {+				len--;+				src++;+				continue;+			}+			v <<= 6;+			v |= decode_table[*src++];+			len --;+			group_size++;+		}+		/* Align a short group properly. */+		v <<= 6 * (4 - group_size);+		/* Unpack the group we just collected. */+		switch (group_size) {+		case 4: d[2] = v & 0xff;+			/* FALLTHROUGH */+		case 3: d[1] = (v >> 8) & 0xff;+			/* FALLTHROUGH */+		case 2: d[0] = (v >> 16) & 0xff;+			break;+		case 1: /* this is invalid! */+			break;+		}+		d += group_size * 3 / 4;+	}++	*out_len = d - out;+	return (out);+}++static char *+url_decode(const char *in)+{+	char *out, *d;+	const char *s;++	out = (char *)malloc(strlen(in) + 1);+	if (out == NULL)+		return (NULL);+	for (s = in, d = out; *s != '\0'; ) {+		if (s[0] == '%' && s[1] != '\0' && s[2] != '\0') {+			/* Try to convert % escape */+			int digit1 = tohex(s[1]);+			int digit2 = tohex(s[2]);+			if (digit1 >= 0 && digit2 >= 0) {+				/* Looks good, consume three chars */+				s += 3;+				/* Convert output */+				*d++ = ((digit1 << 4) | digit2);+				continue;+			}+			/* Else fall through and treat '%' as normal char */+		}+		*d++ = *s++;+	}+	*d = '\0';+	return (out);+}++static int+tohex(int c)+{+	if (c >= '0' && c <= '9')+		return (c - '0');+	else if (c >= 'A' && c <= 'F')+		return (c - 'A' + 10);+	else if (c >= 'a' && c <= 'f')+		return (c - 'a' + 10);+	else+		return (-1);+}
+ c/archive_read_support_format_warc.c view
@@ -0,0 +1,832 @@+/*-+ * Copyright (c) 2014 Sebastian Freundt+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++/**+ * WARC is standardised by ISO TC46/SC4/WG12 and currently available as+ * ISO 28500:2009.+ * For the purposes of this file we used the final draft from:+ * http://bibnum.bnf.fr/warc/WARC_ISO_28500_version1_latestdraft.pdf+ *+ * Todo:+ * [ ] real-world warcs can contain resources at endpoints ending in /+ *     e.g. http://bibnum.bnf.fr/warc/+ *     if you're lucky their response contains a Content-Location: header+ *     pointing to a unix-compliant filename, in the example above it's+ *     Content-Location: http://bibnum.bnf.fr/warc/index.html+ *     however, that's not mandated and github for example doesn't follow+ *     this convention.+ *     We need a set of archive options to control what to do with+ *     entries like these, at the moment care is taken to skip them.+ *+ **/++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_CTYPE_H+#include <ctype.h>+#endif+#ifdef HAVE_TIME_H+#include <time.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_read_private.h"++typedef enum {+	WT_NONE,+	/* warcinfo */+	WT_INFO,+	/* metadata */+	WT_META,+	/* resource */+	WT_RSRC,+	/* request, unsupported */+	WT_REQ,+	/* response, unsupported */+	WT_RSP,+	/* revisit, unsupported */+	WT_RVIS,+	/* conversion, unsupported */+	WT_CONV,+	/* continuation, unsupported at the moment */+	WT_CONT,+	/* invalid type */+	LAST_WT+} warc_type_t;++typedef struct {+	size_t len;+	const char *str;+} warc_string_t;++typedef struct {+	size_t len;+	char *str;+} warc_strbuf_t;++struct warc_s {+	/* content length ahead */+	size_t cntlen;+	/* and how much we've processed so far */+	size_t cntoff;+	/* and how much we need to consume between calls */+	size_t unconsumed;++	/* string pool */+	warc_strbuf_t pool;+	/* previous version */+	unsigned int pver;+	/* stringified format name */+	struct archive_string sver;+};++static int _warc_bid(struct archive_read *a, int);+static int _warc_cleanup(struct archive_read *a);+static int _warc_read(struct archive_read*, const void**, size_t*, int64_t*);+static int _warc_skip(struct archive_read *a);+static int _warc_rdhdr(struct archive_read *a, struct archive_entry *e);++/* private routines */+static unsigned int _warc_rdver(const char buf[10], size_t bsz);+static unsigned int _warc_rdtyp(const char *buf, size_t bsz);+static warc_string_t _warc_rduri(const char *buf, size_t bsz);+static ssize_t _warc_rdlen(const char *buf, size_t bsz);+static time_t _warc_rdrtm(const char *buf, size_t bsz);+static time_t _warc_rdmtm(const char *buf, size_t bsz);+static const char *_warc_find_eoh(const char *buf, size_t bsz);+static const char *_warc_find_eol(const char *buf, size_t bsz);++int+archive_read_support_format_warc(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct warc_s *w;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_warc");++	if ((w = calloc(1, sizeof(*w))) == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate warc data");+		return (ARCHIVE_FATAL);+	}++	r = __archive_read_register_format(+		a, w, "warc",+		_warc_bid, NULL, _warc_rdhdr, _warc_read,+		_warc_skip, NULL, _warc_cleanup, NULL, NULL);++	if (r != ARCHIVE_OK) {+		free(w);+		return (r);+	}+	return (ARCHIVE_OK);+}++static int+_warc_cleanup(struct archive_read *a)+{+	struct warc_s *w = a->format->data;++	if (w->pool.len > 0U) {+		free(w->pool.str);+	}+	archive_string_free(&w->sver);+	free(w);+	a->format->data = NULL;+	return (ARCHIVE_OK);+}++static int+_warc_bid(struct archive_read *a, int best_bid)+{+	const char *hdr;+	ssize_t nrd;+	unsigned int ver;++	(void)best_bid; /* UNUSED */++	/* check first line of file, it should be a record already */+	if ((hdr = __archive_read_ahead(a, 12U, &nrd)) == NULL) {+		/* no idea what to do */+		return -1;+	} else if (nrd < 12) {+		/* nah, not for us, our magic cookie is at least 12 bytes */+		return -1;+	}++	/* otherwise snarf the record's version number */+	ver = _warc_rdver(hdr, nrd);+	if (ver < 1200U || ver > 10000U) {+		/* we only support WARC 0.12 to 1.0 */+		return -1;+	}++	/* otherwise be confident */+	return (64);+}++static int+_warc_rdhdr(struct archive_read *a, struct archive_entry *entry)+{+#define HDR_PROBE_LEN		(12U)+	struct warc_s *w = a->format->data;+	unsigned int ver;+	const char *buf;+	ssize_t nrd;+	const char *eoh;+	/* for the file name, saves some strndup()'ing */+	warc_string_t fnam;+	/* warc record type, not that we really use it a lot */+	warc_type_t ftyp;+	/* content-length+error monad */+	ssize_t cntlen;+	/* record time is the WARC-Date time we reinterpret it as ctime */+	time_t rtime;+	/* mtime is the Last-Modified time which will be the entry's mtime */+	time_t mtime;++start_over:+	/* just use read_ahead() they keep track of unconsumed+	 * bits and bobs for us; no need to put an extra shift in+	 * and reproduce that functionality here */+	buf = __archive_read_ahead(a, HDR_PROBE_LEN, &nrd);++	if (nrd < 0) {+		/* no good */+		archive_set_error(+			&a->archive, ARCHIVE_ERRNO_MISC,+			"Bad record header");+		return (ARCHIVE_FATAL);+	} else if (buf == NULL) {+		/* there should be room for at least WARC/bla\r\n+		 * must be EOF therefore */+		return (ARCHIVE_EOF);+	}+ 	/* looks good so far, try and find the end of the header now */+	eoh = _warc_find_eoh(buf, nrd);+	if (eoh == NULL) {+		/* still no good, the header end might be beyond the+		 * probe we've requested, but then again who'd cram+		 * so much stuff into the header *and* be 28500-compliant */+		archive_set_error(+			&a->archive, ARCHIVE_ERRNO_MISC,+			"Bad record header");+		return (ARCHIVE_FATAL);+	}+	ver = _warc_rdver(buf, eoh - buf);+	/* we currently support WARC 0.12 to 1.0 */+	if (ver == 0U) {+		archive_set_error(+			&a->archive, ARCHIVE_ERRNO_MISC,+			"Invalid record version");+		return (ARCHIVE_FATAL);+	} else if (ver < 1200U || ver > 10000U) {+		archive_set_error(+			&a->archive, ARCHIVE_ERRNO_MISC,+			"Unsupported record version: %u.%u",+			ver / 10000, (ver % 10000) / 100);+		return (ARCHIVE_FATAL);+	}+	cntlen = _warc_rdlen(buf, eoh - buf);+	if (cntlen < 0) {+		/* nightmare!  the specs say content-length is mandatory+		 * so I don't feel overly bad stopping the reader here */+		archive_set_error(+			&a->archive, EINVAL,+			"Bad content length");+		return (ARCHIVE_FATAL);+	}+	rtime = _warc_rdrtm(buf, eoh - buf);+	if (rtime == (time_t)-1) {+		/* record time is mandatory as per WARC/1.0,+		 * so just barf here, fast and loud */+		archive_set_error(+			&a->archive, EINVAL,+			"Bad record time");+		return (ARCHIVE_FATAL);+	}++	/* let the world know we're a WARC archive */+	a->archive.archive_format = ARCHIVE_FORMAT_WARC;+	if (ver != w->pver) {+		/* stringify this entry's version */+		archive_string_sprintf(&w->sver,+			"WARC/%u.%u", ver / 10000, (ver % 10000) / 100);+		/* remember the version */+		w->pver = ver;+	}+	/* start off with the type */+	ftyp = _warc_rdtyp(buf, eoh - buf);+	/* and let future calls know about the content */+	w->cntlen = cntlen;+	w->cntoff = 0U;+	mtime = 0;/* Avoid compiling error on some platform. */++	switch (ftyp) {+	case WT_RSRC:+	case WT_RSP:+		/* only try and read the filename in the cases that are+		 * guaranteed to have one */+		fnam = _warc_rduri(buf, eoh - buf);+		/* check the last character in the URI to avoid creating+		 * directory endpoints as files, see Todo above */+		if (fnam.len == 0 || fnam.str[fnam.len - 1] == '/') {+			/* break here for now */+			fnam.len = 0U;+			fnam.str = NULL;+			break;+		}+		/* bang to our string pool, so we save a+		 * malloc()+free() roundtrip */+		if (fnam.len + 1U > w->pool.len) {+			w->pool.len = ((fnam.len + 64U) / 64U) * 64U;+			w->pool.str = realloc(w->pool.str, w->pool.len);+		}+		memcpy(w->pool.str, fnam.str, fnam.len);+		w->pool.str[fnam.len] = '\0';+		/* let no one else know about the pool, it's a secret, shhh */+		fnam.str = w->pool.str;++		/* snarf mtime or deduce from rtime+		 * this is a custom header added by our writer, it's quite+		 * hard to believe anyone else would go through with it+		 * (apart from being part of some http responses of course) */+		if ((mtime = _warc_rdmtm(buf, eoh - buf)) == (time_t)-1) {+			mtime = rtime;+		}+		break;+	default:+		fnam.len = 0U;+		fnam.str = NULL;+		break;+	}++	/* now eat some of those delicious buffer bits */+	__archive_read_consume(a, eoh - buf);++	switch (ftyp) {+	case WT_RSRC:+	case WT_RSP:+		if (fnam.len > 0U) {+			/* populate entry object */+			archive_entry_set_filetype(entry, AE_IFREG);+			archive_entry_copy_pathname(entry, fnam.str);+			archive_entry_set_size(entry, cntlen);+			archive_entry_set_perm(entry, 0644);+			/* rtime is the new ctime, mtime stays mtime */+			archive_entry_set_ctime(entry, rtime, 0L);+			archive_entry_set_mtime(entry, mtime, 0L);+			break;+		}+		/* FALLTHROUGH */+	default:+		/* consume the content and start over */+		_warc_skip(a);+		goto start_over;+	}+	return (ARCHIVE_OK);+}++static int+_warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off)+{+	struct warc_s *w = a->format->data;+	const char *rab;+	ssize_t nrd;++	if (w->cntoff >= w->cntlen) {+	eof:+		/* it's our lucky day, no work, we can leave early */+		*buf = NULL;+		*bsz = 0U;+		*off = w->cntoff + 4U/*for \r\n\r\n separator*/;+		w->unconsumed = 0U;+		return (ARCHIVE_EOF);+	}++	if (w->unconsumed) {+		__archive_read_consume(a, w->unconsumed);+		w->unconsumed = 0U;+	}++	rab = __archive_read_ahead(a, 1U, &nrd);+	if (nrd < 0) {+		*bsz = 0U;+		/* big catastrophe */+		return (int)nrd;+	} else if (nrd == 0) {+		goto eof;+	} else if ((size_t)nrd > w->cntlen - w->cntoff) {+		/* clamp to content-length */+		nrd = w->cntlen - w->cntoff;+	}+	*off = w->cntoff;+	*bsz = nrd;+	*buf = rab;++	w->cntoff += nrd;+	w->unconsumed = (size_t)nrd;+	return (ARCHIVE_OK);+}++static int+_warc_skip(struct archive_read *a)+{+	struct warc_s *w = a->format->data;++	__archive_read_consume(a, w->cntlen + 4U/*\r\n\r\n separator*/);+	w->cntlen = 0U;+	w->cntoff = 0U;+	return (ARCHIVE_OK);+}+++/* private routines */+static void*+deconst(const void *c)+{+	return (char *)0x1 + (((const char *)c) - (const char *)0x1);+}++static char*+xmemmem(const char *hay, const size_t haysize,+	const char *needle, const size_t needlesize)+{+	const char *const eoh = hay + haysize;+	const char *const eon = needle + needlesize;+	const char *hp;+	const char *np;+	const char *cand;+	unsigned int hsum;+	unsigned int nsum;+	unsigned int eqp;++	/* trivial checks first+         * a 0-sized needle is defined to be found anywhere in haystack+         * then run strchr() to find a candidate in HAYSTACK (i.e. a portion+         * that happens to begin with *NEEDLE) */+	if (needlesize == 0UL) {+		return deconst(hay);+	} else if ((hay = memchr(hay, *needle, haysize)) == NULL) {+		/* trivial */+		return NULL;+	}++	/* First characters of haystack and needle are the same now. Both are+	 * guaranteed to be at least one character long.  Now computes the sum+	 * of characters values of needle together with the sum of the first+	 * needle_len characters of haystack. */+	for (hp = hay + 1U, np = needle + 1U, hsum = *hay, nsum = *hay, eqp = 1U;+	     hp < eoh && np < eon;+	     hsum ^= *hp, nsum ^= *np, eqp &= *hp == *np, hp++, np++);++	/* HP now references the (NEEDLESIZE + 1)-th character. */+	if (np < eon) {+		/* haystack is smaller than needle, :O */+		return NULL;+	} else if (eqp) {+		/* found a match */+		return deconst(hay);+	}++	/* now loop through the rest of haystack,+	 * updating the sum iteratively */+	for (cand = hay; hp < eoh; hp++) {+		hsum ^= *cand++;+		hsum ^= *hp;++		/* Since the sum of the characters is already known to be+		 * equal at that point, it is enough to check just NEEDLESIZE - 1+		 * characters for equality,+		 * also CAND is by design < HP, so no need for range checks */+		if (hsum == nsum && memcmp(cand, needle, needlesize - 1U) == 0) {+			return deconst(cand);+		}+	}+	return NULL;+}++static int+strtoi_lim(const char *str, const char **ep, int llim, int ulim)+{+	int res = 0;+	const char *sp;+	/* we keep track of the number of digits via rulim */+	int rulim;++	for (sp = str, rulim = ulim > 10 ? ulim : 10;+	     res * 10 <= ulim && rulim && *sp >= '0' && *sp <= '9';+	     sp++, rulim /= 10) {+		res *= 10;+		res += *sp - '0';+	}+	if (sp == str) {+		res = -1;+	} else if (res < llim || res > ulim) {+		res = -2;+	}+	*ep = (const char*)sp;+	return res;+}++static time_t+time_from_tm(struct tm *t)+{+#if HAVE_TIMEGM+        /* Use platform timegm() if available. */+        return (timegm(t));+#elif HAVE__MKGMTIME64+        return (_mkgmtime64(t));+#else+        /* Else use direct calculation using POSIX assumptions. */+        /* First, fix up tm_yday based on the year/month/day. */+        if (mktime(t) == (time_t)-1)+                return ((time_t)-1);+        /* Then we can compute timegm() from first principles. */+        return (t->tm_sec+            + t->tm_min * 60+            + t->tm_hour * 3600+            + t->tm_yday * 86400+            + (t->tm_year - 70) * 31536000+            + ((t->tm_year - 69) / 4) * 86400+            - ((t->tm_year - 1) / 100) * 86400+            + ((t->tm_year + 299) / 400) * 86400);+#endif+}++static time_t+xstrpisotime(const char *s, char **endptr)+{+/** like strptime() but strictly for ISO 8601 Zulu strings */+	struct tm tm;+	time_t res = (time_t)-1;++	/* make sure tm is clean */+	memset(&tm, 0, sizeof(tm));++	/* as a courtesy to our callers, and since this is a non-standard+	 * routine, we skip leading whitespace */+	while (*s == ' ' || *s == '\t')+		++s;++	/* read year */+	if ((tm.tm_year = strtoi_lim(s, &s, 1583, 4095)) < 0 || *s++ != '-') {+		goto out;+	}+	/* read month */+	if ((tm.tm_mon = strtoi_lim(s, &s, 1, 12)) < 0 || *s++ != '-') {+		goto out;+	}+	/* read day-of-month */+	if ((tm.tm_mday = strtoi_lim(s, &s, 1, 31)) < 0 || *s++ != 'T') {+		goto out;+	}+	/* read hour */+	if ((tm.tm_hour = strtoi_lim(s, &s, 0, 23)) < 0 || *s++ != ':') {+		goto out;+	}+	/* read minute */+	if ((tm.tm_min = strtoi_lim(s, &s, 0, 59)) < 0 || *s++ != ':') {+		goto out;+	}+	/* read second */+	if ((tm.tm_sec = strtoi_lim(s, &s, 0, 60)) < 0 || *s++ != 'Z') {+		goto out;+	}++	/* massage TM to fulfill some of POSIX' constraints */+	tm.tm_year -= 1900;+	tm.tm_mon--;++	/* now convert our custom tm struct to a unix stamp using UTC */+	res = time_from_tm(&tm);++out:+	if (endptr != NULL) {+		*endptr = deconst(s);+	}+	return res;+}++static unsigned int+_warc_rdver(const char *buf, size_t bsz)+{+	static const char magic[] = "WARC/";+	const char *c;+	unsigned int ver = 0U;+	unsigned int end = 0U;++	if (bsz < 12 || memcmp(buf, magic, sizeof(magic) - 1U) != 0) {+		/* buffer too small or invalid magic */+		return ver;+	}+	/* looks good so far, read the version number for a laugh */+	buf += sizeof(magic) - 1U;++	if (isdigit((unsigned char)buf[0U]) && (buf[1U] == '.') &&+	    isdigit((unsigned char)buf[2U])) {+		/* we support a maximum of 2 digits in the minor version */+		if (isdigit((unsigned char)buf[3U]))+			end = 1U;+		/* set up major version */+		ver = (buf[0U] - '0') * 10000U;+		/* set up minor version */+		if (end == 1U) {+			ver += (buf[2U] - '0') * 1000U;+			ver += (buf[3U] - '0') * 100U;+		} else+			ver += (buf[2U] - '0') * 100U;+		/*+		 * WARC below version 0.12 has a space-separated header+		 * WARC 0.12 and above terminates the version with a CRLF+		 */+		c = buf + 3U + end;+		if (ver >= 1200U) {+			if (memcmp(c, "\r\n", 2U) != 0)+				ver = 0U;+		} else {+			/* ver < 1200U */+			if (*c != ' ' && *c != '\t')+				ver = 0U;+		}+	}+	return ver;+}++static unsigned int+_warc_rdtyp(const char *buf, size_t bsz)+{+	static const char _key[] = "\r\nWARC-Type:";+	const char *val, *eol;++	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {+		/* no bother */+		return WT_NONE;+	}+	val += sizeof(_key) - 1U;+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {+		/* no end of line */+		return WT_NONE;+	}++	/* overread whitespace */+	while (val < eol && (*val == ' ' || *val == '\t'))+		++val;++	if (val + 8U == eol) {+		if (memcmp(val, "resource", 8U) == 0)+			return WT_RSRC;+		else if (memcmp(val, "response", 8U) == 0)+			return WT_RSP;+	}+	return WT_NONE;+}++static warc_string_t+_warc_rduri(const char *buf, size_t bsz)+{+	static const char _key[] = "\r\nWARC-Target-URI:";+	const char *val, *uri, *eol, *p;+	warc_string_t res = {0U, NULL};++	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {+		/* no bother */+		return res;+	}+	/* overread whitespace */+	val += sizeof(_key) - 1U;+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {+		/* no end of line */+		return res;+	}++	while (val < eol && (*val == ' ' || *val == '\t'))+		++val;++	/* overread URL designators */+	if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) {+		/* not touching that! */+		return res;+	}++	/* spaces inside uri are not allowed, CRLF should follow */+	for (p = val; p < eol; p++) {+		if (isspace((unsigned char)*p))+			return res;+	}++	/* there must be at least space for ftp */+	if (uri < (val + 3U))+		return res;++	/* move uri to point to after :// */+	uri += 3U;++	/* now then, inspect the URI */+	if (memcmp(val, "file", 4U) == 0) {+		/* perfect, nothing left to do here */++	} else if (memcmp(val, "http", 4U) == 0 ||+		   memcmp(val, "ftp", 3U) == 0) {+		/* overread domain, and the first / */+		while (uri < eol && *uri++ != '/');+	} else {+		/* not sure what to do? best to bugger off */+		return res;+	}+	res.str = uri;+	res.len = eol - uri;+	return res;+}++static ssize_t+_warc_rdlen(const char *buf, size_t bsz)+{+	static const char _key[] = "\r\nContent-Length:";+	const char *val, *eol;+	char *on = NULL;+	long int len;++	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {+		/* no bother */+		return -1;+	}+	val += sizeof(_key) - 1U;+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {+		/* no end of line */+		return -1;+	}++	/* skip leading whitespace */+	while (val < eol && (*val == ' ' || *val == '\t'))+		val++;+	/* there must be at least one digit */+	if (!isdigit((unsigned char)*val))+		return -1;+	errno = 0;+	len = strtol(val, &on, 10);+	if (errno != 0 || on != eol) {+		/* line must end here */+		return -1;+	}++	return (size_t)len;+}++static time_t+_warc_rdrtm(const char *buf, size_t bsz)+{+	static const char _key[] = "\r\nWARC-Date:";+	const char *val, *eol;+	char *on = NULL;+	time_t res;++	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {+		/* no bother */+		return (time_t)-1;+	}+	val += sizeof(_key) - 1U;+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {+		/* no end of line */+		return -1;+	}++	/* xstrpisotime() kindly overreads whitespace for us, so use that */+	res = xstrpisotime(val, &on);+	if (on != eol) {+		/* line must end here */+		return -1;+	}+	return res;+}++static time_t+_warc_rdmtm(const char *buf, size_t bsz)+{+	static const char _key[] = "\r\nLast-Modified:";+	const char *val, *eol;+	char *on = NULL;+	time_t res;++	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {+		/* no bother */+		return (time_t)-1;+	}+	val += sizeof(_key) - 1U;+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {+		/* no end of line */+		return -1;+	}++	/* xstrpisotime() kindly overreads whitespace for us, so use that */+	res = xstrpisotime(val, &on);+	if (on != eol) {+		/* line must end here */+		return -1;+	}+	return res;+}++static const char*+_warc_find_eoh(const char *buf, size_t bsz)+{+	static const char _marker[] = "\r\n\r\n";+	const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);++	if (hit != NULL) {+		hit += sizeof(_marker) - 1U;+	}+	return hit;+}++static const char*+_warc_find_eol(const char *buf, size_t bsz)+{+	static const char _marker[] = "\r\n";+	const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);++	return hit;+}+/* archive_read_support_format_warc.c ends here */
+ c/archive_read_support_format_xar.c view
@@ -0,0 +1,3328 @@+/*-+ * Copyright (c) 2009 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */+#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#if HAVE_LIBXML_XMLREADER_H+#include <libxml/xmlreader.h>+#elif HAVE_BSDXML_H+#include <bsdxml.h>+#elif HAVE_EXPAT_H+#include <expat.h>+#endif+#ifdef HAVE_BZLIB_H+#include <bzlib.h>+#endif+#if HAVE_LZMA_H+#include <lzma.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif++#include "archive.h"+#include "archive_digest_private.h"+#include "archive_endian.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_read_private.h"++#if (!defined(HAVE_LIBXML_XMLREADER_H) && \+     !defined(HAVE_BSDXML_H) && !defined(HAVE_EXPAT_H)) ||\+	!defined(HAVE_ZLIB_H) || \+	!defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)+/*+ * xar needs several external libraries.+ *   o libxml2 or expat --- XML parser+ *   o openssl or MD5/SHA1 hash function+ *   o zlib+ *   o bzlib2 (option)+ *   o liblzma (option)+ */+int+archive_read_support_format_xar(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_xar");++	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+	    "Xar not supported on this platform");+	return (ARCHIVE_WARN);+}++#else	/* Support xar format */++/* #define DEBUG 1 */+/* #define DEBUG_PRINT_TOC 1 */+#if DEBUG_PRINT_TOC+#define PRINT_TOC(d, outbytes)	do {				\+	unsigned char *x = (unsigned char *)(uintptr_t)d;	\+	unsigned char c = x[outbytes-1];			\+	x[outbytes - 1] = 0;					\+	fprintf(stderr, "%s", x);				\+	fprintf(stderr, "%c", c);				\+	x[outbytes - 1] = c;					\+} while (0)+#else+#define PRINT_TOC(d, outbytes)+#endif++#define HEADER_MAGIC	0x78617221+#define HEADER_SIZE	28+#define HEADER_VERSION	1+#define CKSUM_NONE	0+#define CKSUM_SHA1	1+#define CKSUM_MD5	2++#define MD5_SIZE	16+#define SHA1_SIZE	20+#define MAX_SUM_SIZE	20++enum enctype {+	NONE,+	GZIP,+	BZIP2,+	LZMA,+	XZ,+};++struct chksumval {+	int			 alg;+	size_t			 len;+	unsigned char		 val[MAX_SUM_SIZE];+};++struct chksumwork {+	int			 alg;+#ifdef ARCHIVE_HAS_MD5+	archive_md5_ctx		 md5ctx;+#endif+#ifdef ARCHIVE_HAS_SHA1+	archive_sha1_ctx	 sha1ctx;+#endif+};++struct xattr {+	struct xattr		*next;+	struct archive_string	 name;+	uint64_t		 id;+	uint64_t		 length;+	uint64_t		 offset;+	uint64_t		 size;+	enum enctype		 encoding;+	struct chksumval	 a_sum;+	struct chksumval	 e_sum;+	struct archive_string	 fstype;+};++struct xar_file {+	struct xar_file		*next;+	struct xar_file		*hdnext;+	struct xar_file		*parent;+	int			 subdirs;++	unsigned int		 has;+#define HAS_DATA		0x00001+#define HAS_PATHNAME		0x00002+#define HAS_SYMLINK		0x00004+#define HAS_TIME		0x00008+#define HAS_UID			0x00010+#define HAS_GID			0x00020+#define HAS_MODE		0x00040+#define HAS_TYPE		0x00080+#define HAS_DEV			0x00100+#define HAS_DEVMAJOR		0x00200+#define HAS_DEVMINOR		0x00400+#define HAS_INO			0x00800+#define HAS_FFLAGS		0x01000+#define HAS_XATTR		0x02000+#define HAS_ACL			0x04000+#define HAS_CTIME		0x08000+#define HAS_MTIME		0x10000+#define HAS_ATIME		0x20000++	uint64_t		 id;+	uint64_t		 length;+	uint64_t		 offset;+	uint64_t		 size;+	enum enctype		 encoding;+	struct chksumval	 a_sum;+	struct chksumval	 e_sum;+	struct archive_string	 pathname;+	struct archive_string	 symlink;+	time_t			 ctime;+	time_t			 mtime;+	time_t			 atime;+	struct archive_string	 uname;+	int64_t			 uid;+	struct archive_string	 gname;+	int64_t			 gid;+	mode_t			 mode;+	dev_t			 dev;+	dev_t			 devmajor;+	dev_t			 devminor;+	int64_t			 ino64;+	struct archive_string	 fflags_text;+	unsigned int		 link;+	unsigned int		 nlink;+	struct archive_string	 hardlink;+	struct xattr		*xattr_list;+};++struct hdlink {+	struct hdlink		 *next;++	unsigned int		 id;+	int			 cnt;+	struct xar_file		 *files;+};++struct heap_queue {+	struct xar_file		**files;+	int			 allocated;+	int			 used;+};++enum xmlstatus {+	INIT,+	XAR,+	TOC,+	TOC_CREATION_TIME,+	TOC_CHECKSUM,+	TOC_CHECKSUM_OFFSET,+	TOC_CHECKSUM_SIZE,+	TOC_FILE,+	FILE_DATA,+	FILE_DATA_LENGTH,+	FILE_DATA_OFFSET,+	FILE_DATA_SIZE,+	FILE_DATA_ENCODING,+	FILE_DATA_A_CHECKSUM,+	FILE_DATA_E_CHECKSUM,+	FILE_DATA_CONTENT,+	FILE_EA,+	FILE_EA_LENGTH,+	FILE_EA_OFFSET,+	FILE_EA_SIZE,+	FILE_EA_ENCODING,+	FILE_EA_A_CHECKSUM,+	FILE_EA_E_CHECKSUM,+	FILE_EA_NAME,+	FILE_EA_FSTYPE,+	FILE_CTIME,+	FILE_MTIME,+	FILE_ATIME,+	FILE_GROUP,+	FILE_GID,+	FILE_USER,+	FILE_UID,+	FILE_MODE,+	FILE_DEVICE,+	FILE_DEVICE_MAJOR,+	FILE_DEVICE_MINOR,+	FILE_DEVICENO,+	FILE_INODE,+	FILE_LINK,+	FILE_TYPE,+	FILE_NAME,+	FILE_ACL,+	FILE_ACL_DEFAULT,+	FILE_ACL_ACCESS,+	FILE_ACL_APPLEEXTENDED,+	/* BSD file flags. */+	FILE_FLAGS,+	FILE_FLAGS_USER_NODUMP,+	FILE_FLAGS_USER_IMMUTABLE,+	FILE_FLAGS_USER_APPEND,+	FILE_FLAGS_USER_OPAQUE,+	FILE_FLAGS_USER_NOUNLINK,+	FILE_FLAGS_SYS_ARCHIVED,+	FILE_FLAGS_SYS_IMMUTABLE,+	FILE_FLAGS_SYS_APPEND,+	FILE_FLAGS_SYS_NOUNLINK,+	FILE_FLAGS_SYS_SNAPSHOT,+	/* Linux file flags. */+	FILE_EXT2,+	FILE_EXT2_SecureDeletion,+	FILE_EXT2_Undelete,+	FILE_EXT2_Compress,+	FILE_EXT2_Synchronous,+	FILE_EXT2_Immutable,+	FILE_EXT2_AppendOnly,+	FILE_EXT2_NoDump,+	FILE_EXT2_NoAtime,+	FILE_EXT2_CompDirty,+	FILE_EXT2_CompBlock,+	FILE_EXT2_NoCompBlock,+	FILE_EXT2_CompError,+	FILE_EXT2_BTree,+	FILE_EXT2_HashIndexed,+	FILE_EXT2_iMagic,+	FILE_EXT2_Journaled,+	FILE_EXT2_NoTail,+	FILE_EXT2_DirSync,+	FILE_EXT2_TopDir,+	FILE_EXT2_Reserved,+	UNKNOWN,+};++struct unknown_tag {+	struct unknown_tag	*next;+	struct archive_string	 name;+};++struct xar {+	uint64_t		 offset; /* Current position in the file. */+	int64_t			 total;+	uint64_t		 h_base;+	int			 end_of_file;+#define OUTBUFF_SIZE	(1024 * 64)+	unsigned char		*outbuff;++	enum xmlstatus		 xmlsts;+	enum xmlstatus		 xmlsts_unknown;+	struct unknown_tag	*unknowntags;+	int			 base64text;++	/*+	 * TOC+	 */+	uint64_t		 toc_remaining;+	uint64_t		 toc_total;+	uint64_t		 toc_chksum_offset;+	uint64_t		 toc_chksum_size;++	/*+	 * For Decoding data.+	 */+	enum enctype 		 rd_encoding;+	z_stream		 stream;+	int			 stream_valid;+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+	bz_stream		 bzstream;+	int			 bzstream_valid;+#endif+#if HAVE_LZMA_H && HAVE_LIBLZMA+	lzma_stream		 lzstream;+	int			 lzstream_valid;+#endif+	/*+	 * For Checksum data.+	 */+	struct chksumwork	 a_sumwrk;+	struct chksumwork	 e_sumwrk;++	struct xar_file		*file;	/* current reading file. */+	struct xattr		*xattr; /* current reading extended attribute. */+	struct heap_queue	 file_queue;+	struct xar_file		*hdlink_orgs;+	struct hdlink		*hdlink_list;++	int	 		 entry_init;+	uint64_t		 entry_total;+	uint64_t		 entry_remaining;+	size_t			 entry_unconsumed;+	uint64_t		 entry_size;+	enum enctype 		 entry_encoding;+	struct chksumval	 entry_a_sum;+	struct chksumval	 entry_e_sum;++	struct archive_string_conv *sconv;+};++struct xmlattr {+	struct xmlattr	*next;+	char		*name;+	char		*value;+};++struct xmlattr_list {+	struct xmlattr	*first;+	struct xmlattr	**last;+};++static int	xar_bid(struct archive_read *, int);+static int	xar_read_header(struct archive_read *,+		    struct archive_entry *);+static int	xar_read_data(struct archive_read *,+		    const void **, size_t *, int64_t *);+static int	xar_read_data_skip(struct archive_read *);+static int	xar_cleanup(struct archive_read *);+static int	move_reading_point(struct archive_read *, uint64_t);+static int	rd_contents_init(struct archive_read *,+		    enum enctype, int, int);+static int	rd_contents(struct archive_read *, const void **,+		    size_t *, size_t *, uint64_t);+static uint64_t	atol10(const char *, size_t);+static int64_t	atol8(const char *, size_t);+static size_t	atohex(unsigned char *, size_t, const char *, size_t);+static time_t	parse_time(const char *p, size_t n);+static int	heap_add_entry(struct archive_read *a,+    struct heap_queue *, struct xar_file *);+static struct xar_file *heap_get_entry(struct heap_queue *);+static int	add_link(struct archive_read *,+    struct xar *, struct xar_file *);+static void	checksum_init(struct archive_read *, int, int);+static void	checksum_update(struct archive_read *, const void *,+		    size_t, const void *, size_t);+static int	checksum_final(struct archive_read *, const void *,+		    size_t, const void *, size_t);+static void	checksum_cleanup(struct archive_read *);+static int	decompression_init(struct archive_read *, enum enctype);+static int	decompress(struct archive_read *, const void **,+		    size_t *, const void *, size_t *);+static int	decompression_cleanup(struct archive_read *);+static void	xmlattr_cleanup(struct xmlattr_list *);+static int	file_new(struct archive_read *,+    struct xar *, struct xmlattr_list *);+static void	file_free(struct xar_file *);+static int	xattr_new(struct archive_read *,+    struct xar *, struct xmlattr_list *);+static void	xattr_free(struct xattr *);+static int	getencoding(struct xmlattr_list *);+static int	getsumalgorithm(struct xmlattr_list *);+static int	unknowntag_start(struct archive_read *,+    struct xar *, const char *);+static void	unknowntag_end(struct xar *, const char *);+static int	xml_start(struct archive_read *,+    const char *, struct xmlattr_list *);+static void	xml_end(void *, const char *);+static void	xml_data(void *, const char *, int);+static int	xml_parse_file_flags(struct xar *, const char *);+static int	xml_parse_file_ext2(struct xar *, const char *);+#if defined(HAVE_LIBXML_XMLREADER_H)+static int	xml2_xmlattr_setup(struct archive_read *,+    struct xmlattr_list *, xmlTextReaderPtr);+static int	xml2_read_cb(void *, char *, int);+static int	xml2_close_cb(void *);+static void	xml2_error_hdr(void *, const char *, xmlParserSeverities,+		    xmlTextReaderLocatorPtr);+static int	xml2_read_toc(struct archive_read *);+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)+struct expat_userData {+	int state;+	struct archive_read *archive;+};+static int	expat_xmlattr_setup(struct archive_read *,+    struct xmlattr_list *, const XML_Char **);+static void	expat_start_cb(void *, const XML_Char *, const XML_Char **);+static void	expat_end_cb(void *, const XML_Char *);+static void	expat_data_cb(void *, const XML_Char *, int);+static int	expat_read_toc(struct archive_read *);+#endif++int+archive_read_support_format_xar(struct archive *_a)+{+	struct xar *xar;+	struct archive_read *a = (struct archive_read *)_a;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_xar");++	xar = (struct xar *)calloc(1, sizeof(*xar));+	if (xar == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate xar data");+		return (ARCHIVE_FATAL);+	}++	/* initialize xar->file_queue */+	xar->file_queue.allocated = 0;+	xar->file_queue.used = 0;+	xar->file_queue.files = NULL;++	r = __archive_read_register_format(a,+	    xar,+	    "xar",+	    xar_bid,+	    NULL,+	    xar_read_header,+	    xar_read_data,+	    xar_read_data_skip,+	    NULL,+	    xar_cleanup,+	    NULL,+	    NULL);+	if (r != ARCHIVE_OK)+		free(xar);+	return (r);+}++static int+xar_bid(struct archive_read *a, int best_bid)+{+	const unsigned char *b;+	int bid;++	(void)best_bid; /* UNUSED */++	b = __archive_read_ahead(a, HEADER_SIZE, NULL);+	if (b == NULL)+		return (-1);++	bid = 0;+	/*+	 * Verify magic code+	 */+	if (archive_be32dec(b) != HEADER_MAGIC)+		return (0);+	bid += 32;+	/*+	 * Verify header size+	 */+	if (archive_be16dec(b+4) != HEADER_SIZE)+		return (0);+	bid += 16;+	/*+	 * Verify header version+	 */+	if (archive_be16dec(b+6) != HEADER_VERSION)+		return (0);+	bid += 16;+	/*+	 * Verify type of checksum+	 */+	switch (archive_be32dec(b+24)) {+	case CKSUM_NONE:+	case CKSUM_SHA1:+	case CKSUM_MD5:+		bid += 32;+		break;+	default:+		return (0);+	}++	return (bid);+}++static int+read_toc(struct archive_read *a)+{+	struct xar *xar;+	struct xar_file *file;+	const unsigned char *b;+	uint64_t toc_compressed_size;+	uint64_t toc_uncompressed_size;+	uint32_t toc_chksum_alg;+	ssize_t bytes;+	int r;++	xar = (struct xar *)(a->format->data);++	/*+	 * Read xar header.+	 */+	b = __archive_read_ahead(a, HEADER_SIZE, &bytes);+	if (bytes < 0)+		return ((int)bytes);+	if (bytes < HEADER_SIZE) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated archive header");+		return (ARCHIVE_FATAL);+	}++	if (archive_be32dec(b) != HEADER_MAGIC) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Invalid header magic");+		return (ARCHIVE_FATAL);+	}+	if (archive_be16dec(b+6) != HEADER_VERSION) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unsupported header version(%d)",+		    archive_be16dec(b+6));+		return (ARCHIVE_FATAL);+	}+	toc_compressed_size = archive_be64dec(b+8);+	xar->toc_remaining = toc_compressed_size;+	toc_uncompressed_size = archive_be64dec(b+16);+	toc_chksum_alg = archive_be32dec(b+24);+	__archive_read_consume(a, HEADER_SIZE);+	xar->offset += HEADER_SIZE;+	xar->toc_total = 0;++	/*+	 * Read TOC(Table of Contents).+	 */+	/* Initialize reading contents. */+	r = move_reading_point(a, HEADER_SIZE);+	if (r != ARCHIVE_OK)+		return (r);+	r = rd_contents_init(a, GZIP, toc_chksum_alg, CKSUM_NONE);+	if (r != ARCHIVE_OK)+		return (r);++#ifdef HAVE_LIBXML_XMLREADER_H+	r = xml2_read_toc(a);+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)+	r = expat_read_toc(a);+#endif+	if (r != ARCHIVE_OK)+		return (r);++	/* Set 'The HEAP' base. */+	xar->h_base = xar->offset;+	if (xar->toc_total != toc_uncompressed_size) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "TOC uncompressed size error");+		return (ARCHIVE_FATAL);+	}++	/*+	 * Checksum TOC+	 */+	if (toc_chksum_alg != CKSUM_NONE) {+		r = move_reading_point(a, xar->toc_chksum_offset);+		if (r != ARCHIVE_OK)+			return (r);+		b = __archive_read_ahead(a,+			(size_t)xar->toc_chksum_size, &bytes);+		if (bytes < 0)+			return ((int)bytes);+		if ((uint64_t)bytes < xar->toc_chksum_size) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated archive file");+			return (ARCHIVE_FATAL);+		}+		r = checksum_final(a, b,+			(size_t)xar->toc_chksum_size, NULL, 0);+		__archive_read_consume(a, xar->toc_chksum_size);+		xar->offset += xar->toc_chksum_size;+		if (r != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	/*+	 * Connect hardlinked files.+	 */+	for (file = xar->hdlink_orgs; file != NULL; file = file->hdnext) {+		struct hdlink **hdlink;++		for (hdlink = &(xar->hdlink_list); *hdlink != NULL;+		    hdlink = &((*hdlink)->next)) {+			if ((*hdlink)->id == file->id) {+				struct hdlink *hltmp;+				struct xar_file *f2;+				int nlink = (*hdlink)->cnt + 1;++				file->nlink = nlink;+				for (f2 = (*hdlink)->files; f2 != NULL;+				    f2 = f2->hdnext) {+					f2->nlink = nlink;+					archive_string_copy(+					    &(f2->hardlink), &(file->pathname));+				}+				/* Remove resolved files from hdlist_list. */+				hltmp = *hdlink;+				*hdlink = hltmp->next;+				free(hltmp);+				break;+			}+		}+	}+	a->archive.archive_format = ARCHIVE_FORMAT_XAR;+	a->archive.archive_format_name = "xar";++	return (ARCHIVE_OK);+}++static int+xar_read_header(struct archive_read *a, struct archive_entry *entry)+{+	struct xar *xar;+	struct xar_file *file;+	struct xattr *xattr;+	int r;++	xar = (struct xar *)(a->format->data);+	r = ARCHIVE_OK;++	if (xar->offset == 0) {+		/* Create a character conversion object. */+		if (xar->sconv == NULL) {+			xar->sconv = archive_string_conversion_from_charset(+			    &(a->archive), "UTF-8", 1);+			if (xar->sconv == NULL)+				return (ARCHIVE_FATAL);+		}++		/* Read TOC. */+		r = read_toc(a);+		if (r != ARCHIVE_OK)+			return (r);+	}++	for (;;) {+		file = xar->file = heap_get_entry(&(xar->file_queue));+		if (file == NULL) {+			xar->end_of_file = 1;+			return (ARCHIVE_EOF);+		}+		if ((file->mode & AE_IFMT) != AE_IFDIR)+			break;+		if (file->has != (HAS_PATHNAME | HAS_TYPE))+			break;+		/*+		 * If a file type is a directory and it does not have+		 * any metadata, do not export.+		 */+		file_free(file);+	}+        if (file->has & HAS_ATIME) {+          archive_entry_set_atime(entry, file->atime, 0);+        }+        if (file->has & HAS_CTIME) {+          archive_entry_set_ctime(entry, file->ctime, 0);+        }+        if (file->has & HAS_MTIME) {+          archive_entry_set_mtime(entry, file->mtime, 0);+        }+	archive_entry_set_gid(entry, file->gid);+	if (file->gname.length > 0 &&+	    archive_entry_copy_gname_l(entry, file->gname.s,+		archive_strlen(&(file->gname)), xar->sconv) != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Gname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Gname cannot be converted from %s to current locale.",+		    archive_string_conversion_charset_name(xar->sconv));+		r = ARCHIVE_WARN;+	}+	archive_entry_set_uid(entry, file->uid);+	if (file->uname.length > 0 &&+	    archive_entry_copy_uname_l(entry, file->uname.s,+		archive_strlen(&(file->uname)), xar->sconv) != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Uname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Uname cannot be converted from %s to current locale.",+		    archive_string_conversion_charset_name(xar->sconv));+		r = ARCHIVE_WARN;+	}+	archive_entry_set_mode(entry, file->mode);+	if (archive_entry_copy_pathname_l(entry, file->pathname.s,+	    archive_strlen(&(file->pathname)), xar->sconv) != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Pathname cannot be converted from %s to current locale.",+		    archive_string_conversion_charset_name(xar->sconv));+		r = ARCHIVE_WARN;+	}+++	if (file->symlink.length > 0 &&+	    archive_entry_copy_symlink_l(entry, file->symlink.s,+		archive_strlen(&(file->symlink)), xar->sconv) != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Linkname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Linkname cannot be converted from %s to current locale.",+		    archive_string_conversion_charset_name(xar->sconv));+		r = ARCHIVE_WARN;+	}+	/* Set proper nlink. */+	if ((file->mode & AE_IFMT) == AE_IFDIR)+		archive_entry_set_nlink(entry, file->subdirs + 2);+	else+		archive_entry_set_nlink(entry, file->nlink);+	archive_entry_set_size(entry, file->size);+	if (archive_strlen(&(file->hardlink)) > 0)+		archive_entry_set_hardlink(entry, file->hardlink.s);+	archive_entry_set_ino64(entry, file->ino64);+	if (file->has & HAS_DEV)+		archive_entry_set_dev(entry, file->dev);+	if (file->has & HAS_DEVMAJOR)+		archive_entry_set_devmajor(entry, file->devmajor);+	if (file->has & HAS_DEVMINOR)+		archive_entry_set_devminor(entry, file->devminor);+	if (archive_strlen(&(file->fflags_text)) > 0)+		archive_entry_copy_fflags_text(entry, file->fflags_text.s);++	xar->entry_init = 1;+	xar->entry_total = 0;+	xar->entry_remaining = file->length;+	xar->entry_size = file->size;+	xar->entry_encoding = file->encoding;+	xar->entry_a_sum = file->a_sum;+	xar->entry_e_sum = file->e_sum;+	/*+	 * Read extended attributes.+	 */+	xattr = file->xattr_list;+	while (xattr != NULL) {+		const void *d;+		size_t outbytes = 0;+		size_t used = 0;++		r = move_reading_point(a, xattr->offset);+		if (r != ARCHIVE_OK)+			break;+		r = rd_contents_init(a, xattr->encoding,+		    xattr->a_sum.alg, xattr->e_sum.alg);+		if (r != ARCHIVE_OK)+			break;+		d = NULL;+		r = rd_contents(a, &d, &outbytes, &used, xattr->length);+		if (r != ARCHIVE_OK)+			break;+		if (outbytes != xattr->size) {+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,+			    "Decompressed size error");+			r = ARCHIVE_FATAL;+			break;+		}+		r = checksum_final(a,+		    xattr->a_sum.val, xattr->a_sum.len,+		    xattr->e_sum.val, xattr->e_sum.len);+		if (r != ARCHIVE_OK) {+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,+			    "Xattr checksum error");+			r = ARCHIVE_WARN;+			break;+		}+		if (xattr->name.s == NULL) {+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,+			    "Xattr name error");+			r = ARCHIVE_WARN;+			break;+		}+		archive_entry_xattr_add_entry(entry,+		    xattr->name.s, d, outbytes);+		xattr = xattr->next;+	}+	if (r != ARCHIVE_OK) {+		file_free(file);+		return (r);+	}++	if (xar->entry_remaining > 0)+		/* Move reading point to the beginning of current+		 * file contents. */+		r = move_reading_point(a, file->offset);+	else+		r = ARCHIVE_OK;++	file_free(file);+	return (r);+}++static int+xar_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	struct xar *xar;+	size_t used = 0;+	int r;++	xar = (struct xar *)(a->format->data);++	if (xar->entry_unconsumed) {+		__archive_read_consume(a, xar->entry_unconsumed);+		xar->entry_unconsumed = 0;+	}++	if (xar->end_of_file || xar->entry_remaining <= 0) {+		r = ARCHIVE_EOF;+		goto abort_read_data;+	}++	if (xar->entry_init) {+		r = rd_contents_init(a, xar->entry_encoding,+		    xar->entry_a_sum.alg, xar->entry_e_sum.alg);+		if (r != ARCHIVE_OK) {+			xar->entry_remaining = 0;+			return (r);+		}+		xar->entry_init = 0;+	}++	*buff = NULL;+	r = rd_contents(a, buff, size, &used, xar->entry_remaining);+	if (r != ARCHIVE_OK)+		goto abort_read_data;++	*offset = xar->entry_total;+	xar->entry_total += *size;+	xar->total += *size;+	xar->offset += used;+	xar->entry_remaining -= used;+	xar->entry_unconsumed = used;++	if (xar->entry_remaining == 0) {+		if (xar->entry_total != xar->entry_size) {+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,+			    "Decompressed size error");+			r = ARCHIVE_FATAL;+			goto abort_read_data;+		}+		r = checksum_final(a,+		    xar->entry_a_sum.val, xar->entry_a_sum.len,+		    xar->entry_e_sum.val, xar->entry_e_sum.len);+		if (r != ARCHIVE_OK)+			goto abort_read_data;+	}++	return (ARCHIVE_OK);+abort_read_data:+	*buff = NULL;+	*size = 0;+	*offset = xar->total;+	return (r);+}++static int+xar_read_data_skip(struct archive_read *a)+{+	struct xar *xar;+	int64_t bytes_skipped;++	xar = (struct xar *)(a->format->data);+	if (xar->end_of_file)+		return (ARCHIVE_EOF);+	bytes_skipped = __archive_read_consume(a, xar->entry_remaining ++		xar->entry_unconsumed);+	if (bytes_skipped < 0)+		return (ARCHIVE_FATAL);+	xar->offset += bytes_skipped;+	xar->entry_unconsumed = 0;+	return (ARCHIVE_OK);+}++static int+xar_cleanup(struct archive_read *a)+{+	struct xar *xar;+	struct hdlink *hdlink;+	int i;+	int r;++	xar = (struct xar *)(a->format->data);+	checksum_cleanup(a);+	r = decompression_cleanup(a);+	hdlink = xar->hdlink_list;+	while (hdlink != NULL) {+		struct hdlink *next = hdlink->next;++		free(hdlink);+		hdlink = next;+	}+	for (i = 0; i < xar->file_queue.used; i++)+		file_free(xar->file_queue.files[i]);+	free(xar->file_queue.files);+	while (xar->unknowntags != NULL) {+		struct unknown_tag *tag;++		tag = xar->unknowntags;+		xar->unknowntags = tag->next;+		archive_string_free(&(tag->name));+		free(tag);+	}+	free(xar->outbuff);+	free(xar);+	a->format->data = NULL;+	return (r);+}++static int+move_reading_point(struct archive_read *a, uint64_t offset)+{+	struct xar *xar;++	xar = (struct xar *)(a->format->data);+	if (xar->offset - xar->h_base != offset) {+		/* Seek forward to the start of file contents. */+		int64_t step;++		step = offset - (xar->offset - xar->h_base);+		if (step > 0) {+			step = __archive_read_consume(a, step);+			if (step < 0)+				return ((int)step);+			xar->offset += step;+		} else {+			int64_t pos = __archive_read_seek(a, xar->h_base + offset, SEEK_SET);+			if (pos == ARCHIVE_FAILED) {+				archive_set_error(&(a->archive),+				    ARCHIVE_ERRNO_MISC,+				    "Cannot seek.");+				return (ARCHIVE_FAILED);+			}+			xar->offset = pos;+		}+	}+	return (ARCHIVE_OK);+}++static int+rd_contents_init(struct archive_read *a, enum enctype encoding,+    int a_sum_alg, int e_sum_alg)+{+	int r;++	/* Init decompress library. */+	if ((r = decompression_init(a, encoding)) != ARCHIVE_OK)+		return (r);+	/* Init checksum library. */+	checksum_init(a, a_sum_alg, e_sum_alg);+	return (ARCHIVE_OK);+}++static int+rd_contents(struct archive_read *a, const void **buff, size_t *size,+    size_t *used, uint64_t remaining)+{+	const unsigned char *b;+	ssize_t bytes;++	/* Get whatever bytes are immediately available. */+	b = __archive_read_ahead(a, 1, &bytes);+	if (bytes < 0)+		return ((int)bytes);+	if (bytes == 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Truncated archive file");+		return (ARCHIVE_FATAL);+	}+	if ((uint64_t)bytes > remaining)+		bytes = (ssize_t)remaining;++	/*+	 * Decompress contents of file.+	 */+	*used = bytes;+	if (decompress(a, buff, size, b, used) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/*+	 * Update checksum of a compressed data and a extracted data.+	 */+	checksum_update(a, b, *used, *buff, *size);++	return (ARCHIVE_OK);+}++/*+ * Note that this implementation does not (and should not!) obey+ * locale settings; you cannot simply substitute strtol here, since+ * it does obey locale.+ */++static uint64_t+atol10(const char *p, size_t char_cnt)+{+	uint64_t l;+	int digit;++	if (char_cnt == 0)+		return (0);++	l = 0;+	digit = *p - '0';+	while (digit >= 0 && digit < 10  && char_cnt-- > 0) {+		l = (l * 10) + digit;+		digit = *++p - '0';+	}+	return (l);+}++static int64_t+atol8(const char *p, size_t char_cnt)+{+	int64_t l;+	int digit;++	if (char_cnt == 0)+		return (0);++	l = 0;+	while (char_cnt-- > 0) {+		if (*p >= '0' && *p <= '7')+			digit = *p - '0';+		else+			break;+		p++;+		l <<= 3;+		l |= digit;+	}+	return (l);+}++static size_t+atohex(unsigned char *b, size_t bsize, const char *p, size_t psize)+{+	size_t fbsize = bsize;++	while (bsize && psize > 1) {+		unsigned char x;++		if (p[0] >= 'a' && p[0] <= 'z')+			x = (p[0] - 'a' + 0x0a) << 4;+		else if (p[0] >= 'A' && p[0] <= 'Z')+			x = (p[0] - 'A' + 0x0a) << 4;+		else if (p[0] >= '0' && p[0] <= '9')+			x = (p[0] - '0') << 4;+		else+			return (-1);+		if (p[1] >= 'a' && p[1] <= 'z')+			x |= p[1] - 'a' + 0x0a;+		else if (p[1] >= 'A' && p[1] <= 'Z')+			x |= p[1] - 'A' + 0x0a;+		else if (p[1] >= '0' && p[1] <= '9')+			x |= p[1] - '0';+		else+			return (-1);+		+		*b++ = x;+		bsize--;+		p += 2;+		psize -= 2;+	}+	return (fbsize - bsize);+}++static time_t+time_from_tm(struct tm *t)+{+#if HAVE_TIMEGM+        /* Use platform timegm() if available. */+        return (timegm(t));+#elif HAVE__MKGMTIME64+        return (_mkgmtime64(t));+#else+        /* Else use direct calculation using POSIX assumptions. */+        /* First, fix up tm_yday based on the year/month/day. */+        mktime(t);+        /* Then we can compute timegm() from first principles. */+        return (t->tm_sec+            + t->tm_min * 60+            + t->tm_hour * 3600+            + t->tm_yday * 86400+            + (t->tm_year - 70) * 31536000+            + ((t->tm_year - 69) / 4) * 86400+            - ((t->tm_year - 1) / 100) * 86400+            + ((t->tm_year + 299) / 400) * 86400);+#endif+}++static time_t+parse_time(const char *p, size_t n)+{+	struct tm tm;+	time_t t = 0;+	int64_t data;++	memset(&tm, 0, sizeof(tm));+	if (n != 20)+		return (t);+	data = atol10(p, 4);+	if (data < 1900)+		return (t);+	tm.tm_year = (int)data - 1900;+	p += 4;+	if (*p++ != '-')+		return (t);+	data = atol10(p, 2);+	if (data < 1 || data > 12)+		return (t);+	tm.tm_mon = (int)data -1;+	p += 2;+	if (*p++ != '-')+		return (t);+	data = atol10(p, 2);+	if (data < 1 || data > 31)+		return (t);+	tm.tm_mday = (int)data;+	p += 2;+	if (*p++ != 'T')+		return (t);+	data = atol10(p, 2);+	if (data < 0 || data > 23)+		return (t);+	tm.tm_hour = (int)data;+	p += 2;+	if (*p++ != ':')+		return (t);+	data = atol10(p, 2);+	if (data < 0 || data > 59)+		return (t);+	tm.tm_min = (int)data;+	p += 2;+	if (*p++ != ':')+		return (t);+	data = atol10(p, 2);+	if (data < 0 || data > 60)+		return (t);+	tm.tm_sec = (int)data;+#if 0+	p += 2;+	if (*p != 'Z')+		return (t);+#endif++	t = time_from_tm(&tm);++	return (t);+}++static int+heap_add_entry(struct archive_read *a,+    struct heap_queue *heap, struct xar_file *file)+{+	uint64_t file_id, parent_id;+	int hole, parent;++	/* Expand our pending files list as necessary. */+	if (heap->used >= heap->allocated) {+		struct xar_file **new_pending_files;+		int new_size;++		if (heap->allocated < 1024)+			new_size = 1024;+		else+			new_size = heap->allocated * 2;+		/* Overflow might keep us from growing the list. */+		if (new_size <= heap->allocated) {+			archive_set_error(&a->archive,+			    ENOMEM, "Out of memory");+			return (ARCHIVE_FATAL);+		}+		new_pending_files = (struct xar_file **)+		    malloc(new_size * sizeof(new_pending_files[0]));+		if (new_pending_files == NULL) {+			archive_set_error(&a->archive,+			    ENOMEM, "Out of memory");+			return (ARCHIVE_FATAL);+		}+		if (heap->allocated) {+			memcpy(new_pending_files, heap->files,+			    heap->allocated * sizeof(new_pending_files[0]));+			free(heap->files);+		}+		heap->files = new_pending_files;+		heap->allocated = new_size;+	}++	file_id = file->id;++	/*+	 * Start with hole at end, walk it up tree to find insertion point.+	 */+	hole = heap->used++;+	while (hole > 0) {+		parent = (hole - 1)/2;+		parent_id = heap->files[parent]->id;+		if (file_id >= parent_id) {+			heap->files[hole] = file;+			return (ARCHIVE_OK);+		}+		/* Move parent into hole <==> move hole up tree. */+		heap->files[hole] = heap->files[parent];+		hole = parent;+	}+	heap->files[0] = file;++	return (ARCHIVE_OK);+}++static struct xar_file *+heap_get_entry(struct heap_queue *heap)+{+	uint64_t a_id, b_id, c_id;+	int a, b, c;+	struct xar_file *r, *tmp;++	if (heap->used < 1)+		return (NULL);++	/*+	 * The first file in the list is the earliest; we'll return this.+	 */+	r = heap->files[0];++	/*+	 * Move the last item in the heap to the root of the tree+	 */+	heap->files[0] = heap->files[--(heap->used)];++	/*+	 * Rebalance the heap.+	 */+	a = 0; /* Starting element and its heap key */+	a_id = heap->files[a]->id;+	for (;;) {+		b = a + a + 1; /* First child */+		if (b >= heap->used)+			return (r);+		b_id = heap->files[b]->id;+		c = b + 1; /* Use second child if it is smaller. */+		if (c < heap->used) {+			c_id = heap->files[c]->id;+			if (c_id < b_id) {+				b = c;+				b_id = c_id;+			}+		}+		if (a_id <= b_id)+			return (r);+		tmp = heap->files[a];+		heap->files[a] = heap->files[b];+		heap->files[b] = tmp;+		a = b;+	}+}++static int+add_link(struct archive_read *a, struct xar *xar, struct xar_file *file)+{+	struct hdlink *hdlink;++	for (hdlink = xar->hdlink_list; hdlink != NULL; hdlink = hdlink->next) {+		if (hdlink->id == file->link) {+			file->hdnext = hdlink->files;+			hdlink->cnt++;+			hdlink->files = file;+			return (ARCHIVE_OK);+		}+	}+	hdlink = malloc(sizeof(*hdlink));+	if (hdlink == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	file->hdnext = NULL;+	hdlink->id = file->link;+	hdlink->cnt = 1;+	hdlink->files = file;+	hdlink->next = xar->hdlink_list;+	xar->hdlink_list = hdlink;+	return (ARCHIVE_OK);+}++static void+_checksum_init(struct chksumwork *sumwrk, int sum_alg)+{+	sumwrk->alg = sum_alg;+	switch (sum_alg) {+	case CKSUM_NONE:+		break;+	case CKSUM_SHA1:+		archive_sha1_init(&(sumwrk->sha1ctx));+		break;+	case CKSUM_MD5:+		archive_md5_init(&(sumwrk->md5ctx));+		break;+	}+}++static void+_checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)+{++	switch (sumwrk->alg) {+	case CKSUM_NONE:+		break;+	case CKSUM_SHA1:+		archive_sha1_update(&(sumwrk->sha1ctx), buff, size);+		break;+	case CKSUM_MD5:+		archive_md5_update(&(sumwrk->md5ctx), buff, size);+		break;+	}+}++static int+_checksum_final(struct chksumwork *sumwrk, const void *val, size_t len)+{+	unsigned char sum[MAX_SUM_SIZE];+	int r = ARCHIVE_OK;++	switch (sumwrk->alg) {+	case CKSUM_NONE:+		break;+	case CKSUM_SHA1:+		archive_sha1_final(&(sumwrk->sha1ctx), sum);+		if (len != SHA1_SIZE ||+		    memcmp(val, sum, SHA1_SIZE) != 0)+			r = ARCHIVE_FAILED;+		break;+	case CKSUM_MD5:+		archive_md5_final(&(sumwrk->md5ctx), sum);+		if (len != MD5_SIZE ||+		    memcmp(val, sum, MD5_SIZE) != 0)+			r = ARCHIVE_FAILED;+		break;+	}+	return (r);+}++static void+checksum_init(struct archive_read *a, int a_sum_alg, int e_sum_alg)+{+	struct xar *xar;++	xar = (struct xar *)(a->format->data);+	_checksum_init(&(xar->a_sumwrk), a_sum_alg);+	_checksum_init(&(xar->e_sumwrk), e_sum_alg);+}++static void+checksum_update(struct archive_read *a, const void *abuff, size_t asize,+    const void *ebuff, size_t esize)+{+	struct xar *xar;++	xar = (struct xar *)(a->format->data);+	_checksum_update(&(xar->a_sumwrk), abuff, asize);+	_checksum_update(&(xar->e_sumwrk), ebuff, esize);+}++static int+checksum_final(struct archive_read *a, const void *a_sum_val,+    size_t a_sum_len, const void *e_sum_val, size_t e_sum_len)+{+	struct xar *xar;+	int r;++	xar = (struct xar *)(a->format->data);+	r = _checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len);+	if (r == ARCHIVE_OK)+		r = _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len);+	if (r != ARCHIVE_OK)+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,+		    "Sumcheck error");+	return (r);+}++static int+decompression_init(struct archive_read *a, enum enctype encoding)+{+	struct xar *xar;+	const char *detail;+	int r;++	xar = (struct xar *)(a->format->data);+	xar->rd_encoding = encoding;+	switch (encoding) {+	case NONE:+		break;+	case GZIP:+		if (xar->stream_valid)+			r = inflateReset(&(xar->stream));+		else+			r = inflateInit(&(xar->stream));+		if (r != Z_OK) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Couldn't initialize zlib stream.");+			return (ARCHIVE_FATAL);+		}+		xar->stream_valid = 1;+		xar->stream.total_in = 0;+		xar->stream.total_out = 0;+		break;+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+	case BZIP2:+		if (xar->bzstream_valid) {+			BZ2_bzDecompressEnd(&(xar->bzstream));+			xar->bzstream_valid = 0;+		}+		r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 0);+		if (r == BZ_MEM_ERROR)+			r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 1);+		if (r != BZ_OK) {+			int err = ARCHIVE_ERRNO_MISC;+			detail = NULL;+			switch (r) {+			case BZ_PARAM_ERROR:+				detail = "invalid setup parameter";+				break;+			case BZ_MEM_ERROR:+				err = ENOMEM;+				detail = "out of memory";+				break;+			case BZ_CONFIG_ERROR:+				detail = "mis-compiled library";+				break;+			}+			archive_set_error(&a->archive, err,+			    "Internal error initializing decompressor: %s",+			    detail == NULL ? "??" : detail);+			xar->bzstream_valid = 0;+			return (ARCHIVE_FATAL);+		}+		xar->bzstream_valid = 1;+		xar->bzstream.total_in_lo32 = 0;+		xar->bzstream.total_in_hi32 = 0;+		xar->bzstream.total_out_lo32 = 0;+		xar->bzstream.total_out_hi32 = 0;+		break;+#endif+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)+#if LZMA_VERSION_MAJOR >= 5+/* Effectively disable the limiter. */+#define LZMA_MEMLIMIT   UINT64_MAX+#else+/* NOTE: This needs to check memory size which running system has. */+#define LZMA_MEMLIMIT   (1U << 30)+#endif+	case XZ:+	case LZMA:+		if (xar->lzstream_valid) {+			lzma_end(&(xar->lzstream));+			xar->lzstream_valid = 0;+		}+		if (xar->entry_encoding == XZ)+			r = lzma_stream_decoder(&(xar->lzstream),+			    LZMA_MEMLIMIT,/* memlimit */+			    LZMA_CONCATENATED);+		else+			r = lzma_alone_decoder(&(xar->lzstream),+			    LZMA_MEMLIMIT);/* memlimit */+		if (r != LZMA_OK) {+			switch (r) {+			case LZMA_MEM_ERROR:+				archive_set_error(&a->archive,+				    ENOMEM,+				    "Internal error initializing "+				    "compression library: "+				    "Cannot allocate memory");+				break;+			case LZMA_OPTIONS_ERROR:+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Internal error initializing "+				    "compression library: "+				    "Invalid or unsupported options");+				break;+			default:+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Internal error initializing "+				    "lzma library");+				break;+			}+			return (ARCHIVE_FATAL);+		}+		xar->lzstream_valid = 1;+		xar->lzstream.total_in = 0;+		xar->lzstream.total_out = 0;+		break;+#endif+	/*+	 * Unsupported compression.+	 */+	default:+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)+	case BZIP2:+#endif+#if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA)+	case LZMA:+	case XZ:+#endif+		switch (xar->entry_encoding) {+		case BZIP2: detail = "bzip2"; break;+		case LZMA: detail = "lzma"; break;+		case XZ: detail = "xz"; break;+		default: detail = "??"; break;+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "%s compression not supported on this platform",+		    detail);+		return (ARCHIVE_FAILED);+	}+	return (ARCHIVE_OK);+}++static int+decompress(struct archive_read *a, const void **buff, size_t *outbytes,+    const void *b, size_t *used)+{+	struct xar *xar;+	void *outbuff;+	size_t avail_in, avail_out;+	int r;++	xar = (struct xar *)(a->format->data);+	avail_in = *used;+	outbuff = (void *)(uintptr_t)*buff;+	if (outbuff == NULL) {+		if (xar->outbuff == NULL) {+			xar->outbuff = malloc(OUTBUFF_SIZE);+			if (xar->outbuff == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Couldn't allocate memory for out buffer");+				return (ARCHIVE_FATAL);+			}+		}+		outbuff = xar->outbuff;+		*buff = outbuff;+		avail_out = OUTBUFF_SIZE;+	} else+		avail_out = *outbytes;+	switch (xar->rd_encoding) {+	case GZIP:+		xar->stream.next_in = (Bytef *)(uintptr_t)b;+		xar->stream.avail_in = avail_in;+		xar->stream.next_out = (unsigned char *)outbuff;+		xar->stream.avail_out = avail_out;+		r = inflate(&(xar->stream), 0);+		switch (r) {+		case Z_OK: /* Decompressor made some progress.*/+		case Z_STREAM_END: /* Found end of stream. */+			break;+		default:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "File decompression failed (%d)", r);+			return (ARCHIVE_FATAL);+		}+		*used = avail_in - xar->stream.avail_in;+		*outbytes = avail_out - xar->stream.avail_out;+		break;+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+	case BZIP2:+		xar->bzstream.next_in = (char *)(uintptr_t)b;+		xar->bzstream.avail_in = avail_in;+		xar->bzstream.next_out = (char *)outbuff;+		xar->bzstream.avail_out = avail_out;+		r = BZ2_bzDecompress(&(xar->bzstream));+		switch (r) {+		case BZ_STREAM_END: /* Found end of stream. */+			switch (BZ2_bzDecompressEnd(&(xar->bzstream))) {+			case BZ_OK:+				break;+			default:+				archive_set_error(&(a->archive),+				    ARCHIVE_ERRNO_MISC,+				    "Failed to clean up decompressor");+				return (ARCHIVE_FATAL);+			}+			xar->bzstream_valid = 0;+			/* FALLTHROUGH */+		case BZ_OK: /* Decompressor made some progress. */+			break;+		default:+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "bzip decompression failed");+			return (ARCHIVE_FATAL);+		}+		*used = avail_in - xar->bzstream.avail_in;+		*outbytes = avail_out - xar->bzstream.avail_out;+		break;+#endif+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)+	case LZMA:+	case XZ:+		xar->lzstream.next_in = b;+		xar->lzstream.avail_in = avail_in;+		xar->lzstream.next_out = (unsigned char *)outbuff;+		xar->lzstream.avail_out = avail_out;+		r = lzma_code(&(xar->lzstream), LZMA_RUN);+		switch (r) {+		case LZMA_STREAM_END: /* Found end of stream. */+			lzma_end(&(xar->lzstream));+			xar->lzstream_valid = 0;+			/* FALLTHROUGH */+		case LZMA_OK: /* Decompressor made some progress. */+			break;+		default:+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "%s decompression failed(%d)",+			    (xar->entry_encoding == XZ)?"xz":"lzma",+			    r);+			return (ARCHIVE_FATAL);+		}+		*used = avail_in - xar->lzstream.avail_in;+		*outbytes = avail_out - xar->lzstream.avail_out;+		break;+#endif+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)+	case BZIP2:+#endif+#if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA)+	case LZMA:+	case XZ:+#endif+	case NONE:+	default:+		if (outbuff == xar->outbuff) {+			*buff = b;+			*used = avail_in;+			*outbytes = avail_in;+		} else {+			if (avail_out > avail_in)+				avail_out = avail_in;+			memcpy(outbuff, b, avail_out);+			*used = avail_out;+			*outbytes = avail_out;+		}+		break;+	}+	return (ARCHIVE_OK);+}++static int+decompression_cleanup(struct archive_read *a)+{+	struct xar *xar;+	int r;++	xar = (struct xar *)(a->format->data);+	r = ARCHIVE_OK;+	if (xar->stream_valid) {+		if (inflateEnd(&(xar->stream)) != Z_OK) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "Failed to clean up zlib decompressor");+			r = ARCHIVE_FATAL;+		}+	}+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+	if (xar->bzstream_valid) {+		if (BZ2_bzDecompressEnd(&(xar->bzstream)) != BZ_OK) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "Failed to clean up bzip2 decompressor");+			r = ARCHIVE_FATAL;+		}+	}+#endif+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)+	if (xar->lzstream_valid)+		lzma_end(&(xar->lzstream));+#elif defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)+	if (xar->lzstream_valid) {+		if (lzmadec_end(&(xar->lzstream)) != LZMADEC_OK) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "Failed to clean up lzmadec decompressor");+			r = ARCHIVE_FATAL;+		}+	}+#endif+	return (r);+}++static void+checksum_cleanup(struct archive_read *a) {+	struct xar *xar;++	xar = (struct xar *)(a->format->data);++	_checksum_final(&(xar->a_sumwrk), NULL, 0);+	_checksum_final(&(xar->e_sumwrk), NULL, 0);+}++static void+xmlattr_cleanup(struct xmlattr_list *list)+{+	struct xmlattr *attr, *next;++	attr = list->first;+	while (attr != NULL) {+		next = attr->next;+		free(attr->name);+		free(attr->value);+		free(attr);+		attr = next;+	}+	list->first = NULL;+	list->last = &(list->first);+}++static int+file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)+{+	struct xar_file *file;+	struct xmlattr *attr;++	file = calloc(1, sizeof(*file));+	if (file == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	file->parent = xar->file;+	file->mode = 0777 | AE_IFREG;+	file->atime =  0;+	file->mtime = 0;+	xar->file = file;+	xar->xattr = NULL;+	for (attr = list->first; attr != NULL; attr = attr->next) {+		if (strcmp(attr->name, "id") == 0)+			file->id = atol10(attr->value, strlen(attr->value));+	}+	file->nlink = 1;+	if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	return (ARCHIVE_OK);+}++static void+file_free(struct xar_file *file)+{+	struct xattr *xattr;++	archive_string_free(&(file->pathname));+	archive_string_free(&(file->symlink));+	archive_string_free(&(file->uname));+	archive_string_free(&(file->gname));+	archive_string_free(&(file->hardlink));+	xattr = file->xattr_list;+	while (xattr != NULL) {+		struct xattr *next;++		next = xattr->next;+		xattr_free(xattr);+		xattr = next;+	}++	free(file);+}++static int+xattr_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)+{+	struct xattr *xattr, **nx;+	struct xmlattr *attr;++	xattr = calloc(1, sizeof(*xattr));+	if (xattr == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	xar->xattr = xattr;+	for (attr = list->first; attr != NULL; attr = attr->next) {+		if (strcmp(attr->name, "id") == 0)+			xattr->id = atol10(attr->value, strlen(attr->value));+	}+	/* Chain to xattr list. */+	for (nx = &(xar->file->xattr_list);+	    *nx != NULL; nx = &((*nx)->next)) {+		if (xattr->id < (*nx)->id)+			break;+	}+	xattr->next = *nx;+	*nx = xattr;++	return (ARCHIVE_OK);+}++static void+xattr_free(struct xattr *xattr)+{+	archive_string_free(&(xattr->name));+	free(xattr);+}++static int+getencoding(struct xmlattr_list *list)+{+	struct xmlattr *attr;+	enum enctype encoding = NONE;++	for (attr = list->first; attr != NULL; attr = attr->next) {+		if (strcmp(attr->name, "style") == 0) {+			if (strcmp(attr->value, "application/octet-stream") == 0)+				encoding = NONE;+			else if (strcmp(attr->value, "application/x-gzip") == 0)+				encoding = GZIP;+			else if (strcmp(attr->value, "application/x-bzip2") == 0)+				encoding = BZIP2;+			else if (strcmp(attr->value, "application/x-lzma") == 0)+				encoding = LZMA;+			else if (strcmp(attr->value, "application/x-xz") == 0)+				encoding = XZ;+		}+	}+	return (encoding);+}++static int+getsumalgorithm(struct xmlattr_list *list)+{+	struct xmlattr *attr;+	int alg = CKSUM_NONE;++	for (attr = list->first; attr != NULL; attr = attr->next) {+		if (strcmp(attr->name, "style") == 0) {+			const char *v = attr->value;+			if ((v[0] == 'S' || v[0] == 's') &&+			    (v[1] == 'H' || v[1] == 'h') &&+			    (v[2] == 'A' || v[2] == 'a') &&+			    v[3] == '1' && v[4] == '\0')+				alg = CKSUM_SHA1;+			if ((v[0] == 'M' || v[0] == 'm') &&+			    (v[1] == 'D' || v[1] == 'd') &&+			    v[2] == '5' && v[3] == '\0')+				alg = CKSUM_MD5;+		}+	}+	return (alg);+}++static int+unknowntag_start(struct archive_read *a, struct xar *xar, const char *name)+{+	struct unknown_tag *tag;++	tag = malloc(sizeof(*tag));+	if (tag == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	tag->next = xar->unknowntags;+	archive_string_init(&(tag->name));+	archive_strcpy(&(tag->name), name);+	if (xar->unknowntags == NULL) {+#if DEBUG+		fprintf(stderr, "UNKNOWNTAG_START:%s\n", name);+#endif+		xar->xmlsts_unknown = xar->xmlsts;+		xar->xmlsts = UNKNOWN;+	}+	xar->unknowntags = tag;+	return (ARCHIVE_OK);+}++static void+unknowntag_end(struct xar *xar, const char *name)+{+	struct unknown_tag *tag;++	tag = xar->unknowntags;+	if (tag == NULL || name == NULL)+		return;+	if (strcmp(tag->name.s, name) == 0) {+		xar->unknowntags = tag->next;+		archive_string_free(&(tag->name));+		free(tag);+		if (xar->unknowntags == NULL) {+#if DEBUG+			fprintf(stderr, "UNKNOWNTAG_END:%s\n", name);+#endif+			xar->xmlsts = xar->xmlsts_unknown;+		}+	}+}++static int+xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list)+{+	struct xar *xar;+	struct xmlattr *attr;++	xar = (struct xar *)(a->format->data);++#if DEBUG+	fprintf(stderr, "xml_sta:[%s]\n", name);+	for (attr = list->first; attr != NULL; attr = attr->next)+		fprintf(stderr, "    attr:\"%s\"=\"%s\"\n",+		    attr->name, attr->value);+#endif+	xar->base64text = 0;+	switch (xar->xmlsts) {+	case INIT:+		if (strcmp(name, "xar") == 0)+			xar->xmlsts = XAR;+		else+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		break;+	case XAR:+		if (strcmp(name, "toc") == 0)+			xar->xmlsts = TOC;+		else+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		break;+	case TOC:+		if (strcmp(name, "creation-time") == 0)+			xar->xmlsts = TOC_CREATION_TIME;+		else if (strcmp(name, "checksum") == 0)+			xar->xmlsts = TOC_CHECKSUM;+		else if (strcmp(name, "file") == 0) {+			if (file_new(a, xar, list) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			xar->xmlsts = TOC_FILE;+		}+		else+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		break;+	case TOC_CHECKSUM:+		if (strcmp(name, "offset") == 0)+			xar->xmlsts = TOC_CHECKSUM_OFFSET;+		else if (strcmp(name, "size") == 0)+			xar->xmlsts = TOC_CHECKSUM_SIZE;+		else+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		break;+	case TOC_FILE:+		if (strcmp(name, "file") == 0) {+			if (file_new(a, xar, list) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		}+		else if (strcmp(name, "data") == 0)+			xar->xmlsts = FILE_DATA;+		else if (strcmp(name, "ea") == 0) {+			if (xattr_new(a, xar, list) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			xar->xmlsts = FILE_EA;+		}+		else if (strcmp(name, "ctime") == 0)+			xar->xmlsts = FILE_CTIME;+		else if (strcmp(name, "mtime") == 0)+			xar->xmlsts = FILE_MTIME;+		else if (strcmp(name, "atime") == 0)+			xar->xmlsts = FILE_ATIME;+		else if (strcmp(name, "group") == 0)+			xar->xmlsts = FILE_GROUP;+		else if (strcmp(name, "gid") == 0)+			xar->xmlsts = FILE_GID;+		else if (strcmp(name, "user") == 0)+			xar->xmlsts = FILE_USER;+		else if (strcmp(name, "uid") == 0)+			xar->xmlsts = FILE_UID;+		else if (strcmp(name, "mode") == 0)+			xar->xmlsts = FILE_MODE;+		else if (strcmp(name, "device") == 0)+			xar->xmlsts = FILE_DEVICE;+		else if (strcmp(name, "deviceno") == 0)+			xar->xmlsts = FILE_DEVICENO;+		else if (strcmp(name, "inode") == 0)+			xar->xmlsts = FILE_INODE;+		else if (strcmp(name, "link") == 0)+			xar->xmlsts = FILE_LINK;+		else if (strcmp(name, "type") == 0) {+			xar->xmlsts = FILE_TYPE;+			for (attr = list->first; attr != NULL;+			    attr = attr->next) {+				if (strcmp(attr->name, "link") != 0)+					continue;+				if (strcmp(attr->value, "original") == 0) {+					xar->file->hdnext = xar->hdlink_orgs;+					xar->hdlink_orgs = xar->file;+				} else {+					xar->file->link = (unsigned)atol10(attr->value,+					    strlen(attr->value));+					if (xar->file->link > 0)+						if (add_link(a, xar, xar->file) != ARCHIVE_OK) {+							return (ARCHIVE_FATAL);+						};+				}+			}+		}+		else if (strcmp(name, "name") == 0) {+			xar->xmlsts = FILE_NAME;+			for (attr = list->first; attr != NULL;+			    attr = attr->next) {+				if (strcmp(attr->name, "enctype") == 0 &&+				    strcmp(attr->value, "base64") == 0)+					xar->base64text = 1;+			}+		}+		else if (strcmp(name, "acl") == 0)+			xar->xmlsts = FILE_ACL;+		else if (strcmp(name, "flags") == 0)+			xar->xmlsts = FILE_FLAGS;+		else if (strcmp(name, "ext2") == 0)+			xar->xmlsts = FILE_EXT2;+		else+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		break;+	case FILE_DATA:+		if (strcmp(name, "length") == 0)+			xar->xmlsts = FILE_DATA_LENGTH;+		else if (strcmp(name, "offset") == 0)+			xar->xmlsts = FILE_DATA_OFFSET;+		else if (strcmp(name, "size") == 0)+			xar->xmlsts = FILE_DATA_SIZE;+		else if (strcmp(name, "encoding") == 0) {+			xar->xmlsts = FILE_DATA_ENCODING;+			xar->file->encoding = getencoding(list);+		}+		else if (strcmp(name, "archived-checksum") == 0) {+			xar->xmlsts = FILE_DATA_A_CHECKSUM;+			xar->file->a_sum.alg = getsumalgorithm(list);+		}+		else if (strcmp(name, "extracted-checksum") == 0) {+			xar->xmlsts = FILE_DATA_E_CHECKSUM;+			xar->file->e_sum.alg = getsumalgorithm(list);+		}+		else if (strcmp(name, "content") == 0)+			xar->xmlsts = FILE_DATA_CONTENT;+		else+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		break;+	case FILE_DEVICE:+		if (strcmp(name, "major") == 0)+			xar->xmlsts = FILE_DEVICE_MAJOR;+		else if (strcmp(name, "minor") == 0)+			xar->xmlsts = FILE_DEVICE_MINOR;+		else+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		break;+	case FILE_DATA_CONTENT:+		if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		break;+	case FILE_EA:+		if (strcmp(name, "length") == 0)+			xar->xmlsts = FILE_EA_LENGTH;+		else if (strcmp(name, "offset") == 0)+			xar->xmlsts = FILE_EA_OFFSET;+		else if (strcmp(name, "size") == 0)+			xar->xmlsts = FILE_EA_SIZE;+		else if (strcmp(name, "encoding") == 0) {+			xar->xmlsts = FILE_EA_ENCODING;+			xar->xattr->encoding = getencoding(list);+		} else if (strcmp(name, "archived-checksum") == 0)+			xar->xmlsts = FILE_EA_A_CHECKSUM;+		else if (strcmp(name, "extracted-checksum") == 0)+			xar->xmlsts = FILE_EA_E_CHECKSUM;+		else if (strcmp(name, "name") == 0)+			xar->xmlsts = FILE_EA_NAME;+		else if (strcmp(name, "fstype") == 0)+			xar->xmlsts = FILE_EA_FSTYPE;+		else+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		break;+	case FILE_ACL:+		if (strcmp(name, "appleextended") == 0)+			xar->xmlsts = FILE_ACL_APPLEEXTENDED;+		else if (strcmp(name, "default") == 0)+			xar->xmlsts = FILE_ACL_DEFAULT;+		else if (strcmp(name, "access") == 0)+			xar->xmlsts = FILE_ACL_ACCESS;+		else+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		break;+	case FILE_FLAGS:+		if (!xml_parse_file_flags(xar, name))+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		break;+	case FILE_EXT2:+		if (!xml_parse_file_ext2(xar, name))+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		break;+	case TOC_CREATION_TIME:+	case TOC_CHECKSUM_OFFSET:+	case TOC_CHECKSUM_SIZE:+	case FILE_DATA_LENGTH:+	case FILE_DATA_OFFSET:+	case FILE_DATA_SIZE:+	case FILE_DATA_ENCODING:+	case FILE_DATA_A_CHECKSUM:+	case FILE_DATA_E_CHECKSUM:+	case FILE_EA_LENGTH:+	case FILE_EA_OFFSET:+	case FILE_EA_SIZE:+	case FILE_EA_ENCODING:+	case FILE_EA_A_CHECKSUM:+	case FILE_EA_E_CHECKSUM:+	case FILE_EA_NAME:+	case FILE_EA_FSTYPE:+	case FILE_CTIME:+	case FILE_MTIME:+	case FILE_ATIME:+	case FILE_GROUP:+	case FILE_GID:+	case FILE_USER:+	case FILE_UID:+	case FILE_INODE:+	case FILE_DEVICE_MAJOR:+	case FILE_DEVICE_MINOR:+	case FILE_DEVICENO:+	case FILE_MODE:+	case FILE_TYPE:+	case FILE_LINK:+	case FILE_NAME:+	case FILE_ACL_DEFAULT:+	case FILE_ACL_ACCESS:+	case FILE_ACL_APPLEEXTENDED:+	case FILE_FLAGS_USER_NODUMP:+	case FILE_FLAGS_USER_IMMUTABLE:+	case FILE_FLAGS_USER_APPEND:+	case FILE_FLAGS_USER_OPAQUE:+	case FILE_FLAGS_USER_NOUNLINK:+	case FILE_FLAGS_SYS_ARCHIVED:+	case FILE_FLAGS_SYS_IMMUTABLE:+	case FILE_FLAGS_SYS_APPEND:+	case FILE_FLAGS_SYS_NOUNLINK:+	case FILE_FLAGS_SYS_SNAPSHOT:+	case FILE_EXT2_SecureDeletion:+	case FILE_EXT2_Undelete:+	case FILE_EXT2_Compress:+	case FILE_EXT2_Synchronous:+	case FILE_EXT2_Immutable:+	case FILE_EXT2_AppendOnly:+	case FILE_EXT2_NoDump:+	case FILE_EXT2_NoAtime:+	case FILE_EXT2_CompDirty:+	case FILE_EXT2_CompBlock:+	case FILE_EXT2_NoCompBlock:+	case FILE_EXT2_CompError:+	case FILE_EXT2_BTree:+	case FILE_EXT2_HashIndexed:+	case FILE_EXT2_iMagic:+	case FILE_EXT2_Journaled:+	case FILE_EXT2_NoTail:+	case FILE_EXT2_DirSync:+	case FILE_EXT2_TopDir:+	case FILE_EXT2_Reserved:+	case UNKNOWN:+		if (unknowntag_start(a, xar, name) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		break;+	}+	return (ARCHIVE_OK);+}++static void+xml_end(void *userData, const char *name)+{+	struct archive_read *a;+	struct xar *xar;++	a = (struct archive_read *)userData;+	xar = (struct xar *)(a->format->data);++#if DEBUG+	fprintf(stderr, "xml_end:[%s]\n", name);+#endif+	switch (xar->xmlsts) {+	case INIT:+		break;+	case XAR:+		if (strcmp(name, "xar") == 0)+			xar->xmlsts = INIT;+		break;+	case TOC:+		if (strcmp(name, "toc") == 0)+			xar->xmlsts = XAR;+		break;+	case TOC_CREATION_TIME:+		if (strcmp(name, "creation-time") == 0)+			xar->xmlsts = TOC;+		break;+	case TOC_CHECKSUM:+		if (strcmp(name, "checksum") == 0)+			xar->xmlsts = TOC;+		break;+	case TOC_CHECKSUM_OFFSET:+		if (strcmp(name, "offset") == 0)+			xar->xmlsts = TOC_CHECKSUM;+		break;+	case TOC_CHECKSUM_SIZE:+		if (strcmp(name, "size") == 0)+			xar->xmlsts = TOC_CHECKSUM;+		break;+	case TOC_FILE:+		if (strcmp(name, "file") == 0) {+			if (xar->file->parent != NULL &&+			    ((xar->file->mode & AE_IFMT) == AE_IFDIR))+				xar->file->parent->subdirs++;+			xar->file = xar->file->parent;+			if (xar->file == NULL)+				xar->xmlsts = TOC;+		}+		break;+	case FILE_DATA:+		if (strcmp(name, "data") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_DATA_LENGTH:+		if (strcmp(name, "length") == 0)+			xar->xmlsts = FILE_DATA;+		break;+	case FILE_DATA_OFFSET:+		if (strcmp(name, "offset") == 0)+			xar->xmlsts = FILE_DATA;+		break;+	case FILE_DATA_SIZE:+		if (strcmp(name, "size") == 0)+			xar->xmlsts = FILE_DATA;+		break;+	case FILE_DATA_ENCODING:+		if (strcmp(name, "encoding") == 0)+			xar->xmlsts = FILE_DATA;+		break;+	case FILE_DATA_A_CHECKSUM:+		if (strcmp(name, "archived-checksum") == 0)+			xar->xmlsts = FILE_DATA;+		break;+	case FILE_DATA_E_CHECKSUM:+		if (strcmp(name, "extracted-checksum") == 0)+			xar->xmlsts = FILE_DATA;+		break;+	case FILE_DATA_CONTENT:+		if (strcmp(name, "content") == 0)+			xar->xmlsts = FILE_DATA;+		break;+	case FILE_EA:+		if (strcmp(name, "ea") == 0) {+			xar->xmlsts = TOC_FILE;+			xar->xattr = NULL;+		}+		break;+	case FILE_EA_LENGTH:+		if (strcmp(name, "length") == 0)+			xar->xmlsts = FILE_EA;+		break;+	case FILE_EA_OFFSET:+		if (strcmp(name, "offset") == 0)+			xar->xmlsts = FILE_EA;+		break;+	case FILE_EA_SIZE:+		if (strcmp(name, "size") == 0)+			xar->xmlsts = FILE_EA;+		break;+	case FILE_EA_ENCODING:+		if (strcmp(name, "encoding") == 0)+			xar->xmlsts = FILE_EA;+		break;+	case FILE_EA_A_CHECKSUM:+		if (strcmp(name, "archived-checksum") == 0)+			xar->xmlsts = FILE_EA;+		break;+	case FILE_EA_E_CHECKSUM:+		if (strcmp(name, "extracted-checksum") == 0)+			xar->xmlsts = FILE_EA;+		break;+	case FILE_EA_NAME:+		if (strcmp(name, "name") == 0)+			xar->xmlsts = FILE_EA;+		break;+	case FILE_EA_FSTYPE:+		if (strcmp(name, "fstype") == 0)+			xar->xmlsts = FILE_EA;+		break;+	case FILE_CTIME:+		if (strcmp(name, "ctime") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_MTIME:+		if (strcmp(name, "mtime") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_ATIME:+		if (strcmp(name, "atime") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_GROUP:+		if (strcmp(name, "group") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_GID:+		if (strcmp(name, "gid") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_USER:+		if (strcmp(name, "user") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_UID:+		if (strcmp(name, "uid") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_MODE:+		if (strcmp(name, "mode") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_DEVICE:+		if (strcmp(name, "device") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_DEVICE_MAJOR:+		if (strcmp(name, "major") == 0)+			xar->xmlsts = FILE_DEVICE;+		break;+	case FILE_DEVICE_MINOR:+		if (strcmp(name, "minor") == 0)+			xar->xmlsts = FILE_DEVICE;+		break;+	case FILE_DEVICENO:+		if (strcmp(name, "deviceno") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_INODE:+		if (strcmp(name, "inode") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_LINK:+		if (strcmp(name, "link") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_TYPE:+		if (strcmp(name, "type") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_NAME:+		if (strcmp(name, "name") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_ACL:+		if (strcmp(name, "acl") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_ACL_DEFAULT:+		if (strcmp(name, "default") == 0)+			xar->xmlsts = FILE_ACL;+		break;+	case FILE_ACL_ACCESS:+		if (strcmp(name, "access") == 0)+			xar->xmlsts = FILE_ACL;+		break;+	case FILE_ACL_APPLEEXTENDED:+		if (strcmp(name, "appleextended") == 0)+			xar->xmlsts = FILE_ACL;+		break;+	case FILE_FLAGS:+		if (strcmp(name, "flags") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_FLAGS_USER_NODUMP:+		if (strcmp(name, "UserNoDump") == 0)+			xar->xmlsts = FILE_FLAGS;+		break;+	case FILE_FLAGS_USER_IMMUTABLE:+		if (strcmp(name, "UserImmutable") == 0)+			xar->xmlsts = FILE_FLAGS;+		break;+	case FILE_FLAGS_USER_APPEND:+		if (strcmp(name, "UserAppend") == 0)+			xar->xmlsts = FILE_FLAGS;+		break;+	case FILE_FLAGS_USER_OPAQUE:+		if (strcmp(name, "UserOpaque") == 0)+			xar->xmlsts = FILE_FLAGS;+		break;+	case FILE_FLAGS_USER_NOUNLINK:+		if (strcmp(name, "UserNoUnlink") == 0)+			xar->xmlsts = FILE_FLAGS;+		break;+	case FILE_FLAGS_SYS_ARCHIVED:+		if (strcmp(name, "SystemArchived") == 0)+			xar->xmlsts = FILE_FLAGS;+		break;+	case FILE_FLAGS_SYS_IMMUTABLE:+		if (strcmp(name, "SystemImmutable") == 0)+			xar->xmlsts = FILE_FLAGS;+		break;+	case FILE_FLAGS_SYS_APPEND:+		if (strcmp(name, "SystemAppend") == 0)+			xar->xmlsts = FILE_FLAGS;+		break;+	case FILE_FLAGS_SYS_NOUNLINK:+		if (strcmp(name, "SystemNoUnlink") == 0)+			xar->xmlsts = FILE_FLAGS;+		break;+	case FILE_FLAGS_SYS_SNAPSHOT:+		if (strcmp(name, "SystemSnapshot") == 0)+			xar->xmlsts = FILE_FLAGS;+		break;+	case FILE_EXT2:+		if (strcmp(name, "ext2") == 0)+			xar->xmlsts = TOC_FILE;+		break;+	case FILE_EXT2_SecureDeletion:+		if (strcmp(name, "SecureDeletion") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_Undelete:+		if (strcmp(name, "Undelete") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_Compress:+		if (strcmp(name, "Compress") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_Synchronous:+		if (strcmp(name, "Synchronous") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_Immutable:+		if (strcmp(name, "Immutable") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_AppendOnly:+		if (strcmp(name, "AppendOnly") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_NoDump:+		if (strcmp(name, "NoDump") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_NoAtime:+		if (strcmp(name, "NoAtime") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_CompDirty:+		if (strcmp(name, "CompDirty") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_CompBlock:+		if (strcmp(name, "CompBlock") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_NoCompBlock:+		if (strcmp(name, "NoCompBlock") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_CompError:+		if (strcmp(name, "CompError") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_BTree:+		if (strcmp(name, "BTree") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_HashIndexed:+		if (strcmp(name, "HashIndexed") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_iMagic:+		if (strcmp(name, "iMagic") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_Journaled:+		if (strcmp(name, "Journaled") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_NoTail:+		if (strcmp(name, "NoTail") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_DirSync:+		if (strcmp(name, "DirSync") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_TopDir:+		if (strcmp(name, "TopDir") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case FILE_EXT2_Reserved:+		if (strcmp(name, "Reserved") == 0)+			xar->xmlsts = FILE_EXT2;+		break;+	case UNKNOWN:+		unknowntag_end(xar, name);+		break;+	}+}++static const int base64[256] = {+	-1, -1, -1, -1, -1, -1, -1, -1,+	-1, -1, -1, -1, -1, -1, -1, -1, /* 00 - 0F */+	-1, -1, -1, -1, -1, -1, -1, -1,+	-1, -1, -1, -1, -1, -1, -1, -1, /* 10 - 1F */+	-1, -1, -1, -1, -1, -1, -1, -1,+	-1, -1, -1, 62, -1, -1, -1, 63, /* 20 - 2F */+	52, 53, 54, 55, 56, 57, 58, 59,+	60, 61, -1, -1, -1, -1, -1, -1, /* 30 - 3F */+	-1,  0,  1,  2,  3,  4,  5,  6,+	 7,  8,  9, 10, 11, 12, 13, 14, /* 40 - 4F */+	15, 16, 17, 18, 19, 20, 21, 22,+	23, 24, 25, -1, -1, -1, -1, -1, /* 50 - 5F */+	-1, 26, 27, 28, 29, 30, 31, 32,+	33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */+	41, 42, 43, 44, 45, 46, 47, 48,+	49, 50, 51, -1, -1, -1, -1, -1, /* 70 - 7F */+	-1, -1, -1, -1, -1, -1, -1, -1,+	-1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 8F */+	-1, -1, -1, -1, -1, -1, -1, -1,+	-1, -1, -1, -1, -1, -1, -1, -1, /* 90 - 9F */+	-1, -1, -1, -1, -1, -1, -1, -1,+	-1, -1, -1, -1, -1, -1, -1, -1, /* A0 - AF */+	-1, -1, -1, -1, -1, -1, -1, -1,+	-1, -1, -1, -1, -1, -1, -1, -1, /* B0 - BF */+	-1, -1, -1, -1, -1, -1, -1, -1,+	-1, -1, -1, -1, -1, -1, -1, -1, /* C0 - CF */+	-1, -1, -1, -1, -1, -1, -1, -1,+	-1, -1, -1, -1, -1, -1, -1, -1, /* D0 - DF */+	-1, -1, -1, -1, -1, -1, -1, -1,+	-1, -1, -1, -1, -1, -1, -1, -1, /* E0 - EF */+	-1, -1, -1, -1, -1, -1, -1, -1,+	-1, -1, -1, -1, -1, -1, -1, -1, /* F0 - FF */+};++static void+strappend_base64(struct xar *xar,+    struct archive_string *as, const char *s, size_t l)+{+	unsigned char buff[256];+	unsigned char *out;+	const unsigned char *b;+	size_t len;++	(void)xar; /* UNUSED */+	len = 0;+	out = buff;+	b = (const unsigned char *)s;+	while (l > 0) {+		int n = 0;++		if (base64[b[0]] < 0 || base64[b[1]] < 0)+			break;+		n = base64[*b++] << 18;+		n |= base64[*b++] << 12;+		*out++ = n >> 16;+		len++;+		l -= 2;++		if (l > 0) {+			if (base64[*b] < 0)+				break;+			n |= base64[*b++] << 6;+			*out++ = (n >> 8) & 0xFF;+			len++;+			--l;+		}+		if (l > 0) {+			if (base64[*b] < 0)+				break;+			n |= base64[*b++];+			*out++ = n & 0xFF;+			len++;+			--l;+		}+		if (len+3 >= sizeof(buff)) {+			archive_strncat(as, (const char *)buff, len);+			len = 0;+			out = buff;+		}+	}+	if (len > 0)+		archive_strncat(as, (const char *)buff, len);+}++static int+is_string(const char *known, const char *data, size_t len)+{+	if (strlen(known) != len)+		return -1;+	return memcmp(data, known, len);+}++static void+xml_data(void *userData, const char *s, int len)+{+	struct archive_read *a;+	struct xar *xar;++	a = (struct archive_read *)userData;+	xar = (struct xar *)(a->format->data);++#if DEBUG+	{+		char buff[1024];+		if (len > (int)(sizeof(buff)-1))+			len = (int)(sizeof(buff)-1);+		strncpy(buff, s, len);+		buff[len] = 0;+		fprintf(stderr, "\tlen=%d:\"%s\"\n", len, buff);+	}+#endif+	switch (xar->xmlsts) {+	case TOC_CHECKSUM_OFFSET:+		xar->toc_chksum_offset = atol10(s, len);+		break;+	case TOC_CHECKSUM_SIZE:+		xar->toc_chksum_size = atol10(s, len);+		break;+	default:+		break;+	}+	if (xar->file == NULL)+		return;++	switch (xar->xmlsts) {+	case FILE_NAME:+		if (xar->file->parent != NULL) {+			archive_string_concat(&(xar->file->pathname),+			    &(xar->file->parent->pathname));+			archive_strappend_char(&(xar->file->pathname), '/');+		}+		xar->file->has |= HAS_PATHNAME;+		if (xar->base64text) {+			strappend_base64(xar,+			    &(xar->file->pathname), s, len);+		} else+			archive_strncat(&(xar->file->pathname), s, len);+		break;+	case FILE_LINK:+		xar->file->has |= HAS_SYMLINK;+		archive_strncpy(&(xar->file->symlink), s, len);+		break;+	case FILE_TYPE:+		if (is_string("file", s, len) == 0 ||+		    is_string("hardlink", s, len) == 0)+			xar->file->mode =+			    (xar->file->mode & ~AE_IFMT) | AE_IFREG;+		if (is_string("directory", s, len) == 0)+			xar->file->mode =+			    (xar->file->mode & ~AE_IFMT) | AE_IFDIR;+		if (is_string("symlink", s, len) == 0)+			xar->file->mode =+			    (xar->file->mode & ~AE_IFMT) | AE_IFLNK;+		if (is_string("character special", s, len) == 0)+			xar->file->mode =+			    (xar->file->mode & ~AE_IFMT) | AE_IFCHR;+		if (is_string("block special", s, len) == 0)+			xar->file->mode =+			    (xar->file->mode & ~AE_IFMT) | AE_IFBLK;+		if (is_string("socket", s, len) == 0)+			xar->file->mode =+			    (xar->file->mode & ~AE_IFMT) | AE_IFSOCK;+		if (is_string("fifo", s, len) == 0)+			xar->file->mode =+			    (xar->file->mode & ~AE_IFMT) | AE_IFIFO;+		xar->file->has |= HAS_TYPE;+		break;+	case FILE_INODE:+		xar->file->has |= HAS_INO;+		xar->file->ino64 = atol10(s, len);+		break;+	case FILE_DEVICE_MAJOR:+		xar->file->has |= HAS_DEVMAJOR;+		xar->file->devmajor = (dev_t)atol10(s, len);+		break;+	case FILE_DEVICE_MINOR:+		xar->file->has |= HAS_DEVMINOR;+		xar->file->devminor = (dev_t)atol10(s, len);+		break;+	case FILE_DEVICENO:+		xar->file->has |= HAS_DEV;+		xar->file->dev = (dev_t)atol10(s, len);+		break;+	case FILE_MODE:+		xar->file->has |= HAS_MODE;+		xar->file->mode =+		    (xar->file->mode & AE_IFMT) |+		    ((mode_t)(atol8(s, len)) & ~AE_IFMT);+		break;+	case FILE_GROUP:+		xar->file->has |= HAS_GID;+		archive_strncpy(&(xar->file->gname), s, len);+		break;+	case FILE_GID:+		xar->file->has |= HAS_GID;+		xar->file->gid = atol10(s, len);+		break;+	case FILE_USER:+		xar->file->has |= HAS_UID;+		archive_strncpy(&(xar->file->uname), s, len);+		break;+	case FILE_UID:+		xar->file->has |= HAS_UID;+		xar->file->uid = atol10(s, len);+		break;+	case FILE_CTIME:+		xar->file->has |= HAS_TIME | HAS_CTIME;+		xar->file->ctime = parse_time(s, len);+		break;+	case FILE_MTIME:+		xar->file->has |= HAS_TIME | HAS_MTIME;+		xar->file->mtime = parse_time(s, len);+		break;+	case FILE_ATIME:+		xar->file->has |= HAS_TIME | HAS_ATIME;+		xar->file->atime = parse_time(s, len);+		break;+	case FILE_DATA_LENGTH:+		xar->file->has |= HAS_DATA;+		xar->file->length = atol10(s, len);+		break;+	case FILE_DATA_OFFSET:+		xar->file->has |= HAS_DATA;+		xar->file->offset = atol10(s, len);+		break;+	case FILE_DATA_SIZE:+		xar->file->has |= HAS_DATA;+		xar->file->size = atol10(s, len);+		break;+	case FILE_DATA_A_CHECKSUM:+		xar->file->a_sum.len = atohex(xar->file->a_sum.val,+		    sizeof(xar->file->a_sum.val), s, len);+		break;+	case FILE_DATA_E_CHECKSUM:+		xar->file->e_sum.len = atohex(xar->file->e_sum.val,+		    sizeof(xar->file->e_sum.val), s, len);+		break;+	case FILE_EA_LENGTH:+		xar->file->has |= HAS_XATTR;+		xar->xattr->length = atol10(s, len);+		break;+	case FILE_EA_OFFSET:+		xar->file->has |= HAS_XATTR;+		xar->xattr->offset = atol10(s, len);+		break;+	case FILE_EA_SIZE:+		xar->file->has |= HAS_XATTR;+		xar->xattr->size = atol10(s, len);+		break;+	case FILE_EA_A_CHECKSUM:+		xar->file->has |= HAS_XATTR;+		xar->xattr->a_sum.len = atohex(xar->xattr->a_sum.val,+		    sizeof(xar->xattr->a_sum.val), s, len);+		break;+	case FILE_EA_E_CHECKSUM:+		xar->file->has |= HAS_XATTR;+		xar->xattr->e_sum.len = atohex(xar->xattr->e_sum.val,+		    sizeof(xar->xattr->e_sum.val), s, len);+		break;+	case FILE_EA_NAME:+		xar->file->has |= HAS_XATTR;+		archive_strncpy(&(xar->xattr->name), s, len);+		break;+	case FILE_EA_FSTYPE:+		xar->file->has |= HAS_XATTR;+		archive_strncpy(&(xar->xattr->fstype), s, len);+		break;+		break;+	case FILE_ACL_DEFAULT:+	case FILE_ACL_ACCESS:+	case FILE_ACL_APPLEEXTENDED:+		xar->file->has |= HAS_ACL;+		/* TODO */+		break;+	case INIT:+	case XAR:+	case TOC:+	case TOC_CREATION_TIME:+	case TOC_CHECKSUM:+	case TOC_CHECKSUM_OFFSET:+	case TOC_CHECKSUM_SIZE:+	case TOC_FILE:+	case FILE_DATA:+	case FILE_DATA_ENCODING:+	case FILE_DATA_CONTENT:+	case FILE_DEVICE:+	case FILE_EA:+	case FILE_EA_ENCODING:+	case FILE_ACL:+	case FILE_FLAGS:+	case FILE_FLAGS_USER_NODUMP:+	case FILE_FLAGS_USER_IMMUTABLE:+	case FILE_FLAGS_USER_APPEND:+	case FILE_FLAGS_USER_OPAQUE:+	case FILE_FLAGS_USER_NOUNLINK:+	case FILE_FLAGS_SYS_ARCHIVED:+	case FILE_FLAGS_SYS_IMMUTABLE:+	case FILE_FLAGS_SYS_APPEND:+	case FILE_FLAGS_SYS_NOUNLINK:+	case FILE_FLAGS_SYS_SNAPSHOT:+	case FILE_EXT2:+	case FILE_EXT2_SecureDeletion:+	case FILE_EXT2_Undelete:+	case FILE_EXT2_Compress:+	case FILE_EXT2_Synchronous:+	case FILE_EXT2_Immutable:+	case FILE_EXT2_AppendOnly:+	case FILE_EXT2_NoDump:+	case FILE_EXT2_NoAtime:+	case FILE_EXT2_CompDirty:+	case FILE_EXT2_CompBlock:+	case FILE_EXT2_NoCompBlock:+	case FILE_EXT2_CompError:+	case FILE_EXT2_BTree:+	case FILE_EXT2_HashIndexed:+	case FILE_EXT2_iMagic:+	case FILE_EXT2_Journaled:+	case FILE_EXT2_NoTail:+	case FILE_EXT2_DirSync:+	case FILE_EXT2_TopDir:+	case FILE_EXT2_Reserved:+	case UNKNOWN:+		break;+	}+}++/*+ * BSD file flags.+ */+static int+xml_parse_file_flags(struct xar *xar, const char *name)+{+	const char *flag = NULL;++	if (strcmp(name, "UserNoDump") == 0) {+		xar->xmlsts = FILE_FLAGS_USER_NODUMP;+		flag = "nodump";+	}+	else if (strcmp(name, "UserImmutable") == 0) {+		xar->xmlsts = FILE_FLAGS_USER_IMMUTABLE;+		flag = "uimmutable";+	}+	else if (strcmp(name, "UserAppend") == 0) {+		xar->xmlsts = FILE_FLAGS_USER_APPEND;+		flag = "uappend";+	}+	else if (strcmp(name, "UserOpaque") == 0) {+		xar->xmlsts = FILE_FLAGS_USER_OPAQUE;+		flag = "opaque";+	}+	else if (strcmp(name, "UserNoUnlink") == 0) {+		xar->xmlsts = FILE_FLAGS_USER_NOUNLINK;+		flag = "nouunlink";+	}+	else if (strcmp(name, "SystemArchived") == 0) {+		xar->xmlsts = FILE_FLAGS_SYS_ARCHIVED;+		flag = "archived";+	}+	else if (strcmp(name, "SystemImmutable") == 0) {+		xar->xmlsts = FILE_FLAGS_SYS_IMMUTABLE;+		flag = "simmutable";+	}+	else if (strcmp(name, "SystemAppend") == 0) {+		xar->xmlsts = FILE_FLAGS_SYS_APPEND;+		flag = "sappend";+	}+	else if (strcmp(name, "SystemNoUnlink") == 0) {+		xar->xmlsts = FILE_FLAGS_SYS_NOUNLINK;+		flag = "nosunlink";+	}+	else if (strcmp(name, "SystemSnapshot") == 0) {+		xar->xmlsts = FILE_FLAGS_SYS_SNAPSHOT;+		flag = "snapshot";+	}++	if (flag == NULL)+		return (0);+	xar->file->has |= HAS_FFLAGS;+	if (archive_strlen(&(xar->file->fflags_text)) > 0)+		archive_strappend_char(&(xar->file->fflags_text), ',');+	archive_strcat(&(xar->file->fflags_text), flag);+	return (1);+}++/*+ * Linux file flags.+ */+static int+xml_parse_file_ext2(struct xar *xar, const char *name)+{+	const char *flag = NULL;++	if (strcmp(name, "SecureDeletion") == 0) {+		xar->xmlsts = FILE_EXT2_SecureDeletion;+		flag = "securedeletion";+	}+	else if (strcmp(name, "Undelete") == 0) {+		xar->xmlsts = FILE_EXT2_Undelete;+		flag = "nouunlink";+	}+	else if (strcmp(name, "Compress") == 0) {+		xar->xmlsts = FILE_EXT2_Compress;+		flag = "compress";+	}+	else if (strcmp(name, "Synchronous") == 0) {+		xar->xmlsts = FILE_EXT2_Synchronous;+		flag = "sync";+	}+	else if (strcmp(name, "Immutable") == 0) {+		xar->xmlsts = FILE_EXT2_Immutable;+		flag = "simmutable";+	}+	else if (strcmp(name, "AppendOnly") == 0) {+		xar->xmlsts = FILE_EXT2_AppendOnly;+		flag = "sappend";+	}+	else if (strcmp(name, "NoDump") == 0) {+		xar->xmlsts = FILE_EXT2_NoDump;+		flag = "nodump";+	}+	else if (strcmp(name, "NoAtime") == 0) {+		xar->xmlsts = FILE_EXT2_NoAtime;+		flag = "noatime";+	}+	else if (strcmp(name, "CompDirty") == 0) {+		xar->xmlsts = FILE_EXT2_CompDirty;+		flag = "compdirty";+	}+	else if (strcmp(name, "CompBlock") == 0) {+		xar->xmlsts = FILE_EXT2_CompBlock;+		flag = "comprblk";+	}+	else if (strcmp(name, "NoCompBlock") == 0) {+		xar->xmlsts = FILE_EXT2_NoCompBlock;+		flag = "nocomprblk";+	}+	else if (strcmp(name, "CompError") == 0) {+		xar->xmlsts = FILE_EXT2_CompError;+		flag = "comperr";+	}+	else if (strcmp(name, "BTree") == 0) {+		xar->xmlsts = FILE_EXT2_BTree;+		flag = "btree";+	}+	else if (strcmp(name, "HashIndexed") == 0) {+		xar->xmlsts = FILE_EXT2_HashIndexed;+		flag = "hashidx";+	}+	else if (strcmp(name, "iMagic") == 0) {+		xar->xmlsts = FILE_EXT2_iMagic;+		flag = "imagic";+	}+	else if (strcmp(name, "Journaled") == 0) {+		xar->xmlsts = FILE_EXT2_Journaled;+		flag = "journal";+	}+	else if (strcmp(name, "NoTail") == 0) {+		xar->xmlsts = FILE_EXT2_NoTail;+		flag = "notail";+	}+	else if (strcmp(name, "DirSync") == 0) {+		xar->xmlsts = FILE_EXT2_DirSync;+		flag = "dirsync";+	}+	else if (strcmp(name, "TopDir") == 0) {+		xar->xmlsts = FILE_EXT2_TopDir;+		flag = "topdir";+	}+	else if (strcmp(name, "Reserved") == 0) {+		xar->xmlsts = FILE_EXT2_Reserved;+		flag = "reserved";+	}++	if (flag == NULL)+		return (0);+	if (archive_strlen(&(xar->file->fflags_text)) > 0)+		archive_strappend_char(&(xar->file->fflags_text), ',');+	archive_strcat(&(xar->file->fflags_text), flag);+	return (1);+}++#ifdef HAVE_LIBXML_XMLREADER_H++static int+xml2_xmlattr_setup(struct archive_read *a,+    struct xmlattr_list *list, xmlTextReaderPtr reader)+{+	struct xmlattr *attr;+	int r;++	list->first = NULL;+	list->last = &(list->first);+	r = xmlTextReaderMoveToFirstAttribute(reader);+	while (r == 1) {+		attr = malloc(sizeof*(attr));+		if (attr == NULL) {+			archive_set_error(&a->archive, ENOMEM, "Out of memory");+			return (ARCHIVE_FATAL);+		}+		attr->name = strdup(+		    (const char *)xmlTextReaderConstLocalName(reader));+		if (attr->name == NULL) {+			free(attr);+			archive_set_error(&a->archive, ENOMEM, "Out of memory");+			return (ARCHIVE_FATAL);+		}+		attr->value = strdup(+		    (const char *)xmlTextReaderConstValue(reader));+		if (attr->value == NULL) {+			free(attr->name);+			free(attr);+			archive_set_error(&a->archive, ENOMEM, "Out of memory");+			return (ARCHIVE_FATAL);+		}+		attr->next = NULL;+		*list->last = attr;+		list->last = &(attr->next);+		r = xmlTextReaderMoveToNextAttribute(reader);+	}+	return (r);+}++static int+xml2_read_cb(void *context, char *buffer, int len)+{+	struct archive_read *a;+	struct xar *xar;+	const void *d;+	size_t outbytes;+	size_t used = 0;+	int r;++	a = (struct archive_read *)context;+	xar = (struct xar *)(a->format->data);++	if (xar->toc_remaining <= 0)+		return (0);+	d = buffer;+	outbytes = len;+	r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);+	if (r != ARCHIVE_OK)+		return (r);+	__archive_read_consume(a, used);+	xar->toc_remaining -= used;+	xar->offset += used;+	xar->toc_total += outbytes;+	PRINT_TOC(buffer, len);++	return ((int)outbytes);+}++static int+xml2_close_cb(void *context)+{++	(void)context; /* UNUSED */+	return (0);+}++static void+xml2_error_hdr(void *arg, const char *msg, xmlParserSeverities severity,+    xmlTextReaderLocatorPtr locator)+{+	struct archive_read *a;++	(void)locator; /* UNUSED */+	a = (struct archive_read *)arg;+	switch (severity) {+	case XML_PARSER_SEVERITY_VALIDITY_WARNING:+	case XML_PARSER_SEVERITY_WARNING:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "XML Parsing error: %s", msg);+		break;+	case XML_PARSER_SEVERITY_VALIDITY_ERROR:+	case XML_PARSER_SEVERITY_ERROR:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "XML Parsing error: %s", msg);+		break;+	}+}++static int+xml2_read_toc(struct archive_read *a)+{+	xmlTextReaderPtr reader;+	struct xmlattr_list list;+	int r;++	reader = xmlReaderForIO(xml2_read_cb, xml2_close_cb, a, NULL, NULL, 0);+	if (reader == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Couldn't allocate memory for xml parser");+		return (ARCHIVE_FATAL);+	}+	xmlTextReaderSetErrorHandler(reader, xml2_error_hdr, a);++	while ((r = xmlTextReaderRead(reader)) == 1) {+		const char *name, *value;+		int type, empty;++		type = xmlTextReaderNodeType(reader);+		name = (const char *)xmlTextReaderConstLocalName(reader);+		switch (type) {+		case XML_READER_TYPE_ELEMENT:+			empty = xmlTextReaderIsEmptyElement(reader);+			r = xml2_xmlattr_setup(a, &list, reader);+			if (r == ARCHIVE_OK)+				r = xml_start(a, name, &list);+			xmlattr_cleanup(&list);+			if (r != ARCHIVE_OK)+				return (r);+			if (empty)+				xml_end(a, name);+			break;+		case XML_READER_TYPE_END_ELEMENT:+			xml_end(a, name);+			break;+		case XML_READER_TYPE_TEXT:+			value = (const char *)xmlTextReaderConstValue(reader);+			xml_data(a, value, strlen(value));+			break;+		case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:+		default:+			break;+		}+		if (r < 0)+			break;+	}+	xmlFreeTextReader(reader);+	xmlCleanupParser();++	return ((r == 0)?ARCHIVE_OK:ARCHIVE_FATAL);+}++#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)++static int+expat_xmlattr_setup(struct archive_read *a,+    struct xmlattr_list *list, const XML_Char **atts)+{+	struct xmlattr *attr;+	char *name, *value;++	list->first = NULL;+	list->last = &(list->first);+	if (atts == NULL)+		return (ARCHIVE_OK);+	while (atts[0] != NULL && atts[1] != NULL) {+		attr = malloc(sizeof*(attr));+		name = strdup(atts[0]);+		value = strdup(atts[1]);+		if (attr == NULL || name == NULL || value == NULL) {+			archive_set_error(&a->archive, ENOMEM, "Out of memory");+			free(attr);+			free(name);+			free(value);+			return (ARCHIVE_FATAL);+		}+		attr->name = name;+		attr->value = value;+		attr->next = NULL;+		*list->last = attr;+		list->last = &(attr->next);+		atts += 2;+	}+	return (ARCHIVE_OK);+}++static void+expat_start_cb(void *userData, const XML_Char *name, const XML_Char **atts)+{+	struct expat_userData *ud = (struct expat_userData *)userData;+	struct archive_read *a = ud->archive;+	struct xmlattr_list list;+	int r;++	r = expat_xmlattr_setup(a, &list, atts);+	if (r == ARCHIVE_OK)+		r = xml_start(a, (const char *)name, &list);+	xmlattr_cleanup(&list);+	ud->state = r;+}++static void+expat_end_cb(void *userData, const XML_Char *name)+{+	struct expat_userData *ud = (struct expat_userData *)userData;++	xml_end(ud->archive, (const char *)name);+}++static void+expat_data_cb(void *userData, const XML_Char *s, int len)+{+	struct expat_userData *ud = (struct expat_userData *)userData;++	xml_data(ud->archive, s, len);+}++static int+expat_read_toc(struct archive_read *a)+{+	struct xar *xar;+	XML_Parser parser;+	struct expat_userData ud;++	ud.state = ARCHIVE_OK;+	ud.archive = a;++	xar = (struct xar *)(a->format->data);++	/* Initialize XML Parser library. */+	parser = XML_ParserCreate(NULL);+	if (parser == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Couldn't allocate memory for xml parser");+		return (ARCHIVE_FATAL);+	}+	XML_SetUserData(parser, &ud);+	XML_SetElementHandler(parser, expat_start_cb, expat_end_cb);+	XML_SetCharacterDataHandler(parser, expat_data_cb);+	xar->xmlsts = INIT;++	while (xar->toc_remaining && ud.state == ARCHIVE_OK) {+		enum XML_Status xr;+		const void *d;+		size_t outbytes;+		size_t used;+		int r;++		d = NULL;+		r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);+		if (r != ARCHIVE_OK)+			return (r);+		xar->toc_remaining -= used;+		xar->offset += used;+		xar->toc_total += outbytes;+		PRINT_TOC(d, outbytes);++		xr = XML_Parse(parser, d, outbytes, xar->toc_remaining == 0);+		__archive_read_consume(a, used);+		if (xr == XML_STATUS_ERROR) {+			XML_ParserFree(parser);+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "XML Parsing failed");+			return (ARCHIVE_FATAL);+		}+	}+	XML_ParserFree(parser);+	return (ud.state);+}+#endif /* defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) */++#endif /* Support xar format */
+ c/archive_read_support_format_zip.c view
@@ -0,0 +1,4044 @@+/*-+ * Copyright (c) 2004-2013 Tim Kientzle+ * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA+ * Copyright (c) 2013 Konrad Kleine+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 2009-12-28 03:11:36Z kientzle $");++/*+ * The definitive documentation of the Zip file format is:+ *   http://www.pkware.com/documents/casestudies/APPNOTE.TXT+ *+ * The Info-Zip project has pioneered various extensions to better+ * support Zip on Unix, including the 0x5455 "UT", 0x5855 "UX", 0x7855+ * "Ux", and 0x7875 "ux" extensions for time and ownership+ * information.+ *+ * History of this code: The streaming Zip reader was first added to+ * libarchive in January 2005.  Support for seekable input sources was+ * added in Nov 2011.  Zip64 support (including a significant code+ * refactoring) was added in 2014.+ */++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif+#ifdef HAVE_BZLIB_H+#include <bzlib.h>+#endif+#ifdef HAVE_LZMA_H+#include <lzma.h>+#endif++#include "archive.h"+#include "archive_digest_private.h"+#include "archive_cryptor_private.h"+#include "archive_endian.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_hmac_private.h"+#include "archive_private.h"+#include "archive_rb.h"+#include "archive_read_private.h"+#include "archive_ppmd8_private.h"++#ifndef HAVE_ZLIB_H+#include "archive_crc32.h"+#endif++struct zip_entry {+	struct archive_rb_node	node;+	struct zip_entry	*next;+	int64_t			local_header_offset;+	int64_t			compressed_size;+	int64_t			uncompressed_size;+	int64_t			gid;+	int64_t			uid;+	struct archive_string	rsrcname;+	time_t			mtime;+	time_t			atime;+	time_t			ctime;+	uint32_t		crc32;+	uint16_t		mode;+	uint16_t		zip_flags; /* From GP Flags Field */+	unsigned char		compression;+	unsigned char		system; /* From "version written by" */+	unsigned char		flags; /* Our extra markers. */+	unsigned char		decdat;/* Used for Decryption check */++	/* WinZip AES encryption extra field should be available+	 * when compression is 99. */+	struct {+		/* Vendor version: AE-1 - 0x0001, AE-2 - 0x0002 */+		unsigned	vendor;+#define AES_VENDOR_AE_1	0x0001+#define AES_VENDOR_AE_2	0x0002+		/* AES encryption strength:+		 * 1 - 128 bits, 2 - 192 bits, 2 - 256 bits. */+		unsigned	strength;+		/* Actual compression method. */+		unsigned char	compression;+	}			aes_extra;+};++struct trad_enc_ctx {+	uint32_t	keys[3];+};++/* Bits used in zip_flags. */+#define ZIP_ENCRYPTED	(1 << 0)+#define ZIP_LENGTH_AT_END	(1 << 3)+#define ZIP_STRONG_ENCRYPTED	(1 << 6)+#define ZIP_UTF8_NAME	(1 << 11)+/* See "7.2 Single Password Symmetric Encryption Method"+   in http://www.pkware.com/documents/casestudies/APPNOTE.TXT */+#define ZIP_CENTRAL_DIRECTORY_ENCRYPTED	(1 << 13)++/* Bits used in flags. */+#define LA_USED_ZIP64	(1 << 0)+#define LA_FROM_CENTRAL_DIRECTORY (1 << 1)++/*+ * See "WinZip - AES Encryption Information"+ *     http://www.winzip.com/aes_info.htm+ */+/* Value used in compression method. */+#define WINZIP_AES_ENCRYPTION	99+/* Authentication code size. */+#define AUTH_CODE_SIZE	10+/**/+#define MAX_DERIVED_KEY_BUF_SIZE	(AES_MAX_KEY_SIZE * 2 + 2)++struct zip {+	/* Structural information about the archive. */+	struct archive_string	format_name;+	int64_t			central_directory_offset;+	size_t			central_directory_entries_total;+	size_t			central_directory_entries_on_this_disk;+	int			has_encrypted_entries;++	/* List of entries (seekable Zip only) */+	struct zip_entry	*zip_entries;+	struct archive_rb_tree	tree;+	struct archive_rb_tree	tree_rsrc;++	/* Bytes read but not yet consumed via __archive_read_consume() */+	size_t			unconsumed;++	/* Information about entry we're currently reading. */+	struct zip_entry	*entry;+	int64_t			entry_bytes_remaining;++	/* These count the number of bytes actually read for the entry. */+	int64_t			entry_compressed_bytes_read;+	int64_t			entry_uncompressed_bytes_read;++	/* Running CRC32 of the decompressed data */+	unsigned long		entry_crc32;+	unsigned long		(*crc32func)(unsigned long, const void *,+				    size_t);+	char			ignore_crc32;++	/* Flags to mark progress of decompression. */+	char			decompress_init;+	char			end_of_entry;++	unsigned char 		*uncompressed_buffer;+	size_t 			uncompressed_buffer_size;++#ifdef HAVE_ZLIB_H+	z_stream		stream;+	char			stream_valid;+#endif++#if HAVE_LZMA_H && HAVE_LIBLZMA+	lzma_stream		zipx_lzma_stream;+	char            zipx_lzma_valid;+#endif++#ifdef HAVE_BZLIB_H+	bz_stream		bzstream;+	char            bzstream_valid;+#endif++	IByteIn			zipx_ppmd_stream;+	ssize_t			zipx_ppmd_read_compressed;+	CPpmd8			ppmd8;+	char			ppmd8_valid;+	char			ppmd8_stream_failed;++	struct archive_string_conv *sconv;+	struct archive_string_conv *sconv_default;+	struct archive_string_conv *sconv_utf8;+	int			init_default_conversion;+	int			process_mac_extensions;++	char			init_decryption;++	/* Decryption buffer. */+	/*+	 * The decrypted data starts at decrypted_ptr and+	 * extends for decrypted_bytes_remaining.  Decryption+	 * adds new data to the end of this block, data is returned+	 * to clients from the beginning.  When the block hits the+	 * end of decrypted_buffer, it has to be shuffled back to+	 * the beginning of the buffer.+	 */+	unsigned char 		*decrypted_buffer;+	unsigned char 		*decrypted_ptr;+	size_t 			decrypted_buffer_size;+	size_t 			decrypted_bytes_remaining;+	size_t 			decrypted_unconsumed_bytes;++	/* Traditional PKWARE decryption. */+	struct trad_enc_ctx	tctx;+	char			tctx_valid;++	/* WinZip AES decryption. */+	/* Contexts used for AES decryption. */+	archive_crypto_ctx	cctx;+	char			cctx_valid;+	archive_hmac_sha1_ctx	hctx;+	char			hctx_valid;++	/* Strong encryption's decryption header information. */+	unsigned		iv_size;+	unsigned		alg_id;+	unsigned		bit_len;+	unsigned		flags;+	unsigned		erd_size;+	unsigned		v_size;+	unsigned		v_crc32;+	uint8_t			*iv;+	uint8_t			*erd;+	uint8_t			*v_data;+};++/* Many systems define min or MIN, but not all. */+#define	zipmin(a,b) ((a) < (b) ? (a) : (b))++/* This function is used by Ppmd8_DecodeSymbol during decompression of Ppmd8+ * streams inside ZIP files. It has 2 purposes: one is to fetch the next+ * compressed byte from the stream, second one is to increase the counter how+ * many compressed bytes were read. */+static Byte+ppmd_read(void* p) {+	/* Get the handle to current decompression context. */+	struct archive_read *a = ((IByteIn*)p)->a;+	struct zip *zip = (struct zip*) a->format->data;+	ssize_t bytes_avail = 0;++	/* Fetch next byte. */+	const uint8_t* data = __archive_read_ahead(a, 1, &bytes_avail);+	if(bytes_avail < 1) {+		zip->ppmd8_stream_failed = 1;+		return 0;+	}++	__archive_read_consume(a, 1);++	/* Increment the counter. */+	++zip->zipx_ppmd_read_compressed;++	/* Return the next compressed byte. */+	return data[0];+}++/* ------------------------------------------------------------------------ */++/*+  Traditional PKWARE Decryption functions.+ */++static void+trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c)+{+	uint8_t t;+#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL)++	ctx->keys[0] = CRC32(ctx->keys[0], c);+	ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1;+	t = (ctx->keys[1] >> 24) & 0xff;+	ctx->keys[2] = CRC32(ctx->keys[2], t);+#undef CRC32+}++static uint8_t+trad_enc_decrypt_byte(struct trad_enc_ctx *ctx)+{+	unsigned temp = ctx->keys[2] | 2;+	return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff;+}++static void+trad_enc_decrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in,+    size_t in_len, uint8_t *out, size_t out_len)+{+	unsigned i, max;++	max = (unsigned)((in_len < out_len)? in_len: out_len);++	for (i = 0; i < max; i++) {+		uint8_t t = in[i] ^ trad_enc_decrypt_byte(ctx);+		out[i] = t;+		trad_enc_update_keys(ctx, t);+	}+}++static int+trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len,+    const uint8_t *key, size_t key_len, uint8_t *crcchk)+{+	uint8_t header[12];++	if (key_len < 12) {+		*crcchk = 0xff;+		return -1;+	}++	ctx->keys[0] = 305419896L;+	ctx->keys[1] = 591751049L;+	ctx->keys[2] = 878082192L;++	for (;pw_len; --pw_len)+		trad_enc_update_keys(ctx, *pw++);++	trad_enc_decrypt_update(ctx, key, 12, header, 12);+	/* Return the last byte for CRC check. */+	*crcchk = header[11];+	return 0;+}++#if 0+static void+crypt_derive_key_sha1(const void *p, int size, unsigned char *key,+    int key_size)+{+#define MD_SIZE 20+	archive_sha1_ctx ctx;+	unsigned char md1[MD_SIZE];+	unsigned char md2[MD_SIZE * 2];+	unsigned char mkb[64];+	int i;++	archive_sha1_init(&ctx);+	archive_sha1_update(&ctx, p, size);+	archive_sha1_final(&ctx, md1);++	memset(mkb, 0x36, sizeof(mkb));+	for (i = 0; i < MD_SIZE; i++)+		mkb[i] ^= md1[i];+	archive_sha1_init(&ctx);+	archive_sha1_update(&ctx, mkb, sizeof(mkb));+	archive_sha1_final(&ctx, md2);++	memset(mkb, 0x5C, sizeof(mkb));+	for (i = 0; i < MD_SIZE; i++)+		mkb[i] ^= md1[i];+	archive_sha1_init(&ctx);+	archive_sha1_update(&ctx, mkb, sizeof(mkb));+	archive_sha1_final(&ctx, md2 + MD_SIZE);++	if (key_size > 32)+		key_size = 32;+	memcpy(key, md2, key_size);+#undef MD_SIZE+}+#endif++/*+ * Common code for streaming or seeking modes.+ *+ * Includes code to read local file headers, decompress data+ * from entry bodies, and common API.+ */++static unsigned long+real_crc32(unsigned long crc, const void *buff, size_t len)+{+	return crc32(crc, buff, (unsigned int)len);+}++/* Used by "ignorecrc32" option to speed up tests. */+static unsigned long+fake_crc32(unsigned long crc, const void *buff, size_t len)+{+	(void)crc; /* UNUSED */+	(void)buff; /* UNUSED */+	(void)len; /* UNUSED */+	return 0;+}++static const struct {+	int id;+	const char * name;+} compression_methods[] = {+	{0, "uncompressed"}, /* The file is stored (no compression) */+	{1, "shrinking"}, /* The file is Shrunk */+	{2, "reduced-1"}, /* The file is Reduced with compression factor 1 */+	{3, "reduced-2"}, /* The file is Reduced with compression factor 2 */+	{4, "reduced-3"}, /* The file is Reduced with compression factor 3 */+	{5, "reduced-4"}, /* The file is Reduced with compression factor 4 */+	{6, "imploded"},  /* The file is Imploded */+	{7, "reserved"},  /* Reserved for Tokenizing compression algorithm */+	{8, "deflation"}, /* The file is Deflated */+	{9, "deflation-64-bit"}, /* Enhanced Deflating using Deflate64(tm) */+	{10, "ibm-terse"},/* PKWARE Data Compression Library Imploding+			   * (old IBM TERSE) */+	{11, "reserved"}, /* Reserved by PKWARE */+	{12, "bzip"},     /* File is compressed using BZIP2 algorithm */+	{13, "reserved"}, /* Reserved by PKWARE */+	{14, "lzma"},     /* LZMA (EFS) */+	{15, "reserved"}, /* Reserved by PKWARE */+	{16, "reserved"}, /* Reserved by PKWARE */+	{17, "reserved"}, /* Reserved by PKWARE */+	{18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */+	{19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */+	{95, "xz"},       /* XZ compressed data */+	{96, "jpeg"},     /* JPEG compressed data */+	{97, "wav-pack"}, /* WavPack compressed data */+	{98, "ppmd-1"},   /* PPMd version I, Rev 1 */+	{99, "aes"}       /* WinZip AES encryption  */+};++static const char *+compression_name(const int compression)+{+	static const int num_compression_methods =+		sizeof(compression_methods)/sizeof(compression_methods[0]);+	int i=0;++	while(compression >= 0 && i < num_compression_methods) {+		if (compression_methods[i].id == compression)+			return compression_methods[i].name;+		i++;+	}+	return "??";+}++/* Convert an MSDOS-style date/time into Unix-style time. */+static time_t+zip_time(const char *p)+{+	int msTime, msDate;+	struct tm ts;++	msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]);+	msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]);++	memset(&ts, 0, sizeof(ts));+	ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */+	ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */+	ts.tm_mday = msDate & 0x1f; /* Day of month. */+	ts.tm_hour = (msTime >> 11) & 0x1f;+	ts.tm_min = (msTime >> 5) & 0x3f;+	ts.tm_sec = (msTime << 1) & 0x3e;+	ts.tm_isdst = -1;+	return mktime(&ts);+}++/*+ * The extra data is stored as a list of+ *	id1+size1+data1 + id2+size2+data2 ...+ *  triplets.  id and size are 2 bytes each.+ */+static int+process_extra(struct archive_read *a, struct archive_entry *entry,+     const char *p, size_t extra_length, struct zip_entry* zip_entry)+{+	unsigned offset = 0;+	struct zip *zip = (struct zip *)(a->format->data);++	if (extra_length == 0) {+		return ARCHIVE_OK;+	}++	if (extra_length < 4) {+		size_t i = 0;+		/* Some ZIP files may have trailing 0 bytes. Let's check they+		 * are all 0 and ignore them instead of returning an error.+		 *+		 * This is not technically correct, but some ZIP files look+		 * like this and other tools support those files - so let's+		 * also  support them.+		 */+		for (; i < extra_length; i++) {+			if (p[i] != 0) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Too-small extra data: "+				    "Need at least 4 bytes, "+				    "but only found %d bytes",+				    (int)extra_length);+				return ARCHIVE_FAILED;+			}+		}++		return ARCHIVE_OK;+	}++	while (offset <= extra_length - 4) {+		unsigned short headerid = archive_le16dec(p + offset);+		unsigned short datasize = archive_le16dec(p + offset + 2);++		offset += 4;+		if (offset + datasize > extra_length) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT, "Extra data overflow: "+			    "Need %d bytes but only found %d bytes",+			    (int)datasize, (int)(extra_length - offset));+			return ARCHIVE_FAILED;+		}+#ifdef DEBUG+		fprintf(stderr, "Header id 0x%04x, length %d\n",+		    headerid, datasize);+#endif+		switch (headerid) {+		case 0x0001:+			/* Zip64 extended information extra field. */+			zip_entry->flags |= LA_USED_ZIP64;+			if (zip_entry->uncompressed_size == 0xffffffff) {+				uint64_t t = 0;+				if (datasize < 8+				    || (t = archive_le64dec(p + offset)) >+				    INT64_MAX) {+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_FILE_FORMAT,+					    "Malformed 64-bit "+					    "uncompressed size");+					return ARCHIVE_FAILED;+				}+				zip_entry->uncompressed_size = t;+				offset += 8;+				datasize -= 8;+			}+			if (zip_entry->compressed_size == 0xffffffff) {+				uint64_t t = 0;+				if (datasize < 8+				    || (t = archive_le64dec(p + offset)) >+				    INT64_MAX) {+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_FILE_FORMAT,+					    "Malformed 64-bit "+					    "compressed size");+					return ARCHIVE_FAILED;+				}+				zip_entry->compressed_size = t;+				offset += 8;+				datasize -= 8;+			}+			if (zip_entry->local_header_offset == 0xffffffff) {+				uint64_t t = 0;+				if (datasize < 8+				    || (t = archive_le64dec(p + offset)) >+				    INT64_MAX) {+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_FILE_FORMAT,+					    "Malformed 64-bit "+					    "local header offset");+					return ARCHIVE_FAILED;+				}+				zip_entry->local_header_offset = t;+				offset += 8;+				datasize -= 8;+			}+			/* archive_le32dec(p + offset) gives disk+			 * on which file starts, but we don't handle+			 * multi-volume Zip files. */+			break;+#ifdef DEBUG+		case 0x0017:+		{+			/* Strong encryption field. */+			if (archive_le16dec(p + offset) == 2) {+				unsigned algId =+					archive_le16dec(p + offset + 2);+				unsigned bitLen =+					archive_le16dec(p + offset + 4);+				int	 flags =+					archive_le16dec(p + offset + 6);+				fprintf(stderr, "algId=0x%04x, bitLen=%u, "+				    "flgas=%d\n", algId, bitLen,flags);+			}+			break;+		}+#endif+		case 0x5455:+		{+			/* Extended time field "UT". */+			int flags;+			if (datasize == 0) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Incomplete extended time field");+				return ARCHIVE_FAILED;+			}+			flags = p[offset];+			offset++;+			datasize--;+			/* Flag bits indicate which dates are present. */+			if (flags & 0x01)+			{+#ifdef DEBUG+				fprintf(stderr, "mtime: %lld -> %d\n",+				    (long long)zip_entry->mtime,+				    archive_le32dec(p + offset));+#endif+				if (datasize < 4)+					break;+				zip_entry->mtime = archive_le32dec(p + offset);+				offset += 4;+				datasize -= 4;+			}+			if (flags & 0x02)+			{+				if (datasize < 4)+					break;+				zip_entry->atime = archive_le32dec(p + offset);+				offset += 4;+				datasize -= 4;+			}+			if (flags & 0x04)+			{+				if (datasize < 4)+					break;+				zip_entry->ctime = archive_le32dec(p + offset);+				offset += 4;+				datasize -= 4;+			}+			break;+		}+		case 0x5855:+		{+			/* Info-ZIP Unix Extra Field (old version) "UX". */+			if (datasize >= 8) {+				zip_entry->atime = archive_le32dec(p + offset);+				zip_entry->mtime =+				    archive_le32dec(p + offset + 4);+			}+			if (datasize >= 12) {+				zip_entry->uid =+				    archive_le16dec(p + offset + 8);+				zip_entry->gid =+				    archive_le16dec(p + offset + 10);+			}+			break;+		}+		case 0x6c78:+		{+			/* Experimental 'xl' field */+			/*+			 * Introduced Dec 2013 to provide a way to+			 * include external file attributes (and other+			 * fields that ordinarily appear only in+			 * central directory) in local file header.+			 * This provides file type and permission+			 * information necessary to support full+			 * streaming extraction.  Currently being+			 * discussed with other Zip developers+			 * ... subject to change.+			 *+			 * Format:+			 *  The field starts with a bitmap that specifies+			 *  which additional fields are included.  The+			 *  bitmap is variable length and can be extended in+			 *  the future.+			 *+			 *  n bytes - feature bitmap: first byte has low-order+			 *    7 bits.  If high-order bit is set, a subsequent+			 *    byte holds the next 7 bits, etc.+			 *+			 *  if bitmap & 1, 2 byte "version made by"+			 *  if bitmap & 2, 2 byte "internal file attributes"+			 *  if bitmap & 4, 4 byte "external file attributes"+			 *  if bitmap & 8, 2 byte comment length + n byte+			 *  comment+			 */+			int bitmap, bitmap_last;++			if (datasize < 1)+				break;+			bitmap_last = bitmap = 0xff & p[offset];+			offset += 1;+			datasize -= 1;++			/* We only support first 7 bits of bitmap; skip rest. */+			while ((bitmap_last & 0x80) != 0+			    && datasize >= 1) {+				bitmap_last = p[offset];+				offset += 1;+				datasize -= 1;+			}++			if (bitmap & 1) {+				/* 2 byte "version made by" */+				if (datasize < 2)+					break;+				zip_entry->system+				    = archive_le16dec(p + offset) >> 8;+				offset += 2;+				datasize -= 2;+			}+			if (bitmap & 2) {+				/* 2 byte "internal file attributes" */+				uint32_t internal_attributes;+				if (datasize < 2)+					break;+				internal_attributes+				    = archive_le16dec(p + offset);+				/* Not used by libarchive at present. */+				(void)internal_attributes; /* UNUSED */+				offset += 2;+				datasize -= 2;+			}+			if (bitmap & 4) {+				/* 4 byte "external file attributes" */+				uint32_t external_attributes;+				if (datasize < 4)+					break;+				external_attributes+				    = archive_le32dec(p + offset);+				if (zip_entry->system == 3) {+					zip_entry->mode+					    = external_attributes >> 16;+				} else if (zip_entry->system == 0) {+					// Interpret MSDOS directory bit+					if (0x10 == (external_attributes &+					    0x10)) {+						zip_entry->mode =+						    AE_IFDIR | 0775;+					} else {+						zip_entry->mode =+						    AE_IFREG | 0664;+					}+					if (0x01 == (external_attributes &+					    0x01)) {+						/* Read-only bit;+						 * strip write permissions */+						zip_entry->mode &= 0555;+					}+				} else {+					zip_entry->mode = 0;+				}+				offset += 4;+				datasize -= 4;+			}+			if (bitmap & 8) {+				/* 2 byte comment length + comment */+				uint32_t comment_length;+				if (datasize < 2)+					break;+				comment_length+				    = archive_le16dec(p + offset);+				offset += 2;+				datasize -= 2;++				if (datasize < comment_length)+					break;+				/* Comment is not supported by libarchive */+				offset += comment_length;+				datasize -= comment_length;+			}+			break;+		}+		case 0x7075:+		{+			/* Info-ZIP Unicode Path Extra Field. */+			if (datasize < 5 || entry == NULL)+				break;+			offset += 5;+			datasize -= 5;++			/* The path name in this field is always encoded+			 * in UTF-8. */+			if (zip->sconv_utf8 == NULL) {+				zip->sconv_utf8 =+					archive_string_conversion_from_charset(+					&a->archive, "UTF-8", 1);+				/* If the converter from UTF-8 is not+				 * available, then the path name from the main+				 * field will more likely be correct. */+				if (zip->sconv_utf8 == NULL)+					break;+			}++			/* Make sure the CRC32 of the filename matches. */+			if (!zip->ignore_crc32) {+				const char *cp = archive_entry_pathname(entry);+				if (cp) {+					unsigned long file_crc =+					    zip->crc32func(0, cp, strlen(cp));+					unsigned long utf_crc =+					    archive_le32dec(p + offset - 4);+					if (file_crc != utf_crc) {+#ifdef DEBUG+						fprintf(stderr,+						    "CRC filename mismatch; "+						    "CDE is %lx, but UTF8 "+						    "is outdated with %lx\n",+						    file_crc, utf_crc);+#endif+						break;+					}+				}+			}++			if (archive_entry_copy_pathname_l(entry,+			    p + offset, datasize, zip->sconv_utf8) != 0) {+				/* Ignore the error, and fallback to the path+				 * name from the main field. */+#ifdef DEBUG+				fprintf(stderr, "Failed to read the ZIP "+				    "0x7075 extra field path.\n");+#endif+			}+			break;+		}+		case 0x7855:+			/* Info-ZIP Unix Extra Field (type 2) "Ux". */+#ifdef DEBUG+			fprintf(stderr, "uid %d gid %d\n",+			    archive_le16dec(p + offset),+			    archive_le16dec(p + offset + 2));+#endif+			if (datasize >= 2)+				zip_entry->uid = archive_le16dec(p + offset);+			if (datasize >= 4)+				zip_entry->gid =+				    archive_le16dec(p + offset + 2);+			break;+		case 0x7875:+		{+			/* Info-Zip Unix Extra Field (type 3) "ux". */+			int uidsize = 0, gidsize = 0;++			/* TODO: support arbitrary uidsize/gidsize. */+			if (datasize >= 1 && p[offset] == 1) {/* version=1 */+				if (datasize >= 4) {+					/* get a uid size. */+					uidsize = 0xff & (int)p[offset+1];+					if (uidsize == 2)+						zip_entry->uid =+						    archive_le16dec(+						        p + offset + 2);+					else if (uidsize == 4 && datasize >= 6)+						zip_entry->uid =+						    archive_le32dec(+						        p + offset + 2);+				}+				if (datasize >= (2 + uidsize + 3)) {+					/* get a gid size. */+					gidsize = 0xff &+					    (int)p[offset+2+uidsize];+					if (gidsize == 2)+						zip_entry->gid =+						    archive_le16dec(+						        p+offset+2+uidsize+1);+					else if (gidsize == 4 &&+					    datasize >= (2 + uidsize + 5))+						zip_entry->gid =+						    archive_le32dec(+						        p+offset+2+uidsize+1);+				}+			}+			break;+		}+		case 0x9901:+			/* WinZip AES extra data field. */+			if (datasize < 6) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Incomplete AES field");+				return ARCHIVE_FAILED;+			}+			if (p[offset + 2] == 'A' && p[offset + 3] == 'E') {+				/* Vendor version. */+				zip_entry->aes_extra.vendor =+				    archive_le16dec(p + offset);+				/* AES encryption strength. */+				zip_entry->aes_extra.strength = p[offset + 4];+				/* Actual compression method. */+				zip_entry->aes_extra.compression =+				    p[offset + 5];+			}+			break;+		default:+			break;+		}+		offset += datasize;+	}+	return ARCHIVE_OK;+}++/*+ * Assumes file pointer is at beginning of local file header.+ */+static int+zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,+    struct zip *zip)+{+	const char *p;+	const void *h;+	const wchar_t *wp;+	const char *cp;+	size_t len, filename_length, extra_length;+	struct archive_string_conv *sconv;+	struct zip_entry *zip_entry = zip->entry;+	struct zip_entry zip_entry_central_dir;+	int ret = ARCHIVE_OK;+	char version;++	/* Save a copy of the original for consistency checks. */+	zip_entry_central_dir = *zip_entry;++	zip->decompress_init = 0;+	zip->end_of_entry = 0;+	zip->entry_uncompressed_bytes_read = 0;+	zip->entry_compressed_bytes_read = 0;+	zip->entry_crc32 = zip->crc32func(0, NULL, 0);++	/* Setup default conversion. */+	if (zip->sconv == NULL && !zip->init_default_conversion) {+		zip->sconv_default =+		    archive_string_default_conversion_for_read(&(a->archive));+		zip->init_default_conversion = 1;+	}++	if ((p = __archive_read_ahead(a, 30, NULL)) == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated ZIP file header");+		return (ARCHIVE_FATAL);+	}++	if (memcmp(p, "PK\003\004", 4) != 0) {+		archive_set_error(&a->archive, -1, "Damaged Zip archive");+		return ARCHIVE_FATAL;+	}+	version = p[4];+	zip_entry->system = p[5];+	zip_entry->zip_flags = archive_le16dec(p + 6);+	if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) {+		zip->has_encrypted_entries = 1;+		archive_entry_set_is_data_encrypted(entry, 1);+		if (zip_entry->zip_flags & ZIP_CENTRAL_DIRECTORY_ENCRYPTED &&+			zip_entry->zip_flags & ZIP_ENCRYPTED &&+			zip_entry->zip_flags & ZIP_STRONG_ENCRYPTED) {+			archive_entry_set_is_metadata_encrypted(entry, 1);+			return ARCHIVE_FATAL;+		}+	}+	zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED);+	zip_entry->compression = (char)archive_le16dec(p + 8);+	zip_entry->mtime = zip_time(p + 10);+	zip_entry->crc32 = archive_le32dec(p + 14);+	if (zip_entry->zip_flags & ZIP_LENGTH_AT_END)+		zip_entry->decdat = p[11];+	else+		zip_entry->decdat = p[17];+	zip_entry->compressed_size = archive_le32dec(p + 18);+	zip_entry->uncompressed_size = archive_le32dec(p + 22);+	filename_length = archive_le16dec(p + 26);+	extra_length = archive_le16dec(p + 28);++	__archive_read_consume(a, 30);++	/* Read the filename. */+	if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated ZIP file header");+		return (ARCHIVE_FATAL);+	}+	if (zip_entry->zip_flags & ZIP_UTF8_NAME) {+		/* The filename is stored to be UTF-8. */+		if (zip->sconv_utf8 == NULL) {+			zip->sconv_utf8 =+			    archive_string_conversion_from_charset(+				&a->archive, "UTF-8", 1);+			if (zip->sconv_utf8 == NULL)+				return (ARCHIVE_FATAL);+		}+		sconv = zip->sconv_utf8;+	} else if (zip->sconv != NULL)+		sconv = zip->sconv;+	else+		sconv = zip->sconv_default;++	if (archive_entry_copy_pathname_l(entry,+	    h, filename_length, sconv) != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Pathname cannot be converted "+		    "from %s to current locale.",+		    archive_string_conversion_charset_name(sconv));+		ret = ARCHIVE_WARN;+	}+	__archive_read_consume(a, filename_length);++	/* Read the extra data. */+	if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated ZIP file header");+		return (ARCHIVE_FATAL);+	}++	if (ARCHIVE_OK != process_extra(a, entry, h, extra_length,+	    zip_entry)) {+		return ARCHIVE_FATAL;+	}+	__archive_read_consume(a, extra_length);++	/* Work around a bug in Info-Zip: When reading from a pipe, it+	 * stats the pipe instead of synthesizing a file entry. */+	if ((zip_entry->mode & AE_IFMT) == AE_IFIFO) {+		zip_entry->mode &= ~ AE_IFMT;+		zip_entry->mode |= AE_IFREG;+	}++	/* If the mode is totally empty, set some sane default. */+	if (zip_entry->mode == 0) {+		zip_entry->mode |= 0664;+	}++	/* Windows archivers sometimes use backslash as the directory+	 * separator. Normalize to slash. */+	if (zip_entry->system == 0 &&+	    (wp = archive_entry_pathname_w(entry)) != NULL) {+		if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) {+			size_t i;+			struct archive_wstring s;+			archive_string_init(&s);+			archive_wstrcpy(&s, wp);+			for (i = 0; i < archive_strlen(&s); i++) {+				if (s.s[i] == '\\')+					s.s[i] = '/';+			}+			archive_entry_copy_pathname_w(entry, s.s);+			archive_wstring_free(&s);+		}+	}++	/* Make sure that entries with a trailing '/' are marked as directories+	 * even if the External File Attributes contains bogus values.  If this+	 * is not a directory and there is no type, assume a regular file. */+	if ((zip_entry->mode & AE_IFMT) != AE_IFDIR) {+		int has_slash;++		wp = archive_entry_pathname_w(entry);+		if (wp != NULL) {+			len = wcslen(wp);+			has_slash = len > 0 && wp[len - 1] == L'/';+		} else {+			cp = archive_entry_pathname(entry);+			len = (cp != NULL)?strlen(cp):0;+			has_slash = len > 0 && cp[len - 1] == '/';+		}+		/* Correct file type as needed. */+		if (has_slash) {+			zip_entry->mode &= ~AE_IFMT;+			zip_entry->mode |= AE_IFDIR;+			zip_entry->mode |= 0111;+		} else if ((zip_entry->mode & AE_IFMT) == 0) {+			zip_entry->mode |= AE_IFREG;+		}+	}++	/* Make sure directories end in '/' */+	if ((zip_entry->mode & AE_IFMT) == AE_IFDIR) {+		wp = archive_entry_pathname_w(entry);+		if (wp != NULL) {+			len = wcslen(wp);+			if (len > 0 && wp[len - 1] != L'/') {+				struct archive_wstring s;+				archive_string_init(&s);+				archive_wstrcat(&s, wp);+				archive_wstrappend_wchar(&s, L'/');+				archive_entry_copy_pathname_w(entry, s.s);+				archive_wstring_free(&s);+			}+		} else {+			cp = archive_entry_pathname(entry);+			len = (cp != NULL)?strlen(cp):0;+			if (len > 0 && cp[len - 1] != '/') {+				struct archive_string s;+				archive_string_init(&s);+				archive_strcat(&s, cp);+				archive_strappend_char(&s, '/');+				archive_entry_set_pathname(entry, s.s);+				archive_string_free(&s);+			}+		}+	}++	if (zip_entry->flags & LA_FROM_CENTRAL_DIRECTORY) {+		/* If this came from the central dir, its size info+		 * is definitive, so ignore the length-at-end flag. */+		zip_entry->zip_flags &= ~ZIP_LENGTH_AT_END;+		/* If local header is missing a value, use the one from+		   the central directory.  If both have it, warn about+		   mismatches. */+		if (zip_entry->crc32 == 0) {+			zip_entry->crc32 = zip_entry_central_dir.crc32;+		} else if (!zip->ignore_crc32+		    && zip_entry->crc32 != zip_entry_central_dir.crc32) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Inconsistent CRC32 values");+			ret = ARCHIVE_WARN;+		}+		if (zip_entry->compressed_size == 0) {+			zip_entry->compressed_size+			    = zip_entry_central_dir.compressed_size;+		} else if (zip_entry->compressed_size+		    != zip_entry_central_dir.compressed_size) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Inconsistent compressed size: "+			    "%jd in central directory, %jd in local header",+			    (intmax_t)zip_entry_central_dir.compressed_size,+			    (intmax_t)zip_entry->compressed_size);+			ret = ARCHIVE_WARN;+		}+		if (zip_entry->uncompressed_size == 0) {+			zip_entry->uncompressed_size+			    = zip_entry_central_dir.uncompressed_size;+		} else if (zip_entry->uncompressed_size+		    != zip_entry_central_dir.uncompressed_size) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Inconsistent uncompressed size: "+			    "%jd in central directory, %jd in local header",+			    (intmax_t)zip_entry_central_dir.uncompressed_size,+			    (intmax_t)zip_entry->uncompressed_size);+			ret = ARCHIVE_WARN;+		}+	}++	/* Populate some additional entry fields: */+	archive_entry_set_mode(entry, zip_entry->mode);+	archive_entry_set_uid(entry, zip_entry->uid);+	archive_entry_set_gid(entry, zip_entry->gid);+	archive_entry_set_mtime(entry, zip_entry->mtime, 0);+	archive_entry_set_ctime(entry, zip_entry->ctime, 0);+	archive_entry_set_atime(entry, zip_entry->atime, 0);++	if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) {+		size_t linkname_length;++		if (zip_entry->compressed_size > 64 * 1024) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Zip file with oversized link entry");+			return ARCHIVE_FATAL;+		}++		linkname_length = (size_t)zip_entry->compressed_size;++		archive_entry_set_size(entry, 0);+		p = __archive_read_ahead(a, linkname_length, NULL);+		if (p == NULL) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Truncated Zip file");+			return ARCHIVE_FATAL;+		}++		sconv = zip->sconv;+		if (sconv == NULL && (zip->entry->zip_flags & ZIP_UTF8_NAME))+			sconv = zip->sconv_utf8;+		if (sconv == NULL)+			sconv = zip->sconv_default;+		if (archive_entry_copy_symlink_l(entry, p, linkname_length,+		    sconv) != 0) {+			if (errno != ENOMEM && sconv == zip->sconv_utf8 &&+			    (zip->entry->zip_flags & ZIP_UTF8_NAME))+			    archive_entry_copy_symlink_l(entry, p,+				linkname_length, NULL);+			if (errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for Symlink");+				return (ARCHIVE_FATAL);+			}+			/*+			 * Since there is no character-set regulation for+			 * symlink name, do not report the conversion error+			 * in an automatic conversion.+			 */+			if (sconv != zip->sconv_utf8 ||+			    (zip->entry->zip_flags & ZIP_UTF8_NAME) == 0) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Symlink cannot be converted "+				    "from %s to current locale.",+				    archive_string_conversion_charset_name(+					sconv));+				ret = ARCHIVE_WARN;+			}+		}+		zip_entry->uncompressed_size = zip_entry->compressed_size = 0;++		if (__archive_read_consume(a, linkname_length) < 0) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Read error skipping symlink target name");+			return ARCHIVE_FATAL;+		}+	} else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)+	    || zip_entry->uncompressed_size > 0) {+		/* Set the size only if it's meaningful. */+		archive_entry_set_size(entry, zip_entry->uncompressed_size);+	}+	zip->entry_bytes_remaining = zip_entry->compressed_size;++	/* If there's no body, force read_data() to return EOF immediately. */+	if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)+	    && zip->entry_bytes_remaining < 1)+		zip->end_of_entry = 1;++	/* Set up a more descriptive format name. */+        archive_string_empty(&zip->format_name);+	archive_string_sprintf(&zip->format_name, "ZIP %d.%d (%s)",+	    version / 10, version % 10,+	    compression_name(zip->entry->compression));+	a->archive.archive_format_name = zip->format_name.s;++	return (ret);+}++static int+check_authentication_code(struct archive_read *a, const void *_p)+{+	struct zip *zip = (struct zip *)(a->format->data);++	/* Check authentication code. */+	if (zip->hctx_valid) {+		const void *p;+		uint8_t hmac[20];+		size_t hmac_len = 20;+		int cmp;++		archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len);+		if (_p == NULL) {+			/* Read authentication code. */+			p = __archive_read_ahead(a, AUTH_CODE_SIZE, NULL);+			if (p == NULL) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Truncated ZIP file data");+				return (ARCHIVE_FATAL);+			}+		} else {+			p = _p;+		}+		cmp = memcmp(hmac, p, AUTH_CODE_SIZE);+		__archive_read_consume(a, AUTH_CODE_SIZE);+		if (cmp != 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "ZIP bad Authentication code");+			return (ARCHIVE_WARN);+		}+	}+	return (ARCHIVE_OK);+}++/*+ * Read "uncompressed" data.  There are three cases:+ *  1) We know the size of the data.  This is always true for the+ * seeking reader (we've examined the Central Directory already).+ *  2) ZIP_LENGTH_AT_END was set, but only the CRC was deferred.+ * Info-ZIP seems to do this; we know the size but have to grab+ * the CRC from the data descriptor afterwards.+ *  3) We're streaming and ZIP_LENGTH_AT_END was specified and+ * we have no size information.  In this case, we can do pretty+ * well by watching for the data descriptor record.  The data+ * descriptor is 16 bytes and includes a computed CRC that should+ * provide a strong check.+ *+ * TODO: Technically, the PK\007\010 signature is optional.+ * In the original spec, the data descriptor contained CRC+ * and size fields but had no leading signature.  In practice,+ * newer writers seem to provide the signature pretty consistently.+ *+ * For uncompressed data, the PK\007\010 marker seems essential+ * to be sure we've actually seen the end of the entry.+ *+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets+ * zip->end_of_entry if it consumes all of the data.+ */+static int+zip_read_data_none(struct archive_read *a, const void **_buff,+    size_t *size, int64_t *offset)+{+	struct zip *zip;+	const char *buff;+	ssize_t bytes_avail;+	int r;++	(void)offset; /* UNUSED */++	zip = (struct zip *)(a->format->data);++	if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) {+		const char *p;+		ssize_t grabbing_bytes = 24;++		if (zip->hctx_valid)+			grabbing_bytes += AUTH_CODE_SIZE;+		/* Grab at least 24 bytes. */+		buff = __archive_read_ahead(a, grabbing_bytes, &bytes_avail);+		if (bytes_avail < grabbing_bytes) {+			/* Zip archives have end-of-archive markers+			   that are longer than this, so a failure to get at+			   least 24 bytes really does indicate a truncated+			   file. */+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated ZIP file data");+			return (ARCHIVE_FATAL);+		}+		/* Check for a complete PK\007\010 signature, followed+		 * by the correct 4-byte CRC. */+		p = buff;+		if (zip->hctx_valid)+			p += AUTH_CODE_SIZE;+		if (p[0] == 'P' && p[1] == 'K'+		    && p[2] == '\007' && p[3] == '\010'+		    && (archive_le32dec(p + 4) == zip->entry_crc32+			|| zip->ignore_crc32+			|| (zip->hctx_valid+			 && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) {+			if (zip->entry->flags & LA_USED_ZIP64) {+				uint64_t compressed, uncompressed;+				zip->entry->crc32 = archive_le32dec(p + 4);+				compressed = archive_le64dec(p + 8);+				uncompressed = archive_le64dec(p + 16);+				if (compressed > INT64_MAX || uncompressed >+				    INT64_MAX) {+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_FILE_FORMAT,+					    "Overflow of 64-bit file sizes");+					return ARCHIVE_FAILED;+				}+				zip->entry->compressed_size = compressed;+				zip->entry->uncompressed_size = uncompressed;+				zip->unconsumed = 24;+			} else {+				zip->entry->crc32 = archive_le32dec(p + 4);+				zip->entry->compressed_size =+					archive_le32dec(p + 8);+				zip->entry->uncompressed_size =+					archive_le32dec(p + 12);+				zip->unconsumed = 16;+			}+			if (zip->hctx_valid) {+				r = check_authentication_code(a, buff);+				if (r != ARCHIVE_OK)+					return (r);+			}+			zip->end_of_entry = 1;+			return (ARCHIVE_OK);+		}+		/* If not at EOF, ensure we consume at least one byte. */+		++p;++		/* Scan forward until we see where a PK\007\010 signature+		 * might be. */+		/* Return bytes up until that point.  On the next call,+		 * the code above will verify the data descriptor. */+		while (p < buff + bytes_avail - 4) {+			if (p[3] == 'P') { p += 3; }+			else if (p[3] == 'K') { p += 2; }+			else if (p[3] == '\007') { p += 1; }+			else if (p[3] == '\010' && p[2] == '\007'+			    && p[1] == 'K' && p[0] == 'P') {+				if (zip->hctx_valid)+					p -= AUTH_CODE_SIZE;+				break;+			} else { p += 4; }+		}+		bytes_avail = p - buff;+	} else {+		if (zip->entry_bytes_remaining == 0) {+			zip->end_of_entry = 1;+			if (zip->hctx_valid) {+				r = check_authentication_code(a, NULL);+				if (r != ARCHIVE_OK)+					return (r);+			}+			return (ARCHIVE_OK);+		}+		/* Grab a bunch of bytes. */+		buff = __archive_read_ahead(a, 1, &bytes_avail);+		if (bytes_avail <= 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated ZIP file data");+			return (ARCHIVE_FATAL);+		}+		if (bytes_avail > zip->entry_bytes_remaining)+			bytes_avail = (ssize_t)zip->entry_bytes_remaining;+	}+	if (zip->tctx_valid || zip->cctx_valid) {+		size_t dec_size = bytes_avail;++		if (dec_size > zip->decrypted_buffer_size)+			dec_size = zip->decrypted_buffer_size;+		if (zip->tctx_valid) {+			trad_enc_decrypt_update(&zip->tctx,+			    (const uint8_t *)buff, dec_size,+			    zip->decrypted_buffer, dec_size);+		} else {+			size_t dsize = dec_size;+			archive_hmac_sha1_update(&zip->hctx,+			    (const uint8_t *)buff, dec_size);+			archive_decrypto_aes_ctr_update(&zip->cctx,+			    (const uint8_t *)buff, dec_size,+			    zip->decrypted_buffer, &dsize);+		}+		bytes_avail = dec_size;+		buff = (const char *)zip->decrypted_buffer;+	}+	*size = bytes_avail;+	zip->entry_bytes_remaining -= bytes_avail;+	zip->entry_uncompressed_bytes_read += bytes_avail;+	zip->entry_compressed_bytes_read += bytes_avail;+	zip->unconsumed += bytes_avail;+	*_buff = buff;+	return (ARCHIVE_OK);+}++static int+consume_optional_marker(struct archive_read *a, struct zip *zip)+{+	if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {+		const char *p;++		if (NULL == (p = __archive_read_ahead(a, 24, NULL))) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated ZIP end-of-file record");+			return (ARCHIVE_FATAL);+		}+		/* Consume the optional PK\007\010 marker. */+		if (p[0] == 'P' && p[1] == 'K' &&+		    p[2] == '\007' && p[3] == '\010') {+			p += 4;+			zip->unconsumed = 4;+		}+		if (zip->entry->flags & LA_USED_ZIP64) {+			uint64_t compressed, uncompressed;+			zip->entry->crc32 = archive_le32dec(p);+			compressed = archive_le64dec(p + 4);+			uncompressed = archive_le64dec(p + 12);+			if (compressed > INT64_MAX ||+			    uncompressed > INT64_MAX) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Overflow of 64-bit file sizes");+				return ARCHIVE_FAILED;+			}+			zip->entry->compressed_size = compressed;+			zip->entry->uncompressed_size = uncompressed;+			zip->unconsumed += 20;+		} else {+			zip->entry->crc32 = archive_le32dec(p);+			zip->entry->compressed_size = archive_le32dec(p + 4);+			zip->entry->uncompressed_size = archive_le32dec(p + 8);+			zip->unconsumed += 12;+		}+	}++    return (ARCHIVE_OK);+}++#if HAVE_LZMA_H && HAVE_LIBLZMA+static int+zipx_xz_init(struct archive_read *a, struct zip *zip)+{+	lzma_ret r;++	if(zip->zipx_lzma_valid) {+		lzma_end(&zip->zipx_lzma_stream);+		zip->zipx_lzma_valid = 0;+	}++	memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream));+	r = lzma_stream_decoder(&zip->zipx_lzma_stream, UINT64_MAX, 0);+	if (r != LZMA_OK) {+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,+		    "xz initialization failed(%d)",+		    r);++		return (ARCHIVE_FAILED);+	}++	zip->zipx_lzma_valid = 1;++	free(zip->uncompressed_buffer);++	zip->uncompressed_buffer_size = 256 * 1024;+	zip->uncompressed_buffer =+	    (uint8_t*) malloc(zip->uncompressed_buffer_size);+	if (zip->uncompressed_buffer == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "No memory for xz decompression");+		    return (ARCHIVE_FATAL);+	}++	zip->decompress_init = 1;+	return (ARCHIVE_OK);+}++static int+zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)+{+	lzma_ret r;+	const uint8_t* p;++#pragma pack(push)+#pragma pack(1)+	struct _alone_header {+	    uint8_t bytes[5];+	    uint64_t uncompressed_size;+	} alone_header;+#pragma pack(pop)++	if(zip->zipx_lzma_valid) {+		lzma_end(&zip->zipx_lzma_stream);+		zip->zipx_lzma_valid = 0;+	}++	/* To unpack ZIPX's "LZMA" (id 14) stream we can use standard liblzma+	 * that is a part of XZ Utils. The stream format stored inside ZIPX+	 * file is a modified "lzma alone" file format, that was used by the+	 * `lzma` utility which was later deprecated in favour of `xz` utility. 	 * Since those formats are nearly the same, we can use a standard+	 * "lzma alone" decoder from XZ Utils. */++	memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream));+	r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX);+	if (r != LZMA_OK) {+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,+		    "lzma initialization failed(%d)", r);++		return (ARCHIVE_FAILED);+	}++	/* Flag the cleanup function that we want our lzma-related structures+	 * to be freed later. */+	zip->zipx_lzma_valid = 1;++	/* The "lzma alone" file format and the stream format inside ZIPx are+	 * almost the same. Here's an example of a structure of "lzma alone"+	 * format:+	 *+	 * $ cat /bin/ls | lzma | xxd | head -n 1+	 * 00000000: 5d00 0080 00ff ffff ffff ffff ff00 2814+	 *+	 *    5 bytes        8 bytes        n bytes+	 * <lzma_params><uncompressed_size><data...>+	 *+	 * lzma_params is a 5-byte blob that has to be decoded to extract+	 * parameters of this LZMA stream. The uncompressed_size field is an+	 * uint64_t value that contains information about the size of the+	 * uncompressed file, or UINT64_MAX if this value is unknown.+	 * The <data...> part is the actual lzma-compressed data stream.+	 *+	 * Now here's the structure of the stream inside the ZIPX file:+	 *+	 * $ cat stream_inside_zipx | xxd | head -n 1+	 * 00000000: 0914 0500 5d00 8000 0000 2814 .... ....+	 *+	 *  2byte   2byte    5 bytes     n bytes+	 * <magic1><magic2><lzma_params><data...>+	 *+	 * This means that the ZIPX file contains an additional magic1 and+	 * magic2 headers, the lzma_params field contains the same parameter+	 * set as in the "lzma alone" format, and the <data...> field is the+	 * same as in the "lzma alone" format as well. Note that also the zipx+	 * format is missing the uncompressed_size field.+	 *+	 * So, in order to use the "lzma alone" decoder for the zipx lzma+	 * stream, we simply need to shuffle around some fields, prepare a new+	 * lzma alone header, feed it into lzma alone decoder so it will+	 * initialize itself properly, and then we can start feeding normal+	 * zipx lzma stream into the decoder.+	 */++	/* Read magic1,magic2,lzma_params from the ZIPX stream. */+	if((p = __archive_read_ahead(a, 9, NULL)) == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated lzma data");+		return (ARCHIVE_FATAL);+	}++	if(p[2] != 0x05 || p[3] != 0x00) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Invalid lzma data");+		return (ARCHIVE_FATAL);+	}++	/* Prepare an lzma alone header: copy the lzma_params blob into+	 * a proper place into the lzma alone header. */+	memcpy(&alone_header.bytes[0], p + 4, 5);++	/* Initialize the 'uncompressed size' field to unknown; we'll manually+	 * monitor how many bytes there are still to be uncompressed. */+	alone_header.uncompressed_size = UINT64_MAX;++	if(!zip->uncompressed_buffer) {+		zip->uncompressed_buffer_size = 256 * 1024;+		zip->uncompressed_buffer =+			(uint8_t*) malloc(zip->uncompressed_buffer_size);++		if (zip->uncompressed_buffer == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "No memory for lzma decompression");+			return (ARCHIVE_FATAL);+		}+	}++	zip->zipx_lzma_stream.next_in = (void*) &alone_header;+	zip->zipx_lzma_stream.avail_in = sizeof(alone_header);+	zip->zipx_lzma_stream.total_in = 0;+	zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;+	zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;+	zip->zipx_lzma_stream.total_out = 0;++	/* Feed only the header into the lzma alone decoder. This will+	 * effectively initialize the decoder, and will not produce any+	 * output bytes yet. */+	r = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);+	if (r != LZMA_OK) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+		    "lzma stream initialization error");+		return ARCHIVE_FATAL;+	}++	/* We've already consumed some bytes, so take this into account. */+	__archive_read_consume(a, 9);+	zip->entry_bytes_remaining -= 9;+	zip->entry_compressed_bytes_read += 9;++	zip->decompress_init = 1;+	return (ARCHIVE_OK);+}++static int+zip_read_data_zipx_xz(struct archive_read *a, const void **buff,+	size_t *size, int64_t *offset)+{+	struct zip* zip = (struct zip *)(a->format->data);+	int ret;+	lzma_ret lz_ret;+	const void* compressed_buf;+	ssize_t bytes_avail, in_bytes, to_consume = 0;++	(void) offset; /* UNUSED */++	/* Initialize decompressor if not yet initialized. */+	if (!zip->decompress_init) {+		ret = zipx_xz_init(a, zip);+		if (ret != ARCHIVE_OK)+			return (ret);+	}++	compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);+	if (bytes_avail < 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated xz file body");+		return (ARCHIVE_FATAL);+	}++	in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);+	zip->zipx_lzma_stream.next_in = compressed_buf;+	zip->zipx_lzma_stream.avail_in = in_bytes;+	zip->zipx_lzma_stream.total_in = 0;+	zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;+	zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;+	zip->zipx_lzma_stream.total_out = 0;++	/* Perform the decompression. */+	lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);+	switch(lz_ret) {+		case LZMA_DATA_ERROR:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "xz data error (error %d)", (int) lz_ret);+			return (ARCHIVE_FATAL);++		case LZMA_NO_CHECK:+		case LZMA_OK:+			break;++		default:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "xz unknown error %d", (int) lz_ret);+			return (ARCHIVE_FATAL);++		case LZMA_STREAM_END:+			lzma_end(&zip->zipx_lzma_stream);+			zip->zipx_lzma_valid = 0;++			if((int64_t) zip->zipx_lzma_stream.total_in !=+			    zip->entry_bytes_remaining)+			{+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "xz premature end of stream");+				return (ARCHIVE_FATAL);+			}++			zip->end_of_entry = 1;+			break;+	}++	to_consume = zip->zipx_lzma_stream.total_in;++	__archive_read_consume(a, to_consume);+	zip->entry_bytes_remaining -= to_consume;+	zip->entry_compressed_bytes_read += to_consume;+	zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out;++	*size = zip->zipx_lzma_stream.total_out;+	*buff = zip->uncompressed_buffer;++	ret = consume_optional_marker(a, zip);+	if (ret != ARCHIVE_OK)+		return (ret);++	return (ARCHIVE_OK);+}++static int+zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,+    size_t *size, int64_t *offset)+{+	struct zip* zip = (struct zip *)(a->format->data);+	int ret;+	lzma_ret lz_ret;+	const void* compressed_buf;+	ssize_t bytes_avail, in_bytes, to_consume;++	(void) offset; /* UNUSED */++	/* Initialize decompressor if not yet initialized. */+	if (!zip->decompress_init) {+		ret = zipx_lzma_alone_init(a, zip);+		if (ret != ARCHIVE_OK)+			return (ret);+	}++	/* Fetch more compressed data. The same note as in deflate handler+	 * applies here as well:+	 *+	 * Note: '1' here is a performance optimization. Recall that the+	 * decompression layer returns a count of available bytes; asking for+	 * more than that forces the decompressor to combine reads by copying+	 * data.+	 */+	compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);+	if (bytes_avail < 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated lzma file body");+		return (ARCHIVE_FATAL);+	}++	/* Set decompressor parameters. */+	in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);++	zip->zipx_lzma_stream.next_in = compressed_buf;+	zip->zipx_lzma_stream.avail_in = in_bytes;+	zip->zipx_lzma_stream.total_in = 0;+	zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;+	zip->zipx_lzma_stream.avail_out =+		/* These lzma_alone streams lack end of stream marker, so let's+		 * make sure the unpacker won't try to unpack more than it's+		 * supposed to. */+		zipmin((int64_t) zip->uncompressed_buffer_size,+		    zip->entry->uncompressed_size -+		    zip->entry_uncompressed_bytes_read);+	zip->zipx_lzma_stream.total_out = 0;++	/* Perform the decompression. */+	lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);+	switch(lz_ret) {+		case LZMA_DATA_ERROR:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "lzma data error (error %d)", (int) lz_ret);+			return (ARCHIVE_FATAL);++		/* This case is optional in lzma alone format. It can happen,+		 * but most of the files don't have it. (GitHub #1257) */+		case LZMA_STREAM_END:+			lzma_end(&zip->zipx_lzma_stream);+			zip->zipx_lzma_valid = 0;+			if((int64_t) zip->zipx_lzma_stream.total_in !=+			    zip->entry_bytes_remaining)+			{+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "lzma alone premature end of stream");+				return (ARCHIVE_FATAL);+			}++			zip->end_of_entry = 1;+			break;++		case LZMA_OK:+			break;++		default:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "lzma unknown error %d", (int) lz_ret);+			return (ARCHIVE_FATAL);+	}++	to_consume = zip->zipx_lzma_stream.total_in;++	/* Update pointers. */+	__archive_read_consume(a, to_consume);+	zip->entry_bytes_remaining -= to_consume;+	zip->entry_compressed_bytes_read += to_consume;+	zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out;++	if(zip->entry_bytes_remaining == 0) {+		zip->end_of_entry = 1;+	}++	/* Return values. */+	*size = zip->zipx_lzma_stream.total_out;+	*buff = zip->uncompressed_buffer;++	/* Behave the same way as during deflate decompression. */+	ret = consume_optional_marker(a, zip);+	if (ret != ARCHIVE_OK)+		return (ret);++	/* Free lzma decoder handle because we'll no longer need it. */+	if(zip->end_of_entry) {+		lzma_end(&zip->zipx_lzma_stream);+		zip->zipx_lzma_valid = 0;+	}++	/* If we're here, then we're good! */+	return (ARCHIVE_OK);+}+#endif /* HAVE_LZMA_H && HAVE_LIBLZMA */++static int+zipx_ppmd8_init(struct archive_read *a, struct zip *zip)+{+	const void* p;+	uint32_t val;+	uint32_t order;+	uint32_t mem;+	uint32_t restore_method;++	/* Remove previous decompression context if it exists. */+	if(zip->ppmd8_valid) {+		__archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);+		zip->ppmd8_valid = 0;+	}++	/* Create a new decompression context. */+	__archive_ppmd8_functions.Ppmd8_Construct(&zip->ppmd8);+	zip->ppmd8_stream_failed = 0;++	/* Setup function pointers required by Ppmd8 decompressor. The+	 * 'ppmd_read' function will feed new bytes to the decompressor,+	 * and will increment the 'zip->zipx_ppmd_read_compressed' counter. */+	zip->ppmd8.Stream.In = &zip->zipx_ppmd_stream;+	zip->zipx_ppmd_stream.a = a;+	zip->zipx_ppmd_stream.Read = &ppmd_read;++	/* Reset number of read bytes to 0. */+	zip->zipx_ppmd_read_compressed = 0;++	/* Read Ppmd8 header (2 bytes). */+	p = __archive_read_ahead(a, 2, NULL);+	if(!p) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated file data in PPMd8 stream");+		return (ARCHIVE_FATAL);+	}+	__archive_read_consume(a, 2);++	/* Decode the stream's compression parameters. */+	val = archive_le16dec(p);+	order = (val & 15) + 1;+	mem = ((val >> 4) & 0xff) + 1;+	restore_method = (val >> 12);++	if(order < 2 || restore_method > 2) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Invalid parameter set in PPMd8 stream (order=%d, "+		    "restore=%d)", order, restore_method);+		return (ARCHIVE_FAILED);+	}++	/* Allocate the memory needed to properly decompress the file. */+	if(!__archive_ppmd8_functions.Ppmd8_Alloc(&zip->ppmd8, mem << 20)) {+		archive_set_error(&a->archive, ENOMEM,+		    "Unable to allocate memory for PPMd8 stream: %d bytes",+		    mem << 20);+		return (ARCHIVE_FATAL);+	}++	/* Signal the cleanup function to release Ppmd8 context in the+	 * cleanup phase. */+	zip->ppmd8_valid = 1;++	/* Perform further Ppmd8 initialization. */+	if(!__archive_ppmd8_functions.Ppmd8_RangeDec_Init(&zip->ppmd8)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,+		    "PPMd8 stream range decoder initialization error");+		return (ARCHIVE_FATAL);+	}++	__archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order,+	    restore_method);++	/* Allocate the buffer that will hold uncompressed data. */+	free(zip->uncompressed_buffer);++	zip->uncompressed_buffer_size = 256 * 1024;+	zip->uncompressed_buffer =+	    (uint8_t*) malloc(zip->uncompressed_buffer_size);++	if(zip->uncompressed_buffer == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "No memory for PPMd8 decompression");+		return ARCHIVE_FATAL;+	}++	/* Ppmd8 initialization is done. */+	zip->decompress_init = 1;++	/* We've already read 2 bytes in the output stream. Additionally,+	 * Ppmd8 initialization code could read some data as well. So we+	 * are advancing the stream by 2 bytes plus whatever number of+	 * bytes Ppmd8 init function used. */+	zip->entry_compressed_bytes_read += 2 + zip->zipx_ppmd_read_compressed;++	return ARCHIVE_OK;+}++static int+zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff,+    size_t *size, int64_t *offset)+{+	struct zip* zip = (struct zip *)(a->format->data);+	int ret;+	size_t consumed_bytes = 0;+	ssize_t bytes_avail = 0;++	(void) offset; /* UNUSED */++	/* If we're here for the first time, initialize Ppmd8 decompression+	 * context first. */+	if(!zip->decompress_init) {+		ret = zipx_ppmd8_init(a, zip);+		if(ret != ARCHIVE_OK)+			return ret;+	}++	/* Fetch for more data. We're reading 1 byte here, but libarchive+	 * should prefetch more bytes. */+	(void) __archive_read_ahead(a, 1, &bytes_avail);+	if(bytes_avail < 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated PPMd8 file body");+		return (ARCHIVE_FATAL);+	}++	/* This counter will be updated inside ppmd_read(), which at one+	 * point will be called by Ppmd8_DecodeSymbol. */+	zip->zipx_ppmd_read_compressed = 0;++	/* Decompression loop. */+	do {+		int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol(+		    &zip->ppmd8);+		if(sym < 0) {+			zip->end_of_entry = 1;+			break;+		}++		/* This field is set by ppmd_read() when there was no more data+		 * to be read. */+		if(zip->ppmd8_stream_failed) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated PPMd8 file body");+			return (ARCHIVE_FATAL);+		}++		zip->uncompressed_buffer[consumed_bytes] = (uint8_t) sym;+		++consumed_bytes;+	} while(consumed_bytes < zip->uncompressed_buffer_size);++	/* Update pointers for libarchive. */+	*buff = zip->uncompressed_buffer;+	*size = consumed_bytes;++	/* Update pointers so we can continue decompression in another call. */+	zip->entry_bytes_remaining -= zip->zipx_ppmd_read_compressed;+	zip->entry_compressed_bytes_read += zip->zipx_ppmd_read_compressed;+	zip->entry_uncompressed_bytes_read += consumed_bytes;++	/* If we're at the end of stream, deinitialize Ppmd8 context. */+	if(zip->end_of_entry) {+		__archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);+		zip->ppmd8_valid = 0;+	}++	/* Seek for optional marker, same way as in each zip entry. */+	ret = consume_optional_marker(a, zip);+	if (ret != ARCHIVE_OK)+		return ret;++	return ARCHIVE_OK;+}++#ifdef HAVE_BZLIB_H+static int+zipx_bzip2_init(struct archive_read *a, struct zip *zip)+{+	int r;++	/* Deallocate already existing BZ2 decompression context if it+	 * exists. */+	if(zip->bzstream_valid) {+		BZ2_bzDecompressEnd(&zip->bzstream);+		zip->bzstream_valid = 0;+	}++	/* Allocate a new BZ2 decompression context. */+	memset(&zip->bzstream, 0, sizeof(bz_stream));+	r = BZ2_bzDecompressInit(&zip->bzstream, 0, 1);+	if(r != BZ_OK) {+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,+		    "bzip2 initialization failed(%d)",+		    r);++		return ARCHIVE_FAILED;+	}++	/* Mark the bzstream field to be released in cleanup phase. */+	zip->bzstream_valid = 1;++	/* (Re)allocate the buffer that will contain decompressed bytes. */+	free(zip->uncompressed_buffer);++	zip->uncompressed_buffer_size = 256 * 1024;+	zip->uncompressed_buffer =+	    (uint8_t*) malloc(zip->uncompressed_buffer_size);+	if (zip->uncompressed_buffer == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "No memory for bzip2 decompression");+		    return ARCHIVE_FATAL;+	}++	/* Initialization done. */+	zip->decompress_init = 1;+	return ARCHIVE_OK;+}++static int+zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,+    size_t *size, int64_t *offset)+{+	struct zip *zip = (struct zip *)(a->format->data);+	ssize_t bytes_avail = 0, in_bytes, to_consume;+	const void *compressed_buff;+	int r;+	uint64_t total_out;++	(void) offset; /* UNUSED */++	/* Initialize decompression context if we're here for the first time. */+	if(!zip->decompress_init) {+		r = zipx_bzip2_init(a, zip);+		if(r != ARCHIVE_OK)+			return r;+	}++	/* Fetch more compressed bytes. */+	compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);+	if(bytes_avail < 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated bzip2 file body");+		return (ARCHIVE_FATAL);+	}++	in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);+	if(in_bytes < 1) {+		/* libbz2 doesn't complain when caller feeds avail_in == 0.+		 * It will actually return success in this case, which is+		 * undesirable. This is why we need to make this check+		 * manually. */++		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated bzip2 file body");+		return (ARCHIVE_FATAL);+	}++	/* Setup buffer boundaries. */+	zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff;+	zip->bzstream.avail_in = in_bytes;+	zip->bzstream.total_in_hi32 = 0;+	zip->bzstream.total_in_lo32 = 0;+	zip->bzstream.next_out = (char*) zip->uncompressed_buffer;+	zip->bzstream.avail_out = zip->uncompressed_buffer_size;+	zip->bzstream.total_out_hi32 = 0;+	zip->bzstream.total_out_lo32 = 0;++	/* Perform the decompression. */+	r = BZ2_bzDecompress(&zip->bzstream);+	switch(r) {+		case BZ_STREAM_END:+			/* If we're at the end of the stream, deinitialize the+			 * decompression context now. */+			switch(BZ2_bzDecompressEnd(&zip->bzstream)) {+				case BZ_OK:+					break;+				default:+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_MISC,+					    "Failed to clean up bzip2 "+					    "decompressor");+					return ARCHIVE_FATAL;+			}++			zip->end_of_entry = 1;+			break;+		case BZ_OK:+			/* The decompressor has successfully decoded this+			 * chunk of data, but more data is still in queue. */+			break;+		default:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "bzip2 decompression failed");+			return ARCHIVE_FATAL;+	}++	/* Update the pointers so decompressor can continue decoding. */+	to_consume = zip->bzstream.total_in_lo32;+	__archive_read_consume(a, to_consume);++	total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) ++	    zip->bzstream.total_out_lo32;++	zip->entry_bytes_remaining -= to_consume;+	zip->entry_compressed_bytes_read += to_consume;+	zip->entry_uncompressed_bytes_read += total_out;++	/* Give libarchive its due. */+	*size = total_out;+	*buff = zip->uncompressed_buffer;++	/* Seek for optional marker, like in other entries. */+	r = consume_optional_marker(a, zip);+	if(r != ARCHIVE_OK)+		return r;++	return ARCHIVE_OK;+}++#endif++#ifdef HAVE_ZLIB_H+static int+zip_deflate_init(struct archive_read *a, struct zip *zip)+{+	int r;++	/* If we haven't yet read any data, initialize the decompressor. */+	if (!zip->decompress_init) {+		if (zip->stream_valid)+			r = inflateReset(&zip->stream);+		else+			r = inflateInit2(&zip->stream,+			    -15 /* Don't check for zlib header */);+		if (r != Z_OK) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Can't initialize ZIP decompression.");+			return (ARCHIVE_FATAL);+		}+		/* Stream structure has been set up. */+		zip->stream_valid = 1;+		/* We've initialized decompression for this stream. */+		zip->decompress_init = 1;+	}+	return (ARCHIVE_OK);+}++static int+zip_read_data_deflate(struct archive_read *a, const void **buff,+    size_t *size, int64_t *offset)+{+	struct zip *zip;+	ssize_t bytes_avail;+	const void *compressed_buff, *sp;+	int r;++	(void)offset; /* UNUSED */++	zip = (struct zip *)(a->format->data);++	/* If the buffer hasn't been allocated, allocate it now. */+	if (zip->uncompressed_buffer == NULL) {+		zip->uncompressed_buffer_size = 256 * 1024;+		zip->uncompressed_buffer+		    = (unsigned char *)malloc(zip->uncompressed_buffer_size);+		if (zip->uncompressed_buffer == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "No memory for ZIP decompression");+			return (ARCHIVE_FATAL);+		}+	}++	r = zip_deflate_init(a, zip);+	if (r != ARCHIVE_OK)+		return (r);++	/*+	 * Note: '1' here is a performance optimization.+	 * Recall that the decompression layer returns a count of+	 * available bytes; asking for more than that forces the+	 * decompressor to combine reads by copying data.+	 */+	compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail);+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)+	    && bytes_avail > zip->entry_bytes_remaining) {+		bytes_avail = (ssize_t)zip->entry_bytes_remaining;+	}+	if (bytes_avail < 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated ZIP file body");+		return (ARCHIVE_FATAL);+	}++	if (zip->tctx_valid || zip->cctx_valid) {+		if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) {+			size_t buff_remaining =+			    (zip->decrypted_buffer ++			    zip->decrypted_buffer_size)+			    - (zip->decrypted_ptr ++			    zip->decrypted_bytes_remaining);++			if (buff_remaining > (size_t)bytes_avail)+				buff_remaining = (size_t)bytes_avail;++			if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) &&+			      zip->entry_bytes_remaining > 0) {+				if ((int64_t)(zip->decrypted_bytes_remaining+				    + buff_remaining)+				      > zip->entry_bytes_remaining) {+					if (zip->entry_bytes_remaining <+					    (int64_t)zip->decrypted_bytes_remaining)+						buff_remaining = 0;+					else+						buff_remaining =+						    (size_t)zip->entry_bytes_remaining+						    - zip->decrypted_bytes_remaining;+				}+			}+			if (buff_remaining > 0) {+				if (zip->tctx_valid) {+					trad_enc_decrypt_update(&zip->tctx,+					    compressed_buff, buff_remaining,+					    zip->decrypted_ptr+					      + zip->decrypted_bytes_remaining,+					    buff_remaining);+				} else {+					size_t dsize = buff_remaining;+					archive_decrypto_aes_ctr_update(+					    &zip->cctx,+					    compressed_buff, buff_remaining,+					    zip->decrypted_ptr+					      + zip->decrypted_bytes_remaining,+					    &dsize);+				}+				zip->decrypted_bytes_remaining +=+				    buff_remaining;+			}+		}+		bytes_avail = zip->decrypted_bytes_remaining;+		compressed_buff = (const char *)zip->decrypted_ptr;+	}++	/*+	 * A bug in zlib.h: stream.next_in should be marked 'const'+	 * but isn't (the library never alters data through the+	 * next_in pointer, only reads it).  The result: this ugly+	 * cast to remove 'const'.+	 */+	zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)compressed_buff;+	zip->stream.avail_in = (uInt)bytes_avail;+	zip->stream.total_in = 0;+	zip->stream.next_out = zip->uncompressed_buffer;+	zip->stream.avail_out = (uInt)zip->uncompressed_buffer_size;+	zip->stream.total_out = 0;++	r = inflate(&zip->stream, 0);+	switch (r) {+	case Z_OK:+		break;+	case Z_STREAM_END:+		zip->end_of_entry = 1;+		break;+	case Z_MEM_ERROR:+		archive_set_error(&a->archive, ENOMEM,+		    "Out of memory for ZIP decompression");+		return (ARCHIVE_FATAL);+	default:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "ZIP decompression failed (%d)", r);+		return (ARCHIVE_FATAL);+	}++	/* Consume as much as the compressor actually used. */+	bytes_avail = zip->stream.total_in;+	if (zip->tctx_valid || zip->cctx_valid) {+		zip->decrypted_bytes_remaining -= bytes_avail;+		if (zip->decrypted_bytes_remaining == 0)+			zip->decrypted_ptr = zip->decrypted_buffer;+		else+			zip->decrypted_ptr += bytes_avail;+	}+	/* Calculate compressed data as much as we used.*/+	if (zip->hctx_valid)+		archive_hmac_sha1_update(&zip->hctx, sp, bytes_avail);+	__archive_read_consume(a, bytes_avail);+	zip->entry_bytes_remaining -= bytes_avail;+	zip->entry_compressed_bytes_read += bytes_avail;++	*size = zip->stream.total_out;+	zip->entry_uncompressed_bytes_read += zip->stream.total_out;+	*buff = zip->uncompressed_buffer;++	if (zip->end_of_entry && zip->hctx_valid) {+		r = check_authentication_code(a, NULL);+		if (r != ARCHIVE_OK)+			return (r);+	}++	r = consume_optional_marker(a, zip);+	if (r != ARCHIVE_OK)+		return (r);++	return (ARCHIVE_OK);+}+#endif++static int+read_decryption_header(struct archive_read *a)+{+	struct zip *zip = (struct zip *)(a->format->data);+	const char *p;+	unsigned int remaining_size;+	unsigned int ts;++	/*+	 * Read an initialization vector data field.+	 */+	p = __archive_read_ahead(a, 2, NULL);+	if (p == NULL)+		goto truncated;+	ts = zip->iv_size;+	zip->iv_size = archive_le16dec(p);+	__archive_read_consume(a, 2);+	if (ts < zip->iv_size) {+		free(zip->iv);+		zip->iv = NULL;+	}+	p = __archive_read_ahead(a, zip->iv_size, NULL);+	if (p == NULL)+		goto truncated;+	if (zip->iv == NULL) {+		zip->iv = malloc(zip->iv_size);+		if (zip->iv == NULL)+			goto nomem;+	}+	memcpy(zip->iv, p, zip->iv_size);+	__archive_read_consume(a, zip->iv_size);++	/*+	 * Read a size of remaining decryption header field.+	 */+	p = __archive_read_ahead(a, 14, NULL);+	if (p == NULL)+		goto truncated;+	remaining_size = archive_le32dec(p);+	if (remaining_size < 16 || remaining_size > (1 << 18))+		goto corrupted;++	/* Check if format version is supported. */+	if (archive_le16dec(p+4) != 3) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unsupported encryption format version: %u",+		    archive_le16dec(p+4));+		return (ARCHIVE_FAILED);+	}++	/*+	 * Read an encryption algorithm field.+	 */+	zip->alg_id = archive_le16dec(p+6);+	switch (zip->alg_id) {+	case 0x6601:/* DES */+	case 0x6602:/* RC2 */+	case 0x6603:/* 3DES 168 */+	case 0x6609:/* 3DES 112 */+	case 0x660E:/* AES 128 */+	case 0x660F:/* AES 192 */+	case 0x6610:/* AES 256 */+	case 0x6702:/* RC2 (version >= 5.2) */+	case 0x6720:/* Blowfish */+	case 0x6721:/* Twofish */+	case 0x6801:/* RC4 */+		/* Supported encryption algorithm. */+		break;+	default:+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unknown encryption algorithm: %u", zip->alg_id);+		return (ARCHIVE_FAILED);+	}++	/*+	 * Read a bit length field.+	 */+	zip->bit_len = archive_le16dec(p+8);++	/*+	 * Read a flags field.+	 */+	zip->flags = archive_le16dec(p+10);+	switch (zip->flags & 0xf000) {+	case 0x0001: /* Password is required to decrypt. */+	case 0x0002: /* Certificates only. */+	case 0x0003: /* Password or certificate required to decrypt. */+		break;+	default:+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unknown encryption flag: %u", zip->flags);+		return (ARCHIVE_FAILED);+	}+	if ((zip->flags & 0xf000) == 0 ||+	    (zip->flags & 0xf000) == 0x4000) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unknown encryption flag: %u", zip->flags);+		return (ARCHIVE_FAILED);+	}++	/*+	 * Read an encrypted random data field.+	 */+	ts = zip->erd_size;+	zip->erd_size = archive_le16dec(p+12);+	__archive_read_consume(a, 14);+	if ((zip->erd_size & 0xf) != 0 ||+	    (zip->erd_size + 16) > remaining_size ||+	    (zip->erd_size + 16) < zip->erd_size)+		goto corrupted;++	if (ts < zip->erd_size) {+		free(zip->erd);+		zip->erd = NULL;+	}+	p = __archive_read_ahead(a, zip->erd_size, NULL);+	if (p == NULL)+		goto truncated;+	if (zip->erd == NULL) {+		zip->erd = malloc(zip->erd_size);+		if (zip->erd == NULL)+			goto nomem;+	}+	memcpy(zip->erd, p, zip->erd_size);+	__archive_read_consume(a, zip->erd_size);++	/*+	 * Read a reserved data field.+	 */+	p = __archive_read_ahead(a, 4, NULL);+	if (p == NULL)+		goto truncated;+	/* Reserved data size should be zero. */+	if (archive_le32dec(p) != 0)+		goto corrupted;+	__archive_read_consume(a, 4);++	/*+	 * Read a password validation data field.+	 */+	p = __archive_read_ahead(a, 2, NULL);+	if (p == NULL)+		goto truncated;+	ts = zip->v_size;+	zip->v_size = archive_le16dec(p);+	__archive_read_consume(a, 2);+	if ((zip->v_size & 0x0f) != 0 ||+	    (zip->erd_size + zip->v_size + 16) > remaining_size ||+	    (zip->erd_size + zip->v_size + 16) < (zip->erd_size + zip->v_size))+		goto corrupted;+	if (ts < zip->v_size) {+		free(zip->v_data);+		zip->v_data = NULL;+	}+	p = __archive_read_ahead(a, zip->v_size, NULL);+	if (p == NULL)+		goto truncated;+	if (zip->v_data == NULL) {+		zip->v_data = malloc(zip->v_size);+		if (zip->v_data == NULL)+			goto nomem;+	}+	memcpy(zip->v_data, p, zip->v_size);+	__archive_read_consume(a, zip->v_size);++	p = __archive_read_ahead(a, 4, NULL);+	if (p == NULL)+		goto truncated;+	zip->v_crc32 = archive_le32dec(p);+	__archive_read_consume(a, 4);++	/*return (ARCHIVE_OK);+	 * This is not fully implemented yet.*/+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Encrypted file is unsupported");+	return (ARCHIVE_FAILED);+truncated:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Truncated ZIP file data");+	return (ARCHIVE_FATAL);+corrupted:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Corrupted ZIP file data");+	return (ARCHIVE_FATAL);+nomem:+	archive_set_error(&a->archive, ENOMEM,+	    "No memory for ZIP decryption");+	return (ARCHIVE_FATAL);+}++static int+zip_alloc_decryption_buffer(struct archive_read *a)+{+	struct zip *zip = (struct zip *)(a->format->data);+	size_t bs = 256 * 1024;++	if (zip->decrypted_buffer == NULL) {+		zip->decrypted_buffer_size = bs;+		zip->decrypted_buffer = malloc(bs);+		if (zip->decrypted_buffer == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "No memory for ZIP decryption");+			return (ARCHIVE_FATAL);+		}+	}+	zip->decrypted_ptr = zip->decrypted_buffer;+	return (ARCHIVE_OK);+}++static int+init_traditional_PKWARE_decryption(struct archive_read *a)+{+	struct zip *zip = (struct zip *)(a->format->data);+	const void *p;+	int retry;+	int r;++	if (zip->tctx_valid)+		return (ARCHIVE_OK);++	/*+	   Read the 12 bytes encryption header stored at+	   the start of the data area.+	 */+#define ENC_HEADER_SIZE	12+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)+	    && zip->entry_bytes_remaining < ENC_HEADER_SIZE) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated Zip encrypted body: only %jd bytes available",+		    (intmax_t)zip->entry_bytes_remaining);+		return (ARCHIVE_FATAL);+	}++	p = __archive_read_ahead(a, ENC_HEADER_SIZE, NULL);+	if (p == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated ZIP file data");+		return (ARCHIVE_FATAL);+	}++	for (retry = 0;; retry++) {+		const char *passphrase;+		uint8_t crcchk;++		passphrase = __archive_read_next_passphrase(a);+		if (passphrase == NULL) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    (retry > 0)?+				"Incorrect passphrase":+				"Passphrase required for this entry");+			return (ARCHIVE_FAILED);+		}++		/*+		 * Initialize ctx for Traditional PKWARE Decryption.+		 */+		r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase),+			p, ENC_HEADER_SIZE, &crcchk);+		if (r == 0 && crcchk == zip->entry->decdat)+			break;/* The passphrase is OK. */+		if (retry > 10000) {+			/* Avoid infinity loop. */+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Too many incorrect passphrases");+			return (ARCHIVE_FAILED);+		}+	}++	__archive_read_consume(a, ENC_HEADER_SIZE);+	zip->tctx_valid = 1;+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {+	    zip->entry_bytes_remaining -= ENC_HEADER_SIZE;+	}+	/*zip->entry_uncompressed_bytes_read += ENC_HEADER_SIZE;*/+	zip->entry_compressed_bytes_read += ENC_HEADER_SIZE;+	zip->decrypted_bytes_remaining = 0;++	return (zip_alloc_decryption_buffer(a));+#undef ENC_HEADER_SIZE+}++static int+init_WinZip_AES_decryption(struct archive_read *a)+{+	struct zip *zip = (struct zip *)(a->format->data);+	const void *p;+	const uint8_t *pv;+	size_t key_len, salt_len;+	uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];+	int retry;+	int r;++	if (zip->cctx_valid || zip->hctx_valid)+		return (ARCHIVE_OK);++	switch (zip->entry->aes_extra.strength) {+	case 1: salt_len = 8;  key_len = 16; break;+	case 2: salt_len = 12; key_len = 24; break;+	case 3: salt_len = 16; key_len = 32; break;+	default: goto corrupted;+	}+	p = __archive_read_ahead(a, salt_len + 2, NULL);+	if (p == NULL)+		goto truncated;++	for (retry = 0;; retry++) {+		const char *passphrase;++		passphrase = __archive_read_next_passphrase(a);+		if (passphrase == NULL) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    (retry > 0)?+				"Incorrect passphrase":+				"Passphrase required for this entry");+			return (ARCHIVE_FAILED);+		}+		memset(derived_key, 0, sizeof(derived_key));+		r = archive_pbkdf2_sha1(passphrase, strlen(passphrase),+		    p, salt_len, 1000, derived_key, key_len * 2 + 2);+		if (r != 0) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Decryption is unsupported due to lack of "+			    "crypto library");+			return (ARCHIVE_FAILED);+		}++		/* Check password verification value. */+		pv = ((const uint8_t *)p) + salt_len;+		if (derived_key[key_len * 2] == pv[0] &&+		    derived_key[key_len * 2 + 1] == pv[1])+			break;/* The passphrase is OK. */+		if (retry > 10000) {+			/* Avoid infinity loop. */+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Too many incorrect passphrases");+			return (ARCHIVE_FAILED);+		}+	}++	r = archive_decrypto_aes_ctr_init(&zip->cctx, derived_key, key_len);+	if (r != 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Decryption is unsupported due to lack of crypto library");+		return (ARCHIVE_FAILED);+	}+	r = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len);+	if (r != 0) {+		archive_decrypto_aes_ctr_release(&zip->cctx);+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Failed to initialize HMAC-SHA1");+		return (ARCHIVE_FAILED);+	}+	zip->cctx_valid = zip->hctx_valid = 1;+	__archive_read_consume(a, salt_len + 2);+	zip->entry_bytes_remaining -= salt_len + 2 + AUTH_CODE_SIZE;+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)+	    && zip->entry_bytes_remaining < 0)+		goto corrupted;+	zip->entry_compressed_bytes_read += salt_len + 2 + AUTH_CODE_SIZE;+	zip->decrypted_bytes_remaining = 0;++	zip->entry->compression = zip->entry->aes_extra.compression;+	return (zip_alloc_decryption_buffer(a));++truncated:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Truncated ZIP file data");+	return (ARCHIVE_FATAL);+corrupted:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+	    "Corrupted ZIP file data");+	return (ARCHIVE_FATAL);+}++static int+archive_read_format_zip_read_data(struct archive_read *a,+    const void **buff, size_t *size, int64_t *offset)+{+	int r;+	struct zip *zip = (struct zip *)(a->format->data);++	if (zip->has_encrypted_entries ==+			ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {+		zip->has_encrypted_entries = 0;+	}++	*offset = zip->entry_uncompressed_bytes_read;+	*size = 0;+	*buff = NULL;++	/* If we hit end-of-entry last time, return ARCHIVE_EOF. */+	if (zip->end_of_entry)+		return (ARCHIVE_EOF);++	/* Return EOF immediately if this is a non-regular file. */+	if (AE_IFREG != (zip->entry->mode & AE_IFMT))+		return (ARCHIVE_EOF);++	__archive_read_consume(a, zip->unconsumed);+	zip->unconsumed = 0;++	if (zip->init_decryption) {+		zip->has_encrypted_entries = 1;+		if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED)+			r = read_decryption_header(a);+		else if (zip->entry->compression == WINZIP_AES_ENCRYPTION)+			r = init_WinZip_AES_decryption(a);+		else+			r = init_traditional_PKWARE_decryption(a);+		if (r != ARCHIVE_OK)+			return (r);+		zip->init_decryption = 0;+	}++	switch(zip->entry->compression) {+	case 0:  /* No compression. */+		r =  zip_read_data_none(a, buff, size, offset);+		break;+#ifdef HAVE_BZLIB_H+	case 12: /* ZIPx bzip2 compression. */+		r = zip_read_data_zipx_bzip2(a, buff, size, offset);+		break;+#endif+#if HAVE_LZMA_H && HAVE_LIBLZMA+	case 14: /* ZIPx LZMA compression. */+		r = zip_read_data_zipx_lzma_alone(a, buff, size, offset);+		break;+	case 95: /* ZIPx XZ compression. */+		r = zip_read_data_zipx_xz(a, buff, size, offset);+		break;+#endif+	/* PPMd support is built-in, so we don't need any #if guards. */+	case 98: /* ZIPx PPMd compression. */+		r = zip_read_data_zipx_ppmd(a, buff, size, offset);+		break;++#ifdef HAVE_ZLIB_H+	case 8: /* Deflate compression. */+		r =  zip_read_data_deflate(a, buff, size, offset);+		break;+#endif+	default: /* Unsupported compression. */+		/* Return a warning. */+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unsupported ZIP compression method (%d: %s)",+		    zip->entry->compression, compression_name(zip->entry->compression));+		/* We can't decompress this entry, but we will+		 * be able to skip() it and try the next entry. */+		return (ARCHIVE_FAILED);+		break;+	}+	if (r != ARCHIVE_OK)+		return (r);+	/* Update checksum */+	if (*size)+		zip->entry_crc32 = zip->crc32func(zip->entry_crc32, *buff,+		    (unsigned)*size);+	/* If we hit the end, swallow any end-of-data marker. */+	if (zip->end_of_entry) {+		/* Check file size, CRC against these values. */+		if (zip->entry->compressed_size !=+		    zip->entry_compressed_bytes_read) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "ZIP compressed data is wrong size "+			    "(read %jd, expected %jd)",+			    (intmax_t)zip->entry_compressed_bytes_read,+			    (intmax_t)zip->entry->compressed_size);+			return (ARCHIVE_WARN);+		}+		/* Size field only stores the lower 32 bits of the actual+		 * size. */+		if ((zip->entry->uncompressed_size & UINT32_MAX)+		    != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "ZIP uncompressed data is wrong size "+			    "(read %jd, expected %jd)\n",+			    (intmax_t)zip->entry_uncompressed_bytes_read,+			    (intmax_t)zip->entry->uncompressed_size);+			return (ARCHIVE_WARN);+		}+		/* Check computed CRC against header */+		if ((!zip->hctx_valid ||+		      zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) &&+		   zip->entry->crc32 != zip->entry_crc32+		    && !zip->ignore_crc32) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "ZIP bad CRC: 0x%lx should be 0x%lx",+			    (unsigned long)zip->entry_crc32,+			    (unsigned long)zip->entry->crc32);+			return (ARCHIVE_WARN);+		}+	}++	return (ARCHIVE_OK);+}++static int+archive_read_format_zip_cleanup(struct archive_read *a)+{+	struct zip *zip;+	struct zip_entry *zip_entry, *next_zip_entry;++	zip = (struct zip *)(a->format->data);++#ifdef HAVE_ZLIB_H+	if (zip->stream_valid)+		inflateEnd(&zip->stream);+#endif++#if HAVE_LZMA_H && HAVE_LIBLZMA+    if (zip->zipx_lzma_valid) {+		lzma_end(&zip->zipx_lzma_stream);+	}+#endif++#ifdef HAVE_BZLIB_H+	if (zip->bzstream_valid) {+		BZ2_bzDecompressEnd(&zip->bzstream);+	}+#endif++	free(zip->uncompressed_buffer);++	if (zip->ppmd8_valid)+		__archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);++	if (zip->zip_entries) {+		zip_entry = zip->zip_entries;+		while (zip_entry != NULL) {+			next_zip_entry = zip_entry->next;+			archive_string_free(&zip_entry->rsrcname);+			free(zip_entry);+			zip_entry = next_zip_entry;+		}+	}+	free(zip->decrypted_buffer);+	if (zip->cctx_valid)+		archive_decrypto_aes_ctr_release(&zip->cctx);+	if (zip->hctx_valid)+		archive_hmac_sha1_cleanup(&zip->hctx);+	free(zip->iv);+	free(zip->erd);+	free(zip->v_data);+	archive_string_free(&zip->format_name);+	free(zip);+	(a->format->data) = NULL;+	return (ARCHIVE_OK);+}++static int+archive_read_format_zip_has_encrypted_entries(struct archive_read *_a)+{+	if (_a && _a->format) {+		struct zip * zip = (struct zip *)_a->format->data;+		if (zip) {+			return zip->has_encrypted_entries;+		}+	}+	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;+}++static int+archive_read_format_zip_options(struct archive_read *a,+    const char *key, const char *val)+{+	struct zip *zip;+	int ret = ARCHIVE_FAILED;++	zip = (struct zip *)(a->format->data);+	if (strcmp(key, "compat-2x")  == 0) {+		/* Handle filenames as libarchive 2.x */+		zip->init_default_conversion = (val != NULL) ? 1 : 0;+		return (ARCHIVE_OK);+	} else if (strcmp(key, "hdrcharset")  == 0) {+		if (val == NULL || val[0] == 0)+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "zip: hdrcharset option needs a character-set name"+			);+		else {+			zip->sconv = archive_string_conversion_from_charset(+			    &a->archive, val, 0);+			if (zip->sconv != NULL) {+				if (strcmp(val, "UTF-8") == 0)+					zip->sconv_utf8 = zip->sconv;+				ret = ARCHIVE_OK;+			} else+				ret = ARCHIVE_FATAL;+		}+		return (ret);+	} else if (strcmp(key, "ignorecrc32") == 0) {+		/* Mostly useful for testing. */+		if (val == NULL || val[0] == 0) {+			zip->crc32func = real_crc32;+			zip->ignore_crc32 = 0;+		} else {+			zip->crc32func = fake_crc32;+			zip->ignore_crc32 = 1;+		}+		return (ARCHIVE_OK);+	} else if (strcmp(key, "mac-ext") == 0) {+		zip->process_mac_extensions = (val != NULL && val[0] != 0);+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++int+archive_read_support_format_zip(struct archive *a)+{+	int r;+	r = archive_read_support_format_zip_streamable(a);+	if (r != ARCHIVE_OK)+		return r;+	return (archive_read_support_format_zip_seekable(a));+}++/* ------------------------------------------------------------------------ */++/*+ * Streaming-mode support+ */+++static int+archive_read_support_format_zip_capabilities_streamable(struct archive_read * a)+{+	(void)a; /* UNUSED */+	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |+		ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);+}++static int+archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid)+{+	const char *p;++	(void)best_bid; /* UNUSED */++	if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)+		return (-1);++	/*+	 * Bid of 29 here comes from:+	 *  + 16 bits for "PK",+	 *  + next 16-bit field has 6 options so contributes+	 *    about 16 - log_2(6) ~= 16 - 2.6 ~= 13 bits+	 *+	 * So we've effectively verified ~29 total bits of check data.+	 */+	if (p[0] == 'P' && p[1] == 'K') {+		if ((p[2] == '\001' && p[3] == '\002')+		    || (p[2] == '\003' && p[3] == '\004')+		    || (p[2] == '\005' && p[3] == '\006')+		    || (p[2] == '\006' && p[3] == '\006')+		    || (p[2] == '\007' && p[3] == '\010')+		    || (p[2] == '0' && p[3] == '0'))+			return (29);+	}++	/* TODO: It's worth looking ahead a little bit for a valid+	 * PK signature.  In particular, that would make it possible+	 * to read some UUEncoded SFX files or SFX files coming from+	 * a network socket. */++	return (0);+}++static int+archive_read_format_zip_streamable_read_header(struct archive_read *a,+    struct archive_entry *entry)+{+	struct zip *zip;++	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;+	if (a->archive.archive_format_name == NULL)+		a->archive.archive_format_name = "ZIP";++	zip = (struct zip *)(a->format->data);++	/*+	 * It should be sufficient to call archive_read_next_header() for+	 * a reader to determine if an entry is encrypted or not. If the+	 * encryption of an entry is only detectable when calling+	 * archive_read_data(), so be it. We'll do the same check there+	 * as well.+	 */+	if (zip->has_encrypted_entries ==+			ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW)+		zip->has_encrypted_entries = 0;++	/* Make sure we have a zip_entry structure to use. */+	if (zip->zip_entries == NULL) {+		zip->zip_entries = malloc(sizeof(struct zip_entry));+		if (zip->zip_entries == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Out  of memory");+			return ARCHIVE_FATAL;+		}+	}+	zip->entry = zip->zip_entries;+	memset(zip->entry, 0, sizeof(struct zip_entry));++	if (zip->cctx_valid)+		archive_decrypto_aes_ctr_release(&zip->cctx);+	if (zip->hctx_valid)+		archive_hmac_sha1_cleanup(&zip->hctx);+	zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;+	__archive_read_reset_passphrase(a);++	/* Search ahead for the next local file header. */+	__archive_read_consume(a, zip->unconsumed);+	zip->unconsumed = 0;+	for (;;) {+		int64_t skipped = 0;+		const char *p, *end;+		ssize_t bytes;++		p = __archive_read_ahead(a, 4, &bytes);+		if (p == NULL)+			return (ARCHIVE_FATAL);+		end = p + bytes;++		while (p + 4 <= end) {+			if (p[0] == 'P' && p[1] == 'K') {+				if (p[2] == '\003' && p[3] == '\004') {+					/* Regular file entry. */+					__archive_read_consume(a, skipped);+					return zip_read_local_file_header(a,+					    entry, zip);+				}++                              /*+                               * TODO: We cannot restore permissions+                               * based only on the local file headers.+                               * Consider scanning the central+                               * directory and returning additional+                               * entries for at least directories.+                               * This would allow us to properly set+                               * directory permissions.+			       *+			       * This won't help us fix symlinks+			       * and may not help with regular file+			       * permissions, either.  <sigh>+                               */+                              if (p[2] == '\001' && p[3] == '\002') {+                                      return (ARCHIVE_EOF);+                              }++                              /* End of central directory?  Must be an+                               * empty archive. */+                              if ((p[2] == '\005' && p[3] == '\006')+                                  || (p[2] == '\006' && p[3] == '\006'))+                                      return (ARCHIVE_EOF);+			}+			++p;+			++skipped;+		}+		__archive_read_consume(a, skipped);+	}+}++static int+archive_read_format_zip_read_data_skip_streamable(struct archive_read *a)+{+	struct zip *zip;+	int64_t bytes_skipped;++	zip = (struct zip *)(a->format->data);+	bytes_skipped = __archive_read_consume(a, zip->unconsumed);+	zip->unconsumed = 0;+	if (bytes_skipped < 0)+		return (ARCHIVE_FATAL);++	/* If we've already read to end of data, we're done. */+	if (zip->end_of_entry)+		return (ARCHIVE_OK);++	/* So we know we're streaming... */+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)+	    || zip->entry->compressed_size > 0) {+		/* We know the compressed length, so we can just skip. */+		bytes_skipped = __archive_read_consume(a,+					zip->entry_bytes_remaining);+		if (bytes_skipped < 0)+			return (ARCHIVE_FATAL);+		return (ARCHIVE_OK);+	}++	if (zip->init_decryption) {+		int r;++		zip->has_encrypted_entries = 1;+		if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED)+			r = read_decryption_header(a);+		else if (zip->entry->compression == WINZIP_AES_ENCRYPTION)+			r = init_WinZip_AES_decryption(a);+		else+			r = init_traditional_PKWARE_decryption(a);+		if (r != ARCHIVE_OK)+			return (r);+		zip->init_decryption = 0;+	}++	/* We're streaming and we don't know the length. */+	/* If the body is compressed and we know the format, we can+	 * find an exact end-of-entry by decompressing it. */+	switch (zip->entry->compression) {+#ifdef HAVE_ZLIB_H+	case 8: /* Deflate compression. */+		while (!zip->end_of_entry) {+			int64_t offset = 0;+			const void *buff = NULL;+			size_t size = 0;+			int r;+			r =  zip_read_data_deflate(a, &buff, &size, &offset);+			if (r != ARCHIVE_OK)+				return (r);+		}+		return ARCHIVE_OK;+#endif+	default: /* Uncompressed or unknown. */+		/* Scan for a PK\007\010 signature. */+		for (;;) {+			const char *p, *buff;+			ssize_t bytes_avail;+			buff = __archive_read_ahead(a, 16, &bytes_avail);+			if (bytes_avail < 16) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_FILE_FORMAT,+				    "Truncated ZIP file data");+				return (ARCHIVE_FATAL);+			}+			p = buff;+			while (p <= buff + bytes_avail - 16) {+				if (p[3] == 'P') { p += 3; }+				else if (p[3] == 'K') { p += 2; }+				else if (p[3] == '\007') { p += 1; }+				else if (p[3] == '\010' && p[2] == '\007'+				    && p[1] == 'K' && p[0] == 'P') {+					if (zip->entry->flags & LA_USED_ZIP64)+						__archive_read_consume(a,+						    p - buff + 24);+					else+						__archive_read_consume(a,+						    p - buff + 16);+					return ARCHIVE_OK;+				} else { p += 4; }+			}+			__archive_read_consume(a, p - buff);+		}+	}+}++int+archive_read_support_format_zip_streamable(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct zip *zip;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_zip");++	zip = (struct zip *)calloc(1, sizeof(*zip));+	if (zip == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate zip data");+		return (ARCHIVE_FATAL);+	}++	/* Streamable reader doesn't support mac extensions. */+	zip->process_mac_extensions = 0;++	/*+	 * Until enough data has been read, we cannot tell about+	 * any encrypted entries yet.+	 */+	zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;+	zip->crc32func = real_crc32;++	r = __archive_read_register_format(a,+	    zip,+	    "zip",+	    archive_read_format_zip_streamable_bid,+	    archive_read_format_zip_options,+	    archive_read_format_zip_streamable_read_header,+	    archive_read_format_zip_read_data,+	    archive_read_format_zip_read_data_skip_streamable,+	    NULL,+	    archive_read_format_zip_cleanup,+	    archive_read_support_format_zip_capabilities_streamable,+	    archive_read_format_zip_has_encrypted_entries);++	if (r != ARCHIVE_OK)+		free(zip);+	return (ARCHIVE_OK);+}++/* ------------------------------------------------------------------------ */++/*+ * Seeking-mode support+ */++static int+archive_read_support_format_zip_capabilities_seekable(struct archive_read * a)+{+	(void)a; /* UNUSED */+	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |+		ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);+}++/*+ * TODO: This is a performance sink because it forces the read core to+ * drop buffered data from the start of file, which will then have to+ * be re-read again if this bidder loses.+ *+ * We workaround this a little by passing in the best bid so far so+ * that later bidders can do nothing if they know they'll never+ * outbid.  But we can certainly do better...+ */+static int+read_eocd(struct zip *zip, const char *p, int64_t current_offset)+{+	/* Sanity-check the EOCD we've found. */++	/* This must be the first volume. */+	if (archive_le16dec(p + 4) != 0)+		return 0;+	/* Central directory must be on this volume. */+	if (archive_le16dec(p + 4) != archive_le16dec(p + 6))+		return 0;+	/* All central directory entries must be on this volume. */+	if (archive_le16dec(p + 10) != archive_le16dec(p + 8))+		return 0;+	/* Central directory can't extend beyond start of EOCD record. */+	if (archive_le32dec(p + 16) + archive_le32dec(p + 12)+	    > current_offset)+		return 0;++	/* Save the central directory location for later use. */+	zip->central_directory_offset = archive_le32dec(p + 16);++	/* This is just a tiny bit higher than the maximum+	   returned by the streaming Zip bidder.  This ensures+	   that the more accurate seeking Zip parser wins+	   whenever seek is available. */+	return 32;+}++/*+ * Examine Zip64 EOCD locator:  If it's valid, store the information+ * from it.+ */+static int+read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p)+{+	int64_t eocd64_offset;+	int64_t eocd64_size;++	/* Sanity-check the locator record. */++	/* Central dir must be on first volume. */+	if (archive_le32dec(p + 4) != 0)+		return 0;+	/* Must be only a single volume. */+	if (archive_le32dec(p + 16) != 1)+		return 0;++	/* Find the Zip64 EOCD record. */+	eocd64_offset = archive_le64dec(p + 8);+	if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0)+		return 0;+	if ((p = __archive_read_ahead(a, 56, NULL)) == NULL)+		return 0;+	/* Make sure we can read all of it. */+	eocd64_size = archive_le64dec(p + 4) + 12;+	if (eocd64_size < 56 || eocd64_size > 16384)+		return 0;+	if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL)+		return 0;++	/* Sanity-check the EOCD64 */+	if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */+		return 0;+	if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */+		return 0;+	/* CD can't be split. */+	if (archive_le64dec(p + 24) != archive_le64dec(p + 32))+		return 0;++	/* Save the central directory offset for later use. */+	zip->central_directory_offset = archive_le64dec(p + 48);++	return 32;+}++static int+archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)+{+	struct zip *zip = (struct zip *)a->format->data;+	int64_t file_size, current_offset;+	const char *p;+	int i, tail;++	/* If someone has already bid more than 32, then avoid+	   trashing the look-ahead buffers with a seek. */+	if (best_bid > 32)+		return (-1);++	file_size = __archive_read_seek(a, 0, SEEK_END);+	if (file_size <= 0)+		return 0;++	/* Search last 16k of file for end-of-central-directory+	 * record (which starts with PK\005\006) */+	tail = (int)zipmin(1024 * 16, file_size);+	current_offset = __archive_read_seek(a, -tail, SEEK_END);+	if (current_offset < 0)+		return 0;+	if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL)+		return 0;+	/* Boyer-Moore search backwards from the end, since we want+	 * to match the last EOCD in the file (there can be more than+	 * one if there is an uncompressed Zip archive as a member+	 * within this Zip archive). */+	for (i = tail - 22; i > 0;) {+		switch (p[i]) {+		case 'P':+			if (memcmp(p + i, "PK\005\006", 4) == 0) {+				int ret = read_eocd(zip, p + i,+				    current_offset + i);+				/* Zip64 EOCD locator precedes+				 * regular EOCD if present. */+				if (i >= 20 && memcmp(p + i - 20, "PK\006\007", 4) == 0) {+					int ret_zip64 = read_zip64_eocd(a, zip, p + i - 20);+					if (ret_zip64 > ret)+						ret = ret_zip64;+				}+				return (ret);+			}+			i -= 4;+			break;+		case 'K': i -= 1; break;+		case 005: i -= 2; break;+		case 006: i -= 3; break;+		default: i -= 4; break;+		}+	}+	return 0;+}++/* The red-black trees are only used in seeking mode to manage+ * the in-memory copy of the central directory. */++static int+cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2)+{+	const struct zip_entry *e1 = (const struct zip_entry *)n1;+	const struct zip_entry *e2 = (const struct zip_entry *)n2;++	if (e1->local_header_offset > e2->local_header_offset)+		return -1;+	if (e1->local_header_offset < e2->local_header_offset)+		return 1;+	return 0;+}++static int+cmp_key(const struct archive_rb_node *n, const void *key)+{+	/* This function won't be called */+	(void)n; /* UNUSED */+	(void)key; /* UNUSED */+	return 1;+}++static const struct archive_rb_tree_ops rb_ops = {+	&cmp_node, &cmp_key+};++static int+rsrc_cmp_node(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	const struct zip_entry *e1 = (const struct zip_entry *)n1;+	const struct zip_entry *e2 = (const struct zip_entry *)n2;++	return (strcmp(e2->rsrcname.s, e1->rsrcname.s));+}++static int+rsrc_cmp_key(const struct archive_rb_node *n, const void *key)+{+	const struct zip_entry *e = (const struct zip_entry *)n;+	return (strcmp((const char *)key, e->rsrcname.s));+}++static const struct archive_rb_tree_ops rb_rsrc_ops = {+	&rsrc_cmp_node, &rsrc_cmp_key+};++static const char *+rsrc_basename(const char *name, size_t name_length)+{+	const char *s, *r;++	r = s = name;+	for (;;) {+		s = memchr(s, '/', name_length - (s - name));+		if (s == NULL)+			break;+		r = ++s;+	}+	return (r);+}++static void+expose_parent_dirs(struct zip *zip, const char *name, size_t name_length)+{+	struct archive_string str;+	struct zip_entry *dir;+	char *s;++	archive_string_init(&str);+	archive_strncpy(&str, name, name_length);+	for (;;) {+		s = strrchr(str.s, '/');+		if (s == NULL)+			break;+		*s = '\0';+		/* Transfer the parent directory from zip->tree_rsrc RB+		 * tree to zip->tree RB tree to expose. */+		dir = (struct zip_entry *)+		    __archive_rb_tree_find_node(&zip->tree_rsrc, str.s);+		if (dir == NULL)+			break;+		__archive_rb_tree_remove_node(&zip->tree_rsrc, &dir->node);+		archive_string_free(&dir->rsrcname);+		__archive_rb_tree_insert_node(&zip->tree, &dir->node);+	}+	archive_string_free(&str);+}++static int+slurp_central_directory(struct archive_read *a, struct archive_entry* entry,+    struct zip *zip)+{+	ssize_t i;+	unsigned found;+	int64_t correction;+	ssize_t bytes_avail;+	const char *p;++	/*+	 * Find the start of the central directory.  The end-of-CD+	 * record has our starting point, but there are lots of+	 * Zip archives which have had other data prepended to the+	 * file, which makes the recorded offsets all too small.+	 * So we search forward from the specified offset until we+	 * find the real start of the central directory.  Then we+	 * know the correction we need to apply to account for leading+	 * padding.+	 */+	if (__archive_read_seek(a, zip->central_directory_offset, SEEK_SET) < 0)+		return ARCHIVE_FATAL;++	found = 0;+	while (!found) {+		if ((p = __archive_read_ahead(a, 20, &bytes_avail)) == NULL)+			return ARCHIVE_FATAL;+		for (found = 0, i = 0; !found && i < bytes_avail - 4;) {+			switch (p[i + 3]) {+			case 'P': i += 3; break;+			case 'K': i += 2; break;+			case 001: i += 1; break;+			case 002:+				if (memcmp(p + i, "PK\001\002", 4) == 0) {+					p += i;+					found = 1;+				} else+					i += 4;+				break;+			case 005: i += 1; break;+			case 006:+				if (memcmp(p + i, "PK\005\006", 4) == 0) {+					p += i;+					found = 1;+				} else if (memcmp(p + i, "PK\006\006", 4) == 0) {+					p += i;+					found = 1;+				} else+					i += 1;+				break;+			default: i += 4; break;+			}+		}+		__archive_read_consume(a, i);+	}+	correction = archive_filter_bytes(&a->archive, 0)+			- zip->central_directory_offset;++	__archive_rb_tree_init(&zip->tree, &rb_ops);+	__archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops);++	zip->central_directory_entries_total = 0;+	while (1) {+		struct zip_entry *zip_entry;+		size_t filename_length, extra_length, comment_length;+		uint32_t external_attributes;+		const char *name, *r;++		if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)+			return ARCHIVE_FATAL;+		if (memcmp(p, "PK\006\006", 4) == 0+		    || memcmp(p, "PK\005\006", 4) == 0) {+			break;+		} else if (memcmp(p, "PK\001\002", 4) != 0) {+			archive_set_error(&a->archive,+			    -1, "Invalid central directory signature");+			return ARCHIVE_FATAL;+		}+		if ((p = __archive_read_ahead(a, 46, NULL)) == NULL)+			return ARCHIVE_FATAL;++		zip_entry = calloc(1, sizeof(struct zip_entry));+		if (zip_entry == NULL) {+			archive_set_error(&a->archive, ENOMEM,+				"Can't allocate zip entry");+			return ARCHIVE_FATAL;+		}+		zip_entry->next = zip->zip_entries;+		zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY;+		zip->zip_entries = zip_entry;+		zip->central_directory_entries_total++;++		/* version = p[4]; */+		zip_entry->system = p[5];+		/* version_required = archive_le16dec(p + 6); */+		zip_entry->zip_flags = archive_le16dec(p + 8);+		if (zip_entry->zip_flags+		      & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)){+			zip->has_encrypted_entries = 1;+		}+		zip_entry->compression = (char)archive_le16dec(p + 10);+		zip_entry->mtime = zip_time(p + 12);+		zip_entry->crc32 = archive_le32dec(p + 16);+		if (zip_entry->zip_flags & ZIP_LENGTH_AT_END)+			zip_entry->decdat = p[13];+		else+			zip_entry->decdat = p[19];+		zip_entry->compressed_size = archive_le32dec(p + 20);+		zip_entry->uncompressed_size = archive_le32dec(p + 24);+		filename_length = archive_le16dec(p + 28);+		extra_length = archive_le16dec(p + 30);+		comment_length = archive_le16dec(p + 32);+		/* disk_start = archive_le16dec(p + 34);+		 *   Better be zero.+		 * internal_attributes = archive_le16dec(p + 36);+		 *   text bit */+		external_attributes = archive_le32dec(p + 38);+		zip_entry->local_header_offset =+		    archive_le32dec(p + 42) + correction;++		/* If we can't guess the mode, leave it zero here;+		   when we read the local file header we might get+		   more information. */+		if (zip_entry->system == 3) {+			zip_entry->mode = external_attributes >> 16;+		} else if (zip_entry->system == 0) {+			// Interpret MSDOS directory bit+			if (0x10 == (external_attributes & 0x10)) {+				zip_entry->mode = AE_IFDIR | 0775;+			} else {+				zip_entry->mode = AE_IFREG | 0664;+			}+			if (0x01 == (external_attributes & 0x01)) {+				// Read-only bit; strip write permissions+				zip_entry->mode &= 0555;+			}+		} else {+			zip_entry->mode = 0;+		}++		/* We're done with the regular data; get the filename and+		 * extra data. */+		__archive_read_consume(a, 46);+		p = __archive_read_ahead(a, filename_length + extra_length,+			NULL);+		if (p == NULL) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated ZIP file header");+			return ARCHIVE_FATAL;+		}+		if (ARCHIVE_OK != process_extra(a, entry, p + filename_length,+		    extra_length, zip_entry)) {+			return ARCHIVE_FATAL;+		}++		/*+		 * Mac resource fork files are stored under the+		 * "__MACOSX/" directory, so we should check if+		 * it is.+		 */+		if (!zip->process_mac_extensions) {+			/* Treat every entry as a regular entry. */+			__archive_rb_tree_insert_node(&zip->tree,+			    &zip_entry->node);+		} else {+			name = p;+			r = rsrc_basename(name, filename_length);+			if (filename_length >= 9 &&+			    strncmp("__MACOSX/", name, 9) == 0) {+				/* If this file is not a resource fork nor+				 * a directory. We should treat it as a non+				 * resource fork file to expose it. */+				if (name[filename_length-1] != '/' &&+				    (r - name < 3 || r[0] != '.' ||+				     r[1] != '_')) {+					__archive_rb_tree_insert_node(+					    &zip->tree, &zip_entry->node);+					/* Expose its parent directories. */+					expose_parent_dirs(zip, name,+					    filename_length);+				} else {+					/* This file is a resource fork file or+					 * a directory. */+					archive_strncpy(&(zip_entry->rsrcname),+					     name, filename_length);+					__archive_rb_tree_insert_node(+					    &zip->tree_rsrc, &zip_entry->node);+				}+			} else {+				/* Generate resource fork name to find its+				 * resource file at zip->tree_rsrc. */+				archive_strcpy(&(zip_entry->rsrcname),+				    "__MACOSX/");+				archive_strncat(&(zip_entry->rsrcname),+				    name, r - name);+				archive_strcat(&(zip_entry->rsrcname), "._");+				archive_strncat(&(zip_entry->rsrcname),+				    name + (r - name),+				    filename_length - (r - name));+				/* Register an entry to RB tree to sort it by+				 * file offset. */+				__archive_rb_tree_insert_node(&zip->tree,+				    &zip_entry->node);+			}+		}++		/* Skip the comment too ... */+		__archive_read_consume(a,+		    filename_length + extra_length + comment_length);+	}++	return ARCHIVE_OK;+}++static ssize_t+zip_get_local_file_header_size(struct archive_read *a, size_t extra)+{+	const char *p;+	ssize_t filename_length, extra_length;++	if ((p = __archive_read_ahead(a, extra + 30, NULL)) == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Truncated ZIP file header");+		return (ARCHIVE_WARN);+	}+	p += extra;++	if (memcmp(p, "PK\003\004", 4) != 0) {+		archive_set_error(&a->archive, -1, "Damaged Zip archive");+		return ARCHIVE_WARN;+	}+	filename_length = archive_le16dec(p + 26);+	extra_length = archive_le16dec(p + 28);++	return (30 + filename_length + extra_length);+}++static int+zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry,+    struct zip_entry *rsrc)+{+	struct zip *zip = (struct zip *)a->format->data;+	unsigned char *metadata, *mp;+	int64_t offset = archive_filter_bytes(&a->archive, 0);+	size_t remaining_bytes, metadata_bytes;+	ssize_t hsize;+	int ret = ARCHIVE_OK, eof;++	switch(rsrc->compression) {+	case 0:  /* No compression. */+		if (rsrc->uncompressed_size != rsrc->compressed_size) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Malformed OS X metadata entry: "+			    "inconsistent size");+			return (ARCHIVE_FATAL);+		}+#ifdef HAVE_ZLIB_H+	case 8: /* Deflate compression. */+#endif+		break;+	default: /* Unsupported compression. */+		/* Return a warning. */+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Unsupported ZIP compression method (%s)",+		    compression_name(rsrc->compression));+		/* We can't decompress this entry, but we will+		 * be able to skip() it and try the next entry. */+		return (ARCHIVE_WARN);+	}++	if (rsrc->uncompressed_size > (4 * 1024 * 1024)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Mac metadata is too large: %jd > 4M bytes",+		    (intmax_t)rsrc->uncompressed_size);+		return (ARCHIVE_WARN);+	}+	if (rsrc->compressed_size > (4 * 1024 * 1024)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Mac metadata is too large: %jd > 4M bytes",+		    (intmax_t)rsrc->compressed_size);+		return (ARCHIVE_WARN);+	}++	metadata = malloc((size_t)rsrc->uncompressed_size);+	if (metadata == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory for Mac metadata");+		return (ARCHIVE_FATAL);+	}++	if (offset < rsrc->local_header_offset)+		__archive_read_consume(a, rsrc->local_header_offset - offset);+	else if (offset != rsrc->local_header_offset) {+		__archive_read_seek(a, rsrc->local_header_offset, SEEK_SET);+	}++	hsize = zip_get_local_file_header_size(a, 0);+	__archive_read_consume(a, hsize);++	remaining_bytes = (size_t)rsrc->compressed_size;+	metadata_bytes = (size_t)rsrc->uncompressed_size;+	mp = metadata;+	eof = 0;+	while (!eof && remaining_bytes) {+		const unsigned char *p;+		ssize_t bytes_avail;+		size_t bytes_used;++		p = __archive_read_ahead(a, 1, &bytes_avail);+		if (p == NULL) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Truncated ZIP file header");+			ret = ARCHIVE_WARN;+			goto exit_mac_metadata;+		}+		if ((size_t)bytes_avail > remaining_bytes)+			bytes_avail = remaining_bytes;+		switch(rsrc->compression) {+		case 0:  /* No compression. */+			if ((size_t)bytes_avail > metadata_bytes)+				bytes_avail = metadata_bytes;+			memcpy(mp, p, bytes_avail);+			bytes_used = (size_t)bytes_avail;+			metadata_bytes -= bytes_used;+			mp += bytes_used;+			if (metadata_bytes == 0)+				eof = 1;+			break;+#ifdef HAVE_ZLIB_H+		case 8: /* Deflate compression. */+		{+			int r;++			ret = zip_deflate_init(a, zip);+			if (ret != ARCHIVE_OK)+				goto exit_mac_metadata;+			zip->stream.next_in =+			    (Bytef *)(uintptr_t)(const void *)p;+			zip->stream.avail_in = (uInt)bytes_avail;+			zip->stream.total_in = 0;+			zip->stream.next_out = mp;+			zip->stream.avail_out = (uInt)metadata_bytes;+			zip->stream.total_out = 0;++			r = inflate(&zip->stream, 0);+			switch (r) {+			case Z_OK:+				break;+			case Z_STREAM_END:+				eof = 1;+				break;+			case Z_MEM_ERROR:+				archive_set_error(&a->archive, ENOMEM,+				    "Out of memory for ZIP decompression");+				ret = ARCHIVE_FATAL;+				goto exit_mac_metadata;+			default:+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "ZIP decompression failed (%d)", r);+				ret = ARCHIVE_FATAL;+				goto exit_mac_metadata;+			}+			bytes_used = zip->stream.total_in;+			metadata_bytes -= zip->stream.total_out;+			mp += zip->stream.total_out;+			break;+		}+#endif+		default:+			bytes_used = 0;+			break;+		}+		__archive_read_consume(a, bytes_used);+		remaining_bytes -= bytes_used;+	}+	archive_entry_copy_mac_metadata(entry, metadata,+	    (size_t)rsrc->uncompressed_size - metadata_bytes);++exit_mac_metadata:+	__archive_read_seek(a, offset, SEEK_SET);+	zip->decompress_init = 0;+	free(metadata);+	return (ret);+}++static int+archive_read_format_zip_seekable_read_header(struct archive_read *a,+	struct archive_entry *entry)+{+	struct zip *zip = (struct zip *)a->format->data;+	struct zip_entry *rsrc;+	int64_t offset;+	int r, ret = ARCHIVE_OK;++	/*+	 * It should be sufficient to call archive_read_next_header() for+	 * a reader to determine if an entry is encrypted or not. If the+	 * encryption of an entry is only detectable when calling+	 * archive_read_data(), so be it. We'll do the same check there+	 * as well.+	 */+	if (zip->has_encrypted_entries ==+			ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW)+		zip->has_encrypted_entries = 0;++	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;+	if (a->archive.archive_format_name == NULL)+		a->archive.archive_format_name = "ZIP";++	if (zip->zip_entries == NULL) {+		r = slurp_central_directory(a, entry, zip);+		if (r != ARCHIVE_OK)+			return r;+		/* Get first entry whose local header offset is lower than+		 * other entries in the archive file. */+		zip->entry =+		    (struct zip_entry *)ARCHIVE_RB_TREE_MIN(&zip->tree);+	} else if (zip->entry != NULL) {+		/* Get next entry in local header offset order. */+		zip->entry = (struct zip_entry *)__archive_rb_tree_iterate(+		    &zip->tree, &zip->entry->node, ARCHIVE_RB_DIR_RIGHT);+	}++	if (zip->entry == NULL)+		return ARCHIVE_EOF;++	if (zip->entry->rsrcname.s)+		rsrc = (struct zip_entry *)__archive_rb_tree_find_node(+		    &zip->tree_rsrc, zip->entry->rsrcname.s);+	else+		rsrc = NULL;++	if (zip->cctx_valid)+		archive_decrypto_aes_ctr_release(&zip->cctx);+	if (zip->hctx_valid)+		archive_hmac_sha1_cleanup(&zip->hctx);+	zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;+	__archive_read_reset_passphrase(a);++	/* File entries are sorted by the header offset, we should mostly+	 * use __archive_read_consume to advance a read point to avoid+	 * redundant data reading.  */+	offset = archive_filter_bytes(&a->archive, 0);+	if (offset < zip->entry->local_header_offset)+		__archive_read_consume(a,+		    zip->entry->local_header_offset - offset);+	else if (offset != zip->entry->local_header_offset) {+		__archive_read_seek(a, zip->entry->local_header_offset,+		    SEEK_SET);+	}+	zip->unconsumed = 0;+	r = zip_read_local_file_header(a, entry, zip);+	if (r != ARCHIVE_OK)+		return r;+	if (rsrc) {+		int ret2 = zip_read_mac_metadata(a, entry, rsrc);+		if (ret2 < ret)+			ret = ret2;+	}+	return (ret);+}++/*+ * We're going to seek for the next header anyway, so we don't+ * need to bother doing anything here.+ */+static int+archive_read_format_zip_read_data_skip_seekable(struct archive_read *a)+{+	struct zip *zip;+	zip = (struct zip *)(a->format->data);++	zip->unconsumed = 0;+	return (ARCHIVE_OK);+}++int+archive_read_support_format_zip_seekable(struct archive *_a)+{+	struct archive_read *a = (struct archive_read *)_a;+	struct zip *zip;+	int r;++	archive_check_magic(_a, ARCHIVE_READ_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable");++	zip = (struct zip *)calloc(1, sizeof(*zip));+	if (zip == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate zip data");+		return (ARCHIVE_FATAL);+	}++#ifdef HAVE_COPYFILE_H+	/* Set this by default on Mac OS. */+	zip->process_mac_extensions = 1;+#endif++	/*+	 * Until enough data has been read, we cannot tell about+	 * any encrypted entries yet.+	 */+	zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;+	zip->crc32func = real_crc32;++	r = __archive_read_register_format(a,+	    zip,+	    "zip",+	    archive_read_format_zip_seekable_bid,+	    archive_read_format_zip_options,+	    archive_read_format_zip_seekable_read_header,+	    archive_read_format_zip_read_data,+	    archive_read_format_zip_read_data_skip_seekable,+	    NULL,+	    archive_read_format_zip_cleanup,+	    archive_read_support_format_zip_capabilities_seekable,+	    archive_read_format_zip_has_encrypted_entries);++	if (r != ARCHIVE_OK)+		free(zip);+	return (ARCHIVE_OK);+}++/*# vim:set noet:*/
+ c/archive_string.c view
@@ -0,0 +1,4224 @@+/*-+ * Copyright (c) 2003-2011 Tim Kientzle+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33:22Z kientzle $");++/*+ * Basic resizable string support, to simplify manipulating arbitrary-sized+ * strings while minimizing heap activity.+ *+ * In particular, the buffer used by a string object is only grown, it+ * never shrinks, so you can clear and reuse the same string object+ * without incurring additional memory allocations.+ */++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_ICONV_H+#include <iconv.h>+#endif+#ifdef HAVE_LANGINFO_H+#include <langinfo.h>+#endif+#ifdef HAVE_LOCALCHARSET_H+#include <localcharset.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_WCHAR_H+#include <wchar.h>+#endif+#if defined(_WIN32) && !defined(__CYGWIN__)+#include <windows.h>+#include <locale.h>+#endif++#include "archive_endian.h"+#include "archive_private.h"+#include "archive_string.h"+#include "archive_string_composition.h"++#if !defined(HAVE_WMEMCPY) && !defined(wmemcpy)+#define wmemcpy(a,b,i)  (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))+#endif++#if !defined(HAVE_WMEMMOVE) && !defined(wmemmove)+#define wmemmove(a,b,i)  (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t))+#endif++#undef max+#define max(a, b)       ((a)>(b)?(a):(b))++struct archive_string_conv {+	struct archive_string_conv	*next;+	char				*from_charset;+	char				*to_charset;+	unsigned			 from_cp;+	unsigned			 to_cp;+	/* Set 1 if from_charset and to_charset are the same. */+	int				 same;+	int				 flag;+#define SCONV_TO_CHARSET	1	/* MBS is being converted to specified+					 * charset. */+#define SCONV_FROM_CHARSET	(1<<1)	/* MBS is being converted from+					 * specified charset. */+#define SCONV_BEST_EFFORT 	(1<<2)	/* Copy at least ASCII code. */+#define SCONV_WIN_CP	 	(1<<3)	/* Use Windows API for converting+					 * MBS. */+#define SCONV_UTF8_LIBARCHIVE_2 (1<<4)	/* Incorrect UTF-8 made by libarchive+					 * 2.x in the wrong assumption. */+#define SCONV_NORMALIZATION_C	(1<<6)	/* Need normalization to be Form C.+					 * Before UTF-8 characters are actually+					 * processed. */+#define SCONV_NORMALIZATION_D	(1<<7)	/* Need normalization to be Form D.+					 * Before UTF-8 characters are actually+					 * processed.+					 * Currently this only for MAC OS X. */+#define SCONV_TO_UTF8		(1<<8)	/* "to charset" side is UTF-8. */+#define SCONV_FROM_UTF8		(1<<9)	/* "from charset" side is UTF-8. */+#define SCONV_TO_UTF16BE 	(1<<10)	/* "to charset" side is UTF-16BE. */+#define SCONV_FROM_UTF16BE 	(1<<11)	/* "from charset" side is UTF-16BE. */+#define SCONV_TO_UTF16LE 	(1<<12)	/* "to charset" side is UTF-16LE. */+#define SCONV_FROM_UTF16LE 	(1<<13)	/* "from charset" side is UTF-16LE. */+#define SCONV_TO_UTF16		(SCONV_TO_UTF16BE | SCONV_TO_UTF16LE)+#define SCONV_FROM_UTF16	(SCONV_FROM_UTF16BE | SCONV_FROM_UTF16LE)++#if HAVE_ICONV+	iconv_t				 cd;+	iconv_t				 cd_w;/* Use at archive_mstring on+				 	       * Windows. */+#endif+	/* A temporary buffer for normalization. */+	struct archive_string		 utftmp;+	int (*converter[2])(struct archive_string *, const void *, size_t,+	    struct archive_string_conv *);+	int				 nconverter;+};++#define CP_C_LOCALE	0	/* "C" locale only for this file. */+#define CP_UTF16LE	1200+#define CP_UTF16BE	1201++#define IS_HIGH_SURROGATE_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDBFF)+#define IS_LOW_SURROGATE_LA(uc)	 ((uc) >= 0xDC00 && (uc) <= 0xDFFF)+#define IS_SURROGATE_PAIR_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDFFF)+#define UNICODE_MAX		0x10FFFF+#define UNICODE_R_CHAR		0xFFFD	/* Replacement character. */+/* Set U+FFFD(Replacement character) in UTF-8. */+static const char utf8_replacement_char[] = {0xef, 0xbf, 0xbd};++static struct archive_string_conv *find_sconv_object(struct archive *,+	const char *, const char *);+static void add_sconv_object(struct archive *, struct archive_string_conv *);+static struct archive_string_conv *create_sconv_object(const char *,+	const char *, unsigned, int);+static void free_sconv_object(struct archive_string_conv *);+static struct archive_string_conv *get_sconv_object(struct archive *,+	const char *, const char *, int);+static unsigned make_codepage_from_charset(const char *);+static unsigned get_current_codepage(void);+static unsigned get_current_oemcp(void);+static size_t mbsnbytes(const void *, size_t);+static size_t utf16nbytes(const void *, size_t);+#if defined(_WIN32) && !defined(__CYGWIN__)+static int archive_wstring_append_from_mbs_in_codepage(+    struct archive_wstring *, const char *, size_t,+    struct archive_string_conv *);+static int archive_string_append_from_wcs_in_codepage(struct archive_string *,+    const wchar_t *, size_t, struct archive_string_conv *);+static int is_big_endian(void);+static int strncat_in_codepage(struct archive_string *, const void *,+    size_t, struct archive_string_conv *);+static int win_strncat_from_utf16be(struct archive_string *, const void *,+    size_t, struct archive_string_conv *);+static int win_strncat_from_utf16le(struct archive_string *, const void *,+    size_t, struct archive_string_conv *);+static int win_strncat_to_utf16be(struct archive_string *, const void *,+    size_t, struct archive_string_conv *);+static int win_strncat_to_utf16le(struct archive_string *, const void *,+    size_t, struct archive_string_conv *);+#endif+static int best_effort_strncat_from_utf16be(struct archive_string *,+    const void *, size_t, struct archive_string_conv *);+static int best_effort_strncat_from_utf16le(struct archive_string *,+    const void *, size_t, struct archive_string_conv *);+static int best_effort_strncat_to_utf16be(struct archive_string *,+    const void *, size_t, struct archive_string_conv *);+static int best_effort_strncat_to_utf16le(struct archive_string *,+    const void *, size_t, struct archive_string_conv *);+#if defined(HAVE_ICONV)+static int iconv_strncat_in_locale(struct archive_string *, const void *,+    size_t, struct archive_string_conv *);+#endif+static int best_effort_strncat_in_locale(struct archive_string *,+    const void *, size_t, struct archive_string_conv *);+static int _utf8_to_unicode(uint32_t *, const char *, size_t);+static int utf8_to_unicode(uint32_t *, const char *, size_t);+static inline uint32_t combine_surrogate_pair(uint32_t, uint32_t);+static int cesu8_to_unicode(uint32_t *, const char *, size_t);+static size_t unicode_to_utf8(char *, size_t, uint32_t);+static int utf16_to_unicode(uint32_t *, const char *, size_t, int);+static size_t unicode_to_utf16be(char *, size_t, uint32_t);+static size_t unicode_to_utf16le(char *, size_t, uint32_t);+static int strncat_from_utf8_libarchive2(struct archive_string *,+    const void *, size_t, struct archive_string_conv *);+static int strncat_from_utf8_to_utf8(struct archive_string *, const void *,+    size_t, struct archive_string_conv *);+static int archive_string_normalize_C(struct archive_string *, const void *,+    size_t, struct archive_string_conv *);+static int archive_string_normalize_D(struct archive_string *, const void *,+    size_t, struct archive_string_conv *);+static int archive_string_append_unicode(struct archive_string *,+    const void *, size_t, struct archive_string_conv *);++static struct archive_string *+archive_string_append(struct archive_string *as, const char *p, size_t s)+{+	if (archive_string_ensure(as, as->length + s + 1) == NULL)+		return (NULL);+	if (s)+		memmove(as->s + as->length, p, s);+	as->length += s;+	as->s[as->length] = 0;+	return (as);+}++static struct archive_wstring *+archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s)+{+	if (archive_wstring_ensure(as, as->length + s + 1) == NULL)+		return (NULL);+	if (s)+		wmemmove(as->s + as->length, p, s);+	as->length += s;+	as->s[as->length] = 0;+	return (as);+}++struct archive_string *+archive_array_append(struct archive_string *as, const char *p, size_t s)+{+	return archive_string_append(as, p, s);+}++void+archive_string_concat(struct archive_string *dest, struct archive_string *src)+{+	if (archive_string_append(dest, src->s, src->length) == NULL)+		__archive_errx(1, "Out of memory");+}++void+archive_wstring_concat(struct archive_wstring *dest,+    struct archive_wstring *src)+{+	if (archive_wstring_append(dest, src->s, src->length) == NULL)+		__archive_errx(1, "Out of memory");+}++void+archive_string_free(struct archive_string *as)+{+	as->length = 0;+	as->buffer_length = 0;+	free(as->s);+	as->s = NULL;+}++void+archive_wstring_free(struct archive_wstring *as)+{+	as->length = 0;+	as->buffer_length = 0;+	free(as->s);+	as->s = NULL;+}++struct archive_wstring *+archive_wstring_ensure(struct archive_wstring *as, size_t s)+{+	return (struct archive_wstring *)+		archive_string_ensure((struct archive_string *)as,+					s * sizeof(wchar_t));+}++/* Returns NULL on any allocation failure. */+struct archive_string *+archive_string_ensure(struct archive_string *as, size_t s)+{+	char *p;+	size_t new_length;++	/* If buffer is already big enough, don't reallocate. */+	if (as->s && (s <= as->buffer_length))+		return (as);++	/*+	 * Growing the buffer at least exponentially ensures that+	 * append operations are always linear in the number of+	 * characters appended.  Using a smaller growth rate for+	 * larger buffers reduces memory waste somewhat at the cost of+	 * a larger constant factor.+	 */+	if (as->buffer_length < 32)+		/* Start with a minimum 32-character buffer. */+		new_length = 32;+	else if (as->buffer_length < 8192)+		/* Buffers under 8k are doubled for speed. */+		new_length = as->buffer_length + as->buffer_length;+	else {+		/* Buffers 8k and over grow by at least 25% each time. */+		new_length = as->buffer_length + as->buffer_length / 4;+		/* Be safe: If size wraps, fail. */+		if (new_length < as->buffer_length) {+			/* On failure, wipe the string and return NULL. */+			archive_string_free(as);+			errno = ENOMEM;/* Make sure errno has ENOMEM. */+			return (NULL);+		}+	}+	/*+	 * The computation above is a lower limit to how much we'll+	 * grow the buffer.  In any case, we have to grow it enough to+	 * hold the request.+	 */+	if (new_length < s)+		new_length = s;+	/* Now we can reallocate the buffer. */+	p = (char *)realloc(as->s, new_length);+	if (p == NULL) {+		/* On failure, wipe the string and return NULL. */+		archive_string_free(as);+		errno = ENOMEM;/* Make sure errno has ENOMEM. */+		return (NULL);+	}++	as->s = p;+	as->buffer_length = new_length;+	return (as);+}++/*+ * TODO: See if there's a way to avoid scanning+ * the source string twice.  Then test to see+ * if it actually helps (remember that we're almost+ * always called with pretty short arguments, so+ * such an optimization might not help).+ */+struct archive_string *+archive_strncat(struct archive_string *as, const void *_p, size_t n)+{+	size_t s;+	const char *p, *pp;++	p = (const char *)_p;++	/* Like strlen(p), except won't examine positions beyond p[n]. */+	s = 0;+	pp = p;+	while (s < n && *pp) {+		pp++;+		s++;+	}+	if ((as = archive_string_append(as, p, s)) == NULL)+		__archive_errx(1, "Out of memory");+	return (as);+}++struct archive_wstring *+archive_wstrncat(struct archive_wstring *as, const wchar_t *p, size_t n)+{+	size_t s;+	const wchar_t *pp;++	/* Like strlen(p), except won't examine positions beyond p[n]. */+	s = 0;+	pp = p;+	while (s < n && *pp) {+		pp++;+		s++;+	}+	if ((as = archive_wstring_append(as, p, s)) == NULL)+		__archive_errx(1, "Out of memory");+	return (as);+}++struct archive_string *+archive_strcat(struct archive_string *as, const void *p)+{+	/* strcat is just strncat without an effective limit. +	 * Assert that we'll never get called with a source+	 * string over 16MB.+	 * TODO: Review all uses of strcat in the source+	 * and try to replace them with strncat().+	 */+	return archive_strncat(as, p, 0x1000000);+}++struct archive_wstring *+archive_wstrcat(struct archive_wstring *as, const wchar_t *p)+{+	/* Ditto. */+	return archive_wstrncat(as, p, 0x1000000);+}++struct archive_string *+archive_strappend_char(struct archive_string *as, char c)+{+	if ((as = archive_string_append(as, &c, 1)) == NULL)+		__archive_errx(1, "Out of memory");+	return (as);+}++struct archive_wstring *+archive_wstrappend_wchar(struct archive_wstring *as, wchar_t c)+{+	if ((as = archive_wstring_append(as, &c, 1)) == NULL)+		__archive_errx(1, "Out of memory");+	return (as);+}++/*+ * Get the "current character set" name to use with iconv.+ * On FreeBSD, the empty character set name "" chooses+ * the correct character encoding for the current locale,+ * so this isn't necessary.+ * But iconv on Mac OS 10.6 doesn't seem to handle this correctly;+ * on that system, we have to explicitly call nl_langinfo()+ * to get the right name.  Not sure about other platforms.+ *+ * NOTE: GNU libiconv does not recognize the character-set name+ * which some platform nl_langinfo(CODESET) returns, so we should+ * use locale_charset() instead of nl_langinfo(CODESET) for GNU libiconv.+ */+static const char *+default_iconv_charset(const char *charset) {+	if (charset != NULL && charset[0] != '\0')+		return charset;+#if HAVE_LOCALE_CHARSET && !defined(__APPLE__)+	/* locale_charset() is broken on Mac OS */+	return locale_charset();+#elif HAVE_NL_LANGINFO+	return nl_langinfo(CODESET);+#else+	return "";+#endif+}++#if defined(_WIN32) && !defined(__CYGWIN__)++/*+ * Convert MBS to WCS.+ * Note: returns -1 if conversion fails.+ */+int+archive_wstring_append_from_mbs(struct archive_wstring *dest,+    const char *p, size_t len)+{+	return archive_wstring_append_from_mbs_in_codepage(dest, p, len, NULL);+}++static int+archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest,+    const char *s, size_t length, struct archive_string_conv *sc)+{+	int count, ret = 0;+	UINT from_cp;++	if (sc != NULL)+		from_cp = sc->from_cp;+	else+		from_cp = get_current_codepage();++	if (from_cp == CP_C_LOCALE) {+		/*+		 * "C" locale special processing.+		 */+		wchar_t *ws;+		const unsigned char *mp;++		if (NULL == archive_wstring_ensure(dest,+		    dest->length + length + 1))+			return (-1);++		ws = dest->s + dest->length;+		mp = (const unsigned char *)s;+		count = 0;+		while (count < (int)length && *mp) {+			*ws++ = (wchar_t)*mp++;+			count++;+		}+	} else if (sc != NULL &&+	    (sc->flag & (SCONV_NORMALIZATION_C | SCONV_NORMALIZATION_D))) {+		/*+		 * Normalize UTF-8 and UTF-16BE and convert it directly+		 * to UTF-16 as wchar_t.+		 */+		struct archive_string u16;+		int saved_flag = sc->flag;/* save current flag. */++		if (is_big_endian())+			sc->flag |= SCONV_TO_UTF16BE;+		else+			sc->flag |= SCONV_TO_UTF16LE;++		if (sc->flag & SCONV_FROM_UTF16) {+			/*+			 *  UTF-16BE/LE NFD ===> UTF-16 NFC+			 *  UTF-16BE/LE NFC ===> UTF-16 NFD+			 */+			count = (int)utf16nbytes(s, length);+		} else {+			/*+			 *  UTF-8 NFD ===> UTF-16 NFC+			 *  UTF-8 NFC ===> UTF-16 NFD+			 */+			count = (int)mbsnbytes(s, length);+		}+		u16.s = (char *)dest->s;+		u16.length = dest->length << 1;;+		u16.buffer_length = dest->buffer_length;+		if (sc->flag & SCONV_NORMALIZATION_C)+			ret = archive_string_normalize_C(&u16, s, count, sc);+		else+			ret = archive_string_normalize_D(&u16, s, count, sc);+		dest->s = (wchar_t *)u16.s;+		dest->length = u16.length >> 1;+		dest->buffer_length = u16.buffer_length;+		sc->flag = saved_flag;/* restore the saved flag. */+		return (ret);+	} else if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) {+		count = (int)utf16nbytes(s, length);+		count >>= 1; /* to be WCS length */+		/* Allocate memory for WCS. */+		if (NULL == archive_wstring_ensure(dest,+		    dest->length + count + 1))+			return (-1);+		wmemcpy(dest->s + dest->length, (const wchar_t *)s, count);+		if ((sc->flag & SCONV_FROM_UTF16BE) && !is_big_endian()) {+			uint16_t *u16 = (uint16_t *)(dest->s + dest->length);+			int b;+			for (b = 0; b < count; b++) {+				uint16_t val = archive_le16dec(u16+b);+				archive_be16enc(u16+b, val);+			}+		} else if ((sc->flag & SCONV_FROM_UTF16LE) && is_big_endian()) {+			uint16_t *u16 = (uint16_t *)(dest->s + dest->length);+			int b;+			for (b = 0; b < count; b++) {+				uint16_t val = archive_be16dec(u16+b);+				archive_le16enc(u16+b, val);+			}+		}+	} else {+		DWORD mbflag;+		size_t buffsize;++		if (sc == NULL)+			mbflag = 0;+		else if (sc->flag & SCONV_FROM_CHARSET) {+			/* Do not trust the length which comes from+			 * an archive file. */+			length = mbsnbytes(s, length);+			mbflag = 0;+		} else+			mbflag = MB_PRECOMPOSED;++		buffsize = dest->length + length + 1;+		do {+			/* Allocate memory for WCS. */+			if (NULL == archive_wstring_ensure(dest, buffsize))+				return (-1);+			/* Convert MBS to WCS. */+			count = MultiByteToWideChar(from_cp,+			    mbflag, s, (int)length, dest->s + dest->length,+			    (int)(dest->buffer_length >> 1) -1);+			if (count == 0 &&+			    GetLastError() == ERROR_INSUFFICIENT_BUFFER) {+				/* Expand the WCS buffer. */+				buffsize = dest->buffer_length << 1;+				continue;+			}+			if (count == 0 && length != 0)+				ret = -1;+			break;+		} while (1);+	}+	dest->length += count;+	dest->s[dest->length] = L'\0';+	return (ret);+}++#else++/*+ * Convert MBS to WCS.+ * Note: returns -1 if conversion fails.+ */+int+archive_wstring_append_from_mbs(struct archive_wstring *dest,+    const char *p, size_t len)+{+	size_t r;+	int ret_val = 0;+	/*+	 * No single byte will be more than one wide character,+	 * so this length estimate will always be big enough.+	 */+	// size_t wcs_length = len;+	size_t mbs_length = len;+	const char *mbs = p;+	wchar_t *wcs;+#if HAVE_MBRTOWC+	mbstate_t shift_state;++	memset(&shift_state, 0, sizeof(shift_state));+#endif+	/*+	 * As we decided to have wcs_length == mbs_length == len+	 * we can use len here instead of wcs_length+	 */+	if (NULL == archive_wstring_ensure(dest, dest->length + len + 1))+		return (-1);+	wcs = dest->s + dest->length;+	/*+	 * We cannot use mbsrtowcs/mbstowcs here because those may convert+	 * extra MBS when strlen(p) > len and one wide character consists of+	 * multi bytes.+	 */+	while (*mbs && mbs_length > 0) {+		/*+		 * The buffer we allocated is always big enough.+		 * Keep this code path in a comment if we decide to choose+		 * smaller wcs_length in the future+		 */+/*+		if (wcs_length == 0) {+			dest->length = wcs - dest->s;+			dest->s[dest->length] = L'\0';+			wcs_length = mbs_length;+			if (NULL == archive_wstring_ensure(dest,+			    dest->length + wcs_length + 1))+				return (-1);+			wcs = dest->s + dest->length;+		}+*/+#if HAVE_MBRTOWC+		r = mbrtowc(wcs, mbs, mbs_length, &shift_state);+#else+		r = mbtowc(wcs, mbs, mbs_length);+#endif+		if (r == (size_t)-1 || r == (size_t)-2) {+			ret_val = -1;+			break;+		}+		if (r == 0 || r > mbs_length)+			break;+		wcs++;+		// wcs_length--;+		mbs += r;+		mbs_length -= r;+	}+	dest->length = wcs - dest->s;+	dest->s[dest->length] = L'\0';+	return (ret_val);+}++#endif++#if defined(_WIN32) && !defined(__CYGWIN__)++/*+ * WCS ==> MBS.+ * Note: returns -1 if conversion fails.+ *+ * Win32 builds use WideCharToMultiByte from the Windows API.+ * (Maybe Cygwin should too?  WideCharToMultiByte will know a+ * lot more about local character encodings than the wcrtomb()+ * wrapper is going to know.)+ */+int+archive_string_append_from_wcs(struct archive_string *as,+    const wchar_t *w, size_t len)+{+	return archive_string_append_from_wcs_in_codepage(as, w, len, NULL);+}++static int+archive_string_append_from_wcs_in_codepage(struct archive_string *as,+    const wchar_t *ws, size_t len, struct archive_string_conv *sc)+{+	BOOL defchar_used, *dp;+	int count, ret = 0;+	UINT to_cp;+	int wslen = (int)len;++	if (sc != NULL)+		to_cp = sc->to_cp;+	else+		to_cp = get_current_codepage();++	if (to_cp == CP_C_LOCALE) {+		/*+		 * "C" locale special processing.+		 */+		const wchar_t *wp = ws;+		char *p;++		if (NULL == archive_string_ensure(as,+		    as->length + wslen +1))+			return (-1);+		p = as->s + as->length;+		count = 0;+		defchar_used = 0;+		while (count < wslen && *wp) {+			if (*wp > 255) {+				*p++ = '?';+				wp++;+				defchar_used = 1;+			} else+				*p++ = (char)*wp++;+			count++;+		}+	} else if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) {+		uint16_t *u16;++		if (NULL ==+		    archive_string_ensure(as, as->length + len * 2 + 2))+			return (-1);+		u16 = (uint16_t *)(as->s + as->length);+		count = 0;+		defchar_used = 0;+		if (sc->flag & SCONV_TO_UTF16BE) {+			while (count < (int)len && *ws) {+				archive_be16enc(u16+count, *ws);+				ws++;+				count++;+			}+		} else {+			while (count < (int)len && *ws) {+				archive_le16enc(u16+count, *ws);+				ws++;+				count++;+			}+		}+		count <<= 1; /* to be byte size */+	} else {+		/* Make sure the MBS buffer has plenty to set. */+		if (NULL ==+		    archive_string_ensure(as, as->length + len * 2 + 1))+			return (-1);+		do {+			defchar_used = 0;+			if (to_cp == CP_UTF8 || sc == NULL)+				dp = NULL;+			else+				dp = &defchar_used;+			count = WideCharToMultiByte(to_cp, 0, ws, wslen,+			    as->s + as->length,+			    (int)as->buffer_length - as->length - 1, NULL, dp);+			if (count == 0 &&+			    GetLastError() == ERROR_INSUFFICIENT_BUFFER) {+				/* Expand the MBS buffer and retry. */+				if (NULL == archive_string_ensure(as,+					as->buffer_length + len))+					return (-1);+				continue;+			}+			if (count == 0)+				ret = -1;+			break;+		} while (1);+	}+	as->length += count;+	as->s[as->length] = '\0';+	return (defchar_used?-1:ret);+}++#elif defined(HAVE_WCTOMB) || defined(HAVE_WCRTOMB)++/*+ * Translates a wide character string into current locale character set+ * and appends to the archive_string.  Note: returns -1 if conversion+ * fails.+ */+int+archive_string_append_from_wcs(struct archive_string *as,+    const wchar_t *w, size_t len)+{+	/* We cannot use the standard wcstombs() here because it+	 * cannot tell us how big the output buffer should be.  So+	 * I've built a loop around wcrtomb() or wctomb() that+	 * converts a character at a time and resizes the string as+	 * needed.  We prefer wcrtomb() when it's available because+	 * it's thread-safe. */+	int n, ret_val = 0;+	char *p;+	char *end;+#if HAVE_WCRTOMB+	mbstate_t shift_state;++	memset(&shift_state, 0, sizeof(shift_state));+#else+	/* Clear the shift state before starting. */+	wctomb(NULL, L'\0');+#endif+	/*+	 * Allocate buffer for MBS.+	 * We need this allocation here since it is possible that+	 * as->s is still NULL.+	 */+	if (archive_string_ensure(as, as->length + len + 1) == NULL)+		return (-1);++	p = as->s + as->length;+	end = as->s + as->buffer_length - MB_CUR_MAX -1;+	while (*w != L'\0' && len > 0) {+		if (p >= end) {+			as->length = p - as->s;+			as->s[as->length] = '\0';+			/* Re-allocate buffer for MBS. */+			if (archive_string_ensure(as,+			    as->length + max(len * 2,+			    (size_t)MB_CUR_MAX) + 1) == NULL)+				return (-1);+			p = as->s + as->length;+			end = as->s + as->buffer_length - MB_CUR_MAX -1;+		}+#if HAVE_WCRTOMB+		n = wcrtomb(p, *w++, &shift_state);+#else+		n = wctomb(p, *w++);+#endif+		if (n == -1) {+			if (errno == EILSEQ) {+				/* Skip an illegal wide char. */+				*p++ = '?';+				ret_val = -1;+			} else {+				ret_val = -1;+				break;+			}+		} else+			p += n;+		len--;+	}+	as->length = p - as->s;+	as->s[as->length] = '\0';+	return (ret_val);+}++#else /* HAVE_WCTOMB || HAVE_WCRTOMB */++/*+ * TODO: Test if __STDC_ISO_10646__ is defined.+ * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion+ * one character at a time.  If a non-Windows platform doesn't have+ * either of these, fall back to the built-in UTF8 conversion.+ */+int+archive_string_append_from_wcs(struct archive_string *as,+    const wchar_t *w, size_t len)+{+	(void)as;/* UNUSED */+	(void)w;/* UNUSED */+	(void)len;/* UNUSED */+	errno = ENOSYS;+	return (-1);+}++#endif /* HAVE_WCTOMB || HAVE_WCRTOMB */++/*+ * Find a string conversion object by a pair of 'from' charset name+ * and 'to' charset name from an archive object.+ * Return NULL if not found.+ */+static struct archive_string_conv *+find_sconv_object(struct archive *a, const char *fc, const char *tc)+{+	struct archive_string_conv *sc; ++	if (a == NULL)+		return (NULL);++	for (sc = a->sconv; sc != NULL; sc = sc->next) {+		if (strcmp(sc->from_charset, fc) == 0 &&+		    strcmp(sc->to_charset, tc) == 0)+			break;+	}+	return (sc);+}++/*+ * Register a string object to an archive object.+ */+static void+add_sconv_object(struct archive *a, struct archive_string_conv *sc)+{+	struct archive_string_conv **psc; ++	/* Add a new sconv to sconv list. */+	psc = &(a->sconv);+	while (*psc != NULL)+		psc = &((*psc)->next);+	*psc = sc;+}++static void+add_converter(struct archive_string_conv *sc, int (*converter)+    (struct archive_string *, const void *, size_t,+     struct archive_string_conv *))+{+	if (sc == NULL || sc->nconverter >= 2)+		__archive_errx(1, "Programming error");+	sc->converter[sc->nconverter++] = converter;+}++static void+setup_converter(struct archive_string_conv *sc)+{++	/* Reset. */+	sc->nconverter = 0;++	/*+	 * Perform special sequence for the incorrect UTF-8 filenames+	 * made by libarchive2.x.+	 */+	if (sc->flag & SCONV_UTF8_LIBARCHIVE_2) {+		add_converter(sc, strncat_from_utf8_libarchive2);+		return;+	}++	/*+	 * Convert a string to UTF-16BE/LE.+	 */+	if (sc->flag & SCONV_TO_UTF16) {+		/*+		 * If the current locale is UTF-8, we can translate+		 * a UTF-8 string into a UTF-16BE string.+		 */+		if (sc->flag & SCONV_FROM_UTF8) {+			add_converter(sc, archive_string_append_unicode);+			return;+		}++#if defined(_WIN32) && !defined(__CYGWIN__)+		if (sc->flag & SCONV_WIN_CP) {+			if (sc->flag & SCONV_TO_UTF16BE)+				add_converter(sc, win_strncat_to_utf16be);+			else+				add_converter(sc, win_strncat_to_utf16le);+			return;+		}+#endif++#if defined(HAVE_ICONV)+		if (sc->cd != (iconv_t)-1) {+			add_converter(sc, iconv_strncat_in_locale);+			return;+		}+#endif++		if (sc->flag & SCONV_BEST_EFFORT) {+			if (sc->flag & SCONV_TO_UTF16BE)+				add_converter(sc,+					best_effort_strncat_to_utf16be);+			else+				add_converter(sc,+					best_effort_strncat_to_utf16le);+		} else+			/* Make sure we have no converter. */+			sc->nconverter = 0;+		return;+	}++	/*+	 * Convert a string from UTF-16BE/LE.+	 */+	if (sc->flag & SCONV_FROM_UTF16) {+		/*+		 * At least we should normalize a UTF-16BE string.+		 */+		if (sc->flag & SCONV_NORMALIZATION_D)+			add_converter(sc,archive_string_normalize_D);+		else if (sc->flag & SCONV_NORMALIZATION_C)+			add_converter(sc, archive_string_normalize_C);++		if (sc->flag & SCONV_TO_UTF8) {+			/*+			 * If the current locale is UTF-8, we can translate+			 * a UTF-16BE/LE string into a UTF-8 string directly.+			 */+			if (!(sc->flag &+			    (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))+				add_converter(sc,+				    archive_string_append_unicode);+			return;+		}++#if defined(_WIN32) && !defined(__CYGWIN__)+		if (sc->flag & SCONV_WIN_CP) {+			if (sc->flag & SCONV_FROM_UTF16BE)+				add_converter(sc, win_strncat_from_utf16be);+			else+				add_converter(sc, win_strncat_from_utf16le);+			return;+		}+#endif++#if defined(HAVE_ICONV)+		if (sc->cd != (iconv_t)-1) {+			add_converter(sc, iconv_strncat_in_locale);+			return;+		}+#endif++		if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))+		    == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))+			add_converter(sc, best_effort_strncat_from_utf16be);+		else if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))+		    == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))+			add_converter(sc, best_effort_strncat_from_utf16le);+		else+			/* Make sure we have no converter. */+			sc->nconverter = 0;+		return;+	}++	if (sc->flag & SCONV_FROM_UTF8) {+		/*+		 * At least we should normalize a UTF-8 string.+		 */+		if (sc->flag & SCONV_NORMALIZATION_D)+			add_converter(sc,archive_string_normalize_D);+		else if (sc->flag & SCONV_NORMALIZATION_C)+			add_converter(sc, archive_string_normalize_C);++		/*+		 * Copy UTF-8 string with a check of CESU-8.+		 * Apparently, iconv does not check surrogate pairs in UTF-8+		 * when both from-charset and to-charset are UTF-8, and then+		 * we use our UTF-8 copy code.+		 */+		if (sc->flag & SCONV_TO_UTF8) {+			/*+			 * If the current locale is UTF-8, we can translate+			 * a UTF-16BE string into a UTF-8 string directly.+			 */+			if (!(sc->flag &+			    (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))+				add_converter(sc, strncat_from_utf8_to_utf8);+			return;+		}+	}++#if defined(_WIN32) && !defined(__CYGWIN__)+	/*+	 * On Windows we can use Windows API for a string conversion.+	 */+	if (sc->flag & SCONV_WIN_CP) {+		add_converter(sc, strncat_in_codepage);+		return;+	}+#endif++#if HAVE_ICONV+	if (sc->cd != (iconv_t)-1) {+		add_converter(sc, iconv_strncat_in_locale);+		/*+		 * iconv generally does not support UTF-8-MAC and so+		 * we have to the output of iconv from NFC to NFD if+		 * need.+		 */+		if ((sc->flag & SCONV_FROM_CHARSET) &&+		    (sc->flag & SCONV_TO_UTF8)) {+			if (sc->flag & SCONV_NORMALIZATION_D)+				add_converter(sc, archive_string_normalize_D);+		}+		return;+	}+#endif++	/*+	 * Try conversion in the best effort or no conversion.+	 */+	if ((sc->flag & SCONV_BEST_EFFORT) || sc->same)+		add_converter(sc, best_effort_strncat_in_locale);+	else+		/* Make sure we have no converter. */+		sc->nconverter = 0;+}++/*+ * Return canonicalized charset-name but this supports just UTF-8, UTF-16BE+ * and CP932 which are referenced in create_sconv_object().+ */+static const char *+canonical_charset_name(const char *charset)+{+	char cs[16];+	char *p;+	const char *s;++	if (charset == NULL || charset[0] == '\0'+	    || strlen(charset) > 15)+		return (charset);++	/* Copy name to uppercase. */+	p = cs;+	s = charset;+	while (*s) {+		char c = *s++;+		if (c >= 'a' && c <= 'z')+			c -= 'a' - 'A';+		*p++ = c;+	}+	*p++ = '\0';++	if (strcmp(cs, "UTF-8") == 0 ||+	    strcmp(cs, "UTF8") == 0)+		return ("UTF-8");+	if (strcmp(cs, "UTF-16BE") == 0 ||+	    strcmp(cs, "UTF16BE") == 0)+		return ("UTF-16BE");+	if (strcmp(cs, "UTF-16LE") == 0 ||+	    strcmp(cs, "UTF16LE") == 0)+		return ("UTF-16LE");+	if (strcmp(cs, "CP932") == 0)+		return ("CP932");+	return (charset);+}++/*+ * Create a string conversion object.+ */+static struct archive_string_conv *+create_sconv_object(const char *fc, const char *tc,+    unsigned current_codepage, int flag)+{+	struct archive_string_conv *sc; ++	sc = calloc(1, sizeof(*sc));+	if (sc == NULL)+		return (NULL);+	sc->next = NULL;+	sc->from_charset = strdup(fc);+	if (sc->from_charset == NULL) {+		free(sc);+		return (NULL);+	}+	sc->to_charset = strdup(tc);+	if (sc->to_charset == NULL) {+		free(sc->from_charset);+		free(sc);+		return (NULL);+	}+	archive_string_init(&sc->utftmp);++	if (flag & SCONV_TO_CHARSET) {+		/*+		 * Convert characters from the current locale charset to+		 * a specified charset.+		 */+		sc->from_cp = current_codepage;+		sc->to_cp = make_codepage_from_charset(tc);+#if defined(_WIN32) && !defined(__CYGWIN__)+		if (IsValidCodePage(sc->to_cp))+			flag |= SCONV_WIN_CP;+#endif+	} else if (flag & SCONV_FROM_CHARSET) {+		/*+		 * Convert characters from a specified charset to+		 * the current locale charset.+		 */+		sc->to_cp = current_codepage;+		sc->from_cp = make_codepage_from_charset(fc);+#if defined(_WIN32) && !defined(__CYGWIN__)+		if (IsValidCodePage(sc->from_cp))+			flag |= SCONV_WIN_CP;+#endif+	}++	/*+	 * Check if "from charset" and "to charset" are the same.+	 */+	if (strcmp(fc, tc) == 0 ||+	    (sc->from_cp != (unsigned)-1 && sc->from_cp == sc->to_cp))+		sc->same = 1;+	else+		sc->same = 0;++	/*+	 * Mark if "from charset" or "to charset" are UTF-8 or UTF-16BE/LE.+	 */+	if (strcmp(tc, "UTF-8") == 0)+		flag |= SCONV_TO_UTF8;+	else if (strcmp(tc, "UTF-16BE") == 0)+		flag |= SCONV_TO_UTF16BE;+	else if (strcmp(tc, "UTF-16LE") == 0)+		flag |= SCONV_TO_UTF16LE;+	if (strcmp(fc, "UTF-8") == 0)+		flag |= SCONV_FROM_UTF8;+	else if (strcmp(fc, "UTF-16BE") == 0)+		flag |= SCONV_FROM_UTF16BE;+	else if (strcmp(fc, "UTF-16LE") == 0)+		flag |= SCONV_FROM_UTF16LE;+#if defined(_WIN32) && !defined(__CYGWIN__)+	if (sc->to_cp == CP_UTF8)+		flag |= SCONV_TO_UTF8;+	else if (sc->to_cp == CP_UTF16BE)+		flag |= SCONV_TO_UTF16BE | SCONV_WIN_CP;+	else if (sc->to_cp == CP_UTF16LE)+		flag |= SCONV_TO_UTF16LE | SCONV_WIN_CP;+	if (sc->from_cp == CP_UTF8)+		flag |= SCONV_FROM_UTF8;+	else if (sc->from_cp == CP_UTF16BE)+		flag |= SCONV_FROM_UTF16BE | SCONV_WIN_CP;+	else if (sc->from_cp == CP_UTF16LE)+		flag |= SCONV_FROM_UTF16LE | SCONV_WIN_CP;+#endif++	/*+	 * Set a flag for Unicode NFD. Usually iconv cannot correctly+	 * handle it. So we have to translate NFD characters to NFC ones+	 * ourselves before iconv handles. Another reason is to prevent+	 * that the same sight of two filenames, one is NFC and other+	 * is NFD, would be in its directory.+	 * On Mac OS X, although its filesystem layer automatically+	 * convert filenames to NFD, it would be useful for filename+	 * comparing to find out the same filenames that we normalize+	 * that to be NFD ourselves.+	 */+	if ((flag & SCONV_FROM_CHARSET) &&+	    (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8))) {+#if defined(__APPLE__)+		if (flag & SCONV_TO_UTF8)+			flag |= SCONV_NORMALIZATION_D;+		else+#endif+			flag |= SCONV_NORMALIZATION_C;+	}+#if defined(__APPLE__)+	/*+	 * In case writing an archive file, make sure that a filename+	 * going to be passed to iconv is a Unicode NFC string since+	 * a filename in HFS Plus filesystem is a Unicode NFD one and+	 * iconv cannot handle it with "UTF-8" charset. It is simpler+	 * than a use of "UTF-8-MAC" charset.+	 */+	if ((flag & SCONV_TO_CHARSET) &&+	    (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&+	    !(flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8)))+		flag |= SCONV_NORMALIZATION_C;+	/*+	 * In case reading an archive file. make sure that a filename+	 * will be passed to users is a Unicode NFD string in order to+	 * correctly compare the filename with other one which comes+	 * from HFS Plus filesystem.+	 */+	if ((flag & SCONV_FROM_CHARSET) &&+	   !(flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&+	    (flag & SCONV_TO_UTF8))+		flag |= SCONV_NORMALIZATION_D;+#endif++#if defined(HAVE_ICONV)+	sc->cd_w = (iconv_t)-1;+	/*+	 * Create an iconv object.+	 */+	if (((flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) &&+	    (flag & (SCONV_FROM_UTF8 | SCONV_FROM_UTF16))) ||+	    (flag & SCONV_WIN_CP)) {+		/* This case we won't use iconv. */+		sc->cd = (iconv_t)-1;+	} else {+		sc->cd = iconv_open(tc, fc);+		if (sc->cd == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) {+			/*+			 * Unfortunately, all of iconv implements do support+			 * "CP932" character-set, so we should use "SJIS"+			 * instead if iconv_open failed.+			 */+			if (strcmp(tc, "CP932") == 0)+				sc->cd = iconv_open("SJIS", fc);+			else if (strcmp(fc, "CP932") == 0)+				sc->cd = iconv_open(tc, "SJIS");+		}+#if defined(_WIN32) && !defined(__CYGWIN__)+		/*+		 * archive_mstring on Windows directly convert multi-bytes+		 * into archive_wstring in order not to depend on locale+		 * so that you can do a I18N programming. This will be+		 * used only in archive_mstring_copy_mbs_len_l so far.+		 */+		if (flag & SCONV_FROM_CHARSET) {+			sc->cd_w = iconv_open("UTF-8", fc);+			if (sc->cd_w == (iconv_t)-1 &&+			    (sc->flag & SCONV_BEST_EFFORT)) {+				if (strcmp(fc, "CP932") == 0)+					sc->cd_w = iconv_open("UTF-8", "SJIS");+			}+		}+#endif /* _WIN32 && !__CYGWIN__ */+	}+#endif	/* HAVE_ICONV */++	sc->flag = flag;++	/*+	 * Set up converters.+	 */+	setup_converter(sc);++	return (sc);+}++/*+ * Free a string conversion object.+ */+static void+free_sconv_object(struct archive_string_conv *sc)+{+	free(sc->from_charset);+	free(sc->to_charset);+	archive_string_free(&sc->utftmp);+#if HAVE_ICONV+	if (sc->cd != (iconv_t)-1)+		iconv_close(sc->cd);+	if (sc->cd_w != (iconv_t)-1)+		iconv_close(sc->cd_w);+#endif+	free(sc);+}++#if defined(_WIN32) && !defined(__CYGWIN__)+static unsigned+my_atoi(const char *p)+{+	unsigned cp;++	cp = 0;+	while (*p) {+		if (*p >= '0' && *p <= '9')+			cp = cp * 10 + (*p - '0');+		else+			return (-1);+		p++;+	}+	return (cp);+}++/*+ * Translate Charset name (as used by iconv) into CodePage (as used by Windows)+ * Return -1 if failed.+ *+ * Note: This translation code may be insufficient.+ */+static struct charset {+	const char *name;+	unsigned cp;+} charsets[] = {+	/* MUST BE SORTED! */+	{"ASCII", 1252},+	{"ASMO-708", 708},+	{"BIG5", 950},+	{"CHINESE", 936},+	{"CP367", 1252},+	{"CP819", 1252},+	{"CP1025", 21025},+	{"DOS-720", 720},+	{"DOS-862", 862},+	{"EUC-CN", 51936},+	{"EUC-JP", 51932},+	{"EUC-KR", 949},+	{"EUCCN", 51936},+	{"EUCJP", 51932},+	{"EUCKR", 949},+	{"GB18030", 54936},+	{"GB2312", 936},+	{"HEBREW", 1255},+	{"HZ-GB-2312", 52936},+	{"IBM273", 20273},+	{"IBM277", 20277},+	{"IBM278", 20278},+	{"IBM280", 20280},+	{"IBM284", 20284},+	{"IBM285", 20285},+	{"IBM290", 20290},+	{"IBM297", 20297},+	{"IBM367", 1252},+	{"IBM420", 20420},+	{"IBM423", 20423},+	{"IBM424", 20424},+	{"IBM819", 1252},+	{"IBM871", 20871},+	{"IBM880", 20880},+	{"IBM905", 20905},+	{"IBM924", 20924},+	{"ISO-8859-1", 28591},+	{"ISO-8859-13", 28603},+	{"ISO-8859-15", 28605},+	{"ISO-8859-2", 28592},+	{"ISO-8859-3", 28593},+	{"ISO-8859-4", 28594},+	{"ISO-8859-5", 28595},+	{"ISO-8859-6", 28596},+	{"ISO-8859-7", 28597},+	{"ISO-8859-8", 28598},+	{"ISO-8859-9", 28599},+	{"ISO8859-1", 28591},+	{"ISO8859-13", 28603},+	{"ISO8859-15", 28605},+	{"ISO8859-2", 28592},+	{"ISO8859-3", 28593},+	{"ISO8859-4", 28594},+	{"ISO8859-5", 28595},+	{"ISO8859-6", 28596},+	{"ISO8859-7", 28597},+	{"ISO8859-8", 28598},+	{"ISO8859-9", 28599},+	{"JOHAB", 1361},+	{"KOI8-R", 20866},+	{"KOI8-U", 21866},+	{"KS_C_5601-1987", 949},+	{"LATIN1", 1252},+	{"LATIN2", 28592},+	{"MACINTOSH", 10000},+	{"SHIFT-JIS", 932},+	{"SHIFT_JIS", 932},+	{"SJIS", 932},+	{"US", 1252},+	{"US-ASCII", 1252},+	{"UTF-16", 1200},+	{"UTF-16BE", 1201},+	{"UTF-16LE", 1200},+	{"UTF-8", CP_UTF8},+	{"X-EUROPA", 29001},+	{"X-MAC-ARABIC", 10004},+	{"X-MAC-CE", 10029},+	{"X-MAC-CHINESEIMP", 10008},+	{"X-MAC-CHINESETRAD", 10002},+	{"X-MAC-CROATIAN", 10082},+	{"X-MAC-CYRILLIC", 10007},+	{"X-MAC-GREEK", 10006},+	{"X-MAC-HEBREW", 10005},+	{"X-MAC-ICELANDIC", 10079},+	{"X-MAC-JAPANESE", 10001},+	{"X-MAC-KOREAN", 10003},+	{"X-MAC-ROMANIAN", 10010},+	{"X-MAC-THAI", 10021},+	{"X-MAC-TURKISH", 10081},+	{"X-MAC-UKRAINIAN", 10017},+};+static unsigned+make_codepage_from_charset(const char *charset)+{+	char cs[16];+	char *p;+	unsigned cp;+	int a, b;++	if (charset == NULL || strlen(charset) > 15)+		return -1;++	/* Copy name to uppercase. */+	p = cs;+	while (*charset) {+		char c = *charset++;+		if (c >= 'a' && c <= 'z')+			c -= 'a' - 'A';+		*p++ = c;+	}+	*p++ = '\0';+	cp = -1;++	/* Look it up in the table first, so that we can easily+	 * override CP367, which we map to 1252 instead of 367. */+	a = 0;+	b = sizeof(charsets)/sizeof(charsets[0]);+	while (b > a) {+		int c = (b + a) / 2;+		int r = strcmp(charsets[c].name, cs);+		if (r < 0)+			a = c + 1;+		else if (r > 0)+			b = c;+		else+			return charsets[c].cp;+	}++	/* If it's not in the table, try to parse it. */+	switch (*cs) {+	case 'C':+		if (cs[1] == 'P' && cs[2] >= '0' && cs[2] <= '9') {+			cp = my_atoi(cs + 2);+		} else if (strcmp(cs, "CP_ACP") == 0)+			cp = get_current_codepage();+		else if (strcmp(cs, "CP_OEMCP") == 0)+			cp = get_current_oemcp();+		break;+	case 'I':+		if (cs[1] == 'B' && cs[2] == 'M' &&+		    cs[3] >= '0' && cs[3] <= '9') {+			cp = my_atoi(cs + 3);+		}+		break;+	case 'W':+		if (strncmp(cs, "WINDOWS-", 8) == 0) {+			cp = my_atoi(cs + 8);+			if (cp != 874 && (cp < 1250 || cp > 1258))+				cp = -1;/* This may invalid code. */+		}+		break;+	}+	return (cp);+}++/*+ * Return ANSI Code Page of current locale set by setlocale().+ */+static unsigned+get_current_codepage(void)+{+	char *locale, *p;+	unsigned cp;++	locale = setlocale(LC_CTYPE, NULL);+	if (locale == NULL)+		return (GetACP());+	if (locale[0] == 'C' && locale[1] == '\0')+		return (CP_C_LOCALE);+	p = strrchr(locale, '.');+	if (p == NULL)+		return (GetACP());+	if (strcmp(p+1, "utf8") == 0)+		return CP_UTF8;+	cp = my_atoi(p+1);+	if ((int)cp <= 0)+		return (GetACP());+	return (cp);+}++/*+ * Translation table between Locale Name and ACP/OEMCP.+ */+static struct {+	unsigned acp;+	unsigned ocp;+	const char *locale;+} acp_ocp_map[] = {+	{  950,  950, "Chinese_Taiwan" },+	{  936,  936, "Chinese_People's Republic of China" },+	{  950,  950, "Chinese_Taiwan" },+	{ 1250,  852, "Czech_Czech Republic" },+	{ 1252,  850, "Danish_Denmark" },+	{ 1252,  850, "Dutch_Netherlands" },+	{ 1252,  850, "Dutch_Belgium" },+	{ 1252,  437, "English_United States" },+	{ 1252,  850, "English_Australia" },+	{ 1252,  850, "English_Canada" },+	{ 1252,  850, "English_New Zealand" },+	{ 1252,  850, "English_United Kingdom" },+	{ 1252,  437, "English_United States" },+	{ 1252,  850, "Finnish_Finland" },+	{ 1252,  850, "French_France" },+	{ 1252,  850, "French_Belgium" },+	{ 1252,  850, "French_Canada" },+	{ 1252,  850, "French_Switzerland" },+	{ 1252,  850, "German_Germany" },+	{ 1252,  850, "German_Austria" },+	{ 1252,  850, "German_Switzerland" },+	{ 1253,  737, "Greek_Greece" },+	{ 1250,  852, "Hungarian_Hungary" },+	{ 1252,  850, "Icelandic_Iceland" },+	{ 1252,  850, "Italian_Italy" },+	{ 1252,  850, "Italian_Switzerland" },+	{  932,  932, "Japanese_Japan" },+	{  949,  949, "Korean_Korea" },+	{ 1252,  850, "Norwegian (BokmOl)_Norway" },+	{ 1252,  850, "Norwegian (BokmOl)_Norway" },+	{ 1252,  850, "Norwegian-Nynorsk_Norway" },+	{ 1250,  852, "Polish_Poland" },+	{ 1252,  850, "Portuguese_Portugal" },+	{ 1252,  850, "Portuguese_Brazil" },+	{ 1251,  866, "Russian_Russia" },+	{ 1250,  852, "Slovak_Slovakia" },+	{ 1252,  850, "Spanish_Spain" },+	{ 1252,  850, "Spanish_Mexico" },+	{ 1252,  850, "Spanish_Spain" },+	{ 1252,  850, "Swedish_Sweden" },+	{ 1254,  857, "Turkish_Turkey" },+	{ 0, 0, NULL}+};++/*+ * Return OEM Code Page of current locale set by setlocale().+ */+static unsigned+get_current_oemcp(void)+{+	int i;+	char *locale, *p;+	size_t len;++	locale = setlocale(LC_CTYPE, NULL);+	if (locale == NULL)+		return (GetOEMCP());+	if (locale[0] == 'C' && locale[1] == '\0')+		return (CP_C_LOCALE);++	p = strrchr(locale, '.');+	if (p == NULL)+		return (GetOEMCP());+	len = p - locale;+	for (i = 0; acp_ocp_map[i].acp; i++) {+		if (strncmp(acp_ocp_map[i].locale, locale, len) == 0)+			return (acp_ocp_map[i].ocp);+	}+	return (GetOEMCP());+}+#else++/*+ * POSIX platform does not use CodePage.+ */++static unsigned+get_current_codepage(void)+{+	return (-1);/* Unknown */+}+static unsigned+make_codepage_from_charset(const char *charset)+{+	(void)charset; /* UNUSED */+	return (-1);/* Unknown */+}+static unsigned+get_current_oemcp(void)+{+	return (-1);/* Unknown */+}++#endif /* defined(_WIN32) && !defined(__CYGWIN__) */++/*+ * Return a string conversion object.+ */+static struct archive_string_conv *+get_sconv_object(struct archive *a, const char *fc, const char *tc, int flag)+{+	struct archive_string_conv *sc;+	unsigned current_codepage;++	/* Check if we have made the sconv object. */+	sc = find_sconv_object(a, fc, tc);+	if (sc != NULL)+		return (sc);++	if (a == NULL)+		current_codepage = get_current_codepage();+	else+		current_codepage = a->current_codepage;++	sc = create_sconv_object(canonical_charset_name(fc),+	    canonical_charset_name(tc), current_codepage, flag);+	if (sc == NULL) {+		if (a != NULL)+			archive_set_error(a, ENOMEM,+			    "Could not allocate memory for "+			    "a string conversion object");+		return (NULL);+	}++	/*+	 * If there is no converter for current string conversion object,+	 * we cannot handle this conversion.+	 */+	if (sc->nconverter == 0) {+		if (a != NULL) {+#if HAVE_ICONV+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "iconv_open failed : Cannot handle ``%s''",+			    (flag & SCONV_TO_CHARSET)?tc:fc);+#else+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "A character-set conversion not fully supported "+			    "on this platform");+#endif+		}+		/* Failed; free a sconv object. */+		free_sconv_object(sc);+		return (NULL);+	}++	/*+	 * Success!+	 */+	if (a != NULL)+		add_sconv_object(a, sc);+	return (sc);+}++static const char *+get_current_charset(struct archive *a)+{+	const char *cur_charset;++	if (a == NULL)+		cur_charset = default_iconv_charset("");+	else {+		cur_charset = default_iconv_charset(a->current_code);+		if (a->current_code == NULL) {+			a->current_code = strdup(cur_charset);+			a->current_codepage = get_current_codepage();+			a->current_oemcp = get_current_oemcp();+		}+	}+	return (cur_charset);+}++/*+ * Make and Return a string conversion object.+ * Return NULL if the platform does not support the specified conversion+ * and best_effort is 0.+ * If best_effort is set, A string conversion object must be returned+ * unless memory allocation for the object fails, but the conversion+ * might fail when non-ASCII code is found.+ */+struct archive_string_conv *+archive_string_conversion_to_charset(struct archive *a, const char *charset,+    int best_effort)+{+	int flag = SCONV_TO_CHARSET;++	if (best_effort)+		flag |= SCONV_BEST_EFFORT;+	return (get_sconv_object(a, get_current_charset(a), charset, flag));+}++struct archive_string_conv *+archive_string_conversion_from_charset(struct archive *a, const char *charset,+    int best_effort)+{+	int flag = SCONV_FROM_CHARSET;++	if (best_effort)+		flag |= SCONV_BEST_EFFORT;+	return (get_sconv_object(a, charset, get_current_charset(a), flag));+}++/*+ * archive_string_default_conversion_*_archive() are provided for Windows+ * platform because other archiver application use CP_OEMCP for+ * MultiByteToWideChar() and WideCharToMultiByte() for the filenames+ * in tar or zip files. But mbstowcs/wcstombs(CRT) usually use CP_ACP+ * unless you use setlocale(LC_ALL, ".OCP")(specify CP_OEMCP).+ * So we should make a string conversion between CP_ACP and CP_OEMCP+ * for compatibility.+ */+#if defined(_WIN32) && !defined(__CYGWIN__)+struct archive_string_conv *+archive_string_default_conversion_for_read(struct archive *a)+{+	const char *cur_charset = get_current_charset(a);+	char oemcp[16];++	/* NOTE: a check of cur_charset is unneeded but we need+	 * that get_current_charset() has been surely called at+	 * this time whatever C compiler optimized. */+	if (cur_charset != NULL &&+	    (a->current_codepage == CP_C_LOCALE ||+	     a->current_codepage == a->current_oemcp))+		return (NULL);/* no conversion. */++	_snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);+	/* Make sure a null termination must be set. */+	oemcp[sizeof(oemcp)-1] = '\0';+	return (get_sconv_object(a, oemcp, cur_charset,+	    SCONV_FROM_CHARSET));+}++struct archive_string_conv *+archive_string_default_conversion_for_write(struct archive *a)+{+	const char *cur_charset = get_current_charset(a);+	char oemcp[16];++	/* NOTE: a check of cur_charset is unneeded but we need+	 * that get_current_charset() has been surely called at+	 * this time whatever C compiler optimized. */+	if (cur_charset != NULL &&+	    (a->current_codepage == CP_C_LOCALE ||+	     a->current_codepage == a->current_oemcp))+		return (NULL);/* no conversion. */++	_snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);+	/* Make sure a null termination must be set. */+	oemcp[sizeof(oemcp)-1] = '\0';+	return (get_sconv_object(a, cur_charset, oemcp,+	    SCONV_TO_CHARSET));+}+#else+struct archive_string_conv *+archive_string_default_conversion_for_read(struct archive *a)+{+	(void)a; /* UNUSED */+	return (NULL);+}++struct archive_string_conv *+archive_string_default_conversion_for_write(struct archive *a)+{+	(void)a; /* UNUSED */+	return (NULL);+}+#endif++/*+ * Dispose of all character conversion objects in the archive object.+ */+void+archive_string_conversion_free(struct archive *a)+{+	struct archive_string_conv *sc; +	struct archive_string_conv *sc_next; ++	for (sc = a->sconv; sc != NULL; sc = sc_next) {+		sc_next = sc->next;+		free_sconv_object(sc);+	}+	a->sconv = NULL;+	free(a->current_code);+	a->current_code = NULL;+}++/*+ * Return a conversion charset name.+ */+const char *+archive_string_conversion_charset_name(struct archive_string_conv *sc)+{+	if (sc->flag & SCONV_TO_CHARSET)+		return (sc->to_charset);+	else+		return (sc->from_charset);+}++/*+ * Change the behavior of a string conversion.+ */+void+archive_string_conversion_set_opt(struct archive_string_conv *sc, int opt)+{+	switch (opt) {+	/*+	 * A filename in UTF-8 was made with libarchive 2.x in a wrong+	 * assumption that wchar_t was Unicode.+	 * This option enables simulating the assumption in order to read+	 * that filename correctly.+	 */+	case SCONV_SET_OPT_UTF8_LIBARCHIVE2X:+#if (defined(_WIN32) && !defined(__CYGWIN__)) \+	 || defined(__STDC_ISO_10646__) || defined(__APPLE__)+		/*+		 * Nothing to do for it since wchar_t on these platforms+		 * is really Unicode.+		 */+		(void)sc; /* UNUSED */+#else+		if ((sc->flag & SCONV_UTF8_LIBARCHIVE_2) == 0) {+			sc->flag |= SCONV_UTF8_LIBARCHIVE_2;+			/* Set up string converters. */+			setup_converter(sc);+		}+#endif+		break;+	case SCONV_SET_OPT_NORMALIZATION_C:+		if ((sc->flag & SCONV_NORMALIZATION_C) == 0) {+			sc->flag |= SCONV_NORMALIZATION_C;+			sc->flag &= ~SCONV_NORMALIZATION_D;+			/* Set up string converters. */+			setup_converter(sc);+		}+		break;+	case SCONV_SET_OPT_NORMALIZATION_D:+#if defined(HAVE_ICONV)+		/*+		 * If iconv will take the string, do not change the+		 * setting of the normalization.+		 */+		if (!(sc->flag & SCONV_WIN_CP) &&+		     (sc->flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&+		    !(sc->flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8)))+			break;+#endif+		if ((sc->flag & SCONV_NORMALIZATION_D) == 0) {+			sc->flag |= SCONV_NORMALIZATION_D;+			sc->flag &= ~SCONV_NORMALIZATION_C;+			/* Set up string converters. */+			setup_converter(sc);+		}+		break;+	default:+		break;+	}+}++/*+ *+ * Copy one archive_string to another in locale conversion.+ *+ *	archive_strncat_l();+ *	archive_strncpy_l();+ *+ */++static size_t+mbsnbytes(const void *_p, size_t n)+{+	size_t s;+	const char *p, *pp;++	if (_p == NULL)+		return (0);+	p = (const char *)_p;++	/* Like strlen(p), except won't examine positions beyond p[n]. */+	s = 0;+	pp = p;+	while (s < n && *pp) {+		pp++;+		s++;+	}+	return (s);+}++static size_t+utf16nbytes(const void *_p, size_t n)+{+	size_t s;+	const char *p, *pp;++	if (_p == NULL)+		return (0);+	p = (const char *)_p;++	/* Like strlen(p), except won't examine positions beyond p[n]. */+	s = 0;+	pp = p;+	n >>= 1;+	while (s < n && (pp[0] || pp[1])) {+		pp += 2;+		s++;+	}+	return (s<<1);+}++int+archive_strncpy_l(struct archive_string *as, const void *_p, size_t n,+    struct archive_string_conv *sc)+{+	as->length = 0;+	return (archive_strncat_l(as, _p, n, sc));+}++int+archive_strncat_l(struct archive_string *as, const void *_p, size_t n,+    struct archive_string_conv *sc)+{+	const void *s;+	size_t length = 0;+	int i, r = 0, r2;++	if (_p != NULL && n > 0) {+		if (sc != NULL && (sc->flag & SCONV_FROM_UTF16))+			length = utf16nbytes(_p, n);+		else+			length = mbsnbytes(_p, n);+	}++	/* We must allocate memory even if there is no data for conversion+	 * or copy. This simulates archive_string_append behavior. */+	if (length == 0) {+		int tn = 1;+		if (sc != NULL && (sc->flag & SCONV_TO_UTF16))+			tn = 2;+		if (archive_string_ensure(as, as->length + tn) == NULL)+			return (-1);+		as->s[as->length] = 0;+		if (tn == 2)+			as->s[as->length+1] = 0;+		return (0);+	}++	/*+	 * If sc is NULL, we just make a copy.+	 */+	if (sc == NULL) {+		if (archive_string_append(as, _p, length) == NULL)+			return (-1);/* No memory */+		return (0);+	}++	s = _p;+	i = 0;+	if (sc->nconverter > 1) {+		sc->utftmp.length = 0;+		r2 = sc->converter[0](&(sc->utftmp), s, length, sc);+		if (r2 != 0 && errno == ENOMEM)+			return (r2);+		if (r > r2)+			r = r2;+		s = sc->utftmp.s;+		length = sc->utftmp.length;+		++i;+	}+	r2 = sc->converter[i](as, s, length, sc);+	if (r > r2)+		r = r2;+	return (r);+}++#if HAVE_ICONV++/*+ * Return -1 if conversion fails.+ */+static int+iconv_strncat_in_locale(struct archive_string *as, const void *_p,+    size_t length, struct archive_string_conv *sc)+{+	ICONV_CONST char *itp;+	size_t remaining;+	iconv_t cd;+	char *outp;+	size_t avail, bs;+	int return_value = 0; /* success */+	int to_size, from_size;++	if (sc->flag & SCONV_TO_UTF16)+		to_size = 2;+	else+		to_size = 1;+	if (sc->flag & SCONV_FROM_UTF16)+		from_size = 2;+	else+		from_size = 1;++	if (archive_string_ensure(as, as->length + length*2+to_size) == NULL)+		return (-1);++	cd = sc->cd;+	itp = (char *)(uintptr_t)_p;+	remaining = length;+	outp = as->s + as->length;+	avail = as->buffer_length - as->length - to_size;+	while (remaining >= (size_t)from_size) {+		size_t result = iconv(cd, &itp, &remaining, &outp, &avail);++		if (result != (size_t)-1)+			break; /* Conversion completed. */++		if (errno == EILSEQ || errno == EINVAL) {+			/*+		 	 * If an output charset is UTF-8 or UTF-16BE/LE,+			 * unknown character should be U+FFFD+			 * (replacement character).+			 */+			if (sc->flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) {+				size_t rbytes;+				if (sc->flag & SCONV_TO_UTF8)+					rbytes = sizeof(utf8_replacement_char);+				else+					rbytes = 2;++				if (avail < rbytes) {+					as->length = outp - as->s;+					bs = as->buffer_length ++					    (remaining * to_size) + rbytes;+					if (NULL ==+					    archive_string_ensure(as, bs))+						return (-1);+					outp = as->s + as->length;+					avail = as->buffer_length+					    - as->length - to_size;+				}+				if (sc->flag & SCONV_TO_UTF8)+					memcpy(outp, utf8_replacement_char, sizeof(utf8_replacement_char));+				else if (sc->flag & SCONV_TO_UTF16BE)+					archive_be16enc(outp, UNICODE_R_CHAR);+				else+					archive_le16enc(outp, UNICODE_R_CHAR);+				outp += rbytes;+				avail -= rbytes;+			} else {+				/* Skip the illegal input bytes. */+				*outp++ = '?';+				avail--;+			}+			itp += from_size;+			remaining -= from_size;+			return_value = -1; /* failure */+		} else {+			/* E2BIG no output buffer,+			 * Increase an output buffer.  */+			as->length = outp - as->s;+			bs = as->buffer_length + remaining * 2;+			if (NULL == archive_string_ensure(as, bs))+				return (-1);+			outp = as->s + as->length;+			avail = as->buffer_length - as->length - to_size;+		}+	}+	as->length = outp - as->s;+	as->s[as->length] = 0;+	if (to_size == 2)+		as->s[as->length+1] = 0;+	return (return_value);+}++#endif /* HAVE_ICONV */+++#if defined(_WIN32) && !defined(__CYGWIN__)++/*+ * Translate a string from a some CodePage to an another CodePage by+ * Windows APIs, and copy the result. Return -1 if conversion fails.+ */+static int+strncat_in_codepage(struct archive_string *as,+    const void *_p, size_t length, struct archive_string_conv *sc)+{+	const char *s = (const char *)_p;+	struct archive_wstring aws;+	size_t l;+	int r, saved_flag;++	archive_string_init(&aws);+	saved_flag = sc->flag;+	sc->flag &= ~(SCONV_NORMALIZATION_D | SCONV_NORMALIZATION_C);+	r = archive_wstring_append_from_mbs_in_codepage(&aws, s, length, sc);+	sc->flag = saved_flag;+	if (r != 0) {+		archive_wstring_free(&aws);+		if (errno != ENOMEM)+			archive_string_append(as, s, length);+		return (-1);+	}++	l = as->length;+	r = archive_string_append_from_wcs_in_codepage(+	    as, aws.s, aws.length, sc);+	if (r != 0 && errno != ENOMEM && l == as->length)+		archive_string_append(as, s, length);+	archive_wstring_free(&aws);+	return (r);+}++/*+ * Test whether MBS ==> WCS is okay.+ */+static int+invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)+{+	const char *p = (const char *)_p;+	unsigned codepage;+	DWORD mbflag = MB_ERR_INVALID_CHARS;++	if (sc->flag & SCONV_FROM_CHARSET)+		codepage = sc->to_cp;+	else+		codepage = sc->from_cp;++	if (codepage == CP_C_LOCALE)+		return (0);+	if (codepage != CP_UTF8)+		mbflag |= MB_PRECOMPOSED;++	if (MultiByteToWideChar(codepage, mbflag, p, (int)n, NULL, 0) == 0)+		return (-1); /* Invalid */+	return (0); /* Okay */+}++#else++/*+ * Test whether MBS ==> WCS is okay.+ */+static int+invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)+{+	const char *p = (const char *)_p;+	size_t r;++#if HAVE_MBRTOWC+	mbstate_t shift_state;++	memset(&shift_state, 0, sizeof(shift_state));+#else+	/* Clear the shift state before starting. */+	mbtowc(NULL, NULL, 0);+#endif+	while (n) {+		wchar_t wc;++#if HAVE_MBRTOWC+		r = mbrtowc(&wc, p, n, &shift_state);+#else+		r = mbtowc(&wc, p, n);+#endif+		if (r == (size_t)-1 || r == (size_t)-2)+			return (-1);/* Invalid. */+		if (r == 0)+			break;+		p += r;+		n -= r;+	}+	(void)sc; /* UNUSED */+	return (0); /* All Okey. */+}++#endif /* defined(_WIN32) && !defined(__CYGWIN__) */++/*+ * Basically returns -1 because we cannot make a conversion of charset+ * without iconv but in some cases this would return 0.+ * Returns 0 if all copied characters are ASCII.+ * Returns 0 if both from-locale and to-locale are the same and those+ * can be WCS with no error.+ */+static int+best_effort_strncat_in_locale(struct archive_string *as, const void *_p,+    size_t length, struct archive_string_conv *sc)+{+	size_t remaining;+	const uint8_t *itp;+	int return_value = 0; /* success */++	/*+	 * If both from-locale and to-locale is the same, this makes a copy.+	 * And then this checks all copied MBS can be WCS if so returns 0.+	 */+	if (sc->same) {+		if (archive_string_append(as, _p, length) == NULL)+			return (-1);/* No memory */+		return (invalid_mbs(_p, length, sc));+	}++	/*+	 * If a character is ASCII, this just copies it. If not, this+	 * assigns '?' character instead but in UTF-8 locale this assigns+	 * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD,+	 * a Replacement Character in Unicode.+	 */++	remaining = length;+	itp = (const uint8_t *)_p;+	while (*itp && remaining > 0) {+		if (*itp > 127) {+			// Non-ASCII: Substitute with suitable replacement+			if (sc->flag & SCONV_TO_UTF8) {+				if (archive_string_append(as, utf8_replacement_char, sizeof(utf8_replacement_char)) == NULL) {+					__archive_errx(1, "Out of memory");+				}+			} else {+				archive_strappend_char(as, '?');+			}+			return_value = -1;+		} else {+			archive_strappend_char(as, *itp);+		}+		++itp;+	}+	return (return_value);+}+++/*+ * Unicode conversion functions.+ *   - UTF-8 <===> UTF-8 in removing surrogate pairs.+ *   - UTF-8 NFD ===> UTF-8 NFC in removing surrogate pairs.+ *   - UTF-8 made by libarchive 2.x ===> UTF-8.+ *   - UTF-16BE <===> UTF-8.+ *+ */++/*+ * Utility to convert a single UTF-8 sequence.+ *+ * Usually return used bytes, return used byte in negative value when+ * a unicode character is replaced with U+FFFD.+ * See also http://unicode.org/review/pr-121.html Public Review Issue #121+ * Recommended Practice for Replacement Characters.+ */+static int+_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)+{+	static const char utf8_count[256] = {+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */+		 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */+		 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */+		 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */+		 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */+	};+	int ch, i;+	int cnt;+	uint32_t wc;++	/* Sanity check. */+	if (n == 0)+		return (0);+	/*+	 * Decode 1-4 bytes depending on the value of the first byte.+	 */+	ch = (unsigned char)*s;+	if (ch == 0)+		return (0); /* Standard:  return 0 for end-of-string. */+	cnt = utf8_count[ch];++	/* Invalid sequence or there are not plenty bytes. */+	if ((int)n < cnt) {+		cnt = (int)n;+		for (i = 1; i < cnt; i++) {+			if ((s[i] & 0xc0) != 0x80) {+				cnt = i;+				break;+			}+		}+		goto invalid_sequence;+	}++	/* Make a Unicode code point from a single UTF-8 sequence. */+	switch (cnt) {+	case 1:	/* 1 byte sequence. */+		*pwc = ch & 0x7f;+		return (cnt);+	case 2:	/* 2 bytes sequence. */+		if ((s[1] & 0xc0) != 0x80) {+			cnt = 1;+			goto invalid_sequence;+		}+		*pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);+		return (cnt);+	case 3:	/* 3 bytes sequence. */+		if ((s[1] & 0xc0) != 0x80) {+			cnt = 1;+			goto invalid_sequence;+		}+		if ((s[2] & 0xc0) != 0x80) {+			cnt = 2;+			goto invalid_sequence;+		}+		wc = ((ch & 0x0f) << 12)+		    | ((s[1] & 0x3f) << 6)+		    | (s[2] & 0x3f);+		if (wc < 0x800)+			goto invalid_sequence;/* Overlong sequence. */+		break;+	case 4:	/* 4 bytes sequence. */+		if ((s[1] & 0xc0) != 0x80) {+			cnt = 1;+			goto invalid_sequence;+		}+		if ((s[2] & 0xc0) != 0x80) {+			cnt = 2;+			goto invalid_sequence;+		}+		if ((s[3] & 0xc0) != 0x80) {+			cnt = 3;+			goto invalid_sequence;+		}+		wc = ((ch & 0x07) << 18)+		    | ((s[1] & 0x3f) << 12)+		    | ((s[2] & 0x3f) << 6)+		    | (s[3] & 0x3f);+		if (wc < 0x10000)+			goto invalid_sequence;/* Overlong sequence. */+		break;+	default: /* Others are all invalid sequence. */+		if (ch == 0xc0 || ch == 0xc1)+			cnt = 2;+		else if (ch >= 0xf5 && ch <= 0xf7)+			cnt = 4;+		else if (ch >= 0xf8 && ch <= 0xfb)+			cnt = 5;+		else if (ch == 0xfc || ch == 0xfd)+			cnt = 6;+		else+			cnt = 1;+		if ((int)n < cnt)+			cnt = (int)n;+		for (i = 1; i < cnt; i++) {+			if ((s[i] & 0xc0) != 0x80) {+				cnt = i;+				break;+			}+		}+		goto invalid_sequence;+	}++	/* The code point larger than 0x10FFFF is not legal+	 * Unicode values. */+	if (wc > UNICODE_MAX)+		goto invalid_sequence;+	/* Correctly gets a Unicode, returns used bytes. */+	*pwc = wc;+	return (cnt);+invalid_sequence:+	*pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */+	return (cnt * -1);+}++static int+utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)+{+	int cnt;++	cnt = _utf8_to_unicode(pwc, s, n);+	/* Any of Surrogate pair is not legal Unicode values. */+	if (cnt == 3 && IS_SURROGATE_PAIR_LA(*pwc))+		return (-3);+	return (cnt);+}++static inline uint32_t+combine_surrogate_pair(uint32_t uc, uint32_t uc2)+{+	uc -= 0xD800;+	uc *= 0x400;+	uc += uc2 - 0xDC00;+	uc += 0x10000;+	return (uc);+}++/*+ * Convert a single UTF-8/CESU-8 sequence to a Unicode code point in+ * removing surrogate pairs.+ *+ * CESU-8: The Compatibility Encoding Scheme for UTF-16.+ *+ * Usually return used bytes, return used byte in negative value when+ * a unicode character is replaced with U+FFFD.+ */+static int+cesu8_to_unicode(uint32_t *pwc, const char *s, size_t n)+{+	uint32_t wc = 0;+	int cnt;++	cnt = _utf8_to_unicode(&wc, s, n);+	if (cnt == 3 && IS_HIGH_SURROGATE_LA(wc)) {+		uint32_t wc2 = 0;+		if (n - 3 < 3) {+			/* Invalid byte sequence. */+			goto invalid_sequence;+		}+		cnt = _utf8_to_unicode(&wc2, s+3, n-3);+		if (cnt != 3 || !IS_LOW_SURROGATE_LA(wc2)) {+			/* Invalid byte sequence. */+			goto invalid_sequence;+		}+		wc = combine_surrogate_pair(wc, wc2);+		cnt = 6;+	} else if (cnt == 3 && IS_LOW_SURROGATE_LA(wc)) {+		/* Invalid byte sequence. */+		goto invalid_sequence;+	}+	*pwc = wc;+	return (cnt);+invalid_sequence:+	*pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */+	if (cnt > 0)+		cnt *= -1;+	return (cnt);+}++/*+ * Convert a Unicode code point to a single UTF-8 sequence.+ *+ * NOTE:This function does not check if the Unicode is legal or not.+ * Please you definitely check it before calling this.+ */+static size_t+unicode_to_utf8(char *p, size_t remaining, uint32_t uc)+{+	char *_p = p;++	/* Invalid Unicode char maps to Replacement character */+	if (uc > UNICODE_MAX)+		uc = UNICODE_R_CHAR;+	/* Translate code point to UTF8 */+	if (uc <= 0x7f) {+		if (remaining == 0)+			return (0);+		*p++ = (char)uc;+	} else if (uc <= 0x7ff) {+		if (remaining < 2)+			return (0);+		*p++ = 0xc0 | ((uc >> 6) & 0x1f);+		*p++ = 0x80 | (uc & 0x3f);+	} else if (uc <= 0xffff) {+		if (remaining < 3)+			return (0);+		*p++ = 0xe0 | ((uc >> 12) & 0x0f);+		*p++ = 0x80 | ((uc >> 6) & 0x3f);+		*p++ = 0x80 | (uc & 0x3f);+	} else {+		if (remaining < 4)+			return (0);+		*p++ = 0xf0 | ((uc >> 18) & 0x07);+		*p++ = 0x80 | ((uc >> 12) & 0x3f);+		*p++ = 0x80 | ((uc >> 6) & 0x3f);+		*p++ = 0x80 | (uc & 0x3f);+	}+	return (p - _p);+}++static int+utf16be_to_unicode(uint32_t *pwc, const char *s, size_t n)+{+	return (utf16_to_unicode(pwc, s, n, 1));+}++static int+utf16le_to_unicode(uint32_t *pwc, const char *s, size_t n)+{+	return (utf16_to_unicode(pwc, s, n, 0));+}++static int+utf16_to_unicode(uint32_t *pwc, const char *s, size_t n, int be)+{+	const char *utf16 = s;+	unsigned uc;++	if (n == 0)+		return (0);+	if (n == 1) {+		/* set the Replacement Character instead. */+		*pwc = UNICODE_R_CHAR;+		return (-1);+	}++	if (be)+		uc = archive_be16dec(utf16);+	else+		uc = archive_le16dec(utf16);+	utf16 += 2;+		+	/* If this is a surrogate pair, assemble the full code point.*/+	if (IS_HIGH_SURROGATE_LA(uc)) {+		unsigned uc2;++		if (n >= 4) {+			if (be)+				uc2 = archive_be16dec(utf16);+			else+				uc2 = archive_le16dec(utf16);+		} else+			uc2 = 0;+		if (IS_LOW_SURROGATE_LA(uc2)) {+			uc = combine_surrogate_pair(uc, uc2);+			utf16 += 2;+		} else {+	 		/* Undescribed code point should be U+FFFD+		 	* (replacement character). */+			*pwc = UNICODE_R_CHAR;+			return (-2);+		}+	}++	/*+	 * Surrogate pair values(0xd800 through 0xdfff) are only+	 * used by UTF-16, so, after above calculation, the code+	 * must not be surrogate values, and Unicode has no codes+	 * larger than 0x10ffff. Thus, those are not legal Unicode+	 * values.+	 */+	if (IS_SURROGATE_PAIR_LA(uc) || uc > UNICODE_MAX) {+	 	/* Undescribed code point should be U+FFFD+	 	* (replacement character). */+		*pwc = UNICODE_R_CHAR;+		return (((int)(utf16 - s)) * -1);+	}+	*pwc = uc;+	return ((int)(utf16 - s));+}++static size_t+unicode_to_utf16be(char *p, size_t remaining, uint32_t uc)+{+	char *utf16 = p;++	if (uc > 0xffff) {+		/* We have a code point that won't fit into a+		 * wchar_t; convert it to a surrogate pair. */+		if (remaining < 4)+			return (0);+		uc -= 0x10000;+		archive_be16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);+		archive_be16enc(utf16+2, (uc & 0x3ff) + 0xDC00);+		return (4);+	} else {+		if (remaining < 2)+			return (0);+		archive_be16enc(utf16, uc);+		return (2);+	}+}++static size_t+unicode_to_utf16le(char *p, size_t remaining, uint32_t uc)+{+	char *utf16 = p;++	if (uc > 0xffff) {+		/* We have a code point that won't fit into a+		 * wchar_t; convert it to a surrogate pair. */+		if (remaining < 4)+			return (0);+		uc -= 0x10000;+		archive_le16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);+		archive_le16enc(utf16+2, (uc & 0x3ff) + 0xDC00);+		return (4);+	} else {+		if (remaining < 2)+			return (0);+		archive_le16enc(utf16, uc);+		return (2);+	}+}++/*+ * Copy UTF-8 string in checking surrogate pair.+ * If any surrogate pair are found, it would be canonicalized.+ */+static int+strncat_from_utf8_to_utf8(struct archive_string *as, const void *_p,+    size_t len, struct archive_string_conv *sc)+{+	const char *s;+	char *p, *endp;+	int n, ret = 0;++	(void)sc; /* UNUSED */++	if (archive_string_ensure(as, as->length + len + 1) == NULL)+		return (-1);++	s = (const char *)_p;+	p = as->s + as->length;+	endp = as->s + as->buffer_length -1;+	do {+		uint32_t uc;+		const char *ss = s;+		size_t w;++		/*+		 * Forward byte sequence until a conversion of that is needed.+		 */+		while ((n = utf8_to_unicode(&uc, s, len)) > 0) {+			s += n;+			len -= n;+		}+		if (ss < s) {+			if (p + (s - ss) > endp) {+				as->length = p - as->s;+				if (archive_string_ensure(as,+				    as->buffer_length + len + 1) == NULL)+					return (-1);+				p = as->s + as->length;+				endp = as->s + as->buffer_length -1;+			}++			memcpy(p, ss, s - ss);+			p += s - ss;+		}++		/*+		 * If n is negative, current byte sequence needs a replacement.+		 */+		if (n < 0) {+			if (n == -3 && IS_SURROGATE_PAIR_LA(uc)) {+				/* Current byte sequence may be CESU-8. */+				n = cesu8_to_unicode(&uc, s, len);+			}+			if (n < 0) {+				ret = -1;+				n *= -1;/* Use a replaced unicode character. */+			}++			/* Rebuild UTF-8 byte sequence. */+			while ((w = unicode_to_utf8(p, endp - p, uc)) == 0) {+				as->length = p - as->s;+				if (archive_string_ensure(as,+				    as->buffer_length + len + 1) == NULL)+					return (-1);+				p = as->s + as->length;+				endp = as->s + as->buffer_length -1;+			}+			p += w;+			s += n;+			len -= n;+		}+	} while (n > 0);+	as->length = p - as->s;+	as->s[as->length] = '\0';+	return (ret);+}++static int+archive_string_append_unicode(struct archive_string *as, const void *_p,+    size_t len, struct archive_string_conv *sc)+{+	const char *s;+	char *p, *endp;+	uint32_t uc;+	size_t w;+	int n, ret = 0, ts, tm;+	int (*parse)(uint32_t *, const char *, size_t);+	size_t (*unparse)(char *, size_t, uint32_t);++	if (sc->flag & SCONV_TO_UTF16BE) {+		unparse = unicode_to_utf16be;+		ts = 2;+	} else if (sc->flag & SCONV_TO_UTF16LE) {+		unparse = unicode_to_utf16le;+		ts = 2;+	} else if (sc->flag & SCONV_TO_UTF8) {+		unparse = unicode_to_utf8;+		ts = 1;+	} else {+		/*+		 * This case is going to be converted to another+		 * character-set through iconv.+		 */+		if (sc->flag & SCONV_FROM_UTF16BE) {+			unparse = unicode_to_utf16be;+			ts = 2;+		} else if (sc->flag & SCONV_FROM_UTF16LE) {+			unparse = unicode_to_utf16le;+			ts = 2;+		} else {+			unparse = unicode_to_utf8;+			ts = 1;+		}+	}++	if (sc->flag & SCONV_FROM_UTF16BE) {+		parse = utf16be_to_unicode;+		tm = 1;+	} else if (sc->flag & SCONV_FROM_UTF16LE) {+		parse = utf16le_to_unicode;+		tm = 1;+	} else {+		parse = cesu8_to_unicode;+		tm = ts;+	}++	if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)+		return (-1);++	s = (const char *)_p;+	p = as->s + as->length;+	endp = as->s + as->buffer_length - ts;+	while ((n = parse(&uc, s, len)) != 0) {+		if (n < 0) {+			/* Use a replaced unicode character. */+			n *= -1;+			ret = -1;+		}+		s += n;+		len -= n;+		while ((w = unparse(p, endp - p, uc)) == 0) {+			/* There is not enough output buffer so+			 * we have to expand it. */+			as->length = p - as->s;+			if (archive_string_ensure(as,+			    as->buffer_length + len * tm + ts) == NULL)+				return (-1);+			p = as->s + as->length;+			endp = as->s + as->buffer_length - ts;+		}+		p += w;+	}+	as->length = p - as->s;+	as->s[as->length] = '\0';+	if (ts == 2)+		as->s[as->length+1] = '\0';+	return (ret);+}++/*+ * Following Constants for Hangul compositions this information comes from+ * Unicode Standard Annex #15  http://unicode.org/reports/tr15/+ */+#define HC_SBASE	0xAC00+#define HC_LBASE	0x1100+#define HC_VBASE	0x1161+#define HC_TBASE	0x11A7+#define HC_LCOUNT	19+#define HC_VCOUNT	21+#define HC_TCOUNT	28+#define HC_NCOUNT	(HC_VCOUNT * HC_TCOUNT)+#define HC_SCOUNT	(HC_LCOUNT * HC_NCOUNT)++static uint32_t+get_nfc(uint32_t uc, uint32_t uc2)+{+	int t, b;++	t = 0;+	b = sizeof(u_composition_table)/sizeof(u_composition_table[0]) -1;+	while (b >= t) {+		int m = (t + b) / 2;+		if (u_composition_table[m].cp1 < uc)+			t = m + 1;+		else if (u_composition_table[m].cp1 > uc)+			b = m - 1;+		else if (u_composition_table[m].cp2 < uc2)+			t = m + 1;+		else if (u_composition_table[m].cp2 > uc2)+			b = m - 1;+		else+			return (u_composition_table[m].nfc);+	}+	return (0);+}++#define FDC_MAX 10	/* The maximum number of Following Decomposable+			 * Characters. */++/*+ * Update first code point.+ */+#define UPDATE_UC(new_uc)	do {		\+	uc = new_uc;				\+	ucptr = NULL;				\+} while (0)++/*+ * Replace first code point with second code point.+ */+#define REPLACE_UC_WITH_UC2() do {		\+	uc = uc2;				\+	ucptr = uc2ptr;				\+	n = n2;					\+} while (0)++#define EXPAND_BUFFER() do {			\+	as->length = p - as->s;			\+	if (archive_string_ensure(as,		\+	    as->buffer_length + len * tm + ts) == NULL)\+		return (-1);			\+	p = as->s + as->length;			\+	endp = as->s + as->buffer_length - ts;	\+} while (0)++#define UNPARSE(p, endp, uc)	do {		\+	while ((w = unparse(p, (endp) - (p), uc)) == 0) {\+		EXPAND_BUFFER();		\+	}					\+	p += w;					\+} while (0)++/*+ * Write first code point.+ * If the code point has not be changed from its original code,+ * this just copies it from its original buffer pointer.+ * If not, this converts it to UTF-8 byte sequence and copies it.+ */+#define WRITE_UC()	do {			\+	if (ucptr) {				\+		if (p + n > endp)		\+			EXPAND_BUFFER();	\+		switch (n) {			\+		case 4:				\+			*p++ = *ucptr++;	\+			/* FALL THROUGH */	\+		case 3:				\+			*p++ = *ucptr++;	\+			/* FALL THROUGH */	\+		case 2:				\+			*p++ = *ucptr++;	\+			/* FALL THROUGH */	\+		case 1:				\+			*p++ = *ucptr;		\+			break;			\+		}				\+		ucptr = NULL;			\+	} else {				\+		UNPARSE(p, endp, uc);		\+	}					\+} while (0)++/*+ * Collect following decomposable code points.+ */+#define COLLECT_CPS(start)	do {		\+	int _i;					\+	for (_i = start; _i < FDC_MAX ; _i++) {	\+		nx = parse(&ucx[_i], s, len);	\+		if (nx <= 0)			\+			break;			\+		cx = CCC(ucx[_i]);		\+		if (cl >= cx && cl != 228 && cx != 228)\+			break;			\+		s += nx;			\+		len -= nx;			\+		cl = cx;			\+		ccx[_i] = cx;			\+	}					\+	if (_i >= FDC_MAX) {			\+		ret = -1;			\+		ucx_size = FDC_MAX;		\+	} else					\+		ucx_size = _i;			\+} while (0)++/*+ * Normalize UTF-8/UTF-16BE characters to Form C and copy the result.+ *+ * TODO: Convert composition exclusions, which are never converted+ * from NFC,NFD,NFKC and NFKD, to Form C.+ */+static int+archive_string_normalize_C(struct archive_string *as, const void *_p,+    size_t len, struct archive_string_conv *sc)+{+	const char *s = (const char *)_p;+	char *p, *endp;+	uint32_t uc, uc2;+	size_t w;+	int always_replace, n, n2, ret = 0, spair, ts, tm;+	int (*parse)(uint32_t *, const char *, size_t);+	size_t (*unparse)(char *, size_t, uint32_t);++	always_replace = 1;+	ts = 1;/* text size. */+	if (sc->flag & SCONV_TO_UTF16BE) {+		unparse = unicode_to_utf16be;+		ts = 2;+		if (sc->flag & SCONV_FROM_UTF16BE)+			always_replace = 0;+	} else if (sc->flag & SCONV_TO_UTF16LE) {+		unparse = unicode_to_utf16le;+		ts = 2;+		if (sc->flag & SCONV_FROM_UTF16LE)+			always_replace = 0;+	} else if (sc->flag & SCONV_TO_UTF8) {+		unparse = unicode_to_utf8;+		if (sc->flag & SCONV_FROM_UTF8)+			always_replace = 0;+	} else {+		/*+		 * This case is going to be converted to another+		 * character-set through iconv.+		 */+		always_replace = 0;+		if (sc->flag & SCONV_FROM_UTF16BE) {+			unparse = unicode_to_utf16be;+			ts = 2;+		} else if (sc->flag & SCONV_FROM_UTF16LE) {+			unparse = unicode_to_utf16le;+			ts = 2;+		} else {+			unparse = unicode_to_utf8;+		}+	}++	if (sc->flag & SCONV_FROM_UTF16BE) {+		parse = utf16be_to_unicode;+		tm = 1;+		spair = 4;/* surrogate pair size in UTF-16. */+	} else if (sc->flag & SCONV_FROM_UTF16LE) {+		parse = utf16le_to_unicode;+		tm = 1;+		spair = 4;/* surrogate pair size in UTF-16. */+	} else {+		parse = cesu8_to_unicode;+		tm = ts;+		spair = 6;/* surrogate pair size in UTF-8. */+	}++	if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)+		return (-1);++	p = as->s + as->length;+	endp = as->s + as->buffer_length - ts;+	while ((n = parse(&uc, s, len)) != 0) {+		const char *ucptr, *uc2ptr;++		if (n < 0) {+			/* Use a replaced unicode character. */+			UNPARSE(p, endp, uc);+			s += n*-1;+			len -= n*-1;+			ret = -1;+			continue;+		} else if (n == spair || always_replace)+			/* uc is converted from a surrogate pair.+			 * this should be treated as a changed code. */+			ucptr = NULL;+		else+			ucptr = s;+		s += n;+		len -= n;++		/* Read second code point. */+		while ((n2 = parse(&uc2, s, len)) > 0) {+			uint32_t ucx[FDC_MAX];+			int ccx[FDC_MAX];+			int cl, cx, i, nx, ucx_size;+			int LIndex,SIndex;+			uint32_t nfc;++			if (n2 == spair || always_replace)+				/* uc2 is converted from a surrogate pair.+			 	 * this should be treated as a changed code. */+				uc2ptr = NULL;+			else+				uc2ptr = s;+			s += n2;+			len -= n2;++			/*+			 * If current second code point is out of decomposable+			 * code points, finding compositions is unneeded.+			 */+			if (!IS_DECOMPOSABLE_BLOCK(uc2)) {+				WRITE_UC();+				REPLACE_UC_WITH_UC2();+				continue;+			}++			/*+			 * Try to combine current code points.+			 */+			/*+			 * We have to combine Hangul characters according to+			 * http://uniicode.org/reports/tr15/#Hangul+			 */+			if (0 <= (LIndex = uc - HC_LBASE) &&+			    LIndex < HC_LCOUNT) {+				/*+				 * Hangul Composition.+				 * 1. Two current code points are L and V.+				 */+				int VIndex = uc2 - HC_VBASE;+				if (0 <= VIndex && VIndex < HC_VCOUNT) {+					/* Make syllable of form LV. */+					UPDATE_UC(HC_SBASE ++					    (LIndex * HC_VCOUNT + VIndex) *+					     HC_TCOUNT);+				} else {+					WRITE_UC();+					REPLACE_UC_WITH_UC2();+				}+				continue;+			} else if (0 <= (SIndex = uc - HC_SBASE) &&+			    SIndex < HC_SCOUNT && (SIndex % HC_TCOUNT) == 0) {+				/*+				 * Hangul Composition.+				 * 2. Two current code points are LV and T.+				 */+				int TIndex = uc2 - HC_TBASE;+				if (0 < TIndex && TIndex < HC_TCOUNT) {+					/* Make syllable of form LVT. */+					UPDATE_UC(uc + TIndex);+				} else {+					WRITE_UC();+					REPLACE_UC_WITH_UC2();+				}+				continue;+			} else if ((nfc = get_nfc(uc, uc2)) != 0) {+				/* A composition to current code points+				 * is found. */+				UPDATE_UC(nfc);+				continue;+			} else if ((cl = CCC(uc2)) == 0) {+				/* Clearly 'uc2' the second code point is not+				 * a decomposable code. */+				WRITE_UC();+				REPLACE_UC_WITH_UC2();+				continue;+			}++			/*+			 * Collect following decomposable code points.+			 */+			cx = 0;+			ucx[0] = uc2;+			ccx[0] = cl;+			COLLECT_CPS(1);++			/*+			 * Find a composed code in the collected code points.+			 */+			i = 1;+			while (i < ucx_size) {+				int j;++				if ((nfc = get_nfc(uc, ucx[i])) == 0) {+					i++;+					continue;+				}++				/*+				 * nfc is composed of uc and ucx[i].+				 */+				UPDATE_UC(nfc);++				/*+				 * Remove ucx[i] by shifting+				 * following code points.+				 */+				for (j = i; j+1 < ucx_size; j++) {+					ucx[j] = ucx[j+1];+					ccx[j] = ccx[j+1];+				}+				ucx_size --;++				/*+				 * Collect following code points blocked+				 * by ucx[i] the removed code point.+				 */+				if (ucx_size > 0 && i == ucx_size &&+				    nx > 0 && cx == cl) {+					cl =  ccx[ucx_size-1];+					COLLECT_CPS(ucx_size);+				}+				/*+				 * Restart finding a composed code with+				 * the updated uc from the top of the+				 * collected code points.+				 */+				i = 0;+			}++			/*+			 * Apparently the current code points are not+			 * decomposed characters or already composed.+			 */+			WRITE_UC();+			for (i = 0; i < ucx_size; i++)+				UNPARSE(p, endp, ucx[i]);++			/*+			 * Flush out remaining canonical combining characters.+			 */+			if (nx > 0 && cx == cl && len > 0) {+				while ((nx = parse(&ucx[0], s, len))+				    > 0) {+					cx = CCC(ucx[0]);+					if (cl > cx)+						break;+					s += nx;+					len -= nx;+					cl = cx;+					UNPARSE(p, endp, ucx[0]);+				}+			}+			break;+		}+		if (n2 < 0) {+			WRITE_UC();+			/* Use a replaced unicode character. */+			UNPARSE(p, endp, uc2);+			s += n2*-1;+			len -= n2*-1;+			ret = -1;+			continue;+		} else if (n2 == 0) {+			WRITE_UC();+			break;+		}+	}+	as->length = p - as->s;+	as->s[as->length] = '\0';+	if (ts == 2)+		as->s[as->length+1] = '\0';+	return (ret);+}++static int+get_nfd(uint32_t *cp1, uint32_t *cp2, uint32_t uc)+{+	int t, b;++	/*+	 * These are not converted to NFD on Mac OS.+	 */+	if ((uc >= 0x2000 && uc <= 0x2FFF) ||+	    (uc >= 0xF900 && uc <= 0xFAFF) ||+	    (uc >= 0x2F800 && uc <= 0x2FAFF))+		return (0);+	/*+	 * Those code points are not converted to NFD on Mac OS.+	 * I do not know the reason because it is undocumented.+	 *   NFC        NFD+	 *   1109A  ==> 11099 110BA+	 *   1109C  ==> 1109B 110BA+	 *   110AB  ==> 110A5 110BA+	 */+	if (uc == 0x1109A || uc == 0x1109C || uc == 0x110AB)+		return (0);++	t = 0;+	b = sizeof(u_decomposition_table)/sizeof(u_decomposition_table[0]) -1;+	while (b >= t) {+		int m = (t + b) / 2;+		if (u_decomposition_table[m].nfc < uc)+			t = m + 1;+		else if (u_decomposition_table[m].nfc > uc)+			b = m - 1;+		else {+			*cp1 = u_decomposition_table[m].cp1;+			*cp2 = u_decomposition_table[m].cp2;+			return (1);+		}+	}+	return (0);+}++#define REPLACE_UC_WITH(cp) do {		\+	uc = cp;				\+	ucptr = NULL;				\+} while (0)++/*+ * Normalize UTF-8 characters to Form D and copy the result.+ */+static int+archive_string_normalize_D(struct archive_string *as, const void *_p,+    size_t len, struct archive_string_conv *sc)+{+	const char *s = (const char *)_p;+	char *p, *endp;+	uint32_t uc, uc2;+	size_t w;+	int always_replace, n, n2, ret = 0, spair, ts, tm;+	int (*parse)(uint32_t *, const char *, size_t);+	size_t (*unparse)(char *, size_t, uint32_t);++	always_replace = 1;+	ts = 1;/* text size. */+	if (sc->flag & SCONV_TO_UTF16BE) {+		unparse = unicode_to_utf16be;+		ts = 2;+		if (sc->flag & SCONV_FROM_UTF16BE)+			always_replace = 0;+	} else if (sc->flag & SCONV_TO_UTF16LE) {+		unparse = unicode_to_utf16le;+		ts = 2;+		if (sc->flag & SCONV_FROM_UTF16LE)+			always_replace = 0;+	} else if (sc->flag & SCONV_TO_UTF8) {+		unparse = unicode_to_utf8;+		if (sc->flag & SCONV_FROM_UTF8)+			always_replace = 0;+	} else {+		/*+		 * This case is going to be converted to another+		 * character-set through iconv.+		 */+		always_replace = 0;+		if (sc->flag & SCONV_FROM_UTF16BE) {+			unparse = unicode_to_utf16be;+			ts = 2;+		} else if (sc->flag & SCONV_FROM_UTF16LE) {+			unparse = unicode_to_utf16le;+			ts = 2;+		} else {+			unparse = unicode_to_utf8;+		}+	}++	if (sc->flag & SCONV_FROM_UTF16BE) {+		parse = utf16be_to_unicode;+		tm = 1;+		spair = 4;/* surrogate pair size in UTF-16. */+	} else if (sc->flag & SCONV_FROM_UTF16LE) {+		parse = utf16le_to_unicode;+		tm = 1;+		spair = 4;/* surrogate pair size in UTF-16. */+	} else {+		parse = cesu8_to_unicode;+		tm = ts;+		spair = 6;/* surrogate pair size in UTF-8. */+	}++	if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)+		return (-1);++	p = as->s + as->length;+	endp = as->s + as->buffer_length - ts;+	while ((n = parse(&uc, s, len)) != 0) {+		const char *ucptr;+		uint32_t cp1, cp2;+		int SIndex;+		struct {+			uint32_t uc;+			int ccc;+		} fdc[FDC_MAX];+		int fdi, fdj;+		int ccc;++check_first_code:+		if (n < 0) {+			/* Use a replaced unicode character. */+			UNPARSE(p, endp, uc);+			s += n*-1;+			len -= n*-1;+			ret = -1;+			continue;+		} else if (n == spair || always_replace)+			/* uc is converted from a surrogate pair.+			 * this should be treated as a changed code. */+			ucptr = NULL;+		else+			ucptr = s;+		s += n;+		len -= n;++		/* Hangul Decomposition. */+		if ((SIndex = uc - HC_SBASE) >= 0 && SIndex < HC_SCOUNT) {+			int L = HC_LBASE + SIndex / HC_NCOUNT;+			int V = HC_VBASE + (SIndex % HC_NCOUNT) / HC_TCOUNT;+			int T = HC_TBASE + SIndex % HC_TCOUNT;++			REPLACE_UC_WITH(L);+			WRITE_UC();+			REPLACE_UC_WITH(V);+			WRITE_UC();+			if (T != HC_TBASE) {+				REPLACE_UC_WITH(T);+				WRITE_UC();+			}+			continue;+		}+		if (IS_DECOMPOSABLE_BLOCK(uc) && CCC(uc) != 0) {+			WRITE_UC();+			continue;+		}++		fdi = 0;+		while (get_nfd(&cp1, &cp2, uc) && fdi < FDC_MAX) {+			int k;++			for (k = fdi; k > 0; k--)+				fdc[k] = fdc[k-1];+			fdc[0].ccc = CCC(cp2);+			fdc[0].uc = cp2;+			fdi++;+			REPLACE_UC_WITH(cp1);+		}++		/* Read following code points. */+		while ((n2 = parse(&uc2, s, len)) > 0 &&+		    (ccc = CCC(uc2)) != 0 && fdi < FDC_MAX) {+			int j, k;++			s += n2;+			len -= n2;+			for (j = 0; j < fdi; j++) {+				if (fdc[j].ccc > ccc)+					break;+			}+			if (j < fdi) {+				for (k = fdi; k > j; k--)+					fdc[k] = fdc[k-1];+				fdc[j].ccc = ccc;+				fdc[j].uc = uc2;+			} else {+				fdc[fdi].ccc = ccc;+				fdc[fdi].uc = uc2;+			}+			fdi++;+		}++		WRITE_UC();+		for (fdj = 0; fdj < fdi; fdj++) {+			REPLACE_UC_WITH(fdc[fdj].uc);+			WRITE_UC();+		}++		if (n2 == 0)+			break;+		REPLACE_UC_WITH(uc2);+		n = n2;+		goto check_first_code;+	}+	as->length = p - as->s;+	as->s[as->length] = '\0';+	if (ts == 2)+		as->s[as->length+1] = '\0';+	return (ret);+}++/*+ * libarchive 2.x made incorrect UTF-8 strings in the wrong assumption+ * that WCS is Unicode. It is true for several platforms but some are false.+ * And then people who did not use UTF-8 locale on the non Unicode WCS+ * platform and made a tar file with libarchive(mostly bsdtar) 2.x. Those+ * now cannot get right filename from libarchive 3.x and later since we+ * fixed the wrong assumption and it is incompatible to older its versions.+ * So we provide special option, "compat-2x.x", for resolving it.+ * That option enable the string conversion of libarchive 2.x.+ *+ * Translates the wrong UTF-8 string made by libarchive 2.x into current+ * locale character set and appends to the archive_string.+ * Note: returns -1 if conversion fails.+ */+static int+strncat_from_utf8_libarchive2(struct archive_string *as,+    const void *_p, size_t len, struct archive_string_conv *sc)+{+	const char *s;+	int n;+	char *p;+	char *end;+	uint32_t unicode;+#if HAVE_WCRTOMB+	mbstate_t shift_state;++	memset(&shift_state, 0, sizeof(shift_state));+#else+	/* Clear the shift state before starting. */+	wctomb(NULL, L'\0');+#endif+	(void)sc; /* UNUSED */+	/*+	 * Allocate buffer for MBS.+	 * We need this allocation here since it is possible that+	 * as->s is still NULL.+	 */+	if (archive_string_ensure(as, as->length + len + 1) == NULL)+		return (-1);++	s = (const char *)_p;+	p = as->s + as->length;+	end = as->s + as->buffer_length - MB_CUR_MAX -1;+	while ((n = _utf8_to_unicode(&unicode, s, len)) != 0) {+		wchar_t wc;++		if (p >= end) {+			as->length = p - as->s;+			/* Re-allocate buffer for MBS. */+			if (archive_string_ensure(as,+			    as->length + max(len * 2,+			    (size_t)MB_CUR_MAX) + 1) == NULL)+				return (-1);+			p = as->s + as->length;+			end = as->s + as->buffer_length - MB_CUR_MAX -1;+		}++		/*+		 * As libarchive 2.x, translates the UTF-8 characters into+		 * wide-characters in the assumption that WCS is Unicode.+		 */+		if (n < 0) {+			n *= -1;+			wc = L'?';+		} else+			wc = (wchar_t)unicode;++		s += n;+		len -= n;+		/*+		 * Translates the wide-character into the current locale MBS.+		 */+#if HAVE_WCRTOMB+		n = (int)wcrtomb(p, wc, &shift_state);+#else+		n = (int)wctomb(p, wc);+#endif+		if (n == -1)+			return (-1);+		p += n;+	}+	as->length = p - as->s;+	as->s[as->length] = '\0';+	return (0);+}+++/*+ * Conversion functions between current locale dependent MBS and UTF-16BE.+ *   strncat_from_utf16be() : UTF-16BE --> MBS+ *   strncat_to_utf16be()   : MBS --> UTF16BE+ */++#if defined(_WIN32) && !defined(__CYGWIN__)++/*+ * Convert a UTF-16BE/LE string to current locale and copy the result.+ * Return -1 if conversion fails.+ */+static int+win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes,+    struct archive_string_conv *sc, int be)+{+	struct archive_string tmp;+	const char *u16;+	int ll;+	BOOL defchar;+	char *mbs;+	size_t mbs_size, b;+	int ret = 0;++	bytes &= ~1;+	if (archive_string_ensure(as, as->length + bytes +1) == NULL)+		return (-1);++	mbs = as->s + as->length;+	mbs_size = as->buffer_length - as->length -1;++	if (sc->to_cp == CP_C_LOCALE) {+		/*+		 * "C" locale special process.+		 */+		u16 = _p;+		ll = 0;+		for (b = 0; b < bytes; b += 2) {+			uint16_t val;+			if (be)+				val = archive_be16dec(u16+b);+			else+				val = archive_le16dec(u16+b);+			if (val > 255) {+				*mbs++ = '?';+				ret = -1;+			} else+				*mbs++ = (char)(val&0xff);+			ll++;+		}+		as->length += ll;+		as->s[as->length] = '\0';+		return (ret);+	}++	archive_string_init(&tmp);+	if (be) {+		if (is_big_endian()) {+			u16 = _p;+		} else {+			if (archive_string_ensure(&tmp, bytes+2) == NULL)+				return (-1);+			memcpy(tmp.s, _p, bytes);+			for (b = 0; b < bytes; b += 2) {+				uint16_t val = archive_be16dec(tmp.s+b);+				archive_le16enc(tmp.s+b, val);+			}+			u16 = tmp.s;+		}+	} else {+		if (!is_big_endian()) {+			u16 = _p;+		} else {+			if (archive_string_ensure(&tmp, bytes+2) == NULL)+				return (-1);+			memcpy(tmp.s, _p, bytes);+			for (b = 0; b < bytes; b += 2) {+				uint16_t val = archive_le16dec(tmp.s+b);+				archive_be16enc(tmp.s+b, val);+			}+			u16 = tmp.s;+		}+	}++	do {+		defchar = 0;+		ll = WideCharToMultiByte(sc->to_cp, 0,+		    (LPCWSTR)u16, (int)bytes>>1, mbs, (int)mbs_size,+			NULL, &defchar);+		/* Exit loop if we succeeded */+		if (ll != 0 ||+		    GetLastError() != ERROR_INSUFFICIENT_BUFFER) {+			break;+		}+		/* Else expand buffer and loop to try again. */+		ll = WideCharToMultiByte(sc->to_cp, 0,+		    (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL);+		if (archive_string_ensure(as, ll +1) == NULL)+			return (-1);+		mbs = as->s + as->length;+		mbs_size = as->buffer_length - as->length -1;+	} while (1);+	archive_string_free(&tmp);+	as->length += ll;+	as->s[as->length] = '\0';+	if (ll == 0 || defchar)+		ret = -1;+	return (ret);+}++static int+win_strncat_from_utf16be(struct archive_string *as, const void *_p,+    size_t bytes, struct archive_string_conv *sc)+{+	return (win_strncat_from_utf16(as, _p, bytes, sc, 1));+}++static int+win_strncat_from_utf16le(struct archive_string *as, const void *_p,+    size_t bytes, struct archive_string_conv *sc)+{+	return (win_strncat_from_utf16(as, _p, bytes, sc, 0));+}++static int+is_big_endian(void)+{+	uint16_t d = 1;++	return (archive_be16dec(&d) == 1);+}++/*+ * Convert a current locale string to UTF-16BE/LE and copy the result.+ * Return -1 if conversion fails.+ */+static int+win_strncat_to_utf16(struct archive_string *as16, const void *_p,+    size_t length, struct archive_string_conv *sc, int bigendian)+{+	const char *s = (const char *)_p;+	char *u16;+	size_t count, avail;++	if (archive_string_ensure(as16,+	    as16->length + (length + 1) * 2) == NULL)+		return (-1);++	u16 = as16->s + as16->length;+	avail = as16->buffer_length - 2;+	if (sc->from_cp == CP_C_LOCALE) {+		/*+		 * "C" locale special process.+		 */+		count = 0;+		while (count < length && *s) {+			if (bigendian)+				archive_be16enc(u16, *s);+			else+				archive_le16enc(u16, *s);+			u16 += 2;+			s++;+			count++;+		}+		as16->length += count << 1;+		as16->s[as16->length] = 0;+		as16->s[as16->length+1] = 0;+		return (0);+	}+	do {+		count = MultiByteToWideChar(sc->from_cp,+		    MB_PRECOMPOSED, s, (int)length, (LPWSTR)u16, (int)avail>>1);+		/* Exit loop if we succeeded */+		if (count != 0 ||+		    GetLastError() != ERROR_INSUFFICIENT_BUFFER) {+			break;+		}+		/* Expand buffer and try again */+		count = MultiByteToWideChar(sc->from_cp,+		    MB_PRECOMPOSED, s, (int)length, NULL, 0);+		if (archive_string_ensure(as16, (count +1) * 2)+		    == NULL)+			return (-1);+		u16 = as16->s + as16->length;+		avail = as16->buffer_length - 2;+	} while (1);+	as16->length += count * 2;+	as16->s[as16->length] = 0;+	as16->s[as16->length+1] = 0;+	if (count == 0)+		return (-1);++	if (is_big_endian()) {+		if (!bigendian) {+			while (count > 0) {+				uint16_t v = archive_be16dec(u16);+				archive_le16enc(u16, v);+				u16 += 2;+				count--;+			}+		}+	} else {+		if (bigendian) {+			while (count > 0) {+				uint16_t v = archive_le16dec(u16);+				archive_be16enc(u16, v);+				u16 += 2;+				count--;+			}+		}+	}+	return (0);+}++static int+win_strncat_to_utf16be(struct archive_string *as16, const void *_p,+    size_t length, struct archive_string_conv *sc)+{+	return (win_strncat_to_utf16(as16, _p, length, sc, 1));+}++static int+win_strncat_to_utf16le(struct archive_string *as16, const void *_p,+    size_t length, struct archive_string_conv *sc)+{+	return (win_strncat_to_utf16(as16, _p, length, sc, 0));+}++#endif /* _WIN32 && !__CYGWIN__ */++/*+ * Do the best effort for conversions.+ * We cannot handle UTF-16BE character-set without such iconv,+ * but there is a chance if a string consists just ASCII code or+ * a current locale is UTF-8.+ */++/*+ * Convert a UTF-16BE string to current locale and copy the result.+ * Return -1 if conversion fails.+ */+static int+best_effort_strncat_from_utf16(struct archive_string *as, const void *_p,+    size_t bytes, struct archive_string_conv *sc, int be)+{+	const char *utf16 = (const char *)_p;+	char *mbs;+	uint32_t uc;+	int n, ret;++	(void)sc; /* UNUSED */+	/*+	 * Other case, we should do the best effort.+	 * If all character are ASCII(<0x7f), we can convert it.+	 * if not , we set a alternative character and return -1.+	 */+	ret = 0;+	if (archive_string_ensure(as, as->length + bytes +1) == NULL)+		return (-1);+	mbs = as->s + as->length;++	while ((n = utf16_to_unicode(&uc, utf16, bytes, be)) != 0) {+		if (n < 0) {+			n *= -1;+			ret =  -1;+		}+		bytes -= n;+		utf16 += n;++		if (uc > 127) {+			/* We cannot handle it. */+			*mbs++ = '?';+			ret =  -1;+		} else+			*mbs++ = (char)uc;+	}+	as->length = mbs - as->s;+	as->s[as->length] = '\0';+	return (ret);+}++static int+best_effort_strncat_from_utf16be(struct archive_string *as, const void *_p,+    size_t bytes, struct archive_string_conv *sc)+{+	return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 1));+}++static int+best_effort_strncat_from_utf16le(struct archive_string *as, const void *_p,+    size_t bytes, struct archive_string_conv *sc)+{+	return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 0));+}++/*+ * Convert a current locale string to UTF-16BE/LE and copy the result.+ * Return -1 if conversion fails.+ */+static int+best_effort_strncat_to_utf16(struct archive_string *as16, const void *_p,+    size_t length, struct archive_string_conv *sc, int bigendian)+{+	const char *s = (const char *)_p;+	char *utf16;+	size_t remaining;+	int ret;++	(void)sc; /* UNUSED */+	/*+	 * Other case, we should do the best effort.+	 * If all character are ASCII(<0x7f), we can convert it.+	 * if not , we set a alternative character and return -1.+	 */+	ret = 0;+	remaining = length;++	if (archive_string_ensure(as16,+	    as16->length + (length + 1) * 2) == NULL)+		return (-1);++	utf16 = as16->s + as16->length;+	while (remaining--) {+		unsigned c = *s++;+		if (c > 127) {+			/* We cannot handle it. */+			c = UNICODE_R_CHAR;+			ret = -1;+		}+		if (bigendian)+			archive_be16enc(utf16, c);+		else+			archive_le16enc(utf16, c);+		utf16 += 2;+	}+	as16->length = utf16 - as16->s;+	as16->s[as16->length] = 0;+	as16->s[as16->length+1] = 0;+	return (ret);+}++static int+best_effort_strncat_to_utf16be(struct archive_string *as16, const void *_p,+    size_t length, struct archive_string_conv *sc)+{+	return (best_effort_strncat_to_utf16(as16, _p, length, sc, 1));+}++static int+best_effort_strncat_to_utf16le(struct archive_string *as16, const void *_p,+    size_t length, struct archive_string_conv *sc)+{+	return (best_effort_strncat_to_utf16(as16, _p, length, sc, 0));+}+++/*+ * Multistring operations.+ */++void+archive_mstring_clean(struct archive_mstring *aes)+{+	archive_wstring_free(&(aes->aes_wcs));+	archive_string_free(&(aes->aes_mbs));+	archive_string_free(&(aes->aes_utf8));+	archive_string_free(&(aes->aes_mbs_in_locale));+	aes->aes_set = 0;+}++void+archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src)+{+	dest->aes_set = src->aes_set;+	archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs));+	archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8));+	archive_wstring_copy(&(dest->aes_wcs), &(src->aes_wcs));+}++int+archive_mstring_get_utf8(struct archive *a, struct archive_mstring *aes,+  const char **p)+{+	struct archive_string_conv *sc;+	int r;++	/* If we already have a UTF8 form, return that immediately. */+	if (aes->aes_set & AES_SET_UTF8) {+		*p = aes->aes_utf8.s;+		return (0);+	}++	*p = NULL;+	if (aes->aes_set & AES_SET_MBS) {+		sc = archive_string_conversion_to_charset(a, "UTF-8", 1);+		if (sc == NULL)+			return (-1);/* Couldn't allocate memory for sc. */+		r = archive_strncpy_l(&(aes->aes_utf8), aes->aes_mbs.s,+		    aes->aes_mbs.length, sc);+		if (a == NULL)+			free_sconv_object(sc);+		if (r == 0) {+			aes->aes_set |= AES_SET_UTF8;+			*p = aes->aes_utf8.s;+			return (0);/* success. */+		} else+			return (-1);/* failure. */+	}+	return (0);/* success. */+}++int+archive_mstring_get_mbs(struct archive *a, struct archive_mstring *aes,+    const char **p)+{+	int r, ret = 0;++	(void)a; /* UNUSED */+	/* If we already have an MBS form, return that immediately. */+	if (aes->aes_set & AES_SET_MBS) {+		*p = aes->aes_mbs.s;+		return (ret);+	}++	*p = NULL;+	/* If there's a WCS form, try converting with the native locale. */+	if (aes->aes_set & AES_SET_WCS) {+		archive_string_empty(&(aes->aes_mbs));+		r = archive_string_append_from_wcs(&(aes->aes_mbs),+		    aes->aes_wcs.s, aes->aes_wcs.length);+		*p = aes->aes_mbs.s;+		if (r == 0) {+			aes->aes_set |= AES_SET_MBS;+			return (ret);+		} else+			ret = -1;+	}++	/*+	 * Only a UTF-8 form cannot avail because its conversion already+	 * failed at archive_mstring_update_utf8().+	 */+	return (ret);+}++int+archive_mstring_get_wcs(struct archive *a, struct archive_mstring *aes,+    const wchar_t **wp)+{+	int r, ret = 0;++	(void)a;/* UNUSED */+	/* Return WCS form if we already have it. */+	if (aes->aes_set & AES_SET_WCS) {+		*wp = aes->aes_wcs.s;+		return (ret);+	}++	*wp = NULL;+	/* Try converting MBS to WCS using native locale. */+	if (aes->aes_set & AES_SET_MBS) {+		archive_wstring_empty(&(aes->aes_wcs));+		r = archive_wstring_append_from_mbs(&(aes->aes_wcs),+		    aes->aes_mbs.s, aes->aes_mbs.length);+		if (r == 0) {+			aes->aes_set |= AES_SET_WCS;+			*wp = aes->aes_wcs.s;+		} else+			ret = -1;/* failure. */+	}+	return (ret);+}++int+archive_mstring_get_mbs_l(struct archive_mstring *aes,+    const char **p, size_t *length, struct archive_string_conv *sc)+{+	int r, ret = 0;++#if defined(_WIN32) && !defined(__CYGWIN__)+	/*+	 * Internationalization programming on Windows must use Wide+	 * characters because Windows platform cannot make locale UTF-8.+	 */+	if (sc != NULL && (aes->aes_set & AES_SET_WCS) != 0) {+		archive_string_empty(&(aes->aes_mbs_in_locale));+		r = archive_string_append_from_wcs_in_codepage(+		    &(aes->aes_mbs_in_locale), aes->aes_wcs.s,+		    aes->aes_wcs.length, sc);+		if (r == 0) {+			*p = aes->aes_mbs_in_locale.s;+			if (length != NULL)+				*length = aes->aes_mbs_in_locale.length;+			return (0);+		} else if (errno == ENOMEM)+			return (-1);+		else+			ret = -1;+	}+#endif++	/* If there is not an MBS form but is a WCS form, try converting+	 * with the native locale to be used for translating it to specified+	 * character-set. */+	if ((aes->aes_set & AES_SET_MBS) == 0 &&+	    (aes->aes_set & AES_SET_WCS) != 0) {+		archive_string_empty(&(aes->aes_mbs));+		r = archive_string_append_from_wcs(&(aes->aes_mbs),+		    aes->aes_wcs.s, aes->aes_wcs.length);+		if (r == 0)+			aes->aes_set |= AES_SET_MBS;+		else if (errno == ENOMEM)+			return (-1);+		else+			ret = -1;+	}+	/* If we already have an MBS form, use it to be translated to+	 * specified character-set. */+	if (aes->aes_set & AES_SET_MBS) {+		if (sc == NULL) {+			/* Conversion is unneeded. */+			*p = aes->aes_mbs.s;+			if (length != NULL)+				*length = aes->aes_mbs.length;+			return (0);+		}+		ret = archive_strncpy_l(&(aes->aes_mbs_in_locale),+		    aes->aes_mbs.s, aes->aes_mbs.length, sc);+		*p = aes->aes_mbs_in_locale.s;+		if (length != NULL)+			*length = aes->aes_mbs_in_locale.length;+	} else {+		*p = NULL;+		if (length != NULL)+			*length = 0;+	}+	return (ret);+}++int+archive_mstring_copy_mbs(struct archive_mstring *aes, const char *mbs)+{+	if (mbs == NULL) {+		aes->aes_set = 0;+		return (0);+	}+	return (archive_mstring_copy_mbs_len(aes, mbs, strlen(mbs)));+}++int+archive_mstring_copy_mbs_len(struct archive_mstring *aes, const char *mbs,+    size_t len)+{+	if (mbs == NULL) {+		aes->aes_set = 0;+		return (0);+	}+	aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */+	archive_strncpy(&(aes->aes_mbs), mbs, len);+	archive_string_empty(&(aes->aes_utf8));+	archive_wstring_empty(&(aes->aes_wcs));+	return (0);+}++int+archive_mstring_copy_wcs(struct archive_mstring *aes, const wchar_t *wcs)+{+	return archive_mstring_copy_wcs_len(aes, wcs,+				wcs == NULL ? 0 : wcslen(wcs));+}++int+archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8)+{+  if (utf8 == NULL) {+    aes->aes_set = 0;+    return (0);+  }+  aes->aes_set = AES_SET_UTF8;+  archive_string_empty(&(aes->aes_mbs));+  archive_string_empty(&(aes->aes_wcs));+  archive_strncpy(&(aes->aes_utf8), utf8, strlen(utf8));+  return (int)strlen(utf8);+}++int+archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs,+    size_t len)+{+	if (wcs == NULL) {+		aes->aes_set = 0;+		return (0);+	}+	aes->aes_set = AES_SET_WCS; /* Only WCS form set. */+	archive_string_empty(&(aes->aes_mbs));+	archive_string_empty(&(aes->aes_utf8));+	archive_wstrncpy(&(aes->aes_wcs), wcs, len);+	return (0);+}++int+archive_mstring_copy_mbs_len_l(struct archive_mstring *aes,+    const char *mbs, size_t len, struct archive_string_conv *sc)+{+	int r;++	if (mbs == NULL) {+		aes->aes_set = 0;+		return (0);+	}+	archive_string_empty(&(aes->aes_mbs));+	archive_wstring_empty(&(aes->aes_wcs));+	archive_string_empty(&(aes->aes_utf8));+#if defined(_WIN32) && !defined(__CYGWIN__)+	/*+	 * Internationalization programming on Windows must use Wide+	 * characters because Windows platform cannot make locale UTF-8.+	 */+	if (sc == NULL) {+		if (archive_string_append(&(aes->aes_mbs),+			mbs, mbsnbytes(mbs, len)) == NULL) {+			aes->aes_set = 0;+			r = -1;+		} else {+			aes->aes_set = AES_SET_MBS;+			r = 0;+		}+#if defined(HAVE_ICONV)+	} else if (sc != NULL && sc->cd_w != (iconv_t)-1) {+		/*+		 * This case happens only when MultiByteToWideChar() cannot+		 * handle sc->from_cp, and we have to iconv in order to+		 * translate character-set to wchar_t,UTF-16.+		 */+		iconv_t cd = sc->cd;+		unsigned from_cp;+		int flag;++		/*+		 * Translate multi-bytes from some character-set to UTF-8.+		 */ +		sc->cd = sc->cd_w;+		r = archive_strncpy_l(&(aes->aes_utf8), mbs, len, sc);+		sc->cd = cd;+		if (r != 0) {+			aes->aes_set = 0;+			return (r);+		}+		aes->aes_set = AES_SET_UTF8;++		/*+		 * Append the UTF-8 string into wstring.+		 */ +		flag = sc->flag;+		sc->flag &= ~(SCONV_NORMALIZATION_C+				| SCONV_TO_UTF16| SCONV_FROM_UTF16);+		from_cp = sc->from_cp;+		sc->from_cp = CP_UTF8;+		r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs),+			aes->aes_utf8.s, aes->aes_utf8.length, sc);+		sc->flag = flag;+		sc->from_cp = from_cp;+		if (r == 0)+			aes->aes_set |= AES_SET_WCS;+#endif+	} else {+		r = archive_wstring_append_from_mbs_in_codepage(+		    &(aes->aes_wcs), mbs, len, sc);+		if (r == 0)+			aes->aes_set = AES_SET_WCS;+		else+			aes->aes_set = 0;+	}+#else+	r = archive_strncpy_l(&(aes->aes_mbs), mbs, len, sc);+	if (r == 0)+		aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */+	else+		aes->aes_set = 0;+#endif+	return (r);+}++/*+ * The 'update' form tries to proactively update all forms of+ * this string (WCS and MBS) and returns an error if any of+ * them fail.  This is used by the 'pax' handler, for instance,+ * to detect and report character-conversion failures early while+ * still allowing clients to get potentially useful values from+ * the more tolerant lazy conversions.  (get_mbs and get_wcs will+ * strive to give the user something useful, so you can get hopefully+ * usable values even if some of the character conversions are failing.)+ */+int+archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes,+    const char *utf8)+{+	struct archive_string_conv *sc;+	int r;++	if (utf8 == NULL) {+		aes->aes_set = 0;+		return (0); /* Succeeded in clearing everything. */+	}++	/* Save the UTF8 string. */+	archive_strcpy(&(aes->aes_utf8), utf8);++	/* Empty the mbs and wcs strings. */+	archive_string_empty(&(aes->aes_mbs));+	archive_wstring_empty(&(aes->aes_wcs));++	aes->aes_set = AES_SET_UTF8;	/* Only UTF8 is set now. */++	/* Try converting UTF-8 to MBS, return false on failure. */+	sc = archive_string_conversion_from_charset(a, "UTF-8", 1);+	if (sc == NULL)+		return (-1);/* Couldn't allocate memory for sc. */+	r = archive_strcpy_l(&(aes->aes_mbs), utf8, sc);+	if (a == NULL)+		free_sconv_object(sc);+	if (r != 0)+		return (-1);+	aes->aes_set = AES_SET_UTF8 | AES_SET_MBS; /* Both UTF8 and MBS set. */++	/* Try converting MBS to WCS, return false on failure. */+	if (archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s,+	    aes->aes_mbs.length))+		return (-1);+	aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;++	/* All conversions succeeded. */+	return (0);+}
+ c/archive_string.h view
@@ -0,0 +1,243 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/archive_string.h 201092 2009-12-28 02:26:06Z kientzle $+ *+ */++#ifndef ARCHIVE_STRING_H_INCLUDED+#define ARCHIVE_STRING_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#ifndef __LIBARCHIVE_TEST+#error This header is only to be used internally to libarchive.+#endif+#endif++#include <stdarg.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>  /* required for wchar_t on some systems */+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_WCHAR_H+#include <wchar.h>+#endif++#include "archive.h"++/*+ * Basic resizable/reusable string support similar to Java's "StringBuffer."+ *+ * Unlike sbuf(9), the buffers here are fully reusable and track the+ * length throughout.+ */++struct archive_string {+	char	*s;  /* Pointer to the storage */+	size_t	 length; /* Length of 's' in characters */+	size_t	 buffer_length; /* Length of malloc-ed storage in bytes. */+};++struct archive_wstring {+	wchar_t	*s;  /* Pointer to the storage */+	size_t	 length; /* Length of 's' in characters */+	size_t	 buffer_length; /* Length of malloc-ed storage in bytes. */+};++struct archive_string_conv;++/* Initialize an archive_string object on the stack or elsewhere. */+#define	archive_string_init(a)	\+	do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0)++/* Append a C char to an archive_string, resizing as necessary. */+struct archive_string *+archive_strappend_char(struct archive_string *, char);++/* Ditto for a wchar_t and an archive_wstring. */+struct archive_wstring *+archive_wstrappend_wchar(struct archive_wstring *, wchar_t);++/* Append a raw array to an archive_string, resizing as necessary */+struct archive_string *+archive_array_append(struct archive_string *, const char *, size_t);++/* Convert a Unicode string to current locale and append the result. */+/* Returns -1 if conversion fails. */+int+archive_string_append_from_wcs(struct archive_string *, const wchar_t *, size_t);+++/* Create a string conversion object.+ * Return NULL and set a error message if the conversion is not supported+ * on the platform. */+struct archive_string_conv *+archive_string_conversion_to_charset(struct archive *, const char *, int);+struct archive_string_conv *+archive_string_conversion_from_charset(struct archive *, const char *, int);+/* Create the default string conversion object for reading/writing an archive.+ * Return NULL if the conversion is unneeded.+ * Note: On non Windows platform this always returns NULL.+ */+struct archive_string_conv *+archive_string_default_conversion_for_read(struct archive *);+struct archive_string_conv *+archive_string_default_conversion_for_write(struct archive *);+/* Dispose of a string conversion object. */+void+archive_string_conversion_free(struct archive *);+const char *+archive_string_conversion_charset_name(struct archive_string_conv *);+void+archive_string_conversion_set_opt(struct archive_string_conv *, int);+#define SCONV_SET_OPT_UTF8_LIBARCHIVE2X	1+#define SCONV_SET_OPT_NORMALIZATION_C	2+#define SCONV_SET_OPT_NORMALIZATION_D	4+++/* Copy one archive_string to another in locale conversion.+ * Return -1 if conversion fails. */+int+archive_strncpy_l(struct archive_string *, const void *, size_t,+    struct archive_string_conv *);++/* Copy one archive_string to another in locale conversion.+ * Return -1 if conversion fails. */+int+archive_strncat_l(struct archive_string *, const void *, size_t,+    struct archive_string_conv *);+++/* Copy one archive_string to another */+#define	archive_string_copy(dest, src) \+	((dest)->length = 0, archive_string_concat((dest), (src)))+#define	archive_wstring_copy(dest, src) \+	((dest)->length = 0, archive_wstring_concat((dest), (src)))++/* Concatenate one archive_string to another */+void archive_string_concat(struct archive_string *dest, struct archive_string *src);+void archive_wstring_concat(struct archive_wstring *dest, struct archive_wstring *src);++/* Ensure that the underlying buffer is at least as large as the request. */+struct archive_string *+archive_string_ensure(struct archive_string *, size_t);+struct archive_wstring *+archive_wstring_ensure(struct archive_wstring *, size_t);++/* Append C string, which may lack trailing \0. */+/* The source is declared void * here because this gets used with+ * "signed char *", "unsigned char *" and "char *" arguments.+ * Declaring it "char *" as with some of the other functions just+ * leads to a lot of extra casts. */+struct archive_string *+archive_strncat(struct archive_string *, const void *, size_t);+struct archive_wstring *+archive_wstrncat(struct archive_wstring *, const wchar_t *, size_t);++/* Append a C string to an archive_string, resizing as necessary. */+struct archive_string *+archive_strcat(struct archive_string *, const void *);+struct archive_wstring *+archive_wstrcat(struct archive_wstring *, const wchar_t *);++/* Copy a C string to an archive_string, resizing as necessary. */+#define	archive_strcpy(as,p) \+	archive_strncpy((as), (p), ((p) == NULL ? 0 : strlen(p)))+#define	archive_wstrcpy(as,p) \+	archive_wstrncpy((as), (p), ((p) == NULL ? 0 : wcslen(p)))+#define	archive_strcpy_l(as,p,lo) \+	archive_strncpy_l((as), (p), ((p) == NULL ? 0 : strlen(p)), (lo))++/* Copy a C string to an archive_string with limit, resizing as necessary. */+#define	archive_strncpy(as,p,l) \+	((as)->length=0, archive_strncat((as), (p), (l)))+#define	archive_wstrncpy(as,p,l) \+	((as)->length = 0, archive_wstrncat((as), (p), (l)))++/* Return length of string. */+#define	archive_strlen(a) ((a)->length)++/* Set string length to zero. */+#define	archive_string_empty(a) ((a)->length = 0)+#define	archive_wstring_empty(a) ((a)->length = 0)++/* Release any allocated storage resources. */+void	archive_string_free(struct archive_string *);+void	archive_wstring_free(struct archive_wstring *);++/* Like 'vsprintf', but resizes the underlying string as necessary. */+/* Note: This only implements a small subset of standard printf functionality. */+void	archive_string_vsprintf(struct archive_string *, const char *,+	    va_list) __LA_PRINTF(2, 0);+void	archive_string_sprintf(struct archive_string *, const char *, ...)+	    __LA_PRINTF(2, 3);++/* Translates from MBS to Unicode. */+/* Returns non-zero if conversion failed in any way. */+int archive_wstring_append_from_mbs(struct archive_wstring *dest,+    const char *, size_t);+++/* A "multistring" can hold Unicode, UTF8, or MBS versions of+ * the string.  If you set and read the same version, no translation+ * is done.  If you set and read different versions, the library+ * will attempt to transparently convert.+ */+struct archive_mstring {+	struct archive_string aes_mbs;+	struct archive_string aes_utf8;+	struct archive_wstring aes_wcs;+	struct archive_string aes_mbs_in_locale;+	/* Bitmap of which of the above are valid.  Because we're lazy+	 * about malloc-ing and reusing the underlying storage, we+	 * can't rely on NULL pointers to indicate whether a string+	 * has been set. */+	int aes_set;+#define	AES_SET_MBS 1+#define	AES_SET_UTF8 2+#define	AES_SET_WCS 4+};++void	archive_mstring_clean(struct archive_mstring *);+void	archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src);+int archive_mstring_get_mbs(struct archive *, struct archive_mstring *, const char **);+int archive_mstring_get_utf8(struct archive *, struct archive_mstring *, const char **);+int archive_mstring_get_wcs(struct archive *, struct archive_mstring *, const wchar_t **);+int	archive_mstring_get_mbs_l(struct archive_mstring *, const char **,+	    size_t *, struct archive_string_conv *);+int	archive_mstring_copy_mbs(struct archive_mstring *, const char *mbs);+int	archive_mstring_copy_mbs_len(struct archive_mstring *, const char *mbs,+	    size_t);+int	archive_mstring_copy_utf8(struct archive_mstring *, const char *utf8);+int	archive_mstring_copy_wcs(struct archive_mstring *, const wchar_t *wcs);+int	archive_mstring_copy_wcs_len(struct archive_mstring *,+	    const wchar_t *wcs, size_t);+int	archive_mstring_copy_mbs_len_l(struct archive_mstring *,+	    const char *mbs, size_t, struct archive_string_conv *);+int     archive_mstring_update_utf8(struct archive *, struct archive_mstring *aes, const char *utf8);+++#endif
+ c/archive_string_composition.h view
@@ -0,0 +1,2292 @@+/*-+ * Copyright (c) 2011-2012 libarchive Project+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ *+ */++/*+ * ATTENTION!+ *  This file is generated by build/utils/gen_archive_string_composition_h.sh+ *  from http://unicode.org/Public/6.0.0/ucd/UnicodeData.txt+ *+ *  See also http://unicode.org/report/tr15/+ */++#ifndef ARCHIVE_STRING_COMPOSITION_H_INCLUDED+#define ARCHIVE_STRING_COMPOSITION_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++struct unicode_composition_table {+	uint32_t cp1;+	uint32_t cp2;+	uint32_t nfc;+};++static const struct unicode_composition_table u_composition_table[] = {+	{ 0x0003C , 0x00338 , 0x0226E },+	{ 0x0003D , 0x00338 , 0x02260 },+	{ 0x0003E , 0x00338 , 0x0226F },+	{ 0x00041 , 0x00300 , 0x000C0 },+	{ 0x00041 , 0x00301 , 0x000C1 },+	{ 0x00041 , 0x00302 , 0x000C2 },+	{ 0x00041 , 0x00303 , 0x000C3 },+	{ 0x00041 , 0x00304 , 0x00100 },+	{ 0x00041 , 0x00306 , 0x00102 },+	{ 0x00041 , 0x00307 , 0x00226 },+	{ 0x00041 , 0x00308 , 0x000C4 },+	{ 0x00041 , 0x00309 , 0x01EA2 },+	{ 0x00041 , 0x0030A , 0x000C5 },+	{ 0x00041 , 0x0030C , 0x001CD },+	{ 0x00041 , 0x0030F , 0x00200 },+	{ 0x00041 , 0x00311 , 0x00202 },+	{ 0x00041 , 0x00323 , 0x01EA0 },+	{ 0x00041 , 0x00325 , 0x01E00 },+	{ 0x00041 , 0x00328 , 0x00104 },+	{ 0x00042 , 0x00307 , 0x01E02 },+	{ 0x00042 , 0x00323 , 0x01E04 },+	{ 0x00042 , 0x00331 , 0x01E06 },+	{ 0x00043 , 0x00301 , 0x00106 },+	{ 0x00043 , 0x00302 , 0x00108 },+	{ 0x00043 , 0x00307 , 0x0010A },+	{ 0x00043 , 0x0030C , 0x0010C },+	{ 0x00043 , 0x00327 , 0x000C7 },+	{ 0x00044 , 0x00307 , 0x01E0A },+	{ 0x00044 , 0x0030C , 0x0010E },+	{ 0x00044 , 0x00323 , 0x01E0C },+	{ 0x00044 , 0x00327 , 0x01E10 },+	{ 0x00044 , 0x0032D , 0x01E12 },+	{ 0x00044 , 0x00331 , 0x01E0E },+	{ 0x00045 , 0x00300 , 0x000C8 },+	{ 0x00045 , 0x00301 , 0x000C9 },+	{ 0x00045 , 0x00302 , 0x000CA },+	{ 0x00045 , 0x00303 , 0x01EBC },+	{ 0x00045 , 0x00304 , 0x00112 },+	{ 0x00045 , 0x00306 , 0x00114 },+	{ 0x00045 , 0x00307 , 0x00116 },+	{ 0x00045 , 0x00308 , 0x000CB },+	{ 0x00045 , 0x00309 , 0x01EBA },+	{ 0x00045 , 0x0030C , 0x0011A },+	{ 0x00045 , 0x0030F , 0x00204 },+	{ 0x00045 , 0x00311 , 0x00206 },+	{ 0x00045 , 0x00323 , 0x01EB8 },+	{ 0x00045 , 0x00327 , 0x00228 },+	{ 0x00045 , 0x00328 , 0x00118 },+	{ 0x00045 , 0x0032D , 0x01E18 },+	{ 0x00045 , 0x00330 , 0x01E1A },+	{ 0x00046 , 0x00307 , 0x01E1E },+	{ 0x00047 , 0x00301 , 0x001F4 },+	{ 0x00047 , 0x00302 , 0x0011C },+	{ 0x00047 , 0x00304 , 0x01E20 },+	{ 0x00047 , 0x00306 , 0x0011E },+	{ 0x00047 , 0x00307 , 0x00120 },+	{ 0x00047 , 0x0030C , 0x001E6 },+	{ 0x00047 , 0x00327 , 0x00122 },+	{ 0x00048 , 0x00302 , 0x00124 },+	{ 0x00048 , 0x00307 , 0x01E22 },+	{ 0x00048 , 0x00308 , 0x01E26 },+	{ 0x00048 , 0x0030C , 0x0021E },+	{ 0x00048 , 0x00323 , 0x01E24 },+	{ 0x00048 , 0x00327 , 0x01E28 },+	{ 0x00048 , 0x0032E , 0x01E2A },+	{ 0x00049 , 0x00300 , 0x000CC },+	{ 0x00049 , 0x00301 , 0x000CD },+	{ 0x00049 , 0x00302 , 0x000CE },+	{ 0x00049 , 0x00303 , 0x00128 },+	{ 0x00049 , 0x00304 , 0x0012A },+	{ 0x00049 , 0x00306 , 0x0012C },+	{ 0x00049 , 0x00307 , 0x00130 },+	{ 0x00049 , 0x00308 , 0x000CF },+	{ 0x00049 , 0x00309 , 0x01EC8 },+	{ 0x00049 , 0x0030C , 0x001CF },+	{ 0x00049 , 0x0030F , 0x00208 },+	{ 0x00049 , 0x00311 , 0x0020A },+	{ 0x00049 , 0x00323 , 0x01ECA },+	{ 0x00049 , 0x00328 , 0x0012E },+	{ 0x00049 , 0x00330 , 0x01E2C },+	{ 0x0004A , 0x00302 , 0x00134 },+	{ 0x0004B , 0x00301 , 0x01E30 },+	{ 0x0004B , 0x0030C , 0x001E8 },+	{ 0x0004B , 0x00323 , 0x01E32 },+	{ 0x0004B , 0x00327 , 0x00136 },+	{ 0x0004B , 0x00331 , 0x01E34 },+	{ 0x0004C , 0x00301 , 0x00139 },+	{ 0x0004C , 0x0030C , 0x0013D },+	{ 0x0004C , 0x00323 , 0x01E36 },+	{ 0x0004C , 0x00327 , 0x0013B },+	{ 0x0004C , 0x0032D , 0x01E3C },+	{ 0x0004C , 0x00331 , 0x01E3A },+	{ 0x0004D , 0x00301 , 0x01E3E },+	{ 0x0004D , 0x00307 , 0x01E40 },+	{ 0x0004D , 0x00323 , 0x01E42 },+	{ 0x0004E , 0x00300 , 0x001F8 },+	{ 0x0004E , 0x00301 , 0x00143 },+	{ 0x0004E , 0x00303 , 0x000D1 },+	{ 0x0004E , 0x00307 , 0x01E44 },+	{ 0x0004E , 0x0030C , 0x00147 },+	{ 0x0004E , 0x00323 , 0x01E46 },+	{ 0x0004E , 0x00327 , 0x00145 },+	{ 0x0004E , 0x0032D , 0x01E4A },+	{ 0x0004E , 0x00331 , 0x01E48 },+	{ 0x0004F , 0x00300 , 0x000D2 },+	{ 0x0004F , 0x00301 , 0x000D3 },+	{ 0x0004F , 0x00302 , 0x000D4 },+	{ 0x0004F , 0x00303 , 0x000D5 },+	{ 0x0004F , 0x00304 , 0x0014C },+	{ 0x0004F , 0x00306 , 0x0014E },+	{ 0x0004F , 0x00307 , 0x0022E },+	{ 0x0004F , 0x00308 , 0x000D6 },+	{ 0x0004F , 0x00309 , 0x01ECE },+	{ 0x0004F , 0x0030B , 0x00150 },+	{ 0x0004F , 0x0030C , 0x001D1 },+	{ 0x0004F , 0x0030F , 0x0020C },+	{ 0x0004F , 0x00311 , 0x0020E },+	{ 0x0004F , 0x0031B , 0x001A0 },+	{ 0x0004F , 0x00323 , 0x01ECC },+	{ 0x0004F , 0x00328 , 0x001EA },+	{ 0x00050 , 0x00301 , 0x01E54 },+	{ 0x00050 , 0x00307 , 0x01E56 },+	{ 0x00052 , 0x00301 , 0x00154 },+	{ 0x00052 , 0x00307 , 0x01E58 },+	{ 0x00052 , 0x0030C , 0x00158 },+	{ 0x00052 , 0x0030F , 0x00210 },+	{ 0x00052 , 0x00311 , 0x00212 },+	{ 0x00052 , 0x00323 , 0x01E5A },+	{ 0x00052 , 0x00327 , 0x00156 },+	{ 0x00052 , 0x00331 , 0x01E5E },+	{ 0x00053 , 0x00301 , 0x0015A },+	{ 0x00053 , 0x00302 , 0x0015C },+	{ 0x00053 , 0x00307 , 0x01E60 },+	{ 0x00053 , 0x0030C , 0x00160 },+	{ 0x00053 , 0x00323 , 0x01E62 },+	{ 0x00053 , 0x00326 , 0x00218 },+	{ 0x00053 , 0x00327 , 0x0015E },+	{ 0x00054 , 0x00307 , 0x01E6A },+	{ 0x00054 , 0x0030C , 0x00164 },+	{ 0x00054 , 0x00323 , 0x01E6C },+	{ 0x00054 , 0x00326 , 0x0021A },+	{ 0x00054 , 0x00327 , 0x00162 },+	{ 0x00054 , 0x0032D , 0x01E70 },+	{ 0x00054 , 0x00331 , 0x01E6E },+	{ 0x00055 , 0x00300 , 0x000D9 },+	{ 0x00055 , 0x00301 , 0x000DA },+	{ 0x00055 , 0x00302 , 0x000DB },+	{ 0x00055 , 0x00303 , 0x00168 },+	{ 0x00055 , 0x00304 , 0x0016A },+	{ 0x00055 , 0x00306 , 0x0016C },+	{ 0x00055 , 0x00308 , 0x000DC },+	{ 0x00055 , 0x00309 , 0x01EE6 },+	{ 0x00055 , 0x0030A , 0x0016E },+	{ 0x00055 , 0x0030B , 0x00170 },+	{ 0x00055 , 0x0030C , 0x001D3 },+	{ 0x00055 , 0x0030F , 0x00214 },+	{ 0x00055 , 0x00311 , 0x00216 },+	{ 0x00055 , 0x0031B , 0x001AF },+	{ 0x00055 , 0x00323 , 0x01EE4 },+	{ 0x00055 , 0x00324 , 0x01E72 },+	{ 0x00055 , 0x00328 , 0x00172 },+	{ 0x00055 , 0x0032D , 0x01E76 },+	{ 0x00055 , 0x00330 , 0x01E74 },+	{ 0x00056 , 0x00303 , 0x01E7C },+	{ 0x00056 , 0x00323 , 0x01E7E },+	{ 0x00057 , 0x00300 , 0x01E80 },+	{ 0x00057 , 0x00301 , 0x01E82 },+	{ 0x00057 , 0x00302 , 0x00174 },+	{ 0x00057 , 0x00307 , 0x01E86 },+	{ 0x00057 , 0x00308 , 0x01E84 },+	{ 0x00057 , 0x00323 , 0x01E88 },+	{ 0x00058 , 0x00307 , 0x01E8A },+	{ 0x00058 , 0x00308 , 0x01E8C },+	{ 0x00059 , 0x00300 , 0x01EF2 },+	{ 0x00059 , 0x00301 , 0x000DD },+	{ 0x00059 , 0x00302 , 0x00176 },+	{ 0x00059 , 0x00303 , 0x01EF8 },+	{ 0x00059 , 0x00304 , 0x00232 },+	{ 0x00059 , 0x00307 , 0x01E8E },+	{ 0x00059 , 0x00308 , 0x00178 },+	{ 0x00059 , 0x00309 , 0x01EF6 },+	{ 0x00059 , 0x00323 , 0x01EF4 },+	{ 0x0005A , 0x00301 , 0x00179 },+	{ 0x0005A , 0x00302 , 0x01E90 },+	{ 0x0005A , 0x00307 , 0x0017B },+	{ 0x0005A , 0x0030C , 0x0017D },+	{ 0x0005A , 0x00323 , 0x01E92 },+	{ 0x0005A , 0x00331 , 0x01E94 },+	{ 0x00061 , 0x00300 , 0x000E0 },+	{ 0x00061 , 0x00301 , 0x000E1 },+	{ 0x00061 , 0x00302 , 0x000E2 },+	{ 0x00061 , 0x00303 , 0x000E3 },+	{ 0x00061 , 0x00304 , 0x00101 },+	{ 0x00061 , 0x00306 , 0x00103 },+	{ 0x00061 , 0x00307 , 0x00227 },+	{ 0x00061 , 0x00308 , 0x000E4 },+	{ 0x00061 , 0x00309 , 0x01EA3 },+	{ 0x00061 , 0x0030A , 0x000E5 },+	{ 0x00061 , 0x0030C , 0x001CE },+	{ 0x00061 , 0x0030F , 0x00201 },+	{ 0x00061 , 0x00311 , 0x00203 },+	{ 0x00061 , 0x00323 , 0x01EA1 },+	{ 0x00061 , 0x00325 , 0x01E01 },+	{ 0x00061 , 0x00328 , 0x00105 },+	{ 0x00062 , 0x00307 , 0x01E03 },+	{ 0x00062 , 0x00323 , 0x01E05 },+	{ 0x00062 , 0x00331 , 0x01E07 },+	{ 0x00063 , 0x00301 , 0x00107 },+	{ 0x00063 , 0x00302 , 0x00109 },+	{ 0x00063 , 0x00307 , 0x0010B },+	{ 0x00063 , 0x0030C , 0x0010D },+	{ 0x00063 , 0x00327 , 0x000E7 },+	{ 0x00064 , 0x00307 , 0x01E0B },+	{ 0x00064 , 0x0030C , 0x0010F },+	{ 0x00064 , 0x00323 , 0x01E0D },+	{ 0x00064 , 0x00327 , 0x01E11 },+	{ 0x00064 , 0x0032D , 0x01E13 },+	{ 0x00064 , 0x00331 , 0x01E0F },+	{ 0x00065 , 0x00300 , 0x000E8 },+	{ 0x00065 , 0x00301 , 0x000E9 },+	{ 0x00065 , 0x00302 , 0x000EA },+	{ 0x00065 , 0x00303 , 0x01EBD },+	{ 0x00065 , 0x00304 , 0x00113 },+	{ 0x00065 , 0x00306 , 0x00115 },+	{ 0x00065 , 0x00307 , 0x00117 },+	{ 0x00065 , 0x00308 , 0x000EB },+	{ 0x00065 , 0x00309 , 0x01EBB },+	{ 0x00065 , 0x0030C , 0x0011B },+	{ 0x00065 , 0x0030F , 0x00205 },+	{ 0x00065 , 0x00311 , 0x00207 },+	{ 0x00065 , 0x00323 , 0x01EB9 },+	{ 0x00065 , 0x00327 , 0x00229 },+	{ 0x00065 , 0x00328 , 0x00119 },+	{ 0x00065 , 0x0032D , 0x01E19 },+	{ 0x00065 , 0x00330 , 0x01E1B },+	{ 0x00066 , 0x00307 , 0x01E1F },+	{ 0x00067 , 0x00301 , 0x001F5 },+	{ 0x00067 , 0x00302 , 0x0011D },+	{ 0x00067 , 0x00304 , 0x01E21 },+	{ 0x00067 , 0x00306 , 0x0011F },+	{ 0x00067 , 0x00307 , 0x00121 },+	{ 0x00067 , 0x0030C , 0x001E7 },+	{ 0x00067 , 0x00327 , 0x00123 },+	{ 0x00068 , 0x00302 , 0x00125 },+	{ 0x00068 , 0x00307 , 0x01E23 },+	{ 0x00068 , 0x00308 , 0x01E27 },+	{ 0x00068 , 0x0030C , 0x0021F },+	{ 0x00068 , 0x00323 , 0x01E25 },+	{ 0x00068 , 0x00327 , 0x01E29 },+	{ 0x00068 , 0x0032E , 0x01E2B },+	{ 0x00068 , 0x00331 , 0x01E96 },+	{ 0x00069 , 0x00300 , 0x000EC },+	{ 0x00069 , 0x00301 , 0x000ED },+	{ 0x00069 , 0x00302 , 0x000EE },+	{ 0x00069 , 0x00303 , 0x00129 },+	{ 0x00069 , 0x00304 , 0x0012B },+	{ 0x00069 , 0x00306 , 0x0012D },+	{ 0x00069 , 0x00308 , 0x000EF },+	{ 0x00069 , 0x00309 , 0x01EC9 },+	{ 0x00069 , 0x0030C , 0x001D0 },+	{ 0x00069 , 0x0030F , 0x00209 },+	{ 0x00069 , 0x00311 , 0x0020B },+	{ 0x00069 , 0x00323 , 0x01ECB },+	{ 0x00069 , 0x00328 , 0x0012F },+	{ 0x00069 , 0x00330 , 0x01E2D },+	{ 0x0006A , 0x00302 , 0x00135 },+	{ 0x0006A , 0x0030C , 0x001F0 },+	{ 0x0006B , 0x00301 , 0x01E31 },+	{ 0x0006B , 0x0030C , 0x001E9 },+	{ 0x0006B , 0x00323 , 0x01E33 },+	{ 0x0006B , 0x00327 , 0x00137 },+	{ 0x0006B , 0x00331 , 0x01E35 },+	{ 0x0006C , 0x00301 , 0x0013A },+	{ 0x0006C , 0x0030C , 0x0013E },+	{ 0x0006C , 0x00323 , 0x01E37 },+	{ 0x0006C , 0x00327 , 0x0013C },+	{ 0x0006C , 0x0032D , 0x01E3D },+	{ 0x0006C , 0x00331 , 0x01E3B },+	{ 0x0006D , 0x00301 , 0x01E3F },+	{ 0x0006D , 0x00307 , 0x01E41 },+	{ 0x0006D , 0x00323 , 0x01E43 },+	{ 0x0006E , 0x00300 , 0x001F9 },+	{ 0x0006E , 0x00301 , 0x00144 },+	{ 0x0006E , 0x00303 , 0x000F1 },+	{ 0x0006E , 0x00307 , 0x01E45 },+	{ 0x0006E , 0x0030C , 0x00148 },+	{ 0x0006E , 0x00323 , 0x01E47 },+	{ 0x0006E , 0x00327 , 0x00146 },+	{ 0x0006E , 0x0032D , 0x01E4B },+	{ 0x0006E , 0x00331 , 0x01E49 },+	{ 0x0006F , 0x00300 , 0x000F2 },+	{ 0x0006F , 0x00301 , 0x000F3 },+	{ 0x0006F , 0x00302 , 0x000F4 },+	{ 0x0006F , 0x00303 , 0x000F5 },+	{ 0x0006F , 0x00304 , 0x0014D },+	{ 0x0006F , 0x00306 , 0x0014F },+	{ 0x0006F , 0x00307 , 0x0022F },+	{ 0x0006F , 0x00308 , 0x000F6 },+	{ 0x0006F , 0x00309 , 0x01ECF },+	{ 0x0006F , 0x0030B , 0x00151 },+	{ 0x0006F , 0x0030C , 0x001D2 },+	{ 0x0006F , 0x0030F , 0x0020D },+	{ 0x0006F , 0x00311 , 0x0020F },+	{ 0x0006F , 0x0031B , 0x001A1 },+	{ 0x0006F , 0x00323 , 0x01ECD },+	{ 0x0006F , 0x00328 , 0x001EB },+	{ 0x00070 , 0x00301 , 0x01E55 },+	{ 0x00070 , 0x00307 , 0x01E57 },+	{ 0x00072 , 0x00301 , 0x00155 },+	{ 0x00072 , 0x00307 , 0x01E59 },+	{ 0x00072 , 0x0030C , 0x00159 },+	{ 0x00072 , 0x0030F , 0x00211 },+	{ 0x00072 , 0x00311 , 0x00213 },+	{ 0x00072 , 0x00323 , 0x01E5B },+	{ 0x00072 , 0x00327 , 0x00157 },+	{ 0x00072 , 0x00331 , 0x01E5F },+	{ 0x00073 , 0x00301 , 0x0015B },+	{ 0x00073 , 0x00302 , 0x0015D },+	{ 0x00073 , 0x00307 , 0x01E61 },+	{ 0x00073 , 0x0030C , 0x00161 },+	{ 0x00073 , 0x00323 , 0x01E63 },+	{ 0x00073 , 0x00326 , 0x00219 },+	{ 0x00073 , 0x00327 , 0x0015F },+	{ 0x00074 , 0x00307 , 0x01E6B },+	{ 0x00074 , 0x00308 , 0x01E97 },+	{ 0x00074 , 0x0030C , 0x00165 },+	{ 0x00074 , 0x00323 , 0x01E6D },+	{ 0x00074 , 0x00326 , 0x0021B },+	{ 0x00074 , 0x00327 , 0x00163 },+	{ 0x00074 , 0x0032D , 0x01E71 },+	{ 0x00074 , 0x00331 , 0x01E6F },+	{ 0x00075 , 0x00300 , 0x000F9 },+	{ 0x00075 , 0x00301 , 0x000FA },+	{ 0x00075 , 0x00302 , 0x000FB },+	{ 0x00075 , 0x00303 , 0x00169 },+	{ 0x00075 , 0x00304 , 0x0016B },+	{ 0x00075 , 0x00306 , 0x0016D },+	{ 0x00075 , 0x00308 , 0x000FC },+	{ 0x00075 , 0x00309 , 0x01EE7 },+	{ 0x00075 , 0x0030A , 0x0016F },+	{ 0x00075 , 0x0030B , 0x00171 },+	{ 0x00075 , 0x0030C , 0x001D4 },+	{ 0x00075 , 0x0030F , 0x00215 },+	{ 0x00075 , 0x00311 , 0x00217 },+	{ 0x00075 , 0x0031B , 0x001B0 },+	{ 0x00075 , 0x00323 , 0x01EE5 },+	{ 0x00075 , 0x00324 , 0x01E73 },+	{ 0x00075 , 0x00328 , 0x00173 },+	{ 0x00075 , 0x0032D , 0x01E77 },+	{ 0x00075 , 0x00330 , 0x01E75 },+	{ 0x00076 , 0x00303 , 0x01E7D },+	{ 0x00076 , 0x00323 , 0x01E7F },+	{ 0x00077 , 0x00300 , 0x01E81 },+	{ 0x00077 , 0x00301 , 0x01E83 },+	{ 0x00077 , 0x00302 , 0x00175 },+	{ 0x00077 , 0x00307 , 0x01E87 },+	{ 0x00077 , 0x00308 , 0x01E85 },+	{ 0x00077 , 0x0030A , 0x01E98 },+	{ 0x00077 , 0x00323 , 0x01E89 },+	{ 0x00078 , 0x00307 , 0x01E8B },+	{ 0x00078 , 0x00308 , 0x01E8D },+	{ 0x00079 , 0x00300 , 0x01EF3 },+	{ 0x00079 , 0x00301 , 0x000FD },+	{ 0x00079 , 0x00302 , 0x00177 },+	{ 0x00079 , 0x00303 , 0x01EF9 },+	{ 0x00079 , 0x00304 , 0x00233 },+	{ 0x00079 , 0x00307 , 0x01E8F },+	{ 0x00079 , 0x00308 , 0x000FF },+	{ 0x00079 , 0x00309 , 0x01EF7 },+	{ 0x00079 , 0x0030A , 0x01E99 },+	{ 0x00079 , 0x00323 , 0x01EF5 },+	{ 0x0007A , 0x00301 , 0x0017A },+	{ 0x0007A , 0x00302 , 0x01E91 },+	{ 0x0007A , 0x00307 , 0x0017C },+	{ 0x0007A , 0x0030C , 0x0017E },+	{ 0x0007A , 0x00323 , 0x01E93 },+	{ 0x0007A , 0x00331 , 0x01E95 },+	{ 0x000A8 , 0x00300 , 0x01FED },+	{ 0x000A8 , 0x00301 , 0x00385 },+	{ 0x000A8 , 0x00342 , 0x01FC1 },+	{ 0x000C2 , 0x00300 , 0x01EA6 },+	{ 0x000C2 , 0x00301 , 0x01EA4 },+	{ 0x000C2 , 0x00303 , 0x01EAA },+	{ 0x000C2 , 0x00309 , 0x01EA8 },+	{ 0x000C4 , 0x00304 , 0x001DE },+	{ 0x000C5 , 0x00301 , 0x001FA },+	{ 0x000C6 , 0x00301 , 0x001FC },+	{ 0x000C6 , 0x00304 , 0x001E2 },+	{ 0x000C7 , 0x00301 , 0x01E08 },+	{ 0x000CA , 0x00300 , 0x01EC0 },+	{ 0x000CA , 0x00301 , 0x01EBE },+	{ 0x000CA , 0x00303 , 0x01EC4 },+	{ 0x000CA , 0x00309 , 0x01EC2 },+	{ 0x000CF , 0x00301 , 0x01E2E },+	{ 0x000D4 , 0x00300 , 0x01ED2 },+	{ 0x000D4 , 0x00301 , 0x01ED0 },+	{ 0x000D4 , 0x00303 , 0x01ED6 },+	{ 0x000D4 , 0x00309 , 0x01ED4 },+	{ 0x000D5 , 0x00301 , 0x01E4C },+	{ 0x000D5 , 0x00304 , 0x0022C },+	{ 0x000D5 , 0x00308 , 0x01E4E },+	{ 0x000D6 , 0x00304 , 0x0022A },+	{ 0x000D8 , 0x00301 , 0x001FE },+	{ 0x000DC , 0x00300 , 0x001DB },+	{ 0x000DC , 0x00301 , 0x001D7 },+	{ 0x000DC , 0x00304 , 0x001D5 },+	{ 0x000DC , 0x0030C , 0x001D9 },+	{ 0x000E2 , 0x00300 , 0x01EA7 },+	{ 0x000E2 , 0x00301 , 0x01EA5 },+	{ 0x000E2 , 0x00303 , 0x01EAB },+	{ 0x000E2 , 0x00309 , 0x01EA9 },+	{ 0x000E4 , 0x00304 , 0x001DF },+	{ 0x000E5 , 0x00301 , 0x001FB },+	{ 0x000E6 , 0x00301 , 0x001FD },+	{ 0x000E6 , 0x00304 , 0x001E3 },+	{ 0x000E7 , 0x00301 , 0x01E09 },+	{ 0x000EA , 0x00300 , 0x01EC1 },+	{ 0x000EA , 0x00301 , 0x01EBF },+	{ 0x000EA , 0x00303 , 0x01EC5 },+	{ 0x000EA , 0x00309 , 0x01EC3 },+	{ 0x000EF , 0x00301 , 0x01E2F },+	{ 0x000F4 , 0x00300 , 0x01ED3 },+	{ 0x000F4 , 0x00301 , 0x01ED1 },+	{ 0x000F4 , 0x00303 , 0x01ED7 },+	{ 0x000F4 , 0x00309 , 0x01ED5 },+	{ 0x000F5 , 0x00301 , 0x01E4D },+	{ 0x000F5 , 0x00304 , 0x0022D },+	{ 0x000F5 , 0x00308 , 0x01E4F },+	{ 0x000F6 , 0x00304 , 0x0022B },+	{ 0x000F8 , 0x00301 , 0x001FF },+	{ 0x000FC , 0x00300 , 0x001DC },+	{ 0x000FC , 0x00301 , 0x001D8 },+	{ 0x000FC , 0x00304 , 0x001D6 },+	{ 0x000FC , 0x0030C , 0x001DA },+	{ 0x00102 , 0x00300 , 0x01EB0 },+	{ 0x00102 , 0x00301 , 0x01EAE },+	{ 0x00102 , 0x00303 , 0x01EB4 },+	{ 0x00102 , 0x00309 , 0x01EB2 },+	{ 0x00103 , 0x00300 , 0x01EB1 },+	{ 0x00103 , 0x00301 , 0x01EAF },+	{ 0x00103 , 0x00303 , 0x01EB5 },+	{ 0x00103 , 0x00309 , 0x01EB3 },+	{ 0x00112 , 0x00300 , 0x01E14 },+	{ 0x00112 , 0x00301 , 0x01E16 },+	{ 0x00113 , 0x00300 , 0x01E15 },+	{ 0x00113 , 0x00301 , 0x01E17 },+	{ 0x0014C , 0x00300 , 0x01E50 },+	{ 0x0014C , 0x00301 , 0x01E52 },+	{ 0x0014D , 0x00300 , 0x01E51 },+	{ 0x0014D , 0x00301 , 0x01E53 },+	{ 0x0015A , 0x00307 , 0x01E64 },+	{ 0x0015B , 0x00307 , 0x01E65 },+	{ 0x00160 , 0x00307 , 0x01E66 },+	{ 0x00161 , 0x00307 , 0x01E67 },+	{ 0x00168 , 0x00301 , 0x01E78 },+	{ 0x00169 , 0x00301 , 0x01E79 },+	{ 0x0016A , 0x00308 , 0x01E7A },+	{ 0x0016B , 0x00308 , 0x01E7B },+	{ 0x0017F , 0x00307 , 0x01E9B },+	{ 0x001A0 , 0x00300 , 0x01EDC },+	{ 0x001A0 , 0x00301 , 0x01EDA },+	{ 0x001A0 , 0x00303 , 0x01EE0 },+	{ 0x001A0 , 0x00309 , 0x01EDE },+	{ 0x001A0 , 0x00323 , 0x01EE2 },+	{ 0x001A1 , 0x00300 , 0x01EDD },+	{ 0x001A1 , 0x00301 , 0x01EDB },+	{ 0x001A1 , 0x00303 , 0x01EE1 },+	{ 0x001A1 , 0x00309 , 0x01EDF },+	{ 0x001A1 , 0x00323 , 0x01EE3 },+	{ 0x001AF , 0x00300 , 0x01EEA },+	{ 0x001AF , 0x00301 , 0x01EE8 },+	{ 0x001AF , 0x00303 , 0x01EEE },+	{ 0x001AF , 0x00309 , 0x01EEC },+	{ 0x001AF , 0x00323 , 0x01EF0 },+	{ 0x001B0 , 0x00300 , 0x01EEB },+	{ 0x001B0 , 0x00301 , 0x01EE9 },+	{ 0x001B0 , 0x00303 , 0x01EEF },+	{ 0x001B0 , 0x00309 , 0x01EED },+	{ 0x001B0 , 0x00323 , 0x01EF1 },+	{ 0x001B7 , 0x0030C , 0x001EE },+	{ 0x001EA , 0x00304 , 0x001EC },+	{ 0x001EB , 0x00304 , 0x001ED },+	{ 0x00226 , 0x00304 , 0x001E0 },+	{ 0x00227 , 0x00304 , 0x001E1 },+	{ 0x00228 , 0x00306 , 0x01E1C },+	{ 0x00229 , 0x00306 , 0x01E1D },+	{ 0x0022E , 0x00304 , 0x00230 },+	{ 0x0022F , 0x00304 , 0x00231 },+	{ 0x00292 , 0x0030C , 0x001EF },+	{ 0x00391 , 0x00300 , 0x01FBA },+	{ 0x00391 , 0x00301 , 0x00386 },+	{ 0x00391 , 0x00304 , 0x01FB9 },+	{ 0x00391 , 0x00306 , 0x01FB8 },+	{ 0x00391 , 0x00313 , 0x01F08 },+	{ 0x00391 , 0x00314 , 0x01F09 },+	{ 0x00391 , 0x00345 , 0x01FBC },+	{ 0x00395 , 0x00300 , 0x01FC8 },+	{ 0x00395 , 0x00301 , 0x00388 },+	{ 0x00395 , 0x00313 , 0x01F18 },+	{ 0x00395 , 0x00314 , 0x01F19 },+	{ 0x00397 , 0x00300 , 0x01FCA },+	{ 0x00397 , 0x00301 , 0x00389 },+	{ 0x00397 , 0x00313 , 0x01F28 },+	{ 0x00397 , 0x00314 , 0x01F29 },+	{ 0x00397 , 0x00345 , 0x01FCC },+	{ 0x00399 , 0x00300 , 0x01FDA },+	{ 0x00399 , 0x00301 , 0x0038A },+	{ 0x00399 , 0x00304 , 0x01FD9 },+	{ 0x00399 , 0x00306 , 0x01FD8 },+	{ 0x00399 , 0x00308 , 0x003AA },+	{ 0x00399 , 0x00313 , 0x01F38 },+	{ 0x00399 , 0x00314 , 0x01F39 },+	{ 0x0039F , 0x00300 , 0x01FF8 },+	{ 0x0039F , 0x00301 , 0x0038C },+	{ 0x0039F , 0x00313 , 0x01F48 },+	{ 0x0039F , 0x00314 , 0x01F49 },+	{ 0x003A1 , 0x00314 , 0x01FEC },+	{ 0x003A5 , 0x00300 , 0x01FEA },+	{ 0x003A5 , 0x00301 , 0x0038E },+	{ 0x003A5 , 0x00304 , 0x01FE9 },+	{ 0x003A5 , 0x00306 , 0x01FE8 },+	{ 0x003A5 , 0x00308 , 0x003AB },+	{ 0x003A5 , 0x00314 , 0x01F59 },+	{ 0x003A9 , 0x00300 , 0x01FFA },+	{ 0x003A9 , 0x00301 , 0x0038F },+	{ 0x003A9 , 0x00313 , 0x01F68 },+	{ 0x003A9 , 0x00314 , 0x01F69 },+	{ 0x003A9 , 0x00345 , 0x01FFC },+	{ 0x003AC , 0x00345 , 0x01FB4 },+	{ 0x003AE , 0x00345 , 0x01FC4 },+	{ 0x003B1 , 0x00300 , 0x01F70 },+	{ 0x003B1 , 0x00301 , 0x003AC },+	{ 0x003B1 , 0x00304 , 0x01FB1 },+	{ 0x003B1 , 0x00306 , 0x01FB0 },+	{ 0x003B1 , 0x00313 , 0x01F00 },+	{ 0x003B1 , 0x00314 , 0x01F01 },+	{ 0x003B1 , 0x00342 , 0x01FB6 },+	{ 0x003B1 , 0x00345 , 0x01FB3 },+	{ 0x003B5 , 0x00300 , 0x01F72 },+	{ 0x003B5 , 0x00301 , 0x003AD },+	{ 0x003B5 , 0x00313 , 0x01F10 },+	{ 0x003B5 , 0x00314 , 0x01F11 },+	{ 0x003B7 , 0x00300 , 0x01F74 },+	{ 0x003B7 , 0x00301 , 0x003AE },+	{ 0x003B7 , 0x00313 , 0x01F20 },+	{ 0x003B7 , 0x00314 , 0x01F21 },+	{ 0x003B7 , 0x00342 , 0x01FC6 },+	{ 0x003B7 , 0x00345 , 0x01FC3 },+	{ 0x003B9 , 0x00300 , 0x01F76 },+	{ 0x003B9 , 0x00301 , 0x003AF },+	{ 0x003B9 , 0x00304 , 0x01FD1 },+	{ 0x003B9 , 0x00306 , 0x01FD0 },+	{ 0x003B9 , 0x00308 , 0x003CA },+	{ 0x003B9 , 0x00313 , 0x01F30 },+	{ 0x003B9 , 0x00314 , 0x01F31 },+	{ 0x003B9 , 0x00342 , 0x01FD6 },+	{ 0x003BF , 0x00300 , 0x01F78 },+	{ 0x003BF , 0x00301 , 0x003CC },+	{ 0x003BF , 0x00313 , 0x01F40 },+	{ 0x003BF , 0x00314 , 0x01F41 },+	{ 0x003C1 , 0x00313 , 0x01FE4 },+	{ 0x003C1 , 0x00314 , 0x01FE5 },+	{ 0x003C5 , 0x00300 , 0x01F7A },+	{ 0x003C5 , 0x00301 , 0x003CD },+	{ 0x003C5 , 0x00304 , 0x01FE1 },+	{ 0x003C5 , 0x00306 , 0x01FE0 },+	{ 0x003C5 , 0x00308 , 0x003CB },+	{ 0x003C5 , 0x00313 , 0x01F50 },+	{ 0x003C5 , 0x00314 , 0x01F51 },+	{ 0x003C5 , 0x00342 , 0x01FE6 },+	{ 0x003C9 , 0x00300 , 0x01F7C },+	{ 0x003C9 , 0x00301 , 0x003CE },+	{ 0x003C9 , 0x00313 , 0x01F60 },+	{ 0x003C9 , 0x00314 , 0x01F61 },+	{ 0x003C9 , 0x00342 , 0x01FF6 },+	{ 0x003C9 , 0x00345 , 0x01FF3 },+	{ 0x003CA , 0x00300 , 0x01FD2 },+	{ 0x003CA , 0x00301 , 0x00390 },+	{ 0x003CA , 0x00342 , 0x01FD7 },+	{ 0x003CB , 0x00300 , 0x01FE2 },+	{ 0x003CB , 0x00301 , 0x003B0 },+	{ 0x003CB , 0x00342 , 0x01FE7 },+	{ 0x003CE , 0x00345 , 0x01FF4 },+	{ 0x003D2 , 0x00301 , 0x003D3 },+	{ 0x003D2 , 0x00308 , 0x003D4 },+	{ 0x00406 , 0x00308 , 0x00407 },+	{ 0x00410 , 0x00306 , 0x004D0 },+	{ 0x00410 , 0x00308 , 0x004D2 },+	{ 0x00413 , 0x00301 , 0x00403 },+	{ 0x00415 , 0x00300 , 0x00400 },+	{ 0x00415 , 0x00306 , 0x004D6 },+	{ 0x00415 , 0x00308 , 0x00401 },+	{ 0x00416 , 0x00306 , 0x004C1 },+	{ 0x00416 , 0x00308 , 0x004DC },+	{ 0x00417 , 0x00308 , 0x004DE },+	{ 0x00418 , 0x00300 , 0x0040D },+	{ 0x00418 , 0x00304 , 0x004E2 },+	{ 0x00418 , 0x00306 , 0x00419 },+	{ 0x00418 , 0x00308 , 0x004E4 },+	{ 0x0041A , 0x00301 , 0x0040C },+	{ 0x0041E , 0x00308 , 0x004E6 },+	{ 0x00423 , 0x00304 , 0x004EE },+	{ 0x00423 , 0x00306 , 0x0040E },+	{ 0x00423 , 0x00308 , 0x004F0 },+	{ 0x00423 , 0x0030B , 0x004F2 },+	{ 0x00427 , 0x00308 , 0x004F4 },+	{ 0x0042B , 0x00308 , 0x004F8 },+	{ 0x0042D , 0x00308 , 0x004EC },+	{ 0x00430 , 0x00306 , 0x004D1 },+	{ 0x00430 , 0x00308 , 0x004D3 },+	{ 0x00433 , 0x00301 , 0x00453 },+	{ 0x00435 , 0x00300 , 0x00450 },+	{ 0x00435 , 0x00306 , 0x004D7 },+	{ 0x00435 , 0x00308 , 0x00451 },+	{ 0x00436 , 0x00306 , 0x004C2 },+	{ 0x00436 , 0x00308 , 0x004DD },+	{ 0x00437 , 0x00308 , 0x004DF },+	{ 0x00438 , 0x00300 , 0x0045D },+	{ 0x00438 , 0x00304 , 0x004E3 },+	{ 0x00438 , 0x00306 , 0x00439 },+	{ 0x00438 , 0x00308 , 0x004E5 },+	{ 0x0043A , 0x00301 , 0x0045C },+	{ 0x0043E , 0x00308 , 0x004E7 },+	{ 0x00443 , 0x00304 , 0x004EF },+	{ 0x00443 , 0x00306 , 0x0045E },+	{ 0x00443 , 0x00308 , 0x004F1 },+	{ 0x00443 , 0x0030B , 0x004F3 },+	{ 0x00447 , 0x00308 , 0x004F5 },+	{ 0x0044B , 0x00308 , 0x004F9 },+	{ 0x0044D , 0x00308 , 0x004ED },+	{ 0x00456 , 0x00308 , 0x00457 },+	{ 0x00474 , 0x0030F , 0x00476 },+	{ 0x00475 , 0x0030F , 0x00477 },+	{ 0x004D8 , 0x00308 , 0x004DA },+	{ 0x004D9 , 0x00308 , 0x004DB },+	{ 0x004E8 , 0x00308 , 0x004EA },+	{ 0x004E9 , 0x00308 , 0x004EB },+	{ 0x00627 , 0x00653 , 0x00622 },+	{ 0x00627 , 0x00654 , 0x00623 },+	{ 0x00627 , 0x00655 , 0x00625 },+	{ 0x00648 , 0x00654 , 0x00624 },+	{ 0x0064A , 0x00654 , 0x00626 },+	{ 0x006C1 , 0x00654 , 0x006C2 },+	{ 0x006D2 , 0x00654 , 0x006D3 },+	{ 0x006D5 , 0x00654 , 0x006C0 },+	{ 0x00928 , 0x0093C , 0x00929 },+	{ 0x00930 , 0x0093C , 0x00931 },+	{ 0x00933 , 0x0093C , 0x00934 },+	{ 0x009C7 , 0x009BE , 0x009CB },+	{ 0x009C7 , 0x009D7 , 0x009CC },+	{ 0x00B47 , 0x00B3E , 0x00B4B },+	{ 0x00B47 , 0x00B56 , 0x00B48 },+	{ 0x00B47 , 0x00B57 , 0x00B4C },+	{ 0x00B92 , 0x00BD7 , 0x00B94 },+	{ 0x00BC6 , 0x00BBE , 0x00BCA },+	{ 0x00BC6 , 0x00BD7 , 0x00BCC },+	{ 0x00BC7 , 0x00BBE , 0x00BCB },+	{ 0x00C46 , 0x00C56 , 0x00C48 },+	{ 0x00CBF , 0x00CD5 , 0x00CC0 },+	{ 0x00CC6 , 0x00CC2 , 0x00CCA },+	{ 0x00CC6 , 0x00CD5 , 0x00CC7 },+	{ 0x00CC6 , 0x00CD6 , 0x00CC8 },+	{ 0x00CCA , 0x00CD5 , 0x00CCB },+	{ 0x00D46 , 0x00D3E , 0x00D4A },+	{ 0x00D46 , 0x00D57 , 0x00D4C },+	{ 0x00D47 , 0x00D3E , 0x00D4B },+	{ 0x00DD9 , 0x00DCA , 0x00DDA },+	{ 0x00DD9 , 0x00DCF , 0x00DDC },+	{ 0x00DD9 , 0x00DDF , 0x00DDE },+	{ 0x00DDC , 0x00DCA , 0x00DDD },+	{ 0x01025 , 0x0102E , 0x01026 },+	{ 0x01B05 , 0x01B35 , 0x01B06 },+	{ 0x01B07 , 0x01B35 , 0x01B08 },+	{ 0x01B09 , 0x01B35 , 0x01B0A },+	{ 0x01B0B , 0x01B35 , 0x01B0C },+	{ 0x01B0D , 0x01B35 , 0x01B0E },+	{ 0x01B11 , 0x01B35 , 0x01B12 },+	{ 0x01B3A , 0x01B35 , 0x01B3B },+	{ 0x01B3C , 0x01B35 , 0x01B3D },+	{ 0x01B3E , 0x01B35 , 0x01B40 },+	{ 0x01B3F , 0x01B35 , 0x01B41 },+	{ 0x01B42 , 0x01B35 , 0x01B43 },+	{ 0x01E36 , 0x00304 , 0x01E38 },+	{ 0x01E37 , 0x00304 , 0x01E39 },+	{ 0x01E5A , 0x00304 , 0x01E5C },+	{ 0x01E5B , 0x00304 , 0x01E5D },+	{ 0x01E62 , 0x00307 , 0x01E68 },+	{ 0x01E63 , 0x00307 , 0x01E69 },+	{ 0x01EA0 , 0x00302 , 0x01EAC },+	{ 0x01EA0 , 0x00306 , 0x01EB6 },+	{ 0x01EA1 , 0x00302 , 0x01EAD },+	{ 0x01EA1 , 0x00306 , 0x01EB7 },+	{ 0x01EB8 , 0x00302 , 0x01EC6 },+	{ 0x01EB9 , 0x00302 , 0x01EC7 },+	{ 0x01ECC , 0x00302 , 0x01ED8 },+	{ 0x01ECD , 0x00302 , 0x01ED9 },+	{ 0x01F00 , 0x00300 , 0x01F02 },+	{ 0x01F00 , 0x00301 , 0x01F04 },+	{ 0x01F00 , 0x00342 , 0x01F06 },+	{ 0x01F00 , 0x00345 , 0x01F80 },+	{ 0x01F01 , 0x00300 , 0x01F03 },+	{ 0x01F01 , 0x00301 , 0x01F05 },+	{ 0x01F01 , 0x00342 , 0x01F07 },+	{ 0x01F01 , 0x00345 , 0x01F81 },+	{ 0x01F02 , 0x00345 , 0x01F82 },+	{ 0x01F03 , 0x00345 , 0x01F83 },+	{ 0x01F04 , 0x00345 , 0x01F84 },+	{ 0x01F05 , 0x00345 , 0x01F85 },+	{ 0x01F06 , 0x00345 , 0x01F86 },+	{ 0x01F07 , 0x00345 , 0x01F87 },+	{ 0x01F08 , 0x00300 , 0x01F0A },+	{ 0x01F08 , 0x00301 , 0x01F0C },+	{ 0x01F08 , 0x00342 , 0x01F0E },+	{ 0x01F08 , 0x00345 , 0x01F88 },+	{ 0x01F09 , 0x00300 , 0x01F0B },+	{ 0x01F09 , 0x00301 , 0x01F0D },+	{ 0x01F09 , 0x00342 , 0x01F0F },+	{ 0x01F09 , 0x00345 , 0x01F89 },+	{ 0x01F0A , 0x00345 , 0x01F8A },+	{ 0x01F0B , 0x00345 , 0x01F8B },+	{ 0x01F0C , 0x00345 , 0x01F8C },+	{ 0x01F0D , 0x00345 , 0x01F8D },+	{ 0x01F0E , 0x00345 , 0x01F8E },+	{ 0x01F0F , 0x00345 , 0x01F8F },+	{ 0x01F10 , 0x00300 , 0x01F12 },+	{ 0x01F10 , 0x00301 , 0x01F14 },+	{ 0x01F11 , 0x00300 , 0x01F13 },+	{ 0x01F11 , 0x00301 , 0x01F15 },+	{ 0x01F18 , 0x00300 , 0x01F1A },+	{ 0x01F18 , 0x00301 , 0x01F1C },+	{ 0x01F19 , 0x00300 , 0x01F1B },+	{ 0x01F19 , 0x00301 , 0x01F1D },+	{ 0x01F20 , 0x00300 , 0x01F22 },+	{ 0x01F20 , 0x00301 , 0x01F24 },+	{ 0x01F20 , 0x00342 , 0x01F26 },+	{ 0x01F20 , 0x00345 , 0x01F90 },+	{ 0x01F21 , 0x00300 , 0x01F23 },+	{ 0x01F21 , 0x00301 , 0x01F25 },+	{ 0x01F21 , 0x00342 , 0x01F27 },+	{ 0x01F21 , 0x00345 , 0x01F91 },+	{ 0x01F22 , 0x00345 , 0x01F92 },+	{ 0x01F23 , 0x00345 , 0x01F93 },+	{ 0x01F24 , 0x00345 , 0x01F94 },+	{ 0x01F25 , 0x00345 , 0x01F95 },+	{ 0x01F26 , 0x00345 , 0x01F96 },+	{ 0x01F27 , 0x00345 , 0x01F97 },+	{ 0x01F28 , 0x00300 , 0x01F2A },+	{ 0x01F28 , 0x00301 , 0x01F2C },+	{ 0x01F28 , 0x00342 , 0x01F2E },+	{ 0x01F28 , 0x00345 , 0x01F98 },+	{ 0x01F29 , 0x00300 , 0x01F2B },+	{ 0x01F29 , 0x00301 , 0x01F2D },+	{ 0x01F29 , 0x00342 , 0x01F2F },+	{ 0x01F29 , 0x00345 , 0x01F99 },+	{ 0x01F2A , 0x00345 , 0x01F9A },+	{ 0x01F2B , 0x00345 , 0x01F9B },+	{ 0x01F2C , 0x00345 , 0x01F9C },+	{ 0x01F2D , 0x00345 , 0x01F9D },+	{ 0x01F2E , 0x00345 , 0x01F9E },+	{ 0x01F2F , 0x00345 , 0x01F9F },+	{ 0x01F30 , 0x00300 , 0x01F32 },+	{ 0x01F30 , 0x00301 , 0x01F34 },+	{ 0x01F30 , 0x00342 , 0x01F36 },+	{ 0x01F31 , 0x00300 , 0x01F33 },+	{ 0x01F31 , 0x00301 , 0x01F35 },+	{ 0x01F31 , 0x00342 , 0x01F37 },+	{ 0x01F38 , 0x00300 , 0x01F3A },+	{ 0x01F38 , 0x00301 , 0x01F3C },+	{ 0x01F38 , 0x00342 , 0x01F3E },+	{ 0x01F39 , 0x00300 , 0x01F3B },+	{ 0x01F39 , 0x00301 , 0x01F3D },+	{ 0x01F39 , 0x00342 , 0x01F3F },+	{ 0x01F40 , 0x00300 , 0x01F42 },+	{ 0x01F40 , 0x00301 , 0x01F44 },+	{ 0x01F41 , 0x00300 , 0x01F43 },+	{ 0x01F41 , 0x00301 , 0x01F45 },+	{ 0x01F48 , 0x00300 , 0x01F4A },+	{ 0x01F48 , 0x00301 , 0x01F4C },+	{ 0x01F49 , 0x00300 , 0x01F4B },+	{ 0x01F49 , 0x00301 , 0x01F4D },+	{ 0x01F50 , 0x00300 , 0x01F52 },+	{ 0x01F50 , 0x00301 , 0x01F54 },+	{ 0x01F50 , 0x00342 , 0x01F56 },+	{ 0x01F51 , 0x00300 , 0x01F53 },+	{ 0x01F51 , 0x00301 , 0x01F55 },+	{ 0x01F51 , 0x00342 , 0x01F57 },+	{ 0x01F59 , 0x00300 , 0x01F5B },+	{ 0x01F59 , 0x00301 , 0x01F5D },+	{ 0x01F59 , 0x00342 , 0x01F5F },+	{ 0x01F60 , 0x00300 , 0x01F62 },+	{ 0x01F60 , 0x00301 , 0x01F64 },+	{ 0x01F60 , 0x00342 , 0x01F66 },+	{ 0x01F60 , 0x00345 , 0x01FA0 },+	{ 0x01F61 , 0x00300 , 0x01F63 },+	{ 0x01F61 , 0x00301 , 0x01F65 },+	{ 0x01F61 , 0x00342 , 0x01F67 },+	{ 0x01F61 , 0x00345 , 0x01FA1 },+	{ 0x01F62 , 0x00345 , 0x01FA2 },+	{ 0x01F63 , 0x00345 , 0x01FA3 },+	{ 0x01F64 , 0x00345 , 0x01FA4 },+	{ 0x01F65 , 0x00345 , 0x01FA5 },+	{ 0x01F66 , 0x00345 , 0x01FA6 },+	{ 0x01F67 , 0x00345 , 0x01FA7 },+	{ 0x01F68 , 0x00300 , 0x01F6A },+	{ 0x01F68 , 0x00301 , 0x01F6C },+	{ 0x01F68 , 0x00342 , 0x01F6E },+	{ 0x01F68 , 0x00345 , 0x01FA8 },+	{ 0x01F69 , 0x00300 , 0x01F6B },+	{ 0x01F69 , 0x00301 , 0x01F6D },+	{ 0x01F69 , 0x00342 , 0x01F6F },+	{ 0x01F69 , 0x00345 , 0x01FA9 },+	{ 0x01F6A , 0x00345 , 0x01FAA },+	{ 0x01F6B , 0x00345 , 0x01FAB },+	{ 0x01F6C , 0x00345 , 0x01FAC },+	{ 0x01F6D , 0x00345 , 0x01FAD },+	{ 0x01F6E , 0x00345 , 0x01FAE },+	{ 0x01F6F , 0x00345 , 0x01FAF },+	{ 0x01F70 , 0x00345 , 0x01FB2 },+	{ 0x01F74 , 0x00345 , 0x01FC2 },+	{ 0x01F7C , 0x00345 , 0x01FF2 },+	{ 0x01FB6 , 0x00345 , 0x01FB7 },+	{ 0x01FBF , 0x00300 , 0x01FCD },+	{ 0x01FBF , 0x00301 , 0x01FCE },+	{ 0x01FBF , 0x00342 , 0x01FCF },+	{ 0x01FC6 , 0x00345 , 0x01FC7 },+	{ 0x01FF6 , 0x00345 , 0x01FF7 },+	{ 0x01FFE , 0x00300 , 0x01FDD },+	{ 0x01FFE , 0x00301 , 0x01FDE },+	{ 0x01FFE , 0x00342 , 0x01FDF },+	{ 0x02190 , 0x00338 , 0x0219A },+	{ 0x02192 , 0x00338 , 0x0219B },+	{ 0x02194 , 0x00338 , 0x021AE },+	{ 0x021D0 , 0x00338 , 0x021CD },+	{ 0x021D2 , 0x00338 , 0x021CF },+	{ 0x021D4 , 0x00338 , 0x021CE },+	{ 0x02203 , 0x00338 , 0x02204 },+	{ 0x02208 , 0x00338 , 0x02209 },+	{ 0x0220B , 0x00338 , 0x0220C },+	{ 0x02223 , 0x00338 , 0x02224 },+	{ 0x02225 , 0x00338 , 0x02226 },+	{ 0x0223C , 0x00338 , 0x02241 },+	{ 0x02243 , 0x00338 , 0x02244 },+	{ 0x02245 , 0x00338 , 0x02247 },+	{ 0x02248 , 0x00338 , 0x02249 },+	{ 0x0224D , 0x00338 , 0x0226D },+	{ 0x02261 , 0x00338 , 0x02262 },+	{ 0x02264 , 0x00338 , 0x02270 },+	{ 0x02265 , 0x00338 , 0x02271 },+	{ 0x02272 , 0x00338 , 0x02274 },+	{ 0x02273 , 0x00338 , 0x02275 },+	{ 0x02276 , 0x00338 , 0x02278 },+	{ 0x02277 , 0x00338 , 0x02279 },+	{ 0x0227A , 0x00338 , 0x02280 },+	{ 0x0227B , 0x00338 , 0x02281 },+	{ 0x0227C , 0x00338 , 0x022E0 },+	{ 0x0227D , 0x00338 , 0x022E1 },+	{ 0x02282 , 0x00338 , 0x02284 },+	{ 0x02283 , 0x00338 , 0x02285 },+	{ 0x02286 , 0x00338 , 0x02288 },+	{ 0x02287 , 0x00338 , 0x02289 },+	{ 0x02291 , 0x00338 , 0x022E2 },+	{ 0x02292 , 0x00338 , 0x022E3 },+	{ 0x022A2 , 0x00338 , 0x022AC },+	{ 0x022A8 , 0x00338 , 0x022AD },+	{ 0x022A9 , 0x00338 , 0x022AE },+	{ 0x022AB , 0x00338 , 0x022AF },+	{ 0x022B2 , 0x00338 , 0x022EA },+	{ 0x022B3 , 0x00338 , 0x022EB },+	{ 0x022B4 , 0x00338 , 0x022EC },+	{ 0x022B5 , 0x00338 , 0x022ED },+	{ 0x03046 , 0x03099 , 0x03094 },+	{ 0x0304B , 0x03099 , 0x0304C },+	{ 0x0304D , 0x03099 , 0x0304E },+	{ 0x0304F , 0x03099 , 0x03050 },+	{ 0x03051 , 0x03099 , 0x03052 },+	{ 0x03053 , 0x03099 , 0x03054 },+	{ 0x03055 , 0x03099 , 0x03056 },+	{ 0x03057 , 0x03099 , 0x03058 },+	{ 0x03059 , 0x03099 , 0x0305A },+	{ 0x0305B , 0x03099 , 0x0305C },+	{ 0x0305D , 0x03099 , 0x0305E },+	{ 0x0305F , 0x03099 , 0x03060 },+	{ 0x03061 , 0x03099 , 0x03062 },+	{ 0x03064 , 0x03099 , 0x03065 },+	{ 0x03066 , 0x03099 , 0x03067 },+	{ 0x03068 , 0x03099 , 0x03069 },+	{ 0x0306F , 0x03099 , 0x03070 },+	{ 0x0306F , 0x0309A , 0x03071 },+	{ 0x03072 , 0x03099 , 0x03073 },+	{ 0x03072 , 0x0309A , 0x03074 },+	{ 0x03075 , 0x03099 , 0x03076 },+	{ 0x03075 , 0x0309A , 0x03077 },+	{ 0x03078 , 0x03099 , 0x03079 },+	{ 0x03078 , 0x0309A , 0x0307A },+	{ 0x0307B , 0x03099 , 0x0307C },+	{ 0x0307B , 0x0309A , 0x0307D },+	{ 0x0309D , 0x03099 , 0x0309E },+	{ 0x030A6 , 0x03099 , 0x030F4 },+	{ 0x030AB , 0x03099 , 0x030AC },+	{ 0x030AD , 0x03099 , 0x030AE },+	{ 0x030AF , 0x03099 , 0x030B0 },+	{ 0x030B1 , 0x03099 , 0x030B2 },+	{ 0x030B3 , 0x03099 , 0x030B4 },+	{ 0x030B5 , 0x03099 , 0x030B6 },+	{ 0x030B7 , 0x03099 , 0x030B8 },+	{ 0x030B9 , 0x03099 , 0x030BA },+	{ 0x030BB , 0x03099 , 0x030BC },+	{ 0x030BD , 0x03099 , 0x030BE },+	{ 0x030BF , 0x03099 , 0x030C0 },+	{ 0x030C1 , 0x03099 , 0x030C2 },+	{ 0x030C4 , 0x03099 , 0x030C5 },+	{ 0x030C6 , 0x03099 , 0x030C7 },+	{ 0x030C8 , 0x03099 , 0x030C9 },+	{ 0x030CF , 0x03099 , 0x030D0 },+	{ 0x030CF , 0x0309A , 0x030D1 },+	{ 0x030D2 , 0x03099 , 0x030D3 },+	{ 0x030D2 , 0x0309A , 0x030D4 },+	{ 0x030D5 , 0x03099 , 0x030D6 },+	{ 0x030D5 , 0x0309A , 0x030D7 },+	{ 0x030D8 , 0x03099 , 0x030D9 },+	{ 0x030D8 , 0x0309A , 0x030DA },+	{ 0x030DB , 0x03099 , 0x030DC },+	{ 0x030DB , 0x0309A , 0x030DD },+	{ 0x030EF , 0x03099 , 0x030F7 },+	{ 0x030F0 , 0x03099 , 0x030F8 },+	{ 0x030F1 , 0x03099 , 0x030F9 },+	{ 0x030F2 , 0x03099 , 0x030FA },+	{ 0x030FD , 0x03099 , 0x030FE },+	{ 0x11099 , 0x110BA , 0x1109A },+	{ 0x1109B , 0x110BA , 0x1109C },+	{ 0x110A5 , 0x110BA , 0x110AB },+};++#define CANONICAL_CLASS_MIN	0x0300+#define CANONICAL_CLASS_MAX	0x1D244++#define IS_DECOMPOSABLE_BLOCK(uc)	\+	(((uc)>>8) <= 0x1D2 && u_decomposable_blocks[(uc)>>8])+static const char u_decomposable_blocks[0x1D2+1] = {+	0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,1,1,1,1,0,0,+	1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,+	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,+	0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,+};++/* Get Canonical Combining Class(CCC). */+#define CCC(uc)	\+	(((uc) > 0x1D244)?0:\+	ccc_val[ccc_val_index[ccc_index[(uc)>>8]][((uc)>>4)&0x0F]][(uc)&0x0F])++/* The table of the value of Canonical Combining Class */+static const unsigned char ccc_val[][16] = {+ /* idx=0: XXXX0 - XXXXF */+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=1: 00300 - 0030F */+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },+ /* idx=2: 00310 - 0031F */+ {230, 230, 230, 230, 230, 232, 220, 220, 220, 220, 232, 216, 220, 220, 220, 220 },+ /* idx=3: 00320 - 0032F */+ {220, 202, 202, 220, 220, 220, 220, 202, 202, 220, 220, 220, 220, 220, 220, 220 },+ /* idx=4: 00330 - 0033F */+ {220, 220, 220, 220, 1, 1, 1, 1, 1, 220, 220, 220, 220, 230, 230, 230 },+ /* idx=5: 00340 - 0034F */+ {230, 230, 230, 230, 230, 240, 230, 220, 220, 220, 230, 230, 230, 220, 220, 0 },+ /* idx=6: 00350 - 0035F */+ {230, 230, 230, 220, 220, 220, 220, 230, 232, 220, 220, 230, 233, 234, 234, 233 },+ /* idx=7: 00360 - 0036F */+ {234, 234, 233, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },+ /* idx=8: 00480 - 0048F */+ {0, 0, 0, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=9: 00590 - 0059F */+ {0, 220, 230, 230, 230, 230, 220, 230, 230, 230, 222, 220, 230, 230, 230, 230 },+ /* idx=10: 005A0 - 005AF */+ {230, 230, 220, 220, 220, 220, 220, 220, 230, 230, 220, 230, 230, 222, 228, 230 },+ /* idx=11: 005B0 - 005BF */+ {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23 },+ /* idx=12: 005C0 - 005CF */+ {0, 24, 25, 0, 230, 220, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=13: 00610 - 0061F */+ {230, 230, 230, 230, 230, 230, 230, 230, 30, 31, 32, 0, 0, 0, 0, 0 },+ /* idx=14: 00640 - 0064F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31 },+ /* idx=15: 00650 - 0065F */+ {32, 33, 34, 230, 230, 220, 220, 230, 230, 230, 230, 230, 220, 230, 230, 220 },+ /* idx=16: 00670 - 0067F */+ {35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=17: 006D0 - 006DF */+ {0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 230, 230, 230, 0, 0, 230 },+ /* idx=18: 006E0 - 006EF */+ {230, 230, 230, 220, 230, 0, 0, 230, 230, 0, 220, 230, 230, 220, 0, 0 },+ /* idx=19: 00710 - 0071F */+ {0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=20: 00730 - 0073F */+ {230, 220, 230, 230, 220, 230, 230, 220, 220, 220, 230, 220, 220, 230, 220, 230 },+ /* idx=21: 00740 - 0074F */+ {230, 230, 220, 230, 220, 230, 220, 230, 220, 230, 230, 0, 0, 0, 0, 0 },+ /* idx=22: 007E0 - 007EF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 230 },+ /* idx=23: 007F0 - 007FF */+ {230, 230, 220, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=24: 00810 - 0081F */+ {0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 230, 230, 230, 230, 230 },+ /* idx=25: 00820 - 0082F */+ {230, 230, 230, 230, 0, 230, 230, 230, 0, 230, 230, 230, 230, 230, 0, 0 },+ /* idx=26: 00850 - 0085F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 0, 0, 0, 0 },+ /* idx=27: 00930 - 0093F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },+ /* idx=28: 00940 - 0094F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },+ /* idx=29: 00950 - 0095F */+ {0, 230, 220, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=30: 009B0 - 009BF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },+ /* idx=31: 009C0 - 009CF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },+ /* idx=32: 00A30 - 00A3F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },+ /* idx=33: 00A40 - 00A4F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },+ /* idx=34: 00AB0 - 00ABF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },+ /* idx=35: 00AC0 - 00ACF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },+ /* idx=36: 00B30 - 00B3F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },+ /* idx=37: 00B40 - 00B4F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },+ /* idx=38: 00BC0 - 00BCF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },+ /* idx=39: 00C40 - 00C4F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },+ /* idx=40: 00C50 - 00C5F */+ {0, 0, 0, 0, 0, 84, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=41: 00CB0 - 00CBF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },+ /* idx=42: 00CC0 - 00CCF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },+ /* idx=43: 00D40 - 00D4F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },+ /* idx=44: 00DC0 - 00DCF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0 },+ /* idx=45: 00E30 - 00E3F */+ {0, 0, 0, 0, 0, 0, 0, 0, 103, 103, 9, 0, 0, 0, 0, 0 },+ /* idx=46: 00E40 - 00E4F */+ {0, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, 107, 0, 0, 0, 0 },+ /* idx=47: 00EB0 - 00EBF */+ {0, 0, 0, 0, 0, 0, 0, 0, 118, 118, 0, 0, 0, 0, 0, 0 },+ /* idx=48: 00EC0 - 00ECF */+ {0, 0, 0, 0, 0, 0, 0, 0, 122, 122, 122, 122, 0, 0, 0, 0 },+ /* idx=49: 00F10 - 00F1F */+ {0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 0, 0, 0, 0, 0, 0 },+ /* idx=50: 00F30 - 00F3F */+ {0, 0, 0, 0, 0, 220, 0, 220, 0, 216, 0, 0, 0, 0, 0, 0 },+ /* idx=51: 00F70 - 00F7F */+ {0, 129, 130, 0, 132, 0, 0, 0, 0, 0, 130, 130, 130, 130, 0, 0 },+ /* idx=52: 00F80 - 00F8F */+ {130, 0, 230, 230, 9, 0, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=53: 00FC0 - 00FCF */+ {0, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=54: 01030 - 0103F */+ {0, 0, 0, 0, 0, 0, 0, 7, 0, 9, 9, 0, 0, 0, 0, 0 },+ /* idx=55: 01080 - 0108F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0 },+ /* idx=56: 01350 - 0135F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230 },+ /* idx=57: 01710 - 0171F */+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=58: 01730 - 0173F */+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=59: 017D0 - 017DF */+ {0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0, 0 },+ /* idx=60: 018A0 - 018AF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0 },+ /* idx=61: 01930 - 0193F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 230, 220, 0, 0, 0, 0 },+ /* idx=62: 01A10 - 01A1F */+ {0, 0, 0, 0, 0, 0, 0, 230, 220, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=63: 01A60 - 01A6F */+ {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=64: 01A70 - 01A7F */+ {0, 0, 0, 0, 0, 230, 230, 230, 230, 230, 230, 230, 230, 0, 0, 220 },+ /* idx=65: 01B30 - 01B3F */+ {0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=66: 01B40 - 01B4F */+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=67: 01B60 - 01B6F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 220, 230, 230, 230 },+ /* idx=68: 01B70 - 01B7F */+ {230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=69: 01BA0 - 01BAF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0 },+ /* idx=70: 01BE0 - 01BEF */+ {0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=71: 01BF0 - 01BFF */+ {0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=72: 01C30 - 01C3F */+ {0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=73: 01CD0 - 01CDF */+ {230, 230, 230, 0, 1, 220, 220, 220, 220, 220, 230, 230, 220, 220, 220, 220 },+ /* idx=74: 01CE0 - 01CEF */+ {230, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 220, 0, 0 },+ /* idx=75: 01DC0 - 01DCF */+ {230, 230, 220, 230, 230, 230, 230, 230, 230, 230, 220, 230, 230, 234, 214, 220 },+ /* idx=76: 01DD0 - 01DDF */+ {202, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },+ /* idx=77: 01DE0 - 01DEF */+ {230, 230, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=78: 01DF0 - 01DFF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 220, 230, 220 },+ /* idx=79: 020D0 - 020DF */+ {230, 230, 1, 1, 230, 230, 230, 230, 1, 1, 1, 230, 230, 0, 0, 0 },+ /* idx=80: 020E0 - 020EF */+ {0, 230, 0, 0, 0, 1, 1, 230, 220, 230, 1, 1, 220, 220, 220, 220 },+ /* idx=81: 020F0 - 020FF */+ {230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=82: 02CE0 - 02CEF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230 },+ /* idx=83: 02CF0 - 02CFF */+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=84: 02D70 - 02D7F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 },+ /* idx=85: 02DE0 - 02DEF */+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },+ /* idx=86: 02DF0 - 02DFF */+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },+ /* idx=87: 03020 - 0302F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 228, 232, 222, 224, 224 },+ /* idx=88: 03090 - 0309F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0 },+ /* idx=89: 0A660 - 0A66F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230 },+ /* idx=90: 0A670 - 0A67F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 0, 0 },+ /* idx=91: 0A6F0 - 0A6FF */+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=92: 0A800 - 0A80F */+ {0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=93: 0A8C0 - 0A8CF */+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=94: 0A8E0 - 0A8EF */+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },+ /* idx=95: 0A8F0 - 0A8FF */+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=96: 0A920 - 0A92F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 0, 0 },+ /* idx=97: 0A950 - 0A95F */+ {0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=98: 0A9B0 - 0A9BF */+ {0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=99: 0A9C0 - 0A9CF */+ {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=100: 0AAB0 - 0AABF */+ {230, 0, 230, 230, 220, 0, 0, 230, 230, 0, 0, 0, 0, 0, 230, 230 },+ /* idx=101: 0AAC0 - 0AACF */+ {0, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=102: 0ABE0 - 0ABEF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },+ /* idx=103: 0FB10 - 0FB1F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0 },+ /* idx=104: 0FE20 - 0FE2F */+ {230, 230, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=105: 101F0 - 101FF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0 },+ /* idx=106: 10A00 - 10A0F */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 230 },+ /* idx=107: 10A30 - 10A3F */+ {0, 0, 0, 0, 0, 0, 0, 0, 230, 1, 220, 0, 0, 0, 0, 9 },+ /* idx=108: 11040 - 1104F */+ {0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=109: 110B0 - 110BF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 7, 0, 0, 0, 0, 0 },+ /* idx=110: 1D160 - 1D16F */+ {0, 0, 0, 0, 0, 216, 216, 1, 1, 1, 0, 0, 0, 226, 216, 216 },+ /* idx=111: 1D170 - 1D17F */+ {216, 216, 216, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 220, 220 },+ /* idx=112: 1D180 - 1D18F */+ {220, 220, 220, 0, 0, 230, 230, 230, 230, 230, 220, 220, 0, 0, 0, 0 },+ /* idx=113: 1D1A0 - 1D1AF */+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 0 },+ /* idx=114: 1D240 - 1D24F */+ {0, 0, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+};++/* The index table to ccc_val[*][16] */+static const unsigned char ccc_val_index[][16] = {+ /* idx=0: XXX00 - XXXFF */+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=1: 00300 - 003FF */+ { 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=2: 00400 - 004FF */+ { 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=3: 00500 - 005FF */+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,10,11,12, 0, 0, 0 },+ /* idx=4: 00600 - 006FF */+ { 0,13, 0, 0,14,15, 0,16, 0, 0, 0, 0, 0,17,18, 0 },+ /* idx=5: 00700 - 007FF */+ { 0,19, 0,20,21, 0, 0, 0, 0, 0, 0, 0, 0, 0,22,23 },+ /* idx=6: 00800 - 008FF */+ { 0,24,25, 0, 0,26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=7: 00900 - 009FF */+ { 0, 0, 0,27,28,29, 0, 0, 0, 0, 0,30,31, 0, 0, 0 },+ /* idx=8: 00A00 - 00AFF */+ { 0, 0, 0,32,33, 0, 0, 0, 0, 0, 0,34,35, 0, 0, 0 },+ /* idx=9: 00B00 - 00BFF */+ { 0, 0, 0,36,37, 0, 0, 0, 0, 0, 0, 0,38, 0, 0, 0 },+ /* idx=10: 00C00 - 00CFF */+ { 0, 0, 0, 0,39,40, 0, 0, 0, 0, 0,41,42, 0, 0, 0 },+ /* idx=11: 00D00 - 00DFF */+ { 0, 0, 0, 0,43, 0, 0, 0, 0, 0, 0, 0,44, 0, 0, 0 },+ /* idx=12: 00E00 - 00EFF */+ { 0, 0, 0,45,46, 0, 0, 0, 0, 0, 0,47,48, 0, 0, 0 },+ /* idx=13: 00F00 - 00FFF */+ { 0,49, 0,50, 0, 0, 0,51,52, 0, 0, 0,53, 0, 0, 0 },+ /* idx=14: 01000 - 010FF */+ { 0, 0, 0,54, 0, 0, 0, 0,55, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=15: 01300 - 013FF */+ { 0, 0, 0, 0, 0,56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=16: 01700 - 017FF */+ { 0,57, 0,58, 0, 0, 0, 0, 0, 0, 0, 0, 0,59, 0, 0 },+ /* idx=17: 01800 - 018FF */+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,60, 0, 0, 0, 0, 0 },+ /* idx=18: 01900 - 019FF */+ { 0, 0, 0,61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=19: 01A00 - 01AFF */+ { 0,62, 0, 0, 0, 0,63,64, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=20: 01B00 - 01BFF */+ { 0, 0, 0,65,66, 0,67,68, 0, 0,69, 0, 0, 0,70,71 },+ /* idx=21: 01C00 - 01CFF */+ { 0, 0, 0,72, 0, 0, 0, 0, 0, 0, 0, 0, 0,73,74, 0 },+ /* idx=22: 01D00 - 01DFF */+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,75,76,77,78 },+ /* idx=23: 02000 - 020FF */+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,79,80,81 },+ /* idx=24: 02C00 - 02CFF */+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,82,83 },+ /* idx=25: 02D00 - 02DFF */+ { 0, 0, 0, 0, 0, 0, 0,84, 0, 0, 0, 0, 0, 0,85,86 },+ /* idx=26: 03000 - 030FF */+ { 0, 0,87, 0, 0, 0, 0, 0, 0,88, 0, 0, 0, 0, 0, 0 },+ /* idx=27: 0A600 - 0A6FF */+ { 0, 0, 0, 0, 0, 0,89,90, 0, 0, 0, 0, 0, 0, 0,91 },+ /* idx=28: 0A800 - 0A8FF */+ {92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,93, 0,94,95 },+ /* idx=29: 0A900 - 0A9FF */+ { 0, 0,96, 0, 0,97, 0, 0, 0, 0, 0,98,99, 0, 0, 0 },+ /* idx=30: 0AA00 - 0AAFF */+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,100,101, 0, 0, 0 },+ /* idx=31: 0AB00 - 0ABFF */+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,102, 0 },+ /* idx=32: 0FB00 - 0FBFF */+ { 0,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=33: 0FE00 - 0FEFF */+ { 0, 0,104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=34: 10100 - 101FF */+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105 },+ /* idx=35: 10A00 - 10AFF */+ {106, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+ /* idx=36: 11000 - 110FF */+ { 0, 0, 0, 0,108, 0, 0, 0, 0, 0, 0,109, 0, 0, 0, 0 },+ /* idx=37: 1D100 - 1D1FF */+ { 0, 0, 0, 0, 0, 0,110,111,112, 0,113, 0, 0, 0, 0, 0 },+ /* idx=38: 1D200 - 1D2FF */+ { 0, 0, 0, 0,114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },+};++/* The index table to ccc_val_index[*][16] */+static const unsigned char ccc_index[] = {+  0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 0, 0,15, 0, 0, 0,16,+ 17,18,19,20,21,22, 0, 0,23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,24,25, 0, 0,+ 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,27, 0,+ 28,29,30,31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32, 0, 0,33, 0, 0,34, 0, 0, 0, 0, 0, 0,+  0, 0,35, 0, 0, 0, 0, 0,36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+  0, 0, 0, 0, 0, 0, 0, 0, 0,37,38,};++struct unicode_decomposition_table {+	uint32_t nfc;+	uint32_t cp1;+	uint32_t cp2;+};++static const struct unicode_decomposition_table u_decomposition_table[] = {+	{ 0x000C0 , 0x00041 , 0x00300 },+	{ 0x000C1 , 0x00041 , 0x00301 },+	{ 0x000C2 , 0x00041 , 0x00302 },+	{ 0x000C3 , 0x00041 , 0x00303 },+	{ 0x000C4 , 0x00041 , 0x00308 },+	{ 0x000C5 , 0x00041 , 0x0030A },+	{ 0x000C7 , 0x00043 , 0x00327 },+	{ 0x000C8 , 0x00045 , 0x00300 },+	{ 0x000C9 , 0x00045 , 0x00301 },+	{ 0x000CA , 0x00045 , 0x00302 },+	{ 0x000CB , 0x00045 , 0x00308 },+	{ 0x000CC , 0x00049 , 0x00300 },+	{ 0x000CD , 0x00049 , 0x00301 },+	{ 0x000CE , 0x00049 , 0x00302 },+	{ 0x000CF , 0x00049 , 0x00308 },+	{ 0x000D1 , 0x0004E , 0x00303 },+	{ 0x000D2 , 0x0004F , 0x00300 },+	{ 0x000D3 , 0x0004F , 0x00301 },+	{ 0x000D4 , 0x0004F , 0x00302 },+	{ 0x000D5 , 0x0004F , 0x00303 },+	{ 0x000D6 , 0x0004F , 0x00308 },+	{ 0x000D9 , 0x00055 , 0x00300 },+	{ 0x000DA , 0x00055 , 0x00301 },+	{ 0x000DB , 0x00055 , 0x00302 },+	{ 0x000DC , 0x00055 , 0x00308 },+	{ 0x000DD , 0x00059 , 0x00301 },+	{ 0x000E0 , 0x00061 , 0x00300 },+	{ 0x000E1 , 0x00061 , 0x00301 },+	{ 0x000E2 , 0x00061 , 0x00302 },+	{ 0x000E3 , 0x00061 , 0x00303 },+	{ 0x000E4 , 0x00061 , 0x00308 },+	{ 0x000E5 , 0x00061 , 0x0030A },+	{ 0x000E7 , 0x00063 , 0x00327 },+	{ 0x000E8 , 0x00065 , 0x00300 },+	{ 0x000E9 , 0x00065 , 0x00301 },+	{ 0x000EA , 0x00065 , 0x00302 },+	{ 0x000EB , 0x00065 , 0x00308 },+	{ 0x000EC , 0x00069 , 0x00300 },+	{ 0x000ED , 0x00069 , 0x00301 },+	{ 0x000EE , 0x00069 , 0x00302 },+	{ 0x000EF , 0x00069 , 0x00308 },+	{ 0x000F1 , 0x0006E , 0x00303 },+	{ 0x000F2 , 0x0006F , 0x00300 },+	{ 0x000F3 , 0x0006F , 0x00301 },+	{ 0x000F4 , 0x0006F , 0x00302 },+	{ 0x000F5 , 0x0006F , 0x00303 },+	{ 0x000F6 , 0x0006F , 0x00308 },+	{ 0x000F9 , 0x00075 , 0x00300 },+	{ 0x000FA , 0x00075 , 0x00301 },+	{ 0x000FB , 0x00075 , 0x00302 },+	{ 0x000FC , 0x00075 , 0x00308 },+	{ 0x000FD , 0x00079 , 0x00301 },+	{ 0x000FF , 0x00079 , 0x00308 },+	{ 0x00100 , 0x00041 , 0x00304 },+	{ 0x00101 , 0x00061 , 0x00304 },+	{ 0x00102 , 0x00041 , 0x00306 },+	{ 0x00103 , 0x00061 , 0x00306 },+	{ 0x00104 , 0x00041 , 0x00328 },+	{ 0x00105 , 0x00061 , 0x00328 },+	{ 0x00106 , 0x00043 , 0x00301 },+	{ 0x00107 , 0x00063 , 0x00301 },+	{ 0x00108 , 0x00043 , 0x00302 },+	{ 0x00109 , 0x00063 , 0x00302 },+	{ 0x0010A , 0x00043 , 0x00307 },+	{ 0x0010B , 0x00063 , 0x00307 },+	{ 0x0010C , 0x00043 , 0x0030C },+	{ 0x0010D , 0x00063 , 0x0030C },+	{ 0x0010E , 0x00044 , 0x0030C },+	{ 0x0010F , 0x00064 , 0x0030C },+	{ 0x00112 , 0x00045 , 0x00304 },+	{ 0x00113 , 0x00065 , 0x00304 },+	{ 0x00114 , 0x00045 , 0x00306 },+	{ 0x00115 , 0x00065 , 0x00306 },+	{ 0x00116 , 0x00045 , 0x00307 },+	{ 0x00117 , 0x00065 , 0x00307 },+	{ 0x00118 , 0x00045 , 0x00328 },+	{ 0x00119 , 0x00065 , 0x00328 },+	{ 0x0011A , 0x00045 , 0x0030C },+	{ 0x0011B , 0x00065 , 0x0030C },+	{ 0x0011C , 0x00047 , 0x00302 },+	{ 0x0011D , 0x00067 , 0x00302 },+	{ 0x0011E , 0x00047 , 0x00306 },+	{ 0x0011F , 0x00067 , 0x00306 },+	{ 0x00120 , 0x00047 , 0x00307 },+	{ 0x00121 , 0x00067 , 0x00307 },+	{ 0x00122 , 0x00047 , 0x00327 },+	{ 0x00123 , 0x00067 , 0x00327 },+	{ 0x00124 , 0x00048 , 0x00302 },+	{ 0x00125 , 0x00068 , 0x00302 },+	{ 0x00128 , 0x00049 , 0x00303 },+	{ 0x00129 , 0x00069 , 0x00303 },+	{ 0x0012A , 0x00049 , 0x00304 },+	{ 0x0012B , 0x00069 , 0x00304 },+	{ 0x0012C , 0x00049 , 0x00306 },+	{ 0x0012D , 0x00069 , 0x00306 },+	{ 0x0012E , 0x00049 , 0x00328 },+	{ 0x0012F , 0x00069 , 0x00328 },+	{ 0x00130 , 0x00049 , 0x00307 },+	{ 0x00134 , 0x0004A , 0x00302 },+	{ 0x00135 , 0x0006A , 0x00302 },+	{ 0x00136 , 0x0004B , 0x00327 },+	{ 0x00137 , 0x0006B , 0x00327 },+	{ 0x00139 , 0x0004C , 0x00301 },+	{ 0x0013A , 0x0006C , 0x00301 },+	{ 0x0013B , 0x0004C , 0x00327 },+	{ 0x0013C , 0x0006C , 0x00327 },+	{ 0x0013D , 0x0004C , 0x0030C },+	{ 0x0013E , 0x0006C , 0x0030C },+	{ 0x00143 , 0x0004E , 0x00301 },+	{ 0x00144 , 0x0006E , 0x00301 },+	{ 0x00145 , 0x0004E , 0x00327 },+	{ 0x00146 , 0x0006E , 0x00327 },+	{ 0x00147 , 0x0004E , 0x0030C },+	{ 0x00148 , 0x0006E , 0x0030C },+	{ 0x0014C , 0x0004F , 0x00304 },+	{ 0x0014D , 0x0006F , 0x00304 },+	{ 0x0014E , 0x0004F , 0x00306 },+	{ 0x0014F , 0x0006F , 0x00306 },+	{ 0x00150 , 0x0004F , 0x0030B },+	{ 0x00151 , 0x0006F , 0x0030B },+	{ 0x00154 , 0x00052 , 0x00301 },+	{ 0x00155 , 0x00072 , 0x00301 },+	{ 0x00156 , 0x00052 , 0x00327 },+	{ 0x00157 , 0x00072 , 0x00327 },+	{ 0x00158 , 0x00052 , 0x0030C },+	{ 0x00159 , 0x00072 , 0x0030C },+	{ 0x0015A , 0x00053 , 0x00301 },+	{ 0x0015B , 0x00073 , 0x00301 },+	{ 0x0015C , 0x00053 , 0x00302 },+	{ 0x0015D , 0x00073 , 0x00302 },+	{ 0x0015E , 0x00053 , 0x00327 },+	{ 0x0015F , 0x00073 , 0x00327 },+	{ 0x00160 , 0x00053 , 0x0030C },+	{ 0x00161 , 0x00073 , 0x0030C },+	{ 0x00162 , 0x00054 , 0x00327 },+	{ 0x00163 , 0x00074 , 0x00327 },+	{ 0x00164 , 0x00054 , 0x0030C },+	{ 0x00165 , 0x00074 , 0x0030C },+	{ 0x00168 , 0x00055 , 0x00303 },+	{ 0x00169 , 0x00075 , 0x00303 },+	{ 0x0016A , 0x00055 , 0x00304 },+	{ 0x0016B , 0x00075 , 0x00304 },+	{ 0x0016C , 0x00055 , 0x00306 },+	{ 0x0016D , 0x00075 , 0x00306 },+	{ 0x0016E , 0x00055 , 0x0030A },+	{ 0x0016F , 0x00075 , 0x0030A },+	{ 0x00170 , 0x00055 , 0x0030B },+	{ 0x00171 , 0x00075 , 0x0030B },+	{ 0x00172 , 0x00055 , 0x00328 },+	{ 0x00173 , 0x00075 , 0x00328 },+	{ 0x00174 , 0x00057 , 0x00302 },+	{ 0x00175 , 0x00077 , 0x00302 },+	{ 0x00176 , 0x00059 , 0x00302 },+	{ 0x00177 , 0x00079 , 0x00302 },+	{ 0x00178 , 0x00059 , 0x00308 },+	{ 0x00179 , 0x0005A , 0x00301 },+	{ 0x0017A , 0x0007A , 0x00301 },+	{ 0x0017B , 0x0005A , 0x00307 },+	{ 0x0017C , 0x0007A , 0x00307 },+	{ 0x0017D , 0x0005A , 0x0030C },+	{ 0x0017E , 0x0007A , 0x0030C },+	{ 0x001A0 , 0x0004F , 0x0031B },+	{ 0x001A1 , 0x0006F , 0x0031B },+	{ 0x001AF , 0x00055 , 0x0031B },+	{ 0x001B0 , 0x00075 , 0x0031B },+	{ 0x001CD , 0x00041 , 0x0030C },+	{ 0x001CE , 0x00061 , 0x0030C },+	{ 0x001CF , 0x00049 , 0x0030C },+	{ 0x001D0 , 0x00069 , 0x0030C },+	{ 0x001D1 , 0x0004F , 0x0030C },+	{ 0x001D2 , 0x0006F , 0x0030C },+	{ 0x001D3 , 0x00055 , 0x0030C },+	{ 0x001D4 , 0x00075 , 0x0030C },+	{ 0x001D5 , 0x000DC , 0x00304 },+	{ 0x001D6 , 0x000FC , 0x00304 },+	{ 0x001D7 , 0x000DC , 0x00301 },+	{ 0x001D8 , 0x000FC , 0x00301 },+	{ 0x001D9 , 0x000DC , 0x0030C },+	{ 0x001DA , 0x000FC , 0x0030C },+	{ 0x001DB , 0x000DC , 0x00300 },+	{ 0x001DC , 0x000FC , 0x00300 },+	{ 0x001DE , 0x000C4 , 0x00304 },+	{ 0x001DF , 0x000E4 , 0x00304 },+	{ 0x001E0 , 0x00226 , 0x00304 },+	{ 0x001E1 , 0x00227 , 0x00304 },+	{ 0x001E2 , 0x000C6 , 0x00304 },+	{ 0x001E3 , 0x000E6 , 0x00304 },+	{ 0x001E6 , 0x00047 , 0x0030C },+	{ 0x001E7 , 0x00067 , 0x0030C },+	{ 0x001E8 , 0x0004B , 0x0030C },+	{ 0x001E9 , 0x0006B , 0x0030C },+	{ 0x001EA , 0x0004F , 0x00328 },+	{ 0x001EB , 0x0006F , 0x00328 },+	{ 0x001EC , 0x001EA , 0x00304 },+	{ 0x001ED , 0x001EB , 0x00304 },+	{ 0x001EE , 0x001B7 , 0x0030C },+	{ 0x001EF , 0x00292 , 0x0030C },+	{ 0x001F0 , 0x0006A , 0x0030C },+	{ 0x001F4 , 0x00047 , 0x00301 },+	{ 0x001F5 , 0x00067 , 0x00301 },+	{ 0x001F8 , 0x0004E , 0x00300 },+	{ 0x001F9 , 0x0006E , 0x00300 },+	{ 0x001FA , 0x000C5 , 0x00301 },+	{ 0x001FB , 0x000E5 , 0x00301 },+	{ 0x001FC , 0x000C6 , 0x00301 },+	{ 0x001FD , 0x000E6 , 0x00301 },+	{ 0x001FE , 0x000D8 , 0x00301 },+	{ 0x001FF , 0x000F8 , 0x00301 },+	{ 0x00200 , 0x00041 , 0x0030F },+	{ 0x00201 , 0x00061 , 0x0030F },+	{ 0x00202 , 0x00041 , 0x00311 },+	{ 0x00203 , 0x00061 , 0x00311 },+	{ 0x00204 , 0x00045 , 0x0030F },+	{ 0x00205 , 0x00065 , 0x0030F },+	{ 0x00206 , 0x00045 , 0x00311 },+	{ 0x00207 , 0x00065 , 0x00311 },+	{ 0x00208 , 0x00049 , 0x0030F },+	{ 0x00209 , 0x00069 , 0x0030F },+	{ 0x0020A , 0x00049 , 0x00311 },+	{ 0x0020B , 0x00069 , 0x00311 },+	{ 0x0020C , 0x0004F , 0x0030F },+	{ 0x0020D , 0x0006F , 0x0030F },+	{ 0x0020E , 0x0004F , 0x00311 },+	{ 0x0020F , 0x0006F , 0x00311 },+	{ 0x00210 , 0x00052 , 0x0030F },+	{ 0x00211 , 0x00072 , 0x0030F },+	{ 0x00212 , 0x00052 , 0x00311 },+	{ 0x00213 , 0x00072 , 0x00311 },+	{ 0x00214 , 0x00055 , 0x0030F },+	{ 0x00215 , 0x00075 , 0x0030F },+	{ 0x00216 , 0x00055 , 0x00311 },+	{ 0x00217 , 0x00075 , 0x00311 },+	{ 0x00218 , 0x00053 , 0x00326 },+	{ 0x00219 , 0x00073 , 0x00326 },+	{ 0x0021A , 0x00054 , 0x00326 },+	{ 0x0021B , 0x00074 , 0x00326 },+	{ 0x0021E , 0x00048 , 0x0030C },+	{ 0x0021F , 0x00068 , 0x0030C },+	{ 0x00226 , 0x00041 , 0x00307 },+	{ 0x00227 , 0x00061 , 0x00307 },+	{ 0x00228 , 0x00045 , 0x00327 },+	{ 0x00229 , 0x00065 , 0x00327 },+	{ 0x0022A , 0x000D6 , 0x00304 },+	{ 0x0022B , 0x000F6 , 0x00304 },+	{ 0x0022C , 0x000D5 , 0x00304 },+	{ 0x0022D , 0x000F5 , 0x00304 },+	{ 0x0022E , 0x0004F , 0x00307 },+	{ 0x0022F , 0x0006F , 0x00307 },+	{ 0x00230 , 0x0022E , 0x00304 },+	{ 0x00231 , 0x0022F , 0x00304 },+	{ 0x00232 , 0x00059 , 0x00304 },+	{ 0x00233 , 0x00079 , 0x00304 },+	{ 0x00385 , 0x000A8 , 0x00301 },+	{ 0x00386 , 0x00391 , 0x00301 },+	{ 0x00388 , 0x00395 , 0x00301 },+	{ 0x00389 , 0x00397 , 0x00301 },+	{ 0x0038A , 0x00399 , 0x00301 },+	{ 0x0038C , 0x0039F , 0x00301 },+	{ 0x0038E , 0x003A5 , 0x00301 },+	{ 0x0038F , 0x003A9 , 0x00301 },+	{ 0x00390 , 0x003CA , 0x00301 },+	{ 0x003AA , 0x00399 , 0x00308 },+	{ 0x003AB , 0x003A5 , 0x00308 },+	{ 0x003AC , 0x003B1 , 0x00301 },+	{ 0x003AD , 0x003B5 , 0x00301 },+	{ 0x003AE , 0x003B7 , 0x00301 },+	{ 0x003AF , 0x003B9 , 0x00301 },+	{ 0x003B0 , 0x003CB , 0x00301 },+	{ 0x003CA , 0x003B9 , 0x00308 },+	{ 0x003CB , 0x003C5 , 0x00308 },+	{ 0x003CC , 0x003BF , 0x00301 },+	{ 0x003CD , 0x003C5 , 0x00301 },+	{ 0x003CE , 0x003C9 , 0x00301 },+	{ 0x003D3 , 0x003D2 , 0x00301 },+	{ 0x003D4 , 0x003D2 , 0x00308 },+	{ 0x00400 , 0x00415 , 0x00300 },+	{ 0x00401 , 0x00415 , 0x00308 },+	{ 0x00403 , 0x00413 , 0x00301 },+	{ 0x00407 , 0x00406 , 0x00308 },+	{ 0x0040C , 0x0041A , 0x00301 },+	{ 0x0040D , 0x00418 , 0x00300 },+	{ 0x0040E , 0x00423 , 0x00306 },+	{ 0x00419 , 0x00418 , 0x00306 },+	{ 0x00439 , 0x00438 , 0x00306 },+	{ 0x00450 , 0x00435 , 0x00300 },+	{ 0x00451 , 0x00435 , 0x00308 },+	{ 0x00453 , 0x00433 , 0x00301 },+	{ 0x00457 , 0x00456 , 0x00308 },+	{ 0x0045C , 0x0043A , 0x00301 },+	{ 0x0045D , 0x00438 , 0x00300 },+	{ 0x0045E , 0x00443 , 0x00306 },+	{ 0x00476 , 0x00474 , 0x0030F },+	{ 0x00477 , 0x00475 , 0x0030F },+	{ 0x004C1 , 0x00416 , 0x00306 },+	{ 0x004C2 , 0x00436 , 0x00306 },+	{ 0x004D0 , 0x00410 , 0x00306 },+	{ 0x004D1 , 0x00430 , 0x00306 },+	{ 0x004D2 , 0x00410 , 0x00308 },+	{ 0x004D3 , 0x00430 , 0x00308 },+	{ 0x004D6 , 0x00415 , 0x00306 },+	{ 0x004D7 , 0x00435 , 0x00306 },+	{ 0x004DA , 0x004D8 , 0x00308 },+	{ 0x004DB , 0x004D9 , 0x00308 },+	{ 0x004DC , 0x00416 , 0x00308 },+	{ 0x004DD , 0x00436 , 0x00308 },+	{ 0x004DE , 0x00417 , 0x00308 },+	{ 0x004DF , 0x00437 , 0x00308 },+	{ 0x004E2 , 0x00418 , 0x00304 },+	{ 0x004E3 , 0x00438 , 0x00304 },+	{ 0x004E4 , 0x00418 , 0x00308 },+	{ 0x004E5 , 0x00438 , 0x00308 },+	{ 0x004E6 , 0x0041E , 0x00308 },+	{ 0x004E7 , 0x0043E , 0x00308 },+	{ 0x004EA , 0x004E8 , 0x00308 },+	{ 0x004EB , 0x004E9 , 0x00308 },+	{ 0x004EC , 0x0042D , 0x00308 },+	{ 0x004ED , 0x0044D , 0x00308 },+	{ 0x004EE , 0x00423 , 0x00304 },+	{ 0x004EF , 0x00443 , 0x00304 },+	{ 0x004F0 , 0x00423 , 0x00308 },+	{ 0x004F1 , 0x00443 , 0x00308 },+	{ 0x004F2 , 0x00423 , 0x0030B },+	{ 0x004F3 , 0x00443 , 0x0030B },+	{ 0x004F4 , 0x00427 , 0x00308 },+	{ 0x004F5 , 0x00447 , 0x00308 },+	{ 0x004F8 , 0x0042B , 0x00308 },+	{ 0x004F9 , 0x0044B , 0x00308 },+	{ 0x00622 , 0x00627 , 0x00653 },+	{ 0x00623 , 0x00627 , 0x00654 },+	{ 0x00624 , 0x00648 , 0x00654 },+	{ 0x00625 , 0x00627 , 0x00655 },+	{ 0x00626 , 0x0064A , 0x00654 },+	{ 0x006C0 , 0x006D5 , 0x00654 },+	{ 0x006C2 , 0x006C1 , 0x00654 },+	{ 0x006D3 , 0x006D2 , 0x00654 },+	{ 0x00929 , 0x00928 , 0x0093C },+	{ 0x00931 , 0x00930 , 0x0093C },+	{ 0x00934 , 0x00933 , 0x0093C },+	{ 0x009CB , 0x009C7 , 0x009BE },+	{ 0x009CC , 0x009C7 , 0x009D7 },+	{ 0x00B48 , 0x00B47 , 0x00B56 },+	{ 0x00B4B , 0x00B47 , 0x00B3E },+	{ 0x00B4C , 0x00B47 , 0x00B57 },+	{ 0x00B94 , 0x00B92 , 0x00BD7 },+	{ 0x00BCA , 0x00BC6 , 0x00BBE },+	{ 0x00BCB , 0x00BC7 , 0x00BBE },+	{ 0x00BCC , 0x00BC6 , 0x00BD7 },+	{ 0x00C48 , 0x00C46 , 0x00C56 },+	{ 0x00CC0 , 0x00CBF , 0x00CD5 },+	{ 0x00CC7 , 0x00CC6 , 0x00CD5 },+	{ 0x00CC8 , 0x00CC6 , 0x00CD6 },+	{ 0x00CCA , 0x00CC6 , 0x00CC2 },+	{ 0x00CCB , 0x00CCA , 0x00CD5 },+	{ 0x00D4A , 0x00D46 , 0x00D3E },+	{ 0x00D4B , 0x00D47 , 0x00D3E },+	{ 0x00D4C , 0x00D46 , 0x00D57 },+	{ 0x00DDA , 0x00DD9 , 0x00DCA },+	{ 0x00DDC , 0x00DD9 , 0x00DCF },+	{ 0x00DDD , 0x00DDC , 0x00DCA },+	{ 0x00DDE , 0x00DD9 , 0x00DDF },+	{ 0x01026 , 0x01025 , 0x0102E },+	{ 0x01B06 , 0x01B05 , 0x01B35 },+	{ 0x01B08 , 0x01B07 , 0x01B35 },+	{ 0x01B0A , 0x01B09 , 0x01B35 },+	{ 0x01B0C , 0x01B0B , 0x01B35 },+	{ 0x01B0E , 0x01B0D , 0x01B35 },+	{ 0x01B12 , 0x01B11 , 0x01B35 },+	{ 0x01B3B , 0x01B3A , 0x01B35 },+	{ 0x01B3D , 0x01B3C , 0x01B35 },+	{ 0x01B40 , 0x01B3E , 0x01B35 },+	{ 0x01B41 , 0x01B3F , 0x01B35 },+	{ 0x01B43 , 0x01B42 , 0x01B35 },+	{ 0x01E00 , 0x00041 , 0x00325 },+	{ 0x01E01 , 0x00061 , 0x00325 },+	{ 0x01E02 , 0x00042 , 0x00307 },+	{ 0x01E03 , 0x00062 , 0x00307 },+	{ 0x01E04 , 0x00042 , 0x00323 },+	{ 0x01E05 , 0x00062 , 0x00323 },+	{ 0x01E06 , 0x00042 , 0x00331 },+	{ 0x01E07 , 0x00062 , 0x00331 },+	{ 0x01E08 , 0x000C7 , 0x00301 },+	{ 0x01E09 , 0x000E7 , 0x00301 },+	{ 0x01E0A , 0x00044 , 0x00307 },+	{ 0x01E0B , 0x00064 , 0x00307 },+	{ 0x01E0C , 0x00044 , 0x00323 },+	{ 0x01E0D , 0x00064 , 0x00323 },+	{ 0x01E0E , 0x00044 , 0x00331 },+	{ 0x01E0F , 0x00064 , 0x00331 },+	{ 0x01E10 , 0x00044 , 0x00327 },+	{ 0x01E11 , 0x00064 , 0x00327 },+	{ 0x01E12 , 0x00044 , 0x0032D },+	{ 0x01E13 , 0x00064 , 0x0032D },+	{ 0x01E14 , 0x00112 , 0x00300 },+	{ 0x01E15 , 0x00113 , 0x00300 },+	{ 0x01E16 , 0x00112 , 0x00301 },+	{ 0x01E17 , 0x00113 , 0x00301 },+	{ 0x01E18 , 0x00045 , 0x0032D },+	{ 0x01E19 , 0x00065 , 0x0032D },+	{ 0x01E1A , 0x00045 , 0x00330 },+	{ 0x01E1B , 0x00065 , 0x00330 },+	{ 0x01E1C , 0x00228 , 0x00306 },+	{ 0x01E1D , 0x00229 , 0x00306 },+	{ 0x01E1E , 0x00046 , 0x00307 },+	{ 0x01E1F , 0x00066 , 0x00307 },+	{ 0x01E20 , 0x00047 , 0x00304 },+	{ 0x01E21 , 0x00067 , 0x00304 },+	{ 0x01E22 , 0x00048 , 0x00307 },+	{ 0x01E23 , 0x00068 , 0x00307 },+	{ 0x01E24 , 0x00048 , 0x00323 },+	{ 0x01E25 , 0x00068 , 0x00323 },+	{ 0x01E26 , 0x00048 , 0x00308 },+	{ 0x01E27 , 0x00068 , 0x00308 },+	{ 0x01E28 , 0x00048 , 0x00327 },+	{ 0x01E29 , 0x00068 , 0x00327 },+	{ 0x01E2A , 0x00048 , 0x0032E },+	{ 0x01E2B , 0x00068 , 0x0032E },+	{ 0x01E2C , 0x00049 , 0x00330 },+	{ 0x01E2D , 0x00069 , 0x00330 },+	{ 0x01E2E , 0x000CF , 0x00301 },+	{ 0x01E2F , 0x000EF , 0x00301 },+	{ 0x01E30 , 0x0004B , 0x00301 },+	{ 0x01E31 , 0x0006B , 0x00301 },+	{ 0x01E32 , 0x0004B , 0x00323 },+	{ 0x01E33 , 0x0006B , 0x00323 },+	{ 0x01E34 , 0x0004B , 0x00331 },+	{ 0x01E35 , 0x0006B , 0x00331 },+	{ 0x01E36 , 0x0004C , 0x00323 },+	{ 0x01E37 , 0x0006C , 0x00323 },+	{ 0x01E38 , 0x01E36 , 0x00304 },+	{ 0x01E39 , 0x01E37 , 0x00304 },+	{ 0x01E3A , 0x0004C , 0x00331 },+	{ 0x01E3B , 0x0006C , 0x00331 },+	{ 0x01E3C , 0x0004C , 0x0032D },+	{ 0x01E3D , 0x0006C , 0x0032D },+	{ 0x01E3E , 0x0004D , 0x00301 },+	{ 0x01E3F , 0x0006D , 0x00301 },+	{ 0x01E40 , 0x0004D , 0x00307 },+	{ 0x01E41 , 0x0006D , 0x00307 },+	{ 0x01E42 , 0x0004D , 0x00323 },+	{ 0x01E43 , 0x0006D , 0x00323 },+	{ 0x01E44 , 0x0004E , 0x00307 },+	{ 0x01E45 , 0x0006E , 0x00307 },+	{ 0x01E46 , 0x0004E , 0x00323 },+	{ 0x01E47 , 0x0006E , 0x00323 },+	{ 0x01E48 , 0x0004E , 0x00331 },+	{ 0x01E49 , 0x0006E , 0x00331 },+	{ 0x01E4A , 0x0004E , 0x0032D },+	{ 0x01E4B , 0x0006E , 0x0032D },+	{ 0x01E4C , 0x000D5 , 0x00301 },+	{ 0x01E4D , 0x000F5 , 0x00301 },+	{ 0x01E4E , 0x000D5 , 0x00308 },+	{ 0x01E4F , 0x000F5 , 0x00308 },+	{ 0x01E50 , 0x0014C , 0x00300 },+	{ 0x01E51 , 0x0014D , 0x00300 },+	{ 0x01E52 , 0x0014C , 0x00301 },+	{ 0x01E53 , 0x0014D , 0x00301 },+	{ 0x01E54 , 0x00050 , 0x00301 },+	{ 0x01E55 , 0x00070 , 0x00301 },+	{ 0x01E56 , 0x00050 , 0x00307 },+	{ 0x01E57 , 0x00070 , 0x00307 },+	{ 0x01E58 , 0x00052 , 0x00307 },+	{ 0x01E59 , 0x00072 , 0x00307 },+	{ 0x01E5A , 0x00052 , 0x00323 },+	{ 0x01E5B , 0x00072 , 0x00323 },+	{ 0x01E5C , 0x01E5A , 0x00304 },+	{ 0x01E5D , 0x01E5B , 0x00304 },+	{ 0x01E5E , 0x00052 , 0x00331 },+	{ 0x01E5F , 0x00072 , 0x00331 },+	{ 0x01E60 , 0x00053 , 0x00307 },+	{ 0x01E61 , 0x00073 , 0x00307 },+	{ 0x01E62 , 0x00053 , 0x00323 },+	{ 0x01E63 , 0x00073 , 0x00323 },+	{ 0x01E64 , 0x0015A , 0x00307 },+	{ 0x01E65 , 0x0015B , 0x00307 },+	{ 0x01E66 , 0x00160 , 0x00307 },+	{ 0x01E67 , 0x00161 , 0x00307 },+	{ 0x01E68 , 0x01E62 , 0x00307 },+	{ 0x01E69 , 0x01E63 , 0x00307 },+	{ 0x01E6A , 0x00054 , 0x00307 },+	{ 0x01E6B , 0x00074 , 0x00307 },+	{ 0x01E6C , 0x00054 , 0x00323 },+	{ 0x01E6D , 0x00074 , 0x00323 },+	{ 0x01E6E , 0x00054 , 0x00331 },+	{ 0x01E6F , 0x00074 , 0x00331 },+	{ 0x01E70 , 0x00054 , 0x0032D },+	{ 0x01E71 , 0x00074 , 0x0032D },+	{ 0x01E72 , 0x00055 , 0x00324 },+	{ 0x01E73 , 0x00075 , 0x00324 },+	{ 0x01E74 , 0x00055 , 0x00330 },+	{ 0x01E75 , 0x00075 , 0x00330 },+	{ 0x01E76 , 0x00055 , 0x0032D },+	{ 0x01E77 , 0x00075 , 0x0032D },+	{ 0x01E78 , 0x00168 , 0x00301 },+	{ 0x01E79 , 0x00169 , 0x00301 },+	{ 0x01E7A , 0x0016A , 0x00308 },+	{ 0x01E7B , 0x0016B , 0x00308 },+	{ 0x01E7C , 0x00056 , 0x00303 },+	{ 0x01E7D , 0x00076 , 0x00303 },+	{ 0x01E7E , 0x00056 , 0x00323 },+	{ 0x01E7F , 0x00076 , 0x00323 },+	{ 0x01E80 , 0x00057 , 0x00300 },+	{ 0x01E81 , 0x00077 , 0x00300 },+	{ 0x01E82 , 0x00057 , 0x00301 },+	{ 0x01E83 , 0x00077 , 0x00301 },+	{ 0x01E84 , 0x00057 , 0x00308 },+	{ 0x01E85 , 0x00077 , 0x00308 },+	{ 0x01E86 , 0x00057 , 0x00307 },+	{ 0x01E87 , 0x00077 , 0x00307 },+	{ 0x01E88 , 0x00057 , 0x00323 },+	{ 0x01E89 , 0x00077 , 0x00323 },+	{ 0x01E8A , 0x00058 , 0x00307 },+	{ 0x01E8B , 0x00078 , 0x00307 },+	{ 0x01E8C , 0x00058 , 0x00308 },+	{ 0x01E8D , 0x00078 , 0x00308 },+	{ 0x01E8E , 0x00059 , 0x00307 },+	{ 0x01E8F , 0x00079 , 0x00307 },+	{ 0x01E90 , 0x0005A , 0x00302 },+	{ 0x01E91 , 0x0007A , 0x00302 },+	{ 0x01E92 , 0x0005A , 0x00323 },+	{ 0x01E93 , 0x0007A , 0x00323 },+	{ 0x01E94 , 0x0005A , 0x00331 },+	{ 0x01E95 , 0x0007A , 0x00331 },+	{ 0x01E96 , 0x00068 , 0x00331 },+	{ 0x01E97 , 0x00074 , 0x00308 },+	{ 0x01E98 , 0x00077 , 0x0030A },+	{ 0x01E99 , 0x00079 , 0x0030A },+	{ 0x01E9B , 0x0017F , 0x00307 },+	{ 0x01EA0 , 0x00041 , 0x00323 },+	{ 0x01EA1 , 0x00061 , 0x00323 },+	{ 0x01EA2 , 0x00041 , 0x00309 },+	{ 0x01EA3 , 0x00061 , 0x00309 },+	{ 0x01EA4 , 0x000C2 , 0x00301 },+	{ 0x01EA5 , 0x000E2 , 0x00301 },+	{ 0x01EA6 , 0x000C2 , 0x00300 },+	{ 0x01EA7 , 0x000E2 , 0x00300 },+	{ 0x01EA8 , 0x000C2 , 0x00309 },+	{ 0x01EA9 , 0x000E2 , 0x00309 },+	{ 0x01EAA , 0x000C2 , 0x00303 },+	{ 0x01EAB , 0x000E2 , 0x00303 },+	{ 0x01EAC , 0x01EA0 , 0x00302 },+	{ 0x01EAD , 0x01EA1 , 0x00302 },+	{ 0x01EAE , 0x00102 , 0x00301 },+	{ 0x01EAF , 0x00103 , 0x00301 },+	{ 0x01EB0 , 0x00102 , 0x00300 },+	{ 0x01EB1 , 0x00103 , 0x00300 },+	{ 0x01EB2 , 0x00102 , 0x00309 },+	{ 0x01EB3 , 0x00103 , 0x00309 },+	{ 0x01EB4 , 0x00102 , 0x00303 },+	{ 0x01EB5 , 0x00103 , 0x00303 },+	{ 0x01EB6 , 0x01EA0 , 0x00306 },+	{ 0x01EB7 , 0x01EA1 , 0x00306 },+	{ 0x01EB8 , 0x00045 , 0x00323 },+	{ 0x01EB9 , 0x00065 , 0x00323 },+	{ 0x01EBA , 0x00045 , 0x00309 },+	{ 0x01EBB , 0x00065 , 0x00309 },+	{ 0x01EBC , 0x00045 , 0x00303 },+	{ 0x01EBD , 0x00065 , 0x00303 },+	{ 0x01EBE , 0x000CA , 0x00301 },+	{ 0x01EBF , 0x000EA , 0x00301 },+	{ 0x01EC0 , 0x000CA , 0x00300 },+	{ 0x01EC1 , 0x000EA , 0x00300 },+	{ 0x01EC2 , 0x000CA , 0x00309 },+	{ 0x01EC3 , 0x000EA , 0x00309 },+	{ 0x01EC4 , 0x000CA , 0x00303 },+	{ 0x01EC5 , 0x000EA , 0x00303 },+	{ 0x01EC6 , 0x01EB8 , 0x00302 },+	{ 0x01EC7 , 0x01EB9 , 0x00302 },+	{ 0x01EC8 , 0x00049 , 0x00309 },+	{ 0x01EC9 , 0x00069 , 0x00309 },+	{ 0x01ECA , 0x00049 , 0x00323 },+	{ 0x01ECB , 0x00069 , 0x00323 },+	{ 0x01ECC , 0x0004F , 0x00323 },+	{ 0x01ECD , 0x0006F , 0x00323 },+	{ 0x01ECE , 0x0004F , 0x00309 },+	{ 0x01ECF , 0x0006F , 0x00309 },+	{ 0x01ED0 , 0x000D4 , 0x00301 },+	{ 0x01ED1 , 0x000F4 , 0x00301 },+	{ 0x01ED2 , 0x000D4 , 0x00300 },+	{ 0x01ED3 , 0x000F4 , 0x00300 },+	{ 0x01ED4 , 0x000D4 , 0x00309 },+	{ 0x01ED5 , 0x000F4 , 0x00309 },+	{ 0x01ED6 , 0x000D4 , 0x00303 },+	{ 0x01ED7 , 0x000F4 , 0x00303 },+	{ 0x01ED8 , 0x01ECC , 0x00302 },+	{ 0x01ED9 , 0x01ECD , 0x00302 },+	{ 0x01EDA , 0x001A0 , 0x00301 },+	{ 0x01EDB , 0x001A1 , 0x00301 },+	{ 0x01EDC , 0x001A0 , 0x00300 },+	{ 0x01EDD , 0x001A1 , 0x00300 },+	{ 0x01EDE , 0x001A0 , 0x00309 },+	{ 0x01EDF , 0x001A1 , 0x00309 },+	{ 0x01EE0 , 0x001A0 , 0x00303 },+	{ 0x01EE1 , 0x001A1 , 0x00303 },+	{ 0x01EE2 , 0x001A0 , 0x00323 },+	{ 0x01EE3 , 0x001A1 , 0x00323 },+	{ 0x01EE4 , 0x00055 , 0x00323 },+	{ 0x01EE5 , 0x00075 , 0x00323 },+	{ 0x01EE6 , 0x00055 , 0x00309 },+	{ 0x01EE7 , 0x00075 , 0x00309 },+	{ 0x01EE8 , 0x001AF , 0x00301 },+	{ 0x01EE9 , 0x001B0 , 0x00301 },+	{ 0x01EEA , 0x001AF , 0x00300 },+	{ 0x01EEB , 0x001B0 , 0x00300 },+	{ 0x01EEC , 0x001AF , 0x00309 },+	{ 0x01EED , 0x001B0 , 0x00309 },+	{ 0x01EEE , 0x001AF , 0x00303 },+	{ 0x01EEF , 0x001B0 , 0x00303 },+	{ 0x01EF0 , 0x001AF , 0x00323 },+	{ 0x01EF1 , 0x001B0 , 0x00323 },+	{ 0x01EF2 , 0x00059 , 0x00300 },+	{ 0x01EF3 , 0x00079 , 0x00300 },+	{ 0x01EF4 , 0x00059 , 0x00323 },+	{ 0x01EF5 , 0x00079 , 0x00323 },+	{ 0x01EF6 , 0x00059 , 0x00309 },+	{ 0x01EF7 , 0x00079 , 0x00309 },+	{ 0x01EF8 , 0x00059 , 0x00303 },+	{ 0x01EF9 , 0x00079 , 0x00303 },+	{ 0x01F00 , 0x003B1 , 0x00313 },+	{ 0x01F01 , 0x003B1 , 0x00314 },+	{ 0x01F02 , 0x01F00 , 0x00300 },+	{ 0x01F03 , 0x01F01 , 0x00300 },+	{ 0x01F04 , 0x01F00 , 0x00301 },+	{ 0x01F05 , 0x01F01 , 0x00301 },+	{ 0x01F06 , 0x01F00 , 0x00342 },+	{ 0x01F07 , 0x01F01 , 0x00342 },+	{ 0x01F08 , 0x00391 , 0x00313 },+	{ 0x01F09 , 0x00391 , 0x00314 },+	{ 0x01F0A , 0x01F08 , 0x00300 },+	{ 0x01F0B , 0x01F09 , 0x00300 },+	{ 0x01F0C , 0x01F08 , 0x00301 },+	{ 0x01F0D , 0x01F09 , 0x00301 },+	{ 0x01F0E , 0x01F08 , 0x00342 },+	{ 0x01F0F , 0x01F09 , 0x00342 },+	{ 0x01F10 , 0x003B5 , 0x00313 },+	{ 0x01F11 , 0x003B5 , 0x00314 },+	{ 0x01F12 , 0x01F10 , 0x00300 },+	{ 0x01F13 , 0x01F11 , 0x00300 },+	{ 0x01F14 , 0x01F10 , 0x00301 },+	{ 0x01F15 , 0x01F11 , 0x00301 },+	{ 0x01F18 , 0x00395 , 0x00313 },+	{ 0x01F19 , 0x00395 , 0x00314 },+	{ 0x01F1A , 0x01F18 , 0x00300 },+	{ 0x01F1B , 0x01F19 , 0x00300 },+	{ 0x01F1C , 0x01F18 , 0x00301 },+	{ 0x01F1D , 0x01F19 , 0x00301 },+	{ 0x01F20 , 0x003B7 , 0x00313 },+	{ 0x01F21 , 0x003B7 , 0x00314 },+	{ 0x01F22 , 0x01F20 , 0x00300 },+	{ 0x01F23 , 0x01F21 , 0x00300 },+	{ 0x01F24 , 0x01F20 , 0x00301 },+	{ 0x01F25 , 0x01F21 , 0x00301 },+	{ 0x01F26 , 0x01F20 , 0x00342 },+	{ 0x01F27 , 0x01F21 , 0x00342 },+	{ 0x01F28 , 0x00397 , 0x00313 },+	{ 0x01F29 , 0x00397 , 0x00314 },+	{ 0x01F2A , 0x01F28 , 0x00300 },+	{ 0x01F2B , 0x01F29 , 0x00300 },+	{ 0x01F2C , 0x01F28 , 0x00301 },+	{ 0x01F2D , 0x01F29 , 0x00301 },+	{ 0x01F2E , 0x01F28 , 0x00342 },+	{ 0x01F2F , 0x01F29 , 0x00342 },+	{ 0x01F30 , 0x003B9 , 0x00313 },+	{ 0x01F31 , 0x003B9 , 0x00314 },+	{ 0x01F32 , 0x01F30 , 0x00300 },+	{ 0x01F33 , 0x01F31 , 0x00300 },+	{ 0x01F34 , 0x01F30 , 0x00301 },+	{ 0x01F35 , 0x01F31 , 0x00301 },+	{ 0x01F36 , 0x01F30 , 0x00342 },+	{ 0x01F37 , 0x01F31 , 0x00342 },+	{ 0x01F38 , 0x00399 , 0x00313 },+	{ 0x01F39 , 0x00399 , 0x00314 },+	{ 0x01F3A , 0x01F38 , 0x00300 },+	{ 0x01F3B , 0x01F39 , 0x00300 },+	{ 0x01F3C , 0x01F38 , 0x00301 },+	{ 0x01F3D , 0x01F39 , 0x00301 },+	{ 0x01F3E , 0x01F38 , 0x00342 },+	{ 0x01F3F , 0x01F39 , 0x00342 },+	{ 0x01F40 , 0x003BF , 0x00313 },+	{ 0x01F41 , 0x003BF , 0x00314 },+	{ 0x01F42 , 0x01F40 , 0x00300 },+	{ 0x01F43 , 0x01F41 , 0x00300 },+	{ 0x01F44 , 0x01F40 , 0x00301 },+	{ 0x01F45 , 0x01F41 , 0x00301 },+	{ 0x01F48 , 0x0039F , 0x00313 },+	{ 0x01F49 , 0x0039F , 0x00314 },+	{ 0x01F4A , 0x01F48 , 0x00300 },+	{ 0x01F4B , 0x01F49 , 0x00300 },+	{ 0x01F4C , 0x01F48 , 0x00301 },+	{ 0x01F4D , 0x01F49 , 0x00301 },+	{ 0x01F50 , 0x003C5 , 0x00313 },+	{ 0x01F51 , 0x003C5 , 0x00314 },+	{ 0x01F52 , 0x01F50 , 0x00300 },+	{ 0x01F53 , 0x01F51 , 0x00300 },+	{ 0x01F54 , 0x01F50 , 0x00301 },+	{ 0x01F55 , 0x01F51 , 0x00301 },+	{ 0x01F56 , 0x01F50 , 0x00342 },+	{ 0x01F57 , 0x01F51 , 0x00342 },+	{ 0x01F59 , 0x003A5 , 0x00314 },+	{ 0x01F5B , 0x01F59 , 0x00300 },+	{ 0x01F5D , 0x01F59 , 0x00301 },+	{ 0x01F5F , 0x01F59 , 0x00342 },+	{ 0x01F60 , 0x003C9 , 0x00313 },+	{ 0x01F61 , 0x003C9 , 0x00314 },+	{ 0x01F62 , 0x01F60 , 0x00300 },+	{ 0x01F63 , 0x01F61 , 0x00300 },+	{ 0x01F64 , 0x01F60 , 0x00301 },+	{ 0x01F65 , 0x01F61 , 0x00301 },+	{ 0x01F66 , 0x01F60 , 0x00342 },+	{ 0x01F67 , 0x01F61 , 0x00342 },+	{ 0x01F68 , 0x003A9 , 0x00313 },+	{ 0x01F69 , 0x003A9 , 0x00314 },+	{ 0x01F6A , 0x01F68 , 0x00300 },+	{ 0x01F6B , 0x01F69 , 0x00300 },+	{ 0x01F6C , 0x01F68 , 0x00301 },+	{ 0x01F6D , 0x01F69 , 0x00301 },+	{ 0x01F6E , 0x01F68 , 0x00342 },+	{ 0x01F6F , 0x01F69 , 0x00342 },+	{ 0x01F70 , 0x003B1 , 0x00300 },+	{ 0x01F72 , 0x003B5 , 0x00300 },+	{ 0x01F74 , 0x003B7 , 0x00300 },+	{ 0x01F76 , 0x003B9 , 0x00300 },+	{ 0x01F78 , 0x003BF , 0x00300 },+	{ 0x01F7A , 0x003C5 , 0x00300 },+	{ 0x01F7C , 0x003C9 , 0x00300 },+	{ 0x01F80 , 0x01F00 , 0x00345 },+	{ 0x01F81 , 0x01F01 , 0x00345 },+	{ 0x01F82 , 0x01F02 , 0x00345 },+	{ 0x01F83 , 0x01F03 , 0x00345 },+	{ 0x01F84 , 0x01F04 , 0x00345 },+	{ 0x01F85 , 0x01F05 , 0x00345 },+	{ 0x01F86 , 0x01F06 , 0x00345 },+	{ 0x01F87 , 0x01F07 , 0x00345 },+	{ 0x01F88 , 0x01F08 , 0x00345 },+	{ 0x01F89 , 0x01F09 , 0x00345 },+	{ 0x01F8A , 0x01F0A , 0x00345 },+	{ 0x01F8B , 0x01F0B , 0x00345 },+	{ 0x01F8C , 0x01F0C , 0x00345 },+	{ 0x01F8D , 0x01F0D , 0x00345 },+	{ 0x01F8E , 0x01F0E , 0x00345 },+	{ 0x01F8F , 0x01F0F , 0x00345 },+	{ 0x01F90 , 0x01F20 , 0x00345 },+	{ 0x01F91 , 0x01F21 , 0x00345 },+	{ 0x01F92 , 0x01F22 , 0x00345 },+	{ 0x01F93 , 0x01F23 , 0x00345 },+	{ 0x01F94 , 0x01F24 , 0x00345 },+	{ 0x01F95 , 0x01F25 , 0x00345 },+	{ 0x01F96 , 0x01F26 , 0x00345 },+	{ 0x01F97 , 0x01F27 , 0x00345 },+	{ 0x01F98 , 0x01F28 , 0x00345 },+	{ 0x01F99 , 0x01F29 , 0x00345 },+	{ 0x01F9A , 0x01F2A , 0x00345 },+	{ 0x01F9B , 0x01F2B , 0x00345 },+	{ 0x01F9C , 0x01F2C , 0x00345 },+	{ 0x01F9D , 0x01F2D , 0x00345 },+	{ 0x01F9E , 0x01F2E , 0x00345 },+	{ 0x01F9F , 0x01F2F , 0x00345 },+	{ 0x01FA0 , 0x01F60 , 0x00345 },+	{ 0x01FA1 , 0x01F61 , 0x00345 },+	{ 0x01FA2 , 0x01F62 , 0x00345 },+	{ 0x01FA3 , 0x01F63 , 0x00345 },+	{ 0x01FA4 , 0x01F64 , 0x00345 },+	{ 0x01FA5 , 0x01F65 , 0x00345 },+	{ 0x01FA6 , 0x01F66 , 0x00345 },+	{ 0x01FA7 , 0x01F67 , 0x00345 },+	{ 0x01FA8 , 0x01F68 , 0x00345 },+	{ 0x01FA9 , 0x01F69 , 0x00345 },+	{ 0x01FAA , 0x01F6A , 0x00345 },+	{ 0x01FAB , 0x01F6B , 0x00345 },+	{ 0x01FAC , 0x01F6C , 0x00345 },+	{ 0x01FAD , 0x01F6D , 0x00345 },+	{ 0x01FAE , 0x01F6E , 0x00345 },+	{ 0x01FAF , 0x01F6F , 0x00345 },+	{ 0x01FB0 , 0x003B1 , 0x00306 },+	{ 0x01FB1 , 0x003B1 , 0x00304 },+	{ 0x01FB2 , 0x01F70 , 0x00345 },+	{ 0x01FB3 , 0x003B1 , 0x00345 },+	{ 0x01FB4 , 0x003AC , 0x00345 },+	{ 0x01FB6 , 0x003B1 , 0x00342 },+	{ 0x01FB7 , 0x01FB6 , 0x00345 },+	{ 0x01FB8 , 0x00391 , 0x00306 },+	{ 0x01FB9 , 0x00391 , 0x00304 },+	{ 0x01FBA , 0x00391 , 0x00300 },+	{ 0x01FBC , 0x00391 , 0x00345 },+	{ 0x01FC1 , 0x000A8 , 0x00342 },+	{ 0x01FC2 , 0x01F74 , 0x00345 },+	{ 0x01FC3 , 0x003B7 , 0x00345 },+	{ 0x01FC4 , 0x003AE , 0x00345 },+	{ 0x01FC6 , 0x003B7 , 0x00342 },+	{ 0x01FC7 , 0x01FC6 , 0x00345 },+	{ 0x01FC8 , 0x00395 , 0x00300 },+	{ 0x01FCA , 0x00397 , 0x00300 },+	{ 0x01FCC , 0x00397 , 0x00345 },+	{ 0x01FCD , 0x01FBF , 0x00300 },+	{ 0x01FCE , 0x01FBF , 0x00301 },+	{ 0x01FCF , 0x01FBF , 0x00342 },+	{ 0x01FD0 , 0x003B9 , 0x00306 },+	{ 0x01FD1 , 0x003B9 , 0x00304 },+	{ 0x01FD2 , 0x003CA , 0x00300 },+	{ 0x01FD6 , 0x003B9 , 0x00342 },+	{ 0x01FD7 , 0x003CA , 0x00342 },+	{ 0x01FD8 , 0x00399 , 0x00306 },+	{ 0x01FD9 , 0x00399 , 0x00304 },+	{ 0x01FDA , 0x00399 , 0x00300 },+	{ 0x01FDD , 0x01FFE , 0x00300 },+	{ 0x01FDE , 0x01FFE , 0x00301 },+	{ 0x01FDF , 0x01FFE , 0x00342 },+	{ 0x01FE0 , 0x003C5 , 0x00306 },+	{ 0x01FE1 , 0x003C5 , 0x00304 },+	{ 0x01FE2 , 0x003CB , 0x00300 },+	{ 0x01FE4 , 0x003C1 , 0x00313 },+	{ 0x01FE5 , 0x003C1 , 0x00314 },+	{ 0x01FE6 , 0x003C5 , 0x00342 },+	{ 0x01FE7 , 0x003CB , 0x00342 },+	{ 0x01FE8 , 0x003A5 , 0x00306 },+	{ 0x01FE9 , 0x003A5 , 0x00304 },+	{ 0x01FEA , 0x003A5 , 0x00300 },+	{ 0x01FEC , 0x003A1 , 0x00314 },+	{ 0x01FED , 0x000A8 , 0x00300 },+	{ 0x01FF2 , 0x01F7C , 0x00345 },+	{ 0x01FF3 , 0x003C9 , 0x00345 },+	{ 0x01FF4 , 0x003CE , 0x00345 },+	{ 0x01FF6 , 0x003C9 , 0x00342 },+	{ 0x01FF7 , 0x01FF6 , 0x00345 },+	{ 0x01FF8 , 0x0039F , 0x00300 },+	{ 0x01FFA , 0x003A9 , 0x00300 },+	{ 0x01FFC , 0x003A9 , 0x00345 },+	{ 0x0219A , 0x02190 , 0x00338 },+	{ 0x0219B , 0x02192 , 0x00338 },+	{ 0x021AE , 0x02194 , 0x00338 },+	{ 0x021CD , 0x021D0 , 0x00338 },+	{ 0x021CE , 0x021D4 , 0x00338 },+	{ 0x021CF , 0x021D2 , 0x00338 },+	{ 0x02204 , 0x02203 , 0x00338 },+	{ 0x02209 , 0x02208 , 0x00338 },+	{ 0x0220C , 0x0220B , 0x00338 },+	{ 0x02224 , 0x02223 , 0x00338 },+	{ 0x02226 , 0x02225 , 0x00338 },+	{ 0x02241 , 0x0223C , 0x00338 },+	{ 0x02244 , 0x02243 , 0x00338 },+	{ 0x02247 , 0x02245 , 0x00338 },+	{ 0x02249 , 0x02248 , 0x00338 },+	{ 0x02260 , 0x0003D , 0x00338 },+	{ 0x02262 , 0x02261 , 0x00338 },+	{ 0x0226D , 0x0224D , 0x00338 },+	{ 0x0226E , 0x0003C , 0x00338 },+	{ 0x0226F , 0x0003E , 0x00338 },+	{ 0x02270 , 0x02264 , 0x00338 },+	{ 0x02271 , 0x02265 , 0x00338 },+	{ 0x02274 , 0x02272 , 0x00338 },+	{ 0x02275 , 0x02273 , 0x00338 },+	{ 0x02278 , 0x02276 , 0x00338 },+	{ 0x02279 , 0x02277 , 0x00338 },+	{ 0x02280 , 0x0227A , 0x00338 },+	{ 0x02281 , 0x0227B , 0x00338 },+	{ 0x02284 , 0x02282 , 0x00338 },+	{ 0x02285 , 0x02283 , 0x00338 },+	{ 0x02288 , 0x02286 , 0x00338 },+	{ 0x02289 , 0x02287 , 0x00338 },+	{ 0x022AC , 0x022A2 , 0x00338 },+	{ 0x022AD , 0x022A8 , 0x00338 },+	{ 0x022AE , 0x022A9 , 0x00338 },+	{ 0x022AF , 0x022AB , 0x00338 },+	{ 0x022E0 , 0x0227C , 0x00338 },+	{ 0x022E1 , 0x0227D , 0x00338 },+	{ 0x022E2 , 0x02291 , 0x00338 },+	{ 0x022E3 , 0x02292 , 0x00338 },+	{ 0x022EA , 0x022B2 , 0x00338 },+	{ 0x022EB , 0x022B3 , 0x00338 },+	{ 0x022EC , 0x022B4 , 0x00338 },+	{ 0x022ED , 0x022B5 , 0x00338 },+	{ 0x0304C , 0x0304B , 0x03099 },+	{ 0x0304E , 0x0304D , 0x03099 },+	{ 0x03050 , 0x0304F , 0x03099 },+	{ 0x03052 , 0x03051 , 0x03099 },+	{ 0x03054 , 0x03053 , 0x03099 },+	{ 0x03056 , 0x03055 , 0x03099 },+	{ 0x03058 , 0x03057 , 0x03099 },+	{ 0x0305A , 0x03059 , 0x03099 },+	{ 0x0305C , 0x0305B , 0x03099 },+	{ 0x0305E , 0x0305D , 0x03099 },+	{ 0x03060 , 0x0305F , 0x03099 },+	{ 0x03062 , 0x03061 , 0x03099 },+	{ 0x03065 , 0x03064 , 0x03099 },+	{ 0x03067 , 0x03066 , 0x03099 },+	{ 0x03069 , 0x03068 , 0x03099 },+	{ 0x03070 , 0x0306F , 0x03099 },+	{ 0x03071 , 0x0306F , 0x0309A },+	{ 0x03073 , 0x03072 , 0x03099 },+	{ 0x03074 , 0x03072 , 0x0309A },+	{ 0x03076 , 0x03075 , 0x03099 },+	{ 0x03077 , 0x03075 , 0x0309A },+	{ 0x03079 , 0x03078 , 0x03099 },+	{ 0x0307A , 0x03078 , 0x0309A },+	{ 0x0307C , 0x0307B , 0x03099 },+	{ 0x0307D , 0x0307B , 0x0309A },+	{ 0x03094 , 0x03046 , 0x03099 },+	{ 0x0309E , 0x0309D , 0x03099 },+	{ 0x030AC , 0x030AB , 0x03099 },+	{ 0x030AE , 0x030AD , 0x03099 },+	{ 0x030B0 , 0x030AF , 0x03099 },+	{ 0x030B2 , 0x030B1 , 0x03099 },+	{ 0x030B4 , 0x030B3 , 0x03099 },+	{ 0x030B6 , 0x030B5 , 0x03099 },+	{ 0x030B8 , 0x030B7 , 0x03099 },+	{ 0x030BA , 0x030B9 , 0x03099 },+	{ 0x030BC , 0x030BB , 0x03099 },+	{ 0x030BE , 0x030BD , 0x03099 },+	{ 0x030C0 , 0x030BF , 0x03099 },+	{ 0x030C2 , 0x030C1 , 0x03099 },+	{ 0x030C5 , 0x030C4 , 0x03099 },+	{ 0x030C7 , 0x030C6 , 0x03099 },+	{ 0x030C9 , 0x030C8 , 0x03099 },+	{ 0x030D0 , 0x030CF , 0x03099 },+	{ 0x030D1 , 0x030CF , 0x0309A },+	{ 0x030D3 , 0x030D2 , 0x03099 },+	{ 0x030D4 , 0x030D2 , 0x0309A },+	{ 0x030D6 , 0x030D5 , 0x03099 },+	{ 0x030D7 , 0x030D5 , 0x0309A },+	{ 0x030D9 , 0x030D8 , 0x03099 },+	{ 0x030DA , 0x030D8 , 0x0309A },+	{ 0x030DC , 0x030DB , 0x03099 },+	{ 0x030DD , 0x030DB , 0x0309A },+	{ 0x030F4 , 0x030A6 , 0x03099 },+	{ 0x030F7 , 0x030EF , 0x03099 },+	{ 0x030F8 , 0x030F0 , 0x03099 },+	{ 0x030F9 , 0x030F1 , 0x03099 },+	{ 0x030FA , 0x030F2 , 0x03099 },+	{ 0x030FE , 0x030FD , 0x03099 },+	{ 0x1109A , 0x11099 , 0x110BA },+	{ 0x1109C , 0x1109B , 0x110BA },+	{ 0x110AB , 0x110A5 , 0x110BA },+};++#endif /* ARCHIVE_STRING_COMPOSITION_H_INCLUDED */+
+ c/archive_string_sprintf.c view
@@ -0,0 +1,192 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_string_sprintf.c 189435 2009-03-06 05:14:55Z kientzle $");++/*+ * The use of printf()-family functions can be troublesome+ * for space-constrained applications.  In addition, correctly+ * implementing this function in terms of vsnprintf() requires+ * two calls (one to determine the size, another to format the+ * result), which in turn requires duplicating the argument list+ * using va_copy, which isn't yet universally available. <sigh>+ *+ * So, I've implemented a bare minimum of printf()-like capability+ * here.  This is only used to format error messages, so doesn't+ * require any floating-point support or field-width handling.+ */+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>++#include "archive_string.h"+#include "archive_private.h"++/*+ * Utility functions to format signed/unsigned integers and append+ * them to an archive_string.+ */+static void+append_uint(struct archive_string *as, uintmax_t d, unsigned base)+{+	static const char digits[] = "0123456789abcdef";+	if (d >= base)+		append_uint(as, d/base, base);+	archive_strappend_char(as, digits[d % base]);+}++static void+append_int(struct archive_string *as, intmax_t d, unsigned base)+{+	uintmax_t ud;++	if (d < 0) {+		archive_strappend_char(as, '-');+		ud = (d == INTMAX_MIN) ? (uintmax_t)(INTMAX_MAX) + 1 : (uintmax_t)(-d);+	} else+		ud = d;+	append_uint(as, ud, base);+}+++void+archive_string_sprintf(struct archive_string *as, const char *fmt, ...)+{+	va_list ap;++	va_start(ap, fmt);+	archive_string_vsprintf(as, fmt, ap);+	va_end(ap);+}++/*+ * Like 'vsprintf', but ensures the target is big enough, resizing if+ * necessary.+ */+void+archive_string_vsprintf(struct archive_string *as, const char *fmt,+    va_list ap)+{+	char long_flag;+	intmax_t s; /* Signed integer temp. */+	uintmax_t u; /* Unsigned integer temp. */+	const char *p, *p2;+	const wchar_t *pw;++	if (archive_string_ensure(as, 64) == NULL)+		__archive_errx(1, "Out of memory");++	if (fmt == NULL) {+		as->s[0] = 0;+		return;+	}++	for (p = fmt; *p != '\0'; p++) {+		const char *saved_p = p;++		if (*p != '%') {+			archive_strappend_char(as, *p);+			continue;+		}++		p++;++		long_flag = '\0';+		switch(*p) {+		case 'j':+		case 'l':+		case 'z':+			long_flag = *p;+			p++;+			break;+		}++		switch (*p) {+		case '%':+			archive_strappend_char(as, '%');+			break;+		case 'c':+			s = va_arg(ap, int);+			archive_strappend_char(as, (char)s);+			break;+		case 'd':+			switch(long_flag) {+			case 'j': s = va_arg(ap, intmax_t); break;+			case 'l': s = va_arg(ap, long); break;+			case 'z': s = va_arg(ap, ssize_t); break;+			default:  s = va_arg(ap, int); break;+			}+		        append_int(as, s, 10);+			break;+		case 's':+			switch(long_flag) {+			case 'l':+				pw = va_arg(ap, wchar_t *);+				if (pw == NULL)+					pw = L"(null)";+				if (archive_string_append_from_wcs(as, pw,+				    wcslen(pw)) != 0 && errno == ENOMEM)+					__archive_errx(1, "Out of memory");+				break;+			default:+				p2 = va_arg(ap, char *);+				if (p2 == NULL)+					p2 = "(null)";+				archive_strcat(as, p2);+				break;+			}+			break;+		case 'S':+			pw = va_arg(ap, wchar_t *);+			if (pw == NULL)+				pw = L"(null)";+			if (archive_string_append_from_wcs(as, pw,+			    wcslen(pw)) != 0 && errno == ENOMEM)+				__archive_errx(1, "Out of memory");+			break;+		case 'o': case 'u': case 'x': case 'X':+			/* Common handling for unsigned integer formats. */+			switch(long_flag) {+			case 'j': u = va_arg(ap, uintmax_t); break;+			case 'l': u = va_arg(ap, unsigned long); break;+			case 'z': u = va_arg(ap, size_t); break;+			default:  u = va_arg(ap, unsigned int); break;+			}+			/* Format it in the correct base. */+			switch (*p) {+			case 'o': append_uint(as, u, 8); break;+			case 'u': append_uint(as, u, 10); break;+			default: append_uint(as, u, 16); break;+			}+			break;+		default:+			/* Rewind and print the initial '%' literally. */+			p = saved_p;+			archive_strappend_char(as, *p);+		}+	}+}
+ c/archive_util.c view
@@ -0,0 +1,650 @@+/*-+ * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)+#include <wincrypt.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif+#ifdef HAVE_LZMA_H+#include <lzma.h>+#endif+#ifdef HAVE_BZLIB_H+#include <bzlib.h>+#endif+#ifdef HAVE_LZ4_H+#include <lz4.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_random_private.h"+#include "archive_string.h"++#ifndef O_CLOEXEC+#define O_CLOEXEC	0+#endif++static int archive_utility_string_sort_helper(char **, unsigned int);++/* Generic initialization of 'struct archive' objects. */+int+__archive_clean(struct archive *a)+{+	archive_string_conversion_free(a);+	return (ARCHIVE_OK);+}++int+archive_version_number(void)+{+	return (ARCHIVE_VERSION_NUMBER);+}++const char *+archive_version_string(void)+{+	return (ARCHIVE_VERSION_STRING);+}++int+archive_errno(struct archive *a)+{+	return (a->archive_error_number);+}++const char *+archive_error_string(struct archive *a)+{++	if (a->error != NULL  &&  *a->error != '\0')+		return (a->error);+	else+		return (NULL);+}++int+archive_file_count(struct archive *a)+{+	return (a->file_count);+}++int+archive_format(struct archive *a)+{+	return (a->archive_format);+}++const char *+archive_format_name(struct archive *a)+{+	return (a->archive_format_name);+}+++int+archive_compression(struct archive *a)+{+	return archive_filter_code(a, 0);+}++const char *+archive_compression_name(struct archive *a)+{+	return archive_filter_name(a, 0);+}+++/*+ * Return a count of the number of compressed bytes processed.+ */+la_int64_t+archive_position_compressed(struct archive *a)+{+	return archive_filter_bytes(a, -1);+}++/*+ * Return a count of the number of uncompressed bytes processed.+ */+la_int64_t+archive_position_uncompressed(struct archive *a)+{+	return archive_filter_bytes(a, 0);+}++void+archive_clear_error(struct archive *a)+{+	archive_string_empty(&a->error_string);+	a->error = NULL;+	a->archive_error_number = 0;+}++void+archive_set_error(struct archive *a, int error_number, const char *fmt, ...)+{+	va_list ap;++	a->archive_error_number = error_number;+	if (fmt == NULL) {+		a->error = NULL;+		return;+	}++	archive_string_empty(&(a->error_string));+	va_start(ap, fmt);+	archive_string_vsprintf(&(a->error_string), fmt, ap);+	va_end(ap);+	a->error = a->error_string.s;+}++void+archive_copy_error(struct archive *dest, struct archive *src)+{+	dest->archive_error_number = src->archive_error_number;++	archive_string_copy(&dest->error_string, &src->error_string);+	dest->error = dest->error_string.s;+}++void+__archive_errx(int retvalue, const char *msg)+{+	static const char msg1[] = "Fatal Internal Error in libarchive: ";+	size_t s;++	s = write(2, msg1, strlen(msg1));+	(void)s; /* UNUSED */+	s = write(2, msg, strlen(msg));+	(void)s; /* UNUSED */+	s = write(2, "\n", 1);+	(void)s; /* UNUSED */+	exit(retvalue);+}++/*+ * Create a temporary file+ */+#if defined(_WIN32) && !defined(__CYGWIN__)++/*+ * Do not use Windows tmpfile() function.+ * It will make a temporary file under the root directory+ * and it'll cause permission error if a user who is+ * non-Administrator creates temporary files.+ * Also Windows version of mktemp family including _mktemp_s+ * are not secure.+ */+static int+__archive_mktempx(const char *tmpdir, wchar_t *template)+{+	static const wchar_t prefix[] = L"libarchive_";+	static const wchar_t suffix[] = L"XXXXXXXXXX";+	static const wchar_t num[] = {+		L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',+		L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',+		L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',+		L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',+		L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',+		L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',+		L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',+		L'u', L'v', L'w', L'x', L'y', L'z'+	};+	HCRYPTPROV hProv;+	struct archive_wstring temp_name;+	wchar_t *ws;+	DWORD attr;+	wchar_t *xp, *ep;+	int fd;++	hProv = (HCRYPTPROV)NULL;+	fd = -1;+	ws = NULL;++	if (template == NULL) {+		archive_string_init(&temp_name);++		/* Get a temporary directory. */+		if (tmpdir == NULL) {+			size_t l;+			wchar_t *tmp;++			l = GetTempPathW(0, NULL);+			if (l == 0) {+				la_dosmaperr(GetLastError());+				goto exit_tmpfile;+			}+			tmp = malloc(l*sizeof(wchar_t));+			if (tmp == NULL) {+				errno = ENOMEM;+				goto exit_tmpfile;+			}+			GetTempPathW((DWORD)l, tmp);+			archive_wstrcpy(&temp_name, tmp);+			free(tmp);+		} else {+			if (archive_wstring_append_from_mbs(&temp_name, tmpdir,+			    strlen(tmpdir)) < 0)+				goto exit_tmpfile;+			if (temp_name.s[temp_name.length-1] != L'/')+				archive_wstrappend_wchar(&temp_name, L'/');+		}++		/* Check if temp_name is a directory. */+		attr = GetFileAttributesW(temp_name.s);+		if (attr == (DWORD)-1) {+			if (GetLastError() != ERROR_FILE_NOT_FOUND) {+				la_dosmaperr(GetLastError());+				goto exit_tmpfile;+			}+			ws = __la_win_permissive_name_w(temp_name.s);+			if (ws == NULL) {+				errno = EINVAL;+				goto exit_tmpfile;+			}+			attr = GetFileAttributesW(ws);+			if (attr == (DWORD)-1) {+				la_dosmaperr(GetLastError());+				goto exit_tmpfile;+			}+		}+		if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {+			errno = ENOTDIR;+			goto exit_tmpfile;+		}++		/*+		 * Create a temporary file.+		 */+		archive_wstrcat(&temp_name, prefix);+		archive_wstrcat(&temp_name, suffix);+		ep = temp_name.s + archive_strlen(&temp_name);+		xp = ep - wcslen(suffix);+		template = temp_name.s;+	} else {+		xp = wcschr(template, L'X');+		if (xp == NULL)	/* No X, programming error */+			abort();+		for (ep = xp; *ep == L'X'; ep++)+			continue;+		if (*ep)	/* X followed by non X, programming error */+			abort();+	}++	if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,+		CRYPT_VERIFYCONTEXT)) {+		la_dosmaperr(GetLastError());+		goto exit_tmpfile;+	}++	for (;;) {+		wchar_t *p;+		HANDLE h;++		/* Generate a random file name through CryptGenRandom(). */+		p = xp;+		if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),+		    (BYTE*)p)) {+			la_dosmaperr(GetLastError());+			goto exit_tmpfile;+		}+		for (; p < ep; p++)+			*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];++		free(ws);+		ws = __la_win_permissive_name_w(template);+		if (ws == NULL) {+			errno = EINVAL;+			goto exit_tmpfile;+		}+		if (template == temp_name.s) {+			attr = FILE_ATTRIBUTE_TEMPORARY |+			       FILE_FLAG_DELETE_ON_CLOSE;+		} else {+			/* mkstemp */+			attr = FILE_ATTRIBUTE_NORMAL;+		}+		h = CreateFileW(ws,+		    GENERIC_READ | GENERIC_WRITE | DELETE,+		    0,/* Not share */+		    NULL,+		    CREATE_NEW,/* Create a new file only */+		    attr,+		    NULL);+		if (h == INVALID_HANDLE_VALUE) {+			/* The same file already exists. retry with+			 * a new filename. */+			if (GetLastError() == ERROR_FILE_EXISTS)+				continue;+			/* Otherwise, fail creation temporary file. */+			la_dosmaperr(GetLastError());+			goto exit_tmpfile;+		}+		fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);+		if (fd == -1) {+			la_dosmaperr(GetLastError());+			CloseHandle(h);+			goto exit_tmpfile;+		} else+			break;/* success! */+	}+exit_tmpfile:+	if (hProv != (HCRYPTPROV)NULL)+		CryptReleaseContext(hProv, 0);+	free(ws);+	if (template == temp_name.s)+		archive_wstring_free(&temp_name);+	return (fd);+}++int+__archive_mktemp(const char *tmpdir)+{+	return __archive_mktempx(tmpdir, NULL);+}++int+__archive_mkstemp(wchar_t *template)+{+	return __archive_mktempx(NULL, template);+}++#else++static int+get_tempdir(struct archive_string *temppath)+{+	const char *tmp;++	tmp = getenv("TMPDIR");+	if (tmp == NULL)+#ifdef _PATH_TMP+		tmp = _PATH_TMP;+#else+                tmp = "/tmp";+#endif+	archive_strcpy(temppath, tmp);+	if (temppath->s[temppath->length-1] != '/')+		archive_strappend_char(temppath, '/');+	return (ARCHIVE_OK);+}++#if defined(HAVE_MKSTEMP)++/*+ * We can use mkstemp().+ */++int+__archive_mktemp(const char *tmpdir)+{+	struct archive_string temp_name;+	int fd = -1;++	archive_string_init(&temp_name);+	if (tmpdir == NULL) {+		if (get_tempdir(&temp_name) != ARCHIVE_OK)+			goto exit_tmpfile;+	} else {+		archive_strcpy(&temp_name, tmpdir);+		if (temp_name.s[temp_name.length-1] != '/')+			archive_strappend_char(&temp_name, '/');+	}+	archive_strcat(&temp_name, "libarchive_XXXXXX");+	fd = mkstemp(temp_name.s);+	if (fd < 0)+		goto exit_tmpfile;+	__archive_ensure_cloexec_flag(fd);+	unlink(temp_name.s);+exit_tmpfile:+	archive_string_free(&temp_name);+	return (fd);+}++int+__archive_mkstemp(char *template)+{+	int fd = -1;+	fd = mkstemp(template);+	if (fd >= 0)+		__archive_ensure_cloexec_flag(fd);+	return (fd);+}++#else /* !HAVE_MKSTEMP */++/*+ * We use a private routine.+ */++static int+__archive_mktempx(const char *tmpdir, char *template)+{+        static const char num[] = {+		'0', '1', '2', '3', '4', '5', '6', '7',+		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',+		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',+		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',+		'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',+		'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',+		'm', 'n', 'o', 'p', 'q', 'r', 's', 't',+		'u', 'v', 'w', 'x', 'y', 'z'+        };+	struct archive_string temp_name;+	struct stat st;+	int fd;+	char *tp, *ep;++	fd = -1;+	if (template == NULL) {+		archive_string_init(&temp_name);+		if (tmpdir == NULL) {+			if (get_tempdir(&temp_name) != ARCHIVE_OK)+				goto exit_tmpfile;+		} else+			archive_strcpy(&temp_name, tmpdir);+		if (temp_name.s[temp_name.length-1] == '/') {+			temp_name.s[temp_name.length-1] = '\0';+			temp_name.length --;+		}+		if (la_stat(temp_name.s, &st) < 0)+			goto exit_tmpfile;+		if (!S_ISDIR(st.st_mode)) {+			errno = ENOTDIR;+			goto exit_tmpfile;+		}+		archive_strcat(&temp_name, "/libarchive_");+		tp = temp_name.s + archive_strlen(&temp_name);+		archive_strcat(&temp_name, "XXXXXXXXXX");+		ep = temp_name.s + archive_strlen(&temp_name);+		template = temp_name.s;+	} else {+		tp = strchr(template, 'X');+		if (tp == NULL)	/* No X, programming error */+			abort();+		for (ep = tp; *ep == 'X'; ep++)+			continue;+		if (*ep)	/* X followed by non X, programming error */+			abort();+	}++	do {+		char *p;++		p = tp;+		archive_random(p, ep - p);+		while (p < ep) {+			int d = *((unsigned char *)p) % sizeof(num);+			*p++ = num[d];+		}+		fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,+			  0600);+	} while (fd < 0 && errno == EEXIST);+	if (fd < 0)+		goto exit_tmpfile;+	__archive_ensure_cloexec_flag(fd);+	if (template == temp_name.s)+		unlink(temp_name.s);+exit_tmpfile:+	if (template == temp_name.s)+		archive_string_free(&temp_name);+	return (fd);+}++int+__archive_mktemp(const char *tmpdir)+{+	return __archive_mktempx(tmpdir, NULL);+}++int+__archive_mkstemp(char *template)+{+	return __archive_mktempx(NULL, template);+}++#endif /* !HAVE_MKSTEMP */+#endif /* !_WIN32 || __CYGWIN__ */++/*+ * Set FD_CLOEXEC flag to a file descriptor if it is not set.+ * We have to set the flag if the platform does not provide O_CLOEXEC+ * or F_DUPFD_CLOEXEC flags.+ *+ * Note: This function is absolutely called after creating a new file+ * descriptor even if the platform seemingly provides O_CLOEXEC or+ * F_DUPFD_CLOEXEC macros because it is possible that the platform+ * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.+ */+void+__archive_ensure_cloexec_flag(int fd)+{+#if defined(_WIN32) && !defined(__CYGWIN__)+	(void)fd; /* UNUSED */+#else+	int flags;++	if (fd >= 0) {+		flags = fcntl(fd, F_GETFD);+		if (flags != -1 && (flags & FD_CLOEXEC) == 0)+			fcntl(fd, F_SETFD, flags | FD_CLOEXEC);+	}+#endif+}++/*+ * Utility function to sort a group of strings using quicksort.+ */+static int+archive_utility_string_sort_helper(char **strings, unsigned int n)+{+	unsigned int i, lesser_count, greater_count;+	char **lesser, **greater, **tmp, *pivot;+	int retval1, retval2;++	/* A list of 0 or 1 elements is already sorted */+	if (n <= 1)+		return (ARCHIVE_OK);++	lesser_count = greater_count = 0;+	lesser = greater = NULL;+	pivot = strings[0];+	for (i = 1; i < n; i++)+	{+		if (strcmp(strings[i], pivot) < 0)+		{+			lesser_count++;+			tmp = (char **)realloc(lesser,+				lesser_count * sizeof(char *));+			if (!tmp) {+				free(greater);+				free(lesser);+				return (ARCHIVE_FATAL);+			}+			lesser = tmp;+			lesser[lesser_count - 1] = strings[i];+		}+		else+		{+			greater_count++;+			tmp = (char **)realloc(greater,+				greater_count * sizeof(char *));+			if (!tmp) {+				free(greater);+				free(lesser);+				return (ARCHIVE_FATAL);+			}+			greater = tmp;+			greater[greater_count - 1] = strings[i];+		}+	}++	/* quicksort(lesser) */+	retval1 = archive_utility_string_sort_helper(lesser, lesser_count);+	for (i = 0; i < lesser_count; i++)+		strings[i] = lesser[i];+	free(lesser);++	/* pivot */+	strings[lesser_count] = pivot;++	/* quicksort(greater) */+	retval2 = archive_utility_string_sort_helper(greater, greater_count);+	for (i = 0; i < greater_count; i++)+		strings[lesser_count + 1 + i] = greater[i];+	free(greater);++	return (retval1 < retval2) ? retval1 : retval2;+}++int+archive_utility_string_sort(char **strings)+{+	  unsigned int size = 0;+	  while (strings[size] != NULL)+		size++;+	  return archive_utility_string_sort_helper(strings, size);+}
+ c/archive_version_details.c view
@@ -0,0 +1,151 @@+/*-+ * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");++#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif+#ifdef HAVE_LZMA_H+#include <lzma.h>+#endif+#ifdef HAVE_BZLIB_H+#include <bzlib.h>+#endif+#ifdef HAVE_LZ4_H+#include <lz4.h>+#endif+#ifdef HAVE_ZSTD_H+#include <zstd.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_string.h"++const char *+archive_version_details(void)+{+	static struct archive_string str;+	static int init = 0;+	const char *zlib = archive_zlib_version();+	const char *liblzma = archive_liblzma_version();+	const char *bzlib = archive_bzlib_version();+	const char *liblz4 = archive_liblz4_version();+	const char *libzstd = archive_libzstd_version();++	if (!init) {+		archive_string_init(&str);++		archive_strcat(&str, ARCHIVE_VERSION_STRING);+		if (zlib != NULL) {+			archive_strcat(&str, " zlib/");+			archive_strcat(&str, zlib);+		}+		if (liblzma) {+			archive_strcat(&str, " liblzma/");+			archive_strcat(&str, liblzma);+		}+		if (bzlib) {+			const char *p = bzlib;+			const char *sep = strchr(p, ',');+			if (sep == NULL)+				sep = p + strlen(p);+			archive_strcat(&str, " bz2lib/");+			archive_strncat(&str, p, sep - p);+		}+		if (liblz4) {+			archive_strcat(&str, " liblz4/");+			archive_strcat(&str, liblz4);+		}+		if (libzstd) {+			archive_strcat(&str, " libzstd/");+			archive_strcat(&str, libzstd);+		}+	}+	return str.s;+}++const char *+archive_zlib_version(void)+{+#ifdef HAVE_ZLIB_H+	return ZLIB_VERSION;+#else+	return NULL;+#endif+}++const char *+archive_liblzma_version(void)+{+#ifdef HAVE_LZMA_H+	return LZMA_VERSION_STRING;+#else+	return NULL;+#endif+}++const char *+archive_bzlib_version(void)+{+#ifdef HAVE_BZLIB_H+	return BZ2_bzlibVersion();+#else+	return NULL;+#endif+}++const char *+archive_liblz4_version(void)+{+#if defined(HAVE_LZ4_H) && defined(HAVE_LIBLZ4)+#define str(s) #s+#define NUMBER(x) str(x)+	return NUMBER(LZ4_VERSION_MAJOR) "." NUMBER(LZ4_VERSION_MINOR) "." NUMBER(LZ4_VERSION_RELEASE);+#undef NUMBER+#undef str+#else+	return NULL;+#endif+}++const char *+archive_libzstd_version(void)+{+#if HAVE_ZSTD_H && HAVE_LIBZSTD+	return ZSTD_VERSION_STRING;+#else+	return NULL;+#endif+}
+ c/archive_virtual.c view
@@ -0,0 +1,163 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_virtual.c 201098 2009-12-28 02:58:14Z kientzle $");++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"++int+archive_filter_code(struct archive *a, int n)+{+	return ((a->vtable->archive_filter_code)(a, n));+}++int+archive_filter_count(struct archive *a)+{+	return ((a->vtable->archive_filter_count)(a));+}++const char *+archive_filter_name(struct archive *a, int n)+{+	return ((a->vtable->archive_filter_name)(a, n));+}++la_int64_t+archive_filter_bytes(struct archive *a, int n)+{+	return ((a->vtable->archive_filter_bytes)(a, n));+}++int+archive_free(struct archive *a)+{+	if (a == NULL)+		return (ARCHIVE_OK);+	return ((a->vtable->archive_free)(a));+}++int+archive_write_close(struct archive *a)+{+	return ((a->vtable->archive_close)(a));+}++int+archive_read_close(struct archive *a)+{+	return ((a->vtable->archive_close)(a));+}++int+archive_write_fail(struct archive *a)+{+	a->state = ARCHIVE_STATE_FATAL;+	return a->state;+}++int+archive_write_free(struct archive *a)+{+	return archive_free(a);+}++#if ARCHIVE_VERSION_NUMBER < 4000000+/* For backwards compatibility; will be removed with libarchive 4.0. */+int+archive_write_finish(struct archive *a)+{+	return archive_write_free(a);+}+#endif++int+archive_read_free(struct archive *a)+{+	return archive_free(a);+}++#if ARCHIVE_VERSION_NUMBER < 4000000+/* For backwards compatibility; will be removed with libarchive 4.0. */+int+archive_read_finish(struct archive *a)+{+	return archive_read_free(a);+}+#endif++int+archive_write_header(struct archive *a, struct archive_entry *entry)+{+	++a->file_count;+	return ((a->vtable->archive_write_header)(a, entry));+}++int+archive_write_finish_entry(struct archive *a)+{+	return ((a->vtable->archive_write_finish_entry)(a));+}++la_ssize_t+archive_write_data(struct archive *a, const void *buff, size_t s)+{+	return ((a->vtable->archive_write_data)(a, buff, s));+}++la_ssize_t+archive_write_data_block(struct archive *a, const void *buff, size_t s,+    la_int64_t o)+{+	if (a->vtable->archive_write_data_block == NULL) {+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "archive_write_data_block not supported");+		a->state = ARCHIVE_STATE_FATAL;+		return (ARCHIVE_FATAL);+	}+	return ((a->vtable->archive_write_data_block)(a, buff, s, o));+}++int+archive_read_next_header(struct archive *a, struct archive_entry **entry)+{+	return ((a->vtable->archive_read_next_header)(a, entry));+}++int+archive_read_next_header2(struct archive *a, struct archive_entry *entry)+{+	return ((a->vtable->archive_read_next_header2)(a, entry));+}++int+archive_read_data_block(struct archive *a,+    const void **buff, size_t *s, la_int64_t *o)+{+	return ((a->vtable->archive_read_data_block)(a, buff, s, o));+}
+ c/archive_windows.c view
@@ -0,0 +1,909 @@+/*-+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA+ * Copyright (c) 2003-2007 Kees Zeelenberg+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ */++/*+ * A set of compatibility glue for building libarchive on Windows platforms.+ *+ * Originally created as "libarchive-nonposix.c" by Kees Zeelenberg+ * for the GnuWin32 project, trimmed significantly by Tim Kientzle.+ *+ * Much of the original file was unnecessary for libarchive, because+ * many of the features it emulated were not strictly necessary for+ * libarchive.  I hope for this to shrink further as libarchive+ * internals are gradually reworked to sit more naturally on both+ * POSIX and Windows.  Any ideas for this are greatly appreciated.+ *+ * The biggest remaining issue is the dev/ino emulation; libarchive+ * has a couple of public APIs that rely on dev/ino uniquely+ * identifying a file.  This doesn't match well with Windows.  I'm+ * considering alternative APIs.+ */++#if defined(_WIN32) && !defined(__CYGWIN__)++#include "archive_platform.h"+#include "archive_private.h"+#include "archive_entry.h"+#include <ctype.h>+#include <errno.h>+#include <stddef.h>+#ifdef HAVE_SYS_UTIME_H+#include <sys/utime.h>+#endif+#include <sys/stat.h>+#include <locale.h>+#include <process.h>+#include <stdlib.h>+#include <wchar.h>+#include <windows.h>+#include <share.h>++#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)++#if defined(__LA_LSEEK_NEEDED)+static BOOL SetFilePointerEx_perso(HANDLE hFile,+				   LARGE_INTEGER liDistanceToMove,+				   PLARGE_INTEGER lpNewFilePointer,+				   DWORD dwMoveMethod)+{+	LARGE_INTEGER li;+	li.QuadPart = liDistanceToMove.QuadPart;+	li.LowPart = SetFilePointer(+		hFile, li.LowPart, &li.HighPart, dwMoveMethod);+	if(lpNewFilePointer) {+		lpNewFilePointer->QuadPart = li.QuadPart;+	}+	return li.LowPart != -1 || GetLastError() == NO_ERROR;+}+#endif++struct ustat {+	int64_t		st_atime;+	uint32_t	st_atime_nsec;+	int64_t		st_ctime;+	uint32_t	st_ctime_nsec;+	int64_t		st_mtime;+	uint32_t	st_mtime_nsec;+	gid_t		st_gid;+	/* 64bits ino */+	int64_t		st_ino;+	mode_t		st_mode;+	uint32_t	st_nlink;+	uint64_t	st_size;+	uid_t		st_uid;+	dev_t		st_dev;+	dev_t		st_rdev;+};++/* Transform 64-bits ino into 32-bits by hashing.+ * You do not forget that really unique number size is 64-bits.+ */+#define INOSIZE (8*sizeof(ino_t)) /* 32 */+static __inline ino_t+getino(struct ustat *ub)+{+	ULARGE_INTEGER ino64;+	ino64.QuadPart = ub->st_ino;+	/* I don't know this hashing is correct way */+	return ((ino_t)(ino64.LowPart ^ (ino64.LowPart >> INOSIZE)));+}++/*+ * Prepend "\\?\" to the path name and convert it to unicode to permit+ * an extended-length path for a maximum total path length of 32767+ * characters.+ * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx+ */+wchar_t *+__la_win_permissive_name(const char *name)+{+	wchar_t *wn;+	wchar_t *ws;+	size_t ll;++	ll = strlen(name);+	wn = malloc((ll + 1) * sizeof(wchar_t));+	if (wn == NULL)+		return (NULL);+	ll = mbstowcs(wn, name, ll);+	if (ll == (size_t)-1) {+		free(wn);+		return (NULL);+	}+	wn[ll] = L'\0';+	ws = __la_win_permissive_name_w(wn);+	free(wn);+	return (ws);+}++wchar_t *+__la_win_permissive_name_w(const wchar_t *wname)+{+	wchar_t *wn, *wnp;+	wchar_t *ws, *wsp;+	DWORD l, len, slen;+	int unc;++	/* Get a full-pathname. */+	l = GetFullPathNameW(wname, 0, NULL, NULL);+	if (l == 0)+		return (NULL);+	/* NOTE: GetFullPathNameW has a bug that if the length of the file+	 * name is just 1 then it returns incomplete buffer size. Thus, we+	 * have to add three to the size to allocate a sufficient buffer+	 * size for the full-pathname of the file name. */+	l += 3;+	wnp = malloc(l * sizeof(wchar_t));+	if (wnp == NULL)+		return (NULL);+	len = GetFullPathNameW(wname, l, wnp, NULL);+	wn = wnp;++	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&+	    wnp[2] == L'?' && wnp[3] == L'\\')+		/* We have already a permissive name. */+		return (wn);++	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&+		wnp[2] == L'.' && wnp[3] == L'\\') {+		/* This is a device name */+		if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||+		     (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&+		    wnp[5] == L':' && wnp[6] == L'\\')+			wnp[2] = L'?';/* Not device name. */+		return (wn);+	}++	unc = 0;+	if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {+		wchar_t *p = &wnp[2];++		/* Skip server-name letters. */+		while (*p != L'\\' && *p != L'\0')+			++p;+		if (*p == L'\\') {+			wchar_t *rp = ++p;+			/* Skip share-name letters. */+			while (*p != L'\\' && *p != L'\0')+				++p;+			if (*p == L'\\' && p != rp) {+				/* Now, match patterns such as+				 * "\\server-name\share-name\" */+				wnp += 2;+				len -= 2;+				unc = 1;+			}+		}+	}++	slen = 4 + (unc * 4) + len + 1;+	ws = wsp = malloc(slen * sizeof(wchar_t));+	if (ws == NULL) {+		free(wn);+		return (NULL);+	}+	/* prepend "\\?\" */+	wcsncpy(wsp, L"\\\\?\\", 4);+	wsp += 4;+	slen -= 4;+	if (unc) {+		/* append "UNC\" ---> "\\?\UNC\" */+		wcsncpy(wsp, L"UNC\\", 4);+		wsp += 4;+		slen -= 4;+	}+	wcsncpy(wsp, wnp, slen);+	wsp[slen - 1] = L'\0'; /* Ensure null termination. */+	free(wn);+	return (ws);+}++/*+ * Create a file handle.+ * This can exceed MAX_PATH limitation.+ */+static HANDLE+la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode,+    LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,+    DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)+{+	wchar_t *wpath;+	HANDLE handle;++	handle = CreateFileA(path, dwDesiredAccess, dwShareMode,+	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,+	    hTemplateFile);+	if (handle != INVALID_HANDLE_VALUE)+		return (handle);+	if (GetLastError() != ERROR_PATH_NOT_FOUND)+		return (handle);+	wpath = __la_win_permissive_name(path);+	if (wpath == NULL)+		return (handle);+	handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,+	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,+	    hTemplateFile);+	free(wpath);+	return (handle);+}++#if defined(__LA_LSEEK_NEEDED)+__int64+__la_lseek(int fd, __int64 offset, int whence)+{+	LARGE_INTEGER distance;+	LARGE_INTEGER newpointer;+	HANDLE handle;++	if (fd < 0) {+		errno = EBADF;+		return (-1);+	}+	handle = (HANDLE)_get_osfhandle(fd);+	if (GetFileType(handle) != FILE_TYPE_DISK) {+		errno = EBADF;+		return (-1);+	}+	distance.QuadPart = offset;+	if (!SetFilePointerEx_perso(handle, distance, &newpointer, whence)) {+		DWORD lasterr;++		lasterr = GetLastError();+		if (lasterr == ERROR_BROKEN_PIPE)+			return (0);+		if (lasterr == ERROR_ACCESS_DENIED)+			errno = EBADF;+		else+			la_dosmaperr(lasterr);+		return (-1);+	}+	return (newpointer.QuadPart);+}+#endif++/* This can exceed MAX_PATH limitation. */+int+__la_open(const char *path, int flags, ...)+{+	va_list ap;+	wchar_t *ws;+	int r, pmode;+	DWORD attr;++	va_start(ap, flags);+	pmode = va_arg(ap, int);+	va_end(ap);+	ws = NULL;+	if ((flags & ~O_BINARY) == O_RDONLY) {+		/*+		 * When we open a directory, _open function returns+		 * "Permission denied" error.+		 */+		attr = GetFileAttributesA(path);+		if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) {+			ws = __la_win_permissive_name(path);+			if (ws == NULL) {+				errno = EINVAL;+				return (-1);+			}+			attr = GetFileAttributesW(ws);+		}+		if (attr == (DWORD)-1) {+			la_dosmaperr(GetLastError());+			free(ws);+			return (-1);+		}+		if (attr & FILE_ATTRIBUTE_DIRECTORY) {+			HANDLE handle;++			if (ws != NULL)+				handle = CreateFileW(ws, 0, 0, NULL,+				    OPEN_EXISTING,+				    FILE_FLAG_BACKUP_SEMANTICS |+				    FILE_ATTRIBUTE_READONLY,+					NULL);+			else+				handle = CreateFileA(path, 0, 0, NULL,+				    OPEN_EXISTING,+				    FILE_FLAG_BACKUP_SEMANTICS |+				    FILE_ATTRIBUTE_READONLY,+					NULL);+			free(ws);+			if (handle == INVALID_HANDLE_VALUE) {+				la_dosmaperr(GetLastError());+				return (-1);+			}+			r = _open_osfhandle((intptr_t)handle, _O_RDONLY);+			return (r);+		}+	}+	if (ws == NULL) {+#if defined(__BORLANDC__)+		/* Borland has no mode argument.+		   TODO: Fix mode of new file.  */+		r = _open(path, flags);+#else+		r = _open(path, flags, pmode);+#endif+		if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {+			/* Simulate other POSIX system action to pass our test suite. */+			attr = GetFileAttributesA(path);+			if (attr == (DWORD)-1)+				la_dosmaperr(GetLastError());+			else if (attr & FILE_ATTRIBUTE_DIRECTORY)+				errno = EISDIR;+			else+				errno = EACCES;+			return (-1);+		}+		if (r >= 0 || errno != ENOENT)+			return (r);+		ws = __la_win_permissive_name(path);+		if (ws == NULL) {+			errno = EINVAL;+			return (-1);+		}+	}+	r = _wopen(ws, flags, pmode);+	if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {+		/* Simulate other POSIX system action to pass our test suite. */+		attr = GetFileAttributesW(ws);+		if (attr == (DWORD)-1)+			la_dosmaperr(GetLastError());+		else if (attr & FILE_ATTRIBUTE_DIRECTORY)+			errno = EISDIR;+		else+			errno = EACCES;+	}+	free(ws);+	return (r);+}++ssize_t+__la_read(int fd, void *buf, size_t nbytes)+{+	HANDLE handle;+	DWORD bytes_read, lasterr;+	int r;++#ifdef _WIN64+	if (nbytes > UINT32_MAX)+		nbytes = UINT32_MAX;+#endif+	if (fd < 0) {+		errno = EBADF;+		return (-1);+	}+	/* Do not pass 0 to third parameter of ReadFile(), read bytes.+	 * This will not return to application side. */+	if (nbytes == 0)+		return (0);+	handle = (HANDLE)_get_osfhandle(fd);+	r = ReadFile(handle, buf, (uint32_t)nbytes,+	    &bytes_read, NULL);+	if (r == 0) {+		lasterr = GetLastError();+		if (lasterr == ERROR_NO_DATA) {+			errno = EAGAIN;+			return (-1);+		}+		if (lasterr == ERROR_BROKEN_PIPE)+			return (0);+		if (lasterr == ERROR_ACCESS_DENIED)+			errno = EBADF;+		else+			la_dosmaperr(lasterr);+		return (-1);+	}+	return ((ssize_t)bytes_read);+}++/* Convert Windows FILETIME to UTC */+__inline static void+fileTimeToUTC(const FILETIME *filetime, time_t *t, long *ns)+{+	ULARGE_INTEGER utc;++	utc.HighPart = filetime->dwHighDateTime;+	utc.LowPart  = filetime->dwLowDateTime;+	if (utc.QuadPart >= EPOC_TIME) {+		utc.QuadPart -= EPOC_TIME;+		*t = (time_t)(utc.QuadPart / 10000000);	/* milli seconds base */+		*ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */+	} else {+		*t = 0;+		*ns = 0;+	}+}++/* Stat by handle+ * Windows' stat() does not accept the path added "\\?\" especially "?"+ * character.+ * It means we cannot access the long name path longer than MAX_PATH.+ * So I've implemented a function similar to Windows' stat() to access the+ * long name path.+ * And I've added some feature.+ * 1. set st_ino by nFileIndexHigh and nFileIndexLow of+ *    BY_HANDLE_FILE_INFORMATION.+ * 2. set st_nlink by nNumberOfLinks of BY_HANDLE_FILE_INFORMATION.+ * 3. set st_dev by dwVolumeSerialNumber by BY_HANDLE_FILE_INFORMATION.+ */+static int+__hstat(HANDLE handle, struct ustat *st)+{+	BY_HANDLE_FILE_INFORMATION info;+	ULARGE_INTEGER ino64;+	DWORD ftype;+	mode_t mode;+	time_t t;+	long ns;++	switch (ftype = GetFileType(handle)) {+	case FILE_TYPE_UNKNOWN:+		errno = EBADF;+		return (-1);+	case FILE_TYPE_CHAR:+	case FILE_TYPE_PIPE:+		if (ftype == FILE_TYPE_CHAR) {+			st->st_mode = S_IFCHR;+			st->st_size = 0;+		} else {+			DWORD avail;++			st->st_mode = S_IFIFO;+			if (PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL))+				st->st_size = avail;+			else+				st->st_size = 0;+		}+		st->st_atime = 0;+		st->st_atime_nsec = 0;+		st->st_mtime = 0;+		st->st_mtime_nsec = 0;+		st->st_ctime = 0;+		st->st_ctime_nsec = 0;+		st->st_ino = 0;+		st->st_nlink = 1;+		st->st_uid = 0;+		st->st_gid = 0;+		st->st_rdev = 0;+		st->st_dev = 0;+		return (0);+	case FILE_TYPE_DISK:+		break;+	default:+		/* This ftype is undocumented type. */+		la_dosmaperr(GetLastError());+		return (-1);+	}++	ZeroMemory(&info, sizeof(info));+	if (!GetFileInformationByHandle (handle, &info)) {+		la_dosmaperr(GetLastError());+		return (-1);+	}++	mode = S_IRUSR | S_IRGRP | S_IROTH;+	if ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)+		mode |= S_IWUSR | S_IWGRP | S_IWOTH;+	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)+		mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;+	else+		mode |= S_IFREG;+	st->st_mode = mode;++	fileTimeToUTC(&info.ftLastAccessTime, &t, &ns);+	st->st_atime = t;+	st->st_atime_nsec = ns;+	fileTimeToUTC(&info.ftLastWriteTime, &t, &ns);+	st->st_mtime = t;+	st->st_mtime_nsec = ns;+	fileTimeToUTC(&info.ftCreationTime, &t, &ns);+	st->st_ctime = t;+	st->st_ctime_nsec = ns;+	st->st_size =+	    ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1))+		+ (int64_t)(info.nFileSizeLow);+#ifdef SIMULATE_WIN_STAT+	st->st_ino = 0;+	st->st_nlink = 1;+	st->st_dev = 0;+#else+	/* Getting FileIndex as i-node. We should remove a sequence which+	 * is high-16-bits of nFileIndexHigh. */+	ino64.HighPart = info.nFileIndexHigh & 0x0000FFFFUL;+	ino64.LowPart  = info.nFileIndexLow;+	st->st_ino = ino64.QuadPart;+	st->st_nlink = info.nNumberOfLinks;+	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)+		++st->st_nlink;/* Add parent directory. */+	st->st_dev = info.dwVolumeSerialNumber;+#endif+	st->st_uid = 0;+	st->st_gid = 0;+	st->st_rdev = 0;+	return (0);+}++static void+copy_stat(struct stat *st, struct ustat *us)+{+	st->st_atime = us->st_atime;+	st->st_ctime = us->st_ctime;+	st->st_mtime = us->st_mtime;+	st->st_gid = us->st_gid;+	st->st_ino = getino(us);+	st->st_mode = us->st_mode;+	st->st_nlink = us->st_nlink;+	st->st_size = (off_t)us->st_size;+	st->st_uid = us->st_uid;+	st->st_dev = us->st_dev;+	st->st_rdev = us->st_rdev;+}++/*+ * TODO: Remove a use of __la_fstat and __la_stat.+ * We should use GetFileInformationByHandle in place+ * where We still use the *stat functions.+ */+int+__la_fstat(int fd, struct stat *st)+{+	struct ustat u;+	int ret;++	if (fd < 0) {+		errno = EBADF;+		return (-1);+	}+	ret = __hstat((HANDLE)_get_osfhandle(fd), &u);+	if (ret >= 0) {+		copy_stat(st, &u);+		if (u.st_mode & (S_IFCHR | S_IFIFO)) {+			st->st_dev = fd;+			st->st_rdev = fd;+		}+	}+	return (ret);+}++/* This can exceed MAX_PATH limitation. */+int+__la_stat(const char *path, struct stat *st)+{+	HANDLE handle;+	struct ustat u;+	int ret;++	handle = la_CreateFile(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,+		FILE_FLAG_BACKUP_SEMANTICS,+		NULL);+	if (handle == INVALID_HANDLE_VALUE) {+		la_dosmaperr(GetLastError());+		return (-1);+	}+	ret = __hstat(handle, &u);+	CloseHandle(handle);+	if (ret >= 0) {+		char *p;++		copy_stat(st, &u);+		p = strrchr(path, '.');+		if (p != NULL && strlen(p) == 4) {+			char exttype[4];++			++ p;+			exttype[0] = toupper(*p++);+			exttype[1] = toupper(*p++);+			exttype[2] = toupper(*p++);+			exttype[3] = '\0';+			if (!strcmp(exttype, "EXE") || !strcmp(exttype, "CMD") ||+				!strcmp(exttype, "BAT") || !strcmp(exttype, "COM"))+				st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;+		}+	}+	return (ret);+}++/*+ * This waitpid is limited implementation.+ */+pid_t+__la_waitpid(HANDLE child, int *status, int option)+{+	DWORD cs;++	(void)option;/* UNUSED */+	do {+		if (GetExitCodeProcess(child, &cs) == 0) {+			CloseHandle(child);+			la_dosmaperr(GetLastError());+			*status = 0;+			return (-1);+		}+	} while (cs == STILL_ACTIVE);++	*status = (int)(cs & 0xff);+	return (0);+}++ssize_t+__la_write(int fd, const void *buf, size_t nbytes)+{+	DWORD bytes_written;++#ifdef _WIN64+	if (nbytes > UINT32_MAX)+		nbytes = UINT32_MAX;+#endif+	if (fd < 0) {+		errno = EBADF;+		return (-1);+	}+	if (!WriteFile((HANDLE)_get_osfhandle(fd), buf, (uint32_t)nbytes,+	    &bytes_written, NULL)) {+		DWORD lasterr;++		lasterr = GetLastError();+		if (lasterr == ERROR_ACCESS_DENIED)+			errno = EBADF;+		else+			la_dosmaperr(lasterr);+		return (-1);+	}+	return (bytes_written);+}++/*+ * Replace the Windows path separator '\' with '/'.+ */+static int+replace_pathseparator(struct archive_wstring *ws, const wchar_t *wp)+{+	wchar_t *w;+	size_t path_length;++	if (wp == NULL)+		return(0);+	if (wcschr(wp, L'\\') == NULL)+		return(0);+	path_length = wcslen(wp);+	if (archive_wstring_ensure(ws, path_length) == NULL)+		return(-1);+	archive_wstrncpy(ws, wp, path_length);+	for (w = ws->s; *w; w++) {+		if (*w == L'\\')+			*w = L'/';+	}+	return(1);+}++static int+fix_pathseparator(struct archive_entry *entry)+{+	struct archive_wstring ws;+	const wchar_t *wp;+	int ret = ARCHIVE_OK;++	archive_string_init(&ws);+	wp = archive_entry_pathname_w(entry);+	switch (replace_pathseparator(&ws, wp)) {+	case 0: /* Not replaced. */+		break;+	case 1: /* Replaced. */+		archive_entry_copy_pathname_w(entry, ws.s);+		break;+	default:+		ret = ARCHIVE_FAILED;+	}+	wp = archive_entry_hardlink_w(entry);+	switch (replace_pathseparator(&ws, wp)) {+	case 0: /* Not replaced. */+		break;+	case 1: /* Replaced. */+		archive_entry_copy_hardlink_w(entry, ws.s);+		break;+	default:+		ret = ARCHIVE_FAILED;+	}+	wp = archive_entry_symlink_w(entry);+	switch (replace_pathseparator(&ws, wp)) {+	case 0: /* Not replaced. */+		break;+	case 1: /* Replaced. */+		archive_entry_copy_symlink_w(entry, ws.s);+		break;+	default:+		ret = ARCHIVE_FAILED;+	}+	archive_wstring_free(&ws);+	return(ret);+}++struct archive_entry *+__la_win_entry_in_posix_pathseparator(struct archive_entry *entry)+{+	struct archive_entry *entry_main;+	const wchar_t *wp;+	int has_backslash = 0;+	int ret;++	wp = archive_entry_pathname_w(entry);+	if (wp != NULL && wcschr(wp, L'\\') != NULL)+		has_backslash = 1;+	if (!has_backslash) {+		wp = archive_entry_hardlink_w(entry);+		if (wp != NULL && wcschr(wp, L'\\') != NULL)+			has_backslash = 1;+	}+	if (!has_backslash) {+		wp = archive_entry_symlink_w(entry);+		if (wp != NULL && wcschr(wp, L'\\') != NULL)+			has_backslash = 1;+	}+	/*+	 * If there is no backslash chars, return the original.+	 */+	if (!has_backslash)+		return (entry);++	/* Copy entry so we can modify it as needed. */+	entry_main = archive_entry_clone(entry);+	if (entry_main == NULL)+		return (NULL);+	/* Replace the Windows path-separator '\' with '/'. */+	ret = fix_pathseparator(entry_main);+	if (ret < ARCHIVE_WARN) {+		archive_entry_free(entry_main);+		return (NULL);+	}+	return (entry_main);+}+++/*+ * The following function was modified from PostgreSQL sources and is+ * subject to the copyright below.+ */+/*-------------------------------------------------------------------------+ *+ * win32error.c+ *	  Map win32 error codes to errno values+ *+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group+ *+ * IDENTIFICATION+ *	  $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $+ *+ *-------------------------------------------------------------------------+ */+/*+PostgreSQL Database Management System+(formerly known as Postgres, then as Postgres95)++Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group++Portions Copyright (c) 1994, The Regents of the University of California++Permission to use, copy, modify, and distribute this software and its+documentation for any purpose, without fee, and without a written agreement+is hereby granted, provided that the above copyright notice and this+paragraph and the following two paragraphs appear in all copies.++IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS+DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.++THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY+AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO+PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.+*/++static const struct {+	DWORD		winerr;+	int		doserr;+} doserrors[] =+{+	{	ERROR_INVALID_FUNCTION, EINVAL	},+	{	ERROR_FILE_NOT_FOUND, ENOENT	},+	{	ERROR_PATH_NOT_FOUND, ENOENT	},+	{	ERROR_TOO_MANY_OPEN_FILES, EMFILE	},+	{	ERROR_ACCESS_DENIED, EACCES	},+	{	ERROR_INVALID_HANDLE, EBADF	},+	{	ERROR_ARENA_TRASHED, ENOMEM	},+	{	ERROR_NOT_ENOUGH_MEMORY, ENOMEM	},+	{	ERROR_INVALID_BLOCK, ENOMEM	},+	{	ERROR_BAD_ENVIRONMENT, E2BIG	},+	{	ERROR_BAD_FORMAT, ENOEXEC	},+	{	ERROR_INVALID_ACCESS, EINVAL	},+	{	ERROR_INVALID_DATA, EINVAL	},+	{	ERROR_INVALID_DRIVE, ENOENT	},+	{	ERROR_CURRENT_DIRECTORY, EACCES	},+	{	ERROR_NOT_SAME_DEVICE, EXDEV	},+	{	ERROR_NO_MORE_FILES, ENOENT	},+	{	ERROR_LOCK_VIOLATION, EACCES	},+	{	ERROR_SHARING_VIOLATION, EACCES	},+	{	ERROR_BAD_NETPATH, ENOENT	},+	{	ERROR_NETWORK_ACCESS_DENIED, EACCES	},+	{	ERROR_BAD_NET_NAME, ENOENT	},+	{	ERROR_FILE_EXISTS, EEXIST	},+	{	ERROR_CANNOT_MAKE, EACCES	},+	{	ERROR_FAIL_I24, EACCES	},+	{	ERROR_INVALID_PARAMETER, EINVAL	},+	{	ERROR_NO_PROC_SLOTS, EAGAIN	},+	{	ERROR_DRIVE_LOCKED, EACCES	},+	{	ERROR_BROKEN_PIPE, EPIPE	},+	{	ERROR_DISK_FULL, ENOSPC	},+	{	ERROR_INVALID_TARGET_HANDLE, EBADF	},+	{	ERROR_INVALID_HANDLE, EINVAL	},+	{	ERROR_WAIT_NO_CHILDREN, ECHILD	},+	{	ERROR_CHILD_NOT_COMPLETE, ECHILD	},+	{	ERROR_DIRECT_ACCESS_HANDLE, EBADF	},+	{	ERROR_NEGATIVE_SEEK, EINVAL	},+	{	ERROR_SEEK_ON_DEVICE, EACCES	},+	{	ERROR_DIR_NOT_EMPTY, ENOTEMPTY	},+	{	ERROR_NOT_LOCKED, EACCES	},+	{	ERROR_BAD_PATHNAME, ENOENT	},+	{	ERROR_MAX_THRDS_REACHED, EAGAIN	},+	{	ERROR_LOCK_FAILED, EACCES	},+	{	ERROR_ALREADY_EXISTS, EEXIST	},+	{	ERROR_FILENAME_EXCED_RANGE, ENOENT	},+	{	ERROR_NESTING_NOT_ALLOWED, EAGAIN	},+	{	ERROR_NOT_ENOUGH_QUOTA, ENOMEM	}+};++void+__la_dosmaperr(unsigned long e)+{+	int			i;++	if (e == 0)+	{+		errno = 0;+		return;+	}++	for (i = 0; i < (int)(sizeof(doserrors)/sizeof(doserrors[0])); i++)+	{+		if (doserrors[i].winerr == e)+		{+			errno = doserrors[i].doserr;+			return;+		}+	}++	/* fprintf(stderr, "unrecognized win32 error code: %lu", e); */+	errno = EINVAL;+	return;+}++#endif /* _WIN32 && !__CYGWIN__ */
+ c/archive_windows.h view
@@ -0,0 +1,315 @@+/*-+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA+ * Copyright (c) 2003-2006 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer+ *    in this position and unchanged.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ */++/*+ * TODO: A lot of stuff in here isn't actually used by libarchive and+ * can be trimmed out.  Note that this file is used by libarchive and+ * libarchive_test but nowhere else.  (But note that it gets compiled+ * with many different Windows environments, including MinGW, Visual+ * Studio, and Cygwin.  Significant changes should be tested in all three.)+ */++/*+ * TODO: Don't use off_t in here.  Use __int64 instead.  Note that+ * Visual Studio and the Windows SDK define off_t as 32 bits; Win32's+ * more modern file handling APIs all use __int64 instead of off_t.+ */++#ifndef LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED+#define	LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++/* Start of configuration for native Win32  */+#ifndef MINGW_HAS_SECURE_API+#define MINGW_HAS_SECURE_API 1+#endif++#include <errno.h>+#define	set_errno(val)	((errno)=val)+#include <io.h>+#include <stdlib.h>   //brings in NULL+#if defined(HAVE_STDINT_H)+#include <stdint.h>+#endif+#include <stdio.h>+#include <fcntl.h>+#include <sys/stat.h>+#include <process.h>+#include <direct.h>+#if defined(__MINGW32__) && defined(HAVE_UNISTD_H)+/* Prevent build error from a type mismatch of ftruncate().+ * This unistd.h defines it as ftruncate(int, off_t). */+#include <unistd.h>+#endif+#define NOCRYPT+#include <windows.h>+//#define	EFTYPE 7++#if defined(__BORLANDC__)+#pragma warn -8068	/* Constant out of range in comparison. */+#pragma warn -8072	/* Suspicious pointer arithmetic. */+#endif++#ifndef NULL+#ifdef  __cplusplus+#define	NULL    0+#else+#define	NULL    ((void *)0)+#endif+#endif++/* Alias the Windows _function to the POSIX equivalent. */+#define	close		_close+#define	fcntl(fd, cmd, flg)	/* No operation. */+#ifndef fileno+#define	fileno		_fileno+#endif+#ifdef fstat+#undef fstat+#endif+#define	fstat		__la_fstat+#if !defined(__BORLANDC__)+#ifdef lseek+#undef lseek+#endif+#define	lseek		_lseeki64+#else+#define	lseek		__la_lseek+#define __LA_LSEEK_NEEDED+#endif+#define	lstat		__la_stat+#define	open		__la_open+#define	read		__la_read+#if !defined(__BORLANDC__) && !defined(__WATCOMC__)+#define setmode		_setmode+#endif+#define	la_stat(path,stref)		__la_stat(path,stref)+#if !defined(__WATCOMC__)+#if !defined(__BORLANDC__)+#define	strdup		_strdup+#endif+#define	tzset		_tzset+#if !defined(__BORLANDC__)+#define	umask		_umask+#endif+#endif+#define	waitpid		__la_waitpid+#define	write		__la_write++#if !defined(__WATCOMC__)++#ifndef O_RDONLY+#define	O_RDONLY	_O_RDONLY+#define	O_WRONLY	_O_WRONLY+#define	O_TRUNC		_O_TRUNC+#define	O_CREAT		_O_CREAT+#define	O_EXCL		_O_EXCL+#define	O_BINARY	_O_BINARY+#endif++#ifndef _S_IFIFO+  #define	_S_IFIFO        0010000   /* pipe */+#endif+#ifndef _S_IFCHR+  #define	_S_IFCHR        0020000   /* character special */+#endif+#ifndef _S_IFDIR+  #define	_S_IFDIR        0040000   /* directory */+#endif+#ifndef _S_IFBLK+  #define	_S_IFBLK        0060000   /* block special */+#endif+#ifndef _S_IFLNK+  #define	_S_IFLNK        0120000   /* symbolic link */+#endif+#ifndef _S_IFSOCK+  #define	_S_IFSOCK       0140000   /* socket */+#endif+#ifndef	_S_IFREG+  #define	_S_IFREG        0100000   /* regular */+#endif+#ifndef	_S_IFMT+  #define	_S_IFMT         0170000   /* file type mask */+#endif++#ifndef S_IFIFO+#define	S_IFIFO     _S_IFIFO+#endif+//#define	S_IFCHR  _S_IFCHR+//#define	S_IFDIR  _S_IFDIR+#ifndef S_IFBLK+#define	S_IFBLK     _S_IFBLK+#endif+#ifndef S_IFLNK+#define	S_IFLNK     _S_IFLNK+#endif+#ifndef S_IFSOCK+#define	S_IFSOCK    _S_IFSOCK+#endif+//#define	S_IFREG  _S_IFREG+//#define	S_IFMT   _S_IFMT++#ifndef S_ISBLK+#define	S_ISBLK(m)	(((m) & S_IFMT) == S_IFBLK)	/* block special */+#define	S_ISFIFO(m)	(((m) & S_IFMT) == S_IFIFO)	/* fifo or socket */+#define	S_ISCHR(m)	(((m) & S_IFMT) == S_IFCHR)	/* char special */+#define	S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)	/* directory */+#define	S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)	/* regular file */+#endif+#define	S_ISLNK(m)  (((m) & S_IFMT) == S_IFLNK) /* Symbolic link */+#define	S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* Socket */++#define	_S_ISUID        0004000   /* set user id on execution */+#define	_S_ISGID        0002000   /* set group id on execution */+#define	_S_ISVTX        0001000   /* save swapped text even after use */++#define	S_ISUID        _S_ISUID+#define	S_ISGID        _S_ISGID+#define	S_ISVTX        _S_ISVTX++#define	_S_IRWXU	     (_S_IREAD | _S_IWRITE | _S_IEXEC)+#define	_S_IXUSR	     _S_IEXEC  /* read permission, user */+#define	_S_IWUSR	     _S_IWRITE /* write permission, user */+#define	_S_IRUSR	     _S_IREAD  /* execute/search permission, user */+#define	_S_IRWXG        (_S_IRWXU >> 3)+#define	_S_IXGRP        (_S_IXUSR >> 3) /* read permission, group */+#define	_S_IWGRP        (_S_IWUSR >> 3) /* write permission, group */+#define	_S_IRGRP        (_S_IRUSR >> 3) /* execute/search permission, group */+#define	_S_IRWXO        (_S_IRWXG >> 3)+#define	_S_IXOTH        (_S_IXGRP >> 3) /* read permission, other */+#define	_S_IWOTH        (_S_IWGRP >> 3) /* write permission, other */+#define	_S_IROTH        (_S_IRGRP  >> 3) /* execute/search permission, other */++#ifndef S_IRWXU+#define	S_IRWXU	     _S_IRWXU+#define	S_IXUSR	     _S_IXUSR+#define	S_IWUSR	     _S_IWUSR+#define	S_IRUSR	     _S_IRUSR+#endif+#ifndef S_IRWXG+#define	S_IRWXG        _S_IRWXG+#define	S_IXGRP        _S_IXGRP+#define	S_IWGRP        _S_IWGRP+#endif+#ifndef S_IRGRP+#define	S_IRGRP        _S_IRGRP+#endif+#ifndef S_IRWXO+#define	S_IRWXO        _S_IRWXO+#define	S_IXOTH        _S_IXOTH+#define	S_IWOTH        _S_IWOTH+#define	S_IROTH        _S_IROTH+#endif++#endif++#define	F_DUPFD	  	0	/* Duplicate file descriptor.  */+#define	F_GETFD		1	/* Get file descriptor flags.  */+#define	F_SETFD		2	/* Set file descriptor flags.  */+#define	F_GETFL		3	/* Get file status flags.  */+#define	F_SETFL		4	/* Set file status flags.  */+#define	F_GETOWN		5	/* Get owner (receiver of SIGIO).  */+#define	F_SETOWN		6	/* Set owner (receiver of SIGIO).  */+#define	F_GETLK		7	/* Get record locking info.  */+#define	F_SETLK		8	/* Set record locking info (non-blocking).  */+#define	F_SETLKW		9	/* Set record locking info (blocking).  */++/* XXX missing */+#define	F_GETLK64	7	/* Get record locking info.  */+#define	F_SETLK64	8	/* Set record locking info (non-blocking).  */+#define	F_SETLKW64	9	/* Set record locking info (blocking).  */++/* File descriptor flags used with F_GETFD and F_SETFD.  */+#define	FD_CLOEXEC	1	/* Close on exec.  */++//NOT SURE IF O_NONBLOCK is OK here but at least the 0x0004 flag is not used by anything else...+#define	O_NONBLOCK 0x0004 /* Non-blocking I/O.  */+//#define	O_NDELAY   O_NONBLOCK++/* Symbolic constants for the access() function */+#if !defined(F_OK)+    #define	R_OK    4       /*  Test for read permission    */+    #define	W_OK    2       /*  Test for write permission   */+    #define	X_OK    1       /*  Test for execute permission */+    #define	F_OK    0       /*  Test for existence of file  */+#endif+++/* Replacement POSIX function */+extern int	 __la_fstat(int fd, struct stat *st);+extern int	 __la_lstat(const char *path, struct stat *st);+#if defined(__LA_LSEEK_NEEDED)+extern __int64	 __la_lseek(int fd, __int64 offset, int whence);+#endif+extern int	 __la_open(const char *path, int flags, ...);+extern ssize_t	 __la_read(int fd, void *buf, size_t nbytes);+extern int	 __la_stat(const char *path, struct stat *st);+extern pid_t	 __la_waitpid(HANDLE child, int *status, int option);+extern ssize_t	 __la_write(int fd, const void *buf, size_t nbytes);++#define _stat64i32(path, st)	__la_stat(path, st)+#define _stat64(path, st)	__la_stat(path, st)+/* for status returned by la_waitpid */+#define WIFEXITED(sts)		((sts & 0x100) == 0)+#define WEXITSTATUS(sts)	(sts & 0x0FF)++extern wchar_t *__la_win_permissive_name(const char *name);+extern wchar_t *__la_win_permissive_name_w(const wchar_t *wname);+extern void __la_dosmaperr(unsigned long e);+#define la_dosmaperr(e) __la_dosmaperr(e)+extern struct archive_entry *__la_win_entry_in_posix_pathseparator(+    struct archive_entry *);++#if defined(HAVE_WCRTOMB) && defined(__BORLANDC__)+typedef int mbstate_t;+size_t wcrtomb(char *, wchar_t, mbstate_t *);+#endif++#if defined(_MSC_VER) && _MSC_VER < 1300+WINBASEAPI BOOL WINAPI GetVolumePathNameW(+       LPCWSTR lpszFileName,+       LPWSTR lpszVolumePathName,+       DWORD cchBufferLength+       );+# if _WIN32_WINNT < 0x0500 /* windows.h not providing 0x500 API */+typedef struct _FILE_ALLOCATED_RANGE_BUFFER {+       LARGE_INTEGER FileOffset;+       LARGE_INTEGER Length;+} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER;+#  define FSCTL_SET_SPARSE \+     CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_WRITE_DATA)+#  define FSCTL_QUERY_ALLOCATED_RANGES \+     CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 51,  METHOD_NEITHER, FILE_READ_DATA)+# endif+#endif++#endif /* LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED */
+ c/archive_write.c view
@@ -0,0 +1,789 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write.c 201099 2009-12-28 03:03:00Z kientzle $");++/*+ * This file contains the "essential" portions of the write API, that+ * is, stuff that will essentially always be used by any client that+ * actually needs to write an archive.  Optional pieces have been, as+ * far as possible, separated out into separate files to reduce+ * needlessly bloating statically-linked clients.+ */++#ifdef HAVE_SYS_WAIT_H+#include <sys/wait.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#include <time.h>+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_write_private.h"++static struct archive_vtable *archive_write_vtable(void);++static int	_archive_filter_code(struct archive *, int);+static const char *_archive_filter_name(struct archive *, int);+static int64_t	_archive_filter_bytes(struct archive *, int);+static int  _archive_write_filter_count(struct archive *);+static int	_archive_write_close(struct archive *);+static int	_archive_write_free(struct archive *);+static int	_archive_write_header(struct archive *, struct archive_entry *);+static int	_archive_write_finish_entry(struct archive *);+static ssize_t	_archive_write_data(struct archive *, const void *, size_t);++struct archive_none {+	size_t buffer_size;+	size_t avail;+	char *buffer;+	char *next;+};++static struct archive_vtable *+archive_write_vtable(void)+{+	static struct archive_vtable av;+	static int inited = 0;++	if (!inited) {+		av.archive_close = _archive_write_close;+		av.archive_filter_bytes = _archive_filter_bytes;+		av.archive_filter_code = _archive_filter_code;+		av.archive_filter_name = _archive_filter_name;+		av.archive_filter_count = _archive_write_filter_count;+		av.archive_free = _archive_write_free;+		av.archive_write_header = _archive_write_header;+		av.archive_write_finish_entry = _archive_write_finish_entry;+		av.archive_write_data = _archive_write_data;+		inited = 1;+	}+	return (&av);+}++/*+ * Allocate, initialize and return an archive object.+ */+struct archive *+archive_write_new(void)+{+	struct archive_write *a;+	unsigned char *nulls;++	a = (struct archive_write *)calloc(1, sizeof(*a));+	if (a == NULL)+		return (NULL);+	a->archive.magic = ARCHIVE_WRITE_MAGIC;+	a->archive.state = ARCHIVE_STATE_NEW;+	a->archive.vtable = archive_write_vtable();+	/*+	 * The value 10240 here matches the traditional tar default,+	 * but is otherwise arbitrary.+	 * TODO: Set the default block size from the format selected.+	 */+	a->bytes_per_block = 10240;+	a->bytes_in_last_block = -1;	/* Default */++	/* Initialize a block of nulls for padding purposes. */+	a->null_length = 1024;+	nulls = (unsigned char *)calloc(1, a->null_length);+	if (nulls == NULL) {+		free(a);+		return (NULL);+	}+	a->nulls = nulls;+	return (&a->archive);+}++/*+ * Set the block size.  Returns 0 if successful.+ */+int+archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block)+{+	struct archive_write *a = (struct archive_write *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block");+	a->bytes_per_block = bytes_per_block;+	return (ARCHIVE_OK);+}++/*+ * Get the current block size.  -1 if it has never been set.+ */+int+archive_write_get_bytes_per_block(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block");+	return (a->bytes_per_block);+}++/*+ * Set the size for the last block.+ * Returns 0 if successful.+ */+int+archive_write_set_bytes_in_last_block(struct archive *_a, int bytes)+{+	struct archive_write *a = (struct archive_write *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block");+	a->bytes_in_last_block = bytes;+	return (ARCHIVE_OK);+}++/*+ * Return the value set above.  -1 indicates it has not been set.+ */+int+archive_write_get_bytes_in_last_block(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_write_get_bytes_in_last_block");+	return (a->bytes_in_last_block);+}++/*+ * dev/ino of a file to be rejected.  Used to prevent adding+ * an archive to itself recursively.+ */+int+archive_write_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)+{+	struct archive_write *a = (struct archive_write *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_write_set_skip_file");+	a->skip_file_set = 1;+	a->skip_file_dev = d;+	a->skip_file_ino = i;+	return (ARCHIVE_OK);+}++/*+ * Allocate and return the next filter structure.+ */+struct archive_write_filter *+__archive_write_allocate_filter(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *f;++	f = calloc(1, sizeof(*f));+	f->archive = _a;+	f->state = ARCHIVE_WRITE_FILTER_STATE_NEW;+	if (a->filter_first == NULL)+		a->filter_first = f;+	else+		a->filter_last->next_filter = f;+	a->filter_last = f;+	return f;+}++/*+ * Write data to a particular filter.+ */+int+__archive_write_filter(struct archive_write_filter *f,+    const void *buff, size_t length)+{+	int r;+	/* Never write to non-open filters */+	if (f->state != ARCHIVE_WRITE_FILTER_STATE_OPEN)+		return(ARCHIVE_FATAL);+	if (length == 0)+		return(ARCHIVE_OK);+	if (f->write == NULL)+		/* If unset, a fatal error has already occurred, so this filter+		 * didn't open. We cannot write anything. */+		return(ARCHIVE_FATAL);+	r = (f->write)(f, buff, length);+	f->bytes_written += length;+	return (r);+}++/*+ * Recursive function for opening the filter chain+ * Last filter is opened first+ */+static int+__archive_write_open_filter(struct archive_write_filter *f)+{+	int ret;++	ret = ARCHIVE_OK;+	if (f->next_filter != NULL)+		ret = __archive_write_open_filter(f->next_filter);+	if (ret != ARCHIVE_OK)+		return (ret);+	if (f->state != ARCHIVE_WRITE_FILTER_STATE_NEW)+		return (ARCHIVE_FATAL);+	if (f->open == NULL) {+		f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN;+		return (ARCHIVE_OK);+	}+	ret = (f->open)(f);+	if (ret == ARCHIVE_OK)+		f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN;+	else+		f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL;+	return (ret);+}++/*+ * Open all filters+ */+static int+__archive_write_filters_open(struct archive_write *a)+{+	return (__archive_write_open_filter(a->filter_first));+}++/*+ * Close all filtes+ */+static int+__archive_write_filters_close(struct archive_write *a)+{+	struct archive_write_filter *f;+	int ret, ret1;+	ret = ARCHIVE_OK;+	for (f = a->filter_first; f != NULL; f = f->next_filter) {+		/* Do not close filters that are not open */+		if (f->state == ARCHIVE_WRITE_FILTER_STATE_OPEN) {+			if (f->close != NULL) {+				ret1 = (f->close)(f);+				if (ret1 < ret)+					ret = ret1;+				if (ret1 == ARCHIVE_OK) {+					f->state =+					    ARCHIVE_WRITE_FILTER_STATE_CLOSED;+				} else {+					f->state =+					    ARCHIVE_WRITE_FILTER_STATE_FATAL;+				}+			} else+				f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED;+		}+	}+	return (ret);+}++int+__archive_write_output(struct archive_write *a, const void *buff, size_t length)+{+	return (__archive_write_filter(a->filter_first, buff, length));+}++int+__archive_write_nulls(struct archive_write *a, size_t length)+{+	if (length == 0)+		return (ARCHIVE_OK);++	while (length > 0) {+		size_t to_write = length < a->null_length ? length : a->null_length;+		int r = __archive_write_output(a, a->nulls, to_write);+		if (r < ARCHIVE_OK)+			return (r);+		length -= to_write;+	}+	return (ARCHIVE_OK);+}++static int+archive_write_client_open(struct archive_write_filter *f)+{+	struct archive_write *a = (struct archive_write *)f->archive;+	struct archive_none *state;+	void *buffer;+	size_t buffer_size;+	int ret;++	f->bytes_per_block = archive_write_get_bytes_per_block(f->archive);+	f->bytes_in_last_block =+	    archive_write_get_bytes_in_last_block(f->archive);+	buffer_size = f->bytes_per_block;++	state = (struct archive_none *)calloc(1, sizeof(*state));+	buffer = (char *)malloc(buffer_size);+	if (state == NULL || buffer == NULL) {+		free(state);+		free(buffer);+		archive_set_error(f->archive, ENOMEM,+		    "Can't allocate data for output buffering");+		return (ARCHIVE_FATAL);+	}++	state->buffer_size = buffer_size;+	state->buffer = buffer;+	state->next = state->buffer;+	state->avail = state->buffer_size;+	f->data = state;++	if (a->client_opener == NULL)+		return (ARCHIVE_OK);+	ret = a->client_opener(f->archive, a->client_data);+	if (ret != ARCHIVE_OK) {+		free(state->buffer);+		free(state);+		f->data = NULL;+	}+	return (ret);+}++static int+archive_write_client_write(struct archive_write_filter *f,+    const void *_buff, size_t length)+{+	struct archive_write *a = (struct archive_write *)f->archive;+        struct archive_none *state = (struct archive_none *)f->data;+	const char *buff = (const char *)_buff;+	ssize_t remaining, to_copy;+	ssize_t bytes_written;++	remaining = length;++	/*+	 * If there is no buffer for blocking, just pass the data+	 * straight through to the client write callback.  In+	 * particular, this supports "no write delay" operation for+	 * special applications.  Just set the block size to zero.+	 */+	if (state->buffer_size == 0) {+		while (remaining > 0) {+			bytes_written = (a->client_writer)(&a->archive,+			    a->client_data, buff, remaining);+			if (bytes_written <= 0)+				return (ARCHIVE_FATAL);+			remaining -= bytes_written;+			buff += bytes_written;+		}+		return (ARCHIVE_OK);+	}++	/* If the copy buffer isn't empty, try to fill it. */+	if (state->avail < state->buffer_size) {+		/* If buffer is not empty... */+		/* ... copy data into buffer ... */+		to_copy = ((size_t)remaining > state->avail) ?+			state->avail : (size_t)remaining;+		memcpy(state->next, buff, to_copy);+		state->next += to_copy;+		state->avail -= to_copy;+		buff += to_copy;+		remaining -= to_copy;+		/* ... if it's full, write it out. */+		if (state->avail == 0) {+			char *p = state->buffer;+			size_t to_write = state->buffer_size;+			while (to_write > 0) {+				bytes_written = (a->client_writer)(&a->archive,+				    a->client_data, p, to_write);+				if (bytes_written <= 0)+					return (ARCHIVE_FATAL);+				if ((size_t)bytes_written > to_write) {+					archive_set_error(&(a->archive),+					    -1, "write overrun");+					return (ARCHIVE_FATAL);+				}+				p += bytes_written;+				to_write -= bytes_written;+			}+			state->next = state->buffer;+			state->avail = state->buffer_size;+		}+	}++	while ((size_t)remaining >= state->buffer_size) {+		/* Write out full blocks directly to client. */+		bytes_written = (a->client_writer)(&a->archive,+		    a->client_data, buff, state->buffer_size);+		if (bytes_written <= 0)+			return (ARCHIVE_FATAL);+		buff += bytes_written;+		remaining -= bytes_written;+	}++	if (remaining > 0) {+		/* Copy last bit into copy buffer. */+		memcpy(state->next, buff, remaining);+		state->next += remaining;+		state->avail -= remaining;+	}+	return (ARCHIVE_OK);+}++static int+archive_write_client_close(struct archive_write_filter *f)+{+	struct archive_write *a = (struct archive_write *)f->archive;+	struct archive_none *state = (struct archive_none *)f->data;+	ssize_t block_length;+	ssize_t target_block_length;+	ssize_t bytes_written;+	int ret = ARCHIVE_OK;++	/* If there's pending data, pad and write the last block */+	if (state->next != state->buffer) {+		block_length = state->buffer_size - state->avail;++		/* Tricky calculation to determine size of last block */+		if (a->bytes_in_last_block <= 0)+			/* Default or Zero: pad to full block */+			target_block_length = a->bytes_per_block;+		else+			/* Round to next multiple of bytes_in_last_block. */+			target_block_length = a->bytes_in_last_block *+			    ( (block_length + a->bytes_in_last_block - 1) /+			        a->bytes_in_last_block);+		if (target_block_length > a->bytes_per_block)+			target_block_length = a->bytes_per_block;+		if (block_length < target_block_length) {+			memset(state->next, 0,+			    target_block_length - block_length);+			block_length = target_block_length;+		}+		bytes_written = (a->client_writer)(&a->archive,+		    a->client_data, state->buffer, block_length);+		ret = bytes_written <= 0 ? ARCHIVE_FATAL : ARCHIVE_OK;+	}+	if (a->client_closer)+		(*a->client_closer)(&a->archive, a->client_data);+	free(state->buffer);+	free(state);+	a->client_data = NULL;+	/* Clear passphrase. */+	if (a->passphrase != NULL) {+		memset(a->passphrase, 0, strlen(a->passphrase));+		free(a->passphrase);+		a->passphrase = NULL;+	}+	/* Clear the close handler myself not to be called again. */+	f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED;+	return (ret);+}++/*+ * Open the archive using the current settings.+ */+int+archive_write_open(struct archive *_a, void *client_data,+    archive_open_callback *opener, archive_write_callback *writer,+    archive_close_callback *closer)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *client_filter;+	int ret, r1;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_open");+	archive_clear_error(&a->archive);++	a->client_writer = writer;+	a->client_opener = opener;+	a->client_closer = closer;+	a->client_data = client_data;++	client_filter = __archive_write_allocate_filter(_a);+	client_filter->open = archive_write_client_open;+	client_filter->write = archive_write_client_write;+	client_filter->close = archive_write_client_close;++	ret = __archive_write_filters_open(a);+	if (ret < ARCHIVE_WARN) {+		r1 = __archive_write_filters_close(a);+		__archive_write_filters_free(_a);+		return (r1 < ret ? r1 : ret);+	}++	a->archive.state = ARCHIVE_STATE_HEADER;+	if (a->format_init)+		ret = (a->format_init)(a);+	return (ret);+}++/*+ * Close out the archive.+ */+static int+_archive_write_close(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	int r = ARCHIVE_OK, r1 = ARCHIVE_OK;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL,+	    "archive_write_close");+	if (a->archive.state == ARCHIVE_STATE_NEW+	    || a->archive.state == ARCHIVE_STATE_CLOSED)+		return (ARCHIVE_OK); /* Okay to close() when not open. */++	archive_clear_error(&a->archive);++	/* Finish the last entry if a finish callback is specified */+	if (a->archive.state == ARCHIVE_STATE_DATA+	    && a->format_finish_entry != NULL)+		r = ((a->format_finish_entry)(a));++	/* Finish off the archive. */+	/* TODO: have format closers invoke compression close. */+	if (a->format_close != NULL) {+		r1 = (a->format_close)(a);+		if (r1 < r)+			r = r1;+	}++	/* Finish the compression and close the stream. */+	r1 = __archive_write_filters_close(a);+	if (r1 < r)+		r = r1;++	if (a->archive.state != ARCHIVE_STATE_FATAL)+		a->archive.state = ARCHIVE_STATE_CLOSED;+	return (r);+}++static int+_archive_write_filter_count(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *p = a->filter_first;+	int count = 0;+	while(p) {+		count++;+		p = p->next_filter;+	}+	return count;+}++void+__archive_write_filters_free(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	int r = ARCHIVE_OK, r1;++	while (a->filter_first != NULL) {+		struct archive_write_filter *next+		    = a->filter_first->next_filter;+		if (a->filter_first->free != NULL) {+			r1 = (*a->filter_first->free)(a->filter_first);+			if (r > r1)+				r = r1;+		}+		free(a->filter_first);+		a->filter_first = next;+	}+	a->filter_last = NULL;+}++/*+ * Destroy the archive structure.+ *+ * Be careful: user might just call write_new and then write_free.+ * Don't assume we actually wrote anything or performed any non-trivial+ * initialization.+ */+static int+_archive_write_free(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	int r = ARCHIVE_OK, r1;++	if (_a == NULL)+		return (ARCHIVE_OK);+	/* It is okay to call free() in state FATAL. */+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_free");+	if (a->archive.state != ARCHIVE_STATE_FATAL)+		r = archive_write_close(&a->archive);++	/* Release format resources. */+	if (a->format_free != NULL) {+		r1 = (a->format_free)(a);+		if (r1 < r)+			r = r1;+	}++	__archive_write_filters_free(_a);++	/* Release various dynamic buffers. */+	free((void *)(uintptr_t)(const void *)a->nulls);+	archive_string_free(&a->archive.error_string);+	if (a->passphrase != NULL) {+		/* A passphrase should be cleaned. */+		memset(a->passphrase, 0, strlen(a->passphrase));+		free(a->passphrase);+	}+	a->archive.magic = 0;+	__archive_clean(&a->archive);+	free(a);+	return (r);+}++/*+ * Write the appropriate header.+ */+static int+_archive_write_header(struct archive *_a, struct archive_entry *entry)+{+	struct archive_write *a = (struct archive_write *)_a;+	int ret, r2;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header");+	archive_clear_error(&a->archive);++	if (a->format_write_header == NULL) {+		archive_set_error(&(a->archive), -1,+		    "Format must be set before you can write to an archive.");+		a->archive.state = ARCHIVE_STATE_FATAL;+		return (ARCHIVE_FATAL);+	}++	/* In particular, "retry" and "fatal" get returned immediately. */+	ret = archive_write_finish_entry(&a->archive);+	if (ret == ARCHIVE_FATAL) {+		a->archive.state = ARCHIVE_STATE_FATAL;+		return (ARCHIVE_FATAL);+	}+	if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN)+		return (ret);++	if (a->skip_file_set &&+	    archive_entry_dev_is_set(entry) &&+	    archive_entry_ino_is_set(entry) &&+	    archive_entry_dev(entry) == (dev_t)a->skip_file_dev &&+	    archive_entry_ino64(entry) == a->skip_file_ino) {+		archive_set_error(&a->archive, 0,+		    "Can't add archive to itself");+		return (ARCHIVE_FAILED);+	}++	/* Format and write header. */+	r2 = ((a->format_write_header)(a, entry));+	if (r2 == ARCHIVE_FAILED) {+		return (ARCHIVE_FAILED);+	}+	if (r2 == ARCHIVE_FATAL) {+		a->archive.state = ARCHIVE_STATE_FATAL;+		return (ARCHIVE_FATAL);+	}+	if (r2 < ret)+		ret = r2;++	a->archive.state = ARCHIVE_STATE_DATA;+	return (ret);+}++static int+_archive_write_finish_entry(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	int ret = ARCHIVE_OK;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_write_finish_entry");+	if (a->archive.state & ARCHIVE_STATE_DATA+	    && a->format_finish_entry != NULL)+		ret = (a->format_finish_entry)(a);+	a->archive.state = ARCHIVE_STATE_HEADER;+	return (ret);+}++/*+ * Note that the compressor is responsible for blocking.+ */+static ssize_t+_archive_write_data(struct archive *_a, const void *buff, size_t s)+{+	struct archive_write *a = (struct archive_write *)_a;+	const size_t max_write = INT_MAX;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_DATA, "archive_write_data");+	/* In particular, this catches attempts to pass negative values. */+	if (s > max_write)+		s = max_write;+	archive_clear_error(&a->archive);+	return ((a->format_write_data)(a, buff, s));+}++static struct archive_write_filter *+filter_lookup(struct archive *_a, int n)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *f = a->filter_first;+	if (n == -1)+		return a->filter_last;+	if (n < 0)+		return NULL;+	while (n > 0 && f != NULL) {+		f = f->next_filter;+		--n;+	}+	return f;+}++static int+_archive_filter_code(struct archive *_a, int n)+{+	struct archive_write_filter *f = filter_lookup(_a, n);+	return f == NULL ? -1 : f->code;+}++static const char *+_archive_filter_name(struct archive *_a, int n)+{+	struct archive_write_filter *f = filter_lookup(_a, n);+	return f != NULL ? f->name : NULL;+}++static int64_t+_archive_filter_bytes(struct archive *_a, int n)+{+	struct archive_write_filter *f = filter_lookup(_a, n);+	return f == NULL ? -1 : f->bytes_written;+}
+ c/archive_write_add_filter.c view
@@ -0,0 +1,72 @@+/*-+ * Copyright (c) 2012 Ondrej Holy+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif++#include "archive.h"+#include "archive_private.h"++/* A table that maps filter codes to functions. */+static const+struct { int code; int (*setter)(struct archive *); } codes[] =+{+	{ ARCHIVE_FILTER_NONE,		archive_write_add_filter_none },+	{ ARCHIVE_FILTER_GZIP,		archive_write_add_filter_gzip },+	{ ARCHIVE_FILTER_BZIP2,		archive_write_add_filter_bzip2 },+	{ ARCHIVE_FILTER_COMPRESS,	archive_write_add_filter_compress },+	{ ARCHIVE_FILTER_GRZIP,		archive_write_add_filter_grzip },+	{ ARCHIVE_FILTER_LRZIP,		archive_write_add_filter_lrzip },+	{ ARCHIVE_FILTER_LZ4,		archive_write_add_filter_lz4 },+	{ ARCHIVE_FILTER_LZIP,		archive_write_add_filter_lzip },+	{ ARCHIVE_FILTER_LZMA,		archive_write_add_filter_lzma },+	{ ARCHIVE_FILTER_LZOP,		archive_write_add_filter_lzip },+	{ ARCHIVE_FILTER_UU,		archive_write_add_filter_uuencode },+	{ ARCHIVE_FILTER_XZ,		archive_write_add_filter_xz },+	{ ARCHIVE_FILTER_ZSTD,		archive_write_add_filter_zstd },+	{ -1,			NULL }+};++int+archive_write_add_filter(struct archive *a, int code)+{+	int i;++	for (i = 0; codes[i].code != -1; i++) {+		if (code == codes[i].code)+			return ((codes[i].setter)(a));+	}++	archive_set_error(a, EINVAL, "No such filter");+	return (ARCHIVE_FATAL);+}
+ c/archive_write_add_filter_b64encode.c view
@@ -0,0 +1,304 @@+/*-+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_string.h"+#include "archive_write_private.h"++#define LBYTES	57++struct private_b64encode {+	int			mode;+	struct archive_string	name;+	struct archive_string	encoded_buff;+	size_t			bs;+	size_t			hold_len;+	unsigned char		hold[LBYTES];+};++static int archive_filter_b64encode_options(struct archive_write_filter *,+    const char *, const char *);+static int archive_filter_b64encode_open(struct archive_write_filter *);+static int archive_filter_b64encode_write(struct archive_write_filter *,+    const void *, size_t);+static int archive_filter_b64encode_close(struct archive_write_filter *);+static int archive_filter_b64encode_free(struct archive_write_filter *);+static void la_b64_encode(struct archive_string *, const unsigned char *, size_t);+static int64_t atol8(const char *, size_t);++static const char base64[] = {+	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',+	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',+	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',+	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',+	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',+	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',+	'w', 'x', 'y', 'z', '0', '1', '2', '3',+	'4', '5', '6', '7', '8', '9', '+', '/'+};++/*+ * Add a compress filter to this write handle.+ */+int+archive_write_add_filter_b64encode(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);+	struct private_b64encode *state;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");++	state = (struct private_b64encode *)calloc(1, sizeof(*state));+	if (state == NULL) {+		archive_set_error(f->archive, ENOMEM,+		    "Can't allocate data for b64encode filter");+		return (ARCHIVE_FATAL);+	}+	archive_strcpy(&state->name, "-");+	state->mode = 0644;++	f->data = state;+	f->name = "b64encode";+	f->code = ARCHIVE_FILTER_UU;+	f->open = archive_filter_b64encode_open;+	f->options = archive_filter_b64encode_options;+	f->write = archive_filter_b64encode_write;+	f->close = archive_filter_b64encode_close;+	f->free = archive_filter_b64encode_free;++	return (ARCHIVE_OK);+}++/*+ * Set write options.+ */+static int+archive_filter_b64encode_options(struct archive_write_filter *f, const char *key,+    const char *value)+{+	struct private_b64encode *state = (struct private_b64encode *)f->data;++	if (strcmp(key, "mode") == 0) {+		if (value == NULL) {+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+			    "mode option requires octal digits");+			return (ARCHIVE_FAILED);+		}+		state->mode = (int)atol8(value, strlen(value)) & 0777;+		return (ARCHIVE_OK);+	} else if (strcmp(key, "name") == 0) {+		if (value == NULL) {+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+			    "name option requires a string");+			return (ARCHIVE_FAILED);+		}+		archive_strcpy(&state->name, value);+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++/*+ * Setup callback.+ */+static int+archive_filter_b64encode_open(struct archive_write_filter *f)+{+	struct private_b64encode *state = (struct private_b64encode *)f->data;+	size_t bs = 65536, bpb;++	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {+		/* Buffer size should be a multiple number of the of bytes+		 * per block for performance. */+		bpb = archive_write_get_bytes_per_block(f->archive);+		if (bpb > bs)+			bs = bpb;+		else if (bpb != 0)+			bs -= bs % bpb;+	}++	state->bs = bs;+	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {+		archive_set_error(f->archive, ENOMEM,+		    "Can't allocate data for b64encode buffer");+		return (ARCHIVE_FATAL);+	}++	archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n",+	    state->mode, state->name.s);++	f->data = state;+	return (0);+}++static void+la_b64_encode(struct archive_string *as, const unsigned char *p, size_t len)+{+	int c;++	for (; len >= 3; p += 3, len -= 3) {+		c = p[0] >> 2;+		archive_strappend_char(as, base64[c]);+		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);+		archive_strappend_char(as, base64[c]);+		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);+		archive_strappend_char(as, base64[c]);+		c = p[2] & 0x3f;+		archive_strappend_char(as, base64[c]);+	}+	if (len > 0) {+		c = p[0] >> 2;+		archive_strappend_char(as, base64[c]);+		c = (p[0] & 0x03) << 4;+		if (len == 1) {+			archive_strappend_char(as, base64[c]);+			archive_strappend_char(as, '=');+			archive_strappend_char(as, '=');+		} else {+			c |= (p[1] & 0xf0) >> 4;+			archive_strappend_char(as, base64[c]);+			c = (p[1] & 0x0f) << 2;+			archive_strappend_char(as, base64[c]);+			archive_strappend_char(as, '=');+		}+	}+	archive_strappend_char(as, '\n');+}++/*+ * Write data to the encoded stream.+ */+static int+archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,+    size_t length)+{+	struct private_b64encode *state = (struct private_b64encode *)f->data;+	const unsigned char *p = buff;+	int ret = ARCHIVE_OK;++	if (length == 0)+		return (ret);++	if (state->hold_len) {+		while (state->hold_len < LBYTES && length > 0) {+			state->hold[state->hold_len++] = *p++;+			length--;+		}+		if (state->hold_len < LBYTES)+			return (ret);+		la_b64_encode(&state->encoded_buff, state->hold, LBYTES);+		state->hold_len = 0;+	}++	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)+		la_b64_encode(&state->encoded_buff, p, LBYTES);++	/* Save remaining bytes. */+	if (length > 0) {+		memcpy(state->hold, p, length);+		state->hold_len = length;+	}+	while (archive_strlen(&state->encoded_buff) >= state->bs) {+		ret = __archive_write_filter(f->next_filter,+		    state->encoded_buff.s, state->bs);+		memmove(state->encoded_buff.s,+		    state->encoded_buff.s + state->bs,+		    state->encoded_buff.length - state->bs);+		state->encoded_buff.length -= state->bs;+	}++	return (ret);+}+++/*+ * Finish the compression...+ */+static int+archive_filter_b64encode_close(struct archive_write_filter *f)+{+	struct private_b64encode *state = (struct private_b64encode *)f->data;++	/* Flush remaining bytes. */+	if (state->hold_len != 0)+		la_b64_encode(&state->encoded_buff, state->hold, state->hold_len);+	archive_string_sprintf(&state->encoded_buff, "====\n");+	/* Write the last block */+	archive_write_set_bytes_in_last_block(f->archive, 1);+	return __archive_write_filter(f->next_filter,+	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));+}++static int+archive_filter_b64encode_free(struct archive_write_filter *f)+{+	struct private_b64encode *state = (struct private_b64encode *)f->data;++	archive_string_free(&state->name);+	archive_string_free(&state->encoded_buff);+	free(state);+	return (ARCHIVE_OK);+}++static int64_t+atol8(const char *p, size_t char_cnt)+{+	int64_t l;+	int digit;+        +	l = 0;+	while (char_cnt-- > 0) {+		if (*p >= '0' && *p <= '7')+			digit = *p - '0';+		else+			break;+		p++;+		l <<= 3;+		l |= digit;+	}+	return (l);+}+
+ c/archive_write_add_filter_by_name.c view
@@ -0,0 +1,77 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_private.h"++/* A table that maps names to functions. */+static const+struct { const char *name; int (*setter)(struct archive *); } names[] =+{+	{ "b64encode",		archive_write_add_filter_b64encode },+	{ "bzip2",		archive_write_add_filter_bzip2 },+	{ "compress",		archive_write_add_filter_compress },+	{ "grzip",		archive_write_add_filter_grzip },+	{ "gzip",		archive_write_add_filter_gzip },+	{ "lrzip",		archive_write_add_filter_lrzip },+	{ "lz4",		archive_write_add_filter_lz4 },+	{ "lzip",		archive_write_add_filter_lzip },+	{ "lzma",		archive_write_add_filter_lzma },+	{ "lzop",		archive_write_add_filter_lzop },+	{ "uuencode",		archive_write_add_filter_uuencode },+	{ "xz",			archive_write_add_filter_xz },+	{ "zstd",		archive_write_add_filter_zstd },+	{ NULL,			NULL }+};++int+archive_write_add_filter_by_name(struct archive *a, const char *name)+{+	int i;++	for (i = 0; names[i].name != NULL; i++) {+		if (strcmp(name, names[i].name) == 0)+			return ((names[i].setter)(a));+	}++	archive_set_error(a, EINVAL, "No such filter '%s'", name);+	a->state = ARCHIVE_STATE_FATAL;+	return (ARCHIVE_FATAL);+}
+ c/archive_write_add_filter_bzip2.c view
@@ -0,0 +1,401 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_bzip2.c 201091 2009-12-28 02:22:41Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_BZLIB_H+#include <bzlib.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_write_private.h"++#if ARCHIVE_VERSION_NUMBER < 4000000+int+archive_write_set_compression_bzip2(struct archive *a)+{+	__archive_write_filters_free(a);+	return (archive_write_add_filter_bzip2(a));+}+#endif++struct private_data {+	int		 compression_level;+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+	bz_stream	 stream;+	int64_t		 total_in;+	char		*compressed;+	size_t		 compressed_buffer_size;+#else+	struct archive_write_program_data *pdata;+#endif+};++static int archive_compressor_bzip2_close(struct archive_write_filter *);+static int archive_compressor_bzip2_free(struct archive_write_filter *);+static int archive_compressor_bzip2_open(struct archive_write_filter *);+static int archive_compressor_bzip2_options(struct archive_write_filter *,+		    const char *, const char *);+static int archive_compressor_bzip2_write(struct archive_write_filter *,+		    const void *, size_t);++/*+ * Add a bzip2 compression filter to this write handle.+ */+int+archive_write_add_filter_bzip2(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);+	struct private_data *data;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_bzip2");++	data = calloc(1, sizeof(*data));+	if (data == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	data->compression_level = 9; /* default */++	f->data = data;+	f->options = &archive_compressor_bzip2_options;+	f->close = &archive_compressor_bzip2_close;+	f->free = &archive_compressor_bzip2_free;+	f->open = &archive_compressor_bzip2_open;+	f->code = ARCHIVE_FILTER_BZIP2;+	f->name = "bzip2";+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+	return (ARCHIVE_OK);+#else+	data->pdata = __archive_write_program_allocate("bzip2");+	if (data->pdata == NULL) {+		free(data);+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	data->compression_level = 0;+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+	    "Using external bzip2 program");+	return (ARCHIVE_WARN);+#endif+}++/*+ * Set write options.+ */+static int+archive_compressor_bzip2_options(struct archive_write_filter *f,+    const char *key, const char *value)+{+	struct private_data *data = (struct private_data *)f->data;++	if (strcmp(key, "compression-level") == 0) {+		if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||+		    value[1] != '\0')+			return (ARCHIVE_WARN);+		data->compression_level = value[0] - '0';+		/* Make '0' be a synonym for '1'. */+		/* This way, bzip2 compressor supports the same 0..9+		 * range of levels as gzip. */+		if (data->compression_level < 1)+			data->compression_level = 1;+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+/* Don't compile this if we don't have bzlib. */++/*+ * Yuck.  bzlib.h is not const-correct, so I need this one bit+ * of ugly hackery to convert a const * pointer to a non-const pointer.+ */+#define	SET_NEXT_IN(st,src)					\+	(st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)+static int drive_compressor(struct archive_write_filter *,+		    struct private_data *, int finishing);++/*+ * Setup callback.+ */+static int+archive_compressor_bzip2_open(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	int ret;++	if (data->compressed == NULL) {+		size_t bs = 65536, bpb;+		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {+			/* Buffer size should be a multiple number of the of bytes+			 * per block for performance. */+			bpb = archive_write_get_bytes_per_block(f->archive);+			if (bpb > bs)+				bs = bpb;+			else if (bpb != 0)+				bs -= bs % bpb;+		}+		data->compressed_buffer_size = bs;+		data->compressed+		    = (char *)malloc(data->compressed_buffer_size);+		if (data->compressed == NULL) {+			archive_set_error(f->archive, ENOMEM,+			    "Can't allocate data for compression buffer");+			return (ARCHIVE_FATAL);+		}+	}++	memset(&data->stream, 0, sizeof(data->stream));+	data->stream.next_out = data->compressed;+	data->stream.avail_out = data->compressed_buffer_size;+	f->write = archive_compressor_bzip2_write;++	/* Initialize compression library */+	ret = BZ2_bzCompressInit(&(data->stream),+	    data->compression_level, 0, 30);+	if (ret == BZ_OK) {+		f->data = data;+		return (ARCHIVE_OK);+	}++	/* Library setup failed: clean up. */+	archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+	    "Internal error initializing compression library");++	/* Override the error message if we know what really went wrong. */+	switch (ret) {+	case BZ_PARAM_ERROR:+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library: "+		    "invalid setup parameter");+		break;+	case BZ_MEM_ERROR:+		archive_set_error(f->archive, ENOMEM,+		    "Internal error initializing compression library: "+		    "out of memory");+		break;+	case BZ_CONFIG_ERROR:+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library: "+		    "mis-compiled library");+		break;+	}++	return (ARCHIVE_FATAL);++}++/*+ * Write data to the compressed stream.+ *+ * Returns ARCHIVE_OK if all data written, error otherwise.+ */+static int+archive_compressor_bzip2_write(struct archive_write_filter *f,+    const void *buff, size_t length)+{+	struct private_data *data = (struct private_data *)f->data;++	/* Update statistics */+	data->total_in += length;++	/* Compress input data to output buffer */+	SET_NEXT_IN(data, buff);+	data->stream.avail_in = length;+	if (drive_compressor(f, data, 0))+		return (ARCHIVE_FATAL);+	return (ARCHIVE_OK);+}+++/*+ * Finish the compression.+ */+static int+archive_compressor_bzip2_close(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	int ret;++	/* Finish compression cycle. */+	ret = drive_compressor(f, data, 1);+	if (ret == ARCHIVE_OK) {+		/* Write the last block */+		ret = __archive_write_filter(f->next_filter,+		    data->compressed,+		    data->compressed_buffer_size - data->stream.avail_out);+	}++	switch (BZ2_bzCompressEnd(&(data->stream))) {+	case BZ_OK:+		break;+	default:+		archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,+		    "Failed to clean up compressor");+		ret = ARCHIVE_FATAL;+	}+	return ret;+}++static int+archive_compressor_bzip2_free(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	free(data->compressed);+	free(data);+	f->data = NULL;+	return (ARCHIVE_OK);+}++/*+ * Utility function to push input data through compressor, writing+ * full output blocks as necessary.+ *+ * Note that this handles both the regular write case (finishing ==+ * false) and the end-of-archive case (finishing == true).+ */+static int+drive_compressor(struct archive_write_filter *f,+    struct private_data *data, int finishing)+{+	int ret;++	for (;;) {+		if (data->stream.avail_out == 0) {+			ret = __archive_write_filter(f->next_filter,+			    data->compressed,+			    data->compressed_buffer_size);+			if (ret != ARCHIVE_OK) {+				/* TODO: Handle this write failure */+				return (ARCHIVE_FATAL);+			}+			data->stream.next_out = data->compressed;+			data->stream.avail_out = data->compressed_buffer_size;+		}++		/* If there's nothing to do, we're done. */+		if (!finishing && data->stream.avail_in == 0)+			return (ARCHIVE_OK);++		ret = BZ2_bzCompress(&(data->stream),+		    finishing ? BZ_FINISH : BZ_RUN);++		switch (ret) {+		case BZ_RUN_OK:+			/* In non-finishing case, did compressor+			 * consume everything? */+			if (!finishing && data->stream.avail_in == 0)+				return (ARCHIVE_OK);+			break;+		case BZ_FINISH_OK:  /* Finishing: There's more work to do */+			break;+		case BZ_STREAM_END: /* Finishing: all done */+			/* Only occurs in finishing case */+			return (ARCHIVE_OK);+		default:+			/* Any other return value indicates an error */+			archive_set_error(f->archive,+			    ARCHIVE_ERRNO_PROGRAMMER,+			    "Bzip2 compression failed;"+			    " BZ2_bzCompress() returned %d",+			    ret);+			return (ARCHIVE_FATAL);+		}+	}+}++#else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */++static int+archive_compressor_bzip2_open(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	struct archive_string as;+	int r;++	archive_string_init(&as);+	archive_strcpy(&as, "bzip2");++	/* Specify compression level. */+	if (data->compression_level > 0) {+		archive_strcat(&as, " -");+		archive_strappend_char(&as, '0' + data->compression_level);+	}+	f->write = archive_compressor_bzip2_write;++	r = __archive_write_program_open(f, data->pdata, as.s);+	archive_string_free(&as);+	return (r);+}++static int+archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff,+    size_t length)+{+	struct private_data *data = (struct private_data *)f->data;++	return __archive_write_program_write(f, data->pdata, buff, length);+}++static int+archive_compressor_bzip2_close(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	return __archive_write_program_close(f, data->pdata);+}++static int+archive_compressor_bzip2_free(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	__archive_write_program_free(data->pdata);+	free(data);+	return (ARCHIVE_OK);+}++#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
+ c/archive_write_add_filter_compress.c view
@@ -0,0 +1,447 @@+/*-+ * Copyright (c) 2008 Joerg Sonnenberger+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++/*-+ * Copyright (c) 1985, 1986, 1992, 1993+ *	The Regents of the University of California.  All rights reserved.+ *+ * This code is derived from software contributed to Berkeley by+ * Diomidis Spinellis and James A. Woods, derived from original+ * work by Spencer Thomas and Joseph Orost.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ * 3. Neither the name of the University nor the names of its contributors+ *    may be used to endorse or promote products derived from this software+ *    without specific prior written permission.+ *+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+ * SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_compress.c 201111 2009-12-28 03:33:05Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_write_private.h"++#define	HSIZE		69001	/* 95% occupancy */+#define	HSHIFT		8	/* 8 - trunc(log2(HSIZE / 65536)) */+#define	CHECK_GAP 10000		/* Ratio check interval. */++#define	MAXCODE(bits)	((1 << (bits)) - 1)++/*+ * the next two codes should not be changed lightly, as they must not+ * lie within the contiguous general code space.+ */+#define	FIRST	257		/* First free entry. */+#define	CLEAR	256		/* Table clear output code. */++struct private_data {+	int64_t in_count, out_count, checkpoint;++	int code_len;			/* Number of bits/code. */+	int cur_maxcode;		/* Maximum code, given n_bits. */+	int max_maxcode;		/* Should NEVER generate this code. */+	int hashtab [HSIZE];+	unsigned short codetab [HSIZE];+	int first_free;		/* First unused entry. */+	int compress_ratio;++	int cur_code, cur_fcode;++	int bit_offset;+	unsigned char bit_buf;++	unsigned char	*compressed;+	size_t		 compressed_buffer_size;+	size_t		 compressed_offset;+};++static int archive_compressor_compress_open(struct archive_write_filter *);+static int archive_compressor_compress_write(struct archive_write_filter *,+		    const void *, size_t);+static int archive_compressor_compress_close(struct archive_write_filter *);+static int archive_compressor_compress_free(struct archive_write_filter *);++#if ARCHIVE_VERSION_NUMBER < 4000000+int+archive_write_set_compression_compress(struct archive *a)+{+	__archive_write_filters_free(a);+	return (archive_write_add_filter_compress(a));+}+#endif++/*+ * Add a compress filter to this write handle.+ */+int+archive_write_add_filter_compress(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);++	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_compress");+	f->open = &archive_compressor_compress_open;+	f->code = ARCHIVE_FILTER_COMPRESS;+	f->name = "compress";+	return (ARCHIVE_OK);+}++/*+ * Setup callback.+ */+static int+archive_compressor_compress_open(struct archive_write_filter *f)+{+	struct private_data *state;+	size_t bs = 65536, bpb;++	f->code = ARCHIVE_FILTER_COMPRESS;+	f->name = "compress";++	state = (struct private_data *)calloc(1, sizeof(*state));+	if (state == NULL) {+		archive_set_error(f->archive, ENOMEM,+		    "Can't allocate data for compression");+		return (ARCHIVE_FATAL);+	}++	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {+		/* Buffer size should be a multiple number of the of bytes+		 * per block for performance. */+		bpb = archive_write_get_bytes_per_block(f->archive);+		if (bpb > bs)+			bs = bpb;+		else if (bpb != 0)+			bs -= bs % bpb;+	}+	state->compressed_buffer_size = bs;+	state->compressed = malloc(state->compressed_buffer_size);++	if (state->compressed == NULL) {+		archive_set_error(f->archive, ENOMEM,+		    "Can't allocate data for compression buffer");+		free(state);+		return (ARCHIVE_FATAL);+	}++	f->write = archive_compressor_compress_write;+	f->close = archive_compressor_compress_close;+	f->free = archive_compressor_compress_free;++	state->max_maxcode = 0x10000;	/* Should NEVER generate this code. */+	state->in_count = 0;		/* Length of input. */+	state->bit_buf = 0;+	state->bit_offset = 0;+	state->out_count = 3;		/* Includes 3-byte header mojo. */+	state->compress_ratio = 0;+	state->checkpoint = CHECK_GAP;+	state->code_len = 9;+	state->cur_maxcode = MAXCODE(state->code_len);+	state->first_free = FIRST;++	memset(state->hashtab, 0xff, sizeof(state->hashtab));++	/* Prime output buffer with a gzip header. */+	state->compressed[0] = 0x1f; /* Compress */+	state->compressed[1] = 0x9d;+	state->compressed[2] = 0x90; /* Block mode, 16bit max */+	state->compressed_offset = 3;++	f->data = state;+	return (0);+}++/*-+ * Output the given code.+ * Inputs:+ * 	code:	A n_bits-bit integer.  If == -1, then EOF.  This assumes+ *		that n_bits <= (long)wordsize - 1.+ * Outputs:+ * 	Outputs code to the file.+ * Assumptions:+ *	Chars are 8 bits long.+ * Algorithm:+ * 	Maintain a BITS character long buffer (so that 8 codes will+ * fit in it exactly).  Use the VAX insv instruction to insert each+ * code in turn.  When the buffer fills up empty it and start over.+ */++static const unsigned char rmask[9] =+	{0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};++static int+output_byte(struct archive_write_filter *f, unsigned char c)+{+	struct private_data *state = f->data;++	state->compressed[state->compressed_offset++] = c;+	++state->out_count;++	if (state->compressed_buffer_size == state->compressed_offset) {+		int ret = __archive_write_filter(f->next_filter,+		    state->compressed, state->compressed_buffer_size);+		if (ret != ARCHIVE_OK)+			return ARCHIVE_FATAL;+		state->compressed_offset = 0;+	}++	return ARCHIVE_OK;+}++static int+output_code(struct archive_write_filter *f, int ocode)+{+	struct private_data *state = f->data;+	int bits, ret, clear_flg, bit_offset;++	clear_flg = ocode == CLEAR;++	/*+	 * Since ocode is always >= 8 bits, only need to mask the first+	 * hunk on the left.+	 */+	bit_offset = state->bit_offset % 8;+	state->bit_buf |= (ocode << bit_offset) & 0xff;+	output_byte(f, state->bit_buf);++	bits = state->code_len - (8 - bit_offset);+	ocode >>= 8 - bit_offset;+	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */+	if (bits >= 8) {+		output_byte(f, ocode & 0xff);+		ocode >>= 8;+		bits -= 8;+	}+	/* Last bits. */+	state->bit_offset += state->code_len;+	state->bit_buf = ocode & rmask[bits];+	if (state->bit_offset == state->code_len * 8)+		state->bit_offset = 0;++	/*+	 * If the next entry is going to be too big for the ocode size,+	 * then increase it, if possible.+	 */+	if (clear_flg || state->first_free > state->cur_maxcode) {+	       /*+		* Write the whole buffer, because the input side won't+		* discover the size increase until after it has read it.+		*/+		if (state->bit_offset > 0) {+			while (state->bit_offset < state->code_len * 8) {+				ret = output_byte(f, state->bit_buf);+				if (ret != ARCHIVE_OK)+					return ret;+				state->bit_offset += 8;+				state->bit_buf = 0;+			}+		}+		state->bit_buf = 0;+		state->bit_offset = 0;++		if (clear_flg) {+			state->code_len = 9;+			state->cur_maxcode = MAXCODE(state->code_len);+		} else {+			state->code_len++;+			if (state->code_len == 16)+				state->cur_maxcode = state->max_maxcode;+			else+				state->cur_maxcode = MAXCODE(state->code_len);+		}+	}++	return (ARCHIVE_OK);+}++static int+output_flush(struct archive_write_filter *f)+{+	struct private_data *state = f->data;+	int ret;++	/* At EOF, write the rest of the buffer. */+	if (state->bit_offset % 8) {+		state->code_len = (state->bit_offset % 8 + 7) / 8;+		ret = output_byte(f, state->bit_buf);+		if (ret != ARCHIVE_OK)+			return ret;+	}++	return (ARCHIVE_OK);+}++/*+ * Write data to the compressed stream.+ */+static int+archive_compressor_compress_write(struct archive_write_filter *f,+    const void *buff, size_t length)+{+	struct private_data *state = (struct private_data *)f->data;+	int i;+	int ratio;+	int c, disp, ret;+	const unsigned char *bp;++	if (length == 0)+		return ARCHIVE_OK;++	bp = buff;++	if (state->in_count == 0) {+		state->cur_code = *bp++;+		++state->in_count;+		--length;+	}++	while (length--) {+		c = *bp++;+		state->in_count++;+		state->cur_fcode = (c << 16) + state->cur_code;+		i = ((c << HSHIFT) ^ state->cur_code);	/* Xor hashing. */++		if (state->hashtab[i] == state->cur_fcode) {+			state->cur_code = state->codetab[i];+			continue;+		}+		if (state->hashtab[i] < 0)	/* Empty slot. */+			goto nomatch;+		/* Secondary hash (after G. Knott). */+		if (i == 0)+			disp = 1;+		else+			disp = HSIZE - i;+ probe:+		if ((i -= disp) < 0)+			i += HSIZE;++		if (state->hashtab[i] == state->cur_fcode) {+			state->cur_code = state->codetab[i];+			continue;+		}+		if (state->hashtab[i] >= 0)+			goto probe;+ nomatch:+		ret = output_code(f, state->cur_code);+		if (ret != ARCHIVE_OK)+			return ret;+		state->cur_code = c;+		if (state->first_free < state->max_maxcode) {+			state->codetab[i] = state->first_free++;	/* code -> hashtable */+			state->hashtab[i] = state->cur_fcode;+			continue;+		}+		if (state->in_count < state->checkpoint)+			continue;++		state->checkpoint = state->in_count + CHECK_GAP;++		if (state->in_count <= 0x007fffff && state->out_count != 0)+			ratio = (int)(state->in_count * 256 / state->out_count);+		else if ((ratio = (int)(state->out_count / 256)) == 0)+			ratio = 0x7fffffff;+		else+			ratio = (int)(state->in_count / ratio);++		if (ratio > state->compress_ratio)+			state->compress_ratio = ratio;+		else {+			state->compress_ratio = 0;+			memset(state->hashtab, 0xff, sizeof(state->hashtab));+			state->first_free = FIRST;+			ret = output_code(f, CLEAR);+			if (ret != ARCHIVE_OK)+				return ret;+		}+	}++	return (ARCHIVE_OK);+}+++/*+ * Finish the compression...+ */+static int+archive_compressor_compress_close(struct archive_write_filter *f)+{+	struct private_data *state = (struct private_data *)f->data;+	int ret;++	ret = output_code(f, state->cur_code);+	if (ret != ARCHIVE_OK)+		return ret;+	ret = output_flush(f);+	if (ret != ARCHIVE_OK)+		return ret;++	/* Write the last block */+	ret = __archive_write_filter(f->next_filter,+	    state->compressed, state->compressed_offset);+	return (ret);+}++static int+archive_compressor_compress_free(struct archive_write_filter *f)+{+	struct private_data *state = (struct private_data *)f->data;++	free(state->compressed);+	free(state);+	return (ARCHIVE_OK);+}
+ c/archive_write_add_filter_grzip.c view
@@ -0,0 +1,135 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif++#include "archive.h"+#include "archive_write_private.h"++struct write_grzip {+	struct archive_write_program_data *pdata;+};++static int archive_write_grzip_open(struct archive_write_filter *);+static int archive_write_grzip_options(struct archive_write_filter *,+		    const char *, const char *);+static int archive_write_grzip_write(struct archive_write_filter *,+		    const void *, size_t);+static int archive_write_grzip_close(struct archive_write_filter *);+static int archive_write_grzip_free(struct archive_write_filter *);++int+archive_write_add_filter_grzip(struct archive *_a)+{+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);+	struct write_grzip *data;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_grzip");++	data = calloc(1, sizeof(*data));+	if (data == NULL) {+		archive_set_error(_a, ENOMEM, "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	data->pdata = __archive_write_program_allocate("grzip");+	if (data->pdata == NULL) {+		free(data);+		archive_set_error(_a, ENOMEM, "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}++	f->name = "grzip";+	f->code = ARCHIVE_FILTER_GRZIP;+	f->data = data;+	f->open = archive_write_grzip_open;+	f->options = archive_write_grzip_options;+	f->write = archive_write_grzip_write;+	f->close = archive_write_grzip_close;+	f->free = archive_write_grzip_free;++	/* Note: This filter always uses an external program, so we+	 * return "warn" to inform of the fact. */+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external grzip program for grzip compression");+	return (ARCHIVE_WARN);+}++static int+archive_write_grzip_options(struct archive_write_filter *f, const char *key,+    const char *value)+{+	(void)f; /* UNUSED */+	(void)key; /* UNUSED */+	(void)value; /* UNUSED */+	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+archive_write_grzip_open(struct archive_write_filter *f)+{+	struct write_grzip *data = (struct write_grzip *)f->data;++	return __archive_write_program_open(f, data->pdata, "grzip");+}++static int+archive_write_grzip_write(struct archive_write_filter *f,+    const void *buff, size_t length)+{+	struct write_grzip *data = (struct write_grzip *)f->data;++	return __archive_write_program_write(f, data->pdata, buff, length);+}++static int+archive_write_grzip_close(struct archive_write_filter *f)+{+	struct write_grzip *data = (struct write_grzip *)f->data;++	return __archive_write_program_close(f, data->pdata);+}++static int+archive_write_grzip_free(struct archive_write_filter *f)+{+	struct write_grzip *data = (struct write_grzip *)f->data;++	__archive_write_program_free(data->pdata);+	free(data);+	return (ARCHIVE_OK);+}
+ c/archive_write_add_filter_gzip.c view
@@ -0,0 +1,442 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_gzip.c 201081 2009-12-28 02:04:42Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#include <time.h>+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_string.h"+#include "archive_write_private.h"++#if ARCHIVE_VERSION_NUMBER < 4000000+int+archive_write_set_compression_gzip(struct archive *a)+{+	__archive_write_filters_free(a);+	return (archive_write_add_filter_gzip(a));+}+#endif++/* Don't compile this if we don't have zlib. */++struct private_data {+	int		 compression_level;+	int		 timestamp;+#ifdef HAVE_ZLIB_H+	z_stream	 stream;+	int64_t		 total_in;+	unsigned char	*compressed;+	size_t		 compressed_buffer_size;+	unsigned long	 crc;+#else+	struct archive_write_program_data *pdata;+#endif+};++/*+ * Yuck.  zlib.h is not const-correct, so I need this one bit+ * of ugly hackery to convert a const * pointer to a non-const pointer.+ */+#define	SET_NEXT_IN(st,src)					\+	(st)->stream.next_in = (Bytef *)(uintptr_t)(const void *)(src)++static int archive_compressor_gzip_options(struct archive_write_filter *,+		    const char *, const char *);+static int archive_compressor_gzip_open(struct archive_write_filter *);+static int archive_compressor_gzip_write(struct archive_write_filter *,+		    const void *, size_t);+static int archive_compressor_gzip_close(struct archive_write_filter *);+static int archive_compressor_gzip_free(struct archive_write_filter *);+#ifdef HAVE_ZLIB_H+static int drive_compressor(struct archive_write_filter *,+		    struct private_data *, int finishing);+#endif+++/*+ * Add a gzip compression filter to this write handle.+ */+int+archive_write_add_filter_gzip(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);+	struct private_data *data;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_gzip");++	data = calloc(1, sizeof(*data));+	if (data == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	f->data = data;+	f->open = &archive_compressor_gzip_open;+	f->options = &archive_compressor_gzip_options;+	f->close = &archive_compressor_gzip_close;+	f->free = &archive_compressor_gzip_free;+	f->code = ARCHIVE_FILTER_GZIP;+	f->name = "gzip";+#ifdef HAVE_ZLIB_H+	data->compression_level = Z_DEFAULT_COMPRESSION;+	return (ARCHIVE_OK);+#else+	data->pdata = __archive_write_program_allocate("gzip");+	if (data->pdata == NULL) {+		free(data);+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	data->compression_level = 0;+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+	    "Using external gzip program");+	return (ARCHIVE_WARN);+#endif+}++static int+archive_compressor_gzip_free(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++#ifdef HAVE_ZLIB_H+	free(data->compressed);+#else+	__archive_write_program_free(data->pdata);+#endif+	free(data);+	f->data = NULL;+	return (ARCHIVE_OK);+}++/*+ * Set write options.+ */+static int+archive_compressor_gzip_options(struct archive_write_filter *f, const char *key,+    const char *value)+{+	struct private_data *data = (struct private_data *)f->data;++	if (strcmp(key, "compression-level") == 0) {+		if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||+		    value[1] != '\0')+			return (ARCHIVE_WARN);+		data->compression_level = value[0] - '0';+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "timestamp") == 0) {+		data->timestamp = (value == NULL)?-1:1;+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++#ifdef HAVE_ZLIB_H+/*+ * Setup callback.+ */+static int+archive_compressor_gzip_open(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	int ret;++	if (data->compressed == NULL) {+		size_t bs = 65536, bpb;+		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {+			/* Buffer size should be a multiple number of+			 * the of bytes per block for performance. */+			bpb = archive_write_get_bytes_per_block(f->archive);+			if (bpb > bs)+				bs = bpb;+			else if (bpb != 0)+				bs -= bs % bpb;+		}+		data->compressed_buffer_size = bs;+		data->compressed+		    = (unsigned char *)malloc(data->compressed_buffer_size);+		if (data->compressed == NULL) {+			archive_set_error(f->archive, ENOMEM,+			    "Can't allocate data for compression buffer");+			return (ARCHIVE_FATAL);+		}+	}++	data->crc = crc32(0L, NULL, 0);+	data->stream.next_out = data->compressed;+	data->stream.avail_out = (uInt)data->compressed_buffer_size;++	/* Prime output buffer with a gzip header. */+	data->compressed[0] = 0x1f; /* GZip signature bytes */+	data->compressed[1] = 0x8b;+	data->compressed[2] = 0x08; /* "Deflate" compression */+	data->compressed[3] = 0; /* No options */+	if (data->timestamp >= 0) {+		time_t t = time(NULL);+		data->compressed[4] = (uint8_t)(t)&0xff;  /* Timestamp */+		data->compressed[5] = (uint8_t)(t>>8)&0xff;+		data->compressed[6] = (uint8_t)(t>>16)&0xff;+		data->compressed[7] = (uint8_t)(t>>24)&0xff;+	} else+		memset(&data->compressed[4], 0, 4);+    if (data->compression_level == 9)+	    data->compressed[8] = 2;+    else if(data->compression_level == 1)+	    data->compressed[8] = 4;+    else+	    data->compressed[8] = 0;+	data->compressed[9] = 3; /* OS=Unix */+	data->stream.next_out += 10;+	data->stream.avail_out -= 10;++	f->write = archive_compressor_gzip_write;++	/* Initialize compression library. */+	ret = deflateInit2(&(data->stream),+	    data->compression_level,+	    Z_DEFLATED,+	    -15 /* < 0 to suppress zlib header */,+	    8,+	    Z_DEFAULT_STRATEGY);++	if (ret == Z_OK) {+		f->data = data;+		return (ARCHIVE_OK);+	}++	/* Library setup failed: clean up. */+	archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error "+	    "initializing compression library");++	/* Override the error message if we know what really went wrong. */+	switch (ret) {+	case Z_STREAM_ERROR:+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing "+		    "compression library: invalid setup parameter");+		break;+	case Z_MEM_ERROR:+		archive_set_error(f->archive, ENOMEM,+		    "Internal error initializing compression library");+		break;+	case Z_VERSION_ERROR:+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing "+		    "compression library: invalid library version");+		break;+	}++	return (ARCHIVE_FATAL);+}++/*+ * Write data to the compressed stream.+ */+static int+archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,+    size_t length)+{+	struct private_data *data = (struct private_data *)f->data;+	int ret;++	/* Update statistics */+	data->crc = crc32(data->crc, (const Bytef *)buff, (uInt)length);+	data->total_in += length;++	/* Compress input data to output buffer */+	SET_NEXT_IN(data, buff);+	data->stream.avail_in = (uInt)length;+	if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)+		return (ret);++	return (ARCHIVE_OK);+}++/*+ * Finish the compression...+ */+static int+archive_compressor_gzip_close(struct archive_write_filter *f)+{+	unsigned char trailer[8];+	struct private_data *data = (struct private_data *)f->data;+	int ret;++	/* Finish compression cycle */+	ret = drive_compressor(f, data, 1);+	if (ret == ARCHIVE_OK) {+		/* Write the last compressed data. */+		ret = __archive_write_filter(f->next_filter,+		    data->compressed,+		    data->compressed_buffer_size - data->stream.avail_out);+	}+	if (ret == ARCHIVE_OK) {+		/* Build and write out 8-byte trailer. */+		trailer[0] = (uint8_t)(data->crc)&0xff;+		trailer[1] = (uint8_t)(data->crc >> 8)&0xff;+		trailer[2] = (uint8_t)(data->crc >> 16)&0xff;+		trailer[3] = (uint8_t)(data->crc >> 24)&0xff;+		trailer[4] = (uint8_t)(data->total_in)&0xff;+		trailer[5] = (uint8_t)(data->total_in >> 8)&0xff;+		trailer[6] = (uint8_t)(data->total_in >> 16)&0xff;+		trailer[7] = (uint8_t)(data->total_in >> 24)&0xff;+		ret = __archive_write_filter(f->next_filter, trailer, 8);+	}++	switch (deflateEnd(&(data->stream))) {+	case Z_OK:+		break;+	default:+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+		    "Failed to clean up compressor");+		ret = ARCHIVE_FATAL;+	}+	return ret;+}++/*+ * Utility function to push input data through compressor,+ * writing full output blocks as necessary.+ *+ * Note that this handles both the regular write case (finishing ==+ * false) and the end-of-archive case (finishing == true).+ */+static int+drive_compressor(struct archive_write_filter *f,+    struct private_data *data, int finishing)+{+	int ret;++	for (;;) {+		if (data->stream.avail_out == 0) {+			ret = __archive_write_filter(f->next_filter,+			    data->compressed,+			    data->compressed_buffer_size);+			if (ret != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			data->stream.next_out = data->compressed;+			data->stream.avail_out =+			    (uInt)data->compressed_buffer_size;+		}++		/* If there's nothing to do, we're done. */+		if (!finishing && data->stream.avail_in == 0)+			return (ARCHIVE_OK);++		ret = deflate(&(data->stream),+		    finishing ? Z_FINISH : Z_NO_FLUSH );++		switch (ret) {+		case Z_OK:+			/* In non-finishing case, check if compressor+			 * consumed everything */+			if (!finishing && data->stream.avail_in == 0)+				return (ARCHIVE_OK);+			/* In finishing case, this return always means+			 * there's more work */+			break;+		case Z_STREAM_END:+			/* This return can only occur in finishing case. */+			return (ARCHIVE_OK);+		default:+			/* Any other return value indicates an error. */+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+			    "GZip compression failed:"+			    " deflate() call returned status %d",+			    ret);+			return (ARCHIVE_FATAL);+		}+	}+}++#else /* HAVE_ZLIB_H */++static int+archive_compressor_gzip_open(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	struct archive_string as;+	int r;++	archive_string_init(&as);+	archive_strcpy(&as, "gzip");++	/* Specify compression level. */+	if (data->compression_level > 0) {+		archive_strcat(&as, " -");+		archive_strappend_char(&as, '0' + data->compression_level);+	}+	if (data->timestamp < 0)+		/* Do not save timestamp. */+		archive_strcat(&as, " -n");+	else if (data->timestamp > 0)+		/* Save timestamp. */+		archive_strcat(&as, " -N");++	f->write = archive_compressor_gzip_write;+	r = __archive_write_program_open(f, data->pdata, as.s);+	archive_string_free(&as);+	return (r);+}++static int+archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,+    size_t length)+{+	struct private_data *data = (struct private_data *)f->data;++	return __archive_write_program_write(f, data->pdata, buff, length);+}++static int+archive_compressor_gzip_close(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	return __archive_write_program_close(f, data->pdata);+}++#endif /* HAVE_ZLIB_H */
+ c/archive_write_add_filter_lrzip.c view
@@ -0,0 +1,197 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_string.h"+#include "archive_write_private.h"++struct write_lrzip {+	struct archive_write_program_data *pdata;+	int	compression_level;+	enum { lzma = 0, bzip2, gzip, lzo, none, zpaq } compression;+};++static int archive_write_lrzip_open(struct archive_write_filter *);+static int archive_write_lrzip_options(struct archive_write_filter *,+		    const char *, const char *);+static int archive_write_lrzip_write(struct archive_write_filter *,+		    const void *, size_t);+static int archive_write_lrzip_close(struct archive_write_filter *);+static int archive_write_lrzip_free(struct archive_write_filter *);++int+archive_write_add_filter_lrzip(struct archive *_a)+{+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);+	struct write_lrzip *data;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lrzip");++	data = calloc(1, sizeof(*data));+	if (data == NULL) {+		archive_set_error(_a, ENOMEM, "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	data->pdata = __archive_write_program_allocate("lrzip");+	if (data->pdata == NULL) {+		free(data);+		archive_set_error(_a, ENOMEM, "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}++	f->name = "lrzip";+	f->code = ARCHIVE_FILTER_LRZIP;+	f->data = data;+	f->open = archive_write_lrzip_open;+	f->options = archive_write_lrzip_options;+	f->write = archive_write_lrzip_write;+	f->close = archive_write_lrzip_close;+	f->free = archive_write_lrzip_free;++	/* Note: This filter always uses an external program, so we+	 * return "warn" to inform of the fact. */+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external lrzip program for lrzip compression");+	return (ARCHIVE_WARN);+}++static int+archive_write_lrzip_options(struct archive_write_filter *f, const char *key,+    const char *value)+{+	struct write_lrzip *data = (struct write_lrzip *)f->data;++	if (strcmp(key, "compression") == 0) {+		if (value == NULL)+			return (ARCHIVE_WARN);+		else if (strcmp(value, "bzip2") == 0)+			data->compression = bzip2;+		else if (strcmp(value, "gzip") == 0)+			data->compression = gzip;+		else if (strcmp(value, "lzo") == 0)+			data->compression = lzo;+		else if (strcmp(value, "none") == 0)+			data->compression = none;+		else if (strcmp(value, "zpaq") == 0)+			data->compression = zpaq;+		else+			return (ARCHIVE_WARN);+		return (ARCHIVE_OK);+	} else if (strcmp(key, "compression-level") == 0) {+		if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||+		    value[1] != '\0')+			return (ARCHIVE_WARN);+		data->compression_level = value[0] - '0';+		return (ARCHIVE_OK);+	}+	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+archive_write_lrzip_open(struct archive_write_filter *f)+{+	struct write_lrzip *data = (struct write_lrzip *)f->data;+	struct archive_string as;+	int r;++	archive_string_init(&as);+	archive_strcpy(&as, "lrzip -q");++	/* Specify compression type. */+	switch (data->compression) {+	case lzma:/* default compression */+		break;+	case bzip2:+		archive_strcat(&as, " -b");+		break;+	case gzip:+		archive_strcat(&as, " -g");+		break;+	case lzo:+		archive_strcat(&as, " -l");+		break;+	case none:+		archive_strcat(&as, " -n");+		break;+	case zpaq:+		archive_strcat(&as, " -z");+		break;+	}++	/* Specify compression level. */+	if (data->compression_level > 0) {+		archive_strcat(&as, " -L ");+		archive_strappend_char(&as, '0' + data->compression_level);+	}++	r = __archive_write_program_open(f, data->pdata, as.s);+	archive_string_free(&as);+	return (r);+}++static int+archive_write_lrzip_write(struct archive_write_filter *f,+    const void *buff, size_t length)+{+	struct write_lrzip *data = (struct write_lrzip *)f->data;++	return __archive_write_program_write(f, data->pdata, buff, length);+}++static int+archive_write_lrzip_close(struct archive_write_filter *f)+{+	struct write_lrzip *data = (struct write_lrzip *)f->data;++	return __archive_write_program_close(f, data->pdata);+}++static int+archive_write_lrzip_free(struct archive_write_filter *f)+{+	struct write_lrzip *data = (struct write_lrzip *)f->data;++	__archive_write_program_free(data->pdata);+	free(data);+	return (ARCHIVE_OK);+}
+ c/archive_write_add_filter_lz4.c view
@@ -0,0 +1,700 @@+/*-+ * Copyright (c) 2014 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_LZ4_H+#include <lz4.h>+#endif+#ifdef HAVE_LZ4HC_H+#include <lz4hc.h>+#endif++#include "archive.h"+#include "archive_endian.h"+#include "archive_private.h"+#include "archive_write_private.h"+#include "archive_xxhash.h"++#define LZ4_MAGICNUMBER	0x184d2204++struct private_data {+	int		 compression_level;+	unsigned	 header_written:1;+	unsigned	 version_number:1;+	unsigned	 block_independence:1;+	unsigned	 block_checksum:1;+	unsigned	 stream_size:1;+	unsigned	 stream_checksum:1;+	unsigned	 preset_dictionary:1;+	unsigned	 block_maximum_size:3;+#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2+	int64_t		 total_in;+	char		*out;+	char		*out_buffer;+	size_t		 out_buffer_size;+	size_t		 out_block_size;+	char		*in;+	char		*in_buffer_allocated;+	char		*in_buffer;+	size_t		 in_buffer_size;+	size_t		 block_size;++	void		*xxh32_state;+	void		*lz4_stream;+#else+	struct archive_write_program_data *pdata;+#endif+};++static int archive_filter_lz4_close(struct archive_write_filter *);+static int archive_filter_lz4_free(struct archive_write_filter *);+static int archive_filter_lz4_open(struct archive_write_filter *);+static int archive_filter_lz4_options(struct archive_write_filter *,+		    const char *, const char *);+static int archive_filter_lz4_write(struct archive_write_filter *,+		    const void *, size_t);++/*+ * Add a lz4 compression filter to this write handle.+ */+int+archive_write_add_filter_lz4(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);+	struct private_data *data;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4");++	data = calloc(1, sizeof(*data));+	if (data == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}++	/*+	 * Setup default settings.+	 */+	data->compression_level = 1;+	data->version_number = 0x01;+	data->block_independence = 1;+	data->block_checksum = 0;+	data->stream_size = 0;+	data->stream_checksum = 1;+	data->preset_dictionary = 0;+	data->block_maximum_size = 7;++	/*+	 * Setup a filter setting.+	 */+	f->data = data;+	f->options = &archive_filter_lz4_options;+	f->close = &archive_filter_lz4_close;+	f->free = &archive_filter_lz4_free;+	f->open = &archive_filter_lz4_open;+	f->code = ARCHIVE_FILTER_LZ4;+	f->name = "lz4";+#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2+	return (ARCHIVE_OK);+#else+	/*+	 * We don't have lz4 library, and execute external lz4 program+	 * instead.+	 */+	data->pdata = __archive_write_program_allocate("lz4");+	if (data->pdata == NULL) {+		free(data);+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	data->compression_level = 0;+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+	    "Using external lz4 program");+	return (ARCHIVE_WARN);+#endif+}++/*+ * Set write options.+ */+static int+archive_filter_lz4_options(struct archive_write_filter *f,+    const char *key, const char *value)+{+	struct private_data *data = (struct private_data *)f->data;++	if (strcmp(key, "compression-level") == 0) {+		int val;+		if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) ||+		    value[1] != '\0')+			return (ARCHIVE_WARN);++#ifndef HAVE_LZ4HC_H+		if(val >= 3)+		{+			archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,+				"High compression not included in this build");+			return (ARCHIVE_FATAL);+		}+#endif+		data->compression_level = val;+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "stream-checksum") == 0) {+		data->stream_checksum = value != NULL;+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "block-checksum") == 0) {+		data->block_checksum = value != NULL;+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "block-size") == 0) {+		if (value == NULL || !(value[0] >= '4' && value[0] <= '7') ||+		    value[1] != '\0')+			return (ARCHIVE_WARN);+		data->block_maximum_size = value[0] - '0';+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "block-dependence") == 0) {+		data->block_independence = value == NULL;+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2+/* Don't compile this if we don't have liblz4. */++static int drive_compressor(struct archive_write_filter *, const char *,+    size_t);+static int drive_compressor_independence(struct archive_write_filter *,+    const char *, size_t);+static int drive_compressor_dependence(struct archive_write_filter *,+    const char *, size_t);+static int lz4_write_stream_descriptor(struct archive_write_filter *);+static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *,+    size_t);+++/*+ * Setup callback.+ */+static int+archive_filter_lz4_open(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	size_t required_size;+	static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024,+			   4 * 1024 * 1024 };+	size_t pre_block_size;++	if (data->block_maximum_size < 4)+		data->block_size = bkmap[0];+	else+		data->block_size = bkmap[data->block_maximum_size - 4];++	required_size = 4 + 15 + 4 + data->block_size + 4 + 4;+	if (data->out_buffer_size < required_size) {+		size_t bs = required_size, bpb;+		free(data->out_buffer);+		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {+			/* Buffer size should be a multiple number of+			 * the of bytes per block for performance. */+			bpb = archive_write_get_bytes_per_block(f->archive);+			if (bpb > bs)+				bs = bpb;+			else if (bpb != 0) {+				bs += bpb;+				bs -= bs % bpb;+			}+		}+		data->out_block_size = bs;+		bs += required_size;+		data->out_buffer = malloc(bs);+		data->out = data->out_buffer;+		data->out_buffer_size = bs;+	}++	pre_block_size = (data->block_independence)? 0: 64 * 1024;+	if (data->in_buffer_size < data->block_size + pre_block_size) {+		free(data->in_buffer_allocated);+		data->in_buffer_size = data->block_size;+		data->in_buffer_allocated =+		    malloc(data->in_buffer_size + pre_block_size);+		data->in_buffer = data->in_buffer_allocated + pre_block_size;+		if (!data->block_independence && data->compression_level >= 3)+		    data->in_buffer = data->in_buffer_allocated;+		data->in = data->in_buffer;+		data->in_buffer_size = data->block_size;+	}++	if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) {+		archive_set_error(f->archive, ENOMEM,+		    "Can't allocate data for compression buffer");+		return (ARCHIVE_FATAL);+	}++	f->write = archive_filter_lz4_write;++	return (ARCHIVE_OK);+}++/*+ * Write data to the out stream.+ *+ * Returns ARCHIVE_OK if all data written, error otherwise.+ */+static int+archive_filter_lz4_write(struct archive_write_filter *f,+    const void *buff, size_t length)+{+	struct private_data *data = (struct private_data *)f->data;+	int ret = ARCHIVE_OK;+	const char *p;+	size_t remaining;+	ssize_t size;++	/* If we haven't written a stream descriptor, we have to do it first. */+	if (!data->header_written) {+		ret = lz4_write_stream_descriptor(f);+		if (ret != ARCHIVE_OK)+			return (ret);+		data->header_written = 1;+	}++	/* Update statistics */+	data->total_in += length;++	p = (const char *)buff;+	remaining = length;+	while (remaining) {+		size_t l;+		/* Compress input data to output buffer */+		size = lz4_write_one_block(f, p, remaining);+		if (size < ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		l = data->out - data->out_buffer;+		if (l >= data->out_block_size) {+			ret = __archive_write_filter(f->next_filter,+			    data->out_buffer, data->out_block_size);+			l -= data->out_block_size;+			memcpy(data->out_buffer,+			    data->out_buffer + data->out_block_size, l);+			data->out = data->out_buffer + l;+			if (ret < ARCHIVE_WARN)+				break;+		}+		p += size;+		remaining -= size;+	}++	return (ret);+}++/*+ * Finish the compression.+ */+static int+archive_filter_lz4_close(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	int ret;++	/* Finish compression cycle. */+	ret = (int)lz4_write_one_block(f, NULL, 0);+	if (ret >= 0) {+		/*+		 * Write the last block and the end of the stream data.+		 */++		/* Write End Of Stream. */+		memset(data->out, 0, 4); data->out += 4;+		/* Write Stream checksum if needed. */+		if (data->stream_checksum) {+			unsigned int checksum;+			checksum = __archive_xxhash.XXH32_digest(+					data->xxh32_state);+			data->xxh32_state = NULL;+			archive_le32enc(data->out, checksum);+			data->out += 4;+		}+		ret = __archive_write_filter(f->next_filter,+			    data->out_buffer, data->out - data->out_buffer);+	}+	return ret;+}++static int+archive_filter_lz4_free(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	if (data->lz4_stream != NULL) {+#ifdef HAVE_LZ4HC_H+		if (data->compression_level >= 3)+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7+			LZ4_freeStreamHC(data->lz4_stream);+#else+			LZ4_freeHC(data->lz4_stream);+#endif+		else+#endif+#if LZ4_VERSION_MINOR >= 3+			LZ4_freeStream(data->lz4_stream);+#else+			LZ4_free(data->lz4_stream);+#endif+	}+	free(data->out_buffer);+	free(data->in_buffer_allocated);+	free(data->xxh32_state);+	free(data);+	f->data = NULL;+	return (ARCHIVE_OK);+}++static int+lz4_write_stream_descriptor(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	uint8_t *sd;++	sd = (uint8_t *)data->out;+	/* Write Magic Number. */+	archive_le32enc(&sd[0], LZ4_MAGICNUMBER);+	/* FLG */+	sd[4] = (data->version_number << 6)+	      | (data->block_independence << 5)+	      | (data->block_checksum << 4)+	      | (data->stream_size << 3)+	      | (data->stream_checksum << 2)+	      | (data->preset_dictionary << 0);+	/* BD */+	sd[5] = (data->block_maximum_size << 4);+	sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff;+	data->out += 7;+	if (data->stream_checksum)+		data->xxh32_state = __archive_xxhash.XXH32_init(0);+	else+		data->xxh32_state = NULL;+	return (ARCHIVE_OK);+}++static ssize_t+lz4_write_one_block(struct archive_write_filter *f, const char *p,+    size_t length)+{+	struct private_data *data = (struct private_data *)f->data;+	ssize_t r;++	if (p == NULL) {+		/* Compress remaining uncompressed data. */+		if (data->in_buffer == data->in)+			return 0;+		else {+			size_t l = data->in - data->in_buffer;+			r = drive_compressor(f, data->in_buffer, l);+			if (r == ARCHIVE_OK)+				r = (ssize_t)l;+		}+	} else if ((data->block_independence || data->compression_level < 3) &&+	    data->in_buffer == data->in && length >= data->block_size) {+		r = drive_compressor(f, p, data->block_size);+		if (r == ARCHIVE_OK)+			r = (ssize_t)data->block_size;+	} else {+		size_t remaining_size = data->in_buffer_size -+			(data->in - data->in_buffer);+		size_t l = (remaining_size > length)? length: remaining_size;+		memcpy(data->in, p, l);+		data->in += l;+		if (l == remaining_size) {+			r = drive_compressor(f, data->in_buffer,+			    data->block_size);+			if (r == ARCHIVE_OK)+				r = (ssize_t)l;+			data->in = data->in_buffer;+		} else+			r = (ssize_t)l;+	}++	return (r);+}+++/*+ * Utility function to push input data through compressor, writing+ * full output blocks as necessary.+ *+ * Note that this handles both the regular write case (finishing ==+ * false) and the end-of-archive case (finishing == true).+ */+static int+drive_compressor(struct archive_write_filter *f, const char *p, size_t length)+{+	struct private_data *data = (struct private_data *)f->data;++	if (data->stream_checksum)+		__archive_xxhash.XXH32_update(data->xxh32_state,+			p, (int)length);+	if (data->block_independence)+		return drive_compressor_independence(f, p, length);+	else+		return drive_compressor_dependence(f, p, length);+}++static int+drive_compressor_independence(struct archive_write_filter *f, const char *p,+    size_t length)+{+	struct private_data *data = (struct private_data *)f->data;+	unsigned int outsize;++#ifdef HAVE_LZ4HC_H+	if (data->compression_level >= 3)+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7+		outsize = LZ4_compress_HC(p, data->out + 4,+		     (int)length, (int)data->block_size,+		    data->compression_level);+#else+		outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4,+		    (int)length, (int)data->block_size,+		    data->compression_level);+#endif+	else+#endif+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7+		outsize = LZ4_compress_default(p, data->out + 4,+		    (int)length, (int)data->block_size);+#else+		outsize = LZ4_compress_limitedOutput(p, data->out + 4,+		    (int)length, (int)data->block_size);+#endif++	if (outsize) {+		/* The buffer is compressed. */+		archive_le32enc(data->out, outsize);+		data->out += 4;+	} else {+		/* The buffer is not compressed. The compressed size was+		 * bigger than its uncompressed size. */+		archive_le32enc(data->out, length | 0x80000000);+		data->out += 4;+		memcpy(data->out, p, length);+		outsize = length;+	}+	data->out += outsize;+	if (data->block_checksum) {+		unsigned int checksum =+		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);+		archive_le32enc(data->out, checksum);+		data->out += 4;+	}+	return (ARCHIVE_OK);+}++static int+drive_compressor_dependence(struct archive_write_filter *f, const char *p,+    size_t length)+{+	struct private_data *data = (struct private_data *)f->data;+	int outsize;++#define DICT_SIZE	(64 * 1024)+#ifdef HAVE_LZ4HC_H+	if (data->compression_level >= 3) {+		if (data->lz4_stream == NULL) {+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7+			data->lz4_stream = LZ4_createStreamHC();+			LZ4_resetStreamHC(data->lz4_stream, data->compression_level);+#else+			data->lz4_stream =+			    LZ4_createHC(data->in_buffer_allocated);+#endif+			if (data->lz4_stream == NULL) {+				archive_set_error(f->archive, ENOMEM,+				    "Can't allocate data for compression"+				    " buffer");+				return (ARCHIVE_FATAL);+			}+		}+		else+			LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);++#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7+		outsize = LZ4_compress_HC_continue(+		    data->lz4_stream, p, data->out + 4, (int)length,+		    (int)data->block_size);+#else+		outsize = LZ4_compressHC2_limitedOutput_continue(+		    data->lz4_stream, p, data->out + 4, (int)length,+		    (int)data->block_size, data->compression_level);+#endif+	} else+#endif+	{+		if (data->lz4_stream == NULL) {+			data->lz4_stream = LZ4_createStream();+			if (data->lz4_stream == NULL) {+				archive_set_error(f->archive, ENOMEM,+				    "Can't allocate data for compression"+				    " buffer");+				return (ARCHIVE_FATAL);+			}+		}+		else+			LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);++#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7+		outsize = LZ4_compress_fast_continue(+		    data->lz4_stream, p, data->out + 4, (int)length,+		    (int)data->block_size, 1);+#else+		outsize = LZ4_compress_limitedOutput_continue(+		    data->lz4_stream, p, data->out + 4, (int)length,+		    (int)data->block_size);+#endif+	}++	if (outsize) {+		/* The buffer is compressed. */+		archive_le32enc(data->out, outsize);+		data->out += 4;+	} else {+		/* The buffer is not compressed. The compressed size was+		 * bigger than its uncompressed size. */+		archive_le32enc(data->out, length | 0x80000000);+		data->out += 4;+		memcpy(data->out, p, length);+		outsize = length;+	}+	data->out += outsize;+	if (data->block_checksum) {+		unsigned int checksum =+		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);+		archive_le32enc(data->out, checksum);+		data->out += 4;+	}++	if (length == data->block_size) {+#ifdef HAVE_LZ4HC_H+		if (data->compression_level >= 3) {+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7+			LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);+#else+			LZ4_slideInputBufferHC(data->lz4_stream);+#endif+			data->in_buffer = data->in_buffer_allocated + DICT_SIZE;+		}+		else+#endif+			LZ4_saveDict(data->lz4_stream,+			    data->in_buffer_allocated, DICT_SIZE);+#undef DICT_SIZE+	}+	return (ARCHIVE_OK);+}++#else /* HAVE_LIBLZ4 */++static int+archive_filter_lz4_open(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	struct archive_string as;+	int r;++	archive_string_init(&as);+	archive_strcpy(&as, "lz4 -z -q -q");++	/* Specify a compression level. */+	if (data->compression_level > 0) {+		archive_strcat(&as, " -");+		archive_strappend_char(&as, '0' + data->compression_level);+	}+	/* Specify a block size. */+	archive_strcat(&as, " -B");+	archive_strappend_char(&as, '0' + data->block_maximum_size);++	if (data->block_checksum)+		archive_strcat(&as, " -BX");+	if (data->stream_checksum == 0)+		archive_strcat(&as, " --no-frame-crc");+	if (data->block_independence == 0)+		archive_strcat(&as, " -BD");++	f->write = archive_filter_lz4_write;++	r = __archive_write_program_open(f, data->pdata, as.s);+	archive_string_free(&as);+	return (r);+}++static int+archive_filter_lz4_write(struct archive_write_filter *f, const void *buff,+    size_t length)+{+	struct private_data *data = (struct private_data *)f->data;++	return __archive_write_program_write(f, data->pdata, buff, length);+}++static int+archive_filter_lz4_close(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	return __archive_write_program_close(f, data->pdata);+}++static int+archive_filter_lz4_free(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	__archive_write_program_free(data->pdata);+	free(data);+	return (ARCHIVE_OK);+}++#endif /* HAVE_LIBLZ4 */
+ c/archive_write_add_filter_lzop.c view
@@ -0,0 +1,478 @@+/*-+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");+//#undef HAVE_LZO_LZOCONF_H+//#undef HAVE_LZO_LZO1X_H++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#include <time.h>+#ifdef HAVE_LZO_LZOCONF_H+#include <lzo/lzoconf.h>+#endif+#ifdef HAVE_LZO_LZO1X_H+#include <lzo/lzo1x.h>+#endif++#include "archive.h"+#include "archive_string.h"+#include "archive_endian.h"+#include "archive_write_private.h"++enum lzo_method {+	METHOD_LZO1X_1 = 1,+	METHOD_LZO1X_1_15 = 2,+	METHOD_LZO1X_999 = 3+};+struct write_lzop {+	int compression_level;+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)+	unsigned char	*uncompressed;+	size_t		 uncompressed_buffer_size;+	size_t		 uncompressed_avail_bytes;+	unsigned char	*compressed;+	size_t		 compressed_buffer_size;+	enum lzo_method	 method;+	unsigned char	 level;+	lzo_voidp	 work_buffer;+	lzo_uint32	 work_buffer_size;+	char		 header_written;+#else+	struct archive_write_program_data *pdata;+#endif+};++static int archive_write_lzop_open(struct archive_write_filter *);+static int archive_write_lzop_options(struct archive_write_filter *,+		    const char *, const char *);+static int archive_write_lzop_write(struct archive_write_filter *,+		    const void *, size_t);+static int archive_write_lzop_close(struct archive_write_filter *);+static int archive_write_lzop_free(struct archive_write_filter *);++#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)+/* Maximum block size. */+#define BLOCK_SIZE			(256 * 1024)+/* Block information is composed of uncompressed size(4 bytes),+ * compressed size(4 bytes) and the checksum of uncompressed data(4 bytes)+ * in this lzop writer. */+#define BLOCK_INfO_SIZE			12++#define HEADER_VERSION			9+#define HEADER_LIBVERSION		11+#define HEADER_METHOD			15+#define HEADER_LEVEL			16+#define HEADER_MTIME_LOW		25+#define HEADER_MTIME_HIGH		29+#define HEADER_H_CHECKSUM		34++/*+ * Header template.+ */+static const unsigned char header[] = {+	/* LZOP Magic code 9 bytes */+	0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a,+	/* LZOP utility version(fake data) 2 bytes */+	0x10, 0x30,+	/* LZO library version 2 bytes */+	0x09, 0x40,+	/* Minimum required LZO library version 2 bytes */+	0x09, 0x40,+	/* Method */+	1,+	/* Level */+	5,+	/* Flags 4 bytes+	 *  -OS Unix+	 *  -Stdout+	 *  -Stdin+	 *  -Adler32 used for uncompressed data 4 bytes */+	0x03, 0x00, 0x00, 0x0d,+	/* Mode (AE_IFREG | 0644) 4 bytes */+	0x00, 0x00, 0x81, 0xa4,+	/* Mtime low 4 bytes */+	0x00, 0x00, 0x00, 0x00,+	/* Mtime high 4 bytes */+	0x00, 0x00, 0x00, 0x00,+	/* Filename length */+	0x00,+	/* Header checksum 4 bytes */+	0x00, 0x00, 0x00, 0x00,+};+#endif++int+archive_write_add_filter_lzop(struct archive *_a)+{+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);+	struct write_lzop *data;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lzop");++	data = calloc(1, sizeof(*data));+	if (data == NULL) {+		archive_set_error(_a, ENOMEM, "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}++	f->name = "lzop";+	f->code = ARCHIVE_FILTER_LZOP;+	f->data = data;+	f->open = archive_write_lzop_open;+	f->options = archive_write_lzop_options;+	f->write = archive_write_lzop_write;+	f->close = archive_write_lzop_close;+	f->free = archive_write_lzop_free;+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)+	if (lzo_init() != LZO_E_OK) {+		free(data);+		archive_set_error(_a, ARCHIVE_ERRNO_MISC,+		    "lzo_init(type check) failed");+		return (ARCHIVE_FATAL);+	}+	if (lzo_version() < 0x940) {+		free(data);+		archive_set_error(_a, ARCHIVE_ERRNO_MISC,+		    "liblzo library is too old(%s < 0.940)",+		    lzo_version_string());+		return (ARCHIVE_FATAL);+	}+	data->compression_level = 5;+	return (ARCHIVE_OK);+#else+	data->pdata = __archive_write_program_allocate("lzop");+	if (data->pdata == NULL) {+		free(data);+		archive_set_error(_a, ENOMEM, "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	data->compression_level = 0;+	/* Note: We return "warn" to inform of using an external lzop+	 * program. */+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,+	    "Using external lzop program for lzop compression");+	return (ARCHIVE_WARN);+#endif+}++static int+archive_write_lzop_free(struct archive_write_filter *f)+{+	struct write_lzop *data = (struct write_lzop *)f->data;++#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)+	free(data->uncompressed);+	free(data->compressed);+	free(data->work_buffer);+#else+	__archive_write_program_free(data->pdata);+#endif+	free(data);+	return (ARCHIVE_OK);+}++static int+archive_write_lzop_options(struct archive_write_filter *f, const char *key,+    const char *value)+{+	struct write_lzop *data = (struct write_lzop *)f->data;++	if (strcmp(key, "compression-level") == 0) {+		if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||+		    value[1] != '\0')+			return (ARCHIVE_WARN);+		data->compression_level = value[0] - '0';+		return (ARCHIVE_OK);+	}+	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)+static int+archive_write_lzop_open(struct archive_write_filter *f)+{+	struct write_lzop *data = (struct write_lzop *)f->data;++	switch (data->compression_level) {+	case 1:+		data->method = METHOD_LZO1X_1_15; data->level = 1; break;+	default:+	case 2: case 3: case 4: case 5: case 6:+		data->method = METHOD_LZO1X_1; data->level = 5; break;+	case 7:+		data->method = METHOD_LZO1X_999; data->level = 7; break;+	case 8:+		data->method = METHOD_LZO1X_999; data->level = 8; break;+	case 9:+		data->method = METHOD_LZO1X_999; data->level = 9; break;+	}+	switch (data->method) {+	case METHOD_LZO1X_1:+		data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break;+	case METHOD_LZO1X_1_15:+		data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break;+	case METHOD_LZO1X_999:+		data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break;+	}+	if (data->work_buffer == NULL) {+		data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size);+		if (data->work_buffer == NULL) {+			archive_set_error(f->archive, ENOMEM,+			    "Can't allocate data for compression buffer");+			return (ARCHIVE_FATAL);+		}+	}+	if (data->compressed == NULL) {+		data->compressed_buffer_size = sizeof(header) ++		    BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3;+		data->compressed = (unsigned char *)+		    malloc(data->compressed_buffer_size);+		if (data->compressed == NULL) {+			archive_set_error(f->archive, ENOMEM,+			    "Can't allocate data for compression buffer");+			return (ARCHIVE_FATAL);+		}+	}+	if (data->uncompressed == NULL) {+		data->uncompressed_buffer_size = BLOCK_SIZE;+		data->uncompressed = (unsigned char *)+		    malloc(data->uncompressed_buffer_size);+		if (data->uncompressed == NULL) {+			archive_set_error(f->archive, ENOMEM,+			    "Can't allocate data for compression buffer");+			return (ARCHIVE_FATAL);+		}+		data->uncompressed_avail_bytes = BLOCK_SIZE;+	}+	return (ARCHIVE_OK);+}++static int+make_header(struct archive_write_filter *f)+{+	struct write_lzop *data = (struct write_lzop *)f->data;+	int64_t t;+	uint32_t checksum;++	memcpy(data->compressed, header, sizeof(header));+	/* Overwrite library version. */+	data->compressed[HEADER_LIBVERSION] = (unsigned char )+	    (lzo_version() >> 8) & 0xff;+	data->compressed[HEADER_LIBVERSION + 1] = (unsigned char )+	    lzo_version() & 0xff;+	/* Overwrite method and level. */+	data->compressed[HEADER_METHOD] = (unsigned char)data->method;+	data->compressed[HEADER_LEVEL] = data->level;+	/* Overwrite mtime with current time. */+	t = (int64_t)time(NULL);+	archive_be32enc(&data->compressed[HEADER_MTIME_LOW],+	    (uint32_t)(t & 0xffffffff));+	archive_be32enc(&data->compressed[HEADER_MTIME_HIGH],+	    (uint32_t)((t >> 32) & 0xffffffff));+	/* Overwrite header checksum with calculated value. */+	checksum = lzo_adler32(1, data->compressed + HEADER_VERSION,+			(lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION));+	archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum);+	return (sizeof(header));+}++static int+drive_compressor(struct archive_write_filter *f)+{+	struct write_lzop *data = (struct write_lzop *)f->data;+	unsigned char *p;+	const int block_info_bytes = 12;+	int header_bytes, r;+	lzo_uint usize, csize;+	uint32_t checksum;++	if (!data->header_written) {+		header_bytes = make_header(f);+		data->header_written = 1;+	} else+		header_bytes = 0;+	p = data->compressed;++	usize = (lzo_uint)+	    (data->uncompressed_buffer_size - data->uncompressed_avail_bytes);+	csize = 0;+	switch (data->method) {+	default:+	case METHOD_LZO1X_1:+		r = lzo1x_1_compress(data->uncompressed, usize,+			p + header_bytes + block_info_bytes, &csize,+			data->work_buffer);+		break;+	case METHOD_LZO1X_1_15:+		r = lzo1x_1_15_compress(data->uncompressed, usize,+			p + header_bytes + block_info_bytes, &csize,+			data->work_buffer);+		break;+	case METHOD_LZO1X_999:+		r = lzo1x_999_compress_level(data->uncompressed, usize,+			p + header_bytes + block_info_bytes, &csize,+			data->work_buffer, NULL, 0, 0, data->level);+		break;+	}+	if (r != LZO_E_OK) {+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+		    "Lzop compression failed: returned status %d", r);+		return (ARCHIVE_FATAL);+	}++	/* Store uncompressed size. */+	archive_be32enc(p + header_bytes, (uint32_t)usize);+	/* Store the checksum of the uncompressed data. */+	checksum = lzo_adler32(1, data->uncompressed, usize);+	archive_be32enc(p + header_bytes + 8, checksum);++	if (csize < usize) {+		/* Store compressed size. */+		archive_be32enc(p + header_bytes + 4, (uint32_t)csize);+		r = __archive_write_filter(f->next_filter, data->compressed,+			header_bytes + block_info_bytes + csize);+	} else {+		/*+		 * This case, we output uncompressed data instead.+		 */+		/* Store uncompressed size as compressed size. */+		archive_be32enc(p + header_bytes + 4, (uint32_t)usize);+		r = __archive_write_filter(f->next_filter, data->compressed,+			header_bytes + block_info_bytes);+		if (r != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		r = __archive_write_filter(f->next_filter, data->uncompressed,+			usize);+	}++	if (r != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	return (ARCHIVE_OK);+}++static int+archive_write_lzop_write(struct archive_write_filter *f,+    const void *buff, size_t length)+{+	struct write_lzop *data = (struct write_lzop *)f->data;+	const char *p = buff;+	int r;++	do {+		if (data->uncompressed_avail_bytes > length) {+			memcpy(data->uncompressed+				+ data->uncompressed_buffer_size+				- data->uncompressed_avail_bytes,+			    p, length);+			data->uncompressed_avail_bytes -= length;+			return (ARCHIVE_OK);+		}++		memcpy(data->uncompressed + data->uncompressed_buffer_size+			- data->uncompressed_avail_bytes,+		    p, data->uncompressed_avail_bytes);+		length -= data->uncompressed_avail_bytes;+		p += data->uncompressed_avail_bytes;+		data->uncompressed_avail_bytes = 0;++		r = drive_compressor(f);+		if (r != ARCHIVE_OK) return (r);+		data->uncompressed_avail_bytes = BLOCK_SIZE;+	} while (length);++	return (ARCHIVE_OK);+}++static int+archive_write_lzop_close(struct archive_write_filter *f)+{+	struct write_lzop *data = (struct write_lzop *)f->data;+	const uint32_t endmark = 0;+	int r;++	if (data->uncompressed_avail_bytes < BLOCK_SIZE) {+		/* Compress and output remaining data. */+		r = drive_compressor(f);+		if (r != ARCHIVE_OK)+			return (r);+	}+	/* Write a zero uncompressed size as the end mark of the series of+	 * compressed block. */+	return __archive_write_filter(f->next_filter, &endmark, sizeof(endmark));+}++#else+static int+archive_write_lzop_open(struct archive_write_filter *f)+{+	struct write_lzop *data = (struct write_lzop *)f->data;+	struct archive_string as;+	int r;++	archive_string_init(&as);+	archive_strcpy(&as, "lzop");+	/* Specify compression level. */+	if (data->compression_level > 0) {+		archive_strappend_char(&as, ' ');+		archive_strappend_char(&as, '-');+		archive_strappend_char(&as, '0' + data->compression_level);+	}++	r = __archive_write_program_open(f, data->pdata, as.s);+	archive_string_free(&as);+	return (r);+}++static int+archive_write_lzop_write(struct archive_write_filter *f,+    const void *buff, size_t length)+{+	struct write_lzop *data = (struct write_lzop *)f->data;++	return __archive_write_program_write(f, data->pdata, buff, length);+}++static int+archive_write_lzop_close(struct archive_write_filter *f)+{+	struct write_lzop *data = (struct write_lzop *)f->data;++	return __archive_write_program_close(f, data->pdata);+}+#endif
+ c/archive_write_add_filter_none.c view
@@ -0,0 +1,43 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_none.c 201080 2009-12-28 02:03:54Z kientzle $");++#include "archive.h"++int+archive_write_set_compression_none(struct archive *a)+{+	(void)a; /* UNUSED */+	return (ARCHIVE_OK);+}++int+archive_write_add_filter_none(struct archive *a)+{+	(void)a; /* UNUSED */+	return (ARCHIVE_OK);+}
+ c/archive_write_add_filter_program.c view
@@ -0,0 +1,391 @@+/*-+ * Copyright (c) 2007 Joerg Sonnenberger+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $");++#ifdef HAVE_SYS_WAIT_H+#  include <sys/wait.h>+#endif+#ifdef HAVE_ERRNO_H+#  include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#  include <fcntl.h>+#endif+#ifdef HAVE_STDLIB_H+#  include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#  include <string.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_string.h"+#include "archive_write_private.h"+#include "filter_fork.h"++#if ARCHIVE_VERSION_NUMBER < 4000000+int+archive_write_set_compression_program(struct archive *a, const char *cmd)+{+	__archive_write_filters_free(a);+	return (archive_write_add_filter_program(a, cmd));+}+#endif++struct archive_write_program_data {+#if defined(_WIN32) && !defined(__CYGWIN__)+	HANDLE		 child;+#else+	pid_t		 child;+#endif+	int		 child_stdin, child_stdout;++	char		*child_buf;+	size_t		 child_buf_len, child_buf_avail;+	char		*program_name;+};++struct private_data {+	struct archive_write_program_data *pdata;+	struct archive_string description;+	char		*cmd;+};++static int archive_compressor_program_open(struct archive_write_filter *);+static int archive_compressor_program_write(struct archive_write_filter *,+		    const void *, size_t);+static int archive_compressor_program_close(struct archive_write_filter *);+static int archive_compressor_program_free(struct archive_write_filter *);++/*+ * Add a filter to this write handle that passes all data through an+ * external program.+ */+int+archive_write_add_filter_program(struct archive *_a, const char *cmd)+{+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);+	struct private_data *data;+	static const char prefix[] = "Program: ";++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_program");++	f->data = calloc(1, sizeof(*data));+	if (f->data == NULL)+		goto memerr;+	data = (struct private_data *)f->data;++	data->cmd = strdup(cmd);+	if (data->cmd == NULL)+		goto memerr;++	data->pdata = __archive_write_program_allocate(cmd);+	if (data->pdata == NULL)+		goto memerr;++	/* Make up a description string. */+	if (archive_string_ensure(&data->description,+	    strlen(prefix) + strlen(cmd) + 1) == NULL)+		goto memerr;+	archive_strcpy(&data->description, prefix);+	archive_strcat(&data->description, cmd);++	f->name = data->description.s;+	f->code = ARCHIVE_FILTER_PROGRAM;+	f->open = archive_compressor_program_open;+	f->write = archive_compressor_program_write;+	f->close = archive_compressor_program_close;+	f->free = archive_compressor_program_free;+	return (ARCHIVE_OK);+memerr:+	archive_compressor_program_free(f);+	archive_set_error(_a, ENOMEM,+	    "Can't allocate memory for filter program");+	return (ARCHIVE_FATAL);+}++static int+archive_compressor_program_open(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	return __archive_write_program_open(f, data->pdata, data->cmd);+}++static int+archive_compressor_program_write(struct archive_write_filter *f,+    const void *buff, size_t length)+{+	struct private_data *data = (struct private_data *)f->data;++	return __archive_write_program_write(f, data->pdata, buff, length);+}++static int+archive_compressor_program_close(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	return __archive_write_program_close(f, data->pdata);+}++static int+archive_compressor_program_free(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	if (data) {+		free(data->cmd);+		archive_string_free(&data->description);+		__archive_write_program_free(data->pdata);+		free(data);+		f->data = NULL;+	}+	return (ARCHIVE_OK);+}++/*+ * Allocate resources for executing an external program.+ */+struct archive_write_program_data *+__archive_write_program_allocate(const char *program)+{+	struct archive_write_program_data *data;++	data = calloc(1, sizeof(struct archive_write_program_data));+	if (data == NULL)+		return (data);+	data->child_stdin = -1;+	data->child_stdout = -1;+	data->program_name = strdup(program);+	return (data);+}++/*+ * Release the resources.+ */+int+__archive_write_program_free(struct archive_write_program_data *data)+{++	if (data) {+		free(data->program_name);+		free(data->child_buf);+		free(data);+	}+	return (ARCHIVE_OK);+}++int+__archive_write_program_open(struct archive_write_filter *f,+    struct archive_write_program_data *data, const char *cmd)+{+	int ret;++	if (data->child_buf == NULL) {+		data->child_buf_len = 65536;+		data->child_buf_avail = 0;+		data->child_buf = malloc(data->child_buf_len);++		if (data->child_buf == NULL) {+			archive_set_error(f->archive, ENOMEM,+			    "Can't allocate compression buffer");+			return (ARCHIVE_FATAL);+		}+	}++	ret = __archive_create_child(cmd, &data->child_stdin,+		    &data->child_stdout, &data->child);+	if (ret != ARCHIVE_OK) {+		archive_set_error(f->archive, EINVAL,+		    "Can't launch external program: %s", cmd);+		return (ARCHIVE_FATAL);+	}+	return (ARCHIVE_OK);+}++static ssize_t+child_write(struct archive_write_filter *f,+    struct archive_write_program_data *data, const char *buf, size_t buf_len)+{+	ssize_t ret;++	if (data->child_stdin == -1)+		return (-1);++	if (buf_len == 0)+		return (-1);++	for (;;) {+		do {+			ret = write(data->child_stdin, buf, buf_len);+		} while (ret == -1 && errno == EINTR);++		if (ret > 0)+			return (ret);+		if (ret == 0) {+			close(data->child_stdin);+			data->child_stdin = -1;+			fcntl(data->child_stdout, F_SETFL, 0);+			return (0);+		}+		if (ret == -1 && errno != EAGAIN)+			return (-1);++		if (data->child_stdout == -1) {+			fcntl(data->child_stdin, F_SETFL, 0);+			__archive_check_child(data->child_stdin,+				data->child_stdout);+			continue;+		}++		do {+			ret = read(data->child_stdout,+			    data->child_buf + data->child_buf_avail,+			    data->child_buf_len - data->child_buf_avail);+		} while (ret == -1 && errno == EINTR);++		if (ret == 0 || (ret == -1 && errno == EPIPE)) {+			close(data->child_stdout);+			data->child_stdout = -1;+			fcntl(data->child_stdin, F_SETFL, 0);+			continue;+		}+		if (ret == -1 && errno == EAGAIN) {+			__archive_check_child(data->child_stdin,+				data->child_stdout);+			continue;+		}+		if (ret == -1)+			return (-1);++		data->child_buf_avail += ret;++		ret = __archive_write_filter(f->next_filter,+		    data->child_buf, data->child_buf_avail);+		if (ret != ARCHIVE_OK)+			return (-1);+		data->child_buf_avail = 0;+	}+}++/*+ * Write data to the filter stream.+ */+int+__archive_write_program_write(struct archive_write_filter *f,+    struct archive_write_program_data *data, const void *buff, size_t length)+{+	ssize_t ret;+	const char *buf;++	if (data->child == 0)+		return (ARCHIVE_OK);++	buf = buff;+	while (length > 0) {+		ret = child_write(f, data, buf, length);+		if (ret == -1 || ret == 0) {+			archive_set_error(f->archive, EIO,+			    "Can't write to program: %s", data->program_name);+			return (ARCHIVE_FATAL);+		}+		length -= ret;+		buf += ret;+	}+	return (ARCHIVE_OK);+}++/*+ * Finish the filtering...+ */+int+__archive_write_program_close(struct archive_write_filter *f,+    struct archive_write_program_data *data)+{+	int ret, status;+	ssize_t bytes_read;++	if (data->child == 0)+		return ARCHIVE_OK;++	ret = 0;+	close(data->child_stdin);+	data->child_stdin = -1;+	fcntl(data->child_stdout, F_SETFL, 0);++	for (;;) {+		do {+			bytes_read = read(data->child_stdout,+			    data->child_buf + data->child_buf_avail,+			    data->child_buf_len - data->child_buf_avail);+		} while (bytes_read == -1 && errno == EINTR);++		if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))+			break;++		if (bytes_read == -1) {+			archive_set_error(f->archive, errno,+			    "Error reading from program: %s", data->program_name);+			ret = ARCHIVE_FATAL;+			goto cleanup;+		}+		data->child_buf_avail += bytes_read;++		ret = __archive_write_filter(f->next_filter,+		    data->child_buf, data->child_buf_avail);+		if (ret != ARCHIVE_OK) {+			ret = ARCHIVE_FATAL;+			goto cleanup;+		}+		data->child_buf_avail = 0;+	}++cleanup:+	/* Shut down the child. */+	if (data->child_stdin != -1)+		close(data->child_stdin);+	if (data->child_stdout != -1)+		close(data->child_stdout);+	while (waitpid(data->child, &status, 0) == -1 && errno == EINTR)+		continue;+#if defined(_WIN32) && !defined(__CYGWIN__)+	CloseHandle(data->child);+#endif+	data->child = 0;++	if (status != 0) {+		archive_set_error(f->archive, EIO,+		    "Error closing program: %s", data->program_name);+		ret = ARCHIVE_FATAL;+	}+	return ret;+}+
+ c/archive_write_add_filter_uuencode.c view
@@ -0,0 +1,295 @@+/*-+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_string.h"+#include "archive_write_private.h"++#define LBYTES 45++struct private_uuencode {+	int			mode;+	struct archive_string	name;+	struct archive_string	encoded_buff;+	size_t			bs;+	size_t			hold_len;+	unsigned char		hold[LBYTES];+};++static int archive_filter_uuencode_options(struct archive_write_filter *,+    const char *, const char *);+static int archive_filter_uuencode_open(struct archive_write_filter *);+static int archive_filter_uuencode_write(struct archive_write_filter *,+    const void *, size_t);+static int archive_filter_uuencode_close(struct archive_write_filter *);+static int archive_filter_uuencode_free(struct archive_write_filter *);+static void uu_encode(struct archive_string *, const unsigned char *, size_t);+static int64_t atol8(const char *, size_t);++/*+ * Add a compress filter to this write handle.+ */+int+archive_write_add_filter_uuencode(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);+	struct private_uuencode *state;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");++	state = (struct private_uuencode *)calloc(1, sizeof(*state));+	if (state == NULL) {+		archive_set_error(f->archive, ENOMEM,+		    "Can't allocate data for uuencode filter");+		return (ARCHIVE_FATAL);+	}+	archive_strcpy(&state->name, "-");+	state->mode = 0644;++	f->data = state;+	f->name = "uuencode";+	f->code = ARCHIVE_FILTER_UU;+	f->open = archive_filter_uuencode_open;+	f->options = archive_filter_uuencode_options;+	f->write = archive_filter_uuencode_write;+	f->close = archive_filter_uuencode_close;+	f->free = archive_filter_uuencode_free;++	return (ARCHIVE_OK);+}++/*+ * Set write options.+ */+static int+archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,+    const char *value)+{+	struct private_uuencode *state = (struct private_uuencode *)f->data;++	if (strcmp(key, "mode") == 0) {+		if (value == NULL) {+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+			    "mode option requires octal digits");+			return (ARCHIVE_FAILED);+		}+		state->mode = (int)atol8(value, strlen(value)) & 0777;+		return (ARCHIVE_OK);+	} else if (strcmp(key, "name") == 0) {+		if (value == NULL) {+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+			    "name option requires a string");+			return (ARCHIVE_FAILED);+		}+		archive_strcpy(&state->name, value);+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++/*+ * Setup callback.+ */+static int+archive_filter_uuencode_open(struct archive_write_filter *f)+{+	struct private_uuencode *state = (struct private_uuencode *)f->data;+	size_t bs = 65536, bpb;++	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {+		/* Buffer size should be a multiple number of the of bytes+		 * per block for performance. */+		bpb = archive_write_get_bytes_per_block(f->archive);+		if (bpb > bs)+			bs = bpb;+		else if (bpb != 0)+			bs -= bs % bpb;+	}++	state->bs = bs;+	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {+		archive_set_error(f->archive, ENOMEM,+		    "Can't allocate data for uuencode buffer");+		return (ARCHIVE_FATAL);+	}++	archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",+	    state->mode, state->name.s);++	f->data = state;+	return (0);+}++static void+uu_encode(struct archive_string *as, const unsigned char *p, size_t len)+{+	int c;++	c = (int)len;+	archive_strappend_char(as, c?c + 0x20:'`');+	for (; len >= 3; p += 3, len -= 3) {+		c = p[0] >> 2;+		archive_strappend_char(as, c?c + 0x20:'`');+		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);+		archive_strappend_char(as, c?c + 0x20:'`');+		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);+		archive_strappend_char(as, c?c + 0x20:'`');+		c = p[2] & 0x3f;+		archive_strappend_char(as, c?c + 0x20:'`');+	}+	if (len > 0) {+		c = p[0] >> 2;+		archive_strappend_char(as, c?c + 0x20:'`');+		c = (p[0] & 0x03) << 4;+		if (len == 1) {+			archive_strappend_char(as, c?c + 0x20:'`');+			archive_strappend_char(as, '`');+			archive_strappend_char(as, '`');+		} else {+			c |= (p[1] & 0xf0) >> 4;+			archive_strappend_char(as, c?c + 0x20:'`');+			c = (p[1] & 0x0f) << 2;+			archive_strappend_char(as, c?c + 0x20:'`');+			archive_strappend_char(as, '`');+		}+	}+	archive_strappend_char(as, '\n');+}++/*+ * Write data to the encoded stream.+ */+static int+archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,+    size_t length)+{+	struct private_uuencode *state = (struct private_uuencode *)f->data;+	const unsigned char *p = buff;+	int ret = ARCHIVE_OK;++	if (length == 0)+		return (ret);++	if (state->hold_len) {+		while (state->hold_len < LBYTES && length > 0) {+			state->hold[state->hold_len++] = *p++;+			length--;+		}+		if (state->hold_len < LBYTES)+			return (ret);+		uu_encode(&state->encoded_buff, state->hold, LBYTES);+		state->hold_len = 0;+	}++	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)+		uu_encode(&state->encoded_buff, p, LBYTES);++	/* Save remaining bytes. */+	if (length > 0) {+		memcpy(state->hold, p, length);+		state->hold_len = length;+	}+	while (archive_strlen(&state->encoded_buff) >= state->bs) {+		ret = __archive_write_filter(f->next_filter,+		    state->encoded_buff.s, state->bs);+		memmove(state->encoded_buff.s,+		    state->encoded_buff.s + state->bs,+		    state->encoded_buff.length - state->bs);+		state->encoded_buff.length -= state->bs;+	}++	return (ret);+}+++/*+ * Finish the compression...+ */+static int+archive_filter_uuencode_close(struct archive_write_filter *f)+{+	struct private_uuencode *state = (struct private_uuencode *)f->data;++	/* Flush remaining bytes. */+	if (state->hold_len != 0)+		uu_encode(&state->encoded_buff, state->hold, state->hold_len);+	archive_string_sprintf(&state->encoded_buff, "`\nend\n");+	/* Write the last block */+	archive_write_set_bytes_in_last_block(f->archive, 1);+	return __archive_write_filter(f->next_filter,+	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));+}++static int+archive_filter_uuencode_free(struct archive_write_filter *f)+{+	struct private_uuencode *state = (struct private_uuencode *)f->data;++	archive_string_free(&state->name);+	archive_string_free(&state->encoded_buff);+	free(state);+	return (ARCHIVE_OK);+}++static int64_t+atol8(const char *p, size_t char_cnt)+{+	int64_t l;+	int digit;+        +	l = 0;+	while (char_cnt-- > 0) {+		if (*p >= '0' && *p <= '7')+			digit = *p - '0';+		else+			break;+		p++;+		l <<= 3;+		l |= digit;+	}+	return (l);+}+
+ c/archive_write_add_filter_xz.c view
@@ -0,0 +1,545 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_xz.c 201108 2009-12-28 03:28:21Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#include <time.h>+#ifdef HAVE_LZMA_H+#include <lzma.h>+#endif++#include "archive.h"+#include "archive_endian.h"+#include "archive_private.h"+#include "archive_write_private.h"++#if ARCHIVE_VERSION_NUMBER < 4000000+int+archive_write_set_compression_lzip(struct archive *a)+{+	__archive_write_filters_free(a);+	return (archive_write_add_filter_lzip(a));+}++int+archive_write_set_compression_lzma(struct archive *a)+{+	__archive_write_filters_free(a);+	return (archive_write_add_filter_lzma(a));+}++int+archive_write_set_compression_xz(struct archive *a)+{+	__archive_write_filters_free(a);+	return (archive_write_add_filter_xz(a));+}++#endif++#ifndef HAVE_LZMA_H+int+archive_write_add_filter_xz(struct archive *a)+{+	archive_set_error(a, ARCHIVE_ERRNO_MISC,+	    "xz compression not supported on this platform");+	return (ARCHIVE_FATAL);+}++int+archive_write_add_filter_lzma(struct archive *a)+{+	archive_set_error(a, ARCHIVE_ERRNO_MISC,+	    "lzma compression not supported on this platform");+	return (ARCHIVE_FATAL);+}++int+archive_write_add_filter_lzip(struct archive *a)+{+	archive_set_error(a, ARCHIVE_ERRNO_MISC,+	    "lzma compression not supported on this platform");+	return (ARCHIVE_FATAL);+}+#else+/* Don't compile this if we don't have liblzma. */++struct private_data {+	int		 compression_level;+	uint32_t	 threads;+	lzma_stream	 stream;+	lzma_filter	 lzmafilters[2];+	lzma_options_lzma lzma_opt;+	int64_t		 total_in;+	unsigned char	*compressed;+	size_t		 compressed_buffer_size;+	int64_t		 total_out;+	/* the CRC32 value of uncompressed data for lzip */+	uint32_t	 crc32;+};++static int	archive_compressor_xz_options(struct archive_write_filter *,+		    const char *, const char *);+static int	archive_compressor_xz_open(struct archive_write_filter *);+static int	archive_compressor_xz_write(struct archive_write_filter *,+		    const void *, size_t);+static int	archive_compressor_xz_close(struct archive_write_filter *);+static int	archive_compressor_xz_free(struct archive_write_filter *);+static int	drive_compressor(struct archive_write_filter *,+		    struct private_data *, int finishing);++struct option_value {+	uint32_t dict_size;+	uint32_t nice_len;+	lzma_match_finder mf;+};+static const struct option_value option_values[] = {+	{ 1 << 16, 32, LZMA_MF_HC3},+	{ 1 << 20, 32, LZMA_MF_HC3},+	{ 3 << 19, 32, LZMA_MF_HC4},+	{ 1 << 21, 32, LZMA_MF_BT4},+	{ 3 << 20, 32, LZMA_MF_BT4},+	{ 1 << 22, 32, LZMA_MF_BT4},+	{ 1 << 23, 64, LZMA_MF_BT4},+	{ 1 << 24, 64, LZMA_MF_BT4},+	{ 3 << 23, 64, LZMA_MF_BT4},+	{ 1 << 25, 64, LZMA_MF_BT4}+};++static int+common_setup(struct archive_write_filter *f)+{+	struct private_data *data;+	struct archive_write *a = (struct archive_write *)f->archive;+	data = calloc(1, sizeof(*data));+	if (data == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	f->data = data;+	data->compression_level = LZMA_PRESET_DEFAULT;+	data->threads = 1;+	f->open = &archive_compressor_xz_open;+	f->close = archive_compressor_xz_close;+	f->free = archive_compressor_xz_free;+	f->options = &archive_compressor_xz_options;+	return (ARCHIVE_OK);+}++/*+ * Add an xz compression filter to this write handle.+ */+int+archive_write_add_filter_xz(struct archive *_a)+{+	struct archive_write_filter *f;+	int r;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_xz");+	f = __archive_write_allocate_filter(_a);+	r = common_setup(f);+	if (r == ARCHIVE_OK) {+		f->code = ARCHIVE_FILTER_XZ;+		f->name = "xz";+	}+	return (r);+}++/* LZMA is handled identically, we just need a different compression+ * code set.  (The liblzma setup looks at the code to determine+ * the one place that XZ and LZMA require different handling.) */+int+archive_write_add_filter_lzma(struct archive *_a)+{+	struct archive_write_filter *f;+	int r;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lzma");+	f = __archive_write_allocate_filter(_a);+	r = common_setup(f);+	if (r == ARCHIVE_OK) {+		f->code = ARCHIVE_FILTER_LZMA;+		f->name = "lzma";+	}+	return (r);+}++int+archive_write_add_filter_lzip(struct archive *_a)+{+	struct archive_write_filter *f;+	int r;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lzip");+	f = __archive_write_allocate_filter(_a);+	r = common_setup(f);+	if (r == ARCHIVE_OK) {+		f->code = ARCHIVE_FILTER_LZIP;+		f->name = "lzip";+	}+	return (r);+}++static int+archive_compressor_xz_init_stream(struct archive_write_filter *f,+    struct private_data *data)+{+	static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT;+	int ret;+#ifdef HAVE_LZMA_STREAM_ENCODER_MT+	lzma_mt mt_options;+#endif++	data->stream = lzma_stream_init_data;+	data->stream.next_out = data->compressed;+	data->stream.avail_out = data->compressed_buffer_size;+	if (f->code == ARCHIVE_FILTER_XZ) {+#ifdef HAVE_LZMA_STREAM_ENCODER_MT+		if (data->threads != 1) {+			memset(&mt_options, 0, sizeof(mt_options));+			mt_options.threads = data->threads;+			mt_options.timeout = 300;+			mt_options.filters = data->lzmafilters;+			mt_options.check = LZMA_CHECK_CRC64;+			ret = lzma_stream_encoder_mt(&(data->stream),+			    &mt_options);+		} else+#endif+			ret = lzma_stream_encoder(&(data->stream),+			    data->lzmafilters, LZMA_CHECK_CRC64);+	} else if (f->code == ARCHIVE_FILTER_LZMA) {+		ret = lzma_alone_encoder(&(data->stream), &data->lzma_opt);+	} else {	/* ARCHIVE_FILTER_LZIP */+		int dict_size = data->lzma_opt.dict_size;+		int ds, log2dic, wedges;++		/* Calculate a coded dictionary size */+		if (dict_size < (1 << 12) || dict_size > (1 << 27)) {+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+			    "Unacceptable dictionary size for lzip: %d",+			    dict_size);+			return (ARCHIVE_FATAL);+		}+		for (log2dic = 27; log2dic >= 12; log2dic--) {+			if (dict_size & (1 << log2dic))+				break;+		}+		if (dict_size > (1 << log2dic)) {+			log2dic++;+			wedges =+			    ((1 << log2dic) - dict_size) / (1 << (log2dic - 4));+		} else+			wedges = 0;+		ds = ((wedges << 5) & 0xe0) | (log2dic & 0x1f);++		data->crc32 = 0;+		/* Make a header */+		data->compressed[0] = 0x4C;+		data->compressed[1] = 0x5A;+		data->compressed[2] = 0x49;+		data->compressed[3] = 0x50;+		data->compressed[4] = 1;/* Version */+		data->compressed[5] = (unsigned char)ds;+		data->stream.next_out += 6;+		data->stream.avail_out -= 6;++		ret = lzma_raw_encoder(&(data->stream), data->lzmafilters);+	}+	if (ret == LZMA_OK)+		return (ARCHIVE_OK);++	switch (ret) {+	case LZMA_MEM_ERROR:+		archive_set_error(f->archive, ENOMEM,+		    "Internal error initializing compression library: "+		    "Cannot allocate memory");+		break;+	default:+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library: "+		    "It's a bug in liblzma");+		break;+	}+	return (ARCHIVE_FATAL);+}++/*+ * Setup callback.+ */+static int+archive_compressor_xz_open(struct archive_write_filter *f)+{+	struct private_data *data = f->data;+	int ret;++	if (data->compressed == NULL) {+		size_t bs = 65536, bpb;+		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {+			/* Buffer size should be a multiple number of the of bytes+			 * per block for performance. */+			bpb = archive_write_get_bytes_per_block(f->archive);+			if (bpb > bs)+				bs = bpb;+			else if (bpb != 0)+				bs -= bs % bpb;+		}+		data->compressed_buffer_size = bs;+		data->compressed+		    = (unsigned char *)malloc(data->compressed_buffer_size);+		if (data->compressed == NULL) {+			archive_set_error(f->archive, ENOMEM,+			    "Can't allocate data for compression buffer");+			return (ARCHIVE_FATAL);+		}+	}++	f->write = archive_compressor_xz_write;++	/* Initialize compression library. */+	if (f->code == ARCHIVE_FILTER_LZIP) {+		const struct option_value *val =+		    &option_values[data->compression_level];++		data->lzma_opt.dict_size = val->dict_size;+		data->lzma_opt.preset_dict = NULL;+		data->lzma_opt.preset_dict_size = 0;+		data->lzma_opt.lc = LZMA_LC_DEFAULT;+		data->lzma_opt.lp = LZMA_LP_DEFAULT;+		data->lzma_opt.pb = LZMA_PB_DEFAULT;+		data->lzma_opt.mode =+		    data->compression_level<= 2? LZMA_MODE_FAST:LZMA_MODE_NORMAL;+		data->lzma_opt.nice_len = val->nice_len;+		data->lzma_opt.mf = val->mf;+		data->lzma_opt.depth = 0;+		data->lzmafilters[0].id = LZMA_FILTER_LZMA1;+		data->lzmafilters[0].options = &data->lzma_opt;+		data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */+	} else {+		if (lzma_lzma_preset(&data->lzma_opt, data->compression_level)) {+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+			    "Internal error initializing compression library");+		}+		data->lzmafilters[0].id = LZMA_FILTER_LZMA2;+		data->lzmafilters[0].options = &data->lzma_opt;+		data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */+	}+	ret = archive_compressor_xz_init_stream(f, data);+	if (ret == LZMA_OK) {+		f->data = data;+		return (0);+	}+	return (ARCHIVE_FATAL);+}++/*+ * Set write options.+ */+static int+archive_compressor_xz_options(struct archive_write_filter *f,+    const char *key, const char *value)+{+	struct private_data *data = (struct private_data *)f->data;++	if (strcmp(key, "compression-level") == 0) {+		if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||+		    value[1] != '\0')+			return (ARCHIVE_WARN);+		data->compression_level = value[0] - '0';+		if (data->compression_level > 6)+			data->compression_level = 6;+		return (ARCHIVE_OK);+	} else if (strcmp(key, "threads") == 0) {+		char *endptr;++		if (value == NULL)+			return (ARCHIVE_WARN);+		errno = 0;+		data->threads = (int)strtoul(value, &endptr, 10);+		if (errno != 0 || *endptr != '\0') {+			data->threads = 1;+			return (ARCHIVE_WARN);+		}+		if (data->threads == 0) {+#ifdef HAVE_LZMA_STREAM_ENCODER_MT+			data->threads = lzma_cputhreads();+#else+			data->threads = 1;+#endif+		}+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++/*+ * Write data to the compressed stream.+ */+static int+archive_compressor_xz_write(struct archive_write_filter *f,+    const void *buff, size_t length)+{+	struct private_data *data = (struct private_data *)f->data;+	int ret;++	/* Update statistics */+	data->total_in += length;+	if (f->code == ARCHIVE_FILTER_LZIP)+		data->crc32 = lzma_crc32(buff, length, data->crc32);++	/* Compress input data to output buffer */+	data->stream.next_in = buff;+	data->stream.avail_in = length;+	if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)+		return (ret);++	return (ARCHIVE_OK);+}+++/*+ * Finish the compression...+ */+static int+archive_compressor_xz_close(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	int ret;++	ret = drive_compressor(f, data, 1);+	if (ret == ARCHIVE_OK) {+		data->total_out +=+		    data->compressed_buffer_size - data->stream.avail_out;+		ret = __archive_write_filter(f->next_filter,+		    data->compressed,+		    data->compressed_buffer_size - data->stream.avail_out);+		if (f->code == ARCHIVE_FILTER_LZIP && ret == ARCHIVE_OK) {+			archive_le32enc(data->compressed, data->crc32);+			archive_le64enc(data->compressed+4, data->total_in);+			archive_le64enc(data->compressed+12, data->total_out + 20);+			ret = __archive_write_filter(f->next_filter,+			    data->compressed, 20);+		}+	}+	lzma_end(&(data->stream));+	return ret;+}++static int+archive_compressor_xz_free(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	free(data->compressed);+	free(data);+	f->data = NULL;+	return (ARCHIVE_OK);+}++/*+ * Utility function to push input data through compressor,+ * writing full output blocks as necessary.+ *+ * Note that this handles both the regular write case (finishing ==+ * false) and the end-of-archive case (finishing == true).+ */+static int+drive_compressor(struct archive_write_filter *f,+    struct private_data *data, int finishing)+{+	int ret;++	for (;;) {+		if (data->stream.avail_out == 0) {+			data->total_out += data->compressed_buffer_size;+			ret = __archive_write_filter(f->next_filter,+			    data->compressed,+			    data->compressed_buffer_size);+			if (ret != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			data->stream.next_out = data->compressed;+			data->stream.avail_out = data->compressed_buffer_size;+		}++		/* If there's nothing to do, we're done. */+		if (!finishing && data->stream.avail_in == 0)+			return (ARCHIVE_OK);++		ret = lzma_code(&(data->stream),+		    finishing ? LZMA_FINISH : LZMA_RUN );++		switch (ret) {+		case LZMA_OK:+			/* In non-finishing case, check if compressor+			 * consumed everything */+			if (!finishing && data->stream.avail_in == 0)+				return (ARCHIVE_OK);+			/* In finishing case, this return always means+			 * there's more work */+			break;+		case LZMA_STREAM_END:+			/* This return can only occur in finishing case. */+			if (finishing)+				return (ARCHIVE_OK);+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+			    "lzma compression data error");+			return (ARCHIVE_FATAL);+		case LZMA_MEMLIMIT_ERROR:+			archive_set_error(f->archive, ENOMEM,+			    "lzma compression error: "+			    "%ju MiB would have been needed",+			    (uintmax_t)((lzma_memusage(&(data->stream))+				    + 1024 * 1024 -1)+				/ (1024 * 1024)));+			return (ARCHIVE_FATAL);+		default:+			/* Any other return value indicates an error. */+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+			    "lzma compression failed:"+			    " lzma_code() call returned status %d",+			    ret);+			return (ARCHIVE_FATAL);+		}+	}+}++#endif /* HAVE_LZMA_H */
+ c/archive_write_add_filter_zstd.c view
@@ -0,0 +1,392 @@+/*-+ * Copyright (c) 2017 Sean Purcell+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++__FBSDID("$FreeBSD$");+++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_ZSTD_H+#include <zstd.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_string.h"+#include "archive_write_private.h"++/* Don't compile this if we don't have zstd.h */++struct private_data {+	int		 compression_level;+#if HAVE_ZSTD_H && HAVE_LIBZSTD+	ZSTD_CStream	*cstream;+	int64_t		 total_in;+	ZSTD_outBuffer	 out;+#else+	struct archive_write_program_data *pdata;+#endif+};++/* If we don't have the library use default range values (zstdcli.c v1.4.0) */+#define CLEVEL_MIN -99+#define CLEVEL_STD_MIN 0 /* prior to 1.3.4 and more recent without using --fast */+#define CLEVEL_DEFAULT 3+#define CLEVEL_STD_MAX 19 /* without using --ultra */+#define CLEVEL_MAX 22++#define MINVER_NEGCLEVEL 10304+#define MINVER_MINCLEVEL 10306++static int archive_compressor_zstd_options(struct archive_write_filter *,+		    const char *, const char *);+static int archive_compressor_zstd_open(struct archive_write_filter *);+static int archive_compressor_zstd_write(struct archive_write_filter *,+		    const void *, size_t);+static int archive_compressor_zstd_close(struct archive_write_filter *);+static int archive_compressor_zstd_free(struct archive_write_filter *);+#if HAVE_ZSTD_H && HAVE_LIBZSTD+static int drive_compressor(struct archive_write_filter *,+		    struct private_data *, int, const void *, size_t);+#endif+++/*+ * Add a zstd compression filter to this write handle.+ */+int+archive_write_add_filter_zstd(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);+	struct private_data *data;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_zstd");++	data = calloc(1, sizeof(*data));+	if (data == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	f->data = data;+	f->open = &archive_compressor_zstd_open;+	f->options = &archive_compressor_zstd_options;+	f->close = &archive_compressor_zstd_close;+	f->free = &archive_compressor_zstd_free;+	f->code = ARCHIVE_FILTER_ZSTD;+	f->name = "zstd";+	data->compression_level = CLEVEL_DEFAULT;+#if HAVE_ZSTD_H && HAVE_LIBZSTD+	data->cstream = ZSTD_createCStream();+	if (data->cstream == NULL) {+		free(data);+		archive_set_error(&a->archive, ENOMEM,+		    "Failed to allocate zstd compressor object");+		return (ARCHIVE_FATAL);+	}++	return (ARCHIVE_OK);+#else+	data->pdata = __archive_write_program_allocate("zstd");+	if (data->pdata == NULL) {+		free(data);+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+	    "Using external zstd program");+	return (ARCHIVE_WARN);+#endif+}++static int+archive_compressor_zstd_free(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+#if HAVE_ZSTD_H && HAVE_LIBZSTD+	ZSTD_freeCStream(data->cstream);+	free(data->out.dst);+#else+	__archive_write_program_free(data->pdata);+#endif+	free(data);+	f->data = NULL;+	return (ARCHIVE_OK);+}++static int string_is_numeric (const char* value)+{+       size_t len = strlen(value);+       size_t i;++       if (len == 0) {+               return (ARCHIVE_WARN);+       }+       else if (len == 1 && !(value[0] >= '0' && value[0] <= '9')) {+               return (ARCHIVE_WARN);+       }+       else if (!(value[0] >= '0' && value[0] <= '9') &&+                value[0] != '-' && value[0] != '+') {+               return (ARCHIVE_WARN);+       }++       for (i = 1; i < len; i++) {+               if (!(value[i] >= '0' && value[i] <= '9')) {+                       return (ARCHIVE_WARN);+               }+       }++       return (ARCHIVE_OK);+}++/*+ * Set write options.+ */+static int+archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,+    const char *value)+{+	struct private_data *data = (struct private_data *)f->data;++	if (strcmp(key, "compression-level") == 0) {+		int level = atoi(value);+		/* If we don't have the library, hard-code the max level */+		int minimum = CLEVEL_MIN;+		int maximum = CLEVEL_MAX;+		if (string_is_numeric(value) != ARCHIVE_OK) {+			return (ARCHIVE_WARN);+		}+#if HAVE_ZSTD_H && HAVE_LIBZSTD+		maximum = ZSTD_maxCLevel();+#if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL+		if (ZSTD_versionNumber() >= MINVER_MINCLEVEL) {+			minimum = ZSTD_minCLevel();+		}+		else+#endif+		if (ZSTD_versionNumber() < MINVER_NEGCLEVEL) {+			minimum = CLEVEL_STD_MIN;+		}+#endif+		if (level < minimum || level > maximum) {+			return (ARCHIVE_WARN);+		}+		data->compression_level = level;+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++#if HAVE_ZSTD_H && HAVE_LIBZSTD+/*+ * Setup callback.+ */+static int+archive_compressor_zstd_open(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	if (data->out.dst == NULL) {+		size_t bs = ZSTD_CStreamOutSize(), bpb;+		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {+			/* Buffer size should be a multiple number of+			 * the of bytes per block for performance. */+			bpb = archive_write_get_bytes_per_block(f->archive);+			if (bpb > bs)+				bs = bpb;+			else if (bpb != 0)+				bs -= bs % bpb;+		}+		data->out.size = bs;+		data->out.pos = 0;+		data->out.dst+		    = (unsigned char *)malloc(data->out.size);+		if (data->out.dst == NULL) {+			archive_set_error(f->archive, ENOMEM,+			    "Can't allocate data for compression buffer");+			return (ARCHIVE_FATAL);+		}+	}++	f->write = archive_compressor_zstd_write;++	if (ZSTD_isError(ZSTD_initCStream(data->cstream,+	    data->compression_level))) {+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing zstd compressor object");+		return (ARCHIVE_FATAL);+	}++	return (ARCHIVE_OK);+}++/*+ * Write data to the compressed stream.+ */+static int+archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,+    size_t length)+{+	struct private_data *data = (struct private_data *)f->data;+	int ret;++	/* Update statistics */+	data->total_in += length;++	if ((ret = drive_compressor(f, data, 0, buff, length)) != ARCHIVE_OK)+		return (ret);++	return (ARCHIVE_OK);+}++/*+ * Finish the compression...+ */+static int+archive_compressor_zstd_close(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	/* Finish zstd frame */+	return drive_compressor(f, data, 1, NULL, 0);+}++/*+ * Utility function to push input data through compressor,+ * writing full output blocks as necessary.+ *+ * Note that this handles both the regular write case (finishing ==+ * false) and the end-of-archive case (finishing == true).+ */+static int+drive_compressor(struct archive_write_filter *f,+    struct private_data *data, int finishing, const void *src, size_t length)+{+	ZSTD_inBuffer in = (ZSTD_inBuffer) { src, length, 0 };++	for (;;) {+		if (data->out.pos == data->out.size) {+			const int ret = __archive_write_filter(f->next_filter,+			    data->out.dst, data->out.size);+			if (ret != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			data->out.pos = 0;+		}++		/* If there's nothing to do, we're done. */+		if (!finishing && in.pos == in.size)+			return (ARCHIVE_OK);++		{+			const size_t zstdret = !finishing ?+			    ZSTD_compressStream(data->cstream, &data->out, &in)+			    : ZSTD_endStream(data->cstream, &data->out);++			if (ZSTD_isError(zstdret)) {+				archive_set_error(f->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Zstd compression failed: %s",+				    ZSTD_getErrorName(zstdret));+				return (ARCHIVE_FATAL);+			}++			/* If we're finishing, 0 means nothing left to flush */+			if (finishing && zstdret == 0) {+				const int ret = __archive_write_filter(f->next_filter,+				    data->out.dst, data->out.pos);+				return (ret);+			}+		}+	}+}++#else /* HAVE_ZSTD_H && HAVE_LIBZSTD */++static int+archive_compressor_zstd_open(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;+	struct archive_string as;+	int r;++	archive_string_init(&as);+	/* --no-check matches library default */+	archive_strcpy(&as, "zstd --no-check");++	if (data->compression_level < CLEVEL_STD_MIN) {+		struct archive_string as2;+		archive_string_init(&as2);+		archive_string_sprintf(&as2, " --fast=%d", -data->compression_level);+		archive_string_concat(&as, &as2);+		archive_string_free(&as2);+	} else {+		struct archive_string as2;+		archive_string_init(&as2);+		archive_string_sprintf(&as2, " -%d", data->compression_level);+		archive_string_concat(&as, &as2);+		archive_string_free(&as2);+	}++	if (data->compression_level > CLEVEL_STD_MAX) {+		archive_strcat(&as, " --ultra");+	}++	f->write = archive_compressor_zstd_write;+	r = __archive_write_program_open(f, data->pdata, as.s);+	archive_string_free(&as);+	return (r);+}++static int+archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,+    size_t length)+{+	struct private_data *data = (struct private_data *)f->data;++	return __archive_write_program_write(f, data->pdata, buff, length);+}++static int+archive_compressor_zstd_close(struct archive_write_filter *f)+{+	struct private_data *data = (struct private_data *)f->data;++	return __archive_write_program_close(f, data->pdata);+}++#endif /* HAVE_ZSTD_H && HAVE_LIBZSTD */
+ c/archive_write_disk_posix.c view
@@ -0,0 +1,4540 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * Copyright (c) 2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer+ *    in this position and unchanged.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#if !defined(_WIN32) || defined(__CYGWIN__)++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_SYS_ACL_H+#include <sys/acl.h>+#endif+#ifdef HAVE_SYS_EXTATTR_H+#include <sys/extattr.h>+#endif+#if HAVE_SYS_XATTR_H+#include <sys/xattr.h>+#elif HAVE_ATTR_XATTR_H+#include <attr/xattr.h>+#endif+#ifdef HAVE_SYS_EA_H+#include <sys/ea.h>+#endif+#ifdef HAVE_SYS_IOCTL_H+#include <sys/ioctl.h>+#endif+#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_SYS_TIME_H+#include <sys/time.h>+#endif+#ifdef HAVE_SYS_UTIME_H+#include <sys/utime.h>+#endif+#ifdef HAVE_COPYFILE_H+#include <copyfile.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_GRP_H+#include <grp.h>+#endif+#ifdef HAVE_LANGINFO_H+#include <langinfo.h>+#endif+#ifdef HAVE_LINUX_FS_H+#include <linux/fs.h>	/* for Linux file flags */+#endif+/*+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.+ * As the include guards don't agree, the order of include is important.+ */+#ifdef HAVE_LINUX_EXT2_FS_H+#include <linux/ext2_fs.h>	/* for Linux file flags */+#endif+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)+#include <ext2fs/ext2_fs.h>	/* Linux file flags, broken on Cygwin */+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_PWD_H+#include <pwd.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#ifdef HAVE_UTIME_H+#include <utime.h>+#endif+#ifdef F_GETTIMES /* Tru64 specific */+#include <sys/fcntl1.h>+#endif++/*+ * Macro to cast st_mtime and time_t to an int64 so that 2 numbers can reliably be compared.+ *+ * It assumes that the input is an integer type of no more than 64 bits.+ * If the number is less than zero, t must be a signed type, so it fits in+ * int64_t. Otherwise, it's a nonnegative value so we can cast it to uint64_t+ * without loss. But it could be a large unsigned value, so we have to clip it+ * to INT64_MAX.*+ */+#define to_int64_time(t) \+   ((t) < 0 ? (int64_t)(t) : (uint64_t)(t) > (uint64_t)INT64_MAX ? INT64_MAX : (int64_t)(t))++#if __APPLE__+#include <TargetConditionals.h>+#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && HAVE_QUARANTINE_H+#include <quarantine.h>+#define HAVE_QUARANTINE 1+#endif+#endif++#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif++/* TODO: Support Mac OS 'quarantine' feature.  This is really just a+ * standard tag to mark files that have been downloaded as "tainted".+ * On Mac OS, we should mark the extracted files as tainted if the+ * archive being read was tainted.  Windows has a similar feature; we+ * should investigate ways to support this generically. */++#include "archive.h"+#include "archive_acl_private.h"+#include "archive_string.h"+#include "archive_endian.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_write_disk_private.h"++#ifndef O_BINARY+#define O_BINARY 0+#endif+#ifndef O_CLOEXEC+#define O_CLOEXEC 0+#endif++/* Ignore non-int O_NOFOLLOW constant. */+/* gnulib's fcntl.h does this on AIX, but it seems practical everywhere */+#if defined O_NOFOLLOW && !(INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX)+#undef O_NOFOLLOW+#endif++#ifndef O_NOFOLLOW+#define O_NOFOLLOW 0+#endif++#ifndef AT_FDCWD+#define AT_FDCWD -100+#endif++struct fixup_entry {+	struct fixup_entry	*next;+	struct archive_acl	 acl;+	mode_t			 mode;+	int64_t			 atime;+	int64_t                  birthtime;+	int64_t			 mtime;+	int64_t			 ctime;+	unsigned long		 atime_nanos;+	unsigned long            birthtime_nanos;+	unsigned long		 mtime_nanos;+	unsigned long		 ctime_nanos;+	unsigned long		 fflags_set;+	size_t			 mac_metadata_size;+	void			*mac_metadata;+	int			 fixup; /* bitmask of what needs fixing */+	char			*name;+};++/*+ * We use a bitmask to track which operations remain to be done for+ * this file.  In particular, this helps us avoid unnecessary+ * operations when it's possible to take care of one step as a+ * side-effect of another.  For example, mkdir() can specify the mode+ * for the newly-created object but symlink() cannot.  This means we+ * can skip chmod() if mkdir() succeeded, but we must explicitly+ * chmod() if we're trying to create a directory that already exists+ * (mkdir() failed) or if we're restoring a symlink.  Similarly, we+ * need to verify UID/GID before trying to restore SUID/SGID bits;+ * that verification can occur explicitly through a stat() call or+ * implicitly because of a successful chown() call.+ */+#define	TODO_MODE_FORCE		0x40000000+#define	TODO_MODE_BASE		0x20000000+#define	TODO_SUID		0x10000000+#define	TODO_SUID_CHECK		0x08000000+#define	TODO_SGID		0x04000000+#define	TODO_SGID_CHECK		0x02000000+#define	TODO_APPLEDOUBLE	0x01000000+#define	TODO_MODE		(TODO_MODE_BASE|TODO_SUID|TODO_SGID)+#define	TODO_TIMES		ARCHIVE_EXTRACT_TIME+#define	TODO_OWNER		ARCHIVE_EXTRACT_OWNER+#define	TODO_FFLAGS		ARCHIVE_EXTRACT_FFLAGS+#define	TODO_ACLS		ARCHIVE_EXTRACT_ACL+#define	TODO_XATTR		ARCHIVE_EXTRACT_XATTR+#define	TODO_MAC_METADATA	ARCHIVE_EXTRACT_MAC_METADATA+#define	TODO_HFS_COMPRESSION	ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED++struct archive_write_disk {+	struct archive	archive;++	mode_t			 user_umask;+	struct fixup_entry	*fixup_list;+	struct fixup_entry	*current_fixup;+	int64_t			 user_uid;+	int			 skip_file_set;+	int64_t			 skip_file_dev;+	int64_t			 skip_file_ino;+	time_t			 start_time;++	int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid);+	void  (*cleanup_gid)(void *private);+	void			*lookup_gid_data;+	int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid);+	void  (*cleanup_uid)(void *private);+	void			*lookup_uid_data;++	/*+	 * Full path of last file to satisfy symlink checks.+	 */+	struct archive_string	path_safe;++	/*+	 * Cached stat data from disk for the current entry.+	 * If this is valid, pst points to st.  Otherwise,+	 * pst is null.+	 */+	struct stat		 st;+	struct stat		*pst;++	/* Information about the object being restored right now. */+	struct archive_entry	*entry; /* Entry being extracted. */+	char			*name; /* Name of entry, possibly edited. */+	struct archive_string	 _name_data; /* backing store for 'name' */+	char			*tmpname; /* Temporary name * */+	struct archive_string	 _tmpname_data; /* backing store for 'tmpname' */+	/* Tasks remaining for this object. */+	int			 todo;+	/* Tasks deferred until end-of-archive. */+	int			 deferred;+	/* Options requested by the client. */+	int			 flags;+	/* Handle for the file we're restoring. */+	int			 fd;+	/* Current offset for writing data to the file. */+	int64_t			 offset;+	/* Last offset actually written to disk. */+	int64_t			 fd_offset;+	/* Total bytes actually written to files. */+	int64_t			 total_bytes_written;+	/* Maximum size of file, -1 if unknown. */+	int64_t			 filesize;+	/* Dir we were in before this restore; only for deep paths. */+	int			 restore_pwd;+	/* Mode we should use for this entry; affected by _PERM and umask. */+	mode_t			 mode;+	/* UID/GID to use in restoring this entry. */+	int64_t			 uid;+	int64_t			 gid;+	/*+	 * HFS+ Compression.+	 */+	/* Xattr "com.apple.decmpfs". */+	uint32_t		 decmpfs_attr_size;+	unsigned char		*decmpfs_header_p;+	/* ResourceFork set options used for fsetxattr. */+	int			 rsrc_xattr_options;+	/* Xattr "com.apple.ResourceFork". */+	unsigned char		*resource_fork;+	size_t			 resource_fork_allocated_size;+	unsigned int		 decmpfs_block_count;+	uint32_t		*decmpfs_block_info;+	/* Buffer for compressed data. */+	unsigned char		*compressed_buffer;+	size_t			 compressed_buffer_size;+	size_t			 compressed_buffer_remaining;+	/* The offset of the ResourceFork where compressed data will+	 * be placed. */+	uint32_t		 compressed_rsrc_position;+	uint32_t		 compressed_rsrc_position_v;+	/* Buffer for uncompressed data. */+	char			*uncompressed_buffer;+	size_t			 block_remaining_bytes;+	size_t			 file_remaining_bytes;+#ifdef HAVE_ZLIB_H+	z_stream		 stream;+	int			 stream_valid;+	int			 decmpfs_compression_level;+#endif+};++/*+ * Default mode for dirs created automatically (will be modified by umask).+ * Note that POSIX specifies 0777 for implicitly-created dirs, "modified+ * by the process' file creation mask."+ */+#define	DEFAULT_DIR_MODE 0777+/*+ * Dir modes are restored in two steps:  During the extraction, the permissions+ * in the archive are modified to match the following limits.  During+ * the post-extract fixup pass, the permissions from the archive are+ * applied.+ */+#define	MINIMUM_DIR_MODE 0700+#define	MAXIMUM_DIR_MODE 0775++/*+ * Maximum uncompressed size of a decmpfs block.+ */+#define MAX_DECMPFS_BLOCK_SIZE	(64 * 1024)+/*+ * HFS+ compression type.+ */+#define CMP_XATTR		3/* Compressed data in xattr. */+#define CMP_RESOURCE_FORK	4/* Compressed data in resource fork. */+/*+ * HFS+ compression resource fork.+ */+#define RSRC_H_SIZE	260	/* Base size of Resource fork header. */+#define RSRC_F_SIZE	50	/* Size of Resource fork footer. */+/* Size to write compressed data to resource fork. */+#define COMPRESSED_W_SIZE	(64 * 1024)+/* decmpfs definitions. */+#define MAX_DECMPFS_XATTR_SIZE		3802+#ifndef DECMPFS_XATTR_NAME+#define DECMPFS_XATTR_NAME		"com.apple.decmpfs"+#endif+#define DECMPFS_MAGIC			0x636d7066+#define DECMPFS_COMPRESSION_MAGIC	0+#define DECMPFS_COMPRESSION_TYPE	4+#define DECMPFS_UNCOMPRESSED_SIZE	8+#define DECMPFS_HEADER_SIZE		16++#define HFS_BLOCKS(s)	((s) >> 12)+++static int	la_opendirat(int, const char *);+static int	la_mktemp(struct archive_write_disk *);+static void	fsobj_error(int *, struct archive_string *, int, const char *,+		    const char *);+static int	check_symlinks_fsobj(char *, int *, struct archive_string *,+		    int);+static int	check_symlinks(struct archive_write_disk *);+static int	create_filesystem_object(struct archive_write_disk *);+static struct fixup_entry *current_fixup(struct archive_write_disk *,+		    const char *pathname);+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)+static void	edit_deep_directories(struct archive_write_disk *ad);+#endif+static int	cleanup_pathname_fsobj(char *, int *, struct archive_string *,+		    int);+static int	cleanup_pathname(struct archive_write_disk *);+static int	create_dir(struct archive_write_disk *, char *);+static int	create_parent_dir(struct archive_write_disk *, char *);+static ssize_t	hfs_write_data_block(struct archive_write_disk *,+		    const char *, size_t);+static int	fixup_appledouble(struct archive_write_disk *, const char *);+static int	older(struct stat *, struct archive_entry *);+static int	restore_entry(struct archive_write_disk *);+static int	set_mac_metadata(struct archive_write_disk *, const char *,+				 const void *, size_t);+static int	set_xattrs(struct archive_write_disk *);+static int	clear_nochange_fflags(struct archive_write_disk *);+static int	set_fflags(struct archive_write_disk *);+static int	set_fflags_platform(struct archive_write_disk *, int fd,+		    const char *name, mode_t mode,+		    unsigned long fflags_set, unsigned long fflags_clear);+static int	set_ownership(struct archive_write_disk *);+static int	set_mode(struct archive_write_disk *, int mode);+static int	set_time(int, int, const char *, time_t, long, time_t, long);+static int	set_times(struct archive_write_disk *, int, int, const char *,+		    time_t, long, time_t, long, time_t, long, time_t, long);+static int	set_times_from_entry(struct archive_write_disk *);+static struct fixup_entry *sort_dir_list(struct fixup_entry *p);+static ssize_t	write_data_block(struct archive_write_disk *,+		    const char *, size_t);++static struct archive_vtable *archive_write_disk_vtable(void);++static int	_archive_write_disk_close(struct archive *);+static int	_archive_write_disk_free(struct archive *);+static int	_archive_write_disk_header(struct archive *,+		    struct archive_entry *);+static int64_t	_archive_write_disk_filter_bytes(struct archive *, int);+static int	_archive_write_disk_finish_entry(struct archive *);+static ssize_t	_archive_write_disk_data(struct archive *, const void *,+		    size_t);+static ssize_t	_archive_write_disk_data_block(struct archive *, const void *,+		    size_t, int64_t);++static int+la_mktemp(struct archive_write_disk *a)+{+	int oerrno, fd;+	mode_t mode;++	archive_string_empty(&a->_tmpname_data);+	archive_string_sprintf(&a->_tmpname_data, "%s.XXXXXX", a->name);+	a->tmpname = a->_tmpname_data.s;++	fd = __archive_mkstemp(a->tmpname);+	if (fd == -1)+		return -1;++	mode = a->mode & 0777 & ~a->user_umask;+	if (fchmod(fd, mode) == -1) {+		oerrno = errno;+		close(fd);+		errno = oerrno;+		return -1;+	}+	return fd;+}++static int+la_opendirat(int fd, const char *path) {+	const int flags = O_CLOEXEC+#if defined(O_BINARY)+	    | O_BINARY+#endif+#if defined(O_DIRECTORY)+	    | O_DIRECTORY+#endif+#if defined(O_PATH)+	    | O_PATH+#elif defined(O_SEARCH)+	    | O_SEARCH+#elif defined(__FreeBSD__) && defined(O_EXEC)+	    | O_EXEC+#else+	    | O_RDONLY+#endif+	    ;++#if !defined(HAVE_OPENAT)+	if (fd != AT_FDCWD) {+		errno = ENOTSUP;+		return (-1);+	} else+		return (open(path, flags));+#else+	return (openat(fd, path, flags));+#endif+}++static int+lazy_stat(struct archive_write_disk *a)+{+	if (a->pst != NULL) {+		/* Already have stat() data available. */+		return (ARCHIVE_OK);+	}+#ifdef HAVE_FSTAT+	if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) {+		a->pst = &a->st;+		return (ARCHIVE_OK);+	}+#endif+	/*+	 * XXX At this point, symlinks should not be hit, otherwise+	 * XXX a race occurred.  Do we want to check explicitly for that?+	 */+	if (lstat(a->name, &a->st) == 0) {+		a->pst = &a->st;+		return (ARCHIVE_OK);+	}+	archive_set_error(&a->archive, errno, "Couldn't stat file");+	return (ARCHIVE_WARN);+}++static struct archive_vtable *+archive_write_disk_vtable(void)+{+	static struct archive_vtable av;+	static int inited = 0;++	if (!inited) {+		av.archive_close = _archive_write_disk_close;+		av.archive_filter_bytes = _archive_write_disk_filter_bytes;+		av.archive_free = _archive_write_disk_free;+		av.archive_write_header = _archive_write_disk_header;+		av.archive_write_finish_entry+		    = _archive_write_disk_finish_entry;+		av.archive_write_data = _archive_write_disk_data;+		av.archive_write_data_block = _archive_write_disk_data_block;+		inited = 1;+	}+	return (&av);+}++static int64_t+_archive_write_disk_filter_bytes(struct archive *_a, int n)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	(void)n; /* UNUSED */+	if (n == -1 || n == 0)+		return (a->total_bytes_written);+	return (-1);+}+++int+archive_write_disk_set_options(struct archive *_a, int flags)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;++	a->flags = flags;+	return (ARCHIVE_OK);+}+++/*+ * Extract this entry to disk.+ *+ * TODO: Validate hardlinks.  According to the standards, we're+ * supposed to check each extracted hardlink and squawk if it refers+ * to a file that we didn't restore.  I'm not entirely convinced this+ * is a good idea, but more importantly: Is there any way to validate+ * hardlinks without keeping a complete list of filenames from the+ * entire archive?? Ugh.+ *+ */+static int+_archive_write_disk_header(struct archive *_a, struct archive_entry *entry)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	struct fixup_entry *fe;+	int ret, r;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_write_disk_header");+	archive_clear_error(&a->archive);+	if (a->archive.state & ARCHIVE_STATE_DATA) {+		r = _archive_write_disk_finish_entry(&a->archive);+		if (r == ARCHIVE_FATAL)+			return (r);+	}++	/* Set up for this particular entry. */+	a->pst = NULL;+	a->current_fixup = NULL;+	a->deferred = 0;+	if (a->entry) {+		archive_entry_free(a->entry);+		a->entry = NULL;+	}+	a->entry = archive_entry_clone(entry);+	a->fd = -1;+	a->fd_offset = 0;+	a->offset = 0;+	a->restore_pwd = -1;+	a->uid = a->user_uid;+	a->mode = archive_entry_mode(a->entry);+	if (archive_entry_size_is_set(a->entry))+		a->filesize = archive_entry_size(a->entry);+	else+		a->filesize = -1;+	archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry));+	a->name = a->_name_data.s;+	archive_clear_error(&a->archive);++	/*+	 * Clean up the requested path.  This is necessary for correct+	 * dir restores; the dir restore logic otherwise gets messed+	 * up by nonsense like "dir/.".+	 */+	ret = cleanup_pathname(a);+	if (ret != ARCHIVE_OK)+		return (ret);++	/*+	 * Query the umask so we get predictable mode settings.+	 * This gets done on every call to _write_header in case the+	 * user edits their umask during the extraction for some+	 * reason.+	 */+	umask(a->user_umask = umask(0));++	/* Figure out what we need to do for this entry. */+	a->todo = TODO_MODE_BASE;+	if (a->flags & ARCHIVE_EXTRACT_PERM) {+		a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */+		/*+		 * SGID requires an extra "check" step because we+		 * cannot easily predict the GID that the system will+		 * assign.  (Different systems assign GIDs to files+		 * based on a variety of criteria, including process+		 * credentials and the gid of the enclosing+		 * directory.)  We can only restore the SGID bit if+		 * the file has the right GID, and we only know the+		 * GID if we either set it (see set_ownership) or if+		 * we've actually called stat() on the file after it+		 * was restored.  Since there are several places at+		 * which we might verify the GID, we need a TODO bit+		 * to keep track.+		 */+		if (a->mode & S_ISGID)+			a->todo |= TODO_SGID | TODO_SGID_CHECK;+		/*+		 * Verifying the SUID is simpler, but can still be+		 * done in multiple ways, hence the separate "check" bit.+		 */+		if (a->mode & S_ISUID)+			a->todo |= TODO_SUID | TODO_SUID_CHECK;+	} else {+		/*+		 * User didn't request full permissions, so don't+		 * restore SUID, SGID bits and obey umask.+		 */+		a->mode &= ~S_ISUID;+		a->mode &= ~S_ISGID;+		a->mode &= ~S_ISVTX;+		a->mode &= ~a->user_umask;+	}+	if (a->flags & ARCHIVE_EXTRACT_OWNER)+		a->todo |= TODO_OWNER;+	if (a->flags & ARCHIVE_EXTRACT_TIME)+		a->todo |= TODO_TIMES;+	if (a->flags & ARCHIVE_EXTRACT_ACL) {+#if ARCHIVE_ACL_DARWIN+		/*+		 * On MacOS, platform ACLs get stored in mac_metadata, too.+		 * If we intend to extract mac_metadata and it is present+		 * we skip extracting libarchive NFSv4 ACLs.+		 */+		size_t metadata_size;++		if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 ||+		    archive_entry_mac_metadata(a->entry,+		    &metadata_size) == NULL || metadata_size == 0)+#endif+#if ARCHIVE_ACL_LIBRICHACL+		/*+		 * RichACLs are stored in an extended attribute.+		 * If we intend to extract extended attributes and have this+		 * attribute we skip extracting libarchive NFSv4 ACLs.+		 */+		short extract_acls = 1;+		if (a->flags & ARCHIVE_EXTRACT_XATTR && (+		    archive_entry_acl_types(a->entry) &+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4)) {+			const char *attr_name;+			const void *attr_value;+			size_t attr_size;+			int i = archive_entry_xattr_reset(a->entry);+			while (i--) {+				archive_entry_xattr_next(a->entry, &attr_name,+				    &attr_value, &attr_size);+				if (attr_name != NULL && attr_value != NULL &&+				    attr_size > 0 && strcmp(attr_name,+				    "trusted.richacl") == 0) {+					extract_acls = 0;+					break;+				}+			}+		}+		if (extract_acls)+#endif+#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL+		{+#endif+		if (archive_entry_filetype(a->entry) == AE_IFDIR)+			a->deferred |= TODO_ACLS;+		else+			a->todo |= TODO_ACLS;+#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL+		}+#endif+	}+	if (a->flags & ARCHIVE_EXTRACT_MAC_METADATA) {+		if (archive_entry_filetype(a->entry) == AE_IFDIR)+			a->deferred |= TODO_MAC_METADATA;+		else+			a->todo |= TODO_MAC_METADATA;+	}+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)+	if ((a->flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) == 0) {+		unsigned long set, clear;+		archive_entry_fflags(a->entry, &set, &clear);+		if ((set & ~clear) & UF_COMPRESSED) {+			a->todo |= TODO_HFS_COMPRESSION;+			a->decmpfs_block_count = (unsigned)-1;+		}+	}+	if ((a->flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) != 0 &&+	    (a->mode & AE_IFMT) == AE_IFREG && a->filesize > 0) {+		a->todo |= TODO_HFS_COMPRESSION;+		a->decmpfs_block_count = (unsigned)-1;+	}+	{+		const char *p;++		/* Check if the current file name is a type of the+		 * resource fork file. */+		p = strrchr(a->name, '/');+		if (p == NULL)+			p = a->name;+		else+			p++;+		if (p[0] == '.' && p[1] == '_') {+			/* Do not compress "._XXX" files. */+			a->todo &= ~TODO_HFS_COMPRESSION;+			if (a->filesize > 0)+				a->todo |= TODO_APPLEDOUBLE;+		}+	}+#endif++	if (a->flags & ARCHIVE_EXTRACT_XATTR) {+#if ARCHIVE_XATTR_DARWIN+		/*+		 * On MacOS, extended attributes get stored in mac_metadata,+		 * too. If we intend to extract mac_metadata and it is present+		 * we skip extracting extended attributes.+		 */+		size_t metadata_size;++		if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 ||+		    archive_entry_mac_metadata(a->entry,+		    &metadata_size) == NULL || metadata_size == 0)+#endif+		a->todo |= TODO_XATTR;+	}+	if (a->flags & ARCHIVE_EXTRACT_FFLAGS)+		a->todo |= TODO_FFLAGS;+	if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {+		ret = check_symlinks(a);+		if (ret != ARCHIVE_OK)+			return (ret);+	}+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)+	/* If path exceeds PATH_MAX, shorten the path. */+	edit_deep_directories(a);+#endif++	ret = restore_entry(a);++#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)+	/*+	 * Check if the filesystem the file is restoring on supports+	 * HFS+ Compression. If not, cancel HFS+ Compression.+	 */+	if (a->todo | TODO_HFS_COMPRESSION) {+		/*+		 * NOTE: UF_COMPRESSED is ignored even if the filesystem+		 * supports HFS+ Compression because the file should+		 * have at least an extended attribute "com.apple.decmpfs"+		 * before the flag is set to indicate that the file have+		 * been compressed. If the filesystem does not support+		 * HFS+ Compression the system call will fail.+		 */+		if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0)+			a->todo &= ~TODO_HFS_COMPRESSION;+	}+#endif++	/*+	 * TODO: There are rumours that some extended attributes must+	 * be restored before file data is written.  If this is true,+	 * then we either need to write all extended attributes both+	 * before and after restoring the data, or find some rule for+	 * determining which must go first and which last.  Due to the+	 * many ways people are using xattrs, this may prove to be an+	 * intractable problem.+	 */++#ifdef HAVE_FCHDIR+	/* If we changed directory above, restore it here. */+	if (a->restore_pwd >= 0) {+		r = fchdir(a->restore_pwd);+		if (r != 0) {+			archive_set_error(&a->archive, errno,+			    "chdir() failure");+			ret = ARCHIVE_FATAL;+		}+		close(a->restore_pwd);+		a->restore_pwd = -1;+	}+#endif++	/*+	 * Fixup uses the unedited pathname from archive_entry_pathname(),+	 * because it is relative to the base dir and the edited path+	 * might be relative to some intermediate dir as a result of the+	 * deep restore logic.+	 */+	if (a->deferred & TODO_MODE) {+		fe = current_fixup(a, archive_entry_pathname(entry));+		if (fe == NULL)+			return (ARCHIVE_FATAL);+		fe->fixup |= TODO_MODE_BASE;+		fe->mode = a->mode;+	}++	if ((a->deferred & TODO_TIMES)+		&& (archive_entry_mtime_is_set(entry)+		    || archive_entry_atime_is_set(entry))) {+		fe = current_fixup(a, archive_entry_pathname(entry));+		if (fe == NULL)+			return (ARCHIVE_FATAL);+		fe->mode = a->mode;+		fe->fixup |= TODO_TIMES;+		if (archive_entry_atime_is_set(entry)) {+			fe->atime = archive_entry_atime(entry);+			fe->atime_nanos = archive_entry_atime_nsec(entry);+		} else {+			/* If atime is unset, use start time. */+			fe->atime = a->start_time;+			fe->atime_nanos = 0;+		}+		if (archive_entry_mtime_is_set(entry)) {+			fe->mtime = archive_entry_mtime(entry);+			fe->mtime_nanos = archive_entry_mtime_nsec(entry);+		} else {+			/* If mtime is unset, use start time. */+			fe->mtime = a->start_time;+			fe->mtime_nanos = 0;+		}+		if (archive_entry_birthtime_is_set(entry)) {+			fe->birthtime = archive_entry_birthtime(entry);+			fe->birthtime_nanos = archive_entry_birthtime_nsec(+			    entry);+		} else {+			/* If birthtime is unset, use mtime. */+			fe->birthtime = fe->mtime;+			fe->birthtime_nanos = fe->mtime_nanos;+		}+	}++	if (a->deferred & TODO_ACLS) {+		fe = current_fixup(a, archive_entry_pathname(entry));+		if (fe == NULL)+			return (ARCHIVE_FATAL);+		fe->fixup |= TODO_ACLS;+		archive_acl_copy(&fe->acl, archive_entry_acl(entry));+	}++	if (a->deferred & TODO_MAC_METADATA) {+		const void *metadata;+		size_t metadata_size;+		metadata = archive_entry_mac_metadata(a->entry, &metadata_size);+		if (metadata != NULL && metadata_size > 0) {+			fe = current_fixup(a, archive_entry_pathname(entry));+			if (fe == NULL)+				return (ARCHIVE_FATAL);+			fe->mac_metadata = malloc(metadata_size);+			if (fe->mac_metadata != NULL) {+				memcpy(fe->mac_metadata, metadata,+				    metadata_size);+				fe->mac_metadata_size = metadata_size;+				fe->fixup |= TODO_MAC_METADATA;+			}+		}+	}++	if (a->deferred & TODO_FFLAGS) {+		fe = current_fixup(a, archive_entry_pathname(entry));+		if (fe == NULL)+			return (ARCHIVE_FATAL);+		fe->fixup |= TODO_FFLAGS;+		/* TODO: Complete this.. defer fflags from below. */+	}++	/* We've created the object and are ready to pour data into it. */+	if (ret >= ARCHIVE_WARN)+		a->archive.state = ARCHIVE_STATE_DATA;+	/*+	 * If it's not open, tell our client not to try writing.+	 * In particular, dirs, links, etc, don't get written to.+	 */+	if (a->fd < 0) {+		archive_entry_set_size(entry, 0);+		a->filesize = 0;+	}++	return (ret);+}++int+archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file");+	a->skip_file_set = 1;+	a->skip_file_dev = d;+	a->skip_file_ino = i;+	return (ARCHIVE_OK);+}++static ssize_t+write_data_block(struct archive_write_disk *a, const char *buff, size_t size)+{+	uint64_t start_size = size;+	ssize_t bytes_written = 0;+	ssize_t block_size = 0, bytes_to_write;++	if (size == 0)+		return (ARCHIVE_OK);++	if (a->filesize == 0 || a->fd < 0) {+		archive_set_error(&a->archive, 0,+		    "Attempt to write to an empty file");+		return (ARCHIVE_WARN);+	}++	if (a->flags & ARCHIVE_EXTRACT_SPARSE) {+#if HAVE_STRUCT_STAT_ST_BLKSIZE+		int r;+		if ((r = lazy_stat(a)) != ARCHIVE_OK)+			return (r);+		block_size = a->pst->st_blksize;+#else+		/* XXX TODO XXX Is there a more appropriate choice here ? */+		/* This needn't match the filesystem allocation size. */+		block_size = 16*1024;+#endif+	}++	/* If this write would run beyond the file size, truncate it. */+	if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)+		start_size = size = (size_t)(a->filesize - a->offset);++	/* Write the data. */+	while (size > 0) {+		if (block_size == 0) {+			bytes_to_write = size;+		} else {+			/* We're sparsifying the file. */+			const char *p, *end;+			int64_t block_end;++			/* Skip leading zero bytes. */+			for (p = buff, end = buff + size; p < end; ++p) {+				if (*p != '\0')+					break;+			}+			a->offset += p - buff;+			size -= p - buff;+			buff = p;+			if (size == 0)+				break;++			/* Calculate next block boundary after offset. */+			block_end+			    = (a->offset / block_size + 1) * block_size;++			/* If the adjusted write would cross block boundary,+			 * truncate it to the block boundary. */+			bytes_to_write = size;+			if (a->offset + bytes_to_write > block_end)+				bytes_to_write = block_end - a->offset;+		}+		/* Seek if necessary to the specified offset. */+		if (a->offset != a->fd_offset) {+			if (lseek(a->fd, a->offset, SEEK_SET) < 0) {+				archive_set_error(&a->archive, errno,+				    "Seek failed");+				return (ARCHIVE_FATAL);+			}+			a->fd_offset = a->offset;+		}+		bytes_written = write(a->fd, buff, bytes_to_write);+		if (bytes_written < 0) {+			archive_set_error(&a->archive, errno, "Write failed");+			return (ARCHIVE_WARN);+		}+		buff += bytes_written;+		size -= bytes_written;+		a->total_bytes_written += bytes_written;+		a->offset += bytes_written;+		a->fd_offset = a->offset;+	}+	return (start_size - size);+}++#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\+	&& defined(HAVE_ZLIB_H)++/*+ * Set UF_COMPRESSED file flag.+ * This have to be called after hfs_write_decmpfs() because if the+ * file does not have "com.apple.decmpfs" xattr the flag is ignored.+ */+static int+hfs_set_compressed_fflag(struct archive_write_disk *a)+{+	int r;++	if ((r = lazy_stat(a)) != ARCHIVE_OK)+		return (r);++	a->st.st_flags |= UF_COMPRESSED;+	if (fchflags(a->fd, a->st.st_flags) != 0) {+		archive_set_error(&a->archive, errno,+		    "Failed to set UF_COMPRESSED file flag");+		return (ARCHIVE_WARN);+	}+	return (ARCHIVE_OK);+}++/*+ * HFS+ Compression decmpfs+ *+ *     +------------------------------+ +0+ *     |      Magic(LE 4 bytes)       |+ *     +------------------------------++ *     |      Type(LE 4 bytes)        |+ *     +------------------------------++ *     | Uncompressed size(LE 8 bytes)|+ *     +------------------------------+ +16+ *     |                              |+ *     |       Compressed data        |+ *     |  (Placed only if Type == 3)  |+ *     |                              |+ *     +------------------------------+  +3802 = MAX_DECMPFS_XATTR_SIZE+ *+ *  Type is 3: decmpfs has compressed data.+ *  Type is 4: Resource Fork has compressed data.+ */+/*+ * Write "com.apple.decmpfs"+ */+static int+hfs_write_decmpfs(struct archive_write_disk *a)+{+	int r;+	uint32_t compression_type;++	r = fsetxattr(a->fd, DECMPFS_XATTR_NAME, a->decmpfs_header_p,+	    a->decmpfs_attr_size, 0, 0);+	if (r < 0) {+		archive_set_error(&a->archive, errno,+		    "Cannot restore xattr:%s", DECMPFS_XATTR_NAME);+		compression_type = archive_le32dec(+		    &a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE]);+		if (compression_type == CMP_RESOURCE_FORK)+			fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME,+			    XATTR_SHOWCOMPRESSION);+		return (ARCHIVE_WARN);+	}+	return (ARCHIVE_OK);+}++/*+ * HFS+ Compression Resource Fork+ *+ *     +-----------------------------++ *     |     Header(260 bytes)       |+ *     +-----------------------------++ *     |   Block count(LE 4 bytes)   |+ *     +-----------------------------+  --++ * +-- |     Offset (LE 4 bytes)     |    |+ * |   | [distance from Block count] |    | Block 0+ * |   +-----------------------------+    |+ * |   | Compressed size(LE 4 bytes) |    |+ * |   +-----------------------------+  --++ * |   |                             |+ * |   |      ..................     |+ * |   |                             |+ * |   +-----------------------------+  --++ * |   |     Offset (LE 4 bytes)     |    |+ * |   +-----------------------------+    | Block (Block count -1)+ * |   | Compressed size(LE 4 bytes) |    |+ * +-> +-----------------------------+  --++ *     |   Compressed data(n bytes)  |  Block 0+ *     +-----------------------------++ *     |                             |+ *     |      ..................     |+ *     |                             |+ *     +-----------------------------++ *     |   Compressed data(n bytes)  |  Block (Block count -1)+ *     +-----------------------------++ *     |      Footer(50 bytes)       |+ *     +-----------------------------++ *+ */+/*+ * Write the header of "com.apple.ResourceFork"+ */+static int+hfs_write_resource_fork(struct archive_write_disk *a, unsigned char *buff,+    size_t bytes, uint32_t position)+{+	int ret;++	ret = fsetxattr(a->fd, XATTR_RESOURCEFORK_NAME, buff, bytes,+	    position, a->rsrc_xattr_options);+	if (ret < 0) {+		archive_set_error(&a->archive, errno,+		    "Cannot restore xattr: %s at %u pos %u bytes",+		    XATTR_RESOURCEFORK_NAME,+		    (unsigned)position,+		    (unsigned)bytes);+		return (ARCHIVE_WARN);+	}+	a->rsrc_xattr_options &= ~XATTR_CREATE;+	return (ARCHIVE_OK);+}++static int+hfs_write_compressed_data(struct archive_write_disk *a, size_t bytes_compressed)+{+	int ret;++	ret = hfs_write_resource_fork(a, a->compressed_buffer,+	    bytes_compressed, a->compressed_rsrc_position);+	if (ret == ARCHIVE_OK)+		a->compressed_rsrc_position += bytes_compressed;+	return (ret);+}++static int+hfs_write_resource_fork_header(struct archive_write_disk *a)+{+	unsigned char *buff;+	uint32_t rsrc_bytes;+	uint32_t rsrc_header_bytes;++	/*+	 * Write resource fork header + block info.+	 */+	buff = a->resource_fork;+	rsrc_bytes = a->compressed_rsrc_position - RSRC_F_SIZE;+	rsrc_header_bytes =+		RSRC_H_SIZE +		/* Header base size. */+		4 +			/* Block count. */+		(a->decmpfs_block_count * 8);/* Block info */+	archive_be32enc(buff, 0x100);+	archive_be32enc(buff + 4, rsrc_bytes);+	archive_be32enc(buff + 8, rsrc_bytes - 256);+	archive_be32enc(buff + 12, 0x32);+	memset(buff + 16, 0, 240);+	archive_be32enc(buff + 256, rsrc_bytes - 260);+	return hfs_write_resource_fork(a, buff, rsrc_header_bytes, 0);+}++static size_t+hfs_set_resource_fork_footer(unsigned char *buff, size_t buff_size)+{+	static const char rsrc_footer[RSRC_F_SIZE] = {+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,+		0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c',  'm',+		'p', 'f',   0x00, 0x00, 0x00, 0x0a, 0x00, 0x01,+		0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,+		0x00, 0x00+	};+	if (buff_size < sizeof(rsrc_footer))+		return (0);+	memcpy(buff, rsrc_footer, sizeof(rsrc_footer));+	return (sizeof(rsrc_footer));+}++static int+hfs_reset_compressor(struct archive_write_disk *a)+{+	int ret;++	if (a->stream_valid)+		ret = deflateReset(&a->stream);+	else+		ret = deflateInit(&a->stream, a->decmpfs_compression_level);++	if (ret != Z_OK) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Failed to initialize compressor");+		return (ARCHIVE_FATAL);+	} else+		a->stream_valid = 1;++	return (ARCHIVE_OK);+}++static int+hfs_decompress(struct archive_write_disk *a)+{+	uint32_t *block_info;+	unsigned int block_count;+	uint32_t data_pos, data_size;+	ssize_t r;+	ssize_t bytes_written, bytes_to_write;+	unsigned char *b;++	block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE);+	block_count = archive_le32dec(block_info++);+	while (block_count--) {+		data_pos = RSRC_H_SIZE + archive_le32dec(block_info++);+		data_size = archive_le32dec(block_info++);+		r = fgetxattr(a->fd, XATTR_RESOURCEFORK_NAME,+		    a->compressed_buffer, data_size, data_pos, 0);+		if (r != data_size)  {+			archive_set_error(&a->archive,+			    (r < 0)?errno:ARCHIVE_ERRNO_MISC,+			    "Failed to read resource fork");+			return (ARCHIVE_WARN);+		}+		if (a->compressed_buffer[0] == 0xff) {+			bytes_to_write = data_size -1;+			b = a->compressed_buffer + 1;+		} else {+			uLong dest_len = MAX_DECMPFS_BLOCK_SIZE;+			int zr;++			zr = uncompress((Bytef *)a->uncompressed_buffer,+			    &dest_len, a->compressed_buffer, data_size);+			if (zr != Z_OK) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Failed to decompress resource fork");+				return (ARCHIVE_WARN);+			}+			bytes_to_write = dest_len;+			b = (unsigned char *)a->uncompressed_buffer;+		}+		do {+			bytes_written = write(a->fd, b, bytes_to_write);+			if (bytes_written < 0) {+				archive_set_error(&a->archive, errno,+				    "Write failed");+				return (ARCHIVE_WARN);+			}+			bytes_to_write -= bytes_written;+			b += bytes_written;+		} while (bytes_to_write > 0);+	}+	r = fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, 0);+	if (r == -1)  {+		archive_set_error(&a->archive, errno,+		    "Failed to remove resource fork");+		return (ARCHIVE_WARN);+	}+	return (ARCHIVE_OK);+}++static int+hfs_drive_compressor(struct archive_write_disk *a, const char *buff,+    size_t size)+{+	unsigned char *buffer_compressed;+	size_t bytes_compressed;+	size_t bytes_used;+	int ret;++	ret = hfs_reset_compressor(a);+	if (ret != ARCHIVE_OK)+		return (ret);++	if (a->compressed_buffer == NULL) {+		size_t block_size;++		block_size = COMPRESSED_W_SIZE + RSRC_F_SIZE ++		    + compressBound(MAX_DECMPFS_BLOCK_SIZE);+		a->compressed_buffer = malloc(block_size);+		if (a->compressed_buffer == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Resource Fork");+			return (ARCHIVE_FATAL);+		}+		a->compressed_buffer_size = block_size;+		a->compressed_buffer_remaining = block_size;+	}++	buffer_compressed = a->compressed_buffer ++	    a->compressed_buffer_size - a->compressed_buffer_remaining;+	a->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff;+	a->stream.avail_in = size;+	a->stream.next_out = buffer_compressed;+	a->stream.avail_out = a->compressed_buffer_remaining;+	do {+		ret = deflate(&a->stream, Z_FINISH);+		switch (ret) {+		case Z_OK:+		case Z_STREAM_END:+			break;+		default:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Failed to compress data");+			return (ARCHIVE_FAILED);+		}+	} while (ret == Z_OK);+	bytes_compressed = a->compressed_buffer_remaining - a->stream.avail_out;++	/*+	 * If the compressed size is larger than the original size,+	 * throw away compressed data, use uncompressed data instead.+	 */+	if (bytes_compressed > size) {+		buffer_compressed[0] = 0xFF;/* uncompressed marker. */+		memcpy(buffer_compressed + 1, buff, size);+		bytes_compressed = size + 1;+	}+	a->compressed_buffer_remaining -= bytes_compressed;++	/*+	 * If the compressed size is smaller than MAX_DECMPFS_XATTR_SIZE+	 * and the block count in the file is only one, store compressed+	 * data to decmpfs xattr instead of the resource fork.+	 */+	if (a->decmpfs_block_count == 1 &&+	    (a->decmpfs_attr_size + bytes_compressed)+	      <= MAX_DECMPFS_XATTR_SIZE) {+		archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE],+		    CMP_XATTR);+		memcpy(a->decmpfs_header_p + DECMPFS_HEADER_SIZE,+		    buffer_compressed, bytes_compressed);+		a->decmpfs_attr_size += bytes_compressed;+		a->compressed_buffer_remaining = a->compressed_buffer_size;+		/*+		 * Finish HFS+ Compression.+		 * - Write the decmpfs xattr.+		 * - Set the UF_COMPRESSED file flag.+		 */+		ret = hfs_write_decmpfs(a);+		if (ret == ARCHIVE_OK)+			ret = hfs_set_compressed_fflag(a);+		return (ret);+	}++	/* Update block info. */+	archive_le32enc(a->decmpfs_block_info++,+	    a->compressed_rsrc_position_v - RSRC_H_SIZE);+	archive_le32enc(a->decmpfs_block_info++, bytes_compressed);+	a->compressed_rsrc_position_v += bytes_compressed;++	/*+	 * Write the compressed data to the resource fork.+	 */+	bytes_used = a->compressed_buffer_size - a->compressed_buffer_remaining;+	while (bytes_used >= COMPRESSED_W_SIZE) {+		ret = hfs_write_compressed_data(a, COMPRESSED_W_SIZE);+		if (ret != ARCHIVE_OK)+			return (ret);+		bytes_used -= COMPRESSED_W_SIZE;+		if (bytes_used > COMPRESSED_W_SIZE)+			memmove(a->compressed_buffer,+			    a->compressed_buffer + COMPRESSED_W_SIZE,+			    bytes_used);+		else+			memcpy(a->compressed_buffer,+			    a->compressed_buffer + COMPRESSED_W_SIZE,+			    bytes_used);+	}+	a->compressed_buffer_remaining = a->compressed_buffer_size - bytes_used;++	/*+	 * If the current block is the last block, write the remaining+	 * compressed data and the resource fork footer.+	 */+	if (a->file_remaining_bytes == 0) {+		size_t rsrc_size;+		int64_t bk;++		/* Append the resource footer. */+		rsrc_size = hfs_set_resource_fork_footer(+		    a->compressed_buffer + bytes_used,+		    a->compressed_buffer_remaining);+		ret = hfs_write_compressed_data(a, bytes_used + rsrc_size);+		a->compressed_buffer_remaining = a->compressed_buffer_size;++		/* If the compressed size is not enough smaller than+		 * the uncompressed size. cancel HFS+ compression.+		 * TODO: study a behavior of ditto utility and improve+		 * the condition to fall back into no HFS+ compression. */+		bk = HFS_BLOCKS(a->compressed_rsrc_position);+		bk += bk >> 7;+		if (bk > HFS_BLOCKS(a->filesize))+			return hfs_decompress(a);+		/*+		 * Write the resourcefork header.+		 */+		if (ret == ARCHIVE_OK)+			ret = hfs_write_resource_fork_header(a);+		/*+		 * Finish HFS+ Compression.+		 * - Write the decmpfs xattr.+		 * - Set the UF_COMPRESSED file flag.+		 */+		if (ret == ARCHIVE_OK)+			ret = hfs_write_decmpfs(a);+		if (ret == ARCHIVE_OK)+			ret = hfs_set_compressed_fflag(a);+	}+	return (ret);+}++static ssize_t+hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff,+    size_t size)+{+	const char *buffer_to_write;+	size_t bytes_to_write;+	int ret;++	if (a->decmpfs_block_count == (unsigned)-1) {+		void *new_block;+		size_t new_size;+		unsigned int block_count;++		if (a->decmpfs_header_p == NULL) {+			new_block = malloc(MAX_DECMPFS_XATTR_SIZE+			    + sizeof(uint32_t));+			if (new_block == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for decmpfs");+				return (ARCHIVE_FATAL);+			}+			a->decmpfs_header_p = new_block;+		}+		a->decmpfs_attr_size = DECMPFS_HEADER_SIZE;+		archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_MAGIC],+		    DECMPFS_MAGIC);+		archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE],+		    CMP_RESOURCE_FORK);+		archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE],+		    a->filesize);++		/* Calculate a block count of the file. */+		block_count =+		    (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) /+			MAX_DECMPFS_BLOCK_SIZE;+		/*+		 * Allocate buffer for resource fork.+		 * Set up related pointers;+		 */+		new_size =+		    RSRC_H_SIZE + /* header */+		    4 + /* Block count */+		    (block_count * sizeof(uint32_t) * 2) ++		    RSRC_F_SIZE; /* footer */+		if (new_size > a->resource_fork_allocated_size) {+			new_block = realloc(a->resource_fork, new_size);+			if (new_block == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for ResourceFork");+				return (ARCHIVE_FATAL);+			}+			a->resource_fork_allocated_size = new_size;+			a->resource_fork = new_block;+		}++		/* Allocate uncompressed buffer */+		if (a->uncompressed_buffer == NULL) {+			new_block = malloc(MAX_DECMPFS_BLOCK_SIZE);+			if (new_block == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for decmpfs");+				return (ARCHIVE_FATAL);+			}+			a->uncompressed_buffer = new_block;+		}+		a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE;+		a->file_remaining_bytes = a->filesize;+		a->compressed_buffer_remaining = a->compressed_buffer_size;++		/*+		 * Set up a resource fork.+		 */+		a->rsrc_xattr_options = XATTR_CREATE;+		/* Get the position where we are going to set a bunch+		 * of block info. */+		a->decmpfs_block_info =+		    (uint32_t *)(a->resource_fork + RSRC_H_SIZE);+		/* Set the block count to the resource fork. */+		archive_le32enc(a->decmpfs_block_info++, block_count);+		/* Get the position where we are going to set compressed+		 * data. */+		a->compressed_rsrc_position =+		    RSRC_H_SIZE + 4 + (block_count * 8);+		a->compressed_rsrc_position_v = a->compressed_rsrc_position;+		a->decmpfs_block_count = block_count;+	}++	/* Ignore redundant bytes. */+	if (a->file_remaining_bytes == 0)+		return ((ssize_t)size);++	/* Do not overrun a block size. */+	if (size > a->block_remaining_bytes)+		bytes_to_write = a->block_remaining_bytes;+	else+		bytes_to_write = size;+	/* Do not overrun the file size. */+	if (bytes_to_write > a->file_remaining_bytes)+		bytes_to_write = a->file_remaining_bytes;++	/* For efficiency, if a copy length is full of the uncompressed+	 * buffer size, do not copy writing data to it. */+	if (bytes_to_write == MAX_DECMPFS_BLOCK_SIZE)+		buffer_to_write = buff;+	else {+		memcpy(a->uncompressed_buffer ++		    MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes,+		    buff, bytes_to_write);+		buffer_to_write = a->uncompressed_buffer;+	}+	a->block_remaining_bytes -= bytes_to_write;+	a->file_remaining_bytes -= bytes_to_write;++	if (a->block_remaining_bytes == 0 || a->file_remaining_bytes == 0) {+		ret = hfs_drive_compressor(a, buffer_to_write,+		    MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes);+		if (ret < 0)+			return (ret);+		a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE;+	}+	/* Ignore redundant bytes. */+	if (a->file_remaining_bytes == 0)+		return ((ssize_t)size);+	return (bytes_to_write);+}++static ssize_t+hfs_write_data_block(struct archive_write_disk *a, const char *buff,+    size_t size)+{+	uint64_t start_size = size;+	ssize_t bytes_written = 0;+	ssize_t bytes_to_write;++	if (size == 0)+		return (ARCHIVE_OK);++	if (a->filesize == 0 || a->fd < 0) {+		archive_set_error(&a->archive, 0,+		    "Attempt to write to an empty file");+		return (ARCHIVE_WARN);+	}++	/* If this write would run beyond the file size, truncate it. */+	if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)+		start_size = size = (size_t)(a->filesize - a->offset);++	/* Write the data. */+	while (size > 0) {+		bytes_to_write = size;+		/* Seek if necessary to the specified offset. */+		if (a->offset < a->fd_offset) {+			/* Can't support backward move. */+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Seek failed");+			return (ARCHIVE_FATAL);+		} else if (a->offset > a->fd_offset) {+			int64_t skip = a->offset - a->fd_offset;+			char nullblock[1024];++			memset(nullblock, 0, sizeof(nullblock));+			while (skip > 0) {+				if (skip > (int64_t)sizeof(nullblock))+					bytes_written = hfs_write_decmpfs_block(+					    a, nullblock, sizeof(nullblock));+				else+					bytes_written = hfs_write_decmpfs_block(+					    a, nullblock, skip);+				if (bytes_written < 0) {+					archive_set_error(&a->archive, errno,+					    "Write failed");+					return (ARCHIVE_WARN);+				}+				skip -= bytes_written;+			}++			a->fd_offset = a->offset;+		}+		bytes_written =+		    hfs_write_decmpfs_block(a, buff, bytes_to_write);+		if (bytes_written < 0)+			return (bytes_written);+		buff += bytes_written;+		size -= bytes_written;+		a->total_bytes_written += bytes_written;+		a->offset += bytes_written;+		a->fd_offset = a->offset;+	}+	return (start_size - size);+}+#else+static ssize_t+hfs_write_data_block(struct archive_write_disk *a, const char *buff,+    size_t size)+{+	return (write_data_block(a, buff, size));+}+#endif++static ssize_t+_archive_write_disk_data_block(struct archive *_a,+    const void *buff, size_t size, int64_t offset)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	ssize_t r;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_DATA, "archive_write_data_block");++	a->offset = offset;+	if (a->todo & TODO_HFS_COMPRESSION)+		r = hfs_write_data_block(a, buff, size);+	else+		r = write_data_block(a, buff, size);+	if (r < ARCHIVE_OK)+		return (r);+	if ((size_t)r < size) {+		archive_set_error(&a->archive, 0,+		    "Too much data: Truncating file at %ju bytes",+		    (uintmax_t)a->filesize);+		return (ARCHIVE_WARN);+	}+#if ARCHIVE_VERSION_NUMBER < 3999000+	return (ARCHIVE_OK);+#else+	return (size);+#endif+}++static ssize_t+_archive_write_disk_data(struct archive *_a, const void *buff, size_t size)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_DATA, "archive_write_data");++	if (a->todo & TODO_HFS_COMPRESSION)+		return (hfs_write_data_block(a, buff, size));+	return (write_data_block(a, buff, size));+}++static int+_archive_write_disk_finish_entry(struct archive *_a)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	int ret = ARCHIVE_OK;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_write_finish_entry");+	if (a->archive.state & ARCHIVE_STATE_HEADER)+		return (ARCHIVE_OK);+	archive_clear_error(&a->archive);++	/* Pad or truncate file to the right size. */+	if (a->fd < 0) {+		/* There's no file. */+	} else if (a->filesize < 0) {+		/* File size is unknown, so we can't set the size. */+	} else if (a->fd_offset == a->filesize) {+		/* Last write ended at exactly the filesize; we're done. */+		/* Hopefully, this is the common case. */+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)+	} else if (a->todo & TODO_HFS_COMPRESSION) {+		char null_d[1024];+		ssize_t r;++		if (a->file_remaining_bytes)+			memset(null_d, 0, sizeof(null_d));+		while (a->file_remaining_bytes) {+			if (a->file_remaining_bytes > sizeof(null_d))+				r = hfs_write_data_block(+				    a, null_d, sizeof(null_d));+			else+				r = hfs_write_data_block(+				    a, null_d, a->file_remaining_bytes);+			if (r < 0)+				return ((int)r);+		}+#endif+	} else {+#if HAVE_FTRUNCATE+		if (ftruncate(a->fd, a->filesize) == -1 &&+		    a->filesize == 0) {+			archive_set_error(&a->archive, errno,+			    "File size could not be restored");+			return (ARCHIVE_FAILED);+		}+#endif+		/*+		 * Not all platforms implement the XSI option to+		 * extend files via ftruncate.  Stat() the file again+		 * to see what happened.+		 */+		a->pst = NULL;+		if ((ret = lazy_stat(a)) != ARCHIVE_OK)+			return (ret);+		/* We can use lseek()/write() to extend the file if+		 * ftruncate didn't work or isn't available. */+		if (a->st.st_size < a->filesize) {+			const char nul = '\0';+			if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) {+				archive_set_error(&a->archive, errno,+				    "Seek failed");+				return (ARCHIVE_FATAL);+			}+			if (write(a->fd, &nul, 1) < 0) {+				archive_set_error(&a->archive, errno,+				    "Write to restore size failed");+				return (ARCHIVE_FATAL);+			}+			a->pst = NULL;+		}+	}++	/* Restore metadata. */++	/*+	 * This is specific to Mac OS X.+	 * If the current file is an AppleDouble file, it should be+	 * linked with the data fork file and remove it.+	 */+	if (a->todo & TODO_APPLEDOUBLE) {+		int r2 = fixup_appledouble(a, a->name);+		if (r2 == ARCHIVE_EOF) {+			/* The current file has been successfully linked+			 * with the data fork file and removed. So there+			 * is nothing to do on the current file.  */+			goto finish_metadata;+		}+		if (r2 < ret) ret = r2;+	}++	/*+	 * Look up the "real" UID only if we're going to need it.+	 * TODO: the TODO_SGID condition can be dropped here, can't it?+	 */+	if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) {+		a->uid = archive_write_disk_uid(&a->archive,+		    archive_entry_uname(a->entry),+		    archive_entry_uid(a->entry));+	}+	/* Look up the "real" GID only if we're going to need it. */+	/* TODO: the TODO_SUID condition can be dropped here, can't it? */+	if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) {+		a->gid = archive_write_disk_gid(&a->archive,+		    archive_entry_gname(a->entry),+		    archive_entry_gid(a->entry));+	 }++	/*+	 * Restore ownership before set_mode tries to restore suid/sgid+	 * bits.  If we set the owner, we know what it is and can skip+	 * a stat() call to examine the ownership of the file on disk.+	 */+	if (a->todo & TODO_OWNER) {+		int r2 = set_ownership(a);+		if (r2 < ret) ret = r2;+	}++	/*+	 * HYPOTHESIS:+	 * If we're not root, we won't be setting any security+	 * attributes that may be wiped by the set_mode() routine+	 * below.  We also can't set xattr on non-owner-writable files,+	 * which may be the state after set_mode(). Perform+	 * set_xattrs() first based on these constraints.+	 */+	if (a->user_uid != 0 &&+	    (a->todo & TODO_XATTR)) {+		int r2 = set_xattrs(a);+		if (r2 < ret) ret = r2;+	}++	/*+	 * set_mode must precede ACLs on systems such as Solaris and+	 * FreeBSD where setting the mode implicitly clears extended ACLs+	 */+	if (a->todo & TODO_MODE) {+		int r2 = set_mode(a, a->mode);+		if (r2 < ret) ret = r2;+	}++	/*+	 * Security-related extended attributes (such as+	 * security.capability on Linux) have to be restored last,+	 * since they're implicitly removed by other file changes.+	 * We do this last only when root.+	 */+	if (a->user_uid == 0 &&+	    (a->todo & TODO_XATTR)) {+		int r2 = set_xattrs(a);+		if (r2 < ret) ret = r2;+	}++	/*+	 * Some flags prevent file modification; they must be restored after+	 * file contents are written.+	 */+	if (a->todo & TODO_FFLAGS) {+		int r2 = set_fflags(a);+		if (r2 < ret) ret = r2;+	}++	/*+	 * Time must follow most other metadata;+	 * otherwise atime will get changed.+	 */+	if (a->todo & TODO_TIMES) {+		int r2 = set_times_from_entry(a);+		if (r2 < ret) ret = r2;+	}++	/*+	 * Mac extended metadata includes ACLs.+	 */+	if (a->todo & TODO_MAC_METADATA) {+		const void *metadata;+		size_t metadata_size;+		metadata = archive_entry_mac_metadata(a->entry, &metadata_size);+		if (metadata != NULL && metadata_size > 0) {+			int r2 = set_mac_metadata(a, archive_entry_pathname(+			    a->entry), metadata, metadata_size);+			if (r2 < ret) ret = r2;+		}+	}++	/*+	 * ACLs must be restored after timestamps because there are+	 * ACLs that prevent attribute changes (including time).+	 */+	if (a->todo & TODO_ACLS) {+		int r2;+		r2 = archive_write_disk_set_acls(&a->archive, a->fd,+		    archive_entry_pathname(a->entry),+		    archive_entry_acl(a->entry),+		    archive_entry_mode(a->entry));+		if (r2 < ret) ret = r2;+	}++finish_metadata:+	/* If there's an fd, we can close it now. */+	if (a->fd >= 0) {+		close(a->fd);+		a->fd = -1;+		if (a->tmpname) {+			if (rename(a->tmpname, a->name) == -1) {+				archive_set_error(&a->archive, errno,+				    "Failed to rename temporary file");+				ret = ARCHIVE_FAILED;+				unlink(a->tmpname);+			}+			a->tmpname = NULL;+		}+	}+	/* If there's an entry, we can release it now. */+	archive_entry_free(a->entry);+	a->entry = NULL;+	a->archive.state = ARCHIVE_STATE_HEADER;+	return (ret);+}++int+archive_write_disk_set_group_lookup(struct archive *_a,+    void *private_data,+    la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid),+    void (*cleanup_gid)(void *private))+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup");++	if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL)+		(a->cleanup_gid)(a->lookup_gid_data);++	a->lookup_gid = lookup_gid;+	a->cleanup_gid = cleanup_gid;+	a->lookup_gid_data = private_data;+	return (ARCHIVE_OK);+}++int+archive_write_disk_set_user_lookup(struct archive *_a,+    void *private_data,+    int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid),+    void (*cleanup_uid)(void *private))+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup");++	if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL)+		(a->cleanup_uid)(a->lookup_uid_data);++	a->lookup_uid = lookup_uid;+	a->cleanup_uid = cleanup_uid;+	a->lookup_uid_data = private_data;+	return (ARCHIVE_OK);+}++int64_t+archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id)+{+       struct archive_write_disk *a = (struct archive_write_disk *)_a;+       archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+           ARCHIVE_STATE_ANY, "archive_write_disk_gid");+       if (a->lookup_gid)+               return (a->lookup_gid)(a->lookup_gid_data, name, id);+       return (id);+}+ +int64_t+archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_write_disk_uid");+	if (a->lookup_uid)+		return (a->lookup_uid)(a->lookup_uid_data, name, id);+	return (id);+}++/*+ * Create a new archive_write_disk object and initialize it with global state.+ */+struct archive *+archive_write_disk_new(void)+{+	struct archive_write_disk *a;++	a = (struct archive_write_disk *)calloc(1, sizeof(*a));+	if (a == NULL)+		return (NULL);+	a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC;+	/* We're ready to write a header immediately. */+	a->archive.state = ARCHIVE_STATE_HEADER;+	a->archive.vtable = archive_write_disk_vtable();+	a->start_time = time(NULL);+	/* Query and restore the umask. */+	umask(a->user_umask = umask(0));+#ifdef HAVE_GETEUID+	a->user_uid = geteuid();+#endif /* HAVE_GETEUID */+	if (archive_string_ensure(&a->path_safe, 512) == NULL) {+		free(a);+		return (NULL);+	}+#ifdef HAVE_ZLIB_H+	a->decmpfs_compression_level = 5;+#endif+	return (&a->archive);+}+++/*+ * If pathname is longer than PATH_MAX, chdir to a suitable+ * intermediate dir and edit the path down to a shorter suffix.  Note+ * that this routine never returns an error; if the chdir() attempt+ * fails for any reason, we just go ahead with the long pathname.  The+ * object creation is likely to fail, but any error will get handled+ * at that time.+ */+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)+static void+edit_deep_directories(struct archive_write_disk *a)+{+	int ret;+	char *tail = a->name;++	/* If path is short, avoid the open() below. */+	if (strlen(tail) < PATH_MAX)+		return;++	/* Try to record our starting dir. */+	a->restore_pwd = la_opendirat(AT_FDCWD, ".");+	__archive_ensure_cloexec_flag(a->restore_pwd);+	if (a->restore_pwd < 0)+		return;++	/* As long as the path is too long... */+	while (strlen(tail) >= PATH_MAX) {+		/* Locate a dir prefix shorter than PATH_MAX. */+		tail += PATH_MAX - 8;+		while (tail > a->name && *tail != '/')+			tail--;+		/* Exit if we find a too-long path component. */+		if (tail <= a->name)+			return;+		/* Create the intermediate dir and chdir to it. */+		*tail = '\0'; /* Terminate dir portion */+		ret = create_dir(a, a->name);+		if (ret == ARCHIVE_OK && chdir(a->name) != 0)+			ret = ARCHIVE_FAILED;+		*tail = '/'; /* Restore the / we removed. */+		if (ret != ARCHIVE_OK)+			return;+		tail++;+		/* The chdir() succeeded; we've now shortened the path. */+		a->name = tail;+	}+	return;+}+#endif++/*+ * The main restore function.+ */+static int+restore_entry(struct archive_write_disk *a)+{+	int ret = ARCHIVE_OK, en;++	if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {+		/*+		 * TODO: Fix this.  Apparently, there are platforms+		 * that still allow root to hose the entire filesystem+		 * by unlinking a dir.  The S_ISDIR() test above+		 * prevents us from using unlink() here if the new+		 * object is a dir, but that doesn't mean the old+		 * object isn't a dir.+		 */+		if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)+			(void)clear_nochange_fflags(a);+		if (unlink(a->name) == 0) {+			/* We removed it, reset cached stat. */+			a->pst = NULL;+		} else if (errno == ENOENT) {+			/* File didn't exist, that's just as good. */+		} else if (rmdir(a->name) == 0) {+			/* It was a dir, but now it's gone. */+			a->pst = NULL;+		} else {+			/* We tried, but couldn't get rid of it. */+			archive_set_error(&a->archive, errno,+			    "Could not unlink");+			return(ARCHIVE_FAILED);+		}+	}++	/* Try creating it first; if this fails, we'll try to recover. */+	en = create_filesystem_object(a);++	if ((en == ENOTDIR || en == ENOENT)+	    && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) {+		/* If the parent dir doesn't exist, try creating it. */+		create_parent_dir(a, a->name);+		/* Now try to create the object again. */+		en = create_filesystem_object(a);+	}++	if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) {+		archive_set_error(&a->archive, en,+		    "Hard-link target '%s' does not exist.",+		    archive_entry_hardlink(a->entry));+		return (ARCHIVE_FAILED);+	}++	if ((en == EISDIR || en == EEXIST)+	    && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {+		/* If we're not overwriting, we're done. */+		if (S_ISDIR(a->mode)) {+			/* Don't overwrite any settings on existing directories. */+			a->todo = 0;+		}+		archive_entry_unset_size(a->entry);+		return (ARCHIVE_OK);+	}++	/*+	 * Some platforms return EISDIR if you call+	 * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some+	 * return EEXIST.  POSIX is ambiguous, requiring EISDIR+	 * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT)+	 * on an existing item.+	 */+	if (en == EISDIR) {+		/* A dir is in the way of a non-dir, rmdir it. */+		if (rmdir(a->name) != 0) {+			archive_set_error(&a->archive, errno,+			    "Can't remove already-existing dir");+			return (ARCHIVE_FAILED);+		}+		a->pst = NULL;+		/* Try again. */+		en = create_filesystem_object(a);+	} else if (en == EEXIST) {+		/*+		 * We know something is in the way, but we don't know what;+		 * we need to find out before we go any further.+		 */+		int r = 0;+		/*+		 * The SECURE_SYMLINKS logic has already removed a+		 * symlink to a dir if the client wants that.  So+		 * follow the symlink if we're creating a dir.+		 */+		if (S_ISDIR(a->mode))+			r = la_stat(a->name, &a->st);+		/*+		 * If it's not a dir (or it's a broken symlink),+		 * then don't follow it.+		 */+		if (r != 0 || !S_ISDIR(a->mode))+			r = lstat(a->name, &a->st);+		if (r != 0) {+			archive_set_error(&a->archive, errno,+			    "Can't stat existing object");+			return (ARCHIVE_FAILED);+		}++		/*+		 * NO_OVERWRITE_NEWER doesn't apply to directories.+		 */+		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER)+		    &&  !S_ISDIR(a->st.st_mode)) {+			if (!older(&(a->st), a->entry)) {+				archive_entry_unset_size(a->entry);+				return (ARCHIVE_OK);+			}+		}++		/* If it's our archive, we're done. */+		if (a->skip_file_set &&+		    a->st.st_dev == (dev_t)a->skip_file_dev &&+		    a->st.st_ino == (ino_t)a->skip_file_ino) {+			archive_set_error(&a->archive, 0,+			    "Refusing to overwrite archive");+			return (ARCHIVE_FAILED);+		}++		if (!S_ISDIR(a->st.st_mode)) {+			if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)+				(void)clear_nochange_fflags(a);++			if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) &&+			    S_ISREG(a->st.st_mode)) {+				/* Use a temporary file to extract */+				if ((a->fd = la_mktemp(a)) == -1) {+					archive_set_error(&a->archive, errno,+					    "Can't create temporary file");+					return ARCHIVE_FAILED;+				}+				a->pst = NULL;+				en = 0;+			} else {+				/* A non-dir is in the way, unlink it. */+				if (unlink(a->name) != 0) {+					archive_set_error(&a->archive, errno,+					    "Can't unlink already-existing "+					    "object");+					return (ARCHIVE_FAILED);+				}+				a->pst = NULL;+				/* Try again. */+				en = create_filesystem_object(a);+			}+		} else if (!S_ISDIR(a->mode)) {+			/* A dir is in the way of a non-dir, rmdir it. */+			if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)+				(void)clear_nochange_fflags(a);+			if (rmdir(a->name) != 0) {+				archive_set_error(&a->archive, errno,+				    "Can't replace existing directory with non-directory");+				return (ARCHIVE_FAILED);+			}+			/* Try again. */+			en = create_filesystem_object(a);+		} else {+			/*+			 * There's a dir in the way of a dir.  Don't+			 * waste time with rmdir()/mkdir(), just fix+			 * up the permissions on the existing dir.+			 * Note that we don't change perms on existing+			 * dirs unless _EXTRACT_PERM is specified.+			 */+			if ((a->mode != a->st.st_mode)+			    && (a->todo & TODO_MODE_FORCE))+				a->deferred |= (a->todo & TODO_MODE);+			/* Ownership doesn't need deferred fixup. */+			en = 0; /* Forget the EEXIST. */+		}+	}++	if (en) {+		/* Everything failed; give up here. */+		if ((&a->archive)->error == NULL)+			archive_set_error(&a->archive, en, "Can't create '%s'",+			    a->name);+		return (ARCHIVE_FAILED);+	}++	a->pst = NULL; /* Cached stat data no longer valid. */+	return (ret);+}++/*+ * Returns 0 if creation succeeds, or else returns errno value from+ * the failed system call.   Note:  This function should only ever perform+ * a single system call.+ */+static int+create_filesystem_object(struct archive_write_disk *a)+{+	/* Create the entry. */+	const char *linkname;+	mode_t final_mode, mode;+	int r;+	/* these for check_symlinks_fsobj */+	char *linkname_copy;	/* non-const copy of linkname */+	struct stat st;+	struct archive_string error_string;+	int error_number;++	/* We identify hard/symlinks according to the link names. */+	/* Since link(2) and symlink(2) don't handle modes, we're done here. */+	linkname = archive_entry_hardlink(a->entry);+	if (linkname != NULL) {+#if !HAVE_LINK+		return (EPERM);+#else+		archive_string_init(&error_string);+		linkname_copy = strdup(linkname);+		if (linkname_copy == NULL) {+		    return (EPERM);+		}+		/*+		 * TODO: consider using the cleaned-up path as the link+		 * target?+		 */+		r = cleanup_pathname_fsobj(linkname_copy, &error_number,+		    &error_string, a->flags);+		if (r != ARCHIVE_OK) {+			archive_set_error(&a->archive, error_number, "%s",+			    error_string.s);+			free(linkname_copy);+			archive_string_free(&error_string);+			/*+			 * EPERM is more appropriate than error_number for our+			 * callers+			 */+			return (EPERM);+		}+		r = check_symlinks_fsobj(linkname_copy, &error_number,+		    &error_string, a->flags);+		if (r != ARCHIVE_OK) {+			archive_set_error(&a->archive, error_number, "%s",+			    error_string.s);+			free(linkname_copy);+			archive_string_free(&error_string);+			/*+			 * EPERM is more appropriate than error_number for our+			 * callers+			 */+			return (EPERM);+		}+		free(linkname_copy);+		archive_string_free(&error_string);+		/*+		 * Unlinking and linking here is really not atomic,+		 * but doing it right, would require us to construct+		 * an mktemplink() function, and then use rename(2).+		 */+		if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES)+			unlink(a->name);+		r = link(linkname, a->name) ? errno : 0;+		/*+		 * New cpio and pax formats allow hardlink entries+		 * to carry data, so we may have to open the file+		 * for hardlink entries.+		 *+		 * If the hardlink was successfully created and+		 * the archive doesn't have carry data for it,+		 * consider it to be non-authoritative for meta data.+		 * This is consistent with GNU tar and BSD pax.+		 * If the hardlink does carry data, let the last+		 * archive entry decide ownership.+		 */+		if (r == 0 && a->filesize <= 0) {+			a->todo = 0;+			a->deferred = 0;+		} else if (r == 0 && a->filesize > 0) {+#ifdef HAVE_LSTAT+			r = lstat(a->name, &st);+#else+			r = la_stat(a->name, &st);+#endif+			if (r != 0)+				r = errno;+			else if ((st.st_mode & AE_IFMT) == AE_IFREG) {+				a->fd = open(a->name, O_WRONLY | O_TRUNC |+				    O_BINARY | O_CLOEXEC | O_NOFOLLOW);+				__archive_ensure_cloexec_flag(a->fd);+				if (a->fd < 0)+					r = errno;+			}+		}+		return (r);+#endif+	}+	linkname = archive_entry_symlink(a->entry);+	if (linkname != NULL) {+#if HAVE_SYMLINK+		/*+		 * Unlinking and linking here is really not atomic,+		 * but doing it right, would require us to construct+		 * an mktempsymlink() function, and then use rename(2).+		 */+		if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES)+			unlink(a->name);+		return symlink(linkname, a->name) ? errno : 0;+#else+		return (EPERM);+#endif+	}++	/*+	 * The remaining system calls all set permissions, so let's+	 * try to take advantage of that to avoid an extra chmod()+	 * call.  (Recall that umask is set to zero right now!)+	 */++	/* Mode we want for the final restored object (w/o file type bits). */+	final_mode = a->mode & 07777;+	/*+	 * The mode that will actually be restored in this step.  Note+	 * that SUID, SGID, etc, require additional work to ensure+	 * security, so we never restore them at this point.+	 */+	mode = final_mode & 0777 & ~a->user_umask;++	/* +	 * Always create writable such that [f]setxattr() works if we're not+	 * root.+	 */+	if (a->user_uid != 0 &&+	    a->todo & (TODO_HFS_COMPRESSION | TODO_XATTR)) {+		mode |= 0200;+	}++	switch (a->mode & AE_IFMT) {+	default:+		/* POSIX requires that we fall through here. */+		/* FALLTHROUGH */+	case AE_IFREG:+		a->tmpname = NULL;+		a->fd = open(a->name,+		    O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode);+		__archive_ensure_cloexec_flag(a->fd);+		r = (a->fd < 0);+		break;+	case AE_IFCHR:+#ifdef HAVE_MKNOD+		/* Note: we use AE_IFCHR for the case label, and+		 * S_IFCHR for the mknod() call.  This is correct.  */+		r = mknod(a->name, mode | S_IFCHR,+		    archive_entry_rdev(a->entry));+		break;+#else+		/* TODO: Find a better way to warn about our inability+		 * to restore a char device node. */+		return (EINVAL);+#endif /* HAVE_MKNOD */+	case AE_IFBLK:+#ifdef HAVE_MKNOD+		r = mknod(a->name, mode | S_IFBLK,+		    archive_entry_rdev(a->entry));+		break;+#else+		/* TODO: Find a better way to warn about our inability+		 * to restore a block device node. */+		return (EINVAL);+#endif /* HAVE_MKNOD */+	case AE_IFDIR:+		mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;+		r = mkdir(a->name, mode);+		if (r == 0) {+			/* Defer setting dir times. */+			a->deferred |= (a->todo & TODO_TIMES);+			a->todo &= ~TODO_TIMES;+			/* Never use an immediate chmod(). */+			/* We can't avoid the chmod() entirely if EXTRACT_PERM+			 * because of SysV SGID inheritance. */+			if ((mode != final_mode)+			    || (a->flags & ARCHIVE_EXTRACT_PERM))+				a->deferred |= (a->todo & TODO_MODE);+			a->todo &= ~TODO_MODE;+		}+		break;+	case AE_IFIFO:+#ifdef HAVE_MKFIFO+		r = mkfifo(a->name, mode);+		break;+#else+		/* TODO: Find a better way to warn about our inability+		 * to restore a fifo. */+		return (EINVAL);+#endif /* HAVE_MKFIFO */+	}++	/* All the system calls above set errno on failure. */+	if (r)+		return (errno);++	/* If we managed to set the final mode, we've avoided a chmod(). */+	if (mode == final_mode)+		a->todo &= ~TODO_MODE;+	return (0);+}++/*+ * Cleanup function for archive_extract.  Mostly, this involves processing+ * the fixup list, which is used to address a number of problems:+ *   * Dir permissions might prevent us from restoring a file in that+ *     dir, so we restore the dir with minimum 0700 permissions first,+ *     then correct the mode at the end.+ *   * Similarly, the act of restoring a file touches the directory+ *     and changes the timestamp on the dir, so we have to touch-up dir+ *     timestamps at the end as well.+ *   * Some file flags can interfere with the restore by, for example,+ *     preventing the creation of hardlinks to those files.+ *   * Mac OS extended metadata includes ACLs, so must be deferred on dirs.+ *+ * Note that tar/cpio do not require that archives be in a particular+ * order; there is no way to know when the last file has been restored+ * within a directory, so there's no way to optimize the memory usage+ * here by fixing up the directory any earlier than the+ * end-of-archive.+ *+ * XXX TODO: Directory ACLs should be restored here, for the same+ * reason we set directory perms here. XXX+ */+static int+_archive_write_disk_close(struct archive *_a)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	struct fixup_entry *next, *p;+	int fd, ret;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_write_disk_close");+	ret = _archive_write_disk_finish_entry(&a->archive);++	/* Sort dir list so directories are fixed up in depth-first order. */+	p = sort_dir_list(a->fixup_list);++	while (p != NULL) {+		fd = -1;+		a->pst = NULL; /* Mark stat cache as out-of-date. */+		if (p->fixup &+		    (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) {+			fd = open(p->name,+			    O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC);+		}+		if (p->fixup & TODO_TIMES) {+			set_times(a, fd, p->mode, p->name,+			    p->atime, p->atime_nanos,+			    p->birthtime, p->birthtime_nanos,+			    p->mtime, p->mtime_nanos,+			    p->ctime, p->ctime_nanos);+		}+		if (p->fixup & TODO_MODE_BASE) {+#ifdef HAVE_FCHMOD+			if (fd >= 0)+				fchmod(fd, p->mode);+			else+#endif+			chmod(p->name, p->mode);+		}+		if (p->fixup & TODO_ACLS)+			archive_write_disk_set_acls(&a->archive, fd,+			    p->name, &p->acl, p->mode);+		if (p->fixup & TODO_FFLAGS)+			set_fflags_platform(a, fd, p->name,+			    p->mode, p->fflags_set, 0);+		if (p->fixup & TODO_MAC_METADATA)+			set_mac_metadata(a, p->name, p->mac_metadata,+					 p->mac_metadata_size);+		next = p->next;+		archive_acl_clear(&p->acl);+		free(p->mac_metadata);+		free(p->name);+		if (fd >= 0)+			close(fd);+		free(p);+		p = next;+	}+	a->fixup_list = NULL;+	return (ret);+}++static int+_archive_write_disk_free(struct archive *_a)+{+	struct archive_write_disk *a;+	int ret;+	if (_a == NULL)+		return (ARCHIVE_OK);+	archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free");+	a = (struct archive_write_disk *)_a;+	ret = _archive_write_disk_close(&a->archive);+	archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL);+	archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL);+	archive_entry_free(a->entry);+	archive_string_free(&a->_name_data);+	archive_string_free(&a->_tmpname_data);+	archive_string_free(&a->archive.error_string);+	archive_string_free(&a->path_safe);+	a->archive.magic = 0;+	__archive_clean(&a->archive);+	free(a->decmpfs_header_p);+	free(a->resource_fork);+	free(a->compressed_buffer);+	free(a->uncompressed_buffer);+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\+	&& defined(HAVE_ZLIB_H)+	if (a->stream_valid) {+		switch (deflateEnd(&a->stream)) {+		case Z_OK:+			break;+		default:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Failed to clean up compressor");+			ret = ARCHIVE_FATAL;+			break;+		}+	}+#endif+	free(a);+	return (ret);+}++/*+ * Simple O(n log n) merge sort to order the fixup list.  In+ * particular, we want to restore dir timestamps depth-first.+ */+static struct fixup_entry *+sort_dir_list(struct fixup_entry *p)+{+	struct fixup_entry *a, *b, *t;++	if (p == NULL)+		return (NULL);+	/* A one-item list is already sorted. */+	if (p->next == NULL)+		return (p);++	/* Step 1: split the list. */+	t = p;+	a = p->next->next;+	while (a != NULL) {+		/* Step a twice, t once. */+		a = a->next;+		if (a != NULL)+			a = a->next;+		t = t->next;+	}+	/* Now, t is at the mid-point, so break the list here. */+	b = t->next;+	t->next = NULL;+	a = p;++	/* Step 2: Recursively sort the two sub-lists. */+	a = sort_dir_list(a);+	b = sort_dir_list(b);++	/* Step 3: Merge the returned lists. */+	/* Pick the first element for the merged list. */+	if (strcmp(a->name, b->name) > 0) {+		t = p = a;+		a = a->next;+	} else {+		t = p = b;+		b = b->next;+	}++	/* Always put the later element on the list first. */+	while (a != NULL && b != NULL) {+		if (strcmp(a->name, b->name) > 0) {+			t->next = a;+			a = a->next;+		} else {+			t->next = b;+			b = b->next;+		}+		t = t->next;+	}++	/* Only one list is non-empty, so just splice it on. */+	if (a != NULL)+		t->next = a;+	if (b != NULL)+		t->next = b;++	return (p);+}++/*+ * Returns a new, initialized fixup entry.+ *+ * TODO: Reduce the memory requirements for this list by using a tree+ * structure rather than a simple list of names.+ */+static struct fixup_entry *+new_fixup(struct archive_write_disk *a, const char *pathname)+{+	struct fixup_entry *fe;++	fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry));+	if (fe == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory for a fixup");+		return (NULL);+	}+	fe->next = a->fixup_list;+	a->fixup_list = fe;+	fe->fixup = 0;+	fe->name = strdup(pathname);+	return (fe);+}++/*+ * Returns a fixup structure for the current entry.+ */+static struct fixup_entry *+current_fixup(struct archive_write_disk *a, const char *pathname)+{+	if (a->current_fixup == NULL)+		a->current_fixup = new_fixup(a, pathname);+	return (a->current_fixup);+}++/* Error helper for new *_fsobj functions */+static void+fsobj_error(int *a_eno, struct archive_string *a_estr,+    int err, const char *errstr, const char *path)+{+	if (a_eno)+		*a_eno = err;+	if (a_estr)+		archive_string_sprintf(a_estr, "%s%s", errstr, path);+}++/*+ * TODO: Someday, integrate this with the deep dir support; they both+ * scan the path and both can be optimized by comparing against other+ * recent paths.+ */+/*+ * Checks the given path to see if any elements along it are symlinks.  Returns+ * ARCHIVE_OK if there are none, otherwise puts an error in errmsg.+ */+static int+check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,+    int flags)+{+#if !defined(HAVE_LSTAT) && \+    !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT))+	/* Platform doesn't have lstat, so we can't look for symlinks. */+	(void)path; /* UNUSED */+	(void)error_number; /* UNUSED */+	(void)error_string; /* UNUSED */+	(void)flags; /* UNUSED */+	return (ARCHIVE_OK);+#else+	int res = ARCHIVE_OK;+	char *tail;+	char *head;+	int last;+	char c;+	int r;+	struct stat st;+	int chdir_fd;+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)+	int fd;+#endif++	/* Nothing to do here if name is empty */+	if(path[0] == '\0')+	    return (ARCHIVE_OK);++	/*+	 * Guard against symlink tricks.  Reject any archive entry whose+	 * destination would be altered by a symlink.+	 *+	 * Walk the filename in chunks separated by '/'.  For each segment:+	 *  - if it doesn't exist, continue+	 *  - if it's symlink, abort or remove it+	 *  - if it's a directory and it's not the last chunk, cd into it+	 * As we go:+	 *  head points to the current (relative) path+	 *  tail points to the temporary \0 terminating the segment we're+	 *      currently examining+	 *  c holds what used to be in *tail+	 *  last is 1 if this is the last tail+	 */+	chdir_fd = la_opendirat(AT_FDCWD, ".");+	__archive_ensure_cloexec_flag(chdir_fd);+	if (chdir_fd < 0) {+		fsobj_error(a_eno, a_estr, errno,+		    "Could not open ", path);+		return (ARCHIVE_FATAL);+	}+	head = path;+	tail = path;+	last = 0;+	/* TODO: reintroduce a safe cache here? */+	/* Skip the root directory if the path is absolute. */+	if(tail == path && tail[0] == '/')+		++tail;+	/* Keep going until we've checked the entire name.+	 * head, tail, path all alias the same string, which is+	 * temporarily zeroed at tail, so be careful restoring the+	 * stashed (c=tail[0]) for error messages.+	 * Exiting the loop with break is okay; continue is not.+	 */+	while (!last) {+		/*+		 * Skip the separator we just consumed, plus any adjacent ones+		 */+		while (*tail == '/')+		    ++tail;+		/* Skip the next path element. */+		while (*tail != '\0' && *tail != '/')+			++tail;+		/* is this the last path component? */+		last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0');+		/* temporarily truncate the string here */+		c = tail[0];+		tail[0] = '\0';+		/* Check that we haven't hit a symlink. */+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)+		r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW);+#else+		r = lstat(head, &st);+#endif+		if (r != 0) {+			tail[0] = c;+			/* We've hit a dir that doesn't exist; stop now. */+			if (errno == ENOENT) {+				break;+			} else {+				/*+				 * Treat any other error as fatal - best to be+				 * paranoid here.+				 * Note: This effectively disables deep+				 * directory support when security checks are+				 * enabled. Otherwise, very long pathnames that+				 * trigger an error here could evade the+				 * sandbox.+				 * TODO: We could do better, but it would+				 * probably require merging the symlink checks+				 * with the deep-directory editing.+				 */+				fsobj_error(a_eno, a_estr, errno,+				    "Could not stat ", path);+				res = ARCHIVE_FAILED;+				break;+			}+		} else if (S_ISDIR(st.st_mode)) {+			if (!last) {+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)+				fd = la_opendirat(chdir_fd, head);+				if (fd < 0)+					r = -1;+				else {+					r = 0;+					close(chdir_fd);+					chdir_fd = fd;+				}+#else+				r = chdir(head);+#endif+				if (r != 0) {+					tail[0] = c;+					fsobj_error(a_eno, a_estr, errno,+					    "Could not chdir ", path);+					res = (ARCHIVE_FATAL);+					break;+				}+				/* Our view is now from inside this dir: */+				head = tail + 1;+			}+		} else if (S_ISLNK(st.st_mode)) {+			if (last) {+				/*+				 * Last element is symlink; remove it+				 * so we can overwrite it with the+				 * item being extracted.+				 */+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)+				r = unlinkat(chdir_fd, head, 0);+#else+				r = unlink(head);+#endif+				if (r != 0) {+					tail[0] = c;+					fsobj_error(a_eno, a_estr, errno,+					    "Could not remove symlink ",+					    path);+					res = ARCHIVE_FAILED;+					break;+				}+				/*+				 * Even if we did remove it, a warning+				 * is in order.  The warning is silly,+				 * though, if we're just replacing one+				 * symlink with another symlink.+				 */+				tail[0] = c;+				/*+				 * FIXME:  not sure how important this is to+				 * restore+				 */+				/*+				if (!S_ISLNK(path)) {+					fsobj_error(a_eno, a_estr, 0,+					    "Removing symlink ", path);+				}+				*/+				/* Symlink gone.  No more problem! */+				res = ARCHIVE_OK;+				break;+			} else if (flags & ARCHIVE_EXTRACT_UNLINK) {+				/* User asked us to remove problems. */+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)+				r = unlinkat(chdir_fd, head, 0);+#else+				r = unlink(head);+#endif+				if (r != 0) {+					tail[0] = c;+					fsobj_error(a_eno, a_estr, 0,+					    "Cannot remove intervening "+					    "symlink ", path);+					res = ARCHIVE_FAILED;+					break;+				}+				tail[0] = c;+			} else if ((flags &+			    ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) {+				/*+				 * We are not the last element and we want to+				 * follow symlinks if they are a directory.+				 * +				 * This is needed to extract hardlinks over+				 * symlinks.+				 */+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)+				r = fstatat(chdir_fd, head, &st, 0);+#else+				r = la_stat(head, &st);+#endif+				if (r != 0) {+					tail[0] = c;+					if (errno == ENOENT) {+						break;+					} else {+						fsobj_error(a_eno, a_estr,+						    errno,+						    "Could not stat ", path);+						res = (ARCHIVE_FAILED);+						break;+					}+				} else if (S_ISDIR(st.st_mode)) {+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)+					fd = la_opendirat(chdir_fd, head);+					if (fd < 0)+						r = -1;+					else {+						r = 0;+						close(chdir_fd);+						chdir_fd = fd;+					}+#else+					r = chdir(head);+#endif+					if (r != 0) {+						tail[0] = c;+						fsobj_error(a_eno, a_estr,+						    errno,+						    "Could not chdir ", path);+						res = (ARCHIVE_FATAL);+						break;+					}+					/*+					 * Our view is now from inside+					 * this dir:+					 */+					head = tail + 1;+				} else {+					tail[0] = c;+					fsobj_error(a_eno, a_estr, 0,+					    "Cannot extract through "+					    "symlink ", path);+					res = ARCHIVE_FAILED;+					break;+				}+			} else {+				tail[0] = c;+				fsobj_error(a_eno, a_estr, 0,+				    "Cannot extract through symlink ", path);+				res = ARCHIVE_FAILED;+				break;+			}+		}+		/* be sure to always maintain this */+		tail[0] = c;+		if (tail[0] != '\0')+			tail++; /* Advance to the next segment. */+	}+	/* Catches loop exits via break */+	tail[0] = c;+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)+	/* If we operate with openat(), fstatat() and unlinkat() there was+	 * no chdir(), so just close the fd */+	if (chdir_fd >= 0)+		close(chdir_fd);+#elif HAVE_FCHDIR+	/* If we changed directory above, restore it here. */+	if (chdir_fd >= 0) {+		r = fchdir(chdir_fd);+		if (r != 0) {+			fsobj_error(a_eno, a_estr, errno,+			    "chdir() failure", "");+		}+		close(chdir_fd);+		chdir_fd = -1;+		if (r != 0) {+			res = (ARCHIVE_FATAL);+		}+	}+#endif+	/* TODO: reintroduce a safe cache here? */+	return res;+#endif+}++/*+ * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise+ * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED}+ */+static int+check_symlinks(struct archive_write_disk *a)+{+	struct archive_string error_string;+	int error_number;+	int rc;+	archive_string_init(&error_string);+	rc = check_symlinks_fsobj(a->name, &error_number, &error_string,+	    a->flags);+	if (rc != ARCHIVE_OK) {+		archive_set_error(&a->archive, error_number, "%s",+		    error_string.s);+	}+	archive_string_free(&error_string);+	a->pst = NULL;	/* to be safe */+	return rc;+}+++#if defined(__CYGWIN__)+/*+ * 1. Convert a path separator from '\' to '/' .+ *    We shouldn't check multibyte character directly because some+ *    character-set have been using the '\' character for a part of+ *    its multibyte character code.+ * 2. Replace unusable characters in Windows with underscore('_').+ * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx+ */+static void+cleanup_pathname_win(char *path)+{+	wchar_t wc;+	char *p;+	size_t alen, l;+	int mb, complete, utf8;++	alen = 0;+	mb = 0;+	complete = 1;+	utf8 = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)? 1: 0;+	for (p = path; *p != '\0'; p++) {+		++alen;+		if (*p == '\\') {+			/* If previous byte is smaller than 128,+			 * this is not second byte of multibyte characters,+			 * so we can replace '\' with '/'. */+			if (utf8 || !mb)+				*p = '/';+			else+				complete = 0;/* uncompleted. */+		} else if (*(unsigned char *)p > 127)+			mb = 1;+		else+			mb = 0;+		/* Rewrite the path name if its next character is unusable. */+		if (*p == ':' || *p == '*' || *p == '?' || *p == '"' ||+		    *p == '<' || *p == '>' || *p == '|')+			*p = '_';+	}+	if (complete)+		return;++	/*+	 * Convert path separator in wide-character.+	 */+	p = path;+	while (*p != '\0' && alen) {+		l = mbtowc(&wc, p, alen);+		if (l == (size_t)-1) {+			while (*p != '\0') {+				if (*p == '\\')+					*p = '/';+				++p;+			}+			break;+		}+		if (l == 1 && wc == L'\\')+			*p = '/';+		p += l;+		alen -= l;+	}+}+#endif++/*+ * Canonicalize the pathname.  In particular, this strips duplicate+ * '/' characters, '.' elements, and trailing '/'.  It also raises an+ * error for an empty path, a trailing '..', (if _SECURE_NODOTDOT is+ * set) any '..' in the path or (if ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS+ * is set) if the path is absolute.+ */+static int+cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr,+    int flags)+{+	char *dest, *src;+	char separator = '\0';++	dest = src = path;+	if (*src == '\0') {+		fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,+		    "Invalid empty ", "pathname");+		return (ARCHIVE_FAILED);+	}++#if defined(__CYGWIN__)+	cleanup_pathname_win(path);+#endif+	/* Skip leading '/'. */+	if (*src == '/') {+		if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) {+			fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,+			    "Path is ", "absolute");+			return (ARCHIVE_FAILED);+		}++		separator = *src++;+	}++	/* Scan the pathname one element at a time. */+	for (;;) {+		/* src points to first char after '/' */+		if (src[0] == '\0') {+			break;+		} else if (src[0] == '/') {+			/* Found '//', ignore second one. */+			src++;+			continue;+		} else if (src[0] == '.') {+			if (src[1] == '\0') {+				/* Ignore trailing '.' */+				break;+			} else if (src[1] == '/') {+				/* Skip './'. */+				src += 2;+				continue;+			} else if (src[1] == '.') {+				if (src[2] == '/' || src[2] == '\0') {+					/* Conditionally warn about '..' */+					if (flags+					    & ARCHIVE_EXTRACT_SECURE_NODOTDOT) {+						fsobj_error(a_eno, a_estr,+						    ARCHIVE_ERRNO_MISC,+						    "Path contains ", "'..'");+						return (ARCHIVE_FAILED);+					}+				}+				/*+				 * Note: Under no circumstances do we+				 * remove '..' elements.  In+				 * particular, restoring+				 * '/foo/../bar/' should create the+				 * 'foo' dir as a side-effect.+				 */+			}+		}++		/* Copy current element, including leading '/'. */+		if (separator)+			*dest++ = '/';+		while (*src != '\0' && *src != '/') {+			*dest++ = *src++;+		}++		if (*src == '\0')+			break;++		/* Skip '/' separator. */+		separator = *src++;+	}+	/*+	 * We've just copied zero or more path elements, not including the+	 * final '/'.+	 */+	if (dest == path) {+		/*+		 * Nothing got copied.  The path must have been something+		 * like '.' or '/' or './' or '/././././/./'.+		 */+		if (separator)+			*dest++ = '/';+		else+			*dest++ = '.';+	}+	/* Terminate the result. */+	*dest = '\0';+	return (ARCHIVE_OK);+}++static int+cleanup_pathname(struct archive_write_disk *a)+{+	struct archive_string error_string;+	int error_number;+	int rc;+	archive_string_init(&error_string);+	rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string,+	    a->flags);+	if (rc != ARCHIVE_OK) {+		archive_set_error(&a->archive, error_number, "%s",+		    error_string.s);+	}+	archive_string_free(&error_string);+	return rc;+}++/*+ * Create the parent directory of the specified path, assuming path+ * is already in mutable storage.+ */+static int+create_parent_dir(struct archive_write_disk *a, char *path)+{+	char *slash;+	int r;++	/* Remove tail element to obtain parent name. */+	slash = strrchr(path, '/');+	if (slash == NULL)+		return (ARCHIVE_OK);+	*slash = '\0';+	r = create_dir(a, path);+	*slash = '/';+	return (r);+}++/*+ * Create the specified dir, recursing to create parents as necessary.+ *+ * Returns ARCHIVE_OK if the path exists when we're done here.+ * Otherwise, returns ARCHIVE_FAILED.+ * Assumes path is in mutable storage; path is unchanged on exit.+ */+static int+create_dir(struct archive_write_disk *a, char *path)+{+	struct stat st;+	struct fixup_entry *le;+	char *slash, *base;+	mode_t mode_final, mode;+	int r;++	/* Check for special names and just skip them. */+	slash = strrchr(path, '/');+	if (slash == NULL)+		base = path;+	else+		base = slash + 1;++	if (base[0] == '\0' ||+	    (base[0] == '.' && base[1] == '\0') ||+	    (base[0] == '.' && base[1] == '.' && base[2] == '\0')) {+		/* Don't bother trying to create null path, '.', or '..'. */+		if (slash != NULL) {+			*slash = '\0';+			r = create_dir(a, path);+			*slash = '/';+			return (r);+		}+		return (ARCHIVE_OK);+	}++	/*+	 * Yes, this should be stat() and not lstat().  Using lstat()+	 * here loses the ability to extract through symlinks.  Also note+	 * that this should not use the a->st cache.+	 */+	if (la_stat(path, &st) == 0) {+		if (S_ISDIR(st.st_mode))+			return (ARCHIVE_OK);+		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {+			archive_set_error(&a->archive, EEXIST,+			    "Can't create directory '%s'", path);+			return (ARCHIVE_FAILED);+		}+		if (unlink(path) != 0) {+			archive_set_error(&a->archive, errno,+			    "Can't create directory '%s': "+			    "Conflicting file cannot be removed",+			    path);+			return (ARCHIVE_FAILED);+		}+	} else if (errno != ENOENT && errno != ENOTDIR) {+		/* Stat failed? */+		archive_set_error(&a->archive, errno,+		    "Can't test directory '%s'", path);+		return (ARCHIVE_FAILED);+	} else if (slash != NULL) {+		*slash = '\0';+		r = create_dir(a, path);+		*slash = '/';+		if (r != ARCHIVE_OK)+			return (r);+	}++	/*+	 * Mode we want for the final restored directory.  Per POSIX,+	 * implicitly-created dirs must be created obeying the umask.+	 * There's no mention whether this is different for privileged+	 * restores (which the rest of this code handles by pretending+	 * umask=0).  I've chosen here to always obey the user's umask for+	 * implicit dirs, even if _EXTRACT_PERM was specified.+	 */+	mode_final = DEFAULT_DIR_MODE & ~a->user_umask;+	/* Mode we want on disk during the restore process. */+	mode = mode_final;+	mode |= MINIMUM_DIR_MODE;+	mode &= MAXIMUM_DIR_MODE;+	if (mkdir(path, mode) == 0) {+		if (mode != mode_final) {+			le = new_fixup(a, path);+			if (le == NULL)+				return (ARCHIVE_FATAL);+			le->fixup |=TODO_MODE_BASE;+			le->mode = mode_final;+		}+		return (ARCHIVE_OK);+	}++	/*+	 * Without the following check, a/b/../b/c/d fails at the+	 * second visit to 'b', so 'd' can't be created.  Note that we+	 * don't add it to the fixup list here, as it's already been+	 * added.+	 */+	if (la_stat(path, &st) == 0 && S_ISDIR(st.st_mode))+		return (ARCHIVE_OK);++	archive_set_error(&a->archive, errno, "Failed to create dir '%s'",+	    path);+	return (ARCHIVE_FAILED);+}++/*+ * Note: Although we can skip setting the user id if the desired user+ * id matches the current user, we cannot skip setting the group, as+ * many systems set the gid based on the containing directory.  So+ * we have to perform a chown syscall if we want to set the SGID+ * bit.  (The alternative is to stat() and then possibly chown(); it's+ * more efficient to skip the stat() and just always chown().)  Note+ * that a successful chown() here clears the TODO_SGID_CHECK bit, which+ * allows set_mode to skip the stat() check for the GID.+ */+static int+set_ownership(struct archive_write_disk *a)+{+#if !defined(__CYGWIN__) && !defined(__linux__)+/*+ * On Linux, a process may have the CAP_CHOWN capability.+ * On Windows there is no 'root' user with uid 0.+ * Elsewhere we can skip calling chown if we are not root and the desired+ * user id does not match the current user.+ */+	if (a->user_uid != 0 && a->user_uid != a->uid) {+		archive_set_error(&a->archive, errno,+		    "Can't set UID=%jd", (intmax_t)a->uid);+		return (ARCHIVE_WARN);+	}+#endif++#ifdef HAVE_FCHOWN+	/* If we have an fd, we can avoid a race. */+	if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) {+		/* We've set owner and know uid/gid are correct. */+		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);+		return (ARCHIVE_OK);+	}+#endif++	/* We prefer lchown() but will use chown() if that's all we have. */+	/* Of course, if we have neither, this will always fail. */+#ifdef HAVE_LCHOWN+	if (lchown(a->name, a->uid, a->gid) == 0) {+		/* We've set owner and know uid/gid are correct. */+		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);+		return (ARCHIVE_OK);+	}+#elif HAVE_CHOWN+	if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) {+		/* We've set owner and know uid/gid are correct. */+		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);+		return (ARCHIVE_OK);+	}+#endif++	archive_set_error(&a->archive, errno,+	    "Can't set user=%jd/group=%jd for %s",+	    (intmax_t)a->uid, (intmax_t)a->gid, a->name);+	return (ARCHIVE_WARN);+}++/*+ * Note: Returns 0 on success, non-zero on failure.+ */+static int+set_time(int fd, int mode, const char *name,+    time_t atime, long atime_nsec,+    time_t mtime, long mtime_nsec)+{+	/* Select the best implementation for this platform. */+#if defined(HAVE_UTIMENSAT) && defined(HAVE_FUTIMENS)+	/*+	 * utimensat() and futimens() are defined in+	 * POSIX.1-2008. They support ns resolution and setting times+	 * on fds and symlinks.+	 */+	struct timespec ts[2];+	(void)mode; /* UNUSED */+	ts[0].tv_sec = atime;+	ts[0].tv_nsec = atime_nsec;+	ts[1].tv_sec = mtime;+	ts[1].tv_nsec = mtime_nsec;+	if (fd >= 0)+		return futimens(fd, ts);+	return utimensat(AT_FDCWD, name, ts, AT_SYMLINK_NOFOLLOW);++#elif HAVE_UTIMES+	/*+	 * The utimes()-family functions support µs-resolution and+	 * setting times fds and symlinks.  utimes() is documented as+	 * LEGACY by POSIX, futimes() and lutimes() are not described+	 * in POSIX.+	 */+	struct timeval times[2];++	times[0].tv_sec = atime;+	times[0].tv_usec = atime_nsec / 1000;+	times[1].tv_sec = mtime;+	times[1].tv_usec = mtime_nsec / 1000;++#ifdef HAVE_FUTIMES+	if (fd >= 0)+		return (futimes(fd, times));+#else+	(void)fd; /* UNUSED */+#endif+#ifdef HAVE_LUTIMES+	(void)mode; /* UNUSED */+	return (lutimes(name, times));+#else+	if (S_ISLNK(mode))+		return (0);+	return (utimes(name, times));+#endif++#elif defined(HAVE_UTIME)+	/*+	 * utime() is POSIX-standard but only supports 1s resolution and+	 * does not support fds or symlinks.+	 */+	struct utimbuf times;+	(void)fd; /* UNUSED */+	(void)name; /* UNUSED */+	(void)atime_nsec; /* UNUSED */+	(void)mtime_nsec; /* UNUSED */+	times.actime = atime;+	times.modtime = mtime;+	if (S_ISLNK(mode))+		return (ARCHIVE_OK);+	return (utime(name, &times));++#else+	/*+	 * We don't know how to set the time on this platform.+	 */+	(void)fd; /* UNUSED */+	(void)mode; /* UNUSED */+	(void)name; /* UNUSED */+	(void)atime_nsec; /* UNUSED */+	(void)mtime_nsec; /* UNUSED */+	return (ARCHIVE_WARN);+#endif+}++#ifdef F_SETTIMES+static int+set_time_tru64(int fd, int mode, const char *name,+    time_t atime, long atime_nsec,+    time_t mtime, long mtime_nsec,+    time_t ctime, long ctime_nsec)+{+	struct attr_timbuf tstamp;+	tstamp.atime.tv_sec = atime;+	tstamp.mtime.tv_sec = mtime;+	tstamp.ctime.tv_sec = ctime;+#if defined (__hpux) && defined (__ia64)+	tstamp.atime.tv_nsec = atime_nsec;+	tstamp.mtime.tv_nsec = mtime_nsec;+	tstamp.ctime.tv_nsec = ctime_nsec;+#else+	tstamp.atime.tv_usec = atime_nsec / 1000;+	tstamp.mtime.tv_usec = mtime_nsec / 1000;+	tstamp.ctime.tv_usec = ctime_nsec / 1000;+#endif+	return (fcntl(fd,F_SETTIMES,&tstamp));+}+#endif /* F_SETTIMES */++static int+set_times(struct archive_write_disk *a,+    int fd, int mode, const char *name,+    time_t atime, long atime_nanos,+    time_t birthtime, long birthtime_nanos,+    time_t mtime, long mtime_nanos,+    time_t cctime, long ctime_nanos)+{+	/* Note: set_time doesn't use libarchive return conventions!+	 * It uses syscall conventions.  So 0 here instead of ARCHIVE_OK. */+	int r1 = 0, r2 = 0;++#ifdef F_SETTIMES+	 /*+	 * on Tru64 try own fcntl first which can restore even the+	 * ctime, fall back to default code path below if it fails+	 * or if we are not running as root+	 */+	if (a->user_uid == 0 &&+	    set_time_tru64(fd, mode, name,+			   atime, atime_nanos, mtime,+			   mtime_nanos, cctime, ctime_nanos) == 0) {+		return (ARCHIVE_OK);+	}+#else /* Tru64 */+	(void)cctime; /* UNUSED */+	(void)ctime_nanos; /* UNUSED */+#endif /* Tru64 */++#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME+	/*+	 * If you have struct stat.st_birthtime, we assume BSD+	 * birthtime semantics, in which {f,l,}utimes() updates+	 * birthtime to earliest mtime.  So we set the time twice,+	 * first using the birthtime, then using the mtime.  If+	 * birthtime == mtime, this isn't necessary, so we skip it.+	 * If birthtime > mtime, then this won't work, so we skip it.+	 */+	if (birthtime < mtime+	    || (birthtime == mtime && birthtime_nanos < mtime_nanos))+		r1 = set_time(fd, mode, name,+			      atime, atime_nanos,+			      birthtime, birthtime_nanos);+#else+	(void)birthtime; /* UNUSED */+	(void)birthtime_nanos; /* UNUSED */+#endif+	r2 = set_time(fd, mode, name,+		      atime, atime_nanos,+		      mtime, mtime_nanos);+	if (r1 != 0 || r2 != 0) {+		archive_set_error(&a->archive, errno,+				  "Can't restore time");+		return (ARCHIVE_WARN);+	}+	return (ARCHIVE_OK);+}++static int+set_times_from_entry(struct archive_write_disk *a)+{+	time_t atime, birthtime, mtime, cctime;+	long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec;++	/* Suitable defaults. */+	atime = birthtime = mtime = cctime = a->start_time;+	atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0;++	/* If no time was provided, we're done. */+	if (!archive_entry_atime_is_set(a->entry)+#if HAVE_STRUCT_STAT_ST_BIRTHTIME+	    && !archive_entry_birthtime_is_set(a->entry)+#endif+	    && !archive_entry_mtime_is_set(a->entry))+		return (ARCHIVE_OK);++	if (archive_entry_atime_is_set(a->entry)) {+		atime = archive_entry_atime(a->entry);+		atime_nsec = archive_entry_atime_nsec(a->entry);+	}+	if (archive_entry_birthtime_is_set(a->entry)) {+		birthtime = archive_entry_birthtime(a->entry);+		birthtime_nsec = archive_entry_birthtime_nsec(a->entry);+	}+	if (archive_entry_mtime_is_set(a->entry)) {+		mtime = archive_entry_mtime(a->entry);+		mtime_nsec = archive_entry_mtime_nsec(a->entry);+	}+	if (archive_entry_ctime_is_set(a->entry)) {+		cctime = archive_entry_ctime(a->entry);+		ctime_nsec = archive_entry_ctime_nsec(a->entry);+	}++	return set_times(a, a->fd, a->mode, a->name,+			 atime, atime_nsec,+			 birthtime, birthtime_nsec,+			 mtime, mtime_nsec,+			 cctime, ctime_nsec);+}++static int+set_mode(struct archive_write_disk *a, int mode)+{+	int r = ARCHIVE_OK;+	int r2;+	mode &= 07777; /* Strip off file type bits. */++	if (a->todo & TODO_SGID_CHECK) {+		/*+		 * If we don't know the GID is right, we must stat()+		 * to verify it.  We can't just check the GID of this+		 * process, since systems sometimes set GID from+		 * the enclosing dir or based on ACLs.+		 */+		if ((r = lazy_stat(a)) != ARCHIVE_OK)+			return (r);+		if (a->pst->st_gid != a->gid) {+			mode &= ~ S_ISGID;+			if (a->flags & ARCHIVE_EXTRACT_OWNER) {+				/*+				 * This is only an error if you+				 * requested owner restore.  If you+				 * didn't, we'll try to restore+				 * sgid/suid, but won't consider it a+				 * problem if we can't.+				 */+				archive_set_error(&a->archive, -1,+				    "Can't restore SGID bit");+				r = ARCHIVE_WARN;+			}+		}+		/* While we're here, double-check the UID. */+		if (a->pst->st_uid != a->uid+		    && (a->todo & TODO_SUID)) {+			mode &= ~ S_ISUID;+			if (a->flags & ARCHIVE_EXTRACT_OWNER) {+				archive_set_error(&a->archive, -1,+				    "Can't restore SUID bit");+				r = ARCHIVE_WARN;+			}+		}+		a->todo &= ~TODO_SGID_CHECK;+		a->todo &= ~TODO_SUID_CHECK;+	} else if (a->todo & TODO_SUID_CHECK) {+		/*+		 * If we don't know the UID is right, we can just check+		 * the user, since all systems set the file UID from+		 * the process UID.+		 */+		if (a->user_uid != a->uid) {+			mode &= ~ S_ISUID;+			if (a->flags & ARCHIVE_EXTRACT_OWNER) {+				archive_set_error(&a->archive, -1,+				    "Can't make file SUID");+				r = ARCHIVE_WARN;+			}+		}+		a->todo &= ~TODO_SUID_CHECK;+	}++	if (S_ISLNK(a->mode)) {+#ifdef HAVE_LCHMOD+		/*+		 * If this is a symlink, use lchmod().  If the+		 * platform doesn't support lchmod(), just skip it.  A+		 * platform that doesn't provide a way to set+		 * permissions on symlinks probably ignores+		 * permissions on symlinks, so a failure here has no+		 * impact.+		 */+		if (lchmod(a->name, mode) != 0) {+			switch (errno) {+			case ENOTSUP:+			case ENOSYS:+#if ENOTSUP != EOPNOTSUPP+			case EOPNOTSUPP:+#endif+				/*+				 * if lchmod is defined but the platform+				 * doesn't support it, silently ignore+				 * error+				 */+				break;+			default:+				archive_set_error(&a->archive, errno,+				    "Can't set permissions to 0%o", (int)mode);+				r = ARCHIVE_WARN;+			}+		}+#endif+	} else if (!S_ISDIR(a->mode)) {+		/*+		 * If it's not a symlink and not a dir, then use+		 * fchmod() or chmod(), depending on whether we have+		 * an fd.  Dirs get their perms set during the+		 * post-extract fixup, which is handled elsewhere.+		 */+#ifdef HAVE_FCHMOD+		if (a->fd >= 0)+			r2 = fchmod(a->fd, mode);+		else+#endif+		/* If this platform lacks fchmod(), then+		 * we'll just use chmod(). */+		r2 = chmod(a->name, mode);++		if (r2 != 0) {+			archive_set_error(&a->archive, errno,+			    "Can't set permissions to 0%o", (int)mode);+			r = ARCHIVE_WARN;+		}+	}+	return (r);+}++static int+set_fflags(struct archive_write_disk *a)+{+	struct fixup_entry *le;+	unsigned long	set, clear;+	int		r;+	mode_t		mode = archive_entry_mode(a->entry);+	/*+	 * Make 'critical_flags' hold all file flags that can't be+	 * immediately restored.  For example, on BSD systems,+	 * SF_IMMUTABLE prevents hardlinks from being created, so+	 * should not be set until after any hardlinks are created.  To+	 * preserve some semblance of portability, this uses #ifdef+	 * extensively.  Ugly, but it works.+	 *+	 * Yes, Virginia, this does create a security race.  It's mitigated+	 * somewhat by the practice of creating dirs 0700 until the extract+	 * is done, but it would be nice if we could do more than that.+	 * People restoring critical file systems should be wary of+	 * other programs that might try to muck with files as they're+	 * being restored.+	 */+	const int	critical_flags = 0+#ifdef SF_IMMUTABLE+	    | SF_IMMUTABLE+#endif+#ifdef UF_IMMUTABLE+	    | UF_IMMUTABLE+#endif+#ifdef SF_APPEND+	    | SF_APPEND+#endif+#ifdef UF_APPEND+	    | UF_APPEND+#endif+#if defined(FS_APPEND_FL)+	    | FS_APPEND_FL+#elif defined(EXT2_APPEND_FL)+	    | EXT2_APPEND_FL+#endif+#if defined(FS_IMMUTABLE_FL)+	    | FS_IMMUTABLE_FL+#elif defined(EXT2_IMMUTABLE_FL)+	    | EXT2_IMMUTABLE_FL+#endif+#ifdef FS_JOURNAL_DATA_FL+	    | FS_JOURNAL_DATA_FL+#endif+	;++	if (a->todo & TODO_FFLAGS) {+		archive_entry_fflags(a->entry, &set, &clear);++		/*+		 * The first test encourages the compiler to eliminate+		 * all of this if it's not necessary.+		 */+		if ((critical_flags != 0)  &&  (set & critical_flags)) {+			le = current_fixup(a, a->name);+			if (le == NULL)+				return (ARCHIVE_FATAL);+			le->fixup |= TODO_FFLAGS;+			le->fflags_set = set;+			/* Store the mode if it's not already there. */+			if ((le->fixup & TODO_MODE) == 0)+				le->mode = mode;+		} else {+			r = set_fflags_platform(a, a->fd,+			    a->name, mode, set, clear);+			if (r != ARCHIVE_OK)+				return (r);+		}+	}+	return (ARCHIVE_OK);+}++static int+clear_nochange_fflags(struct archive_write_disk *a)+{+	mode_t		mode = archive_entry_mode(a->entry);+	const int nochange_flags = 0+#ifdef SF_IMMUTABLE+	    | SF_IMMUTABLE+#endif+#ifdef UF_IMMUTABLE+	    | UF_IMMUTABLE+#endif+#ifdef SF_APPEND+	    | SF_APPEND+#endif+#ifdef UF_APPEND+	    | UF_APPEND+#endif+#ifdef EXT2_APPEND_FL+	    | EXT2_APPEND_FL+#endif+#ifdef EXT2_IMMUTABLE_FL+	    | EXT2_IMMUTABLE_FL+#endif+	;++	return (set_fflags_platform(a, a->fd, a->name, mode, 0,+	    nochange_flags));+}+++#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS)+/*+ * BSD reads flags using stat() and sets them with one of {f,l,}chflags()+ */+static int+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,+    mode_t mode, unsigned long set, unsigned long clear)+{+	int r;+	const int sf_mask = 0+#ifdef SF_APPEND+	    | SF_APPEND+#endif+#ifdef SF_ARCHIVED+	    | SF_ARCHIVED+#endif+#ifdef SF_IMMUTABLE+	    | SF_IMMUTABLE+#endif+#ifdef SF_NOUNLINK+	    | SF_NOUNLINK+#endif+	;+	(void)mode; /* UNUSED */++	if (set == 0  && clear == 0)+		return (ARCHIVE_OK);++	/*+	 * XXX Is the stat here really necessary?  Or can I just use+	 * the 'set' flags directly?  In particular, I'm not sure+	 * about the correct approach if we're overwriting an existing+	 * file that already has flags on it. XXX+	 */+	if ((r = lazy_stat(a)) != ARCHIVE_OK)+		return (r);++	a->st.st_flags &= ~clear;+	a->st.st_flags |= set;++	/* Only super-user may change SF_* flags */++	if (a->user_uid != 0)+		a->st.st_flags &= ~sf_mask;++#ifdef HAVE_FCHFLAGS+	/* If platform has fchflags() and we were given an fd, use it. */+	if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0)+		return (ARCHIVE_OK);+#endif+	/*+	 * If we can't use the fd to set the flags, we'll use the+	 * pathname to set flags.  We prefer lchflags() but will use+	 * chflags() if we must.+	 */+#ifdef HAVE_LCHFLAGS+	if (lchflags(name, a->st.st_flags) == 0)+		return (ARCHIVE_OK);+#elif defined(HAVE_CHFLAGS)+	if (S_ISLNK(a->st.st_mode)) {+		archive_set_error(&a->archive, errno,+		    "Can't set file flags on symlink.");+		return (ARCHIVE_WARN);+	}+	if (chflags(name, a->st.st_flags) == 0)+		return (ARCHIVE_OK);+#endif+	archive_set_error(&a->archive, errno,+	    "Failed to set file flags");+	return (ARCHIVE_WARN);+}++#elif (defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS) && \+       defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \+      (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && \+       defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))+/*+ * Linux uses ioctl() to read and write file flags.+ */+static int+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,+    mode_t mode, unsigned long set, unsigned long clear)+{+	int		 ret;+	int		 myfd = fd;+	int newflags, oldflags;+	/*+	 * Linux has no define for the flags that are only settable by+	 * the root user.  This code may seem a little complex, but+	 * there seem to be some Linux systems that lack these+	 * defines. (?)  The code below degrades reasonably gracefully+	 * if sf_mask is incomplete.+	 */+	const int sf_mask = 0+#if defined(FS_IMMUTABLE_FL)+	    | FS_IMMUTABLE_FL+#elif defined(EXT2_IMMUTABLE_FL)+	    | EXT2_IMMUTABLE_FL+#endif+#if defined(FS_APPEND_FL)+	    | FS_APPEND_FL+#elif defined(EXT2_APPEND_FL)+	    | EXT2_APPEND_FL+#endif+#if defined(FS_JOURNAL_DATA_FL)+	    | FS_JOURNAL_DATA_FL+#endif+	;++	if (set == 0 && clear == 0)+		return (ARCHIVE_OK);+	/* Only regular files and dirs can have flags. */+	if (!S_ISREG(mode) && !S_ISDIR(mode))+		return (ARCHIVE_OK);++	/* If we weren't given an fd, open it ourselves. */+	if (myfd < 0) {+		myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC);+		__archive_ensure_cloexec_flag(myfd);+	}+	if (myfd < 0)+		return (ARCHIVE_OK);++	/*+	 * XXX As above, this would be way simpler if we didn't have+	 * to read the current flags from disk. XXX+	 */+	ret = ARCHIVE_OK;++	/* Read the current file flags. */+	if (ioctl(myfd,+#ifdef FS_IOC_GETFLAGS+	    FS_IOC_GETFLAGS,+#else+	    EXT2_IOC_GETFLAGS,+#endif+	    &oldflags) < 0)+		goto fail;++	/* Try setting the flags as given. */+	newflags = (oldflags & ~clear) | set;+	if (ioctl(myfd,+#ifdef FS_IOC_SETFLAGS+	    FS_IOC_SETFLAGS,+#else+	    EXT2_IOC_SETFLAGS,+#endif+	    &newflags) >= 0)+		goto cleanup;+	if (errno != EPERM)+		goto fail;++	/* If we couldn't set all the flags, try again with a subset. */+	newflags &= ~sf_mask;+	oldflags &= sf_mask;+	newflags |= oldflags;+	if (ioctl(myfd,+#ifdef FS_IOC_SETFLAGS+	    FS_IOC_SETFLAGS,+#else+	    EXT2_IOC_SETFLAGS,+#endif+	    &newflags) >= 0)+		goto cleanup;++	/* We couldn't set the flags, so report the failure. */+fail:+	archive_set_error(&a->archive, errno,+	    "Failed to set file flags");+	ret = ARCHIVE_WARN;+cleanup:+	if (fd < 0)+		close(myfd);+	return (ret);+}++#else++/*+ * Of course, some systems have neither BSD chflags() nor Linux' flags+ * support through ioctl().+ */+static int+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,+    mode_t mode, unsigned long set, unsigned long clear)+{+	(void)a; /* UNUSED */+	(void)fd; /* UNUSED */+	(void)name; /* UNUSED */+	(void)mode; /* UNUSED */+	(void)set; /* UNUSED */+	(void)clear; /* UNUSED */+	return (ARCHIVE_OK);+}++#endif /* __linux */++#ifndef HAVE_COPYFILE_H+/* Default is to simply drop Mac extended metadata. */+static int+set_mac_metadata(struct archive_write_disk *a, const char *pathname,+		 const void *metadata, size_t metadata_size)+{+	(void)a; /* UNUSED */+	(void)pathname; /* UNUSED */+	(void)metadata; /* UNUSED */+	(void)metadata_size; /* UNUSED */+	return (ARCHIVE_OK);+}++static int+fixup_appledouble(struct archive_write_disk *a, const char *pathname)+{+	(void)a; /* UNUSED */+	(void)pathname; /* UNUSED */+	return (ARCHIVE_OK);+}+#else++/*+ * On Mac OS, we use copyfile() to unpack the metadata and+ * apply it to the target file.+ */++#if defined(HAVE_SYS_XATTR_H)+static int+copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd)+{+	ssize_t xattr_size;+	char *xattr_names = NULL, *xattr_val = NULL;+	int ret = ARCHIVE_OK, xattr_i;++	xattr_size = flistxattr(tmpfd, NULL, 0, 0);+	if (xattr_size == -1) {+		archive_set_error(&a->archive, errno,+		    "Failed to read metadata(xattr)");+		ret = ARCHIVE_WARN;+		goto exit_xattr;+	}+	xattr_names = malloc(xattr_size);+	if (xattr_names == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory for metadata(xattr)");+		ret = ARCHIVE_FATAL;+		goto exit_xattr;+	}+	xattr_size = flistxattr(tmpfd, xattr_names, xattr_size, 0);+	if (xattr_size == -1) {+		archive_set_error(&a->archive, errno,+		    "Failed to read metadata(xattr)");+		ret = ARCHIVE_WARN;+		goto exit_xattr;+	}+	for (xattr_i = 0; xattr_i < xattr_size;+	    xattr_i += strlen(xattr_names + xattr_i) + 1) {+		char *xattr_val_saved;+		ssize_t s;+		int f;++		s = fgetxattr(tmpfd, xattr_names + xattr_i, NULL, 0, 0, 0);+		if (s == -1) {+			archive_set_error(&a->archive, errno,+			    "Failed to get metadata(xattr)");+			ret = ARCHIVE_WARN;+			goto exit_xattr;+		}+		xattr_val_saved = xattr_val;+		xattr_val = realloc(xattr_val, s);+		if (xattr_val == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Failed to get metadata(xattr)");+			ret = ARCHIVE_WARN;+			free(xattr_val_saved);+			goto exit_xattr;+		}+		s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0);+		if (s == -1) {+			archive_set_error(&a->archive, errno,+			    "Failed to get metadata(xattr)");+			ret = ARCHIVE_WARN;+			goto exit_xattr;+		}+		f = fsetxattr(dffd, xattr_names + xattr_i, xattr_val, s, 0, 0);+		if (f == -1) {+			archive_set_error(&a->archive, errno,+			    "Failed to get metadata(xattr)");+			ret = ARCHIVE_WARN;+			goto exit_xattr;+		}+	}+exit_xattr:+	free(xattr_names);+	free(xattr_val);+	return (ret);+}+#endif++static int+copy_acls(struct archive_write_disk *a, int tmpfd, int dffd)+{+#ifndef HAVE_SYS_ACL_H+	return 0;+#else+	acl_t acl, dfacl = NULL;+	int acl_r, ret = ARCHIVE_OK;++	acl = acl_get_fd(tmpfd);+	if (acl == NULL) {+		if (errno == ENOENT)+			/* There are not any ACLs. */+			return (ret);+		archive_set_error(&a->archive, errno,+		    "Failed to get metadata(acl)");+		ret = ARCHIVE_WARN;+		goto exit_acl;+	}+	dfacl = acl_dup(acl);+	acl_r = acl_set_fd(dffd, dfacl);+	if (acl_r == -1) {+		archive_set_error(&a->archive, errno,+		    "Failed to get metadata(acl)");+		ret = ARCHIVE_WARN;+		goto exit_acl;+	}+exit_acl:+	if (acl)+		acl_free(acl);+	if (dfacl)+		acl_free(dfacl);+	return (ret);+#endif+}++static int+create_tempdatafork(struct archive_write_disk *a, const char *pathname)+{+	struct archive_string tmpdatafork;+	int tmpfd;++	archive_string_init(&tmpdatafork);+	archive_strcpy(&tmpdatafork, "tar.md.XXXXXX");+	tmpfd = mkstemp(tmpdatafork.s);+	if (tmpfd < 0) {+		archive_set_error(&a->archive, errno,+		    "Failed to mkstemp");+		archive_string_free(&tmpdatafork);+		return (-1);+	}+	if (copyfile(pathname, tmpdatafork.s, 0,+	    COPYFILE_UNPACK | COPYFILE_NOFOLLOW+	    | COPYFILE_ACL | COPYFILE_XATTR) < 0) {+		archive_set_error(&a->archive, errno,+		    "Failed to restore metadata");+		close(tmpfd);+		tmpfd = -1;+	}+	unlink(tmpdatafork.s);+	archive_string_free(&tmpdatafork);+	return (tmpfd);+}++static int+copy_metadata(struct archive_write_disk *a, const char *metadata,+    const char *datafork, int datafork_compressed)+{+	int ret = ARCHIVE_OK;++	if (datafork_compressed) {+		int dffd, tmpfd;++		tmpfd = create_tempdatafork(a, metadata);+		if (tmpfd == -1)+			return (ARCHIVE_WARN);++		/*+		 * Do not open the data fork compressed by HFS+ compression+		 * with at least a writing mode(O_RDWR or O_WRONLY). it+		 * makes the data fork uncompressed.+		 */+		dffd = open(datafork, 0);+		if (dffd == -1) {+			archive_set_error(&a->archive, errno,+			    "Failed to open the data fork for metadata");+			close(tmpfd);+			return (ARCHIVE_WARN);+		}++#if defined(HAVE_SYS_XATTR_H)+		ret = copy_xattrs(a, tmpfd, dffd);+		if (ret == ARCHIVE_OK)+#endif+			ret = copy_acls(a, tmpfd, dffd);+		close(tmpfd);+		close(dffd);+	} else {+		if (copyfile(metadata, datafork, 0,+		    COPYFILE_UNPACK | COPYFILE_NOFOLLOW+		    | COPYFILE_ACL | COPYFILE_XATTR) < 0) {+			archive_set_error(&a->archive, errno,+			    "Failed to restore metadata");+			ret = ARCHIVE_WARN;+		}+	}+	return (ret);+}++static int+set_mac_metadata(struct archive_write_disk *a, const char *pathname,+		 const void *metadata, size_t metadata_size)+{+	struct archive_string tmp;+	ssize_t written;+	int fd;+	int ret = ARCHIVE_OK;++	/* This would be simpler if copyfile() could just accept the+	 * metadata as a block of memory; then we could sidestep this+	 * silly dance of writing the data to disk just so that+	 * copyfile() can read it back in again. */+	archive_string_init(&tmp);+	archive_strcpy(&tmp, pathname);+	archive_strcat(&tmp, ".XXXXXX");+	fd = mkstemp(tmp.s);++	if (fd < 0) {+		archive_set_error(&a->archive, errno,+				  "Failed to restore metadata");+		archive_string_free(&tmp);+		return (ARCHIVE_WARN);+	}+	written = write(fd, metadata, metadata_size);+	close(fd);+	if ((size_t)written != metadata_size) {+		archive_set_error(&a->archive, errno,+				  "Failed to restore metadata");+		ret = ARCHIVE_WARN;+	} else {+		int compressed;++#if defined(UF_COMPRESSED)+		if ((a->todo & TODO_HFS_COMPRESSION) != 0 &&+		    (ret = lazy_stat(a)) == ARCHIVE_OK)+			compressed = a->st.st_flags & UF_COMPRESSED;+		else+#endif+			compressed = 0;+		ret = copy_metadata(a, tmp.s, pathname, compressed);+	}+	unlink(tmp.s);+	archive_string_free(&tmp);+	return (ret);+}++static int+fixup_appledouble(struct archive_write_disk *a, const char *pathname)+{+	char buff[8];+	struct stat st;+	const char *p;+	struct archive_string datafork;+	int fd = -1, ret = ARCHIVE_OK;++	archive_string_init(&datafork);+	/* Check if the current file name is a type of the resource+	 * fork file. */+	p = strrchr(pathname, '/');+	if (p == NULL)+		p = pathname;+	else+		p++;+	if (p[0] != '.' || p[1] != '_')+		goto skip_appledouble;++	/*+	 * Check if the data fork file exists.+	 *+	 * TODO: Check if this write disk object has handled it.+	 */+	archive_strncpy(&datafork, pathname, p - pathname);+	archive_strcat(&datafork, p + 2);+	if (lstat(datafork.s, &st) == -1 ||+	    (st.st_mode & AE_IFMT) != AE_IFREG)+		goto skip_appledouble;++	/*+	 * Check if the file is in the AppleDouble form.+	 */+	fd = open(pathname, O_RDONLY | O_BINARY | O_CLOEXEC);+	__archive_ensure_cloexec_flag(fd);+	if (fd == -1) {+		archive_set_error(&a->archive, errno,+		    "Failed to open a restoring file");+		ret = ARCHIVE_WARN;+		goto skip_appledouble;+	}+	if (read(fd, buff, 8) == -1) {+		archive_set_error(&a->archive, errno,+		    "Failed to read a restoring file");+		close(fd);+		ret = ARCHIVE_WARN;+		goto skip_appledouble;+	}+	close(fd);+	/* Check AppleDouble Magic Code. */+	if (archive_be32dec(buff) != 0x00051607)+		goto skip_appledouble;+	/* Check AppleDouble Version. */+	if (archive_be32dec(buff+4) != 0x00020000)+		goto skip_appledouble;++	ret = copy_metadata(a, pathname, datafork.s,+#if defined(UF_COMPRESSED)+	    st.st_flags & UF_COMPRESSED);+#else+	    0);+#endif+	if (ret == ARCHIVE_OK) {+		unlink(pathname);+		ret = ARCHIVE_EOF;+	}+skip_appledouble:+	archive_string_free(&datafork);+	return (ret);+}+#endif++#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX+/*+ * Restore extended attributes -  Linux, Darwin and AIX implementations:+ * AIX' ea interface is syntaxwise identical to the Linux xattr interface.+ */+static int+set_xattrs(struct archive_write_disk *a)+{+	struct archive_entry *entry = a->entry;+	struct archive_string errlist;+	int ret = ARCHIVE_OK;+	int i = archive_entry_xattr_reset(entry);+	short fail = 0;++	archive_string_init(&errlist);++	while (i--) {+		const char *name;+		const void *value;+		size_t size;+		int e;++		archive_entry_xattr_next(entry, &name, &value, &size);++		if (name == NULL)+			continue;+#if ARCHIVE_XATTR_LINUX+		/* Linux: quietly skip POSIX.1e ACL extended attributes */+		if (strncmp(name, "system.", 7) == 0 &&+		   (strcmp(name + 7, "posix_acl_access") == 0 ||+		    strcmp(name + 7, "posix_acl_default") == 0))+			continue;+		if (strncmp(name, "trusted.SGI_", 12) == 0 &&+		   (strcmp(name + 12, "ACL_DEFAULT") == 0 ||+		    strcmp(name + 12, "ACL_FILE") == 0))+			continue;++		/* Linux: xfsroot namespace is obsolete and unsupported */+		if (strncmp(name, "xfsroot.", 8) == 0) {+			fail = 1;+			archive_strcat(&errlist, name);+			archive_strappend_char(&errlist, ' ');+			continue;+		}+#endif++		if (a->fd >= 0) {+#if ARCHIVE_XATTR_LINUX+			e = fsetxattr(a->fd, name, value, size, 0);+#elif ARCHIVE_XATTR_DARWIN+			e = fsetxattr(a->fd, name, value, size, 0, 0);+#elif ARCHIVE_XATTR_AIX+			e = fsetea(a->fd, name, value, size, 0);+#endif+		} else {+#if ARCHIVE_XATTR_LINUX+			e = lsetxattr(archive_entry_pathname(entry),+			    name, value, size, 0);+#elif ARCHIVE_XATTR_DARWIN+			e = setxattr(archive_entry_pathname(entry),+			    name, value, size, 0, XATTR_NOFOLLOW);+#elif ARCHIVE_XATTR_AIX+			e = lsetea(archive_entry_pathname(entry),+			    name, value, size, 0);+#endif+		}+		if (e == -1) {+			ret = ARCHIVE_WARN;+			archive_strcat(&errlist, name);+			archive_strappend_char(&errlist, ' ');+			if (errno != ENOTSUP && errno != ENOSYS)+				fail = 1;+		}+	}++	if (ret == ARCHIVE_WARN) {+		if (fail && errlist.length > 0) {+			errlist.length--;+			errlist.s[errlist.length] = '\0';+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Cannot restore extended attributes: %s",+			    errlist.s);+		} else+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Cannot restore extended "+			    "attributes on this file system.");+	}++	archive_string_free(&errlist);+	return (ret);+}+#elif ARCHIVE_XATTR_FREEBSD+/*+ * Restore extended attributes -  FreeBSD implementation+ */+static int+set_xattrs(struct archive_write_disk *a)+{+	struct archive_entry *entry = a->entry;+	struct archive_string errlist;+	int ret = ARCHIVE_OK;+	int i = archive_entry_xattr_reset(entry);+	short fail = 0;++	archive_string_init(&errlist);++	while (i--) {+		const char *name;+		const void *value;+		size_t size;+		archive_entry_xattr_next(entry, &name, &value, &size);+		if (name != NULL) {+			int e;+			int namespace;++			if (strncmp(name, "user.", 5) == 0) {+				/* "user." attributes go to user namespace */+				name += 5;+				namespace = EXTATTR_NAMESPACE_USER;+			} else {+				/* Other namespaces are unsupported */+				archive_strcat(&errlist, name);+				archive_strappend_char(&errlist, ' ');+				fail = 1;+				ret = ARCHIVE_WARN;+				continue;+			}++			if (a->fd >= 0) {+				e = extattr_set_fd(a->fd, namespace, name,+				    value, size);+			} else {+				e = extattr_set_link(+				    archive_entry_pathname(entry), namespace,+				    name, value, size);+			}+			if (e != (int)size) {+				archive_strcat(&errlist, name);+				archive_strappend_char(&errlist, ' ');+				ret = ARCHIVE_WARN;+				if (errno != ENOTSUP && errno != ENOSYS)+					fail = 1;+			}+		}+	}++	if (ret == ARCHIVE_WARN) {+		if (fail && errlist.length > 0) {+			errlist.length--;+			errlist.s[errlist.length] = '\0';++			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Cannot restore extended attributes: %s",+			    errlist.s);+		} else+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Cannot restore extended "+			    "attributes on this file system.");+	}++	archive_string_free(&errlist);+	return (ret);+}+#else+/*+ * Restore extended attributes - stub implementation for unsupported systems+ */+static int+set_xattrs(struct archive_write_disk *a)+{+	static int warning_done = 0;++	/* If there aren't any extended attributes, then it's okay not+	 * to extract them, otherwise, issue a single warning. */+	if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) {+		warning_done = 1;+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Cannot restore extended attributes on this system");+		return (ARCHIVE_WARN);+	}+	/* Warning was already emitted; suppress further warnings. */+	return (ARCHIVE_OK);+}+#endif++/*+ * Test if file on disk is older than entry.+ */+static int+older(struct stat *st, struct archive_entry *entry)+{+	/* First, test the seconds and return if we have a definite answer. */+	/* Definitely older. */+	if (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry)))+		return (1);+	/* Definitely younger. */+	if (to_int64_time(st->st_mtime) > to_int64_time(archive_entry_mtime(entry)))+		return (0);+	/* If this platform supports fractional seconds, try those. */+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC+	/* Definitely older. */+	if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry))+		return (1);+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC+	/* Definitely older. */+	if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry))+		return (1);+#elif HAVE_STRUCT_STAT_ST_MTIME_N+	/* older. */+	if (st->st_mtime_n < archive_entry_mtime_nsec(entry))+		return (1);+#elif HAVE_STRUCT_STAT_ST_UMTIME+	/* older. */+	if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry))+		return (1);+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC+	/* older. */+	if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry))+		return (1);+#else+	/* This system doesn't have high-res timestamps. */+#endif+	/* Same age or newer, so not older. */+	return (0);+}++#ifndef ARCHIVE_ACL_SUPPORT+int+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,+    struct archive_acl *abstract_acl, __LA_MODE_T mode)+{+	(void)a; /* UNUSED */+	(void)fd; /* UNUSED */+	(void)name; /* UNUSED */+	(void)abstract_acl; /* UNUSED */+	(void)mode; /* UNUSED */+	return (ARCHIVE_OK);+}+#endif++#endif /* !_WIN32 || __CYGWIN__ */+
+ c/archive_write_disk_private.h view
@@ -0,0 +1,45 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer+ *    in this position and unchanged.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/archive_write_disk_private.h 201086 2009-12-28 02:17:53Z kientzle $+ */++#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED+#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++#include "archive_platform_acl.h"+#include "archive_acl_private.h"+#include "archive_entry.h"++struct archive_write_disk;++int archive_write_disk_set_acls(struct archive *, int, const char *,+    struct archive_acl *, __LA_MODE_T);++#endif
+ c/archive_write_disk_set_standard_lookup.c view
@@ -0,0 +1,263 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_disk_set_standard_lookup.c 201083 2009-12-28 02:09:57Z kientzle $");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_GRP_H+#include <grp.h>+#endif+#ifdef HAVE_PWD_H+#include <pwd.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_read_private.h"+#include "archive_write_disk_private.h"++struct bucket {+	char	*name;+	int	 hash;+	id_t	 id;+};++static const size_t cache_size = 127;+static unsigned int	hash(const char *);+static int64_t	lookup_gid(void *, const char *uname, int64_t);+static int64_t	lookup_uid(void *, const char *uname, int64_t);+static void	cleanup(void *);++/*+ * Installs functions that use getpwnam()/getgrnam()---along with+ * a simple cache to accelerate such lookups---into the archive_write_disk+ * object.  This is in a separate file because getpwnam()/getgrnam()+ * can pull in a LOT of library code (including NIS/LDAP functions, which+ * pull in DNS resolvers, etc).  This can easily top 500kB, which makes+ * it inappropriate for some space-constrained applications.+ *+ * Applications that are size-sensitive may want to just use the+ * real default functions (defined in archive_write_disk.c) that just+ * use the uid/gid without the lookup.  Or define your own custom functions+ * if you prefer.+ *+ * TODO: Replace these hash tables with simpler move-to-front LRU+ * lists with a bounded size (128 items?).  The hash is a bit faster,+ * but has a bad pathology in which it thrashes a single bucket.  Even+ * walking a list of 128 items is a lot faster than calling+ * getpwnam()!+ */+int+archive_write_disk_set_standard_lookup(struct archive *a)+{+	struct bucket *ucache = calloc(cache_size, sizeof(struct bucket));+	struct bucket *gcache = calloc(cache_size, sizeof(struct bucket));+	if (ucache == NULL || gcache == NULL) {+		free(ucache);+		free(gcache);+		return (ARCHIVE_FATAL);+	}+	archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup);+	archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup);+	return (ARCHIVE_OK);+}++static int64_t+lookup_gid(void *private_data, const char *gname, int64_t gid)+{+	int h;+	struct bucket *b;+	struct bucket *gcache = (struct bucket *)private_data;++	/* If no gname, just use the gid provided. */+	if (gname == NULL || *gname == '\0')+		return (gid);++	/* Try to find gname in the cache. */+	h = hash(gname);+	b = &gcache[h % cache_size ];+	if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0)+		return ((gid_t)b->id);++	/* Free the cache slot for a new entry. */+	free(b->name);+	b->name = strdup(gname);+	/* Note: If strdup fails, that's okay; we just won't cache. */+	b->hash = h;+#if HAVE_GRP_H+#  if HAVE_GETGRNAM_R+	{+		char _buffer[128];+		size_t bufsize = 128;+		char *buffer = _buffer;+		char *allocated = NULL;+		struct group	grent, *result;+		int r;++		for (;;) {+			result = &grent; /* Old getgrnam_r ignores last arg. */+			r = getgrnam_r(gname, &grent, buffer, bufsize, &result);+			if (r == 0)+				break;+			if (r != ERANGE)+				break;+			bufsize *= 2;+			free(allocated);+			allocated = malloc(bufsize);+			if (allocated == NULL)+				break;+			buffer = allocated;+		}+		if (result != NULL)+			gid = result->gr_gid;+		free(allocated);+	}+#  else /* HAVE_GETGRNAM_R */+	{+		struct group *result;++		result = getgrnam(gname);+		if (result != NULL)+			gid = result->gr_gid;+	}+#  endif /* HAVE_GETGRNAM_R */+#elif defined(_WIN32) && !defined(__CYGWIN__)+	/* TODO: do a gname->gid lookup for Windows. */+#else+	#error No way to perform gid lookups on this platform+#endif+	b->id = (gid_t)gid;++	return (gid);+}++static int64_t+lookup_uid(void *private_data, const char *uname, int64_t uid)+{+	int h;+	struct bucket *b;+	struct bucket *ucache = (struct bucket *)private_data;++	/* If no uname, just use the uid provided. */+	if (uname == NULL || *uname == '\0')+		return (uid);++	/* Try to find uname in the cache. */+	h = hash(uname);+	b = &ucache[h % cache_size ];+	if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0)+		return ((uid_t)b->id);++	/* Free the cache slot for a new entry. */+	free(b->name);+	b->name = strdup(uname);+	/* Note: If strdup fails, that's okay; we just won't cache. */+	b->hash = h;+#if HAVE_PWD_H+#  if HAVE_GETPWNAM_R+	{+		char _buffer[128];+		size_t bufsize = 128;+		char *buffer = _buffer;+		char *allocated = NULL;+		struct passwd	pwent, *result;+		int r;++		for (;;) {+			result = &pwent; /* Old getpwnam_r ignores last arg. */+			r = getpwnam_r(uname, &pwent, buffer, bufsize, &result);+			if (r == 0)+				break;+			if (r != ERANGE)+				break;+			bufsize *= 2;+			free(allocated);+			allocated = malloc(bufsize);+			if (allocated == NULL)+				break;+			buffer = allocated;+		}+		if (result != NULL)+			uid = result->pw_uid;+		free(allocated);+	}+#  else /* HAVE_GETPWNAM_R */+	{+		struct passwd *result;++		result = getpwnam(uname);+		if (result != NULL)+			uid = result->pw_uid;+	}+#endif	/* HAVE_GETPWNAM_R */+#elif defined(_WIN32) && !defined(__CYGWIN__)+	/* TODO: do a uname->uid lookup for Windows. */+#else+	#error No way to look up uids on this platform+#endif+	b->id = (uid_t)uid;++	return (uid);+}++static void+cleanup(void *private)+{+	size_t i;+	struct bucket *cache = (struct bucket *)private;++	for (i = 0; i < cache_size; i++)+		free(cache[i].name);+	free(cache);+}+++static unsigned int+hash(const char *p)+{+	/* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,+	   as used by ELF for hashing function names. */+	unsigned g, h = 0;+	while (*p != '\0') {+		h = (h << 4) + *p++;+		if ((g = h & 0xF0000000) != 0) {+			h ^= g >> 24;+			h &= 0x0FFFFFFF;+		}+	}+	return h;+}
+ c/archive_write_disk_windows.c view
@@ -0,0 +1,2834 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer+ *    in this position and unchanged.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#if defined(_WIN32) && !defined(__CYGWIN__)++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_SYS_UTIME_H+#include <sys/utime.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#include <winioctl.h>++/* TODO: Support Mac OS 'quarantine' feature.  This is really just a+ * standard tag to mark files that have been downloaded as "tainted".+ * On Mac OS, we should mark the extracted files as tainted if the+ * archive being read was tainted.  Windows has a similar feature; we+ * should investigate ways to support this generically. */++#include "archive.h"+#include "archive_acl_private.h"+#include "archive_string.h"+#include "archive_entry.h"+#include "archive_private.h"++#ifndef O_BINARY+#define O_BINARY 0+#endif+#ifndef IO_REPARSE_TAG_SYMLINK+/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */+#define	IO_REPARSE_TAG_SYMLINK 0xA000000CL+#endif++static BOOL SetFilePointerEx_perso(HANDLE hFile,+                             LARGE_INTEGER liDistanceToMove,+                             PLARGE_INTEGER lpNewFilePointer,+                             DWORD dwMoveMethod)+{+	LARGE_INTEGER li;+	li.QuadPart = liDistanceToMove.QuadPart;+	li.LowPart = SetFilePointer(+	    hFile, li.LowPart, &li.HighPart, dwMoveMethod);+	if(lpNewFilePointer) {+		lpNewFilePointer->QuadPart = li.QuadPart;+	}+	return li.LowPart != (DWORD)-1 || GetLastError() == NO_ERROR;+}++struct fixup_entry {+	struct fixup_entry	*next;+	struct archive_acl	 acl;+	mode_t			 mode;+	int64_t			 atime;+	int64_t                  birthtime;+	int64_t			 mtime;+	int64_t			 ctime;+	unsigned long		 atime_nanos;+	unsigned long            birthtime_nanos;+	unsigned long		 mtime_nanos;+	unsigned long		 ctime_nanos;+	unsigned long		 fflags_set;+	int			 fixup; /* bitmask of what needs fixing */+	wchar_t			*name;+};++/*+ * We use a bitmask to track which operations remain to be done for+ * this file.  In particular, this helps us avoid unnecessary+ * operations when it's possible to take care of one step as a+ * side-effect of another.  For example, mkdir() can specify the mode+ * for the newly-created object but symlink() cannot.  This means we+ * can skip chmod() if mkdir() succeeded, but we must explicitly+ * chmod() if we're trying to create a directory that already exists+ * (mkdir() failed) or if we're restoring a symlink.  Similarly, we+ * need to verify UID/GID before trying to restore SUID/SGID bits;+ * that verification can occur explicitly through a stat() call or+ * implicitly because of a successful chown() call.+ */+#define	TODO_MODE_FORCE		0x40000000+#define	TODO_MODE_BASE		0x20000000+#define	TODO_SUID		0x10000000+#define	TODO_SUID_CHECK		0x08000000+#define	TODO_SGID		0x04000000+#define	TODO_SGID_CHECK		0x02000000+#define	TODO_MODE		(TODO_MODE_BASE|TODO_SUID|TODO_SGID)+#define	TODO_TIMES		ARCHIVE_EXTRACT_TIME+#define	TODO_OWNER		ARCHIVE_EXTRACT_OWNER+#define	TODO_FFLAGS		ARCHIVE_EXTRACT_FFLAGS+#define	TODO_ACLS		ARCHIVE_EXTRACT_ACL+#define	TODO_XATTR		ARCHIVE_EXTRACT_XATTR+#define	TODO_MAC_METADATA	ARCHIVE_EXTRACT_MAC_METADATA++struct archive_write_disk {+	struct archive	archive;++	mode_t			 user_umask;+	struct fixup_entry	*fixup_list;+	struct fixup_entry	*current_fixup;+	int64_t			 user_uid;+	int			 skip_file_set;+	int64_t			 skip_file_dev;+	int64_t			 skip_file_ino;+	time_t			 start_time;++	int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid);+	void  (*cleanup_gid)(void *private);+	void			*lookup_gid_data;+	int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid);+	void  (*cleanup_uid)(void *private);+	void			*lookup_uid_data;++	/*+	 * Full path of last file to satisfy symlink checks.+	 */+	struct archive_wstring	path_safe;++	/*+	 * Cached stat data from disk for the current entry.+	 * If this is valid, pst points to st.  Otherwise,+	 * pst is null.+	 */+	BY_HANDLE_FILE_INFORMATION		 st;+	BY_HANDLE_FILE_INFORMATION		*pst;++	/* Information about the object being restored right now. */+	struct archive_entry	*entry; /* Entry being extracted. */+	wchar_t			*name; /* Name of entry, possibly edited. */+	struct archive_wstring	 _name_data; /* backing store for 'name' */+	wchar_t			*tmpname; /* Temporary name */+	struct archive_wstring	_tmpname_data; /* backing store for 'tmpname' */+	/* Tasks remaining for this object. */+	int			 todo;+	/* Tasks deferred until end-of-archive. */+	int			 deferred;+	/* Options requested by the client. */+	int			 flags;+	/* Handle for the file we're restoring. */+	HANDLE		 fh;+	/* Current offset for writing data to the file. */+	int64_t			 offset;+	/* Last offset actually written to disk. */+	int64_t			 fd_offset;+	/* Total bytes actually written to files. */+	int64_t			 total_bytes_written;+	/* Maximum size of file, -1 if unknown. */+	int64_t			 filesize;+	/* Dir we were in before this restore; only for deep paths. */+	int			 restore_pwd;+	/* Mode we should use for this entry; affected by _PERM and umask. */+	mode_t			 mode;+	/* UID/GID to use in restoring this entry. */+	int64_t			 uid;+	int64_t			 gid;+};++/*+ * Default mode for dirs created automatically (will be modified by umask).+ * Note that POSIX specifies 0777 for implicitly-created dirs, "modified+ * by the process' file creation mask."+ */+#define	DEFAULT_DIR_MODE 0777+/*+ * Dir modes are restored in two steps:  During the extraction, the permissions+ * in the archive are modified to match the following limits.  During+ * the post-extract fixup pass, the permissions from the archive are+ * applied.+ */+#define	MINIMUM_DIR_MODE 0700+#define	MAXIMUM_DIR_MODE 0775++static int	disk_unlink(const wchar_t *);+static int	disk_rmdir(const wchar_t *);+static int	check_symlinks(struct archive_write_disk *);+static int	create_filesystem_object(struct archive_write_disk *);+static struct fixup_entry *current_fixup(struct archive_write_disk *,+		    const wchar_t *pathname);+static int	cleanup_pathname(struct archive_write_disk *);+static int	create_dir(struct archive_write_disk *, wchar_t *);+static int	create_parent_dir(struct archive_write_disk *, wchar_t *);+static int	la_chmod(const wchar_t *, mode_t);+static int	la_mktemp(struct archive_write_disk *);+static int	older(BY_HANDLE_FILE_INFORMATION *, struct archive_entry *);+static int	permissive_name_w(struct archive_write_disk *);+static int	restore_entry(struct archive_write_disk *);+static int	set_acls(struct archive_write_disk *, HANDLE h,+		    const wchar_t *, struct archive_acl *);+static int	set_xattrs(struct archive_write_disk *);+static int	clear_nochange_fflags(struct archive_write_disk *);+static int	set_fflags(struct archive_write_disk *);+static int	set_fflags_platform(const wchar_t *, unsigned long,+		    unsigned long);+static int	set_ownership(struct archive_write_disk *);+static int	set_mode(struct archive_write_disk *, int mode);+static int	set_times(struct archive_write_disk *, HANDLE, int,+		    const wchar_t *, time_t, long, time_t, long, time_t,+		    long, time_t, long);+static int	set_times_from_entry(struct archive_write_disk *);+static struct fixup_entry *sort_dir_list(struct fixup_entry *p);+static ssize_t	write_data_block(struct archive_write_disk *,+		    const char *, size_t);++static struct archive_vtable *archive_write_disk_vtable(void);++static int	_archive_write_disk_close(struct archive *);+static int	_archive_write_disk_free(struct archive *);+static int	_archive_write_disk_header(struct archive *,+		    struct archive_entry *);+static int64_t	_archive_write_disk_filter_bytes(struct archive *, int);+static int	_archive_write_disk_finish_entry(struct archive *);+static ssize_t	_archive_write_disk_data(struct archive *, const void *,+		    size_t);+static ssize_t	_archive_write_disk_data_block(struct archive *, const void *,+		    size_t, int64_t);++#define bhfi_dev(bhfi)	((bhfi)->dwVolumeSerialNumber)+/* Treat FileIndex as i-node. We should remove a sequence number+ * which is high-16-bits of nFileIndexHigh. */+#define bhfi_ino(bhfi)	\+	((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \+    + (bhfi)->nFileIndexLow)+#define bhfi_size(bhfi)	\+    ((((int64_t)(bhfi)->nFileSizeHigh) << 32) + (bhfi)->nFileSizeLow)++static int+file_information(struct archive_write_disk *a, wchar_t *path,+    BY_HANDLE_FILE_INFORMATION *st, mode_t *mode, int sim_lstat)+{+	HANDLE h;+	int r;+	DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;+	WIN32_FIND_DATAW	findData;++	if (sim_lstat || mode != NULL) {+		h = FindFirstFileW(path, &findData);+		if (h == INVALID_HANDLE_VALUE &&+		    GetLastError() == ERROR_INVALID_NAME) {+			wchar_t *full;+			full = __la_win_permissive_name_w(path);+			h = FindFirstFileW(full, &findData);+			free(full);+		}+		if (h == INVALID_HANDLE_VALUE) {+			la_dosmaperr(GetLastError());+			return (-1);+		}+		FindClose(h);+	}++	/* Is symlink file ? */+	if (sim_lstat && +	    ((findData.dwFileAttributes+		        & FILE_ATTRIBUTE_REPARSE_POINT) &&+		(findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)))+		flag |= FILE_FLAG_OPEN_REPARSE_POINT;++	h = CreateFileW(a->name, 0, 0, NULL,+	    OPEN_EXISTING, flag, NULL);+	if (h == INVALID_HANDLE_VALUE &&+	    GetLastError() == ERROR_INVALID_NAME) {+		wchar_t *full;+		full = __la_win_permissive_name_w(path);+		h = CreateFileW(full, 0, 0, NULL,+		    OPEN_EXISTING, flag, NULL);+		free(full);+	}+	if (h == INVALID_HANDLE_VALUE) {+		la_dosmaperr(GetLastError());+		return (-1);+	}+	r = GetFileInformationByHandle(h, st);+	CloseHandle(h);+	if (r == 0) {+		la_dosmaperr(GetLastError());+		return (-1);+	}++	if (mode == NULL)+		return (0);++	*mode = S_IRUSR | S_IRGRP | S_IROTH;+	if ((st->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)+		*mode |= S_IWUSR | S_IWGRP | S_IWOTH;+	if ((st->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&+	    findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)+		*mode |= S_IFLNK;+	else if (st->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)+		*mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;+	else {+		const wchar_t *p;++		*mode |= S_IFREG;+		p = wcsrchr(path, L'.');+		if (p != NULL && wcslen(p) == 4) {+			switch (p[1]) {+			case L'B': case L'b':+				if ((p[2] == L'A' || p[2] == L'a' ) &&+				    (p[3] == L'T' || p[3] == L't' ))+					*mode |= S_IXUSR | S_IXGRP | S_IXOTH;+				break;+			case L'C': case L'c':+				if (((p[2] == L'M' || p[2] == L'm' ) &&+				    (p[3] == L'D' || p[3] == L'd' )))+					*mode |= S_IXUSR | S_IXGRP | S_IXOTH;+				break;+			case L'E': case L'e':+				if ((p[2] == L'X' || p[2] == L'x' ) &&+				    (p[3] == L'E' || p[3] == L'e' ))+					*mode |= S_IXUSR | S_IXGRP | S_IXOTH;+				break;+			default:+				break;+			}+		}+	}+	return (0);+}++/* + * Note: The path, for example, "aa/a/../b../c" will be converted to "aa/c"+ * by GetFullPathNameW() W32 API, which __la_win_permissive_name_w uses.+ * It means we cannot handle multiple dirs in one archive_entry.+ * So we have to make the full-pathname in another way, which does not+ * break "../" path string.+ */+static int+permissive_name_w(struct archive_write_disk *a)+{+	wchar_t *wn, *wnp;+	wchar_t *ws, *wsp;+	DWORD l;++	wnp = a->name;+	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&+	    wnp[2] == L'?' && wnp[3] == L'\\')+		/* We have already a permissive name. */+		return (0);++	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&+		wnp[2] == L'.' && wnp[3] == L'\\') {+		/* This is a device name */+		if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||+		     (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&+			 wnp[5] == L':' && wnp[6] == L'\\') {+			wnp[2] = L'?';/* Not device name. */+			return (0);+		}+	}++	/*+	 * A full-pathname starting with a drive name like "C:\abc".+	 */+	if (((wnp[0] >= L'a' && wnp[0] <= L'z') ||+	     (wnp[0] >= L'A' && wnp[0] <= L'Z')) &&+		 wnp[1] == L':' && wnp[2] == L'\\') {+		wn = _wcsdup(wnp);+		if (wn == NULL)+			return (-1);+		archive_wstring_ensure(&(a->_name_data), 4 + wcslen(wn) + 1);+		a->name = a->_name_data.s;+		/* Prepend "\\?\" */+		archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);+		archive_wstrcat(&(a->_name_data), wn);+		free(wn);+		return (0);+	}++	/*+	 * A full-pathname pointing to a network drive+	 * like "\\<server-name>\<share-name>\file". +	 */+	if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {+		const wchar_t *p = &wnp[2];++		/* Skip server-name letters. */+		while (*p != L'\\' && *p != L'\0')+			++p;+		if (*p == L'\\') {+			const wchar_t *rp = ++p;+			/* Skip share-name letters. */+			while (*p != L'\\' && *p != L'\0')+				++p;+			if (*p == L'\\' && p != rp) {+				/* Now, match patterns such as+				 * "\\server-name\share-name\" */+				wn = _wcsdup(wnp);+				if (wn == NULL)+					return (-1);+				archive_wstring_ensure(&(a->_name_data),+					8 + wcslen(wn) + 1);+				a->name = a->_name_data.s;+				/* Prepend "\\?\UNC\" */+				archive_wstrncpy(&(a->_name_data),+					L"\\\\?\\UNC\\", 8);+				archive_wstrcat(&(a->_name_data), wn+2);+				free(wn);+				return (0);+			}+		}+		return (0);+	}++	/*+	 * Get current working directory.+	 */+	l = GetCurrentDirectoryW(0, NULL);+	if (l == 0)+		return (-1);+	ws = malloc(l * sizeof(wchar_t));+	l = GetCurrentDirectoryW(l, ws);+	if (l == 0) {+		free(ws);+		return (-1);+	}+	wsp = ws;++	/*+	 * A full-pathname starting without a drive name like "\abc".+	 */+	if (wnp[0] == L'\\') {+		wn = _wcsdup(wnp);+		if (wn == NULL)+			return (-1);+		archive_wstring_ensure(&(a->_name_data),+			4 + 2 + wcslen(wn) + 1);+		a->name = a->_name_data.s;+		/* Prepend "\\?\" and drive name. */+		archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);+		archive_wstrncat(&(a->_name_data), wsp, 2);+		archive_wstrcat(&(a->_name_data), wn);+		free(wsp);+		free(wn);+		return (0);+	}++	wn = _wcsdup(wnp);+	if (wn == NULL)+		return (-1);+	archive_wstring_ensure(&(a->_name_data), 4 + l + 1 + wcslen(wn) + 1);+	a->name = a->_name_data.s;+	/* Prepend "\\?\" and drive name if not already added. */+	if (l > 3 && wsp[0] == L'\\' && wsp[1] == L'\\' &&+		wsp[2] == L'?' && wsp[3] == L'\\')+	{+		archive_wstrncpy(&(a->_name_data), wsp, l);+	}+	else if (l > 2 && wsp[0] == L'\\' && wsp[1] == L'\\' && wsp[2] != L'\\')+	{+		archive_wstrncpy(&(a->_name_data), L"\\\\?\\UNC\\", 8);+		archive_wstrncat(&(a->_name_data), wsp+2, l-2);+	}+	else+	{+		archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);+		archive_wstrncat(&(a->_name_data), wsp, l);+	}+	archive_wstrncat(&(a->_name_data), L"\\", 1);+	archive_wstrcat(&(a->_name_data), wn);+	a->name = a->_name_data.s;+	free(wsp);+	free(wn);+	return (0);+}++static int+la_chmod(const wchar_t *path, mode_t mode)+{+	DWORD attr;+	BOOL r;+	wchar_t *fullname;+	int ret = 0;++	fullname = NULL;+	attr = GetFileAttributesW(path);+	if (attr == (DWORD)-1 &&+	    GetLastError() == ERROR_INVALID_NAME) {+		fullname = __la_win_permissive_name_w(path);+		attr = GetFileAttributesW(fullname);+	}+	if (attr == (DWORD)-1) {+		la_dosmaperr(GetLastError());+		ret = -1;+		goto exit_chmode;+	}+	if (mode & _S_IWRITE)+		attr &= ~FILE_ATTRIBUTE_READONLY;+	else+		attr |= FILE_ATTRIBUTE_READONLY;+	if (fullname != NULL)+		r = SetFileAttributesW(fullname, attr);+	else+		r = SetFileAttributesW(path, attr);+	if (r == 0) {+		la_dosmaperr(GetLastError());+		ret = -1;+	}+exit_chmode:+	free(fullname);+	return (ret);+}++static int+la_mktemp(struct archive_write_disk *a)+{+	int fd;+	mode_t mode;++	archive_wstring_empty(&(a->_tmpname_data));+	archive_wstrcpy(&(a->_tmpname_data), a->name);+	archive_wstrcat(&(a->_tmpname_data), L".XXXXXX");+	a->tmpname = a->_tmpname_data.s;++	fd = __archive_mkstemp(a->tmpname);+	if (fd == -1)+		return -1;++	mode = a->mode & 0777 & ~a->user_umask;+	if (la_chmod(a->tmpname, mode) == -1) {+		la_dosmaperr(GetLastError());+		_close(fd);+		return -1;+	}+	return (fd);+}++static void *+la_GetFunctionKernel32(const char *name)+{+	static HINSTANCE lib;+	static int set;+	if (!set) {+		set = 1;+		lib = LoadLibrary(TEXT("kernel32.dll"));+	}+	if (lib == NULL) {+		fprintf(stderr, "Can't load kernel32.dll?!\n");+		exit(1);+	}+	return (void *)GetProcAddress(lib, name);+}++static int+la_CreateHardLinkW(wchar_t *linkname, wchar_t *target)+{+	static BOOLEAN (WINAPI *f)(LPWSTR, LPWSTR, LPSECURITY_ATTRIBUTES);+	static int set;+	BOOL ret;++	if (!set) {+		set = 1;+		f = la_GetFunctionKernel32("CreateHardLinkW");+	}+	if (!f) {+		errno = ENOTSUP;+		return (0);+	}+	ret = (*f)(linkname, target, NULL);+	if (!ret) {+		/* Under windows 2000, it is necessary to remove+		 * the "\\?\" prefix. */+#define IS_UNC(name)	((name[0] == L'U' || name[0] == L'u') &&	\+			 (name[1] == L'N' || name[1] == L'n') &&	\+			 (name[2] == L'C' || name[2] == L'c') &&	\+			 name[3] == L'\\')+		if (!wcsncmp(linkname,L"\\\\?\\", 4)) {+			linkname += 4;+			if (IS_UNC(linkname))+				linkname += 4;+		}+		if (!wcsncmp(target,L"\\\\?\\", 4)) {+			target += 4;+			if (IS_UNC(target))+				target += 4;+		}+#undef IS_UNC+		ret = (*f)(linkname, target, NULL);+	}+	return (ret);+}++/*+ * Create file or directory symolic link+ *+ * If linktype is AE_SYMLINK_TYPE_UNDEFINED (or unknown), guess linktype from+ * the link target+ */+static int+la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target,+    int linktype) {+	static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD);+	static int set;+	wchar_t *ttarget, *p;+	int len;+	DWORD attrs = 0;+	DWORD flags = 0;+	DWORD newflags = 0;+	BOOL ret = 0;++	if (!set) {+		set = 1;+		f = la_GetFunctionKernel32("CreateSymbolicLinkW");+	}+	if (!f)+		return (0);++	len = wcslen(target);+	if (len == 0) {+		errno = EINVAL;+		return(0);+	}+	/*+	 * When writing path targets, we need to translate slashes+	 * to backslashes+	 */+	ttarget = malloc((len + 1) * sizeof(wchar_t));+	if (ttarget == NULL)+		return(0);++	p = ttarget;++	while(*target != L'\0') {+		if (*target == L'/')+			*p = L'\\';+		else+			*p = *target;+		target++;+		p++;+	}+	*p = L'\0';++	/*+	 * In case of undefined symlink type we guess it from the target.+	 * If the target equals ".", "..", ends with a backslash or a+	 * backslash followed by "." or ".." we assume it is a directory+	 * symlink. In all other cases we assume a file symlink.+	 */+	if (linktype != AE_SYMLINK_TYPE_FILE && (+		linktype == AE_SYMLINK_TYPE_DIRECTORY ||+		*(p - 1) == L'\\' || (*(p - 1) == L'.' && (+	    len == 1 || *(p - 2) == L'\\' || ( *(p - 2) == L'.' && (+	    len == 2 || *(p - 3) == L'\\')))))) {+#if defined(SYMBOLIC_LINK_FLAG_DIRECTORY)+		flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;+#else+		flags |= 0x1;+#endif+	}++#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)+	newflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;+#else+	newflags = flags | 0x2;+#endif++	/*+	 * Windows won't overwrite existing links+	 */+	attrs = GetFileAttributesW(linkname);+	if (attrs != INVALID_FILE_ATTRIBUTES) {+		if (attrs & FILE_ATTRIBUTE_DIRECTORY)+			disk_rmdir(linkname);+		else+			disk_unlink(linkname);+	}++	ret = (*f)(linkname, ttarget, newflags);+	/*+	 * Prior to Windows 10 calling CreateSymbolicLinkW() will fail+	 * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set+	 */+	if (!ret) {+		ret = (*f)(linkname, ttarget, flags);+	}+	free(ttarget);+	return (ret);+}++static int+la_ftruncate(HANDLE handle, int64_t length)+{+	LARGE_INTEGER distance;++	if (GetFileType(handle) != FILE_TYPE_DISK) {+		errno = EBADF;+		return (-1);+	}+	distance.QuadPart = length;+	if (!SetFilePointerEx_perso(handle, distance, NULL, FILE_BEGIN)) {+		la_dosmaperr(GetLastError());+		return (-1);+	}+	if (!SetEndOfFile(handle)) {+		la_dosmaperr(GetLastError());+		return (-1);+	}+	return (0);+}++static int+lazy_stat(struct archive_write_disk *a)+{+	if (a->pst != NULL) {+		/* Already have stat() data available. */+		return (ARCHIVE_OK);+	}+	if (a->fh != INVALID_HANDLE_VALUE &&+	    GetFileInformationByHandle(a->fh, &a->st) == 0) {+		a->pst = &a->st;+		return (ARCHIVE_OK);+	}++	/*+	 * XXX At this point, symlinks should not be hit, otherwise+	 * XXX a race occurred.  Do we want to check explicitly for that?+	 */+	if (file_information(a, a->name, &a->st, NULL, 1) == 0) {+		a->pst = &a->st;+		return (ARCHIVE_OK);+	}+	archive_set_error(&a->archive, errno, "Couldn't stat file");+	return (ARCHIVE_WARN);+}++static struct archive_vtable *+archive_write_disk_vtable(void)+{+	static struct archive_vtable av;+	static int inited = 0;++	if (!inited) {+		av.archive_close = _archive_write_disk_close;+		av.archive_filter_bytes = _archive_write_disk_filter_bytes;+		av.archive_free = _archive_write_disk_free;+		av.archive_write_header = _archive_write_disk_header;+		av.archive_write_finish_entry+		    = _archive_write_disk_finish_entry;+		av.archive_write_data = _archive_write_disk_data;+		av.archive_write_data_block = _archive_write_disk_data_block;+		inited = 1;+	}+	return (&av);+}++static int64_t+_archive_write_disk_filter_bytes(struct archive *_a, int n)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	(void)n; /* UNUSED */+	if (n == -1 || n == 0)+		return (a->total_bytes_written);+	return (-1);+}+++int+archive_write_disk_set_options(struct archive *_a, int flags)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;++	a->flags = flags;+	return (ARCHIVE_OK);+}+++/*+ * Extract this entry to disk.+ *+ * TODO: Validate hardlinks.  According to the standards, we're+ * supposed to check each extracted hardlink and squawk if it refers+ * to a file that we didn't restore.  I'm not entirely convinced this+ * is a good idea, but more importantly: Is there any way to validate+ * hardlinks without keeping a complete list of filenames from the+ * entire archive?? Ugh.+ *+ */+static int+_archive_write_disk_header(struct archive *_a, struct archive_entry *entry)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	struct fixup_entry *fe;+	int ret, r;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_write_disk_header");+	archive_clear_error(&a->archive);+	if (a->archive.state & ARCHIVE_STATE_DATA) {+		r = _archive_write_disk_finish_entry(&a->archive);+		if (r == ARCHIVE_FATAL)+			return (r);+	}++	/* Set up for this particular entry. */+	a->pst = NULL;+	a->current_fixup = NULL;+	a->deferred = 0;+	archive_entry_free(a->entry);+	a->entry = NULL;+	a->entry = archive_entry_clone(entry);+	a->fh = INVALID_HANDLE_VALUE;+	a->fd_offset = 0;+	a->offset = 0;+	a->restore_pwd = -1;+	a->uid = a->user_uid;+	a->mode = archive_entry_mode(a->entry);+	if (archive_entry_size_is_set(a->entry))+		a->filesize = archive_entry_size(a->entry);+	else+		a->filesize = -1;+	archive_wstrcpy(&(a->_name_data), archive_entry_pathname_w(a->entry));+	a->name = a->_name_data.s;+	archive_clear_error(&a->archive);++	/*+	 * Clean up the requested path.  This is necessary for correct+	 * dir restores; the dir restore logic otherwise gets messed+	 * up by nonsense like "dir/.".+	 */+	ret = cleanup_pathname(a);+	if (ret != ARCHIVE_OK)+		return (ret);++	/*+	 * Generate a full-pathname and use it from here.+	 */+	if (permissive_name_w(a) < 0) {+		errno = EINVAL;+		return (ARCHIVE_FAILED);+	}++	/*+	 * Query the umask so we get predictable mode settings.+	 * This gets done on every call to _write_header in case the+	 * user edits their umask during the extraction for some+	 * reason.+	 */+	umask(a->user_umask = umask(0));++	/* Figure out what we need to do for this entry. */+	a->todo = TODO_MODE_BASE;+	if (a->flags & ARCHIVE_EXTRACT_PERM) {+		a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */+		/*+		 * SGID requires an extra "check" step because we+		 * cannot easily predict the GID that the system will+		 * assign.  (Different systems assign GIDs to files+		 * based on a variety of criteria, including process+		 * credentials and the gid of the enclosing+		 * directory.)  We can only restore the SGID bit if+		 * the file has the right GID, and we only know the+		 * GID if we either set it (see set_ownership) or if+		 * we've actually called stat() on the file after it+		 * was restored.  Since there are several places at+		 * which we might verify the GID, we need a TODO bit+		 * to keep track.+		 */+		if (a->mode & S_ISGID)+			a->todo |= TODO_SGID | TODO_SGID_CHECK;+		/*+		 * Verifying the SUID is simpler, but can still be+		 * done in multiple ways, hence the separate "check" bit.+		 */+		if (a->mode & S_ISUID)+			a->todo |= TODO_SUID | TODO_SUID_CHECK;+	} else {+		/*+		 * User didn't request full permissions, so don't+		 * restore SUID, SGID bits and obey umask.+		 */+		a->mode &= ~S_ISUID;+		a->mode &= ~S_ISGID;+		a->mode &= ~S_ISVTX;+		a->mode &= ~a->user_umask;+	}+#if 0+	if (a->flags & ARCHIVE_EXTRACT_OWNER)+		a->todo |= TODO_OWNER;+#endif+	if (a->flags & ARCHIVE_EXTRACT_TIME)+		a->todo |= TODO_TIMES;+	if (a->flags & ARCHIVE_EXTRACT_ACL) {+		if (archive_entry_filetype(a->entry) == AE_IFDIR)+			a->deferred |= TODO_ACLS;+		else+			a->todo |= TODO_ACLS;+	}+	if (a->flags & ARCHIVE_EXTRACT_XATTR)+		a->todo |= TODO_XATTR;+	if (a->flags & ARCHIVE_EXTRACT_FFLAGS)+		a->todo |= TODO_FFLAGS;+	if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {+		ret = check_symlinks(a);+		if (ret != ARCHIVE_OK)+			return (ret);+	}++	ret = restore_entry(a);++	/*+	 * TODO: There are rumours that some extended attributes must+	 * be restored before file data is written.  If this is true,+	 * then we either need to write all extended attributes both+	 * before and after restoring the data, or find some rule for+	 * determining which must go first and which last.  Due to the+	 * many ways people are using xattrs, this may prove to be an+	 * intractable problem.+	 */++	/*+	 * Fixup uses the unedited pathname from archive_entry_pathname(),+	 * because it is relative to the base dir and the edited path+	 * might be relative to some intermediate dir as a result of the+	 * deep restore logic.+	 */+	if (a->deferred & TODO_MODE) {+		fe = current_fixup(a, archive_entry_pathname_w(entry));+		fe->fixup |= TODO_MODE_BASE;+		fe->mode = a->mode;+	}++	if ((a->deferred & TODO_TIMES)+		&& (archive_entry_mtime_is_set(entry)+		    || archive_entry_atime_is_set(entry))) {+		fe = current_fixup(a, archive_entry_pathname_w(entry));+		fe->mode = a->mode;+		fe->fixup |= TODO_TIMES;+		if (archive_entry_atime_is_set(entry)) {+			fe->atime = archive_entry_atime(entry);+			fe->atime_nanos = archive_entry_atime_nsec(entry);+		} else {+			/* If atime is unset, use start time. */+			fe->atime = a->start_time;+			fe->atime_nanos = 0;+		}+		if (archive_entry_mtime_is_set(entry)) {+			fe->mtime = archive_entry_mtime(entry);+			fe->mtime_nanos = archive_entry_mtime_nsec(entry);+		} else {+			/* If mtime is unset, use start time. */+			fe->mtime = a->start_time;+			fe->mtime_nanos = 0;+		}+		if (archive_entry_birthtime_is_set(entry)) {+			fe->birthtime = archive_entry_birthtime(entry);+			fe->birthtime_nanos = archive_entry_birthtime_nsec(entry);+		} else {+			/* If birthtime is unset, use mtime. */+			fe->birthtime = fe->mtime;+			fe->birthtime_nanos = fe->mtime_nanos;+		}+	}++	if (a->deferred & TODO_ACLS) {+		fe = current_fixup(a, archive_entry_pathname_w(entry));+		archive_acl_copy(&fe->acl, archive_entry_acl(entry));+	}++	if (a->deferred & TODO_FFLAGS) {+		unsigned long set, clear;++		fe = current_fixup(a, archive_entry_pathname_w(entry));+		archive_entry_fflags(entry, &set, &clear);+		fe->fflags_set = set;+	}++	/*+	 * On Windows, A creating sparse file requires a special mark.+	 */+	if (a->fh != INVALID_HANDLE_VALUE &&+	    archive_entry_sparse_count(entry) > 0) {+		int64_t base = 0, offset, length;+		int i, cnt = archive_entry_sparse_reset(entry);+		int sparse = 0;++		for (i = 0; i < cnt; i++) {+			archive_entry_sparse_next(entry, &offset, &length);+			if (offset - base >= 4096) {+				sparse = 1;/* we have a hole. */+				break;+			}+			base = offset + length;+		}+		if (sparse) {+			DWORD dmy;+			/* Mark this file as sparse. */+			DeviceIoControl(a->fh, FSCTL_SET_SPARSE,+			    NULL, 0, NULL, 0, &dmy, NULL);+		}+	}++	/* We've created the object and are ready to pour data into it. */+	if (ret >= ARCHIVE_WARN)+		a->archive.state = ARCHIVE_STATE_DATA;+	/*+	 * If it's not open, tell our client not to try writing.+	 * In particular, dirs, links, etc, don't get written to.+	 */+	if (a->fh == INVALID_HANDLE_VALUE) {+		archive_entry_set_size(entry, 0);+		a->filesize = 0;+	}++	return (ret);+}++int+archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file");+	a->skip_file_set = 1;+	a->skip_file_dev = d;+	a->skip_file_ino = i;+	return (ARCHIVE_OK);+}++static ssize_t+write_data_block(struct archive_write_disk *a, const char *buff, size_t size)+{+	OVERLAPPED ol;+	uint64_t start_size = size;+	DWORD bytes_written = 0;+	ssize_t block_size = 0, bytes_to_write;++	if (size == 0)+		return (ARCHIVE_OK);++	if (a->filesize == 0 || a->fh == INVALID_HANDLE_VALUE) {+		archive_set_error(&a->archive, 0,+		    "Attempt to write to an empty file");+		return (ARCHIVE_WARN);+	}++	if (a->flags & ARCHIVE_EXTRACT_SPARSE) {+		/* XXX TODO XXX Is there a more appropriate choice here ? */+		/* This needn't match the filesystem allocation size. */+		block_size = 16*1024;+	}++	/* If this write would run beyond the file size, truncate it. */+	if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)+		start_size = size = (size_t)(a->filesize - a->offset);++	/* Write the data. */+	while (size > 0) {+		if (block_size == 0) {+			bytes_to_write = size;+		} else {+			/* We're sparsifying the file. */+			const char *p, *end;+			int64_t block_end;++			/* Skip leading zero bytes. */+			for (p = buff, end = buff + size; p < end; ++p) {+				if (*p != '\0')+					break;+			}+			a->offset += p - buff;+			size -= p - buff;+			buff = p;+			if (size == 0)+				break;++			/* Calculate next block boundary after offset. */+			block_end+			    = (a->offset / block_size + 1) * block_size;++			/* If the adjusted write would cross block boundary,+			 * truncate it to the block boundary. */+			bytes_to_write = size;+			if (a->offset + bytes_to_write > block_end)+				bytes_to_write = (DWORD)(block_end - a->offset);+		}+		memset(&ol, 0, sizeof(ol));+		ol.Offset = (DWORD)(a->offset & 0xFFFFFFFF);+		ol.OffsetHigh = (DWORD)(a->offset >> 32);+		if (!WriteFile(a->fh, buff, (uint32_t)bytes_to_write,+		    &bytes_written, &ol)) {+			DWORD lasterr;++			lasterr = GetLastError();+			if (lasterr == ERROR_ACCESS_DENIED)+				errno = EBADF;+			else+				la_dosmaperr(lasterr);+			archive_set_error(&a->archive, errno, "Write failed");+			return (ARCHIVE_WARN);+		}+		buff += bytes_written;+		size -= bytes_written;+		a->total_bytes_written += bytes_written;+		a->offset += bytes_written;+		a->fd_offset = a->offset;+	}+	return ((ssize_t)(start_size - size));+}++static ssize_t+_archive_write_disk_data_block(struct archive *_a,+    const void *buff, size_t size, int64_t offset)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	ssize_t r;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_DATA, "archive_write_data_block");++	a->offset = offset;+	r = write_data_block(a, buff, size);+	if (r < ARCHIVE_OK)+		return (r);+	if ((size_t)r < size) {+		archive_set_error(&a->archive, 0,+		    "Write request too large");+		return (ARCHIVE_WARN);+	}+#if ARCHIVE_VERSION_NUMBER < 3999000+	return (ARCHIVE_OK);+#else+	return (size);+#endif+}++static ssize_t+_archive_write_disk_data(struct archive *_a, const void *buff, size_t size)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_DATA, "archive_write_data");++	return (write_data_block(a, buff, size));+}++static int+_archive_write_disk_finish_entry(struct archive *_a)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	int ret = ARCHIVE_OK;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_write_finish_entry");+	if (a->archive.state & ARCHIVE_STATE_HEADER)+		return (ARCHIVE_OK);+	archive_clear_error(&a->archive);++	/* Pad or truncate file to the right size. */+	if (a->fh == INVALID_HANDLE_VALUE) {+		/* There's no file. */+	} else if (a->filesize < 0) {+		/* File size is unknown, so we can't set the size. */+	} else if (a->fd_offset == a->filesize) {+		/* Last write ended at exactly the filesize; we're done. */+		/* Hopefully, this is the common case. */+	} else {+		if (la_ftruncate(a->fh, a->filesize) == -1) {+			archive_set_error(&a->archive, errno,+			    "File size could not be restored");+			return (ARCHIVE_FAILED);+		}+	}++	/* Restore metadata. */++	/*+	 * Look up the "real" UID only if we're going to need it.+	 * TODO: the TODO_SGID condition can be dropped here, can't it?+	 */+	if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) {+		a->uid = archive_write_disk_uid(&a->archive,+		    archive_entry_uname(a->entry),+		    archive_entry_uid(a->entry));+	}+	/* Look up the "real" GID only if we're going to need it. */+	/* TODO: the TODO_SUID condition can be dropped here, can't it? */+	if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) {+		a->gid = archive_write_disk_gid(&a->archive,+		    archive_entry_gname(a->entry),+		    archive_entry_gid(a->entry));+	 }++	/*+	 * Restore ownership before set_mode tries to restore suid/sgid+	 * bits.  If we set the owner, we know what it is and can skip+	 * a stat() call to examine the ownership of the file on disk.+	 */+	if (a->todo & TODO_OWNER)+		ret = set_ownership(a);++	/*+	 * set_mode must precede ACLs on systems such as Solaris and+	 * FreeBSD where setting the mode implicitly clears extended ACLs+	 */+	if (a->todo & TODO_MODE) {+		int r2 = set_mode(a, a->mode);+		if (r2 < ret) ret = r2;+	}++	/*+	 * Security-related extended attributes (such as+	 * security.capability on Linux) have to be restored last,+	 * since they're implicitly removed by other file changes.+	 */+	if (a->todo & TODO_XATTR) {+		int r2 = set_xattrs(a);+		if (r2 < ret) ret = r2;+	}++	/*+	 * Some flags prevent file modification; they must be restored after+	 * file contents are written.+	 */+	if (a->todo & TODO_FFLAGS) {+		int r2 = set_fflags(a);+		if (r2 < ret) ret = r2;+	}++	/*+	 * Time must follow most other metadata;+	 * otherwise atime will get changed.+	 */+	if (a->todo & TODO_TIMES) {+		int r2 = set_times_from_entry(a);+		if (r2 < ret) ret = r2;+	}++	/*+	 * ACLs must be restored after timestamps because there are+	 * ACLs that prevent attribute changes (including time).+	 */+	if (a->todo & TODO_ACLS) {+		int r2 = set_acls(a, a->fh,+				  archive_entry_pathname_w(a->entry),+				  archive_entry_acl(a->entry));+		if (r2 < ret) ret = r2;+	}++	/* If there's an fd, we can close it now. */+	if (a->fh != INVALID_HANDLE_VALUE) {+		CloseHandle(a->fh);+		a->fh = INVALID_HANDLE_VALUE;+		if (a->tmpname) {+			/* Windows does not support atomic rename */+			disk_unlink(a->name);+			if (_wrename(a->tmpname, a->name) != 0) {+				la_dosmaperr(GetLastError());+				archive_set_error(&a->archive, errno,+				    "Failed to rename temporary file");+				ret = ARCHIVE_FAILED;+				disk_unlink(a->tmpname);+			}+			a->tmpname = NULL;+		}+	}+	/* If there's an entry, we can release it now. */+	archive_entry_free(a->entry);+	a->entry = NULL;+	a->archive.state = ARCHIVE_STATE_HEADER;+	return (ret);+}++int+archive_write_disk_set_group_lookup(struct archive *_a,+    void *private_data,+    la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid),+    void (*cleanup_gid)(void *private))+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup");++	if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL)+		(a->cleanup_gid)(a->lookup_gid_data);++	a->lookup_gid = lookup_gid;+	a->cleanup_gid = cleanup_gid;+	a->lookup_gid_data = private_data;+	return (ARCHIVE_OK);+}++int+archive_write_disk_set_user_lookup(struct archive *_a,+    void *private_data,+    int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid),+    void (*cleanup_uid)(void *private))+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup");++	if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL)+		(a->cleanup_uid)(a->lookup_uid_data);++	a->lookup_uid = lookup_uid;+	a->cleanup_uid = cleanup_uid;+	a->lookup_uid_data = private_data;+	return (ARCHIVE_OK);+}++int64_t+archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id)+{+       struct archive_write_disk *a = (struct archive_write_disk *)_a;+       archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+           ARCHIVE_STATE_ANY, "archive_write_disk_gid");+       if (a->lookup_gid)+               return (a->lookup_gid)(a->lookup_gid_data, name, id);+       return (id);+}+ +int64_t+archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id)+{+       struct archive_write_disk *a = (struct archive_write_disk *)_a;+       archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+           ARCHIVE_STATE_ANY, "archive_write_disk_uid");+       if (a->lookup_uid)+               return (a->lookup_uid)(a->lookup_uid_data, name, id);+       return (id);+}++/*+ * Create a new archive_write_disk object and initialize it with global state.+ */+struct archive *+archive_write_disk_new(void)+{+	struct archive_write_disk *a;++	a = (struct archive_write_disk *)calloc(1, sizeof(*a));+	if (a == NULL)+		return (NULL);+	a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC;+	/* We're ready to write a header immediately. */+	a->archive.state = ARCHIVE_STATE_HEADER;+	a->archive.vtable = archive_write_disk_vtable();+	a->start_time = time(NULL);+	/* Query and restore the umask. */+	umask(a->user_umask = umask(0));+	if (archive_wstring_ensure(&a->path_safe, 512) == NULL) {+		free(a);+		return (NULL);+	}+	return (&a->archive);+}++static int+disk_unlink(const wchar_t *path)+{+	wchar_t *fullname;+	int r;++	r = _wunlink(path);+	if (r != 0 && GetLastError() == ERROR_INVALID_NAME) {+		fullname = __la_win_permissive_name_w(path);+		r = _wunlink(fullname);+		free(fullname);+	}+	return (r);+}++static int+disk_rmdir(const wchar_t *path)+{+	wchar_t *fullname;+	int r;++	r = _wrmdir(path);+	if (r != 0 && GetLastError() == ERROR_INVALID_NAME) {+		fullname = __la_win_permissive_name_w(path);+		r = _wrmdir(fullname);+		free(fullname);+	}+	return (r);+}++/*+ * The main restore function.+ */+static int+restore_entry(struct archive_write_disk *a)+{+	int ret = ARCHIVE_OK, en;++	if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {+		/*+		 * TODO: Fix this.  Apparently, there are platforms+		 * that still allow root to hose the entire filesystem+		 * by unlinking a dir.  The S_ISDIR() test above+		 * prevents us from using unlink() here if the new+		 * object is a dir, but that doesn't mean the old+		 * object isn't a dir.+		 */+		if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)+			(void)clear_nochange_fflags(a);+		if (disk_unlink(a->name) == 0) {+			/* We removed it, reset cached stat. */+			a->pst = NULL;+		} else if (errno == ENOENT) {+			/* File didn't exist, that's just as good. */+		} else if (disk_rmdir(a->name) == 0) {+			/* It was a dir, but now it's gone. */+			a->pst = NULL;+		} else {+			/* We tried, but couldn't get rid of it. */+			archive_set_error(&a->archive, errno,+			    "Could not unlink");+			return(ARCHIVE_FAILED);+		}+	}++	/* Try creating it first; if this fails, we'll try to recover. */+	en = create_filesystem_object(a);++	if ((en == ENOTDIR || en == ENOENT)+	    && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) {+		wchar_t *full;+		/* If the parent dir doesn't exist, try creating it. */+		create_parent_dir(a, a->name);+		/* Now try to create the object again. */+		full = __la_win_permissive_name_w(a->name);+		if (full == NULL) {+			en = EINVAL;+		} else {+			/* Remove multiple directories such as "a/../b../c" */+			archive_wstrcpy(&(a->_name_data), full);+			a->name = a->_name_data.s;+			free(full);+			en = create_filesystem_object(a);+		}+	}++	if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) {+		archive_set_error(&a->archive, en,+			"Hard-link target '%s' does not exist.",+			archive_entry_hardlink(a->entry));+		return (ARCHIVE_FAILED);+	}++	if ((en == EISDIR || en == EEXIST)+	    && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {+		/* If we're not overwriting, we're done. */+		if (S_ISDIR(a->mode)) {+			/* Don't overwrite any settings on existing directories. */+			a->todo = 0;+		}+		archive_entry_unset_size(a->entry);+		return (ARCHIVE_OK);+	}++	/*+	 * Some platforms return EISDIR if you call+	 * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some+	 * return EEXIST.  POSIX is ambiguous, requiring EISDIR+	 * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT)+	 * on an existing item.+	 */+	if (en == EISDIR) {+		/* A dir is in the way of a non-dir, rmdir it. */+		if (disk_rmdir(a->name) != 0) {+			archive_set_error(&a->archive, errno,+			    "Can't remove already-existing dir");+			return (ARCHIVE_FAILED);+		}+		a->pst = NULL;+		/* Try again. */+		en = create_filesystem_object(a);+	} else if (en == EEXIST) {+		mode_t st_mode;+		mode_t lst_mode;+		BY_HANDLE_FILE_INFORMATION lst;+		/*+		 * We know something is in the way, but we don't know what;+		 * we need to find out before we go any further.+		 */+		int r = 0;+		int dirlnk = 0;++		/*+		 * The SECURE_SYMLINK logic has already removed a+		 * symlink to a dir if the client wants that.  So+		 * follow the symlink if we're creating a dir.+		 * If it's not a dir (or it's a broken symlink),+		 * then don't follow it.+		 *+		 * Windows distinguishes file and directory symlinks.+		 * A file symlink may erroneously point to a directory+		 * and a directory symlink to a file. Windows does not follow+		 * such symlinks. We always need both source and target+		 * information.+		 */+		r = file_information(a, a->name, &lst, &lst_mode, 1);+		if (r != 0) {+			archive_set_error(&a->archive, errno,+			    "Can't stat existing object");+			return (ARCHIVE_FAILED);+		} else if (S_ISLNK(lst_mode)) {+			if (lst.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)+				dirlnk = 1;+			/* In case of a symlink we need target information */+			r = file_information(a, a->name, &a->st, &st_mode, 0);+			if (r != 0) {+				a->st = lst;+				st_mode = lst_mode;+			}+		} else {+			a->st = lst;+			st_mode = lst_mode;+		}++		/*+		 * NO_OVERWRITE_NEWER doesn't apply to directories.+		 */+		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER)+		    &&  !S_ISDIR(st_mode)) {+			if (!older(&(a->st), a->entry)) {+				archive_entry_unset_size(a->entry);+				return (ARCHIVE_OK);+			}+		}++		/* If it's our archive, we're done. */+		if (a->skip_file_set &&+		    bhfi_dev(&a->st) == a->skip_file_dev &&+		    bhfi_ino(&a->st) == a->skip_file_ino) {+			archive_set_error(&a->archive, 0,+			    "Refusing to overwrite archive");+			return (ARCHIVE_FAILED);+		}++		if (!S_ISDIR(st_mode)) {+			if (a->flags &+			    ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {+				(void)clear_nochange_fflags(a);+			}+			if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) &&+				S_ISREG(st_mode)) {+				int fd = la_mktemp(a);++				if (fd == -1) {+					la_dosmaperr(GetLastError());+					archive_set_error(&a->archive, errno,+					    "Can't create temporary file");+					return (ARCHIVE_FAILED);+				}+				a->fh = (HANDLE)_get_osfhandle(fd);+				if (a->fh == INVALID_HANDLE_VALUE) {+					la_dosmaperr(GetLastError());+					return (ARCHIVE_FAILED);+				}+				a->pst = NULL;+				en = 0;+			} else {+				if (dirlnk) {+					/* Edge case: dir symlink pointing+					 * to a file */+					if (disk_rmdir(a->name) != 0) {+						archive_set_error(&a->archive,+						    errno, "Can't unlink "+						    "directory symlink");+						return (ARCHIVE_FAILED);+					}+				} else {+					if (disk_unlink(a->name) != 0) {+						/* A non-dir is in the way,+						 * unlink it. */+						archive_set_error(&a->archive,+						    errno, "Can't unlink "+						    "already-existing object");+						return (ARCHIVE_FAILED);+					}+				}+				a->pst = NULL;+				/* Try again. */+				en = create_filesystem_object(a);+			}+		} else if (!S_ISDIR(a->mode)) {+			/* A dir is in the way of a non-dir, rmdir it. */+			if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)+				(void)clear_nochange_fflags(a);+			if (disk_rmdir(a->name) != 0) {+				archive_set_error(&a->archive, errno,+				    "Can't remove already-existing dir");+				return (ARCHIVE_FAILED);+			}+			/* Try again. */+			en = create_filesystem_object(a);+		} else {+			/*+			 * There's a dir in the way of a dir.  Don't+			 * waste time with rmdir()/mkdir(), just fix+			 * up the permissions on the existing dir.+			 * Note that we don't change perms on existing+			 * dirs unless _EXTRACT_PERM is specified.+			 */+			if ((a->mode != st_mode)+			    && (a->todo & TODO_MODE_FORCE))+				a->deferred |= (a->todo & TODO_MODE);+			/* Ownership doesn't need deferred fixup. */+			en = 0; /* Forget the EEXIST. */+		}+	}++	if (en) {+		/* Everything failed; give up here. */+		archive_set_error(&a->archive, en, "Can't create '%ls'",+		    a->name);+		return (ARCHIVE_FAILED);+	}++	a->pst = NULL; /* Cached stat data no longer valid. */+	return (ret);+}++/*+ * Returns 0 if creation succeeds, or else returns errno value from+ * the failed system call.   Note:  This function should only ever perform+ * a single system call.+ */+static int+create_filesystem_object(struct archive_write_disk *a)+{+	/* Create the entry. */+	const wchar_t *linkname;+	wchar_t *fullname;+	mode_t final_mode, mode;+	int r;+	DWORD attrs = 0;++	/* We identify hard/symlinks according to the link names. */+	/* Since link(2) and symlink(2) don't handle modes, we're done here. */+	linkname = archive_entry_hardlink_w(a->entry);+	if (linkname != NULL) {+		wchar_t *linkfull, *namefull;++		linkfull = __la_win_permissive_name_w(linkname);+		namefull = __la_win_permissive_name_w(a->name);+		if (linkfull == NULL || namefull == NULL) {+			errno = EINVAL;+			r = -1;+		} else {+			/*+			 * Unlinking and linking here is really not atomic,+			 * but doing it right, would require us to construct+			 * an mktemplink() function, and then use _wrename().+			 */+			if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) {+				attrs = GetFileAttributesW(namefull);+				if (attrs != INVALID_FILE_ATTRIBUTES) {+					if (attrs & FILE_ATTRIBUTE_DIRECTORY)+						disk_rmdir(namefull);+					else+						disk_unlink(namefull);+				}+			}+			r = la_CreateHardLinkW(namefull, linkfull);+			if (r == 0) {+				la_dosmaperr(GetLastError());+				r = errno;+			} else+				r = 0;+		}+		/*+		 * New cpio and pax formats allow hardlink entries+		 * to carry data, so we may have to open the file+		 * for hardlink entries.+		 *+		 * If the hardlink was successfully created and+		 * the archive doesn't have carry data for it,+		 * consider it to be non-authoritative for meta data.+		 * This is consistent with GNU tar and BSD pax.+		 * If the hardlink does carry data, let the last+		 * archive entry decide ownership.+		 */+		if (r == 0 && a->filesize <= 0) {+			a->todo = 0;+			a->deferred = 0;+		} else if (r == 0 && a->filesize > 0) {+			a->fh = CreateFileW(namefull, GENERIC_WRITE, 0, NULL,+			    TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);+			if (a->fh == INVALID_HANDLE_VALUE) {+				la_dosmaperr(GetLastError());+				r = errno;+			}+		}+		free(linkfull);+		free(namefull);+		return (r);+	}+	linkname = archive_entry_symlink_w(a->entry);+	if (linkname != NULL) {+		/*+		 * Unlinking and linking here is really not atomic,+		 * but doing it right, would require us to construct+		 * an mktemplink() function, and then use _wrename().+		 */+		attrs = GetFileAttributesW(a->name);+		if (attrs != INVALID_FILE_ATTRIBUTES) {+			if (attrs & FILE_ATTRIBUTE_DIRECTORY)+				disk_rmdir(a->name);+			else+				disk_unlink(a->name);+		}+#if HAVE_SYMLINK+		return symlink(linkname, a->name) ? errno : 0;+#else+		errno = 0;+		r = la_CreateSymbolicLinkW((const wchar_t *)a->name, linkname,+		    archive_entry_symlink_type(a->entry));+		if (r == 0) {+			if (errno == 0)+				la_dosmaperr(GetLastError());+			r = errno;+		} else+			r = 0;+		return (r);+#endif+	}++	/*+	 * The remaining system calls all set permissions, so let's+	 * try to take advantage of that to avoid an extra chmod()+	 * call.  (Recall that umask is set to zero right now!)+	 */++	/* Mode we want for the final restored object (w/o file type bits). */+	final_mode = a->mode & 07777;+	/*+	 * The mode that will actually be restored in this step.  Note+	 * that SUID, SGID, etc, require additional work to ensure+	 * security, so we never restore them at this point.+	 */+	mode = final_mode & 0777 & ~a->user_umask;++	switch (a->mode & AE_IFMT) {+	default:+		/* POSIX requires that we fall through here. */+		/* FALLTHROUGH */+	case AE_IFREG:+		a->tmpname = NULL;+		fullname = a->name;+		/* O_WRONLY | O_CREAT | O_EXCL */+		a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,+		    CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);+		if (a->fh == INVALID_HANDLE_VALUE &&+		    GetLastError() == ERROR_INVALID_NAME &&+		    fullname == a->name) {+			fullname = __la_win_permissive_name_w(a->name);+			a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,+			    CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);+		}+		if (a->fh == INVALID_HANDLE_VALUE) {+			if (GetLastError() == ERROR_ACCESS_DENIED) {+				DWORD attr;+				/* Simulate an errno of POSIX system. */+				attr = GetFileAttributesW(fullname);+				if (attr == (DWORD)-1)+					la_dosmaperr(GetLastError());+				else if (attr & FILE_ATTRIBUTE_DIRECTORY)+					errno = EISDIR;+				else+					errno = EACCES;+			} else+				la_dosmaperr(GetLastError());+			r = 1;+		} else+			r = 0;+		if (fullname != a->name)+			free(fullname);+		break;+	case AE_IFCHR:+	case AE_IFBLK:+		/* TODO: Find a better way to warn about our inability+		 * to restore a block device node. */+		return (EINVAL);+	case AE_IFDIR:+		mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;+		fullname = a->name;+		r = CreateDirectoryW(fullname, NULL);+		if (r == 0 && GetLastError() == ERROR_INVALID_NAME &&+			fullname == a->name) {+			fullname = __la_win_permissive_name_w(a->name);+			r = CreateDirectoryW(fullname, NULL);+		}+		if (r != 0) {+			r = 0;+			/* Defer setting dir times. */+			a->deferred |= (a->todo & TODO_TIMES);+			a->todo &= ~TODO_TIMES;+			/* Never use an immediate chmod(). */+			/* We can't avoid the chmod() entirely if EXTRACT_PERM+			 * because of SysV SGID inheritance. */+			if ((mode != final_mode)+			    || (a->flags & ARCHIVE_EXTRACT_PERM))+				a->deferred |= (a->todo & TODO_MODE);+			a->todo &= ~TODO_MODE;+		} else {+			la_dosmaperr(GetLastError());+			r = -1;+		}+		if (fullname != a->name)+			free(fullname);+		break;+	case AE_IFIFO:+		/* TODO: Find a better way to warn about our inability+		 * to restore a fifo. */+		return (EINVAL);+	}++	/* All the system calls above set errno on failure. */+	if (r)+		return (errno);++	/* If we managed to set the final mode, we've avoided a chmod(). */+	if (mode == final_mode)+		a->todo &= ~TODO_MODE;+	return (0);+}++/*+ * Cleanup function for archive_extract.  Mostly, this involves processing+ * the fixup list, which is used to address a number of problems:+ *   * Dir permissions might prevent us from restoring a file in that+ *     dir, so we restore the dir with minimum 0700 permissions first,+ *     then correct the mode at the end.+ *   * Similarly, the act of restoring a file touches the directory+ *     and changes the timestamp on the dir, so we have to touch-up dir+ *     timestamps at the end as well.+ *   * Some file flags can interfere with the restore by, for example,+ *     preventing the creation of hardlinks to those files.+ *   * Mac OS extended metadata includes ACLs, so must be deferred on dirs.+ *+ * Note that tar/cpio do not require that archives be in a particular+ * order; there is no way to know when the last file has been restored+ * within a directory, so there's no way to optimize the memory usage+ * here by fixing up the directory any earlier than the+ * end-of-archive.+ *+ * XXX TODO: Directory ACLs should be restored here, for the same+ * reason we set directory perms here. XXX+ */+static int+_archive_write_disk_close(struct archive *_a)+{+	struct archive_write_disk *a = (struct archive_write_disk *)_a;+	struct fixup_entry *next, *p;+	int ret;++	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+	    "archive_write_disk_close");+	ret = _archive_write_disk_finish_entry(&a->archive);++	/* Sort dir list so directories are fixed up in depth-first order. */+	p = sort_dir_list(a->fixup_list);++	while (p != NULL) {+		a->pst = NULL; /* Mark stat cache as out-of-date. */+		if (p->fixup & TODO_TIMES) {+			set_times(a, INVALID_HANDLE_VALUE, p->mode, p->name,+			    p->atime, p->atime_nanos,+			    p->birthtime, p->birthtime_nanos,+			    p->mtime, p->mtime_nanos,+			    p->ctime, p->ctime_nanos);+		}+		if (p->fixup & TODO_MODE_BASE)+			la_chmod(p->name, p->mode);+		if (p->fixup & TODO_ACLS)+			set_acls(a, INVALID_HANDLE_VALUE, p->name, &p->acl);+		if (p->fixup & TODO_FFLAGS)+			set_fflags_platform(p->name, p->fflags_set, 0);+		next = p->next;+		archive_acl_clear(&p->acl);+		free(p->name);+		free(p);+		p = next;+	}+	a->fixup_list = NULL;+	return (ret);+}++static int+_archive_write_disk_free(struct archive *_a)+{+	struct archive_write_disk *a;+	int ret;+	if (_a == NULL)+		return (ARCHIVE_OK);+	archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC,+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free");+	a = (struct archive_write_disk *)_a;+	ret = _archive_write_disk_close(&a->archive);+	archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL);+	archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL);+	archive_entry_free(a->entry);+	archive_wstring_free(&a->_name_data);+	archive_wstring_free(&a->_tmpname_data);+	archive_string_free(&a->archive.error_string);+	archive_wstring_free(&a->path_safe);+	a->archive.magic = 0;+	__archive_clean(&a->archive);+	free(a);+	return (ret);+}++/*+ * Simple O(n log n) merge sort to order the fixup list.  In+ * particular, we want to restore dir timestamps depth-first.+ */+static struct fixup_entry *+sort_dir_list(struct fixup_entry *p)+{+	struct fixup_entry *a, *b, *t;++	if (p == NULL)+		return (NULL);+	/* A one-item list is already sorted. */+	if (p->next == NULL)+		return (p);++	/* Step 1: split the list. */+	t = p;+	a = p->next->next;+	while (a != NULL) {+		/* Step a twice, t once. */+		a = a->next;+		if (a != NULL)+			a = a->next;+		t = t->next;+	}+	/* Now, t is at the mid-point, so break the list here. */+	b = t->next;+	t->next = NULL;+	a = p;++	/* Step 2: Recursively sort the two sub-lists. */+	a = sort_dir_list(a);+	b = sort_dir_list(b);++	/* Step 3: Merge the returned lists. */+	/* Pick the first element for the merged list. */+	if (wcscmp(a->name, b->name) > 0) {+		t = p = a;+		a = a->next;+	} else {+		t = p = b;+		b = b->next;+	}++	/* Always put the later element on the list first. */+	while (a != NULL && b != NULL) {+		if (wcscmp(a->name, b->name) > 0) {+			t->next = a;+			a = a->next;+		} else {+			t->next = b;+			b = b->next;+		}+		t = t->next;+	}++	/* Only one list is non-empty, so just splice it on. */+	if (a != NULL)+		t->next = a;+	if (b != NULL)+		t->next = b;++	return (p);+}++/*+ * Returns a new, initialized fixup entry.+ *+ * TODO: Reduce the memory requirements for this list by using a tree+ * structure rather than a simple list of names.+ */+static struct fixup_entry *+new_fixup(struct archive_write_disk *a, const wchar_t *pathname)+{+	struct fixup_entry *fe;++	fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry));+	if (fe == NULL)+		return (NULL);+	fe->next = a->fixup_list;+	a->fixup_list = fe;+	fe->fixup = 0;+	fe->name = _wcsdup(pathname);+	fe->fflags_set = 0;+	return (fe);+}++/*+ * Returns a fixup structure for the current entry.+ */+static struct fixup_entry *+current_fixup(struct archive_write_disk *a, const wchar_t *pathname)+{+	if (a->current_fixup == NULL)+		a->current_fixup = new_fixup(a, pathname);+	return (a->current_fixup);+}++/*+ * TODO: The deep-directory support bypasses this; disable deep directory+ * support if we're doing symlink checks.+ */+/*+ * TODO: Someday, integrate this with the deep dir support; they both+ * scan the path and both can be optimized by comparing against other+ * recent paths.+ */+static int+check_symlinks(struct archive_write_disk *a)+{+	wchar_t *pn, *p;+	wchar_t c;+	int r;+	BY_HANDLE_FILE_INFORMATION st;+	mode_t st_mode;++	/*+	 * Guard against symlink tricks.  Reject any archive entry whose+	 * destination would be altered by a symlink.+	 */+	/* Whatever we checked last time doesn't need to be re-checked. */+	pn = a->name;+	p = a->path_safe.s;+	while ((*pn != '\0') && (*p == *pn))+		++p, ++pn;+	/* Skip leading backslashes */+	while (*pn == '\\')+		++pn;+	c = pn[0];+	/* Keep going until we've checked the entire name. */+	while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) {+		/* Skip the next path element. */+		while (*pn != '\0' && *pn != '\\')+			++pn;+		c = pn[0];+		pn[0] = '\0';+		/* Check that we haven't hit a symlink. */+		r = file_information(a, a->name, &st, &st_mode, 1);+		if (r != 0) {+			/* We've hit a dir that doesn't exist; stop now. */+			if (errno == ENOENT)+				break;+		} else if (S_ISLNK(st_mode)) {+			if (c == '\0') {+				/*+				 * Last element is a file or directory symlink.+				 * Remove it so we can overwrite it with the+				 * item being extracted.+				 */+				if (a->flags &+				    ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {+					(void)clear_nochange_fflags(a);+				}+				if (st.dwFileAttributes &+				    FILE_ATTRIBUTE_DIRECTORY) {+					r = disk_rmdir(a->name);+				} else {+					r = disk_unlink(a->name);+				}+				if (r) {+					archive_set_error(&a->archive, errno,+					    "Could not remove symlink %ls",+					    a->name);+					pn[0] = c;+					return (ARCHIVE_FAILED);+				}+				a->pst = NULL;+				/*+				 * Even if we did remove it, a warning+				 * is in order.  The warning is silly,+				 * though, if we're just replacing one+				 * symlink with another symlink.+				 */+				if (!S_ISLNK(a->mode)) {+					archive_set_error(&a->archive, 0,+					    "Removing symlink %ls",+					    a->name);+				}+				/* Symlink gone.  No more problem! */+				pn[0] = c;+				return (0);+			} else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {+				/* User asked us to remove problems. */+				if (a->flags &+				    ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {+					(void)clear_nochange_fflags(a);+				}+				if (st.dwFileAttributes &+				    FILE_ATTRIBUTE_DIRECTORY) {+					r = disk_rmdir(a->name);+				} else {+					r = disk_unlink(a->name);+				}+				if (r != 0) {+					archive_set_error(&a->archive, 0,+					    "Cannot remove intervening "+					    "symlink %ls", a->name);+					pn[0] = c;+					return (ARCHIVE_FAILED);+				}+				a->pst = NULL;+			} else {+				archive_set_error(&a->archive, 0,+				    "Cannot extract through symlink %ls",+				    a->name);+				pn[0] = c;+				return (ARCHIVE_FAILED);+			}+		}+		pn[0] = c;+		pn++;+	}+	pn[0] = c;+	/* We've checked and/or cleaned the whole path, so remember it. */+	archive_wstrcpy(&a->path_safe, a->name);+	return (ARCHIVE_OK);+}++static int+guidword(wchar_t *p, int n)+{+	int i;++	for (i = 0; i < n; i++) {+		if ((*p >= L'0' && *p <= L'9') ||+		    (*p >= L'a' && *p <= L'f') ||+		    (*p >= L'A' && *p <= L'F'))+			p++;+		else+			return (-1);+	}+	return (0);+}++/*+ * Canonicalize the pathname.  In particular, this strips duplicate+ * '\' characters, '.' elements, and trailing '\'.  It also raises an+ * error for an empty path, a trailing '..' or (if _SECURE_NODOTDOT is+ * set) any '..' in the path.+ */+static int+cleanup_pathname(struct archive_write_disk *a)+{+	wchar_t *dest, *src, *p, *top;+	wchar_t separator = L'\0';++	p = a->name;+	if (*p == L'\0') {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Invalid empty pathname");+		return (ARCHIVE_FAILED);+	}++	/* Replace '/' by '\' */+	for (; *p != L'\0'; p++) {+		if (*p == L'/')+			*p = L'\\';+	}+	p = a->name;++	/* Skip leading "\\.\" or "\\?\" or "\\?\UNC\" or+	 * "\\?\Volume{GUID}\"+	 * (absolute path prefixes used by Windows API) */+	if (p[0] == L'\\' && p[1] == L'\\' &&+	    (p[2] == L'.' || p[2] == L'?') && p[3] ==  L'\\')+	{+		/* A path begin with "\\?\UNC\" */+		if (p[2] == L'?' &&+		    (p[4] == L'U' || p[4] == L'u') &&+		    (p[5] == L'N' || p[5] == L'n') &&+		    (p[6] == L'C' || p[6] == L'c') &&+		    p[7] == L'\\')+			p += 8;+		/* A path begin with "\\?\Volume{GUID}\" */+		else if (p[2] == L'?' &&+		    (p[4] == L'V' || p[4] == L'v') &&+		    (p[5] == L'O' || p[5] == L'o') &&+		    (p[6] == L'L' || p[6] == L'l') &&+		    (p[7] == L'U' || p[7] == L'u') &&+		    (p[8] == L'M' || p[8] == L'm') &&+		    (p[9] == L'E' || p[9] == L'e') &&+		    p[10] == L'{') {+			if (guidword(p+11, 8) == 0 && p[19] == L'-' &&+			    guidword(p+20, 4) == 0 && p[24] == L'-' &&+			    guidword(p+25, 4) == 0 && p[29] == L'-' &&+			    guidword(p+30, 4) == 0 && p[34] == L'-' &&+			    guidword(p+35, 12) == 0 && p[47] == L'}' &&+			    p[48] == L'\\')+				p += 49;+			else+				p += 4;+		/* A path begin with "\\.\PhysicalDriveX" */+		} else if (p[2] == L'.' &&+		    (p[4] == L'P' || p[4] == L'p') &&+		    (p[5] == L'H' || p[5] == L'h') &&+		    (p[6] == L'Y' || p[6] == L'y') &&+		    (p[7] == L'S' || p[7] == L's') &&+		    (p[8] == L'I' || p[8] == L'i') &&+		    (p[9] == L'C' || p[9] == L'c') &&+		    (p[9] == L'A' || p[9] == L'a') &&+		    (p[9] == L'L' || p[9] == L'l') &&+		    (p[9] == L'D' || p[9] == L'd') &&+		    (p[9] == L'R' || p[9] == L'r') &&+		    (p[9] == L'I' || p[9] == L'i') &&+		    (p[9] == L'V' || p[9] == L'v') &&+		    (p[9] == L'E' || p[9] == L'e') &&+		    (p[10] >= L'0' && p[10] <= L'9') &&+		    p[11] == L'\0') {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Path is a physical drive name");+			return (ARCHIVE_FAILED);+		} else+			p += 4;+	}++	/* Skip leading drive letter from archives created+	 * on Windows. */+	if (((p[0] >= L'a' && p[0] <= L'z') ||+	     (p[0] >= L'A' && p[0] <= L'Z')) &&+		 p[1] == L':') {+		if (p[2] == L'\0') {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Path is a drive name");+			return (ARCHIVE_FAILED);+		}+		if (p[2] == L'\\')+			p += 2;+	}++	top = dest = src = p;+	/* Rewrite the path name if its character is a unusable. */+	for (; *p != L'\0'; p++) {+		if (*p == L':' || *p == L'*' || *p == L'?' || *p == L'"' ||+		    *p == L'<' || *p == L'>' || *p == L'|')+			*p = L'_';+	}+	/* Skip leading '\'. */+	if (*src == L'\\')+		separator = *src++;++	/* Scan the pathname one element at a time. */+	for (;;) {+		/* src points to first char after '\' */+		if (src[0] == L'\0') {+			break;+		} else if (src[0] == L'\\') {+			/* Found '\\'('//'), ignore second one. */+			src++;+			continue;+		} else if (src[0] == L'.') {+			if (src[1] == L'\0') {+				/* Ignore trailing '.' */+				break;+			} else if (src[1] == L'\\') {+				/* Skip '.\'. */+				src += 2;+				continue;+			} else if (src[1] == L'.') {+				if (src[2] == L'\\' || src[2] == L'\0') {+					/* Conditionally warn about '..' */+					if (a->flags &+					    ARCHIVE_EXTRACT_SECURE_NODOTDOT) {+						archive_set_error(&a->archive,+						    ARCHIVE_ERRNO_MISC,+						    "Path contains '..'");+						return (ARCHIVE_FAILED);+					}+				}+				/*+				 * Note: Under no circumstances do we+				 * remove '..' elements.  In+				 * particular, restoring+				 * '\foo\..\bar\' should create the+				 * 'foo' dir as a side-effect.+				 */+			}+		}++		/* Copy current element, including leading '\'. */+		if (separator)+			*dest++ = L'\\';+		while (*src != L'\0' && *src != L'\\') {+			*dest++ = *src++;+		}++		if (*src == L'\0')+			break;++		/* Skip '\' separator. */+		separator = *src++;+	}+	/*+	 * We've just copied zero or more path elements, not including the+	 * final '\'.+	 */+	if (dest == top) {+		/*+		 * Nothing got copied.  The path must have been something+		 * like '.' or '\' or './' or '/././././/./'.+		 */+		if (separator)+			*dest++ = L'\\';+		else+			*dest++ = L'.';+	}+	/* Terminate the result. */+	*dest = L'\0';+	return (ARCHIVE_OK);+}++/*+ * Create the parent directory of the specified path, assuming path+ * is already in mutable storage.+ */+static int+create_parent_dir(struct archive_write_disk *a, wchar_t *path)+{+	wchar_t *slash;+	int r;++	/* Remove tail element to obtain parent name. */+	slash = wcsrchr(path, L'\\');+	if (slash == NULL)+		return (ARCHIVE_OK);+	*slash = L'\0';+	r = create_dir(a, path);+	*slash = L'\\';+	return (r);+}++/*+ * Create the specified dir, recursing to create parents as necessary.+ *+ * Returns ARCHIVE_OK if the path exists when we're done here.+ * Otherwise, returns ARCHIVE_FAILED.+ * Assumes path is in mutable storage; path is unchanged on exit.+ */+static int+create_dir(struct archive_write_disk *a, wchar_t *path)+{+	BY_HANDLE_FILE_INFORMATION st;+	struct fixup_entry *le;+	wchar_t *slash, *base, *full;+	mode_t mode_final, mode, st_mode;+	int r;++	/* Check for special names and just skip them. */+	slash = wcsrchr(path, L'\\');+	if (slash == NULL)+		base = path;+	else+		base = slash + 1;++	if (base[0] == L'\0' ||+	    (base[0] == L'.' && base[1] == L'\0') ||+	    (base[0] == L'.' && base[1] == L'.' && base[2] == L'\0')) {+		/* Don't bother trying to create null path, '.', or '..'. */+		if (slash != NULL) {+			*slash = L'\0';+			r = create_dir(a, path);+			*slash = L'\\';+			return (r);+		}+		return (ARCHIVE_OK);+	}++	/*+	 * Yes, this should be stat() and not lstat().  Using lstat()+	 * here loses the ability to extract through symlinks.  Also note+	 * that this should not use the a->st cache.+	 */+	if (file_information(a, path, &st, &st_mode, 0) == 0) {+		if (S_ISDIR(st_mode))+			return (ARCHIVE_OK);+		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {+			archive_set_error(&a->archive, EEXIST,+			    "Can't create directory '%ls'", path);+			return (ARCHIVE_FAILED);+		}+		if (disk_unlink(path) != 0) {+			archive_set_error(&a->archive, errno,+			    "Can't create directory '%ls': "+			    "Conflicting file cannot be removed",+			    path);+			return (ARCHIVE_FAILED);+		}+	} else if (errno != ENOENT && errno != ENOTDIR) {+		/* Stat failed? */+		archive_set_error(&a->archive, errno,+		    "Can't test directory '%ls'", path);+		return (ARCHIVE_FAILED);+	} else if (slash != NULL) {+		*slash = '\0';+		r = create_dir(a, path);+		*slash = '\\';+		if (r != ARCHIVE_OK)+			return (r);+	}++	/*+	 * Mode we want for the final restored directory.  Per POSIX,+	 * implicitly-created dirs must be created obeying the umask.+	 * There's no mention whether this is different for privileged+	 * restores (which the rest of this code handles by pretending+	 * umask=0).  I've chosen here to always obey the user's umask for+	 * implicit dirs, even if _EXTRACT_PERM was specified.+	 */+	mode_final = DEFAULT_DIR_MODE & ~a->user_umask;+	/* Mode we want on disk during the restore process. */+	mode = mode_final;+	mode |= MINIMUM_DIR_MODE;+	mode &= MAXIMUM_DIR_MODE;+	/*+	 * Apply __la_win_permissive_name_w to path in order to+	 * remove '../' path string.+	 */+	full = __la_win_permissive_name_w(path);+	if (full == NULL)+		errno = EINVAL;+	else if (CreateDirectoryW(full, NULL) != 0) {+		if (mode != mode_final) {+			le = new_fixup(a, path);+			le->fixup |=TODO_MODE_BASE;+			le->mode = mode_final;+		}+		free(full);+		return (ARCHIVE_OK);+	} else {+		la_dosmaperr(GetLastError());+	}+	free(full);++	/*+	 * Without the following check, a/b/../b/c/d fails at the+	 * second visit to 'b', so 'd' can't be created.  Note that we+	 * don't add it to the fixup list here, as it's already been+	 * added.+	 */+	if (file_information(a, path, &st, &st_mode, 0) == 0 &&+	    S_ISDIR(st_mode))+		return (ARCHIVE_OK);++	archive_set_error(&a->archive, errno, "Failed to create dir '%ls'",+	    path);+	return (ARCHIVE_FAILED);+}++/*+ * Note: Although we can skip setting the user id if the desired user+ * id matches the current user, we cannot skip setting the group, as+ * many systems set the gid based on the containing directory.  So+ * we have to perform a chown syscall if we want to set the SGID+ * bit.  (The alternative is to stat() and then possibly chown(); it's+ * more efficient to skip the stat() and just always chown().)  Note+ * that a successful chown() here clears the TODO_SGID_CHECK bit, which+ * allows set_mode to skip the stat() check for the GID.+ */+static int+set_ownership(struct archive_write_disk *a)+{+/* unfortunately, on win32 there is no 'root' user with uid 0,+   so we just have to try the chown and see if it works */++	/* If we know we can't change it, don't bother trying. */+	if (a->user_uid != 0  &&  a->user_uid != a->uid) {+		archive_set_error(&a->archive, errno,+		    "Can't set UID=%jd", (intmax_t)a->uid);+		return (ARCHIVE_WARN);+	}++	archive_set_error(&a->archive, errno,+	    "Can't set user=%jd/group=%jd for %ls",+	    (intmax_t)a->uid, (intmax_t)a->gid, a->name);+	return (ARCHIVE_WARN);+}++static int+set_times(struct archive_write_disk *a,+    HANDLE h, int mode, const wchar_t *name,+    time_t atime, long atime_nanos,+    time_t birthtime, long birthtime_nanos,+    time_t mtime, long mtime_nanos,+    time_t ctime_sec, long ctime_nanos)+{+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)+#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\+	 + (((nsec)/1000)*10))++	HANDLE hw = 0;+	ULARGE_INTEGER wintm;+	FILETIME *pfbtime;+	FILETIME fatime, fbtime, fmtime;++	(void)ctime_sec; /* UNUSED */+	(void)ctime_nanos; /* UNUSED */++	if (h != INVALID_HANDLE_VALUE) {+		hw = NULL;+	} else {+		wchar_t *ws;++		if (S_ISLNK(mode))+			return (ARCHIVE_OK);+		ws = __la_win_permissive_name_w(name);+		if (ws == NULL)+			goto settimes_failed;+		hw = CreateFileW(ws, FILE_WRITE_ATTRIBUTES,+		    0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);+		free(ws);+		if (hw == INVALID_HANDLE_VALUE)+			goto settimes_failed;+		h = hw;+	}++	wintm.QuadPart = WINTIME(atime, atime_nanos);+	fatime.dwLowDateTime = wintm.LowPart;+	fatime.dwHighDateTime = wintm.HighPart;+	wintm.QuadPart = WINTIME(mtime, mtime_nanos);+	fmtime.dwLowDateTime = wintm.LowPart;+	fmtime.dwHighDateTime = wintm.HighPart;+	/*+	 * SetFileTime() supports birthtime.+	 */+	if (birthtime > 0 || birthtime_nanos > 0) {+		wintm.QuadPart = WINTIME(birthtime, birthtime_nanos);+		fbtime.dwLowDateTime = wintm.LowPart;+		fbtime.dwHighDateTime = wintm.HighPart;+		pfbtime = &fbtime;+	} else+		pfbtime = NULL;+	if (SetFileTime(h, pfbtime, &fatime, &fmtime) == 0)+		goto settimes_failed;+	CloseHandle(hw);+	return (ARCHIVE_OK);++settimes_failed:+	CloseHandle(hw);+	archive_set_error(&a->archive, EINVAL, "Can't restore time");+	return (ARCHIVE_WARN);+}++static int+set_times_from_entry(struct archive_write_disk *a)+{+	time_t atime, birthtime, mtime, ctime_sec;+	long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec;++	/* Suitable defaults. */+	atime = birthtime = mtime = ctime_sec = a->start_time;+	atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0;++	/* If no time was provided, we're done. */+	if (!archive_entry_atime_is_set(a->entry)+	    && !archive_entry_birthtime_is_set(a->entry)+	    && !archive_entry_mtime_is_set(a->entry))+		return (ARCHIVE_OK);++	if (archive_entry_atime_is_set(a->entry)) {+		atime = archive_entry_atime(a->entry);+		atime_nsec = archive_entry_atime_nsec(a->entry);+	}+	if (archive_entry_birthtime_is_set(a->entry)) {+		birthtime = archive_entry_birthtime(a->entry);+		birthtime_nsec = archive_entry_birthtime_nsec(a->entry);+	}+	if (archive_entry_mtime_is_set(a->entry)) {+		mtime = archive_entry_mtime(a->entry);+		mtime_nsec = archive_entry_mtime_nsec(a->entry);+	}+	if (archive_entry_ctime_is_set(a->entry)) {+		ctime_sec = archive_entry_ctime(a->entry);+		ctime_nsec = archive_entry_ctime_nsec(a->entry);+	}++	return set_times(a, a->fh, a->mode, a->name,+			 atime, atime_nsec,+			 birthtime, birthtime_nsec,+			 mtime, mtime_nsec,+			 ctime_sec, ctime_nsec);+}++static int+set_mode(struct archive_write_disk *a, int mode)+{+	int r = ARCHIVE_OK;+	mode &= 07777; /* Strip off file type bits. */++	if (a->todo & TODO_SGID_CHECK) {+		/*+		 * If we don't know the GID is right, we must stat()+		 * to verify it.  We can't just check the GID of this+		 * process, since systems sometimes set GID from+		 * the enclosing dir or based on ACLs.+		 */+		if ((r = lazy_stat(a)) != ARCHIVE_OK)+			return (r);+		if (0 != a->gid) {+			mode &= ~ S_ISGID;+		}+		/* While we're here, double-check the UID. */+		if (0 != a->uid+		    && (a->todo & TODO_SUID)) {+			mode &= ~ S_ISUID;+		}+		a->todo &= ~TODO_SGID_CHECK;+		a->todo &= ~TODO_SUID_CHECK;+	} else if (a->todo & TODO_SUID_CHECK) {+		/*+		 * If we don't know the UID is right, we can just check+		 * the user, since all systems set the file UID from+		 * the process UID.+		 */+		if (a->user_uid != a->uid) {+			mode &= ~ S_ISUID;+		}+		a->todo &= ~TODO_SUID_CHECK;+	}++	if (S_ISLNK(a->mode)) {+#ifdef HAVE_LCHMOD+		/*+		 * If this is a symlink, use lchmod().  If the+		 * platform doesn't support lchmod(), just skip it.  A+		 * platform that doesn't provide a way to set+		 * permissions on symlinks probably ignores+		 * permissions on symlinks, so a failure here has no+		 * impact.+		 */+		if (lchmod(a->name, mode) != 0) {+			archive_set_error(&a->archive, errno,+			    "Can't set permissions to 0%o", (int)mode);+			r = ARCHIVE_WARN;+		}+#endif+	} else if (!S_ISDIR(a->mode)) {+		/*+		 * If it's not a symlink and not a dir, then use+		 * fchmod() or chmod(), depending on whether we have+		 * an fd.  Dirs get their perms set during the+		 * post-extract fixup, which is handled elsewhere.+		 */+#ifdef HAVE_FCHMOD+		if (a->fd >= 0) {+			if (fchmod(a->fd, mode) != 0) {+				archive_set_error(&a->archive, errno,+				    "Can't set permissions to 0%o", (int)mode);+				r = ARCHIVE_WARN;+			}+		} else+#endif+			/* If this platform lacks fchmod(), then+			 * we'll just use chmod(). */+			if (la_chmod(a->name, mode) != 0) {+				archive_set_error(&a->archive, errno,+				    "Can't set permissions to 0%o", (int)mode);+				r = ARCHIVE_WARN;+			}+	}+	return (r);+}++static int set_fflags_platform(const wchar_t *name, unsigned long fflags_set,+    unsigned long fflags_clear)+{+	DWORD oldflags, newflags;+	wchar_t *fullname;++	const DWORD settable_flags =+	    FILE_ATTRIBUTE_ARCHIVE |+	    FILE_ATTRIBUTE_HIDDEN |+	    FILE_ATTRIBUTE_NORMAL |+	    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED |+	    FILE_ATTRIBUTE_OFFLINE |+	    FILE_ATTRIBUTE_READONLY |+	    FILE_ATTRIBUTE_SYSTEM |+	    FILE_ATTRIBUTE_TEMPORARY;++	oldflags = GetFileAttributesW(name);+	if (oldflags == (DWORD)-1 &&+	    GetLastError() == ERROR_INVALID_NAME) {+		fullname = __la_win_permissive_name_w(name);+		oldflags = GetFileAttributesW(fullname);+	}+	if (oldflags == (DWORD)-1) {+		la_dosmaperr(GetLastError());+		return (ARCHIVE_WARN);+	}+	newflags = ((oldflags & ~fflags_clear) | fflags_set) & settable_flags;+	if(SetFileAttributesW(name, newflags) == 0)+		return (ARCHIVE_WARN);+	return (ARCHIVE_OK);+}++static int+clear_nochange_fflags(struct archive_write_disk *a)+{+	return (set_fflags_platform(a->name, 0, FILE_ATTRIBUTE_READONLY));+}++static int+set_fflags(struct archive_write_disk *a)+{+	unsigned long	set, clear;++	if (a->todo & TODO_FFLAGS) {+		archive_entry_fflags(a->entry, &set, &clear);+		if (set == 0  && clear == 0)+			return (ARCHIVE_OK);+		return (set_fflags_platform(a->name, set, clear));++        }+	return (ARCHIVE_OK);+}++/* Default empty function body to satisfy mainline code. */+static int+set_acls(struct archive_write_disk *a, HANDLE h, const wchar_t *name,+	 struct archive_acl *acl)+{+	(void)a; /* UNUSED */+	(void)h; /* UNUSED */+	(void)name; /* UNUSED */+	(void)acl; /* UNUSED */+	return (ARCHIVE_OK);+}++/*+ * Restore extended attributes - stub implementation for unsupported systems+ */+static int+set_xattrs(struct archive_write_disk *a)+{+	static int warning_done = 0;++	/* If there aren't any extended attributes, then it's okay not+	 * to extract them, otherwise, issue a single warning. */+	if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) {+		warning_done = 1;+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Cannot restore extended attributes on this system");+		return (ARCHIVE_WARN);+	}+	/* Warning was already emitted; suppress further warnings. */+	return (ARCHIVE_OK);+}++static void+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)+{+	ULARGE_INTEGER utc;++	utc.HighPart = filetime->dwHighDateTime;+	utc.LowPart  = filetime->dwLowDateTime;+	if (utc.QuadPart >= EPOC_TIME) {+		utc.QuadPart -= EPOC_TIME;+		/* milli seconds base */+		*t = (time_t)(utc.QuadPart / 10000000);+		/* nano seconds base */+		*ns = (long)(utc.QuadPart % 10000000) * 100;+	} else {+		*t = 0;+		*ns = 0;+	}+}+/*+ * Test if file on disk is older than entry.+ */+static int+older(BY_HANDLE_FILE_INFORMATION *st, struct archive_entry *entry)+{+	time_t sec;+	long nsec;++	fileTimeToUtc(&st->ftLastWriteTime, &sec, &nsec);+	/* First, test the seconds and return if we have a definite answer. */+	/* Definitely older. */+	if (sec < archive_entry_mtime(entry))+		return (1);+	/* Definitely younger. */+	if (sec > archive_entry_mtime(entry))+		return (0);+	if (nsec < archive_entry_mtime_nsec(entry))+		return (1);+	/* Same age or newer, so not older. */+	return (0);+}++#endif /* _WIN32 && !__CYGWIN__ */+
+ c/archive_write_open_fd.c view
@@ -0,0 +1,144 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_open_fd.c 201093 2009-12-28 02:28:44Z kientzle $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_IO_H+#include <io.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"++struct write_fd_data {+	int		fd;+};++static int	file_close(struct archive *, void *);+static int	file_open(struct archive *, void *);+static ssize_t	file_write(struct archive *, void *, const void *buff, size_t);++int+archive_write_open_fd(struct archive *a, int fd)+{+	struct write_fd_data *mine;++	mine = (struct write_fd_data *)malloc(sizeof(*mine));+	if (mine == NULL) {+		archive_set_error(a, ENOMEM, "No memory");+		return (ARCHIVE_FATAL);+	}+	mine->fd = fd;+#if defined(__CYGWIN__) || defined(_WIN32)+	setmode(mine->fd, O_BINARY);+#endif+	return (archive_write_open(a, mine,+		    file_open, file_write, file_close));+}++static int+file_open(struct archive *a, void *client_data)+{+	struct write_fd_data *mine;+	struct stat st;++	mine = (struct write_fd_data *)client_data;++	if (fstat(mine->fd, &st) != 0) {+		archive_set_error(a, errno, "Couldn't stat fd %d", mine->fd);+		return (ARCHIVE_FATAL);+	}++	/*+	 * If this is a regular file, don't add it to itself.+	 */+	if (S_ISREG(st.st_mode))+		archive_write_set_skip_file(a, st.st_dev, st.st_ino);++	/*+	 * If client hasn't explicitly set the last block handling,+	 * then set it here.+	 */+	if (archive_write_get_bytes_in_last_block(a) < 0) {+		/* If the output is a block or character device, fifo,+		 * or stdout, pad the last block, otherwise leave it+		 * unpadded. */+		if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||+		    S_ISFIFO(st.st_mode) || (mine->fd == 1))+			/* Last block will be fully padded. */+			archive_write_set_bytes_in_last_block(a, 0);+		else+			archive_write_set_bytes_in_last_block(a, 1);+	}++	return (ARCHIVE_OK);+}++static ssize_t+file_write(struct archive *a, void *client_data, const void *buff, size_t length)+{+	struct write_fd_data	*mine;+	ssize_t	bytesWritten;++	mine = (struct write_fd_data *)client_data;+	for (;;) {+		bytesWritten = write(mine->fd, buff, length);+		if (bytesWritten <= 0) {+			if (errno == EINTR)+				continue;+			archive_set_error(a, errno, "Write error");+			return (-1);+		}+		return (bytesWritten);+	}+}++static int+file_close(struct archive *a, void *client_data)+{+	struct write_fd_data	*mine = (struct write_fd_data *)client_data;++	(void)a; /* UNUSED */+	free(mine);+	return (ARCHIVE_OK);+}
+ c/archive_write_open_file.c view
@@ -0,0 +1,109 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_file.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"++struct write_FILE_data {+	FILE		*f;+};++static int	file_close(struct archive *, void *);+static int	file_open(struct archive *, void *);+static ssize_t	file_write(struct archive *, void *, const void *buff, size_t);++int+archive_write_open_FILE(struct archive *a, FILE *f)+{+	struct write_FILE_data *mine;++	mine = (struct write_FILE_data *)malloc(sizeof(*mine));+	if (mine == NULL) {+		archive_set_error(a, ENOMEM, "No memory");+		return (ARCHIVE_FATAL);+	}+	mine->f = f;+	return (archive_write_open(a, mine,+		    file_open, file_write, file_close));+}++static int+file_open(struct archive *a, void *client_data)+{+	(void)a; /* UNUSED */+	(void)client_data; /* UNUSED */++	return (ARCHIVE_OK);+}++static ssize_t+file_write(struct archive *a, void *client_data, const void *buff, size_t length)+{+	struct write_FILE_data	*mine;+	size_t	bytesWritten;++	mine = client_data;+	for (;;) {+		bytesWritten = fwrite(buff, 1, length, mine->f);+		if (bytesWritten <= 0) {+			if (errno == EINTR)+				continue;+			archive_set_error(a, errno, "Write error");+			return (-1);+		}+		return (bytesWritten);+	}+}++static int+file_close(struct archive *a, void *client_data)+{+	struct write_FILE_data	*mine = client_data;++	(void)a; /* UNUSED */+	free(mine);+	return (ARCHIVE_OK);+}
+ c/archive_write_open_filename.c view
@@ -0,0 +1,253 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_open_filename.c 191165 2009-04-17 00:39:35Z kientzle $");++#ifdef HAVE_SYS_STAT_H+#include <sys/stat.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_FCNTL_H+#include <fcntl.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_string.h"++#ifndef O_BINARY+#define O_BINARY 0+#endif+#ifndef O_CLOEXEC+#define O_CLOEXEC	0+#endif++struct write_file_data {+	int		fd;+	struct archive_mstring filename;+};++static int	file_close(struct archive *, void *);+static int	file_open(struct archive *, void *);+static ssize_t	file_write(struct archive *, void *, const void *buff, size_t);+static int	open_filename(struct archive *, int, const void *);++int+archive_write_open_file(struct archive *a, const char *filename)+{+	return (archive_write_open_filename(a, filename));+}++int+archive_write_open_filename(struct archive *a, const char *filename)+{++	if (filename == NULL || filename[0] == '\0')+		return (archive_write_open_fd(a, 1));++	return (open_filename(a, 1, filename));+}++int+archive_write_open_filename_w(struct archive *a, const wchar_t *filename)+{++	if (filename == NULL || filename[0] == L'\0')+		return (archive_write_open_fd(a, 1));++	return (open_filename(a, 0, filename));+}++static int+open_filename(struct archive *a, int mbs_fn, const void *filename)+{+	struct write_file_data *mine;+	int r;++	mine = (struct write_file_data *)calloc(1, sizeof(*mine));+	if (mine == NULL) {+		archive_set_error(a, ENOMEM, "No memory");+		return (ARCHIVE_FATAL);+	}+	if (mbs_fn)+		r = archive_mstring_copy_mbs(&mine->filename, filename);+	else+		r = archive_mstring_copy_wcs(&mine->filename, filename);+	if (r < 0) {+		if (errno == ENOMEM) {+			archive_set_error(a, ENOMEM, "No memory");+			return (ARCHIVE_FATAL);+		}+		if (mbs_fn)+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Can't convert '%s' to WCS",+			    (const char *)filename);+		else+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "Can't convert '%S' to MBS",+			    (const wchar_t *)filename);+		return (ARCHIVE_FAILED);+	}+	mine->fd = -1;+	return (archive_write_open(a, mine,+		file_open, file_write, file_close));+}++static int+file_open(struct archive *a, void *client_data)+{+	int flags;+	struct write_file_data *mine;+	struct stat st;+#if defined(_WIN32) && !defined(__CYGWIN__)+	wchar_t *fullpath;+#endif+	const wchar_t *wcs;+	const char *mbs;++	mine = (struct write_file_data *)client_data;+	flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC;++	/*+	 * Open the file.+	 */+	mbs = NULL; wcs = NULL;+#if defined(_WIN32) && !defined(__CYGWIN__)+	if (archive_mstring_get_wcs(a, &mine->filename, &wcs) != 0) {+		if (errno == ENOMEM)+			archive_set_error(a, errno, "No memory");+		else {+			archive_mstring_get_mbs(a, &mine->filename, &mbs);+			archive_set_error(a, errno,+			    "Can't convert '%s' to WCS", mbs);+		}+		return (ARCHIVE_FATAL);+	}+	fullpath = __la_win_permissive_name_w(wcs);+	if (fullpath != NULL) {+		mine->fd = _wopen(fullpath, flags, 0666);+		free(fullpath);+	} else+		mine->fd = _wopen(wcs, flags, 0666);+#else+	if (archive_mstring_get_mbs(a, &mine->filename, &mbs) != 0) {+		if (errno == ENOMEM)+			archive_set_error(a, errno, "No memory");+		else {+			archive_mstring_get_wcs(a, &mine->filename, &wcs);+			archive_set_error(a, errno,+			    "Can't convert '%S' to MBS", wcs);+		}+		return (ARCHIVE_FATAL);+	}+	mine->fd = open(mbs, flags, 0666);+	__archive_ensure_cloexec_flag(mine->fd);+#endif+	if (mine->fd < 0) {+		if (mbs != NULL)+			archive_set_error(a, errno, "Failed to open '%s'", mbs);+		else+			archive_set_error(a, errno, "Failed to open '%S'", wcs);+		return (ARCHIVE_FATAL);+	}++	if (fstat(mine->fd, &st) != 0) {+		if (mbs != NULL)+			archive_set_error(a, errno, "Couldn't stat '%s'", mbs);+		else+			archive_set_error(a, errno, "Couldn't stat '%S'", wcs);+		return (ARCHIVE_FATAL);+	}++	/*+	 * Set up default last block handling.+	 */+	if (archive_write_get_bytes_in_last_block(a) < 0) {+		if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||+		    S_ISFIFO(st.st_mode))+			/* Pad last block when writing to device or FIFO. */+			archive_write_set_bytes_in_last_block(a, 0);+		else+			/* Don't pad last block otherwise. */+			archive_write_set_bytes_in_last_block(a, 1);+	}++	/*+	 * If the output file is a regular file, don't add it to+	 * itself.  If it's a device file, it's okay to add the device+	 * entry to the output archive.+	 */+	if (S_ISREG(st.st_mode))+		archive_write_set_skip_file(a, st.st_dev, st.st_ino);++	return (ARCHIVE_OK);+}++static ssize_t+file_write(struct archive *a, void *client_data, const void *buff,+    size_t length)+{+	struct write_file_data	*mine;+	ssize_t	bytesWritten;++	mine = (struct write_file_data *)client_data;+	for (;;) {+		bytesWritten = write(mine->fd, buff, length);+		if (bytesWritten <= 0) {+			if (errno == EINTR)+				continue;+			archive_set_error(a, errno, "Write error");+			return (-1);+		}+		return (bytesWritten);+	}+}++static int+file_close(struct archive *a, void *client_data)+{+	struct write_file_data	*mine = (struct write_file_data *)client_data;++	(void)a; /* UNUSED */++	if (mine->fd >= 0)+		close(mine->fd);++	archive_mstring_clean(&mine->filename);+	free(mine);+	return (ARCHIVE_OK);+}
+ c/archive_write_open_memory.c view
@@ -0,0 +1,113 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_memory.c,v 1.3 2007/01/09 08:05:56 kientzle Exp $");++#include <errno.h>+#include <stdlib.h>+#include <string.h>++#include "archive.h"++struct write_memory_data {+	size_t	used;+	size_t  size;+	size_t * client_size;+	unsigned char * buff;+};++static int	memory_write_close(struct archive *, void *);+static int	memory_write_open(struct archive *, void *);+static ssize_t	memory_write(struct archive *, void *, const void *buff, size_t);++/*+ * Client provides a pointer to a block of memory to receive+ * the data.  The 'size' param both tells us the size of the+ * client buffer and lets us tell the client the final size.+ */+int+archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t *used)+{+	struct write_memory_data *mine;++	mine = (struct write_memory_data *)calloc(1, sizeof(*mine));+	if (mine == NULL) {+		archive_set_error(a, ENOMEM, "No memory");+		return (ARCHIVE_FATAL);+	}+	mine->buff = buff;+	mine->size = buffSize;+	mine->client_size = used;+	return (archive_write_open(a, mine,+		    memory_write_open, memory_write, memory_write_close));+}++static int+memory_write_open(struct archive *a, void *client_data)+{+	struct write_memory_data *mine;+	mine = client_data;+	mine->used = 0;+	if (mine->client_size != NULL)+		*mine->client_size = mine->used;+	/* Disable padding if it hasn't been set explicitly. */+	if (-1 == archive_write_get_bytes_in_last_block(a))+		archive_write_set_bytes_in_last_block(a, 1);+	return (ARCHIVE_OK);+}++/*+ * Copy the data into the client buffer.+ * Note that we update mine->client_size on every write.+ * In particular, this means the client can follow exactly+ * how much has been written into their buffer at any time.+ */+static ssize_t+memory_write(struct archive *a, void *client_data, const void *buff, size_t length)+{+	struct write_memory_data *mine;+	mine = client_data;++	if (mine->used + length > mine->size) {+		archive_set_error(a, ENOMEM, "Buffer exhausted");+		return (ARCHIVE_FATAL);+	}+	memcpy(mine->buff + mine->used, buff, length);+	mine->used += length;+	if (mine->client_size != NULL)+		*mine->client_size = mine->used;+	return (length);+}++static int+memory_write_close(struct archive *a, void *client_data)+{+	struct write_memory_data *mine;+	(void)a; /* UNUSED */+	mine = client_data;+	free(mine);+	return (ARCHIVE_OK);+}
+ c/archive_write_private.h view
@@ -0,0 +1,164 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/archive_write_private.h 201155 2009-12-29 05:20:12Z kientzle $+ */++#ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED+#define ARCHIVE_WRITE_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#ifndef __LIBARCHIVE_TEST+#error This header is only to be used internally to libarchive.+#endif+#endif++#include "archive.h"+#include "archive_string.h"+#include "archive_private.h"++#define	ARCHIVE_WRITE_FILTER_STATE_NEW		1U+#define	ARCHIVE_WRITE_FILTER_STATE_OPEN		2U+#define	ARCHIVE_WRITE_FILTER_STATE_CLOSED	4U+#define	ARCHIVE_WRITE_FILTER_STATE_FATAL	0x8000U++struct archive_write;++struct archive_write_filter {+	int64_t bytes_written;+	struct archive *archive; /* Associated archive. */+	struct archive_write_filter *next_filter; /* Who I write to. */+	int	(*options)(struct archive_write_filter *,+	    const char *key, const char *value);+	int	(*open)(struct archive_write_filter *);+	int	(*write)(struct archive_write_filter *, const void *, size_t);+	int	(*close)(struct archive_write_filter *);+	int	(*free)(struct archive_write_filter *);+	void	 *data;+	const char *name;+	int	  code;+	int	  bytes_per_block;+	int	  bytes_in_last_block;+	int	  state;+};++#if ARCHIVE_VERSION < 4000000+void __archive_write_filters_free(struct archive *);+#endif++struct archive_write_filter *__archive_write_allocate_filter(struct archive *);++int __archive_write_output(struct archive_write *, const void *, size_t);+int __archive_write_nulls(struct archive_write *, size_t);+int __archive_write_filter(struct archive_write_filter *, const void *, size_t);++struct archive_write {+	struct archive	archive;++	/* Dev/ino of the archive being written. */+	int		  skip_file_set;+	int64_t		  skip_file_dev;+	int64_t		  skip_file_ino;++	/* Utility:  Pointer to a block of nulls. */+	const unsigned char	*nulls;+	size_t			 null_length;++	/* Callbacks to open/read/write/close archive stream. */+	archive_open_callback	*client_opener;+	archive_write_callback	*client_writer;+	archive_close_callback	*client_closer;+	void			*client_data;++	/*+	 * Blocking information.  Note that bytes_in_last_block is+	 * misleadingly named; I should find a better name.  These+	 * control the final output from all compressors, including+	 * compression_none.+	 */+	int		  bytes_per_block;+	int		  bytes_in_last_block;++	/*+	 * First and last write filters in the pipeline.+	 */+	struct archive_write_filter *filter_first;+	struct archive_write_filter *filter_last;++	/*+	 * Pointers to format-specific functions for writing.  They're+	 * initialized by archive_write_set_format_XXX() calls.+	 */+	void	 *format_data;+	const char *format_name;+	int	(*format_init)(struct archive_write *);+	int	(*format_options)(struct archive_write *,+		    const char *key, const char *value);+	int	(*format_finish_entry)(struct archive_write *);+	int 	(*format_write_header)(struct archive_write *,+		    struct archive_entry *);+	ssize_t	(*format_write_data)(struct archive_write *,+		    const void *buff, size_t);+	int	(*format_close)(struct archive_write *);+	int	(*format_free)(struct archive_write *);+++	/*+	 * Encryption passphrase.+	 */+	char		*passphrase;+	archive_passphrase_callback *passphrase_callback;+	void		*passphrase_client_data;+};++/*+ * Utility function to format a USTAR header into a buffer.  If+ * "strict" is set, this tries to create the absolutely most portable+ * version of a ustar header.  If "strict" is set to 0, then it will+ * relax certain requirements.+ *+ * Generally, format-specific declarations don't belong in this+ * header; this is a rare example of a function that is shared by+ * two very similar formats (ustar and pax).+ */+int+__archive_write_format_header_ustar(struct archive_write *, char buff[512],+    struct archive_entry *, int tartype, int strict,+    struct archive_string_conv *);++struct archive_write_program_data;+struct archive_write_program_data * __archive_write_program_allocate(const char *program_name);+int	__archive_write_program_free(struct archive_write_program_data *);+int	__archive_write_program_open(struct archive_write_filter *,+	    struct archive_write_program_data *, const char *);+int	__archive_write_program_close(struct archive_write_filter *,+	    struct archive_write_program_data *);+int	__archive_write_program_write(struct archive_write_filter *,+	    struct archive_write_program_data *, const void *, size_t);++/*+ * Get a encryption passphrase.+ */+const char * __archive_write_get_passphrase(struct archive_write *a);+#endif
+ c/archive_write_set_format.c view
@@ -0,0 +1,123 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format.c 201168 2009-12-29 06:15:32Z kientzle $");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif++#include "archive.h"+#include "archive_private.h"+#include "archive_write_set_format_private.h"++/* A table that maps format codes to functions. */+static const+struct { int code; int (*setter)(struct archive *); } codes[] =+{+	{ ARCHIVE_FORMAT_7ZIP,		archive_write_set_format_7zip },+	{ ARCHIVE_FORMAT_CPIO,		archive_write_set_format_cpio },+	{ ARCHIVE_FORMAT_CPIO_POSIX,	archive_write_set_format_cpio },+	{ ARCHIVE_FORMAT_CPIO_SVR4_NOCRC,	archive_write_set_format_cpio_newc },+	{ ARCHIVE_FORMAT_ISO9660,	archive_write_set_format_iso9660 },+	{ ARCHIVE_FORMAT_MTREE,		archive_write_set_format_mtree },+	{ ARCHIVE_FORMAT_RAW,		archive_write_set_format_raw },+	{ ARCHIVE_FORMAT_SHAR,		archive_write_set_format_shar },+	{ ARCHIVE_FORMAT_SHAR_BASE,	archive_write_set_format_shar },+	{ ARCHIVE_FORMAT_SHAR_DUMP,	archive_write_set_format_shar_dump },+	{ ARCHIVE_FORMAT_TAR,	archive_write_set_format_pax_restricted },+	{ ARCHIVE_FORMAT_TAR_GNUTAR,	archive_write_set_format_gnutar },+	{ ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, archive_write_set_format_pax },+	{ ARCHIVE_FORMAT_TAR_PAX_RESTRICTED,+				archive_write_set_format_pax_restricted },+	{ ARCHIVE_FORMAT_TAR_USTAR,	archive_write_set_format_ustar },+	{ ARCHIVE_FORMAT_WARC,		archive_write_set_format_warc },+	{ ARCHIVE_FORMAT_XAR,		archive_write_set_format_xar },+	{ ARCHIVE_FORMAT_ZIP,		archive_write_set_format_zip },+	{ 0,		NULL }+};++int+archive_write_set_format(struct archive *a, int code)+{+	int i;++	for (i = 0; codes[i].code != 0; i++) {+		if (code == codes[i].code)+			return ((codes[i].setter)(a));+	}++	archive_set_error(a, EINVAL, "No such format");+	return (ARCHIVE_FATAL);+}++void+__archive_write_entry_filetype_unsupported(struct archive *a,+    struct archive_entry *entry, const char *format)+{+	const char *name = NULL;++	switch (archive_entry_filetype(entry)) {+	/*+	 * All formats should be able to archive regular files (AE_IFREG)+	 */+	case AE_IFDIR:+		name = "directories";+		break;+	case AE_IFLNK:+		name = "symbolic links";+		break;+	case AE_IFCHR:+		name = "character devices";+		break;+	case AE_IFBLK:+		name = "block devices";+		break;+	case AE_IFIFO:+		name = "named pipes";+		break;+	case AE_IFSOCK:+		name = "sockets";+		break;+	default:+		break;+	}++	if (name != NULL) {+		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,+		    "%s: %s format cannot archive %s",+		    archive_entry_pathname(entry), format, name);+	} else {+		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,+		    "%s: %s format cannot archive files with mode 0%lo",+		    archive_entry_pathname(entry), format,+		    (unsigned long)archive_entry_mode(entry));+	}+}
+ c/archive_write_set_format_7zip.c view
@@ -0,0 +1,2317 @@+/*-+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdlib.h>+#ifdef HAVE_BZLIB_H+#include <bzlib.h>+#endif+#if HAVE_LZMA_H+#include <lzma.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif++#include "archive.h"+#ifndef HAVE_ZLIB_H+#include "archive_crc32.h"+#endif+#include "archive_endian.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_ppmd7_private.h"+#include "archive_private.h"+#include "archive_rb.h"+#include "archive_string.h"+#include "archive_write_private.h"+#include "archive_write_set_format_private.h"++/*+ * Codec ID+ */+#define _7Z_COPY	0+#define _7Z_LZMA1	0x030101+#define _7Z_LZMA2	0x21+#define _7Z_DEFLATE	0x040108+#define _7Z_BZIP2	0x040202+#define _7Z_PPMD	0x030401++/*+ * 7-Zip header property IDs.+ */+#define kEnd			0x00+#define kHeader			0x01+#define kArchiveProperties	0x02+#define kAdditionalStreamsInfo	0x03+#define kMainStreamsInfo	0x04+#define kFilesInfo		0x05+#define kPackInfo		0x06+#define kUnPackInfo		0x07+#define kSubStreamsInfo		0x08+#define kSize			0x09+#define kCRC			0x0A+#define kFolder			0x0B+#define kCodersUnPackSize	0x0C+#define kNumUnPackStream	0x0D+#define kEmptyStream		0x0E+#define kEmptyFile		0x0F+#define kAnti			0x10+#define kName			0x11+#define kCTime			0x12+#define kATime			0x13+#define kMTime			0x14+#define kAttributes		0x15+#define kEncodedHeader		0x17++enum la_zaction {+	ARCHIVE_Z_FINISH,+	ARCHIVE_Z_RUN+};++/*+ * A stream object of universal compressor.+ */+struct la_zstream {+	const uint8_t		*next_in;+	size_t			 avail_in;+	uint64_t		 total_in;++	uint8_t			*next_out;+	size_t			 avail_out;+	uint64_t		 total_out;++	uint32_t		 prop_size;+	uint8_t			*props;++	int			 valid;+	void			*real_stream;+	int			 (*code) (struct archive *a,+				    struct la_zstream *lastrm,+				    enum la_zaction action);+	int			 (*end)(struct archive *a,+				    struct la_zstream *lastrm);+};++#define PPMD7_DEFAULT_ORDER	6+#define PPMD7_DEFAULT_MEM_SIZE	(1 << 24)++struct ppmd_stream {+	int			 stat;+	CPpmd7			 ppmd7_context;+	CPpmd7z_RangeEnc	 range_enc;+	IByteOut		 byteout;+	uint8_t			*buff;+	uint8_t			*buff_ptr;+	uint8_t			*buff_end;+	size_t			 buff_bytes;+};++struct coder {+	unsigned		 codec;+	size_t			 prop_size;+	uint8_t			*props;+};++struct file {+	struct archive_rb_node	 rbnode;++	struct file		*next;+	unsigned		 name_len;+	uint8_t			*utf16name;/* UTF16-LE name. */+	uint64_t		 size;+	unsigned		 flg;+#define MTIME_IS_SET	(1<<0)+#define ATIME_IS_SET	(1<<1)+#define CTIME_IS_SET	(1<<2)+#define CRC32_IS_SET	(1<<3)+#define HAS_STREAM	(1<<4)++	struct {+		time_t		 time;+		long		 time_ns;+	}			 times[3];+#define MTIME 0+#define ATIME 1+#define CTIME 2++	mode_t			 mode;+	uint32_t		 crc32;++	signed int		 dir:1;+};++struct _7zip {+	int			 temp_fd;+	uint64_t		 temp_offset;++	struct file		*cur_file;+	size_t			 total_number_entry;+	size_t			 total_number_nonempty_entry;+	size_t			 total_number_empty_entry;+	size_t			 total_number_dir_entry;+	size_t			 total_bytes_entry_name;+	size_t			 total_number_time_defined[3];+	uint64_t		 total_bytes_compressed;+	uint64_t		 total_bytes_uncompressed;+	uint64_t		 entry_bytes_remaining;+	uint32_t		 entry_crc32;+	uint32_t		 precode_crc32;+	uint32_t		 encoded_crc32;+	int			 crc32flg;+#define	PRECODE_CRC32	1+#define	ENCODED_CRC32	2++	unsigned		 opt_compression;+	int			 opt_compression_level;++	struct la_zstream	 stream;+	struct coder		 coder;++	struct archive_string_conv *sconv;++	/*+	 * Compressed data buffer.+	 */+	unsigned char		 wbuff[512 * 20 * 6];+	size_t			 wbuff_remaining;++	/*+	 * The list of the file entries which has its contents is used to+	 * manage struct file objects.+	 * We use 'next' (a member of struct file) to chain.+	 */+	struct {+		struct file	*first;+		struct file	**last;+	}			 file_list, empty_list;+	struct archive_rb_tree	 rbtree;/* for empty files */+};++static int	_7z_options(struct archive_write *,+		    const char *, const char *);+static int	_7z_write_header(struct archive_write *,+		    struct archive_entry *);+static ssize_t	_7z_write_data(struct archive_write *,+		    const void *, size_t);+static int	_7z_finish_entry(struct archive_write *);+static int	_7z_close(struct archive_write *);+static int	_7z_free(struct archive_write *);+static int	file_cmp_node(const struct archive_rb_node *,+		    const struct archive_rb_node *);+static int	file_cmp_key(const struct archive_rb_node *, const void *);+static int	file_new(struct archive_write *a, struct archive_entry *,+		    struct file **);+static void	file_free(struct file *);+static void	file_register(struct _7zip *, struct file *);+static void	file_register_empty(struct _7zip *, struct file *);+static void	file_init_register(struct _7zip *);+static void	file_init_register_empty(struct _7zip *);+static void	file_free_register(struct _7zip *);+static ssize_t	compress_out(struct archive_write *, const void *, size_t ,+		    enum la_zaction);+static int	compression_init_encoder_copy(struct archive *,+		    struct la_zstream *);+static int	compression_code_copy(struct archive *,+		    struct la_zstream *, enum la_zaction);+static int	compression_end_copy(struct archive *, struct la_zstream *);+static int	compression_init_encoder_deflate(struct archive *,+		    struct la_zstream *, int, int);+#ifdef HAVE_ZLIB_H+static int	compression_code_deflate(struct archive *,+		    struct la_zstream *, enum la_zaction);+static int	compression_end_deflate(struct archive *, struct la_zstream *);+#endif+static int	compression_init_encoder_bzip2(struct archive *,+		    struct la_zstream *, int);+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+static int	compression_code_bzip2(struct archive *,+		    struct la_zstream *, enum la_zaction);+static int	compression_end_bzip2(struct archive *, struct la_zstream *);+#endif+static int	compression_init_encoder_lzma1(struct archive *,+		    struct la_zstream *, int);+static int	compression_init_encoder_lzma2(struct archive *,+		    struct la_zstream *, int);+#if defined(HAVE_LZMA_H)+static int	compression_code_lzma(struct archive *,+		    struct la_zstream *, enum la_zaction);+static int	compression_end_lzma(struct archive *, struct la_zstream *);+#endif+static int	compression_init_encoder_ppmd(struct archive *,+		    struct la_zstream *, unsigned, uint32_t);+static int	compression_code_ppmd(struct archive *,+		    struct la_zstream *, enum la_zaction);+static int	compression_end_ppmd(struct archive *, struct la_zstream *);+static int	_7z_compression_init_encoder(struct archive_write *, unsigned,+		    int);+static int	compression_code(struct archive *,+		    struct la_zstream *, enum la_zaction);+static int	compression_end(struct archive *,+		    struct la_zstream *);+static int	enc_uint64(struct archive_write *, uint64_t);+static int	make_header(struct archive_write *, uint64_t, uint64_t,+		    uint64_t, int, struct coder *);+static int	make_streamsInfo(struct archive_write *, uint64_t, uint64_t,+		    	uint64_t, int, struct coder *, int, uint32_t);++int+archive_write_set_format_7zip(struct archive *_a)+{+	static const struct archive_rb_tree_ops rb_ops = {+		file_cmp_node, file_cmp_key+	};+	struct archive_write *a = (struct archive_write *)_a;+	struct _7zip *zip;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_7zip");++	/* If another format was already registered, unregister it. */+	if (a->format_free != NULL)+		(a->format_free)(a);++	zip = calloc(1, sizeof(*zip));+	if (zip == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate 7-Zip data");+		return (ARCHIVE_FATAL);+	}+	zip->temp_fd = -1;+	__archive_rb_tree_init(&(zip->rbtree), &rb_ops);+	file_init_register(zip);+	file_init_register_empty(zip);++	/* Set default compression type and its level. */+#if HAVE_LZMA_H+	zip->opt_compression = _7Z_LZMA1;+#elif defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+	zip->opt_compression = _7Z_BZIP2;+#elif defined(HAVE_ZLIB_H)+	zip->opt_compression = _7Z_DEFLATE;+#else+	zip->opt_compression = _7Z_COPY;+#endif+	zip->opt_compression_level = 6;++	a->format_data = zip;++	a->format_name = "7zip";+	a->format_options = _7z_options;+	a->format_write_header = _7z_write_header;+	a->format_write_data = _7z_write_data;+	a->format_finish_entry = _7z_finish_entry;+	a->format_close = _7z_close;+	a->format_free = _7z_free;+	a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;+	a->archive.archive_format_name = "7zip";++	return (ARCHIVE_OK);+}++static int+_7z_options(struct archive_write *a, const char *key, const char *value)+{+	struct _7zip *zip;++	zip = (struct _7zip *)a->format_data;++	if (strcmp(key, "compression") == 0) {+		const char *name = NULL;++		if (value == NULL || strcmp(value, "copy") == 0 ||+		    strcmp(value, "COPY") == 0 ||+		    strcmp(value, "store") == 0 ||+		    strcmp(value, "STORE") == 0)+			zip->opt_compression = _7Z_COPY;+		else if (strcmp(value, "deflate") == 0 ||+		    strcmp(value, "DEFLATE") == 0)+#if HAVE_ZLIB_H+			zip->opt_compression = _7Z_DEFLATE;+#else+			name = "deflate";+#endif+		else if (strcmp(value, "bzip2") == 0 ||+		    strcmp(value, "BZIP2") == 0)+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+			zip->opt_compression = _7Z_BZIP2;+#else+			name = "bzip2";+#endif+		else if (strcmp(value, "lzma1") == 0 ||+		    strcmp(value, "LZMA1") == 0)+#if HAVE_LZMA_H+			zip->opt_compression = _7Z_LZMA1;+#else+			name = "lzma1";+#endif+		else if (strcmp(value, "lzma2") == 0 ||+		    strcmp(value, "LZMA2") == 0)+#if HAVE_LZMA_H+			zip->opt_compression = _7Z_LZMA2;+#else+			name = "lzma2";+#endif+		else if (strcmp(value, "ppmd") == 0 ||+		    strcmp(value, "PPMD") == 0 ||+		    strcmp(value, "PPMd") == 0)+			zip->opt_compression = _7Z_PPMD;+		else {+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "Unknown compression name: `%s'",+			    value);+			return (ARCHIVE_FAILED);+		}+		if (name != NULL) {+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "`%s' compression not supported "+			    "on this platform",+			    name);+			return (ARCHIVE_FAILED);+		}+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "compression-level") == 0) {+		if (value == NULL ||+		    !(value[0] >= '0' && value[0] <= '9') ||+		    value[1] != '\0') {+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "Illegal value `%s'",+			    value);+			return (ARCHIVE_FAILED);+		}+		zip->opt_compression_level = value[0] - '0';+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+_7z_write_header(struct archive_write *a, struct archive_entry *entry)+{+	struct _7zip *zip;+	struct file *file;+	int r;++	zip = (struct _7zip *)a->format_data;+	zip->cur_file = NULL;+	zip->entry_bytes_remaining = 0;++	if (zip->sconv == NULL) {+		zip->sconv = archive_string_conversion_to_charset(+		    &a->archive, "UTF-16LE", 1);+		if (zip->sconv == NULL)+			return (ARCHIVE_FATAL);+	}++	r = file_new(a, entry, &file);+	if (r < ARCHIVE_WARN) {+		if (file != NULL)+			file_free(file);+		return (r);+	}+	if (file->size == 0 && file->dir) {+		if (!__archive_rb_tree_insert_node(&(zip->rbtree),+		    (struct archive_rb_node *)file)) {+			/* We have already had the same file. */+			file_free(file);+			return (ARCHIVE_OK);+		}+	}++	if (file->flg & MTIME_IS_SET)+		zip->total_number_time_defined[MTIME]++;+	if (file->flg & CTIME_IS_SET)+		zip->total_number_time_defined[CTIME]++;+	if (file->flg & ATIME_IS_SET)+		zip->total_number_time_defined[ATIME]++;++	zip->total_number_entry++;+	zip->total_bytes_entry_name += file->name_len + 2;+	if (file->size == 0) {+		/* Count up the number of empty files. */+		zip->total_number_empty_entry++;+		if (file->dir)+			zip->total_number_dir_entry++;+		else+			file_register_empty(zip, file);+		return (r);+	}++	/*+	 * Init compression.+	 */+	if ((zip->total_number_entry - zip->total_number_empty_entry) == 1) {+		r = _7z_compression_init_encoder(a, zip->opt_compression,+			zip->opt_compression_level);+		if (r < 0) {+			file_free(file);+			return (ARCHIVE_FATAL);+		}+	}++	/* Register a non-empty file. */+	file_register(zip, file);++	/*+	 * Set the current file to cur_file to read its contents.+	 */+	zip->cur_file = file;+++	/* Save a offset of current file in temporary file. */+	zip->entry_bytes_remaining = file->size;+	zip->entry_crc32 = 0;++	/*+	 * Store a symbolic link name as file contents.+	 */+	if (archive_entry_filetype(entry) == AE_IFLNK) {+		ssize_t bytes;+		const void *p = (const void *)archive_entry_symlink(entry);+		bytes = compress_out(a, p, (size_t)file->size, ARCHIVE_Z_RUN);+		if (bytes < 0)+			return ((int)bytes);+		zip->entry_crc32 = crc32(zip->entry_crc32, p, (unsigned)bytes);+		zip->entry_bytes_remaining -= bytes;+	}++	return (r);+}++/*+ * Write data to a temporary file.+ */+static int+write_to_temp(struct archive_write *a, const void *buff, size_t s)+{+	struct _7zip *zip;+	const unsigned char *p;+	ssize_t ws;++	zip = (struct _7zip *)a->format_data;++	/*+	 * Open a temporary file.+	 */+	if (zip->temp_fd == -1) {+		zip->temp_offset = 0;+		zip->temp_fd = __archive_mktemp(NULL);+		if (zip->temp_fd < 0) {+			archive_set_error(&a->archive, errno,+			    "Couldn't create temporary file");+			return (ARCHIVE_FATAL);+		}+	}++	p = (const unsigned char *)buff;+	while (s) {+		ws = write(zip->temp_fd, p, s);+		if (ws < 0) {+			archive_set_error(&(a->archive), errno,+			    "fwrite function failed");+			return (ARCHIVE_FATAL);+		}+		s -= ws;+		p += ws;+		zip->temp_offset += ws;+	}+	return (ARCHIVE_OK);+}++static ssize_t+compress_out(struct archive_write *a, const void *buff, size_t s,+    enum la_zaction run)+{+	struct _7zip *zip = (struct _7zip *)a->format_data;+	int r;++	if (run == ARCHIVE_Z_FINISH && zip->stream.total_in == 0 && s == 0)+		return (0);++	if ((zip->crc32flg & PRECODE_CRC32) && s)+		zip->precode_crc32 = crc32(zip->precode_crc32, buff,+		    (unsigned)s);+	zip->stream.next_in = (const unsigned char *)buff;+	zip->stream.avail_in = s;+	for (;;) {+		/* Compress file data. */+		r = compression_code(&(a->archive), &(zip->stream), run);+		if (r != ARCHIVE_OK && r != ARCHIVE_EOF)+			return (ARCHIVE_FATAL);+		if (zip->stream.avail_out == 0) {+			if (write_to_temp(a, zip->wbuff, sizeof(zip->wbuff))+			    != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			zip->stream.next_out = zip->wbuff;+			zip->stream.avail_out = sizeof(zip->wbuff);+			if (zip->crc32flg & ENCODED_CRC32)+				zip->encoded_crc32 = crc32(zip->encoded_crc32,+				    zip->wbuff, sizeof(zip->wbuff));+			if (run == ARCHIVE_Z_FINISH && r != ARCHIVE_EOF)+				continue;+		}+		if (zip->stream.avail_in == 0)+			break;+	}+	if (run == ARCHIVE_Z_FINISH) {+		uint64_t bytes = sizeof(zip->wbuff) - zip->stream.avail_out;+		if (write_to_temp(a, zip->wbuff, (size_t)bytes) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		if ((zip->crc32flg & ENCODED_CRC32) && bytes)+			zip->encoded_crc32 = crc32(zip->encoded_crc32,+			    zip->wbuff, (unsigned)bytes);+	}++	return (s);+}++static ssize_t+_7z_write_data(struct archive_write *a, const void *buff, size_t s)+{+	struct _7zip *zip;+	ssize_t bytes;++	zip = (struct _7zip *)a->format_data;++	if (s > zip->entry_bytes_remaining)+		s = (size_t)zip->entry_bytes_remaining;+	if (s == 0 || zip->cur_file == NULL)+		return (0);+	bytes = compress_out(a, buff, s, ARCHIVE_Z_RUN);+	if (bytes < 0)+		return (bytes);+	zip->entry_crc32 = crc32(zip->entry_crc32, buff, (unsigned)bytes);+	zip->entry_bytes_remaining -= bytes;+	return (bytes);+}++static int+_7z_finish_entry(struct archive_write *a)+{+	struct _7zip *zip;+	size_t s;+	ssize_t r;++	zip = (struct _7zip *)a->format_data;+	if (zip->cur_file == NULL)+		return (ARCHIVE_OK);++	while (zip->entry_bytes_remaining > 0) {+		s = (size_t)zip->entry_bytes_remaining;+		if (s > a->null_length)+			s = a->null_length;+		r = _7z_write_data(a, a->nulls, s);+		if (r < 0)+			return ((int)r);+	}+	zip->total_bytes_compressed += zip->stream.total_in;+	zip->total_bytes_uncompressed += zip->stream.total_out;+	zip->cur_file->crc32 = zip->entry_crc32;+	zip->cur_file = NULL;++	return (ARCHIVE_OK);+}++static int+flush_wbuff(struct archive_write *a)+{+	struct _7zip *zip;+	int r;+	size_t s;++	zip = (struct _7zip *)a->format_data;+	s = sizeof(zip->wbuff) - zip->wbuff_remaining;+	r = __archive_write_output(a, zip->wbuff, s);+	if (r != ARCHIVE_OK)+		return (r);+	zip->wbuff_remaining = sizeof(zip->wbuff);+	return (r);+}++static int+copy_out(struct archive_write *a, uint64_t offset, uint64_t length)+{+	struct _7zip *zip;+	int r;++	zip = (struct _7zip *)a->format_data;+	if (zip->temp_offset > 0 &&+	    lseek(zip->temp_fd, offset, SEEK_SET) < 0) {+		archive_set_error(&(a->archive), errno, "lseek failed");+		return (ARCHIVE_FATAL);+	}+	while (length) {+		size_t rsize;+		ssize_t rs;+		unsigned char *wb;++		if (length > zip->wbuff_remaining)+			rsize = zip->wbuff_remaining;+		else+			rsize = (size_t)length;+		wb = zip->wbuff + (sizeof(zip->wbuff) - zip->wbuff_remaining);+		rs = read(zip->temp_fd, wb, rsize);+		if (rs < 0) {+			archive_set_error(&(a->archive), errno,+			    "Can't read temporary file(%jd)",+			    (intmax_t)rs);+			return (ARCHIVE_FATAL);+		}+		if (rs == 0) {+			archive_set_error(&(a->archive), 0,+			    "Truncated 7-Zip archive");+			return (ARCHIVE_FATAL);+		}+		zip->wbuff_remaining -= rs;+		length -= rs;+		if (zip->wbuff_remaining == 0) {+			r = flush_wbuff(a);+			if (r != ARCHIVE_OK)+				return (r);+		}+	}+	return (ARCHIVE_OK);+}++static int+_7z_close(struct archive_write *a)+{+	struct _7zip *zip;+	unsigned char *wb;+	uint64_t header_offset, header_size, header_unpacksize;+	uint64_t length;+	uint32_t header_crc32;+	int r;++	zip = (struct _7zip *)a->format_data;++	if (zip->total_number_entry > 0) {+		struct archive_rb_node *n;+		uint64_t data_offset, data_size, data_unpacksize;+		unsigned header_compression;++		r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);+		if (r < 0)+			return (r);+		data_offset = 0;+		data_size = zip->stream.total_out;+		data_unpacksize = zip->stream.total_in;+		zip->coder.codec = zip->opt_compression;+		zip->coder.prop_size = zip->stream.prop_size;+		zip->coder.props = zip->stream.props;+		zip->stream.prop_size = 0;+		zip->stream.props = NULL;+		zip->total_number_nonempty_entry =+		    zip->total_number_entry - zip->total_number_empty_entry;++		/* Connect an empty file list. */+		if (zip->empty_list.first != NULL) {+			*zip->file_list.last = zip->empty_list.first;+			zip->file_list.last = zip->empty_list.last;+		}+		/* Connect a directory file list. */+		ARCHIVE_RB_TREE_FOREACH(n, &(zip->rbtree)) {+			file_register(zip, (struct file *)n);+		}++		/*+		 * NOTE: 7z command supports just LZMA1, LZMA2 and COPY for+		 * the compression type for encoding the header.+		 */+#if HAVE_LZMA_H+		header_compression = _7Z_LZMA1;+		/* If the stored file is only one, do not encode the header.+		 * This is the same way 7z command does. */+		if (zip->total_number_entry == 1)+			header_compression = _7Z_COPY;+#else+		header_compression = _7Z_COPY;+#endif+		r = _7z_compression_init_encoder(a, header_compression, 6);+		if (r < 0)+			return (r);+		zip->crc32flg = PRECODE_CRC32;+		zip->precode_crc32 = 0;+		r = make_header(a, data_offset, data_size, data_unpacksize,+			1, &(zip->coder));+		if (r < 0)+			return (r);+		r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);+		if (r < 0)+			return (r);+		header_offset = data_offset + data_size;+		header_size = zip->stream.total_out;+		header_crc32 = zip->precode_crc32;+		header_unpacksize = zip->stream.total_in;++		if (header_compression != _7Z_COPY) {+			/*+			 * Encode the header in order to reduce the size+			 * of the archive.+			 */+			free(zip->coder.props);+			zip->coder.codec = header_compression;+			zip->coder.prop_size = zip->stream.prop_size;+			zip->coder.props = zip->stream.props;+			zip->stream.prop_size = 0;+			zip->stream.props = NULL;++			r = _7z_compression_init_encoder(a, _7Z_COPY, 0);+			if (r < 0)+				return (r);+			zip->crc32flg = ENCODED_CRC32;+			zip->encoded_crc32 = 0;++			/*+			 * Make EncodedHeader.+			 */+			r = enc_uint64(a, kEncodedHeader);+			if (r < 0)+				return (r);+			r = make_streamsInfo(a, header_offset, header_size,+			      header_unpacksize, 1, &(zip->coder), 0,+			      header_crc32);+			if (r < 0)+				return (r);+			r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);+			if (r < 0)+				return (r);+			header_offset = header_offset + header_size;+			header_size = zip->stream.total_out;+			header_crc32 = zip->encoded_crc32;+		}+		zip->crc32flg = 0;+	} else {+		header_offset = header_size = 0;+		header_crc32 = 0;+	}+	+	length = zip->temp_offset;++	/*+	 * Make the zip header on wbuff(write buffer).+	 */+	wb = zip->wbuff;+	zip->wbuff_remaining = sizeof(zip->wbuff);+	memcpy(&wb[0], "7z\xBC\xAF\x27\x1C", 6);+	wb[6] = 0;/* Major version. */+	wb[7] = 3;/* Minor version. */+	archive_le64enc(&wb[12], header_offset);/* Next Header Offset */+	archive_le64enc(&wb[20], header_size);/* Next Header Size */+	archive_le32enc(&wb[28], header_crc32);/* Next Header CRC */+	archive_le32enc(&wb[8], crc32(0, &wb[12], 20));/* Start Header CRC */+	zip->wbuff_remaining -= 32;++	/*+	 * Read all file contents and an encoded header from the temporary+	 * file and write out it.+	 */+	r = copy_out(a, 0, length);+	if (r != ARCHIVE_OK)+		return (r);+	r = flush_wbuff(a);+	return (r);+}++/*+ * Encode 64 bits value into 7-Zip's encoded UINT64 value.+ */+static int+enc_uint64(struct archive_write *a, uint64_t val)+{+	unsigned mask = 0x80;+	uint8_t numdata[9];+	int i;++	numdata[0] = 0;+	for (i = 1; i < (int)sizeof(numdata); i++) {+		if (val < mask) {+			numdata[0] |= (uint8_t)val;+			break;+		}+		numdata[i] = (uint8_t)val;+		val >>= 8;+		numdata[0] |= mask;+		mask >>= 1;+	}+	return ((int)compress_out(a, numdata, i, ARCHIVE_Z_RUN));+}++static int+make_substreamsInfo(struct archive_write *a, struct coder *coders)+{+	struct _7zip *zip = (struct _7zip *)a->format_data;+	struct file *file;+	int r;++	/*+	 * Make SubStreamsInfo.+	 */+	r = enc_uint64(a, kSubStreamsInfo);+	if (r < 0)+		return (r);++	if (zip->total_number_nonempty_entry > 1 && coders->codec != _7Z_COPY) {+		/*+		 * Make NumUnPackStream.+		 */+		r = enc_uint64(a, kNumUnPackStream);+		if (r < 0)+			return (r);++		/* Write numUnpackStreams */+		r = enc_uint64(a, zip->total_number_nonempty_entry);+		if (r < 0)+			return (r);++		/*+		 * Make kSize.+		 */+		r = enc_uint64(a, kSize);+		if (r < 0)+			return (r);+		file = zip->file_list.first;+		for (;file != NULL; file = file->next) {+			if (file->next == NULL ||+			    file->next->size == 0)+				break;+			r = enc_uint64(a, file->size);+			if (r < 0)+				return (r);+		}+	}++	/*+	 * Make CRC.+	 */+	r = enc_uint64(a, kCRC);+	if (r < 0)+		return (r);+++	/* All are defined */+	r = enc_uint64(a, 1);+	if (r < 0)+		return (r);+	file = zip->file_list.first;+	for (;file != NULL; file = file->next) {+		uint8_t crc[4];+		if (file->size == 0)+			break;+		archive_le32enc(crc, file->crc32);+		r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN);+		if (r < 0)+			return (r);+	}++	/* Write End. */+	r = enc_uint64(a, kEnd);+	if (r < 0)+		return (r);+	return (ARCHIVE_OK);+}++static int+make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size,+    uint64_t unpack_size, int num_coder, struct coder *coders, int substrm,+    uint32_t header_crc)+{+	struct _7zip *zip = (struct _7zip *)a->format_data;+	uint8_t codec_buff[8];+	int numFolders, fi;+	int codec_size;+	int i, r;++	if (coders->codec == _7Z_COPY)+		numFolders = (int)zip->total_number_nonempty_entry;+	else+		numFolders = 1;++	/*+	 * Make PackInfo.+	 */+	r = enc_uint64(a, kPackInfo);+	if (r < 0)+		return (r);++	/* Write PackPos. */+	r = enc_uint64(a, offset);+	if (r < 0)+		return (r);++	/* Write NumPackStreams. */+	r = enc_uint64(a, numFolders);+	if (r < 0)+		return (r);++	/* Make Size. */+	r = enc_uint64(a, kSize);+	if (r < 0)+		return (r);++	if (numFolders > 1) {+		struct file *file = zip->file_list.first;+		for (;file != NULL; file = file->next) {+			if (file->size == 0)+				break;+			r = enc_uint64(a, file->size);+			if (r < 0)+				return (r);+		}+	} else {+		/* Write size. */+		r = enc_uint64(a, pack_size);+		if (r < 0)+			return (r);+	}++	r = enc_uint64(a, kEnd);+	if (r < 0)+		return (r);++	/*+	 * Make UnPackInfo.+	 */+	r = enc_uint64(a, kUnPackInfo);+	if (r < 0)+		return (r);++	/*+	 * Make Folder.+	 */+	r = enc_uint64(a, kFolder);+	if (r < 0)+		return (r);++	/* Write NumFolders. */+	r = enc_uint64(a, numFolders);+	if (r < 0)+		return (r);++	/* Write External. */+	r = enc_uint64(a, 0);+	if (r < 0)+		return (r);++	for (fi = 0; fi < numFolders; fi++) {+		/* Write NumCoders. */+		r = enc_uint64(a, num_coder);+		if (r < 0)+			return (r);++		for (i = 0; i < num_coder; i++) {+			unsigned codec_id = coders[i].codec;++			/* Write Codec flag. */+			archive_be64enc(codec_buff, codec_id);+			for (codec_size = 8; codec_size > 0; codec_size--) {+				if (codec_buff[8 - codec_size])+					break;+			}+			if (codec_size == 0)+				codec_size = 1;+			if (coders[i].prop_size)+				r = enc_uint64(a, codec_size | 0x20);+			else+				r = enc_uint64(a, codec_size);+			if (r < 0)+				return (r);++			/* Write Codec ID. */+			codec_size &= 0x0f;+			r = (int)compress_out(a, &codec_buff[8-codec_size],+				codec_size, ARCHIVE_Z_RUN);+			if (r < 0)+				return (r);++			if (coders[i].prop_size) {+				/* Write Codec property size. */+				r = enc_uint64(a, coders[i].prop_size);+				if (r < 0)+					return (r);++				/* Write Codec properties. */+				r = (int)compress_out(a, coders[i].props,+					coders[i].prop_size, ARCHIVE_Z_RUN);+				if (r < 0)+					return (r);+			}+		}+	}++	/*+	 * Make CodersUnPackSize.+	 */+	r = enc_uint64(a, kCodersUnPackSize);+	if (r < 0)+		return (r);++	if (numFolders > 1) {+		struct file *file = zip->file_list.first;+		for (;file != NULL; file = file->next) {+			if (file->size == 0)+				break;+			r = enc_uint64(a, file->size);+			if (r < 0)+				return (r);+		}++	} else {+		/* Write UnPackSize. */+		r = enc_uint64(a, unpack_size);+		if (r < 0)+			return (r);+	}++	if (!substrm) {+		uint8_t crc[4];+		/*+		 * Make CRC.+		 */+		r = enc_uint64(a, kCRC);+		if (r < 0)+			return (r);++		/* All are defined */+		r = enc_uint64(a, 1);+		if (r < 0)+			return (r);+		archive_le32enc(crc, header_crc);+		r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN);+		if (r < 0)+			return (r);+	}++	/* Write End. */+	r = enc_uint64(a, kEnd);+	if (r < 0)+		return (r);++	if (substrm) {+		/*+		 * Make SubStreamsInfo.+		 */+		r = make_substreamsInfo(a, coders);+		if (r < 0)+			return (r);+	}+++	/* Write End. */+	r = enc_uint64(a, kEnd);+	if (r < 0)+		return (r);++	return (ARCHIVE_OK);+}+++#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)+static uint64_t+utcToFiletime(time_t t, long ns)+{+	uint64_t fileTime;++	fileTime = t;+	fileTime *= 10000000;+	fileTime += ns / 100;+	fileTime += EPOC_TIME;+	return (fileTime);+}++static int+make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti)+{+	uint8_t filetime[8];+	struct _7zip *zip = (struct _7zip *)a->format_data;+	struct file *file;+	int r;+	uint8_t b, mask;++	/*+	 * Make Time Bools.+	 */+	if (zip->total_number_time_defined[ti] == zip->total_number_entry) {+		/* Write Time Type. */+		r = enc_uint64(a, type);+		if (r < 0)+			return (r);+		/* Write EmptyStream Size. */+		r = enc_uint64(a, 2 + zip->total_number_entry * 8);+		if (r < 0)+			return (r);+		/* All are defined. */+		r = enc_uint64(a, 1);+		if (r < 0)+			return (r);+	} else {+		if (zip->total_number_time_defined[ti] == 0)+			return (ARCHIVE_OK);++		/* Write Time Type. */+		r = enc_uint64(a, type);+		if (r < 0)+			return (r);+		/* Write EmptyStream Size. */+		r = enc_uint64(a, 2 + ((zip->total_number_entry + 7) >> 3)+			+ zip->total_number_time_defined[ti] * 8);+		if (r < 0)+			return (r);++		/* All are not defined. */+		r = enc_uint64(a, 0);+		if (r < 0)+			return (r);++		b = 0;+		mask = 0x80;+		file = zip->file_list.first;+		for (;file != NULL; file = file->next) {+			if (file->flg & flg)+				b |= mask;+			mask >>= 1;+			if (mask == 0) {+				r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);+				if (r < 0)+					return (r);+				mask = 0x80;+				b = 0;+			}+		}+		if (mask != 0x80) {+			r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);+			if (r < 0)+				return (r);+		}+	}++	/* External. */+	r = enc_uint64(a, 0);+	if (r < 0)+		return (r);+++	/*+	 * Make Times.+	 */+	file = zip->file_list.first;+	for (;file != NULL; file = file->next) {+		if ((file->flg & flg) == 0)+			continue;+		archive_le64enc(filetime, utcToFiletime(file->times[ti].time,+			file->times[ti].time_ns));+		r = (int)compress_out(a, filetime, 8, ARCHIVE_Z_RUN);+		if (r < 0)+			return (r);+	}++	return (ARCHIVE_OK);+}++static int+make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size,+    uint64_t unpack_size, int codernum, struct coder *coders)+{+	struct _7zip *zip = (struct _7zip *)a->format_data;+	struct file *file;+	int r;+	uint8_t b, mask;++	/*+	 * Make FilesInfo.+	 */+	r = enc_uint64(a, kHeader);+	if (r < 0)+		return (r);++	/*+	 * If there are empty files only, do not write MainStreamInfo.+	 */+	if (zip->total_number_nonempty_entry) {+		/*+		 * Make MainStreamInfo.+		 */+		r = enc_uint64(a, kMainStreamsInfo);+		if (r < 0)+			return (r);+		r = make_streamsInfo(a, offset, pack_size, unpack_size,+		      codernum, coders, 1, 0);+		if (r < 0)+			return (r);+	}++	/*+	 * Make FilesInfo.+	 */+	r = enc_uint64(a, kFilesInfo);+	if (r < 0)+		return (r);++	/* Write numFiles. */+	r = enc_uint64(a, zip->total_number_entry);+	if (r < 0)+		return (r);++	if (zip->total_number_empty_entry > 0) {+		/* Make EmptyStream. */+		r = enc_uint64(a, kEmptyStream);+		if (r < 0)+			return (r);++		/* Write EmptyStream Size. */+		r = enc_uint64(a, (zip->total_number_entry+7)>>3);+		if (r < 0)+			return (r);++		b = 0;+		mask = 0x80;+		file = zip->file_list.first;+		for (;file != NULL; file = file->next) {+			if (file->size == 0)+				b |= mask;+			mask >>= 1;+			if (mask == 0) {+				r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);+				if (r < 0)+					return (r);+				mask = 0x80;+				b = 0;+			}+		}+		if (mask != 0x80) {+			r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);+			if (r < 0)+				return (r);+		}+	}++	if (zip->total_number_empty_entry > zip->total_number_dir_entry) {+		/* Make EmptyFile. */+		r = enc_uint64(a, kEmptyFile);+		if (r < 0)+			return (r);++		/* Write EmptyFile Size. */+		r = enc_uint64(a, (zip->total_number_empty_entry + 7) >> 3);+		if (r < 0)+			return (r);++		b = 0;+		mask = 0x80;+		file = zip->file_list.first;+		for (;file != NULL; file = file->next) {+			if (file->size)+				continue;+			if (!file->dir)+				b |= mask;+			mask >>= 1;+			if (mask == 0) {+				r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);+				if (r < 0)+					return (r);+				mask = 0x80;+				b = 0;+			}+		}+		if (mask != 0x80) {+			r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);+			if (r < 0)+				return (r);+		}+	}++	/* Make Name. */+	r = enc_uint64(a, kName);+	if (r < 0)+		return (r);++	/* Write Name size. */+	r = enc_uint64(a, zip->total_bytes_entry_name+1);+	if (r < 0)+		return (r);++	/* Write dmy byte. */+	r = enc_uint64(a, 0);+	if (r < 0)+		return (r);++	file = zip->file_list.first;+	for (;file != NULL; file = file->next) {+		r = (int)compress_out(a, file->utf16name, file->name_len+2,+			ARCHIVE_Z_RUN);+		if (r < 0)+			return (r);+	}++	/* Make MTime. */+	r = make_time(a, kMTime, MTIME_IS_SET, MTIME);+	if (r < 0)+		return (r);++	/* Make CTime. */+	r = make_time(a, kCTime, CTIME_IS_SET, CTIME);+	if (r < 0)+		return (r);++	/* Make ATime. */+	r = make_time(a, kATime, ATIME_IS_SET, ATIME);+	if (r < 0)+		return (r);++	/* Make Attributes. */+	r = enc_uint64(a, kAttributes);+	if (r < 0)+		return (r);++	/* Write Attributes size. */+	r = enc_uint64(a, 2 + zip->total_number_entry * 4);+	if (r < 0)+		return (r);++	/* Write "All Are Defined". */+	r = enc_uint64(a, 1);+	if (r < 0)+		return (r);++	/* Write dmy byte. */+	r = enc_uint64(a, 0);+	if (r < 0)+		return (r);++	file = zip->file_list.first;+	for (;file != NULL; file = file->next) {+		/*+		 * High 16bits is unix mode.+		 * Low 16bits is Windows attributes.+		 */+		uint32_t encattr, attr;+		if (file->dir)+			attr = 0x8010;+		else+			attr = 0x8020;+		if ((file->mode & 0222) == 0)+			attr |= 1;/* Read Only. */+		attr |= ((uint32_t)file->mode) << 16;+		archive_le32enc(&encattr, attr);+		r = (int)compress_out(a, &encattr, 4, ARCHIVE_Z_RUN);+		if (r < 0)+			return (r);+	}++	/* Write End. */+	r = enc_uint64(a, kEnd);+	if (r < 0)+		return (r);++	/* Write End. */+	r = enc_uint64(a, kEnd);+	if (r < 0)+		return (r);++	return (ARCHIVE_OK);+}+++static int+_7z_free(struct archive_write *a)+{+	struct _7zip *zip = (struct _7zip *)a->format_data;++	/* Close the temporary file. */+	if (zip->temp_fd >= 0)+		close(zip->temp_fd);++	file_free_register(zip);+	compression_end(&(a->archive), &(zip->stream));+	free(zip->coder.props);+	free(zip);++	return (ARCHIVE_OK);+}++static int+file_cmp_node(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	const struct file *f1 = (const struct file *)n1;+	const struct file *f2 = (const struct file *)n2;++	if (f1->name_len == f2->name_len)+		return (memcmp(f1->utf16name, f2->utf16name, f1->name_len));+	return (f1->name_len > f2->name_len)?1:-1;+}+        +static int+file_cmp_key(const struct archive_rb_node *n, const void *key)+{+	const struct file *f = (const struct file *)n;++	return (f->name_len - *(const char *)key);+}++static int+file_new(struct archive_write *a, struct archive_entry *entry,+    struct file **newfile)+{+	struct _7zip *zip;+	struct file *file;+	const char *u16;+	size_t u16len;+	int ret = ARCHIVE_OK;++	zip = (struct _7zip *)a->format_data;+	*newfile = NULL;++	file = calloc(1, sizeof(*file));+	if (file == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}++	if (0 > archive_entry_pathname_l(entry, &u16, &u16len, zip->sconv)) {+		if (errno == ENOMEM) {+			free(file);+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for UTF-16LE");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "A filename cannot be converted to UTF-16LE;"+		    "You should disable making Joliet extension");+		ret = ARCHIVE_WARN;+	}+	file->utf16name = malloc(u16len + 2);+	if (file->utf16name == NULL) {+		free(file);+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory for Name");+		return (ARCHIVE_FATAL);+	}+	memcpy(file->utf16name, u16, u16len);+	file->utf16name[u16len+0] = 0;+	file->utf16name[u16len+1] = 0;+	file->name_len = (unsigned)u16len;+	file->mode = archive_entry_mode(entry);+	if (archive_entry_filetype(entry) == AE_IFREG)+		file->size = archive_entry_size(entry);+	else+		archive_entry_set_size(entry, 0);+	if (archive_entry_filetype(entry) == AE_IFDIR)+		file->dir = 1;+	else if (archive_entry_filetype(entry) == AE_IFLNK)+		file->size = strlen(archive_entry_symlink(entry));+	if (archive_entry_mtime_is_set(entry)) {+		file->flg |= MTIME_IS_SET;+		file->times[MTIME].time = archive_entry_mtime(entry);+		file->times[MTIME].time_ns = archive_entry_mtime_nsec(entry);+	}+	if (archive_entry_atime_is_set(entry)) {+		file->flg |= ATIME_IS_SET;+		file->times[ATIME].time = archive_entry_atime(entry);+		file->times[ATIME].time_ns = archive_entry_atime_nsec(entry);+	}+	if (archive_entry_ctime_is_set(entry)) {+		file->flg |= CTIME_IS_SET;+		file->times[CTIME].time = archive_entry_ctime(entry);+		file->times[CTIME].time_ns = archive_entry_ctime_nsec(entry);+	}++	*newfile = file;+	return (ret);+}++static void+file_free(struct file *file)+{+	free(file->utf16name);+	free(file);+}++static void+file_register(struct _7zip *zip, struct file *file)+{+	file->next = NULL;+	*zip->file_list.last = file;+	zip->file_list.last = &(file->next);+}++static void+file_init_register(struct _7zip *zip)+{+	zip->file_list.first = NULL;+	zip->file_list.last = &(zip->file_list.first);+}++static void+file_free_register(struct _7zip *zip)+{+	struct file *file, *file_next;++	file = zip->file_list.first;+	while (file != NULL) {+		file_next = file->next;+		file_free(file);+		file = file_next;+	}+}++static void+file_register_empty(struct _7zip *zip, struct file *file)+{+	file->next = NULL;+	*zip->empty_list.last = file;+	zip->empty_list.last = &(file->next);+}++static void+file_init_register_empty(struct _7zip *zip)+{+	zip->empty_list.first = NULL;+	zip->empty_list.last = &(zip->empty_list.first);+}++#if !defined(HAVE_ZLIB_H) || !defined(HAVE_BZLIB_H) ||\+	 !defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H)+static int+compression_unsupported_encoder(struct archive *a,+    struct la_zstream *lastrm, const char *name)+{++	archive_set_error(a, ARCHIVE_ERRNO_MISC,+	    "%s compression not supported on this platform", name);+	lastrm->valid = 0;+	lastrm->real_stream = NULL;+	return (ARCHIVE_FAILED);+}+#endif++/*+ * _7_COPY compressor.+ */+static int+compression_init_encoder_copy(struct archive *a, struct la_zstream *lastrm)+{++	if (lastrm->valid)+		compression_end(a, lastrm);+	lastrm->valid = 1;+	lastrm->code = compression_code_copy;+	lastrm->end = compression_end_copy;+	return (ARCHIVE_OK);+}++static int+compression_code_copy(struct archive *a,+    struct la_zstream *lastrm, enum la_zaction action)+{+	size_t bytes;++	(void)a; /* UNUSED */+	if (lastrm->avail_out > lastrm->avail_in)+		bytes = lastrm->avail_in;+	else+		bytes = lastrm->avail_out;+	if (bytes) {+		memcpy(lastrm->next_out, lastrm->next_in, bytes);+		lastrm->next_in += bytes;+		lastrm->avail_in -= bytes;+		lastrm->total_in += bytes;+		lastrm->next_out += bytes;+		lastrm->avail_out -= bytes;+		lastrm->total_out += bytes;+	}+	if (action == ARCHIVE_Z_FINISH && lastrm->avail_in == 0)+		return (ARCHIVE_EOF);+	return (ARCHIVE_OK);+}++static int+compression_end_copy(struct archive *a, struct la_zstream *lastrm)+{+	(void)a; /* UNUSED */+	lastrm->valid = 0;+	return (ARCHIVE_OK);+}++/*+ * _7_DEFLATE compressor.+ */+#ifdef HAVE_ZLIB_H+static int+compression_init_encoder_deflate(struct archive *a,+    struct la_zstream *lastrm, int level, int withheader)+{+	z_stream *strm;++	if (lastrm->valid)+		compression_end(a, lastrm);+	strm = calloc(1, sizeof(*strm));+	if (strm == NULL) {+		archive_set_error(a, ENOMEM,+		    "Can't allocate memory for gzip stream");+		return (ARCHIVE_FATAL);+	}+	/* zlib.h is not const-correct, so we need this one bit+	 * of ugly hackery to convert a const * pointer to+	 * a non-const pointer. */+	strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;+	strm->avail_in = (uInt)lastrm->avail_in;+	strm->total_in = (uLong)lastrm->total_in;+	strm->next_out = lastrm->next_out;+	strm->avail_out = (uInt)lastrm->avail_out;+	strm->total_out = (uLong)lastrm->total_out;+	if (deflateInit2(strm, level, Z_DEFLATED,+	    (withheader)?15:-15,+	    8, Z_DEFAULT_STRATEGY) != Z_OK) {+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library");+		return (ARCHIVE_FATAL);+	}+	lastrm->real_stream = strm;+	lastrm->valid = 1;+	lastrm->code = compression_code_deflate;+	lastrm->end = compression_end_deflate;+	return (ARCHIVE_OK);+}++static int+compression_code_deflate(struct archive *a,+    struct la_zstream *lastrm, enum la_zaction action)+{+	z_stream *strm;+	int r;++	strm = (z_stream *)lastrm->real_stream;+	/* zlib.h is not const-correct, so we need this one bit+	 * of ugly hackery to convert a const * pointer to+	 * a non-const pointer. */+	strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;+	strm->avail_in = (uInt)lastrm->avail_in;+	strm->total_in = (uLong)lastrm->total_in;+	strm->next_out = lastrm->next_out;+	strm->avail_out = (uInt)lastrm->avail_out;+	strm->total_out = (uLong)lastrm->total_out;+	r = deflate(strm,+	    (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH);+	lastrm->next_in = strm->next_in;+	lastrm->avail_in = strm->avail_in;+	lastrm->total_in = strm->total_in;+	lastrm->next_out = strm->next_out;+	lastrm->avail_out = strm->avail_out;+	lastrm->total_out = strm->total_out;+	switch (r) {+	case Z_OK:+		return (ARCHIVE_OK);+	case Z_STREAM_END:+		return (ARCHIVE_EOF);+	default:+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "GZip compression failed:"+		    " deflate() call returned status %d", r);+		return (ARCHIVE_FATAL);+	}+}++static int+compression_end_deflate(struct archive *a, struct la_zstream *lastrm)+{+	z_stream *strm;+	int r;++	strm = (z_stream *)lastrm->real_stream;+	r = deflateEnd(strm);+	free(strm);+	lastrm->real_stream = NULL;+	lastrm->valid = 0;+	if (r != Z_OK) {+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Failed to clean up compressor");+		return (ARCHIVE_FATAL);+	}+	return (ARCHIVE_OK);+}+#else+static int+compression_init_encoder_deflate(struct archive *a,+    struct la_zstream *lastrm, int level, int withheader)+{++	(void) level; /* UNUSED */+	(void) withheader; /* UNUSED */+	if (lastrm->valid)+		compression_end(a, lastrm);+	return (compression_unsupported_encoder(a, lastrm, "deflate"));+}+#endif++/*+ * _7_BZIP2 compressor.+ */+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+static int+compression_init_encoder_bzip2(struct archive *a,+    struct la_zstream *lastrm, int level)+{+	bz_stream *strm;++	if (lastrm->valid)+		compression_end(a, lastrm);+	strm = calloc(1, sizeof(*strm));+	if (strm == NULL) {+		archive_set_error(a, ENOMEM,+		    "Can't allocate memory for bzip2 stream");+		return (ARCHIVE_FATAL);+	}+	/* bzlib.h is not const-correct, so we need this one bit+	 * of ugly hackery to convert a const * pointer to+	 * a non-const pointer. */+	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;+	strm->avail_in = lastrm->avail_in;+	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);+	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);+	strm->next_out = (char *)lastrm->next_out;+	strm->avail_out = lastrm->avail_out;+	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);+	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);+	if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library");+		return (ARCHIVE_FATAL);+	}+	lastrm->real_stream = strm;+	lastrm->valid = 1;+	lastrm->code = compression_code_bzip2;+	lastrm->end = compression_end_bzip2;+	return (ARCHIVE_OK);+}++static int+compression_code_bzip2(struct archive *a,+    struct la_zstream *lastrm, enum la_zaction action)+{+	bz_stream *strm;+	int r;++	strm = (bz_stream *)lastrm->real_stream;+	/* bzlib.h is not const-correct, so we need this one bit+	 * of ugly hackery to convert a const * pointer to+	 * a non-const pointer. */+	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;+	strm->avail_in = lastrm->avail_in;+	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);+	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);+	strm->next_out = (char *)lastrm->next_out;+	strm->avail_out = lastrm->avail_out;+	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);+	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);+	r = BZ2_bzCompress(strm,+	    (action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN);+	lastrm->next_in = (const unsigned char *)strm->next_in;+	lastrm->avail_in = strm->avail_in;+	lastrm->total_in =+	    (((uint64_t)(uint32_t)strm->total_in_hi32) << 32)+	    + (uint64_t)(uint32_t)strm->total_in_lo32;+	lastrm->next_out = (unsigned char *)strm->next_out;+	lastrm->avail_out = strm->avail_out;+	lastrm->total_out =+	    (((uint64_t)(uint32_t)strm->total_out_hi32) << 32)+	    + (uint64_t)(uint32_t)strm->total_out_lo32;+	switch (r) {+	case BZ_RUN_OK:     /* Non-finishing */+	case BZ_FINISH_OK:  /* Finishing: There's more work to do */+		return (ARCHIVE_OK);+	case BZ_STREAM_END: /* Finishing: all done */+		/* Only occurs in finishing case */+		return (ARCHIVE_EOF);+	default:+		/* Any other return value indicates an error */+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Bzip2 compression failed:"+		    " BZ2_bzCompress() call returned status %d", r);+		return (ARCHIVE_FATAL);+	}+}++static int+compression_end_bzip2(struct archive *a, struct la_zstream *lastrm)+{+	bz_stream *strm;+	int r;++	strm = (bz_stream *)lastrm->real_stream;+	r = BZ2_bzCompressEnd(strm);+	free(strm);+	lastrm->real_stream = NULL;+	lastrm->valid = 0;+	if (r != BZ_OK) {+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Failed to clean up compressor");+		return (ARCHIVE_FATAL);+	}+	return (ARCHIVE_OK);+}++#else+static int+compression_init_encoder_bzip2(struct archive *a,+    struct la_zstream *lastrm, int level)+{++	(void) level; /* UNUSED */+	if (lastrm->valid)+		compression_end(a, lastrm);+	return (compression_unsupported_encoder(a, lastrm, "bzip2"));+}+#endif++/*+ * _7_LZMA1, _7_LZMA2 compressor.+ */+#if defined(HAVE_LZMA_H)+static int+compression_init_encoder_lzma(struct archive *a,+    struct la_zstream *lastrm, int level, uint64_t filter_id)+{+	static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;+	lzma_stream *strm;+	lzma_filter *lzmafilters;+	lzma_options_lzma lzma_opt;+	int r;++	if (lastrm->valid)+		compression_end(a, lastrm);+	strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2);+	if (strm == NULL) {+		archive_set_error(a, ENOMEM,+		    "Can't allocate memory for lzma stream");+		return (ARCHIVE_FATAL);+	}+	lzmafilters = (lzma_filter *)(strm+1);+	if (level > 6)+		level = 6;+	if (lzma_lzma_preset(&lzma_opt, level)) {+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ENOMEM,+		    "Internal error initializing compression library");+		return (ARCHIVE_FATAL);+	}+	lzmafilters[0].id = filter_id;+	lzmafilters[0].options = &lzma_opt;+	lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */++	r = lzma_properties_size(&(lastrm->prop_size), lzmafilters);+	if (r != LZMA_OK) {+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "lzma_properties_size failed");+		return (ARCHIVE_FATAL);+	}+	if (lastrm->prop_size) {+		lastrm->props = malloc(lastrm->prop_size);+		if (lastrm->props == NULL) {+			free(strm);+			lastrm->real_stream = NULL;+			archive_set_error(a, ENOMEM,+			    "Cannot allocate memory");+			return (ARCHIVE_FATAL);+		}+		r = lzma_properties_encode(lzmafilters,  lastrm->props);+		if (r != LZMA_OK) {+			free(strm);+			lastrm->real_stream = NULL;+			archive_set_error(a, ARCHIVE_ERRNO_MISC,+			    "lzma_properties_encode failed");+			return (ARCHIVE_FATAL);+		}+	}++	*strm = lzma_init_data;+	r = lzma_raw_encoder(strm, lzmafilters);+	switch (r) {+	case LZMA_OK:+		lastrm->real_stream = strm;+		lastrm->valid = 1;+		lastrm->code = compression_code_lzma;+		lastrm->end = compression_end_lzma;+		r = ARCHIVE_OK;+		break;+	case LZMA_MEM_ERROR:+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ENOMEM,+		    "Internal error initializing compression library: "+		    "Cannot allocate memory");+		r =  ARCHIVE_FATAL;+		break;+        default:+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library: "+		    "It's a bug in liblzma");+		r =  ARCHIVE_FATAL;+		break;+	}+	return (r);+}++static int+compression_init_encoder_lzma1(struct archive *a,+    struct la_zstream *lastrm, int level)+{+	return compression_init_encoder_lzma(a, lastrm, level,+		    LZMA_FILTER_LZMA1);+}++static int+compression_init_encoder_lzma2(struct archive *a,+    struct la_zstream *lastrm, int level)+{+	return compression_init_encoder_lzma(a, lastrm, level,+		    LZMA_FILTER_LZMA2);+}++static int+compression_code_lzma(struct archive *a,+    struct la_zstream *lastrm, enum la_zaction action)+{+	lzma_stream *strm;+	int r;++	strm = (lzma_stream *)lastrm->real_stream;+	strm->next_in = lastrm->next_in;+	strm->avail_in = lastrm->avail_in;+	strm->total_in = lastrm->total_in;+	strm->next_out = lastrm->next_out;+	strm->avail_out = lastrm->avail_out;+	strm->total_out = lastrm->total_out;+	r = lzma_code(strm,+	    (action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN);+	lastrm->next_in = strm->next_in;+	lastrm->avail_in = strm->avail_in;+	lastrm->total_in = strm->total_in;+	lastrm->next_out = strm->next_out;+	lastrm->avail_out = strm->avail_out;+	lastrm->total_out = strm->total_out;+	switch (r) {+	case LZMA_OK:+		/* Non-finishing case */+		return (ARCHIVE_OK);+	case LZMA_STREAM_END:+		/* This return can only occur in finishing case. */+		return (ARCHIVE_EOF);+	case LZMA_MEMLIMIT_ERROR:+		archive_set_error(a, ENOMEM,+		    "lzma compression error:"+		    " %ju MiB would have been needed",+		    (uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1)+			/ (1024 * 1024)));+		return (ARCHIVE_FATAL);+	default:+		/* Any other return value indicates an error */+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "lzma compression failed:"+		    " lzma_code() call returned status %d", r);+		return (ARCHIVE_FATAL);+	}+}++static int+compression_end_lzma(struct archive *a, struct la_zstream *lastrm)+{+	lzma_stream *strm;++	(void)a; /* UNUSED */+	strm = (lzma_stream *)lastrm->real_stream;+	lzma_end(strm);+	free(strm);+	lastrm->valid = 0;+	lastrm->real_stream = NULL;+	return (ARCHIVE_OK);+}+#else+static int+compression_init_encoder_lzma1(struct archive *a,+    struct la_zstream *lastrm, int level)+{++	(void) level; /* UNUSED */+	if (lastrm->valid)+		compression_end(a, lastrm);+	return (compression_unsupported_encoder(a, lastrm, "lzma"));+}+static int+compression_init_encoder_lzma2(struct archive *a,+    struct la_zstream *lastrm, int level)+{++	(void) level; /* UNUSED */+	if (lastrm->valid)+		compression_end(a, lastrm);+	return (compression_unsupported_encoder(a, lastrm, "lzma"));+}+#endif++/*+ * _7_PPMD compressor.+ */+static void+ppmd_write(void *p, Byte b)+{+	struct archive_write *a = ((IByteOut *)p)->a;+	struct _7zip *zip = (struct _7zip *)(a->format_data);+	struct la_zstream *lastrm = &(zip->stream);+	struct ppmd_stream *strm;++	if (lastrm->avail_out) {+		*lastrm->next_out++ = b;+		lastrm->avail_out--;+		lastrm->total_out++;+		return;+	}+	strm = (struct ppmd_stream *)lastrm->real_stream;+	if (strm->buff_ptr < strm->buff_end) {+		*strm->buff_ptr++ = b;+		strm->buff_bytes++;+	}+}++static int+compression_init_encoder_ppmd(struct archive *a,+    struct la_zstream *lastrm, unsigned maxOrder, uint32_t msize)+{+	struct ppmd_stream *strm;+	uint8_t *props;+	int r;++	if (lastrm->valid)+		compression_end(a, lastrm);+	strm = calloc(1, sizeof(*strm));+	if (strm == NULL) {+		archive_set_error(a, ENOMEM,+		    "Can't allocate memory for PPMd");+		return (ARCHIVE_FATAL);+	}+	strm->buff = malloc(32);+	if (strm->buff == NULL) {+		free(strm);+		archive_set_error(a, ENOMEM,+		    "Can't allocate memory for PPMd");+		return (ARCHIVE_FATAL);+	}+	strm->buff_ptr = strm->buff;+	strm->buff_end = strm->buff + 32;++	props = malloc(1+4);+	if (props == NULL) {+		free(strm->buff);+		free(strm);+		archive_set_error(a, ENOMEM,+		    "Coludn't allocate memory for PPMd");+		return (ARCHIVE_FATAL);+	}+	props[0] = maxOrder;+	archive_le32enc(props+1, msize);+	__archive_ppmd7_functions.Ppmd7_Construct(&strm->ppmd7_context);+	r = __archive_ppmd7_functions.Ppmd7_Alloc(+		&strm->ppmd7_context, msize);+	if (r == 0) {+		free(strm->buff);+		free(strm);+		free(props);+		archive_set_error(a, ENOMEM,+		    "Coludn't allocate memory for PPMd");+		return (ARCHIVE_FATAL);+	}+	__archive_ppmd7_functions.Ppmd7_Init(&(strm->ppmd7_context), maxOrder);+	strm->byteout.a = (struct archive_write *)a;+	strm->byteout.Write = ppmd_write;+	strm->range_enc.Stream = &(strm->byteout);+	__archive_ppmd7_functions.Ppmd7z_RangeEnc_Init(&(strm->range_enc));+	strm->stat = 0;++	lastrm->real_stream = strm;+	lastrm->valid = 1;+	lastrm->code = compression_code_ppmd;+	lastrm->end = compression_end_ppmd;+	lastrm->prop_size = 5;+	lastrm->props = props;+	return (ARCHIVE_OK);+}++static int+compression_code_ppmd(struct archive *a,+    struct la_zstream *lastrm, enum la_zaction action)+{+	struct ppmd_stream *strm;++	(void)a; /* UNUSED */++	strm = (struct ppmd_stream *)lastrm->real_stream;++	/* Copy encoded data if there are remaining bytes from previous call. */+	if (strm->buff_bytes) {+		uint8_t *p = strm->buff_ptr - strm->buff_bytes;+		while (lastrm->avail_out && strm->buff_bytes) {+			*lastrm->next_out++ = *p++;+			lastrm->avail_out--;+			lastrm->total_out++;+			strm->buff_bytes--;+		}+		if (strm->buff_bytes)+			return (ARCHIVE_OK);+		if (strm->stat == 1)+			return (ARCHIVE_EOF);+		strm->buff_ptr = strm->buff;+	}+	while (lastrm->avail_in && lastrm->avail_out) {+		__archive_ppmd7_functions.Ppmd7_EncodeSymbol(+			&(strm->ppmd7_context), &(strm->range_enc),+			*lastrm->next_in++);+		lastrm->avail_in--;+		lastrm->total_in++;+	}+	if (lastrm->avail_in == 0 && action == ARCHIVE_Z_FINISH) {+		__archive_ppmd7_functions.Ppmd7z_RangeEnc_FlushData(+			&(strm->range_enc));+		strm->stat = 1;+		/* Return EOF if there are no remaining bytes. */+		if (strm->buff_bytes == 0)+			return (ARCHIVE_EOF);+	}+	return (ARCHIVE_OK);+}++static int+compression_end_ppmd(struct archive *a, struct la_zstream *lastrm)+{+	struct ppmd_stream *strm;++	(void)a; /* UNUSED */++	strm = (struct ppmd_stream *)lastrm->real_stream;+	__archive_ppmd7_functions.Ppmd7_Free(&strm->ppmd7_context);+	free(strm->buff);+	free(strm);+	lastrm->real_stream = NULL;+	lastrm->valid = 0;+	return (ARCHIVE_OK);+}++/*+ * Universal compressor initializer.+ */+static int+_7z_compression_init_encoder(struct archive_write *a, unsigned compression,+    int compression_level)+{+	struct _7zip *zip;+	int r;++	zip = (struct _7zip *)a->format_data;+	switch (compression) {+	case _7Z_DEFLATE:+		r = compression_init_encoder_deflate(+		    &(a->archive), &(zip->stream),+		    compression_level, 0);+		break;+	case _7Z_BZIP2:+		r = compression_init_encoder_bzip2(+		    &(a->archive), &(zip->stream),+		    compression_level);+		break;+	case _7Z_LZMA1:+		r = compression_init_encoder_lzma1(+		    &(a->archive), &(zip->stream),+		    compression_level);+		break;+	case _7Z_LZMA2:+		r = compression_init_encoder_lzma2(+		    &(a->archive), &(zip->stream),+		    compression_level);+		break;+	case _7Z_PPMD:+		r = compression_init_encoder_ppmd(+		    &(a->archive), &(zip->stream),+		    PPMD7_DEFAULT_ORDER, PPMD7_DEFAULT_MEM_SIZE);+		break;+	case _7Z_COPY:+	default:+		r = compression_init_encoder_copy(+		    &(a->archive), &(zip->stream));+		break;+	}+	if (r == ARCHIVE_OK) {+		zip->stream.total_in = 0;+		zip->stream.next_out = zip->wbuff;+		zip->stream.avail_out = sizeof(zip->wbuff);+		zip->stream.total_out = 0;+	}++	return (r);+}++static int+compression_code(struct archive *a, struct la_zstream *lastrm,+    enum la_zaction action)+{+	if (lastrm->valid)+		return (lastrm->code(a, lastrm, action));+	return (ARCHIVE_OK);+}++static int+compression_end(struct archive *a, struct la_zstream *lastrm)+{+	if (lastrm->valid) {+		lastrm->prop_size = 0;+		free(lastrm->props);+		lastrm->props = NULL;+		return (lastrm->end(a, lastrm));+	}+	return (ARCHIVE_OK);+}++
+ c/archive_write_set_format_ar.c view
@@ -0,0 +1,570 @@+/*-+ * Copyright (c) 2007 Kai Wang+ * Copyright (c) 2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer+ *    in this position and unchanged.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ar.c 201108 2009-12-28 03:28:21Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_write_private.h"+#include "archive_write_set_format_private.h"++struct ar_w {+	uint64_t	 entry_bytes_remaining;+	uint64_t	 entry_padding;+	int		 is_strtab;+	int		 has_strtab;+	char		 wrote_global_header;+	char		*strtab;+};++/*+ * Define structure of the "ar" header.+ */+#define AR_name_offset 0+#define AR_name_size 16+#define AR_date_offset 16+#define AR_date_size 12+#define AR_uid_offset 28+#define AR_uid_size 6+#define AR_gid_offset 34+#define AR_gid_size 6+#define AR_mode_offset 40+#define AR_mode_size 8+#define AR_size_offset 48+#define AR_size_size 10+#define AR_fmag_offset 58+#define AR_fmag_size 2++static int		 archive_write_set_format_ar(struct archive_write *);+static int		 archive_write_ar_header(struct archive_write *,+			     struct archive_entry *);+static ssize_t		 archive_write_ar_data(struct archive_write *,+			     const void *buff, size_t s);+static int		 archive_write_ar_free(struct archive_write *);+static int		 archive_write_ar_close(struct archive_write *);+static int		 archive_write_ar_finish_entry(struct archive_write *);+static const char	*ar_basename(const char *path);+static int		 format_octal(int64_t v, char *p, int s);+static int		 format_decimal(int64_t v, char *p, int s);++int+archive_write_set_format_ar_bsd(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	int r;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_ar_bsd");+	r = archive_write_set_format_ar(a);+	if (r == ARCHIVE_OK) {+		a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;+		a->archive.archive_format_name = "ar (BSD)";+	}+	return (r);+}++int+archive_write_set_format_ar_svr4(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	int r;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_ar_svr4");+	r = archive_write_set_format_ar(a);+	if (r == ARCHIVE_OK) {+		a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;+		a->archive.archive_format_name = "ar (GNU/SVR4)";+	}+	return (r);+}++/*+ * Generic initialization.+ */+static int+archive_write_set_format_ar(struct archive_write *a)+{+	struct ar_w *ar;++	/* If someone else was already registered, unregister them. */+	if (a->format_free != NULL)+		(a->format_free)(a);++	ar = (struct ar_w *)calloc(1, sizeof(*ar));+	if (ar == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data");+		return (ARCHIVE_FATAL);+	}+	a->format_data = ar;++	a->format_name = "ar";+	a->format_write_header = archive_write_ar_header;+	a->format_write_data = archive_write_ar_data;+	a->format_close = archive_write_ar_close;+	a->format_free = archive_write_ar_free;+	a->format_finish_entry = archive_write_ar_finish_entry;+	return (ARCHIVE_OK);+}++static int+archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)+{+	int ret, append_fn;+	char buff[60];+	char *ss, *se;+	struct ar_w *ar;+	const char *pathname;+	const char *filename;+	int64_t size;++	append_fn = 0;+	ar = (struct ar_w *)a->format_data;+	ar->is_strtab = 0;+	filename = NULL;+	size = archive_entry_size(entry);+++	/*+	 * Reject files with empty name.+	 */+	pathname = archive_entry_pathname(entry);+	if (pathname == NULL || *pathname == '\0') {+		archive_set_error(&a->archive, EINVAL,+		    "Invalid filename");+		return (ARCHIVE_WARN);+	}++	/*+	 * If we are now at the beginning of the archive,+	 * we need first write the ar global header.+	 */+	if (!ar->wrote_global_header) {+		__archive_write_output(a, "!<arch>\n", 8);+		ar->wrote_global_header = 1;+	}++	memset(buff, ' ', 60);+	memcpy(&buff[AR_fmag_offset], "`\n", 2);++	if (strcmp(pathname, "/") == 0 ) {+		/* Entry is archive symbol table in GNU format */+		buff[AR_name_offset] = '/';+		goto stat;+	}+	if (strcmp(pathname, "/SYM64/") == 0) {+		/* Entry is archive symbol table in GNU 64-bit format */+		memcpy(buff + AR_name_offset, "/SYM64/", 7);+		goto stat;+	}+	if (strcmp(pathname, "__.SYMDEF") == 0) {+		/* Entry is archive symbol table in BSD format */+		memcpy(buff + AR_name_offset, "__.SYMDEF", 9);+		goto stat;+	}+	if (strcmp(pathname, "//") == 0) {+		/*+		 * Entry is archive filename table, inform that we should+		 * collect strtab in next _data call.+		 */+		ar->is_strtab = 1;+		buff[AR_name_offset] = buff[AR_name_offset + 1] = '/';+		/*+		 * For archive string table, only ar_size field should+		 * be set.+		 */+		goto size;+	}++	/*+	 * Otherwise, entry is a normal archive member.+	 * Strip leading paths from filenames, if any.+	 */+	if ((filename = ar_basename(pathname)) == NULL) {+		/* Reject filenames with trailing "/" */+		archive_set_error(&a->archive, EINVAL,+		    "Invalid filename");+		return (ARCHIVE_WARN);+	}++	if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) {+		/*+		 * SVR4/GNU variant use a "/" to mark then end of the filename,+		 * make it possible to have embedded spaces in the filename.+		 * So, the longest filename here (without extension) is+		 * actually 15 bytes.+		 */+		if (strlen(filename) <= 15) {+			memcpy(&buff[AR_name_offset],+			    filename, strlen(filename));+			buff[AR_name_offset + strlen(filename)] = '/';+		} else {+			/*+			 * For filename longer than 15 bytes, GNU variant+			 * makes use of a string table and instead stores the+			 * offset of the real filename to in the ar_name field.+			 * The string table should have been written before.+			 */+			if (ar->has_strtab <= 0) {+				archive_set_error(&a->archive, EINVAL,+				    "Can't find string table");+				return (ARCHIVE_WARN);+			}++			se = (char *)malloc(strlen(filename) + 3);+			if (se == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate filename buffer");+				return (ARCHIVE_FATAL);+			}++			memcpy(se, filename, strlen(filename));+			strcpy(se + strlen(filename), "/\n");++			ss = strstr(ar->strtab, se);+			free(se);++			if (ss == NULL) {+				archive_set_error(&a->archive, EINVAL,+				    "Invalid string table");+				return (ARCHIVE_WARN);+			}++			/*+			 * GNU variant puts "/" followed by digits into+			 * ar_name field. These digits indicates the real+			 * filename string's offset to the string table.+			 */+			buff[AR_name_offset] = '/';+			if (format_decimal(ss - ar->strtab,+			    buff + AR_name_offset + 1,+			    AR_name_size - 1)) {+				archive_set_error(&a->archive, ERANGE,+				    "string table offset too large");+				return (ARCHIVE_WARN);+			}+		}+	} else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) {+		/*+		 * BSD variant: for any file name which is more than+		 * 16 chars or contains one or more embedded space(s), the+		 * string "#1/" followed by the ASCII length of the name is+		 * put into the ar_name field. The file size (stored in the+		 * ar_size field) is incremented by the length of the name.+		 * The name is then written immediately following the+		 * archive header.+		 */+		if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) {+			memcpy(&buff[AR_name_offset], filename, strlen(filename));+			buff[AR_name_offset + strlen(filename)] = ' ';+		}+		else {+			memcpy(buff + AR_name_offset, "#1/", 3);+			if (format_decimal(strlen(filename),+			    buff + AR_name_offset + 3,+			    AR_name_size - 3)) {+				archive_set_error(&a->archive, ERANGE,+				    "File name too long");+				return (ARCHIVE_WARN);+			}+			append_fn = 1;+			size += strlen(filename);+		}+	}++stat:+	if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) {+		archive_set_error(&a->archive, ERANGE,+		    "File modification time too large");+		return (ARCHIVE_WARN);+	}+	if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) {+		archive_set_error(&a->archive, ERANGE,+		    "Numeric user ID too large");+		return (ARCHIVE_WARN);+	}+	if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) {+		archive_set_error(&a->archive, ERANGE,+		    "Numeric group ID too large");+		return (ARCHIVE_WARN);+	}+	if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) {+		archive_set_error(&a->archive, ERANGE,+		    "Numeric mode too large");+		return (ARCHIVE_WARN);+	}+	/*+	 * Sanity Check: A non-pseudo archive member should always be+	 * a regular file.+	 */+	if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) {+		archive_set_error(&a->archive, EINVAL,+		    "Regular file required for non-pseudo member");+		return (ARCHIVE_WARN);+	}++size:+	if (format_decimal(size, buff + AR_size_offset, AR_size_size)) {+		archive_set_error(&a->archive, ERANGE,+		    "File size out of range");+		return (ARCHIVE_WARN);+	}++	ret = __archive_write_output(a, buff, 60);+	if (ret != ARCHIVE_OK)+		return (ret);++	ar->entry_bytes_remaining = size;+	ar->entry_padding = ar->entry_bytes_remaining % 2;++	if (append_fn > 0) {+		ret = __archive_write_output(a, filename, strlen(filename));+		if (ret != ARCHIVE_OK)+			return (ret);+		ar->entry_bytes_remaining -= strlen(filename);+	}++	return (ARCHIVE_OK);+}++static ssize_t+archive_write_ar_data(struct archive_write *a, const void *buff, size_t s)+{+	struct ar_w *ar;+	int ret;++	ar = (struct ar_w *)a->format_data;+	if (s > ar->entry_bytes_remaining)+		s = (size_t)ar->entry_bytes_remaining;++	if (ar->is_strtab > 0) {+		if (ar->has_strtab > 0) {+			archive_set_error(&a->archive, EINVAL,+			    "More than one string tables exist");+			return (ARCHIVE_WARN);+		}++		ar->strtab = (char *)malloc(s + 1);+		if (ar->strtab == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate strtab buffer");+			return (ARCHIVE_FATAL);+		}+		memcpy(ar->strtab, buff, s);+		ar->strtab[s] = '\0';+		ar->has_strtab = 1;+	}++	ret = __archive_write_output(a, buff, s);+	if (ret != ARCHIVE_OK)+		return (ret);++	ar->entry_bytes_remaining -= s;+	return (s);+}++static int+archive_write_ar_free(struct archive_write *a)+{+	struct ar_w *ar;++	ar = (struct ar_w *)a->format_data;++	if (ar == NULL)+		return (ARCHIVE_OK);++	if (ar->has_strtab > 0) {+		free(ar->strtab);+		ar->strtab = NULL;+	}++	free(ar);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}++static int+archive_write_ar_close(struct archive_write *a)+{+	struct ar_w *ar;+	int ret;++	/*+	 * If we haven't written anything yet, we need to write+	 * the ar global header now to make it a valid ar archive.+	 */+	ar = (struct ar_w *)a->format_data;+	if (!ar->wrote_global_header) {+		ar->wrote_global_header = 1;+		ret = __archive_write_output(a, "!<arch>\n", 8);+		return (ret);+	}++	return (ARCHIVE_OK);+}++static int+archive_write_ar_finish_entry(struct archive_write *a)+{+	struct ar_w *ar;+	int ret;++	ar = (struct ar_w *)a->format_data;++	if (ar->entry_bytes_remaining != 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Entry remaining bytes larger than 0");+		return (ARCHIVE_WARN);+	}++	if (ar->entry_padding == 0) {+		return (ARCHIVE_OK);+	}++	if (ar->entry_padding != 1) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Padding wrong size: %ju should be 1 or 0",+		    (uintmax_t)ar->entry_padding);+		return (ARCHIVE_WARN);+	}++	ret = __archive_write_output(a, "\n", 1);+	return (ret);+}++/*+ * Format a number into the specified field using base-8.+ * NB: This version is slightly different from the one in+ * _ustar.c+ */+static int+format_octal(int64_t v, char *p, int s)+{+	int len;+	char *h;++	len = s;+	h = p;++	/* Octal values can't be negative, so use 0. */+	if (v < 0) {+		while (len-- > 0)+			*p++ = '0';+		return (-1);+	}++	p += s;		/* Start at the end and work backwards. */+	do {+		*--p = (char)('0' + (v & 7));+		v >>= 3;+	} while (--s > 0 && v > 0);++	if (v == 0) {+		memmove(h, p, len - s);+		p = h + len - s;+		while (s-- > 0)+			*p++ = ' ';+		return (0);+	}+	/* If it overflowed, fill field with max value. */+	while (len-- > 0)+		*p++ = '7';++	return (-1);+}++/*+ * Format a number into the specified field using base-10.+ */+static int+format_decimal(int64_t v, char *p, int s)+{+	int len;+	char *h;++	len = s;+	h = p;++	/* Negative values in ar header are meaningless, so use 0. */+	if (v < 0) {+		while (len-- > 0)+			*p++ = '0';+		return (-1);+	}++	p += s;+	do {+		*--p = (char)('0' + (v % 10));+		v /= 10;+	} while (--s > 0 && v > 0);++	if (v == 0) {+		memmove(h, p, len - s);+		p = h + len - s;+		while (s-- > 0)+			*p++ = ' ';+		return (0);+	}+	/* If it overflowed, fill field with max value. */+	while (len-- > 0)+		*p++ = '9';++	return (-1);+}++static const char *+ar_basename(const char *path)+{+	const char *endp, *startp;++	endp = path + strlen(path) - 1;+	/*+	 * For filename with trailing slash(es), we return+	 * NULL indicating an error.+	 */+	if (*endp == '/')+		return (NULL);++	/* Find the start of the base */+	startp = endp;+	while (startp > path && *(startp - 1) != '/')+		startp--;+	+	return (startp);+}
+ c/archive_write_set_format_by_name.c view
@@ -0,0 +1,92 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_by_name.c 201168 2009-12-29 06:15:32Z kientzle $");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_private.h"++/* A table that maps names to functions. */+static const+struct { const char *name; int (*setter)(struct archive *); } names[] =+{+	{ "7zip",	archive_write_set_format_7zip },+	{ "ar",		archive_write_set_format_ar_bsd },+	{ "arbsd",	archive_write_set_format_ar_bsd },+	{ "argnu",	archive_write_set_format_ar_svr4 },+	{ "arsvr4",	archive_write_set_format_ar_svr4 },+	{ "bsdtar",	archive_write_set_format_pax_restricted },+	{ "cd9660",	archive_write_set_format_iso9660 },+	{ "cpio",	archive_write_set_format_cpio },+	{ "gnutar",	archive_write_set_format_gnutar },+	{ "iso",	archive_write_set_format_iso9660 },+	{ "iso9660",	archive_write_set_format_iso9660 },+	{ "mtree",	archive_write_set_format_mtree },+	{ "mtree-classic",	archive_write_set_format_mtree_classic },+	{ "newc",	archive_write_set_format_cpio_newc },+	{ "odc",	archive_write_set_format_cpio },+	{ "oldtar",	archive_write_set_format_v7tar },+	{ "pax",	archive_write_set_format_pax },+	{ "paxr",	archive_write_set_format_pax_restricted },+	{ "posix",	archive_write_set_format_pax },+	{ "raw",	archive_write_set_format_raw },+	{ "rpax",	archive_write_set_format_pax_restricted },+	{ "shar",	archive_write_set_format_shar },+	{ "shardump",	archive_write_set_format_shar_dump },+	{ "ustar",	archive_write_set_format_ustar },+	{ "v7tar",	archive_write_set_format_v7tar },+	{ "v7",		archive_write_set_format_v7tar },+	{ "warc",	archive_write_set_format_warc },+	{ "xar",	archive_write_set_format_xar },+	{ "zip",	archive_write_set_format_zip },+	{ NULL,		NULL }+};++int+archive_write_set_format_by_name(struct archive *a, const char *name)+{+	int i;++	for (i = 0; names[i].name != NULL; i++) {+		if (strcmp(name, names[i].name) == 0)+			return ((names[i].setter)(a));+	}++	archive_set_error(a, EINVAL, "No such format '%s'", name);+	a->state = ARCHIVE_STATE_FATAL;+	return (ARCHIVE_FATAL);+}
+ c/archive_write_set_format_cpio.c view
@@ -0,0 +1,500 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2009-12-29 06:34:23Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_write_private.h"+#include "archive_write_set_format_private.h"++static ssize_t	archive_write_cpio_data(struct archive_write *,+		    const void *buff, size_t s);+static int	archive_write_cpio_close(struct archive_write *);+static int	archive_write_cpio_free(struct archive_write *);+static int	archive_write_cpio_finish_entry(struct archive_write *);+static int	archive_write_cpio_header(struct archive_write *,+		    struct archive_entry *);+static int	archive_write_cpio_options(struct archive_write *,+		    const char *, const char *);+static int	format_octal(int64_t, void *, int);+static int64_t	format_octal_recursive(int64_t, char *, int);+static int	write_header(struct archive_write *, struct archive_entry *);++struct cpio {+	uint64_t	  entry_bytes_remaining;++	int64_t		  ino_next;++	struct		 { int64_t old; int new;} *ino_list;+	size_t		  ino_list_size;+	size_t		  ino_list_next;++	struct archive_string_conv *opt_sconv;+	struct archive_string_conv *sconv_default;+	int		  init_default_conversion;+};++#define	c_magic_offset 0+#define	c_magic_size 6+#define	c_dev_offset 6+#define	c_dev_size 6+#define	c_ino_offset 12+#define	c_ino_size 6+#define	c_mode_offset 18+#define	c_mode_size 6+#define	c_uid_offset 24+#define	c_uid_size 6+#define	c_gid_offset 30+#define	c_gid_size 6+#define	c_nlink_offset 36+#define	c_nlink_size 6+#define	c_rdev_offset 42+#define	c_rdev_size 6+#define	c_mtime_offset 48+#define	c_mtime_size 11+#define	c_namesize_offset 59+#define	c_namesize_size 6+#define	c_filesize_offset 65+#define	c_filesize_size 11++/*+ * Set output format to 'cpio' format.+ */+int+archive_write_set_format_cpio(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct cpio *cpio;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_cpio");++	/* If someone else was already registered, unregister them. */+	if (a->format_free != NULL)+		(a->format_free)(a);++	cpio = (struct cpio *)calloc(1, sizeof(*cpio));+	if (cpio == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");+		return (ARCHIVE_FATAL);+	}+	a->format_data = cpio;+	a->format_name = "cpio";+	a->format_options = archive_write_cpio_options;+	a->format_write_header = archive_write_cpio_header;+	a->format_write_data = archive_write_cpio_data;+	a->format_finish_entry = archive_write_cpio_finish_entry;+	a->format_close = archive_write_cpio_close;+	a->format_free = archive_write_cpio_free;+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;+	a->archive.archive_format_name = "POSIX cpio";+	return (ARCHIVE_OK);+}++static int+archive_write_cpio_options(struct archive_write *a, const char *key,+    const char *val)+{+	struct cpio *cpio = (struct cpio *)a->format_data;+	int ret = ARCHIVE_FAILED;++	if (strcmp(key, "hdrcharset")  == 0) {+		if (val == NULL || val[0] == 0)+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "%s: hdrcharset option needs a character-set name",+			    a->format_name);+		else {+			cpio->opt_sconv = archive_string_conversion_to_charset(+			    &a->archive, val, 0);+			if (cpio->opt_sconv != NULL)+				ret = ARCHIVE_OK;+			else+				ret = ARCHIVE_FATAL;+		}+		return (ret);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++/*+ * Ino values are as long as 64 bits on some systems; cpio format+ * only allows 18 bits and relies on the ino values to identify hardlinked+ * files.  So, we can't merely "hash" the ino numbers since collisions+ * would corrupt the archive.  Instead, we generate synthetic ino values+ * to store in the archive and maintain a map of original ino values to+ * synthetic ones so we can preserve hardlink information.+ *+ * TODO: Make this more efficient.  It's not as bad as it looks (most+ * files don't have any hardlinks and we don't do any work here for those),+ * but it wouldn't be hard to do better.+ *+ * TODO: Work with dev/ino pairs here instead of just ino values.+ */+static int+synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)+{+	int64_t ino = archive_entry_ino64(entry);+	int ino_new;+	size_t i;++	/*+	 * If no index number was given, don't assign one.  In+	 * particular, this handles the end-of-archive marker+	 * correctly by giving it a zero index value.  (This is also+	 * why we start our synthetic index numbers with one below.)+	 */+	if (ino == 0)+		return (0);++	/* Don't store a mapping if we don't need to. */+	if (archive_entry_nlink(entry) < 2) {+		return (int)(++cpio->ino_next);+	}++	/* Look up old ino; if we have it, this is a hardlink+	 * and we reuse the same value. */+	for (i = 0; i < cpio->ino_list_next; ++i) {+		if (cpio->ino_list[i].old == ino)+			return (cpio->ino_list[i].new);+	}++	/* Assign a new index number. */+	ino_new = (int)(++cpio->ino_next);++	/* Ensure space for the new mapping. */+	if (cpio->ino_list_size <= cpio->ino_list_next) {+		size_t newsize = cpio->ino_list_size < 512+		    ? 512 : cpio->ino_list_size * 2;+		void *newlist = realloc(cpio->ino_list,+		    sizeof(cpio->ino_list[0]) * newsize);+		if (newlist == NULL)+			return (-1);++		cpio->ino_list_size = newsize;+		cpio->ino_list = newlist;+	}++	/* Record and return the new value. */+	cpio->ino_list[cpio->ino_list_next].old = ino;+	cpio->ino_list[cpio->ino_list_next].new = ino_new;+	++cpio->ino_list_next;+	return (ino_new);+}+++static struct archive_string_conv *+get_sconv(struct archive_write *a)+{+	struct cpio *cpio;+	struct archive_string_conv *sconv;++	cpio = (struct cpio *)a->format_data;+	sconv = cpio->opt_sconv;+	if (sconv == NULL) {+		if (!cpio->init_default_conversion) {+			cpio->sconv_default =+			    archive_string_default_conversion_for_write(+			      &(a->archive));+			cpio->init_default_conversion = 1;+		}+		sconv = cpio->sconv_default;+	}+	return (sconv);+}++static int+archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry)+{+	const char *path;+	size_t len;++	if (archive_entry_filetype(entry) == 0) {+		archive_set_error(&a->archive, -1, "Filetype required");+		return (ARCHIVE_FAILED);+	}++	if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0+	    && errno == ENOMEM) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory for Pathname");+		return (ARCHIVE_FATAL);+	}+	if (len == 0 || path == NULL || path[0] == '\0') {+		archive_set_error(&a->archive, -1, "Pathname required");+		return (ARCHIVE_FAILED);+	}++	if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) {+		archive_set_error(&a->archive, -1, "Size required");+		return (ARCHIVE_FAILED);+	}+	return write_header(a, entry);+}++static int+write_header(struct archive_write *a, struct archive_entry *entry)+{+	struct cpio *cpio;+	const char *p, *path;+	int pathlength, ret, ret_final;+	int64_t	ino;+	char h[76];+	struct archive_string_conv *sconv;+	struct archive_entry *entry_main;+	size_t len;++	cpio = (struct cpio *)a->format_data;+	ret_final = ARCHIVE_OK;+	sconv = get_sconv(a);++#if defined(_WIN32) && !defined(__CYGWIN__)+	/* Make sure the path separators in pathname, hardlink and symlink+	 * are all slash '/', not the Windows path separator '\'. */+	entry_main = __la_win_entry_in_posix_pathseparator(entry);+	if (entry_main == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate ustar data");+		return(ARCHIVE_FATAL);+	}+	if (entry != entry_main)+		entry = entry_main;+	else+		entry_main = NULL;+#else+	entry_main = NULL;+#endif++	ret = archive_entry_pathname_l(entry, &path, &len, sconv);+	if (ret != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathname");+			ret_final = ARCHIVE_FATAL;+			goto exit_write_header;+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate pathname '%s' to %s",+		    archive_entry_pathname(entry),+		    archive_string_conversion_charset_name(sconv));+		ret_final = ARCHIVE_WARN;+	}+	/* Include trailing null. */+	pathlength = (int)len + 1;++	memset(h, 0, sizeof(h));+	format_octal(070707, h + c_magic_offset, c_magic_size);+	format_octal(archive_entry_dev(entry), h + c_dev_offset, c_dev_size);++	ino = synthesize_ino_value(cpio, entry);+	if (ino < 0) {+		archive_set_error(&a->archive, ENOMEM,+		    "No memory for ino translation table");+		ret_final = ARCHIVE_FATAL;+		goto exit_write_header;+	} else if (ino > 0777777) {+		archive_set_error(&a->archive, ERANGE,+		    "Too many files for this cpio format");+		ret_final = ARCHIVE_FATAL;+		goto exit_write_header;+	}+	format_octal(ino & 0777777, h + c_ino_offset, c_ino_size);++	/* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */+	format_octal(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);+	format_octal(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);+	format_octal(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);+	format_octal(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);+	if (archive_entry_filetype(entry) == AE_IFBLK+	    || archive_entry_filetype(entry) == AE_IFCHR)+	    format_octal(archive_entry_dev(entry), h + c_rdev_offset, c_rdev_size);+	else+	    format_octal(0, h + c_rdev_offset, c_rdev_size);+	format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);+	format_octal(pathlength, h + c_namesize_offset, c_namesize_size);++	/* Non-regular files don't store bodies. */+	if (archive_entry_filetype(entry) != AE_IFREG)+		archive_entry_set_size(entry, 0);++	/* Symlinks get the link written as the body of the entry. */+	ret = archive_entry_symlink_l(entry, &p, &len, sconv);+	if (ret != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Linkname");+			ret_final = ARCHIVE_FATAL;+			goto exit_write_header;+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate linkname '%s' to %s",+		    archive_entry_symlink(entry),+		    archive_string_conversion_charset_name(sconv));+		ret_final = ARCHIVE_WARN;+	}+	if (len > 0 && p != NULL  &&  *p != '\0')+		ret = format_octal(strlen(p), h + c_filesize_offset,+		    c_filesize_size);+	else+		ret = format_octal(archive_entry_size(entry),+		    h + c_filesize_offset, c_filesize_size);+	if (ret) {+		archive_set_error(&a->archive, ERANGE,+		    "File is too large for cpio format.");+		ret_final = ARCHIVE_FAILED;+		goto exit_write_header;+	}++	ret = __archive_write_output(a, h, sizeof(h));+	if (ret != ARCHIVE_OK) {+		ret_final = ARCHIVE_FATAL;+		goto exit_write_header;+	}++	ret = __archive_write_output(a, path, pathlength);+	if (ret != ARCHIVE_OK) {+		ret_final = ARCHIVE_FATAL;+		goto exit_write_header;+	}++	cpio->entry_bytes_remaining = archive_entry_size(entry);++	/* Write the symlink now. */+	if (p != NULL  &&  *p != '\0') {+		ret = __archive_write_output(a, p, strlen(p));+		if (ret != ARCHIVE_OK) {+			ret_final = ARCHIVE_FATAL;+			goto exit_write_header;+		}+	}+exit_write_header:+	archive_entry_free(entry_main);+	return (ret_final);+}++static ssize_t+archive_write_cpio_data(struct archive_write *a, const void *buff, size_t s)+{+	struct cpio *cpio;+	int ret;++	cpio = (struct cpio *)a->format_data;+	if (s > cpio->entry_bytes_remaining)+		s = (size_t)cpio->entry_bytes_remaining;++	ret = __archive_write_output(a, buff, s);+	cpio->entry_bytes_remaining -= s;+	if (ret >= 0)+		return (s);+	else+		return (ret);+}++/*+ * Format a number into the specified field.+ */+static int+format_octal(int64_t v, void *p, int digits)+{+	int64_t	max;+	int	ret;++	max = (((int64_t)1) << (digits * 3)) - 1;+	if (v >= 0  &&  v <= max) {+	    format_octal_recursive(v, (char *)p, digits);+	    ret = 0;+	} else {+	    format_octal_recursive(max, (char *)p, digits);+	    ret = -1;+	}+	return (ret);+}++static int64_t+format_octal_recursive(int64_t v, char *p, int s)+{+	if (s == 0)+		return (v);+	v = format_octal_recursive(v, p+1, s-1);+	*p = '0' + ((char)v & 7);+	return (v >> 3);+}++static int+archive_write_cpio_close(struct archive_write *a)+{+	int er;+	struct archive_entry *trailer;++	trailer = archive_entry_new2(NULL);+	/* nlink = 1 here for GNU cpio compat. */+	archive_entry_set_nlink(trailer, 1);+	archive_entry_set_size(trailer, 0);+	archive_entry_set_pathname(trailer, "TRAILER!!!");+	er = write_header(a, trailer);+	archive_entry_free(trailer);+	return (er);+}++static int+archive_write_cpio_free(struct archive_write *a)+{+	struct cpio *cpio;++	cpio = (struct cpio *)a->format_data;+	free(cpio->ino_list);+	free(cpio);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}++static int+archive_write_cpio_finish_entry(struct archive_write *a)+{+	struct cpio *cpio;++	cpio = (struct cpio *)a->format_data;+	return (__archive_write_nulls(a,+		(size_t)cpio->entry_bytes_remaining));+}
+ c/archive_write_set_format_cpio_newc.c view
@@ -0,0 +1,457 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o.+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio_newc.c 201160 2009-12-29 05:41:57Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_write_private.h"+#include "archive_write_set_format_private.h"++static ssize_t	archive_write_newc_data(struct archive_write *,+		    const void *buff, size_t s);+static int	archive_write_newc_close(struct archive_write *);+static int	archive_write_newc_free(struct archive_write *);+static int	archive_write_newc_finish_entry(struct archive_write *);+static int	archive_write_newc_header(struct archive_write *,+		    struct archive_entry *);+static int      archive_write_newc_options(struct archive_write *,+		    const char *, const char *);+static int	format_hex(int64_t, void *, int);+static int64_t	format_hex_recursive(int64_t, char *, int);+static int	write_header(struct archive_write *, struct archive_entry *);++struct cpio {+	uint64_t	  entry_bytes_remaining;+	int		  padding;++	struct archive_string_conv *opt_sconv;+	struct archive_string_conv *sconv_default;+	int		  init_default_conversion;+};++#define	c_magic_offset 0+#define	c_magic_size 6+#define	c_ino_offset 6+#define	c_ino_size 8+#define	c_mode_offset 14+#define	c_mode_size 8+#define	c_uid_offset 22+#define	c_uid_size 8+#define	c_gid_offset 30+#define	c_gid_size 8+#define	c_nlink_offset 38+#define	c_nlink_size 8+#define	c_mtime_offset 46+#define	c_mtime_size 8+#define	c_filesize_offset 54+#define	c_filesize_size 8+#define	c_devmajor_offset 62+#define	c_devmajor_size 8+#define	c_devminor_offset 70+#define	c_devminor_size 8+#define	c_rdevmajor_offset 78+#define	c_rdevmajor_size 8+#define	c_rdevminor_offset 86+#define	c_rdevminor_size 8+#define	c_namesize_offset 94+#define	c_namesize_size 8+#define	c_checksum_offset 102+#define	c_checksum_size 8+#define	c_header_size 110++/* Logic trick: difference between 'n' and next multiple of 4 */+#define PAD4(n)	(3 & (1 + ~(n)))++/*+ * Set output format to 'cpio' format.+ */+int+archive_write_set_format_cpio_newc(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct cpio *cpio;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_newc");++	/* If someone else was already registered, unregister them. */+	if (a->format_free != NULL)+		(a->format_free)(a);++	cpio = (struct cpio *)calloc(1, sizeof(*cpio));+	if (cpio == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");+		return (ARCHIVE_FATAL);+	}+	a->format_data = cpio;+	a->format_name = "cpio";+	a->format_options = archive_write_newc_options;+	a->format_write_header = archive_write_newc_header;+	a->format_write_data = archive_write_newc_data;+	a->format_finish_entry = archive_write_newc_finish_entry;+	a->format_close = archive_write_newc_close;+	a->format_free = archive_write_newc_free;+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;+	a->archive.archive_format_name = "SVR4 cpio nocrc";+	return (ARCHIVE_OK);+}++static int+archive_write_newc_options(struct archive_write *a, const char *key,+    const char *val)+{+	struct cpio *cpio = (struct cpio *)a->format_data;+	int ret = ARCHIVE_FAILED;++	if (strcmp(key, "hdrcharset")  == 0) {+		if (val == NULL || val[0] == 0)+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "%s: hdrcharset option needs a character-set name",+			    a->format_name);+		else {+			cpio->opt_sconv = archive_string_conversion_to_charset(+			    &a->archive, val, 0);+			if (cpio->opt_sconv != NULL)+				ret = ARCHIVE_OK;+			else+				ret = ARCHIVE_FATAL;+		}+		return (ret);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static struct archive_string_conv *+get_sconv(struct archive_write *a)+{+	struct cpio *cpio;+	struct archive_string_conv *sconv;++	cpio = (struct cpio *)a->format_data;+	sconv = cpio->opt_sconv;+	if (sconv == NULL) {+		if (!cpio->init_default_conversion) {+			cpio->sconv_default =+			    archive_string_default_conversion_for_write(+			      &(a->archive));+			cpio->init_default_conversion = 1;+		}+		sconv = cpio->sconv_default;+	}+	return (sconv);+}++static int+archive_write_newc_header(struct archive_write *a, struct archive_entry *entry)+{+	const char *path;+	size_t len;++	if (archive_entry_filetype(entry) == 0) {+		archive_set_error(&a->archive, -1, "Filetype required");+		return (ARCHIVE_FAILED);+	}++	if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0+	    && errno == ENOMEM) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory for Pathname");+		return (ARCHIVE_FATAL);+	}+	if (len == 0 || path == NULL || path[0] == '\0') {+		archive_set_error(&a->archive, -1, "Pathname required");+		return (ARCHIVE_FAILED);+	}++	if (archive_entry_hardlink(entry) == NULL+	    && (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0)) {+		archive_set_error(&a->archive, -1, "Size required");+		return (ARCHIVE_FAILED);+	}+	return write_header(a, entry);+}++static int+write_header(struct archive_write *a, struct archive_entry *entry)+{+	int64_t ino;+	struct cpio *cpio;+	const char *p, *path;+	int pathlength, ret, ret_final;+	char h[c_header_size];+	struct archive_string_conv *sconv;+	struct archive_entry *entry_main;+	size_t len;+	int pad;++	cpio = (struct cpio *)a->format_data;+	ret_final = ARCHIVE_OK;+	sconv = get_sconv(a);++#if defined(_WIN32) && !defined(__CYGWIN__)+	/* Make sure the path separators in pathname, hardlink and symlink+	 * are all slash '/', not the Windows path separator '\'. */+	entry_main = __la_win_entry_in_posix_pathseparator(entry);+	if (entry_main == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate ustar data");+		return(ARCHIVE_FATAL);+	}+	if (entry != entry_main)+		entry = entry_main;+	else+		entry_main = NULL;+#else+	entry_main = NULL;+#endif++	ret = archive_entry_pathname_l(entry, &path, &len, sconv);+	if (ret != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathname");+			ret_final = ARCHIVE_FATAL;+			goto exit_write_header;+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate pathname '%s' to %s",+		    archive_entry_pathname(entry),+		    archive_string_conversion_charset_name(sconv));+		ret_final = ARCHIVE_WARN;+	}+	pathlength = (int)len + 1; /* Include trailing null. */++	memset(h, 0, c_header_size);+	format_hex(0x070701, h + c_magic_offset, c_magic_size);+	format_hex(archive_entry_devmajor(entry), h + c_devmajor_offset,+	    c_devmajor_size);+	format_hex(archive_entry_devminor(entry), h + c_devminor_offset,+	    c_devminor_size);++	ino = archive_entry_ino64(entry);+	if (ino > 0xffffffff) {+		archive_set_error(&a->archive, ERANGE,+		    "large inode number truncated");+		ret_final = ARCHIVE_WARN;+	}++	/* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */+	format_hex(ino & 0xffffffff, h + c_ino_offset, c_ino_size);+	format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);+	format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);+	format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);+	format_hex(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);+	if (archive_entry_filetype(entry) == AE_IFBLK+	    || archive_entry_filetype(entry) == AE_IFCHR) {+	    format_hex(archive_entry_rdevmajor(entry), h + c_rdevmajor_offset, c_rdevmajor_size);+	    format_hex(archive_entry_rdevminor(entry), h + c_rdevminor_offset, c_rdevminor_size);+	} else {+	    format_hex(0, h + c_rdevmajor_offset, c_rdevmajor_size);+	    format_hex(0, h + c_rdevminor_offset, c_rdevminor_size);+	}+	format_hex(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);+	format_hex(pathlength, h + c_namesize_offset, c_namesize_size);+	format_hex(0, h + c_checksum_offset, c_checksum_size);++	/* Non-regular files don't store bodies. */+	if (archive_entry_filetype(entry) != AE_IFREG)+		archive_entry_set_size(entry, 0);++	/* Symlinks get the link written as the body of the entry. */+	ret = archive_entry_symlink_l(entry, &p, &len, sconv);+	if (ret != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Likname");+			ret_final = ARCHIVE_FATAL;+			goto exit_write_header;+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate linkname '%s' to %s",+		    archive_entry_symlink(entry),+		    archive_string_conversion_charset_name(sconv));+		ret_final = ARCHIVE_WARN;+	}+	if (len > 0 && p != NULL  &&  *p != '\0')+		ret = format_hex(strlen(p), h + c_filesize_offset,+		    c_filesize_size);+	else+		ret = format_hex(archive_entry_size(entry),+		    h + c_filesize_offset, c_filesize_size);+	if (ret) {+		archive_set_error(&a->archive, ERANGE,+		    "File is too large for this format.");+		ret_final = ARCHIVE_FAILED;+		goto exit_write_header;+	}++	ret = __archive_write_output(a, h, c_header_size);+	if (ret != ARCHIVE_OK) {+		ret_final = ARCHIVE_FATAL;+		goto exit_write_header;+	}++	/* Pad pathname to even length. */+	ret = __archive_write_output(a, path, pathlength);+	if (ret != ARCHIVE_OK) {+		ret_final = ARCHIVE_FATAL;+		goto exit_write_header;+	}+	pad = PAD4(pathlength + c_header_size);+	if (pad) {+		ret = __archive_write_output(a, "\0\0\0", pad);+		if (ret != ARCHIVE_OK) {+			ret_final = ARCHIVE_FATAL;+			goto exit_write_header;+		}+	}++	cpio->entry_bytes_remaining = archive_entry_size(entry);+	cpio->padding = (int)PAD4(cpio->entry_bytes_remaining);++	/* Write the symlink now. */+	if (p != NULL  &&  *p != '\0') {+		ret = __archive_write_output(a, p, strlen(p));+		if (ret != ARCHIVE_OK) {+			ret_final = ARCHIVE_FATAL;+			goto exit_write_header;+		}+		pad = PAD4(strlen(p));+		ret = __archive_write_output(a, "\0\0\0", pad);+		if (ret != ARCHIVE_OK) {+			ret_final = ARCHIVE_FATAL;+			goto exit_write_header;+		}+	}+exit_write_header:+	archive_entry_free(entry_main);+	return (ret_final);+}++static ssize_t+archive_write_newc_data(struct archive_write *a, const void *buff, size_t s)+{+	struct cpio *cpio;+	int ret;++	cpio = (struct cpio *)a->format_data;+	if (s > cpio->entry_bytes_remaining)+		s = (size_t)cpio->entry_bytes_remaining;++	ret = __archive_write_output(a, buff, s);+	cpio->entry_bytes_remaining -= s;+	if (ret >= 0)+		return (s);+	else+		return (ret);+}++/*+ * Format a number into the specified field.+ */+static int+format_hex(int64_t v, void *p, int digits)+{+	int64_t	max;+	int	ret;++	max = (((int64_t)1) << (digits * 4)) - 1;+	if (v >= 0  &&  v <= max) {+	    format_hex_recursive(v, (char *)p, digits);+	    ret = 0;+	} else {+	    format_hex_recursive(max, (char *)p, digits);+	    ret = -1;+	}+	return (ret);+}++static int64_t+format_hex_recursive(int64_t v, char *p, int s)+{+	if (s == 0)+		return (v);+	v = format_hex_recursive(v, p+1, s-1);+	*p = "0123456789abcdef"[v & 0xf];+	return (v >> 4);+}++static int+archive_write_newc_close(struct archive_write *a)+{+	int er;+	struct archive_entry *trailer;++	trailer = archive_entry_new();+	archive_entry_set_nlink(trailer, 1);+	archive_entry_set_size(trailer, 0);+	archive_entry_set_pathname(trailer, "TRAILER!!!");+	/* Bypass the required data checks. */+	er = write_header(a, trailer);+	archive_entry_free(trailer);+	return (er);+}++static int+archive_write_newc_free(struct archive_write *a)+{+	struct cpio *cpio;++	cpio = (struct cpio *)a->format_data;+	free(cpio);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}++static int+archive_write_newc_finish_entry(struct archive_write *a)+{+	struct cpio *cpio;++	cpio = (struct cpio *)a->format_data;+	return (__archive_write_nulls(a,+		(size_t)cpio->entry_bytes_remaining + cpio->padding));+}
+ c/archive_write_set_format_filter_by_ext.c view
@@ -0,0 +1,142 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2015 Okhotnikov Kirill+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_by_name.c 201168 2009-12-29 06:15:32Z kientzle $");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_private.h"++/* A table that maps names to functions. */+static const+struct { const char *name; int (*format)(struct archive *); int (*filter)(struct archive *);  } names[] =+{+	{ ".7z",	archive_write_set_format_7zip,            archive_write_add_filter_none},+	{ ".zip",	archive_write_set_format_zip,             archive_write_add_filter_none},+	{ ".jar",	archive_write_set_format_zip,             archive_write_add_filter_none},+	{ ".cpio",	archive_write_set_format_cpio,            archive_write_add_filter_none},+	{ ".iso",	archive_write_set_format_iso9660,         archive_write_add_filter_none},+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)+	{ ".a",	        archive_write_set_format_ar_bsd,          archive_write_add_filter_none},+	{ ".ar",	archive_write_set_format_ar_bsd,          archive_write_add_filter_none},+#else         +	{ ".a",	        archive_write_set_format_ar_svr4,         archive_write_add_filter_none},+	{ ".ar",	archive_write_set_format_ar_svr4,         archive_write_add_filter_none},+#endif        +	{ ".tar",	archive_write_set_format_pax_restricted,  archive_write_add_filter_none},+	{ ".tgz",	archive_write_set_format_pax_restricted,  archive_write_add_filter_gzip},+	{ ".tar.gz",	archive_write_set_format_pax_restricted,  archive_write_add_filter_gzip},+	{ ".tar.bz2",	archive_write_set_format_pax_restricted,  archive_write_add_filter_bzip2},+	{ ".tar.xz",	archive_write_set_format_pax_restricted,  archive_write_add_filter_xz},+	{ NULL,		NULL,                             NULL }+};++static +int cmpsuff(const char *str, const char *suffix)+{+  size_t length_str, length_suffix;++  if ((str == NULL) || (suffix == NULL))+    return -1;++  length_str = strlen(str);+  length_suffix = strlen(suffix);++  if (length_str >= length_suffix) {+    return strcmp(str + (length_str - length_suffix), suffix);+  } else {+    return -1;+  }+}++static int get_array_index(const char *name)+{+  int i;++  for (i = 0; names[i].name != NULL; i++) +  {+    if (cmpsuff(name, names[i].name) == 0)+      return i;+  }    +  return -1;+  +}++int+archive_write_set_format_filter_by_ext(struct archive *a, const char *filename)+{+  int names_index = get_array_index(filename);+  +  if (names_index >= 0)+  {  +    int format_state = (names[names_index].format)(a);+    if (format_state == ARCHIVE_OK)+      return ((names[names_index].filter)(a));+    else+      return format_state;+  }    ++  archive_set_error(a, EINVAL, "No such format '%s'", filename);+  a->state = ARCHIVE_STATE_FATAL;+  return (ARCHIVE_FATAL);+}++int+archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext)+{+  int names_index = get_array_index(filename);+  +  if (names_index < 0)+    names_index = get_array_index(def_ext);+  +  if (names_index >= 0)+  {  +    int format_state = (names[names_index].format)(a);+    if (format_state == ARCHIVE_OK)+      return ((names[names_index].filter)(a));+    else+      return format_state;+  }    ++  archive_set_error(a, EINVAL, "No such format '%s'", filename);+  a->state = ARCHIVE_STATE_FATAL;+  return (ARCHIVE_FATAL);+}++++
+ c/archive_write_set_format_gnutar.c view
@@ -0,0 +1,755 @@+/*-+ * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).+ * Author: Jonas Gastal <jgastal@profusion.mobi>+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA+ *+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_gnu_tar.c 191579 2009-04-27 18:35:03Z gastal $");+++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_write_private.h"+#include "archive_write_set_format_private.h"++struct gnutar {+	uint64_t	entry_bytes_remaining;+	uint64_t	entry_padding;+	const char *	linkname;+	size_t		linkname_length;+	const char *	pathname;+	size_t		pathname_length;+	const char *	uname;+	size_t		uname_length;+	const char *	gname;+	size_t		gname_length;+	struct archive_string_conv *opt_sconv;+	struct archive_string_conv *sconv_default;+	int init_default_conversion;+};++/*+ * Define structure of GNU tar header.+ */+#define	GNUTAR_name_offset 0+#define	GNUTAR_name_size 100+#define	GNUTAR_mode_offset 100+#define	GNUTAR_mode_size 7+#define	GNUTAR_mode_max_size 8+#define	GNUTAR_uid_offset 108+#define	GNUTAR_uid_size 7+#define	GNUTAR_uid_max_size 8+#define	GNUTAR_gid_offset 116+#define	GNUTAR_gid_size 7+#define	GNUTAR_gid_max_size 8+#define	GNUTAR_size_offset 124+#define	GNUTAR_size_size 11+#define	GNUTAR_size_max_size 12+#define	GNUTAR_mtime_offset 136+#define	GNUTAR_mtime_size 11+#define	GNUTAR_mtime_max_size 11+#define	GNUTAR_checksum_offset 148+#define	GNUTAR_checksum_size 8+#define	GNUTAR_typeflag_offset 156+#define	GNUTAR_typeflag_size 1+#define	GNUTAR_linkname_offset 157+#define	GNUTAR_linkname_size 100+#define	GNUTAR_magic_offset 257+#define	GNUTAR_magic_size 6+#define	GNUTAR_version_offset 263+#define	GNUTAR_version_size 2+#define	GNUTAR_uname_offset 265+#define	GNUTAR_uname_size 32+#define	GNUTAR_gname_offset 297+#define	GNUTAR_gname_size 32+#define	GNUTAR_rdevmajor_offset 329+#define	GNUTAR_rdevmajor_size 6+#define	GNUTAR_rdevmajor_max_size 8+#define	GNUTAR_rdevminor_offset 337+#define	GNUTAR_rdevminor_size 6+#define	GNUTAR_rdevminor_max_size 8++/*+ * A filled-in copy of the header for initialization.+ */+static const char template_header[] = {+	/* name: 100 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,+	/* Mode, null termination: 8 bytes */+	'0','0','0','0','0','0', '0','\0',+	/* uid, null termination: 8 bytes */+	'0','0','0','0','0','0', '0','\0',+	/* gid, null termination: 8 bytes */+	'0','0','0','0','0','0', '0','\0',+	/* size, space termination: 12 bytes */+	'0','0','0','0','0','0','0','0','0','0','0', '\0',+	/* mtime, space termination: 12 bytes */+	'0','0','0','0','0','0','0','0','0','0','0', '\0',+	/* Initial checksum value: 8 spaces */+	' ',' ',' ',' ',' ',' ',' ',' ',+	/* Typeflag: 1 byte */+	'0',			/* '0' = regular file */+	/* Linkname: 100 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,+	/* Magic: 8 bytes */+	'u','s','t','a','r',' ', ' ','\0',+	/* Uname: 32 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	/* Gname: 32 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	/* rdevmajor + null padding: 8 bytes */+	'\0','\0','\0','\0','\0','\0', '\0','\0',+	/* rdevminor + null padding: 8 bytes */+	'\0','\0','\0','\0','\0','\0', '\0','\0',+	/* Padding: 167 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0+};++static int      archive_write_gnutar_options(struct archive_write *,+		    const char *, const char *);+static int	archive_format_gnutar_header(struct archive_write *, char h[512],+		    struct archive_entry *, int tartype);+static int      archive_write_gnutar_header(struct archive_write *,+		    struct archive_entry *entry);+static ssize_t	archive_write_gnutar_data(struct archive_write *a, const void *buff,+		    size_t s);+static int	archive_write_gnutar_free(struct archive_write *);+static int	archive_write_gnutar_close(struct archive_write *);+static int	archive_write_gnutar_finish_entry(struct archive_write *);+static int	format_256(int64_t, char *, int);+static int	format_number(int64_t, char *, int size, int maxsize);+static int	format_octal(int64_t, char *, int);++/*+ * Set output format to 'GNU tar' format.+ */+int+archive_write_set_format_gnutar(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct gnutar *gnutar;++	gnutar = (struct gnutar *)calloc(1, sizeof(*gnutar));+	if (gnutar == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate gnutar data");+		return (ARCHIVE_FATAL);+	}+	a->format_data = gnutar;+	a->format_name = "gnutar";+	a->format_options = archive_write_gnutar_options;+	a->format_write_header = archive_write_gnutar_header;+	a->format_write_data = archive_write_gnutar_data;+	a->format_close = archive_write_gnutar_close;+	a->format_free = archive_write_gnutar_free;+	a->format_finish_entry = archive_write_gnutar_finish_entry;+	a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;+	a->archive.archive_format_name = "GNU tar";+	return (ARCHIVE_OK);+}++static int+archive_write_gnutar_options(struct archive_write *a, const char *key,+    const char *val)+{+	struct gnutar *gnutar = (struct gnutar *)a->format_data;+	int ret = ARCHIVE_FAILED;++	if (strcmp(key, "hdrcharset")  == 0) {+		if (val == NULL || val[0] == 0)+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "%s: hdrcharset option needs a character-set name",+			    a->format_name);+		else {+			gnutar->opt_sconv = archive_string_conversion_to_charset(+			    &a->archive, val, 0);+			if (gnutar->opt_sconv != NULL)+				ret = ARCHIVE_OK;+			else+				ret = ARCHIVE_FATAL;+		}+		return (ret);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+archive_write_gnutar_close(struct archive_write *a)+{+	return (__archive_write_nulls(a, 512*2));+}++static int+archive_write_gnutar_free(struct archive_write *a)+{+	struct gnutar *gnutar;++	gnutar = (struct gnutar *)a->format_data;+	free(gnutar);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}++static int+archive_write_gnutar_finish_entry(struct archive_write *a)+{+	struct gnutar *gnutar;+	int ret;++	gnutar = (struct gnutar *)a->format_data;+	ret = __archive_write_nulls(a, (size_t)+	    (gnutar->entry_bytes_remaining + gnutar->entry_padding));+	gnutar->entry_bytes_remaining = gnutar->entry_padding = 0;+	return (ret);+}++static ssize_t+archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s)+{+	struct gnutar *gnutar;+	int ret;++	gnutar = (struct gnutar *)a->format_data;+	if (s > gnutar->entry_bytes_remaining)+		s = (size_t)gnutar->entry_bytes_remaining;+	ret = __archive_write_output(a, buff, s);+	gnutar->entry_bytes_remaining -= s;+	if (ret != ARCHIVE_OK)+		return (ret);+	return (s);+}++static int+archive_write_gnutar_header(struct archive_write *a,+     struct archive_entry *entry)+{+	char buff[512];+	int r, ret, ret2 = ARCHIVE_OK;+	int tartype;+	struct gnutar *gnutar;+	struct archive_string_conv *sconv;+	struct archive_entry *entry_main;++	gnutar = (struct gnutar *)a->format_data;++	/* Setup default string conversion. */+	if (gnutar->opt_sconv == NULL) {+		if (!gnutar->init_default_conversion) {+			gnutar->sconv_default =+			    archive_string_default_conversion_for_write(+				&(a->archive));+			gnutar->init_default_conversion = 1;+		}+		sconv = gnutar->sconv_default;+	} else+		sconv = gnutar->opt_sconv;++	/* Only regular files (not hardlinks) have data. */+	if (archive_entry_hardlink(entry) != NULL ||+	    archive_entry_symlink(entry) != NULL ||+	    !(archive_entry_filetype(entry) == AE_IFREG))+		archive_entry_set_size(entry, 0);++	if (AE_IFDIR == archive_entry_filetype(entry)) {+		const char *p;+		size_t path_length;+		/*+		 * Ensure a trailing '/'.  Modify the entry so+		 * the client sees the change.+		 */+#if defined(_WIN32) && !defined(__CYGWIN__)+		const wchar_t *wp;++		wp = archive_entry_pathname_w(entry);+		if (wp != NULL && wp[wcslen(wp) -1] != L'/') {+			struct archive_wstring ws;++			archive_string_init(&ws);+			path_length = wcslen(wp);+			if (archive_wstring_ensure(&ws,+			    path_length + 2) == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate ustar data");+				archive_wstring_free(&ws);+				return(ARCHIVE_FATAL);+			}+			/* Should we keep '\' ? */+			if (wp[path_length -1] == L'\\')+				path_length--;+			archive_wstrncpy(&ws, wp, path_length);+			archive_wstrappend_wchar(&ws, L'/');+			archive_entry_copy_pathname_w(entry, ws.s);+			archive_wstring_free(&ws);+			p = NULL;+		} else+#endif+			p = archive_entry_pathname(entry);+		/*+		 * On Windows, this is a backup operation just in+		 * case getting WCS failed. On POSIX, this is a+		 * normal operation.+		 */+		if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {+			struct archive_string as;++			archive_string_init(&as);+			path_length = strlen(p);+			if (archive_string_ensure(&as,+			    path_length + 2) == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate ustar data");+				archive_string_free(&as);+				return(ARCHIVE_FATAL);+			}+#if defined(_WIN32) && !defined(__CYGWIN__)+			/* NOTE: This might break the pathname+			 * if the current code page is CP932 and+			 * the pathname includes a character '\'+			 * as a part of its multibyte pathname. */+			if (p[strlen(p) -1] == '\\')+				path_length--;+			else+#endif+			archive_strncpy(&as, p, path_length);+			archive_strappend_char(&as, '/');+			archive_entry_copy_pathname(entry, as.s);+			archive_string_free(&as);+		}+	}++#if defined(_WIN32) && !defined(__CYGWIN__)+	/* Make sure the path separators in pathname, hardlink and symlink+	 * are all slash '/', not the Windows path separator '\'. */+	entry_main = __la_win_entry_in_posix_pathseparator(entry);+	if (entry_main == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate ustar data");+		return(ARCHIVE_FATAL);+	}+	if (entry != entry_main)+		entry = entry_main;+	else+		entry_main = NULL;+#else+	entry_main = NULL;+#endif+	r = archive_entry_pathname_l(entry, &(gnutar->pathname),+	    &(gnutar->pathname_length), sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathame");+			ret = ARCHIVE_FATAL;+			goto exit_write_header;+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate pathname '%s' to %s",+		    archive_entry_pathname(entry),+		    archive_string_conversion_charset_name(sconv));+		ret2 = ARCHIVE_WARN;+	}+	r = archive_entry_uname_l(entry, &(gnutar->uname),+	    &(gnutar->uname_length), sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Uname");+			ret = ARCHIVE_FATAL;+			goto exit_write_header;+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate uname '%s' to %s",+		    archive_entry_uname(entry),+		    archive_string_conversion_charset_name(sconv));+		ret2 = ARCHIVE_WARN;+	}+	r = archive_entry_gname_l(entry, &(gnutar->gname),+	    &(gnutar->gname_length), sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Gname");+			ret = ARCHIVE_FATAL;+			goto exit_write_header;+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate gname '%s' to %s",+		    archive_entry_gname(entry),+		    archive_string_conversion_charset_name(sconv));+		ret2 = ARCHIVE_WARN;+	}++	/* If linkname is longer than 100 chars we need to add a 'K' header. */+	r = archive_entry_hardlink_l(entry, &(gnutar->linkname),+	    &(gnutar->linkname_length), sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Linkname");+			ret = ARCHIVE_FATAL;+			goto exit_write_header;+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate linkname '%s' to %s",+		    archive_entry_hardlink(entry),+		    archive_string_conversion_charset_name(sconv));+		ret2 = ARCHIVE_WARN;+	}+	if (gnutar->linkname_length == 0) {+		r = archive_entry_symlink_l(entry, &(gnutar->linkname),+		    &(gnutar->linkname_length), sconv);+		if (r != 0) {+			if (errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for Linkname");+				ret = ARCHIVE_FATAL;+				goto exit_write_header;+			}+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Can't translate linkname '%s' to %s",+			    archive_entry_hardlink(entry),+			    archive_string_conversion_charset_name(sconv));+			ret2 = ARCHIVE_WARN;+		}+	}+	if (gnutar->linkname_length > GNUTAR_linkname_size) {+		size_t length = gnutar->linkname_length + 1;+		struct archive_entry *temp = archive_entry_new2(&a->archive);++		/* Uname/gname here don't really matter since no one reads them;+		 * these are the values that GNU tar happens to use on FreeBSD. */+		archive_entry_set_uname(temp, "root");+		archive_entry_set_gname(temp, "wheel");++		archive_entry_set_pathname(temp, "././@LongLink");+		archive_entry_set_size(temp, length);+		ret = archive_format_gnutar_header(a, buff, temp, 'K');+		archive_entry_free(temp);+		if (ret < ARCHIVE_WARN)+			goto exit_write_header;+		ret = __archive_write_output(a, buff, 512);+		if (ret < ARCHIVE_WARN)+			goto exit_write_header;+		/* Write name and trailing null byte. */+		ret = __archive_write_output(a, gnutar->linkname, length);+		if (ret < ARCHIVE_WARN)+			goto exit_write_header;+		/* Pad to 512 bytes */+		ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));+		if (ret < ARCHIVE_WARN)+			goto exit_write_header;+	}++	/* If pathname is longer than 100 chars we need to add an 'L' header. */+	if (gnutar->pathname_length > GNUTAR_name_size) {+		const char *pathname = gnutar->pathname;+		size_t length = gnutar->pathname_length + 1;+		struct archive_entry *temp = archive_entry_new2(&a->archive);++		/* Uname/gname here don't really matter since no one reads them;+		 * these are the values that GNU tar happens to use on FreeBSD. */+		archive_entry_set_uname(temp, "root");+		archive_entry_set_gname(temp, "wheel");++		archive_entry_set_pathname(temp, "././@LongLink");+		archive_entry_set_size(temp, length);+		ret = archive_format_gnutar_header(a, buff, temp, 'L');+		archive_entry_free(temp);+		if (ret < ARCHIVE_WARN)+			goto exit_write_header;+		ret = __archive_write_output(a, buff, 512);+		if(ret < ARCHIVE_WARN)+			goto exit_write_header;+		/* Write pathname + trailing null byte. */+		ret = __archive_write_output(a, pathname, length);+		if(ret < ARCHIVE_WARN)+			goto exit_write_header;+		/* Pad to multiple of 512 bytes. */+		ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));+		if (ret < ARCHIVE_WARN)+			goto exit_write_header;+	}++	if (archive_entry_hardlink(entry) != NULL) {+		tartype = '1';+	} else+		switch (archive_entry_filetype(entry)) {+		case AE_IFREG: tartype = '0' ; break;+		case AE_IFLNK: tartype = '2' ; break;+		case AE_IFCHR: tartype = '3' ; break;+		case AE_IFBLK: tartype = '4' ; break;+		case AE_IFDIR: tartype = '5' ; break;+		case AE_IFIFO: tartype = '6' ; break;+		default: /* AE_IFSOCK and unknown */+			__archive_write_entry_filetype_unsupported(+                            &a->archive, entry, "gnutar");+			ret = ARCHIVE_FAILED;+			goto exit_write_header;+		}++	ret = archive_format_gnutar_header(a, buff, entry, tartype);+	if (ret < ARCHIVE_WARN)+		goto exit_write_header;+	if (ret2 < ret)+		ret = ret2;+	ret2 = __archive_write_output(a, buff, 512);+	if (ret2 < ARCHIVE_WARN) {+		ret = ret2;+		goto exit_write_header;+	}+	if (ret2 < ret)+		ret = ret2;++	gnutar->entry_bytes_remaining = archive_entry_size(entry);+	gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining);+exit_write_header:+	archive_entry_free(entry_main);+	return (ret);+}++static int+archive_format_gnutar_header(struct archive_write *a, char h[512],+    struct archive_entry *entry, int tartype)+{+	unsigned int checksum;+	int i, ret;+	size_t copy_length;+	const char *p;+	struct gnutar *gnutar;++	gnutar = (struct gnutar *)a->format_data;++	ret = 0;++	/*+	 * The "template header" already includes the signature,+	 * various end-of-field markers, and other required elements.+	 */+	memcpy(h, &template_header, 512);++	/*+	 * Because the block is already null-filled, and strings+	 * are allowed to exactly fill their destination (without null),+	 * I use memcpy(dest, src, strlen()) here a lot to copy strings.+	 */++	if (tartype == 'K' || tartype == 'L') {+		p = archive_entry_pathname(entry);+		copy_length = strlen(p);+	} else {+		p = gnutar->pathname;+		copy_length = gnutar->pathname_length;+	}+	if (copy_length > GNUTAR_name_size)+		copy_length = GNUTAR_name_size;+	memcpy(h + GNUTAR_name_offset, p, copy_length);++	if ((copy_length = gnutar->linkname_length) > 0) {+		if (copy_length > GNUTAR_linkname_size)+			copy_length = GNUTAR_linkname_size;+		memcpy(h + GNUTAR_linkname_offset, gnutar->linkname,+		    copy_length);+	}++	/* TODO: How does GNU tar handle unames longer than GNUTAR_uname_size? */+	if (tartype == 'K' || tartype == 'L') {+		p = archive_entry_uname(entry);+		copy_length = strlen(p);+	} else {+		p = gnutar->uname;+		copy_length = gnutar->uname_length;+	}+	if (copy_length > 0) {+		if (copy_length > GNUTAR_uname_size)+			copy_length = GNUTAR_uname_size;+		memcpy(h + GNUTAR_uname_offset, p, copy_length);+	}++	/* TODO: How does GNU tar handle gnames longer than GNUTAR_gname_size? */+	if (tartype == 'K' || tartype == 'L') {+		p = archive_entry_gname(entry);+		copy_length = strlen(p);+	} else {+		p = gnutar->gname;+		copy_length = gnutar->gname_length;+	}+	if (copy_length > 0) {+		if (strlen(p) > GNUTAR_gname_size)+			copy_length = GNUTAR_gname_size;+		memcpy(h + GNUTAR_gname_offset, p, copy_length);+	}++	/* By truncating the mode here, we ensure it always fits. */+	format_octal(archive_entry_mode(entry) & 07777,+	    h + GNUTAR_mode_offset, GNUTAR_mode_size);++	/* GNU tar supports base-256 here, so should never overflow. */+	if (format_number(archive_entry_uid(entry), h + GNUTAR_uid_offset,+		GNUTAR_uid_size, GNUTAR_uid_max_size)) {+		archive_set_error(&a->archive, ERANGE,+		    "Numeric user ID %jd too large",+		    (intmax_t)archive_entry_uid(entry));+		ret = ARCHIVE_FAILED;+	}++	/* GNU tar supports base-256 here, so should never overflow. */+	if (format_number(archive_entry_gid(entry), h + GNUTAR_gid_offset,+		GNUTAR_gid_size, GNUTAR_gid_max_size)) {+		archive_set_error(&a->archive, ERANGE,+		    "Numeric group ID %jd too large",+		    (intmax_t)archive_entry_gid(entry));+		ret = ARCHIVE_FAILED;+	}++	/* GNU tar supports base-256 here, so should never overflow. */+	if (format_number(archive_entry_size(entry), h + GNUTAR_size_offset,+		GNUTAR_size_size, GNUTAR_size_max_size)) {+		archive_set_error(&a->archive, ERANGE,+		    "File size out of range");+		ret = ARCHIVE_FAILED;+	}++	/* Shouldn't overflow before 2106, since mtime field is 33 bits. */+	format_octal(archive_entry_mtime(entry),+	    h + GNUTAR_mtime_offset, GNUTAR_mtime_size);++	if (archive_entry_filetype(entry) == AE_IFBLK+	    || archive_entry_filetype(entry) == AE_IFCHR) {+		if (format_octal(archive_entry_rdevmajor(entry),+		    h + GNUTAR_rdevmajor_offset,+			GNUTAR_rdevmajor_size)) {+			archive_set_error(&a->archive, ERANGE,+			    "Major device number too large");+			ret = ARCHIVE_FAILED;+		}++		if (format_octal(archive_entry_rdevminor(entry),+		    h + GNUTAR_rdevminor_offset,+			GNUTAR_rdevminor_size)) {+			archive_set_error(&a->archive, ERANGE,+			    "Minor device number too large");+			ret = ARCHIVE_FAILED;+		}+	}++	h[GNUTAR_typeflag_offset] = tartype;++	checksum = 0;+	for (i = 0; i < 512; i++)+		checksum += 255 & (unsigned int)h[i];+	h[GNUTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */+	/* h[GNUTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */+	format_octal(checksum, h + GNUTAR_checksum_offset, 6);+	return (ret);+}++/*+ * Format a number into a field, falling back to base-256 if necessary.+ */+static int+format_number(int64_t v, char *p, int s, int maxsize)+{+	int64_t limit = ((int64_t)1 << (s*3));++	if (v < limit)+		return (format_octal(v, p, s));+	return (format_256(v, p, maxsize));+}++/*+ * Format a number into the specified field using base-256.+ */+static int+format_256(int64_t v, char *p, int s)+{+	p += s;+	while (s-- > 0) {+		*--p = (char)(v & 0xff);+		v >>= 8;+	}+	*p |= 0x80; /* Set the base-256 marker bit. */+	return (0);+}++/*+ * Format a number into the specified field using octal.+ */+static int+format_octal(int64_t v, char *p, int s)+{+	int len = s;++	/* Octal values can't be negative, so use 0. */+	if (v < 0)+		v = 0;++	p += s;		/* Start at the end and work backwards. */+	while (s-- > 0) {+		*--p = (char)('0' + (v & 7));+		v >>= 3;+	}++	if (v == 0)+		return (0);++	/* If it overflowed, fill field with max value. */+	while (len-- > 0)+		*p++ = '7';++	return (-1);+}
+ c/archive_write_set_format_iso9660.c view
@@ -0,0 +1,8162 @@+/*-+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#ifdef HAVE_SYS_UTSNAME_H+#include <sys/utsname.h>+#endif+#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#include <stdio.h>+#include <stdarg.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#include <time.h>+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif++#include "archive.h"+#include "archive_endian.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_rb.h"+#include "archive_write_private.h"++#if defined(_WIN32) && !defined(__CYGWIN__)+#define getuid()			0+#define getgid()			0+#endif++/*#define DEBUG 1*/+#ifdef DEBUG+/* To compare to the ISO image file made by mkisofs. */+#define COMPAT_MKISOFS		1+#endif++#define LOGICAL_BLOCK_BITS			11+#define LOGICAL_BLOCK_SIZE			2048+#define PATH_TABLE_BLOCK_SIZE			4096++#define SYSTEM_AREA_BLOCK			16+#define PRIMARY_VOLUME_DESCRIPTOR_BLOCK 	1+#define SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK 	1+#define BOOT_RECORD_DESCRIPTOR_BLOCK	 	1+#define VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK	1+#define NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK	1+#define RRIP_ER_BLOCK				1+#define PADDING_BLOCK				150++#define FD_1_2M_SIZE		(1024 * 1200)+#define FD_1_44M_SIZE		(1024 * 1440)+#define FD_2_88M_SIZE		(1024 * 2880)+#define MULTI_EXTENT_SIZE	(ARCHIVE_LITERAL_LL(1) << 32)	/* 4Gi bytes. */+#define MAX_DEPTH		8+#define RR_CE_SIZE		28		/* SUSP "CE" extension size */++#define FILE_FLAG_EXISTENCE	0x01+#define FILE_FLAG_DIRECTORY	0x02+#define FILE_FLAG_ASSOCIATED	0x04+#define FILE_FLAG_RECORD	0x08+#define FILE_FLAG_PROTECTION	0x10+#define FILE_FLAG_MULTI_EXTENT	0x80++static const char rrip_identifier[] =+	"RRIP_1991A";+static const char rrip_descriptor[] =+	"THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR "+	"POSIX FILE SYSTEM SEMANTICS";+static const char rrip_source[] =+	"PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE.  "+	"SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR "+	"CONTACT INFORMATION.";+#define RRIP_ER_ID_SIZE		(sizeof(rrip_identifier)-1)+#define RRIP_ER_DSC_SIZE	(sizeof(rrip_descriptor)-1)+#define RRIP_ER_SRC_SIZE	(sizeof(rrip_source)-1)+#define RRIP_ER_SIZE		(8 + RRIP_ER_ID_SIZE + \+				RRIP_ER_DSC_SIZE + RRIP_ER_SRC_SIZE)++static const unsigned char zisofs_magic[8] = {+	0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07+};++#define ZF_HEADER_SIZE	16	/* zisofs header size. */+#define ZF_LOG2_BS	15	/* log2 block size; 32K bytes. */+#define ZF_BLOCK_SIZE	(1UL << ZF_LOG2_BS)++/*+ * Manage extra records.+ */+struct extr_rec {+	int		 location;+	int		 offset;+	unsigned char	 buf[LOGICAL_BLOCK_SIZE];+	struct extr_rec	*next;+};++struct ctl_extr_rec {+	int		 use_extr;+	unsigned char	*bp;+	struct isoent	*isoent;+	unsigned char	*ce_ptr;+	int		 cur_len;+	int		 dr_len;+	int		 limit;+	int		 extr_off;+	int		 extr_loc;+};+#define DR_SAFETY	RR_CE_SIZE+#define DR_LIMIT	(254 - DR_SAFETY)++/*+ * The relation of struct isofile and isoent and archive_entry.+ *+ * Primary volume tree  --> struct isoent+ *                                |+ *                                v+ *                          struct isofile --> archive_entry+ *                                ^+ *                                |+ * Joliet volume tree   --> struct isoent+ *+ * struct isoent has specific information for volume.+ */++struct isofile {+	/* Used for managing struct isofile list. */+	struct isofile		*allnext;+	struct isofile		*datanext;+	/* Used for managing a hardlinked struct isofile list. */+	struct isofile		*hlnext;+	struct isofile		*hardlink_target;++	struct archive_entry	*entry;++	/*+	 * Used for making a directory tree.+	 */+	struct archive_string	 parentdir;+	struct archive_string	 basename;+	struct archive_string	 basename_utf16;+	struct archive_string	 symlink;+	int			 dircnt;	/* The number of elements of+						 * its parent directory */++	/*+	 * Used for a Directory Record.+	 */+	struct content {+		int64_t		 offset_of_temp;+		int64_t		 size;+		int		 blocks;+		uint32_t 	 location;+		/*+		 * One extent equals one content.+		 * If this entry has multi extent, `next' variable points+		 * next content data.+		 */+		struct content	*next;		/* next content	*/+	} content, *cur_content;+	int			 write_content;++	enum {+		NO = 0,+		BOOT_CATALOG,+		BOOT_IMAGE+	} boot;++	/*+	 * Used for a zisofs.+	 */+	struct {+		unsigned char	 header_size;+		unsigned char	 log2_bs;+		uint32_t	 uncompressed_size;+	} zisofs;+};++struct isoent {+	/* Keep `rbnode' at the first member of struct isoent. */+	struct archive_rb_node	 rbnode;++	struct isofile		*file;++	struct isoent		*parent;+	/* A list of children.(use chnext) */+	struct {+		struct isoent	*first;+		struct isoent	**last;+		int		 cnt;+	}			 children;+	struct archive_rb_tree	 rbtree;++	/* A list of sub directories.(use drnext) */+	struct {+		struct isoent	*first;+		struct isoent	**last;+		int		 cnt;+	}			 subdirs;+	/* A sorted list of sub directories. */+	struct isoent		**children_sorted;+	/* Used for managing struct isoent list. */+	struct isoent		*chnext;+	struct isoent		*drnext;+	struct isoent		*ptnext;++	/*+	 * Used for making a Directory Record.+	 */+	int			 dir_number;+	struct {+		int		 vd;+		int		 self;+		int		 parent;+		int		 normal;+	}			 dr_len;+	uint32_t 		 dir_location;+	int			 dir_block;++	/*+	 * Identifier:+	 *   on primary, ISO9660 file/directory name.+	 *   on joliet, UCS2 file/directory name.+	 * ext_off   : offset of identifier extension.+	 * ext_len   : length of identifier extension.+	 * id_len    : byte size of identifier.+	 *   on primary, this is ext_off + ext_len + version length.+	 *   on joliet, this is ext_off + ext_len.+	 * mb_len    : length of multibyte-character of identifier.+	 *   on primary, mb_len and id_len are always the same.+	 *   on joliet, mb_len and id_len are different.+	 */+	char			*identifier;+	int			 ext_off;+	int			 ext_len;+	int			 id_len;+	int			 mb_len;++	/*+	 * Used for making a Rockridge extension.+	 * This is a part of Directory Records.+	 */+	struct isoent		*rr_parent;+	struct isoent		*rr_child;++	/* Extra Record.(which we call in this source file)+	 * A maximum size of the Directory Record is 254.+	 * so, if generated RRIP data of a file cannot into a Directory+	 * Record because of its size, that surplus data relocate this+	 * Extra Record.+	 */+	struct {+		struct extr_rec	*first;+		struct extr_rec	**last;+		struct extr_rec	*current;+	}			 extr_rec_list;++	signed int		 virtual:1;+	/* If set to one, this file type is a directory.+	 * A convenience flag to be used as+	 * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR".+	 */+	signed int		 dir:1;+};++struct hardlink {+	struct archive_rb_node	 rbnode;+	int			 nlink;+	struct {+		struct isofile	*first;+		struct isofile	**last;+	}			 file_list;+};++/*+ * ISO writer options+ */+struct iso_option {+	/*+	 * Usage  : abstract-file=<value>+	 * Type   : string, max 37 bytes+	 * Default: Not specified+	 * COMPAT : mkisofs -abstract <value>+	 *+	 * Specifies Abstract Filename.+	 * This file shall be described in the Root Directory+	 * and containing a abstract statement.+	 */+	unsigned int	 abstract_file:1;+#define OPT_ABSTRACT_FILE_DEFAULT	0	/* Not specified */+#define ABSTRACT_FILE_SIZE		37++	/*+	 * Usage  : application-id=<value>+	 * Type   : string, max 128 bytes+	 * Default: Not specified+	 * COMPAT : mkisofs -A/-appid <value>.+	 *+	 * Specifies Application Identifier.+	 * If the first byte is set to '_'(5F), the remaining+	 * bytes of this option shall specify an identifier+	 * for a file containing the identification of the+	 * application.+	 * This file shall be described in the Root Directory.+	 */+	unsigned int	 application_id:1;+#define OPT_APPLICATION_ID_DEFAULT	0	/* Use default identifier */+#define APPLICATION_IDENTIFIER_SIZE	128++	/*+	 * Usage : !allow-vernum+	 * Type  : boolean+	 * Default: Enabled+	 *	  : Violates the ISO9660 standard if disable.+	 * COMPAT: mkisofs -N+	 *+	 * Allow filenames to use version numbers.+	 */+	unsigned int	 allow_vernum:1;+#define OPT_ALLOW_VERNUM_DEFAULT	1	/* Enabled */++	/*+	 * Usage  : biblio-file=<value>+	 * Type   : string, max 37 bytes+	 * Default: Not specified+	 * COMPAT : mkisofs -biblio <value>+	 *+	 * Specifies Bibliographic Filename.+	 * This file shall be described in the Root Directory+	 * and containing bibliographic records.+	 */+	unsigned int	 biblio_file:1;+#define OPT_BIBLIO_FILE_DEFAULT		0	/* Not specified */+#define BIBLIO_FILE_SIZE		37++	/*+	 * Usage  : boot=<value>+	 * Type   : string+	 * Default: Not specified+	 * COMPAT : mkisofs -b/-eltorito-boot <value>+	 *+	 * Specifies "El Torito" boot image file to make+	 * a bootable CD.+	 */+	unsigned int	 boot:1;+#define OPT_BOOT_DEFAULT		0	/* Not specified */++	/*+	 * Usage  : boot-catalog=<value>+	 * Type   : string+	 * Default: "boot.catalog"+	 * COMPAT : mkisofs -c/-eltorito-catalog <value>+	 *+	 * Specifies a fullpath of El Torito boot catalog.+	 */+	unsigned int	 boot_catalog:1;+#define OPT_BOOT_CATALOG_DEFAULT	0	/* Not specified */++	/*+	 * Usage  : boot-info-table+	 * Type   : boolean+	 * Default: Disabled+	 * COMPAT : mkisofs -boot-info-table+	 *+	 * Modify the boot image file specified by `boot'+	 * option; ISO writer stores boot file information+	 * into the boot file in ISO image at offset 8+	 * through offset 64.+	 */+	unsigned int	 boot_info_table:1;+#define OPT_BOOT_INFO_TABLE_DEFAULT	0	/* Disabled */++	/*+	 * Usage  : boot-load-seg=<value>+	 * Type   : hexadecimal+	 * Default: Not specified+	 * COMPAT : mkisofs -boot-load-seg <value>+	 *+	 * Specifies a load segment for boot image.+	 * This is used with no-emulation mode.+	 */+	unsigned int	 boot_load_seg:1;+#define OPT_BOOT_LOAD_SEG_DEFAULT	0	/* Not specified */++	/*+	 * Usage  : boot-load-size=<value>+	 * Type   : decimal+	 * Default: Not specified+	 * COMPAT : mkisofs -boot-load-size <value>+	 *+	 * Specifies a sector count for boot image.+	 * This is used with no-emulation mode.+	 */+	unsigned int	 boot_load_size:1;+#define OPT_BOOT_LOAD_SIZE_DEFAULT	0	/* Not specified */++	/*+	 * Usage  : boot-type=<boot-media-type>+	 *        : 'no-emulation' : 'no emulation' image+	 *        :           'fd' : floppy disk image+	 *        :    'hard-disk' : hard disk image+	 * Type   : string+	 * Default: Auto detect+	 *        : We check a size of boot image;+	 *        : If the size is just 1.22M/1.44M/2.88M,+	 *        : we assume boot_type is 'fd';+	 *        : otherwise boot_type is 'no-emulation'.+	 * COMPAT :+	 *    boot=no-emulation+	 *	mkisofs -no-emul-boot+	 *    boot=fd+	 *	This is a default on the mkisofs.+	 *    boot=hard-disk+	 *	mkisofs -hard-disk-boot+	 *+	 * Specifies a type of "El Torito" boot image.+	 */+	unsigned int	 boot_type:2;+#define OPT_BOOT_TYPE_AUTO		0	/* auto detect		  */+#define OPT_BOOT_TYPE_NO_EMU		1	/* ``no emulation'' image */+#define OPT_BOOT_TYPE_FD		2	/* floppy disk image	  */+#define OPT_BOOT_TYPE_HARD_DISK		3	/* hard disk image	  */+#define OPT_BOOT_TYPE_DEFAULT		OPT_BOOT_TYPE_AUTO++	/*+	 * Usage  : compression-level=<value>+	 * Type   : decimal+	 * Default: Not specified+	 * COMPAT : NONE+	 *+	 * Specifies compression level for option zisofs=direct.+	 */+	unsigned int	 compression_level:1;+#define OPT_COMPRESSION_LEVEL_DEFAULT	0	/* Not specified */++	/*+	 * Usage  : copyright-file=<value>+	 * Type   : string, max 37 bytes+	 * Default: Not specified+	 * COMPAT : mkisofs -copyright <value>+	 *+	 * Specifies Copyright Filename.+	 * This file shall be described in the Root Directory+	 * and containing a copyright statement.+	 */+	unsigned int	 copyright_file:1;+#define OPT_COPYRIGHT_FILE_DEFAULT	0	/* Not specified */+#define COPYRIGHT_FILE_SIZE		37++	/*+	 * Usage  : gid=<value>+	 * Type   : decimal+	 * Default: Not specified+	 * COMPAT : mkisofs -gid <value>+	 *+	 * Specifies a group id to rewrite the group id of all files.+	 */+	unsigned int	 gid:1;+#define OPT_GID_DEFAULT			0	/* Not specified */++	/*+	 * Usage  : iso-level=[1234]+	 * Type   : decimal+	 * Default: 1+	 * COMPAT : mkisofs -iso-level <value>+	 *+	 * Specifies ISO9600 Level.+	 * Level 1: [DEFAULT]+	 *   - limits each file size less than 4Gi bytes;+	 *   - a File Name shall not contain more than eight+	 *     d-characters or eight d1-characters;+	 *   - a File Name Extension shall not contain more than+	 *     three d-characters or three d1-characters;+	 *   - a Directory Identifier shall not contain more+	 *     than eight d-characters or eight d1-characters.+	 * Level 2:+	 *   - limits each file size less than 4Giga bytes;+	 *   - a File Name shall not contain more than thirty+	 *     d-characters or thirty d1-characters;+	 *   - a File Name Extension shall not contain more than+	 *     thirty d-characters or thirty d1-characters;+	 *   - a Directory Identifier shall not contain more+	 *     than thirty-one d-characters or thirty-one+	 *     d1-characters.+	 * Level 3:+	 *   - no limit of file size; use multi extent.+	 * Level 4:+	 *   - this level 4 simulates mkisofs option+	 *     '-iso-level 4';+	 *   - crate a enhanced volume as mkisofs doing;+	 *   - allow a File Name to have leading dot;+	 *   - allow a File Name to have all ASCII letters;+	 *   - allow a File Name to have multiple dots;+	 *   - allow more then 8 depths of directory trees;+	 *   - disable a version number to a File Name;+	 *   - disable a forced period to the tail of a File Name;+	 *   - the maximum length of files and directories is raised to 193.+	 *     if rockridge option is disabled, raised to 207.+	 */+	unsigned int	 iso_level:3;+#define OPT_ISO_LEVEL_DEFAULT		1	/* ISO Level 1 */++	/*+	 * Usage  : joliet[=long]+	 *        : !joliet+	 *        :   Do not generate Joliet Volume and Records.+	 *        : joliet [DEFAULT]+	 *        :   Generates Joliet Volume and Directory Records.+	 *        :   [COMPAT: mkisofs -J/-joliet]+	 *        : joliet=long+	 *        :   The joliet filenames are up to 103 Unicode+	 *        :   characters.+	 *        :   This option breaks the Joliet specification.+	 *        :   [COMPAT: mkisofs -J -joliet-long]+	 * Type   : boolean/string+	 * Default: Enabled+	 * COMPAT : mkisofs -J / -joliet-long+	 *+	 * Generates Joliet Volume and Directory Records.+	 */+	unsigned int	 joliet:2;+#define OPT_JOLIET_DISABLE		0	/* Not generate Joliet Records. */+#define OPT_JOLIET_ENABLE		1	/* Generate Joliet Records.  */+#define OPT_JOLIET_LONGNAME		2	/* Use long joliet filenames.*/+#define OPT_JOLIET_DEFAULT		OPT_JOLIET_ENABLE++	/*+	 * Usage  : !limit-depth+	 * Type   : boolean+	 * Default: Enabled+	 *	  : Violates the ISO9660 standard if disable.+	 * COMPAT : mkisofs -D/-disable-deep-relocation+	 *+	 * The number of levels in hierarchy cannot exceed eight.+	 */+	unsigned int	 limit_depth:1;+#define OPT_LIMIT_DEPTH_DEFAULT		1	/* Enabled */++	/*+	 * Usage  : !limit-dirs+	 * Type   : boolean+	 * Default: Enabled+	 *	  : Violates the ISO9660 standard if disable.+	 * COMPAT : mkisofs -no-limit-pathtables+	 *+	 * Limits the number of directories less than 65536 due+	 * to the size of the Parent Directory Number of Path+	 * Table.+	 */+	unsigned int	 limit_dirs:1;+#define OPT_LIMIT_DIRS_DEFAULT		1	/* Enabled */++	/*+	 * Usage  : !pad+	 * Type   : boolean+	 * Default: Enabled+	 * COMPAT : -pad/-no-pad+	 *+	 * Pads the end of the ISO image by null of 300Ki bytes.+	 */+	unsigned int	 pad:1;+#define OPT_PAD_DEFAULT			1	/* Enabled */++	/*+	 * Usage  : publisher=<value>+	 * Type   : string, max 128 bytes+	 * Default: Not specified+	 * COMPAT : mkisofs -publisher <value>+	 *+	 * Specifies Publisher Identifier.+	 * If the first byte is set to '_'(5F), the remaining+	 * bytes of this option shall specify an identifier+	 * for a file containing the identification of the user.+	 * This file shall be described in the Root Directory.+	 */+	unsigned int	 publisher:1;+#define OPT_PUBLISHER_DEFAULT		0	/* Not specified */+#define PUBLISHER_IDENTIFIER_SIZE	128++	/*+	 * Usage  : rockridge+	 *        : !rockridge+	 *        :    disable to generate SUSP and RR records.+	 *        : rockridge+	 *        :    the same as 'rockridge=useful'.+	 *        : rockridge=strict+	 *        :    generate SUSP and RR records.+	 *        :    [COMPAT: mkisofs -R]+	 *        : rockridge=useful [DEFAULT]+	 *        :    generate SUSP and RR records.+	 *        :    [COMPAT: mkisofs -r]+	 *        :    NOTE  Our rockridge=useful option does not set a zero+	 *        :          to uid and gid, you should use application+	 *        :          option such as --gid,--gname,--uid and --uname+	 *        :          bsdtar options instead.+	 * Type   : boolean/string+	 * Default: Enabled as rockridge=useful+	 * COMPAT : mkisofs -r / -R+	 *+	 * Generates SUSP and RR records.+	 */+	unsigned int	 rr:2;+#define OPT_RR_DISABLED			0+#define OPT_RR_STRICT			1+#define OPT_RR_USEFUL			2+#define OPT_RR_DEFAULT			OPT_RR_USEFUL++	/*+	 * Usage  : volume-id=<value>+	 * Type   : string, max 32 bytes+	 * Default: Not specified+	 * COMPAT : mkisofs -V <value>+	 *+	 * Specifies Volume Identifier.+	 */+	unsigned int	 volume_id:1;+#define OPT_VOLUME_ID_DEFAULT		0	/* Use default identifier */+#define VOLUME_IDENTIFIER_SIZE		32++	/*+	 * Usage  : !zisofs [DEFAULT] +	 *        :    Disable to generate RRIP 'ZF' extension.+	 *        : zisofs+	 *        :    Make files zisofs file and generate RRIP 'ZF'+ 	 *        :    extension. So you do not need mkzftree utility+	 *        :    for making zisofs.+	 *        :    When the file size is less than one Logical Block+	 *        :    size, that file will not zisofs'ed since it does+	 *        :    reduce an ISO-image size.+	 *        :+	 *        :    When you specify option 'boot=<boot-image>', that+	 *        :    'boot-image' file won't be converted to zisofs file.+	 * Type   : boolean+	 * Default: Disabled+	 *+	 * Generates RRIP 'ZF' System Use Entry.+	 */+	unsigned int	 zisofs:1;+#define OPT_ZISOFS_DISABLED		0+#define OPT_ZISOFS_DIRECT		1+#define OPT_ZISOFS_DEFAULT		OPT_ZISOFS_DISABLED++};++struct iso9660 {+	/* The creation time of ISO image. */+	time_t			 birth_time;+	/* A file stream of a temporary file, which file contents+	 * save to until ISO image can be created. */+	int			 temp_fd;++	struct isofile		*cur_file;+	struct isoent		*cur_dirent;+	struct archive_string	 cur_dirstr;+	uint64_t		 bytes_remaining;+	int			 need_multi_extent;++	/* Temporary string buffer for Joliet extension. */ +	struct archive_string	 utf16be;+	struct archive_string	 mbs;++	struct archive_string_conv *sconv_to_utf16be;+	struct archive_string_conv *sconv_from_utf16be;++	/* A list of all of struct isofile entries. */+	struct {+		struct isofile	*first;+		struct isofile	**last;+	}			 all_file_list;++	/* A list of struct isofile entries which have its+	 * contents and are not a directory, a hardlinked file+	 * and a symlink file. */+	struct {+		struct isofile	*first;+		struct isofile	**last;+	}			 data_file_list;++	/* Used for managing to find hardlinking files. */+	struct archive_rb_tree	 hardlink_rbtree;++	/* Used for making the Path Table Record. */+	struct vdd {+		/* the root of entry tree. */+		struct isoent	*rootent;+		enum vdd_type {+			VDD_PRIMARY,+			VDD_JOLIET,+			VDD_ENHANCED+		} vdd_type;++		struct path_table {+			struct isoent		*first;+			struct isoent		**last;+			struct isoent		**sorted;+			int			 cnt;+		} *pathtbl;+		int				 max_depth;++		int		 path_table_block;+		int		 path_table_size;+		int		 location_type_L_path_table;+		int		 location_type_M_path_table;+		int		 total_dir_block;+	} primary, joliet;++	/* Used for making a Volume Descriptor. */+	int			 volume_space_size;+	int			 volume_sequence_number;+	int			 total_file_block;+	struct archive_string	 volume_identifier;+	struct archive_string	 publisher_identifier;+	struct archive_string	 data_preparer_identifier;+	struct archive_string	 application_identifier;+	struct archive_string	 copyright_file_identifier;+	struct archive_string	 abstract_file_identifier;+	struct archive_string	 bibliographic_file_identifier;++	/* Used for making rockridge extensions. */+	int			 location_rrip_er;++	/* Used for making zisofs. */+	struct {+		signed int	 detect_magic:1;+		signed int	 making:1;+		signed int	 allzero:1;+		unsigned char	 magic_buffer[64];+		int		 magic_cnt;++#ifdef HAVE_ZLIB_H+		/*+		 * Copy a compressed file to iso9660.zisofs.temp_fd+		 * and also copy a uncompressed file(original file) to+		 * iso9660.temp_fd . If the number of logical block+		 * of the compressed file is less than the number of+		 * logical block of the uncompressed file, use it and+		 * remove the copy of the uncompressed file.+		 * but if not, we use uncompressed file and remove+		 * the copy of the compressed file.+		 */+		uint32_t	*block_pointers;+		size_t		 block_pointers_allocated;+		int		 block_pointers_cnt;+		int		 block_pointers_idx;+		int64_t		 total_size;+		int64_t		 block_offset;++		z_stream	 stream;+		int		 stream_valid;+		int64_t		 remaining;+		int		 compression_level;+#endif+	} zisofs;++	struct isoent		*directories_too_deep;+	int			 dircnt_max;++	/* Write buffer. */+#define wb_buffmax()	(LOGICAL_BLOCK_SIZE * 32)+#define wb_remaining(a)	(((struct iso9660 *)(a)->format_data)->wbuff_remaining)+#define wb_offset(a)	(((struct iso9660 *)(a)->format_data)->wbuff_offset \+		+ wb_buffmax() - wb_remaining(a))+	unsigned char		 wbuff[LOGICAL_BLOCK_SIZE * 32];+	size_t			 wbuff_remaining;+	enum {+		WB_TO_STREAM,+		WB_TO_TEMP+	} 			 wbuff_type;+	int64_t			 wbuff_offset;+	int64_t			 wbuff_written;+	int64_t			 wbuff_tail;++	/* 'El Torito' boot data. */+	struct {+		/* boot catalog file */+		struct archive_string	 catalog_filename;+		struct isoent		*catalog;+		/* boot image file */+		struct archive_string	 boot_filename;+		struct isoent		*boot;++		unsigned char		 platform_id;+#define BOOT_PLATFORM_X86	0+#define BOOT_PLATFORM_PPC	1+#define BOOT_PLATFORM_MAC	2+		struct archive_string	 id;+		unsigned char		 media_type;+#define BOOT_MEDIA_NO_EMULATION		0+#define BOOT_MEDIA_1_2M_DISKETTE	1+#define BOOT_MEDIA_1_44M_DISKETTE	2+#define BOOT_MEDIA_2_88M_DISKETTE	3+#define BOOT_MEDIA_HARD_DISK		4+		unsigned char		 system_type;+		uint16_t		 boot_load_seg;+		uint16_t		 boot_load_size;+#define BOOT_LOAD_SIZE		4+	} el_torito;++	struct iso_option	 opt;+};++/*+ * Types of Volume Descriptor+ */+enum VD_type {+	VDT_BOOT_RECORD=0,	/* Boot Record Volume Descriptor 	*/+	VDT_PRIMARY=1,		/* Primary Volume Descriptor		*/+	VDT_SUPPLEMENTARY=2,	/* Supplementary Volume Descriptor	*/+	VDT_TERMINATOR=255	/* Volume Descriptor Set Terminator	*/+};++/*+ * Types of Directory Record+ */+enum dir_rec_type {+	DIR_REC_VD,		/* Stored in Volume Descriptor.	*/+	DIR_REC_SELF,		/* Stored as Current Directory.	*/+	DIR_REC_PARENT,		/* Stored as Parent Directory.	*/+	DIR_REC_NORMAL 		/* Stored as Child.		*/+};++/*+ * Kinds of Volume Descriptor Character+ */+enum vdc {+	VDC_STD,+	VDC_LOWERCASE,+	VDC_UCS2,+	VDC_UCS2_DIRECT+};++/*+ * IDentifier Resolver.+ * Used for resolving duplicated filenames.+ */+struct idr {+	struct idrent {+		struct archive_rb_node	rbnode;+		/* Used in wait_list. */+		struct idrent		*wnext;+		struct idrent		*avail;++		struct isoent		*isoent;+		int			 weight;+		int			 noff;+		int			 rename_num;+	} *idrent_pool;++	struct archive_rb_tree		 rbtree;++	struct {+		struct idrent		*first;+		struct idrent		**last;+	} wait_list;++	int				 pool_size;+	int				 pool_idx;+	int				 num_size;+	int				 null_size;++	char				 char_map[0x80];+};++enum char_type {+	A_CHAR,+	D_CHAR+};+++static int	iso9660_options(struct archive_write *,+		    const char *, const char *);+static int	iso9660_write_header(struct archive_write *,+		    struct archive_entry *);+static ssize_t	iso9660_write_data(struct archive_write *,+		    const void *, size_t);+static int	iso9660_finish_entry(struct archive_write *);+static int	iso9660_close(struct archive_write *);+static int	iso9660_free(struct archive_write *);++static void	get_system_identitier(char *, size_t);+static void	set_str(unsigned char *, const char *, size_t, char,+		    const char *);+static inline int joliet_allowed_char(unsigned char, unsigned char);+static int	set_str_utf16be(struct archive_write *, unsigned char *,+			const char *, size_t, uint16_t, enum vdc);+static int	set_str_a_characters_bp(struct archive_write *,+			unsigned char *, int, int, const char *, enum vdc);+static int	set_str_d_characters_bp(struct archive_write *,+			unsigned char *, int, int, const char *, enum  vdc);+static void	set_VD_bp(unsigned char *, enum VD_type, unsigned char);+static inline void set_unused_field_bp(unsigned char *, int, int);++static unsigned char *extra_open_record(unsigned char *, int,+		    struct isoent *, struct ctl_extr_rec *);+static void	extra_close_record(struct ctl_extr_rec *, int);+static unsigned char * extra_next_record(struct ctl_extr_rec *, int);+static unsigned char *extra_get_record(struct isoent *, int *, int *, int *);+static void	extra_tell_used_size(struct ctl_extr_rec *, int);+static int	extra_setup_location(struct isoent *, int);+static int	set_directory_record_rr(unsigned char *, int,+		    struct isoent *, struct iso9660 *, enum dir_rec_type);+static int	set_directory_record(unsigned char *, size_t,+		    struct isoent *, struct iso9660 *, enum dir_rec_type,+		    enum vdd_type);+static inline int get_dir_rec_size(struct iso9660 *, struct isoent *,+		    enum dir_rec_type, enum vdd_type);+static inline unsigned char *wb_buffptr(struct archive_write *);+static int	wb_write_out(struct archive_write *);+static int	wb_consume(struct archive_write *, size_t);+#ifdef HAVE_ZLIB_H+static int	wb_set_offset(struct archive_write *, int64_t);+#endif+static int	write_null(struct archive_write *, size_t);+static int	write_VD_terminator(struct archive_write *);+static int	set_file_identifier(unsigned char *, int, int, enum vdc,+		    struct archive_write *, struct vdd *,+		    struct archive_string *, const char *, int,+		    enum char_type);+static int	write_VD(struct archive_write *, struct vdd *);+static int	write_VD_boot_record(struct archive_write *);+static int	write_information_block(struct archive_write *);+static int	write_path_table(struct archive_write *, int,+		    struct vdd *);+static int	write_directory_descriptors(struct archive_write *,+		    struct vdd *);+static int	write_file_descriptors(struct archive_write *);+static int	write_rr_ER(struct archive_write *);+static void	calculate_path_table_size(struct vdd *);++static void	isofile_init_entry_list(struct iso9660 *);+static void	isofile_add_entry(struct iso9660 *, struct isofile *);+static void	isofile_free_all_entries(struct iso9660 *);+static void	isofile_init_entry_data_file_list(struct iso9660 *);+static void	isofile_add_data_file(struct iso9660 *, struct isofile *);+static struct isofile * isofile_new(struct archive_write *,+		    struct archive_entry *);+static void	isofile_free(struct isofile *);+static int	isofile_gen_utility_names(struct archive_write *,+		    struct isofile *);+static int	isofile_register_hardlink(struct archive_write *,+		    struct isofile *);+static void	isofile_connect_hardlink_files(struct iso9660 *);+static void	isofile_init_hardlinks(struct iso9660 *);+static void	isofile_free_hardlinks(struct iso9660 *);++static struct isoent *isoent_new(struct isofile *);+static int	isoent_clone_tree(struct archive_write *,+		    struct isoent **, struct isoent *);+static void	_isoent_free(struct isoent *isoent);+static void	isoent_free_all(struct isoent *);+static struct isoent * isoent_create_virtual_dir(struct archive_write *,+		    struct iso9660 *, const char *);+static int	isoent_cmp_node(const struct archive_rb_node *,+		    const struct archive_rb_node *);+static int	isoent_cmp_key(const struct archive_rb_node *,+		    const void *);+static int	isoent_add_child_head(struct isoent *, struct isoent *);+static int	isoent_add_child_tail(struct isoent *, struct isoent *);+static void	isoent_remove_child(struct isoent *, struct isoent *);+static void	isoent_setup_directory_location(struct iso9660 *,+		    int, struct vdd *);+static void	isoent_setup_file_location(struct iso9660 *, int);+static int	get_path_component(char *, size_t, const char *);+static int	isoent_tree(struct archive_write *, struct isoent **);+static struct isoent *isoent_find_child(struct isoent *, const char *);+static struct isoent *isoent_find_entry(struct isoent *, const char *);+static void	idr_relaxed_filenames(char *);+static void	idr_init(struct iso9660 *, struct vdd *, struct idr *);+static void	idr_cleanup(struct idr *);+static int	idr_ensure_poolsize(struct archive_write *, struct idr *,+		    int);+static int	idr_start(struct archive_write *, struct idr *,+		    int, int, int, int, const struct archive_rb_tree_ops *);+static void	idr_register(struct idr *, struct isoent *, int,+		    int);+static void	idr_extend_identifier(struct idrent *, int, int);+static void	idr_resolve(struct idr *, void (*)(unsigned char *, int));+static void	idr_set_num(unsigned char *, int);+static void	idr_set_num_beutf16(unsigned char *, int);+static int	isoent_gen_iso9660_identifier(struct archive_write *,+		    struct isoent *, struct idr *);+static int	isoent_gen_joliet_identifier(struct archive_write *,+		    struct isoent *, struct idr *);+static int	isoent_cmp_iso9660_identifier(const struct isoent *,+		    const struct isoent *);+static int	isoent_cmp_node_iso9660(const struct archive_rb_node *,+		    const struct archive_rb_node *);+static int	isoent_cmp_key_iso9660(const struct archive_rb_node *,+		    const void *);+static int	isoent_cmp_joliet_identifier(const struct isoent *,+		    const struct isoent *);+static int	isoent_cmp_node_joliet(const struct archive_rb_node *,+		    const struct archive_rb_node *);+static int	isoent_cmp_key_joliet(const struct archive_rb_node *,+		    const void *);+static inline void path_table_add_entry(struct path_table *, struct isoent *);+static inline struct isoent * path_table_last_entry(struct path_table *);+static int	isoent_make_path_table(struct archive_write *);+static int	isoent_find_out_boot_file(struct archive_write *,+		    struct isoent *);+static int	isoent_create_boot_catalog(struct archive_write *,+		    struct isoent *);+static size_t	fd_boot_image_size(int);+static int	make_boot_catalog(struct archive_write *);+static int	setup_boot_information(struct archive_write *);++static int	zisofs_init(struct archive_write *, struct isofile *);+static void	zisofs_detect_magic(struct archive_write *,+		    const void *, size_t);+static int	zisofs_write_to_temp(struct archive_write *,+		    const void *, size_t);+static int	zisofs_finish_entry(struct archive_write *);+static int	zisofs_rewind_boot_file(struct archive_write *);+static int	zisofs_free(struct archive_write *);++int+archive_write_set_format_iso9660(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct iso9660 *iso9660;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660");++	/* If another format was already registered, unregister it. */+	if (a->format_free != NULL)+		(a->format_free)(a);++	iso9660 = calloc(1, sizeof(*iso9660));+	if (iso9660 == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate iso9660 data");+		return (ARCHIVE_FATAL);+	}+	iso9660->birth_time = 0;+	iso9660->temp_fd = -1;+	iso9660->cur_file = NULL;+	iso9660->primary.max_depth = 0;+	iso9660->primary.vdd_type = VDD_PRIMARY;+	iso9660->primary.pathtbl = NULL;+	iso9660->joliet.rootent = NULL;+	iso9660->joliet.max_depth = 0;+	iso9660->joliet.vdd_type = VDD_JOLIET;+	iso9660->joliet.pathtbl = NULL;+	isofile_init_entry_list(iso9660);+	isofile_init_entry_data_file_list(iso9660);+	isofile_init_hardlinks(iso9660);+	iso9660->directories_too_deep = NULL;+	iso9660->dircnt_max = 1;+	iso9660->wbuff_remaining = wb_buffmax();+	iso9660->wbuff_type = WB_TO_TEMP;+	iso9660->wbuff_offset = 0;+	iso9660->wbuff_written = 0;+	iso9660->wbuff_tail = 0;+	archive_string_init(&(iso9660->utf16be));+	archive_string_init(&(iso9660->mbs));++	/*+	 * Init Identifiers used for PVD and SVD.+	 */+	archive_string_init(&(iso9660->volume_identifier));+	archive_strcpy(&(iso9660->volume_identifier), "CDROM");+	archive_string_init(&(iso9660->publisher_identifier));+	archive_string_init(&(iso9660->data_preparer_identifier));+	archive_string_init(&(iso9660->application_identifier));+	archive_strcpy(&(iso9660->application_identifier),+	    archive_version_string());+	archive_string_init(&(iso9660->copyright_file_identifier));+	archive_string_init(&(iso9660->abstract_file_identifier));+	archive_string_init(&(iso9660->bibliographic_file_identifier));++	/*+	 * Init El Torito bootable CD variables.+	 */+	archive_string_init(&(iso9660->el_torito.catalog_filename));+	iso9660->el_torito.catalog = NULL;+	/* Set default file name of boot catalog  */+	archive_strcpy(&(iso9660->el_torito.catalog_filename),+	    "boot.catalog");+	archive_string_init(&(iso9660->el_torito.boot_filename));+	iso9660->el_torito.boot = NULL;+	iso9660->el_torito.platform_id = BOOT_PLATFORM_X86;+	archive_string_init(&(iso9660->el_torito.id));+	iso9660->el_torito.boot_load_seg = 0;+	iso9660->el_torito.boot_load_size = BOOT_LOAD_SIZE;++	/*+	 * Init zisofs variables.+	 */+#ifdef HAVE_ZLIB_H+	iso9660->zisofs.block_pointers = NULL;+	iso9660->zisofs.block_pointers_allocated = 0;+	iso9660->zisofs.stream_valid = 0;+	iso9660->zisofs.compression_level = 9;+	memset(&(iso9660->zisofs.stream), 0,+	    sizeof(iso9660->zisofs.stream));+#endif++	/*+	 * Set default value of iso9660 options.+	 */+	iso9660->opt.abstract_file = OPT_ABSTRACT_FILE_DEFAULT;+	iso9660->opt.application_id = OPT_APPLICATION_ID_DEFAULT;+	iso9660->opt.allow_vernum = OPT_ALLOW_VERNUM_DEFAULT;+	iso9660->opt.biblio_file = OPT_BIBLIO_FILE_DEFAULT;+	iso9660->opt.boot = OPT_BOOT_DEFAULT;+	iso9660->opt.boot_catalog = OPT_BOOT_CATALOG_DEFAULT;+	iso9660->opt.boot_info_table = OPT_BOOT_INFO_TABLE_DEFAULT;+	iso9660->opt.boot_load_seg = OPT_BOOT_LOAD_SEG_DEFAULT;+	iso9660->opt.boot_load_size = OPT_BOOT_LOAD_SIZE_DEFAULT;+	iso9660->opt.boot_type = OPT_BOOT_TYPE_DEFAULT;+	iso9660->opt.compression_level = OPT_COMPRESSION_LEVEL_DEFAULT;+	iso9660->opt.copyright_file = OPT_COPYRIGHT_FILE_DEFAULT;+	iso9660->opt.iso_level = OPT_ISO_LEVEL_DEFAULT;+	iso9660->opt.joliet = OPT_JOLIET_DEFAULT;+	iso9660->opt.limit_depth = OPT_LIMIT_DEPTH_DEFAULT;+	iso9660->opt.limit_dirs = OPT_LIMIT_DIRS_DEFAULT;+	iso9660->opt.pad = OPT_PAD_DEFAULT;+	iso9660->opt.publisher = OPT_PUBLISHER_DEFAULT;+	iso9660->opt.rr = OPT_RR_DEFAULT;+	iso9660->opt.volume_id = OPT_VOLUME_ID_DEFAULT;+	iso9660->opt.zisofs = OPT_ZISOFS_DEFAULT;++	/* Create the root directory. */+	iso9660->primary.rootent =+	    isoent_create_virtual_dir(a, iso9660, "");+	if (iso9660->primary.rootent == NULL) {+		free(iso9660);+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	iso9660->primary.rootent->parent = iso9660->primary.rootent;+	iso9660->cur_dirent = iso9660->primary.rootent;+	archive_string_init(&(iso9660->cur_dirstr));+	archive_string_ensure(&(iso9660->cur_dirstr), 1);+	iso9660->cur_dirstr.s[0] = 0;+	iso9660->sconv_to_utf16be = NULL;+	iso9660->sconv_from_utf16be = NULL;++	a->format_data = iso9660;+	a->format_name = "iso9660";+	a->format_options = iso9660_options;+	a->format_write_header = iso9660_write_header;+	a->format_write_data = iso9660_write_data;+	a->format_finish_entry = iso9660_finish_entry;+	a->format_close = iso9660_close;+	a->format_free = iso9660_free;+	a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;+	a->archive.archive_format_name = "ISO9660";++	return (ARCHIVE_OK);+}++static int+get_str_opt(struct archive_write *a, struct archive_string *s,+    size_t maxsize, const char *key, const char *value)+{++	if (strlen(value) > maxsize) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Value is longer than %zu characters "+		    "for option ``%s''", maxsize, key);+		return (ARCHIVE_FATAL);+	}+	archive_strcpy(s, value);+	return (ARCHIVE_OK);+}++static int+get_num_opt(struct archive_write *a, int *num, int high, int low,+    const char *key, const char *value)+{+	const char *p = value;+	int data = 0;+	int neg = 0;++	if (p == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Invalid value(empty) for option ``%s''", key);+		return (ARCHIVE_FATAL);+	}+	if (*p == '-') {+		neg = 1;+		p++;+	}+	while (*p) {+		if (*p >= '0' && *p <= '9')+			data = data * 10 + *p - '0';+		else {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Invalid value for option ``%s''", key);+			return (ARCHIVE_FATAL);+		}+		if (data > high) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Invalid value(over %d) for "+			    "option ``%s''", high, key);+			return (ARCHIVE_FATAL);+		}+		if (data < low) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Invalid value(under %d) for "+			    "option ``%s''", low, key);+			return (ARCHIVE_FATAL);+		}+		p++;+	}+	if (neg)+		data *= -1;+	*num = data;++	return (ARCHIVE_OK);+}++static int+iso9660_options(struct archive_write *a, const char *key, const char *value)+{+	struct iso9660 *iso9660 = a->format_data;+	const char *p;+	int r;++	switch (key[0]) {+	case 'a':+		if (strcmp(key, "abstract-file") == 0) {+			r = get_str_opt(a,+			    &(iso9660->abstract_file_identifier),+			    ABSTRACT_FILE_SIZE, key, value);+			iso9660->opt.abstract_file = r == ARCHIVE_OK;+			return (r);+		}+		if (strcmp(key, "application-id") == 0) {+			r = get_str_opt(a,+			    &(iso9660->application_identifier),+			    APPLICATION_IDENTIFIER_SIZE, key, value);+			iso9660->opt.application_id = r == ARCHIVE_OK;+			return (r);+		}+		if (strcmp(key, "allow-vernum") == 0) {+			iso9660->opt.allow_vernum = value != NULL;+			return (ARCHIVE_OK);+		}+		break;+	case 'b':+		if (strcmp(key, "biblio-file") == 0) {+			r = get_str_opt(a,+			    &(iso9660->bibliographic_file_identifier),+			    BIBLIO_FILE_SIZE, key, value);+			iso9660->opt.biblio_file = r == ARCHIVE_OK;+			return (r);+		}+		if (strcmp(key, "boot") == 0) {+			if (value == NULL)+				iso9660->opt.boot = 0;+			else {+				iso9660->opt.boot = 1;+				archive_strcpy(+				    &(iso9660->el_torito.boot_filename),+				    value);+			}+			return (ARCHIVE_OK);+		}+		if (strcmp(key, "boot-catalog") == 0) {+			r = get_str_opt(a,+			    &(iso9660->el_torito.catalog_filename),+			    1024, key, value);+			iso9660->opt.boot_catalog = r == ARCHIVE_OK;+			return (r);+		}+		if (strcmp(key, "boot-info-table") == 0) {+			iso9660->opt.boot_info_table = value != NULL;+			return (ARCHIVE_OK);+		}+		if (strcmp(key, "boot-load-seg") == 0) {+			uint32_t seg;++			iso9660->opt.boot_load_seg = 0;+			if (value == NULL)+				goto invalid_value;+			seg = 0;+			p = value;+			if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))+				p += 2;+			while (*p) {+				if (seg)+					seg <<= 4;+				if (*p >= 'A' && *p <= 'F')+					seg += *p - 'A' + 0x0a;+				else if (*p >= 'a' && *p <= 'f')+					seg += *p - 'a' + 0x0a;+				else if (*p >= '0' && *p <= '9')+					seg += *p - '0';+				else+					goto invalid_value;+				if (seg > 0xffff) {+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_MISC,+					    "Invalid value(over 0xffff) for "+					    "option ``%s''", key);+					return (ARCHIVE_FATAL);+				}+				p++;+			}+			iso9660->el_torito.boot_load_seg = (uint16_t)seg;+			iso9660->opt.boot_load_seg = 1;+			return (ARCHIVE_OK);+		}+		if (strcmp(key, "boot-load-size") == 0) {+			int num = 0;+			r = get_num_opt(a, &num, 0xffff, 1, key, value);+			iso9660->opt.boot_load_size = r == ARCHIVE_OK;+			if (r != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			iso9660->el_torito.boot_load_size = (uint16_t)num;+			return (ARCHIVE_OK);+		}+		if (strcmp(key, "boot-type") == 0) {+			if (value == NULL)+				goto invalid_value;+			if (strcmp(value, "no-emulation") == 0)+				iso9660->opt.boot_type = OPT_BOOT_TYPE_NO_EMU;+			else if (strcmp(value, "fd") == 0)+				iso9660->opt.boot_type = OPT_BOOT_TYPE_FD;+			else if (strcmp(value, "hard-disk") == 0)+				iso9660->opt.boot_type = OPT_BOOT_TYPE_HARD_DISK;+			else+				goto invalid_value;+			return (ARCHIVE_OK);+		}+		break;+	case 'c':+		if (strcmp(key, "compression-level") == 0) {+#ifdef HAVE_ZLIB_H+			if (value == NULL ||+			    !(value[0] >= '0' && value[0] <= '9') ||+			    value[1] != '\0')+				goto invalid_value;+                	iso9660->zisofs.compression_level = value[0] - '0';+			iso9660->opt.compression_level = 1;+                	return (ARCHIVE_OK);+#else+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "Option ``%s'' "+			    "is not supported on this platform.", key);+			return (ARCHIVE_FATAL);+#endif+		}+		if (strcmp(key, "copyright-file") == 0) {+			r = get_str_opt(a,+			    &(iso9660->copyright_file_identifier),+			    COPYRIGHT_FILE_SIZE, key, value);+			iso9660->opt.copyright_file = r == ARCHIVE_OK;+			return (r);+		}+#ifdef DEBUG+		/* Specifies Volume creation date and time;+		 * year(4),month(2),day(2),hour(2),minute(2),second(2).+		 * e.g. "20090929033757"+		 */+		if (strcmp(key, "creation") == 0) {+			struct tm tm;+			char buf[5];++			p = value;+			if (p == NULL || strlen(p) < 14)+				goto invalid_value;+			memset(&tm, 0, sizeof(tm));+			memcpy(buf, p, 4); buf[4] = '\0'; p += 4;+			tm.tm_year = strtol(buf, NULL, 10) - 1900;+			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;+			tm.tm_mon = strtol(buf, NULL, 10) - 1;+			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;+			tm.tm_mday = strtol(buf, NULL, 10);+			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;+			tm.tm_hour = strtol(buf, NULL, 10);+			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;+			tm.tm_min = strtol(buf, NULL, 10);+			memcpy(buf, p, 2); buf[2] = '\0';+			tm.tm_sec = strtol(buf, NULL, 10);+			iso9660->birth_time = mktime(&tm);+			return (ARCHIVE_OK);+		}+#endif+		break;+	case 'i':+		if (strcmp(key, "iso-level") == 0) {+			if (value != NULL && value[1] == '\0' &&+			    (value[0] >= '1' && value[0] <= '4')) {+				iso9660->opt.iso_level = value[0]-'0';+				return (ARCHIVE_OK);+			}+			goto invalid_value;+		}+		break;+	case 'j':+		if (strcmp(key, "joliet") == 0) {+			if (value == NULL)+				iso9660->opt.joliet = OPT_JOLIET_DISABLE;+			else if (strcmp(value, "1") == 0)+				iso9660->opt.joliet = OPT_JOLIET_ENABLE;+			else if (strcmp(value, "long") == 0)+				iso9660->opt.joliet = OPT_JOLIET_LONGNAME;+			else+				goto invalid_value;+			return (ARCHIVE_OK);+		}+		break;+	case 'l':+		if (strcmp(key, "limit-depth") == 0) {+			iso9660->opt.limit_depth = value != NULL;+			return (ARCHIVE_OK);+		}+		if (strcmp(key, "limit-dirs") == 0) {+			iso9660->opt.limit_dirs = value != NULL;+			return (ARCHIVE_OK);+		}+		break;+	case 'p':+		if (strcmp(key, "pad") == 0) {+			iso9660->opt.pad = value != NULL;+			return (ARCHIVE_OK);+		}+		if (strcmp(key, "publisher") == 0) {+			r = get_str_opt(a,+			    &(iso9660->publisher_identifier),+			    PUBLISHER_IDENTIFIER_SIZE, key, value);+			iso9660->opt.publisher = r == ARCHIVE_OK;+			return (r);+		}+		break;+	case 'r':+		if (strcmp(key, "rockridge") == 0 ||+		    strcmp(key, "Rockridge") == 0) {+			if (value == NULL)+				iso9660->opt.rr = OPT_RR_DISABLED;+			else if (strcmp(value, "1") == 0)+				iso9660->opt.rr = OPT_RR_USEFUL;+			else if (strcmp(value, "strict") == 0)+				iso9660->opt.rr = OPT_RR_STRICT;+			else if (strcmp(value, "useful") == 0)+				iso9660->opt.rr = OPT_RR_USEFUL;+			else+				goto invalid_value;+			return (ARCHIVE_OK);+		}+		break;+	case 'v':+		if (strcmp(key, "volume-id") == 0) {+			r = get_str_opt(a, &(iso9660->volume_identifier),+			    VOLUME_IDENTIFIER_SIZE, key, value);+			iso9660->opt.volume_id = r == ARCHIVE_OK;+			return (r);+		}+		break;+	case 'z':+		if (strcmp(key, "zisofs") == 0) {+			if (value == NULL)+				iso9660->opt.zisofs = OPT_ZISOFS_DISABLED;+			else {+#ifdef HAVE_ZLIB_H+				iso9660->opt.zisofs = OPT_ZISOFS_DIRECT;+#else+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "``zisofs'' "+				    "is not supported on this platform.");+				return (ARCHIVE_FATAL);+#endif+			}+			return (ARCHIVE_OK);+		}+		break;+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);++invalid_value:+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+	    "Invalid value for option ``%s''", key);+	return (ARCHIVE_FAILED);+}++static int+iso9660_write_header(struct archive_write *a, struct archive_entry *entry)+{+	struct iso9660 *iso9660;+	struct isofile *file;+	struct isoent *isoent;+	int r, ret = ARCHIVE_OK;++	iso9660 = a->format_data;++	iso9660->cur_file = NULL;+	iso9660->bytes_remaining = 0;+	iso9660->need_multi_extent = 0;+	if (archive_entry_filetype(entry) == AE_IFLNK+	    && iso9660->opt.rr == OPT_RR_DISABLED) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Ignore symlink file.");+		iso9660->cur_file = NULL;+		return (ARCHIVE_WARN);+	}+	if (archive_entry_filetype(entry) == AE_IFREG &&+	    archive_entry_size(entry) >= MULTI_EXTENT_SIZE) {+		if (iso9660->opt.iso_level < 3) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "Ignore over %lld bytes file. "+			    "This file too large.",+			    MULTI_EXTENT_SIZE);+				iso9660->cur_file = NULL;+			return (ARCHIVE_WARN);+		}+		iso9660->need_multi_extent = 1;+	}++	file = isofile_new(a, entry);+	if (file == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate data");+		return (ARCHIVE_FATAL);+	}+	r = isofile_gen_utility_names(a, file);+	if (r < ARCHIVE_WARN) {+		isofile_free(file);+		return (r);+	}+	else if (r < ret)+		ret = r;++	/*+	 * Ignore a path which looks like the top of directory name+	 * since we have already made the root directory of an ISO image.+	 */+	if (archive_strlen(&(file->parentdir)) == 0 &&+	    archive_strlen(&(file->basename)) == 0) {+		isofile_free(file);+		return (r);+	}++	isofile_add_entry(iso9660, file);+	isoent = isoent_new(file);+	if (isoent == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate data");+		return (ARCHIVE_FATAL);+	}+	if (isoent->file->dircnt > iso9660->dircnt_max)+		iso9660->dircnt_max = isoent->file->dircnt;++	/* Add the current file into tree */+	r = isoent_tree(a, &isoent);+	if (r != ARCHIVE_OK)+		return (r);++	/* If there is the same file in tree and+	 * the current file is older than the file in tree.+	 * So we don't need the current file data anymore. */+	if (isoent->file != file)+		return (ARCHIVE_OK);++	/* Non regular files contents are unneeded to be saved to+	 * temporary files. */+	if (archive_entry_filetype(file->entry) != AE_IFREG)+		return (ret);++	/*+	 * Set the current file to cur_file to read its contents.+	 */+	iso9660->cur_file = file;++	if (archive_entry_nlink(file->entry) > 1) {+		r = isofile_register_hardlink(a, file);+		if (r != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	/*+	 * Prepare to save the contents of the file.+	 */+	if (iso9660->temp_fd < 0) {+		iso9660->temp_fd = __archive_mktemp(NULL);+		if (iso9660->temp_fd < 0) {+			archive_set_error(&a->archive, errno,+			    "Couldn't create temporary file");+			return (ARCHIVE_FATAL);+		}+	}++	/* Save an offset of current file in temporary file. */+	file->content.offset_of_temp = wb_offset(a);+	file->cur_content = &(file->content);+	r = zisofs_init(a, file);+	if (r < ret)+		ret = r;+	iso9660->bytes_remaining =  archive_entry_size(file->entry);++	return (ret);+}++static int+write_to_temp(struct archive_write *a, const void *buff, size_t s)+{+	struct iso9660 *iso9660 = a->format_data;+	ssize_t written;+	const unsigned char *b;++	b = (const unsigned char *)buff;+	while (s) {+		written = write(iso9660->temp_fd, b, s);+		if (written < 0) {+			archive_set_error(&a->archive, errno,+			    "Can't write to temporary file");+			return (ARCHIVE_FATAL);+		}+		s -= written;+		b += written;+	}+	return (ARCHIVE_OK);+}++static int+wb_write_to_temp(struct archive_write *a, const void *buff, size_t s)+{+	const char *xp = buff;+	size_t xs = s;++	/*+	 * If a written data size is big enough to use system-call+	 * and there is no waiting data, this calls write_to_temp() in+	 * order to reduce a extra memory copy.+	 */+	if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) {+		struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;+		xs = s % LOGICAL_BLOCK_SIZE;+		iso9660->wbuff_offset += s - xs;+		if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		if (xs == 0)+			return (ARCHIVE_OK);+		xp += s - xs;+	}++	while (xs) {+		size_t size = xs;+		if (size > wb_remaining(a))+			size = wb_remaining(a);+		memcpy(wb_buffptr(a), xp, size);+		if (wb_consume(a, size) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		xs -= size;+		xp += size;+	}+	return (ARCHIVE_OK);+}++static int+wb_write_padding_to_temp(struct archive_write *a, int64_t csize)+{+	size_t ns;+	int ret;++	ns = (size_t)(csize % LOGICAL_BLOCK_SIZE);+	if (ns != 0)+		ret = write_null(a, LOGICAL_BLOCK_SIZE - ns);+	else+		ret = ARCHIVE_OK;+	return (ret);+}++static ssize_t+write_iso9660_data(struct archive_write *a, const void *buff, size_t s)+{+	struct iso9660 *iso9660 = a->format_data;+	size_t ws;++	if (iso9660->temp_fd < 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Couldn't create temporary file");+		return (ARCHIVE_FATAL);+	}++	ws = s;+	if (iso9660->need_multi_extent &&+	    (iso9660->cur_file->cur_content->size + ws) >=+	      (MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE)) {+		struct content *con;+		size_t ts;++		ts = (size_t)(MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE -+		    iso9660->cur_file->cur_content->size);++		if (iso9660->zisofs.detect_magic)+			zisofs_detect_magic(a, buff, ts);++		if (iso9660->zisofs.making) {+			if (zisofs_write_to_temp(a, buff, ts) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		} else {+			if (wb_write_to_temp(a, buff, ts) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			iso9660->cur_file->cur_content->size += ts;+		}++		/* Write padding. */+		if (wb_write_padding_to_temp(a,+		    iso9660->cur_file->cur_content->size) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);++		/* Compute the logical block number. */+		iso9660->cur_file->cur_content->blocks = (int)+		    ((iso9660->cur_file->cur_content->size+		     + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);++		/*+		 * Make next extent.+		 */+		ws -= ts;+		buff = (const void *)(((const unsigned char *)buff) + ts);+		/* Make a content for next extent. */+		con = calloc(1, sizeof(*con));+		if (con == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate content data");+			return (ARCHIVE_FATAL);+		}+		con->offset_of_temp = wb_offset(a);+		iso9660->cur_file->cur_content->next = con;+		iso9660->cur_file->cur_content = con;+#ifdef HAVE_ZLIB_H+		iso9660->zisofs.block_offset = 0;+#endif+	}++	if (iso9660->zisofs.detect_magic)+		zisofs_detect_magic(a, buff, ws);++	if (iso9660->zisofs.making) {+		if (zisofs_write_to_temp(a, buff, ws) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	} else {+		if (wb_write_to_temp(a, buff, ws) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		iso9660->cur_file->cur_content->size += ws;+	}++	return (s);+}++static ssize_t+iso9660_write_data(struct archive_write *a, const void *buff, size_t s)+{+	struct iso9660 *iso9660 = a->format_data;+	ssize_t r;++	if (iso9660->cur_file == NULL)+		return (0);+	if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG)+		return (0);+	if (s > iso9660->bytes_remaining)+		s = (size_t)iso9660->bytes_remaining;+	if (s == 0)+		return (0);++	r = write_iso9660_data(a, buff, s);+	if (r > 0)+		iso9660->bytes_remaining -= r;+	return (r);+}++static int+iso9660_finish_entry(struct archive_write *a)+{+	struct iso9660 *iso9660 = a->format_data;++	if (iso9660->cur_file == NULL)+		return (ARCHIVE_OK);+	if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG)+		return (ARCHIVE_OK);+	if (iso9660->cur_file->content.size == 0)+		return (ARCHIVE_OK);++	/* If there are unwritten data, write null data instead. */+	while (iso9660->bytes_remaining > 0) {+		size_t s;++		s = (iso9660->bytes_remaining > a->null_length)?+		    a->null_length: (size_t)iso9660->bytes_remaining;+		if (write_iso9660_data(a, a->nulls, s) < 0)+			return (ARCHIVE_FATAL);+		iso9660->bytes_remaining -= s;+	}++	if (iso9660->zisofs.making && zisofs_finish_entry(a) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Write padding. */+	if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size)+	    != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Compute the logical block number. */+	iso9660->cur_file->cur_content->blocks = (int)+	    ((iso9660->cur_file->cur_content->size+	     + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);++	/* Add the current file to data file list. */+	isofile_add_data_file(iso9660, iso9660->cur_file);++	return (ARCHIVE_OK);+}++static int+iso9660_close(struct archive_write *a)+{+	struct iso9660 *iso9660;+	int ret, blocks;++	iso9660 = a->format_data;++	/*+	 * Write remaining data out to the temporary file.+	 */+	if (wb_remaining(a) > 0) {+		ret = wb_write_out(a);+		if (ret < 0)+			return (ret);+	}++	/*+	 * Preparations...+	 */+#ifdef DEBUG+	if (iso9660->birth_time == 0)+#endif+		time(&(iso9660->birth_time));++	/*+	 * Prepare a bootable ISO image.+	 */+	if (iso9660->opt.boot) {+		/* Find out the boot file entry. */+		ret = isoent_find_out_boot_file(a, iso9660->primary.rootent);+		if (ret < 0)+			return (ret);+		/* Reconvert the boot file from zisofs'ed form to+		 * plain form. */+		ret = zisofs_rewind_boot_file(a);+		if (ret < 0)+			return (ret);+		/* Write remaining data out to the temporary file. */+		if (wb_remaining(a) > 0) {+			ret = wb_write_out(a);+			if (ret < 0)+				return (ret);+		}+		/* Create the boot catalog. */+		ret = isoent_create_boot_catalog(a, iso9660->primary.rootent);+		if (ret < 0)+			return (ret);+	}++	/*+	 * Prepare joliet extensions.+	 */+	if (iso9660->opt.joliet) {+		/* Make a new tree for joliet. */+		ret = isoent_clone_tree(a, &(iso9660->joliet.rootent),+		    iso9660->primary.rootent);+		if (ret < 0)+			return (ret);+		/* Make sure we have UTF-16BE converters.+		 * if there is no file entry, converters are still+		 * uninitialized. */+		if (iso9660->sconv_to_utf16be == NULL) {+			iso9660->sconv_to_utf16be =+			    archive_string_conversion_to_charset(+				&(a->archive), "UTF-16BE", 1);+			if (iso9660->sconv_to_utf16be == NULL)+				/* Couldn't allocate memory */+				return (ARCHIVE_FATAL);+			iso9660->sconv_from_utf16be =+			    archive_string_conversion_from_charset(+				&(a->archive), "UTF-16BE", 1);+			if (iso9660->sconv_from_utf16be == NULL)+				/* Couldn't allocate memory */+				return (ARCHIVE_FATAL);+		}+	}++	/*+	 * Make Path Tables.+	 */+	ret = isoent_make_path_table(a);+	if (ret < 0)+		return (ret);++	/*+	 * Calculate a total volume size and setup all locations of+	 * contents of an iso9660 image.+	 */+	blocks = SYSTEM_AREA_BLOCK+		+ PRIMARY_VOLUME_DESCRIPTOR_BLOCK+		+ VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK+		+ NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;+	if (iso9660->opt.boot)+		blocks += BOOT_RECORD_DESCRIPTOR_BLOCK;+	if (iso9660->opt.joliet)+		blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK;+	if (iso9660->opt.iso_level == 4)+		blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK;++	/* Setup the locations of Path Table. */+	iso9660->primary.location_type_L_path_table = blocks;+	blocks += iso9660->primary.path_table_block;+	iso9660->primary.location_type_M_path_table = blocks;+	blocks += iso9660->primary.path_table_block;+	if (iso9660->opt.joliet) {+		iso9660->joliet.location_type_L_path_table = blocks;+		blocks += iso9660->joliet.path_table_block;+		iso9660->joliet.location_type_M_path_table = blocks;+		blocks += iso9660->joliet.path_table_block;+	}++	/* Setup the locations of directories. */+	isoent_setup_directory_location(iso9660, blocks,+	    &(iso9660->primary));+	blocks += iso9660->primary.total_dir_block;+	if (iso9660->opt.joliet) {+		isoent_setup_directory_location(iso9660, blocks,+		    &(iso9660->joliet));+		blocks += iso9660->joliet.total_dir_block;+	}++	if (iso9660->opt.rr) {+		iso9660->location_rrip_er = blocks;+		blocks += RRIP_ER_BLOCK;+	}++	/* Setup the locations of all file contents. */+ 	isoent_setup_file_location(iso9660, blocks);+	blocks += iso9660->total_file_block;+	if (iso9660->opt.boot && iso9660->opt.boot_info_table) {+		ret = setup_boot_information(a);+		if (ret < 0)+			return (ret);+	}++	/* Now we have a total volume size. */+	iso9660->volume_space_size = blocks;+	if (iso9660->opt.pad)+		iso9660->volume_space_size += PADDING_BLOCK;+	iso9660->volume_sequence_number = 1;+++	/*+	 * Write an ISO 9660 image.+	 */++	/* Switch to start using wbuff as file buffer. */+	iso9660->wbuff_remaining = wb_buffmax();+	iso9660->wbuff_type = WB_TO_STREAM;+	iso9660->wbuff_offset = 0;+	iso9660->wbuff_written = 0;+	iso9660->wbuff_tail = 0;++	/* Write The System Area */+	ret = write_null(a, SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE);+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Write Primary Volume Descriptor */+	ret = write_VD(a, &(iso9660->primary));+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	if (iso9660->opt.boot) {+		/* Write Boot Record Volume Descriptor */+		ret = write_VD_boot_record(a);+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	if (iso9660->opt.iso_level == 4) {+		/* Write Enhanced Volume Descriptor */+		iso9660->primary.vdd_type = VDD_ENHANCED;+		ret = write_VD(a, &(iso9660->primary));+		iso9660->primary.vdd_type = VDD_PRIMARY;+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	if (iso9660->opt.joliet) {+		ret = write_VD(a, &(iso9660->joliet));+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	/* Write Volume Descriptor Set Terminator */+	ret = write_VD_terminator(a);+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Write Non-ISO File System Information */+	ret = write_information_block(a);+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Write Type L Path Table */+	ret = write_path_table(a, 0, &(iso9660->primary));+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Write Type M Path Table */+	ret = write_path_table(a, 1, &(iso9660->primary));+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	if (iso9660->opt.joliet) {+		/* Write Type L Path Table */+		ret = write_path_table(a, 0, &(iso9660->joliet));+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);++		/* Write Type M Path Table */+		ret = write_path_table(a, 1, &(iso9660->joliet));+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	/* Write Directory Descriptors */+	ret = write_directory_descriptors(a, &(iso9660->primary));+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	if (iso9660->opt.joliet) {+		ret = write_directory_descriptors(a, &(iso9660->joliet));+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	if (iso9660->opt.rr) {+		/* Write Rockridge ER(Extensions Reference) */+		ret = write_rr_ER(a);+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	/* Write File Descriptors */+	ret = write_file_descriptors(a);+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Write Padding  */+	if (iso9660->opt.pad) {+		ret = write_null(a, PADDING_BLOCK * LOGICAL_BLOCK_SIZE);+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	if (iso9660->directories_too_deep != NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "%s: Directories too deep.",+		    archive_entry_pathname(+			iso9660->directories_too_deep->file->entry));+		return (ARCHIVE_WARN);+	}++	/* Write remaining data out. */+	ret = wb_write_out(a);++	return (ret);+}++static int+iso9660_free(struct archive_write *a)+{+	struct iso9660 *iso9660;+	int i, ret;++	iso9660 = a->format_data;++	/* Close the temporary file. */+	if (iso9660->temp_fd >= 0)+		close(iso9660->temp_fd);++	/* Free some stuff for zisofs operations. */+	ret = zisofs_free(a);++	/* Remove directory entries in tree which includes file entries. */+	isoent_free_all(iso9660->primary.rootent);+	for (i = 0; i < iso9660->primary.max_depth; i++)+		free(iso9660->primary.pathtbl[i].sorted);+	free(iso9660->primary.pathtbl);++	if (iso9660->opt.joliet) {+		isoent_free_all(iso9660->joliet.rootent);+		for (i = 0; i < iso9660->joliet.max_depth; i++)+			free(iso9660->joliet.pathtbl[i].sorted);+		free(iso9660->joliet.pathtbl);+	}++	/* Remove isofile entries. */+	isofile_free_all_entries(iso9660);+	isofile_free_hardlinks(iso9660);++	archive_string_free(&(iso9660->cur_dirstr));+	archive_string_free(&(iso9660->volume_identifier));+	archive_string_free(&(iso9660->publisher_identifier));+	archive_string_free(&(iso9660->data_preparer_identifier));+	archive_string_free(&(iso9660->application_identifier));+	archive_string_free(&(iso9660->copyright_file_identifier));+	archive_string_free(&(iso9660->abstract_file_identifier));+	archive_string_free(&(iso9660->bibliographic_file_identifier));+	archive_string_free(&(iso9660->el_torito.catalog_filename));+	archive_string_free(&(iso9660->el_torito.boot_filename));+	archive_string_free(&(iso9660->el_torito.id));+	archive_string_free(&(iso9660->utf16be));+	archive_string_free(&(iso9660->mbs));++	free(iso9660);+	a->format_data = NULL;++	return (ret);+}++/*+ * Get the System Identifier+ */+static void+get_system_identitier(char *system_id, size_t size)+{+#if defined(HAVE_SYS_UTSNAME_H)+	struct utsname u;++	uname(&u);+	strncpy(system_id, u.sysname, size-1);+	system_id[size-1] = '\0';+#elif defined(_WIN32) && !defined(__CYGWIN__)+	strncpy(system_id, "Windows", size-1);+	system_id[size-1] = '\0';+#else+#error no way to get the system identifier on your platform.+#endif+}++static void+set_str(unsigned char *p, const char *s, size_t l, char f, const char *map)+{+	unsigned char c;++	if (s == NULL)+		s = "";+	while ((c = *s++) != 0 && l > 0) {+		if (c >= 0x80 || map[c] == 0)+		 {+			/* illegal character */+			if (c >= 'a' && c <= 'z') {+				/* convert c from a-z to A-Z */+				c -= 0x20;+			} else+				c = 0x5f;+		}+		*p++ = c;+		l--;+	}+	/* If l isn't zero, fill p buffer by the character+	 * which indicated by f. */+	if (l > 0)+		memset(p , f, l);+}++static inline int+joliet_allowed_char(unsigned char high, unsigned char low)+{+	int utf16 = (high << 8) | low;++	if (utf16 <= 0x001F)+		return (0);++	switch (utf16) {+	case 0x002A: /* '*' */+	case 0x002F: /* '/' */+	case 0x003A: /* ':' */+	case 0x003B: /* ';' */+	case 0x003F: /* '?' */+	case 0x005C: /* '\' */+		return (0);/* Not allowed. */+	}+	return (1);+}++static int+set_str_utf16be(struct archive_write *a, unsigned char *p, const char *s,+    size_t l, uint16_t uf, enum vdc vdc)+{+	size_t size, i;+	int onepad;++	if (s == NULL)+		s = "";+	if (l & 0x01) {+		onepad = 1;+		l &= ~1;+	} else+		onepad = 0;+	if (vdc == VDC_UCS2) {+		struct iso9660 *iso9660 = a->format_data;+		if (archive_strncpy_l(&iso9660->utf16be, s, strlen(s),+		    iso9660->sconv_to_utf16be) != 0 && errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for UTF-16BE");+			return (ARCHIVE_FATAL);+		}+		size = iso9660->utf16be.length;+		if (size > l)+			size = l;+		memcpy(p, iso9660->utf16be.s, size);+	} else {+		const uint16_t *u16 = (const uint16_t *)s;++		size = 0;+		while (*u16++)+			size += 2;+		if (size > l)+			size = l;+		memcpy(p, s, size);+	}+	for (i = 0; i < size; i += 2, p += 2) {+		if (!joliet_allowed_char(p[0], p[1]))+			archive_be16enc(p, 0x005F);/* '_' */+	}+	l -= size;+	while (l > 0) {+		archive_be16enc(p, uf);+		p += 2;+		l -= 2;+	}+	if (onepad)+		*p = 0;+	return (ARCHIVE_OK);+}++static const char a_characters_map[0x80] = {+/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */+    1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */+};++static const char a1_characters_map[0x80] = {+/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */+    1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */+};++static const char d_characters_map[0x80] = {+/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */+};++static const char d1_characters_map[0x80] = {+/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */+};++static int+set_str_a_characters_bp(struct archive_write *a, unsigned char *bp,+    int from, int to, const char *s, enum vdc vdc)+{+	int r;++	switch (vdc) {+	case VDC_STD:+		set_str(bp+from, s, to - from + 1, 0x20,+		    a_characters_map);+		r = ARCHIVE_OK;+		break;+	case VDC_LOWERCASE:+		set_str(bp+from, s, to - from + 1, 0x20,+		    a1_characters_map);+		r = ARCHIVE_OK;+		break;+	case VDC_UCS2:+	case VDC_UCS2_DIRECT:+		r = set_str_utf16be(a, bp+from, s, to - from + 1,+		    0x0020, vdc);+		break;+	default:+		r = ARCHIVE_FATAL;+	}+	return (r);+}++static int+set_str_d_characters_bp(struct archive_write *a, unsigned char *bp,+    int from, int to, const char *s, enum  vdc vdc)+{+	int r;++	switch (vdc) {+	case VDC_STD:+		set_str(bp+from, s, to - from + 1, 0x20,+		    d_characters_map);+		r = ARCHIVE_OK;+		break;+	case VDC_LOWERCASE:+		set_str(bp+from, s, to - from + 1, 0x20,+		    d1_characters_map);+		r = ARCHIVE_OK;+		break;+	case VDC_UCS2:+	case VDC_UCS2_DIRECT:+		r = set_str_utf16be(a, bp+from, s, to - from + 1,+		    0x0020, vdc);+		break;+	default:+		r = ARCHIVE_FATAL;+	}+	return (r);+}++static void+set_VD_bp(unsigned char *bp, enum VD_type type, unsigned char ver)+{++	/* Volume Descriptor Type */+	bp[1] = (unsigned char)type;+	/* Standard Identifier */+	memcpy(bp + 2, "CD001", 5);+	/* Volume Descriptor Version */+	bp[7] = ver;+}++static inline void+set_unused_field_bp(unsigned char *bp, int from, int to)+{+	memset(bp + from, 0, to - from + 1);+}++/*+ * 8-bit unsigned numerical values.+ * ISO9660 Standard 7.1.1+ */+static inline void+set_num_711(unsigned char *p, unsigned char value)+{+	*p = value;+}++/*+ * 8-bit signed numerical values.+ * ISO9660 Standard 7.1.2+ */+static inline void+set_num_712(unsigned char *p, char value)+{+	*((char *)p) = value;+}++/*+ * Least significant byte first.+ * ISO9660 Standard 7.2.1+ */+static inline void+set_num_721(unsigned char *p, uint16_t value)+{+	archive_le16enc(p, value);+}++/*+ * Most significant byte first.+ * ISO9660 Standard 7.2.2+ */+static inline void+set_num_722(unsigned char *p, uint16_t value)+{+	archive_be16enc(p, value);+}++/*+ * Both-byte orders.+ * ISO9660 Standard 7.2.3+ */+static void+set_num_723(unsigned char *p, uint16_t value)+{+	archive_le16enc(p, value);+	archive_be16enc(p+2, value);+}++/*+ * Least significant byte first.+ * ISO9660 Standard 7.3.1+ */+static inline void+set_num_731(unsigned char *p, uint32_t value)+{+	archive_le32enc(p, value);+}++/*+ * Most significant byte first.+ * ISO9660 Standard 7.3.2+ */+static inline void+set_num_732(unsigned char *p, uint32_t value)+{+	archive_be32enc(p, value);+}++/*+ * Both-byte orders.+ * ISO9660 Standard 7.3.3+ */+static inline void+set_num_733(unsigned char *p, uint32_t value)+{+	archive_le32enc(p, value);+	archive_be32enc(p+4, value);+}++static void+set_digit(unsigned char *p, size_t s, int value)+{++	while (s--) {+		p[s] = '0' + (value % 10);+		value /= 10;+	}+}++#if defined(HAVE_STRUCT_TM_TM_GMTOFF)+#define get_gmoffset(tm)	((tm)->tm_gmtoff)+#elif defined(HAVE_STRUCT_TM___TM_GMTOFF)+#define get_gmoffset(tm)	((tm)->__tm_gmtoff)+#else+static long+get_gmoffset(struct tm *tm)+{+	long offset;++#if defined(HAVE__GET_TIMEZONE)+	_get_timezone(&offset);+#elif defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)+	offset = _timezone;+#else+	offset = timezone;+#endif+	offset *= -1;+	if (tm->tm_isdst)+		offset += 3600;+	return (offset);+}+#endif++static void+get_tmfromtime(struct tm *tm, time_t *t)+{+#if HAVE_LOCALTIME_R+	tzset();+	localtime_r(t, tm);+#elif HAVE__LOCALTIME64_S+	__time64_t tmp_t = (__time64_t) *t; //time_t may be shorter than 64 bits+	_localtime64_s(tm, &tmp_t);+#else+	memcpy(tm, localtime(t), sizeof(*tm));+#endif+}++/*+ * Date and Time Format.+ * ISO9660 Standard 8.4.26.1+ */+static void+set_date_time(unsigned char *p, time_t t)+{+	struct tm tm;++	get_tmfromtime(&tm, &t);+	set_digit(p, 4, tm.tm_year + 1900);+	set_digit(p+4, 2, tm.tm_mon + 1);+	set_digit(p+6, 2, tm.tm_mday);+	set_digit(p+8, 2, tm.tm_hour);+	set_digit(p+10, 2, tm.tm_min);+	set_digit(p+12, 2, tm.tm_sec);+	set_digit(p+14, 2, 0);+	set_num_712(p+16, (char)(get_gmoffset(&tm)/(60*15)));+}++static void+set_date_time_null(unsigned char *p)+{+	memset(p, (int)'0', 16);+	p[16] = 0;+}++static void+set_time_915(unsigned char *p, time_t t)+{+	struct tm tm;++	get_tmfromtime(&tm, &t);+	set_num_711(p+0, tm.tm_year);+	set_num_711(p+1, tm.tm_mon+1);+	set_num_711(p+2, tm.tm_mday);+	set_num_711(p+3, tm.tm_hour);+	set_num_711(p+4, tm.tm_min);+	set_num_711(p+5, tm.tm_sec);+	set_num_712(p+6, (char)(get_gmoffset(&tm)/(60*15)));+}+++/*+ * Write SUSP "CE" System Use Entry.+ */+static int+set_SUSP_CE(unsigned char *p, int location, int offset, int size)+{+	unsigned char *bp = p -1;+	/*  Extend the System Use Area+	 *   "CE" Format:+	 *               len  ver+	 *    +----+----+----+----+-----------+-----------++	 *    | 'C'| 'E'| 1C | 01 | LOCATION1 | LOCATION2 |+	 *    +----+----+----+----+-----------+-----------++	 *    0    1    2    3    4          12          20+	 *    +-----------++	 *    | LOCATION3 |+	 *    +-----------++	 *   20          28+	 *   LOCATION1 : Location of Continuation of System Use Area.+	 *   LOCATION2 : Offset to Start of Continuation.+	 *   LOCATION3 : Length of the Continuation.+	 */++	bp[1] = 'C';+	bp[2] = 'E';+	bp[3] = RR_CE_SIZE;	/* length	*/+	bp[4] = 1;		/* version	*/+	set_num_733(bp+5, location);+	set_num_733(bp+13, offset);+	set_num_733(bp+21, size);+	return (RR_CE_SIZE);+}++/*+ * The functions, which names are beginning with extra_, are used to+ * control extra records.+ * The maximum size of a Directory Record is 254. When a filename is+ * very long, all of RRIP data of a file won't stored to the Directory+ * Record and so remaining RRIP data store to an extra record instead.+ */+static unsigned char *+extra_open_record(unsigned char *bp, int dr_len, struct isoent *isoent,+    struct ctl_extr_rec *ctl)+{+	ctl->bp = bp;+	if (bp != NULL)+		bp += dr_len;+	ctl->use_extr = 0;+	ctl->isoent = isoent;+	ctl->ce_ptr = NULL;+	ctl->cur_len = ctl->dr_len = dr_len;+	ctl->limit = DR_LIMIT;++	return (bp);+}++static void+extra_close_record(struct ctl_extr_rec *ctl, int ce_size)+{+	int padding = 0;++	if (ce_size > 0)+		extra_tell_used_size(ctl, ce_size);+	/* Padding. */+	if (ctl->cur_len & 0x01) {+		ctl->cur_len++;+		if (ctl->bp != NULL)+			ctl->bp[ctl->cur_len] = 0;+		padding = 1;+	}+	if (ctl->use_extr) {+		if (ctl->ce_ptr != NULL)+			set_SUSP_CE(ctl->ce_ptr, ctl->extr_loc,+			    ctl->extr_off, ctl->cur_len - padding);+	} else+		ctl->dr_len = ctl->cur_len;+}++#define extra_space(ctl)	((ctl)->limit - (ctl)->cur_len)++static unsigned char *+extra_next_record(struct ctl_extr_rec *ctl, int length)+{+	int cur_len = ctl->cur_len;/* save cur_len */++	/* Close the current extra record or Directory Record. */+	extra_close_record(ctl, RR_CE_SIZE);++	/* Get a next extra record. */+	ctl->use_extr = 1;+	if (ctl->bp != NULL) {+		/* Storing data into an extra record. */+		unsigned char *p;++		/* Save the pointer where a CE extension will be+		 * stored to. */+		ctl->ce_ptr = &ctl->bp[cur_len+1];+		p = extra_get_record(ctl->isoent,+		    &ctl->limit, &ctl->extr_off, &ctl->extr_loc);+		ctl->bp = p - 1;/* the base of bp offset is 1. */+	} else+		/* Calculating the size of an extra record. */+		(void)extra_get_record(ctl->isoent,+		    &ctl->limit, NULL, NULL);+	ctl->cur_len = 0;+	/* Check if an extra record is almost full.+	 * If so, get a next one. */+	if (extra_space(ctl) < length)+		(void)extra_next_record(ctl, length);++	return (ctl->bp);+}++static inline struct extr_rec *+extra_last_record(struct isoent *isoent)+{+	if (isoent->extr_rec_list.first == NULL)+		return (NULL);+	return ((struct extr_rec *)(void *)+		((char *)(isoent->extr_rec_list.last)+		    - offsetof(struct extr_rec, next)));+}++static unsigned char *+extra_get_record(struct isoent *isoent, int *space, int *off, int *loc)+{+	struct extr_rec *rec;++	isoent = isoent->parent;+	if (off != NULL) {+		/* Storing data into an extra record. */+		rec = isoent->extr_rec_list.current;+		if (DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset)+			rec = rec->next;+	} else {+		/* Calculating the size of an extra record. */+		rec = extra_last_record(isoent);+		if (rec == NULL ||+		    DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) {+			rec = malloc(sizeof(*rec));+			if (rec == NULL)+				return (NULL);+			rec->location = 0;+			rec->offset = 0;+			/* Insert `rec` into the tail of isoent->extr_rec_list */+			rec->next = NULL;+			/*+			 * Note: testing isoent->extr_rec_list.last == NULL+			 * here is really unneeded since it has been already+			 * initialized at isoent_new function but Clang Static+			 * Analyzer claims that it is dereference of null+			 * pointer.+			 */+			if (isoent->extr_rec_list.last == NULL)+				isoent->extr_rec_list.last =+					&(isoent->extr_rec_list.first);+			*isoent->extr_rec_list.last = rec;+			isoent->extr_rec_list.last = &(rec->next);+		}+	}+	*space = LOGICAL_BLOCK_SIZE - rec->offset - DR_SAFETY;+	if (*space & 0x01)+		*space -= 1;/* Keep padding space. */+	if (off != NULL)+		*off = rec->offset;+	if (loc != NULL)+		*loc = rec->location;+	isoent->extr_rec_list.current = rec;++	return (&rec->buf[rec->offset]);+}++static void+extra_tell_used_size(struct ctl_extr_rec *ctl, int size)+{+	struct isoent *isoent;+	struct extr_rec *rec;++	if (ctl->use_extr) {+		isoent = ctl->isoent->parent;+		rec = isoent->extr_rec_list.current;+		if (rec != NULL)+			rec->offset += size;+	}+	ctl->cur_len += size;+}++static int+extra_setup_location(struct isoent *isoent, int location)+{+	struct extr_rec *rec;+	int cnt;++	cnt = 0;+	rec = isoent->extr_rec_list.first;+	isoent->extr_rec_list.current = rec;+	while (rec) {+		cnt++;+		rec->location = location++;+		rec->offset = 0;+		rec = rec->next;+	}+	return (cnt);+}++/*+ * Create the RRIP entries.+ */+static int+set_directory_record_rr(unsigned char *bp, int dr_len,+    struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t)+{+	/* Flags(BP 5) of the Rockridge "RR" System Use Field */+	unsigned char rr_flag;+#define RR_USE_PX	0x01+#define RR_USE_PN	0x02+#define RR_USE_SL	0x04+#define RR_USE_NM	0x08+#define RR_USE_CL	0x10+#define RR_USE_PL	0x20+#define RR_USE_RE	0x40+#define RR_USE_TF	0x80+	int length;+	struct ctl_extr_rec ctl;+	struct isoent *rr_parent, *pxent;+	struct isofile *file;++	bp = extra_open_record(bp, dr_len, isoent, &ctl);++	if (t == DIR_REC_PARENT) {+		rr_parent = isoent->rr_parent;+		pxent = isoent->parent;+		if (rr_parent != NULL)+			isoent = rr_parent;+		else+			isoent = isoent->parent;+	} else {+		rr_parent = NULL;+		pxent = isoent;+	}+	file = isoent->file;++	if (t != DIR_REC_NORMAL) {+		rr_flag = RR_USE_PX | RR_USE_TF;+		if (rr_parent != NULL)+			rr_flag |= RR_USE_PL;+	} else {+		rr_flag = RR_USE_PX | RR_USE_NM | RR_USE_TF;+		if (archive_entry_filetype(file->entry) == AE_IFLNK)+			rr_flag |= RR_USE_SL;+		if (isoent->rr_parent != NULL)+			rr_flag |= RR_USE_RE;+		if (isoent->rr_child != NULL)+			rr_flag |= RR_USE_CL;+		if (archive_entry_filetype(file->entry) == AE_IFCHR ||+		    archive_entry_filetype(file->entry) == AE_IFBLK)+			rr_flag |= RR_USE_PN;+#ifdef COMPAT_MKISOFS+		/*+		 * mkisofs 2.01.01a63 records "RE" extension to+		 * the entry of "rr_moved" directory.+		 * I don't understand this behavior.+		 */+		if (isoent->virtual &&+		    isoent->parent == iso9660->primary.rootent &&+		    strcmp(isoent->file->basename.s, "rr_moved") == 0)+			rr_flag |= RR_USE_RE;+#endif+	}++	/* Write "SP" System Use Entry. */+	if (t == DIR_REC_SELF && isoent == isoent->parent) {+		length = 7;+		if (bp != NULL) {+			bp[1] = 'S';+			bp[2] = 'P';+			bp[3] = length;+			bp[4] = 1;	/* version	*/+			bp[5] = 0xBE;  /* Check Byte	*/+			bp[6] = 0xEF;  /* Check Byte	*/+			bp[7] = 0;+			bp += length;+		}+		extra_tell_used_size(&ctl, length);+	}++	/* Write "RR" System Use Entry. */+	length = 5;+	if (extra_space(&ctl) < length)+		bp = extra_next_record(&ctl, length);+	if (bp != NULL) {+		bp[1] = 'R';+		bp[2] = 'R';+		bp[3] = length;+		bp[4] = 1;	/* version */+		bp[5] = rr_flag;+		bp += length;+	}+	extra_tell_used_size(&ctl, length);++	/* Write "NM" System Use Entry. */+	if (rr_flag & RR_USE_NM) {+		/*+		 *   "NM" Format:+		 *     e.g. a basename is 'foo'+		 *               len  ver  flg+		 *    +----+----+----+----+----+----+----+----++		 *    | 'N'| 'M'| 08 | 01 | 00 | 'f'| 'o'| 'o'|+		 *    +----+----+----+----+----+----+----+----++		 *    <----------------- len ----------------->+		 */+		size_t nmlen = file->basename.length;+		const char *nm = file->basename.s;+		size_t nmmax;++		if (extra_space(&ctl) < 6)+			bp = extra_next_record(&ctl, 6);+		if (bp != NULL) {+			bp[1] = 'N';+			bp[2] = 'M';+			bp[4] = 1;	    /* version	*/+		}+		nmmax = extra_space(&ctl);+		if (nmmax > 0xff)+			nmmax = 0xff;+		while (nmlen + 5 > nmmax) {+			length = (int)nmmax;+			if (bp != NULL) {+				bp[3] = length;+				bp[5] = 0x01;/* Alternate Name continues+					       * in next "NM" field */+				memcpy(bp+6, nm, length - 5);+				bp += length;+			}+			nmlen -= length - 5;+			nm += length - 5;+			extra_tell_used_size(&ctl, length);+			if (extra_space(&ctl) < 6) {+				bp = extra_next_record(&ctl, 6);+				nmmax = extra_space(&ctl);+				if (nmmax > 0xff)+					nmmax = 0xff;+			}+			if (bp != NULL) {+				bp[1] = 'N';+				bp[2] = 'M';+				bp[4] = 1;    /* version */+			}+		}+		length = 5 + (int)nmlen;+		if (bp != NULL) {+			bp[3] = length;+			bp[5] = 0;+			memcpy(bp+6, nm, nmlen);+			bp += length;+		}+		extra_tell_used_size(&ctl, length);+	}++	/* Write "PX" System Use Entry. */+	if (rr_flag & RR_USE_PX) {+		/*+		 *   "PX" Format:+		 *               len  ver+		 *    +----+----+----+----+-----------+-----------++		 *    | 'P'| 'X'| 2C | 01 | FILE MODE |   LINKS   |+		 *    +----+----+----+----+-----------+-----------++		 *    0    1    2    3    4          12          20+		 *    +-----------+-----------+------------------++		 *    |  USER ID  | GROUP ID  |FILE SERIAL NUMBER|+		 *    +-----------+-----------+------------------++		 *   20          28          36                 44+		 */+		length = 44;+		if (extra_space(&ctl) < length)+			bp = extra_next_record(&ctl, length);+		if (bp != NULL) {+			mode_t mode;+			int64_t uid;+			int64_t gid;++			mode = archive_entry_mode(file->entry);+			uid = archive_entry_uid(file->entry);+			gid = archive_entry_gid(file->entry);+			if (iso9660->opt.rr == OPT_RR_USEFUL) {+				/*+				 * This action is similar to mkisofs -r option+				 * but our rockridge=useful option does not+				 * set a zero to uid and gid.+				 */+				/* set all read bit ON */+				mode |= 0444;+#if !defined(_WIN32) && !defined(__CYGWIN__)+				if (mode & 0111)+#endif+					/* set all exec bit ON */+					mode |= 0111;+				/* clear all write bits. */+				mode &= ~0222;+				/* clear setuid,setgid,sticky bits. */+				mode &= ~07000;+			}++			bp[1] = 'P';+			bp[2] = 'X';+			bp[3] = length;+			bp[4] = 1;	/* version	*/+			/* file mode */+			set_num_733(bp+5, mode);+			/* file links (stat.st_nlink) */+			set_num_733(bp+13,+			    archive_entry_nlink(file->entry));+			set_num_733(bp+21, (uint32_t)uid);+			set_num_733(bp+29, (uint32_t)gid);+			/* File Serial Number */+			if (pxent->dir)+				set_num_733(bp+37, pxent->dir_location);+			else if (file->hardlink_target != NULL)+				set_num_733(bp+37,+				    file->hardlink_target->cur_content->location);+			else+				set_num_733(bp+37,+				    file->cur_content->location);+			bp += length;+		}+		extra_tell_used_size(&ctl, length);+	}++	/* Write "SL" System Use Entry. */+	if (rr_flag & RR_USE_SL) {+		/*+		 *   "SL" Format:+		 *     e.g. a symbolic name is 'foo/bar'+		 *               len  ver  flg+		 *    +----+----+----+----+----+------------++		 *    | 'S'| 'L'| 0F | 01 | 00 | components |+		 *    +----+----+----+----+----+-----+------++		 *    0    1    2    3    4    5  ...|...  15+		 *    <----------------- len --------+------>+		 *    components :                   |+		 *     cflg clen                     |+		 *    +----+----+----+----+----+     |+		 *    | 00 | 03 | 'f'| 'o'| 'o'| <---++		 *    +----+----+----+----+----+     |+		 *    5    6    7    8    9   10     |+		 *     cflg clen                     |+		 *    +----+----+----+----+----+     |+		 *    | 00 | 03 | 'b'| 'a'| 'r'| <---++		 *    +----+----+----+----+----++		 *   10   11   12   13   14   15+		 *+		 *    - cflg : flag of component+		 *    - clen : length of component+		 */+		const char *sl;+		char sl_last;++		if (extra_space(&ctl) < 7)+			bp = extra_next_record(&ctl, 7);+		sl = file->symlink.s;+		sl_last = '\0';+		if (bp != NULL) {+			bp[1] = 'S';+			bp[2] = 'L';+			bp[4] = 1;	/* version	*/+		}+		for (;;) {+			unsigned char *nc, *cf,  *cl, cldmy = 0;+			int sllen, slmax;++			slmax = extra_space(&ctl);+			if (slmax > 0xff)+				slmax = 0xff;+			if (bp != NULL)+				nc = &bp[6];+			else+				nc = NULL;+			cf = cl = NULL;+			sllen = 0;+			while (*sl && sllen + 11 < slmax) {+				if (sl_last == '\0' && sl[0] == '/') {+					/*+					 *     flg  len+					 *    +----+----++					 *    | 08 | 00 | ROOT component.+					 *    +----+----+ ("/")+					 *+				 	 * Root component has to appear+				 	 * at the first component only.+					 */+					if (nc != NULL) {+						cf = nc++;+						*cf = 0x08; /* ROOT */+						*nc++ = 0;+					}+					sllen += 2;+					sl++;+					sl_last = '/';+					cl = NULL;+					continue;+				}+				if (((sl_last == '\0' || sl_last == '/') &&+				      sl[0] == '.' && sl[1] == '.' &&+				     (sl[2] == '/' || sl[2] == '\0')) ||+				    (sl[0] == '/' &&+				      sl[1] == '.' && sl[2] == '.' &&+				     (sl[3] == '/' || sl[3] == '\0'))) {+					/*+					 *     flg  len+					 *    +----+----++					 *    | 04 | 00 | PARENT component.+					 *    +----+----+ ("..")+					 */+					if (nc != NULL) {+						cf = nc++;+						*cf = 0x04; /* PARENT */+						*nc++ = 0;+					}+					sllen += 2;+					if (sl[0] == '/')+						sl += 3;/* skip "/.." */+					else+						sl += 2;/* skip ".." */+					sl_last = '.';+					cl = NULL;+					continue;+				}+				if (((sl_last == '\0' || sl_last == '/') &&+				      sl[0] == '.' &&+				     (sl[1] == '/' || sl[1] == '\0')) ||+				    (sl[0] == '/' && sl[1] == '.' &&+				     (sl[2] == '/' || sl[2] == '\0'))) {+					/*+					 *     flg  len+					 *    +----+----++					 *    | 02 | 00 | CURRENT component.+					 *    +----+----+ (".")+					 */+					if (nc != NULL) {+						cf = nc++;+						*cf = 0x02; /* CURRENT */+						*nc++ = 0;+					}+					sllen += 2;+					if (sl[0] == '/')+						sl += 2;/* skip "/." */+					else+						sl ++;  /* skip "." */+					sl_last = '.';+					cl = NULL;+					continue;+				}+				if (sl[0] == '/' || cl == NULL) {+					if (nc != NULL) {+						cf = nc++;+						*cf = 0;+						cl = nc++;+						*cl = 0;+					} else+						cl = &cldmy;+					sllen += 2;+					if (sl[0] == '/') {+						sl_last = *sl++;+						continue;+					}+				}+				sl_last = *sl++;+				if (nc != NULL) {+					*nc++ = sl_last;+					(*cl) ++;+				}+				sllen++;+			}+			if (*sl) {+				length = 5 + sllen;+				if (bp != NULL) {+					/*+					 * Mark flg as CONTINUE component.+					 */+					*cf |= 0x01;+					/*+					 *               len  ver  flg+					 *    +----+----+----+----+----+-+					 *    | 'S'| 'L'| XX | 01 | 01 |+					 *    +----+----+----+----+----+-+					 *                           ^+					 *           continues in next "SL"+					 */+					bp[3] = length;+					bp[5] = 0x01;/* This Symbolic Link+						      * continues in next+						      * "SL" field */+					bp += length;+				}+				extra_tell_used_size(&ctl, length);+				if (extra_space(&ctl) < 11)+					bp = extra_next_record(&ctl, 11);+				if (bp != NULL) {+					/* Next 'SL' */+					bp[1] = 'S';+					bp[2] = 'L';+					bp[4] = 1;    /* version */+				}+			} else {+				length = 5 + sllen;+				if (bp != NULL) {+					bp[3] = length;+					bp[5] = 0;+					bp += length;+				}+				extra_tell_used_size(&ctl, length);+				break;+			}+		}+	}++	/* Write "TF" System Use Entry. */+	if (rr_flag & RR_USE_TF) {+		/*+		 *   "TF" Format:+		 *               len  ver+		 *    +----+----+----+----+-----+-------------++		 *    | 'T'| 'F'| XX | 01 |FLAGS| TIME STAMPS |+		 *    +----+----+----+----+-----+-------------++		 *    0    1    2    3    4     5            XX+		 *    TIME STAMPS : ISO 9660 Standard 9.1.5.+		 *                  If TF_LONG_FORM FLAGS is set,+		 *                  use ISO9660 Standard 8.4.26.1.+		 */+#define TF_CREATION	0x01	/* Creation time recorded		*/+#define TF_MODIFY	0x02	/* Modification time recorded		*/+#define TF_ACCESS	0x04	/* Last Access time recorded		*/+#define TF_ATTRIBUTES	0x08	/* Last Attribute Change time recorded  */+#define TF_BACKUP	0x10	/* Last Backup time recorded		*/+#define TF_EXPIRATION	0x20	/* Expiration time recorded		*/+#define TF_EFFECTIVE	0x40	/* Effective time recorded		*/+#define TF_LONG_FORM	0x80	/* ISO 9660 17-byte time format used	*/+		unsigned char tf_flags;++		length = 5;+		tf_flags = 0;+#ifndef COMPAT_MKISOFS+		if (archive_entry_birthtime_is_set(file->entry) &&+		    archive_entry_birthtime(file->entry) <=+		    archive_entry_mtime(file->entry)) {+			length += 7;+			tf_flags |= TF_CREATION;+		}+#endif+		if (archive_entry_mtime_is_set(file->entry)) {+			length += 7;+			tf_flags |= TF_MODIFY;+		}+		if (archive_entry_atime_is_set(file->entry)) {+			length += 7;+			tf_flags |= TF_ACCESS;+		}+		if (archive_entry_ctime_is_set(file->entry)) {+			length += 7;+			tf_flags |= TF_ATTRIBUTES;+		}+		if (extra_space(&ctl) < length)+			bp = extra_next_record(&ctl, length);+		if (bp != NULL) {+			bp[1] = 'T';+			bp[2] = 'F';+			bp[3] = length;+			bp[4] = 1;	/* version	*/+			bp[5] = tf_flags;+			bp += 5;+			/* Creation time */+			if (tf_flags & TF_CREATION) {+				set_time_915(bp+1,+				    archive_entry_birthtime(file->entry));+				bp += 7;+			}+			/* Modification time */+			if (tf_flags & TF_MODIFY) {+				set_time_915(bp+1,+				    archive_entry_mtime(file->entry));+				bp += 7;+			}+			/* Last Access time */+			if (tf_flags & TF_ACCESS) {+				set_time_915(bp+1,+				    archive_entry_atime(file->entry));+				bp += 7;+			}+			/* Last Attribute Change time */+			if (tf_flags & TF_ATTRIBUTES) {+				set_time_915(bp+1,+				    archive_entry_ctime(file->entry));+				bp += 7;+			}+		}+		extra_tell_used_size(&ctl, length);+	}++	/* Write "RE" System Use Entry. */+	if (rr_flag & RR_USE_RE) {+		/*+		 *   "RE" Format:+		 *               len  ver+		 *    +----+----+----+----++		 *    | 'R'| 'E'| 04 | 01 |+		 *    +----+----+----+----++		 *    0    1    2    3    4+		 */+		length = 4;+		if (extra_space(&ctl) < length)+			bp = extra_next_record(&ctl, length);+		if (bp != NULL) {+			bp[1] = 'R';+			bp[2] = 'E';+			bp[3] = length;+			bp[4] = 1;	/* version	*/+			bp += length;+		}+		extra_tell_used_size(&ctl, length);+	}++	/* Write "PL" System Use Entry. */+	if (rr_flag & RR_USE_PL) {+		/*+		 *   "PL" Format:+		 *               len  ver+		 *    +----+----+----+----+------------++		 *    | 'P'| 'L'| 0C | 01 | *LOCATION  |+		 *    +----+----+----+----+------------++		 *    0    1    2    3    4           12+		 *    *LOCATION: location of parent directory+		 */+		length = 12;+		if (extra_space(&ctl) < length)+			bp = extra_next_record(&ctl, length);+		if (bp != NULL) {+			bp[1] = 'P';+			bp[2] = 'L';+			bp[3] = length;+			bp[4] = 1;	/* version	*/+			set_num_733(bp + 5,+			    rr_parent->dir_location);+			bp += length;+		}+		extra_tell_used_size(&ctl, length);+	}++	/* Write "CL" System Use Entry. */+	if (rr_flag & RR_USE_CL) {+		/*+		 *   "CL" Format:+		 *               len  ver+		 *    +----+----+----+----+------------++		 *    | 'C'| 'L'| 0C | 01 | *LOCATION  |+		 *    +----+----+----+----+------------++		 *    0    1    2    3    4           12+		 *    *LOCATION: location of child directory+		 */+		length = 12;+		if (extra_space(&ctl) < length)+			bp = extra_next_record(&ctl, length);+		if (bp != NULL) {+			bp[1] = 'C';+			bp[2] = 'L';+			bp[3] = length;+			bp[4] = 1;	/* version	*/+			set_num_733(bp + 5,+			    isoent->rr_child->dir_location);+			bp += length;+		}+		extra_tell_used_size(&ctl, length);+	}++	/* Write "PN" System Use Entry. */+	if (rr_flag & RR_USE_PN) {+		/*+		 *   "PN" Format:+		 *               len  ver+		 *    +----+----+----+----+------------+------------++		 *    | 'P'| 'N'| 14 | 01 | dev_t high | dev_t low  |+		 *    +----+----+----+----+------------+------------++		 *    0    1    2    3    4           12           20+		 */+		length = 20;+		if (extra_space(&ctl) < length)+			bp = extra_next_record(&ctl, length);+		if (bp != NULL) {+			uint64_t dev;++			bp[1] = 'P';+			bp[2] = 'N';+			bp[3] = length;+			bp[4] = 1;	/* version	*/+			dev = (uint64_t)archive_entry_rdev(file->entry);+			set_num_733(bp + 5, (uint32_t)(dev >> 32));+			set_num_733(bp + 13, (uint32_t)(dev & 0xFFFFFFFF));+			bp += length;+		}+		extra_tell_used_size(&ctl, length);+	}++	/* Write "ZF" System Use Entry. */+	if (file->zisofs.header_size) {+		/*+		 *   "ZF" Format:+		 *               len  ver+		 *    +----+----+----+----+----+----+-------------++		 *    | 'Z'| 'F'| 10 | 01 | 'p'| 'z'| Header Size |+		 *    +----+----+----+----+----+----+-------------++		 *    0    1    2    3    4    5    6             7+		 *    +--------------------+-------------------++		 *    | Log2 of block Size | Uncompressed Size |+		 *    +--------------------+-------------------++		 *    7                    8                   16+		 */+		length = 16;+		if (extra_space(&ctl) < length)+			bp = extra_next_record(&ctl, length);+		if (bp != NULL) {+			bp[1] = 'Z';+			bp[2] = 'F';+			bp[3] = length;+			bp[4] = 1;	/* version	*/+			bp[5] = 'p';+			bp[6] = 'z';+			bp[7] = file->zisofs.header_size;+			bp[8] = file->zisofs.log2_bs;+			set_num_733(bp + 9, file->zisofs.uncompressed_size);+			bp += length;+		}+		extra_tell_used_size(&ctl, length);+	}++	/* Write "CE" System Use Entry. */+	if (t == DIR_REC_SELF && isoent == isoent->parent) {+		length = RR_CE_SIZE;+		if (bp != NULL)+			set_SUSP_CE(bp+1, iso9660->location_rrip_er,+			    0, RRIP_ER_SIZE);+		extra_tell_used_size(&ctl, length);+	}++	extra_close_record(&ctl, 0);++	return (ctl.dr_len);+}++/*+ * Write data of a Directory Record or calculate writing bytes itself.+ * If parameter `p' is NULL, calculates the size of writing data, which+ * a Directory Record needs to write, then it saved and return+ * the calculated size.+ * Parameter `n' is a remaining size of buffer. when parameter `p' is+ * not NULL, check whether that `n' is not less than the saved size.+ * if that `n' is small, return zero.+ *+ * This format of the Directory Record is according to+ * ISO9660 Standard 9.1+ */+static int+set_directory_record(unsigned char *p, size_t n, struct isoent *isoent,+    struct iso9660 *iso9660, enum dir_rec_type t,+    enum vdd_type vdd_type)+{+	unsigned char *bp;+	size_t dr_len;+	size_t fi_len;++	if (p != NULL) {+		/*+		 * Check whether a write buffer size is less than the+		 * saved size which is needed to write this Directory+		 * Record.+		 */+		switch (t) {+		case DIR_REC_VD:+			dr_len = isoent->dr_len.vd; break;+		case DIR_REC_SELF:+			dr_len = isoent->dr_len.self; break;+		case DIR_REC_PARENT:+			dr_len = isoent->dr_len.parent; break;+		case DIR_REC_NORMAL:+		default:+			dr_len = isoent->dr_len.normal; break;+		}+		if (dr_len > n)+			return (0);/* Needs more buffer size. */+	}++	if (t == DIR_REC_NORMAL && isoent->identifier != NULL)+		fi_len = isoent->id_len;+	else+		fi_len = 1;++	if (p != NULL) {+		struct isoent *xisoent;+		struct isofile *file;+		unsigned char flag;++		if (t == DIR_REC_PARENT)+			xisoent = isoent->parent;+		else+			xisoent = isoent;+		file = isoent->file;+		if (file->hardlink_target != NULL)+			file = file->hardlink_target;+		/* Make a file flag. */+		if (xisoent->dir)+			flag = FILE_FLAG_DIRECTORY;+		else {+			if (file->cur_content->next != NULL)+				flag = FILE_FLAG_MULTI_EXTENT;+			else+				flag = 0;+		}++		bp = p -1;+		/* Extended Attribute Record Length */+		set_num_711(bp+2, 0);+		/* Location of Extent */+		if (xisoent->dir)+			set_num_733(bp+3, xisoent->dir_location);+		else+			set_num_733(bp+3, file->cur_content->location);+		/* Data Length */+		if (xisoent->dir)+			set_num_733(bp+11,+			    xisoent->dir_block * LOGICAL_BLOCK_SIZE);+		else+			set_num_733(bp+11, (uint32_t)file->cur_content->size);+		/* Recording Date and Time */+		/* NOTE:+		 *  If a file type is symbolic link, you are seeing this+		 *  field value is different from a value mkisofs makes.+		 *  libarchive uses lstat to get this one, but it+		 *  seems mkisofs uses stat to get.+		 */+		set_time_915(bp+19,+		    archive_entry_mtime(xisoent->file->entry));+		/* File Flags */+		bp[26] = flag;+		/* File Unit Size */+		set_num_711(bp+27, 0);+		/* Interleave Gap Size */+		set_num_711(bp+28, 0);+		/* Volume Sequence Number */+		set_num_723(bp+29, iso9660->volume_sequence_number);+		/* Length of File Identifier */+		set_num_711(bp+33, (unsigned char)fi_len);+		/* File Identifier */+		switch (t) {+		case DIR_REC_VD:+		case DIR_REC_SELF:+			set_num_711(bp+34, 0);+			break;+		case DIR_REC_PARENT:+			set_num_711(bp+34, 1);+			break;+		case DIR_REC_NORMAL:+			if (isoent->identifier != NULL)+				memcpy(bp+34, isoent->identifier, fi_len);+			else+				set_num_711(bp+34, 0);+			break;+		}+	} else+		bp = NULL;+	dr_len = 33 + fi_len;+	/* Padding Field */+	if (dr_len & 0x01) {+		dr_len ++;+		if (p != NULL)+			bp[dr_len] = 0;+	}++	/* Volume Descriptor does not record extension. */+	if (t == DIR_REC_VD) {+		if (p != NULL)+			/* Length of Directory Record */+			set_num_711(p, (unsigned char)dr_len);+		else+			isoent->dr_len.vd = (int)dr_len;+		return ((int)dr_len);+	}++	/* Rockridge */+	if (iso9660->opt.rr && vdd_type != VDD_JOLIET)+		dr_len = set_directory_record_rr(bp, (int)dr_len,+		    isoent, iso9660, t);++	if (p != NULL)+		/* Length of Directory Record */+		set_num_711(p, (unsigned char)dr_len);+	else {+		/*+		 * Save the size which is needed to write this+		 * Directory Record.+		 */+		switch (t) {+		case DIR_REC_VD:+			/* This case does not come, but compiler+			 * complains that DIR_REC_VD not handled+			 *  in switch ....  */+			break;+		case DIR_REC_SELF:+			isoent->dr_len.self = (int)dr_len; break;+		case DIR_REC_PARENT:+			isoent->dr_len.parent = (int)dr_len; break;+		case DIR_REC_NORMAL:+			isoent->dr_len.normal = (int)dr_len; break;+		}+	}++	return ((int)dr_len);+}++/*+ * Calculate the size of a directory record.+ */+static inline int+get_dir_rec_size(struct iso9660 *iso9660, struct isoent *isoent,+    enum dir_rec_type t, enum vdd_type vdd_type)+{++	return (set_directory_record(NULL, SIZE_MAX,+	    isoent, iso9660, t, vdd_type));+}++/*+ * Manage to write ISO-image data with wbuff to reduce calling+ * __archive_write_output() for performance.+ */+++static inline unsigned char *+wb_buffptr(struct archive_write *a)+{+	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;++	return (&(iso9660->wbuff[sizeof(iso9660->wbuff)+		- iso9660->wbuff_remaining]));+}++static int+wb_write_out(struct archive_write *a)+{+	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;+	size_t wsize, nw;+	int r;++	wsize = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining;+	nw = wsize % LOGICAL_BLOCK_SIZE;+	if (iso9660->wbuff_type == WB_TO_STREAM)+		r = __archive_write_output(a, iso9660->wbuff, wsize - nw);+	else+		r = write_to_temp(a, iso9660->wbuff, wsize - nw);+	/* Increase the offset. */+	iso9660->wbuff_offset += wsize - nw;+	if (iso9660->wbuff_offset > iso9660->wbuff_written)+		iso9660->wbuff_written = iso9660->wbuff_offset;+	iso9660->wbuff_remaining = sizeof(iso9660->wbuff);+	if (nw) {+		iso9660->wbuff_remaining -= nw;+		memmove(iso9660->wbuff, iso9660->wbuff + wsize - nw, nw);+	}+	return (r);+}++static int+wb_consume(struct archive_write *a, size_t size)+{+	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;++	if (size > iso9660->wbuff_remaining ||+	    iso9660->wbuff_remaining == 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Internal Programming error: iso9660:wb_consume()"+		    " size=%jd, wbuff_remaining=%jd",+		    (intmax_t)size, (intmax_t)iso9660->wbuff_remaining);+		return (ARCHIVE_FATAL);+	}+	iso9660->wbuff_remaining -= size;+	if (iso9660->wbuff_remaining < LOGICAL_BLOCK_SIZE)+		return (wb_write_out(a));+	return (ARCHIVE_OK);+}++#ifdef HAVE_ZLIB_H++static int+wb_set_offset(struct archive_write *a, int64_t off)+{+	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;+	int64_t used, ext_bytes;++	if (iso9660->wbuff_type != WB_TO_TEMP) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Internal Programming error: iso9660:wb_set_offset()");+		return (ARCHIVE_FATAL);+	}++	used = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining;+	if (iso9660->wbuff_offset + used > iso9660->wbuff_tail)+		iso9660->wbuff_tail = iso9660->wbuff_offset + used;+	if (iso9660->wbuff_offset < iso9660->wbuff_written) {+		if (used > 0 &&+		    write_to_temp(a, iso9660->wbuff, (size_t)used) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		iso9660->wbuff_offset = iso9660->wbuff_written;+		lseek(iso9660->temp_fd, iso9660->wbuff_offset, SEEK_SET);+		iso9660->wbuff_remaining = sizeof(iso9660->wbuff);+		used = 0;+	}+	if (off < iso9660->wbuff_offset) {+		/*+		 * Write out waiting data.+		 */+		if (used > 0) {+			if (wb_write_out(a) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		}+		lseek(iso9660->temp_fd, off, SEEK_SET);+		iso9660->wbuff_offset = off;+		iso9660->wbuff_remaining = sizeof(iso9660->wbuff);+	} else if (off <= iso9660->wbuff_tail) {+		iso9660->wbuff_remaining = (size_t)+		    (sizeof(iso9660->wbuff) - (off - iso9660->wbuff_offset));+	} else {+		ext_bytes = off - iso9660->wbuff_tail;+		iso9660->wbuff_remaining = (size_t)(sizeof(iso9660->wbuff)+		   - (iso9660->wbuff_tail - iso9660->wbuff_offset));+		while (ext_bytes >= (int64_t)iso9660->wbuff_remaining) {+			if (write_null(a, (size_t)iso9660->wbuff_remaining)+			    != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			ext_bytes -= iso9660->wbuff_remaining;+		}+		if (ext_bytes > 0) {+			if (write_null(a, (size_t)ext_bytes) != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		}+	}+	return (ARCHIVE_OK);+}++#endif /* HAVE_ZLIB_H */++static int+write_null(struct archive_write *a, size_t size)+{+	size_t remaining;+	unsigned char *p, *old;+	int r;++	remaining = wb_remaining(a);+	p = wb_buffptr(a);+	if (size <= remaining) {+		memset(p, 0, size);+		return (wb_consume(a, size));+	}+	memset(p, 0, remaining);+	r = wb_consume(a, remaining);+	if (r != ARCHIVE_OK)+		return (r);+	size -= remaining;+	old = p;+	p = wb_buffptr(a);+	memset(p, 0, old - p);+	remaining = wb_remaining(a);+	while (size) {+		size_t wsize = size;++		if (wsize > remaining)+			wsize = remaining;+		r = wb_consume(a, wsize);+		if (r != ARCHIVE_OK)+			return (r);+		size -= wsize;+	}+	return (ARCHIVE_OK);+}++/*+ * Write Volume Descriptor Set Terminator+ */+static int+write_VD_terminator(struct archive_write *a)+{+	unsigned char *bp;++	bp = wb_buffptr(a) -1;+	set_VD_bp(bp, VDT_TERMINATOR, 1);+	set_unused_field_bp(bp, 8, LOGICAL_BLOCK_SIZE);++	return (wb_consume(a, LOGICAL_BLOCK_SIZE));+}++static int+set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc,+    struct archive_write *a, struct vdd *vdd, struct archive_string *id,+    const char *label, int leading_under, enum char_type char_type)+{+	char identifier[256];+	struct isoent *isoent;+	const char *ids;+	size_t len;+	int r;++	if (id->length > 0 && leading_under && id->s[0] != '_') {+		if (char_type == A_CHAR)+			r = set_str_a_characters_bp(a, bp, from, to, id->s, vdc);+		else+			r = set_str_d_characters_bp(a, bp, from, to, id->s, vdc);+	} else if (id->length > 0) {+		ids = id->s;+		if (leading_under)+			ids++;+		isoent = isoent_find_entry(vdd->rootent, ids);+		if (isoent == NULL) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Not Found %s `%s'.",+			    label, ids);+			return (ARCHIVE_FATAL);+		}+		len = isoent->ext_off + isoent->ext_len;+		if (vdd->vdd_type == VDD_JOLIET) {+			if (len > sizeof(identifier)-2)+				len = sizeof(identifier)-2;+		} else {+			if (len > sizeof(identifier)-1)+				len = sizeof(identifier)-1;+		}+		memcpy(identifier, isoent->identifier, len);+		identifier[len] = '\0';+		if (vdd->vdd_type == VDD_JOLIET) {+			identifier[len+1] = 0;+			vdc = VDC_UCS2_DIRECT;+		}+		if (char_type == A_CHAR)+			r = set_str_a_characters_bp(a, bp, from, to,+			    identifier, vdc);+		else+			r = set_str_d_characters_bp(a, bp, from, to,+			    identifier, vdc);+	} else {+		if (char_type == A_CHAR)+			r = set_str_a_characters_bp(a, bp, from, to, NULL, vdc);+		else+			r = set_str_d_characters_bp(a, bp, from, to, NULL, vdc);+	}+	return (r);+}++/*+ * Write Primary/Supplementary Volume Descriptor+ */+static int+write_VD(struct archive_write *a, struct vdd *vdd)+{+	struct iso9660 *iso9660;+	unsigned char *bp;+	uint16_t volume_set_size = 1;+	char identifier[256];+	enum VD_type vdt;+	enum vdc vdc;+	unsigned char vd_ver, fst_ver;+	int r;++	iso9660 = a->format_data;+	switch (vdd->vdd_type) {+	case VDD_JOLIET:+		vdt = VDT_SUPPLEMENTARY;+		vd_ver = fst_ver = 1;+		vdc = VDC_UCS2;+		break;+	case VDD_ENHANCED:+		vdt = VDT_SUPPLEMENTARY;+		vd_ver = fst_ver = 2;+		vdc = VDC_LOWERCASE;+		break;+	case VDD_PRIMARY:+	default:+		vdt = VDT_PRIMARY;+		vd_ver = fst_ver = 1;+#ifdef COMPAT_MKISOFS+		vdc = VDC_LOWERCASE;+#else+		vdc = VDC_STD;+#endif+		break;+	}++	bp = wb_buffptr(a) -1;+	/* Volume Descriptor Type */+	set_VD_bp(bp, vdt, vd_ver);+	/* Unused Field */+	set_unused_field_bp(bp, 8, 8);+	/* System Identifier */+	get_system_identitier(identifier, sizeof(identifier));+	r = set_str_a_characters_bp(a, bp, 9, 40, identifier, vdc);+	if (r != ARCHIVE_OK)+		return (r);+	/* Volume Identifier */+	r = set_str_d_characters_bp(a, bp, 41, 72,+	    iso9660->volume_identifier.s, vdc);+	if (r != ARCHIVE_OK)+		return (r);+	/* Unused Field */+	set_unused_field_bp(bp, 73, 80);+	/* Volume Space Size */+	set_num_733(bp+81, iso9660->volume_space_size);+	if (vdd->vdd_type == VDD_JOLIET) {+		/* Escape Sequences */+		bp[89] = 0x25;/* UCS-2 Level 3 */+		bp[90] = 0x2F;+		bp[91] = 0x45;+		memset(bp + 92, 0, 120 - 92 + 1);+	} else {+		/* Unused Field */+		set_unused_field_bp(bp, 89, 120);+	}+	/* Volume Set Size */+	set_num_723(bp+121, volume_set_size);+	/* Volume Sequence Number */+	set_num_723(bp+125, iso9660->volume_sequence_number);+	/* Logical Block Size */+	set_num_723(bp+129, LOGICAL_BLOCK_SIZE);+	/* Path Table Size */+	set_num_733(bp+133, vdd->path_table_size);+	/* Location of Occurrence of Type L Path Table */+	set_num_731(bp+141, vdd->location_type_L_path_table);+	/* Location of Optional Occurrence of Type L Path Table */+	set_num_731(bp+145, 0);+	/* Location of Occurrence of Type M Path Table */+	set_num_732(bp+149, vdd->location_type_M_path_table);+	/* Location of Optional Occurrence of Type M Path Table */+	set_num_732(bp+153, 0);+	/* Directory Record for Root Directory(BP 157 to 190) */+	set_directory_record(bp+157, 190-157+1, vdd->rootent,+	    iso9660, DIR_REC_VD, vdd->vdd_type);+	/* Volume Set Identifier */+	r = set_str_d_characters_bp(a, bp, 191, 318, "", vdc);+	if (r != ARCHIVE_OK)+		return (r);+	/* Publisher Identifier */+	r = set_file_identifier(bp, 319, 446, vdc, a, vdd,+	    &(iso9660->publisher_identifier),+	    "Publisher File", 1, A_CHAR);+	if (r != ARCHIVE_OK)+		return (r);+	/* Data Preparer Identifier */+	r = set_file_identifier(bp, 447, 574, vdc, a, vdd,+	    &(iso9660->data_preparer_identifier),+	    "Data Preparer File", 1, A_CHAR);+	if (r != ARCHIVE_OK)+		return (r);+	/* Application Identifier */+	r = set_file_identifier(bp, 575, 702, vdc, a, vdd,+	    &(iso9660->application_identifier),+	    "Application File", 1, A_CHAR);+	if (r != ARCHIVE_OK)+		return (r);+	/* Copyright File Identifier */+	r = set_file_identifier(bp, 703, 739, vdc, a, vdd,+	    &(iso9660->copyright_file_identifier),+	    "Copyright File", 0, D_CHAR);+	if (r != ARCHIVE_OK)+		return (r);+	/* Abstract File Identifier */+	r = set_file_identifier(bp, 740, 776, vdc, a, vdd,+	    &(iso9660->abstract_file_identifier),+	    "Abstract File", 0, D_CHAR);+	if (r != ARCHIVE_OK)+		return (r);+	/* Bibliographic File Identifier */+	r = set_file_identifier(bp, 777, 813, vdc, a, vdd,+	    &(iso9660->bibliographic_file_identifier),+	    "Bibliongraphic File", 0, D_CHAR);+	if (r != ARCHIVE_OK)+		return (r);+	/* Volume Creation Date and Time */+	set_date_time(bp+814, iso9660->birth_time);+	/* Volume Modification Date and Time */+	set_date_time(bp+831, iso9660->birth_time);+	/* Volume Expiration Date and Time(obsolete) */+	set_date_time_null(bp+848);+	/* Volume Effective Date and Time */+	set_date_time(bp+865, iso9660->birth_time);+	/* File Structure Version */+	bp[882] = fst_ver;+	/* Reserved */+	bp[883] = 0;+	/* Application Use */+	memset(bp + 884, 0x20, 1395 - 884 + 1);+	/* Reserved */+	set_unused_field_bp(bp, 1396, LOGICAL_BLOCK_SIZE);++	return (wb_consume(a, LOGICAL_BLOCK_SIZE));+}++/*+ * Write Boot Record Volume Descriptor+ */+static int+write_VD_boot_record(struct archive_write *a)+{+	struct iso9660 *iso9660;+	unsigned char *bp;++	iso9660 = a->format_data;+	bp = wb_buffptr(a) -1;+	/* Volume Descriptor Type */+	set_VD_bp(bp, VDT_BOOT_RECORD, 1);+	/* Boot System Identifier */+	memcpy(bp+8, "EL TORITO SPECIFICATION", 23);+	set_unused_field_bp(bp, 8+23, 39);+	/* Unused */+	set_unused_field_bp(bp, 40, 71);+	/* Absolute pointer to first sector of Boot Catalog */+	set_num_731(bp+72,+	    iso9660->el_torito.catalog->file->content.location);+	/* Unused */+	set_unused_field_bp(bp, 76, LOGICAL_BLOCK_SIZE);++	return (wb_consume(a, LOGICAL_BLOCK_SIZE));+}++enum keytype {+	KEY_FLG,+	KEY_STR,+	KEY_INT,+	KEY_HEX+};+static void+set_option_info(struct archive_string *info, int *opt, const char *key,+    enum keytype type,  ...)+{+	va_list ap;+	char prefix;+	const char *s;+	int d;++	prefix = (*opt==0)? ' ':',';+	va_start(ap, type);+	switch (type) {+	case KEY_FLG:+		d = va_arg(ap, int);+		archive_string_sprintf(info, "%c%s%s",+		    prefix, (d == 0)?"!":"", key);+		break;+	case KEY_STR:+		s = va_arg(ap, const char *);+		archive_string_sprintf(info, "%c%s=%s",+		    prefix, key, s);+		break;+	case KEY_INT:+		d = va_arg(ap, int);+		archive_string_sprintf(info, "%c%s=%d",+		    prefix, key, d);+		break;+	case KEY_HEX:+		d = va_arg(ap, int);+		archive_string_sprintf(info, "%c%s=%x",+		    prefix, key, d);+		break;+	}+	va_end(ap);++	*opt = 1;+}++/*+ * Make Non-ISO File System Information+ */+static int+write_information_block(struct archive_write *a)+{+	struct iso9660 *iso9660;+	char buf[128];+	const char *v;+	int opt, r;+	struct archive_string info;+	size_t info_size = LOGICAL_BLOCK_SIZE *+			       NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;++	iso9660 = (struct iso9660 *)a->format_data;+	if (info_size > wb_remaining(a)) {+		r = wb_write_out(a);+		if (r != ARCHIVE_OK)+			return (r);+	}+	archive_string_init(&info);+	if (archive_string_ensure(&info, info_size) == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	memset(info.s, 0, info_size);+	opt = 0;+#if defined(HAVE__CTIME64_S)+	{+		__time64_t iso9660_birth_time_tmp = (__time64_t) iso9660->birth_time; //time_t may be shorter than 64 bits+		_ctime64_s(buf, sizeof(buf), &(iso9660_birth_time_tmp));+	}+#elif defined(HAVE_CTIME_R)+	ctime_r(&(iso9660->birth_time), buf);+#else+	strncpy(buf, ctime(&(iso9660->birth_time)), sizeof(buf)-1);+	buf[sizeof(buf)-1] = '\0';+#endif+	archive_string_sprintf(&info,+	    "INFO %s%s", buf, archive_version_string());+	if (iso9660->opt.abstract_file != OPT_ABSTRACT_FILE_DEFAULT)+		set_option_info(&info, &opt, "abstract-file",+		    KEY_STR, iso9660->abstract_file_identifier.s);+	if (iso9660->opt.application_id != OPT_APPLICATION_ID_DEFAULT)+		set_option_info(&info, &opt, "application-id",+		    KEY_STR, iso9660->application_identifier.s);+	if (iso9660->opt.allow_vernum != OPT_ALLOW_VERNUM_DEFAULT)+		set_option_info(&info, &opt, "allow-vernum",+		    KEY_FLG, iso9660->opt.allow_vernum);+	if (iso9660->opt.biblio_file != OPT_BIBLIO_FILE_DEFAULT)+		set_option_info(&info, &opt, "biblio-file",+		    KEY_STR, iso9660->bibliographic_file_identifier.s);+	if (iso9660->opt.boot != OPT_BOOT_DEFAULT)+		set_option_info(&info, &opt, "boot",+		    KEY_STR, iso9660->el_torito.boot_filename.s);+	if (iso9660->opt.boot_catalog != OPT_BOOT_CATALOG_DEFAULT)+		set_option_info(&info, &opt, "boot-catalog",+		    KEY_STR, iso9660->el_torito.catalog_filename.s);+	if (iso9660->opt.boot_info_table != OPT_BOOT_INFO_TABLE_DEFAULT)+		set_option_info(&info, &opt, "boot-info-table",+		    KEY_FLG, iso9660->opt.boot_info_table);+	if (iso9660->opt.boot_load_seg != OPT_BOOT_LOAD_SEG_DEFAULT)+		set_option_info(&info, &opt, "boot-load-seg",+		    KEY_HEX, iso9660->el_torito.boot_load_seg);+	if (iso9660->opt.boot_load_size != OPT_BOOT_LOAD_SIZE_DEFAULT)+		set_option_info(&info, &opt, "boot-load-size",+		    KEY_INT, iso9660->el_torito.boot_load_size);+	if (iso9660->opt.boot_type != OPT_BOOT_TYPE_DEFAULT) {+		v = "no-emulation";+		if (iso9660->opt.boot_type == OPT_BOOT_TYPE_FD)+			v = "fd";+		if (iso9660->opt.boot_type == OPT_BOOT_TYPE_HARD_DISK)+			v = "hard-disk";+		set_option_info(&info, &opt, "boot-type",+		    KEY_STR, v);+	}+#ifdef HAVE_ZLIB_H+	if (iso9660->opt.compression_level != OPT_COMPRESSION_LEVEL_DEFAULT)+		set_option_info(&info, &opt, "compression-level",+		    KEY_INT, iso9660->zisofs.compression_level);+#endif+	if (iso9660->opt.copyright_file != OPT_COPYRIGHT_FILE_DEFAULT)+		set_option_info(&info, &opt, "copyright-file",+		    KEY_STR, iso9660->copyright_file_identifier.s);+	if (iso9660->opt.iso_level != OPT_ISO_LEVEL_DEFAULT)+		set_option_info(&info, &opt, "iso-level",+		    KEY_INT, iso9660->opt.iso_level);+	if (iso9660->opt.joliet != OPT_JOLIET_DEFAULT) {+		if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)+			set_option_info(&info, &opt, "joliet",+			    KEY_STR, "long");+		else+			set_option_info(&info, &opt, "joliet",+			    KEY_FLG, iso9660->opt.joliet);+	}+	if (iso9660->opt.limit_depth != OPT_LIMIT_DEPTH_DEFAULT)+		set_option_info(&info, &opt, "limit-depth",+		    KEY_FLG, iso9660->opt.limit_depth);+	if (iso9660->opt.limit_dirs != OPT_LIMIT_DIRS_DEFAULT)+		set_option_info(&info, &opt, "limit-dirs",+		    KEY_FLG, iso9660->opt.limit_dirs);+	if (iso9660->opt.pad != OPT_PAD_DEFAULT)+		set_option_info(&info, &opt, "pad",+		    KEY_FLG, iso9660->opt.pad);+	if (iso9660->opt.publisher != OPT_PUBLISHER_DEFAULT)+		set_option_info(&info, &opt, "publisher",+		    KEY_STR, iso9660->publisher_identifier.s);+	if (iso9660->opt.rr != OPT_RR_DEFAULT) {+		if (iso9660->opt.rr == OPT_RR_DISABLED)+			set_option_info(&info, &opt, "rockridge",+			    KEY_FLG, iso9660->opt.rr);+		else if (iso9660->opt.rr == OPT_RR_STRICT)+			set_option_info(&info, &opt, "rockridge",+			    KEY_STR, "strict");+		else if (iso9660->opt.rr == OPT_RR_USEFUL)+			set_option_info(&info, &opt, "rockridge",+			    KEY_STR, "useful");+	}+	if (iso9660->opt.volume_id != OPT_VOLUME_ID_DEFAULT)+		set_option_info(&info, &opt, "volume-id",+		    KEY_STR, iso9660->volume_identifier.s);+	if (iso9660->opt.zisofs != OPT_ZISOFS_DEFAULT)+		set_option_info(&info, &opt, "zisofs",+		    KEY_FLG, iso9660->opt.zisofs);++	memcpy(wb_buffptr(a), info.s, info_size);+	archive_string_free(&info);+	return (wb_consume(a, info_size));+}++static int+write_rr_ER(struct archive_write *a)+{+	unsigned char *p;++	p = wb_buffptr(a);++	memset(p, 0, LOGICAL_BLOCK_SIZE);+	p[0] = 'E';+	p[1] = 'R';+	p[3] = 0x01;+	p[2] = RRIP_ER_SIZE;+	p[4] = RRIP_ER_ID_SIZE;+	p[5] = RRIP_ER_DSC_SIZE;+	p[6] = RRIP_ER_SRC_SIZE;+	p[7] = 0x01;+	memcpy(&p[8], rrip_identifier, p[4]);+	memcpy(&p[8+p[4]], rrip_descriptor, p[5]);+	memcpy(&p[8+p[4]+p[5]], rrip_source, p[6]);++	return (wb_consume(a, LOGICAL_BLOCK_SIZE));+}++static void+calculate_path_table_size(struct vdd *vdd)+{+	int depth, size;+	struct path_table *pt;++	pt = vdd->pathtbl;+	size = 0;+	for (depth = 0; depth < vdd->max_depth; depth++) {+		struct isoent **ptbl;+		int i, cnt;++		if ((cnt = pt[depth].cnt) == 0)+			break;++		ptbl = pt[depth].sorted;+		for (i = 0; i < cnt; i++) {+			int len;++			if (ptbl[i]->identifier == NULL)+				len = 1; /* root directory */+			else+				len = ptbl[i]->id_len;+			if (len & 0x01)+				len++; /* Padding Field */+			size += 8 + len;+		}+	}+	vdd->path_table_size = size;+	vdd->path_table_block =+	    ((size + PATH_TABLE_BLOCK_SIZE -1) /+	    PATH_TABLE_BLOCK_SIZE) *+	    (PATH_TABLE_BLOCK_SIZE / LOGICAL_BLOCK_SIZE);+}++static int+_write_path_table(struct archive_write *a, int type_m, int depth,+    struct vdd *vdd)+{+	unsigned char *bp, *wb;+	struct isoent **ptbl;+	size_t wbremaining;+	int i, r, wsize;++	if (vdd->pathtbl[depth].cnt == 0)+		return (0);++	wsize = 0;+	wb = wb_buffptr(a);+	wbremaining = wb_remaining(a);+	bp = wb - 1;+	ptbl = vdd->pathtbl[depth].sorted;+	for (i = 0; i < vdd->pathtbl[depth].cnt; i++) {+		struct isoent *np;+		size_t len;++		np = ptbl[i];+		if (np->identifier == NULL)+			len = 1; /* root directory */+		else+			len = np->id_len;+		if (wbremaining - ((bp+1) - wb) < (len + 1 + 8)) {+			r = wb_consume(a, (bp+1) - wb);+			if (r < 0)+				return (r);+			wb = wb_buffptr(a);+			wbremaining = wb_remaining(a);+			bp = wb -1;+		}+		/* Length of Directory Identifier */+		set_num_711(bp+1, (unsigned char)len);+		/* Extended Attribute Record Length */+		set_num_711(bp+2, 0);+		/* Location of Extent */+		if (type_m)+			set_num_732(bp+3, np->dir_location);+		else+			set_num_731(bp+3, np->dir_location);+		/* Parent Directory Number */+		if (type_m)+			set_num_722(bp+7, np->parent->dir_number);+		else+			set_num_721(bp+7, np->parent->dir_number);+		/* Directory Identifier */+		if (np->identifier == NULL)+			bp[9] = 0;+		else+			memcpy(&bp[9], np->identifier, len);+		if (len & 0x01) {+			/* Padding Field */+			bp[9+len] = 0;+			len++;+		}+		wsize += 8 + (int)len;+		bp += 8 + len;+	}+	if ((bp + 1) > wb) {+		r = wb_consume(a, (bp+1)-wb);+		if (r < 0)+			return (r);+	}+	return (wsize);+}++static int+write_path_table(struct archive_write *a, int type_m, struct vdd *vdd)+{+	int depth, r;+	size_t path_table_size;++	r = ARCHIVE_OK;+	path_table_size = 0;+	for (depth = 0; depth < vdd->max_depth; depth++) {+		r = _write_path_table(a, type_m, depth, vdd);+		if (r < 0)+			return (r);+		path_table_size += r;+	}++	/* Write padding data. */+	path_table_size = path_table_size % PATH_TABLE_BLOCK_SIZE;+	if (path_table_size > 0)+		r = write_null(a, PATH_TABLE_BLOCK_SIZE - path_table_size);+	return (r);+}++static int+calculate_directory_descriptors(struct iso9660 *iso9660, struct vdd *vdd,+    struct isoent *isoent, int depth)+{+	struct isoent **enttbl;+	int bs, block, i;++	block = 1;+	bs = get_dir_rec_size(iso9660, isoent, DIR_REC_SELF, vdd->vdd_type);+	bs += get_dir_rec_size(iso9660, isoent, DIR_REC_PARENT, vdd->vdd_type);++	if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET &&+	    !iso9660->opt.rr && depth + 1 >= vdd->max_depth))+		return (block);++	enttbl = isoent->children_sorted;+	for (i = 0; i < isoent->children.cnt; i++) {+		struct isoent *np = enttbl[i];+		struct isofile *file;++		file = np->file;+		if (file->hardlink_target != NULL)+			file = file->hardlink_target;+		file->cur_content = &(file->content);+		do {+			int dr_l;++			dr_l = get_dir_rec_size(iso9660, np, DIR_REC_NORMAL,+			    vdd->vdd_type);+			if ((bs + dr_l) > LOGICAL_BLOCK_SIZE) {+				block ++;+				bs = dr_l;+			} else+				bs += dr_l;+			file->cur_content = file->cur_content->next;+		} while (file->cur_content != NULL);+	}+	return (block);+}++static int+_write_directory_descriptors(struct archive_write *a, struct vdd *vdd,+    struct isoent *isoent, int depth)+{+	struct iso9660 *iso9660 = a->format_data;+	struct isoent **enttbl;+	unsigned char *p, *wb;+	int i, r;+	int dr_l;++	p = wb = wb_buffptr(a);+#define WD_REMAINING	(LOGICAL_BLOCK_SIZE - (p - wb))+	p += set_directory_record(p, WD_REMAINING, isoent,+	    iso9660, DIR_REC_SELF, vdd->vdd_type);+	p += set_directory_record(p, WD_REMAINING, isoent,+	    iso9660, DIR_REC_PARENT, vdd->vdd_type);++	if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET &&+	    !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) {+		memset(p, 0, WD_REMAINING);+		return (wb_consume(a, LOGICAL_BLOCK_SIZE));+	}++	enttbl = isoent->children_sorted;+	for (i = 0; i < isoent->children.cnt; i++) {+		struct isoent *np = enttbl[i];+		struct isofile *file = np->file;++		if (file->hardlink_target != NULL)+			file = file->hardlink_target;+		file->cur_content = &(file->content);+		do {+			dr_l = set_directory_record(p, WD_REMAINING,+			    np, iso9660, DIR_REC_NORMAL,+			    vdd->vdd_type);+			if (dr_l == 0) {+				memset(p, 0, WD_REMAINING);+				r = wb_consume(a, LOGICAL_BLOCK_SIZE);+				if (r < 0)+					return (r);+				p = wb = wb_buffptr(a);+				dr_l = set_directory_record(p,+				    WD_REMAINING, np, iso9660,+				    DIR_REC_NORMAL, vdd->vdd_type);+			}+			p += dr_l;+			file->cur_content = file->cur_content->next;+		} while (file->cur_content != NULL);+	}+	memset(p, 0, WD_REMAINING);+	return (wb_consume(a, LOGICAL_BLOCK_SIZE));+}++static int+write_directory_descriptors(struct archive_write *a, struct vdd *vdd)+{+	struct isoent *np;+	int depth, r;++	depth = 0;+	np = vdd->rootent;+	do {+		struct extr_rec *extr;++		r = _write_directory_descriptors(a, vdd, np, depth);+		if (r < 0)+			return (r);+		if (vdd->vdd_type != VDD_JOLIET) {+			/*+			 * This extract record is used by SUSP,RRIP.+			 * Not for joliet.+			 */+			for (extr = np->extr_rec_list.first;+			    extr != NULL;+			    extr = extr->next) {+				unsigned char *wb;++				wb = wb_buffptr(a);+				memcpy(wb, extr->buf, extr->offset);+				memset(wb + extr->offset, 0,+				    LOGICAL_BLOCK_SIZE - extr->offset);+				r = wb_consume(a, LOGICAL_BLOCK_SIZE);+				if (r < 0)+					return (r);+			}+		}++		if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {+			/* Enter to sub directories. */+			np = np->subdirs.first;+			depth++;+			continue;+		}+		while (np != np->parent) {+			if (np->drnext == NULL) {+				/* Return to the parent directory. */+				np = np->parent;+				depth--;+			} else {+				np = np->drnext;+				break;+			}+		}+	} while (np != np->parent);++	return (ARCHIVE_OK);+}++/*+ * Read file contents from the temporary file, and write it.+ */+static int+write_file_contents(struct archive_write *a, int64_t offset, int64_t size)+{+	struct iso9660 *iso9660 = a->format_data;+	int r;++	lseek(iso9660->temp_fd, offset, SEEK_SET);++	while (size) {+		size_t rsize;+		ssize_t rs;+		unsigned char *wb;++		wb = wb_buffptr(a);+		rsize = wb_remaining(a);+		if (rsize > (size_t)size)+			rsize = (size_t)size;+		rs = read(iso9660->temp_fd, wb, rsize);+		if (rs <= 0) {+			archive_set_error(&a->archive, errno,+			    "Can't read temporary file(%jd)", (intmax_t)rs);+			return (ARCHIVE_FATAL);+		}+		size -= rs;+		r = wb_consume(a, rs);+		if (r < 0)+			return (r);+	}+	return (ARCHIVE_OK);+}++static int+write_file_descriptors(struct archive_write *a)+{+	struct iso9660 *iso9660 = a->format_data;+	struct isofile *file;+	int64_t blocks, offset;+	int r;++	blocks = 0;+	offset = 0;++	/* Make the boot catalog contents, and write it. */+	if (iso9660->el_torito.catalog != NULL) {+		r = make_boot_catalog(a);+		if (r < 0)+			return (r);+	}++	/* Write the boot file contents. */+	if (iso9660->el_torito.boot != NULL) {+		file = iso9660->el_torito.boot->file;+		blocks = file->content.blocks;+		offset = file->content.offset_of_temp;+		if (offset != 0) {+			r = write_file_contents(a, offset,+			    blocks << LOGICAL_BLOCK_BITS);+			if (r < 0)+				return (r);+			blocks = 0;+			offset = 0;+		}+	}++	/* Write out all file contents. */+	for (file = iso9660->data_file_list.first;+	    file != NULL; file = file->datanext) {++		if (!file->write_content)+			continue;++		if ((offset + (blocks << LOGICAL_BLOCK_BITS)) <+		     file->content.offset_of_temp) {+			if (blocks > 0) {+				r = write_file_contents(a, offset,+				    blocks << LOGICAL_BLOCK_BITS);+				if (r < 0)+					return (r);+			}+			blocks = 0;+			offset = file->content.offset_of_temp;+		}++		file->cur_content = &(file->content);+		do {+			blocks += file->cur_content->blocks;+			/* Next fragment */+			file->cur_content = file->cur_content->next;+		} while (file->cur_content != NULL);+	}++	/* Flush out remaining blocks. */+	if (blocks > 0) {+		r = write_file_contents(a, offset,+		    blocks << LOGICAL_BLOCK_BITS);+		if (r < 0)+			return (r);+	}++	return (ARCHIVE_OK);+}++static void+isofile_init_entry_list(struct iso9660 *iso9660)+{+	iso9660->all_file_list.first = NULL;+	iso9660->all_file_list.last = &(iso9660->all_file_list.first);+}++static void+isofile_add_entry(struct iso9660 *iso9660, struct isofile *file)+{+	file->allnext = NULL;+	*iso9660->all_file_list.last = file;+	iso9660->all_file_list.last = &(file->allnext);+}++static void+isofile_free_all_entries(struct iso9660 *iso9660)+{+	struct isofile *file, *file_next;++	file = iso9660->all_file_list.first;+	while (file != NULL) {+		file_next = file->allnext;+		isofile_free(file);+		file = file_next;+	}+}++static void+isofile_init_entry_data_file_list(struct iso9660 *iso9660)+{+	iso9660->data_file_list.first = NULL;+	iso9660->data_file_list.last = &(iso9660->data_file_list.first);+}++static void+isofile_add_data_file(struct iso9660 *iso9660, struct isofile *file)+{+	file->datanext = NULL;+	*iso9660->data_file_list.last = file;+	iso9660->data_file_list.last = &(file->datanext);+}+++static struct isofile *+isofile_new(struct archive_write *a, struct archive_entry *entry)+{+	struct isofile *file;++	file = calloc(1, sizeof(*file));+	if (file == NULL)+		return (NULL);++	if (entry != NULL)+		file->entry = archive_entry_clone(entry);+	else+		file->entry = archive_entry_new2(&a->archive);+	if (file->entry == NULL) {+		free(file);+		return (NULL);+	}+	archive_string_init(&(file->parentdir));+	archive_string_init(&(file->basename));+	archive_string_init(&(file->basename_utf16));+	archive_string_init(&(file->symlink));+	file->cur_content = &(file->content);++	return (file);+}++static void+isofile_free(struct isofile *file)+{+	struct content *con, *tmp;++	con = file->content.next;+	while (con != NULL) {+		tmp = con;+		con = con->next;+		free(tmp);+	}+	archive_entry_free(file->entry);+	archive_string_free(&(file->parentdir));+	archive_string_free(&(file->basename));+	archive_string_free(&(file->basename_utf16));+	archive_string_free(&(file->symlink));+	free(file);+}++#if defined(_WIN32) || defined(__CYGWIN__)+static int+cleanup_backslash_1(char *p)+{+	int mb, dos;++	mb = dos = 0;+	while (*p) {+		if (*(unsigned char *)p > 127)+			mb = 1;+		if (*p == '\\') {+			/* If we have not met any multi-byte characters,+			 * we can replace '\' with '/'. */+			if (!mb)+				*p = '/';+			dos = 1;+		}+		p++;+	}+	if (!mb || !dos)+		return (0);+	return (-1);+}++static void+cleanup_backslash_2(wchar_t *p)+{++	/* Convert a path-separator from '\' to  '/' */+	while (*p != L'\0') {+		if (*p == L'\\')+			*p = L'/';+		p++;+	}+}+#endif++/*+ * Generate a parent directory name and a base name from a pathname.+ */+static int+isofile_gen_utility_names(struct archive_write *a, struct isofile *file)+{+	struct iso9660 *iso9660;+	const char *pathname;+	char *p, *dirname, *slash;+	size_t len;+	int ret = ARCHIVE_OK;++	iso9660 = a->format_data;++	archive_string_empty(&(file->parentdir));+	archive_string_empty(&(file->basename));+	archive_string_empty(&(file->basename_utf16));+	archive_string_empty(&(file->symlink));++	pathname =  archive_entry_pathname(file->entry);+	if (pathname == NULL || pathname[0] == '\0') {/* virtual root */+		file->dircnt = 0;+		return (ret);+	}++	/*+	 * Make a UTF-16BE basename if Joliet extension enabled.+	 */+	if (iso9660->opt.joliet) {+		const char *u16, *ulast;+		size_t u16len, ulen_last;++		if (iso9660->sconv_to_utf16be == NULL) {+			iso9660->sconv_to_utf16be =+			    archive_string_conversion_to_charset(+				&(a->archive), "UTF-16BE", 1);+			if (iso9660->sconv_to_utf16be == NULL)+				/* Couldn't allocate memory */+				return (ARCHIVE_FATAL);+			iso9660->sconv_from_utf16be =+			    archive_string_conversion_from_charset(+				&(a->archive), "UTF-16BE", 1);+			if (iso9660->sconv_from_utf16be == NULL)+				/* Couldn't allocate memory */+				return (ARCHIVE_FATAL);+		}++		/*+		 * Convert a filename to UTF-16BE.+		 */+		if (0 > archive_entry_pathname_l(file->entry, &u16, &u16len,+		    iso9660->sconv_to_utf16be)) {+			if (errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for UTF-16BE");+				return (ARCHIVE_FATAL);+			}+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "A filename cannot be converted to UTF-16BE;"+			    "You should disable making Joliet extension");+			ret = ARCHIVE_WARN;+		}++		/*+		 * Make sure a path separator is not in the last;+		 * Remove trailing '/'.+		 */+		while (u16len >= 2) {+#if defined(_WIN32) || defined(__CYGWIN__)+			if (u16[u16len-2] == 0 &&+			    (u16[u16len-1] == '/' || u16[u16len-1] == '\\'))+#else+			if (u16[u16len-2] == 0 && u16[u16len-1] == '/')+#endif+			{+				u16len -= 2;+			} else+				break;+		}++		/*+		 * Find a basename in UTF-16BE.+		 */+		ulast = u16;+		u16len >>= 1;+		ulen_last = u16len;+		while (u16len > 0) {+#if defined(_WIN32) || defined(__CYGWIN__)+			if (u16[0] == 0 && (u16[1] == '/' || u16[1] == '\\'))+#else+			if (u16[0] == 0 && u16[1] == '/')+#endif+			{+				ulast = u16 + 2;+				ulen_last = u16len -1;+			}+			u16 += 2;+			u16len --;+		}+		ulen_last <<= 1;+		if (archive_string_ensure(&(file->basename_utf16),+		    ulen_last) == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for UTF-16BE");+			return (ARCHIVE_FATAL);+		}++		/*+		 * Set UTF-16BE basename.+		 */+		memcpy(file->basename_utf16.s, ulast, ulen_last);+		file->basename_utf16.length = ulen_last;+	}++	archive_strcpy(&(file->parentdir), pathname);+#if defined(_WIN32) || defined(__CYGWIN__)+	/*+	 * Convert a path-separator from '\' to  '/'+	 */+	if (cleanup_backslash_1(file->parentdir.s) != 0) {+		const wchar_t *wp = archive_entry_pathname_w(file->entry);+		struct archive_wstring ws;++		if (wp != NULL) {+			int r;+			archive_string_init(&ws);+			archive_wstrcpy(&ws, wp);+			cleanup_backslash_2(ws.s);+			archive_string_empty(&(file->parentdir));+			r = archive_string_append_from_wcs(&(file->parentdir),+			    ws.s, ws.length);+			archive_wstring_free(&ws);+			if (r < 0 && errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory");+				return (ARCHIVE_FATAL);+			}+		}+	}+#endif++	len = file->parentdir.length;+	p = dirname = file->parentdir.s;++	/*+	 * Remove leading '/', '../' and './' elements+	 */+	while (*p) {+		if (p[0] == '/') {+			p++;+			len--;+		} else if (p[0] != '.')+			break;+		else if (p[1] == '.' && p[2] == '/') {+			p += 3;+			len -= 3;+		} else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) {+			p += 2;+			len -= 2;+		} else if (p[1] == '\0') {+			p++;+			len--;+		} else+			break;+	}+	if (p != dirname) {+		memmove(dirname, p, len+1);+		p = dirname;+	}+	/*+	 * Remove "/","/." and "/.." elements from tail.+	 */+	while (len > 0) {+		size_t ll = len;++		if (len > 0 && p[len-1] == '/') {+			p[len-1] = '\0';+			len--;+		}+		if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {+			p[len-2] = '\0';+			len -= 2;+		}+		if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&+		    p[len-1] == '.') {+			p[len-3] = '\0';+			len -= 3;+		}+		if (ll == len)+			break;+	}+	while (*p) {+		if (p[0] == '/') {+			if (p[1] == '/')+				/* Convert '//' --> '/' */+				memmove(p, p+1, strlen(p+1) + 1);+			else if (p[1] == '.' && p[2] == '/')+				/* Convert '/./' --> '/' */+				memmove(p, p+2, strlen(p+2) + 1);+			else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {+				/* Convert 'dir/dir1/../dir2/'+				 *     --> 'dir/dir2/'+				 */+				char *rp = p -1;+				while (rp >= dirname) {+					if (*rp == '/')+						break;+					--rp;+				}+				if (rp > dirname) {+					strcpy(rp, p+3);+					p = rp;+				} else {+					strcpy(dirname, p+4);+					p = dirname;+				}+			} else+				p++;+		} else+			p++;+	}+	p = dirname;+	len = strlen(p);++	if (archive_entry_filetype(file->entry) == AE_IFLNK) {+		/* Convert symlink name too. */+		pathname = archive_entry_symlink(file->entry);+		archive_strcpy(&(file->symlink),  pathname);+#if defined(_WIN32) || defined(__CYGWIN__)+		/*+		 * Convert a path-separator from '\' to  '/'+		 */+		if (archive_strlen(&(file->symlink)) > 0 &&+		    cleanup_backslash_1(file->symlink.s) != 0) {+			const wchar_t *wp =+			    archive_entry_symlink_w(file->entry);+			struct archive_wstring ws;++			if (wp != NULL) {+				int r;+				archive_string_init(&ws);+				archive_wstrcpy(&ws, wp);+				cleanup_backslash_2(ws.s);+				archive_string_empty(&(file->symlink));+				r = archive_string_append_from_wcs(+				    &(file->symlink),+				    ws.s, ws.length);+				archive_wstring_free(&ws);+				if (r < 0 && errno == ENOMEM) {+					archive_set_error(&a->archive, ENOMEM,+					    "Can't allocate memory");+					return (ARCHIVE_FATAL);+				}+			}+		}+#endif+	}+	/*+	 * - Count up directory elements.+	 * - Find out the position which points the last position of+	 *   path separator('/').+	 */+	slash = NULL;+	file->dircnt = 0;+	for (; *p != '\0'; p++)+		if (*p == '/') {+			slash = p;+			file->dircnt++;+		}+	if (slash == NULL) {+		/* The pathname doesn't have a parent directory. */+		file->parentdir.length = len;+		archive_string_copy(&(file->basename), &(file->parentdir));+		archive_string_empty(&(file->parentdir));+		*file->parentdir.s = '\0';+		return (ret);+	}++	/* Make a basename from dirname and slash */+	*slash  = '\0';+	file->parentdir.length = slash - dirname;+	archive_strcpy(&(file->basename),  slash + 1);+	if (archive_entry_filetype(file->entry) == AE_IFDIR)+		file->dircnt ++;+	return (ret);+}++/*+ * Register a entry to get a hardlink target.+ */+static int+isofile_register_hardlink(struct archive_write *a, struct isofile *file)+{+	struct iso9660 *iso9660 = a->format_data;+	struct hardlink *hl;+	const char *pathname;++	archive_entry_set_nlink(file->entry, 1);+	pathname = archive_entry_hardlink(file->entry);+	if (pathname == NULL) {+		/* This `file` is a hardlink target. */+		hl = malloc(sizeof(*hl));+		if (hl == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory");+			return (ARCHIVE_FATAL);+		}+		hl->nlink = 1;+		/* A hardlink target must be the first position. */+		file->hlnext = NULL;+		hl->file_list.first = file;+		hl->file_list.last = &(file->hlnext);+		__archive_rb_tree_insert_node(&(iso9660->hardlink_rbtree),+		    (struct archive_rb_node *)hl);+	} else {+		hl = (struct hardlink *)__archive_rb_tree_find_node(+		    &(iso9660->hardlink_rbtree), pathname);+		if (hl != NULL) {+			/* Insert `file` entry into the tail. */+			file->hlnext = NULL;+			*hl->file_list.last = file;+			hl->file_list.last = &(file->hlnext);+			hl->nlink++;+		}+		archive_entry_unset_size(file->entry);+	}++	return (ARCHIVE_OK);+}++/*+ * Hardlinked files have to have the same location of extent.+ * We have to find out hardlink target entries for the entries+ * which have a hardlink target name.+ */+static void+isofile_connect_hardlink_files(struct iso9660 *iso9660)+{+	struct archive_rb_node *n;+	struct hardlink *hl;+	struct isofile *target, *nf;++	ARCHIVE_RB_TREE_FOREACH(n, &(iso9660->hardlink_rbtree)) {+		hl = (struct hardlink *)n;++		/* The first entry must be a hardlink target. */+		target = hl->file_list.first;+		archive_entry_set_nlink(target->entry, hl->nlink);+		/* Set a hardlink target to reference entries. */+		for (nf = target->hlnext;+		    nf != NULL; nf = nf->hlnext) {+			nf->hardlink_target = target;+			archive_entry_set_nlink(nf->entry, hl->nlink);+		}+	}+}++static int+isofile_hd_cmp_node(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	const struct hardlink *h1 = (const struct hardlink *)n1;+	const struct hardlink *h2 = (const struct hardlink *)n2;++	return (strcmp(archive_entry_pathname(h1->file_list.first->entry),+		       archive_entry_pathname(h2->file_list.first->entry)));+}++static int+isofile_hd_cmp_key(const struct archive_rb_node *n, const void *key)+{+	const struct hardlink *h = (const struct hardlink *)n;++	return (strcmp(archive_entry_pathname(h->file_list.first->entry),+		       (const char *)key));+}++static void+isofile_init_hardlinks(struct iso9660 *iso9660)+{+	static const struct archive_rb_tree_ops rb_ops = {+		isofile_hd_cmp_node, isofile_hd_cmp_key,+	};++	__archive_rb_tree_init(&(iso9660->hardlink_rbtree), &rb_ops);+}++static void+isofile_free_hardlinks(struct iso9660 *iso9660)+{+	struct archive_rb_node *n, *tmp;++	ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(iso9660->hardlink_rbtree), tmp) {+		__archive_rb_tree_remove_node(&(iso9660->hardlink_rbtree), n);+		free(n);+	}+}++static struct isoent *+isoent_new(struct isofile *file)+{+	struct isoent *isoent;+	static const struct archive_rb_tree_ops rb_ops = {+		isoent_cmp_node, isoent_cmp_key,+	};++	isoent = calloc(1, sizeof(*isoent));+	if (isoent == NULL)+		return (NULL);+	isoent->file = file;+	isoent->children.first = NULL;+	isoent->children.last = &(isoent->children.first);+	__archive_rb_tree_init(&(isoent->rbtree), &rb_ops);+	isoent->subdirs.first = NULL;+	isoent->subdirs.last = &(isoent->subdirs.first);+	isoent->extr_rec_list.first = NULL;+	isoent->extr_rec_list.last = &(isoent->extr_rec_list.first);+	isoent->extr_rec_list.current = NULL;+	if (archive_entry_filetype(file->entry) == AE_IFDIR)+		isoent->dir = 1;++	return (isoent);+}++static inline struct isoent *+isoent_clone(struct isoent *src)+{+	return (isoent_new(src->file));+}++static void+_isoent_free(struct isoent *isoent)+{+	struct extr_rec *er, *er_next;++	free(isoent->children_sorted);+	free(isoent->identifier);+	er = isoent->extr_rec_list.first;+	while (er != NULL) {+		er_next = er->next;+		free(er);+		er = er_next;+	}+	free(isoent);+}++static void+isoent_free_all(struct isoent *isoent)+{+	struct isoent *np, *np_temp;++	if (isoent == NULL)+		return;+	np = isoent;+	for (;;) {+		if (np->dir) {+			if (np->children.first != NULL) {+				/* Enter to sub directories. */+				np = np->children.first;+				continue;+			}+		}+		for (;;) {+			np_temp = np;+			if (np->chnext == NULL) {+				/* Return to the parent directory. */+				np = np->parent;+				_isoent_free(np_temp);+				if (np == np_temp)+					return;+			} else {+				np = np->chnext;+				_isoent_free(np_temp);+				break;+			}+		}+	}+}++static struct isoent *+isoent_create_virtual_dir(struct archive_write *a, struct iso9660 *iso9660, const char *pathname)+{+	struct isofile *file;+	struct isoent *isoent;++	file = isofile_new(a, NULL);+	if (file == NULL)+		return (NULL);+	archive_entry_set_pathname(file->entry, pathname);+	archive_entry_unset_mtime(file->entry);+	archive_entry_unset_atime(file->entry);+	archive_entry_unset_ctime(file->entry);+	archive_entry_set_uid(file->entry, getuid());+	archive_entry_set_gid(file->entry, getgid());+	archive_entry_set_mode(file->entry, 0555 | AE_IFDIR);+	archive_entry_set_nlink(file->entry, 2);+	if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) {+		isofile_free(file);+		return (NULL);+	}+	isofile_add_entry(iso9660, file);++	isoent = isoent_new(file);+	if (isoent == NULL)+		return (NULL);+	isoent->dir = 1;+	isoent->virtual = 1;++	return (isoent);+}++static int+isoent_cmp_node(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	const struct isoent *e1 = (const struct isoent *)n1;+	const struct isoent *e2 = (const struct isoent *)n2;++	return (strcmp(e1->file->basename.s, e2->file->basename.s));+}++static int+isoent_cmp_key(const struct archive_rb_node *n, const void *key)+{+	const struct isoent *e = (const struct isoent *)n;++	return (strcmp(e->file->basename.s, (const char *)key));+}++static int+isoent_add_child_head(struct isoent *parent, struct isoent *child)+{++	if (!__archive_rb_tree_insert_node(+	    &(parent->rbtree), (struct archive_rb_node *)child))+		return (0);+	if ((child->chnext = parent->children.first) == NULL)+		parent->children.last = &(child->chnext);+	parent->children.first = child;+	parent->children.cnt++;+	child->parent = parent;++	/* Add a child to a sub-directory chain */+	if (child->dir) {+		if ((child->drnext = parent->subdirs.first) == NULL)+			parent->subdirs.last = &(child->drnext);+		parent->subdirs.first = child;+		parent->subdirs.cnt++;+		child->parent = parent;+	} else+		child->drnext = NULL;+	return (1);+}++static int+isoent_add_child_tail(struct isoent *parent, struct isoent *child)+{++	if (!__archive_rb_tree_insert_node(+	    &(parent->rbtree), (struct archive_rb_node *)child))+		return (0);+	child->chnext = NULL;+	*parent->children.last = child;+	parent->children.last = &(child->chnext);+	parent->children.cnt++;+	child->parent = parent;++	/* Add a child to a sub-directory chain */+	child->drnext = NULL;+	if (child->dir) {+		*parent->subdirs.last = child;+		parent->subdirs.last = &(child->drnext);+		parent->subdirs.cnt++;+		child->parent = parent;+	}+	return (1);+}++static void+isoent_remove_child(struct isoent *parent, struct isoent *child)+{+	struct isoent *ent;++	/* Remove a child entry from children chain. */+	ent = parent->children.first;+	while (ent->chnext != child)+		ent = ent->chnext;+	if ((ent->chnext = ent->chnext->chnext) == NULL)+		parent->children.last = &(ent->chnext);+	parent->children.cnt--;++	if (child->dir) {+		/* Remove a child entry from sub-directory chain. */+		ent = parent->subdirs.first;+		while (ent->drnext != child)+			ent = ent->drnext;+		if ((ent->drnext = ent->drnext->drnext) == NULL)+			parent->subdirs.last = &(ent->drnext);+		parent->subdirs.cnt--;+	}++	__archive_rb_tree_remove_node(&(parent->rbtree),+	    (struct archive_rb_node *)child);+}++static int+isoent_clone_tree(struct archive_write *a, struct isoent **nroot,+    struct isoent *root)+{+	struct isoent *np, *xroot, *newent;++	np = root;+	xroot = NULL;+	do {+		newent = isoent_clone(np);+		if (newent == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory");+			return (ARCHIVE_FATAL);+		}+		if (xroot == NULL) {+			*nroot = xroot = newent;+			newent->parent = xroot;+		} else+			isoent_add_child_tail(xroot, newent);+		if (np->dir && np->children.first != NULL) {+			/* Enter to sub directories. */+			np = np->children.first;+			xroot = newent;+			continue;+		}+		while (np != np->parent) {+			if (np->chnext == NULL) {+				/* Return to the parent directory. */+				np = np->parent;+				xroot = xroot->parent;+			} else {+				np = np->chnext;+				break;+			}+		}+	} while (np != np->parent);++	return (ARCHIVE_OK);+}++/*+ * Setup directory locations.+ */+static void+isoent_setup_directory_location(struct iso9660 *iso9660, int location,+    struct vdd *vdd)+{+	struct isoent *np;+	int depth;++	vdd->total_dir_block = 0;+	depth = 0;+	np = vdd->rootent;+	do {+		int block;++		np->dir_block = calculate_directory_descriptors(+		    iso9660, vdd, np, depth);+		vdd->total_dir_block += np->dir_block;+		np->dir_location = location;+		location += np->dir_block;+		block = extra_setup_location(np, location);+		vdd->total_dir_block += block;+		location += block;++		if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {+			/* Enter to sub directories. */+			np = np->subdirs.first;+			depth++;+			continue;+		}+		while (np != np->parent) {+			if (np->drnext == NULL) {+				/* Return to the parent directory. */+				np = np->parent;+				depth--;+			} else {+				np = np->drnext;+				break;+			}+		}+	} while (np != np->parent);+}++static void+_isoent_file_location(struct iso9660 *iso9660, struct isoent *isoent,+    int *symlocation)+{+	struct isoent **children;+	int n;++	if (isoent->children.cnt == 0)+		return;++	children = isoent->children_sorted;+	for (n = 0; n < isoent->children.cnt; n++) {+		struct isoent *np;+		struct isofile *file;++		np = children[n];+		if (np->dir)+			continue;+		if (np == iso9660->el_torito.boot)+			continue;+		file = np->file;+		if (file->boot || file->hardlink_target != NULL)+			continue;+		if (archive_entry_filetype(file->entry) == AE_IFLNK ||+		    file->content.size == 0) {+			/*+			 * Do not point a valid location.+			 * Make sure entry is not hardlink file.+			 */+			file->content.location = (*symlocation)--;+			continue;+		}++		file->write_content = 1;+	}+}++/*+ * Setup file locations.+ */+static void+isoent_setup_file_location(struct iso9660 *iso9660, int location)+{+	struct isoent *isoent;+	struct isoent *np;+	struct isofile *file;+	size_t size;+	int block;+	int depth;+	int joliet;+	int symlocation;+	int total_block;++	iso9660->total_file_block = 0;+	if ((isoent = iso9660->el_torito.catalog) != NULL) {+		isoent->file->content.location = location;+		block = (int)((archive_entry_size(isoent->file->entry) ++		    LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);+		location += block;+		iso9660->total_file_block += block;+	}+	if ((isoent = iso9660->el_torito.boot) != NULL) {+		isoent->file->content.location = location;+		size = fd_boot_image_size(iso9660->el_torito.media_type);+		if (size == 0)+			size = (size_t)archive_entry_size(isoent->file->entry);+		block = ((int)size + LOGICAL_BLOCK_SIZE -1)+		    >> LOGICAL_BLOCK_BITS;+		location += block;+		iso9660->total_file_block += block;+		isoent->file->content.blocks = block;+	}++	depth = 0;+	symlocation = -16;+	if (!iso9660->opt.rr && iso9660->opt.joliet) {+		joliet = 1;+		np = iso9660->joliet.rootent;+	} else {+		joliet = 0;+		np = iso9660->primary.rootent;+	}+	do {+		_isoent_file_location(iso9660, np, &symlocation);++		if (np->subdirs.first != NULL &&+		    (joliet ||+		    ((iso9660->opt.rr == OPT_RR_DISABLED &&+		      depth + 2 < iso9660->primary.max_depth) ||+		     (iso9660->opt.rr &&+		      depth + 1 < iso9660->primary.max_depth)))) {+			/* Enter to sub directories. */+			np = np->subdirs.first;+			depth++;+			continue;+		}+		while (np != np->parent) {+			if (np->drnext == NULL) {+				/* Return to the parent directory. */+				np = np->parent;+				depth--;+			} else {+				np = np->drnext;+				break;+			}+		}+	} while (np != np->parent);++	total_block = 0;+	for (file = iso9660->data_file_list.first;+	    file != NULL; file = file->datanext) {++		if (!file->write_content)+			continue;++		file->cur_content = &(file->content);+		do {+			file->cur_content->location = location;+			location += file->cur_content->blocks;+			total_block += file->cur_content->blocks;+			/* Next fragment */+			file->cur_content = file->cur_content->next;+		} while (file->cur_content != NULL);+	}+	iso9660->total_file_block += total_block;+}++static int+get_path_component(char *name, size_t n, const char *fn)+{+	char *p;+	size_t l;++	p = strchr(fn, '/');+	if (p == NULL) {+		if ((l = strlen(fn)) == 0)+			return (0);+	} else+		l = p - fn;+	if (l > n -1)+		return (-1);+	memcpy(name, fn, l);+	name[l] = '\0';++	return ((int)l);+}++/*+ * Add a new entry into the tree.+ */+static int+isoent_tree(struct archive_write *a, struct isoent **isoentpp)+{+#if defined(_WIN32) && !defined(__CYGWIN__)+	char name[_MAX_FNAME];/* Included null terminator size. */+#elif defined(NAME_MAX) && NAME_MAX >= 255+	char name[NAME_MAX+1];+#else+	char name[256];+#endif+	struct iso9660 *iso9660 = a->format_data;+	struct isoent *dent, *isoent, *np;+	struct isofile *f1, *f2;+	const char *fn, *p;+	int l;++	isoent = *isoentpp;+	dent = iso9660->primary.rootent;+	if (isoent->file->parentdir.length > 0)+		fn = p = isoent->file->parentdir.s;+	else+		fn = p = "";++	/*+	 * If the path of the parent directory of `isoent' entry is+	 * the same as the path of `cur_dirent', add isoent to+	 * `cur_dirent'.+	 */+	if (archive_strlen(&(iso9660->cur_dirstr))+	      == archive_strlen(&(isoent->file->parentdir)) &&+	    strcmp(iso9660->cur_dirstr.s, fn) == 0) {+		if (!isoent_add_child_tail(iso9660->cur_dirent, isoent)) {+			np = (struct isoent *)__archive_rb_tree_find_node(+			    &(iso9660->cur_dirent->rbtree),+			    isoent->file->basename.s);+			goto same_entry;+		}+		return (ARCHIVE_OK);+	}++	for (;;) {+		l = get_path_component(name, sizeof(name), fn);+		if (l == 0) {+			np = NULL;+			break;+		}+		if (l < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "A name buffer is too small");+			_isoent_free(isoent);+			return (ARCHIVE_FATAL);+		}++		np = isoent_find_child(dent, name);+		if (np == NULL || fn[0] == '\0')+			break;++		/* Find next subdirectory. */+		if (!np->dir) {+			/* NOT Directory! */+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "`%s' is not directory, we cannot insert `%s' ",+			    archive_entry_pathname(np->file->entry),+			    archive_entry_pathname(isoent->file->entry));+			_isoent_free(isoent);+			*isoentpp = NULL;+			return (ARCHIVE_FAILED);+		}+		fn += l;+		if (fn[0] == '/')+			fn++;+		dent = np;+	}+	if (np == NULL) {+		/*+		 * Create virtual parent directories.+		 */+		while (fn[0] != '\0') {+			struct isoent *vp;+			struct archive_string as;++			archive_string_init(&as);+			archive_strncat(&as, p, fn - p + l);+			if (as.s[as.length-1] == '/') {+				as.s[as.length-1] = '\0';+				as.length--;+			}+			vp = isoent_create_virtual_dir(a, iso9660, as.s);+			if (vp == NULL) {+				archive_string_free(&as);+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory");+				_isoent_free(isoent);+				*isoentpp = NULL;+				return (ARCHIVE_FATAL);+			}+			archive_string_free(&as);++			if (vp->file->dircnt > iso9660->dircnt_max)+				iso9660->dircnt_max = vp->file->dircnt;+			isoent_add_child_tail(dent, vp);+			np = vp;++			fn += l;+			if (fn[0] == '/')+				fn++;+			l = get_path_component(name, sizeof(name), fn);+			if (l < 0) {+				archive_string_free(&as);+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "A name buffer is too small");+				_isoent_free(isoent);+				*isoentpp = NULL;+				return (ARCHIVE_FATAL);+			}+			dent = np;+		}++		/* Found out the parent directory where isoent can be+		 * inserted. */+		iso9660->cur_dirent = dent;+		archive_string_empty(&(iso9660->cur_dirstr));+		archive_string_ensure(&(iso9660->cur_dirstr),+		    archive_strlen(&(dent->file->parentdir)) ++		    archive_strlen(&(dent->file->basename)) + 2);+		if (archive_strlen(&(dent->file->parentdir)) ++		    archive_strlen(&(dent->file->basename)) == 0)+			iso9660->cur_dirstr.s[0] = 0;+		else {+			if (archive_strlen(&(dent->file->parentdir)) > 0) {+				archive_string_copy(&(iso9660->cur_dirstr),+				    &(dent->file->parentdir));+				archive_strappend_char(&(iso9660->cur_dirstr), '/');+			}+			archive_string_concat(&(iso9660->cur_dirstr),+			    &(dent->file->basename));+		}++		if (!isoent_add_child_tail(dent, isoent)) {+			np = (struct isoent *)__archive_rb_tree_find_node(+			    &(dent->rbtree), isoent->file->basename.s);+			goto same_entry;+		}+		return (ARCHIVE_OK);+	}++same_entry:+	/*+	 * We have already has the entry the filename of which is+	 * the same.+	 */+	f1 = np->file;+	f2 = isoent->file;++	/* If the file type of entries is different,+	 * we cannot handle it. */+	if (archive_entry_filetype(f1->entry) !=+	    archive_entry_filetype(f2->entry)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Found duplicate entries `%s' and its file type is "+		    "different",+		    archive_entry_pathname(f1->entry));+		_isoent_free(isoent);+		*isoentpp = NULL;+		return (ARCHIVE_FAILED);+	}++	/* Swap file entries. */+	np->file = f2;+	isoent->file = f1;+	np->virtual = 0;++	_isoent_free(isoent);+	*isoentpp = np;+	return (ARCHIVE_OK);+}++/*+ * Find a entry from `isoent'+ */+static struct isoent *+isoent_find_child(struct isoent *isoent, const char *child_name)+{+	struct isoent *np;++	np = (struct isoent *)__archive_rb_tree_find_node(+	    &(isoent->rbtree), child_name);+	return (np);+}++/*+ * Find a entry full-path of which is specified by `fn' parameter,+ * in the tree.+ */+static struct isoent *+isoent_find_entry(struct isoent *rootent, const char *fn)+{+#if defined(_WIN32) && !defined(__CYGWIN__)+	char name[_MAX_FNAME];/* Included null terminator size. */+#elif defined(NAME_MAX) && NAME_MAX >= 255+	char name[NAME_MAX+1];+#else+	char name[256];+#endif+	struct isoent *isoent, *np;+	int l;++	isoent = rootent;+	np = NULL;+	for (;;) {+		l = get_path_component(name, sizeof(name), fn);+		if (l == 0)+			break;+		fn += l;+		if (fn[0] == '/')+			fn++;++		np = isoent_find_child(isoent, name);+		if (np == NULL)+			break;+		if (fn[0] == '\0')+			break;/* We found out the entry */++		/* Try sub directory. */+		isoent = np;+		np = NULL;+		if (!isoent->dir)+			break;/* Not directory */+	}++	return (np);+}++/*+ * Following idr_* functions are used for resolving duplicated filenames+ * and unreceivable filenames to generate ISO9660/Joliet Identifiers.+ */++static void+idr_relaxed_filenames(char *map)+{+	int i;++	for (i = 0x21; i <= 0x2F; i++)+		map[i] = 1;+	for (i = 0x3A; i <= 0x41; i++)+		map[i] = 1;+	for (i = 0x5B; i <= 0x5E; i++)+		map[i] = 1;+	map[0x60] = 1;+	for (i = 0x7B; i <= 0x7E; i++)+		map[i] = 1;+}++static void+idr_init(struct iso9660 *iso9660, struct vdd *vdd, struct idr *idr)+{++	idr->idrent_pool = NULL;+	idr->pool_size = 0;+	if (vdd->vdd_type != VDD_JOLIET) {+		if (iso9660->opt.iso_level <= 3) {+			memcpy(idr->char_map, d_characters_map,+			    sizeof(idr->char_map));+		} else {+			memcpy(idr->char_map, d1_characters_map,+			    sizeof(idr->char_map));+			idr_relaxed_filenames(idr->char_map);+		}+	}+}++static void+idr_cleanup(struct idr *idr)+{+	free(idr->idrent_pool);+}++static int+idr_ensure_poolsize(struct archive_write *a, struct idr *idr,+    int cnt)+{++	if (idr->pool_size < cnt) {+		void *p;+		const int bk = (1 << 7) - 1;+		int psize;++		psize = (cnt + bk) & ~bk;+		p = realloc(idr->idrent_pool, sizeof(struct idrent) * psize);+		if (p == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory");+			return (ARCHIVE_FATAL);+		}+		idr->idrent_pool = (struct idrent *)p;+		idr->pool_size = psize;+	}+	return (ARCHIVE_OK);+}++static int+idr_start(struct archive_write *a, struct idr *idr, int cnt, int ffmax,+    int num_size, int null_size, const struct archive_rb_tree_ops *rbt_ops)+{+	int r;++	(void)ffmax; /* UNUSED */++	r = idr_ensure_poolsize(a, idr, cnt);+	if (r != ARCHIVE_OK)+		return (r);+	__archive_rb_tree_init(&(idr->rbtree), rbt_ops);+	idr->wait_list.first = NULL;+	idr->wait_list.last = &(idr->wait_list.first);+	idr->pool_idx = 0;+	idr->num_size = num_size;+	idr->null_size = null_size;+	return (ARCHIVE_OK);+}++static void+idr_register(struct idr *idr, struct isoent *isoent, int weight, int noff)+{+	struct idrent *idrent, *n;++	idrent = &(idr->idrent_pool[idr->pool_idx++]);+	idrent->wnext = idrent->avail = NULL;+	idrent->isoent = isoent;+	idrent->weight = weight;+	idrent->noff = noff;+	idrent->rename_num = 0;++	if (!__archive_rb_tree_insert_node(&(idr->rbtree), &(idrent->rbnode))) {+		n = (struct idrent *)__archive_rb_tree_find_node(+		    &(idr->rbtree), idrent->isoent);+		if (n != NULL) {+			/* this `idrent' needs to rename. */+			idrent->avail = n;+			*idr->wait_list.last = idrent;+			idr->wait_list.last = &(idrent->wnext);+		}+	}+}++static void+idr_extend_identifier(struct idrent *wnp, int numsize, int nullsize)+{+	unsigned char *p;+	int wnp_ext_off;++	wnp_ext_off = wnp->isoent->ext_off;+	if (wnp->noff + numsize != wnp_ext_off) {+		p = (unsigned char *)wnp->isoent->identifier;+		/* Extend the filename; foo.c --> foo___.c */+		memmove(p + wnp->noff + numsize, p + wnp_ext_off,+		    wnp->isoent->ext_len + nullsize);+		wnp->isoent->ext_off = wnp_ext_off = wnp->noff + numsize;+		wnp->isoent->id_len = wnp_ext_off + wnp->isoent->ext_len;+	}+}++static void+idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num))+{+	struct idrent *n;+	unsigned char *p;++	for (n = idr->wait_list.first; n != NULL; n = n->wnext) {+		idr_extend_identifier(n, idr->num_size, idr->null_size);+		p = (unsigned char *)n->isoent->identifier + n->noff;+		do {+			fsetnum(p, n->avail->rename_num++);+		} while (!__archive_rb_tree_insert_node(+		    &(idr->rbtree), &(n->rbnode)));+	}+}++static void+idr_set_num(unsigned char *p, int num)+{+	static const char xdig[] = {+		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',+		'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',+		'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',+		'U', 'V', 'W', 'X', 'Y', 'Z'+	};++	num %= sizeof(xdig) * sizeof(xdig) * sizeof(xdig);+	p[0] = xdig[(num / (sizeof(xdig) * sizeof(xdig)))];+	num %= sizeof(xdig) * sizeof(xdig);+	p[1] = xdig[ (num / sizeof(xdig))];+	num %= sizeof(xdig);+	p[2] = xdig[num];+}++static void+idr_set_num_beutf16(unsigned char *p, int num)+{+	static const uint16_t xdig[] = {+		0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,+		0x0036, 0x0037, 0x0038, 0x0039,+		0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046,+		0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C,+		0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052,+		0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,+		0x0059, 0x005A+	};+#define XDIG_CNT	(sizeof(xdig)/sizeof(xdig[0]))++	num %= XDIG_CNT * XDIG_CNT * XDIG_CNT;+	archive_be16enc(p, xdig[(num / (XDIG_CNT * XDIG_CNT))]);+	num %= XDIG_CNT * XDIG_CNT;+	archive_be16enc(p+2, xdig[ (num / XDIG_CNT)]);+	num %= XDIG_CNT;+	archive_be16enc(p+4, xdig[num]);+}++/*+ * Generate ISO9660 Identifier.+ */+static int+isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent,+    struct idr *idr)+{+	struct iso9660 *iso9660;+	struct isoent *np;+	char *p;+	int l, r;+	const char *char_map;+	char allow_ldots, allow_multidot, allow_period, allow_vernum;+	int fnmax, ffmax, dnmax;+	static const struct archive_rb_tree_ops rb_ops = {+		isoent_cmp_node_iso9660, isoent_cmp_key_iso9660+	};++	if (isoent->children.cnt == 0)+		return (0);++	iso9660 = a->format_data;+	char_map = idr->char_map;+	if (iso9660->opt.iso_level <= 3) {+		allow_ldots = 0;+		allow_multidot = 0;+		allow_period = 1;+		allow_vernum = iso9660->opt.allow_vernum;+		if (iso9660->opt.iso_level == 1) {+			fnmax = 8;+			ffmax = 12;/* fnmax + '.' + 3 */+			dnmax = 8;+		} else {+			fnmax = 30;+			ffmax = 31;+			dnmax = 31;+		}+	} else {+		allow_ldots = allow_multidot = 1;+		allow_period = allow_vernum = 0;+		if (iso9660->opt.rr)+			/*+			 * MDR : The maximum size of Directory Record(254).+			 * DRL : A Directory Record Length(33).+			 * CE  : A size of SUSP CE System Use Entry(28).+			 * MDR - DRL - CE = 254 - 33 - 28 = 193.+			 */+			fnmax = ffmax = dnmax = 193;+		else+			/*+			 * XA  : CD-ROM XA System Use Extension+			 *       Information(14).+			 * MDR - DRL - XA = 254 - 33 -14 = 207.+			 */+			fnmax = ffmax = dnmax = 207;+	}++	r = idr_start(a, idr, isoent->children.cnt, ffmax, 3, 1, &rb_ops);+	if (r < 0)+		return (r);++	for (np = isoent->children.first; np != NULL; np = np->chnext) {+		char *dot, *xdot;+		int ext_off, noff, weight;++		l = (int)np->file->basename.length;+		p = malloc(l+31+2+1);+		if (p == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory");+			return (ARCHIVE_FATAL);+		}+		memcpy(p, np->file->basename.s, l);+		p[l] = '\0';+		np->identifier = p;++		dot = xdot = NULL;+		if (!allow_ldots) {+			/*+			 * If there is a '.' character at the first byte,+			 * it has to be replaced by '_' character.+			 */+			if (*p == '.')+				*p++ = '_';+		}+		for (;*p; p++) {+			if (*p & 0x80) {+				*p = '_';+				continue;+			}+			if (char_map[(unsigned char)*p]) {+				/* if iso-level is '4', a character '.' is+				 * allowed by char_map. */+				if (*p == '.') {+					xdot = dot;+					dot = p;+				}+				continue;+			}+			if (*p >= 'a' && *p <= 'z') {+				*p -= 'a' - 'A';+				continue;+			}+			if (*p == '.') {+				xdot = dot;+				dot = p;+				if (allow_multidot)+					continue;+			}+			*p = '_';+		}+		p = np->identifier;+		weight = -1;+		if (dot == NULL) {+			int nammax;++			if (np->dir)+				nammax = dnmax;+			else+				nammax = fnmax;++			if (l > nammax) {+				p[nammax] = '\0';+				weight = nammax;+				ext_off = nammax;+			} else+				ext_off = l;+		} else {+			*dot = '.';+			ext_off = (int)(dot - p);++			if (iso9660->opt.iso_level == 1) {+				if (dot - p <= 8) {+					if (strlen(dot) > 4) {+						/* A length of a file extension+						 * must be less than 4 */+						dot[4] = '\0';+						weight = 0;+					}+				} else {+					p[8] = dot[0];+					p[9] = dot[1];+					p[10] = dot[2];+					p[11] = dot[3];+					p[12] = '\0';+					weight = 8;+					ext_off = 8;+				}+			} else if (np->dir) {+				if (l > dnmax) {+					p[dnmax] = '\0';+					weight = dnmax;+					if (ext_off > dnmax)+						ext_off = dnmax;+				}+			} else if (l > ffmax) {+				int extlen = (int)strlen(dot);+				int xdoff;++				if (xdot != NULL)+					xdoff = (int)(xdot - p);+				else+					xdoff = 0;++				if (extlen > 1 && xdoff < fnmax-1) {+					int off;++					if (extlen > ffmax)+						extlen = ffmax;+					off = ffmax - extlen;+					if (off == 0) {+						/* A dot('.')  character+						 * doesn't place to the first+						 * byte of identifier. */+						off ++;+						extlen --;+					}+					memmove(p+off, dot, extlen);+					p[ffmax] = '\0';+					ext_off = off;+					weight = off;+#ifdef COMPAT_MKISOFS+				} else if (xdoff >= fnmax-1) {+					/* Simulate a bug(?) of mkisofs. */+					p[fnmax-1] = '\0';+					ext_off = fnmax-1;+					weight = fnmax-1;+#endif+				} else {+					p[fnmax] = '\0';+					ext_off = fnmax;+					weight = fnmax;+				}+			}+		}+		/* Save an offset of a file name extension to sort files. */+		np->ext_off = ext_off;+		np->ext_len = (int)strlen(&p[ext_off]);+		np->id_len = l = ext_off + np->ext_len;++		/* Make an offset of the number which is used to be set+		 * hexadecimal number to avoid duplicate identifier. */+		if (iso9660->opt.iso_level == 1) {+			if (ext_off >= 5)+				noff = 5;+			else+				noff = ext_off;+		} else {+			if (l == ffmax)+				noff = ext_off - 3;+			else if (l == ffmax-1)+				noff = ext_off - 2;+			else if (l == ffmax-2)+				noff = ext_off - 1;+			else+				noff = ext_off;+		}+		/* Register entry to the identifier resolver. */+		idr_register(idr, np, weight, noff);+	}++	/* Resolve duplicate identifier. */+	idr_resolve(idr, idr_set_num);++	/* Add a period and a version number to identifiers. */+	for (np = isoent->children.first; np != NULL; np = np->chnext) {+		if (!np->dir && np->rr_child == NULL) {+			p = np->identifier + np->ext_off + np->ext_len;+			if (np->ext_len == 0 && allow_period) {+				*p++ = '.';+				np->ext_len = 1;+			}+			if (np->ext_len == 1 && !allow_period) {+				*--p = '\0';+				np->ext_len = 0;+			}+			np->id_len = np->ext_off + np->ext_len;+			if (allow_vernum) {+				*p++ = ';';+				*p++ = '1';+				np->id_len += 2;+			}+			*p = '\0';+		} else+			np->id_len = np->ext_off + np->ext_len;+		np->mb_len = np->id_len;+	}+	return (ARCHIVE_OK);+}++/*+ * Generate Joliet Identifier.+ */+static int+isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,+    struct idr *idr)+{+	struct iso9660 *iso9660;+	struct isoent *np;+	unsigned char *p;+	size_t l;+	int r;+	size_t ffmax, parent_len;+	static const struct archive_rb_tree_ops rb_ops = {+		isoent_cmp_node_joliet, isoent_cmp_key_joliet+	};++	if (isoent->children.cnt == 0)+		return (0);++	iso9660 = a->format_data;+	if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)+		ffmax = 206;+	else+		ffmax = 128;++	r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops);+	if (r < 0)+		return (r);++	parent_len = 1;+	for (np = isoent; np->parent != np; np = np->parent)+		parent_len += np->mb_len + 1;++	for (np = isoent->children.first; np != NULL; np = np->chnext) {+		unsigned char *dot;+		int ext_off, noff, weight;+		size_t lt;++		if ((l = np->file->basename_utf16.length) > ffmax)+			l = ffmax;++		p = malloc((l+1)*2);+		if (p == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory");+			return (ARCHIVE_FATAL);+		}+		memcpy(p, np->file->basename_utf16.s, l);+		p[l] = 0;+		p[l+1] = 0;++		np->identifier = (char *)p;+		lt = l;+		dot = p + l;+		weight = 0;+		while (lt > 0) {+			if (!joliet_allowed_char(p[0], p[1]))+				archive_be16enc(p, 0x005F); /* '_' */+			else if (p[0] == 0 && p[1] == 0x2E) /* '.' */+				dot = p;+			p += 2;+			lt -= 2;+		}+		ext_off = (int)(dot - (unsigned char *)np->identifier);+		np->ext_off = ext_off;+		np->ext_len = (int)l - ext_off;+		np->id_len = (int)l;++		/*+		 * Get a length of MBS of a full-pathname.+		 */+		if (np->file->basename_utf16.length > ffmax) {+			if (archive_strncpy_l(&iso9660->mbs,+			    (const char *)np->identifier, l,+				iso9660->sconv_from_utf16be) != 0 &&+			    errno == ENOMEM) {+				archive_set_error(&a->archive, errno,+				    "No memory");+				return (ARCHIVE_FATAL);+			}+			np->mb_len = (int)iso9660->mbs.length;+			if (np->mb_len != (int)np->file->basename.length)+				weight = np->mb_len;+		} else+			np->mb_len = (int)np->file->basename.length;++		/* If a length of full-pathname is longer than 240 bytes,+		 * it violates Joliet extensions regulation. */+		if (parent_len > 240+		    || np->mb_len > 240+		    || parent_len + np->mb_len > 240) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "The regulation of Joliet extensions;"+			    " A length of a full-pathname of `%s' is "+			    "longer than 240 bytes, (p=%d, b=%d)",+			    archive_entry_pathname(np->file->entry),+			    (int)parent_len, (int)np->mb_len);+			return (ARCHIVE_FATAL);+		}++		/* Make an offset of the number which is used to be set+		 * hexadecimal number to avoid duplicate identifier. */+		if (l == ffmax)+			noff = ext_off - 6;+		else if (l == ffmax-2)+			noff = ext_off - 4;+		else if (l == ffmax-4)+			noff = ext_off - 2;+		else+			noff = ext_off;+		/* Register entry to the identifier resolver. */+		idr_register(idr, np, weight, noff);+	}++	/* Resolve duplicate identifier with Joliet Volume. */+	idr_resolve(idr, idr_set_num_beutf16);++	return (ARCHIVE_OK);+}++/*+ * This comparing rule is according to ISO9660 Standard 9.3+ */+static int+isoent_cmp_iso9660_identifier(const struct isoent *p1, const struct isoent *p2)+{+	const char *s1, *s2;+	int cmp;+	int l;++	s1 = p1->identifier;+	s2 = p2->identifier;++	/* Compare File Name */+	l = p1->ext_off;+	if (l > p2->ext_off)+		l = p2->ext_off;+	cmp = memcmp(s1, s2, l);+	if (cmp != 0)+		return (cmp);+	if (p1->ext_off < p2->ext_off) {+		s2 += l;+		l = p2->ext_off - p1->ext_off;+		while (l--)+			if (0x20 != *s2++)+				return (0x20+				    - *(const unsigned char *)(s2 - 1));+	} else if (p1->ext_off > p2->ext_off) {+		s1 += l;+		l = p1->ext_off - p2->ext_off;+		while (l--)+			if (0x20 != *s1++)+				return (*(const unsigned char *)(s1 - 1)+				    - 0x20);+	}+	/* Compare File Name Extension */+	if (p1->ext_len == 0 && p2->ext_len == 0)+		return (0);+	if (p1->ext_len == 1 && p2->ext_len == 1)+		return (0);+	if (p1->ext_len <= 1)+		return (-1);+	if (p2->ext_len <= 1)+		return (1);+	l = p1->ext_len;+	if (l > p2->ext_len)+		l = p2->ext_len;+	s1 = p1->identifier + p1->ext_off;+	s2 = p2->identifier + p2->ext_off;+	if (l > 1) {+		cmp = memcmp(s1, s2, l);+		if (cmp != 0)+			return (cmp);+	}+	if (p1->ext_len < p2->ext_len) {+		s2 += l;+		l = p2->ext_len - p1->ext_len;+		while (l--)+			if (0x20 != *s2++)+				return (0x20+				    - *(const unsigned char *)(s2 - 1));+	} else if (p1->ext_len > p2->ext_len) {+		s1 += l;+		l = p1->ext_len - p2->ext_len;+		while (l--)+			if (0x20 != *s1++)+				return (*(const unsigned char *)(s1 - 1)+				    - 0x20);+	}+	/* Compare File Version Number */+	/* No operation. The File Version Number is always one. */++	return (cmp);+}++static int+isoent_cmp_node_iso9660(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	const struct idrent *e1 = (const struct idrent *)n1;+	const struct idrent *e2 = (const struct idrent *)n2;++	return (isoent_cmp_iso9660_identifier(e2->isoent, e1->isoent));+}++static int+isoent_cmp_key_iso9660(const struct archive_rb_node *node, const void *key)+{+	const struct isoent *isoent = (const struct isoent *)key;+	const struct idrent *idrent = (const struct idrent *)node;++	return (isoent_cmp_iso9660_identifier(isoent, idrent->isoent));+}++static int+isoent_cmp_joliet_identifier(const struct isoent *p1, const struct isoent *p2)+{+	const unsigned char *s1, *s2;+	int cmp;+	int l;++	s1 = (const unsigned char *)p1->identifier;+	s2 = (const unsigned char *)p2->identifier;++	/* Compare File Name */+	l = p1->ext_off;+	if (l > p2->ext_off)+		l = p2->ext_off;+	cmp = memcmp(s1, s2, l);+	if (cmp != 0)+		return (cmp);+	if (p1->ext_off < p2->ext_off) {+		s2 += l;+		l = p2->ext_off - p1->ext_off;+		while (l--)+			if (0 != *s2++)+				return (- *(const unsigned char *)(s2 - 1));+	} else if (p1->ext_off > p2->ext_off) {+		s1 += l;+		l = p1->ext_off - p2->ext_off;+		while (l--)+			if (0 != *s1++)+				return (*(const unsigned char *)(s1 - 1));+	}+	/* Compare File Name Extension */+	if (p1->ext_len == 0 && p2->ext_len == 0)+		return (0);+	if (p1->ext_len == 2 && p2->ext_len == 2)+		return (0);+	if (p1->ext_len <= 2)+		return (-1);+	if (p2->ext_len <= 2)+		return (1);+	l = p1->ext_len;+	if (l > p2->ext_len)+		l = p2->ext_len;+	s1 = (unsigned char *)(p1->identifier + p1->ext_off);+	s2 = (unsigned char *)(p2->identifier + p2->ext_off);+	if (l > 1) {+		cmp = memcmp(s1, s2, l);+		if (cmp != 0)+			return (cmp);+	}+	if (p1->ext_len < p2->ext_len) {+		s2 += l;+		l = p2->ext_len - p1->ext_len;+		while (l--)+			if (0 != *s2++)+				return (- *(const unsigned char *)(s2 - 1));+	} else if (p1->ext_len > p2->ext_len) {+		s1 += l;+		l = p1->ext_len - p2->ext_len;+		while (l--)+			if (0 != *s1++)+				return (*(const unsigned char *)(s1 - 1));+	}+	/* Compare File Version Number */+	/* No operation. The File Version Number is always one. */++	return (cmp);+}++static int+isoent_cmp_node_joliet(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	const struct idrent *e1 = (const struct idrent *)n1;+	const struct idrent *e2 = (const struct idrent *)n2;++	return (isoent_cmp_joliet_identifier(e2->isoent, e1->isoent));+}++static int+isoent_cmp_key_joliet(const struct archive_rb_node *node, const void *key)+{+	const struct isoent *isoent = (const struct isoent *)key;+	const struct idrent *idrent = (const struct idrent *)node;++	return (isoent_cmp_joliet_identifier(isoent, idrent->isoent));+}++static int+isoent_make_sorted_files(struct archive_write *a, struct isoent *isoent,+    struct idr *idr)+{+	struct archive_rb_node *rn;+	struct isoent **children;++	children = malloc(isoent->children.cnt * sizeof(struct isoent *));+	if (children == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	isoent->children_sorted = children;++	ARCHIVE_RB_TREE_FOREACH(rn, &(idr->rbtree)) {+		struct idrent *idrent = (struct idrent *)rn;+		*children ++ = idrent->isoent;+	}+	return (ARCHIVE_OK);+}++/*+ * - Generate ISO9660 and Joliet identifiers from basenames.+ * - Sort files by each directory.+ */+static int+isoent_traverse_tree(struct archive_write *a, struct vdd* vdd)+{+	struct iso9660 *iso9660 = a->format_data;+	struct isoent *np;+	struct idr idr;+	int depth;+	int r;+	int (*genid)(struct archive_write *, struct isoent *, struct idr *);++	idr_init(iso9660, vdd, &idr);+	np = vdd->rootent;+	depth = 0;+	if (vdd->vdd_type == VDD_JOLIET)+		genid = isoent_gen_joliet_identifier;+	else+		genid = isoent_gen_iso9660_identifier;+	do {+		if (np->virtual &&+		    !archive_entry_mtime_is_set(np->file->entry)) {+			/* Set properly times to virtual directory */+			archive_entry_set_mtime(np->file->entry,+			    iso9660->birth_time, 0);+			archive_entry_set_atime(np->file->entry,+			    iso9660->birth_time, 0);+			archive_entry_set_ctime(np->file->entry,+			    iso9660->birth_time, 0);+		}+		if (np->children.first != NULL) {+			if (vdd->vdd_type != VDD_JOLIET &&+			    !iso9660->opt.rr && depth + 1 >= vdd->max_depth) {+				if (np->children.cnt > 0)+					iso9660->directories_too_deep = np;+			} else {+				/* Generate Identifier */+				r = genid(a, np, &idr);+				if (r < 0)+					goto exit_traverse_tree;+				r = isoent_make_sorted_files(a, np, &idr);+				if (r < 0)+					goto exit_traverse_tree;++				if (np->subdirs.first != NULL &&+				    depth + 1 < vdd->max_depth) {+					/* Enter to sub directories. */+					np = np->subdirs.first;+					depth++;+					continue;+				}+			}+		}+		while (np != np->parent) {+			if (np->drnext == NULL) {+				/* Return to the parent directory. */+				np = np->parent;+				depth--;+			} else {+				np = np->drnext;+				break;+			}+		}+	} while (np != np->parent);++	r = ARCHIVE_OK;+exit_traverse_tree:+	idr_cleanup(&idr);++	return (r);+}++/*+ * Collect directory entries into path_table by a directory depth.+ */+static int+isoent_collect_dirs(struct vdd *vdd, struct isoent *rootent, int depth)+{+	struct isoent *np;++	if (rootent == NULL)+		rootent = vdd->rootent;+	np = rootent;+	do {+		/* Register current directory to pathtable. */+		path_table_add_entry(&(vdd->pathtbl[depth]), np);++		if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {+			/* Enter to sub directories. */+			np = np->subdirs.first;+			depth++;+			continue;+		}+		while (np != rootent) {+			if (np->drnext == NULL) {+				/* Return to the parent directory. */+				np = np->parent;+				depth--;+			} else {+				np = np->drnext;+				break;+			}+		}+	} while (np != rootent);++	return (ARCHIVE_OK);+}++/*+ * The entry whose number of levels in a directory hierarchy is+ * large than eight relocate to rr_move directory.+ */+static int+isoent_rr_move_dir(struct archive_write *a, struct isoent **rr_moved,+    struct isoent *curent, struct isoent **newent)+{+	struct iso9660 *iso9660 = a->format_data;+	struct isoent *rrmoved, *mvent, *np;++	if ((rrmoved = *rr_moved) == NULL) {+		struct isoent *rootent = iso9660->primary.rootent;+		/* There isn't rr_move entry.+		 * Create rr_move entry and insert it into the root entry.+		 */+		rrmoved = isoent_create_virtual_dir(a, iso9660, "rr_moved");+		if (rrmoved == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory");+			return (ARCHIVE_FATAL);+		}+		/* Add "rr_moved" entry to the root entry. */+		isoent_add_child_head(rootent, rrmoved);+		archive_entry_set_nlink(rootent->file->entry,+		    archive_entry_nlink(rootent->file->entry) + 1);+		/* Register "rr_moved" entry to second level pathtable. */+		path_table_add_entry(&(iso9660->primary.pathtbl[1]), rrmoved);+		/* Save rr_moved. */+		*rr_moved = rrmoved;+	}+	/*+	 * Make a clone of curent which is going to be relocated+	 * to rr_moved.+	 */+	mvent = isoent_clone(curent);+	if (mvent == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	/* linking..  and use for creating "CL", "PL" and "RE" */+	mvent->rr_parent = curent->parent;+	curent->rr_child = mvent;+	/*+	 * Move subdirectories from the curent to mvent+	 */+	if (curent->children.first != NULL) {+		*mvent->children.last = curent->children.first;+		mvent->children.last = curent->children.last;+	}+	for (np = mvent->children.first; np != NULL; np = np->chnext)+		np->parent = mvent;+	mvent->children.cnt = curent->children.cnt;+	curent->children.cnt = 0;+	curent->children.first = NULL;+	curent->children.last = &curent->children.first;++	if (curent->subdirs.first != NULL) {+		*mvent->subdirs.last = curent->subdirs.first;+		mvent->subdirs.last = curent->subdirs.last;+	}+	mvent->subdirs.cnt = curent->subdirs.cnt;+	curent->subdirs.cnt = 0;+	curent->subdirs.first = NULL;+	curent->subdirs.last = &curent->subdirs.first;++	/*+	 * The mvent becomes a child of the rr_moved entry.+	 */+	isoent_add_child_tail(rrmoved, mvent);+	archive_entry_set_nlink(rrmoved->file->entry,+	    archive_entry_nlink(rrmoved->file->entry) + 1);+	/*+	 * This entry which relocated to the rr_moved directory+	 * has to set the flag as a file.+	 * See also RRIP 4.1.5.1 Description of the "CL" System Use Entry.+	 */+	curent->dir = 0;++	*newent = mvent;++	return (ARCHIVE_OK);+}++static int+isoent_rr_move(struct archive_write *a)+{+	struct iso9660 *iso9660 = a->format_data;+	struct path_table *pt;+	struct isoent *rootent, *rr_moved;+	struct isoent *np, *last;+	int r;++	pt = &(iso9660->primary.pathtbl[MAX_DEPTH-1]);+	/* There aren't level 8 directories reaching a deeper level. */+	if (pt->cnt == 0)+		return (ARCHIVE_OK);++	rootent = iso9660->primary.rootent;+	/* If "rr_moved" directory is already existing,+	 * we have to use it. */+	rr_moved = isoent_find_child(rootent, "rr_moved");+	if (rr_moved != NULL &&+	    rr_moved != rootent->children.first) {+		/*+		 * It's necessary that rr_move is the first entry+		 * of the root.+		 */+		/* Remove "rr_moved" entry from children chain. */+		isoent_remove_child(rootent, rr_moved);++		/* Add "rr_moved" entry into the head of children chain. */+		isoent_add_child_head(rootent, rr_moved);+	}++	/*+	 * Check level 8 path_table.+	 * If find out sub directory entries, that entries move to rr_move.+	 */+	np = pt->first;+	while (np != NULL) {+		last = path_table_last_entry(pt);+		for (; np != NULL; np = np->ptnext) {+			struct isoent *mvent;+			struct isoent *newent;++			if (!np->dir)+				continue;+			for (mvent = np->subdirs.first;+			    mvent != NULL; mvent = mvent->drnext) {+				r = isoent_rr_move_dir(a, &rr_moved,+				    mvent, &newent);+				if (r < 0)+					return (r);+				isoent_collect_dirs(&(iso9660->primary),+				    newent, 2);+			}+		}+		/* If new entries are added to level 8 path_talbe,+		 * its sub directory entries move to rr_move too.+		 */+		np = last->ptnext;+	}++	return (ARCHIVE_OK);+}++/*+ * This comparing rule is according to ISO9660 Standard 6.9.1+ */+static int+_compare_path_table(const void *v1, const void *v2)+{+	const struct isoent *p1, *p2;+	const char *s1, *s2;+	int cmp, l;++	p1 = *((const struct isoent **)(uintptr_t)v1);+	p2 = *((const struct isoent **)(uintptr_t)v2);++	/* Compare parent directory number */+	cmp = p1->parent->dir_number - p2->parent->dir_number;+	if (cmp != 0)+		return (cmp);++	/* Compare identifier */+	s1 = p1->identifier;+	s2 = p2->identifier;+	l = p1->ext_off;+	if (l > p2->ext_off)+		l = p2->ext_off;+	cmp = strncmp(s1, s2, l);+	if (cmp != 0)+		return (cmp);+	if (p1->ext_off < p2->ext_off) {+		s2 += l;+		l = p2->ext_off - p1->ext_off;+		while (l--)+			if (0x20 != *s2++)+				return (0x20+				    - *(const unsigned char *)(s2 - 1));+	} else if (p1->ext_off > p2->ext_off) {+		s1 += l;+		l = p1->ext_off - p2->ext_off;+		while (l--)+			if (0x20 != *s1++)+				return (*(const unsigned char *)(s1 - 1)+				    - 0x20);+	}+	return (0);+}++static int+_compare_path_table_joliet(const void *v1, const void *v2)+{+	const struct isoent *p1, *p2;+	const unsigned char *s1, *s2;+	int cmp, l;++	p1 = *((const struct isoent **)(uintptr_t)v1);+	p2 = *((const struct isoent **)(uintptr_t)v2);++	/* Compare parent directory number */+	cmp = p1->parent->dir_number - p2->parent->dir_number;+	if (cmp != 0)+		return (cmp);++	/* Compare identifier */+	s1 = (const unsigned char *)p1->identifier;+	s2 = (const unsigned char *)p2->identifier;+	l = p1->ext_off;+	if (l > p2->ext_off)+		l = p2->ext_off;+	cmp = memcmp(s1, s2, l);+	if (cmp != 0)+		return (cmp);+	if (p1->ext_off < p2->ext_off) {+		s2 += l;+		l = p2->ext_off - p1->ext_off;+		while (l--)+			if (0 != *s2++)+				return (- *(const unsigned char *)(s2 - 1));+	} else if (p1->ext_off > p2->ext_off) {+		s1 += l;+		l = p1->ext_off - p2->ext_off;+		while (l--)+			if (0 != *s1++)+				return (*(const unsigned char *)(s1 - 1));+	}+	return (0);+}++static inline void+path_table_add_entry(struct path_table *pathtbl, struct isoent *ent)+{+	ent->ptnext = NULL;+	*pathtbl->last = ent;+	pathtbl->last = &(ent->ptnext);+	pathtbl->cnt ++;+}++static inline struct isoent *+path_table_last_entry(struct path_table *pathtbl)+{+	if (pathtbl->first == NULL)+		return (NULL);+	return (((struct isoent *)(void *)+		((char *)(pathtbl->last) - offsetof(struct isoent, ptnext))));+}++/*+ * Sort directory entries in path_table+ * and assign directory number to each entries.+ */+static int+isoent_make_path_table_2(struct archive_write *a, struct vdd *vdd,+    int depth, int *dir_number)+{+	struct isoent *np;+	struct isoent **enttbl;+	struct path_table *pt;+	int i;++	pt = &vdd->pathtbl[depth];+	if (pt->cnt == 0) {+		pt->sorted = NULL;+		return (ARCHIVE_OK);+	}+	enttbl = malloc(pt->cnt * sizeof(struct isoent *));+	if (enttbl == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	pt->sorted = enttbl;+	for (np = pt->first; np != NULL; np = np->ptnext)+		*enttbl ++ = np;+	enttbl = pt->sorted;++	switch (vdd->vdd_type) {+	case VDD_PRIMARY:+	case VDD_ENHANCED:+#ifdef __COMPAR_FN_T+		qsort(enttbl, pt->cnt, sizeof(struct isoent *),+		    (__compar_fn_t)_compare_path_table);+#else+		qsort(enttbl, pt->cnt, sizeof(struct isoent *),+		    _compare_path_table);+#endif+		break;+	case VDD_JOLIET:+#ifdef __COMPAR_FN_T+		qsort(enttbl, pt->cnt, sizeof(struct isoent *),+		    (__compar_fn_t)_compare_path_table_joliet);+#else+		qsort(enttbl, pt->cnt, sizeof(struct isoent *),+		    _compare_path_table_joliet);+#endif+		break;+	}+	for (i = 0; i < pt->cnt; i++)+		enttbl[i]->dir_number = (*dir_number)++;++	return (ARCHIVE_OK);+}++static int+isoent_alloc_path_table(struct archive_write *a, struct vdd *vdd,+    int max_depth)+{+	int i;++	vdd->max_depth = max_depth;+	vdd->pathtbl = malloc(sizeof(*vdd->pathtbl) * vdd->max_depth);+	if (vdd->pathtbl == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	for (i = 0; i < vdd->max_depth; i++) {+		vdd->pathtbl[i].first = NULL;+		vdd->pathtbl[i].last = &(vdd->pathtbl[i].first);+		vdd->pathtbl[i].sorted = NULL;+		vdd->pathtbl[i].cnt = 0;+	}+	return (ARCHIVE_OK);+}++/*+ * Make Path Tables+ */+static int+isoent_make_path_table(struct archive_write *a)+{+	struct iso9660 *iso9660 = a->format_data;+	int depth, r;+	int dir_number;++	/*+	 * Init Path Table.+	 */+	if (iso9660->dircnt_max >= MAX_DEPTH &&+	    (!iso9660->opt.limit_depth || iso9660->opt.iso_level == 4))+		r = isoent_alloc_path_table(a, &(iso9660->primary),+		    iso9660->dircnt_max + 1);+	else+		/* The number of levels in the hierarchy cannot exceed+		 * eight. */+		r = isoent_alloc_path_table(a, &(iso9660->primary),+		    MAX_DEPTH);+	if (r < 0)+		return (r);+	if (iso9660->opt.joliet) {+		r = isoent_alloc_path_table(a, &(iso9660->joliet),+		    iso9660->dircnt_max + 1);+		if (r < 0)+			return (r);+	}++	/* Step 0.+	 * - Collect directories for primary and joliet.+	 */+	isoent_collect_dirs(&(iso9660->primary), NULL, 0);+	if (iso9660->opt.joliet)+		isoent_collect_dirs(&(iso9660->joliet), NULL, 0);+	/*+	 * Rockridge; move deeper depth directories to rr_moved.+	 */+	if (iso9660->opt.rr) {+		r = isoent_rr_move(a);+		if (r < 0)+			return (r);+	}++ 	/* Update nlink. */+	isofile_connect_hardlink_files(iso9660);++	/* Step 1.+	 * - Renew a value of the depth of that directories.+	 * - Resolve hardlinks.+ 	 * - Convert pathnames to ISO9660 name or UCS2(joliet).+	 * - Sort files by each directory.+	 */+	r = isoent_traverse_tree(a, &(iso9660->primary));+	if (r < 0)+		return (r);+	if (iso9660->opt.joliet) {+		r = isoent_traverse_tree(a, &(iso9660->joliet));+		if (r < 0)+			return (r);+	}++	/* Step 2.+	 * - Sort directories.+	 * - Assign all directory number.+	 */+	dir_number = 1;+	for (depth = 0; depth < iso9660->primary.max_depth; depth++) {+		r = isoent_make_path_table_2(a, &(iso9660->primary),+		    depth, &dir_number);+		if (r < 0)+			return (r);+	}+	if (iso9660->opt.joliet) {+		dir_number = 1;+		for (depth = 0; depth < iso9660->joliet.max_depth; depth++) {+			r = isoent_make_path_table_2(a, &(iso9660->joliet),+			    depth, &dir_number);+			if (r < 0)+				return (r);+		}+	}+	if (iso9660->opt.limit_dirs && dir_number > 0xffff) {+		/*+		 * Maximum number of directories is 65535(0xffff)+		 * doe to size(16bit) of Parent Directory Number of+		 * the Path Table.+		 * See also ISO9660 Standard 9.4.+		 */+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Too many directories(%d) over 65535.", dir_number);+		return (ARCHIVE_FATAL);+	}++	/* Get the size of the Path Table. */+	calculate_path_table_size(&(iso9660->primary));+	if (iso9660->opt.joliet)+		calculate_path_table_size(&(iso9660->joliet));++	return (ARCHIVE_OK);+}++static int+isoent_find_out_boot_file(struct archive_write *a, struct isoent *rootent)+{+	struct iso9660 *iso9660 = a->format_data;++	/* Find a isoent of the boot file. */+	iso9660->el_torito.boot = isoent_find_entry(rootent,+	    iso9660->el_torito.boot_filename.s);+	if (iso9660->el_torito.boot == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Can't find the boot image file ``%s''",+		    iso9660->el_torito.boot_filename.s);+		return (ARCHIVE_FATAL);+	}+	iso9660->el_torito.boot->file->boot = BOOT_IMAGE;+	return (ARCHIVE_OK);+}++static int+isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent)+{+	struct iso9660 *iso9660 = a->format_data;+	struct isofile *file;+	struct isoent *isoent;+	struct archive_entry *entry;++	(void)rootent; /* UNUSED */+	/*+	 * Create the entry which is the "boot.catalog" file.+	 */+	file = isofile_new(a, NULL);+	if (file == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	archive_entry_set_pathname(file->entry,+	    iso9660->el_torito.catalog_filename.s);+	archive_entry_set_size(file->entry, LOGICAL_BLOCK_SIZE);+	archive_entry_set_mtime(file->entry, iso9660->birth_time, 0);+	archive_entry_set_atime(file->entry, iso9660->birth_time, 0);+	archive_entry_set_ctime(file->entry, iso9660->birth_time, 0);+	archive_entry_set_uid(file->entry, getuid());+	archive_entry_set_gid(file->entry, getgid());+	archive_entry_set_mode(file->entry, AE_IFREG | 0444);+	archive_entry_set_nlink(file->entry, 1);++	if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) {+		isofile_free(file);+		return (ARCHIVE_FATAL);+	}+	file->boot = BOOT_CATALOG;+	file->content.size = LOGICAL_BLOCK_SIZE;+	isofile_add_entry(iso9660, file);++	isoent = isoent_new(file);+	if (isoent == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	isoent->virtual = 1;++	/* Add the "boot.catalog" entry into tree */+	if (isoent_tree(a, &isoent) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	iso9660->el_torito.catalog = isoent;+	/*+	 * Get a boot media type.+	 */+	switch (iso9660->opt.boot_type) {+	default:+	case OPT_BOOT_TYPE_AUTO:+		/* Try detecting a media type of the boot image. */+		entry = iso9660->el_torito.boot->file->entry;+		if (archive_entry_size(entry) == FD_1_2M_SIZE)+			iso9660->el_torito.media_type =+			    BOOT_MEDIA_1_2M_DISKETTE;+		else if (archive_entry_size(entry) == FD_1_44M_SIZE)+			iso9660->el_torito.media_type =+			    BOOT_MEDIA_1_44M_DISKETTE;+		else if (archive_entry_size(entry) == FD_2_88M_SIZE)+			iso9660->el_torito.media_type =+			    BOOT_MEDIA_2_88M_DISKETTE;+		else+			/* We cannot decide whether the boot image is+			 * hard-disk. */+			iso9660->el_torito.media_type =+			    BOOT_MEDIA_NO_EMULATION;+		break;+	case OPT_BOOT_TYPE_NO_EMU:+		iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION;+		break;+	case OPT_BOOT_TYPE_HARD_DISK:+		iso9660->el_torito.media_type = BOOT_MEDIA_HARD_DISK;+		break;+	case OPT_BOOT_TYPE_FD:+		entry = iso9660->el_torito.boot->file->entry;+		if (archive_entry_size(entry) <= FD_1_2M_SIZE)+			iso9660->el_torito.media_type =+			    BOOT_MEDIA_1_2M_DISKETTE;+		else if (archive_entry_size(entry) <= FD_1_44M_SIZE)+			iso9660->el_torito.media_type =+			    BOOT_MEDIA_1_44M_DISKETTE;+		else if (archive_entry_size(entry) <= FD_2_88M_SIZE)+			iso9660->el_torito.media_type =+			    BOOT_MEDIA_2_88M_DISKETTE;+		else {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Boot image file(``%s'') size is too big "+			    "for fd type.",+			    iso9660->el_torito.boot_filename.s);+			return (ARCHIVE_FATAL);+		}+		break;+	}++	/*+	 * Get a system type.+	 * TODO: `El Torito' specification says "A copy of byte 5 from the+	 *       Partition Table found in the boot image".+	 */+	iso9660->el_torito.system_type = 0;++	/*+	 * Get an ID.+	 */+	if (iso9660->opt.publisher)+		archive_string_copy(&(iso9660->el_torito.id),+		    &(iso9660->publisher_identifier));+++	return (ARCHIVE_OK);+}++/*+ * If a media type is floppy, return its image size.+ * otherwise return 0.+ */+static size_t+fd_boot_image_size(int media_type)+{+	switch (media_type) {+	case BOOT_MEDIA_1_2M_DISKETTE:+		return (FD_1_2M_SIZE);+	case BOOT_MEDIA_1_44M_DISKETTE:+		return (FD_1_44M_SIZE);+	case BOOT_MEDIA_2_88M_DISKETTE:+		return (FD_2_88M_SIZE);+	default:+		return (0);+	}+}++/*+ * Make a boot catalog image data.+ */+static int+make_boot_catalog(struct archive_write *a)+{+	struct iso9660 *iso9660 = a->format_data;+	unsigned char *block;+	unsigned char *p;+	uint16_t sum, *wp;++	block = wb_buffptr(a);+	memset(block, 0, LOGICAL_BLOCK_SIZE);+	p = block;+	/*+	 * Validation Entry+	 */+	/* Header ID */+	p[0] = 1;+	/* Platform ID */+	p[1] = iso9660->el_torito.platform_id;+	/* Reserved */+	p[2] = p[3] = 0;+	/* ID */+	if (archive_strlen(&(iso9660->el_torito.id)) > 0)+		strncpy((char *)p+4, iso9660->el_torito.id.s, 23);+	p[27] = 0;+	/* Checksum */+	p[28] = p[29] = 0;+	/* Key */+	p[30] = 0x55;+	p[31] = 0xAA;++	sum = 0;+	wp = (uint16_t *)block;+	while (wp < (uint16_t *)&block[32])+		sum += archive_le16dec(wp++);+	set_num_721(&block[28], (~sum) + 1);++	/*+	 * Initial/Default Entry+	 */+	p = &block[32];+	/* Boot Indicator */+	p[0] = 0x88;+	/* Boot media type */+	p[1] = iso9660->el_torito.media_type;+	/* Load Segment */+	if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION)+		set_num_721(&p[2], iso9660->el_torito.boot_load_seg);+	else+		set_num_721(&p[2], 0);+	/* System Type */+	p[4] = iso9660->el_torito.system_type;+	/* Unused */+	p[5] = 0;+	/* Sector Count */+	if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION)+		set_num_721(&p[6], iso9660->el_torito.boot_load_size);+	else+		set_num_721(&p[6], 1);+	/* Load RBA */+	set_num_731(&p[8],+	    iso9660->el_torito.boot->file->content.location);+	/* Unused */+	memset(&p[12], 0, 20);++	return (wb_consume(a, LOGICAL_BLOCK_SIZE));+}++static int+setup_boot_information(struct archive_write *a)+{+	struct iso9660 *iso9660 = a->format_data;+	struct isoent *np;+	int64_t size;+	uint32_t sum;+	unsigned char buff[4096];++	np = iso9660->el_torito.boot;+	lseek(iso9660->temp_fd,+	    np->file->content.offset_of_temp + 64, SEEK_SET);+	size = archive_entry_size(np->file->entry) - 64;+	if (size <= 0) {+		archive_set_error(&a->archive, errno,+		    "Boot file(%jd) is too small", (intmax_t)size + 64);+		return (ARCHIVE_FATAL);+	}+	sum = 0;+	while (size > 0) {+		size_t rsize;+		ssize_t i, rs;++		if (size > (int64_t)sizeof(buff))+			rsize = sizeof(buff);+		else+			rsize = (size_t)size;++		rs = read(iso9660->temp_fd, buff, rsize);+		if (rs <= 0) {+			archive_set_error(&a->archive, errno,+			    "Can't read temporary file(%jd)",+			    (intmax_t)rs);+			return (ARCHIVE_FATAL);+		}+		for (i = 0; i < rs; i += 4)+			sum += archive_le32dec(buff + i);+		size -= rs;+	}+	/* Set the location of Primary Volume Descriptor. */+	set_num_731(buff, SYSTEM_AREA_BLOCK);+	/* Set the location of the boot file. */+	set_num_731(buff+4, np->file->content.location);+	/* Set the size of the boot file. */+	size = fd_boot_image_size(iso9660->el_torito.media_type);+	if (size == 0)+		size = archive_entry_size(np->file->entry);+	set_num_731(buff+8, (uint32_t)size);+	/* Set the sum of the boot file. */+	set_num_731(buff+12, sum);+	/* Clear reserved bytes. */+	memset(buff+16, 0, 40);++	/* Overwrite the boot file. */+	lseek(iso9660->temp_fd,+	    np->file->content.offset_of_temp + 8, SEEK_SET);+	return (write_to_temp(a, buff, 56));+}++#ifdef HAVE_ZLIB_H++static int+zisofs_init_zstream(struct archive_write *a)+{+	struct iso9660 *iso9660 = a->format_data;+	int r;++	iso9660->zisofs.stream.next_in = NULL;+	iso9660->zisofs.stream.avail_in = 0;+	iso9660->zisofs.stream.total_in = 0;+	iso9660->zisofs.stream.total_out = 0;+	if (iso9660->zisofs.stream_valid)+		r = deflateReset(&(iso9660->zisofs.stream));+	else {+		r = deflateInit(&(iso9660->zisofs.stream),+		    iso9660->zisofs.compression_level);+		iso9660->zisofs.stream_valid = 1;+	}+	switch (r) {+	case Z_OK:+		break;+	default:+	case Z_STREAM_ERROR:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing "+		    "compression library: invalid setup parameter");+		return (ARCHIVE_FATAL);+	case Z_MEM_ERROR:+		archive_set_error(&a->archive, ENOMEM,+		    "Internal error initializing "+		    "compression library");+		return (ARCHIVE_FATAL);+	case Z_VERSION_ERROR:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing "+		    "compression library: invalid library version");+		return (ARCHIVE_FATAL);+	}+	return (ARCHIVE_OK);+}++#endif /* HAVE_ZLIB_H */++static int+zisofs_init(struct archive_write *a,  struct isofile *file)+{+	struct iso9660 *iso9660 = a->format_data;+#ifdef HAVE_ZLIB_H+	uint64_t tsize;+	size_t _ceil, bpsize;+	int r;+#endif++	iso9660->zisofs.detect_magic = 0;+	iso9660->zisofs.making = 0;++	if (!iso9660->opt.rr || !iso9660->opt.zisofs)+		return (ARCHIVE_OK);++	if (archive_entry_size(file->entry) >= 24 &&+	    archive_entry_size(file->entry) < MULTI_EXTENT_SIZE) {+		/* Acceptable file size for zisofs. */+		iso9660->zisofs.detect_magic = 1;+		iso9660->zisofs.magic_cnt = 0;+	}+	if (!iso9660->zisofs.detect_magic)+		return (ARCHIVE_OK);++#ifdef HAVE_ZLIB_H+	/* The number of Logical Blocks which uncompressed data+	 * will use in iso-image file is the same as the number of+	 * Logical Blocks which zisofs(compressed) data will use+	 * in ISO-image file. It won't reduce iso-image file size. */+	if (archive_entry_size(file->entry) <= LOGICAL_BLOCK_SIZE)+		return (ARCHIVE_OK);++	/* Initialize compression library */+	r = zisofs_init_zstream(a);+	if (r != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Mark file->zisofs to create RRIP 'ZF' Use Entry. */+	file->zisofs.header_size = ZF_HEADER_SIZE >> 2;+	file->zisofs.log2_bs = ZF_LOG2_BS;+	file->zisofs.uncompressed_size =+		(uint32_t)archive_entry_size(file->entry);++	/* Calculate a size of Block Pointers of zisofs. */+	_ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1)+		>> file->zisofs.log2_bs;+	iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1;+	iso9660->zisofs.block_pointers_idx = 0;++	/* Ensure a buffer size used for Block Pointers */+	bpsize = iso9660->zisofs.block_pointers_cnt *+	    sizeof(iso9660->zisofs.block_pointers[0]);+	if (iso9660->zisofs.block_pointers_allocated < bpsize) {+		free(iso9660->zisofs.block_pointers);+		iso9660->zisofs.block_pointers = malloc(bpsize);+		if (iso9660->zisofs.block_pointers == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate data");+			return (ARCHIVE_FATAL);+		}+		iso9660->zisofs.block_pointers_allocated = bpsize;+	}++	/*+	 * Skip zisofs header and Block Pointers, which we will write+	 * after all compressed data of a file written to the temporary+	 * file.+	 */+	tsize = ZF_HEADER_SIZE + bpsize;+	if (write_null(a, (size_t)tsize) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/*+	 * Initialize some variables to make zisofs.+	 */+	archive_le32enc(&(iso9660->zisofs.block_pointers[0]),+		(uint32_t)tsize);+	iso9660->zisofs.remaining = file->zisofs.uncompressed_size;+	iso9660->zisofs.making = 1;+	iso9660->zisofs.allzero = 1;+	iso9660->zisofs.block_offset = tsize;+	iso9660->zisofs.total_size = tsize;+	iso9660->cur_file->cur_content->size = tsize;+#endif++	return (ARCHIVE_OK);+}++static void+zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s)+{+	struct iso9660 *iso9660 = a->format_data;+	struct isofile *file = iso9660->cur_file;+	const unsigned char *p, *endp;+	const unsigned char *magic_buff;+	uint32_t uncompressed_size;+	unsigned char header_size;+	unsigned char log2_bs;+	size_t _ceil, doff;+	uint32_t bst, bed;+	int magic_max;+	int64_t entry_size;++	entry_size = archive_entry_size(file->entry);+	if ((int64_t)sizeof(iso9660->zisofs.magic_buffer) > entry_size)+		magic_max = (int)entry_size;+	else+		magic_max = sizeof(iso9660->zisofs.magic_buffer);++	if (iso9660->zisofs.magic_cnt == 0 && s >= (size_t)magic_max)+		/* It's unnecessary we copy buffer. */+		magic_buff = buff;+	else {+		if (iso9660->zisofs.magic_cnt < magic_max) {+			size_t l;++			l = sizeof(iso9660->zisofs.magic_buffer)+			    - iso9660->zisofs.magic_cnt;+			if (l > s)+				l = s;+			memcpy(iso9660->zisofs.magic_buffer+			    + iso9660->zisofs.magic_cnt, buff, l);+			iso9660->zisofs.magic_cnt += (int)l;+			if (iso9660->zisofs.magic_cnt < magic_max)+				return;+		}+		magic_buff = iso9660->zisofs.magic_buffer;+	}+	iso9660->zisofs.detect_magic = 0;+	p = magic_buff;++	/* Check the magic code of zisofs. */+	if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0)+		/* This is not zisofs file which made by mkzftree. */+		return;+	p += sizeof(zisofs_magic);++	/* Read a zisofs header. */+	uncompressed_size = archive_le32dec(p);+	header_size = p[4];+	log2_bs = p[5];+	if (uncompressed_size < 24 || header_size != 4 ||+	    log2_bs > 30 || log2_bs < 7)+		return;/* Invalid or not supported header. */++	/* Calculate a size of Block Pointers of zisofs. */+	_ceil = (uncompressed_size ++	        (ARCHIVE_LITERAL_LL(1) << log2_bs) -1) >> log2_bs;+	doff = (_ceil + 1) * 4 + 16;+	if (entry_size < (int64_t)doff)+		return;/* Invalid data. */++	/* Check every Block Pointer has valid value. */+	p = magic_buff + 16;+	endp = magic_buff + magic_max;+	while (_ceil && p + 8 <= endp) {+		bst = archive_le32dec(p);+		if (bst != doff)+			return;/* Invalid data. */+		p += 4;+		bed = archive_le32dec(p);+		if (bed < bst || bed > entry_size)+			return;/* Invalid data. */+		doff += bed - bst;+		_ceil--;+	}++	file->zisofs.uncompressed_size = uncompressed_size;+	file->zisofs.header_size = header_size;+	file->zisofs.log2_bs = log2_bs;++	/* Disable making a zisofs image. */+	iso9660->zisofs.making = 0;+}++#ifdef HAVE_ZLIB_H++/*+ * Compress data and write it to a temporary file.+ */+static int+zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s)+{+	struct iso9660 *iso9660 = a->format_data;+	struct isofile *file = iso9660->cur_file;+	const unsigned char *b;+	z_stream *zstrm;+	size_t avail, csize;+	int flush, r;++	zstrm = &(iso9660->zisofs.stream);+	zstrm->next_out = wb_buffptr(a);+	zstrm->avail_out = (uInt)wb_remaining(a);+	b = (const unsigned char *)buff;+	do {+		avail = ZF_BLOCK_SIZE - zstrm->total_in;+		if (s < avail) {+			avail = s;+			flush = Z_NO_FLUSH;+		} else+			flush = Z_FINISH;+		iso9660->zisofs.remaining -= avail;+		if (iso9660->zisofs.remaining <= 0)+			flush = Z_FINISH;++		zstrm->next_in = (Bytef *)(uintptr_t)(const void *)b;+		zstrm->avail_in = (uInt)avail;++		/*+		 * Check if current data block are all zero.+		 */+		if (iso9660->zisofs.allzero) {+			const unsigned char *nonzero = b;+			const unsigned char *nonzeroend = b + avail;++			while (nonzero < nonzeroend)+				if (*nonzero++) {+					iso9660->zisofs.allzero = 0;+					break;+				}+		}+		b += avail;+		s -= avail;++		/*+		 * If current data block are all zero, we do not use+		 * compressed data.+		 */+		if (flush == Z_FINISH && iso9660->zisofs.allzero &&+		    avail + zstrm->total_in == ZF_BLOCK_SIZE) {+			if (iso9660->zisofs.block_offset !=+			    file->cur_content->size) {+				int64_t diff;++				r = wb_set_offset(a,+				    file->cur_content->offset_of_temp ++				        iso9660->zisofs.block_offset);+				if (r != ARCHIVE_OK)+					return (r);+				diff = file->cur_content->size -+				    iso9660->zisofs.block_offset;+				file->cur_content->size -= diff;+				iso9660->zisofs.total_size -= diff;+			}+			zstrm->avail_in = 0;+		}++		/*+		 * Compress file data.+		 */+		while (zstrm->avail_in > 0) {+			csize = zstrm->total_out;+			r = deflate(zstrm, flush);+			switch (r) {+			case Z_OK:+			case Z_STREAM_END:+				csize = zstrm->total_out - csize;+				if (wb_consume(a, csize) != ARCHIVE_OK)+					return (ARCHIVE_FATAL);+				iso9660->zisofs.total_size += csize;+				iso9660->cur_file->cur_content->size += csize;+				zstrm->next_out = wb_buffptr(a);+				zstrm->avail_out = (uInt)wb_remaining(a);+				break;+			default:+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "Compression failed:"+				    " deflate() call returned status %d",+				    r);+				return (ARCHIVE_FATAL);+			}+		}++		if (flush == Z_FINISH) {+			/*+			 * Save the information of one zisofs block.+			 */+			iso9660->zisofs.block_pointers_idx ++;+			archive_le32enc(&(iso9660->zisofs.block_pointers[+			    iso9660->zisofs.block_pointers_idx]),+				(uint32_t)iso9660->zisofs.total_size);+			r = zisofs_init_zstream(a);+			if (r != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			iso9660->zisofs.allzero = 1;+			iso9660->zisofs.block_offset = file->cur_content->size;+		}+	} while (s);++	return (ARCHIVE_OK);+}++static int+zisofs_finish_entry(struct archive_write *a)+{+	struct iso9660 *iso9660 = a->format_data;+	struct isofile *file = iso9660->cur_file;+	unsigned char buff[16];+	size_t s;+	int64_t tail;++	/* Direct temp file stream to zisofs temp file stream. */+	archive_entry_set_size(file->entry, iso9660->zisofs.total_size);++	/*+	 * Save a file pointer which points the end of current zisofs data.+	 */+	tail = wb_offset(a);++	/*+	 * Make a header.+	 *+	 * +-----------------+----------------+-----------------++	 * | Header 16 bytes | Block Pointers | Compressed data |+	 * +-----------------+----------------+-----------------++	 * 0                16               +X+	 * Block Pointers :+	 *   4 * (((Uncompressed file size + block_size -1) / block_size) + 1)+	 *+	 * Write zisofs header.+	 *    Magic number+	 * +----+----+----+----+----+----+----+----++	 * | 37 | E4 | 53 | 96 | C9 | DB | D6 | 07 |+	 * +----+----+----+----+----+----+----+----++	 * 0    1    2    3    4    5    6    7    8+	 *+	 * +------------------------+------------------++	 * | Uncompressed file size | header_size >> 2 |+	 * +------------------------+------------------++	 * 8                       12                 13+	 *+	 * +-----------------+----------------++	 * | log2 block_size | Reserved(0000) |+	 * +-----------------+----------------++	 * 13               14               16+	 */+	memcpy(buff, zisofs_magic, 8);+	set_num_731(buff+8, file->zisofs.uncompressed_size);+	buff[12] = file->zisofs.header_size;+	buff[13] = file->zisofs.log2_bs;+	buff[14] = buff[15] = 0;/* Reserved */++	/* Move to the right position to write the header. */+	wb_set_offset(a, file->content.offset_of_temp);++	/* Write the header. */+	if (wb_write_to_temp(a, buff, 16) != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/*+	 * Write zisofs Block Pointers.+	 */+	s = iso9660->zisofs.block_pointers_cnt *+	    sizeof(iso9660->zisofs.block_pointers[0]);+	if (wb_write_to_temp(a, iso9660->zisofs.block_pointers, s)+	    != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Set a file pointer back to the end of the temporary file. */+	wb_set_offset(a, tail);++	return (ARCHIVE_OK);+}++static int+zisofs_free(struct archive_write *a)+{+	struct iso9660 *iso9660 = a->format_data;+	int ret = ARCHIVE_OK;++	free(iso9660->zisofs.block_pointers);+	if (iso9660->zisofs.stream_valid &&+	    deflateEnd(&(iso9660->zisofs.stream)) != Z_OK) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Failed to clean up compressor");+		ret = ARCHIVE_FATAL;+	}+	iso9660->zisofs.block_pointers = NULL;+	iso9660->zisofs.stream_valid = 0;+	return (ret);+}++struct zisofs_extract {+	int		 pz_log2_bs; /* Log2 of block size */+	uint64_t	 pz_uncompressed_size;+	size_t		 uncompressed_buffer_size;++	signed int	 initialized:1;+	signed int	 header_passed:1;++	uint32_t	 pz_offset;+	unsigned char	*block_pointers;+	size_t		 block_pointers_size;+	size_t		 block_pointers_avail;+	size_t		 block_off;+	uint32_t	 block_avail;++	z_stream	 stream;+	int		 stream_valid;+};++static ssize_t+zisofs_extract_init(struct archive_write *a, struct zisofs_extract *zisofs,+    const unsigned char *p, size_t bytes)+{+	size_t avail = bytes;+	size_t _ceil, xsize;++	/* Allocate block pointers buffer. */+	_ceil = (size_t)((zisofs->pz_uncompressed_size ++		(((int64_t)1) << zisofs->pz_log2_bs) - 1)+		>> zisofs->pz_log2_bs);+	xsize = (_ceil + 1) * 4;+	if (zisofs->block_pointers == NULL) {+		size_t alloc = ((xsize >> 10) + 1) << 10;+		zisofs->block_pointers = malloc(alloc);+		if (zisofs->block_pointers == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "No memory for zisofs decompression");+			return (ARCHIVE_FATAL);+		}+	}+	zisofs->block_pointers_size = xsize;++	/* Allocate uncompressed data buffer. */+	zisofs->uncompressed_buffer_size = (size_t)1UL << zisofs->pz_log2_bs;++	/*+	 * Read the file header, and check the magic code of zisofs.+	 */+	if (!zisofs->header_passed) {+		int err = 0;+		if (avail < 16) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Illegal zisofs file body");+			return (ARCHIVE_FATAL);+		}++		if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0)+			err = 1;+		else if (archive_le32dec(p + 8) != zisofs->pz_uncompressed_size)+			err = 1;+		else if (p[12] != 4 || p[13] != zisofs->pz_log2_bs)+			err = 1;+		if (err) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Illegal zisofs file body");+			return (ARCHIVE_FATAL);+		}+		avail -= 16;+		p += 16;+		zisofs->header_passed = 1;+	}++	/*+	 * Read block pointers.+	 */+	if (zisofs->header_passed &&+	    zisofs->block_pointers_avail < zisofs->block_pointers_size) {+		xsize = zisofs->block_pointers_size+		    - zisofs->block_pointers_avail;+		if (avail < xsize)+			xsize = avail;+		memcpy(zisofs->block_pointers+		    + zisofs->block_pointers_avail, p, xsize);+		zisofs->block_pointers_avail += xsize;+		avail -= xsize;+	    	if (zisofs->block_pointers_avail+		    == zisofs->block_pointers_size) {+			/* We've got all block pointers and initialize+			 * related variables.	*/+			zisofs->block_off = 0;+			zisofs->block_avail = 0;+			/* Complete a initialization */+			zisofs->initialized = 1;+		}+	}+	return ((ssize_t)avail);+}++static ssize_t+zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs,+    const unsigned char *p, size_t bytes)+{+	size_t avail;+	int r;++	if (!zisofs->initialized) {+		ssize_t rs = zisofs_extract_init(a, zisofs, p, bytes);+		if (rs < 0)+			return (rs);+		if (!zisofs->initialized) {+			/* We need more data. */+			zisofs->pz_offset += (uint32_t)bytes;+			return (bytes);+		}+		avail = rs;+		p += bytes - avail;+	} else+		avail = bytes;++	/*+	 * Get block offsets from block pointers.+	 */+	if (zisofs->block_avail == 0) {+		uint32_t bst, bed;++		if (zisofs->block_off + 4 >= zisofs->block_pointers_size) {+			/* There isn't a pair of offsets. */+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Illegal zisofs block pointers");+			return (ARCHIVE_FATAL);+		}+		bst = archive_le32dec(+		    zisofs->block_pointers + zisofs->block_off);+		if (bst != zisofs->pz_offset + (bytes - avail)) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Illegal zisofs block pointers(cannot seek)");+			return (ARCHIVE_FATAL);+		}+		bed = archive_le32dec(+		    zisofs->block_pointers + zisofs->block_off + 4);+		if (bed < bst) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Illegal zisofs block pointers");+			return (ARCHIVE_FATAL);+		}+		zisofs->block_avail = bed - bst;+		zisofs->block_off += 4;++		/* Initialize compression library for new block. */+		if (zisofs->stream_valid)+			r = inflateReset(&zisofs->stream);+		else+			r = inflateInit(&zisofs->stream);+		if (r != Z_OK) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Can't initialize zisofs decompression.");+			return (ARCHIVE_FATAL);+		}+		zisofs->stream_valid = 1;+		zisofs->stream.total_in = 0;+		zisofs->stream.total_out = 0;+	}++	/*+	 * Make uncompressed data.+	 */+	if (zisofs->block_avail == 0) {+		/*+		 * It's basically 32K bytes NUL data.+		 */+		unsigned char *wb;+		size_t size, wsize;++		size = zisofs->uncompressed_buffer_size;+		while (size) {+			wb = wb_buffptr(a);+			if (size > wb_remaining(a))+				wsize = wb_remaining(a);+			else+				wsize = size;+			memset(wb, 0, wsize);+			r = wb_consume(a, wsize);+			if (r < 0)+				return (r);+			size -= wsize;+		}+	} else {+		zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p;+		if (avail > zisofs->block_avail)+			zisofs->stream.avail_in = zisofs->block_avail;+		else+			zisofs->stream.avail_in = (uInt)avail;+		zisofs->stream.next_out = wb_buffptr(a);+		zisofs->stream.avail_out = (uInt)wb_remaining(a);++		r = inflate(&zisofs->stream, 0);+		switch (r) {+		case Z_OK: /* Decompressor made some progress.*/+		case Z_STREAM_END: /* Found end of stream. */+			break;+		default:+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "zisofs decompression failed (%d)", r);+			return (ARCHIVE_FATAL);+		}+		avail -= zisofs->stream.next_in - p;+		zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p);+		r = wb_consume(a, wb_remaining(a) - zisofs->stream.avail_out);+		if (r < 0)+			return (r);+	}+	zisofs->pz_offset += (uint32_t)bytes;+	return (bytes - avail);+}++static int+zisofs_rewind_boot_file(struct archive_write *a)+{+	struct iso9660 *iso9660 = a->format_data;+	struct isofile *file;+	unsigned char *rbuff;+	ssize_t r;+	size_t remaining, rbuff_size;+	struct zisofs_extract zext;+	int64_t read_offset, write_offset, new_offset;+	int fd, ret = ARCHIVE_OK;++	file = iso9660->el_torito.boot->file;+	/*+	 * There is nothing to do if this boot file does not have+	 * zisofs header.+	 */+	if (file->zisofs.header_size == 0)+		return (ARCHIVE_OK);++	/*+	 * Uncompress the zisofs'ed file contents.+	 */+	memset(&zext, 0, sizeof(zext));+	zext.pz_uncompressed_size = file->zisofs.uncompressed_size;+	zext.pz_log2_bs = file->zisofs.log2_bs;++	fd = iso9660->temp_fd;+	new_offset = wb_offset(a);+	read_offset = file->content.offset_of_temp;+	remaining = (size_t)file->content.size;+	if (remaining > 1024 * 32)+		rbuff_size = 1024 * 32;+	else+		rbuff_size = remaining;++	rbuff = malloc(rbuff_size);+	if (rbuff == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	while (remaining) {+		size_t rsize;+		ssize_t rs;++		/* Get the current file pointer. */+		write_offset = lseek(fd, 0, SEEK_CUR);++		/* Change the file pointer to read. */+		lseek(fd, read_offset, SEEK_SET);++		rsize = rbuff_size;+		if (rsize > remaining)+			rsize = remaining;+		rs = read(iso9660->temp_fd, rbuff, rsize);+		if (rs <= 0) {+			archive_set_error(&a->archive, errno,+			    "Can't read temporary file(%jd)", (intmax_t)rs);+			ret = ARCHIVE_FATAL;+			break;+		}+		remaining -= rs;+		read_offset += rs;++		/* Put the file pointer back to write. */+		lseek(fd, write_offset, SEEK_SET);++		r = zisofs_extract(a, &zext, rbuff, rs);+		if (r < 0) {+			ret = (int)r;+			break;+		}+	}++	if (ret == ARCHIVE_OK) {+		/*+		 * Change the boot file content from zisofs'ed data+		 * to plain data.+		 */+		file->content.offset_of_temp = new_offset;+		file->content.size = file->zisofs.uncompressed_size;+		archive_entry_set_size(file->entry, file->content.size);+		/* Set to be no zisofs. */+		file->zisofs.header_size = 0;+		file->zisofs.log2_bs = 0;+		file->zisofs.uncompressed_size = 0;+		r = wb_write_padding_to_temp(a, file->content.size);+		if (r < 0)+			ret = ARCHIVE_FATAL;+	}++	/*+	 * Free the resource we used in this function only.+	 */+	free(rbuff);+	free(zext.block_pointers);+	if (zext.stream_valid && inflateEnd(&(zext.stream)) != Z_OK) {+        	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Failed to clean up compressor");+		ret = ARCHIVE_FATAL;+	}++	return (ret);+}++#else++static int+zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s)+{+	(void)buff; /* UNUSED */+	(void)s; /* UNUSED */+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Programming error");+	return (ARCHIVE_FATAL);+}++static int+zisofs_rewind_boot_file(struct archive_write *a)+{+	struct iso9660 *iso9660 = a->format_data;++	if (iso9660->el_torito.boot->file->zisofs.header_size != 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "We cannot extract the zisofs imaged boot file;"+		    " this may not boot in being zisofs imaged");+		return (ARCHIVE_FAILED);+	}+	return (ARCHIVE_OK);+}++static int+zisofs_finish_entry(struct archive_write *a)+{+	(void)a; /* UNUSED */+	return (ARCHIVE_OK);+}++static int+zisofs_free(struct archive_write *a)+{+	(void)a; /* UNUSED */+	return (ARCHIVE_OK);+}++#endif /* HAVE_ZLIB_H */+
+ c/archive_write_set_format_mtree.c view
@@ -0,0 +1,2228 @@+/*-+ * Copyright (c) 2008 Joerg Sonnenberger+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_mtree.c 201171 2009-12-29 06:39:07Z kientzle $");++#ifdef HAVE_SYS_TYPES_H+#include <sys/types.h>+#endif+#include <errno.h>+#include <stdlib.h>+#include <string.h>++#include "archive.h"+#include "archive_digest_private.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_rb.h"+#include "archive_string.h"+#include "archive_write_private.h"++#define INDENTNAMELEN	15+#define MAXLINELEN	80+#define SET_KEYS	\+	(F_FLAGS | F_GID | F_GNAME | F_MODE | F_TYPE | F_UID | F_UNAME)++struct attr_counter {+	struct attr_counter *prev;+	struct attr_counter *next;+	struct mtree_entry *m_entry;+	int count;+};++struct att_counter_set {+	struct attr_counter *uid_list;+	struct attr_counter *gid_list;+	struct attr_counter *mode_list;+	struct attr_counter *flags_list;+};++struct mtree_chain {+	struct mtree_entry *first;+	struct mtree_entry **last;+};++/*+ * The Data only for a directory file.+ */+struct dir_info {+	struct archive_rb_tree rbtree;+	struct mtree_chain children;+	struct mtree_entry *chnext;+	int virtual;+};++/*+ * The Data only for a regular file.+ */+struct reg_info {+	int compute_sum;+	uint32_t crc;+#ifdef ARCHIVE_HAS_MD5+	unsigned char buf_md5[16];+#endif+#ifdef ARCHIVE_HAS_RMD160+	unsigned char buf_rmd160[20];+#endif+#ifdef ARCHIVE_HAS_SHA1+	unsigned char buf_sha1[20];+#endif+#ifdef ARCHIVE_HAS_SHA256+	unsigned char buf_sha256[32];+#endif+#ifdef ARCHIVE_HAS_SHA384+	unsigned char buf_sha384[48];+#endif+#ifdef ARCHIVE_HAS_SHA512+	unsigned char buf_sha512[64];+#endif+};++struct mtree_entry {+	struct archive_rb_node rbnode;+	struct mtree_entry *next;+	struct mtree_entry *parent;+	struct dir_info *dir_info;+	struct reg_info *reg_info;++	struct archive_string parentdir;+	struct archive_string basename;+	struct archive_string pathname;+	struct archive_string symlink;+	struct archive_string uname;+	struct archive_string gname;+	struct archive_string fflags_text;+	unsigned int nlink;+	mode_t filetype;+	mode_t mode;+	int64_t size;+	int64_t uid;+	int64_t gid;+	time_t mtime;+	long mtime_nsec;+	unsigned long fflags_set;+	unsigned long fflags_clear;+	dev_t rdevmajor;+	dev_t rdevminor;+	dev_t devmajor;+	dev_t devminor;+	int64_t ino;+};++struct mtree_writer {+	struct mtree_entry *mtree_entry;+	struct mtree_entry *root;+	struct mtree_entry *cur_dirent;+	struct archive_string cur_dirstr;+	struct mtree_chain file_list;++	struct archive_string ebuf;+	struct archive_string buf;+	int first;+	uint64_t entry_bytes_remaining;++	/*+	 * Set global value.+	 */+	struct {+		int		processing;+		mode_t		type;+		int		keys;+		int64_t		uid;+		int64_t		gid;+		mode_t		mode;+		unsigned long	fflags_set;+		unsigned long	fflags_clear;+	} set;+	struct att_counter_set	acs;+	int classic;+	int depth;++	/* check sum */+	int compute_sum;+	uint32_t crc;+	uint64_t crc_len;+#ifdef ARCHIVE_HAS_MD5+	archive_md5_ctx md5ctx;+#endif+#ifdef ARCHIVE_HAS_RMD160+	archive_rmd160_ctx rmd160ctx;+#endif+#ifdef ARCHIVE_HAS_SHA1+	archive_sha1_ctx sha1ctx;+#endif+#ifdef ARCHIVE_HAS_SHA256+	archive_sha256_ctx sha256ctx;+#endif+#ifdef ARCHIVE_HAS_SHA384+	archive_sha384_ctx sha384ctx;+#endif+#ifdef ARCHIVE_HAS_SHA512+	archive_sha512_ctx sha512ctx;+#endif+	/* Keyword options */+	int keys;+#define	F_CKSUM		0x00000001		/* checksum */+#define	F_DEV		0x00000002		/* device type */+#define	F_DONE		0x00000004		/* directory done */+#define	F_FLAGS		0x00000008		/* file flags */+#define	F_GID		0x00000010		/* gid */+#define	F_GNAME		0x00000020		/* group name */+#define	F_IGN		0x00000040		/* ignore */+#define	F_MAGIC		0x00000080		/* name has magic chars */+#define	F_MD5		0x00000100		/* MD5 digest */+#define	F_MODE		0x00000200		/* mode */+#define	F_NLINK		0x00000400		/* number of links */+#define	F_NOCHANGE 	0x00000800		/* If owner/mode "wrong", do+						 * not change */+#define	F_OPT		0x00001000		/* existence optional */+#define	F_RMD160 	0x00002000		/* RIPEMD160 digest */+#define	F_SHA1		0x00004000		/* SHA-1 digest */+#define	F_SIZE		0x00008000		/* size */+#define	F_SLINK		0x00010000		/* symbolic link */+#define	F_TAGS		0x00020000		/* tags */+#define	F_TIME		0x00040000		/* modification time */+#define	F_TYPE		0x00080000		/* file type */+#define	F_UID		0x00100000		/* uid */+#define	F_UNAME		0x00200000		/* user name */+#define	F_VISIT		0x00400000		/* file visited */+#define	F_SHA256	0x00800000		/* SHA-256 digest */+#define	F_SHA384	0x01000000		/* SHA-384 digest */+#define	F_SHA512	0x02000000		/* SHA-512 digest */+#define	F_INO		0x04000000		/* inode number */+#define	F_RESDEV	0x08000000		/* device ID on which the+						 * entry resides */++	/* Options */+	int dironly;		/* If it is set, ignore all files except+				 * directory files, like mtree(8) -d option. */+	int indent;		/* If it is set, indent output data. */+	int output_global_set;	/* If it is set, use /set keyword to set+				 * global values. When generating mtree+				 * classic format, it is set by default. */+};++#define DEFAULT_KEYS	(F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\+			 | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\+			 | F_UNAME)+#define attr_counter_set_reset	attr_counter_set_free++static void attr_counter_free(struct attr_counter **);+static int attr_counter_inc(struct attr_counter **, struct attr_counter *,+	struct attr_counter *, struct mtree_entry *);+static struct attr_counter * attr_counter_new(struct mtree_entry *,+	struct attr_counter *);+static int attr_counter_set_collect(struct mtree_writer *,+	struct mtree_entry *);+static void attr_counter_set_free(struct mtree_writer *);+static int get_global_set_keys(struct mtree_writer *, struct mtree_entry *);+static int mtree_entry_add_child_tail(struct mtree_entry *,+	struct mtree_entry *);+static int mtree_entry_create_virtual_dir(struct archive_write *, const char *,+	struct mtree_entry **);+static int mtree_entry_cmp_node(const struct archive_rb_node *,+	const struct archive_rb_node *);+static int mtree_entry_cmp_key(const struct archive_rb_node *, const void *);+static int mtree_entry_exchange_same_entry(struct archive_write *,+    struct mtree_entry *, struct mtree_entry *);+static void mtree_entry_free(struct mtree_entry *);+static int mtree_entry_new(struct archive_write *, struct archive_entry *,+	struct mtree_entry **);+static void mtree_entry_register_free(struct mtree_writer *);+static void mtree_entry_register_init(struct mtree_writer *);+static int mtree_entry_setup_filenames(struct archive_write *,+	struct mtree_entry *, struct archive_entry *);+static int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **);+static void sum_init(struct mtree_writer *);+static void sum_update(struct mtree_writer *, const void *, size_t);+static void sum_final(struct mtree_writer *, struct reg_info *);+static void sum_write(struct archive_string *, struct reg_info *);+static int write_mtree_entry(struct archive_write *, struct mtree_entry *);+static int write_dot_dot_entry(struct archive_write *, struct mtree_entry *);++#define	COMPUTE_CRC(var, ch)	(var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]+static const uint32_t crctab[] = {+	0x0,+	0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,+	0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,+	0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,+	0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,+	0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,+	0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,+	0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,+	0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,+	0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,+	0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,+	0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,+	0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,+	0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,+	0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,+	0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,+	0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,+	0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,+	0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,+	0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,+	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,+	0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,+	0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,+	0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,+	0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,+	0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,+	0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,+	0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,+	0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,+	0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,+	0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,+	0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,+	0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,+	0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,+	0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,+	0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,+	0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,+	0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,+	0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,+	0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,+	0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,+	0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,+	0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,+	0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,+	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,+	0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,+	0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,+	0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,+	0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,+	0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,+	0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,+	0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4+};++static const unsigned char safe_char[256] = {+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */+	/* !"$%&'()*+,-./  EXCLUSION:0x20( ) 0x23(#) */+	0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */+	/* 0123456789:;<>?  EXCLUSION:0x3d(=) */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */+	/* @ABCDEFGHIJKLMNO */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */+	/* PQRSTUVWXYZ[]^_ EXCLUSION:0x5c(\)  */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 50 - 5F */+	/* `abcdefghijklmno */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */+	/* pqrstuvwxyz{|}~ */+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */+};++static void+mtree_quote(struct archive_string *s, const char *str)+{+	const char *start;+	char buf[4];+	unsigned char c;++	for (start = str; *str != '\0'; ++str) {+		if (safe_char[*(const unsigned char *)str])+			continue;+		if (start != str)+			archive_strncat(s, start, str - start);+		c = (unsigned char)*str;+		buf[0] = '\\';+		buf[1] = (c / 64) + '0';+		buf[2] = (c / 8 % 8) + '0';+		buf[3] = (c % 8) + '0';+		archive_strncat(s, buf, 4);+		start = str + 1;+	}++	if (start != str)+		archive_strncat(s, start, str - start);+}++/*+ * Indent a line as the mtree utility does so it is readable for people.+ */+static void+mtree_indent(struct mtree_writer *mtree)+{+	int i, fn, nd, pd;+	const char *r, *s, *x;++	if (mtree->classic) {+		if (mtree->indent) {+			nd = 0;+			pd = mtree->depth * 4;+		} else {+			nd = mtree->depth?4:0;+			pd = 0;+		}+	} else+		nd = pd = 0;+	fn = 1;+	s = r = mtree->ebuf.s;+	x = NULL;+	while (*r == ' ')+		r++;+	while ((r = strchr(r, ' ')) != NULL) {+		if (fn) {+			fn = 0;+			for (i = 0; i < nd + pd; i++)+				archive_strappend_char(&mtree->buf, ' ');+			archive_strncat(&mtree->buf, s, r - s);+			if (nd + (r -s) > INDENTNAMELEN) {+				archive_strncat(&mtree->buf, " \\\n", 3);+				for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)+					archive_strappend_char(&mtree->buf, ' ');+			} else {+				for (i = (int)(r -s + nd);+				    i < (INDENTNAMELEN + 1); i++)+					archive_strappend_char(&mtree->buf, ' ');+			}+			s = ++r;+			x = NULL;+			continue;+		}+		if (pd + (r - s) <= MAXLINELEN - 3 - INDENTNAMELEN)+			x = r++;+		else {+			if (x == NULL)+				x = r;+			archive_strncat(&mtree->buf, s, x - s);+			archive_strncat(&mtree->buf, " \\\n", 3);+			for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)+				archive_strappend_char(&mtree->buf, ' ');+			s = r = ++x;+			x = NULL;+		}+	}+	if (fn) {+		for (i = 0; i < nd + pd; i++)+			archive_strappend_char(&mtree->buf, ' ');+		archive_strcat(&mtree->buf, s);+		s += strlen(s);+	}+	if (x != NULL && pd + strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) {+		/* Last keyword is longer. */+		archive_strncat(&mtree->buf, s, x - s);+		archive_strncat(&mtree->buf, " \\\n", 3);+		for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)+			archive_strappend_char(&mtree->buf, ' ');+		s = ++x;+	}+	archive_strcat(&mtree->buf, s);+	archive_string_empty(&mtree->ebuf);+}++/*+ * Write /set keyword.+ * Set the most used value of uid, gid, mode and fflags, which are+ * collected by the attr_counter_set_collect() function.+ */+static void+write_global(struct mtree_writer *mtree)+{+	struct archive_string setstr;+	struct archive_string unsetstr;+	struct att_counter_set *acs;+	int keys, oldkeys, effkeys;++	archive_string_init(&setstr);+	archive_string_init(&unsetstr);+	keys = mtree->keys & SET_KEYS;+	oldkeys = mtree->set.keys;+	effkeys = keys;+	acs = &mtree->acs;+	if (mtree->set.processing) {+		/*+		 * Check if the global data needs updating.+		 */+		effkeys &= ~F_TYPE;+		if (acs->uid_list == NULL)+			effkeys &= ~(F_UNAME | F_UID);+		else if (oldkeys & (F_UNAME | F_UID)) {+			if (acs->uid_list->count < 2 ||+			    mtree->set.uid == acs->uid_list->m_entry->uid)+				effkeys &= ~(F_UNAME | F_UID);+		}+		if (acs->gid_list == NULL)+			effkeys &= ~(F_GNAME | F_GID);+		else if (oldkeys & (F_GNAME | F_GID)) {+			if (acs->gid_list->count < 2 ||+			    mtree->set.gid == acs->gid_list->m_entry->gid)+				effkeys &= ~(F_GNAME | F_GID);+		}+		if (acs->mode_list == NULL)+			effkeys &= ~F_MODE;+		else if (oldkeys & F_MODE) {+			if (acs->mode_list->count < 2 ||+			    mtree->set.mode == acs->mode_list->m_entry->mode)+				effkeys &= ~F_MODE;+		}+		if (acs->flags_list == NULL)+			effkeys &= ~F_FLAGS;+		else if ((oldkeys & F_FLAGS) != 0) {+			if (acs->flags_list->count < 2 ||+			    (acs->flags_list->m_entry->fflags_set ==+				mtree->set.fflags_set &&+			     acs->flags_list->m_entry->fflags_clear ==+				mtree->set.fflags_clear))+				effkeys &= ~F_FLAGS;+		}+	} else {+		if (acs->uid_list == NULL)+			keys &= ~(F_UNAME | F_UID);+		if (acs->gid_list == NULL)+			keys &= ~(F_GNAME | F_GID);+		if (acs->mode_list == NULL)+			keys &= ~F_MODE;+		if (acs->flags_list == NULL)+			keys &= ~F_FLAGS;+	}+	if ((keys & effkeys & F_TYPE) != 0) {+		if (mtree->dironly) {+			archive_strcat(&setstr, " type=dir");+			mtree->set.type = AE_IFDIR;+		} else {+			archive_strcat(&setstr, " type=file");+			mtree->set.type = AE_IFREG;+		}+	}+	if ((keys & effkeys & F_UNAME) != 0) {+		if (archive_strlen(&(acs->uid_list->m_entry->uname)) > 0) {+			archive_strcat(&setstr, " uname=");+			mtree_quote(&setstr, acs->uid_list->m_entry->uname.s);+		} else {+			keys &= ~F_UNAME;+			if ((oldkeys & F_UNAME) != 0)+				archive_strcat(&unsetstr, " uname");+		}+	}+	if ((keys & effkeys & F_UID) != 0) {+		mtree->set.uid = acs->uid_list->m_entry->uid;+		archive_string_sprintf(&setstr, " uid=%jd",+		    (intmax_t)mtree->set.uid);+	}+	if ((keys & effkeys & F_GNAME) != 0) {+		if (archive_strlen(&(acs->gid_list->m_entry->gname)) > 0) {+			archive_strcat(&setstr, " gname=");+			mtree_quote(&setstr, acs->gid_list->m_entry->gname.s);+		} else {+			keys &= ~F_GNAME;+			if ((oldkeys & F_GNAME) != 0)+				archive_strcat(&unsetstr, " gname");+		}+	}+	if ((keys & effkeys & F_GID) != 0) {+		mtree->set.gid = acs->gid_list->m_entry->gid;+		archive_string_sprintf(&setstr, " gid=%jd",+		    (intmax_t)mtree->set.gid);+	}+	if ((keys & effkeys & F_MODE) != 0) {+		mtree->set.mode = acs->mode_list->m_entry->mode;+		archive_string_sprintf(&setstr, " mode=%o",+		    (unsigned int)mtree->set.mode);+	}+	if ((keys & effkeys & F_FLAGS) != 0) {+		if (archive_strlen(+		    &(acs->flags_list->m_entry->fflags_text)) > 0) {+			archive_strcat(&setstr, " flags=");+			mtree_quote(&setstr,+			    acs->flags_list->m_entry->fflags_text.s);+			mtree->set.fflags_set =+			    acs->flags_list->m_entry->fflags_set;+			mtree->set.fflags_clear =+			    acs->flags_list->m_entry->fflags_clear;+		} else {+			keys &= ~F_FLAGS;+			if ((oldkeys & F_FLAGS) != 0)+				archive_strcat(&unsetstr, " flags");+		}+	}+	if (unsetstr.length > 0)+		archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s);+	archive_string_free(&unsetstr);+	if (setstr.length > 0)+		archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s);+	archive_string_free(&setstr);+	mtree->set.keys = keys;+	mtree->set.processing = 1;+}++static struct attr_counter *+attr_counter_new(struct mtree_entry *me, struct attr_counter *prev)+{+	struct attr_counter *ac;++	ac = malloc(sizeof(*ac));+	if (ac != NULL) {+		ac->prev = prev;+		ac->next = NULL;+		ac->count = 1;+		ac->m_entry = me;+	}+	return (ac);+}++static void+attr_counter_free(struct attr_counter **top)+{+	struct attr_counter *ac, *tac;++	if (*top == NULL)+		return;+	ac = *top;+        while (ac != NULL) {+		tac = ac->next;+		free(ac);+		ac = tac;+	}+	*top = NULL;+}++static int+attr_counter_inc(struct attr_counter **top, struct attr_counter *ac,+    struct attr_counter *last, struct mtree_entry *me)+{+	struct attr_counter *pac;++	if (ac != NULL) {+		ac->count++;+		if (*top == ac || ac->prev->count >= ac->count)+			return (0);+		for (pac = ac->prev; pac; pac = pac->prev) {+			if (pac->count >= ac->count)+				break;+		}+		ac->prev->next = ac->next;+		if (ac->next != NULL)+			ac->next->prev = ac->prev;+		if (pac != NULL) {+			ac->prev = pac;+			ac->next = pac->next;+			pac->next = ac;+			if (ac->next != NULL)+				ac->next->prev = ac;+		} else {+			ac->prev = NULL;+			ac->next = *top;+			*top = ac;+			ac->next->prev = ac;+		}+	} else if (last != NULL) {+		ac = attr_counter_new(me, last);+		if (ac == NULL)+			return (-1);+		last->next = ac;+	}+	return (0);+}++/*+ * Tabulate uid, gid, mode and fflags of a entry in order to be used for /set.+ */+static int+attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me)+{+	struct attr_counter *ac, *last;+	struct att_counter_set *acs = &mtree->acs;+	int keys = mtree->keys;++	if (keys & (F_UNAME | F_UID)) {+		if (acs->uid_list == NULL) {+			acs->uid_list = attr_counter_new(me, NULL);+			if (acs->uid_list == NULL)+				return (-1);+		} else {+			last = NULL;+			for (ac = acs->uid_list; ac; ac = ac->next) {+				if (ac->m_entry->uid == me->uid)+					break;+				last = ac;+			}+			if (attr_counter_inc(&acs->uid_list, ac, last, me) < 0)+				return (-1);+		}+	}+	if (keys & (F_GNAME | F_GID)) {+		if (acs->gid_list == NULL) {+			acs->gid_list = attr_counter_new(me, NULL);+			if (acs->gid_list == NULL)+				return (-1);+		} else {+			last = NULL;+			for (ac = acs->gid_list; ac; ac = ac->next) {+				if (ac->m_entry->gid == me->gid)+					break;+				last = ac;+			}+			if (attr_counter_inc(&acs->gid_list, ac, last, me) < 0)+				return (-1);+		}+	}+	if (keys & F_MODE) {+		if (acs->mode_list == NULL) {+			acs->mode_list = attr_counter_new(me, NULL);+			if (acs->mode_list == NULL)+				return (-1);+		} else {+			last = NULL;+			for (ac = acs->mode_list; ac; ac = ac->next) {+				if (ac->m_entry->mode == me->mode)+					break;+				last = ac;+			}+			if (attr_counter_inc(&acs->mode_list, ac, last, me) < 0)+				return (-1);+		}+	}+	if (keys & F_FLAGS) {+		if (acs->flags_list == NULL) {+			acs->flags_list = attr_counter_new(me, NULL);+			if (acs->flags_list == NULL)+				return (-1);+		} else {+			last = NULL;+			for (ac = acs->flags_list; ac; ac = ac->next) {+				if (ac->m_entry->fflags_set == me->fflags_set &&+				    ac->m_entry->fflags_clear ==+							me->fflags_clear)+					break;+				last = ac;+			}+			if (attr_counter_inc(&acs->flags_list, ac, last, me) < 0)+				return (-1);+		}+	}++	return (0);+}++static void+attr_counter_set_free(struct mtree_writer *mtree)+{+	struct att_counter_set *acs = &mtree->acs;++	attr_counter_free(&acs->uid_list);+	attr_counter_free(&acs->gid_list);+	attr_counter_free(&acs->mode_list);+	attr_counter_free(&acs->flags_list);+}++static int+get_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me)+{+	int keys;++	keys = mtree->keys;++	/*+	 * If a keyword has been set by /set, we do not need to+	 * output it.+	 */+	if (mtree->set.keys == 0)+		return (keys);/* /set is not used. */++	if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 &&+	     mtree->set.gid == me->gid)+		keys &= ~(F_GNAME | F_GID);+	if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 &&+	     mtree->set.uid == me->uid)+		keys &= ~(F_UNAME | F_UID);+	if (mtree->set.keys & F_FLAGS) {+		if (mtree->set.fflags_set == me->fflags_set &&+		    mtree->set.fflags_clear == me->fflags_clear)+			keys &= ~F_FLAGS;+	}+	if ((mtree->set.keys & F_MODE) != 0 && mtree->set.mode == me->mode)+		keys &= ~F_MODE;++	switch (me->filetype) {+	case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR:+	case AE_IFBLK: case AE_IFIFO:+		break;+	case AE_IFDIR:+		if ((mtree->set.keys & F_TYPE) != 0 &&+		    mtree->set.type == AE_IFDIR)+			keys &= ~F_TYPE;+		break;+	case AE_IFREG:+	default:	/* Handle unknown file types as regular files. */+		if ((mtree->set.keys & F_TYPE) != 0 &&+		    mtree->set.type == AE_IFREG)+			keys &= ~F_TYPE;+		break;+	}++	return (keys);+}++static int+mtree_entry_new(struct archive_write *a, struct archive_entry *entry,+    struct mtree_entry **m_entry)+{+	struct mtree_entry *me;+	const char *s;+	int r;+	static const struct archive_rb_tree_ops rb_ops = {+		mtree_entry_cmp_node, mtree_entry_cmp_key+	};++	me = calloc(1, sizeof(*me));+	if (me == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory for a mtree entry");+		*m_entry = NULL;+		return (ARCHIVE_FATAL);+	}++	r = mtree_entry_setup_filenames(a, me, entry);+	if (r < ARCHIVE_WARN) {+		mtree_entry_free(me);+		*m_entry = NULL;+		return (r);+	}++	if ((s = archive_entry_symlink(entry)) != NULL)+		archive_strcpy(&me->symlink, s);+	me->nlink = archive_entry_nlink(entry);+	me->filetype = archive_entry_filetype(entry);+	me->mode = archive_entry_mode(entry) & 07777;+	me->uid = archive_entry_uid(entry);+	me->gid = archive_entry_gid(entry);+	if ((s = archive_entry_uname(entry)) != NULL)+		archive_strcpy(&me->uname, s);+	if ((s = archive_entry_gname(entry)) != NULL)+		archive_strcpy(&me->gname, s);+	if ((s = archive_entry_fflags_text(entry)) != NULL)+		archive_strcpy(&me->fflags_text, s);+	archive_entry_fflags(entry, &me->fflags_set, &me->fflags_clear);+	me->mtime = archive_entry_mtime(entry);+	me->mtime_nsec = archive_entry_mtime_nsec(entry);+	me->rdevmajor = archive_entry_rdevmajor(entry);+	me->rdevminor = archive_entry_rdevminor(entry);+	me->devmajor = archive_entry_devmajor(entry);+	me->devminor = archive_entry_devminor(entry);+	me->ino = archive_entry_ino(entry);+	me->size = archive_entry_size(entry);+	if (me->filetype == AE_IFDIR) {+		me->dir_info = calloc(1, sizeof(*me->dir_info));+		if (me->dir_info == NULL) {+			mtree_entry_free(me);+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for a mtree entry");+			*m_entry = NULL;+			return (ARCHIVE_FATAL);+		}+		__archive_rb_tree_init(&me->dir_info->rbtree, &rb_ops);+		me->dir_info->children.first = NULL;+		me->dir_info->children.last = &(me->dir_info->children.first);+		me->dir_info->chnext = NULL;+	} else if (me->filetype == AE_IFREG) {+		me->reg_info = calloc(1, sizeof(*me->reg_info));+		if (me->reg_info == NULL) {+			mtree_entry_free(me);+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for a mtree entry");+			*m_entry = NULL;+			return (ARCHIVE_FATAL);+		}+		me->reg_info->compute_sum = 0;+	}++	*m_entry = me;+	return (ARCHIVE_OK);+}++static void+mtree_entry_free(struct mtree_entry *me)+{+	archive_string_free(&me->parentdir);+	archive_string_free(&me->basename);+	archive_string_free(&me->pathname);+	archive_string_free(&me->symlink);+	archive_string_free(&me->uname);+	archive_string_free(&me->gname);+	archive_string_free(&me->fflags_text);+	free(me->dir_info);+	free(me->reg_info);+	free(me);+}++static int+archive_write_mtree_header(struct archive_write *a,+    struct archive_entry *entry)+{+	struct mtree_writer *mtree= a->format_data;+	struct mtree_entry *mtree_entry;+	int r, r2;++	if (mtree->first) {+		mtree->first = 0;+		archive_strcat(&mtree->buf, "#mtree\n");+		if ((mtree->keys & SET_KEYS) == 0)+			mtree->output_global_set = 0;/* Disabled. */+	}++	mtree->entry_bytes_remaining = archive_entry_size(entry);++	/* While directory only mode, we do not handle non directory files. */+	if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR)+		return (ARCHIVE_OK);++	r2 = mtree_entry_new(a, entry, &mtree_entry);+	if (r2 < ARCHIVE_WARN)+		return (r2);+	r = mtree_entry_tree_add(a, &mtree_entry);+	if (r < ARCHIVE_WARN) {+		mtree_entry_free(mtree_entry);+		return (r);+	}+	mtree->mtree_entry = mtree_entry;++	/* If the current file is a regular file, we have to+	 * compute the sum of its content.+	 * Initialize a bunch of checksum context. */+	if (mtree_entry->reg_info)+		sum_init(mtree);++	return (r2);+}++static int+write_mtree_entry(struct archive_write *a, struct mtree_entry *me)+{+	struct mtree_writer *mtree = a->format_data;+	struct archive_string *str;+	int keys, ret;++	if (me->dir_info) {+		if (mtree->classic) {+			/*+			 * Output a comment line to describe the full+			 * pathname of the entry as mtree utility does+			 * while generating classic format.+			 */+			if (!mtree->dironly)+				archive_strappend_char(&mtree->buf, '\n');+			if (me->parentdir.s)+				archive_string_sprintf(&mtree->buf,+				    "# %s/%s\n",+				    me->parentdir.s, me->basename.s);+			else+				archive_string_sprintf(&mtree->buf,+				    "# %s\n",+				    me->basename.s);+		}+		if (mtree->output_global_set)+			write_global(mtree);+	}+	archive_string_empty(&mtree->ebuf);+	str = (mtree->indent || mtree->classic)? &mtree->ebuf : &mtree->buf;++	if (!mtree->classic && me->parentdir.s) {+		/*+		 * If generating format is not classic one(v1), output+		 * a full pathname.+		 */+		mtree_quote(str, me->parentdir.s);+		archive_strappend_char(str, '/');+	}+	mtree_quote(str, me->basename.s);++	keys = get_global_set_keys(mtree, me);+	if ((keys & F_NLINK) != 0 &&+	    me->nlink != 1 && me->filetype != AE_IFDIR)+		archive_string_sprintf(str, " nlink=%u", me->nlink);++	if ((keys & F_GNAME) != 0 && archive_strlen(&me->gname) > 0) {+		archive_strcat(str, " gname=");+		mtree_quote(str, me->gname.s);+	}+	if ((keys & F_UNAME) != 0 && archive_strlen(&me->uname) > 0) {+		archive_strcat(str, " uname=");+		mtree_quote(str, me->uname.s);+	}+	if ((keys & F_FLAGS) != 0) {+		if (archive_strlen(&me->fflags_text) > 0) {+			archive_strcat(str, " flags=");+			mtree_quote(str, me->fflags_text.s);+		} else if (mtree->set.processing &&+		    (mtree->set.keys & F_FLAGS) != 0)+			/* Overwrite the global parameter. */+			archive_strcat(str, " flags=none");+	}+	if ((keys & F_TIME) != 0)+		archive_string_sprintf(str, " time=%jd.%jd",+		    (intmax_t)me->mtime, (intmax_t)me->mtime_nsec);+	if ((keys & F_MODE) != 0)+		archive_string_sprintf(str, " mode=%o", (unsigned int)me->mode);+	if ((keys & F_GID) != 0)+		archive_string_sprintf(str, " gid=%jd", (intmax_t)me->gid);+	if ((keys & F_UID) != 0)+		archive_string_sprintf(str, " uid=%jd", (intmax_t)me->uid);++	if ((keys & F_INO) != 0)+		archive_string_sprintf(str, " inode=%jd", (intmax_t)me->ino);+	if ((keys & F_RESDEV) != 0) {+		archive_string_sprintf(str,+		    " resdevice=native,%ju,%ju",+		    (uintmax_t)me->devmajor,+		    (uintmax_t)me->devminor);+	}++	switch (me->filetype) {+	case AE_IFLNK:+		if ((keys & F_TYPE) != 0)+			archive_strcat(str, " type=link");+		if ((keys & F_SLINK) != 0) {+			archive_strcat(str, " link=");+			mtree_quote(str, me->symlink.s);+		}+		break;+	case AE_IFSOCK:+		if ((keys & F_TYPE) != 0)+			archive_strcat(str, " type=socket");+		break;+	case AE_IFCHR:+		if ((keys & F_TYPE) != 0)+			archive_strcat(str, " type=char");+		if ((keys & F_DEV) != 0) {+			archive_string_sprintf(str,+			    " device=native,%ju,%ju",+			    (uintmax_t)me->rdevmajor,+			    (uintmax_t)me->rdevminor);+		}+		break;+	case AE_IFBLK:+		if ((keys & F_TYPE) != 0)+			archive_strcat(str, " type=block");+		if ((keys & F_DEV) != 0) {+			archive_string_sprintf(str,+			    " device=native,%ju,%ju",+			    (uintmax_t)me->rdevmajor,+			    (uintmax_t)me->rdevminor);+		}+		break;+	case AE_IFDIR:+		if ((keys & F_TYPE) != 0)+			archive_strcat(str, " type=dir");+		break;+	case AE_IFIFO:+		if ((keys & F_TYPE) != 0)+			archive_strcat(str, " type=fifo");+		break;+	case AE_IFREG:+	default:	/* Handle unknown file types as regular files. */+		if ((keys & F_TYPE) != 0)+			archive_strcat(str, " type=file");+		if ((keys & F_SIZE) != 0)+			archive_string_sprintf(str, " size=%jd",+			    (intmax_t)me->size);+		break;+	}++	/* Write a bunch of sum. */+	if (me->reg_info)+		sum_write(str, me->reg_info);++	archive_strappend_char(str, '\n');+	if (mtree->indent || mtree->classic)+		mtree_indent(mtree);++	if (mtree->buf.length > 32768) {+		ret = __archive_write_output(+			a, mtree->buf.s, mtree->buf.length);+		archive_string_empty(&mtree->buf);+	} else+		ret = ARCHIVE_OK;+	return (ret);+}++static int+write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n)+{+	struct mtree_writer *mtree = a->format_data;+	int ret;++	if (n->parentdir.s) {+		if (mtree->indent) {+			int i, pd = mtree->depth * 4;+			for (i = 0; i < pd; i++)+				archive_strappend_char(&mtree->buf, ' ');+		}+		archive_string_sprintf(&mtree->buf, "# %s/%s\n",+			n->parentdir.s, n->basename.s);+	}++	if (mtree->indent) {+		archive_string_empty(&mtree->ebuf);+		archive_strncat(&mtree->ebuf, "..\n\n", (mtree->dironly)?3:4);+		mtree_indent(mtree);+	} else+		archive_strncat(&mtree->buf, "..\n\n", (mtree->dironly)?3:4);++	if (mtree->buf.length > 32768) {+		ret = __archive_write_output(+			a, mtree->buf.s, mtree->buf.length);+		archive_string_empty(&mtree->buf);+	} else+		ret = ARCHIVE_OK;+	return (ret);+}++/*+ * Write mtree entries saved at attr_counter_set_collect() function.+ */+static int+write_mtree_entry_tree(struct archive_write *a)+{+	struct mtree_writer *mtree = a->format_data;+	struct mtree_entry *np = mtree->root;+	struct archive_rb_node *n;+	int ret;++	do {+		if (mtree->output_global_set) {+			/*+			 * Collect attribute information to know which value+			 * is frequently used among the children.+			 */+			attr_counter_set_reset(mtree);+			ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {+				struct mtree_entry *e = (struct mtree_entry *)n;+				if (attr_counter_set_collect(mtree, e) < 0) {+					archive_set_error(&a->archive, ENOMEM,+					    "Can't allocate memory");+					return (ARCHIVE_FATAL);+				}+			}+		}+		if (!np->dir_info->virtual || mtree->classic) {+			ret = write_mtree_entry(a, np);+			if (ret != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		} else {+			/* Whenever output_global_set is enabled+			 * output global value(/set keywords)+			 * even if the directory entry is not allowed+			 * to be written because the global values+			 * can be used for the children. */+			if (mtree->output_global_set)+				write_global(mtree);+		}+		/*+		 * Output the attribute of all files except directory files.+		 */+		mtree->depth++;+		ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {+			struct mtree_entry *e = (struct mtree_entry *)n;++			if (e->dir_info)+				mtree_entry_add_child_tail(np, e);+			else {+				ret = write_mtree_entry(a, e);+				if (ret != ARCHIVE_OK)+					return (ARCHIVE_FATAL);+			}+		}+		mtree->depth--;++		if (np->dir_info->children.first != NULL) {+			/*+			 * Descend the tree.+			 */+			np = np->dir_info->children.first;+			if (mtree->indent)+				mtree->depth++;+			continue;+		} else if (mtree->classic) {+			/*+			 * While printing mtree classic, if there are not+			 * any directory files(except "." and "..") in the+			 * directory, output two dots ".." as returning+			 * the parent directory.+			 */+			ret = write_dot_dot_entry(a, np);+			if (ret != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+		}++		while (np != np->parent) {+			if (np->dir_info->chnext == NULL) {+				/*+				 * Ascend the tree; go back to the parent.+				 */+				if (mtree->indent)+					mtree->depth--;+				if (mtree->classic) {+					ret = write_dot_dot_entry(a,+						np->parent);+					if (ret != ARCHIVE_OK)+						return (ARCHIVE_FATAL);+				}+				np = np->parent;+			} else {+				/*+				 * Switch to next mtree entry in the directory.+				 */+				np = np->dir_info->chnext;+				break;+			}+		}+	} while (np != np->parent); ++	return (ARCHIVE_OK);+}++static int+archive_write_mtree_finish_entry(struct archive_write *a)+{+	struct mtree_writer *mtree = a->format_data;+	struct mtree_entry *me;++	if ((me = mtree->mtree_entry) == NULL)+		return (ARCHIVE_OK);+	mtree->mtree_entry = NULL;++	if (me->reg_info)+		sum_final(mtree, me->reg_info);++	return (ARCHIVE_OK);+}++static int+archive_write_mtree_close(struct archive_write *a)+{+	struct mtree_writer *mtree= a->format_data;+	int ret;++	if (mtree->root != NULL) {+		ret = write_mtree_entry_tree(a);+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	archive_write_set_bytes_in_last_block(&a->archive, 1);++	return __archive_write_output(a, mtree->buf.s, mtree->buf.length);+}++static ssize_t+archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n)+{+	struct mtree_writer *mtree= a->format_data;++	if (n > mtree->entry_bytes_remaining)+		n = (size_t)mtree->entry_bytes_remaining;+	mtree->entry_bytes_remaining -= n;++	/* We don't need to compute a regular file sum */+	if (mtree->mtree_entry == NULL)+		return (n);++	if (mtree->mtree_entry->filetype == AE_IFREG)+		sum_update(mtree, buff, n);++	return (n);+}++static int+archive_write_mtree_free(struct archive_write *a)+{+	struct mtree_writer *mtree= a->format_data;++	if (mtree == NULL)+		return (ARCHIVE_OK);++	/* Make sure we do not leave any entries. */+	mtree_entry_register_free(mtree);+	archive_string_free(&mtree->cur_dirstr);+	archive_string_free(&mtree->ebuf);+	archive_string_free(&mtree->buf);+	attr_counter_set_free(mtree);+	free(mtree);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}++static int+archive_write_mtree_options(struct archive_write *a, const char *key,+    const char *value)+{+	struct mtree_writer *mtree= a->format_data;+	int keybit = 0;++	switch (key[0]) {+	case 'a':+		if (strcmp(key, "all") == 0)+			keybit = ~0;+		break;+	case 'c':+		if (strcmp(key, "cksum") == 0)+			keybit = F_CKSUM;+		break;+	case 'd':+		if (strcmp(key, "device") == 0)+			keybit = F_DEV;+		else if (strcmp(key, "dironly") == 0) {+			mtree->dironly = (value != NULL)? 1: 0;+			return (ARCHIVE_OK);+		}+		break;+	case 'f':+		if (strcmp(key, "flags") == 0)+			keybit = F_FLAGS;+		break;+	case 'g':+		if (strcmp(key, "gid") == 0)+			keybit = F_GID;+		else if (strcmp(key, "gname") == 0)+			keybit = F_GNAME;+		break;+	case 'i':+		if (strcmp(key, "indent") == 0) {+			mtree->indent = (value != NULL)? 1: 0;+			return (ARCHIVE_OK);+		} else if (strcmp(key, "inode") == 0) {+			keybit = F_INO;+		}+		break;+	case 'l':+		if (strcmp(key, "link") == 0)+			keybit = F_SLINK;+		break;+	case 'm':+		if (strcmp(key, "md5") == 0 ||+		    strcmp(key, "md5digest") == 0)+			keybit = F_MD5;+		if (strcmp(key, "mode") == 0)+			keybit = F_MODE;+		break;+	case 'n':+		if (strcmp(key, "nlink") == 0)+			keybit = F_NLINK;+		break;+	case 'r':+		if (strcmp(key, "resdevice") == 0) {+			keybit = F_RESDEV;+		} else if (strcmp(key, "ripemd160digest") == 0 ||+		    strcmp(key, "rmd160") == 0 ||+		    strcmp(key, "rmd160digest") == 0)+			keybit = F_RMD160;+		break;+	case 's':+		if (strcmp(key, "sha1") == 0 ||+		    strcmp(key, "sha1digest") == 0)+			keybit = F_SHA1;+		if (strcmp(key, "sha256") == 0 ||+		    strcmp(key, "sha256digest") == 0)+			keybit = F_SHA256;+		if (strcmp(key, "sha384") == 0 ||+		    strcmp(key, "sha384digest") == 0)+			keybit = F_SHA384;+		if (strcmp(key, "sha512") == 0 ||+		    strcmp(key, "sha512digest") == 0)+			keybit = F_SHA512;+		if (strcmp(key, "size") == 0)+			keybit = F_SIZE;+		break;+	case 't':+		if (strcmp(key, "time") == 0)+			keybit = F_TIME;+		else if (strcmp(key, "type") == 0)+			keybit = F_TYPE;+		break;+	case 'u':+		if (strcmp(key, "uid") == 0)+			keybit = F_UID;+		else if (strcmp(key, "uname") == 0)+			keybit = F_UNAME;+		else if (strcmp(key, "use-set") == 0) {+			mtree->output_global_set = (value != NULL)? 1: 0;+			return (ARCHIVE_OK);+		}+		break;+	}+	if (keybit != 0) {+		if (value != NULL)+			mtree->keys |= keybit;+		else+			mtree->keys &= ~keybit;+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+archive_write_set_format_mtree_default(struct archive *_a, const char *fn)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct mtree_writer *mtree;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn);++	if (a->format_free != NULL)+		(a->format_free)(a);++	if ((mtree = calloc(1, sizeof(*mtree))) == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate mtree data");+		return (ARCHIVE_FATAL);+	}++	mtree->mtree_entry = NULL;+	mtree->first = 1;+	memset(&(mtree->set), 0, sizeof(mtree->set));+	mtree->keys = DEFAULT_KEYS;+	mtree->dironly = 0;+	mtree->indent = 0;+	archive_string_init(&mtree->ebuf);+	archive_string_init(&mtree->buf);+	mtree_entry_register_init(mtree);+	a->format_data = mtree;+	a->format_free = archive_write_mtree_free;+	a->format_name = "mtree";+	a->format_options = archive_write_mtree_options;+	a->format_write_header = archive_write_mtree_header;+	a->format_close = archive_write_mtree_close;+	a->format_write_data = archive_write_mtree_data;+	a->format_finish_entry = archive_write_mtree_finish_entry;+	a->archive.archive_format = ARCHIVE_FORMAT_MTREE;+	a->archive.archive_format_name = "mtree";++	return (ARCHIVE_OK);+}++int+archive_write_set_format_mtree(struct archive *_a)+{+	return archive_write_set_format_mtree_default(_a,+		"archive_write_set_format_mtree");+}++int+archive_write_set_format_mtree_classic(struct archive *_a)+{+	int r;++	r = archive_write_set_format_mtree_default(_a,+		"archive_write_set_format_mtree_classic");+	if (r == ARCHIVE_OK) {+		struct archive_write *a = (struct archive_write *)_a;+		struct mtree_writer *mtree;++		mtree = (struct mtree_writer *)a->format_data;++		/* Set to output a mtree archive in classic format. */+		mtree->classic = 1;+		/* Basically, mtree classic format uses '/set' global+		 * value. */+		mtree->output_global_set = 1;+	}+	return (r);+}++static void+sum_init(struct mtree_writer *mtree)+{++	mtree->compute_sum = 0;++	if (mtree->keys & F_CKSUM) {+		mtree->compute_sum |= F_CKSUM;+		mtree->crc = 0;+		mtree->crc_len = 0;+	}+#ifdef ARCHIVE_HAS_MD5+	if (mtree->keys & F_MD5) {+		if (archive_md5_init(&mtree->md5ctx) == ARCHIVE_OK)+			mtree->compute_sum |= F_MD5;+		else+			mtree->keys &= ~F_MD5;/* Not supported. */+	}+#endif+#ifdef ARCHIVE_HAS_RMD160+	if (mtree->keys & F_RMD160) {+		if (archive_rmd160_init(&mtree->rmd160ctx) == ARCHIVE_OK)+			mtree->compute_sum |= F_RMD160;+		else+			mtree->keys &= ~F_RMD160;/* Not supported. */+	}+#endif+#ifdef ARCHIVE_HAS_SHA1+	if (mtree->keys & F_SHA1) {+		if (archive_sha1_init(&mtree->sha1ctx) == ARCHIVE_OK)+			mtree->compute_sum |= F_SHA1;+		else+			mtree->keys &= ~F_SHA1;/* Not supported. */+	}+#endif+#ifdef ARCHIVE_HAS_SHA256+	if (mtree->keys & F_SHA256) {+		if (archive_sha256_init(&mtree->sha256ctx) == ARCHIVE_OK)+			mtree->compute_sum |= F_SHA256;+		else+			mtree->keys &= ~F_SHA256;/* Not supported. */+	}+#endif+#ifdef ARCHIVE_HAS_SHA384+	if (mtree->keys & F_SHA384) {+		if (archive_sha384_init(&mtree->sha384ctx) == ARCHIVE_OK)+			mtree->compute_sum |= F_SHA384;+		else+			mtree->keys &= ~F_SHA384;/* Not supported. */+	}+#endif+#ifdef ARCHIVE_HAS_SHA512+	if (mtree->keys & F_SHA512) {+		if (archive_sha512_init(&mtree->sha512ctx) == ARCHIVE_OK)+			mtree->compute_sum |= F_SHA512;+		else+			mtree->keys &= ~F_SHA512;/* Not supported. */+	}+#endif+}++static void+sum_update(struct mtree_writer *mtree, const void *buff, size_t n)+{+	if (mtree->compute_sum & F_CKSUM) {+		/*+		 * Compute a POSIX 1003.2 checksum+		 */+		const unsigned char *p;+		size_t nn;++		for (nn = n, p = buff; nn--; ++p)+			COMPUTE_CRC(mtree->crc, *p);+		mtree->crc_len += n;+	}+#ifdef ARCHIVE_HAS_MD5+	if (mtree->compute_sum & F_MD5)+		archive_md5_update(&mtree->md5ctx, buff, n);+#endif+#ifdef ARCHIVE_HAS_RMD160+	if (mtree->compute_sum & F_RMD160)+		archive_rmd160_update(&mtree->rmd160ctx, buff, n);+#endif+#ifdef ARCHIVE_HAS_SHA1+	if (mtree->compute_sum & F_SHA1)+		archive_sha1_update(&mtree->sha1ctx, buff, n);+#endif+#ifdef ARCHIVE_HAS_SHA256+	if (mtree->compute_sum & F_SHA256)+		archive_sha256_update(&mtree->sha256ctx, buff, n);+#endif+#ifdef ARCHIVE_HAS_SHA384+	if (mtree->compute_sum & F_SHA384)+		archive_sha384_update(&mtree->sha384ctx, buff, n);+#endif+#ifdef ARCHIVE_HAS_SHA512+	if (mtree->compute_sum & F_SHA512)+		archive_sha512_update(&mtree->sha512ctx, buff, n);+#endif+}++static void+sum_final(struct mtree_writer *mtree, struct reg_info *reg)+{++	if (mtree->compute_sum & F_CKSUM) {+		uint64_t len;+		/* Include the length of the file. */+		for (len = mtree->crc_len; len != 0; len >>= 8)+			COMPUTE_CRC(mtree->crc, len & 0xff);+		reg->crc = ~mtree->crc;+	}+#ifdef ARCHIVE_HAS_MD5+	if (mtree->compute_sum & F_MD5)+		archive_md5_final(&mtree->md5ctx, reg->buf_md5);+#endif+#ifdef ARCHIVE_HAS_RMD160+	if (mtree->compute_sum & F_RMD160)+		archive_rmd160_final(&mtree->rmd160ctx, reg->buf_rmd160);+#endif+#ifdef ARCHIVE_HAS_SHA1+	if (mtree->compute_sum & F_SHA1)+		archive_sha1_final(&mtree->sha1ctx, reg->buf_sha1);+#endif+#ifdef ARCHIVE_HAS_SHA256+	if (mtree->compute_sum & F_SHA256)+		archive_sha256_final(&mtree->sha256ctx, reg->buf_sha256);+#endif+#ifdef ARCHIVE_HAS_SHA384+	if (mtree->compute_sum & F_SHA384)+		archive_sha384_final(&mtree->sha384ctx, reg->buf_sha384);+#endif+#ifdef ARCHIVE_HAS_SHA512+	if (mtree->compute_sum & F_SHA512)+		archive_sha512_final(&mtree->sha512ctx, reg->buf_sha512);+#endif+	/* Save what types of sum are computed. */+	reg->compute_sum = mtree->compute_sum;+}++#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \+    defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) || \+    defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512)+static void+strappend_bin(struct archive_string *s, const unsigned char *bin, int n)+{+	static const char hex[] = "0123456789abcdef";+	int i;++	for (i = 0; i < n; i++) {+		archive_strappend_char(s, hex[bin[i] >> 4]);+		archive_strappend_char(s, hex[bin[i] & 0x0f]);+	}+}+#endif++static void+sum_write(struct archive_string *str, struct reg_info *reg)+{++	if (reg->compute_sum & F_CKSUM) {+		archive_string_sprintf(str, " cksum=%ju",+		    (uintmax_t)reg->crc);+	}+#ifdef ARCHIVE_HAS_MD5+	if (reg->compute_sum & F_MD5) {+		archive_strcat(str, " md5digest=");+		strappend_bin(str, reg->buf_md5, sizeof(reg->buf_md5));+	}+#endif+#ifdef ARCHIVE_HAS_RMD160+	if (reg->compute_sum & F_RMD160) {+		archive_strcat(str, " rmd160digest=");+		strappend_bin(str, reg->buf_rmd160, sizeof(reg->buf_rmd160));+	}+#endif+#ifdef ARCHIVE_HAS_SHA1+	if (reg->compute_sum & F_SHA1) {+		archive_strcat(str, " sha1digest=");+		strappend_bin(str, reg->buf_sha1, sizeof(reg->buf_sha1));+	}+#endif+#ifdef ARCHIVE_HAS_SHA256+	if (reg->compute_sum & F_SHA256) {+		archive_strcat(str, " sha256digest=");+		strappend_bin(str, reg->buf_sha256, sizeof(reg->buf_sha256));+	}+#endif+#ifdef ARCHIVE_HAS_SHA384+	if (reg->compute_sum & F_SHA384) {+		archive_strcat(str, " sha384digest=");+		strappend_bin(str, reg->buf_sha384, sizeof(reg->buf_sha384));+	}+#endif+#ifdef ARCHIVE_HAS_SHA512+	if (reg->compute_sum & F_SHA512) {+		archive_strcat(str, " sha512digest=");+		strappend_bin(str, reg->buf_sha512, sizeof(reg->buf_sha512));+	}+#endif+}++static int+mtree_entry_cmp_node(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	const struct mtree_entry *e1 = (const struct mtree_entry *)n1;+	const struct mtree_entry *e2 = (const struct mtree_entry *)n2;++	return (strcmp(e2->basename.s, e1->basename.s));+}++static int+mtree_entry_cmp_key(const struct archive_rb_node *n, const void *key)+{+	const struct mtree_entry *e = (const struct mtree_entry *)n;++	return (strcmp((const char *)key, e->basename.s));+}++#if defined(_WIN32) || defined(__CYGWIN__)+static int+cleanup_backslash_1(char *p)+{+	int mb, dos;++	mb = dos = 0;+	while (*p) {+		if (*(unsigned char *)p > 127)+			mb = 1;+		if (*p == '\\') {+			/* If we have not met any multi-byte characters,+			 * we can replace '\' with '/'. */+			if (!mb)+				*p = '/';+			dos = 1;+		}+		p++;+	}+	if (!mb || !dos)+		return (0);+	return (-1);+}++static void+cleanup_backslash_2(wchar_t *p)+{++	/* Convert a path-separator from '\' to  '/' */+	while (*p != L'\0') {+		if (*p == L'\\')+			*p = L'/';+		p++;+	}+}+#endif++/*+ * Generate a parent directory name and a base name from a pathname.+ */+static int+mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file,+    struct archive_entry *entry)+{+	const char *pathname;+	char *p, *dirname, *slash;+	size_t len;+	int ret = ARCHIVE_OK;++	archive_strcpy(&file->pathname, archive_entry_pathname(entry));+#if defined(_WIN32) || defined(__CYGWIN__)+	/*+	 * Convert a path-separator from '\' to  '/'+	 */+	if (cleanup_backslash_1(file->pathname.s) != 0) {+		const wchar_t *wp = archive_entry_pathname_w(entry);+		struct archive_wstring ws;++		if (wp != NULL) {+			int r;+			archive_string_init(&ws);+			archive_wstrcpy(&ws, wp);+			cleanup_backslash_2(ws.s);+			archive_string_empty(&(file->pathname));+			r = archive_string_append_from_wcs(&(file->pathname),+			    ws.s, ws.length);+			archive_wstring_free(&ws);+			if (r < 0 && errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory");+				return (ARCHIVE_FATAL);+			}+		}+	}+#else+	(void)a; /* UNUSED */+#endif+	pathname =  file->pathname.s;+	if (strcmp(pathname, ".") == 0) {+		archive_strcpy(&file->basename, ".");+		return (ARCHIVE_OK);+	}++	archive_strcpy(&(file->parentdir), pathname);++	len = file->parentdir.length;+	p = dirname = file->parentdir.s;++	/*+	 * Remove leading '/' and '../' elements+	 */+	while (*p) {+		if (p[0] == '/') {+			p++;+			len--;+		} else if (p[0] != '.')+			break;+		else if (p[1] == '.' && p[2] == '/') {+			p += 3;+			len -= 3;+		} else+			break;+	}+	if (p != dirname) {+		memmove(dirname, p, len+1);+		p = dirname;+	}+	/*+	 * Remove "/","/." and "/.." elements from tail.+	 */+	while (len > 0) {+		size_t ll = len;++		if (len > 0 && p[len-1] == '/') {+			p[len-1] = '\0';+			len--;+		}+		if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {+			p[len-2] = '\0';+			len -= 2;+		}+		if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&+		    p[len-1] == '.') {+			p[len-3] = '\0';+			len -= 3;+		}+		if (ll == len)+			break;+	}+	while (*p) {+		if (p[0] == '/') {+			if (p[1] == '/')+				/* Convert '//' --> '/' */+				memmove(p, p+1, strlen(p+1) + 1);+			else if (p[1] == '.' && p[2] == '/')+				/* Convert '/./' --> '/' */+				memmove(p, p+2, strlen(p+2) + 1);+			else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {+				/* Convert 'dir/dir1/../dir2/'+				 *     --> 'dir/dir2/'+				 */+				char *rp = p -1;+				while (rp >= dirname) {+					if (*rp == '/')+						break;+					--rp;+				}+				if (rp > dirname) {+					strcpy(rp, p+3);+					p = rp;+				} else {+					strcpy(dirname, p+4);+					p = dirname;+				}+			} else+				p++;+		} else+			p++;+	}+	p = dirname;+	len = strlen(p);++	/*+	 * Add "./" prefix.+	 * NOTE: If the pathname does not have a path separator, we have+	 * to add "./" to the head of the pathname because mtree reader+	 * will suppose that it is v1(a.k.a classic) mtree format and+	 * change the directory unexpectedly and so it will make a wrong+	 * path.+	 */+	if (strcmp(p, ".") != 0 && strncmp(p, "./", 2) != 0) {+		struct archive_string as;+		archive_string_init(&as);+		archive_strcpy(&as, "./");+		archive_strncat(&as, p, len);+		archive_string_empty(&file->parentdir);+		archive_string_concat(&file->parentdir, &as);+		archive_string_free(&as);+		p = file->parentdir.s;+		len = archive_strlen(&file->parentdir);+	}++	/*+	 * Find out the position which points the last position of+	 * path separator('/').+	 */+	slash = NULL;+	for (; *p != '\0'; p++) {+		if (*p == '/')+			slash = p;+	}+	if (slash == NULL) {+		/* The pathname doesn't have a parent directory. */+		file->parentdir.length = len;+		archive_string_copy(&(file->basename), &(file->parentdir));+		archive_string_empty(&(file->parentdir));+		*file->parentdir.s = '\0';+		return (ret);+	}++	/* Make a basename from file->parentdir.s and slash */+	*slash  = '\0';+	file->parentdir.length = slash - file->parentdir.s;+	archive_strcpy(&(file->basename),  slash + 1);+	return (ret);+}++static int+mtree_entry_create_virtual_dir(struct archive_write *a, const char *pathname,+    struct mtree_entry **m_entry)+{+	struct archive_entry *entry;+	struct mtree_entry *file;+	int r;++	entry = archive_entry_new();+	if (entry == NULL) {+		*m_entry = NULL;+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	archive_entry_copy_pathname(entry, pathname);+	archive_entry_set_mode(entry, AE_IFDIR | 0755);+	archive_entry_set_mtime(entry, time(NULL), 0);++	r = mtree_entry_new(a, entry, &file);+	archive_entry_free(entry);+	if (r < ARCHIVE_WARN) {+		*m_entry = NULL;+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}++	file->dir_info->virtual = 1;++	*m_entry = file;+	return (ARCHIVE_OK);+}++static void+mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file)+{+        file->next = NULL;+        *mtree->file_list.last = file;+        mtree->file_list.last = &(file->next);+}++static void+mtree_entry_register_init(struct mtree_writer *mtree)+{+	mtree->file_list.first = NULL;+	mtree->file_list.last = &(mtree->file_list.first);+}++static void+mtree_entry_register_free(struct mtree_writer *mtree)+{+	struct mtree_entry *file, *file_next;++	file = mtree->file_list.first;+	while (file != NULL) {+		file_next = file->next;+		mtree_entry_free(file);+		file = file_next;+	}+}++static int+mtree_entry_add_child_tail(struct mtree_entry *parent,+    struct mtree_entry *child)+{+	child->dir_info->chnext = NULL;+	*parent->dir_info->children.last = child;+	parent->dir_info->children.last = &(child->dir_info->chnext);+	return (1);+}++/*+ * Find a entry from a parent entry with the name.+ */+static struct mtree_entry *+mtree_entry_find_child(struct mtree_entry *parent, const char *child_name)+{+	struct mtree_entry *np;++	if (parent == NULL)+		return (NULL);+	np = (struct mtree_entry *)__archive_rb_tree_find_node(+	    &(parent->dir_info->rbtree), child_name);+	return (np);+}++static int+get_path_component(char *name, size_t n, const char *fn)+{+	char *p;+	size_t l;++	p = strchr(fn, '/');+	if (p == NULL) {+		if ((l = strlen(fn)) == 0)+			return (0);+	} else+		l = p - fn;+	if (l > n -1)+		return (-1);+	memcpy(name, fn, l);+	name[l] = '\0';++	return ((int)l);+}++/*+ * Add a new entry into the tree.+ */+static int+mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep)+{+#if defined(_WIN32) && !defined(__CYGWIN__)+	char name[_MAX_FNAME];/* Included null terminator size. */+#elif defined(NAME_MAX) && NAME_MAX >= 255+	char name[NAME_MAX+1];+#else+	char name[256];+#endif+	struct mtree_writer *mtree = (struct mtree_writer *)a->format_data;+	struct mtree_entry *dent, *file, *np;+	const char *fn, *p;+	int l, r;++	file = *filep;+	if (file->parentdir.length == 0 && file->basename.length == 1 &&+	    file->basename.s[0] == '.') {+		file->parent = file;+		if (mtree->root != NULL) {+			np = mtree->root;+			goto same_entry;+		}+		mtree->root = file;+		mtree_entry_register_add(mtree, file);+		return (ARCHIVE_OK);+	}++	if (file->parentdir.length == 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Internal programming error "+		    "in generating canonical name for %s",+		    file->pathname.s);+		return (ARCHIVE_FAILED);+	}++	fn = p = file->parentdir.s;++	/*+	 * If the path of the parent directory of `file' entry is+	 * the same as the path of `cur_dirent', add `file' entry to+	 * `cur_dirent'.+	 */+	if (archive_strlen(&(mtree->cur_dirstr))+	      == archive_strlen(&(file->parentdir)) &&+	    strcmp(mtree->cur_dirstr.s, fn) == 0) {+		if (!__archive_rb_tree_insert_node(+		    &(mtree->cur_dirent->dir_info->rbtree),+		    (struct archive_rb_node *)file)) {+			/* There is the same name in the tree. */+			np = (struct mtree_entry *)__archive_rb_tree_find_node(+			    &(mtree->cur_dirent->dir_info->rbtree),+			    file->basename.s);+			goto same_entry;+		}+		file->parent = mtree->cur_dirent;+		mtree_entry_register_add(mtree, file);+		return (ARCHIVE_OK);+	}++	dent = mtree->root;+	for (;;) {+		l = get_path_component(name, sizeof(name), fn);+		if (l == 0) {+			np = NULL;+			break;+		}+		if (l < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "A name buffer is too small");+			return (ARCHIVE_FATAL);+		}+		if (l == 1 && name[0] == '.' && dent != NULL &&+		    dent == mtree->root) {+			fn += l;+			if (fn[0] == '/')+				fn++;+			continue;+		}++		np = mtree_entry_find_child(dent, name);+		if (np == NULL || fn[0] == '\0')+			break;++		/* Find next sub directory. */+		if (!np->dir_info) {+			/* NOT Directory! */+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "`%s' is not directory, we cannot insert `%s' ",+			    np->pathname.s, file->pathname.s);+			return (ARCHIVE_FAILED);+		}+		fn += l;+		if (fn[0] == '/')+			fn++;+		dent = np;+	}+	if (np == NULL) {+		/*+		 * Create virtual parent directories.+		 */+		while (fn[0] != '\0') {+			struct mtree_entry *vp;+			struct archive_string as;++			archive_string_init(&as);+			archive_strncat(&as, p, fn - p + l);+			if (as.s[as.length-1] == '/') {+				as.s[as.length-1] = '\0';+				as.length--;+			}+			r = mtree_entry_create_virtual_dir(a, as.s, &vp);+			archive_string_free(&as);+			if (r < ARCHIVE_WARN)+				return (r);++			if (strcmp(vp->pathname.s, ".") == 0) {+				vp->parent = vp;+				mtree->root = vp;+			} else {+				__archive_rb_tree_insert_node(+				    &(dent->dir_info->rbtree),+				    (struct archive_rb_node *)vp);+				vp->parent = dent;+			}+			mtree_entry_register_add(mtree, vp);+			np = vp;++			fn += l;+			if (fn[0] == '/')+				fn++;+			l = get_path_component(name, sizeof(name), fn);+			if (l < 0) {+				archive_string_free(&as);+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "A name buffer is too small");+				return (ARCHIVE_FATAL);+			}+			dent = np;+		}++		/* Found out the parent directory where `file' can be+		 * inserted. */+		mtree->cur_dirent = dent;+		archive_string_empty(&(mtree->cur_dirstr));+		archive_string_ensure(&(mtree->cur_dirstr),+		    archive_strlen(&(dent->parentdir)) ++		    archive_strlen(&(dent->basename)) + 2);+		if (archive_strlen(&(dent->parentdir)) ++		    archive_strlen(&(dent->basename)) == 0)+			mtree->cur_dirstr.s[0] = 0;+		else {+			if (archive_strlen(&(dent->parentdir)) > 0) {+				archive_string_copy(&(mtree->cur_dirstr),+				    &(dent->parentdir));+				archive_strappend_char(+				    &(mtree->cur_dirstr), '/');+			}+			archive_string_concat(&(mtree->cur_dirstr),+			    &(dent->basename));+		}++		if (!__archive_rb_tree_insert_node(+		    &(dent->dir_info->rbtree),+		    (struct archive_rb_node *)file)) {+			np = (struct mtree_entry *)__archive_rb_tree_find_node(+			    &(dent->dir_info->rbtree), file->basename.s);+			goto same_entry;+		}+		file->parent = dent;+		mtree_entry_register_add(mtree, file);+		return (ARCHIVE_OK);+	}++same_entry:+	/*+	 * We have already has the entry the filename of which is+	 * the same.+	 */+	r = mtree_entry_exchange_same_entry(a, np, file);+	if (r < ARCHIVE_WARN)+		return (r);+	if (np->dir_info)+		np->dir_info->virtual = 0;+	*filep = np;+	mtree_entry_free(file);+	return (ARCHIVE_WARN);+}++static int+mtree_entry_exchange_same_entry(struct archive_write *a, struct mtree_entry *np,+    struct mtree_entry *file)+{++	if ((np->mode & AE_IFMT) != (file->mode & AE_IFMT)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Found duplicate entries `%s' and its file type is "+		    "different",+		    np->pathname.s);+		return (ARCHIVE_FAILED);+	}++	/* Update the existent mtree entry's attributes by the new one's. */+	archive_string_empty(&np->symlink);+	archive_string_concat(&np->symlink, &file->symlink);+	archive_string_empty(&np->uname);+	archive_string_concat(&np->uname, &file->uname);+	archive_string_empty(&np->gname);+	archive_string_concat(&np->gname, &file->gname);+	archive_string_empty(&np->fflags_text);+	archive_string_concat(&np->fflags_text, &file->fflags_text);+	np->nlink = file->nlink;+	np->filetype = file->filetype;+	np->mode = file->mode;+	np->size = file->size;+	np->uid = file->uid;+	np->gid = file->gid;+	np->fflags_set = file->fflags_set;+	np->fflags_clear = file->fflags_clear;+	np->mtime = file->mtime;+	np->mtime_nsec = file->mtime_nsec;+	np->rdevmajor = file->rdevmajor;+	np->rdevminor = file->rdevminor;+	np->devmajor = file->devmajor;+	np->devminor = file->devminor;+	np->ino = file->ino;++	return (ARCHIVE_WARN);+}
+ c/archive_write_set_format_pax.c view
@@ -0,0 +1,2044 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA+ * Copyright (c) 2016 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_pax.c 201162 2009-12-29 05:47:46Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_write_private.h"+#include "archive_write_set_format_private.h"++struct sparse_block {+	struct sparse_block	*next;+	int		is_hole;+	uint64_t	offset;+	uint64_t	remaining;+};++struct pax {+	uint64_t	entry_bytes_remaining;+	uint64_t	entry_padding;+	struct archive_string	l_url_encoded_name;+	struct archive_string	pax_header;+	struct archive_string	sparse_map;+	size_t			sparse_map_padding;+	struct sparse_block	*sparse_list;+	struct sparse_block	*sparse_tail;+	struct archive_string_conv *sconv_utf8;+	int			 opt_binary;++	unsigned flags;+#define WRITE_SCHILY_XATTR       (1 << 0)+#define WRITE_LIBARCHIVE_XATTR   (1 << 1)+};++static void		 add_pax_attr(struct archive_string *, const char *key,+			     const char *value);+static void		 add_pax_attr_binary(struct archive_string *,+			     const char *key,+			     const char *value, size_t value_len);+static void		 add_pax_attr_int(struct archive_string *,+			     const char *key, int64_t value);+static void		 add_pax_attr_time(struct archive_string *,+			     const char *key, int64_t sec,+			     unsigned long nanos);+static int		 add_pax_acl(struct archive_write *,+			    struct archive_entry *, struct pax *, int);+static ssize_t		 archive_write_pax_data(struct archive_write *,+			     const void *, size_t);+static int		 archive_write_pax_close(struct archive_write *);+static int		 archive_write_pax_free(struct archive_write *);+static int		 archive_write_pax_finish_entry(struct archive_write *);+static int		 archive_write_pax_header(struct archive_write *,+			     struct archive_entry *);+static int		 archive_write_pax_options(struct archive_write *,+			     const char *, const char *);+static char		*base64_encode(const char *src, size_t len);+static char		*build_gnu_sparse_name(char *dest, const char *src);+static char		*build_pax_attribute_name(char *dest, const char *src);+static char		*build_ustar_entry_name(char *dest, const char *src,+			     size_t src_length, const char *insert);+static char		*format_int(char *dest, int64_t);+static int		 has_non_ASCII(const char *);+static void		 sparse_list_clear(struct pax *);+static int		 sparse_list_add(struct pax *, int64_t, int64_t);+static char		*url_encode(const char *in);++/*+ * Set output format to 'restricted pax' format.+ *+ * This is the same as normal 'pax', but tries to suppress+ * the pax header whenever possible.  This is the default for+ * bsdtar, for instance.+ */+int+archive_write_set_format_pax_restricted(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	int r;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_pax_restricted");++	r = archive_write_set_format_pax(&a->archive);+	a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;+	a->archive.archive_format_name = "restricted POSIX pax interchange";+	return (r);+}++/*+ * Set output format to 'pax' format.+ */+int+archive_write_set_format_pax(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct pax *pax;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_pax");++	if (a->format_free != NULL)+		(a->format_free)(a);++	pax = (struct pax *)calloc(1, sizeof(*pax));+	if (pax == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate pax data");+		return (ARCHIVE_FATAL);+	}+	pax->flags = WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR;++	a->format_data = pax;+	a->format_name = "pax";+	a->format_options = archive_write_pax_options;+	a->format_write_header = archive_write_pax_header;+	a->format_write_data = archive_write_pax_data;+	a->format_close = archive_write_pax_close;+	a->format_free = archive_write_pax_free;+	a->format_finish_entry = archive_write_pax_finish_entry;+	a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;+	a->archive.archive_format_name = "POSIX pax interchange";+	return (ARCHIVE_OK);+}++static int+archive_write_pax_options(struct archive_write *a, const char *key,+    const char *val)+{+	struct pax *pax = (struct pax *)a->format_data;+	int ret = ARCHIVE_FAILED;++	if (strcmp(key, "hdrcharset")  == 0) {+		/*+		 * The character-set we can use are defined in+		 * IEEE Std 1003.1-2001+		 */+		if (val == NULL || val[0] == 0)+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "pax: hdrcharset option needs a character-set name");+		else if (strcmp(val, "BINARY") == 0 ||+		    strcmp(val, "binary") == 0) {+			/*+			 * Specify binary mode. We will not convert+			 * filenames, uname and gname to any charsets.+			 */+			pax->opt_binary = 1;+			ret = ARCHIVE_OK;+		} else if (strcmp(val, "UTF-8") == 0) {+			/*+			 * Specify UTF-8 character-set to be used for+			 * filenames. This is almost the test that+			 * running platform supports the string conversion.+			 * Especially libarchive_test needs this trick for+			 * its test.+			 */+			pax->sconv_utf8 = archive_string_conversion_to_charset(+			    &(a->archive), "UTF-8", 0);+			if (pax->sconv_utf8 == NULL)+				ret = ARCHIVE_FATAL;+			else+				ret = ARCHIVE_OK;+		} else+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "pax: invalid charset name");+		return (ret);+	} else if (strcmp(key, "xattrheader") == 0) {+		if (val == NULL || val[0] == 0) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "pax: xattrheader requires a value");+		} else if (strcmp(val, "ALL") == 0 ||+		    strcmp(val, "all") == 0) {+			pax->flags |= WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR;+			ret = ARCHIVE_OK;+		} else if (strcmp(val, "SCHILY") == 0 ||+		    strcmp(val, "schily") == 0) {+			pax->flags |= WRITE_SCHILY_XATTR;+			pax->flags &= ~WRITE_LIBARCHIVE_XATTR;+			ret = ARCHIVE_OK;+		} else if (strcmp(val, "LIBARCHIVE") == 0 ||+		    strcmp(val, "libarchive") == 0) {+			pax->flags |= WRITE_LIBARCHIVE_XATTR;+			pax->flags &= ~WRITE_SCHILY_XATTR;+			ret = ARCHIVE_OK;+		} else+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "pax: invalid xattr header name");+		return (ret);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++/*+ * Note: This code assumes that 'nanos' has the same sign as 'sec',+ * which implies that sec=-1, nanos=200000000 represents -1.2 seconds+ * and not -0.8 seconds.  This is a pretty pedantic point, as we're+ * unlikely to encounter many real files created before Jan 1, 1970,+ * much less ones with timestamps recorded to sub-second resolution.+ */+static void+add_pax_attr_time(struct archive_string *as, const char *key,+    int64_t sec, unsigned long nanos)+{+	int digit, i;+	char *t;+	/*+	 * Note that each byte contributes fewer than 3 base-10+	 * digits, so this will always be big enough.+	 */+	char tmp[1 + 3*sizeof(sec) + 1 + 3*sizeof(nanos)];++	tmp[sizeof(tmp) - 1] = 0;+	t = tmp + sizeof(tmp) - 1;++	/* Skip trailing zeros in the fractional part. */+	for (digit = 0, i = 10; i > 0 && digit == 0; i--) {+		digit = nanos % 10;+		nanos /= 10;+	}++	/* Only format the fraction if it's non-zero. */+	if (i > 0) {+		while (i > 0) {+			*--t = "0123456789"[digit];+			digit = nanos % 10;+			nanos /= 10;+			i--;+		}+		*--t = '.';+	}+	t = format_int(t, sec);++	add_pax_attr(as, key, t);+}++static char *+format_int(char *t, int64_t i)+{+	uint64_t ui;++	if (i < 0) +		ui = (i == INT64_MIN) ? (uint64_t)(INT64_MAX) + 1 : (uint64_t)(-i);+	else+		ui = i;++	do {+		*--t = "0123456789"[ui % 10];+	} while (ui /= 10);+	if (i < 0)+		*--t = '-';+	return (t);+}++static void+add_pax_attr_int(struct archive_string *as, const char *key, int64_t value)+{+	char tmp[1 + 3 * sizeof(value)];++	tmp[sizeof(tmp) - 1] = 0;+	add_pax_attr(as, key, format_int(tmp + sizeof(tmp) - 1, value));+}++/*+ * Add a key/value attribute to the pax header.  This function handles+ * the length field and various other syntactic requirements.+ */+static void+add_pax_attr(struct archive_string *as, const char *key, const char *value)+{+	add_pax_attr_binary(as, key, value, strlen(value));+}++/*+ * Add a key/value attribute to the pax header.  This function handles+ * binary values.+ */+static void+add_pax_attr_binary(struct archive_string *as, const char *key,+		    const char *value, size_t value_len)+{+	int digits, i, len, next_ten;+	char tmp[1 + 3 * sizeof(int)];	/* < 3 base-10 digits per byte */++	/*-+	 * PAX attributes have the following layout:+	 *     <len> <space> <key> <=> <value> <nl>+	 */+	len = 1 + (int)strlen(key) + 1 + (int)value_len + 1;++	/*+	 * The <len> field includes the length of the <len> field, so+	 * computing the correct length is tricky.  I start by+	 * counting the number of base-10 digits in 'len' and+	 * computing the next higher power of 10.+	 */+	next_ten = 1;+	digits = 0;+	i = len;+	while (i > 0) {+		i = i / 10;+		digits++;+		next_ten = next_ten * 10;+	}+	/*+	 * For example, if string without the length field is 99+	 * chars, then adding the 2 digit length "99" will force the+	 * total length past 100, requiring an extra digit.  The next+	 * statement adjusts for this effect.+	 */+	if (len + digits >= next_ten)+		digits++;++	/* Now, we have the right length so we can build the line. */+	tmp[sizeof(tmp) - 1] = 0;	/* Null-terminate the work area. */+	archive_strcat(as, format_int(tmp + sizeof(tmp) - 1, len + digits));+	archive_strappend_char(as, ' ');+	archive_strcat(as, key);+	archive_strappend_char(as, '=');+	archive_array_append(as, value, value_len);+	archive_strappend_char(as, '\n');+}++static void+archive_write_pax_header_xattr(struct pax *pax, const char *encoded_name,+    const void *value, size_t value_len)+{+	struct archive_string s;+	char *encoded_value;++	if (pax->flags & WRITE_LIBARCHIVE_XATTR) {+		encoded_value = base64_encode((const char *)value, value_len);++		if (encoded_name != NULL && encoded_value != NULL) {+			archive_string_init(&s);+			archive_strcpy(&s, "LIBARCHIVE.xattr.");+			archive_strcat(&s, encoded_name);+			add_pax_attr(&(pax->pax_header), s.s, encoded_value);+			archive_string_free(&s);+		}+		free(encoded_value);+	}+	if (pax->flags & WRITE_SCHILY_XATTR) {+		archive_string_init(&s);+		archive_strcpy(&s, "SCHILY.xattr.");+		archive_strcat(&s, encoded_name);+		add_pax_attr_binary(&(pax->pax_header), s.s, value, value_len);+		archive_string_free(&s);+	}+}++static int+archive_write_pax_header_xattrs(struct archive_write *a,+    struct pax *pax, struct archive_entry *entry)+{+	int i = archive_entry_xattr_reset(entry);++	while (i--) {+		const char *name;+		const void *value;+		char *url_encoded_name = NULL, *encoded_name = NULL;+		size_t size;+		int r;++		archive_entry_xattr_next(entry, &name, &value, &size);+		url_encoded_name = url_encode(name);+		if (url_encoded_name != NULL) {+			/* Convert narrow-character to UTF-8. */+			r = archive_strcpy_l(&(pax->l_url_encoded_name),+			    url_encoded_name, pax->sconv_utf8);+			free(url_encoded_name); /* Done with this. */+			if (r == 0)+				encoded_name = pax->l_url_encoded_name.s;+			else if (errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for Linkname");+				return (ARCHIVE_FATAL);+			}+		}++		archive_write_pax_header_xattr(pax, encoded_name,+		    value, size);++	}+	return (ARCHIVE_OK);+}++static int+get_entry_hardlink(struct archive_write *a, struct archive_entry *entry,+    const char **name, size_t *length, struct archive_string_conv *sc)+{+	int r;+	+	r = archive_entry_hardlink_l(entry, name, length, sc);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Linkname");+			return (ARCHIVE_FATAL);+		}+		return (ARCHIVE_WARN);+	}+	return (ARCHIVE_OK);+}++static int+get_entry_pathname(struct archive_write *a, struct archive_entry *entry,+    const char **name, size_t *length, struct archive_string_conv *sc)+{+	int r;++	r = archive_entry_pathname_l(entry, name, length, sc);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathname");+			return (ARCHIVE_FATAL);+		}+		return (ARCHIVE_WARN);+	}+	return (ARCHIVE_OK);+}++static int+get_entry_uname(struct archive_write *a, struct archive_entry *entry,+    const char **name, size_t *length, struct archive_string_conv *sc)+{+	int r;++	r = archive_entry_uname_l(entry, name, length, sc);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Uname");+			return (ARCHIVE_FATAL);+		}+		return (ARCHIVE_WARN);+	}+	return (ARCHIVE_OK);+}++static int+get_entry_gname(struct archive_write *a, struct archive_entry *entry,+    const char **name, size_t *length, struct archive_string_conv *sc)+{+	int r;++	r = archive_entry_gname_l(entry, name, length, sc);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Gname");+			return (ARCHIVE_FATAL);+		}+		return (ARCHIVE_WARN);+	}+	return (ARCHIVE_OK);+}++static int+get_entry_symlink(struct archive_write *a, struct archive_entry *entry,+    const char **name, size_t *length, struct archive_string_conv *sc)+{+	int r;++	r = archive_entry_symlink_l(entry, name, length, sc);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Linkname");+			return (ARCHIVE_FATAL);+		}+		return (ARCHIVE_WARN);+	}+	return (ARCHIVE_OK);+}++/* Add ACL to pax header */+static int+add_pax_acl(struct archive_write *a,+    struct archive_entry *entry, struct pax *pax, int flags)+{+	char *p;+	const char *attr;+	int acl_types;++	acl_types = archive_entry_acl_types(entry);++	if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)+		attr = "SCHILY.acl.ace";+	else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)+		attr = "SCHILY.acl.access";+	else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)+		attr = "SCHILY.acl.default";+	else+		return (ARCHIVE_FATAL);++	p = archive_entry_acl_to_text_l(entry, NULL, flags, pax->sconv_utf8);+	if (p == NULL) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM, "%s %s",+			    "Can't allocate memory for ", attr);+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT, "%s %s %s",+		    "Can't translate ", attr, " to UTF-8");+		return(ARCHIVE_WARN);+	}++	if (*p != '\0') {+		add_pax_attr(&(pax->pax_header),+		    attr, p);+	}+	free(p);+	return(ARCHIVE_OK);+}++/*+ * TODO: Consider adding 'comment' and 'charset' fields to+ * archive_entry so that clients can specify them.  Also, consider+ * adding generic key/value tags so clients can add arbitrary+ * key/value data.+ *+ * TODO: Break up this 700-line function!!!!  Yowza!+ */+static int+archive_write_pax_header(struct archive_write *a,+    struct archive_entry *entry_original)+{+	struct archive_entry *entry_main;+	const char *p;+	const char *suffix;+	int need_extension, r, ret;+	int acl_types;+	int sparse_count;+	uint64_t sparse_total, real_size;+	struct pax *pax;+	const char *hardlink;+	const char *path = NULL, *linkpath = NULL;+	const char *uname = NULL, *gname = NULL;+	const void *mac_metadata;+	size_t mac_metadata_size;+	struct archive_string_conv *sconv;+	size_t hardlink_length, path_length, linkpath_length;+	size_t uname_length, gname_length;++	char paxbuff[512];+	char ustarbuff[512];+	char ustar_entry_name[256];+	char pax_entry_name[256];+	char gnu_sparse_name[256];+	struct archive_string entry_name;++	ret = ARCHIVE_OK;+	need_extension = 0;+	pax = (struct pax *)a->format_data;++	/* Sanity check. */+	if (archive_entry_pathname(entry_original) == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			  "Can't record entry in tar file without pathname");+		return (ARCHIVE_FAILED);+	}++	/*+	 * Choose a header encoding.+	 */+	if (pax->opt_binary)+		sconv = NULL;/* Binary mode. */+	else {+		/* Header encoding is UTF-8. */+		if (pax->sconv_utf8 == NULL) {+			/* Initialize the string conversion object+			 * we must need */+			pax->sconv_utf8 = archive_string_conversion_to_charset(+			    &(a->archive), "UTF-8", 1);+			if (pax->sconv_utf8 == NULL)+				/* Couldn't allocate memory */+				return (ARCHIVE_FAILED);+		}+		sconv = pax->sconv_utf8;+	}++	r = get_entry_hardlink(a, entry_original, &hardlink,+	    &hardlink_length, sconv);+	if (r == ARCHIVE_FATAL)+		return (r);+	else if (r != ARCHIVE_OK) {+		r = get_entry_hardlink(a, entry_original, &hardlink,+		    &hardlink_length, NULL);+		if (r == ARCHIVE_FATAL)+			return (r);+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate linkname '%s' to %s", hardlink,+		    archive_string_conversion_charset_name(sconv));+		ret = ARCHIVE_WARN;+		sconv = NULL;/* The header charset switches to binary mode. */+	}++	/* Make sure this is a type of entry that we can handle here */+	if (hardlink == NULL) {+		switch (archive_entry_filetype(entry_original)) {+		case AE_IFBLK:+		case AE_IFCHR:+		case AE_IFIFO:+		case AE_IFLNK:+		case AE_IFREG:+			break;+		case AE_IFDIR:+		{+			/*+			 * Ensure a trailing '/'.  Modify the original+			 * entry so the client sees the change.+			 */+#if defined(_WIN32) && !defined(__CYGWIN__)+			const wchar_t *wp;++			wp = archive_entry_pathname_w(entry_original);+			if (wp != NULL && wp[wcslen(wp) -1] != L'/') {+				struct archive_wstring ws;++				archive_string_init(&ws);+				path_length = wcslen(wp);+				if (archive_wstring_ensure(&ws,+				    path_length + 2) == NULL) {+					archive_set_error(&a->archive, ENOMEM,+					    "Can't allocate pax data");+					archive_wstring_free(&ws);+					return(ARCHIVE_FATAL);+				}+				/* Should we keep '\' ? */+				if (wp[path_length -1] == L'\\')+					path_length--;+				archive_wstrncpy(&ws, wp, path_length);+				archive_wstrappend_wchar(&ws, L'/');+				archive_entry_copy_pathname_w(+				    entry_original, ws.s);+				archive_wstring_free(&ws);+				p = NULL;+			} else+#endif+				p = archive_entry_pathname(entry_original);+			/*+			 * On Windows, this is a backup operation just in+			 * case getting WCS failed. On POSIX, this is a+			 * normal operation.+			 */+			if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {+				struct archive_string as;++				archive_string_init(&as);+				path_length = strlen(p);+				if (archive_string_ensure(&as,+				    path_length + 2) == NULL) {+					archive_set_error(&a->archive, ENOMEM,+					    "Can't allocate pax data");+					archive_string_free(&as);+					return(ARCHIVE_FATAL);+				}+#if defined(_WIN32) && !defined(__CYGWIN__)+				/* NOTE: This might break the pathname+				 * if the current code page is CP932 and+				 * the pathname includes a character '\'+				 * as a part of its multibyte pathname. */+				if (p[strlen(p) -1] == '\\')+					path_length--;+				else+#endif+				archive_strncpy(&as, p, path_length);+				archive_strappend_char(&as, '/');+				archive_entry_copy_pathname(+				    entry_original, as.s);+				archive_string_free(&as);+			}+			break;+		}+		default: /* AE_IFSOCK and unknown */+			__archive_write_entry_filetype_unsupported(+			    &a->archive, entry_original, "pax");+			return (ARCHIVE_FAILED);+		}+	}++	/*+	 * If Mac OS metadata blob is here, recurse to write that+	 * as a separate entry.  This is really a pretty poor design:+	 * In particular, it doubles the overhead for long filenames.+	 * TODO: Help Apple folks design something better and figure+	 * out how to transition from this legacy format.+	 *+	 * Note that this code is present on every platform; clients+	 * on non-Mac are unlikely to ever provide this data, but+	 * applications that copy entries from one archive to another+	 * should not lose data just because the local filesystem+	 * can't store it.+	 */+	mac_metadata =+	    archive_entry_mac_metadata(entry_original, &mac_metadata_size);+	if (mac_metadata != NULL) {+		const char *oname;+		char *name, *bname;+		size_t name_length;+		struct archive_entry *extra = archive_entry_new2(&a->archive);++		oname = archive_entry_pathname(entry_original);+		name_length = strlen(oname);+		name = malloc(name_length + 3);+		if (name == NULL || extra == NULL) {+			/* XXX error message */+			archive_entry_free(extra);+			free(name);+			return (ARCHIVE_FAILED);+		}+		strcpy(name, oname);+		/* Find last '/'; strip trailing '/' characters */+		bname = strrchr(name, '/');+		while (bname != NULL && bname[1] == '\0') {+			*bname = '\0';+			bname = strrchr(name, '/');+		}+		if (bname == NULL) {+			memmove(name + 2, name, name_length + 1);+			memmove(name, "._", 2);+		} else {+			bname += 1;+			memmove(bname + 2, bname, strlen(bname) + 1);+			memmove(bname, "._", 2);+		}+		archive_entry_copy_pathname(extra, name);+		free(name);++		archive_entry_set_size(extra, mac_metadata_size);+		archive_entry_set_filetype(extra, AE_IFREG);+		archive_entry_set_perm(extra,+		    archive_entry_perm(entry_original));+		archive_entry_set_mtime(extra,+		    archive_entry_mtime(entry_original),+		    archive_entry_mtime_nsec(entry_original));+		archive_entry_set_gid(extra,+		    archive_entry_gid(entry_original));+		archive_entry_set_gname(extra,+		    archive_entry_gname(entry_original));+		archive_entry_set_uid(extra,+		    archive_entry_uid(entry_original));+		archive_entry_set_uname(extra,+		    archive_entry_uname(entry_original));++		/* Recurse to write the special copyfile entry. */+		r = archive_write_pax_header(a, extra);+		archive_entry_free(extra);+		if (r < ARCHIVE_WARN)+			return (r);+		if (r < ret)+			ret = r;+		r = (int)archive_write_pax_data(a, mac_metadata,+		    mac_metadata_size);+		if (r < ARCHIVE_WARN)+			return (r);+		if (r < ret)+			ret = r;+		r = archive_write_pax_finish_entry(a);+		if (r < ARCHIVE_WARN)+			return (r);+		if (r < ret)+			ret = r;+	}++	/* Copy entry so we can modify it as needed. */+#if defined(_WIN32) && !defined(__CYGWIN__)+	/* Make sure the path separators in pathname, hardlink and symlink+	 * are all slash '/', not the Windows path separator '\'. */+	entry_main = __la_win_entry_in_posix_pathseparator(entry_original);+	if (entry_main == entry_original)+		entry_main = archive_entry_clone(entry_original);+#else+	entry_main = archive_entry_clone(entry_original);+#endif+	if (entry_main == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate pax data");+		return(ARCHIVE_FATAL);+	}+	archive_string_empty(&(pax->pax_header)); /* Blank our work area. */+	archive_string_empty(&(pax->sparse_map));+	sparse_total = 0;+	sparse_list_clear(pax);++	if (hardlink == NULL &&+	    archive_entry_filetype(entry_main) == AE_IFREG)+		sparse_count = archive_entry_sparse_reset(entry_main);+	else+		sparse_count = 0;+	if (sparse_count) {+		int64_t offset, length, last_offset = 0;+		/* Get the last entry of sparse block. */+		while (archive_entry_sparse_next(+		    entry_main, &offset, &length) == ARCHIVE_OK)+			last_offset = offset + length;++		/* If the last sparse block does not reach the end of file,+		 * We have to add a empty sparse block as the last entry to+		 * manage storing file data. */+		if (last_offset < archive_entry_size(entry_main))+			archive_entry_sparse_add_entry(entry_main,+			    archive_entry_size(entry_main), 0);+		sparse_count = archive_entry_sparse_reset(entry_main);+	}++	/*+	 * First, check the name fields and see if any of them+	 * require binary coding.  If any of them does, then all of+	 * them do.+	 */+	r = get_entry_pathname(a, entry_main, &path, &path_length, sconv);+	if (r == ARCHIVE_FATAL) {+		archive_entry_free(entry_main);+		return (r);+	} else if (r != ARCHIVE_OK) {+		r = get_entry_pathname(a, entry_main, &path,+		    &path_length, NULL);+		if (r == ARCHIVE_FATAL) {+			archive_entry_free(entry_main);+			return (r);+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate pathname '%s' to %s", path,+		    archive_string_conversion_charset_name(sconv));+		ret = ARCHIVE_WARN;+		sconv = NULL;/* The header charset switches to binary mode. */+	}+	r = get_entry_uname(a, entry_main, &uname, &uname_length, sconv);+	if (r == ARCHIVE_FATAL) {+		archive_entry_free(entry_main);+		return (r);+	} else if (r != ARCHIVE_OK) {+		r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL);+		if (r == ARCHIVE_FATAL) {+			archive_entry_free(entry_main);+			return (r);+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate uname '%s' to %s", uname,+		    archive_string_conversion_charset_name(sconv));+		ret = ARCHIVE_WARN;+		sconv = NULL;/* The header charset switches to binary mode. */+	}+	r = get_entry_gname(a, entry_main, &gname, &gname_length, sconv);+	if (r == ARCHIVE_FATAL) {+		archive_entry_free(entry_main);+		return (r);+	} else if (r != ARCHIVE_OK) {+		r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL);+		if (r == ARCHIVE_FATAL) {+			archive_entry_free(entry_main);+			return (r);+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate gname '%s' to %s", gname,+		    archive_string_conversion_charset_name(sconv));+		ret = ARCHIVE_WARN;+		sconv = NULL;/* The header charset switches to binary mode. */+	}+	linkpath = hardlink;+	linkpath_length = hardlink_length;+	if (linkpath == NULL) {+		r = get_entry_symlink(a, entry_main, &linkpath,+		    &linkpath_length, sconv);+		if (r == ARCHIVE_FATAL) {+			archive_entry_free(entry_main);+			return (r);+		} else if (r != ARCHIVE_OK) {+			r = get_entry_symlink(a, entry_main, &linkpath,+			    &linkpath_length, NULL);+			if (r == ARCHIVE_FATAL) {+				archive_entry_free(entry_main);+				return (r);+			}+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Can't translate linkname '%s' to %s", linkpath,+			    archive_string_conversion_charset_name(sconv));+			ret = ARCHIVE_WARN;+			sconv = NULL;+		}+	}++	/* If any string conversions failed, get all attributes+	 * in binary-mode. */+	if (sconv == NULL && !pax->opt_binary) {+		if (hardlink != NULL) {+			r = get_entry_hardlink(a, entry_main, &hardlink,+			    &hardlink_length, NULL);+			if (r == ARCHIVE_FATAL) {+				archive_entry_free(entry_main);+				return (r);+			}+			linkpath = hardlink;+			linkpath_length = hardlink_length;+		}+		r = get_entry_pathname(a, entry_main, &path,+		    &path_length, NULL);+		if (r == ARCHIVE_FATAL) {+			archive_entry_free(entry_main);+			return (r);+		}+		r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL);+		if (r == ARCHIVE_FATAL) {+			archive_entry_free(entry_main);+			return (r);+		}+		r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL);+		if (r == ARCHIVE_FATAL) {+			archive_entry_free(entry_main);+			return (r);+		}+	}++	/* Store the header encoding first, to be nice to readers. */+	if (sconv == NULL)+		add_pax_attr(&(pax->pax_header), "hdrcharset", "BINARY");+++	/*+	 * If name is too long, or has non-ASCII characters, add+	 * 'path' to pax extended attrs.  (Note that an unconvertible+	 * name must have non-ASCII characters.)+	 */+	if (has_non_ASCII(path)) {+		/* We have non-ASCII characters. */+		add_pax_attr(&(pax->pax_header), "path", path);+		archive_entry_set_pathname(entry_main,+		    build_ustar_entry_name(ustar_entry_name,+			path, path_length, NULL));+		need_extension = 1;+	} else {+		/* We have an all-ASCII path; we'd like to just store+		 * it in the ustar header if it will fit.  Yes, this+		 * duplicates some of the logic in+		 * archive_write_set_format_ustar.c+		 */+		if (path_length <= 100) {+			/* Fits in the old 100-char tar name field. */+		} else {+			/* Find largest suffix that will fit. */+			/* Note: strlen() > 100, so strlen() - 100 - 1 >= 0 */+			suffix = strchr(path + path_length - 100 - 1, '/');+			/* Don't attempt an empty prefix. */+			if (suffix == path)+				suffix = strchr(suffix + 1, '/');+			/* We can put it in the ustar header if it's+			 * all ASCII and it's either <= 100 characters+			 * or can be split at a '/' into a prefix <=+			 * 155 chars and a suffix <= 100 chars.  (Note+			 * the strchr() above will return NULL exactly+			 * when the path can't be split.)+			 */+			if (suffix == NULL       /* Suffix > 100 chars. */+			    || suffix[1] == '\0'    /* empty suffix */+			    || suffix - path > 155)  /* Prefix > 155 chars */+			{+				add_pax_attr(&(pax->pax_header), "path", path);+				archive_entry_set_pathname(entry_main,+				    build_ustar_entry_name(ustar_entry_name,+					path, path_length, NULL));+				need_extension = 1;+			}+		}+	}++	if (linkpath != NULL) {+		/* If link name is too long or has non-ASCII characters, add+		 * 'linkpath' to pax extended attrs. */+		if (linkpath_length > 100 || has_non_ASCII(linkpath)) {+			add_pax_attr(&(pax->pax_header), "linkpath", linkpath);+			if (linkpath_length > 100) {+				if (hardlink != NULL)+					archive_entry_set_hardlink(entry_main,+					    "././@LongHardLink");+				else+					archive_entry_set_symlink(entry_main,+					    "././@LongSymLink");+			}+			need_extension = 1;+		}+	}+	/* Save a pathname since it will be renamed if `entry_main` has+	 * sparse blocks. */+	archive_string_init(&entry_name);+	archive_strcpy(&entry_name, archive_entry_pathname(entry_main));++	/* If file size is too large, add 'size' to pax extended attrs. */+	if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) {+		add_pax_attr_int(&(pax->pax_header), "size",+		    archive_entry_size(entry_main));+		need_extension = 1;+	}++	/* If numeric GID is too large, add 'gid' to pax extended attrs. */+	if ((unsigned int)archive_entry_gid(entry_main) >= (1 << 18)) {+		add_pax_attr_int(&(pax->pax_header), "gid",+		    archive_entry_gid(entry_main));+		need_extension = 1;+	}++	/* If group name is too large or has non-ASCII characters, add+	 * 'gname' to pax extended attrs. */+	if (gname != NULL) {+		if (gname_length > 31 || has_non_ASCII(gname)) {+			add_pax_attr(&(pax->pax_header), "gname", gname);+			need_extension = 1;+		}+	}++	/* If numeric UID is too large, add 'uid' to pax extended attrs. */+	if ((unsigned int)archive_entry_uid(entry_main) >= (1 << 18)) {+		add_pax_attr_int(&(pax->pax_header), "uid",+		    archive_entry_uid(entry_main));+		need_extension = 1;+	}++	/* Add 'uname' to pax extended attrs if necessary. */+	if (uname != NULL) {+		if (uname_length > 31 || has_non_ASCII(uname)) {+			add_pax_attr(&(pax->pax_header), "uname", uname);+			need_extension = 1;+		}+	}++	/*+	 * POSIX/SUSv3 doesn't provide a standard key for large device+	 * numbers.  I use the same keys here that Joerg Schilling+	 * used for 'star.'  (Which, somewhat confusingly, are called+	 * "devXXX" even though they code "rdev" values.)  No doubt,+	 * other implementations use other keys.  Note that there's no+	 * reason we can't write the same information into a number of+	 * different keys.+	 *+	 * Of course, this is only needed for block or char device entries.+	 */+	if (archive_entry_filetype(entry_main) == AE_IFBLK+	    || archive_entry_filetype(entry_main) == AE_IFCHR) {+		/*+		 * If rdevmajor is too large, add 'SCHILY.devmajor' to+		 * extended attributes.+		 */+		int rdevmajor, rdevminor;+		rdevmajor = archive_entry_rdevmajor(entry_main);+		rdevminor = archive_entry_rdevminor(entry_main);+		if (rdevmajor >= (1 << 18)) {+			add_pax_attr_int(&(pax->pax_header), "SCHILY.devmajor",+			    rdevmajor);+			/*+			 * Non-strict formatting below means we don't+			 * have to truncate here.  Not truncating improves+			 * the chance that some more modern tar archivers+			 * (such as GNU tar 1.13) can restore the full+			 * value even if they don't understand the pax+			 * extended attributes.  See my rant below about+			 * file size fields for additional details.+			 */+			/* archive_entry_set_rdevmajor(entry_main,+			   rdevmajor & ((1 << 18) - 1)); */+			need_extension = 1;+		}++		/*+		 * If devminor is too large, add 'SCHILY.devminor' to+		 * extended attributes.+		 */+		if (rdevminor >= (1 << 18)) {+			add_pax_attr_int(&(pax->pax_header), "SCHILY.devminor",+			    rdevminor);+			/* Truncation is not necessary here, either. */+			/* archive_entry_set_rdevminor(entry_main,+			   rdevminor & ((1 << 18) - 1)); */+			need_extension = 1;+		}+	}++	/*+	 * Technically, the mtime field in the ustar header can+	 * support 33 bits, but many platforms use signed 32-bit time+	 * values.  The cutoff of 0x7fffffff here is a compromise.+	 * Yes, this check is duplicated just below; this helps to+	 * avoid writing an mtime attribute just to handle a+	 * high-resolution timestamp in "restricted pax" mode.+	 */+	if (!need_extension &&+	    ((archive_entry_mtime(entry_main) < 0)+		|| (archive_entry_mtime(entry_main) >= 0x7fffffff)))+		need_extension = 1;++	/* I use a star-compatible file flag attribute. */+	p = archive_entry_fflags_text(entry_main);+	if (!need_extension && p != NULL  &&  *p != '\0')+		need_extension = 1;++	/* If there are extended attributes, we need an extension */+	if (!need_extension && archive_entry_xattr_count(entry_original) > 0)+		need_extension = 1;++	/* If there are sparse info, we need an extension */+	if (!need_extension && sparse_count > 0)+		need_extension = 1;++	acl_types = archive_entry_acl_types(entry_original);++	/* If there are any ACL entries, we need an extension */+	if (!need_extension && acl_types != 0)+		need_extension = 1;++	/* If the symlink type is defined, we need an extension */+	if (!need_extension && archive_entry_symlink_type(entry_main) > 0)+		need_extension = 1;++	/*+	 * Libarchive used to include these in extended headers for+	 * restricted pax format, but that confused people who+	 * expected ustar-like time semantics.  So now we only include+	 * them in full pax format.+	 */+	if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED) {+		if (archive_entry_ctime(entry_main) != 0  ||+		    archive_entry_ctime_nsec(entry_main) != 0)+			add_pax_attr_time(&(pax->pax_header), "ctime",+			    archive_entry_ctime(entry_main),+			    archive_entry_ctime_nsec(entry_main));++		if (archive_entry_atime(entry_main) != 0 ||+		    archive_entry_atime_nsec(entry_main) != 0)+			add_pax_attr_time(&(pax->pax_header), "atime",+			    archive_entry_atime(entry_main),+			    archive_entry_atime_nsec(entry_main));++		/* Store birth/creationtime only if it's earlier than mtime */+		if (archive_entry_birthtime_is_set(entry_main) &&+		    archive_entry_birthtime(entry_main)+		    < archive_entry_mtime(entry_main))+			add_pax_attr_time(&(pax->pax_header),+			    "LIBARCHIVE.creationtime",+			    archive_entry_birthtime(entry_main),+			    archive_entry_birthtime_nsec(entry_main));+	}++	/*+	 * The following items are handled differently in "pax+	 * restricted" format.  In particular, in "pax restricted"+	 * format they won't be added unless need_extension is+	 * already set (we're already generating an extended header, so+	 * may as well include these).+	 */+	if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||+	    need_extension) {+		if (archive_entry_mtime(entry_main) < 0  ||+		    archive_entry_mtime(entry_main) >= 0x7fffffff  ||+		    archive_entry_mtime_nsec(entry_main) != 0)+			add_pax_attr_time(&(pax->pax_header), "mtime",+			    archive_entry_mtime(entry_main),+			    archive_entry_mtime_nsec(entry_main));++		/* I use a star-compatible file flag attribute. */+		p = archive_entry_fflags_text(entry_main);+		if (p != NULL  &&  *p != '\0')+			add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p);++		/* I use star-compatible ACL attributes. */+		if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {+			ret = add_pax_acl(a, entry_original, pax,+			    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |+			    ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA |+			    ARCHIVE_ENTRY_ACL_STYLE_COMPACT);+			if (ret == ARCHIVE_FATAL) {+				archive_entry_free(entry_main);+				archive_string_free(&entry_name);+				return (ARCHIVE_FATAL);+			}+		}+		if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {+			ret = add_pax_acl(a, entry_original, pax,+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS |+			    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |+			    ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);+			if (ret == ARCHIVE_FATAL) {+				archive_entry_free(entry_main);+				archive_string_free(&entry_name);+				return (ARCHIVE_FATAL);+			}+		}+		if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) {+			ret = add_pax_acl(a, entry_original, pax,+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |+			    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |+			    ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);+			if (ret == ARCHIVE_FATAL) {+				archive_entry_free(entry_main);+				archive_string_free(&entry_name);+				return (ARCHIVE_FATAL);+			}+		}++		/* We use GNU-tar-compatible sparse attributes. */+		if (sparse_count > 0) {+			int64_t soffset, slength;++			add_pax_attr_int(&(pax->pax_header),+			    "GNU.sparse.major", 1);+			add_pax_attr_int(&(pax->pax_header),+			    "GNU.sparse.minor", 0);+			/*+			 * Make sure to store the original path, since+			 * truncation to ustar limit happened already.+			 */+			add_pax_attr(&(pax->pax_header),+			    "GNU.sparse.name", path);+			add_pax_attr_int(&(pax->pax_header),+			    "GNU.sparse.realsize",+			    archive_entry_size(entry_main));++			/* Rename the file name which will be used for+			 * ustar header to a special name, which GNU+			 * PAX Format 1.0 requires */+			archive_entry_set_pathname(entry_main,+			    build_gnu_sparse_name(gnu_sparse_name,+			        entry_name.s));++			/*+			 * - Make a sparse map, which will precede a file data.+			 * - Get the total size of available data of sparse.+			 */+			archive_string_sprintf(&(pax->sparse_map), "%d\n",+			    sparse_count);+			while (archive_entry_sparse_next(entry_main,+			    &soffset, &slength) == ARCHIVE_OK) {+				archive_string_sprintf(&(pax->sparse_map),+				    "%jd\n%jd\n",+				    (intmax_t)soffset,+				    (intmax_t)slength);+				sparse_total += slength;+				if (sparse_list_add(pax, soffset, slength)+				    != ARCHIVE_OK) {+					archive_set_error(&a->archive,+					    ENOMEM,+					    "Can't allocate memory");+					archive_entry_free(entry_main);+					archive_string_free(&entry_name);+					return (ARCHIVE_FATAL);+				}+			}+		}++		/* Store extended attributes */+		if (archive_write_pax_header_xattrs(a, pax, entry_original)+		    == ARCHIVE_FATAL) {+			archive_entry_free(entry_main);+			archive_string_free(&entry_name);+			return (ARCHIVE_FATAL);+		}++		/* Store extended symlink information */+		if (archive_entry_symlink_type(entry_main) ==+		    AE_SYMLINK_TYPE_FILE) {+			add_pax_attr(&(pax->pax_header),+			    "LIBARCHIVE.symlinktype", "file");+		} else if (archive_entry_symlink_type(entry_main) ==+		    AE_SYMLINK_TYPE_DIRECTORY) {+			add_pax_attr(&(pax->pax_header),+			    "LIBARCHIVE.symlinktype", "dir");+		}+	}++	/* Only regular files have data. */+	if (archive_entry_filetype(entry_main) != AE_IFREG)+		archive_entry_set_size(entry_main, 0);++	/*+	 * Pax-restricted does not store data for hardlinks, in order+	 * to improve compatibility with ustar.+	 */+	if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE &&+	    hardlink != NULL)+		archive_entry_set_size(entry_main, 0);++	/*+	 * XXX Full pax interchange format does permit a hardlink+	 * entry to have data associated with it.  I'm not supporting+	 * that here because the client expects me to tell them whether+	 * or not this format expects data for hardlinks.  If I+	 * don't check here, then every pax archive will end up with+	 * duplicated data for hardlinks.  Someday, there may be+	 * need to select this behavior, in which case the following+	 * will need to be revisited. XXX+	 */+	if (hardlink != NULL)+		archive_entry_set_size(entry_main, 0);++	/* Save a real file size. */+	real_size = archive_entry_size(entry_main);+	/*+	 * Overwrite a file size by the total size of sparse blocks and+	 * the size of sparse map info. That file size is the length of+	 * the data, which we will exactly store into an archive file.+	 */+	if (archive_strlen(&(pax->sparse_map))) {+		size_t mapsize = archive_strlen(&(pax->sparse_map));+		pax->sparse_map_padding = 0x1ff & (-(ssize_t)mapsize);+		archive_entry_set_size(entry_main,+		    mapsize + pax->sparse_map_padding + sparse_total);+	}++	/* Format 'ustar' header for main entry.+	 *+	 * The trouble with file size: If the reader can't understand+	 * the file size, they may not be able to locate the next+	 * entry and the rest of the archive is toast.  Pax-compliant+	 * readers are supposed to ignore the file size in the main+	 * header, so the question becomes how to maximize portability+	 * for readers that don't support pax attribute extensions.+	 * For maximum compatibility, I permit numeric extensions in+	 * the main header so that the file size stored will always be+	 * correct, even if it's in a format that only some+	 * implementations understand.  The technique used here is:+	 *+	 *  a) If possible, follow the standard exactly.  This handles+	 *  files up to 8 gigabytes minus 1.+	 *+	 *  b) If that fails, try octal but omit the field terminator.+	 *  That handles files up to 64 gigabytes minus 1.+	 *+	 *  c) Otherwise, use base-256 extensions.  That handles files+	 *  up to 2^63 in this implementation, with the potential to+	 *  go up to 2^94.  That should hold us for a while. ;-)+	 *+	 * The non-strict formatter uses similar logic for other+	 * numeric fields, though they're less critical.+	 */+	if (__archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0,+	    NULL) == ARCHIVE_FATAL) {+		archive_entry_free(entry_main);+		archive_string_free(&entry_name);+		return (ARCHIVE_FATAL);+	}++	/* If we built any extended attributes, write that entry first. */+	if (archive_strlen(&(pax->pax_header)) > 0) {+		struct archive_entry *pax_attr_entry;+		time_t s;+		int64_t uid, gid;+		int mode;++		pax_attr_entry = archive_entry_new2(&a->archive);+		p = entry_name.s;+		archive_entry_set_pathname(pax_attr_entry,+		    build_pax_attribute_name(pax_entry_name, p));+		archive_entry_set_size(pax_attr_entry,+		    archive_strlen(&(pax->pax_header)));+		/* Copy uid/gid (but clip to ustar limits). */+		uid = archive_entry_uid(entry_main);+		if (uid >= 1 << 18)+			uid = (1 << 18) - 1;+		archive_entry_set_uid(pax_attr_entry, uid);+		gid = archive_entry_gid(entry_main);+		if (gid >= 1 << 18)+			gid = (1 << 18) - 1;+		archive_entry_set_gid(pax_attr_entry, gid);+		/* Copy mode over (but not setuid/setgid bits) */+		mode = archive_entry_mode(entry_main);+#ifdef S_ISUID+		mode &= ~S_ISUID;+#endif+#ifdef S_ISGID+		mode &= ~S_ISGID;+#endif+#ifdef S_ISVTX+		mode &= ~S_ISVTX;+#endif+		archive_entry_set_mode(pax_attr_entry, mode);++		/* Copy uname/gname. */+		archive_entry_set_uname(pax_attr_entry,+		    archive_entry_uname(entry_main));+		archive_entry_set_gname(pax_attr_entry,+		    archive_entry_gname(entry_main));++		/* Copy mtime, but clip to ustar limits. */+		s = archive_entry_mtime(entry_main);+		if (s < 0) { s = 0; }+		if (s >= 0x7fffffff) { s = 0x7fffffff; }+		archive_entry_set_mtime(pax_attr_entry, s, 0);++		/* Standard ustar doesn't support atime. */+		archive_entry_set_atime(pax_attr_entry, 0, 0);++		/* Standard ustar doesn't support ctime. */+		archive_entry_set_ctime(pax_attr_entry, 0, 0);++		r = __archive_write_format_header_ustar(a, paxbuff,+		    pax_attr_entry, 'x', 1, NULL);++		archive_entry_free(pax_attr_entry);++		/* Note that the 'x' header shouldn't ever fail to format */+		if (r < ARCHIVE_WARN) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "archive_write_pax_header: "+			    "'x' header failed?!  This can't happen.\n");+			archive_entry_free(entry_main);+			archive_string_free(&entry_name);+			return (ARCHIVE_FATAL);+		} else if (r < ret)+			ret = r;+		r = __archive_write_output(a, paxbuff, 512);+		if (r != ARCHIVE_OK) {+			sparse_list_clear(pax);+			pax->entry_bytes_remaining = 0;+			pax->entry_padding = 0;+			archive_entry_free(entry_main);+			archive_string_free(&entry_name);+			return (ARCHIVE_FATAL);+		}++		pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header));+		pax->entry_padding =+		    0x1ff & (-(int64_t)pax->entry_bytes_remaining);++		r = __archive_write_output(a, pax->pax_header.s,+		    archive_strlen(&(pax->pax_header)));+		if (r != ARCHIVE_OK) {+			/* If a write fails, we're pretty much toast. */+			archive_entry_free(entry_main);+			archive_string_free(&entry_name);+			return (ARCHIVE_FATAL);+		}+		/* Pad out the end of the entry. */+		r = __archive_write_nulls(a, (size_t)pax->entry_padding);+		if (r != ARCHIVE_OK) {+			/* If a write fails, we're pretty much toast. */+			archive_entry_free(entry_main);+			archive_string_free(&entry_name);+			return (ARCHIVE_FATAL);+		}+		pax->entry_bytes_remaining = pax->entry_padding = 0;+	}++	/* Write the header for main entry. */+	r = __archive_write_output(a, ustarbuff, 512);+	if (r != ARCHIVE_OK) {+		archive_entry_free(entry_main);+		archive_string_free(&entry_name);+		return (r);+	}++	/*+	 * Inform the client of the on-disk size we're using, so+	 * they can avoid unnecessarily writing a body for something+	 * that we're just going to ignore.+	 */+	archive_entry_set_size(entry_original, real_size);+	if (pax->sparse_list == NULL && real_size > 0) {+		/* This is not a sparse file but we handle its data as+		 * a sparse block. */+		sparse_list_add(pax, 0, real_size);+		sparse_total = real_size;+	}+	pax->entry_padding = 0x1ff & (-(int64_t)sparse_total);+	archive_entry_free(entry_main);+	archive_string_free(&entry_name);++	return (ret);+}++/*+ * We need a valid name for the regular 'ustar' entry.  This routine+ * tries to hack something more-or-less reasonable.+ *+ * The approach here tries to preserve leading dir names.  We do so by+ * working with four sections:+ *   1) "prefix" directory names,+ *   2) "suffix" directory names,+ *   3) inserted dir name (optional),+ *   4) filename.+ *+ * These sections must satisfy the following requirements:+ *   * Parts 1 & 2 together form an initial portion of the dir name.+ *   * Part 3 is specified by the caller.  (It should not contain a leading+ *     or trailing '/'.)+ *   * Part 4 forms an initial portion of the base filename.+ *   * The filename must be <= 99 chars to fit the ustar 'name' field.+ *   * Parts 2, 3, 4 together must be <= 99 chars to fit the ustar 'name' fld.+ *   * Part 1 must be <= 155 chars to fit the ustar 'prefix' field.+ *   * If the original name ends in a '/', the new name must also end in a '/'+ *   * Trailing '/.' sequences may be stripped.+ *+ * Note: Recall that the ustar format does not store the '/' separating+ * parts 1 & 2, but does store the '/' separating parts 2 & 3.+ */+static char *+build_ustar_entry_name(char *dest, const char *src, size_t src_length,+    const char *insert)+{+	const char *prefix, *prefix_end;+	const char *suffix, *suffix_end;+	const char *filename, *filename_end;+	char *p;+	int need_slash = 0; /* Was there a trailing slash? */+	size_t suffix_length = 99;+	size_t insert_length;++	/* Length of additional dir element to be added. */+	if (insert == NULL)+		insert_length = 0;+	else+		/* +2 here allows for '/' before and after the insert. */+		insert_length = strlen(insert) + 2;++	/* Step 0: Quick bailout in a common case. */+	if (src_length < 100 && insert == NULL) {+		strncpy(dest, src, src_length);+		dest[src_length] = '\0';+		return (dest);+	}++	/* Step 1: Locate filename and enforce the length restriction. */+	filename_end = src + src_length;+	/* Remove trailing '/' chars and '/.' pairs. */+	for (;;) {+		if (filename_end > src && filename_end[-1] == '/') {+			filename_end --;+			need_slash = 1; /* Remember to restore trailing '/'. */+			continue;+		}+		if (filename_end > src + 1 && filename_end[-1] == '.'+		    && filename_end[-2] == '/') {+			filename_end -= 2;+			need_slash = 1; /* "foo/." will become "foo/" */+			continue;+		}+		break;+	}+	if (need_slash)+		suffix_length--;+	/* Find start of filename. */+	filename = filename_end - 1;+	while ((filename > src) && (*filename != '/'))+		filename --;+	if ((*filename == '/') && (filename < filename_end - 1))+		filename ++;+	/* Adjust filename_end so that filename + insert fits in 99 chars. */+	suffix_length -= insert_length;+	if (filename_end > filename + suffix_length)+		filename_end = filename + suffix_length;+	/* Calculate max size for "suffix" section (#3 above). */+	suffix_length -= filename_end - filename;++	/* Step 2: Locate the "prefix" section of the dirname, including+	 * trailing '/'. */+	prefix = src;+	prefix_end = prefix + 155;+	if (prefix_end > filename)+		prefix_end = filename;+	while (prefix_end > prefix && *prefix_end != '/')+		prefix_end--;+	if ((prefix_end < filename) && (*prefix_end == '/'))+		prefix_end++;++	/* Step 3: Locate the "suffix" section of the dirname,+	 * including trailing '/'. */+	suffix = prefix_end;+	suffix_end = suffix + suffix_length; /* Enforce limit. */+	if (suffix_end > filename)+		suffix_end = filename;+	if (suffix_end < suffix)+		suffix_end = suffix;+	while (suffix_end > suffix && *suffix_end != '/')+		suffix_end--;+	if ((suffix_end < filename) && (*suffix_end == '/'))+		suffix_end++;++	/* Step 4: Build the new name. */+	/* The OpenBSD strlcpy function is safer, but less portable. */+	/* Rather than maintain two versions, just use the strncpy version. */+	p = dest;+	if (prefix_end > prefix) {+		strncpy(p, prefix, prefix_end - prefix);+		p += prefix_end - prefix;+	}+	if (suffix_end > suffix) {+		strncpy(p, suffix, suffix_end - suffix);+		p += suffix_end - suffix;+	}+	if (insert != NULL) {+		/* Note: assume insert does not have leading or trailing '/' */+		strcpy(p, insert);+		p += strlen(insert);+		*p++ = '/';+	}+	strncpy(p, filename, filename_end - filename);+	p += filename_end - filename;+	if (need_slash)+		*p++ = '/';+	*p = '\0';++	return (dest);+}++/*+ * The ustar header for the pax extended attributes must have a+ * reasonable name:  SUSv3 requires 'dirname'/PaxHeader.'pid'/'filename'+ * where 'pid' is the PID of the archiving process.  Unfortunately,+ * that makes testing a pain since the output varies for each run,+ * so I'm sticking with the simpler 'dirname'/PaxHeader/'filename'+ * for now.  (Someday, I'll make this settable.  Then I can use the+ * SUS recommendation as default and test harnesses can override it+ * to get predictable results.)+ *+ * Joerg Schilling has argued that this is unnecessary because, in+ * practice, if the pax extended attributes get extracted as regular+ * files, no one is going to bother reading those attributes to+ * manually restore them.  Based on this, 'star' uses+ * /tmp/PaxHeader/'basename' as the ustar header name.  This is a+ * tempting argument, in part because it's simpler than the SUSv3+ * recommendation, but I'm not entirely convinced.  I'm also+ * uncomfortable with the fact that "/tmp" is a Unix-ism.+ *+ * The following routine leverages build_ustar_entry_name() above and+ * so is simpler than you might think.  It just needs to provide the+ * additional path element and handle a few pathological cases).+ */+static char *+build_pax_attribute_name(char *dest, const char *src)+{+	char buff[64];+	const char *p;++	/* Handle the null filename case. */+	if (src == NULL || *src == '\0') {+		strcpy(dest, "PaxHeader/blank");+		return (dest);+	}++	/* Prune final '/' and other unwanted final elements. */+	p = src + strlen(src);+	for (;;) {+		/* Ends in "/", remove the '/' */+		if (p > src && p[-1] == '/') {+			--p;+			continue;+		}+		/* Ends in "/.", remove the '.' */+		if (p > src + 1 && p[-1] == '.'+		    && p[-2] == '/') {+			--p;+			continue;+		}+		break;+	}++	/* Pathological case: After above, there was nothing left.+	 * This includes "/." "/./." "/.//./." etc. */+	if (p == src) {+		strcpy(dest, "/PaxHeader/rootdir");+		return (dest);+	}++	/* Convert unadorned "." into a suitable filename. */+	if (*src == '.' && p == src + 1) {+		strcpy(dest, "PaxHeader/currentdir");+		return (dest);+	}++	/*+	 * TODO: Push this string into the 'pax' structure to avoid+	 * recomputing it every time.  That will also open the door+	 * to having clients override it.+	 */+#if HAVE_GETPID && 0  /* Disable this for now; see above comment. */+	sprintf(buff, "PaxHeader.%d", getpid());+#else+	/* If the platform can't fetch the pid, don't include it. */+	strcpy(buff, "PaxHeader");+#endif+	/* General case: build a ustar-compatible name adding+	 * "/PaxHeader/". */+	build_ustar_entry_name(dest, src, p - src, buff);++	return (dest);+}++/*+ * GNU PAX Format 1.0 requires the special name, which pattern is:+ * <dir>/GNUSparseFile.<pid>/<original file name>+ *+ * Since reproducible archives are more important, use 0 as pid.+ *+ * This function is used for only Sparse file, a file type of which+ * is regular file.+ */+static char *+build_gnu_sparse_name(char *dest, const char *src)+{+	const char *p;++	/* Handle the null filename case. */+	if (src == NULL || *src == '\0') {+		strcpy(dest, "GNUSparseFile/blank");+		return (dest);+	}++	/* Prune final '/' and other unwanted final elements. */+	p = src + strlen(src);+	for (;;) {+		/* Ends in "/", remove the '/' */+		if (p > src && p[-1] == '/') {+			--p;+			continue;+		}+		/* Ends in "/.", remove the '.' */+		if (p > src + 1 && p[-1] == '.'+		    && p[-2] == '/') {+			--p;+			continue;+		}+		break;+	}++	/* General case: build a ustar-compatible name adding+	 * "/GNUSparseFile/". */+	build_ustar_entry_name(dest, src, p - src, "GNUSparseFile.0");++	return (dest);+}++/* Write two null blocks for the end of archive */+static int+archive_write_pax_close(struct archive_write *a)+{+	return (__archive_write_nulls(a, 512 * 2));+}++static int+archive_write_pax_free(struct archive_write *a)+{+	struct pax *pax;++	pax = (struct pax *)a->format_data;+	if (pax == NULL)+		return (ARCHIVE_OK);++	archive_string_free(&pax->pax_header);+	archive_string_free(&pax->sparse_map);+	archive_string_free(&pax->l_url_encoded_name);+	sparse_list_clear(pax);+	free(pax);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}++static int+archive_write_pax_finish_entry(struct archive_write *a)+{+	struct pax *pax;+	uint64_t remaining;+	int ret;++	pax = (struct pax *)a->format_data;+	remaining = pax->entry_bytes_remaining;+	if (remaining == 0) {+		while (pax->sparse_list) {+			struct sparse_block *sb;+			if (!pax->sparse_list->is_hole)+				remaining += pax->sparse_list->remaining;+			sb = pax->sparse_list->next;+			free(pax->sparse_list);+			pax->sparse_list = sb;+		}+	}+	ret = __archive_write_nulls(a, (size_t)(remaining + pax->entry_padding));+	pax->entry_bytes_remaining = pax->entry_padding = 0;+	return (ret);+}++static ssize_t+archive_write_pax_data(struct archive_write *a, const void *buff, size_t s)+{+	struct pax *pax;+	size_t ws;+	size_t total;+	int ret;++	pax = (struct pax *)a->format_data;++	/*+	 * According to GNU PAX format 1.0, write a sparse map+	 * before the body.+	 */+	if (archive_strlen(&(pax->sparse_map))) {+		ret = __archive_write_output(a, pax->sparse_map.s,+		    archive_strlen(&(pax->sparse_map)));+		if (ret != ARCHIVE_OK)+			return (ret);+		ret = __archive_write_nulls(a, pax->sparse_map_padding);+		if (ret != ARCHIVE_OK)+			return (ret);+		archive_string_empty(&(pax->sparse_map));+	}++	total = 0;+	while (total < s) {+		const unsigned char *p;++		while (pax->sparse_list != NULL &&+		    pax->sparse_list->remaining == 0) {+			struct sparse_block *sb = pax->sparse_list->next;+			free(pax->sparse_list);+			pax->sparse_list = sb;+		}++		if (pax->sparse_list == NULL)+			return (total);++		p = ((const unsigned char *)buff) + total;+		ws = s - total;+		if (ws > pax->sparse_list->remaining)+			ws = (size_t)pax->sparse_list->remaining;++		if (pax->sparse_list->is_hole) {+			/* Current block is hole thus we do not write+			 * the body. */+			pax->sparse_list->remaining -= ws;+			total += ws;+			continue;+		}++		ret = __archive_write_output(a, p, ws);+		pax->sparse_list->remaining -= ws;+		total += ws;+		if (ret != ARCHIVE_OK)+			return (ret);+	}+	return (total);+}++static int+has_non_ASCII(const char *_p)+{+	const unsigned char *p = (const unsigned char *)_p;++	if (p == NULL)+		return (1);+	while (*p != '\0' && *p < 128)+		p++;+	return (*p != '\0');+}++/*+ * Used by extended attribute support; encodes the name+ * so that there will be no '=' characters in the result.+ */+static char *+url_encode(const char *in)+{+	const char *s;+	char *d;+	int out_len = 0;+	char *out;++	for (s = in; *s != '\0'; s++) {+		if (*s < 33 || *s > 126 || *s == '%' || *s == '=')+			out_len += 3;+		else+			out_len++;+	}++	out = (char *)malloc(out_len + 1);+	if (out == NULL)+		return (NULL);++	for (s = in, d = out; *s != '\0'; s++) {+		/* encode any non-printable ASCII character or '%' or '=' */+		if (*s < 33 || *s > 126 || *s == '%' || *s == '=') {+			/* URL encoding is '%' followed by two hex digits */+			*d++ = '%';+			*d++ = "0123456789ABCDEF"[0x0f & (*s >> 4)];+			*d++ = "0123456789ABCDEF"[0x0f & *s];+		} else {+			*d++ = *s;+		}+	}+	*d = '\0';+	return (out);+}++/*+ * Encode a sequence of bytes into a C string using base-64 encoding.+ *+ * Returns a null-terminated C string allocated with malloc(); caller+ * is responsible for freeing the result.+ */+static char *+base64_encode(const char *s, size_t len)+{+	static const char digits[64] =+	    { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',+	      'P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d',+	      'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s',+	      't','u','v','w','x','y','z','0','1','2','3','4','5','6','7',+	      '8','9','+','/' };+	int v;+	char *d, *out;++	/* 3 bytes becomes 4 chars, but round up and allow for trailing NUL */+	out = (char *)malloc((len * 4 + 2) / 3 + 1);+	if (out == NULL)+		return (NULL);+	d = out;++	/* Convert each group of 3 bytes into 4 characters. */+	while (len >= 3) {+		v = (((int)s[0] << 16) & 0xff0000)+		    | (((int)s[1] << 8) & 0xff00)+		    | (((int)s[2]) & 0x00ff);+		s += 3;+		len -= 3;+		*d++ = digits[(v >> 18) & 0x3f];+		*d++ = digits[(v >> 12) & 0x3f];+		*d++ = digits[(v >> 6) & 0x3f];+		*d++ = digits[(v) & 0x3f];+	}+	/* Handle final group of 1 byte (2 chars) or 2 bytes (3 chars). */+	switch (len) {+	case 0: break;+	case 1:+		v = (((int)s[0] << 16) & 0xff0000);+		*d++ = digits[(v >> 18) & 0x3f];+		*d++ = digits[(v >> 12) & 0x3f];+		break;+	case 2:+		v = (((int)s[0] << 16) & 0xff0000)+		    | (((int)s[1] << 8) & 0xff00);+		*d++ = digits[(v >> 18) & 0x3f];+		*d++ = digits[(v >> 12) & 0x3f];+		*d++ = digits[(v >> 6) & 0x3f];+		break;+	}+	/* Add trailing NUL character so output is a valid C string. */+	*d = '\0';+	return (out);+}++static void+sparse_list_clear(struct pax *pax)+{+	while (pax->sparse_list != NULL) {+		struct sparse_block *sb = pax->sparse_list;+		pax->sparse_list = sb->next;+		free(sb);+	}+	pax->sparse_tail = NULL;+}++static int+_sparse_list_add_block(struct pax *pax, int64_t offset, int64_t length,+    int is_hole)+{+	struct sparse_block *sb;++	sb = (struct sparse_block *)malloc(sizeof(*sb));+	if (sb == NULL)+		return (ARCHIVE_FATAL);+	sb->next = NULL;+	sb->is_hole = is_hole;+	sb->offset = offset;+	sb->remaining = length;+	if (pax->sparse_list == NULL || pax->sparse_tail == NULL)+		pax->sparse_list = pax->sparse_tail = sb;+	else {+		pax->sparse_tail->next = sb;+		pax->sparse_tail = sb;+	}+	return (ARCHIVE_OK);+}++static int+sparse_list_add(struct pax *pax, int64_t offset, int64_t length)+{+	int64_t last_offset;+	int r;++	if (pax->sparse_tail == NULL)+		last_offset = 0;+	else {+		last_offset = pax->sparse_tail->offset ++		    pax->sparse_tail->remaining;+	}+	if (last_offset < offset) {+		/* Add a hole block. */+		r = _sparse_list_add_block(pax, last_offset,+		    offset - last_offset, 1);+		if (r != ARCHIVE_OK)+			return (r);+	}+	/* Add data block. */+	return (_sparse_list_add_block(pax, offset, length, 0));+}+
+ c/archive_write_set_format_private.h view
@@ -0,0 +1,42 @@+/*-+ * Copyright (c) 2020 Martin Matuska+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ */++#ifndef ARCHIVE_WRITE_SET_FORMAT_PRIVATE_H_INCLUDED+#define ARCHIVE_WRITE_SET_FORMAT_PRIVATE_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#ifndef __LIBARCHIVE_TEST+#error This header is only to be used internally to libarchive.+#endif+#endif++#include "archive.h"+#include "archive_entry.h"++void __archive_write_entry_filetype_unsupported(struct archive *a,+    struct archive_entry *entry, const char *format);+#endif
+ c/archive_write_set_format_raw.c view
@@ -0,0 +1,125 @@+/*-+ * Copyright (c) 2013 Marek Kubica+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif++#include "archive_entry.h"+#include "archive_write_private.h"++static ssize_t	archive_write_raw_data(struct archive_write *,+		    const void *buff, size_t s);+static int	archive_write_raw_free(struct archive_write *);+static int	archive_write_raw_header(struct archive_write *,+		    struct archive_entry *);++struct raw {+        int entries_written;+};++/*+ * Set output format to 'raw' format.+ */+int+archive_write_set_format_raw(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct raw *raw;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_raw");++	/* If someone else was already registered, unregister them. */+	if (a->format_free != NULL)+		(a->format_free)(a);++	raw = (struct raw *)calloc(1, sizeof(*raw));+	if (raw == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Can't allocate raw data");+		return (ARCHIVE_FATAL);+	}+	raw->entries_written = 0;+	a->format_data = raw;+	a->format_name = "raw";+        /* no options exist for this format */+	a->format_options = NULL;+	a->format_write_header = archive_write_raw_header;+	a->format_write_data = archive_write_raw_data;+	a->format_finish_entry = NULL;+        /* nothing needs to be done on closing */+	a->format_close = NULL;+	a->format_free = archive_write_raw_free;+	a->archive.archive_format = ARCHIVE_FORMAT_RAW;+	a->archive.archive_format_name = "RAW";+	return (ARCHIVE_OK);+}++static int+archive_write_raw_header(struct archive_write *a, struct archive_entry *entry)+{+	struct raw *raw = (struct raw *)a->format_data;++	if (archive_entry_filetype(entry) != AE_IFREG) {+		archive_set_error(&a->archive, ERANGE,+		    "Raw format only supports filetype AE_IFREG");+		return (ARCHIVE_FATAL);+	}+++	if (raw->entries_written > 0) {+		archive_set_error(&a->archive, ERANGE,+		    "Raw format only supports one entry per archive");+		return (ARCHIVE_FATAL);+	}+	raw->entries_written++;++	return (ARCHIVE_OK);+}++static ssize_t+archive_write_raw_data(struct archive_write *a, const void *buff, size_t s)+{+	int ret;++	ret = __archive_write_output(a, buff, s);+	if (ret >= 0)+		return (s);+	else+		return (ret);+}++static int+archive_write_raw_free(struct archive_write *a)+{+	struct raw *raw;++	raw = (struct raw *)a->format_data;+	free(raw);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}
+ c/archive_write_set_format_shar.c view
@@ -0,0 +1,641 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2008 Joerg Sonnenberger+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_shar.c 189438 2009-03-06 05:58:56Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_private.h"+#include "archive_write_private.h"+#include "archive_write_set_format_private.h"++struct shar {+	int			 dump;+	int			 end_of_line;+	struct archive_entry	*entry;+	int			 has_data;+	char			*last_dir;++	/* Line buffer for uuencoded dump format */+	char			 outbuff[45];+	size_t			 outpos;++	int			 wrote_header;+	struct archive_string	 work;+	struct archive_string	 quoted_name;+};++static int	archive_write_shar_close(struct archive_write *);+static int	archive_write_shar_free(struct archive_write *);+static int	archive_write_shar_header(struct archive_write *,+		    struct archive_entry *);+static ssize_t	archive_write_shar_data_sed(struct archive_write *,+		    const void * buff, size_t);+static ssize_t	archive_write_shar_data_uuencode(struct archive_write *,+		    const void * buff, size_t);+static int	archive_write_shar_finish_entry(struct archive_write *);++/*+ * Copy the given string to the buffer, quoting all shell meta characters+ * found.+ */+static void+shar_quote(struct archive_string *buf, const char *str, int in_shell)+{+	static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";+	size_t len;++	while (*str != '\0') {+		if ((len = strcspn(str, meta)) != 0) {+			archive_strncat(buf, str, len);+			str += len;+		} else if (*str == '\n') {+			if (in_shell)+				archive_strcat(buf, "\"\n\"");+			else+				archive_strcat(buf, "\\n");+			++str;+		} else {+			archive_strappend_char(buf, '\\');+			archive_strappend_char(buf, *str);+			++str;+		}+	}+}++/*+ * Set output format to 'shar' format.+ */+int+archive_write_set_format_shar(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct shar *shar;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_shar");++	/* If someone else was already registered, unregister them. */+	if (a->format_free != NULL)+		(a->format_free)(a);++	shar = (struct shar *)calloc(1, sizeof(*shar));+	if (shar == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data");+		return (ARCHIVE_FATAL);+	}+	archive_string_init(&shar->work);+	archive_string_init(&shar->quoted_name);+	a->format_data = shar;+	a->format_name = "shar";+	a->format_write_header = archive_write_shar_header;+	a->format_close = archive_write_shar_close;+	a->format_free = archive_write_shar_free;+	a->format_write_data = archive_write_shar_data_sed;+	a->format_finish_entry = archive_write_shar_finish_entry;+	a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;+	a->archive.archive_format_name = "shar";+	return (ARCHIVE_OK);+}++/*+ * An alternate 'shar' that uses uudecode instead of 'sed' to encode+ * file contents and can therefore be used to archive binary files.+ * In addition, this variant also attempts to restore ownership, file modes,+ * and other extended file information.+ */+int+archive_write_set_format_shar_dump(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct shar *shar;++	archive_write_set_format_shar(&a->archive);+	shar = (struct shar *)a->format_data;+	shar->dump = 1;+	a->format_write_data = archive_write_shar_data_uuencode;+	a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;+	a->archive.archive_format_name = "shar dump";+	return (ARCHIVE_OK);+}++static int+archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)+{+	const char *linkname;+	const char *name;+	char *p, *pp;+	struct shar *shar;++	shar = (struct shar *)a->format_data;+	if (!shar->wrote_header) {+		archive_strcat(&shar->work, "#!/bin/sh\n");+		archive_strcat(&shar->work, "# This is a shell archive\n");+		shar->wrote_header = 1;+	}++	/* Save the entry for the closing. */+	archive_entry_free(shar->entry);+	shar->entry = archive_entry_clone(entry);+	name = archive_entry_pathname(entry);++	/* Handle some preparatory issues. */+	switch(archive_entry_filetype(entry)) {+	case AE_IFREG:+		/* Only regular files have non-zero size. */+		break;+	case AE_IFDIR:+		archive_entry_set_size(entry, 0);+		/* Don't bother trying to recreate '.' */+		if (strcmp(name, ".") == 0  ||  strcmp(name, "./") == 0)+			return (ARCHIVE_OK);+		break;+	case AE_IFIFO:+	case AE_IFCHR:+	case AE_IFBLK:+		/* All other file types have zero size in the archive. */+		archive_entry_set_size(entry, 0);+		break;+	default:+		archive_entry_set_size(entry, 0);+		if (archive_entry_hardlink(entry) == NULL &&+		    archive_entry_symlink(entry) == NULL) {+			__archive_write_entry_filetype_unsupported(+			    &a->archive, entry, "shar");+			return (ARCHIVE_WARN);+		}+	}++	archive_string_empty(&shar->quoted_name);+	shar_quote(&shar->quoted_name, name, 1);++	/* Stock preparation for all file types. */+	archive_string_sprintf(&shar->work, "echo x %s\n", shar->quoted_name.s);++	if (archive_entry_filetype(entry) != AE_IFDIR) {+		/* Try to create the dir. */+		p = strdup(name);+		pp = strrchr(p, '/');+		/* If there is a / character, try to create the dir. */+		if (pp != NULL) {+			*pp = '\0';++			/* Try to avoid a lot of redundant mkdir commands. */+			if (strcmp(p, ".") == 0) {+				/* Don't try to "mkdir ." */+				free(p);+			} else if (shar->last_dir == NULL) {+				archive_strcat(&shar->work, "mkdir -p ");+				shar_quote(&shar->work, p, 1);+				archive_strcat(&shar->work,+				    " > /dev/null 2>&1\n");+				shar->last_dir = p;+			} else if (strcmp(p, shar->last_dir) == 0) {+				/* We've already created this exact dir. */+				free(p);+			} else if (strlen(p) < strlen(shar->last_dir) &&+			    strncmp(p, shar->last_dir, strlen(p)) == 0) {+				/* We've already created a subdir. */+				free(p);+			} else {+				archive_strcat(&shar->work, "mkdir -p ");+				shar_quote(&shar->work, p, 1);+				archive_strcat(&shar->work,+				    " > /dev/null 2>&1\n");+				shar->last_dir = p;+			}+		} else {+			free(p);+		}+	}++	/* Handle file-type specific issues. */+	shar->has_data = 0;+	if ((linkname = archive_entry_hardlink(entry)) != NULL) {+		archive_strcat(&shar->work, "ln -f ");+		shar_quote(&shar->work, linkname, 1);+		archive_string_sprintf(&shar->work, " %s\n",+		    shar->quoted_name.s);+	} else if ((linkname = archive_entry_symlink(entry)) != NULL) {+		archive_strcat(&shar->work, "ln -fs ");+		shar_quote(&shar->work, linkname, 1);+		archive_string_sprintf(&shar->work, " %s\n",+		    shar->quoted_name.s);+	} else {+		switch(archive_entry_filetype(entry)) {+		case AE_IFREG:+			if (archive_entry_size(entry) == 0) {+				/* More portable than "touch." */+				archive_string_sprintf(&shar->work,+				    "test -e \"%s\" || :> \"%s\"\n",+				    shar->quoted_name.s, shar->quoted_name.s);+			} else {+				if (shar->dump) {+					unsigned int mode = archive_entry_mode(entry) & 0777;+					archive_string_sprintf(&shar->work,+					    "uudecode -p > %s << 'SHAR_END'\n",+					    shar->quoted_name.s);+					archive_string_sprintf(&shar->work,+					    "begin %o ", mode);+					shar_quote(&shar->work, name, 0);+					archive_strcat(&shar->work, "\n");+				} else {+					archive_string_sprintf(&shar->work,+					    "sed 's/^X//' > %s << 'SHAR_END'\n",+					    shar->quoted_name.s);+				}+				shar->has_data = 1;+				shar->end_of_line = 1;+				shar->outpos = 0;+			}+			break;+		case AE_IFDIR:+			archive_string_sprintf(&shar->work,+			    "mkdir -p %s > /dev/null 2>&1\n",+			    shar->quoted_name.s);+			/* Record that we just created this directory. */+			free(shar->last_dir);++			shar->last_dir = strdup(name);+			/* Trim a trailing '/'. */+			pp = strrchr(shar->last_dir, '/');+			if (pp != NULL && pp[1] == '\0')+				*pp = '\0';+			/*+			 * TODO: Put dir name/mode on a list to be fixed+			 * up at end of archive.+			 */+			break;+		case AE_IFIFO:+			archive_string_sprintf(&shar->work,+			    "mkfifo %s\n", shar->quoted_name.s);+			break;+		case AE_IFCHR:+			archive_string_sprintf(&shar->work,+			    "mknod %s c %ju %ju\n", shar->quoted_name.s,+			    (uintmax_t)archive_entry_rdevmajor(entry),+			    (uintmax_t)archive_entry_rdevminor(entry));+			break;+		case AE_IFBLK:+			archive_string_sprintf(&shar->work,+			    "mknod %s b %ju %ju\n", shar->quoted_name.s,+			    (uintmax_t)archive_entry_rdevmajor(entry),+			    (uintmax_t)archive_entry_rdevminor(entry));+			break;+		default:+			return (ARCHIVE_WARN);+		}+	}++	return (ARCHIVE_OK);+}++static ssize_t+archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)+{+	static const size_t ensured = 65533;+	struct shar *shar;+	const char *src;+	char *buf, *buf_end;+	int ret;+	size_t written = n;++	shar = (struct shar *)a->format_data;+	if (!shar->has_data || n == 0)+		return (0);++	src = (const char *)buff;++	/*+	 * ensure is the number of bytes in buffer before expanding the+	 * current character.  Each operation writes the current character+	 * and optionally the start-of-new-line marker.  This can happen+	 * twice before entering the loop, so make sure three additional+	 * bytes can be written.+	 */+	if (archive_string_ensure(&shar->work, ensured + 3) == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}++	if (shar->work.length > ensured) {+		ret = __archive_write_output(a, shar->work.s,+		    shar->work.length);+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		archive_string_empty(&shar->work);+	}+	buf = shar->work.s + shar->work.length;+	buf_end = shar->work.s + ensured;++	if (shar->end_of_line) {+		*buf++ = 'X';+		shar->end_of_line = 0;+	}++	while (n-- != 0) {+		if ((*buf++ = *src++) == '\n') {+			if (n == 0)+				shar->end_of_line = 1;+			else+				*buf++ = 'X';+		}++		if (buf >= buf_end) {+			shar->work.length = buf - shar->work.s;+			ret = __archive_write_output(a, shar->work.s,+			    shar->work.length);+			if (ret != ARCHIVE_OK)+				return (ARCHIVE_FATAL);+			archive_string_empty(&shar->work);+			buf = shar->work.s;+		}+	}++	shar->work.length = buf - shar->work.s;++	return (written);+}++#define	UUENC(c)	(((c)!=0) ? ((c) & 077) + ' ': '`')++static void+uuencode_group(const char _in[3], char out[4])+{+	const unsigned char *in = (const unsigned char *)_in;+	int t;++	t = (in[0] << 16) | (in[1] << 8) | in[2];+	out[0] = UUENC( 0x3f & (t >> 18) );+	out[1] = UUENC( 0x3f & (t >> 12) );+	out[2] = UUENC( 0x3f & (t >> 6) );+	out[3] = UUENC( 0x3f & t );+}++static int+_uuencode_line(struct archive_write *a, struct shar *shar, const char *inbuf, size_t len)+{+	char *buf;+	size_t alloc_len;++	/* len <= 45 -> expanded to 60 + len byte + new line */+	alloc_len = shar->work.length + 62;+	if (archive_string_ensure(&shar->work, alloc_len) == NULL) {+		archive_set_error(&a->archive, ENOMEM, "Out of memory");+		return (ARCHIVE_FATAL);+	}++	buf = shar->work.s + shar->work.length;+	*buf++ = UUENC(len);+	while (len >= 3) {+		uuencode_group(inbuf, buf);+		len -= 3;+		inbuf += 3;+		buf += 4;+	}+	if (len != 0) {+		char tmp_buf[3];+		tmp_buf[0] = inbuf[0];+		if (len == 1)+			tmp_buf[1] = '\0';+		else+			tmp_buf[1] = inbuf[1];+		tmp_buf[2] = '\0';+		uuencode_group(tmp_buf, buf);+		buf += 4;+	}+	*buf++ = '\n';+	if ((buf - shar->work.s) > (ptrdiff_t)(shar->work.length + 62)) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC, "Buffer overflow");+		return (ARCHIVE_FATAL);+	}+	shar->work.length = buf - shar->work.s;+	return (ARCHIVE_OK);+}++#define uuencode_line(__a, __shar, __inbuf, __len) \+	do { \+		int r = _uuencode_line(__a, __shar, __inbuf, __len); \+		if (r != ARCHIVE_OK) \+			return (ARCHIVE_FATAL); \+	} while (0)++static ssize_t+archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,+    size_t length)+{+	struct shar *shar;+	const char *src;+	size_t n;+	int ret;++	shar = (struct shar *)a->format_data;+	if (!shar->has_data)+		return (ARCHIVE_OK);+	src = (const char *)buff;++	if (shar->outpos != 0) {+		n = 45 - shar->outpos;+		if (n > length)+			n = length;+		memcpy(shar->outbuff + shar->outpos, src, n);+		if (shar->outpos + n < 45) {+			shar->outpos += n;+			return length;+		}+		uuencode_line(a, shar, shar->outbuff, 45);+		src += n;+		n = length - n;+	} else {+		n = length;+	}++	while (n >= 45) {+		uuencode_line(a, shar, src, 45);+		src += 45;+		n -= 45;++		if (shar->work.length < 65536)+			continue;+		ret = __archive_write_output(a, shar->work.s,+		    shar->work.length);+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		archive_string_empty(&shar->work);+	}+	if (n != 0) {+		memcpy(shar->outbuff, src, n);+		shar->outpos = n;+	}+	return (length);+}++static int+archive_write_shar_finish_entry(struct archive_write *a)+{+	const char *g, *p, *u;+	struct shar *shar;+	int ret;++	shar = (struct shar *)a->format_data;+	if (shar->entry == NULL)+		return (0);++	if (shar->dump) {+		/* Finish uuencoded data. */+		if (shar->has_data) {+			if (shar->outpos > 0)+				uuencode_line(a, shar, shar->outbuff,+				    shar->outpos);+			archive_strcat(&shar->work, "`\nend\n");+			archive_strcat(&shar->work, "SHAR_END\n");+		}+		/* Restore file mode, owner, flags. */+		/*+		 * TODO: Don't immediately restore mode for+		 * directories; defer that to end of script.+		 */+		archive_string_sprintf(&shar->work, "chmod %o ",+		    (unsigned int)(archive_entry_mode(shar->entry) & 07777));+		shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1);+		archive_strcat(&shar->work, "\n");++		u = archive_entry_uname(shar->entry);+		g = archive_entry_gname(shar->entry);+		if (u != NULL || g != NULL) {+			archive_strcat(&shar->work, "chown ");+			if (u != NULL)+				shar_quote(&shar->work, u, 1);+			if (g != NULL) {+				archive_strcat(&shar->work, ":");+				shar_quote(&shar->work, g, 1);+			}+			archive_strcat(&shar->work, " ");+			shar_quote(&shar->work,+			    archive_entry_pathname(shar->entry), 1);+			archive_strcat(&shar->work, "\n");+		}++		if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {+			archive_string_sprintf(&shar->work, "chflags %s ", p);+			shar_quote(&shar->work,+			    archive_entry_pathname(shar->entry), 1);+			archive_strcat(&shar->work, "\n");+		}++		/* TODO: restore ACLs */++	} else {+		if (shar->has_data) {+			/* Finish sed-encoded data:  ensure last line ends. */+			if (!shar->end_of_line)+				archive_strappend_char(&shar->work, '\n');+			archive_strcat(&shar->work, "SHAR_END\n");+		}+	}++	archive_entry_free(shar->entry);+	shar->entry = NULL;++	if (shar->work.length < 65536)+		return (ARCHIVE_OK);++	ret = __archive_write_output(a, shar->work.s, shar->work.length);+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	archive_string_empty(&shar->work);++	return (ARCHIVE_OK);+}++static int+archive_write_shar_close(struct archive_write *a)+{+	struct shar *shar;+	int ret;++	/*+	 * TODO: Accumulate list of directory names/modes and+	 * fix them all up at end-of-archive.+	 */++	shar = (struct shar *)a->format_data;++	/*+	 * Only write the end-of-archive markers if the archive was+	 * actually started.  This avoids problems if someone sets+	 * shar format, then sets another format (which would invoke+	 * shar_finish to free the format-specific data).+	 */+	if (shar->wrote_header == 0)+		return (ARCHIVE_OK);++	archive_strcat(&shar->work, "exit\n");++	ret = __archive_write_output(a, shar->work.s, shar->work.length);+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);++	/* Shar output is never padded. */+	archive_write_set_bytes_in_last_block(&a->archive, 1);+	/*+	 * TODO: shar should also suppress padding of+	 * uncompressed data within gzip/bzip2 streams.+	 */++	return (ARCHIVE_OK);+}++static int+archive_write_shar_free(struct archive_write *a)+{+	struct shar *shar;++	shar = (struct shar *)a->format_data;+	if (shar == NULL)+		return (ARCHIVE_OK);++	archive_entry_free(shar->entry);+	free(shar->last_dir);+	archive_string_free(&(shar->work));+	archive_string_free(&(shar->quoted_name));+	free(shar);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}
+ c/archive_write_set_format_ustar.c view
@@ -0,0 +1,758 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ustar.c 191579 2009-04-27 18:35:03Z kientzle $");+++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_write_private.h"+#include "archive_write_set_format_private.h"++struct ustar {+	uint64_t	entry_bytes_remaining;+	uint64_t	entry_padding;++	struct archive_string_conv *opt_sconv;+	struct archive_string_conv *sconv_default;+	int	init_default_conversion;+};++/*+ * Define structure of POSIX 'ustar' tar header.+ */+#define	USTAR_name_offset 0+#define	USTAR_name_size 100+#define	USTAR_mode_offset 100+#define	USTAR_mode_size 6+#define	USTAR_mode_max_size 8+#define	USTAR_uid_offset 108+#define	USTAR_uid_size 6+#define	USTAR_uid_max_size 8+#define	USTAR_gid_offset 116+#define	USTAR_gid_size 6+#define	USTAR_gid_max_size 8+#define	USTAR_size_offset 124+#define	USTAR_size_size 11+#define	USTAR_size_max_size 12+#define	USTAR_mtime_offset 136+#define	USTAR_mtime_size 11+#define	USTAR_mtime_max_size 11+#define	USTAR_checksum_offset 148+#define	USTAR_checksum_size 8+#define	USTAR_typeflag_offset 156+#define	USTAR_typeflag_size 1+#define	USTAR_linkname_offset 157+#define	USTAR_linkname_size 100+#define	USTAR_magic_offset 257+#define	USTAR_magic_size 6+#define	USTAR_version_offset 263+#define	USTAR_version_size 2+#define	USTAR_uname_offset 265+#define	USTAR_uname_size 32+#define	USTAR_gname_offset 297+#define	USTAR_gname_size 32+#define	USTAR_rdevmajor_offset 329+#define	USTAR_rdevmajor_size 6+#define	USTAR_rdevmajor_max_size 8+#define	USTAR_rdevminor_offset 337+#define	USTAR_rdevminor_size 6+#define	USTAR_rdevminor_max_size 8+#define	USTAR_prefix_offset 345+#define	USTAR_prefix_size 155+#define	USTAR_padding_offset 500+#define	USTAR_padding_size 12++/*+ * A filled-in copy of the header for initialization.+ */+static const char template_header[] = {+	/* name: 100 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,+	/* Mode, space-null termination: 8 bytes */+	'0','0','0','0','0','0', ' ','\0',+	/* uid, space-null termination: 8 bytes */+	'0','0','0','0','0','0', ' ','\0',+	/* gid, space-null termination: 8 bytes */+	'0','0','0','0','0','0', ' ','\0',+	/* size, space termination: 12 bytes */+	'0','0','0','0','0','0','0','0','0','0','0', ' ',+	/* mtime, space termination: 12 bytes */+	'0','0','0','0','0','0','0','0','0','0','0', ' ',+	/* Initial checksum value: 8 spaces */+	' ',' ',' ',' ',' ',' ',' ',' ',+	/* Typeflag: 1 byte */+	'0',			/* '0' = regular file */+	/* Linkname: 100 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,+	/* Magic: 6 bytes, Version: 2 bytes */+	'u','s','t','a','r','\0', '0','0',+	/* Uname: 32 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	/* Gname: 32 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	/* rdevmajor + space/null padding: 8 bytes */+	'0','0','0','0','0','0', ' ','\0',+	/* rdevminor + space/null padding: 8 bytes */+	'0','0','0','0','0','0', ' ','\0',+	/* Prefix: 155 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,+	/* Padding: 12 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0+};++static ssize_t	archive_write_ustar_data(struct archive_write *a, const void *buff,+		    size_t s);+static int	archive_write_ustar_free(struct archive_write *);+static int	archive_write_ustar_close(struct archive_write *);+static int	archive_write_ustar_finish_entry(struct archive_write *);+static int	archive_write_ustar_header(struct archive_write *,+		    struct archive_entry *entry);+static int	archive_write_ustar_options(struct archive_write *,+		    const char *, const char *);+static int	format_256(int64_t, char *, int);+static int	format_number(int64_t, char *, int size, int max, int strict);+static int	format_octal(int64_t, char *, int);++/*+ * Set output format to 'ustar' format.+ */+int+archive_write_set_format_ustar(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct ustar *ustar;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_ustar");++	/* If someone else was already registered, unregister them. */+	if (a->format_free != NULL)+		(a->format_free)(a);++	/* Basic internal sanity test. */+	if (sizeof(template_header) != 512) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Internal: template_header wrong size: %zu should be 512",+		    sizeof(template_header));+		return (ARCHIVE_FATAL);+	}++	ustar = (struct ustar *)calloc(1, sizeof(*ustar));+	if (ustar == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate ustar data");+		return (ARCHIVE_FATAL);+	}+	a->format_data = ustar;+	a->format_name = "ustar";+	a->format_options = archive_write_ustar_options;+	a->format_write_header = archive_write_ustar_header;+	a->format_write_data = archive_write_ustar_data;+	a->format_close = archive_write_ustar_close;+	a->format_free = archive_write_ustar_free;+	a->format_finish_entry = archive_write_ustar_finish_entry;+	a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;+	a->archive.archive_format_name = "POSIX ustar";+	return (ARCHIVE_OK);+}++static int+archive_write_ustar_options(struct archive_write *a, const char *key,+    const char *val)+{+	struct ustar *ustar = (struct ustar *)a->format_data;+	int ret = ARCHIVE_FAILED;++	if (strcmp(key, "hdrcharset")  == 0) {+		if (val == NULL || val[0] == 0)+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "%s: hdrcharset option needs a character-set name",+			    a->format_name);+		else {+			ustar->opt_sconv = archive_string_conversion_to_charset(+			    &a->archive, val, 0);+			if (ustar->opt_sconv != NULL)+				ret = ARCHIVE_OK;+			else+				ret = ARCHIVE_FATAL;+		}+		return (ret);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)+{+	char buff[512];+	int ret, ret2;+	struct ustar *ustar;+	struct archive_entry *entry_main;+	struct archive_string_conv *sconv;++	ustar = (struct ustar *)a->format_data;++	/* Setup default string conversion. */+	if (ustar->opt_sconv == NULL) {+		if (!ustar->init_default_conversion) {+			ustar->sconv_default =+			    archive_string_default_conversion_for_write(&(a->archive));+			ustar->init_default_conversion = 1;+		}+		sconv = ustar->sconv_default;+	} else+		sconv = ustar->opt_sconv;++	/* Sanity check. */+	if (archive_entry_pathname(entry) == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Can't record entry in tar file without pathname");+		return (ARCHIVE_FAILED);+	}++	/* Only regular files (not hardlinks) have data. */+	if (archive_entry_hardlink(entry) != NULL ||+	    archive_entry_symlink(entry) != NULL ||+	    !(archive_entry_filetype(entry) == AE_IFREG))+		archive_entry_set_size(entry, 0);++	if (AE_IFDIR == archive_entry_filetype(entry)) {+		const char *p;+		size_t path_length;+		/*+		 * Ensure a trailing '/'.  Modify the entry so+		 * the client sees the change.+		 */+#if defined(_WIN32) && !defined(__CYGWIN__)+		const wchar_t *wp;++		wp = archive_entry_pathname_w(entry);+		if (wp != NULL && wp[wcslen(wp) -1] != L'/') {+			struct archive_wstring ws;++			archive_string_init(&ws);+			path_length = wcslen(wp);+			if (archive_wstring_ensure(&ws,+			    path_length + 2) == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate ustar data");+				archive_wstring_free(&ws);+				return(ARCHIVE_FATAL);+			}+			/* Should we keep '\' ? */+			if (wp[path_length -1] == L'\\')+				path_length--;+			archive_wstrncpy(&ws, wp, path_length);+			archive_wstrappend_wchar(&ws, L'/');+			archive_entry_copy_pathname_w(entry, ws.s);+			archive_wstring_free(&ws);+			p = NULL;+		} else+#endif+			p = archive_entry_pathname(entry);+		/*+		 * On Windows, this is a backup operation just in+		 * case getting WCS failed. On POSIX, this is a+		 * normal operation.+		 */+		if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {+			struct archive_string as;++			archive_string_init(&as);+			path_length = strlen(p);+			if (archive_string_ensure(&as,+			    path_length + 2) == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate ustar data");+				archive_string_free(&as);+				return(ARCHIVE_FATAL);+			}+#if defined(_WIN32) && !defined(__CYGWIN__)+			/* NOTE: This might break the pathname+			 * if the current code page is CP932 and+			 * the pathname includes a character '\'+			 * as a part of its multibyte pathname. */+			if (p[strlen(p) -1] == '\\')+				path_length--;+			else+#endif+			archive_strncpy(&as, p, path_length);+			archive_strappend_char(&as, '/');+			archive_entry_copy_pathname(entry, as.s);+			archive_string_free(&as);+		}+	}++#if defined(_WIN32) && !defined(__CYGWIN__)+	/* Make sure the path separators in pathname, hardlink and symlink+	 * are all slash '/', not the Windows path separator '\'. */+	entry_main = __la_win_entry_in_posix_pathseparator(entry);+	if (entry_main == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate ustar data");+		return(ARCHIVE_FATAL);+	}+	if (entry != entry_main)+		entry = entry_main;+	else+		entry_main = NULL;+#else+	entry_main = NULL;+#endif+	ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1, sconv);+	if (ret < ARCHIVE_WARN) {+		archive_entry_free(entry_main);+		return (ret);+	}+	ret2 = __archive_write_output(a, buff, 512);+	if (ret2 < ARCHIVE_WARN) {+		archive_entry_free(entry_main);+		return (ret2);+	}+	if (ret2 < ret)+		ret = ret2;++	ustar->entry_bytes_remaining = archive_entry_size(entry);+	ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining);+	archive_entry_free(entry_main);+	return (ret);+}++/*+ * Format a basic 512-byte "ustar" header.+ *+ * Returns -1 if format failed (due to field overflow).+ * Note that this always formats as much of the header as possible.+ * If "strict" is set to zero, it will extend numeric fields as+ * necessary (overwriting terminators or using base-256 extensions).+ *+ * This is exported so that other 'tar' formats can use it.+ */+int+__archive_write_format_header_ustar(struct archive_write *a, char h[512],+    struct archive_entry *entry, int tartype, int strict,+    struct archive_string_conv *sconv)+{+	unsigned int checksum;+	int i, r, ret;+	size_t copy_length;+	const char *p, *pp;+	int mytartype;++	ret = 0;+	mytartype = -1;+	/*+	 * The "template header" already includes the "ustar"+	 * signature, various end-of-field markers and other required+	 * elements.+	 */+	memcpy(h, &template_header, 512);++	/*+	 * Because the block is already null-filled, and strings+	 * are allowed to exactly fill their destination (without null),+	 * I use memcpy(dest, src, strlen()) here a lot to copy strings.+	 */+	r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate pathname '%s' to %s",+		    pp, archive_string_conversion_charset_name(sconv));+		ret = ARCHIVE_WARN;+	}+	if (copy_length <= USTAR_name_size)+		memcpy(h + USTAR_name_offset, pp, copy_length);+	else {+		/* Store in two pieces, splitting at a '/'. */+		p = strchr(pp + copy_length - USTAR_name_size - 1, '/');+		/*+		 * Look for the next '/' if we chose the first character+		 * as the separator.  (ustar format doesn't permit+		 * an empty prefix.)+		 */+		if (p == pp)+			p = strchr(p + 1, '/');+		/* Fail if the name won't fit. */+		if (!p) {+			/* No separator. */+			archive_set_error(&a->archive, ENAMETOOLONG,+			    "Pathname too long");+			ret = ARCHIVE_FAILED;+		} else if (p[1] == '\0') {+			/*+			 * The only feasible separator is a final '/';+			 * this would result in a non-empty prefix and+			 * an empty name, which POSIX doesn't+			 * explicitly forbid, but it just feels wrong.+			 */+			archive_set_error(&a->archive, ENAMETOOLONG,+			    "Pathname too long");+			ret = ARCHIVE_FAILED;+		} else if (p  > pp + USTAR_prefix_size) {+			/* Prefix is too long. */+			archive_set_error(&a->archive, ENAMETOOLONG,+			    "Pathname too long");+			ret = ARCHIVE_FAILED;+		} else {+			/* Copy prefix and remainder to appropriate places */+			memcpy(h + USTAR_prefix_offset, pp, p - pp);+			memcpy(h + USTAR_name_offset, p + 1,+			    pp + copy_length - p - 1);+		}+	}++	r = archive_entry_hardlink_l(entry, &p, &copy_length, sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Linkname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate linkname '%s' to %s",+		    p, archive_string_conversion_charset_name(sconv));+		ret = ARCHIVE_WARN;+	}+	if (copy_length > 0)+		mytartype = '1';+	else {+		r = archive_entry_symlink_l(entry, &p, &copy_length, sconv);+		if (r != 0) {+			if (errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for Linkname");+				return (ARCHIVE_FATAL);+			}+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Can't translate linkname '%s' to %s",+			    p, archive_string_conversion_charset_name(sconv));+			ret = ARCHIVE_WARN;+		}+	}+	if (copy_length > 0) {+		if (copy_length > USTAR_linkname_size) {+			archive_set_error(&a->archive, ENAMETOOLONG,+			    "Link contents too long");+			ret = ARCHIVE_FAILED;+			copy_length = USTAR_linkname_size;+		}+		memcpy(h + USTAR_linkname_offset, p, copy_length);+	}++	r = archive_entry_uname_l(entry, &p, &copy_length, sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Uname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate uname '%s' to %s",+		    p, archive_string_conversion_charset_name(sconv));+		ret = ARCHIVE_WARN;+	}+	if (copy_length > 0) {+		if (copy_length > USTAR_uname_size) {+			if (tartype != 'x') {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC, "Username too long");+				ret = ARCHIVE_FAILED;+			}+			copy_length = USTAR_uname_size;+		}+		memcpy(h + USTAR_uname_offset, p, copy_length);+	}++	r = archive_entry_gname_l(entry, &p, &copy_length, sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Gname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate gname '%s' to %s",+		    p, archive_string_conversion_charset_name(sconv));+		ret = ARCHIVE_WARN;+	}+	if (copy_length > 0) {+		if (strlen(p) > USTAR_gname_size) {+			if (tartype != 'x') {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC, "Group name too long");+				ret = ARCHIVE_FAILED;+			}+			copy_length = USTAR_gname_size;+		}+		memcpy(h + USTAR_gname_offset, p, copy_length);+	}++	if (format_number(archive_entry_mode(entry) & 07777,+	    h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {+		archive_set_error(&a->archive, ERANGE,+		    "Numeric mode too large");+		ret = ARCHIVE_FAILED;+	}++	if (format_number(archive_entry_uid(entry),+	    h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {+		archive_set_error(&a->archive, ERANGE,+		    "Numeric user ID too large");+		ret = ARCHIVE_FAILED;+	}++	if (format_number(archive_entry_gid(entry),+	    h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {+		archive_set_error(&a->archive, ERANGE,+		    "Numeric group ID too large");+		ret = ARCHIVE_FAILED;+	}++	if (format_number(archive_entry_size(entry),+	    h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {+		archive_set_error(&a->archive, ERANGE,+		    "File size out of range");+		ret = ARCHIVE_FAILED;+	}++	if (format_number(archive_entry_mtime(entry),+	    h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {+		archive_set_error(&a->archive, ERANGE,+		    "File modification time too large");+		ret = ARCHIVE_FAILED;+	}++	if (archive_entry_filetype(entry) == AE_IFBLK+	    || archive_entry_filetype(entry) == AE_IFCHR) {+		if (format_number(archive_entry_rdevmajor(entry),+		    h + USTAR_rdevmajor_offset, USTAR_rdevmajor_size,+		    USTAR_rdevmajor_max_size, strict)) {+			archive_set_error(&a->archive, ERANGE,+			    "Major device number too large");+			ret = ARCHIVE_FAILED;+		}++		if (format_number(archive_entry_rdevminor(entry),+		    h + USTAR_rdevminor_offset, USTAR_rdevminor_size,+		    USTAR_rdevminor_max_size, strict)) {+			archive_set_error(&a->archive, ERANGE,+			    "Minor device number too large");+			ret = ARCHIVE_FAILED;+		}+	}++	if (tartype >= 0) {+		h[USTAR_typeflag_offset] = tartype;+	} else if (mytartype >= 0) {+		h[USTAR_typeflag_offset] = mytartype;+	} else {+		switch (archive_entry_filetype(entry)) {+		case AE_IFREG: h[USTAR_typeflag_offset] = '0' ; break;+		case AE_IFLNK: h[USTAR_typeflag_offset] = '2' ; break;+		case AE_IFCHR: h[USTAR_typeflag_offset] = '3' ; break;+		case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break;+		case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break;+		case AE_IFIFO: h[USTAR_typeflag_offset] = '6' ; break;+		default: /* AE_IFSOCK and unknown */+			__archive_write_entry_filetype_unsupported(+			    &a->archive, entry, "ustar");+			ret = ARCHIVE_FAILED;+		}+	}++	checksum = 0;+	for (i = 0; i < 512; i++)+		checksum += 255 & (unsigned int)h[i];+	h[USTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */+	/* h[USTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */+	format_octal(checksum, h + USTAR_checksum_offset, 6);+	return (ret);+}++/*+ * Format a number into a field, with some intelligence.+ */+static int+format_number(int64_t v, char *p, int s, int maxsize, int strict)+{+	int64_t limit;++	limit = ((int64_t)1 << (s*3));++	/* "Strict" only permits octal values with proper termination. */+	if (strict)+		return (format_octal(v, p, s));++	/*+	 * In non-strict mode, we allow the number to overwrite one or+	 * more bytes of the field termination.  Even old tar+	 * implementations should be able to handle this with no+	 * problem.+	 */+	if (v >= 0) {+		while (s <= maxsize) {+			if (v < limit)+				return (format_octal(v, p, s));+			s++;+			limit <<= 3;+		}+	}++	/* Base-256 can handle any number, positive or negative. */+	return (format_256(v, p, maxsize));+}++/*+ * Format a number into the specified field using base-256.+ */+static int+format_256(int64_t v, char *p, int s)+{+	p += s;+	while (s-- > 0) {+		*--p = (char)(v & 0xff);+		v >>= 8;+	}+	*p |= 0x80; /* Set the base-256 marker bit. */+	return (0);+}++/*+ * Format a number into the specified field.+ */+static int+format_octal(int64_t v, char *p, int s)+{+	int len;++	len = s;++	/* Octal values can't be negative, so use 0. */+	if (v < 0) {+		while (len-- > 0)+			*p++ = '0';+		return (-1);+	}++	p += s;		/* Start at the end and work backwards. */+	while (s-- > 0) {+		*--p = (char)('0' + (v & 7));+		v >>= 3;+	}++	if (v == 0)+		return (0);++	/* If it overflowed, fill field with max value. */+	while (len-- > 0)+		*p++ = '7';++	return (-1);+}++static int+archive_write_ustar_close(struct archive_write *a)+{+	return (__archive_write_nulls(a, 512*2));+}++static int+archive_write_ustar_free(struct archive_write *a)+{+	struct ustar *ustar;++	ustar = (struct ustar *)a->format_data;+	free(ustar);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}++static int+archive_write_ustar_finish_entry(struct archive_write *a)+{+	struct ustar *ustar;+	int ret;++	ustar = (struct ustar *)a->format_data;+	ret = __archive_write_nulls(a,+	    (size_t)(ustar->entry_bytes_remaining + ustar->entry_padding));+	ustar->entry_bytes_remaining = ustar->entry_padding = 0;+	return (ret);+}++static ssize_t+archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s)+{+	struct ustar *ustar;+	int ret;++	ustar = (struct ustar *)a->format_data;+	if (s > ustar->entry_bytes_remaining)+		s = (size_t)ustar->entry_bytes_remaining;+	ret = __archive_write_output(a, buff, s);+	ustar->entry_bytes_remaining -= s;+	if (ret != ARCHIVE_OK)+		return (ret);+	return (s);+}
+ c/archive_write_set_format_v7tar.c view
@@ -0,0 +1,638 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");+++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_write_private.h"+#include "archive_write_set_format_private.h"++struct v7tar {+	uint64_t	entry_bytes_remaining;+	uint64_t	entry_padding;++	struct archive_string_conv *opt_sconv;+	struct archive_string_conv *sconv_default;+	int	init_default_conversion;+};++/*+ * Define structure of POSIX 'v7tar' tar header.+ */+#define	V7TAR_name_offset 0+#define	V7TAR_name_size 100+#define	V7TAR_mode_offset 100+#define	V7TAR_mode_size 6+#define	V7TAR_mode_max_size 8+#define	V7TAR_uid_offset 108+#define	V7TAR_uid_size 6+#define	V7TAR_uid_max_size 8+#define	V7TAR_gid_offset 116+#define	V7TAR_gid_size 6+#define	V7TAR_gid_max_size 8+#define	V7TAR_size_offset 124+#define	V7TAR_size_size 11+#define	V7TAR_size_max_size 12+#define	V7TAR_mtime_offset 136+#define	V7TAR_mtime_size 11+#define	V7TAR_mtime_max_size 12+#define	V7TAR_checksum_offset 148+#define	V7TAR_checksum_size 8+#define	V7TAR_typeflag_offset 156+#define	V7TAR_typeflag_size 1+#define	V7TAR_linkname_offset 157+#define	V7TAR_linkname_size 100+#define	V7TAR_padding_offset 257+#define	V7TAR_padding_size 255++/*+ * A filled-in copy of the header for initialization.+ */+static const char template_header[] = {+	/* name: 100 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,+	/* Mode, space-null termination: 8 bytes */+	'0','0','0','0','0','0', ' ','\0',+	/* uid, space-null termination: 8 bytes */+	'0','0','0','0','0','0', ' ','\0',+	/* gid, space-null termination: 8 bytes */+	'0','0','0','0','0','0', ' ','\0',+	/* size, space termination: 12 bytes */+	'0','0','0','0','0','0','0','0','0','0','0', ' ',+	/* mtime, space termination: 12 bytes */+	'0','0','0','0','0','0','0','0','0','0','0', ' ',+	/* Initial checksum value: 8 spaces */+	' ',' ',' ',' ',' ',' ',' ',' ',+	/* Typeflag: 1 byte */+	0,+	/* Linkname: 100 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,+	/* Padding: 255 bytes */+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0+};++static ssize_t	archive_write_v7tar_data(struct archive_write *a, const void *buff,+		    size_t s);+static int	archive_write_v7tar_free(struct archive_write *);+static int	archive_write_v7tar_close(struct archive_write *);+static int	archive_write_v7tar_finish_entry(struct archive_write *);+static int	archive_write_v7tar_header(struct archive_write *,+		    struct archive_entry *entry);+static int	archive_write_v7tar_options(struct archive_write *,+		    const char *, const char *);+static int	format_256(int64_t, char *, int);+static int	format_number(int64_t, char *, int size, int max, int strict);+static int	format_octal(int64_t, char *, int);+static int	format_header_v7tar(struct archive_write *, char h[512],+		    struct archive_entry *, int, struct archive_string_conv *);++/*+ * Set output format to 'v7tar' format.+ */+int+archive_write_set_format_v7tar(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct v7tar *v7tar;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_v7tar");++	/* If someone else was already registered, unregister them. */+	if (a->format_free != NULL)+		(a->format_free)(a);++	/* Basic internal sanity test. */+	if (sizeof(template_header) != 512) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Internal: template_header wrong size: %zu should be 512",+		    sizeof(template_header));+		return (ARCHIVE_FATAL);+	}++	v7tar = (struct v7tar *)calloc(1, sizeof(*v7tar));+	if (v7tar == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate v7tar data");+		return (ARCHIVE_FATAL);+	}+	a->format_data = v7tar;+	a->format_name = "tar (non-POSIX)";+	a->format_options = archive_write_v7tar_options;+	a->format_write_header = archive_write_v7tar_header;+	a->format_write_data = archive_write_v7tar_data;+	a->format_close = archive_write_v7tar_close;+	a->format_free = archive_write_v7tar_free;+	a->format_finish_entry = archive_write_v7tar_finish_entry;+	a->archive.archive_format = ARCHIVE_FORMAT_TAR;+	a->archive.archive_format_name = "tar (non-POSIX)";+	return (ARCHIVE_OK);+}++static int+archive_write_v7tar_options(struct archive_write *a, const char *key,+    const char *val)+{+	struct v7tar *v7tar = (struct v7tar *)a->format_data;+	int ret = ARCHIVE_FAILED;++	if (strcmp(key, "hdrcharset")  == 0) {+		if (val == NULL || val[0] == 0)+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "%s: hdrcharset option needs a character-set name",+			    a->format_name);+		else {+			v7tar->opt_sconv = archive_string_conversion_to_charset(+			    &a->archive, val, 0);+			if (v7tar->opt_sconv != NULL)+				ret = ARCHIVE_OK;+			else+				ret = ARCHIVE_FATAL;+		}+		return (ret);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry)+{+	char buff[512];+	int ret, ret2;+	struct v7tar *v7tar;+	struct archive_entry *entry_main;+	struct archive_string_conv *sconv;++	v7tar = (struct v7tar *)a->format_data;++	/* Setup default string conversion. */+	if (v7tar->opt_sconv == NULL) {+		if (!v7tar->init_default_conversion) {+			v7tar->sconv_default =+			    archive_string_default_conversion_for_write(+				&(a->archive));+			v7tar->init_default_conversion = 1;+		}+		sconv = v7tar->sconv_default;+	} else+		sconv = v7tar->opt_sconv;++	/* Sanity check. */+	if (archive_entry_pathname(entry) == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Can't record entry in tar file without pathname");+		return (ARCHIVE_FAILED);+	}++	/* Only regular files (not hardlinks) have data. */+	if (archive_entry_hardlink(entry) != NULL ||+	    archive_entry_symlink(entry) != NULL ||+	    !(archive_entry_filetype(entry) == AE_IFREG))+		archive_entry_set_size(entry, 0);++	if (AE_IFDIR == archive_entry_filetype(entry)) {+		const char *p;+		size_t path_length;+		/*+		 * Ensure a trailing '/'.  Modify the entry so+		 * the client sees the change.+		 */+#if defined(_WIN32) && !defined(__CYGWIN__)+		const wchar_t *wp;++		wp = archive_entry_pathname_w(entry);+		if (wp != NULL && wp[wcslen(wp) -1] != L'/') {+			struct archive_wstring ws;++			archive_string_init(&ws);+			path_length = wcslen(wp);+			if (archive_wstring_ensure(&ws,+			    path_length + 2) == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate v7tar data");+				archive_wstring_free(&ws);+				return(ARCHIVE_FATAL);+			}+			/* Should we keep '\' ? */+			if (wp[path_length -1] == L'\\')+				path_length--;+			archive_wstrncpy(&ws, wp, path_length);+			archive_wstrappend_wchar(&ws, L'/');+			archive_entry_copy_pathname_w(entry, ws.s);+			archive_wstring_free(&ws);+			p = NULL;+		} else+#endif+			p = archive_entry_pathname(entry);+		/*+		 * On Windows, this is a backup operation just in+		 * case getting WCS failed. On POSIX, this is a+		 * normal operation.+		 */+		if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {+			struct archive_string as;++			archive_string_init(&as);+			path_length = strlen(p);+			if (archive_string_ensure(&as,+			    path_length + 2) == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate v7tar data");+				archive_string_free(&as);+				return(ARCHIVE_FATAL);+			}+#if defined(_WIN32) && !defined(__CYGWIN__)+			/* NOTE: This might break the pathname+			 * if the current code page is CP932 and+			 * the pathname includes a character '\'+			 * as a part of its multibyte pathname. */+			if (p[strlen(p) -1] == '\\')+				path_length--;+			else+#endif+			archive_strncpy(&as, p, path_length);+			archive_strappend_char(&as, '/');+			archive_entry_copy_pathname(entry, as.s);+			archive_string_free(&as);+		}+	}++#if defined(_WIN32) && !defined(__CYGWIN__)+	/* Make sure the path separators in pathname, hardlink and symlink+	 * are all slash '/', not the Windows path separator '\'. */+	entry_main = __la_win_entry_in_posix_pathseparator(entry);+	if (entry_main == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate v7tar data");+		return(ARCHIVE_FATAL);+	}+	if (entry != entry_main)+		entry = entry_main;+	else+		entry_main = NULL;+#else+	entry_main = NULL;+#endif+	ret = format_header_v7tar(a, buff, entry, 1, sconv);+	if (ret < ARCHIVE_WARN) {+		archive_entry_free(entry_main);+		return (ret);+	}+	ret2 = __archive_write_output(a, buff, 512);+	if (ret2 < ARCHIVE_WARN) {+		archive_entry_free(entry_main);+		return (ret2);+	}+	if (ret2 < ret)+		ret = ret2;++	v7tar->entry_bytes_remaining = archive_entry_size(entry);+	v7tar->entry_padding = 0x1ff & (-(int64_t)v7tar->entry_bytes_remaining);+	archive_entry_free(entry_main);+	return (ret);+}++/*+ * Format a basic 512-byte "v7tar" header.+ *+ * Returns -1 if format failed (due to field overflow).+ * Note that this always formats as much of the header as possible.+ * If "strict" is set to zero, it will extend numeric fields as+ * necessary (overwriting terminators or using base-256 extensions).+ *+ */+static int+format_header_v7tar(struct archive_write *a, char h[512],+    struct archive_entry *entry, int strict,+    struct archive_string_conv *sconv)+{+	unsigned int checksum;+	int i, r, ret;+	size_t copy_length;+	const char *p, *pp;+	int mytartype;++	ret = 0;+	mytartype = -1;+	/*+	 * The "template header" already includes the "v7tar"+	 * signature, various end-of-field markers and other required+	 * elements.+	 */+	memcpy(h, &template_header, 512);++	/*+	 * Because the block is already null-filled, and strings+	 * are allowed to exactly fill their destination (without null),+	 * I use memcpy(dest, src, strlen()) here a lot to copy strings.+	 */+	r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate pathname '%s' to %s",+		    pp, archive_string_conversion_charset_name(sconv));+		ret = ARCHIVE_WARN;+	}+	if (strict && copy_length < V7TAR_name_size)+		memcpy(h + V7TAR_name_offset, pp, copy_length);+	else if (!strict && copy_length <= V7TAR_name_size)+		memcpy(h + V7TAR_name_offset, pp, copy_length);+	else {+		/* Prefix is too long. */+		archive_set_error(&a->archive, ENAMETOOLONG,+		    "Pathname too long");+		ret = ARCHIVE_FAILED;+	}++	r = archive_entry_hardlink_l(entry, &p, &copy_length, sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Linkname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate linkname '%s' to %s",+		    p, archive_string_conversion_charset_name(sconv));+		ret = ARCHIVE_WARN;+	}+	if (copy_length > 0)+		mytartype = '1';+	else {+		r = archive_entry_symlink_l(entry, &p, &copy_length, sconv);+		if (r != 0) {+			if (errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for Linkname");+				return (ARCHIVE_FATAL);+			}+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Can't translate linkname '%s' to %s",+			    p, archive_string_conversion_charset_name(sconv));+			ret = ARCHIVE_WARN;+		}+	}+	if (copy_length > 0) {+		if (copy_length >= V7TAR_linkname_size) {+			archive_set_error(&a->archive, ENAMETOOLONG,+			    "Link contents too long");+			ret = ARCHIVE_FAILED;+			copy_length = V7TAR_linkname_size;+		}+		memcpy(h + V7TAR_linkname_offset, p, copy_length);+	}++	if (format_number(archive_entry_mode(entry) & 07777,+	    h + V7TAR_mode_offset, V7TAR_mode_size,+	    V7TAR_mode_max_size, strict)) {+		archive_set_error(&a->archive, ERANGE,+		    "Numeric mode too large");+		ret = ARCHIVE_FAILED;+	}++	if (format_number(archive_entry_uid(entry),+	    h + V7TAR_uid_offset, V7TAR_uid_size, V7TAR_uid_max_size, strict)) {+		archive_set_error(&a->archive, ERANGE,+		    "Numeric user ID too large");+		ret = ARCHIVE_FAILED;+	}++	if (format_number(archive_entry_gid(entry),+	    h + V7TAR_gid_offset, V7TAR_gid_size, V7TAR_gid_max_size, strict)) {+		archive_set_error(&a->archive, ERANGE,+		    "Numeric group ID too large");+		ret = ARCHIVE_FAILED;+	}++	if (format_number(archive_entry_size(entry),+	    h + V7TAR_size_offset, V7TAR_size_size,+	    V7TAR_size_max_size, strict)) {+		archive_set_error(&a->archive, ERANGE,+		    "File size out of range");+		ret = ARCHIVE_FAILED;+	}++	if (format_number(archive_entry_mtime(entry),+	    h + V7TAR_mtime_offset, V7TAR_mtime_size,+	    V7TAR_mtime_max_size, strict)) {+		archive_set_error(&a->archive, ERANGE,+		    "File modification time too large");+		ret = ARCHIVE_FAILED;+	}++	if (mytartype >= 0) {+		h[V7TAR_typeflag_offset] = mytartype;+	} else {+		switch (archive_entry_filetype(entry)) {+		case AE_IFREG: case AE_IFDIR:+			break;+		case AE_IFLNK:+			h[V7TAR_typeflag_offset] = '2';+			break;+		default:+			/* AE_IFBLK, AE_IFCHR, AE_IFIFO, AE_IFSOCK+			 * and unknown */+			__archive_write_entry_filetype_unsupported(+			    &a->archive, entry, "v7tar");+			ret = ARCHIVE_FAILED;+		}+	}++	checksum = 0;+	for (i = 0; i < 512; i++)+		checksum += 255 & (unsigned int)h[i];+	format_octal(checksum, h + V7TAR_checksum_offset, 6);+	/* Can't be pre-set in the template. */+	h[V7TAR_checksum_offset + 6] = '\0';+	return (ret);+}++/*+ * Format a number into a field, with some intelligence.+ */+static int+format_number(int64_t v, char *p, int s, int maxsize, int strict)+{+	int64_t limit;++	limit = ((int64_t)1 << (s*3));++	/* "Strict" only permits octal values with proper termination. */+	if (strict)+		return (format_octal(v, p, s));++	/*+	 * In non-strict mode, we allow the number to overwrite one or+	 * more bytes of the field termination.  Even old tar+	 * implementations should be able to handle this with no+	 * problem.+	 */+	if (v >= 0) {+		while (s <= maxsize) {+			if (v < limit)+				return (format_octal(v, p, s));+			s++;+			limit <<= 3;+		}+	}++	/* Base-256 can handle any number, positive or negative. */+	return (format_256(v, p, maxsize));+}++/*+ * Format a number into the specified field using base-256.+ */+static int+format_256(int64_t v, char *p, int s)+{+	p += s;+	while (s-- > 0) {+		*--p = (char)(v & 0xff);+		v >>= 8;+	}+	*p |= 0x80; /* Set the base-256 marker bit. */+	return (0);+}++/*+ * Format a number into the specified field.+ */+static int+format_octal(int64_t v, char *p, int s)+{+	int len;++	len = s;++	/* Octal values can't be negative, so use 0. */+	if (v < 0) {+		while (len-- > 0)+			*p++ = '0';+		return (-1);+	}++	p += s;		/* Start at the end and work backwards. */+	while (s-- > 0) {+		*--p = (char)('0' + (v & 7));+		v >>= 3;+	}++	if (v == 0)+		return (0);++	/* If it overflowed, fill field with max value. */+	while (len-- > 0)+		*p++ = '7';++	return (-1);+}++static int+archive_write_v7tar_close(struct archive_write *a)+{+	return (__archive_write_nulls(a, 512*2));+}++static int+archive_write_v7tar_free(struct archive_write *a)+{+	struct v7tar *v7tar;++	v7tar = (struct v7tar *)a->format_data;+	free(v7tar);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}++static int+archive_write_v7tar_finish_entry(struct archive_write *a)+{+	struct v7tar *v7tar;+	int ret;++	v7tar = (struct v7tar *)a->format_data;+	ret = __archive_write_nulls(a,+	    (size_t)(v7tar->entry_bytes_remaining + v7tar->entry_padding));+	v7tar->entry_bytes_remaining = v7tar->entry_padding = 0;+	return (ret);+}++static ssize_t+archive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s)+{+	struct v7tar *v7tar;+	int ret;++	v7tar = (struct v7tar *)a->format_data;+	if (s > v7tar->entry_bytes_remaining)+		s = (size_t)v7tar->entry_bytes_remaining;+	ret = __archive_write_output(a, buff, s);+	v7tar->entry_bytes_remaining -= s;+	if (ret != ARCHIVE_OK)+		return (ret);+	return (s);+}
+ c/archive_write_set_format_warc.c view
@@ -0,0 +1,453 @@+/*-+ * Copyright (c) 2014 Sebastian Freundt+ * Author: Sebastian Freundt  <devel@fresse.org>+ *+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include <stdio.h>+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_TIME_H+#include <time.h>+#endif++#include "archive.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_random_private.h"+#include "archive_write_private.h"+#include "archive_write_set_format_private.h"++struct warc_s {+	unsigned int omit_warcinfo:1;++	time_t now;+	mode_t typ;+	unsigned int rng;+	/* populated size */+	uint64_t populz;+};++static const char warcinfo[] =+    "software: libarchive/" ARCHIVE_VERSION_ONLY_STRING "\r\n"+    "format: WARC file version 1.0\r\n";++typedef enum {+	WT_NONE,+	/* warcinfo */+	WT_INFO,+	/* metadata */+	WT_META,+	/* resource */+	WT_RSRC,+	/* request, unsupported */+	WT_REQ,+	/* response, unsupported */+	WT_RSP,+	/* revisit, unsupported */+	WT_RVIS,+	/* conversion, unsupported */+	WT_CONV,+	/* continuation, unsupported at the moment */+	WT_CONT,+	/* invalid type */+	LAST_WT+} warc_type_t;++typedef struct {+	warc_type_t type;+	const char *tgturi;+	const char *recid;+	time_t rtime;+	time_t mtime;+	const char *cnttyp;+	uint64_t cntlen;+} warc_essential_hdr_t;++typedef struct {+	unsigned int u[4U];+} warc_uuid_t;++static int _warc_options(struct archive_write*, const char *key, const char *v);+static int _warc_header(struct archive_write *a, struct archive_entry *entry);+static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t sz);+static int _warc_finish_entry(struct archive_write *a);+static int _warc_close(struct archive_write *a);+static int _warc_free(struct archive_write *a);++/* private routines */+static ssize_t _popul_ehdr(struct archive_string *t, size_t z, warc_essential_hdr_t);+static int _gen_uuid(warc_uuid_t *tgt);+++/*+ * Set output format to ISO 28500 (aka WARC) format.+ */+int+archive_write_set_format_warc(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct warc_s *w;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_warc");++	/* If another format was already registered, unregister it. */+	if (a->format_free != NULL) {+		(a->format_free)(a);+	}++	w = malloc(sizeof(*w));+	if (w == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate warc data");+		return (ARCHIVE_FATAL);+	}+	/* by default we're emitting a file wide header */+	w->omit_warcinfo = 0U;+	/* obtain current time for date fields */+	w->now = time(NULL);+	/* reset file type info */+	w->typ = 0;+	/* also initialise our rng */+	w->rng = (unsigned int)w->now;++	a->format_data = w;+	a->format_name = "WARC/1.0";+	a->format_options = _warc_options;+	a->format_write_header = _warc_header;+	a->format_write_data = _warc_data;+	a->format_close = _warc_close;+	a->format_free = _warc_free;+	a->format_finish_entry = _warc_finish_entry;+	a->archive.archive_format = ARCHIVE_FORMAT_WARC;+	a->archive.archive_format_name = "WARC/1.0";+	return (ARCHIVE_OK);+}+++/* archive methods */+static int+_warc_options(struct archive_write *a, const char *key, const char *val)+{+	struct warc_s *w = a->format_data;++	if (strcmp(key, "omit-warcinfo") == 0) {+		if (val == NULL || strcmp(val, "true") == 0) {+			/* great */+			w->omit_warcinfo = 1U;+			return (ARCHIVE_OK);+		}+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+_warc_header(struct archive_write *a, struct archive_entry *entry)+{+	struct warc_s *w = a->format_data;+	struct archive_string hdr;+#define MAX_HDR_SIZE 512++	/* check whether warcinfo record needs outputting */+	if (!w->omit_warcinfo) {+		ssize_t r;+		warc_essential_hdr_t wi = {+			WT_INFO,+			/*uri*/NULL,+			/*urn*/NULL,+			/*rtm*/0,+			/*mtm*/0,+			/*cty*/"application/warc-fields",+			/*len*/sizeof(warcinfo) - 1U,+		};+		wi.rtime = w->now;+		wi.mtime = w->now;++		archive_string_init(&hdr);+		r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi);+		if (r >= 0) {+			/* jackpot! */+			/* now also use HDR buffer for the actual warcinfo */+			archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1);++			/* append end-of-record indicator */+			archive_strncat(&hdr, "\r\n\r\n", 4);++			/* write to output stream */+			__archive_write_output(a, hdr.s, archive_strlen(&hdr));+		}+		/* indicate we're done with file header writing */+		w->omit_warcinfo = 1U;+		archive_string_free(&hdr);+	}++	if (archive_entry_pathname(entry) == NULL) {+		archive_set_error(&a->archive, EINVAL,+		    "Invalid filename");+		return (ARCHIVE_WARN);+	}++	w->typ = archive_entry_filetype(entry);+	w->populz = 0U;+	if (w->typ == AE_IFREG) {+		warc_essential_hdr_t rh = {+			WT_RSRC,+			/*uri*/NULL,+			/*urn*/NULL,+			/*rtm*/0,+			/*mtm*/0,+			/*cty*/NULL,+			/*len*/0,+		};+		ssize_t r;+		rh.tgturi = archive_entry_pathname(entry);+		rh.rtime = w->now;+		rh.mtime = archive_entry_mtime(entry);+		rh.cntlen = (size_t)archive_entry_size(entry);++		archive_string_init(&hdr);+		r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh);+		if (r < 0) {+			/* don't bother */+			archive_set_error(+				&a->archive,+				ARCHIVE_ERRNO_FILE_FORMAT,+				"cannot archive file");+			return (ARCHIVE_WARN);+		}+		/* otherwise append to output stream */+		__archive_write_output(a, hdr.s, r);+		/* and let subsequent calls to _data() know about the size */+		w->populz = rh.cntlen;+		archive_string_free(&hdr);+		return (ARCHIVE_OK);+	}+	/* just resort to erroring as per Tim's advice */+	__archive_write_entry_filetype_unsupported(+	    &a->archive, entry, "WARC");+	return (ARCHIVE_FAILED);+}++static ssize_t+_warc_data(struct archive_write *a, const void *buf, size_t len)+{+	struct warc_s *w = a->format_data;++	if (w->typ == AE_IFREG) {+		int rc;++		/* never write more bytes than announced */+		if (len > w->populz) {+			len = (size_t)w->populz;+		}++		/* now then, out we put the whole shebang */+		rc = __archive_write_output(a, buf, len);+		if (rc != ARCHIVE_OK) {+			return rc;+		}+	}+	return len;+}++static int+_warc_finish_entry(struct archive_write *a)+{+	static const char _eor[] = "\r\n\r\n";+	struct warc_s *w = a->format_data;++	if (w->typ == AE_IFREG) {+		int rc = __archive_write_output(a, _eor, sizeof(_eor) - 1U);++		if (rc != ARCHIVE_OK) {+			return rc;+		}+	}+	/* reset type info */+	w->typ = 0;+	return (ARCHIVE_OK);+}++static int+_warc_close(struct archive_write *a)+{+	(void)a; /* UNUSED */+	return (ARCHIVE_OK);+}++static int+_warc_free(struct archive_write *a)+{+	struct warc_s *w = a->format_data;++	free(w);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}+++/* private routines */+static void+xstrftime(struct archive_string *as, const char *fmt, time_t t)+{+/** like strftime(3) but for time_t objects */+	struct tm *rt;+#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S)+	struct tm timeHere;+#endif+#if defined(HAVE__GMTIME64_S)+	errno_t terr;+	__time64_t tmptime;+#endif+	char strtime[100];+	size_t len;++#ifdef HAVE_GMTIME_R+	if ((rt = gmtime_r(&t, &timeHere)) == NULL)+		return;+#elif defined(HAVE__GMTIME64_S)+	tmptime = t;+	terr = _gmtime64_s(&timeHere, &tmptime);+	if (terr)+		rt = NULL;+	else+		rt = &timeHere;+#else+	if ((rt = gmtime(&t)) == NULL)+		return;+#endif+	/* leave the hard yacker to our role model strftime() */+	len = strftime(strtime, sizeof(strtime)-1, fmt, rt);+	archive_strncat(as, strtime, len);+}++static ssize_t+_popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr)+{+	static const char _ver[] = "WARC/1.0\r\n";+	static const char * const _typ[LAST_WT] = {+		NULL, "warcinfo", "metadata", "resource", NULL+	};+	char std_uuid[48U];++	if (hdr.type == WT_NONE || hdr.type > WT_RSRC) {+		/* brilliant, how exactly did we get here? */+		return -1;+	}++	archive_strcpy(tgt, _ver);++	archive_string_sprintf(tgt, "WARC-Type: %s\r\n", _typ[hdr.type]);++	if (hdr.tgturi != NULL) {+		/* check if there's a xyz:// */+		static const char _uri[] = "";+		static const char _fil[] = "file://";+		const char *u;+		char *chk = strchr(hdr.tgturi, ':');++		if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') {+			/* yep, it's definitely a URI */+			u = _uri;+		} else {+			/* hm, best to prepend file:// then */+			u = _fil;+		}+		archive_string_sprintf(tgt,+			"WARC-Target-URI: %s%s\r\n", u, hdr.tgturi);+	}++	/* record time is usually when the http is sent off,+	 * just treat the archive writing as such for a moment */+	xstrftime(tgt, "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.rtime);++	/* while we're at it, record the mtime */+	xstrftime(tgt, "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.mtime);++	if (hdr.recid == NULL) {+		/* generate one, grrrr */+		warc_uuid_t u;++		_gen_uuid(&u);+		/* Unfortunately, archive_string_sprintf does not+		 * handle the minimum number following '%'.+		 * So we have to use snprintf function here instead+		 * of archive_string_snprintf function. */+#if defined(_WIN32) && !defined(__CYGWIN__) && !( defined(_MSC_VER) && _MSC_VER >= 1900)+#define snprintf _snprintf+#endif+		snprintf(+			std_uuid, sizeof(std_uuid),+			"<urn:uuid:%08x-%04x-%04x-%04x-%04x%08x>",+			u.u[0U],+			u.u[1U] >> 16U, u.u[1U] & 0xffffU,+			u.u[2U] >> 16U, u.u[2U] & 0xffffU,+			u.u[3U]);+		hdr.recid = std_uuid;+	}++	/* record-id is mandatory, fingers crossed we won't fail */+	archive_string_sprintf(tgt, "WARC-Record-ID: %s\r\n", hdr.recid);++	if (hdr.cnttyp != NULL) {+		archive_string_sprintf(tgt, "Content-Type: %s\r\n", hdr.cnttyp);+	}++	/* next one is mandatory */+	archive_string_sprintf(tgt, "Content-Length: %ju\r\n", (uintmax_t)hdr.cntlen);+	/**/+	archive_strncat(tgt, "\r\n", 2);++	return (archive_strlen(tgt) >= tsz)? -1: (ssize_t)archive_strlen(tgt);+}++static int+_gen_uuid(warc_uuid_t *tgt)+{+	archive_random(tgt->u, sizeof(tgt->u));+	/* obey uuid version 4 rules */+	tgt->u[1U] &= 0xffff0fffU;+	tgt->u[1U] |= 0x4000U;+	tgt->u[2U] &= 0x3fffffffU;+	tgt->u[2U] |= 0x80000000U;+	return 0;+}++/* archive_write_set_format_warc.c ends here */
+ c/archive_write_set_format_xar.c view
@@ -0,0 +1,3259 @@+/*-+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_LIMITS_H+#include <limits.h>+#endif+#include <stdlib.h>+#if HAVE_LIBXML_XMLWRITER_H+#include <libxml/xmlwriter.h>+#endif+#ifdef HAVE_BZLIB_H+#include <bzlib.h>+#endif+#if HAVE_LZMA_H+#include <lzma.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif++#include "archive.h"+#include "archive_digest_private.h"+#include "archive_endian.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_private.h"+#include "archive_rb.h"+#include "archive_string.h"+#include "archive_write_private.h"++/*+ * Differences to xar utility.+ * - Subdocument is not supported yet.+ * - ACL is not supported yet.+ * - When writing an XML element <link type="<file-type>">, <file-type>+ *   which is a file type a symbolic link is referencing is always marked+ *   as "broken". Xar utility uses stat(2) to get the file type, but, in+ *   libarchive format writer, we should not use it; if it is needed, we+ *   should get about it at archive_read_disk.c.+ * - It is possible to appear both <flags> and <ext2> elements.+ *   Xar utility generates <flags> on BSD platform and <ext2> on Linux+ *   platform.+ *+ */++#if !(defined(HAVE_LIBXML_XMLWRITER_H) && defined(LIBXML_VERSION) &&\+	LIBXML_VERSION >= 20703) ||\+	!defined(HAVE_ZLIB_H) || \+	!defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)+/*+ * xar needs several external libraries.+ *   o libxml2+ *   o openssl or MD5/SHA1 hash function+ *   o zlib+ *   o bzlib2 (option)+ *   o liblzma (option)+ */+int+archive_write_set_format_xar(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;++	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+	    "Xar not supported on this platform");+	return (ARCHIVE_WARN);+}++#else	/* Support xar format */++/*#define DEBUG_PRINT_TOC		1 */++#define BAD_CAST_CONST (const xmlChar *)++#define HEADER_MAGIC	0x78617221+#define HEADER_SIZE	28+#define HEADER_VERSION	1++enum sumalg {+	CKSUM_NONE = 0,+	CKSUM_SHA1 = 1,+	CKSUM_MD5 = 2+};++#define MD5_SIZE	16+#define SHA1_SIZE	20+#define MAX_SUM_SIZE	20+#define MD5_NAME	"md5"+#define SHA1_NAME	"sha1"++enum enctype {+	NONE,+	GZIP,+	BZIP2,+	LZMA,+	XZ,+};++struct chksumwork {+	enum sumalg		 alg;+#ifdef ARCHIVE_HAS_MD5+	archive_md5_ctx		 md5ctx;+#endif+#ifdef ARCHIVE_HAS_SHA1+	archive_sha1_ctx	 sha1ctx;+#endif+};++enum la_zaction {+	ARCHIVE_Z_FINISH,+	ARCHIVE_Z_RUN+};++/*+ * Universal zstream.+ */+struct la_zstream {+	const unsigned char	*next_in;+	size_t			 avail_in;+	uint64_t		 total_in;++	unsigned char		*next_out;+	size_t			 avail_out;+	uint64_t		 total_out;++	int			 valid;+	void			*real_stream;+	int			 (*code) (struct archive *a,+				    struct la_zstream *lastrm,+				    enum la_zaction action);+	int			 (*end)(struct archive *a,+				    struct la_zstream *lastrm);+};++struct chksumval {+	enum sumalg		 alg;+	size_t			 len;+	unsigned char		 val[MAX_SUM_SIZE];+};++struct heap_data {+	int			 id;+	struct heap_data	*next;+	uint64_t		 temp_offset;+	uint64_t		 length;	/* archived size.	*/+	uint64_t		 size;		/* extracted size.	*/+	enum enctype		 compression;+	struct chksumval	 a_sum;		/* archived checksum.	*/+	struct chksumval	 e_sum;		/* extracted checksum.	*/+};++struct file {+	struct archive_rb_node	 rbnode;++	int			 id;+	struct archive_entry	*entry;++	struct archive_rb_tree	 rbtree;+	struct file		*next;+	struct file		*chnext;+	struct file		*hlnext;+	/* For hardlinked files.+	 * Use only when archive_entry_nlink() > 1 */+	struct file		*hardlink_target;+	struct file		*parent;	/* parent directory entry */+	/*+	 * To manage sub directory files.+	 * We use 'chnext' (a member of struct file) to chain.+	 */+	struct {+		struct file	*first;+		struct file	**last;+	}			 children;++	/* For making a directory tree. */+        struct archive_string    parentdir;+        struct archive_string    basename;+        struct archive_string    symlink;++	int			 ea_idx;+	struct {+		struct heap_data *first;+		struct heap_data **last;+	}			 xattr;+	struct heap_data	 data;+        struct archive_string    script;++	signed int		 virtual:1;+	signed int		 dir:1;+};++struct hardlink {+	struct archive_rb_node	 rbnode;+	int			 nlink;+	struct {+		struct file	*first;+		struct file	**last;+	}			 file_list;+};++struct xar {+	int			 temp_fd;+	uint64_t		 temp_offset;++	int			 file_idx;+	struct file		*root;+	struct file		*cur_dirent;+	struct archive_string	 cur_dirstr;+	struct file		*cur_file;+	uint64_t		 bytes_remaining;+	struct archive_string	 tstr;+	struct archive_string	 vstr;++	enum sumalg		 opt_toc_sumalg;+	enum sumalg		 opt_sumalg;+	enum enctype		 opt_compression;+	int			 opt_compression_level;+	uint32_t		 opt_threads;++	struct chksumwork	 a_sumwrk;	/* archived checksum.	*/+	struct chksumwork	 e_sumwrk;	/* extracted checksum.	*/+	struct la_zstream	 stream;+	struct archive_string_conv *sconv;+	/*+	 * Compressed data buffer.+	 */+	unsigned char		 wbuff[1024 * 64];+	size_t			 wbuff_remaining;++	struct heap_data	 toc;+	/*+	 * The list of all file entries is used to manage struct file+	 * objects.+	 * We use 'next' (a member of struct file) to chain.+	 */+	struct {+		struct file	*first;+		struct file	**last;+	}			 file_list;+	/*+	 * The list of hard-linked file entries.+	 * We use 'hlnext' (a member of struct file) to chain.+	 */+	struct archive_rb_tree	 hardlink_rbtree;+};++static int	xar_options(struct archive_write *,+		    const char *, const char *);+static int	xar_write_header(struct archive_write *,+		    struct archive_entry *);+static ssize_t	xar_write_data(struct archive_write *,+		    const void *, size_t);+static int	xar_finish_entry(struct archive_write *);+static int	xar_close(struct archive_write *);+static int	xar_free(struct archive_write *);++static struct file *file_new(struct archive_write *a, struct archive_entry *);+static void	file_free(struct file *);+static struct file *file_create_virtual_dir(struct archive_write *a, struct xar *,+		    const char *);+static int	file_add_child_tail(struct file *, struct file *);+static struct file *file_find_child(struct file *, const char *);+static int	file_gen_utility_names(struct archive_write *,+		    struct file *);+static int	get_path_component(char *, int, const char *);+static int	file_tree(struct archive_write *, struct file **);+static void	file_register(struct xar *, struct file *);+static void	file_init_register(struct xar *);+static void	file_free_register(struct xar *);+static int	file_register_hardlink(struct archive_write *,+		    struct file *);+static void	file_connect_hardlink_files(struct xar *);+static void	file_init_hardlinks(struct xar *);+static void	file_free_hardlinks(struct xar *);++static void	checksum_init(struct chksumwork *, enum sumalg);+static void	checksum_update(struct chksumwork *, const void *, size_t);+static void	checksum_final(struct chksumwork *, struct chksumval *);+static int	compression_init_encoder_gzip(struct archive *,+		    struct la_zstream *, int, int);+static int	compression_code_gzip(struct archive *,+		    struct la_zstream *, enum la_zaction);+static int	compression_end_gzip(struct archive *, struct la_zstream *);+static int	compression_init_encoder_bzip2(struct archive *,+		    struct la_zstream *, int);+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+static int	compression_code_bzip2(struct archive *,+		    struct la_zstream *, enum la_zaction);+static int	compression_end_bzip2(struct archive *, struct la_zstream *);+#endif+static int	compression_init_encoder_lzma(struct archive *,+		    struct la_zstream *, int);+static int	compression_init_encoder_xz(struct archive *,+		    struct la_zstream *, int, int);+#if defined(HAVE_LZMA_H)+static int	compression_code_lzma(struct archive *,+		    struct la_zstream *, enum la_zaction);+static int	compression_end_lzma(struct archive *, struct la_zstream *);+#endif+static int	xar_compression_init_encoder(struct archive_write *);+static int	compression_code(struct archive *,+		    struct la_zstream *, enum la_zaction);+static int	compression_end(struct archive *,+		    struct la_zstream *);+static int	save_xattrs(struct archive_write *, struct file *);+static int	getalgsize(enum sumalg);+static const char *getalgname(enum sumalg);++int+archive_write_set_format_xar(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct xar *xar;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_xar");++	/* If another format was already registered, unregister it. */+	if (a->format_free != NULL)+		(a->format_free)(a);++	xar = calloc(1, sizeof(*xar));+	if (xar == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate xar data");+		return (ARCHIVE_FATAL);+	}+	xar->temp_fd = -1;+	file_init_register(xar);+	file_init_hardlinks(xar);+	archive_string_init(&(xar->tstr));+	archive_string_init(&(xar->vstr));++	/*+	 * Create the root directory.+	 */+	xar->root = file_create_virtual_dir(a, xar, "");+	if (xar->root == NULL) {+		free(xar);+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate xar data");+		return (ARCHIVE_FATAL);+	}+	xar->root->parent = xar->root;+	file_register(xar, xar->root);+	xar->cur_dirent = xar->root;+	archive_string_init(&(xar->cur_dirstr));+	archive_string_ensure(&(xar->cur_dirstr), 1);+	xar->cur_dirstr.s[0] = 0;++	/*+	 * Initialize option.+	 */+	/* Set default checksum type. */+	xar->opt_toc_sumalg = CKSUM_SHA1;+	xar->opt_sumalg = CKSUM_SHA1;+	/* Set default compression type, level, and number of threads. */+	xar->opt_compression = GZIP;+	xar->opt_compression_level = 6;+	xar->opt_threads = 1;++	a->format_data = xar;++	a->format_name = "xar";+	a->format_options = xar_options;+	a->format_write_header = xar_write_header;+	a->format_write_data = xar_write_data;+	a->format_finish_entry = xar_finish_entry;+	a->format_close = xar_close;+	a->format_free = xar_free;+	a->archive.archive_format = ARCHIVE_FORMAT_XAR;+	a->archive.archive_format_name = "xar";++	return (ARCHIVE_OK);+}++static int+xar_options(struct archive_write *a, const char *key, const char *value)+{+	struct xar *xar;++	xar = (struct xar *)a->format_data;++	if (strcmp(key, "checksum") == 0) {+		if (value == NULL)+			xar->opt_sumalg = CKSUM_NONE;+		else if (strcmp(value, "none") == 0)+			xar->opt_sumalg = CKSUM_NONE;+		else if (strcmp(value, "sha1") == 0)+			xar->opt_sumalg = CKSUM_SHA1;+		else if (strcmp(value, "md5") == 0)+			xar->opt_sumalg = CKSUM_MD5;+		else {+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "Unknown checksum name: `%s'",+			    value);+			return (ARCHIVE_FAILED);+		}+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "compression") == 0) {+		const char *name = NULL;++		if (value == NULL)+			xar->opt_compression = NONE;+		else if (strcmp(value, "none") == 0)+			xar->opt_compression = NONE;+		else if (strcmp(value, "gzip") == 0)+			xar->opt_compression = GZIP;+		else if (strcmp(value, "bzip2") == 0)+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+			xar->opt_compression = BZIP2;+#else+			name = "bzip2";+#endif+		else if (strcmp(value, "lzma") == 0)+#if HAVE_LZMA_H+			xar->opt_compression = LZMA;+#else+			name = "lzma";+#endif+		else if (strcmp(value, "xz") == 0)+#if HAVE_LZMA_H+			xar->opt_compression = XZ;+#else+			name = "xz";+#endif+		else {+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "Unknown compression name: `%s'",+			    value);+			return (ARCHIVE_FAILED);+		}+		if (name != NULL) {+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "`%s' compression not supported "+			    "on this platform",+			    name);+			return (ARCHIVE_FAILED);+		}+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "compression-level") == 0) {+		if (value == NULL ||+		    !(value[0] >= '0' && value[0] <= '9') ||+		    value[1] != '\0') {+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "Illegal value `%s'",+			    value);+			return (ARCHIVE_FAILED);+		}+		xar->opt_compression_level = value[0] - '0';+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "toc-checksum") == 0) {+		if (value == NULL)+			xar->opt_toc_sumalg = CKSUM_NONE;+		else if (strcmp(value, "none") == 0)+			xar->opt_toc_sumalg = CKSUM_NONE;+		else if (strcmp(value, "sha1") == 0)+			xar->opt_toc_sumalg = CKSUM_SHA1;+		else if (strcmp(value, "md5") == 0)+			xar->opt_toc_sumalg = CKSUM_MD5;+		else {+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "Unknown checksum name: `%s'",+			    value);+			return (ARCHIVE_FAILED);+		}+		return (ARCHIVE_OK);+	}+	if (strcmp(key, "threads") == 0) {+		char *endptr;++		if (value == NULL)+			return (ARCHIVE_FAILED);+		errno = 0;+		xar->opt_threads = (int)strtoul(value, &endptr, 10);+		if (errno != 0 || *endptr != '\0') {+			xar->opt_threads = 1;+			archive_set_error(&(a->archive),+			    ARCHIVE_ERRNO_MISC,+			    "Illegal value `%s'",+			    value);+			return (ARCHIVE_FAILED);+		}+		if (xar->opt_threads == 0) {+#ifdef HAVE_LZMA_STREAM_ENCODER_MT+			xar->opt_threads = lzma_cputhreads();+#else+			xar->opt_threads = 1;+#endif+		}+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++static int+xar_write_header(struct archive_write *a, struct archive_entry *entry)+{+	struct xar *xar;+	struct file *file;+	struct archive_entry *file_entry;+	int r, r2;++	xar = (struct xar *)a->format_data;+	xar->cur_file = NULL;+	xar->bytes_remaining = 0;++	if (xar->sconv == NULL) {+		xar->sconv = archive_string_conversion_to_charset(+		    &a->archive, "UTF-8", 1);+		if (xar->sconv == NULL)+			return (ARCHIVE_FATAL);+	}++	file = file_new(a, entry);+	if (file == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate data");+		return (ARCHIVE_FATAL);+	}+	r2 = file_gen_utility_names(a, file);+	if (r2 < ARCHIVE_WARN)+		return (r2);++	/*+	 * Ignore a path which looks like the top of directory name+	 * since we have already made the root directory of an Xar archive.+	 */+	if (archive_strlen(&(file->parentdir)) == 0 &&+	    archive_strlen(&(file->basename)) == 0) {+		file_free(file);+		return (r2);+	}++	/* Add entry into tree */+	file_entry = file->entry;+	r = file_tree(a, &file);+	if (r != ARCHIVE_OK)+		return (r);+	/* There is the same file in tree and+	 * the current file is older than the file in tree.+	 * So we don't need the current file data anymore. */+	if (file->entry != file_entry)+		return (r2);+	if (file->id == 0)+		file_register(xar, file);++	/* A virtual file, which is a directory, does not have+	 * any contents and we won't store it into a archive+	 * file other than its name. */+	if (file->virtual)+		return (r2);++	/*+	 * Prepare to save the contents of the file.+	 */+	if (xar->temp_fd == -1) {+		int algsize;+		xar->temp_offset = 0;+		xar->temp_fd = __archive_mktemp(NULL);+		if (xar->temp_fd < 0) {+			archive_set_error(&a->archive, errno,+			    "Couldn't create temporary file");+			return (ARCHIVE_FATAL);+		}+		algsize = getalgsize(xar->opt_toc_sumalg);+		if (algsize > 0) {+			if (lseek(xar->temp_fd, algsize, SEEK_SET) < 0) {+				archive_set_error(&(a->archive), errno,+				    "lseek failed");+				return (ARCHIVE_FATAL);+			}+			xar->temp_offset = algsize;+		}+	}++	if (archive_entry_hardlink(file->entry) == NULL) {+		r = save_xattrs(a, file);+		if (r != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	/* Non regular files contents are unneeded to be saved to+	 * a temporary file. */+	if (archive_entry_filetype(file->entry) != AE_IFREG)+		return (r2);++	/*+	 * Set the current file to cur_file to read its contents.+	 */+	xar->cur_file = file;++	if (archive_entry_nlink(file->entry) > 1) {+		r = file_register_hardlink(a, file);+		if (r != ARCHIVE_OK)+			return (r);+		if (archive_entry_hardlink(file->entry) != NULL) {+			archive_entry_unset_size(file->entry);+			return (r2);+		}+	}++	/* Save a offset of current file in temporary file. */+	file->data.temp_offset = xar->temp_offset;+	file->data.size = archive_entry_size(file->entry);+	file->data.compression = xar->opt_compression;+	xar->bytes_remaining = archive_entry_size(file->entry);+	checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);+	checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);+	r = xar_compression_init_encoder(a);++	if (r != ARCHIVE_OK)+		return (r);+	else+		return (r2);+}++static int+write_to_temp(struct archive_write *a, const void *buff, size_t s)+{+	struct xar *xar;+	const unsigned char *p;+	ssize_t ws;++	xar = (struct xar *)a->format_data;+	p = (const unsigned char *)buff;+	while (s) {+		ws = write(xar->temp_fd, p, s);+		if (ws < 0) {+			archive_set_error(&(a->archive), errno,+			    "fwrite function failed");+			return (ARCHIVE_FATAL);+		}+		s -= ws;+		p += ws;+		xar->temp_offset += ws;+	}+	return (ARCHIVE_OK);+}++static ssize_t+xar_write_data(struct archive_write *a, const void *buff, size_t s)+{+	struct xar *xar;+	enum la_zaction run;+	size_t size = 0;+	size_t rsize;+	int r;++	xar = (struct xar *)a->format_data;++	if (s > xar->bytes_remaining)+		s = (size_t)xar->bytes_remaining;+	if (s == 0 || xar->cur_file == NULL)+		return (0);+	if (xar->cur_file->data.compression == NONE) {+		checksum_update(&(xar->e_sumwrk), buff, s);+		checksum_update(&(xar->a_sumwrk), buff, s);+		size = rsize = s;+	} else {+		xar->stream.next_in = (const unsigned char *)buff;+		xar->stream.avail_in = s;+		if (xar->bytes_remaining > s)+			run = ARCHIVE_Z_RUN;+		else+			run = ARCHIVE_Z_FINISH;+		/* Compress file data. */+		for (;;) {+			r = compression_code(&(a->archive), &(xar->stream),+			    run);+			if (r != ARCHIVE_OK && r != ARCHIVE_EOF)+				return (ARCHIVE_FATAL);+			if (xar->stream.avail_out == 0 ||+			    run == ARCHIVE_Z_FINISH) {+				size = sizeof(xar->wbuff) -+				    xar->stream.avail_out;+				checksum_update(&(xar->a_sumwrk), xar->wbuff,+				    size);+				xar->cur_file->data.length += size;+				if (write_to_temp(a, xar->wbuff,+				    size) != ARCHIVE_OK)+					return (ARCHIVE_FATAL);+				if (r == ARCHIVE_OK) {+					/* Output buffer was full */+					xar->stream.next_out = xar->wbuff;+					xar->stream.avail_out =+					    sizeof(xar->wbuff);+				} else {+					/* ARCHIVE_EOF - We are done */+					break;+				}+			} else {+				/* Compressor wants more input */+				break;+			}+		}+		rsize = s - xar->stream.avail_in;+		checksum_update(&(xar->e_sumwrk), buff, rsize);+	}+#if !defined(_WIN32) || defined(__CYGWIN__)+	if (xar->bytes_remaining ==+	    (uint64_t)archive_entry_size(xar->cur_file->entry)) {+		/*+		 * Get the path of a shell script if so.+		 */+		const unsigned char *b = (const unsigned char *)buff;++		archive_string_empty(&(xar->cur_file->script));+		if (rsize > 2 && b[0] == '#' && b[1] == '!') {+			size_t i, end, off;++			off = 2;+			if (b[off] == ' ')+				off++;+#ifdef PATH_MAX+			if ((rsize - off) > PATH_MAX)+				end = off + PATH_MAX;+			else+#endif+				end = rsize;+			/* Find the end of a script path. */+			for (i = off; i < end && b[i] != '\0' &&+			    b[i] != '\n' && b[i] != '\r' &&+			    b[i] != ' ' && b[i] != '\t'; i++)+				;+			archive_strncpy(&(xar->cur_file->script), b + off,+			    i - off);+		}+	}+#endif++	if (xar->cur_file->data.compression == NONE) {+		if (write_to_temp(a, buff, size) != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		xar->cur_file->data.length += size;+	}+	xar->bytes_remaining -= rsize;++	return (rsize);+}++static int+xar_finish_entry(struct archive_write *a)+{+	struct xar *xar;+	struct file *file;+	size_t s;+	ssize_t w;++	xar = (struct xar *)a->format_data;+	if (xar->cur_file == NULL)+		return (ARCHIVE_OK);++	while (xar->bytes_remaining > 0) {+		s = (size_t)xar->bytes_remaining;+		if (s > a->null_length)+			s = a->null_length;+		w = xar_write_data(a, a->nulls, s);+		if (w > 0)+			xar->bytes_remaining -= w;+		else+			return (w);+	}+	file = xar->cur_file;+	checksum_final(&(xar->e_sumwrk), &(file->data.e_sum));+	checksum_final(&(xar->a_sumwrk), &(file->data.a_sum));+	xar->cur_file = NULL;++	return (ARCHIVE_OK);+}++static int+xmlwrite_string_attr(struct archive_write *a, xmlTextWriterPtr writer,+	const char *key, const char *value,+	const char *attrkey, const char *attrvalue)+{+	int r;++	r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key));+	if (r < 0) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "xmlTextWriterStartElement() failed: %d", r);+		return (ARCHIVE_FATAL);+	}+	if (attrkey != NULL && attrvalue != NULL) {+		r = xmlTextWriterWriteAttribute(writer,+		    BAD_CAST_CONST(attrkey), BAD_CAST_CONST(attrvalue));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterWriteAttribute() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+	}+	if (value != NULL) {+		r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterWriteString() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+	}+	r = xmlTextWriterEndElement(writer);+	if (r < 0) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "xmlTextWriterEndElement() failed: %d", r);+		return (ARCHIVE_FATAL);+	}+	return (ARCHIVE_OK);+}++static int+xmlwrite_string(struct archive_write *a, xmlTextWriterPtr writer,+	const char *key, const char *value)+{+	int r;++	if (value == NULL)+		return (ARCHIVE_OK);++	r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key));+	if (r < 0) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "xmlTextWriterStartElement() failed: %d", r);+		return (ARCHIVE_FATAL);+	}+	if (value != NULL) {+		r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterWriteString() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+	}+	r = xmlTextWriterEndElement(writer);+	if (r < 0) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "xmlTextWriterEndElement() failed: %d", r);+		return (ARCHIVE_FATAL);+	}+	return (ARCHIVE_OK);+}++static int+xmlwrite_fstring(struct archive_write *a, xmlTextWriterPtr writer,+	const char *key, const char *fmt, ...)+{+	struct xar *xar;+	va_list ap;++	xar = (struct xar *)a->format_data;+	va_start(ap, fmt);+	archive_string_empty(&xar->vstr);+	archive_string_vsprintf(&xar->vstr, fmt, ap);+	va_end(ap);+	return (xmlwrite_string(a, writer, key, xar->vstr.s));+}++static int+xmlwrite_time(struct archive_write *a, xmlTextWriterPtr writer,+	const char *key, time_t t, int z)+{+	char timestr[100];+	struct tm tm;+#if defined(HAVE__GMTIME64_S)+	__time64_t tmptime;+#endif++#if defined(HAVE_GMTIME_R)+	gmtime_r(&t, &tm);+#elif defined(HAVE__GMTIME64_S)+	tmptime = t;+	_gmtime64_s(&tm, &tmptime);+#else+	memcpy(&tm, gmtime(&t), sizeof(tm));+#endif+	memset(&timestr, 0, sizeof(timestr));+	/* Do not use %F and %T for portability. */+	strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%S", &tm);+	if (z)+		strcat(timestr, "Z");+	return (xmlwrite_string(a, writer, key, timestr));+}++static int+xmlwrite_mode(struct archive_write *a, xmlTextWriterPtr writer,+	const char *key, mode_t mode)+{+	char ms[5];++	ms[0] = '0';+	ms[1] = '0' + ((mode >> 6) & 07);+	ms[2] = '0' + ((mode >> 3) & 07);+	ms[3] = '0' + (mode & 07);+	ms[4] = '\0';++	return (xmlwrite_string(a, writer, key, ms));+}++static int+xmlwrite_sum(struct archive_write *a, xmlTextWriterPtr writer,+	const char *key, struct chksumval *sum)+{+	const char *algname;+	int algsize;+	char buff[MAX_SUM_SIZE*2 + 1];+	char *p;+	unsigned char *s;+	int i, r;++	if (sum->len > 0) {+		algname = getalgname(sum->alg);+		algsize = getalgsize(sum->alg);+		if (algname != NULL) {+			const char *hex = "0123456789abcdef";+			p = buff;+			s = sum->val;+			for (i = 0; i < algsize; i++) {+				*p++ = hex[(*s >> 4)];+				*p++ = hex[(*s & 0x0f)];+				s++;+			}+			*p = '\0';+			r = xmlwrite_string_attr(a, writer,+			    key, buff,+			    "style", algname);+			if (r < 0)+				return (ARCHIVE_FATAL);+		}+	}+	return (ARCHIVE_OK);+}++static int+xmlwrite_heap(struct archive_write *a, xmlTextWriterPtr writer,+	struct heap_data *heap)+{+	const char *encname;+	int r;++	r = xmlwrite_fstring(a, writer, "length", "%ju", heap->length);+	if (r < 0)+		return (ARCHIVE_FATAL);+	r = xmlwrite_fstring(a, writer, "offset", "%ju", heap->temp_offset);+	if (r < 0)+		return (ARCHIVE_FATAL);+	r = xmlwrite_fstring(a, writer, "size", "%ju", heap->size);+	if (r < 0)+		return (ARCHIVE_FATAL);+	switch (heap->compression) {+	case GZIP:+		encname = "application/x-gzip"; break;+	case BZIP2:+		encname = "application/x-bzip2"; break;+	case LZMA:+		encname = "application/x-lzma"; break;+	case XZ:+		encname = "application/x-xz"; break;+	default:+		encname = "application/octet-stream"; break;+	}+	r = xmlwrite_string_attr(a, writer, "encoding", NULL,+	    "style", encname);+	if (r < 0)+		return (ARCHIVE_FATAL);+	r = xmlwrite_sum(a, writer, "archived-checksum", &(heap->a_sum));+	if (r < 0)+		return (ARCHIVE_FATAL);+	r = xmlwrite_sum(a, writer, "extracted-checksum", &(heap->e_sum));+	if (r < 0)+		return (ARCHIVE_FATAL);+	return (ARCHIVE_OK);+}++/*+ * xar utility records fflags as following xml elements:+ *   <flags>+ *     <UserNoDump/>+ *     .....+ *   </flags>+ * or+ *   <ext2>+ *     <NoDump/>+ *     .....+ *   </ext2>+ * If xar is running on BSD platform, records <flags>..</flags>;+ * if xar is running on linux platform, records <ext2>..</ext2>;+ * otherwise does not record.+ *+ * Our implements records both <flags> and <ext2> if it's necessary.+ */+static int+make_fflags_entry(struct archive_write *a, xmlTextWriterPtr writer,+    const char *element, const char *fflags_text)+{+	static const struct flagentry {+		const char	*name;+		const char	*xarname;+	}+	flagbsd[] = {+		{ "sappnd",	"SystemAppend"},+		{ "sappend",	"SystemAppend"},+		{ "arch",	"SystemArchived"},+		{ "archived",	"SystemArchived"},+		{ "schg",	"SystemImmutable"},+		{ "schange",	"SystemImmutable"},+		{ "simmutable",	"SystemImmutable"},+		{ "nosunlnk",	"SystemNoUnlink"},+		{ "nosunlink",	"SystemNoUnlink"},+		{ "snapshot",	"SystemSnapshot"},+		{ "uappnd",	"UserAppend"},+		{ "uappend",	"UserAppend"},+		{ "uchg",	"UserImmutable"},+		{ "uchange",	"UserImmutable"},+		{ "uimmutable",	"UserImmutable"},+		{ "nodump",	"UserNoDump"},+		{ "noopaque",	"UserOpaque"},+		{ "nouunlnk",	"UserNoUnlink"},+		{ "nouunlink",	"UserNoUnlink"},+		{ NULL, NULL}+	},+	flagext2[] = {+		{ "sappnd",	"AppendOnly"},+		{ "sappend",	"AppendOnly"},+		{ "schg",	"Immutable"},+		{ "schange",	"Immutable"},+		{ "simmutable",	"Immutable"},+		{ "nodump",	"NoDump"},+		{ "nouunlnk",	"Undelete"},+		{ "nouunlink",	"Undelete"},+		{ "btree",	"BTree"},+		{ "comperr",	"CompError"},+		{ "compress",	"Compress"},+		{ "noatime",	"NoAtime"},+		{ "compdirty",	"CompDirty"},+		{ "comprblk",	"CompBlock"},+		{ "dirsync",	"DirSync"},+		{ "hashidx",	"HashIndexed"},+		{ "imagic",	"iMagic"},+		{ "journal",	"Journaled"},+		{ "securedeletion",	"SecureDeletion"},+		{ "sync",	"Synchronous"},+		{ "notail",	"NoTail"},+		{ "topdir",	"TopDir"},+		{ "reserved",	"Reserved"},+		{ NULL, NULL}+	};+	const struct flagentry *fe, *flagentry;+#define FLAGENTRY_MAXSIZE ((sizeof(flagbsd)+sizeof(flagext2))/sizeof(flagbsd))+	const struct flagentry *avail[FLAGENTRY_MAXSIZE];+	const char *p;+	int i, n, r;++	if (strcmp(element, "ext2") == 0)+		flagentry = flagext2;+	else+		flagentry = flagbsd;+	n = 0;+	p = fflags_text;+	do {+		const char *cp;++		cp = strchr(p, ',');+		if (cp == NULL)+			cp = p + strlen(p);++		for (fe = flagentry; fe->name != NULL; fe++) {+			if (fe->name[cp - p] != '\0'+			    || p[0] != fe->name[0])+				continue;+			if (strncmp(p, fe->name, cp - p) == 0) {+				avail[n++] = fe;+				break;+			}+		}+		if (*cp == ',')+			p = cp + 1;+		else+			p = NULL;+	} while (p != NULL);++	if (n > 0) {+		r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(element));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterStartElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+		for (i = 0; i < n; i++) {+			r = xmlwrite_string(a, writer,+			    avail[i]->xarname, NULL);+			if (r != ARCHIVE_OK)+				return (r);+		}++		r = xmlTextWriterEndElement(writer);+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterEndElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+	}+	return (ARCHIVE_OK);+}++static int+make_file_entry(struct archive_write *a, xmlTextWriterPtr writer,+    struct file *file)+{+	struct xar *xar;+	const char *filetype, *filelink, *fflags;+	struct archive_string linkto;+	struct heap_data *heap;+	unsigned char *tmp;+	const char *p;+	size_t len;+	int r, r2, l, ll;++	xar = (struct xar *)a->format_data;+	r2 = ARCHIVE_OK;++	/*+	 * Make a file name entry, "<name>".+	 */+	l = ll = archive_strlen(&(file->basename));+	tmp = malloc(l);+	if (tmp == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate memory");+		return (ARCHIVE_FATAL);+	}+	r = UTF8Toisolat1(tmp, &l, BAD_CAST(file->basename.s), &ll);+	free(tmp);+	if (r < 0) {+		r = xmlTextWriterStartElement(writer, BAD_CAST("name"));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterStartElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+		r = xmlTextWriterWriteAttribute(writer,+		    BAD_CAST("enctype"), BAD_CAST("base64"));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterWriteAttribute() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+		r = xmlTextWriterWriteBase64(writer, file->basename.s,+		    0, archive_strlen(&(file->basename)));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterWriteBase64() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+		r = xmlTextWriterEndElement(writer);+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterEndElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+	} else {+		r = xmlwrite_string(a, writer, "name", file->basename.s);+		if (r < 0)+			return (ARCHIVE_FATAL);+	}++	/*+	 * Make a file type entry, "<type>".+	 */+	filelink = NULL;+	archive_string_init(&linkto);+	switch (archive_entry_filetype(file->entry)) {+	case AE_IFDIR:+		filetype = "directory"; break;+	case AE_IFLNK:+		filetype = "symlink"; break;+	case AE_IFCHR:+		filetype = "character special"; break;+	case AE_IFBLK:+		filetype = "block special"; break;+	case AE_IFSOCK:+		filetype = "socket"; break;+	case AE_IFIFO:+		filetype = "fifo"; break;+	case AE_IFREG:+	default:+		if (file->hardlink_target != NULL) {+			filetype = "hardlink";+			filelink = "link";+			if (file->hardlink_target == file)+				archive_strcpy(&linkto, "original");+			else+				archive_string_sprintf(&linkto, "%d",+				    file->hardlink_target->id);+		} else+			filetype = "file";+		break;+	}+	r = xmlwrite_string_attr(a, writer, "type", filetype,+	    filelink, linkto.s);+	archive_string_free(&linkto);+	if (r < 0)+		return (ARCHIVE_FATAL);++	/*+	 * On a virtual directory, we record "name" and "type" only.+	 */+	if (file->virtual)+		return (ARCHIVE_OK);++	switch (archive_entry_filetype(file->entry)) {+	case AE_IFLNK:+		/*+		 * xar utility has checked a file type, which+		 * a symbolic-link file has referenced.+		 * For example:+		 *   <link type="directory">../ref/</link>+		 *   The symlink target file is "../ref/" and its+		 *   file type is a directory.+		 *+		 *   <link type="file">../f</link>+		 *   The symlink target file is "../f" and its+		 *   file type is a regular file.+		 *+		 * But our implementation cannot do it, and then we+		 * always record that a attribute "type" is "broken",+		 * for example:+		 *   <link type="broken">foo/bar</link>+		 *   It means "foo/bar" is not reachable.+		 */+		r = xmlwrite_string_attr(a, writer, "link",+		    file->symlink.s,+		    "type", "broken");+		if (r < 0)+			return (ARCHIVE_FATAL);+		break;+	case AE_IFCHR:+	case AE_IFBLK:+		r = xmlTextWriterStartElement(writer, BAD_CAST("device"));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterStartElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+		r = xmlwrite_fstring(a, writer, "major",+		    "%d", archive_entry_rdevmajor(file->entry));+		if (r < 0)+			return (ARCHIVE_FATAL);+		r = xmlwrite_fstring(a, writer, "minor",+		    "%d", archive_entry_rdevminor(file->entry));+		if (r < 0)+			return (ARCHIVE_FATAL);+		r = xmlTextWriterEndElement(writer);+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterEndElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+		break;+	default:+		break;+	}++	/*+	 * Make a inode entry, "<inode>".+	 */+	r = xmlwrite_fstring(a, writer, "inode",+	    "%jd", archive_entry_ino64(file->entry));+	if (r < 0)+		return (ARCHIVE_FATAL);+	if (archive_entry_dev(file->entry) != 0) {+		r = xmlwrite_fstring(a, writer, "deviceno",+		    "%d", archive_entry_dev(file->entry));+		if (r < 0)+			return (ARCHIVE_FATAL);+	}++	/*+	 * Make a file mode entry, "<mode>".+	 */+	r = xmlwrite_mode(a, writer, "mode",+	    archive_entry_mode(file->entry));+	if (r < 0)+		return (ARCHIVE_FATAL);++	/*+	 * Make a user entry, "<uid>" and "<user>.+	 */+	r = xmlwrite_fstring(a, writer, "uid",+	    "%d", archive_entry_uid(file->entry));+	if (r < 0)+		return (ARCHIVE_FATAL);+	r = archive_entry_uname_l(file->entry, &p, &len, xar->sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Uname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate uname '%s' to UTF-8",+		    archive_entry_uname(file->entry));+		r2 = ARCHIVE_WARN;+	}+	if (len > 0) {+		r = xmlwrite_string(a, writer, "user", p);+		if (r < 0)+			return (ARCHIVE_FATAL);+	}++	/*+	 * Make a group entry, "<gid>" and "<group>.+	 */+	r = xmlwrite_fstring(a, writer, "gid",+	    "%d", archive_entry_gid(file->entry));+	if (r < 0)+		return (ARCHIVE_FATAL);+	r = archive_entry_gname_l(file->entry, &p, &len, xar->sconv);+	if (r != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Gname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate gname '%s' to UTF-8",+		    archive_entry_gname(file->entry));+		r2 = ARCHIVE_WARN;+	}+	if (len > 0) {+		r = xmlwrite_string(a, writer, "group", p);+		if (r < 0)+			return (ARCHIVE_FATAL);+	}++	/*+	 * Make a ctime entry, "<ctime>".+	 */+	if (archive_entry_ctime_is_set(file->entry)) {+		r = xmlwrite_time(a, writer, "ctime",+		    archive_entry_ctime(file->entry), 1);+		if (r < 0)+			return (ARCHIVE_FATAL);+	}++	/*+	 * Make a mtime entry, "<mtime>".+	 */+	if (archive_entry_mtime_is_set(file->entry)) {+		r = xmlwrite_time(a, writer, "mtime",+		    archive_entry_mtime(file->entry), 1);+		if (r < 0)+			return (ARCHIVE_FATAL);+	}++	/*+	 * Make a atime entry, "<atime>".+	 */+	if (archive_entry_atime_is_set(file->entry)) {+		r = xmlwrite_time(a, writer, "atime",+		    archive_entry_atime(file->entry), 1);+		if (r < 0)+			return (ARCHIVE_FATAL);+	}++	/*+	 * Make fflags entries, "<flags>" and "<ext2>".+	 */+	fflags = archive_entry_fflags_text(file->entry);+	if (fflags != NULL) {+		r = make_fflags_entry(a, writer, "flags", fflags);+		if (r < 0)+			return (r);+		r = make_fflags_entry(a, writer, "ext2", fflags);+		if (r < 0)+			return (r);+	}++	/*+	 * Make extended attribute entries, "<ea>".+	 */+	archive_entry_xattr_reset(file->entry);+	for (heap = file->xattr.first; heap != NULL; heap = heap->next) {+		const char *name;+		const void *value;+		size_t size;++		archive_entry_xattr_next(file->entry,+		    &name, &value, &size);+		r = xmlTextWriterStartElement(writer, BAD_CAST("ea"));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterStartElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+		r = xmlTextWriterWriteFormatAttribute(writer,+		    BAD_CAST("id"), "%d", heap->id);+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterWriteAttribute() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+		r = xmlwrite_heap(a, writer, heap);+		if (r < 0)+			return (ARCHIVE_FATAL);+		r = xmlwrite_string(a, writer, "name", name);+		if (r < 0)+			return (ARCHIVE_FATAL);++		r = xmlTextWriterEndElement(writer);+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterEndElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+	}++	/*+	 * Make a file data entry, "<data>".+	 */+	if (file->data.length > 0) {+		r = xmlTextWriterStartElement(writer, BAD_CAST("data"));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterStartElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}++		r = xmlwrite_heap(a, writer, &(file->data));+		if (r < 0)+			return (ARCHIVE_FATAL);++		r = xmlTextWriterEndElement(writer);+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterEndElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+	}++	if (archive_strlen(&file->script) > 0) {+		r = xmlTextWriterStartElement(writer, BAD_CAST("content"));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterStartElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}++		r = xmlwrite_string(a, writer,+		    "interpreter", file->script.s);+		if (r < 0)+			return (ARCHIVE_FATAL);++		r = xmlwrite_string(a, writer, "type", "script");+		if (r < 0)+			return (ARCHIVE_FATAL);++		r = xmlTextWriterEndElement(writer);+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterEndElement() failed: %d", r);+			return (ARCHIVE_FATAL);+		}+	}++	return (r2);+}++/*+ * Make the TOC+ */+static int+make_toc(struct archive_write *a)+{+	struct xar *xar;+	struct file *np;+	xmlBufferPtr bp;+	xmlTextWriterPtr writer;+	int algsize;+	int r, ret;++	xar = (struct xar *)a->format_data;++	ret = ARCHIVE_FATAL;++	/*+	 * Initialize xml writer.+	 */+	writer = NULL;+	bp = xmlBufferCreate();+	if (bp == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "xmlBufferCreate() "+		    "couldn't create xml buffer");+		goto exit_toc;+	}+	writer = xmlNewTextWriterMemory(bp, 0);+	if (writer == NULL) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "xmlNewTextWriterMemory() "+		    "couldn't create xml writer");+		goto exit_toc;+	}+	r = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);+	if (r < 0) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "xmlTextWriterStartDocument() failed: %d", r);+		goto exit_toc;+	}+	r = xmlTextWriterSetIndent(writer, 4);+	if (r < 0) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "xmlTextWriterSetIndent() failed: %d", r);+		goto exit_toc;+	}++	/*+	 * Start recording TOC+	 */+	r = xmlTextWriterStartElement(writer, BAD_CAST("xar"));+	if (r < 0) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "xmlTextWriterStartElement() failed: %d", r);+		goto exit_toc;+	}+	r = xmlTextWriterStartElement(writer, BAD_CAST("toc"));+	if (r < 0) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "xmlTextWriterStartDocument() failed: %d", r);+		goto exit_toc;+	}++	/*+	 * Record the creation time of the archive file.+	 */+	r = xmlwrite_time(a, writer, "creation-time", time(NULL), 0);+	if (r < 0)+		goto exit_toc;++	/*+	 * Record the checksum value of TOC+	 */+	algsize = getalgsize(xar->opt_toc_sumalg);+	if (algsize) {+		/*+		 * Record TOC checksum+		 */+		r = xmlTextWriterStartElement(writer, BAD_CAST("checksum"));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterStartElement() failed: %d", r);+			goto exit_toc;+		}+		r = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"),+		    BAD_CAST_CONST(getalgname(xar->opt_toc_sumalg)));+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterWriteAttribute() failed: %d", r);+			goto exit_toc;+		}++		/*+		 * Record the offset of the value of checksum of TOC+		 */+		r = xmlwrite_string(a, writer, "offset", "0");+		if (r < 0)+			goto exit_toc;++		/*+		 * Record the size of the value of checksum of TOC+		 */+		r = xmlwrite_fstring(a, writer, "size", "%d", algsize);+		if (r < 0)+			goto exit_toc;++		r = xmlTextWriterEndElement(writer);+		if (r < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "xmlTextWriterEndElement() failed: %d", r);+			goto exit_toc;+		}+	}++	np = xar->root;+	do {+		if (np != np->parent) {+			r = make_file_entry(a, writer, np);+			if (r != ARCHIVE_OK)+				goto exit_toc;+		}++		if (np->dir && np->children.first != NULL) {+			/* Enter to sub directories. */+			np = np->children.first;+			r = xmlTextWriterStartElement(writer,+			    BAD_CAST("file"));+			if (r < 0) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "xmlTextWriterStartElement() "+				    "failed: %d", r);+				goto exit_toc;+			}+			r = xmlTextWriterWriteFormatAttribute(+			    writer, BAD_CAST("id"), "%d", np->id);+			if (r < 0) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "xmlTextWriterWriteAttribute() "+				    "failed: %d", r);+				goto exit_toc;+			}+			continue;+		}+		while (np != np->parent) {+			r = xmlTextWriterEndElement(writer);+			if (r < 0) {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "xmlTextWriterEndElement() "+				    "failed: %d", r);+				goto exit_toc;+			}+			if (np->chnext == NULL) {+				/* Return to the parent directory. */+				np = np->parent;+			} else {+				np = np->chnext;+				r = xmlTextWriterStartElement(writer,+				    BAD_CAST("file"));+				if (r < 0) {+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_MISC,+					    "xmlTextWriterStartElement() "+					    "failed: %d", r);+					goto exit_toc;+				}+				r = xmlTextWriterWriteFormatAttribute(+				    writer, BAD_CAST("id"), "%d", np->id);+				if (r < 0) {+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_MISC,+					    "xmlTextWriterWriteAttribute() "+					    "failed: %d", r);+					goto exit_toc;+				}+				break;+			}+		}+	} while (np != np->parent);++	r = xmlTextWriterEndDocument(writer);+	if (r < 0) {+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_MISC,+		    "xmlTextWriterEndDocument() failed: %d", r);+		goto exit_toc;+	}+#if DEBUG_PRINT_TOC+	fprintf(stderr, "\n---TOC-- %d bytes --\n%s\n",+	    strlen((const char *)bp->content), bp->content);+#endif++	/*+	 * Compress the TOC and calculate the sum of the TOC.+	 */+	xar->toc.temp_offset = xar->temp_offset;+	xar->toc.size = bp->use;+	checksum_init(&(xar->a_sumwrk), xar->opt_toc_sumalg);++	r = compression_init_encoder_gzip(&(a->archive),+	    &(xar->stream), 6, 1);+	if (r != ARCHIVE_OK)+		goto exit_toc;+	xar->stream.next_in = bp->content;+	xar->stream.avail_in = bp->use;+	xar->stream.total_in = 0;+	xar->stream.next_out = xar->wbuff;+	xar->stream.avail_out = sizeof(xar->wbuff);+	xar->stream.total_out = 0;+	for (;;) {+		size_t size;++		r = compression_code(&(a->archive),+		    &(xar->stream), ARCHIVE_Z_FINISH);+		if (r != ARCHIVE_OK && r != ARCHIVE_EOF)+			goto exit_toc;+		size = sizeof(xar->wbuff) - xar->stream.avail_out;+		checksum_update(&(xar->a_sumwrk), xar->wbuff, size);+		if (write_to_temp(a, xar->wbuff, size) != ARCHIVE_OK)+			goto exit_toc;+		if (r == ARCHIVE_EOF)+			break;+		xar->stream.next_out = xar->wbuff;+		xar->stream.avail_out = sizeof(xar->wbuff);+	}+	r = compression_end(&(a->archive), &(xar->stream));+	if (r != ARCHIVE_OK)+		goto exit_toc;+	xar->toc.length = xar->stream.total_out;+	xar->toc.compression = GZIP;+	checksum_final(&(xar->a_sumwrk), &(xar->toc.a_sum));++	ret = ARCHIVE_OK;+exit_toc:+	if (writer)+		xmlFreeTextWriter(writer);+	if (bp)+		xmlBufferFree(bp);++	return (ret);+}++static int+flush_wbuff(struct archive_write *a)+{+	struct xar *xar;+	int r;+	size_t s;++	xar = (struct xar *)a->format_data;+	s = sizeof(xar->wbuff) - xar->wbuff_remaining;+	r = __archive_write_output(a, xar->wbuff, s);+	if (r != ARCHIVE_OK)+		return (r);+	xar->wbuff_remaining = sizeof(xar->wbuff);+	return (r);+}++static int+copy_out(struct archive_write *a, uint64_t offset, uint64_t length)+{+	struct xar *xar;+	int r;++	xar = (struct xar *)a->format_data;+	if (lseek(xar->temp_fd, offset, SEEK_SET) < 0) {+		archive_set_error(&(a->archive), errno, "lseek failed");+		return (ARCHIVE_FATAL);+	}+	while (length) {+		size_t rsize;+		ssize_t rs;+		unsigned char *wb;++		if (length > xar->wbuff_remaining)+			rsize = xar->wbuff_remaining;+		else+			rsize = (size_t)length;+		wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);+		rs = read(xar->temp_fd, wb, rsize);+		if (rs < 0) {+			archive_set_error(&(a->archive), errno,+			    "Can't read temporary file(%jd)",+			    (intmax_t)rs);+			return (ARCHIVE_FATAL);+		}+		if (rs == 0) {+			archive_set_error(&(a->archive), 0,+			    "Truncated xar archive");+			return (ARCHIVE_FATAL);+		}+		xar->wbuff_remaining -= rs;+		length -= rs;+		if (xar->wbuff_remaining == 0) {+			r = flush_wbuff(a);+			if (r != ARCHIVE_OK)+				return (r);+		}+	}+	return (ARCHIVE_OK);+}++static int+xar_close(struct archive_write *a)+{+	struct xar *xar;+	unsigned char *wb;+	uint64_t length;+	int r;++	xar = (struct xar *)a->format_data;++	/* Empty! */+	if (xar->root->children.first == NULL)+		return (ARCHIVE_OK);++	/* Save the length of all file extended attributes and contents. */+	length = xar->temp_offset;++	/* Connect hardlinked files */+	file_connect_hardlink_files(xar);++	/* Make the TOC */+	r = make_toc(a);+	if (r != ARCHIVE_OK)+		return (r);+	/*+	 * Make the xar header on wbuff(write buffer).+	 */+	wb = xar->wbuff;+	xar->wbuff_remaining = sizeof(xar->wbuff);+	archive_be32enc(&wb[0], HEADER_MAGIC);+	archive_be16enc(&wb[4], HEADER_SIZE);+	archive_be16enc(&wb[6], HEADER_VERSION);+	archive_be64enc(&wb[8], xar->toc.length);+	archive_be64enc(&wb[16], xar->toc.size);+	archive_be32enc(&wb[24], xar->toc.a_sum.alg);+	xar->wbuff_remaining -= HEADER_SIZE;++	/*+	 * Write the TOC+	 */+	r = copy_out(a, xar->toc.temp_offset, xar->toc.length);+	if (r != ARCHIVE_OK)+		return (r);++	/* Write the checksum value of the TOC. */+	if (xar->toc.a_sum.len) {+		if (xar->wbuff_remaining < xar->toc.a_sum.len) {+			r = flush_wbuff(a);+			if (r != ARCHIVE_OK)+				return (r);+		}+		wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);+		memcpy(wb, xar->toc.a_sum.val, xar->toc.a_sum.len);+		xar->wbuff_remaining -= xar->toc.a_sum.len;+	}++	/*+	 * Write all file extended attributes and contents.+	 */+	r = copy_out(a, xar->toc.a_sum.len, length);+	if (r != ARCHIVE_OK)+		return (r);+	r = flush_wbuff(a);+	return (r);+}++static int+xar_free(struct archive_write *a)+{+	struct xar *xar;++	xar = (struct xar *)a->format_data;++	/* Close the temporary file. */+	if (xar->temp_fd >= 0)+		close(xar->temp_fd);++	archive_string_free(&(xar->cur_dirstr));+	archive_string_free(&(xar->tstr));+	archive_string_free(&(xar->vstr));+	file_free_hardlinks(xar);+	file_free_register(xar);+	compression_end(&(a->archive), &(xar->stream));+	free(xar);++	return (ARCHIVE_OK);+}++static int+file_cmp_node(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	const struct file *f1 = (const struct file *)n1;+	const struct file *f2 = (const struct file *)n2;++	return (strcmp(f1->basename.s, f2->basename.s));+}++static int+file_cmp_key(const struct archive_rb_node *n, const void *key)+{+	const struct file *f = (const struct file *)n;++	return (strcmp(f->basename.s, (const char *)key));+}++static struct file *+file_new(struct archive_write *a, struct archive_entry *entry)+{+	struct file *file;+	static const struct archive_rb_tree_ops rb_ops = {+		file_cmp_node, file_cmp_key+	};++	file = calloc(1, sizeof(*file));+	if (file == NULL)+		return (NULL);++	if (entry != NULL)+		file->entry = archive_entry_clone(entry);+	else+		file->entry = archive_entry_new2(&a->archive);+	if (file->entry == NULL) {+		free(file);+		return (NULL);+	}+	__archive_rb_tree_init(&(file->rbtree), &rb_ops);+	file->children.first = NULL;+	file->children.last = &(file->children.first);+	file->xattr.first = NULL;+	file->xattr.last = &(file->xattr.first);+	archive_string_init(&(file->parentdir));+	archive_string_init(&(file->basename));+	archive_string_init(&(file->symlink));+	archive_string_init(&(file->script));+	if (entry != NULL && archive_entry_filetype(entry) == AE_IFDIR)+		file->dir = 1;++	return (file);+}++static void+file_free(struct file *file)+{+	struct heap_data *heap, *next_heap;++	heap = file->xattr.first;+	while (heap != NULL) {+		next_heap = heap->next;+		free(heap);+		heap = next_heap;+	}+	archive_string_free(&(file->parentdir));+	archive_string_free(&(file->basename));+	archive_string_free(&(file->symlink));+	archive_string_free(&(file->script));+	archive_entry_free(file->entry);+	free(file);+}++static struct file *+file_create_virtual_dir(struct archive_write *a, struct xar *xar,+    const char *pathname)+{+	struct file *file;++	(void)xar; /* UNUSED */++	file = file_new(a, NULL);+	if (file == NULL)+		return (NULL);+	archive_entry_set_pathname(file->entry, pathname);+	archive_entry_set_mode(file->entry, 0555 | AE_IFDIR);++	file->dir = 1;+	file->virtual = 1;++	return (file);+}++static int+file_add_child_tail(struct file *parent, struct file *child)+{+	if (!__archive_rb_tree_insert_node(+	    &(parent->rbtree), (struct archive_rb_node *)child))+		return (0);+	child->chnext = NULL;+	*parent->children.last = child;+	parent->children.last = &(child->chnext);+	child->parent = parent;+	return (1);+}++/*+ * Find a entry from `parent'+ */+static struct file *+file_find_child(struct file *parent, const char *child_name)+{+	struct file *np;++	np = (struct file *)__archive_rb_tree_find_node(+	    &(parent->rbtree), child_name);+	return (np);+}++#if defined(_WIN32) || defined(__CYGWIN__)+static void+cleanup_backslash(char *utf8, size_t len)+{++	/* Convert a path-separator from '\' to  '/' */+	while (*utf8 != '\0' && len) {+		if (*utf8 == '\\')+			*utf8 = '/';+		++utf8;+		--len;+	}+}+#else+#define cleanup_backslash(p, len)	/* nop */+#endif++/*+ * Generate a parent directory name and a base name from a pathname.+ */+static int+file_gen_utility_names(struct archive_write *a, struct file *file)+{+	struct xar *xar;+	const char *pp;+	char *p, *dirname, *slash;+	size_t len;+	int r = ARCHIVE_OK;++	xar = (struct xar *)a->format_data;+	archive_string_empty(&(file->parentdir));+	archive_string_empty(&(file->basename));+	archive_string_empty(&(file->symlink));++	if (file->parent == file)/* virtual root */+		return (ARCHIVE_OK);++	if (archive_entry_pathname_l(file->entry, &pp, &len, xar->sconv)+	    != 0) {+		if (errno == ENOMEM) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for Pathname");+			return (ARCHIVE_FATAL);+		}+		archive_set_error(&a->archive,+		    ARCHIVE_ERRNO_FILE_FORMAT,+		    "Can't translate pathname '%s' to UTF-8",+		    archive_entry_pathname(file->entry));+		r = ARCHIVE_WARN;+	}+	archive_strncpy(&(file->parentdir), pp, len);+	len = file->parentdir.length;+	p = dirname = file->parentdir.s;+	/*+	 * Convert a path-separator from '\' to  '/'+	 */+	cleanup_backslash(p, len);++	/*+	 * Remove leading '/', '../' and './' elements+	 */+	while (*p) {+		if (p[0] == '/') {+			p++;+			len--;+		} else if (p[0] != '.')+			break;+		else if (p[1] == '.' && p[2] == '/') {+			p += 3;+			len -= 3;+		} else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) {+			p += 2;+			len -= 2;+		} else if (p[1] == '\0') {+			p++;+			len--;+		} else+			break;+	}+	if (p != dirname) {+		memmove(dirname, p, len+1);+		p = dirname;+	}+	/*+	 * Remove "/","/." and "/.." elements from tail.+	 */+	while (len > 0) {+		size_t ll = len;++		if (p[len-1] == '/') {+			p[len-1] = '\0';+			len--;+		}+		if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {+			p[len-2] = '\0';+			len -= 2;+		}+		if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&+		    p[len-1] == '.') {+			p[len-3] = '\0';+			len -= 3;+		}+		if (ll == len)+			break;+	}+	while (*p) {+		if (p[0] == '/') {+			if (p[1] == '/')+				/* Convert '//' --> '/' */+				memmove(p, p+1, strlen(p+1) + 1);+			else if (p[1] == '.' && p[2] == '/')+				/* Convert '/./' --> '/' */+				memmove(p, p+2, strlen(p+2) + 1);+			else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {+				/* Convert 'dir/dir1/../dir2/'+				 *     --> 'dir/dir2/'+				 */+				char *rp = p -1;+				while (rp >= dirname) {+					if (*rp == '/')+						break;+					--rp;+				}+				if (rp > dirname) {+					strcpy(rp, p+3);+					p = rp;+				} else {+					strcpy(dirname, p+4);+					p = dirname;+				}+			} else+				p++;+		} else+			p++;+	}+	p = dirname;+	len = strlen(p);++	if (archive_entry_filetype(file->entry) == AE_IFLNK) {+		size_t len2;+		/* Convert symlink name too. */+		if (archive_entry_symlink_l(file->entry, &pp, &len2,+		    xar->sconv) != 0) {+			if (errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for Linkname");+				return (ARCHIVE_FATAL);+			}+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Can't translate symlink '%s' to UTF-8",+			    archive_entry_symlink(file->entry));+			r = ARCHIVE_WARN;+		}+		archive_strncpy(&(file->symlink), pp, len2);+		cleanup_backslash(file->symlink.s, file->symlink.length);+	}+	/*+	 * - Count up directory elements.+	 * - Find out the position which points the last position of+	 *   path separator('/').+	 */+	slash = NULL;+	for (; *p != '\0'; p++)+		if (*p == '/')+			slash = p;+	if (slash == NULL) {+		/* The pathname doesn't have a parent directory. */+		file->parentdir.length = len;+		archive_string_copy(&(file->basename), &(file->parentdir));+		archive_string_empty(&(file->parentdir));+		*file->parentdir.s = '\0';+		return (r);+	}++	/* Make a basename from dirname and slash */+	*slash  = '\0';+	file->parentdir.length = slash - dirname;+	archive_strcpy(&(file->basename),  slash + 1);+	return (r);+}++static int+get_path_component(char *name, int n, const char *fn)+{+	char *p;+	int l;++	p = strchr(fn, '/');+	if (p == NULL) {+		if ((l = strlen(fn)) == 0)+			return (0);+	} else+		l = p - fn;+	if (l > n -1)+		return (-1);+	memcpy(name, fn, l);+	name[l] = '\0';++	return (l);+}++/*+ * Add a new entry into the tree.+ */+static int+file_tree(struct archive_write *a, struct file **filepp)+{+#if defined(_WIN32) && !defined(__CYGWIN__)+	char name[_MAX_FNAME];/* Included null terminator size. */+#elif defined(NAME_MAX) && NAME_MAX >= 255+	char name[NAME_MAX+1];+#else+	char name[256];+#endif+	struct xar *xar = (struct xar *)a->format_data;+	struct file *dent, *file, *np;+	struct archive_entry *ent;+	const char *fn, *p;+	int l;++	file = *filepp;+	dent = xar->root;+	if (file->parentdir.length > 0)+		fn = p = file->parentdir.s;+	else+		fn = p = "";++	/*+	 * If the path of the parent directory of `file' entry is+	 * the same as the path of `cur_dirent', add isoent to+	 * `cur_dirent'.+	 */+	if (archive_strlen(&(xar->cur_dirstr))+	      == archive_strlen(&(file->parentdir)) &&+	    strcmp(xar->cur_dirstr.s, fn) == 0) {+		if (!file_add_child_tail(xar->cur_dirent, file)) {+			np = (struct file *)__archive_rb_tree_find_node(+			    &(xar->cur_dirent->rbtree),+			    file->basename.s);+			goto same_entry;+		}+		return (ARCHIVE_OK);+	}++	for (;;) {+		l = get_path_component(name, sizeof(name), fn);+		if (l == 0) {+			np = NULL;+			break;+		}+		if (l < 0) {+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "A name buffer is too small");+			file_free(file);+			*filepp = NULL;+			return (ARCHIVE_FATAL);+		}++		np = file_find_child(dent, name);+		if (np == NULL || fn[0] == '\0')+			break;++		/* Find next subdirectory. */+		if (!np->dir) {+			/* NOT Directory! */+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_MISC,+			    "`%s' is not directory, we cannot insert `%s' ",+			    archive_entry_pathname(np->entry),+			    archive_entry_pathname(file->entry));+			file_free(file);+			*filepp = NULL;+			return (ARCHIVE_FAILED);+		}+		fn += l;+		if (fn[0] == '/')+			fn++;+		dent = np;+	}+	if (np == NULL) {+		/*+		 * Create virtual parent directories.+		 */+		while (fn[0] != '\0') {+			struct file *vp;+			struct archive_string as;++			archive_string_init(&as);+			archive_strncat(&as, p, fn - p + l);+			if (as.s[as.length-1] == '/') {+				as.s[as.length-1] = '\0';+				as.length--;+			}+			vp = file_create_virtual_dir(a, xar, as.s);+			if (vp == NULL) {+				archive_string_free(&as);+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory");+				file_free(file);+				*filepp = NULL;+				return (ARCHIVE_FATAL);+			}+			archive_string_free(&as);+			if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED)+				return (ARCHIVE_FATAL);+			file_add_child_tail(dent, vp);+			file_register(xar, vp);+			np = vp;++			fn += l;+			if (fn[0] == '/')+				fn++;+			l = get_path_component(name, sizeof(name), fn);+			if (l < 0) {+				archive_string_free(&as);+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "A name buffer is too small");+				file_free(file);+				*filepp = NULL;+				return (ARCHIVE_FATAL);+			}+			dent = np;+		}++		/* Found out the parent directory where isoent can be+		 * inserted. */+		xar->cur_dirent = dent;+		archive_string_empty(&(xar->cur_dirstr));+		archive_string_ensure(&(xar->cur_dirstr),+		    archive_strlen(&(dent->parentdir)) ++		    archive_strlen(&(dent->basename)) + 2);+		if (archive_strlen(&(dent->parentdir)) ++		    archive_strlen(&(dent->basename)) == 0)+			xar->cur_dirstr.s[0] = 0;+		else {+			if (archive_strlen(&(dent->parentdir)) > 0) {+				archive_string_copy(&(xar->cur_dirstr),+				    &(dent->parentdir));+				archive_strappend_char(&(xar->cur_dirstr), '/');+			}+			archive_string_concat(&(xar->cur_dirstr),+			    &(dent->basename));+		}++		if (!file_add_child_tail(dent, file)) {+			np = (struct file *)__archive_rb_tree_find_node(+			    &(dent->rbtree), file->basename.s);+			goto same_entry;+		}+		return (ARCHIVE_OK);+	}++same_entry:+	/*+	 * We have already has the entry the filename of which is+	 * the same.+	 */+	if (archive_entry_filetype(np->entry) !=+	    archive_entry_filetype(file->entry)) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Found duplicate entries `%s' and its file type is "+		    "different",+		    archive_entry_pathname(np->entry));+		file_free(file);+		*filepp = NULL;+		return (ARCHIVE_FAILED);+	}++	/* Swap files. */+	ent = np->entry;+	np->entry = file->entry;+	file->entry = ent;+	np->virtual = 0;++	file_free(file);+	*filepp = np;+	return (ARCHIVE_OK);+}++static void+file_register(struct xar *xar, struct file *file)+{+	file->id = xar->file_idx++;+        file->next = NULL;+        *xar->file_list.last = file;+        xar->file_list.last = &(file->next);+}++static void+file_init_register(struct xar *xar)+{+	xar->file_list.first = NULL;+	xar->file_list.last = &(xar->file_list.first);+}++static void+file_free_register(struct xar *xar)+{+	struct file *file, *file_next;++	file = xar->file_list.first;+	while (file != NULL) {+		file_next = file->next;+		file_free(file);+		file = file_next;+	}+}++/*+ * Register entry to get a hardlink target.+ */+static int+file_register_hardlink(struct archive_write *a, struct file *file)+{+	struct xar *xar = (struct xar *)a->format_data;+	struct hardlink *hl;+	const char *pathname;++	archive_entry_set_nlink(file->entry, 1);+	pathname = archive_entry_hardlink(file->entry);+	if (pathname == NULL) {+		/* This `file` is a hardlink target. */+		hl = malloc(sizeof(*hl));+		if (hl == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory");+			return (ARCHIVE_FATAL);+		}+		hl->nlink = 1;+		/* A hardlink target must be the first position. */+		file->hlnext = NULL;+		hl->file_list.first = file;+		hl->file_list.last = &(file->hlnext);+		__archive_rb_tree_insert_node(&(xar->hardlink_rbtree),+		    (struct archive_rb_node *)hl);+	} else {+		hl = (struct hardlink *)__archive_rb_tree_find_node(+		    &(xar->hardlink_rbtree), pathname);+		if (hl != NULL) {+			/* Insert `file` entry into the tail. */+			file->hlnext = NULL;+			*hl->file_list.last = file;+			hl->file_list.last = &(file->hlnext);+			hl->nlink++;+		}+		archive_entry_unset_size(file->entry);+	}++	return (ARCHIVE_OK);+}++/*+ * Hardlinked files have to have the same location of extent.+ * We have to find out hardlink target entries for entries which+ * have a hardlink target name.+ */+static void+file_connect_hardlink_files(struct xar *xar)+{+	struct archive_rb_node *n;+	struct hardlink *hl;+	struct file *target, *nf;++	ARCHIVE_RB_TREE_FOREACH(n, &(xar->hardlink_rbtree)) {+		hl = (struct hardlink *)n;++		/* The first entry must be a hardlink target. */+		target = hl->file_list.first;+		archive_entry_set_nlink(target->entry, hl->nlink);+		if (hl->nlink > 1)+			/* It means this file is a hardlink+			 * target itself. */+			target->hardlink_target = target;+		for (nf = target->hlnext;+		    nf != NULL; nf = nf->hlnext) {+			nf->hardlink_target = target;+			archive_entry_set_nlink(nf->entry, hl->nlink);+		}+	}+}++static int+file_hd_cmp_node(const struct archive_rb_node *n1,+    const struct archive_rb_node *n2)+{+	const struct hardlink *h1 = (const struct hardlink *)n1;+	const struct hardlink *h2 = (const struct hardlink *)n2;++	return (strcmp(archive_entry_pathname(h1->file_list.first->entry),+		       archive_entry_pathname(h2->file_list.first->entry)));+}++static int+file_hd_cmp_key(const struct archive_rb_node *n, const void *key)+{+	const struct hardlink *h = (const struct hardlink *)n;++	return (strcmp(archive_entry_pathname(h->file_list.first->entry),+		       (const char *)key));+}+++static void+file_init_hardlinks(struct xar *xar)+{+	static const struct archive_rb_tree_ops rb_ops = {+		file_hd_cmp_node, file_hd_cmp_key,+	};++	__archive_rb_tree_init(&(xar->hardlink_rbtree), &rb_ops);+}++static void+file_free_hardlinks(struct xar *xar)+{+	struct archive_rb_node *n, *tmp;++	ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(xar->hardlink_rbtree), tmp) {+		__archive_rb_tree_remove_node(&(xar->hardlink_rbtree), n);+		free(n);+	}+}++static void+checksum_init(struct chksumwork *sumwrk, enum sumalg sum_alg)+{+	sumwrk->alg = sum_alg;+	switch (sum_alg) {+	case CKSUM_NONE:+		break;+	case CKSUM_SHA1:+		archive_sha1_init(&(sumwrk->sha1ctx));+		break;+	case CKSUM_MD5:+		archive_md5_init(&(sumwrk->md5ctx));+		break;+	}+}++static void+checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)+{++	switch (sumwrk->alg) {+	case CKSUM_NONE:+		break;+	case CKSUM_SHA1:+		archive_sha1_update(&(sumwrk->sha1ctx), buff, size);+		break;+	case CKSUM_MD5:+		archive_md5_update(&(sumwrk->md5ctx), buff, size);+		break;+	}+}++static void+checksum_final(struct chksumwork *sumwrk, struct chksumval *sumval)+{++	switch (sumwrk->alg) {+	case CKSUM_NONE:+		sumval->len = 0;+		break;+	case CKSUM_SHA1:+		archive_sha1_final(&(sumwrk->sha1ctx), sumval->val);+		sumval->len = SHA1_SIZE;+		break;+	case CKSUM_MD5:+		archive_md5_final(&(sumwrk->md5ctx), sumval->val);+		sumval->len = MD5_SIZE;+		break;+	}+	sumval->alg = sumwrk->alg;+}++#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H)+static int+compression_unsupported_encoder(struct archive *a,+    struct la_zstream *lastrm, const char *name)+{++	archive_set_error(a, ARCHIVE_ERRNO_MISC,+	    "%s compression not supported on this platform", name);+	lastrm->valid = 0;+	lastrm->real_stream = NULL;+	return (ARCHIVE_FAILED);+}+#endif++static int+compression_init_encoder_gzip(struct archive *a,+    struct la_zstream *lastrm, int level, int withheader)+{+	z_stream *strm;++	if (lastrm->valid)+		compression_end(a, lastrm);+	strm = calloc(1, sizeof(*strm));+	if (strm == NULL) {+		archive_set_error(a, ENOMEM,+		    "Can't allocate memory for gzip stream");+		return (ARCHIVE_FATAL);+	}+	/* zlib.h is not const-correct, so we need this one bit+	 * of ugly hackery to convert a const * pointer to+	 * a non-const pointer. */+	strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;+	strm->avail_in = lastrm->avail_in;+	strm->total_in = (uLong)lastrm->total_in;+	strm->next_out = lastrm->next_out;+	strm->avail_out = lastrm->avail_out;+	strm->total_out = (uLong)lastrm->total_out;+	if (deflateInit2(strm, level, Z_DEFLATED,+	    (withheader)?15:-15,+	    8, Z_DEFAULT_STRATEGY) != Z_OK) {+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library");+		return (ARCHIVE_FATAL);+	}+	lastrm->real_stream = strm;+	lastrm->valid = 1;+	lastrm->code = compression_code_gzip;+	lastrm->end = compression_end_gzip;+	return (ARCHIVE_OK);+}++static int+compression_code_gzip(struct archive *a,+    struct la_zstream *lastrm, enum la_zaction action)+{+	z_stream *strm;+	int r;++	strm = (z_stream *)lastrm->real_stream;+	/* zlib.h is not const-correct, so we need this one bit+	 * of ugly hackery to convert a const * pointer to+	 * a non-const pointer. */+	strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;+	strm->avail_in = lastrm->avail_in;+	strm->total_in = (uLong)lastrm->total_in;+	strm->next_out = lastrm->next_out;+	strm->avail_out = lastrm->avail_out;+	strm->total_out = (uLong)lastrm->total_out;+	r = deflate(strm,+	    (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH);+	lastrm->next_in = strm->next_in;+	lastrm->avail_in = strm->avail_in;+	lastrm->total_in = strm->total_in;+	lastrm->next_out = strm->next_out;+	lastrm->avail_out = strm->avail_out;+	lastrm->total_out = strm->total_out;+	switch (r) {+	case Z_OK:+		return (ARCHIVE_OK);+	case Z_STREAM_END:+		return (ARCHIVE_EOF);+	default:+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "GZip compression failed:"+		    " deflate() call returned status %d", r);+		return (ARCHIVE_FATAL);+	}+}++static int+compression_end_gzip(struct archive *a, struct la_zstream *lastrm)+{+	z_stream *strm;+	int r;++	strm = (z_stream *)lastrm->real_stream;+	r = deflateEnd(strm);+	free(strm);+	lastrm->real_stream = NULL;+	lastrm->valid = 0;+	if (r != Z_OK) {+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Failed to clean up compressor");+		return (ARCHIVE_FATAL);+	}+	return (ARCHIVE_OK);+}++#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)+static int+compression_init_encoder_bzip2(struct archive *a,+    struct la_zstream *lastrm, int level)+{+	bz_stream *strm;++	if (lastrm->valid)+		compression_end(a, lastrm);+	strm = calloc(1, sizeof(*strm));+	if (strm == NULL) {+		archive_set_error(a, ENOMEM,+		    "Can't allocate memory for bzip2 stream");+		return (ARCHIVE_FATAL);+	}+	/* bzlib.h is not const-correct, so we need this one bit+	 * of ugly hackery to convert a const * pointer to+	 * a non-const pointer. */+	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;+	strm->avail_in = lastrm->avail_in;+	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);+	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);+	strm->next_out = (char *)lastrm->next_out;+	strm->avail_out = lastrm->avail_out;+	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);+	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);+	if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library");+		return (ARCHIVE_FATAL);+	}+	lastrm->real_stream = strm;+	lastrm->valid = 1;+	lastrm->code = compression_code_bzip2;+	lastrm->end = compression_end_bzip2;+	return (ARCHIVE_OK);+}++static int+compression_code_bzip2(struct archive *a,+    struct la_zstream *lastrm, enum la_zaction action)+{+	bz_stream *strm;+	int r;++	strm = (bz_stream *)lastrm->real_stream;+	/* bzlib.h is not const-correct, so we need this one bit+	 * of ugly hackery to convert a const * pointer to+	 * a non-const pointer. */+	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;+	strm->avail_in = lastrm->avail_in;+	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);+	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);+	strm->next_out = (char *)lastrm->next_out;+	strm->avail_out = lastrm->avail_out;+	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);+	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);+	r = BZ2_bzCompress(strm,+	    (action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN);+	lastrm->next_in = (const unsigned char *)strm->next_in;+	lastrm->avail_in = strm->avail_in;+	lastrm->total_in =+	    (((uint64_t)(uint32_t)strm->total_in_hi32) << 32)+	    + (uint64_t)(uint32_t)strm->total_in_lo32;+	lastrm->next_out = (unsigned char *)strm->next_out;+	lastrm->avail_out = strm->avail_out;+	lastrm->total_out =+	    (((uint64_t)(uint32_t)strm->total_out_hi32) << 32)+	    + (uint64_t)(uint32_t)strm->total_out_lo32;+	switch (r) {+	case BZ_RUN_OK:     /* Non-finishing */+	case BZ_FINISH_OK:  /* Finishing: There's more work to do */+		return (ARCHIVE_OK);+	case BZ_STREAM_END: /* Finishing: all done */+		/* Only occurs in finishing case */+		return (ARCHIVE_EOF);+	default:+		/* Any other return value indicates an error */+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Bzip2 compression failed:"+		    " BZ2_bzCompress() call returned status %d", r);+		return (ARCHIVE_FATAL);+	}+}++static int+compression_end_bzip2(struct archive *a, struct la_zstream *lastrm)+{+	bz_stream *strm;+	int r;++	strm = (bz_stream *)lastrm->real_stream;+	r = BZ2_bzCompressEnd(strm);+	free(strm);+	lastrm->real_stream = NULL;+	lastrm->valid = 0;+	if (r != BZ_OK) {+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Failed to clean up compressor");+		return (ARCHIVE_FATAL);+	}+	return (ARCHIVE_OK);+}++#else+static int+compression_init_encoder_bzip2(struct archive *a,+    struct la_zstream *lastrm, int level)+{++	(void) level; /* UNUSED */+	if (lastrm->valid)+		compression_end(a, lastrm);+	return (compression_unsupported_encoder(a, lastrm, "bzip2"));+}+#endif++#if defined(HAVE_LZMA_H)+static int+compression_init_encoder_lzma(struct archive *a,+    struct la_zstream *lastrm, int level)+{+	static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;+	lzma_stream *strm;+	lzma_options_lzma lzma_opt;+	int r;++	if (lastrm->valid)+		compression_end(a, lastrm);+	if (lzma_lzma_preset(&lzma_opt, level)) {+		lastrm->real_stream = NULL;+		archive_set_error(a, ENOMEM,+		    "Internal error initializing compression library");+		return (ARCHIVE_FATAL);+	}+	strm = calloc(1, sizeof(*strm));+	if (strm == NULL) {+		archive_set_error(a, ENOMEM,+		    "Can't allocate memory for lzma stream");+		return (ARCHIVE_FATAL);+	}+	*strm = lzma_init_data;+	r = lzma_alone_encoder(strm, &lzma_opt);+	switch (r) {+	case LZMA_OK:+		lastrm->real_stream = strm;+		lastrm->valid = 1;+		lastrm->code = compression_code_lzma;+		lastrm->end = compression_end_lzma;+		r = ARCHIVE_OK;+		break;+	case LZMA_MEM_ERROR:+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ENOMEM,+		    "Internal error initializing compression library: "+		    "Cannot allocate memory");+		r =  ARCHIVE_FATAL;+		break;+        default:+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library: "+		    "It's a bug in liblzma");+		r =  ARCHIVE_FATAL;+		break;+	}+	return (r);+}++static int+compression_init_encoder_xz(struct archive *a,+    struct la_zstream *lastrm, int level, int threads)+{+	static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;+	lzma_stream *strm;+	lzma_filter *lzmafilters;+	lzma_options_lzma lzma_opt;+	int r;+#ifdef HAVE_LZMA_STREAM_ENCODER_MT+	lzma_mt mt_options;+#endif++	(void)threads; /* UNUSED (if multi-threaded LZMA library not avail) */++	if (lastrm->valid)+		compression_end(a, lastrm);+	strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2);+	if (strm == NULL) {+		archive_set_error(a, ENOMEM,+		    "Can't allocate memory for xz stream");+		return (ARCHIVE_FATAL);+	}+	lzmafilters = (lzma_filter *)(strm+1);+	if (level > 6)+		level = 6;+	if (lzma_lzma_preset(&lzma_opt, level)) {+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ENOMEM,+		    "Internal error initializing compression library");+		return (ARCHIVE_FATAL);+	}+	lzmafilters[0].id = LZMA_FILTER_LZMA2;+	lzmafilters[0].options = &lzma_opt;+	lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */++	*strm = lzma_init_data;+#ifdef HAVE_LZMA_STREAM_ENCODER_MT+	if (threads > 1) {+		memset(&mt_options, 0, sizeof(mt_options));+		mt_options.threads = threads;+		mt_options.timeout = 300;+		mt_options.filters = lzmafilters;+		mt_options.check = LZMA_CHECK_CRC64;+		r = lzma_stream_encoder_mt(strm, &mt_options);+	} else+#endif+		r = lzma_stream_encoder(strm, lzmafilters, LZMA_CHECK_CRC64);+	switch (r) {+	case LZMA_OK:+		lastrm->real_stream = strm;+		lastrm->valid = 1;+		lastrm->code = compression_code_lzma;+		lastrm->end = compression_end_lzma;+		r = ARCHIVE_OK;+		break;+	case LZMA_MEM_ERROR:+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ENOMEM,+		    "Internal error initializing compression library: "+		    "Cannot allocate memory");+		r =  ARCHIVE_FATAL;+		break;+        default:+		free(strm);+		lastrm->real_stream = NULL;+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "Internal error initializing compression library: "+		    "It's a bug in liblzma");+		r =  ARCHIVE_FATAL;+		break;+	}+	return (r);+}++static int+compression_code_lzma(struct archive *a,+    struct la_zstream *lastrm, enum la_zaction action)+{+	lzma_stream *strm;+	int r;++	strm = (lzma_stream *)lastrm->real_stream;+	strm->next_in = lastrm->next_in;+	strm->avail_in = lastrm->avail_in;+	strm->total_in = lastrm->total_in;+	strm->next_out = lastrm->next_out;+	strm->avail_out = lastrm->avail_out;+	strm->total_out = lastrm->total_out;+	r = lzma_code(strm,+	    (action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN);+	lastrm->next_in = strm->next_in;+	lastrm->avail_in = strm->avail_in;+	lastrm->total_in = strm->total_in;+	lastrm->next_out = strm->next_out;+	lastrm->avail_out = strm->avail_out;+	lastrm->total_out = strm->total_out;+	switch (r) {+	case LZMA_OK:+		/* Non-finishing case */+		return (ARCHIVE_OK);+	case LZMA_STREAM_END:+		/* This return can only occur in finishing case. */+		return (ARCHIVE_EOF);+	case LZMA_MEMLIMIT_ERROR:+		archive_set_error(a, ENOMEM,+		    "lzma compression error:"+		    " %ju MiB would have been needed",+		    (uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1)+			/ (1024 * 1024)));+		return (ARCHIVE_FATAL);+	default:+		/* Any other return value indicates an error */+		archive_set_error(a, ARCHIVE_ERRNO_MISC,+		    "lzma compression failed:"+		    " lzma_code() call returned status %d", r);+		return (ARCHIVE_FATAL);+	}+}++static int+compression_end_lzma(struct archive *a, struct la_zstream *lastrm)+{+	lzma_stream *strm;++	(void)a; /* UNUSED */+	strm = (lzma_stream *)lastrm->real_stream;+	lzma_end(strm);+	free(strm);+	lastrm->valid = 0;+	lastrm->real_stream = NULL;+	return (ARCHIVE_OK);+}+#else+static int+compression_init_encoder_lzma(struct archive *a,+    struct la_zstream *lastrm, int level)+{++	(void) level; /* UNUSED */+	if (lastrm->valid)+		compression_end(a, lastrm);+	return (compression_unsupported_encoder(a, lastrm, "lzma"));+}+static int+compression_init_encoder_xz(struct archive *a,+    struct la_zstream *lastrm, int level, int threads)+{++	(void) level; /* UNUSED */+	(void) threads; /* UNUSED */+	if (lastrm->valid)+		compression_end(a, lastrm);+	return (compression_unsupported_encoder(a, lastrm, "xz"));+}+#endif++static int+xar_compression_init_encoder(struct archive_write *a)+{+	struct xar *xar;+	int r;++	xar = (struct xar *)a->format_data;+	switch (xar->opt_compression) {+	case GZIP:+		r = compression_init_encoder_gzip(+		    &(a->archive), &(xar->stream),+		    xar->opt_compression_level, 1);+		break;+	case BZIP2:+		r = compression_init_encoder_bzip2(+		    &(a->archive), &(xar->stream),+		    xar->opt_compression_level);+		break;+	case LZMA:+		r = compression_init_encoder_lzma(+		    &(a->archive), &(xar->stream),+		    xar->opt_compression_level);+		break;+	case XZ:+		r = compression_init_encoder_xz(+		    &(a->archive), &(xar->stream),+		    xar->opt_compression_level, xar->opt_threads);+		break;+	default:+		r = ARCHIVE_OK;+		break;+	}+	if (r == ARCHIVE_OK) {+		xar->stream.total_in = 0;+		xar->stream.next_out = xar->wbuff;+		xar->stream.avail_out = sizeof(xar->wbuff);+		xar->stream.total_out = 0;+	}++	return (r);+}++static int+compression_code(struct archive *a, struct la_zstream *lastrm,+    enum la_zaction action)+{+	if (lastrm->valid)+		return (lastrm->code(a, lastrm, action));+	return (ARCHIVE_OK);+}++static int+compression_end(struct archive *a, struct la_zstream *lastrm)+{+	if (lastrm->valid)+		return (lastrm->end(a, lastrm));+	return (ARCHIVE_OK);+}+++static int+save_xattrs(struct archive_write *a, struct file *file)+{+	struct xar *xar;+	const char *name;+	const void *value;+	struct heap_data *heap;+	size_t size;+	int count, r;++	xar = (struct xar *)a->format_data;+	count = archive_entry_xattr_reset(file->entry);+	if (count == 0)+		return (ARCHIVE_OK);+	while (count--) {+		archive_entry_xattr_next(file->entry,+		    &name, &value, &size);+		checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);+		checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);++		heap = calloc(1, sizeof(*heap));+		if (heap == NULL) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't allocate memory for xattr");+			return (ARCHIVE_FATAL);+		}+		heap->id = file->ea_idx++;+		heap->temp_offset = xar->temp_offset;+		heap->size = size;/* save a extracted size */+		heap->compression = xar->opt_compression;+		/* Get a extracted sumcheck value. */+		checksum_update(&(xar->e_sumwrk), value, size);+		checksum_final(&(xar->e_sumwrk), &(heap->e_sum));++		/*+		 * Not compression to xattr is simple way.+		 */+		if (heap->compression == NONE) {+			checksum_update(&(xar->a_sumwrk), value, size);+			checksum_final(&(xar->a_sumwrk), &(heap->a_sum));+			if (write_to_temp(a, value, size)+			    != ARCHIVE_OK) {+				free(heap);+				return (ARCHIVE_FATAL);+			}+			heap->length = size;+			/* Add heap to the tail of file->xattr. */+			heap->next = NULL;+			*file->xattr.last = heap;+			file->xattr.last = &(heap->next);+			/* Next xattr */+			continue;+		}++		/*+		 * Init compression library.+		 */+		r = xar_compression_init_encoder(a);+		if (r != ARCHIVE_OK) {+			free(heap);+			return (ARCHIVE_FATAL);+		}++		xar->stream.next_in = (const unsigned char *)value;+		xar->stream.avail_in = size;+		for (;;) {+			r = compression_code(&(a->archive),+			    &(xar->stream), ARCHIVE_Z_FINISH);+			if (r != ARCHIVE_OK && r != ARCHIVE_EOF) {+				free(heap);+				return (ARCHIVE_FATAL);+			}+			size = sizeof(xar->wbuff) - xar->stream.avail_out;+			checksum_update(&(xar->a_sumwrk),+			    xar->wbuff, size);+			if (write_to_temp(a, xar->wbuff, size)+			    != ARCHIVE_OK) {+				free(heap);+				return (ARCHIVE_FATAL);+			}+			if (r == ARCHIVE_OK) {+				xar->stream.next_out = xar->wbuff;+				xar->stream.avail_out = sizeof(xar->wbuff);+			} else {+				checksum_final(&(xar->a_sumwrk),+				    &(heap->a_sum));+				heap->length = xar->stream.total_out;+				/* Add heap to the tail of file->xattr. */+				heap->next = NULL;+				*file->xattr.last = heap;+				file->xattr.last = &(heap->next);+				break;+			}+		}+		/* Clean up compression library. */+		r = compression_end(&(a->archive), &(xar->stream));+		if (r != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}+	return (ARCHIVE_OK);+}++static int+getalgsize(enum sumalg sumalg)+{+	switch (sumalg) {+	default:+	case CKSUM_NONE:+		return (0);+	case CKSUM_SHA1:+		return (SHA1_SIZE);+	case CKSUM_MD5:+		return (MD5_SIZE);+	}+}++static const char *+getalgname(enum sumalg sumalg)+{+	switch (sumalg) {+	default:+	case CKSUM_NONE:+		return (NULL);+	case CKSUM_SHA1:+		return (SHA1_NAME);+	case CKSUM_MD5:+		return (MD5_NAME);+	}+}++#endif /* Support xar format */
+ c/archive_write_set_format_zip.c view
@@ -0,0 +1,1695 @@+/*-+ * Copyright (c) 2008 Anselm Strauss+ * Copyright (c) 2009 Joerg Sonnenberger+ * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++/*+ * Development supported by Google Summer of Code 2008.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_zip.c 201168 2009-12-29 06:15:32Z kientzle $");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#ifdef HAVE_LANGINFO_H+#include <langinfo.h>+#endif+#ifdef HAVE_STDLIB_H+#include <stdlib.h>+#endif+#ifdef HAVE_STRING_H+#include <string.h>+#endif+#ifdef HAVE_ZLIB_H+#include <zlib.h>+#endif++#include "archive.h"+#include "archive_cryptor_private.h"+#include "archive_endian.h"+#include "archive_entry.h"+#include "archive_entry_locale.h"+#include "archive_hmac_private.h"+#include "archive_private.h"+#include "archive_random_private.h"+#include "archive_write_private.h"+#include "archive_write_set_format_private.h"++#ifndef HAVE_ZLIB_H+#include "archive_crc32.h"+#endif++#define ZIP_ENTRY_FLAG_ENCRYPTED	(1<<0)+#define ZIP_ENTRY_FLAG_LENGTH_AT_END	(1<<3)+#define ZIP_ENTRY_FLAG_UTF8_NAME	(1 << 11)++#define ZIP_4GB_MAX ARCHIVE_LITERAL_LL(0xffffffff)+#define ZIP_4GB_MAX_UNCOMPRESSED ARCHIVE_LITERAL_LL(0xff000000)++enum compression {+	COMPRESSION_UNSPECIFIED = -1,+	COMPRESSION_STORE = 0,+	COMPRESSION_DEFLATE = 8+};++#ifdef HAVE_ZLIB_H+#define COMPRESSION_DEFAULT	COMPRESSION_DEFLATE+#else+#define COMPRESSION_DEFAULT	COMPRESSION_STORE+#endif++enum encryption {+	ENCRYPTION_NONE	= 0,+	ENCRYPTION_TRADITIONAL, /* Traditional PKWARE encryption. */+	ENCRYPTION_WINZIP_AES128, /* WinZIP AES-128 encryption. */+	ENCRYPTION_WINZIP_AES256, /* WinZIP AES-256 encryption. */+};++#define TRAD_HEADER_SIZE	12+/*+ * See "WinZip - AES Encryption Information"+ *     http://www.winzip.com/aes_info.htm+ */+/* Value used in compression method. */+#define WINZIP_AES_ENCRYPTION	99+/* A WinZip AES header size which is stored at the beginning of+ * file contents. */+#define WINZIP_AES128_HEADER_SIZE	(8 + 2)+#define WINZIP_AES256_HEADER_SIZE	(16 + 2)+/* AES vendor version. */+#define AES_VENDOR_AE_1 0x0001+#define AES_VENDOR_AE_2 0x0002+/* Authentication code size. */+#define AUTH_CODE_SIZE		10+/**/+#define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2)++struct cd_segment {+	struct cd_segment *next;+	size_t buff_size;+	unsigned char *buff;+	unsigned char *p;+};++struct trad_enc_ctx {+	uint32_t keys[3];+};++struct zip {++	int64_t entry_offset;+	int64_t entry_compressed_size;+	int64_t entry_uncompressed_size;+	int64_t entry_compressed_written;+	int64_t entry_uncompressed_written;+	int64_t entry_uncompressed_limit;+	struct archive_entry *entry;+	uint32_t entry_crc32;+	enum compression entry_compression;+	enum encryption  entry_encryption;+	int entry_flags;+	int entry_uses_zip64;+	int experiments;+	struct trad_enc_ctx tctx;+	char tctx_valid;+	unsigned char trad_chkdat;+	unsigned aes_vendor;+	archive_crypto_ctx cctx;+	char cctx_valid;+	archive_hmac_sha1_ctx hctx;+	char hctx_valid;++	unsigned char *file_header;+	size_t file_header_extra_offset;+	unsigned long (*crc32func)(unsigned long crc, const void *buff, size_t len);++	struct cd_segment *central_directory;+	struct cd_segment *central_directory_last;+	size_t central_directory_bytes;+	size_t central_directory_entries;++	int64_t written_bytes; /* Overall position in file. */++	struct archive_string_conv *opt_sconv;+	struct archive_string_conv *sconv_default;+	enum compression requested_compression;+	int deflate_compression_level;+	int init_default_conversion;+	enum encryption  encryption_type;++#define ZIP_FLAG_AVOID_ZIP64 1+#define ZIP_FLAG_FORCE_ZIP64 2+#define ZIP_FLAG_EXPERIMENT_xl 4+	int flags;++#ifdef HAVE_ZLIB_H+	z_stream stream;+#endif+	size_t len_buf;+	unsigned char *buf;+};++/* Don't call this min or MIN, since those are already defined+   on lots of platforms (but not all). */+#define zipmin(a, b) ((a) > (b) ? (b) : (a))++static ssize_t archive_write_zip_data(struct archive_write *,+		   const void *buff, size_t s);+static int archive_write_zip_close(struct archive_write *);+static int archive_write_zip_free(struct archive_write *);+static int archive_write_zip_finish_entry(struct archive_write *);+static int archive_write_zip_header(struct archive_write *,+	      struct archive_entry *);+static int archive_write_zip_options(struct archive_write *,+	      const char *, const char *);+static unsigned int dos_time(const time_t);+static size_t path_length(struct archive_entry *);+static int write_path(struct archive_entry *, struct archive_write *);+static void copy_path(struct archive_entry *, unsigned char *);+static struct archive_string_conv *get_sconv(struct archive_write *, struct zip *);+static int trad_enc_init(struct trad_enc_ctx *, const char *, size_t);+static unsigned trad_enc_encrypt_update(struct trad_enc_ctx *, const uint8_t *,+    size_t, uint8_t *, size_t);+static int init_traditional_pkware_encryption(struct archive_write *);+static int is_traditional_pkware_encryption_supported(void);+static int init_winzip_aes_encryption(struct archive_write *);+static int is_winzip_aes_encryption_supported(int encryption);++static unsigned char *+cd_alloc(struct zip *zip, size_t length)+{+	unsigned char *p;++	if (zip->central_directory == NULL+	    || (zip->central_directory_last->p + length+		> zip->central_directory_last->buff + zip->central_directory_last->buff_size)) {+		struct cd_segment *segment = calloc(1, sizeof(*segment));+		if (segment == NULL)+			return NULL;+		segment->buff_size = 64 * 1024;+		segment->buff = malloc(segment->buff_size);+		if (segment->buff == NULL) {+			free(segment);+			return NULL;+		}+		segment->p = segment->buff;++		if (zip->central_directory == NULL) {+			zip->central_directory+			    = zip->central_directory_last+			    = segment;+		} else {+			zip->central_directory_last->next = segment;+			zip->central_directory_last = segment;+		}+	}++	p = zip->central_directory_last->p;+	zip->central_directory_last->p += length;+	zip->central_directory_bytes += length;+	return (p);+}++static unsigned long+real_crc32(unsigned long crc, const void *buff, size_t len)+{+	return crc32(crc, buff, (unsigned int)len);+}++static unsigned long+fake_crc32(unsigned long crc, const void *buff, size_t len)+{+	(void)crc; /* UNUSED */+	(void)buff; /* UNUSED */+	(void)len; /* UNUSED */+	return 0;+}++static int+archive_write_zip_options(struct archive_write *a, const char *key,+    const char *val)+{+	struct zip *zip = a->format_data;+	int ret = ARCHIVE_FAILED;++	if (strcmp(key, "compression") == 0) {+		/*+		 * Set compression to use on all future entries.+		 * This only affects regular files.+		 */+		if (val == NULL || val[0] == 0) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "%s: compression option needs a compression name",+			    a->format_name);+		} else if (strcmp(val, "deflate") == 0) {+#ifdef HAVE_ZLIB_H+			zip->requested_compression = COMPRESSION_DEFLATE;+			ret = ARCHIVE_OK;+#else+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "deflate compression not supported");+#endif+		} else if (strcmp(val, "store") == 0) {+			zip->requested_compression = COMPRESSION_STORE;+			ret = ARCHIVE_OK;+		}+		return (ret);+	} else if (strcmp(key, "compression-level") == 0) {+		if (val == NULL || !(val[0] >= '0' && val[0] <= '9') || val[1] != '\0') {+			return ARCHIVE_WARN;+		}++		if (val[0] == '0') {+			zip->requested_compression = COMPRESSION_STORE;+			return ARCHIVE_OK;+		} else {+#ifdef HAVE_ZLIB_H+			zip->requested_compression = COMPRESSION_DEFLATE;+			zip->deflate_compression_level = val[0] - '0';+			return ARCHIVE_OK;+#else+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "deflate compression not supported");+#endif+		}+	} else if (strcmp(key, "encryption") == 0) {+		if (val == NULL) {+			zip->encryption_type = ENCRYPTION_NONE;+			ret = ARCHIVE_OK;+		} else if (val[0] == '1' || strcmp(val, "traditional") == 0+		    || strcmp(val, "zipcrypt") == 0+		    || strcmp(val, "ZipCrypt") == 0) {+			if (is_traditional_pkware_encryption_supported()) {+				zip->encryption_type = ENCRYPTION_TRADITIONAL;+				ret = ARCHIVE_OK;+			} else {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "encryption not supported");+			}+		} else if (strcmp(val, "aes128") == 0) {+			if (is_winzip_aes_encryption_supported(+			    ENCRYPTION_WINZIP_AES128)) {+				zip->encryption_type = ENCRYPTION_WINZIP_AES128;+				ret = ARCHIVE_OK;+			} else {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "encryption not supported");+			}+		} else if (strcmp(val, "aes256") == 0) {+			if (is_winzip_aes_encryption_supported(+			    ENCRYPTION_WINZIP_AES256)) {+				zip->encryption_type = ENCRYPTION_WINZIP_AES256;+				ret = ARCHIVE_OK;+			} else {+				archive_set_error(&a->archive,+				    ARCHIVE_ERRNO_MISC,+				    "encryption not supported");+			}+		} else {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "%s: unknown encryption '%s'",+			    a->format_name, val);+		}+		return (ret);+	} else if (strcmp(key, "experimental") == 0) {+		if (val == NULL || val[0] == 0) {+			zip->flags &= ~ ZIP_FLAG_EXPERIMENT_xl;+		} else {+			zip->flags |= ZIP_FLAG_EXPERIMENT_xl;+		}+		return (ARCHIVE_OK);+	} else if (strcmp(key, "fakecrc32") == 0) {+		/*+		 * FOR TESTING ONLY:  disable CRC calculation to speed up+		 * certain complex tests.+		 */+		if (val == NULL || val[0] == 0) {+			zip->crc32func = real_crc32;+		} else {+			zip->crc32func = fake_crc32;+		}+		return (ARCHIVE_OK);+	} else if (strcmp(key, "hdrcharset")  == 0) {+		/*+		 * Set the character set used in translating filenames.+		 */+		if (val == NULL || val[0] == 0) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "%s: hdrcharset option needs a character-set name",+			    a->format_name);+		} else {+			zip->opt_sconv = archive_string_conversion_to_charset(+			    &a->archive, val, 0);+			if (zip->opt_sconv != NULL)+				ret = ARCHIVE_OK;+			else+				ret = ARCHIVE_FATAL;+		}+		return (ret);+	} else if (strcmp(key, "zip64") == 0) {+		/*+		 * Bias decisions about Zip64: force them to be+		 * generated in certain cases where they are not+		 * forbidden or avoid them in certain cases where they+		 * are not strictly required.+		 */+		if (val != NULL && *val != '\0') {+			zip->flags |= ZIP_FLAG_FORCE_ZIP64;+			zip->flags &= ~ZIP_FLAG_AVOID_ZIP64;+		} else {+			zip->flags &= ~ZIP_FLAG_FORCE_ZIP64;+			zip->flags |= ZIP_FLAG_AVOID_ZIP64;+		}+		return (ARCHIVE_OK);+	}++	/* Note: The "warn" return is just to inform the options+	 * supervisor that we didn't handle it.  It will generate+	 * a suitable error if no one used this option. */+	return (ARCHIVE_WARN);+}++int+archive_write_zip_set_compression_deflate(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	int ret = ARCHIVE_FAILED;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+		ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+		"archive_write_zip_set_compression_deflate");+	if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		"Can only use archive_write_zip_set_compression_deflate"+		" with zip format");+		ret = ARCHIVE_FATAL;+	} else {+#ifdef HAVE_ZLIB_H+		struct zip *zip = a->format_data;+		zip->requested_compression = COMPRESSION_DEFLATE;+		ret = ARCHIVE_OK;+#else+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			"deflate compression not supported");+		ret = ARCHIVE_FAILED;+#endif+	}+	return (ret);+}++int+archive_write_zip_set_compression_store(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct zip *zip = a->format_data;+	int ret = ARCHIVE_FAILED;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+		ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,+		"archive_write_zip_set_compression_deflate");+	if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			"Can only use archive_write_zip_set_compression_store"+			" with zip format");+		ret = ARCHIVE_FATAL;+	} else {+		zip->requested_compression = COMPRESSION_STORE;+		ret = ARCHIVE_OK;+	}+	return (ret);+}++int+archive_write_set_format_zip(struct archive *_a)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct zip *zip;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,+	    ARCHIVE_STATE_NEW, "archive_write_set_format_zip");++	/* If another format was already registered, unregister it. */+	if (a->format_free != NULL)+		(a->format_free)(a);++	zip = (struct zip *) calloc(1, sizeof(*zip));+	if (zip == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate zip data");+		return (ARCHIVE_FATAL);+	}++	/* "Unspecified" lets us choose the appropriate compression. */+	zip->requested_compression = COMPRESSION_UNSPECIFIED;+#ifdef HAVE_ZLIB_H+	zip->deflate_compression_level = Z_DEFAULT_COMPRESSION;+#endif+	zip->crc32func = real_crc32;++	/* A buffer used for both compression and encryption. */+	zip->len_buf = 65536;+	zip->buf = malloc(zip->len_buf);+	if (zip->buf == NULL) {+		free(zip);+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate compression buffer");+		return (ARCHIVE_FATAL);+	}++	a->format_data = zip;+	a->format_name = "zip";+	a->format_options = archive_write_zip_options;+	a->format_write_header = archive_write_zip_header;+	a->format_write_data = archive_write_zip_data;+	a->format_finish_entry = archive_write_zip_finish_entry;+	a->format_close = archive_write_zip_close;+	a->format_free = archive_write_zip_free;+	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;+	a->archive.archive_format_name = "ZIP";++	return (ARCHIVE_OK);+}++static int+is_all_ascii(const char *p)+{+	const unsigned char *pp = (const unsigned char *)p;++	while (*pp) {+		if (*pp++ > 127)+			return (0);+	}+	return (1);+}++static int+archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)+{+	unsigned char local_header[32];+	unsigned char local_extra[144];+	struct zip *zip = a->format_data;+	unsigned char *e;+	unsigned char *cd_extra;+	size_t filename_length;+	const char *slink = NULL;+	size_t slink_size = 0;+	struct archive_string_conv *sconv = get_sconv(a, zip);+	int ret, ret2 = ARCHIVE_OK;+	mode_t type;+	int version_needed = 10;++	/* Ignore types of entries that we don't support. */+	type = archive_entry_filetype(entry);+	if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) {+		__archive_write_entry_filetype_unsupported(+		    &a->archive, entry, "zip");+		return ARCHIVE_FAILED;+	};++	/* If we're not using Zip64, reject large files. */+	if (zip->flags & ZIP_FLAG_AVOID_ZIP64) {+		/* Reject entries over 4GB. */+		if (archive_entry_size_is_set(entry)+		    && (archive_entry_size(entry) > ZIP_4GB_MAX)) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Files > 4GB require Zip64 extensions");+			return ARCHIVE_FAILED;+		}+		/* Reject entries if archive is > 4GB. */+		if (zip->written_bytes > ZIP_4GB_MAX) {+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+			    "Archives > 4GB require Zip64 extensions");+			return ARCHIVE_FAILED;+		}+	}++	/* Only regular files can have size > 0. */+	if (type != AE_IFREG)+		archive_entry_set_size(entry, 0);+++	/* Reset information from last entry. */+	zip->entry_offset = zip->written_bytes;+	zip->entry_uncompressed_limit = INT64_MAX;+	zip->entry_compressed_size = 0;+	zip->entry_uncompressed_size = 0;+	zip->entry_compressed_written = 0;+	zip->entry_uncompressed_written = 0;+	zip->entry_flags = 0;+	zip->entry_uses_zip64 = 0;+	zip->entry_crc32 = zip->crc32func(0, NULL, 0);+	zip->entry_encryption = 0;+	archive_entry_free(zip->entry);+	zip->entry = NULL;++	if (zip->cctx_valid)+		archive_encrypto_aes_ctr_release(&zip->cctx);+	if (zip->hctx_valid)+		archive_hmac_sha1_cleanup(&zip->hctx);+	zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;++	if (type == AE_IFREG+		    &&(!archive_entry_size_is_set(entry)+			|| archive_entry_size(entry) > 0)) {+		switch (zip->encryption_type) {+		case ENCRYPTION_TRADITIONAL:+		case ENCRYPTION_WINZIP_AES128:+		case ENCRYPTION_WINZIP_AES256:+			zip->entry_flags |= ZIP_ENTRY_FLAG_ENCRYPTED;+			zip->entry_encryption = zip->encryption_type;+			break;+		default:+			break;+		}+	}+++#if defined(_WIN32) && !defined(__CYGWIN__)+	/* Make sure the path separators in pathname, hardlink and symlink+	 * are all slash '/', not the Windows path separator '\'. */+	zip->entry = __la_win_entry_in_posix_pathseparator(entry);+	if (zip->entry == entry)+		zip->entry = archive_entry_clone(entry);+#else+	zip->entry = archive_entry_clone(entry);+#endif+	if (zip->entry == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate zip header data");+		return (ARCHIVE_FATAL);+	}++	if (sconv != NULL) {+		const char *p;+		size_t len;++		if (archive_entry_pathname_l(entry, &p, &len, sconv) != 0) {+			if (errno == ENOMEM) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate memory for Pathname");+				return (ARCHIVE_FATAL);+			}+			archive_set_error(&a->archive,+			    ARCHIVE_ERRNO_FILE_FORMAT,+			    "Can't translate Pathname '%s' to %s",+			    archive_entry_pathname(entry),+			    archive_string_conversion_charset_name(sconv));+			ret2 = ARCHIVE_WARN;+		}+		if (len > 0)+			archive_entry_set_pathname(zip->entry, p);++		/*+		 * There is no standard for symlink handling; we convert+		 * it using the same character-set translation that we use+		 * for filename.+		 */+		if (type == AE_IFLNK) {+			if (archive_entry_symlink_l(entry, &p, &len, sconv)) {+				if (errno == ENOMEM) {+					archive_set_error(&a->archive, ENOMEM,+					    "Can't allocate memory "+					    " for Symlink");+					return (ARCHIVE_FATAL);+				}+				/* No error if we can't convert. */+			} else if (len > 0)+				archive_entry_set_symlink(zip->entry, p);+		}+	}++	/* If filename isn't ASCII and we can use UTF-8, set the UTF-8 flag. */+	if (!is_all_ascii(archive_entry_pathname(zip->entry))) {+		if (zip->opt_sconv != NULL) {+			if (strcmp(archive_string_conversion_charset_name(+					zip->opt_sconv), "UTF-8") == 0)+				zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME;+#if HAVE_NL_LANGINFO+		} else if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) {+			zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME;+#endif+		}+	}+	filename_length = path_length(zip->entry);++	/* Determine appropriate compression and size for this entry. */+	if (type == AE_IFLNK) {+		slink = archive_entry_symlink(zip->entry);+		if (slink != NULL)+			slink_size = strlen(slink);+		else+			slink_size = 0;+		zip->entry_uncompressed_limit = slink_size;+		zip->entry_compressed_size = slink_size;+		zip->entry_uncompressed_size = slink_size;+		zip->entry_crc32 = zip->crc32func(zip->entry_crc32,+		    (const unsigned char *)slink, slink_size);+		zip->entry_compression = COMPRESSION_STORE;+		version_needed = 20;+	} else if (type != AE_IFREG) {+		zip->entry_compression = COMPRESSION_STORE;+		zip->entry_uncompressed_limit = 0;+		version_needed = 20;+	} else if (archive_entry_size_is_set(zip->entry)) {+		int64_t size = archive_entry_size(zip->entry);+		int64_t additional_size = 0;++		zip->entry_uncompressed_limit = size;+		zip->entry_compression = zip->requested_compression;+		if (zip->entry_compression == COMPRESSION_UNSPECIFIED) {+			zip->entry_compression = COMPRESSION_DEFAULT;+		}+		if (zip->entry_compression == COMPRESSION_STORE) {+			zip->entry_compressed_size = size;+			zip->entry_uncompressed_size = size;+			version_needed = 10;+		} else {+			zip->entry_uncompressed_size = size;+			version_needed = 20;+		}++		if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {+			switch (zip->entry_encryption) {+			case ENCRYPTION_TRADITIONAL:+				additional_size = TRAD_HEADER_SIZE;+				version_needed = 20;+				break;+			case ENCRYPTION_WINZIP_AES128:+				additional_size = WINZIP_AES128_HEADER_SIZE+				    + AUTH_CODE_SIZE;+				version_needed = 20;+				break;+			case ENCRYPTION_WINZIP_AES256:+				additional_size = WINZIP_AES256_HEADER_SIZE+				    + AUTH_CODE_SIZE;+				version_needed = 20;+				break;+			default:+				break;+			}+			if (zip->entry_compression == COMPRESSION_STORE)+				zip->entry_compressed_size += additional_size;+		}++		/*+		 * Set Zip64 extension in any of the following cases+		 * (this was suggested by discussion on info-zip-dev+		 * mailing list):+		 *  = Zip64 is being forced by user+		 *  = File is over 4GiB uncompressed+		 *    (including encryption header, if any)+		 *  = File is close to 4GiB and is being compressed+		 *    (compression might make file larger)+		 */+		if ((zip->flags & ZIP_FLAG_FORCE_ZIP64)+		    || (zip->entry_uncompressed_size + additional_size > ZIP_4GB_MAX)+		    || (zip->entry_uncompressed_size > ZIP_4GB_MAX_UNCOMPRESSED+			&& zip->entry_compression != COMPRESSION_STORE)) {+			zip->entry_uses_zip64 = 1;+			version_needed = 45;+		}++		/* We may know the size, but never the CRC. */+		zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END;+	} else {+		/* We don't know the size.  In this case, we prefer+		 * deflate (it has a clear end-of-data marker which+		 * makes length-at-end more reliable) and will+		 * enable Zip64 extensions unless we're told not to.+		 */+		zip->entry_compression = COMPRESSION_DEFAULT;+		zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END;+		if ((zip->flags & ZIP_FLAG_AVOID_ZIP64) == 0) {+			zip->entry_uses_zip64 = 1;+			version_needed = 45;+		} else if (zip->entry_compression == COMPRESSION_STORE) {+			version_needed = 10;+		} else {+			version_needed = 20;+		}++		if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {+			switch (zip->entry_encryption) {+			case ENCRYPTION_TRADITIONAL:+			case ENCRYPTION_WINZIP_AES128:+			case ENCRYPTION_WINZIP_AES256:+				if (version_needed < 20)+					version_needed = 20;+				break;+			default:+				break;+			}+		}+	}++	/* Format the local header. */+	memset(local_header, 0, sizeof(local_header));+	memcpy(local_header, "PK\003\004", 4);+	archive_le16enc(local_header + 4, version_needed);+	archive_le16enc(local_header + 6, zip->entry_flags);+	if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128+	    || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)+		archive_le16enc(local_header + 8, WINZIP_AES_ENCRYPTION);+	else+		archive_le16enc(local_header + 8, zip->entry_compression);+	archive_le32enc(local_header + 10,+		dos_time(archive_entry_mtime(zip->entry)));+	archive_le32enc(local_header + 14, zip->entry_crc32);+	if (zip->entry_uses_zip64) {+		/* Zip64 data in the local header "must" include both+		 * compressed and uncompressed sizes AND those fields+		 * are included only if these are 0xffffffff;+		 * THEREFORE these must be set this way, even if we+		 * know one of them is smaller. */+		archive_le32enc(local_header + 18, ZIP_4GB_MAX);+		archive_le32enc(local_header + 22, ZIP_4GB_MAX);+	} else {+		archive_le32enc(local_header + 18, (uint32_t)zip->entry_compressed_size);+		archive_le32enc(local_header + 22, (uint32_t)zip->entry_uncompressed_size);+	}+	archive_le16enc(local_header + 26, (uint16_t)filename_length);++	if (zip->entry_encryption == ENCRYPTION_TRADITIONAL) {+		if (zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END)+			zip->trad_chkdat = local_header[11];+		else+			zip->trad_chkdat = local_header[17];+	}++	/* Format as much of central directory file header as we can: */+	zip->file_header = cd_alloc(zip, 46);+	/* If (zip->file_header == NULL) XXXX */+	++zip->central_directory_entries;+	memset(zip->file_header, 0, 46);+	memcpy(zip->file_header, "PK\001\002", 4);+	/* "Made by PKZip 2.0 on Unix." */+	archive_le16enc(zip->file_header + 4, 3 * 256 + version_needed);+	archive_le16enc(zip->file_header + 6, version_needed);+	archive_le16enc(zip->file_header + 8, zip->entry_flags);+	if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128+	    || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)+		archive_le16enc(zip->file_header + 10, WINZIP_AES_ENCRYPTION);+	else+		archive_le16enc(zip->file_header + 10, zip->entry_compression);+	archive_le32enc(zip->file_header + 12,+		dos_time(archive_entry_mtime(zip->entry)));+	archive_le16enc(zip->file_header + 28, (uint16_t)filename_length);+	/* Following Info-Zip, store mode in the "external attributes" field. */+	archive_le32enc(zip->file_header + 38,+	    ((uint32_t)archive_entry_mode(zip->entry)) << 16);+	e = cd_alloc(zip, filename_length);+	/* If (e == NULL) XXXX */+	copy_path(zip->entry, e);++	/* Format extra data. */+	memset(local_extra, 0, sizeof(local_extra));+	e = local_extra;++	/* First, extra blocks that are the same between+	 * the local file header and the central directory.+	 * We format them once and then duplicate them. */++	/* UT timestamp, length depends on what timestamps are set. */+	memcpy(e, "UT", 2);+	archive_le16enc(e + 2,+	    1+	    + (archive_entry_mtime_is_set(entry) ? 4 : 0)+	    + (archive_entry_atime_is_set(entry) ? 4 : 0)+	    + (archive_entry_ctime_is_set(entry) ? 4 : 0));+	e += 4;+	*e++ =+	    (archive_entry_mtime_is_set(entry) ? 1 : 0)+	    | (archive_entry_atime_is_set(entry) ? 2 : 0)+	    | (archive_entry_ctime_is_set(entry) ? 4 : 0);+	if (archive_entry_mtime_is_set(entry)) {+		archive_le32enc(e, (uint32_t)archive_entry_mtime(entry));+		e += 4;+	}+	if (archive_entry_atime_is_set(entry)) {+		archive_le32enc(e, (uint32_t)archive_entry_atime(entry));+		e += 4;+	}+	if (archive_entry_ctime_is_set(entry)) {+		archive_le32enc(e, (uint32_t)archive_entry_ctime(entry));+		e += 4;+	}++	/* ux Unix extra data, length 11, version 1 */+	/* TODO: If uid < 64k, use 2 bytes, ditto for gid. */+	memcpy(e, "ux\013\000\001", 5);+	e += 5;+	*e++ = 4; /* Length of following UID */+	archive_le32enc(e, (uint32_t)archive_entry_uid(entry));+	e += 4;+	*e++ = 4; /* Length of following GID */+	archive_le32enc(e, (uint32_t)archive_entry_gid(entry));+	e += 4;++	/* AES extra data field: WinZIP AES information, ID=0x9901 */+	if ((zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED)+	    && (zip->entry_encryption == ENCRYPTION_WINZIP_AES128+	        || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)) {++		memcpy(e, "\001\231\007\000\001\000AE", 8);+		/* AES vendor version AE-2 does not store a CRC.+		 * WinZip 11 uses AE-1, which does store the CRC,+		 * but it does not store the CRC when the file size+		 * is less than 20 bytes. So we simulate what+		 * WinZip 11 does.+		 * NOTE: WinZip 9.0 and 10.0 uses AE-2 by default. */+		if (archive_entry_size_is_set(zip->entry)+		    && archive_entry_size(zip->entry) < 20) {+			archive_le16enc(e+4, AES_VENDOR_AE_2);+			zip->aes_vendor = AES_VENDOR_AE_2;/* no CRC. */+		} else+			zip->aes_vendor = AES_VENDOR_AE_1;+		e += 8;+		/* AES encryption strength. */+		*e++ = (zip->entry_encryption == ENCRYPTION_WINZIP_AES128)?1:3;+		/* Actual compression method. */+		archive_le16enc(e, zip->entry_compression);+		e += 2;+	}++	/* Copy UT ,ux, and AES-extra into central directory as well. */+	zip->file_header_extra_offset = zip->central_directory_bytes;+	cd_extra = cd_alloc(zip, e - local_extra);+	memcpy(cd_extra, local_extra, e - local_extra);++	/*+	 * Following extra blocks vary between local header and+	 * central directory. These are the local header versions.+	 * Central directory versions get formatted in+	 * archive_write_zip_finish_entry() below.+	 */++	/* "[Zip64 entry] in the local header MUST include BOTH+	 * original [uncompressed] and compressed size fields." */+	if (zip->entry_uses_zip64) {+		unsigned char *zip64_start = e;+		memcpy(e, "\001\000\020\000", 4);+		e += 4;+		archive_le64enc(e, zip->entry_uncompressed_size);+		e += 8;+		archive_le64enc(e, zip->entry_compressed_size);+		e += 8;+		archive_le16enc(zip64_start + 2, (uint16_t)(e - (zip64_start + 4)));+	}++	if (zip->flags & ZIP_FLAG_EXPERIMENT_xl) {+		/* Experimental 'xl' extension to improve streaming. */+		unsigned char *external_info = e;+		int included = 7;+		memcpy(e, "xl\000\000", 4); // 0x6c65 + 2-byte length+		e += 4;+		e[0] = included; /* bitmap of included fields */+		e += 1;+		if (included & 1) {+			archive_le16enc(e, /* "Version created by" */+			    3 * 256 + version_needed);+			e += 2;+		}+		if (included & 2) {+			archive_le16enc(e, 0); /* internal file attributes */+			e += 2;+		}+		if (included & 4) {+			archive_le32enc(e,  /* external file attributes */+			    ((uint32_t)archive_entry_mode(zip->entry)) << 16);+			e += 4;+		}+		if (included & 8) {+			// Libarchive does not currently support file comments.+		}+		archive_le16enc(external_info + 2, (uint16_t)(e - (external_info + 4)));+	}++	/* Update local header with size of extra data and write it all out: */+	archive_le16enc(local_header + 28, (uint16_t)(e - local_extra));++	ret = __archive_write_output(a, local_header, 30);+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	zip->written_bytes += 30;++	ret = write_path(zip->entry, a);+	if (ret <= ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	zip->written_bytes += ret;++	ret = __archive_write_output(a, local_extra, e - local_extra);+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	zip->written_bytes += e - local_extra;++	/* For symlinks, write the body now. */+	if (slink != NULL) {+		ret = __archive_write_output(a, slink, slink_size);+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		zip->entry_compressed_written += slink_size;+		zip->entry_uncompressed_written += slink_size;+		zip->written_bytes += slink_size;+	}++#ifdef HAVE_ZLIB_H+	if (zip->entry_compression == COMPRESSION_DEFLATE) {+		zip->stream.zalloc = Z_NULL;+		zip->stream.zfree = Z_NULL;+		zip->stream.opaque = Z_NULL;+		zip->stream.next_out = zip->buf;+		zip->stream.avail_out = (uInt)zip->len_buf;+		if (deflateInit2(&zip->stream, zip->deflate_compression_level,+		    Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {+			archive_set_error(&a->archive, ENOMEM,+			    "Can't init deflate compressor");+			return (ARCHIVE_FATAL);+		}+	}+#endif++	return (ret2);+}++static ssize_t+archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)+{+	int ret;+	struct zip *zip = a->format_data;++	if ((int64_t)s > zip->entry_uncompressed_limit)+		s = (size_t)zip->entry_uncompressed_limit;+	zip->entry_uncompressed_written += s;++	if (s == 0) return 0;++	if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {+		switch (zip->entry_encryption) {+		case ENCRYPTION_TRADITIONAL:+			/* Initialize traditional PKWARE encryption context. */+			if (!zip->tctx_valid) {+				ret = init_traditional_pkware_encryption(a);+				if (ret != ARCHIVE_OK)+					return (ret);+				zip->tctx_valid = 1;+			}+			break;+		case ENCRYPTION_WINZIP_AES128:+		case ENCRYPTION_WINZIP_AES256:+			if (!zip->cctx_valid) {+				ret = init_winzip_aes_encryption(a);+				if (ret != ARCHIVE_OK)+					return (ret);+				zip->cctx_valid = zip->hctx_valid = 1;+			}+			break;+		default:+			break;+		}+	}++	switch (zip->entry_compression) {+	case COMPRESSION_STORE:+		if (zip->tctx_valid || zip->cctx_valid) {+			const uint8_t *rb = (const uint8_t *)buff;+			const uint8_t * const re = rb + s;++			while (rb < re) {+				size_t l;++				if (zip->tctx_valid) {+					l = trad_enc_encrypt_update(&zip->tctx,+					    rb, re - rb,+					    zip->buf, zip->len_buf);+				} else {+					l = zip->len_buf;+					ret = archive_encrypto_aes_ctr_update(+					    &zip->cctx,+					    rb, re - rb, zip->buf, &l);+					if (ret < 0) {+						archive_set_error(&a->archive,+						    ARCHIVE_ERRNO_MISC,+						    "Failed to encrypt file");+						return (ARCHIVE_FAILED);+					}+					archive_hmac_sha1_update(&zip->hctx,+					    zip->buf, l);+				}+				ret = __archive_write_output(a, zip->buf, l);+				if (ret != ARCHIVE_OK)+					return (ret);+				zip->entry_compressed_written += l;+				zip->written_bytes += l;+				rb += l;+			}+		} else {+			ret = __archive_write_output(a, buff, s);+			if (ret != ARCHIVE_OK)+				return (ret);+			zip->written_bytes += s;+			zip->entry_compressed_written += s;+		}+		break;+#if HAVE_ZLIB_H+	case COMPRESSION_DEFLATE:+		zip->stream.next_in = (unsigned char*)(uintptr_t)buff;+		zip->stream.avail_in = (uInt)s;+		do {+			ret = deflate(&zip->stream, Z_NO_FLUSH);+			if (ret == Z_STREAM_ERROR)+				return (ARCHIVE_FATAL);+			if (zip->stream.avail_out == 0) {+				if (zip->tctx_valid) {+					trad_enc_encrypt_update(&zip->tctx,+					    zip->buf, zip->len_buf,+					    zip->buf, zip->len_buf);+				} else if (zip->cctx_valid) {+					size_t outl = zip->len_buf;+					ret = archive_encrypto_aes_ctr_update(+					    &zip->cctx,+					    zip->buf, zip->len_buf,+					    zip->buf, &outl);+					if (ret < 0) {+						archive_set_error(&a->archive,+						    ARCHIVE_ERRNO_MISC,+						    "Failed to encrypt file");+						return (ARCHIVE_FAILED);+					}+					archive_hmac_sha1_update(&zip->hctx,+					    zip->buf, zip->len_buf);+				}+				ret = __archive_write_output(a, zip->buf,+					zip->len_buf);+				if (ret != ARCHIVE_OK)+					return (ret);+				zip->entry_compressed_written += zip->len_buf;+				zip->written_bytes += zip->len_buf;+				zip->stream.next_out = zip->buf;+				zip->stream.avail_out = (uInt)zip->len_buf;+			}+		} while (zip->stream.avail_in != 0);+		break;+#endif++	default:+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Invalid ZIP compression type");+		return ARCHIVE_FATAL;+	}++	zip->entry_uncompressed_limit -= s;+	if (!zip->cctx_valid || zip->aes_vendor != AES_VENDOR_AE_2)+		zip->entry_crc32 =+		    zip->crc32func(zip->entry_crc32, buff, (unsigned)s);+	return (s);++}++static int+archive_write_zip_finish_entry(struct archive_write *a)+{+	struct zip *zip = a->format_data;+	int ret;++#if HAVE_ZLIB_H+	if (zip->entry_compression == COMPRESSION_DEFLATE) {+		for (;;) {+			size_t remainder;++			ret = deflate(&zip->stream, Z_FINISH);+			if (ret == Z_STREAM_ERROR)+				return (ARCHIVE_FATAL);+			remainder = zip->len_buf - zip->stream.avail_out;+			if (zip->tctx_valid) {+				trad_enc_encrypt_update(&zip->tctx,+				    zip->buf, remainder, zip->buf, remainder);+			} else if (zip->cctx_valid) {+				size_t outl = remainder;+				ret = archive_encrypto_aes_ctr_update(+				    &zip->cctx, zip->buf, remainder,+				    zip->buf, &outl);+				if (ret < 0) {+					archive_set_error(&a->archive,+					    ARCHIVE_ERRNO_MISC,+					    "Failed to encrypt file");+					return (ARCHIVE_FAILED);+				}+				archive_hmac_sha1_update(&zip->hctx,+				    zip->buf, remainder);+			}+			ret = __archive_write_output(a, zip->buf, remainder);+			if (ret != ARCHIVE_OK)+				return (ret);+			zip->entry_compressed_written += remainder;+			zip->written_bytes += remainder;+			zip->stream.next_out = zip->buf;+			if (zip->stream.avail_out != 0)+				break;+			zip->stream.avail_out = (uInt)zip->len_buf;+		}+		deflateEnd(&zip->stream);+	}+#endif+	if (zip->hctx_valid) {+		uint8_t hmac[20];+		size_t hmac_len = 20;++		archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len);+		ret = __archive_write_output(a, hmac, AUTH_CODE_SIZE);+		if (ret != ARCHIVE_OK)+			return (ret);+		zip->entry_compressed_written += AUTH_CODE_SIZE;+		zip->written_bytes += AUTH_CODE_SIZE;+	}++	/* Write trailing data descriptor. */+	if ((zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END) != 0) {+		char d[24];+		memcpy(d, "PK\007\010", 4);+		if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2)+			archive_le32enc(d + 4, 0);/* no CRC.*/+		else+			archive_le32enc(d + 4, zip->entry_crc32);+		if (zip->entry_uses_zip64) {+			archive_le64enc(d + 8,+				(uint64_t)zip->entry_compressed_written);+			archive_le64enc(d + 16,+				(uint64_t)zip->entry_uncompressed_written);+			ret = __archive_write_output(a, d, 24);+			zip->written_bytes += 24;+		} else {+			archive_le32enc(d + 8,+				(uint32_t)zip->entry_compressed_written);+			archive_le32enc(d + 12,+				(uint32_t)zip->entry_uncompressed_written);+			ret = __archive_write_output(a, d, 16);+			zip->written_bytes += 16;+		}+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+	}++	/* Append Zip64 extra data to central directory information. */+	if (zip->entry_compressed_written > ZIP_4GB_MAX+	    || zip->entry_uncompressed_written > ZIP_4GB_MAX+	    || zip->entry_offset > ZIP_4GB_MAX) {+		unsigned char zip64[32];+		unsigned char *z = zip64, *zd;+		memcpy(z, "\001\000\000\000", 4);+		z += 4;+		if (zip->entry_uncompressed_written >= ZIP_4GB_MAX) {+			archive_le64enc(z, zip->entry_uncompressed_written);+			z += 8;+		}+		if (zip->entry_compressed_written >= ZIP_4GB_MAX) {+			archive_le64enc(z, zip->entry_compressed_written);+			z += 8;+		}+		if (zip->entry_offset >= ZIP_4GB_MAX) {+			archive_le64enc(z, zip->entry_offset);+			z += 8;+		}+		archive_le16enc(zip64 + 2, (uint16_t)(z - (zip64 + 4)));+		zd = cd_alloc(zip, z - zip64);+		if (zd == NULL) {+			archive_set_error(&a->archive, ENOMEM,+				"Can't allocate zip data");+			return (ARCHIVE_FATAL);+		}+		memcpy(zd, zip64, z - zip64);+		/* Zip64 means version needs to be set to at least 4.5 */+		if (archive_le16dec(zip->file_header + 6) < 45)+			archive_le16enc(zip->file_header + 6, 45);+	}++	/* Fix up central directory file header. */+	if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2)+		archive_le32enc(zip->file_header + 16, 0);/* no CRC.*/+	else+		archive_le32enc(zip->file_header + 16, zip->entry_crc32);+	archive_le32enc(zip->file_header + 20,+		(uint32_t)zipmin(zip->entry_compressed_written,+				 ZIP_4GB_MAX));+	archive_le32enc(zip->file_header + 24,+		(uint32_t)zipmin(zip->entry_uncompressed_written,+				 ZIP_4GB_MAX));+	archive_le16enc(zip->file_header + 30,+	    (uint16_t)(zip->central_directory_bytes - zip->file_header_extra_offset));+	archive_le32enc(zip->file_header + 42,+		(uint32_t)zipmin(zip->entry_offset,+				 ZIP_4GB_MAX));++	return (ARCHIVE_OK);+}++static int+archive_write_zip_close(struct archive_write *a)+{+	uint8_t buff[64];+	int64_t offset_start, offset_end;+	struct zip *zip = a->format_data;+	struct cd_segment *segment;+	int ret;++	offset_start = zip->written_bytes;+	segment = zip->central_directory;+	while (segment != NULL) {+		ret = __archive_write_output(a,+		    segment->buff, segment->p - segment->buff);+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		zip->written_bytes += segment->p - segment->buff;+		segment = segment->next;+	}+	offset_end = zip->written_bytes;++	/* If central dir info is too large, write Zip64 end-of-cd */+	if (offset_end - offset_start > ZIP_4GB_MAX+	    || offset_start > ZIP_4GB_MAX+	    || zip->central_directory_entries > 0xffffUL+	    || (zip->flags & ZIP_FLAG_FORCE_ZIP64)) {+	  /* Zip64 end-of-cd record */+	  memset(buff, 0, 56);+	  memcpy(buff, "PK\006\006", 4);+	  archive_le64enc(buff + 4, 44);+	  archive_le16enc(buff + 12, 45);+	  archive_le16enc(buff + 14, 45);+	  /* This is disk 0 of 0. */+	  archive_le64enc(buff + 24, zip->central_directory_entries);+	  archive_le64enc(buff + 32, zip->central_directory_entries);+	  archive_le64enc(buff + 40, offset_end - offset_start);+	  archive_le64enc(buff + 48, offset_start);+	  ret = __archive_write_output(a, buff, 56);+	  if (ret != ARCHIVE_OK)+		  return (ARCHIVE_FATAL);+	  zip->written_bytes += 56;++	  /* Zip64 end-of-cd locator record. */+	  memset(buff, 0, 20);+	  memcpy(buff, "PK\006\007", 4);+	  archive_le32enc(buff + 4, 0);+	  archive_le64enc(buff + 8, offset_end);+	  archive_le32enc(buff + 16, 1);+	  ret = __archive_write_output(a, buff, 20);+	  if (ret != ARCHIVE_OK)+		  return (ARCHIVE_FATAL);+	  zip->written_bytes += 20;++	}++	/* Format and write end of central directory. */+	memset(buff, 0, sizeof(buff));+	memcpy(buff, "PK\005\006", 4);+	archive_le16enc(buff + 8, (uint16_t)zipmin(0xffffU,+		zip->central_directory_entries));+	archive_le16enc(buff + 10, (uint16_t)zipmin(0xffffU,+		zip->central_directory_entries));+	archive_le32enc(buff + 12,+		(uint32_t)zipmin(ZIP_4GB_MAX, (offset_end - offset_start)));+	archive_le32enc(buff + 16,+		(uint32_t)zipmin(ZIP_4GB_MAX, offset_start));+	ret = __archive_write_output(a, buff, 22);+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	zip->written_bytes += 22;+	return (ARCHIVE_OK);+}++static int+archive_write_zip_free(struct archive_write *a)+{+	struct zip *zip;+	struct cd_segment *segment;++	zip = a->format_data;+	while (zip->central_directory != NULL) {+		segment = zip->central_directory;+		zip->central_directory = segment->next;+		free(segment->buff);+		free(segment);+	}+	free(zip->buf);+	archive_entry_free(zip->entry);+	if (zip->cctx_valid)+		archive_encrypto_aes_ctr_release(&zip->cctx);+	if (zip->hctx_valid)+		archive_hmac_sha1_cleanup(&zip->hctx);+	/* TODO: Free opt_sconv, sconv_default */++	free(zip);+	a->format_data = NULL;+	return (ARCHIVE_OK);+}++/* Convert into MSDOS-style date/time. */+static unsigned int+dos_time(const time_t unix_time)+{+	struct tm *t;+	unsigned int dt;+#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)+	struct tm tmbuf;+#endif+#if defined(HAVE__LOCALTIME64_S)+	errno_t terr;+	__time64_t tmptime;+#endif++	/* This will not preserve time when creating/extracting the archive+	 * on two systems with different time zones. */+#if defined(HAVE_LOCALTIME_R)+	t = localtime_r(&unix_time, &tmbuf);+#elif defined(HAVE__LOCALTIME64_S)+	tmptime = unix_time;+	terr = _localtime64_s(&tmbuf, &tmptime);+	if (terr)+		t = NULL;+	else+		t = &tmbuf;+#else+	t = localtime(&unix_time);+#endif++	/* MSDOS-style date/time is only between 1980-01-01 and 2107-12-31 */+	if (t->tm_year < 1980 - 1900)+		/* Set minimum date/time '1980-01-01 00:00:00'. */+		dt = 0x00210000U;+	else if (t->tm_year > 2107 - 1900)+		/* Set maximum date/time '2107-12-31 23:59:58'. */+		dt = 0xff9fbf7dU;+	else {+		dt = 0;+		dt += ((t->tm_year - 80) & 0x7f) << 9;+		dt += ((t->tm_mon + 1) & 0x0f) << 5;+		dt += (t->tm_mday & 0x1f);+		dt <<= 16;+		dt += (t->tm_hour & 0x1f) << 11;+		dt += (t->tm_min & 0x3f) << 5;+		dt += (t->tm_sec & 0x3e) >> 1; /* Only counting every 2 seconds. */+	}+	return dt;+}++static size_t+path_length(struct archive_entry *entry)+{+	mode_t type;+	const char *path;+	size_t len;++	type = archive_entry_filetype(entry);+	path = archive_entry_pathname(entry);++	if (path == NULL)+		return (0);+	len = strlen(path);+	if (type == AE_IFDIR && (path[0] == '\0' || path[len - 1] != '/'))+		++len; /* Space for the trailing / */+	return len;+}++static int+write_path(struct archive_entry *entry, struct archive_write *archive)+{+	int ret;+	const char *path;+	mode_t type;+	size_t written_bytes;++	path = archive_entry_pathname(entry);+	type = archive_entry_filetype(entry);+	written_bytes = 0;++	if (path == NULL)+		return (ARCHIVE_FATAL);++	ret = __archive_write_output(archive, path, strlen(path));+	if (ret != ARCHIVE_OK)+		return (ARCHIVE_FATAL);+	written_bytes += strlen(path);++	/* Folders are recognized by a trailing slash. */+	if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) {+		ret = __archive_write_output(archive, "/", 1);+		if (ret != ARCHIVE_OK)+			return (ARCHIVE_FATAL);+		written_bytes += 1;+	}++	return ((int)written_bytes);+}++static void+copy_path(struct archive_entry *entry, unsigned char *p)+{+	const char *path;+	size_t pathlen;+	mode_t type;++	path = archive_entry_pathname(entry);+	pathlen = strlen(path);+	type = archive_entry_filetype(entry);++	memcpy(p, path, pathlen);++	/* Folders are recognized by a trailing slash. */+	if ((type == AE_IFDIR) && (path[pathlen - 1] != '/'))+		p[pathlen] = '/';+}+++static struct archive_string_conv *+get_sconv(struct archive_write *a, struct zip *zip)+{+	if (zip->opt_sconv != NULL)+		return (zip->opt_sconv);++	if (!zip->init_default_conversion) {+		zip->sconv_default =+		    archive_string_default_conversion_for_write(&(a->archive));+		zip->init_default_conversion = 1;+	}+	return (zip->sconv_default);+}++/*+  Traditional PKWARE Decryption functions.+ */++static void+trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c)+{+	uint8_t t;+#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL)++	ctx->keys[0] = CRC32(ctx->keys[0], c);+	ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1;+	t = (ctx->keys[1] >> 24) & 0xff;+	ctx->keys[2] = CRC32(ctx->keys[2], t);+#undef CRC32+}++static uint8_t+trad_enc_decrypt_byte(struct trad_enc_ctx *ctx)+{+	unsigned temp = ctx->keys[2] | 2;+	return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff;+}++static unsigned+trad_enc_encrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in,+    size_t in_len, uint8_t *out, size_t out_len)+{+	unsigned i, max;++	max = (unsigned)((in_len < out_len)? in_len: out_len);++	for (i = 0; i < max; i++) {+		uint8_t t = in[i];+		out[i] = t ^ trad_enc_decrypt_byte(ctx);+		trad_enc_update_keys(ctx, t);+	}+	return i;+}++static int+trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len)+{++	ctx->keys[0] = 305419896L;+	ctx->keys[1] = 591751049L;+	ctx->keys[2] = 878082192L;++	for (;pw_len; --pw_len)+		trad_enc_update_keys(ctx, *pw++);+	return 0;+}++static int+is_traditional_pkware_encryption_supported(void)+{+	uint8_t key[TRAD_HEADER_SIZE];++	if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK)+		return (0);+	return (1);+}++static int+init_traditional_pkware_encryption(struct archive_write *a)+{+	struct zip *zip = a->format_data;+	const char *passphrase;+	uint8_t key[TRAD_HEADER_SIZE];+	uint8_t key_encrypted[TRAD_HEADER_SIZE];+	int ret;++	passphrase = __archive_write_get_passphrase(a);+	if (passphrase == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Encryption needs passphrase");+		return ARCHIVE_FAILED;+	}+	if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Can't generate random number for encryption");+		return ARCHIVE_FATAL;+	}+	trad_enc_init(&zip->tctx, passphrase, strlen(passphrase));+	/* Set the last key code which will be used as a check code+	 * for verifying passphrase in decryption. */+	key[TRAD_HEADER_SIZE-1] = zip->trad_chkdat;+	trad_enc_encrypt_update(&zip->tctx, key, TRAD_HEADER_SIZE,+	    key_encrypted, TRAD_HEADER_SIZE);+	/* Write encrypted keys in the top of the file content. */+	ret = __archive_write_output(a, key_encrypted, TRAD_HEADER_SIZE);+	if (ret != ARCHIVE_OK)+		return (ret);+	zip->written_bytes += TRAD_HEADER_SIZE;+	zip->entry_compressed_written += TRAD_HEADER_SIZE;+	return (ret);+}++static int+init_winzip_aes_encryption(struct archive_write *a)+{+	struct zip *zip = a->format_data;+	const char *passphrase;+	size_t key_len, salt_len;+	uint8_t salt[16 + 2];+	uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];+	int ret;++	passphrase = __archive_write_get_passphrase(a);+	if (passphrase == NULL) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Encryption needs passphrase");+		return (ARCHIVE_FAILED);+	}+	if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128) {+		salt_len = 8;+		key_len = 16;+	} else {+		/* AES 256 */+		salt_len = 16;+		key_len = 32;+	}+	if (archive_random(salt, salt_len) != ARCHIVE_OK) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Can't generate random number for encryption");+		return (ARCHIVE_FATAL);+	}+	archive_pbkdf2_sha1(passphrase, strlen(passphrase),+	    salt, salt_len, 1000, derived_key, key_len * 2 + 2);++	ret = archive_encrypto_aes_ctr_init(&zip->cctx, derived_key, key_len);+	if (ret != 0) {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Decryption is unsupported due to lack of crypto library");+		return (ARCHIVE_FAILED);+	}+	ret = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len,+	    key_len);+	if (ret != 0) {+		archive_encrypto_aes_ctr_release(&zip->cctx);+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Failed to initialize HMAC-SHA1");+		return (ARCHIVE_FAILED);+        }++	/* Set a password verification value after the 'salt'. */+	salt[salt_len] = derived_key[key_len * 2];+	salt[salt_len + 1] = derived_key[key_len * 2 + 1];++	/* Write encrypted keys in the top of the file content. */+	ret = __archive_write_output(a, salt, salt_len + 2);+	if (ret != ARCHIVE_OK)+		return (ret);+	zip->written_bytes += salt_len + 2;+	zip->entry_compressed_written += salt_len + 2;++	return (ARCHIVE_OK);+}++static int+is_winzip_aes_encryption_supported(int encryption)+{+	size_t key_len, salt_len;+	uint8_t salt[16 + 2];+	uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];+	archive_crypto_ctx cctx;+	archive_hmac_sha1_ctx hctx;+	int ret;++	if (encryption == ENCRYPTION_WINZIP_AES128) {+		salt_len = 8;+		key_len = 16;+	} else {+		/* AES 256 */+		salt_len = 16;+		key_len = 32;+	}+	if (archive_random(salt, salt_len) != ARCHIVE_OK)+		return (0);+	ret = archive_pbkdf2_sha1("p", 1, salt, salt_len, 1000,+	    derived_key, key_len * 2 + 2);+	if (ret != 0)+		return (0);++	ret = archive_encrypto_aes_ctr_init(&cctx, derived_key, key_len);+	if (ret != 0)+		return (0);+	ret = archive_hmac_sha1_init(&hctx, derived_key + key_len,+	    key_len);+	archive_encrypto_aes_ctr_release(&cctx);+	if (ret != 0)+		return (0);+	archive_hmac_sha1_cleanup(&hctx);+	return (1);+}
+ c/archive_write_set_options.c view
@@ -0,0 +1,130 @@+/*-+ * Copyright (c) 2003-2010 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#include "archive_write_private.h"+#include "archive_options_private.h"++static int	archive_set_format_option(struct archive *a,+		    const char *m, const char *o, const char *v);+static int	archive_set_filter_option(struct archive *a,+		    const char *m, const char *o, const char *v);+static int	archive_set_option(struct archive *a,+		    const char *m, const char *o, const char *v);++int+archive_write_set_format_option(struct archive *a, const char *m, const char *o,+    const char *v)+{+	return _archive_set_option(a, m, o, v,+	    ARCHIVE_WRITE_MAGIC, "archive_write_set_format_option",+	    archive_set_format_option);+}++int+archive_write_set_filter_option(struct archive *a, const char *m, const char *o,+    const char *v)+{+	return _archive_set_option(a, m, o, v,+	    ARCHIVE_WRITE_MAGIC, "archive_write_set_filter_option",+	    archive_set_filter_option);+}++int+archive_write_set_option(struct archive *a, const char *m, const char *o,+    const char *v)+{+	return _archive_set_option(a, m, o, v,+	    ARCHIVE_WRITE_MAGIC, "archive_write_set_option",+	    archive_set_option);+}++int+archive_write_set_options(struct archive *a, const char *options)+{+	return _archive_set_options(a, options,+	    ARCHIVE_WRITE_MAGIC, "archive_write_set_options",+	    archive_set_option);+}++static int+archive_set_format_option(struct archive *_a, const char *m, const char *o,+    const char *v)+{+	struct archive_write *a = (struct archive_write *)_a;++	if (a->format_name == NULL)+		return (m == NULL)?ARCHIVE_FAILED:ARCHIVE_WARN - 1;+	/* If the format name didn't match, return a special code for+	 * _archive_set_option[s]. */+	if (m != NULL && strcmp(m, a->format_name) != 0)+		return (ARCHIVE_WARN - 1);+	if (a->format_options == NULL)+		return (ARCHIVE_WARN);+	return a->format_options(a, o, v);+}++static int+archive_set_filter_option(struct archive *_a, const char *m, const char *o,+    const char *v)+{+	struct archive_write *a = (struct archive_write *)_a;+	struct archive_write_filter *filter;+	int r, rv = ARCHIVE_WARN;++	for (filter = a->filter_first; filter != NULL; filter = filter->next_filter) {+		if (filter->options == NULL)+			continue;+		if (m != NULL && strcmp(filter->name, m) != 0)+			continue;++		r = filter->options(filter, o, v);++		if (r == ARCHIVE_FATAL)+			return (ARCHIVE_FATAL);++		if (m != NULL)+			return (r);++		if (r == ARCHIVE_OK)+			rv = ARCHIVE_OK;+	}+	/* If the filter name didn't match, return a special code for+	 * _archive_set_option[s]. */+	if (rv == ARCHIVE_WARN && m != NULL)+		rv = ARCHIVE_WARN - 1;+	return (rv);+}++static int+archive_set_option(struct archive *a, const char *m, const char *o,+    const char *v)+{+	return _archive_set_either_option(a, m, o, v,+	    archive_set_format_option,+	    archive_set_filter_option);+}
+ c/archive_write_set_passphrase.c view
@@ -0,0 +1,95 @@+/*-+ * Copyright (c) 2014 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"+__FBSDID("$FreeBSD$");++#ifdef HAVE_ERRNO_H+#include <errno.h>+#endif+#include "archive_write_private.h"++int+archive_write_set_passphrase(struct archive *_a, const char *p)+{+	struct archive_write *a = (struct archive_write *)_a;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW,+		"archive_write_set_passphrase");++	if (p == NULL || p[0] == '\0') {+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,+		    "Empty passphrase is unacceptable");+		return (ARCHIVE_FAILED);+	}+	free(a->passphrase);+	a->passphrase = strdup(p);+	if (a->passphrase == NULL) {+		archive_set_error(&a->archive, ENOMEM,+		    "Can't allocate data for passphrase");+		return (ARCHIVE_FATAL);+	}+	return (ARCHIVE_OK);+}+++int+archive_write_set_passphrase_callback(struct archive *_a, void *client_data,+    archive_passphrase_callback *cb)+{+	struct archive_write *a = (struct archive_write *)_a;++	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW,+		"archive_write_set_passphrase_callback");++	a->passphrase_callback = cb;+	a->passphrase_client_data = client_data;+	return (ARCHIVE_OK);+}+++const char *+__archive_write_get_passphrase(struct archive_write *a)+{++	if (a->passphrase != NULL)+		return (a->passphrase);++	if (a->passphrase_callback != NULL) {+		const char *p;+		p = a->passphrase_callback(&a->archive,+		    a->passphrase_client_data);+		if (p != NULL) {+			a->passphrase = strdup(p);+			if (a->passphrase == NULL) {+				archive_set_error(&a->archive, ENOMEM,+				    "Can't allocate data for passphrase");+				return (NULL);+			}+			return (a->passphrase);+		}+	}+	return (NULL);+}
+ c/archive_xxhash.h view
@@ -0,0 +1,48 @@+/*-+ * Copyright (c) 2014 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ */++#ifndef ARCHIVE_XXHASH_H_INCLUDED+#define ARCHIVE_XXHASH_H_INCLUDED++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif+++typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;++struct archive_xxhash {+	unsigned int  (*XXH32)(const void* input, unsigned int len,+			unsigned int seed);+	void*         (*XXH32_init)(unsigned int seed);+	XXH_errorcode (*XXH32_update)(void* state, const void* input,+			unsigned int len);+	unsigned int  (*XXH32_digest)(void* state);+};++extern const struct archive_xxhash __archive_xxhash;++#endif
+ c/autoconf-darwin/config.h view
@@ -0,0 +1,1428 @@+/* config.h.  Generated from config.h.in by configure.  */+/* config.h.in.  Generated from configure.ac by autoheader.  */++/* Darwin ACL support */+/* #undef ARCHIVE_ACL_DARWIN */++/* FreeBSD ACL support */+/* #undef ARCHIVE_ACL_FREEBSD */++/* FreeBSD NFSv4 ACL support */+/* #undef ARCHIVE_ACL_FREEBSD_NFS4 */++/* Linux POSIX.1e ACL support via libacl */+/* #undef ARCHIVE_ACL_LIBACL */++/* Linux NFSv4 ACL support via librichacl */+/* #undef ARCHIVE_ACL_LIBRICHACL */++/* Solaris ACL support */+/* #undef ARCHIVE_ACL_SUNOS */++/* Solaris NFSv4 ACL support */+/* #undef ARCHIVE_ACL_SUNOS_NFS4 */++/* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_MD5_LIBC */++/* MD5 via ARCHIVE_CRYPTO_MD5_LIBMD supported. */+/* #undef ARCHIVE_CRYPTO_MD5_LIBMD */++/* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */+#define ARCHIVE_CRYPTO_MD5_LIBSYSTEM 1++/* MD5 via ARCHIVE_CRYPTO_MD5_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_MD5_MBEDTLS */++/* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */++/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_MD5_OPENSSL */++/* MD5 via ARCHIVE_CRYPTO_MD5_WIN supported. */+/* #undef ARCHIVE_CRYPTO_MD5_WIN */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_LIBC */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBMD supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_LIBMD */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_MBEDTLS */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_NETTLE */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_OPENSSL */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_LIBC */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBMD supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_LIBMD */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */+#define ARCHIVE_CRYPTO_SHA1_LIBSYSTEM 1++/* SHA1 via ARCHIVE_CRYPTO_SHA1_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_MBEDTLS */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_OPENSSL */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_WIN */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC2 supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC2 */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC3 */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBMD supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBMD */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */+#define ARCHIVE_CRYPTO_SHA256_LIBSYSTEM 1++/* SHA256 via ARCHIVE_CRYPTO_SHA256_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_MBEDTLS */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_OPENSSL */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_WIN */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC2 supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC2 */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC3 supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC3 */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */+#define ARCHIVE_CRYPTO_SHA384_LIBSYSTEM 1++/* SHA384 via ARCHIVE_CRYPTO_SHA384_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_MBEDTLS */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_OPENSSL */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_WIN */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC2 supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC2 */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC3 */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBMD supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBMD */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */+#define ARCHIVE_CRYPTO_SHA512_LIBSYSTEM 1++/* SHA512 via ARCHIVE_CRYPTO_SHA512_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_MBEDTLS */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_OPENSSL */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_WIN */++/* AIX xattr support */+/* #undef ARCHIVE_XATTR_AIX */++/* Darwin xattr support */+/* #undef ARCHIVE_XATTR_DARWIN */++/* FreeBSD xattr support */+/* #undef ARCHIVE_XATTR_FREEBSD */++/* Linux xattr support */+/* #undef ARCHIVE_XATTR_LINUX */++/* Version number of bsdcat */+#define BSDCAT_VERSION_STRING "3.4.3"++/* Version number of bsdcpio */+#define BSDCPIO_VERSION_STRING "3.4.3"++/* Version number of bsdtar */+#define BSDTAR_VERSION_STRING "3.4.3"++/* Define to 1 if the system has the type `ace_t'. */+/* #undef HAVE_ACE_T */++/* Define to 1 if you have the `acl' function. */+/* #undef HAVE_ACL */++/* Define to 1 if the system has the type `aclent_t'. */+/* #undef HAVE_ACLENT_T */++/* Define to 1 if you have the `acl_add_flag_np' function. */+/* #undef HAVE_ACL_ADD_FLAG_NP */++/* Define to 1 if you have the `acl_add_perm' function. */+/* #undef HAVE_ACL_ADD_PERM */++/* Define to 1 if you have the `acl_clear_flags_np' function. */+/* #undef HAVE_ACL_CLEAR_FLAGS_NP */++/* Define to 1 if you have the `acl_clear_perms' function. */+/* #undef HAVE_ACL_CLEAR_PERMS */++/* Define to 1 if you have the `acl_create_entry' function. */+/* #undef HAVE_ACL_CREATE_ENTRY */++/* Define to 1 if you have the `acl_delete_def_file' function. */+/* #undef HAVE_ACL_DELETE_DEF_FILE */++/* Define to 1 if the system has the type `acl_entry_t'. */+/* #undef HAVE_ACL_ENTRY_T */++/* Define to 1 if you have the `acl_free' function. */+/* #undef HAVE_ACL_FREE */++/* Define to 1 if you have the `acl_get_brand_np' function. */+/* #undef HAVE_ACL_GET_BRAND_NP */++/* Define to 1 if you have the `acl_get_entry' function. */+/* #undef HAVE_ACL_GET_ENTRY */++/* Define to 1 if you have the `acl_get_entry_type_np' function. */+/* #undef HAVE_ACL_GET_ENTRY_TYPE_NP */++/* Define to 1 if you have the `acl_get_fd' function. */+/* #undef HAVE_ACL_GET_FD */++/* Define to 1 if you have the `acl_get_fd_np' function. */+/* #undef HAVE_ACL_GET_FD_NP */++/* Define to 1 if you have the `acl_get_file' function. */+/* #undef HAVE_ACL_GET_FILE */++/* Define to 1 if you have the `acl_get_flagset_np' function. */+/* #undef HAVE_ACL_GET_FLAGSET_NP */++/* Define to 1 if you have the `acl_get_flag_np' function. */+/* #undef HAVE_ACL_GET_FLAG_NP */++/* Define to 1 if you have the `acl_get_link_np' function. */+/* #undef HAVE_ACL_GET_LINK_NP */++/* Define to 1 if you have the `acl_get_perm' function. */+/* #undef HAVE_ACL_GET_PERM */++/* Define to 1 if you have the `acl_get_permset' function. */+/* #undef HAVE_ACL_GET_PERMSET */++/* Define to 1 if you have the `acl_get_perm_np' function. */+/* #undef HAVE_ACL_GET_PERM_NP */++/* Define to 1 if you have the `acl_get_qualifier' function. */+/* #undef HAVE_ACL_GET_QUALIFIER */++/* Define to 1 if you have the `acl_get_tag_type' function. */+/* #undef HAVE_ACL_GET_TAG_TYPE */++/* Define to 1 if you have the `acl_init' function. */+/* #undef HAVE_ACL_INIT */++/* Define to 1 if you have the `acl_is_trivial_np' function. */+/* #undef HAVE_ACL_IS_TRIVIAL_NP */++/* Define to 1 if you have the <acl/libacl.h> header file. */+/* #undef HAVE_ACL_LIBACL_H */++/* Define to 1 if the system has the type `acl_permset_t'. */+/* #undef HAVE_ACL_PERMSET_T */++/* Define to 1 if you have the `acl_set_entry_type_np' function. */+/* #undef HAVE_ACL_SET_ENTRY_TYPE_NP */++/* Define to 1 if you have the `acl_set_fd' function. */+/* #undef HAVE_ACL_SET_FD */++/* Define to 1 if you have the `acl_set_fd_np' function. */+/* #undef HAVE_ACL_SET_FD_NP */++/* Define to 1 if you have the `acl_set_file' function. */+/* #undef HAVE_ACL_SET_FILE */++/* Define to 1 if you have the `acl_set_link_np' function. */+/* #undef HAVE_ACL_SET_LINK_NP */++/* Define to 1 if you have the `acl_set_qualifier' function. */+/* #undef HAVE_ACL_SET_QUALIFIER */++/* Define to 1 if you have the `acl_set_tag_type' function. */+/* #undef HAVE_ACL_SET_TAG_TYPE */++/* Define to 1 if the system has the type `acl_t'. */+/* #undef HAVE_ACL_T */++/* Define to 1 if the system has the type `acl_tag_t'. */+/* #undef HAVE_ACL_TAG_T */++/* Define to 1 if you have the `arc4random_buf' function. */+#define HAVE_ARC4RANDOM_BUF 1++/* Define to 1 if you have the <attr/xattr.h> header file. */+/* #undef HAVE_ATTR_XATTR_H */++/* Define to 1 if you have the <bcrypt.h> header file. */+/* #undef HAVE_BCRYPT_H */++/* Define to 1 if you have the <blake2.h> header file. */+/* #undef HAVE_BLAKE2_H */++/* Define to 1 if you have the <bzlib.h> header file. */+/* #undef HAVE_BZLIB_H */++/* Define to 1 if you have the `chflags' function. */+#define HAVE_CHFLAGS 1++/* Define to 1 if you have the `chown' function. */+#define HAVE_CHOWN 1++/* Define to 1 if you have the `chroot' function. */+#define HAVE_CHROOT 1++/* Define to 1 if you have the <copyfile.h> header file. */+#define HAVE_COPYFILE_H 1++/* Define to 1 if you have the `ctime_r' function. */+#define HAVE_CTIME_R 1++/* Define to 1 if you have the <ctype.h> header file. */+#define HAVE_CTYPE_H 1++/* Define to 1 if you have the `cygwin_conv_path' function. */+/* #undef HAVE_CYGWIN_CONV_PATH */++/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACE_GETACL */++/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACE_GETACLCNT */++/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACE_SETACL */++/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if+   you don't. */+/* #undef HAVE_DECL_ACL_SYNCHRONIZE */++/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if+   you don't. */+/* #undef HAVE_DECL_ACL_TYPE_EXTENDED */++/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACL_TYPE_NFS4 */++/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACL_USER */++/* Define to 1 if you have the declaration of `EXTATTR_NAMESPACE_USER', and to+   0 if you don't. */+/* #undef HAVE_DECL_EXTATTR_NAMESPACE_USER */++/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.+   */+/* #undef HAVE_DECL_GETACL */++/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you+   don't. */+/* #undef HAVE_DECL_GETACLCNT */++/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_INT32_MAX 1++/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you+   don't. */+#define HAVE_DECL_INT32_MIN 1++/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_INT64_MAX 1++/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you+   don't. */+#define HAVE_DECL_INT64_MIN 1++/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_INTMAX_MAX 1++/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you+   don't. */+#define HAVE_DECL_INTMAX_MIN 1++/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.+   */+/* #undef HAVE_DECL_SETACL */++/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_SIZE_MAX 1++/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_SSIZE_MAX 1++/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you+   don't. */+#define HAVE_DECL_STRERROR_R 1++/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_UINT32_MAX 1++/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_UINT64_MAX 1++/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_UINTMAX_MAX 1++/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if+   you don't. */+/* #undef HAVE_DECL_XATTR_NOFOLLOW */++/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.+   */+#define HAVE_DIRENT_H 1++/* Define to 1 if you have a dirfd function or macro */+#define HAVE_DIRFD 1++/* Define to 1 if you have the <dlfcn.h> header file. */+#define HAVE_DLFCN_H 1++/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */+/* #undef HAVE_DOPRNT */++/* Define to 1 if nl_langinfo supports D_MD_ORDER */+#define HAVE_D_MD_ORDER 1++/* A possible errno value for invalid file format errors */+#define HAVE_EFTYPE 1++/* A possible errno value for invalid file format errors */+#define HAVE_EILSEQ 1++/* Define to 1 if you have the <errno.h> header file. */+#define HAVE_ERRNO_H 1++/* Define to 1 if you have the <expat.h> header file. */+/* #undef HAVE_EXPAT_H */++/* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */+/* #undef HAVE_EXT2FS_EXT2_FS_H */++/* Define to 1 if you have the `extattr_get_fd' function. */+/* #undef HAVE_EXTATTR_GET_FD */++/* Define to 1 if you have the `extattr_get_file' function. */+/* #undef HAVE_EXTATTR_GET_FILE */++/* Define to 1 if you have the `extattr_get_link' function. */+/* #undef HAVE_EXTATTR_GET_LINK */++/* Define to 1 if you have the `extattr_list_fd' function. */+/* #undef HAVE_EXTATTR_LIST_FD */++/* Define to 1 if you have the `extattr_list_file' function. */+/* #undef HAVE_EXTATTR_LIST_FILE */++/* Define to 1 if you have the `extattr_list_link' function. */+/* #undef HAVE_EXTATTR_LIST_LINK */++/* Define to 1 if you have the `extattr_set_fd' function. */+/* #undef HAVE_EXTATTR_SET_FD */++/* Define to 1 if you have the `extattr_set_link' function. */+/* #undef HAVE_EXTATTR_SET_LINK */++/* Define to 1 if you have the `facl' function. */+/* #undef HAVE_FACL */++/* Define to 1 if you have the `fchdir' function. */+#define HAVE_FCHDIR 1++/* Define to 1 if you have the `fchflags' function. */+#define HAVE_FCHFLAGS 1++/* Define to 1 if you have the `fchmod' function. */+#define HAVE_FCHMOD 1++/* Define to 1 if you have the `fchown' function. */+#define HAVE_FCHOWN 1++/* Define to 1 if you have the `fcntl' function. */+#define HAVE_FCNTL 1++/* Define to 1 if you have the <fcntl.h> header file. */+#define HAVE_FCNTL_H 1++/* Define to 1 if you have the `fdopendir' function. */+#define HAVE_FDOPENDIR 1++/* Define to 1 if you have the `fgetea' function. */+/* #undef HAVE_FGETEA */++/* Define to 1 if you have the `fgetxattr' function. */+/* #undef HAVE_FGETXATTR */++/* Define to 1 if you have the `flistea' function. */+/* #undef HAVE_FLISTEA */++/* Define to 1 if you have the `flistxattr' function. */+/* #undef HAVE_FLISTXATTR */++/* Define to 1 if you have the `fork' function. */+#define HAVE_FORK 1++/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */+#define HAVE_FSEEKO 1++/* Define to 1 if you have the `fsetea' function. */+/* #undef HAVE_FSETEA */++/* Define to 1 if you have the `fsetxattr' function. */+/* #undef HAVE_FSETXATTR */++/* Define to 1 if you have the `fstat' function. */+#define HAVE_FSTAT 1++/* Define to 1 if you have the `fstatat' function. */+#define HAVE_FSTATAT 1++/* Define to 1 if you have the `fstatfs' function. */+#define HAVE_FSTATFS 1++/* Define to 1 if you have the `fstatvfs' function. */+#define HAVE_FSTATVFS 1++/* Define to 1 if you have the `ftruncate' function. */+#define HAVE_FTRUNCATE 1++/* Define to 1 if you have the `futimens' function. */+#define HAVE_FUTIMENS 1++/* Define to 1 if you have the `futimes' function. */+#define HAVE_FUTIMES 1++/* Define to 1 if you have the `futimesat' function. */+/* #undef HAVE_FUTIMESAT */++/* Define to 1 if you have the `getea' function. */+/* #undef HAVE_GETEA */++/* Define to 1 if you have the `geteuid' function. */+#define HAVE_GETEUID 1++/* Define to 1 if you have the `getgrgid_r' function. */+#define HAVE_GETGRGID_R 1++/* Define to 1 if you have the `getgrnam_r' function. */+#define HAVE_GETGRNAM_R 1++/* Define to 1 if you have the `getpid' function. */+#define HAVE_GETPID 1++/* Define to 1 if you have the `getpwnam_r' function. */+#define HAVE_GETPWNAM_R 1++/* Define to 1 if you have the `getpwuid_r' function. */+#define HAVE_GETPWUID_R 1++/* Define to 1 if you have the `getvfsbyname' function. */+#define HAVE_GETVFSBYNAME 1++/* Define to 1 if you have the `getxattr' function. */+/* #undef HAVE_GETXATTR */++/* Define to 1 if you have the `gmtime_r' function. */+#define HAVE_GMTIME_R 1++/* Define to 1 if you have the <grp.h> header file. */+#define HAVE_GRP_H 1++/* Define if you have the iconv() function and it works. */+/* #undef HAVE_ICONV */++/* Define to 1 if you have the <iconv.h> header file. */+/* #undef HAVE_ICONV_H */++/* Define to 1 if the system has the type `intmax_t'. */+#define HAVE_INTMAX_T 1++/* Define to 1 if you have the <inttypes.h> header file. */+#define HAVE_INTTYPES_H 1++/* Define to 1 if you have the <io.h> header file. */+/* #undef HAVE_IO_H */++/* Define to 1 if you have the <langinfo.h> header file. */+#define HAVE_LANGINFO_H 1++/* Define to 1 if you have the `lchflags' function. */+#define HAVE_LCHFLAGS 1++/* Define to 1 if you have the `lchmod' function. */+#define HAVE_LCHMOD 1++/* Define to 1 if you have the `lchown' function. */+#define HAVE_LCHOWN 1++/* Define to 1 if you have the `lgetea' function. */+/* #undef HAVE_LGETEA */++/* Define to 1 if you have the `lgetxattr' function. */+/* #undef HAVE_LGETXATTR */++/* Define to 1 if you have the `acl' library (-lacl). */+/* #undef HAVE_LIBACL */++/* Define to 1 if you have the `b2' library (-lb2). */+/* #undef HAVE_LIBB2 */++/* Define to 1 if you have the `bz2' library (-lbz2). */+/* #undef HAVE_LIBBZ2 */++/* Define to 1 if you have the `charset' library (-lcharset). */+/* #undef HAVE_LIBCHARSET */++/* Define to 1 if you have the `crypto' library (-lcrypto). */+/* #undef HAVE_LIBCRYPTO */++/* Define to 1 if you have the `eay32' library (-leay32). */+/* #undef HAVE_LIBEAY32 */++/* Define to 1 if you have the `eay64' library (-leay64). */+/* #undef HAVE_LIBEAY64 */++/* Define to 1 if you have the `expat' library (-lexpat). */+/* #undef HAVE_LIBEXPAT */++/* Define to 1 if you have the `lz4' library (-llz4). */+/* #undef HAVE_LIBLZ4 */++/* Define to 1 if you have the `lzma' library (-llzma). */+/* #undef HAVE_LIBLZMA */++/* Define to 1 if you have the `lzo2' library (-llzo2). */+/* #undef HAVE_LIBLZO2 */++/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */+/* #undef HAVE_LIBMBEDCRYPTO */++/* Define to 1 if you have the `md' library (-lmd). */+/* #undef HAVE_LIBMD */++/* Define to 1 if you have the `nettle' library (-lnettle). */+/* #undef HAVE_LIBNETTLE */++/* Define to 1 if you have the `pcre' library (-lpcre). */+/* #undef HAVE_LIBPCRE */++/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */+/* #undef HAVE_LIBPCREPOSIX */++/* Define to 1 if you have the `regex' library (-lregex). */+/* #undef HAVE_LIBREGEX */++/* Define to 1 if you have the `richacl' library (-lrichacl). */+/* #undef HAVE_LIBRICHACL */++/* Define to 1 if you have the `xml2' library (-lxml2). */+/* #undef HAVE_LIBXML2 */++/* Define to 1 if you have the <libxml/xmlreader.h> header file. */+/* #undef HAVE_LIBXML_XMLREADER_H */++/* Define to 1 if you have the <libxml/xmlwriter.h> header file. */+/* #undef HAVE_LIBXML_XMLWRITER_H */++/* Define to 1 if you have the `z' library (-lz). */+/* #undef HAVE_LIBZ */++/* Define to 1 if you have the `zstd' library (-lzstd). */+/* #undef HAVE_LIBZSTD */++/* Define to 1 if you have the <limits.h> header file. */+#define HAVE_LIMITS_H 1++/* Define to 1 if you have the `link' function. */+#define HAVE_LINK 1++/* Define to 1 if you have the <linux/fiemap.h> header file. */+/* #undef HAVE_LINUX_FIEMAP_H */++/* Define to 1 if you have the <linux/fs.h> header file. */+/* #undef HAVE_LINUX_FS_H */++/* Define to 1 if you have the <linux/magic.h> header file. */+/* #undef HAVE_LINUX_MAGIC_H */++/* Define to 1 if you have the <linux/types.h> header file. */+/* #undef HAVE_LINUX_TYPES_H */++/* Define to 1 if you have the `listea' function. */+/* #undef HAVE_LISTEA */++/* Define to 1 if you have the `listxattr' function. */+/* #undef HAVE_LISTXATTR */++/* Define to 1 if you have the `llistea' function. */+/* #undef HAVE_LLISTEA */++/* Define to 1 if you have the `llistxattr' function. */+/* #undef HAVE_LLISTXATTR */++/* Define to 1 if you have the <localcharset.h> header file. */+/* #undef HAVE_LOCALCHARSET_H */++/* Define to 1 if you have the `locale_charset' function. */+/* #undef HAVE_LOCALE_CHARSET */++/* Define to 1 if you have the <locale.h> header file. */+#define HAVE_LOCALE_H 1++/* Define to 1 if you have the `localtime_r' function. */+#define HAVE_LOCALTIME_R 1++/* Define to 1 if the system has the type `long long int'. */+#define HAVE_LONG_LONG_INT 1++/* Define to 1 if you have the `lsetea' function. */+/* #undef HAVE_LSETEA */++/* Define to 1 if you have the `lsetxattr' function. */+/* #undef HAVE_LSETXATTR */++/* Define to 1 if you have the `lstat' function. */+#define HAVE_LSTAT 1++/* Define to 1 if `lstat' has the bug that it succeeds when given the+   zero-length file name argument. */+/* #undef HAVE_LSTAT_EMPTY_STRING_BUG */++/* Define to 1 if you have the `lutimes' function. */+#define HAVE_LUTIMES 1++/* Define to 1 if you have the <lz4hc.h> header file. */+/* #undef HAVE_LZ4HC_H */++/* Define to 1 if you have the <lz4.h> header file. */+/* #undef HAVE_LZ4_H */++/* Define to 1 if you have the <lzma.h> header file. */+/* #undef HAVE_LZMA_H */++/* Define to 1 if you have the `lzma_stream_encoder_mt' function. */+/* #undef HAVE_LZMA_STREAM_ENCODER_MT */++/* Define to 1 if you have the <lzo/lzo1x.h> header file. */+/* #undef HAVE_LZO_LZO1X_H */++/* Define to 1 if you have the <lzo/lzoconf.h> header file. */+/* #undef HAVE_LZO_LZOCONF_H */++/* Define to 1 if you have the <mbedtls/aes.h> header file. */+/* #undef HAVE_MBEDTLS_AES_H */++/* Define to 1 if you have the <mbedtls/md.h> header file. */+/* #undef HAVE_MBEDTLS_MD_H */++/* Define to 1 if you have the <mbedtls/pkcs5.h> header file. */+/* #undef HAVE_MBEDTLS_PKCS5_H */++/* Define to 1 if you have the `mbrtowc' function. */+#define HAVE_MBRTOWC 1++/* Define to 1 if you have the `mbr_gid_to_uuid' function. */+/* #undef HAVE_MBR_GID_TO_UUID */++/* Define to 1 if you have the `mbr_uid_to_uuid' function. */+/* #undef HAVE_MBR_UID_TO_UUID */++/* Define to 1 if you have the `mbr_uuid_to_id' function. */+/* #undef HAVE_MBR_UUID_TO_ID */++/* Define to 1 if you have the <md5.h> header file. */+/* #undef HAVE_MD5_H */++/* Define to 1 if you have the <membership.h> header file. */+#define HAVE_MEMBERSHIP_H 1++/* Define to 1 if you have the `memmove' function. */+#define HAVE_MEMMOVE 1++/* Define to 1 if you have the <memory.h> header file. */+#define HAVE_MEMORY_H 1++/* Define to 1 if you have the `memset' function. */+#define HAVE_MEMSET 1++/* Define to 1 if you have the `mkdir' function. */+#define HAVE_MKDIR 1++/* Define to 1 if you have the `mkfifo' function. */+#define HAVE_MKFIFO 1++/* Define to 1 if you have the `mknod' function. */+#define HAVE_MKNOD 1++/* Define to 1 if you have the `mkstemp' function. */+#define HAVE_MKSTEMP 1++/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */+/* #undef HAVE_NDIR_H */++/* Define to 1 if you have the <nettle/aes.h> header file. */+/* #undef HAVE_NETTLE_AES_H */++/* Define to 1 if you have the <nettle/hmac.h> header file. */+/* #undef HAVE_NETTLE_HMAC_H */++/* Define to 1 if you have the <nettle/md5.h> header file. */+/* #undef HAVE_NETTLE_MD5_H */++/* Define to 1 if you have the <nettle/pbkdf2.h> header file. */+/* #undef HAVE_NETTLE_PBKDF2_H */++/* Define to 1 if you have the <nettle/ripemd160.h> header file. */+/* #undef HAVE_NETTLE_RIPEMD160_H */++/* Define to 1 if you have the <nettle/sha.h> header file. */+/* #undef HAVE_NETTLE_SHA_H */++/* Define to 1 if you have the `nl_langinfo' function. */+#define HAVE_NL_LANGINFO 1++/* Define to 1 if you have the `openat' function. */+#define HAVE_OPENAT 1++/* Define to 1 if you have the <openssl/evp.h> header file. */+/* #undef HAVE_OPENSSL_EVP_H */++/* Define to 1 if you have the <paths.h> header file. */+#define HAVE_PATHS_H 1++/* Define to 1 if you have the <pcreposix.h> header file. */+/* #undef HAVE_PCREPOSIX_H */++/* Define to 1 if you have the `pipe' function. */+#define HAVE_PIPE 1++/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */+/* #undef HAVE_PKCS5_PBKDF2_HMAC_SHA1 */++/* Define to 1 if you have the `poll' function. */+#define HAVE_POLL 1++/* Define to 1 if you have the <poll.h> header file. */+#define HAVE_POLL_H 1++/* Define to 1 if you have the `posix_spawnp' function. */+#define HAVE_POSIX_SPAWNP 1++/* Define to 1 if you have the <pthread.h> header file. */+#define HAVE_PTHREAD_H 1++/* Define to 1 if you have the <pwd.h> header file. */+#define HAVE_PWD_H 1++/* Define to 1 if you have a POSIX compatible readdir_r */+#define HAVE_READDIR_R 1++/* Define to 1 if you have the `readlink' function. */+#define HAVE_READLINK 1++/* Define to 1 if you have the `readlinkat' function. */+#define HAVE_READLINKAT 1++/* Define to 1 if you have the `readpassphrase' function. */+#define HAVE_READPASSPHRASE 1++/* Define to 1 if you have the <readpassphrase.h> header file. */+#define HAVE_READPASSPHRASE_H 1++/* Define to 1 if you have the <regex.h> header file. */+/* #undef HAVE_REGEX_H */++/* Define to 1 if you have the `richacl_alloc' function. */+/* #undef HAVE_RICHACL_ALLOC */++/* Define to 1 if you have the `richacl_equiv_mode' function. */+/* #undef HAVE_RICHACL_EQUIV_MODE */++/* Define to 1 if you have the `richacl_free' function. */+/* #undef HAVE_RICHACL_FREE */++/* Define to 1 if you have the `richacl_get_fd' function. */+/* #undef HAVE_RICHACL_GET_FD */++/* Define to 1 if you have the `richacl_get_file' function. */+/* #undef HAVE_RICHACL_GET_FILE */++/* Define to 1 if you have the `richacl_set_fd' function. */+/* #undef HAVE_RICHACL_SET_FD */++/* Define to 1 if you have the `richacl_set_file' function. */+/* #undef HAVE_RICHACL_SET_FILE */++/* Define to 1 if you have the <ripemd.h> header file. */+/* #undef HAVE_RIPEMD_H */++/* Define to 1 if you have the `select' function. */+#define HAVE_SELECT 1++/* Define to 1 if you have the `setenv' function. */+#define HAVE_SETENV 1++/* Define to 1 if you have the `setlocale' function. */+#define HAVE_SETLOCALE 1++/* Define to 1 if you have the `setxattr' function. */+/* #undef HAVE_SETXATTR */++/* Define to 1 if you have the <sha256.h> header file. */+/* #undef HAVE_SHA256_H */++/* Define to 1 if you have the <sha512.h> header file. */+/* #undef HAVE_SHA512_H */++/* Define to 1 if you have the <sha.h> header file. */+/* #undef HAVE_SHA_H */++/* Define to 1 if you have the `sigaction' function. */+#define HAVE_SIGACTION 1++/* Define to 1 if you have the <signal.h> header file. */+#define HAVE_SIGNAL_H 1++/* Define to 1 if you have the <spawn.h> header file. */+#define HAVE_SPAWN_H 1++/* Define to 1 if you have the `statfs' function. */+#define HAVE_STATFS 1++/* Define to 1 if you have the `statvfs' function. */+#define HAVE_STATVFS 1++/* Define to 1 if `stat' has the bug that it succeeds when given the+   zero-length file name argument. */+/* #undef HAVE_STAT_EMPTY_STRING_BUG */++/* Define to 1 if you have the <stdarg.h> header file. */+#define HAVE_STDARG_H 1++/* Define to 1 if you have the <stdint.h> header file. */+#define HAVE_STDINT_H 1++/* Define to 1 if you have the <stdlib.h> header file. */+#define HAVE_STDLIB_H 1++/* Define to 1 if you have the `strchr' function. */+#define HAVE_STRCHR 1++/* Define to 1 if you have the `strdup' function. */+#define HAVE_STRDUP 1++/* Define to 1 if you have the `strerror' function. */+#define HAVE_STRERROR 1++/* Define to 1 if you have the `strerror_r' function. */+#define HAVE_STRERROR_R 1++/* Define to 1 if you have the `strftime' function. */+#define HAVE_STRFTIME 1++/* Define to 1 if you have the <strings.h> header file. */+#define HAVE_STRINGS_H 1++/* Define to 1 if you have the <string.h> header file. */+#define HAVE_STRING_H 1++/* Define to 1 if you have the `strncpy_s' function. */+/* #undef HAVE_STRNCPY_S */++/* Define to 1 if you have the `strrchr' function. */+#define HAVE_STRRCHR 1++/* Define to 1 if the system has the type `struct richace'. */+/* #undef HAVE_STRUCT_RICHACE */++/* Define to 1 if the system has the type `struct richacl'. */+/* #undef HAVE_STRUCT_RICHACL */++/* Define to 1 if `f_namemax' is a member of `struct statfs'. */+/* #undef HAVE_STRUCT_STATFS_F_NAMEMAX */++/* Define to 1 if `f_iosize' is a member of `struct statvfs'. */+/* #undef HAVE_STRUCT_STATVFS_F_IOSIZE */++/* Define to 1 if `st_birthtime' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1++/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1++/* Define to 1 if `st_blksize' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1++/* Define to 1 if `st_flags' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_FLAGS 1++/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1++/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_MTIME_N */++/* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_MTIME_USEC */++/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC */++/* Define to 1 if `st_umtime' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_UMTIME */++/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */+#define HAVE_STRUCT_TM_TM_GMTOFF 1++/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */+/* #undef HAVE_STRUCT_TM___TM_GMTOFF */++/* Define to 1 if the system has the type `struct vfsconf'. */+#define HAVE_STRUCT_VFSCONF 1++/* Define to 1 if the system has the type `struct xvfsconf'. */+/* #undef HAVE_STRUCT_XVFSCONF */++/* Define to 1 if you have the `symlink' function. */+#define HAVE_SYMLINK 1++/* Define to 1 if you have the <sys/acl.h> header file. */++/* Define to 1 if you have the <sys/cdefs.h> header file. */+#define HAVE_SYS_CDEFS_H 1++/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.+   */+/* #undef HAVE_SYS_DIR_H */++/* Define to 1 if you have the <sys/ea.h> header file. */+/* #undef HAVE_SYS_EA_H */++/* Define to 1 if you have the <sys/extattr.h> header file. */+/* #undef HAVE_SYS_EXTATTR_H */++/* Define to 1 if you have the <sys/ioctl.h> header file. */+#define HAVE_SYS_IOCTL_H 1++/* Define to 1 if you have the <sys/mkdev.h> header file. */+/* #undef HAVE_SYS_MKDEV_H */++/* Define to 1 if you have the <sys/mount.h> header file. */+#define HAVE_SYS_MOUNT_H 1++/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.+   */+/* #undef HAVE_SYS_NDIR_H */++/* Define to 1 if you have the <sys/param.h> header file. */+#define HAVE_SYS_PARAM_H 1++/* Define to 1 if you have the <sys/poll.h> header file. */+#define HAVE_SYS_POLL_H 1++/* Define to 1 if you have the <sys/richacl.h> header file. */+/* #undef HAVE_SYS_RICHACL_H */++/* Define to 1 if you have the <sys/select.h> header file. */+#define HAVE_SYS_SELECT_H 1++/* Define to 1 if you have the <sys/statfs.h> header file. */+/* #undef HAVE_SYS_STATFS_H */++/* Define to 1 if you have the <sys/statvfs.h> header file. */+#define HAVE_SYS_STATVFS_H 1++/* Define to 1 if you have the <sys/stat.h> header file. */+#define HAVE_SYS_STAT_H 1++/* Define to 1 if you have the <sys/sysmacros.h> header file. */+/* #undef HAVE_SYS_SYSMACROS_H */++/* Define to 1 if you have the <sys/time.h> header file. */+#define HAVE_SYS_TIME_H 1++/* Define to 1 if you have the <sys/types.h> header file. */+#define HAVE_SYS_TYPES_H 1++/* Define to 1 if you have the <sys/utime.h> header file. */+/* #undef HAVE_SYS_UTIME_H */++/* Define to 1 if you have the <sys/utsname.h> header file. */+#define HAVE_SYS_UTSNAME_H 1++/* Define to 1 if you have the <sys/vfs.h> header file. */+/* #undef HAVE_SYS_VFS_H */++/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */+#define HAVE_SYS_WAIT_H 1++/* Define to 1 if you have the <sys/xattr.h> header file. */+#define HAVE_SYS_XATTR_H 1++/* Define to 1 if you have the `timegm' function. */+#define HAVE_TIMEGM 1++/* Define to 1 if you have the <time.h> header file. */+#define HAVE_TIME_H 1++/* Define to 1 if you have the `tzset' function. */+#define HAVE_TZSET 1++/* Define to 1 if the system has the type `uintmax_t'. */+#define HAVE_UINTMAX_T 1++/* Define to 1 if you have the <unistd.h> header file. */+#define HAVE_UNISTD_H 1++/* Define to 1 if you have the `unlinkat' function. */+#define HAVE_UNLINKAT 1++/* Define to 1 if you have the `unsetenv' function. */+#define HAVE_UNSETENV 1++/* Define to 1 if the system has the type `unsigned long long'. */+#define HAVE_UNSIGNED_LONG_LONG 1++/* Define to 1 if the system has the type `unsigned long long int'. */+#define HAVE_UNSIGNED_LONG_LONG_INT 1++/* Define to 1 if you have the `utime' function. */+#define HAVE_UTIME 1++/* Define to 1 if you have the `utimensat' function. */+#define HAVE_UTIMENSAT 1++/* Define to 1 if you have the `utimes' function. */+#define HAVE_UTIMES 1++/* Define to 1 if you have the <utime.h> header file. */+#define HAVE_UTIME_H 1++/* Define to 1 if you have the `vfork' function. */+#define HAVE_VFORK 1++/* Define to 1 if you have the `vprintf' function. */+#define HAVE_VPRINTF 1++/* Define to 1 if you have the <wchar.h> header file. */+#define HAVE_WCHAR_H 1++/* Define to 1 if the system has the type `wchar_t'. */+#define HAVE_WCHAR_T 1++/* Define to 1 if you have the `wcrtomb' function. */+#define HAVE_WCRTOMB 1++/* Define to 1 if you have the `wcscmp' function. */+#define HAVE_WCSCMP 1++/* Define to 1 if you have the `wcscpy' function. */+#define HAVE_WCSCPY 1++/* Define to 1 if you have the `wcslen' function. */+#define HAVE_WCSLEN 1++/* Define to 1 if you have the `wctomb' function. */+#define HAVE_WCTOMB 1++/* Define to 1 if you have the <wctype.h> header file. */+#define HAVE_WCTYPE_H 1++/* Define to 1 if you have the <wincrypt.h> header file. */+/* #undef HAVE_WINCRYPT_H */++/* Define to 1 if you have the <windows.h> header file. */+/* #undef HAVE_WINDOWS_H */++/* Define to 1 if you have the <winioctl.h> header file. */+/* #undef HAVE_WINIOCTL_H */++/* Define to 1 if you have the `wmemcmp' function. */+#define HAVE_WMEMCMP 1++/* Define to 1 if you have the `wmemcpy' function. */+#define HAVE_WMEMCPY 1++/* Define to 1 if you have the `wmemmove' function. */+#define HAVE_WMEMMOVE 1++/* Define to 1 if you have a working EXT2_IOC_GETFLAGS */+/* #undef HAVE_WORKING_EXT2_IOC_GETFLAGS */++/* Define to 1 if you have a working FS_IOC_GETFLAGS */+/* #undef HAVE_WORKING_FS_IOC_GETFLAGS */++/* Define to 1 if you have the <zlib.h> header file. */+/* #undef HAVE_ZLIB_H */++/* Define to 1 if you have the <zstd.h> header file. */+/* #undef HAVE_ZSTD_H */++/* Define to 1 if you have the `_ctime64_s' function. */+/* #undef HAVE__CTIME64_S */++/* Define to 1 if you have the `_fseeki64' function. */+/* #undef HAVE__FSEEKI64 */++/* Define to 1 if you have the `_get_timezone' function. */+/* #undef HAVE__GET_TIMEZONE */++/* Define to 1 if you have the `_gmtime64_s' function. */+/* #undef HAVE__GMTIME64_S */++/* Define to 1 if you have the `_localtime64_s' function. */+/* #undef HAVE__LOCALTIME64_S */++/* Define to 1 if you have the `_mkgmtime64' function. */+/* #undef HAVE__MKGMTIME64 */++/* Define as const if the declaration of iconv() needs const. */+/* #undef ICONV_CONST */++/* Version number of libarchive as a single integer */+#define LIBARCHIVE_VERSION_NUMBER "3004003"++/* Version number of libarchive */+#define LIBARCHIVE_VERSION_STRING "3.4.3"++/* Define to 1 if `lstat' dereferences a symlink specified with a trailing+   slash. */+/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */++/* Define to the sub-directory where libtool stores uninstalled libraries. */+#define LT_OBJDIR ".libs/"++/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.+   */+/* #undef MAJOR_IN_MKDEV */++/* Define to 1 if `major', `minor', and `makedev' are declared in+   <sysmacros.h>. */+/* #undef MAJOR_IN_SYSMACROS */++/* Define to '0x05020000' for Windows Server 2003 APIs. */+/* #undef NTDDI_VERSION */++/* Name of package */+#define PACKAGE "libarchive"++/* Define to the address where bug reports for this package should be sent. */+#define PACKAGE_BUGREPORT "libarchive-discuss@googlegroups.com"++/* Define to the full name of this package. */+#define PACKAGE_NAME "libarchive"++/* Define to the full name and version of this package. */+#define PACKAGE_STRING "libarchive 3.4.3"++/* Define to the one symbol short name of this package. */+#define PACKAGE_TARNAME "libarchive"++/* Define to the home page for this package. */+#define PACKAGE_URL ""++/* Define to the version of this package. */+#define PACKAGE_VERSION "3.4.3"++/* Define to 1 if PCRE_STATIC needs to be defined. */+/* #undef PCRE_STATIC */++/* The size of `wchar_t', as computed by sizeof. */+#define SIZEOF_WCHAR_T 4++/* Define to 1 if you have the ANSI C header files. */+#define STDC_HEADERS 1++/* Define to 1 if strerror_r returns char *. */+/* #undef STRERROR_R_CHAR_P */++/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */+#define TIME_WITH_SYS_TIME 1++/* Enable extensions on AIX 3, Interix.  */+#ifndef _ALL_SOURCE+# define _ALL_SOURCE 1+#endif+/* Enable GNU extensions on systems that have them.  */+#ifndef _GNU_SOURCE+# define _GNU_SOURCE 1+#endif+/* Enable threading extensions on Solaris.  */+#ifndef _POSIX_PTHREAD_SEMANTICS+# define _POSIX_PTHREAD_SEMANTICS 1+#endif+/* Enable extensions on HP NonStop.  */+#ifndef _TANDEM_SOURCE+# define _TANDEM_SOURCE 1+#endif+/* Enable general extensions on Solaris.  */+#ifndef __EXTENSIONS__+# define __EXTENSIONS__ 1+#endif+++/* Version number of package */+#define VERSION "3.4.3"++/* Define to '0x0502' for Windows Server 2003 APIs. */+/* #undef WINVER */++/* Enable large inode numbers on Mac OS X 10.5.  */+#ifndef _DARWIN_USE_64_BIT_INODE+# define _DARWIN_USE_64_BIT_INODE 1+#endif++/* Number of bits in a file offset, on hosts where this is settable. */+/* #undef _FILE_OFFSET_BITS */++/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */+/* #undef _LARGEFILE_SOURCE */++/* Define for large files, on AIX-style hosts. */+/* #undef _LARGE_FILES */++/* Define to 1 if on MINIX. */+/* #undef _MINIX */++/* Define to 2 if the system does not provide POSIX.1 features except with+   this defined. */+/* #undef _POSIX_1_SOURCE */++/* Define to 1 if you need to in order for `stat' and other things to work. */+/* #undef _POSIX_SOURCE */++/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the+   #define below would cause a syntax error. */+/* #undef _UINT32_T */++/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the+   #define below would cause a syntax error. */+/* #undef _UINT64_T */++/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the+   #define below would cause a syntax error. */+/* #undef _UINT8_T */++/* Define to '0x0502' for Windows Server 2003 APIs. */+/* #undef _WIN32_WINNT */++/* Define to empty if `const' does not conform to ANSI C. */+/* #undef const */++/* Define to match typeof st_gid field of struct stat if <sys/types.h> doesn't+   define. */+/* #undef gid_t */++/* Define to `unsigned long' if <sys/types.h> does not define. */+/* #undef id_t */++/* Define to the type of a signed integer type of width exactly 16 bits if+   such a type exists and the standard includes do not define it. */+/* #undef int16_t */++/* Define to the type of a signed integer type of width exactly 32 bits if+   such a type exists and the standard includes do not define it. */+/* #undef int32_t */++/* Define to the type of a signed integer type of width exactly 64 bits if+   such a type exists and the standard includes do not define it. */+/* #undef int64_t */++/* Define to the widest signed integer type if <stdint.h> and <inttypes.h> do+   not define. */+/* #undef intmax_t */++/* Define to `int' if <sys/types.h> does not define. */+/* #undef mode_t */++/* Define to `long long' if <sys/types.h> does not define. */+/* #undef off_t */++/* Define to `unsigned int' if <sys/types.h> does not define. */+/* #undef size_t */++/* Define to match typeof st_uid field of struct stat if <sys/types.h> doesn't+   define. */+/* #undef uid_t */++/* Define to the type of an unsigned integer type of width exactly 16 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint16_t */++/* Define to the type of an unsigned integer type of width exactly 32 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint32_t */++/* Define to the type of an unsigned integer type of width exactly 64 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint64_t */++/* Define to the type of an unsigned integer type of width exactly 8 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint8_t */++/* Define to the widest unsigned integer type if <stdint.h> and <inttypes.h>+   do not define. */+/* #undef uintmax_t */++/* Define to `unsigned int' if <sys/types.h> does not define. */+/* #undef uintptr_t */
+ c/autoconf-freebsd/config.h view
@@ -0,0 +1,1417 @@+/* config.h.  Generated from config.h.in by configure.  */+/* config.h.in.  Generated from configure.ac by autoheader.  */++/* Darwin ACL support */+/* #undef ARCHIVE_ACL_DARWIN */++/* FreeBSD ACL support */+/* #undef ARCHIVE_ACL_FREEBSD */++/* FreeBSD NFSv4 ACL support */+/* #undef ARCHIVE_ACL_FREEBSD_NFS4 */++/* Linux POSIX.1e ACL support via libacl */+/* #undef ARCHIVE_ACL_LIBACL */++/* Linux NFSv4 ACL support via librichacl */+/* #undef ARCHIVE_ACL_LIBRICHACL */++/* Solaris ACL support */+/* #undef ARCHIVE_ACL_SUNOS */++/* Solaris NFSv4 ACL support */+/* #undef ARCHIVE_ACL_SUNOS_NFS4 */++/* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_MD5_LIBC */++/* MD5 via ARCHIVE_CRYPTO_MD5_LIBMD supported. */++/* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */+/* #undef ARCHIVE_CRYPTO_MD5_LIBSYSTEM */++/* MD5 via ARCHIVE_CRYPTO_MD5_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_MD5_MBEDTLS */++/* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */++/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_MD5_OPENSSL */++/* MD5 via ARCHIVE_CRYPTO_MD5_WIN supported. */+/* #undef ARCHIVE_CRYPTO_MD5_WIN */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_LIBC */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBMD supported. */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_MBEDTLS */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_NETTLE */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_OPENSSL */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_LIBC */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBMD supported. */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_LIBSYSTEM */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_MBEDTLS */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_OPENSSL */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_WIN */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC2 supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC2 */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC3 */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBMD supported. */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBSYSTEM */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_MBEDTLS */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_OPENSSL */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_WIN */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC2 supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC2 */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC3 supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC3 */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_LIBSYSTEM */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_MBEDTLS */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_OPENSSL */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_WIN */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC2 supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC2 */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC3 */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBMD supported. */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBSYSTEM */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_MBEDTLS */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_OPENSSL */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_WIN */++/* AIX xattr support */+/* #undef ARCHIVE_XATTR_AIX */++/* Darwin xattr support */+/* #undef ARCHIVE_XATTR_DARWIN */++/* FreeBSD xattr support */+/* #undef ARCHIVE_XATTR_FREEBSD */++/* Linux xattr support */+/* #undef ARCHIVE_XATTR_LINUX */++/* Version number of bsdcat */+#define BSDCAT_VERSION_STRING "3.4.3"++/* Version number of bsdcpio */+#define BSDCPIO_VERSION_STRING "3.4.3"++/* Version number of bsdtar */+#define BSDTAR_VERSION_STRING "3.4.3"++/* Define to 1 if the system has the type `ace_t'. */+/* #undef HAVE_ACE_T */++/* Define to 1 if you have the `acl' function. */+/* #undef HAVE_ACL */++/* Define to 1 if the system has the type `aclent_t'. */+/* #undef HAVE_ACLENT_T */++/* Define to 1 if you have the `acl_add_flag_np' function. */+/* #undef HAVE_ACL_ADD_FLAG_NP */++/* Define to 1 if you have the `acl_add_perm' function. */+/* #undef HAVE_ACL_ADD_PERM */++/* Define to 1 if you have the `acl_clear_flags_np' function. */+/* #undef HAVE_ACL_CLEAR_FLAGS_NP */++/* Define to 1 if you have the `acl_clear_perms' function. */+/* #undef HAVE_ACL_CLEAR_PERMS */++/* Define to 1 if you have the `acl_create_entry' function. */+/* #undef HAVE_ACL_CREATE_ENTRY */++/* Define to 1 if you have the `acl_delete_def_file' function. */+/* #undef HAVE_ACL_DELETE_DEF_FILE */++/* Define to 1 if the system has the type `acl_entry_t'. */+/* #undef HAVE_ACL_ENTRY_T */++/* Define to 1 if you have the `acl_free' function. */+/* #undef HAVE_ACL_FREE */++/* Define to 1 if you have the `acl_get_brand_np' function. */+/* #undef HAVE_ACL_GET_BRAND_NP */++/* Define to 1 if you have the `acl_get_entry' function. */+/* #undef HAVE_ACL_GET_ENTRY */++/* Define to 1 if you have the `acl_get_entry_type_np' function. */+/* #undef HAVE_ACL_GET_ENTRY_TYPE_NP */++/* Define to 1 if you have the `acl_get_fd' function. */+/* #undef HAVE_ACL_GET_FD */++/* Define to 1 if you have the `acl_get_fd_np' function. */+/* #undef HAVE_ACL_GET_FD_NP */++/* Define to 1 if you have the `acl_get_file' function. */+/* #undef HAVE_ACL_GET_FILE */++/* Define to 1 if you have the `acl_get_flagset_np' function. */+/* #undef HAVE_ACL_GET_FLAGSET_NP */++/* Define to 1 if you have the `acl_get_flag_np' function. */+/* #undef HAVE_ACL_GET_FLAG_NP */++/* Define to 1 if you have the `acl_get_link_np' function. */+/* #undef HAVE_ACL_GET_LINK_NP */++/* Define to 1 if you have the `acl_get_perm' function. */+/* #undef HAVE_ACL_GET_PERM */++/* Define to 1 if you have the `acl_get_permset' function. */+/* #undef HAVE_ACL_GET_PERMSET */++/* Define to 1 if you have the `acl_get_perm_np' function. */+/* #undef HAVE_ACL_GET_PERM_NP */++/* Define to 1 if you have the `acl_get_qualifier' function. */+/* #undef HAVE_ACL_GET_QUALIFIER */++/* Define to 1 if you have the `acl_get_tag_type' function. */+/* #undef HAVE_ACL_GET_TAG_TYPE */++/* Define to 1 if you have the `acl_init' function. */+/* #undef HAVE_ACL_INIT */++/* Define to 1 if you have the `acl_is_trivial_np' function. */+/* #undef HAVE_ACL_IS_TRIVIAL_NP */++/* Define to 1 if you have the <acl/libacl.h> header file. */+/* #undef HAVE_ACL_LIBACL_H */++/* Define to 1 if the system has the type `acl_permset_t'. */+/* #undef HAVE_ACL_PERMSET_T */++/* Define to 1 if you have the `acl_set_entry_type_np' function. */+/* #undef HAVE_ACL_SET_ENTRY_TYPE_NP */++/* Define to 1 if you have the `acl_set_fd' function. */+/* #undef HAVE_ACL_SET_FD */++/* Define to 1 if you have the `acl_set_fd_np' function. */+/* #undef HAVE_ACL_SET_FD_NP */++/* Define to 1 if you have the `acl_set_file' function. */+/* #undef HAVE_ACL_SET_FILE */++/* Define to 1 if you have the `acl_set_link_np' function. */+/* #undef HAVE_ACL_SET_LINK_NP */++/* Define to 1 if you have the `acl_set_qualifier' function. */+/* #undef HAVE_ACL_SET_QUALIFIER */++/* Define to 1 if you have the `acl_set_tag_type' function. */+/* #undef HAVE_ACL_SET_TAG_TYPE */++/* Define to 1 if the system has the type `acl_t'. */+/* #undef HAVE_ACL_T */++/* Define to 1 if the system has the type `acl_tag_t'. */+/* #undef HAVE_ACL_TAG_T */++/* Define to 1 if you have the `arc4random_buf' function. */+#define HAVE_ARC4RANDOM_BUF 1++/* Define to 1 if you have the <attr/xattr.h> header file. */+/* #undef HAVE_ATTR_XATTR_H */++/* Define to 1 if you have the <bcrypt.h> header file. */+/* #undef HAVE_BCRYPT_H */++/* Define to 1 if you have the <blake2.h> header file. */+/* #undef HAVE_BLAKE2_H */++/* Define to 1 if you have the <bzlib.h> header file. */+/* #undef HAVE_BZLIB_H */++/* Define to 1 if you have the `chflags' function. */+#define HAVE_CHFLAGS 1++/* Define to 1 if you have the `chown' function. */+#define HAVE_CHOWN 1++/* Define to 1 if you have the `chroot' function. */+#define HAVE_CHROOT 1++/* Define to 1 if you have the <copyfile.h> header file. */+/* #undef HAVE_COPYFILE_H */++/* Define to 1 if you have the `ctime_r' function. */+#define HAVE_CTIME_R 1++/* Define to 1 if you have the <ctype.h> header file. */+#define HAVE_CTYPE_H 1++/* Define to 1 if you have the `cygwin_conv_path' function. */+/* #undef HAVE_CYGWIN_CONV_PATH */++/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACE_GETACL */++/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACE_GETACLCNT */++/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACE_SETACL */++/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if+   you don't. */+/* #undef HAVE_DECL_ACL_SYNCHRONIZE */++/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if+   you don't. */+/* #undef HAVE_DECL_ACL_TYPE_EXTENDED */++/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACL_TYPE_NFS4 */++/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACL_USER */++/* Define to 1 if you have the declaration of `EXTATTR_NAMESPACE_USER', and to+   0 if you don't. */+/* #undef HAVE_DECL_EXTATTR_NAMESPACE_USER */++/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.+   */+/* #undef HAVE_DECL_GETACL */++/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you+   don't. */+/* #undef HAVE_DECL_GETACLCNT */++/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_INT32_MAX 1++/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you+   don't. */+#define HAVE_DECL_INT32_MIN 1++/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_INT64_MAX 1++/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you+   don't. */+#define HAVE_DECL_INT64_MIN 1++/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_INTMAX_MAX 1++/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you+   don't. */+#define HAVE_DECL_INTMAX_MIN 1++/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.+   */+/* #undef HAVE_DECL_SETACL */++/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_SIZE_MAX 1++/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_SSIZE_MAX 1++/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you+   don't. */+#define HAVE_DECL_STRERROR_R 1++/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_UINT32_MAX 1++/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_UINT64_MAX 1++/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_UINTMAX_MAX 1++/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if+   you don't. */+/* #undef HAVE_DECL_XATTR_NOFOLLOW */++/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.+   */+#define HAVE_DIRENT_H 1++/* Define to 1 if you have a dirfd function or macro */+#define HAVE_DIRFD 1++/* Define to 1 if you have the <dlfcn.h> header file. */+#define HAVE_DLFCN_H 1++/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */+/* #undef HAVE_DOPRNT */++/* Define to 1 if nl_langinfo supports D_MD_ORDER */+#define HAVE_D_MD_ORDER 1++/* A possible errno value for invalid file format errors */+#define HAVE_EFTYPE 1++/* A possible errno value for invalid file format errors */+#define HAVE_EILSEQ 1++/* Define to 1 if you have the <errno.h> header file. */+#define HAVE_ERRNO_H 1++/* Define to 1 if you have the <expat.h> header file. */+/* #undef HAVE_EXPAT_H */++/* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */+/* #undef HAVE_EXT2FS_EXT2_FS_H */++/* Define to 1 if you have the `extattr_get_fd' function. */+/* #undef HAVE_EXTATTR_GET_FD */++/* Define to 1 if you have the `extattr_get_file' function. */+/* #undef HAVE_EXTATTR_GET_FILE */++/* Define to 1 if you have the `extattr_get_link' function. */+/* #undef HAVE_EXTATTR_GET_LINK */++/* Define to 1 if you have the `extattr_list_fd' function. */+/* #undef HAVE_EXTATTR_LIST_FD */++/* Define to 1 if you have the `extattr_list_file' function. */+/* #undef HAVE_EXTATTR_LIST_FILE */++/* Define to 1 if you have the `extattr_list_link' function. */+/* #undef HAVE_EXTATTR_LIST_LINK */++/* Define to 1 if you have the `extattr_set_fd' function. */+/* #undef HAVE_EXTATTR_SET_FD */++/* Define to 1 if you have the `extattr_set_link' function. */+/* #undef HAVE_EXTATTR_SET_LINK */++/* Define to 1 if you have the `facl' function. */+/* #undef HAVE_FACL */++/* Define to 1 if you have the `fchdir' function. */+#define HAVE_FCHDIR 1++/* Define to 1 if you have the `fchflags' function. */+#define HAVE_FCHFLAGS 1++/* Define to 1 if you have the `fchmod' function. */+#define HAVE_FCHMOD 1++/* Define to 1 if you have the `fchown' function. */+#define HAVE_FCHOWN 1++/* Define to 1 if you have the `fcntl' function. */+#define HAVE_FCNTL 1++/* Define to 1 if you have the <fcntl.h> header file. */+#define HAVE_FCNTL_H 1++/* Define to 1 if you have the `fdopendir' function. */+#define HAVE_FDOPENDIR 1++/* Define to 1 if you have the `fgetea' function. */+/* #undef HAVE_FGETEA */++/* Define to 1 if you have the `fgetxattr' function. */+/* #undef HAVE_FGETXATTR */++/* Define to 1 if you have the `flistea' function. */+/* #undef HAVE_FLISTEA */++/* Define to 1 if you have the `flistxattr' function. */+/* #undef HAVE_FLISTXATTR */++/* Define to 1 if you have the `fork' function. */+#define HAVE_FORK 1++/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */+#define HAVE_FSEEKO 1++/* Define to 1 if you have the `fsetea' function. */+/* #undef HAVE_FSETEA */++/* Define to 1 if you have the `fsetxattr' function. */+/* #undef HAVE_FSETXATTR */++/* Define to 1 if you have the `fstat' function. */+#define HAVE_FSTAT 1++/* Define to 1 if you have the `fstatat' function. */+#define HAVE_FSTATAT 1++/* Define to 1 if you have the `fstatfs' function. */+#define HAVE_FSTATFS 1++/* Define to 1 if you have the `fstatvfs' function. */+#define HAVE_FSTATVFS 1++/* Define to 1 if you have the `ftruncate' function. */+#define HAVE_FTRUNCATE 1++/* Define to 1 if you have the `futimens' function. */+#define HAVE_FUTIMENS 1++/* Define to 1 if you have the `futimes' function. */+#define HAVE_FUTIMES 1++/* Define to 1 if you have the `futimesat' function. */+#define HAVE_FUTIMESAT 1++/* Define to 1 if you have the `getea' function. */+/* #undef HAVE_GETEA */++/* Define to 1 if you have the `geteuid' function. */+#define HAVE_GETEUID 1++/* Define to 1 if you have the `getgrgid_r' function. */+#define HAVE_GETGRGID_R 1++/* Define to 1 if you have the `getgrnam_r' function. */+#define HAVE_GETGRNAM_R 1++/* Define to 1 if you have the `getpid' function. */+#define HAVE_GETPID 1++/* Define to 1 if you have the `getpwnam_r' function. */+#define HAVE_GETPWNAM_R 1++/* Define to 1 if you have the `getpwuid_r' function. */+#define HAVE_GETPWUID_R 1++/* Define to 1 if you have the `getvfsbyname' function. */+#define HAVE_GETVFSBYNAME 1++/* Define to 1 if you have the `getxattr' function. */+/* #undef HAVE_GETXATTR */++/* Define to 1 if you have the `gmtime_r' function. */+#define HAVE_GMTIME_R 1++/* Define to 1 if you have the <grp.h> header file. */+#define HAVE_GRP_H 1++/* Define if you have the iconv() function and it works. */+/* #undef HAVE_ICONV */++/* Define to 1 if you have the <iconv.h> header file. */+/* #undef HAVE_ICONV_H */++/* Define to 1 if the system has the type `intmax_t'. */+#define HAVE_INTMAX_T 1++/* Define to 1 if you have the <inttypes.h> header file. */+#define HAVE_INTTYPES_H 1++/* Define to 1 if you have the <io.h> header file. */+/* #undef HAVE_IO_H */++/* Define to 1 if you have the <langinfo.h> header file. */+#define HAVE_LANGINFO_H 1++/* Define to 1 if you have the `lchflags' function. */+#define HAVE_LCHFLAGS 1++/* Define to 1 if you have the `lchmod' function. */+#define HAVE_LCHMOD 1++/* Define to 1 if you have the `lchown' function. */+#define HAVE_LCHOWN 1++/* Define to 1 if you have the `lgetea' function. */+/* #undef HAVE_LGETEA */++/* Define to 1 if you have the `lgetxattr' function. */+/* #undef HAVE_LGETXATTR */++/* Define to 1 if you have the `acl' library (-lacl). */+/* #undef HAVE_LIBACL */++/* Define to 1 if you have the `b2' library (-lb2). */+/* #undef HAVE_LIBB2 */++/* Define to 1 if you have the `bz2' library (-lbz2). */+/* #undef HAVE_LIBBZ2 */++/* Define to 1 if you have the `charset' library (-lcharset). */+/* #undef HAVE_LIBCHARSET */++/* Define to 1 if you have the `crypto' library (-lcrypto). */+/* #undef HAVE_LIBCRYPTO */++/* Define to 1 if you have the `eay32' library (-leay32). */+/* #undef HAVE_LIBEAY32 */++/* Define to 1 if you have the `eay64' library (-leay64). */+/* #undef HAVE_LIBEAY64 */++/* Define to 1 if you have the `expat' library (-lexpat). */+/* #undef HAVE_LIBEXPAT */++/* Define to 1 if you have the `lz4' library (-llz4). */+/* #undef HAVE_LIBLZ4 */++/* Define to 1 if you have the `lzma' library (-llzma). */+/* #undef HAVE_LIBLZMA */++/* Define to 1 if you have the `lzo2' library (-llzo2). */+/* #undef HAVE_LIBLZO2 */++/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */+/* #undef HAVE_LIBMBEDCRYPTO */++/* Define to 1 if you have the `md' library (-lmd). */++/* Define to 1 if you have the `nettle' library (-lnettle). */+/* #undef HAVE_LIBNETTLE */++/* Define to 1 if you have the `pcre' library (-lpcre). */+/* #undef HAVE_LIBPCRE */++/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */+/* #undef HAVE_LIBPCREPOSIX */++/* Define to 1 if you have the `regex' library (-lregex). */+/* #undef HAVE_LIBREGEX */++/* Define to 1 if you have the `richacl' library (-lrichacl). */+/* #undef HAVE_LIBRICHACL */++/* Define to 1 if you have the `xml2' library (-lxml2). */+/* #undef HAVE_LIBXML2 */++/* Define to 1 if you have the <libxml/xmlreader.h> header file. */+/* #undef HAVE_LIBXML_XMLREADER_H */++/* Define to 1 if you have the <libxml/xmlwriter.h> header file. */+/* #undef HAVE_LIBXML_XMLWRITER_H */++/* Define to 1 if you have the `z' library (-lz). */+/* #undef HAVE_LIBZ */++/* Define to 1 if you have the `zstd' library (-lzstd). */+/* #undef HAVE_LIBZSTD */++/* Define to 1 if you have the <limits.h> header file. */+#define HAVE_LIMITS_H 1++/* Define to 1 if you have the `link' function. */+#define HAVE_LINK 1++/* Define to 1 if you have the <linux/fiemap.h> header file. */+/* #undef HAVE_LINUX_FIEMAP_H */++/* Define to 1 if you have the <linux/fs.h> header file. */+/* #undef HAVE_LINUX_FS_H */++/* Define to 1 if you have the <linux/magic.h> header file. */+/* #undef HAVE_LINUX_MAGIC_H */++/* Define to 1 if you have the <linux/types.h> header file. */+/* #undef HAVE_LINUX_TYPES_H */++/* Define to 1 if you have the `listea' function. */+/* #undef HAVE_LISTEA */++/* Define to 1 if you have the `listxattr' function. */+/* #undef HAVE_LISTXATTR */++/* Define to 1 if you have the `llistea' function. */+/* #undef HAVE_LLISTEA */++/* Define to 1 if you have the `llistxattr' function. */+/* #undef HAVE_LLISTXATTR */++/* Define to 1 if you have the <localcharset.h> header file. */+/* #undef HAVE_LOCALCHARSET_H */++/* Define to 1 if you have the `locale_charset' function. */+/* #undef HAVE_LOCALE_CHARSET */++/* Define to 1 if you have the <locale.h> header file. */+#define HAVE_LOCALE_H 1++/* Define to 1 if you have the `localtime_r' function. */+#define HAVE_LOCALTIME_R 1++/* Define to 1 if the system has the type `long long int'. */+#define HAVE_LONG_LONG_INT 1++/* Define to 1 if you have the `lsetea' function. */+/* #undef HAVE_LSETEA */++/* Define to 1 if you have the `lsetxattr' function. */+/* #undef HAVE_LSETXATTR */++/* Define to 1 if you have the `lstat' function. */+#define HAVE_LSTAT 1++/* Define to 1 if `lstat' has the bug that it succeeds when given the+   zero-length file name argument. */+/* #undef HAVE_LSTAT_EMPTY_STRING_BUG */++/* Define to 1 if you have the `lutimes' function. */+#define HAVE_LUTIMES 1++/* Define to 1 if you have the <lz4hc.h> header file. */+/* #undef HAVE_LZ4HC_H */++/* Define to 1 if you have the <lz4.h> header file. */+/* #undef HAVE_LZ4_H */++/* Define to 1 if you have the <lzma.h> header file. */+/* #undef HAVE_LZMA_H */++/* Define to 1 if you have the `lzma_stream_encoder_mt' function. */+/* #undef HAVE_LZMA_STREAM_ENCODER_MT */++/* Define to 1 if you have the <lzo/lzo1x.h> header file. */+/* #undef HAVE_LZO_LZO1X_H */++/* Define to 1 if you have the <lzo/lzoconf.h> header file. */+/* #undef HAVE_LZO_LZOCONF_H */++/* Define to 1 if you have the <mbedtls/aes.h> header file. */+/* #undef HAVE_MBEDTLS_AES_H */++/* Define to 1 if you have the <mbedtls/md.h> header file. */+/* #undef HAVE_MBEDTLS_MD_H */++/* Define to 1 if you have the <mbedtls/pkcs5.h> header file. */+/* #undef HAVE_MBEDTLS_PKCS5_H */++/* Define to 1 if you have the `mbrtowc' function. */+#define HAVE_MBRTOWC 1++/* Define to 1 if you have the `mbr_gid_to_uuid' function. */+/* #undef HAVE_MBR_GID_TO_UUID */++/* Define to 1 if you have the `mbr_uid_to_uuid' function. */+/* #undef HAVE_MBR_UID_TO_UUID */++/* Define to 1 if you have the `mbr_uuid_to_id' function. */+/* #undef HAVE_MBR_UUID_TO_ID */++/* Define to 1 if you have the <md5.h> header file. */++/* Define to 1 if you have the <membership.h> header file. */+/* #undef HAVE_MEMBERSHIP_H */++/* Define to 1 if you have the `memmove' function. */+#define HAVE_MEMMOVE 1++/* Define to 1 if you have the <memory.h> header file. */+#define HAVE_MEMORY_H 1++/* Define to 1 if you have the `memset' function. */+#define HAVE_MEMSET 1++/* Define to 1 if you have the `mkdir' function. */+#define HAVE_MKDIR 1++/* Define to 1 if you have the `mkfifo' function. */+#define HAVE_MKFIFO 1++/* Define to 1 if you have the `mknod' function. */+#define HAVE_MKNOD 1++/* Define to 1 if you have the `mkstemp' function. */+#define HAVE_MKSTEMP 1++/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */+/* #undef HAVE_NDIR_H */++/* Define to 1 if you have the <nettle/aes.h> header file. */+/* #undef HAVE_NETTLE_AES_H */++/* Define to 1 if you have the <nettle/hmac.h> header file. */+/* #undef HAVE_NETTLE_HMAC_H */++/* Define to 1 if you have the <nettle/md5.h> header file. */+/* #undef HAVE_NETTLE_MD5_H */++/* Define to 1 if you have the <nettle/pbkdf2.h> header file. */+/* #undef HAVE_NETTLE_PBKDF2_H */++/* Define to 1 if you have the <nettle/ripemd160.h> header file. */+/* #undef HAVE_NETTLE_RIPEMD160_H */++/* Define to 1 if you have the <nettle/sha.h> header file. */+/* #undef HAVE_NETTLE_SHA_H */++/* Define to 1 if you have the `nl_langinfo' function. */+#define HAVE_NL_LANGINFO 1++/* Define to 1 if you have the `openat' function. */+#define HAVE_OPENAT 1++/* Define to 1 if you have the <openssl/evp.h> header file. */+/* #undef HAVE_OPENSSL_EVP_H */++/* Define to 1 if you have the <paths.h> header file. */+#define HAVE_PATHS_H 1++/* Define to 1 if you have the <pcreposix.h> header file. */+/* #undef HAVE_PCREPOSIX_H */++/* Define to 1 if you have the `pipe' function. */+#define HAVE_PIPE 1++/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */+/* #undef HAVE_PKCS5_PBKDF2_HMAC_SHA1 */++/* Define to 1 if you have the `poll' function. */+#define HAVE_POLL 1++/* Define to 1 if you have the <poll.h> header file. */+#define HAVE_POLL_H 1++/* Define to 1 if you have the `posix_spawnp' function. */+#define HAVE_POSIX_SPAWNP 1++/* Define to 1 if you have the <pthread.h> header file. */+#define HAVE_PTHREAD_H 1++/* Define to 1 if you have the <pwd.h> header file. */+#define HAVE_PWD_H 1++/* Define to 1 if you have a POSIX compatible readdir_r */+#define HAVE_READDIR_R 1++/* Define to 1 if you have the `readlink' function. */+#define HAVE_READLINK 1++/* Define to 1 if you have the `readlinkat' function. */+#define HAVE_READLINKAT 1++/* Define to 1 if you have the `readpassphrase' function. */+#define HAVE_READPASSPHRASE 1++/* Define to 1 if you have the <readpassphrase.h> header file. */+#define HAVE_READPASSPHRASE_H 1++/* Define to 1 if you have the <regex.h> header file. */+/* #undef HAVE_REGEX_H */++/* Define to 1 if you have the `richacl_alloc' function. */+/* #undef HAVE_RICHACL_ALLOC */++/* Define to 1 if you have the `richacl_equiv_mode' function. */+/* #undef HAVE_RICHACL_EQUIV_MODE */++/* Define to 1 if you have the `richacl_free' function. */+/* #undef HAVE_RICHACL_FREE */++/* Define to 1 if you have the `richacl_get_fd' function. */+/* #undef HAVE_RICHACL_GET_FD */++/* Define to 1 if you have the `richacl_get_file' function. */+/* #undef HAVE_RICHACL_GET_FILE */++/* Define to 1 if you have the `richacl_set_fd' function. */+/* #undef HAVE_RICHACL_SET_FD */++/* Define to 1 if you have the `richacl_set_file' function. */+/* #undef HAVE_RICHACL_SET_FILE */++/* Define to 1 if you have the <ripemd.h> header file. */++/* Define to 1 if you have the `select' function. */+#define HAVE_SELECT 1++/* Define to 1 if you have the `setenv' function. */+#define HAVE_SETENV 1++/* Define to 1 if you have the `setlocale' function. */+#define HAVE_SETLOCALE 1++/* Define to 1 if you have the `setxattr' function. */+/* #undef HAVE_SETXATTR */++/* Define to 1 if you have the <sha256.h> header file. */++/* Define to 1 if you have the <sha512.h> header file. */++/* Define to 1 if you have the <sha.h> header file. */++/* Define to 1 if you have the `sigaction' function. */+#define HAVE_SIGACTION 1++/* Define to 1 if you have the <signal.h> header file. */+#define HAVE_SIGNAL_H 1++/* Define to 1 if you have the <spawn.h> header file. */+#define HAVE_SPAWN_H 1++/* Define to 1 if you have the `statfs' function. */+#define HAVE_STATFS 1++/* Define to 1 if you have the `statvfs' function. */+#define HAVE_STATVFS 1++/* Define to 1 if `stat' has the bug that it succeeds when given the+   zero-length file name argument. */+/* #undef HAVE_STAT_EMPTY_STRING_BUG */++/* Define to 1 if you have the <stdarg.h> header file. */+#define HAVE_STDARG_H 1++/* Define to 1 if you have the <stdint.h> header file. */+#define HAVE_STDINT_H 1++/* Define to 1 if you have the <stdlib.h> header file. */+#define HAVE_STDLIB_H 1++/* Define to 1 if you have the `strchr' function. */+#define HAVE_STRCHR 1++/* Define to 1 if you have the `strdup' function. */+#define HAVE_STRDUP 1++/* Define to 1 if you have the `strerror' function. */+#define HAVE_STRERROR 1++/* Define to 1 if you have the `strerror_r' function. */+#define HAVE_STRERROR_R 1++/* Define to 1 if you have the `strftime' function. */+#define HAVE_STRFTIME 1++/* Define to 1 if you have the <strings.h> header file. */+#define HAVE_STRINGS_H 1++/* Define to 1 if you have the <string.h> header file. */+#define HAVE_STRING_H 1++/* Define to 1 if you have the `strncpy_s' function. */+/* #undef HAVE_STRNCPY_S */++/* Define to 1 if you have the `strrchr' function. */+#define HAVE_STRRCHR 1++/* Define to 1 if the system has the type `struct richace'. */+/* #undef HAVE_STRUCT_RICHACE */++/* Define to 1 if the system has the type `struct richacl'. */+/* #undef HAVE_STRUCT_RICHACL */++/* Define to 1 if `f_namemax' is a member of `struct statfs'. */+#define HAVE_STRUCT_STATFS_F_NAMEMAX 1++/* Define to 1 if `f_iosize' is a member of `struct statvfs'. */+/* #undef HAVE_STRUCT_STATVFS_F_IOSIZE */++/* Define to 1 if `st_birthtime' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1++/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1++/* Define to 1 if `st_blksize' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1++/* Define to 1 if `st_flags' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_FLAGS 1++/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1++/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_MTIME_N */++/* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_MTIME_USEC */++/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1++/* Define to 1 if `st_umtime' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_UMTIME */++/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */+#define HAVE_STRUCT_TM_TM_GMTOFF 1++/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */+/* #undef HAVE_STRUCT_TM___TM_GMTOFF */++/* Define to 1 if the system has the type `struct vfsconf'. */+#define HAVE_STRUCT_VFSCONF 1++/* Define to 1 if the system has the type `struct xvfsconf'. */+#define HAVE_STRUCT_XVFSCONF 1++/* Define to 1 if you have the `symlink' function. */+#define HAVE_SYMLINK 1++/* Define to 1 if you have the <sys/acl.h> header file. */++/* Define to 1 if you have the <sys/cdefs.h> header file. */+#define HAVE_SYS_CDEFS_H 1++/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.+   */+/* #undef HAVE_SYS_DIR_H */++/* Define to 1 if you have the <sys/ea.h> header file. */+/* #undef HAVE_SYS_EA_H */++/* Define to 1 if you have the <sys/extattr.h> header file. */+#define HAVE_SYS_EXTATTR_H 1++/* Define to 1 if you have the <sys/ioctl.h> header file. */+#define HAVE_SYS_IOCTL_H 1++/* Define to 1 if you have the <sys/mkdev.h> header file. */+/* #undef HAVE_SYS_MKDEV_H */++/* Define to 1 if you have the <sys/mount.h> header file. */+#define HAVE_SYS_MOUNT_H 1++/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.+   */+/* #undef HAVE_SYS_NDIR_H */++/* Define to 1 if you have the <sys/param.h> header file. */+#define HAVE_SYS_PARAM_H 1++/* Define to 1 if you have the <sys/poll.h> header file. */+#define HAVE_SYS_POLL_H 1++/* Define to 1 if you have the <sys/richacl.h> header file. */+/* #undef HAVE_SYS_RICHACL_H */++/* Define to 1 if you have the <sys/select.h> header file. */+#define HAVE_SYS_SELECT_H 1++/* Define to 1 if you have the <sys/statfs.h> header file. */+/* #undef HAVE_SYS_STATFS_H */++/* Define to 1 if you have the <sys/statvfs.h> header file. */+#define HAVE_SYS_STATVFS_H 1++/* Define to 1 if you have the <sys/stat.h> header file. */+#define HAVE_SYS_STAT_H 1++/* Define to 1 if you have the <sys/sysmacros.h> header file. */+/* #undef HAVE_SYS_SYSMACROS_H */++/* Define to 1 if you have the <sys/time.h> header file. */+#define HAVE_SYS_TIME_H 1++/* Define to 1 if you have the <sys/types.h> header file. */+#define HAVE_SYS_TYPES_H 1++/* Define to 1 if you have the <sys/utime.h> header file. */+/* #undef HAVE_SYS_UTIME_H */++/* Define to 1 if you have the <sys/utsname.h> header file. */+#define HAVE_SYS_UTSNAME_H 1++/* Define to 1 if you have the <sys/vfs.h> header file. */+/* #undef HAVE_SYS_VFS_H */++/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */+#define HAVE_SYS_WAIT_H 1++/* Define to 1 if you have the <sys/xattr.h> header file. */+/* #undef HAVE_SYS_XATTR_H */++/* Define to 1 if you have the `timegm' function. */+#define HAVE_TIMEGM 1++/* Define to 1 if you have the <time.h> header file. */+#define HAVE_TIME_H 1++/* Define to 1 if you have the `tzset' function. */+#define HAVE_TZSET 1++/* Define to 1 if the system has the type `uintmax_t'. */+#define HAVE_UINTMAX_T 1++/* Define to 1 if you have the <unistd.h> header file. */+#define HAVE_UNISTD_H 1++/* Define to 1 if you have the `unlinkat' function. */+#define HAVE_UNLINKAT 1++/* Define to 1 if you have the `unsetenv' function. */+#define HAVE_UNSETENV 1++/* Define to 1 if the system has the type `unsigned long long'. */+#define HAVE_UNSIGNED_LONG_LONG 1++/* Define to 1 if the system has the type `unsigned long long int'. */+#define HAVE_UNSIGNED_LONG_LONG_INT 1++/* Define to 1 if you have the `utime' function. */+#define HAVE_UTIME 1++/* Define to 1 if you have the `utimensat' function. */+#define HAVE_UTIMENSAT 1++/* Define to 1 if you have the `utimes' function. */+#define HAVE_UTIMES 1++/* Define to 1 if you have the <utime.h> header file. */+#define HAVE_UTIME_H 1++/* Define to 1 if you have the `vfork' function. */+#define HAVE_VFORK 1++/* Define to 1 if you have the `vprintf' function. */+#define HAVE_VPRINTF 1++/* Define to 1 if you have the <wchar.h> header file. */+#define HAVE_WCHAR_H 1++/* Define to 1 if the system has the type `wchar_t'. */+#define HAVE_WCHAR_T 1++/* Define to 1 if you have the `wcrtomb' function. */+#define HAVE_WCRTOMB 1++/* Define to 1 if you have the `wcscmp' function. */+#define HAVE_WCSCMP 1++/* Define to 1 if you have the `wcscpy' function. */+#define HAVE_WCSCPY 1++/* Define to 1 if you have the `wcslen' function. */+#define HAVE_WCSLEN 1++/* Define to 1 if you have the `wctomb' function. */+#define HAVE_WCTOMB 1++/* Define to 1 if you have the <wctype.h> header file. */+#define HAVE_WCTYPE_H 1++/* Define to 1 if you have the <wincrypt.h> header file. */+/* #undef HAVE_WINCRYPT_H */++/* Define to 1 if you have the <windows.h> header file. */+/* #undef HAVE_WINDOWS_H */++/* Define to 1 if you have the <winioctl.h> header file. */+/* #undef HAVE_WINIOCTL_H */++/* Define to 1 if you have the `wmemcmp' function. */+#define HAVE_WMEMCMP 1++/* Define to 1 if you have the `wmemcpy' function. */+#define HAVE_WMEMCPY 1++/* Define to 1 if you have the `wmemmove' function. */+#define HAVE_WMEMMOVE 1++/* Define to 1 if you have a working EXT2_IOC_GETFLAGS */+/* #undef HAVE_WORKING_EXT2_IOC_GETFLAGS */++/* Define to 1 if you have a working FS_IOC_GETFLAGS */+/* #undef HAVE_WORKING_FS_IOC_GETFLAGS */++/* Define to 1 if you have the <zlib.h> header file. */+/* #undef HAVE_ZLIB_H */++/* Define to 1 if you have the <zstd.h> header file. */+/* #undef HAVE_ZSTD_H */++/* Define to 1 if you have the `_ctime64_s' function. */+/* #undef HAVE__CTIME64_S */++/* Define to 1 if you have the `_fseeki64' function. */+/* #undef HAVE__FSEEKI64 */++/* Define to 1 if you have the `_get_timezone' function. */+/* #undef HAVE__GET_TIMEZONE */++/* Define to 1 if you have the `_gmtime64_s' function. */+/* #undef HAVE__GMTIME64_S */++/* Define to 1 if you have the `_localtime64_s' function. */+/* #undef HAVE__LOCALTIME64_S */++/* Define to 1 if you have the `_mkgmtime64' function. */+/* #undef HAVE__MKGMTIME64 */++/* Define as const if the declaration of iconv() needs const. */+/* #undef ICONV_CONST */++/* Version number of libarchive as a single integer */+#define LIBARCHIVE_VERSION_NUMBER "3004003"++/* Version number of libarchive */+#define LIBARCHIVE_VERSION_STRING "3.4.3"++/* Define to 1 if `lstat' dereferences a symlink specified with a trailing+   slash. */+#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1++/* Define to the sub-directory where libtool stores uninstalled libraries. */+#define LT_OBJDIR ".libs/"++/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.+   */+/* #undef MAJOR_IN_MKDEV */++/* Define to 1 if `major', `minor', and `makedev' are declared in+   <sysmacros.h>. */+/* #undef MAJOR_IN_SYSMACROS */++/* Define to '0x05020000' for Windows Server 2003 APIs. */+/* #undef NTDDI_VERSION */++/* Name of package */+#define PACKAGE "libarchive"++/* Define to the address where bug reports for this package should be sent. */+#define PACKAGE_BUGREPORT "libarchive-discuss@googlegroups.com"++/* Define to the full name of this package. */+#define PACKAGE_NAME "libarchive"++/* Define to the full name and version of this package. */+#define PACKAGE_STRING "libarchive 3.4.3"++/* Define to the one symbol short name of this package. */+#define PACKAGE_TARNAME "libarchive"++/* Define to the home page for this package. */+#define PACKAGE_URL ""++/* Define to the version of this package. */+#define PACKAGE_VERSION "3.4.3"++/* Define to 1 if PCRE_STATIC needs to be defined. */+/* #undef PCRE_STATIC */++/* The size of `wchar_t', as computed by sizeof. */+#define SIZEOF_WCHAR_T 4++/* Define to 1 if you have the ANSI C header files. */+#define STDC_HEADERS 1++/* Define to 1 if strerror_r returns char *. */+/* #undef STRERROR_R_CHAR_P */++/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */+#define TIME_WITH_SYS_TIME 1++/* Enable extensions on AIX 3, Interix.  */+#ifndef _ALL_SOURCE+# define _ALL_SOURCE 1+#endif+/* Enable GNU extensions on systems that have them.  */+#ifndef _GNU_SOURCE+# define _GNU_SOURCE 1+#endif+/* Enable threading extensions on Solaris.  */+#ifndef _POSIX_PTHREAD_SEMANTICS+# define _POSIX_PTHREAD_SEMANTICS 1+#endif+/* Enable extensions on HP NonStop.  */+#ifndef _TANDEM_SOURCE+# define _TANDEM_SOURCE 1+#endif+/* Enable general extensions on Solaris.  */+#ifndef __EXTENSIONS__+# define __EXTENSIONS__ 1+#endif+++/* Version number of package */+#define VERSION "3.4.3"++/* Define to '0x0502' for Windows Server 2003 APIs. */+/* #undef WINVER */++/* Enable large inode numbers on Mac OS X 10.5.  */+#ifndef _DARWIN_USE_64_BIT_INODE+# define _DARWIN_USE_64_BIT_INODE 1+#endif++/* Number of bits in a file offset, on hosts where this is settable. */+/* #undef _FILE_OFFSET_BITS */++/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */+/* #undef _LARGEFILE_SOURCE */++/* Define for large files, on AIX-style hosts. */+/* #undef _LARGE_FILES */++/* Define to 1 if on MINIX. */+/* #undef _MINIX */++/* Define to 2 if the system does not provide POSIX.1 features except with+   this defined. */+/* #undef _POSIX_1_SOURCE */++/* Define to 1 if you need to in order for `stat' and other things to work. */+/* #undef _POSIX_SOURCE */++/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the+   #define below would cause a syntax error. */+/* #undef _UINT32_T */++/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the+   #define below would cause a syntax error. */+/* #undef _UINT64_T */++/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the+   #define below would cause a syntax error. */+/* #undef _UINT8_T */++/* Define to '0x0502' for Windows Server 2003 APIs. */+/* #undef _WIN32_WINNT */++/* Define to empty if `const' does not conform to ANSI C. */+/* #undef const */++/* Define to match typeof st_gid field of struct stat if <sys/types.h> doesn't+   define. */+/* #undef gid_t */++/* Define to `unsigned long' if <sys/types.h> does not define. */+/* #undef id_t */++/* Define to the type of a signed integer type of width exactly 16 bits if+   such a type exists and the standard includes do not define it. */+/* #undef int16_t */++/* Define to the type of a signed integer type of width exactly 32 bits if+   such a type exists and the standard includes do not define it. */+/* #undef int32_t */++/* Define to the type of a signed integer type of width exactly 64 bits if+   such a type exists and the standard includes do not define it. */+/* #undef int64_t */++/* Define to the widest signed integer type if <stdint.h> and <inttypes.h> do+   not define. */+/* #undef intmax_t */++/* Define to `int' if <sys/types.h> does not define. */+/* #undef mode_t */++/* Define to `long long' if <sys/types.h> does not define. */+/* #undef off_t */++/* Define to `unsigned int' if <sys/types.h> does not define. */+/* #undef size_t */++/* Define to match typeof st_uid field of struct stat if <sys/types.h> doesn't+   define. */+/* #undef uid_t */++/* Define to the type of an unsigned integer type of width exactly 16 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint16_t */++/* Define to the type of an unsigned integer type of width exactly 32 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint32_t */++/* Define to the type of an unsigned integer type of width exactly 64 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint64_t */++/* Define to the type of an unsigned integer type of width exactly 8 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint8_t */++/* Define to the widest unsigned integer type if <stdint.h> and <inttypes.h>+   do not define. */+/* #undef uintmax_t */++/* Define to `unsigned int' if <sys/types.h> does not define. */+/* #undef uintptr_t */
+ c/autoconf-linux/config.h view
@@ -0,0 +1,1427 @@+/* config.h.  Generated from config.h.in by configure.  */+/* config.h.in.  Generated from configure.ac by autoheader.  */++/* Darwin ACL support */+/* #undef ARCHIVE_ACL_DARWIN */++/* FreeBSD ACL support */+/* #undef ARCHIVE_ACL_FREEBSD */++/* FreeBSD NFSv4 ACL support */+/* #undef ARCHIVE_ACL_FREEBSD_NFS4 */++/* Linux POSIX.1e ACL support via libacl */+/* #undef ARCHIVE_ACL_LIBACL */++/* Linux NFSv4 ACL support via librichacl */+/* #undef ARCHIVE_ACL_LIBRICHACL */++/* Solaris ACL support */+/* #undef ARCHIVE_ACL_SUNOS */++/* Solaris NFSv4 ACL support */+/* #undef ARCHIVE_ACL_SUNOS_NFS4 */++/* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_MD5_LIBC */++/* MD5 via ARCHIVE_CRYPTO_MD5_LIBMD supported. */+/* #undef ARCHIVE_CRYPTO_MD5_LIBMD */++/* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */+/* #undef ARCHIVE_CRYPTO_MD5_LIBSYSTEM */++/* MD5 via ARCHIVE_CRYPTO_MD5_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_MD5_MBEDTLS */++/* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */++/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_MD5_OPENSSL */++/* MD5 via ARCHIVE_CRYPTO_MD5_WIN supported. */+/* #undef ARCHIVE_CRYPTO_MD5_WIN */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_LIBC */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBMD supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_LIBMD */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_MBEDTLS */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_NETTLE */++/* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_RMD160_OPENSSL */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_LIBC */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBMD supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_LIBMD */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_LIBSYSTEM */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_MBEDTLS */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_OPENSSL */++/* SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA1_WIN */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC2 supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC2 */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC3 */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBMD supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBMD */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_LIBSYSTEM */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_MBEDTLS */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_OPENSSL */++/* SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA256_WIN */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC2 supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC2 */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC3 supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC3 */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_LIBSYSTEM */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_MBEDTLS */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_OPENSSL */++/* SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA384_WIN */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC2 supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC2 */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC3 */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBMD supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBMD */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_LIBSYSTEM */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_MBEDTLS supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_MBEDTLS */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_OPENSSL */++/* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */+/* #undef ARCHIVE_CRYPTO_SHA512_WIN */++/* AIX xattr support */+/* #undef ARCHIVE_XATTR_AIX */++/* Darwin xattr support */+/* #undef ARCHIVE_XATTR_DARWIN */++/* FreeBSD xattr support */+/* #undef ARCHIVE_XATTR_FREEBSD */++/* Linux xattr support */+/* #undef ARCHIVE_XATTR_LINUX */++/* Version number of bsdcat */+#define BSDCAT_VERSION_STRING "3.4.3"++/* Version number of bsdcpio */+#define BSDCPIO_VERSION_STRING "3.4.3"++/* Version number of bsdtar */+#define BSDTAR_VERSION_STRING "3.4.3"++/* Define to 1 if the system has the type `ace_t'. */+/* #undef HAVE_ACE_T */++/* Define to 1 if you have the `acl' function. */+/* #undef HAVE_ACL */++/* Define to 1 if the system has the type `aclent_t'. */+/* #undef HAVE_ACLENT_T */++/* Define to 1 if you have the `acl_add_flag_np' function. */+/* #undef HAVE_ACL_ADD_FLAG_NP */++/* Define to 1 if you have the `acl_add_perm' function. */+/* #undef HAVE_ACL_ADD_PERM */++/* Define to 1 if you have the `acl_clear_flags_np' function. */+/* #undef HAVE_ACL_CLEAR_FLAGS_NP */++/* Define to 1 if you have the `acl_clear_perms' function. */+/* #undef HAVE_ACL_CLEAR_PERMS */++/* Define to 1 if you have the `acl_create_entry' function. */+/* #undef HAVE_ACL_CREATE_ENTRY */++/* Define to 1 if you have the `acl_delete_def_file' function. */+/* #undef HAVE_ACL_DELETE_DEF_FILE */++/* Define to 1 if the system has the type `acl_entry_t'. */+/* #undef HAVE_ACL_ENTRY_T */++/* Define to 1 if you have the `acl_free' function. */+/* #undef HAVE_ACL_FREE */++/* Define to 1 if you have the `acl_get_brand_np' function. */+/* #undef HAVE_ACL_GET_BRAND_NP */++/* Define to 1 if you have the `acl_get_entry' function. */+/* #undef HAVE_ACL_GET_ENTRY */++/* Define to 1 if you have the `acl_get_entry_type_np' function. */+/* #undef HAVE_ACL_GET_ENTRY_TYPE_NP */++/* Define to 1 if you have the `acl_get_fd' function. */+/* #undef HAVE_ACL_GET_FD */++/* Define to 1 if you have the `acl_get_fd_np' function. */+/* #undef HAVE_ACL_GET_FD_NP */++/* Define to 1 if you have the `acl_get_file' function. */+/* #undef HAVE_ACL_GET_FILE */++/* Define to 1 if you have the `acl_get_flagset_np' function. */+/* #undef HAVE_ACL_GET_FLAGSET_NP */++/* Define to 1 if you have the `acl_get_flag_np' function. */+/* #undef HAVE_ACL_GET_FLAG_NP */++/* Define to 1 if you have the `acl_get_link_np' function. */+/* #undef HAVE_ACL_GET_LINK_NP */++/* Define to 1 if you have the `acl_get_perm' function. */+/* #undef HAVE_ACL_GET_PERM */++/* Define to 1 if you have the `acl_get_permset' function. */+/* #undef HAVE_ACL_GET_PERMSET */++/* Define to 1 if you have the `acl_get_perm_np' function. */+/* #undef HAVE_ACL_GET_PERM_NP */++/* Define to 1 if you have the `acl_get_qualifier' function. */+/* #undef HAVE_ACL_GET_QUALIFIER */++/* Define to 1 if you have the `acl_get_tag_type' function. */+/* #undef HAVE_ACL_GET_TAG_TYPE */++/* Define to 1 if you have the `acl_init' function. */+/* #undef HAVE_ACL_INIT */++/* Define to 1 if you have the `acl_is_trivial_np' function. */+/* #undef HAVE_ACL_IS_TRIVIAL_NP */++/* Define to 1 if you have the <acl/libacl.h> header file. */+#define HAVE_ACL_LIBACL_H 1++/* Define to 1 if the system has the type `acl_permset_t'. */+/* #undef HAVE_ACL_PERMSET_T */++/* Define to 1 if you have the `acl_set_entry_type_np' function. */+/* #undef HAVE_ACL_SET_ENTRY_TYPE_NP */++/* Define to 1 if you have the `acl_set_fd' function. */+/* #undef HAVE_ACL_SET_FD */++/* Define to 1 if you have the `acl_set_fd_np' function. */+/* #undef HAVE_ACL_SET_FD_NP */++/* Define to 1 if you have the `acl_set_file' function. */+/* #undef HAVE_ACL_SET_FILE */++/* Define to 1 if you have the `acl_set_link_np' function. */+/* #undef HAVE_ACL_SET_LINK_NP */++/* Define to 1 if you have the `acl_set_qualifier' function. */+/* #undef HAVE_ACL_SET_QUALIFIER */++/* Define to 1 if you have the `acl_set_tag_type' function. */+/* #undef HAVE_ACL_SET_TAG_TYPE */++/* Define to 1 if the system has the type `acl_t'. */+/* #undef HAVE_ACL_T */++/* Define to 1 if the system has the type `acl_tag_t'. */+/* #undef HAVE_ACL_TAG_T */++/* Define to 1 if you have the `arc4random_buf' function. */+/* #undef HAVE_ARC4RANDOM_BUF */++/* Define to 1 if you have the <attr/xattr.h> header file. */+/* #undef HAVE_ATTR_XATTR_H */++/* Define to 1 if you have the <bcrypt.h> header file. */+/* #undef HAVE_BCRYPT_H */++/* Define to 1 if you have the <blake2.h> header file. */+/* #undef HAVE_BLAKE2_H */++/* Define to 1 if you have the <bzlib.h> header file. */+/* #undef HAVE_BZLIB_H */++/* Define to 1 if you have the `chflags' function. */+/* #undef HAVE_CHFLAGS */++/* Define to 1 if you have the `chown' function. */+#define HAVE_CHOWN 1++/* Define to 1 if you have the `chroot' function. */+#define HAVE_CHROOT 1++/* Define to 1 if you have the <copyfile.h> header file. */+/* #undef HAVE_COPYFILE_H */++/* Define to 1 if you have the `ctime_r' function. */+#define HAVE_CTIME_R 1++/* Define to 1 if you have the <ctype.h> header file. */+#define HAVE_CTYPE_H 1++/* Define to 1 if you have the `cygwin_conv_path' function. */+/* #undef HAVE_CYGWIN_CONV_PATH */++/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACE_GETACL */++/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACE_GETACLCNT */++/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACE_SETACL */++/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if+   you don't. */+/* #undef HAVE_DECL_ACL_SYNCHRONIZE */++/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if+   you don't. */+/* #undef HAVE_DECL_ACL_TYPE_EXTENDED */++/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACL_TYPE_NFS4 */++/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you+   don't. */+/* #undef HAVE_DECL_ACL_USER */++/* Define to 1 if you have the declaration of `EXTATTR_NAMESPACE_USER', and to+   0 if you don't. */+/* #undef HAVE_DECL_EXTATTR_NAMESPACE_USER */++/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.+   */+/* #undef HAVE_DECL_GETACL */++/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you+   don't. */+/* #undef HAVE_DECL_GETACLCNT */++/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_INT32_MAX 1++/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you+   don't. */+#define HAVE_DECL_INT32_MIN 1++/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_INT64_MAX 1++/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you+   don't. */+#define HAVE_DECL_INT64_MIN 1++/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_INTMAX_MAX 1++/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you+   don't. */+#define HAVE_DECL_INTMAX_MIN 1++/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.+   */+/* #undef HAVE_DECL_SETACL */++/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_SIZE_MAX 1++/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_SSIZE_MAX 1++/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you+   don't. */+#define HAVE_DECL_STRERROR_R 1++/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_UINT32_MAX 1++/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_UINT64_MAX 1++/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you+   don't. */+#define HAVE_DECL_UINTMAX_MAX 1++/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if+   you don't. */+/* #undef HAVE_DECL_XATTR_NOFOLLOW */++/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.+   */+#define HAVE_DIRENT_H 1++/* Define to 1 if you have a dirfd function or macro */+#define HAVE_DIRFD 1++/* Define to 1 if you have the <dlfcn.h> header file. */+#define HAVE_DLFCN_H 1++/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */+/* #undef HAVE_DOPRNT */++/* Define to 1 if nl_langinfo supports D_MD_ORDER */+/* #undef HAVE_D_MD_ORDER */++/* A possible errno value for invalid file format errors */+/* #undef HAVE_EFTYPE */++/* A possible errno value for invalid file format errors */+#define HAVE_EILSEQ 1++/* Define to 1 if you have the <errno.h> header file. */+#define HAVE_ERRNO_H 1++/* Define to 1 if you have the <expat.h> header file. */+/* #undef HAVE_EXPAT_H */++/* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */++/* Define to 1 if you have the `extattr_get_fd' function. */+/* #undef HAVE_EXTATTR_GET_FD */++/* Define to 1 if you have the `extattr_get_file' function. */+/* #undef HAVE_EXTATTR_GET_FILE */++/* Define to 1 if you have the `extattr_get_link' function. */+/* #undef HAVE_EXTATTR_GET_LINK */++/* Define to 1 if you have the `extattr_list_fd' function. */+/* #undef HAVE_EXTATTR_LIST_FD */++/* Define to 1 if you have the `extattr_list_file' function. */+/* #undef HAVE_EXTATTR_LIST_FILE */++/* Define to 1 if you have the `extattr_list_link' function. */+/* #undef HAVE_EXTATTR_LIST_LINK */++/* Define to 1 if you have the `extattr_set_fd' function. */+/* #undef HAVE_EXTATTR_SET_FD */++/* Define to 1 if you have the `extattr_set_link' function. */+/* #undef HAVE_EXTATTR_SET_LINK */++/* Define to 1 if you have the `facl' function. */+/* #undef HAVE_FACL */++/* Define to 1 if you have the `fchdir' function. */+#define HAVE_FCHDIR 1++/* Define to 1 if you have the `fchflags' function. */+/* #undef HAVE_FCHFLAGS */++/* Define to 1 if you have the `fchmod' function. */+#define HAVE_FCHMOD 1++/* Define to 1 if you have the `fchown' function. */+#define HAVE_FCHOWN 1++/* Define to 1 if you have the `fcntl' function. */+#define HAVE_FCNTL 1++/* Define to 1 if you have the <fcntl.h> header file. */+#define HAVE_FCNTL_H 1++/* Define to 1 if you have the `fdopendir' function. */+#define HAVE_FDOPENDIR 1++/* Define to 1 if you have the `fgetea' function. */+/* #undef HAVE_FGETEA */++/* Define to 1 if you have the `fgetxattr' function. */+/* #undef HAVE_FGETXATTR */++/* Define to 1 if you have the `flistea' function. */+/* #undef HAVE_FLISTEA */++/* Define to 1 if you have the `flistxattr' function. */+/* #undef HAVE_FLISTXATTR */++/* Define to 1 if you have the `fork' function. */+#define HAVE_FORK 1++/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */+#define HAVE_FSEEKO 1++/* Define to 1 if you have the `fsetea' function. */+/* #undef HAVE_FSETEA */++/* Define to 1 if you have the `fsetxattr' function. */+/* #undef HAVE_FSETXATTR */++/* Define to 1 if you have the `fstat' function. */+#define HAVE_FSTAT 1++/* Define to 1 if you have the `fstatat' function. */+#define HAVE_FSTATAT 1++/* Define to 1 if you have the `fstatfs' function. */+#define HAVE_FSTATFS 1++/* Define to 1 if you have the `fstatvfs' function. */+#define HAVE_FSTATVFS 1++/* Define to 1 if you have the `ftruncate' function. */+#define HAVE_FTRUNCATE 1++/* Define to 1 if you have the `futimens' function. */+#define HAVE_FUTIMENS 1++/* Define to 1 if you have the `futimes' function. */+#define HAVE_FUTIMES 1++/* Define to 1 if you have the `futimesat' function. */+#define HAVE_FUTIMESAT 1++/* Define to 1 if you have the `getea' function. */+/* #undef HAVE_GETEA */++/* Define to 1 if you have the `geteuid' function. */+#define HAVE_GETEUID 1++/* Define to 1 if you have the `getgrgid_r' function. */+#define HAVE_GETGRGID_R 1++/* Define to 1 if you have the `getgrnam_r' function. */+#define HAVE_GETGRNAM_R 1++/* Define to 1 if you have the `getpid' function. */+#define HAVE_GETPID 1++/* Define to 1 if you have the `getpwnam_r' function. */+#define HAVE_GETPWNAM_R 1++/* Define to 1 if you have the `getpwuid_r' function. */+#define HAVE_GETPWUID_R 1++/* Define to 1 if you have the `getvfsbyname' function. */+/* #undef HAVE_GETVFSBYNAME */++/* Define to 1 if you have the `getxattr' function. */+/* #undef HAVE_GETXATTR */++/* Define to 1 if you have the `gmtime_r' function. */+#define HAVE_GMTIME_R 1++/* Define to 1 if you have the <grp.h> header file. */+#define HAVE_GRP_H 1++/* Define if you have the iconv() function and it works. */+/* #undef HAVE_ICONV */++/* Define to 1 if you have the <iconv.h> header file. */+/* #undef HAVE_ICONV_H */++/* Define to 1 if the system has the type `intmax_t'. */+#define HAVE_INTMAX_T 1++/* Define to 1 if you have the <inttypes.h> header file. */+#define HAVE_INTTYPES_H 1++/* Define to 1 if you have the <io.h> header file. */+/* #undef HAVE_IO_H */++/* Define to 1 if you have the <langinfo.h> header file. */+#define HAVE_LANGINFO_H 1++/* Define to 1 if you have the `lchflags' function. */+/* #undef HAVE_LCHFLAGS */++/* Define to 1 if you have the `lchmod' function. */+/* #undef HAVE_LCHMOD */++/* Define to 1 if you have the `lchown' function. */+#define HAVE_LCHOWN 1++/* Define to 1 if you have the `lgetea' function. */+/* #undef HAVE_LGETEA */++/* Define to 1 if you have the `lgetxattr' function. */+/* #undef HAVE_LGETXATTR */++/* Define to 1 if you have the `acl' library (-lacl). */+/* #undef HAVE_LIBACL */++/* Define to 1 if you have the `b2' library (-lb2). */+/* #undef HAVE_LIBB2 */++/* Define to 1 if you have the `bz2' library (-lbz2). */+/* #undef HAVE_LIBBZ2 */++/* Define to 1 if you have the `charset' library (-lcharset). */+/* #undef HAVE_LIBCHARSET */++/* Define to 1 if you have the `crypto' library (-lcrypto). */+/* #undef HAVE_LIBCRYPTO */++/* Define to 1 if you have the `eay32' library (-leay32). */+/* #undef HAVE_LIBEAY32 */++/* Define to 1 if you have the `eay64' library (-leay64). */+/* #undef HAVE_LIBEAY64 */++/* Define to 1 if you have the `expat' library (-lexpat). */+/* #undef HAVE_LIBEXPAT */++/* Define to 1 if you have the `lz4' library (-llz4). */+/* #undef HAVE_LIBLZ4 */++/* Define to 1 if you have the `lzma' library (-llzma). */+/* #undef HAVE_LIBLZMA */++/* Define to 1 if you have the `lzo2' library (-llzo2). */+/* #undef HAVE_LIBLZO2 */++/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */+/* #undef HAVE_LIBMBEDCRYPTO */++/* Define to 1 if you have the `md' library (-lmd). */+/* #undef HAVE_LIBMD */++/* Define to 1 if you have the `nettle' library (-lnettle). */+/* #undef HAVE_LIBNETTLE */++/* Define to 1 if you have the `pcre' library (-lpcre). */+/* #undef HAVE_LIBPCRE */++/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */+/* #undef HAVE_LIBPCREPOSIX */++/* Define to 1 if you have the `regex' library (-lregex). */+/* #undef HAVE_LIBREGEX */++/* Define to 1 if you have the `richacl' library (-lrichacl). */+/* #undef HAVE_LIBRICHACL */++/* Define to 1 if you have the `xml2' library (-lxml2). */+/* #undef HAVE_LIBXML2 */++/* Define to 1 if you have the <libxml/xmlreader.h> header file. */+/* #undef HAVE_LIBXML_XMLREADER_H */++/* Define to 1 if you have the <libxml/xmlwriter.h> header file. */+/* #undef HAVE_LIBXML_XMLWRITER_H */++/* Define to 1 if you have the `z' library (-lz). */+/* #undef HAVE_LIBZ */++/* Define to 1 if you have the `zstd' library (-lzstd). */+/* #undef HAVE_LIBZSTD */++/* Define to 1 if you have the <limits.h> header file. */+#define HAVE_LIMITS_H 1++/* Define to 1 if you have the `link' function. */+#define HAVE_LINK 1++/* Define to 1 if you have the <linux/fiemap.h> header file. */+#define HAVE_LINUX_FIEMAP_H 1++/* Define to 1 if you have the <linux/fs.h> header file. */+#define HAVE_LINUX_FS_H 1++/* Define to 1 if you have the <linux/magic.h> header file. */+#define HAVE_LINUX_MAGIC_H 1++/* Define to 1 if you have the <linux/types.h> header file. */+#define HAVE_LINUX_TYPES_H 1++/* Define to 1 if you have the `listea' function. */+/* #undef HAVE_LISTEA */++/* Define to 1 if you have the `listxattr' function. */+/* #undef HAVE_LISTXATTR */++/* Define to 1 if you have the `llistea' function. */+/* #undef HAVE_LLISTEA */++/* Define to 1 if you have the `llistxattr' function. */+/* #undef HAVE_LLISTXATTR */++/* Define to 1 if you have the <localcharset.h> header file. */+/* #undef HAVE_LOCALCHARSET_H */++/* Define to 1 if you have the `locale_charset' function. */+/* #undef HAVE_LOCALE_CHARSET */++/* Define to 1 if you have the <locale.h> header file. */+#define HAVE_LOCALE_H 1++/* Define to 1 if you have the `localtime_r' function. */+#define HAVE_LOCALTIME_R 1++/* Define to 1 if the system has the type `long long int'. */+#define HAVE_LONG_LONG_INT 1++/* Define to 1 if you have the `lsetea' function. */+/* #undef HAVE_LSETEA */++/* Define to 1 if you have the `lsetxattr' function. */+/* #undef HAVE_LSETXATTR */++/* Define to 1 if you have the `lstat' function. */+#define HAVE_LSTAT 1++/* Define to 1 if `lstat' has the bug that it succeeds when given the+   zero-length file name argument. */+/* #undef HAVE_LSTAT_EMPTY_STRING_BUG */++/* Define to 1 if you have the `lutimes' function. */+#define HAVE_LUTIMES 1++/* Define to 1 if you have the <lz4hc.h> header file. */+/* #undef HAVE_LZ4HC_H */++/* Define to 1 if you have the <lz4.h> header file. */+/* #undef HAVE_LZ4_H */++/* Define to 1 if you have the <lzma.h> header file. */+/* #undef HAVE_LZMA_H */++/* Define to 1 if you have the `lzma_stream_encoder_mt' function. */+/* #undef HAVE_LZMA_STREAM_ENCODER_MT */++/* Define to 1 if you have the <lzo/lzo1x.h> header file. */+/* #undef HAVE_LZO_LZO1X_H */++/* Define to 1 if you have the <lzo/lzoconf.h> header file. */+/* #undef HAVE_LZO_LZOCONF_H */++/* Define to 1 if you have the <mbedtls/aes.h> header file. */+/* #undef HAVE_MBEDTLS_AES_H */++/* Define to 1 if you have the <mbedtls/md.h> header file. */+/* #undef HAVE_MBEDTLS_MD_H */++/* Define to 1 if you have the <mbedtls/pkcs5.h> header file. */+/* #undef HAVE_MBEDTLS_PKCS5_H */++/* Define to 1 if you have the `mbrtowc' function. */+#define HAVE_MBRTOWC 1++/* Define to 1 if you have the `mbr_gid_to_uuid' function. */+/* #undef HAVE_MBR_GID_TO_UUID */++/* Define to 1 if you have the `mbr_uid_to_uuid' function. */+/* #undef HAVE_MBR_UID_TO_UUID */++/* Define to 1 if you have the `mbr_uuid_to_id' function. */+/* #undef HAVE_MBR_UUID_TO_ID */++/* Define to 1 if you have the <md5.h> header file. */+/* #undef HAVE_MD5_H */++/* Define to 1 if you have the <membership.h> header file. */+/* #undef HAVE_MEMBERSHIP_H */++/* Define to 1 if you have the `memmove' function. */+#define HAVE_MEMMOVE 1++/* Define to 1 if you have the <memory.h> header file. */+#define HAVE_MEMORY_H 1++/* Define to 1 if you have the `memset' function. */+#define HAVE_MEMSET 1++/* Define to 1 if you have the `mkdir' function. */+#define HAVE_MKDIR 1++/* Define to 1 if you have the `mkfifo' function. */+#define HAVE_MKFIFO 1++/* Define to 1 if you have the `mknod' function. */+#define HAVE_MKNOD 1++/* Define to 1 if you have the `mkstemp' function. */+#define HAVE_MKSTEMP 1++/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */+/* #undef HAVE_NDIR_H */++/* Define to 1 if you have the <nettle/aes.h> header file. */+/* #undef HAVE_NETTLE_AES_H */++/* Define to 1 if you have the <nettle/hmac.h> header file. */+/* #undef HAVE_NETTLE_HMAC_H */++/* Define to 1 if you have the <nettle/md5.h> header file. */+/* #undef HAVE_NETTLE_MD5_H */++/* Define to 1 if you have the <nettle/pbkdf2.h> header file. */+/* #undef HAVE_NETTLE_PBKDF2_H */++/* Define to 1 if you have the <nettle/ripemd160.h> header file. */+/* #undef HAVE_NETTLE_RIPEMD160_H */++/* Define to 1 if you have the <nettle/sha.h> header file. */+/* #undef HAVE_NETTLE_SHA_H */++/* Define to 1 if you have the `nl_langinfo' function. */+#define HAVE_NL_LANGINFO 1++/* Define to 1 if you have the `openat' function. */+#define HAVE_OPENAT 1++/* Define to 1 if you have the <openssl/evp.h> header file. */+/* #undef HAVE_OPENSSL_EVP_H */++/* Define to 1 if you have the <paths.h> header file. */+#define HAVE_PATHS_H 1++/* Define to 1 if you have the <pcreposix.h> header file. */+/* #undef HAVE_PCREPOSIX_H */++/* Define to 1 if you have the `pipe' function. */+#define HAVE_PIPE 1++/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */+/* #undef HAVE_PKCS5_PBKDF2_HMAC_SHA1 */++/* Define to 1 if you have the `poll' function. */+#define HAVE_POLL 1++/* Define to 1 if you have the <poll.h> header file. */+#define HAVE_POLL_H 1++/* Define to 1 if you have the `posix_spawnp' function. */+#define HAVE_POSIX_SPAWNP 1++/* Define to 1 if you have the <pthread.h> header file. */+#define HAVE_PTHREAD_H 1++/* Define to 1 if you have the <pwd.h> header file. */+#define HAVE_PWD_H 1++/* Define to 1 if you have a POSIX compatible readdir_r */+#define HAVE_READDIR_R 1++/* Define to 1 if you have the `readlink' function. */+#define HAVE_READLINK 1++/* Define to 1 if you have the `readlinkat' function. */+#define HAVE_READLINKAT 1++/* Define to 1 if you have the `readpassphrase' function. */+/* #undef HAVE_READPASSPHRASE */++/* Define to 1 if you have the <readpassphrase.h> header file. */+/* #undef HAVE_READPASSPHRASE_H */++/* Define to 1 if you have the <regex.h> header file. */+/* #undef HAVE_REGEX_H */++/* Define to 1 if you have the `richacl_alloc' function. */+/* #undef HAVE_RICHACL_ALLOC */++/* Define to 1 if you have the `richacl_equiv_mode' function. */+/* #undef HAVE_RICHACL_EQUIV_MODE */++/* Define to 1 if you have the `richacl_free' function. */+/* #undef HAVE_RICHACL_FREE */++/* Define to 1 if you have the `richacl_get_fd' function. */+/* #undef HAVE_RICHACL_GET_FD */++/* Define to 1 if you have the `richacl_get_file' function. */+/* #undef HAVE_RICHACL_GET_FILE */++/* Define to 1 if you have the `richacl_set_fd' function. */+/* #undef HAVE_RICHACL_SET_FD */++/* Define to 1 if you have the `richacl_set_file' function. */+/* #undef HAVE_RICHACL_SET_FILE */++/* Define to 1 if you have the <ripemd.h> header file. */+/* #undef HAVE_RIPEMD_H */++/* Define to 1 if you have the `select' function. */+#define HAVE_SELECT 1++/* Define to 1 if you have the `setenv' function. */+#define HAVE_SETENV 1++/* Define to 1 if you have the `setlocale' function. */+#define HAVE_SETLOCALE 1++/* Define to 1 if you have the `setxattr' function. */+/* #undef HAVE_SETXATTR */++/* Define to 1 if you have the <sha256.h> header file. */+/* #undef HAVE_SHA256_H */++/* Define to 1 if you have the <sha512.h> header file. */+/* #undef HAVE_SHA512_H */++/* Define to 1 if you have the <sha.h> header file. */+/* #undef HAVE_SHA_H */++/* Define to 1 if you have the `sigaction' function. */+#define HAVE_SIGACTION 1++/* Define to 1 if you have the <signal.h> header file. */+#define HAVE_SIGNAL_H 1++/* Define to 1 if you have the <spawn.h> header file. */+#define HAVE_SPAWN_H 1++/* Define to 1 if you have the `statfs' function. */+#define HAVE_STATFS 1++/* Define to 1 if you have the `statvfs' function. */+#define HAVE_STATVFS 1++/* Define to 1 if `stat' has the bug that it succeeds when given the+   zero-length file name argument. */+/* #undef HAVE_STAT_EMPTY_STRING_BUG */++/* Define to 1 if you have the <stdarg.h> header file. */+#define HAVE_STDARG_H 1++/* Define to 1 if you have the <stdint.h> header file. */+#define HAVE_STDINT_H 1++/* Define to 1 if you have the <stdlib.h> header file. */+#define HAVE_STDLIB_H 1++/* Define to 1 if you have the `strchr' function. */+#define HAVE_STRCHR 1++/* Define to 1 if you have the `strdup' function. */+#define HAVE_STRDUP 1++/* Define to 1 if you have the `strerror' function. */+#define HAVE_STRERROR 1++/* Define to 1 if you have the `strerror_r' function. */+#define HAVE_STRERROR_R 1++/* Define to 1 if you have the `strftime' function. */+#define HAVE_STRFTIME 1++/* Define to 1 if you have the <strings.h> header file. */+#define HAVE_STRINGS_H 1++/* Define to 1 if you have the <string.h> header file. */+#define HAVE_STRING_H 1++/* Define to 1 if you have the `strncpy_s' function. */+/* #undef HAVE_STRNCPY_S */++/* Define to 1 if you have the `strrchr' function. */+#define HAVE_STRRCHR 1++/* Define to 1 if the system has the type `struct richace'. */+/* #undef HAVE_STRUCT_RICHACE */++/* Define to 1 if the system has the type `struct richacl'. */+/* #undef HAVE_STRUCT_RICHACL */++/* Define to 1 if `f_namemax' is a member of `struct statfs'. */+/* #undef HAVE_STRUCT_STATFS_F_NAMEMAX */++/* Define to 1 if `f_iosize' is a member of `struct statvfs'. */+/* #undef HAVE_STRUCT_STATVFS_F_IOSIZE */++/* Define to 1 if `st_birthtime' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_BIRTHTIME */++/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC */++/* Define to 1 if `st_blksize' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1++/* Define to 1 if `st_flags' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_FLAGS */++/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC */++/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_MTIME_N */++/* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_MTIME_USEC */++/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1++/* Define to 1 if `st_umtime' is a member of `struct stat'. */+/* #undef HAVE_STRUCT_STAT_ST_UMTIME */++/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */+#define HAVE_STRUCT_TM_TM_GMTOFF 1++/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */+/* #undef HAVE_STRUCT_TM___TM_GMTOFF */++/* Define to 1 if the system has the type `struct vfsconf'. */+/* #undef HAVE_STRUCT_VFSCONF */++/* Define to 1 if the system has the type `struct xvfsconf'. */+/* #undef HAVE_STRUCT_XVFSCONF */++/* Define to 1 if you have the `symlink' function. */+#define HAVE_SYMLINK 1++/* Define to 1 if you have the <sys/acl.h> header file. */++/* Define to 1 if you have the <sys/cdefs.h> header file. */+#define HAVE_SYS_CDEFS_H 1++/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.+   */+/* #undef HAVE_SYS_DIR_H */++/* Define to 1 if you have the <sys/ea.h> header file. */+/* #undef HAVE_SYS_EA_H */++/* Define to 1 if you have the <sys/extattr.h> header file. */+/* #undef HAVE_SYS_EXTATTR_H */++/* Define to 1 if you have the <sys/ioctl.h> header file. */+#define HAVE_SYS_IOCTL_H 1++/* Define to 1 if you have the <sys/mkdev.h> header file. */+/* #undef HAVE_SYS_MKDEV_H */++/* Define to 1 if you have the <sys/mount.h> header file. */+#define HAVE_SYS_MOUNT_H 1++/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.+   */+/* #undef HAVE_SYS_NDIR_H */++/* Define to 1 if you have the <sys/param.h> header file. */+#define HAVE_SYS_PARAM_H 1++/* Define to 1 if you have the <sys/poll.h> header file. */+#define HAVE_SYS_POLL_H 1++/* Define to 1 if you have the <sys/richacl.h> header file. */+/* #undef HAVE_SYS_RICHACL_H */++/* Define to 1 if you have the <sys/select.h> header file. */+#define HAVE_SYS_SELECT_H 1++/* Define to 1 if you have the <sys/statfs.h> header file. */+#define HAVE_SYS_STATFS_H 1++/* Define to 1 if you have the <sys/statvfs.h> header file. */+#define HAVE_SYS_STATVFS_H 1++/* Define to 1 if you have the <sys/stat.h> header file. */+#define HAVE_SYS_STAT_H 1++/* Define to 1 if you have the <sys/sysmacros.h> header file. */+#define HAVE_SYS_SYSMACROS_H 1++/* Define to 1 if you have the <sys/time.h> header file. */+#define HAVE_SYS_TIME_H 1++/* Define to 1 if you have the <sys/types.h> header file. */+#define HAVE_SYS_TYPES_H 1++/* Define to 1 if you have the <sys/utime.h> header file. */+/* #undef HAVE_SYS_UTIME_H */++/* Define to 1 if you have the <sys/utsname.h> header file. */+#define HAVE_SYS_UTSNAME_H 1++/* Define to 1 if you have the <sys/vfs.h> header file. */+#define HAVE_SYS_VFS_H 1++/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */+#define HAVE_SYS_WAIT_H 1++/* Define to 1 if you have the <sys/xattr.h> header file. */+#define HAVE_SYS_XATTR_H 1++/* Define to 1 if you have the `timegm' function. */+#define HAVE_TIMEGM 1++/* Define to 1 if you have the <time.h> header file. */+#define HAVE_TIME_H 1++/* Define to 1 if you have the `tzset' function. */+#define HAVE_TZSET 1++/* Define to 1 if the system has the type `uintmax_t'. */+#define HAVE_UINTMAX_T 1++/* Define to 1 if you have the <unistd.h> header file. */+#define HAVE_UNISTD_H 1++/* Define to 1 if you have the `unlinkat' function. */+#define HAVE_UNLINKAT 1++/* Define to 1 if you have the `unsetenv' function. */+#define HAVE_UNSETENV 1++/* Define to 1 if the system has the type `unsigned long long'. */+#define HAVE_UNSIGNED_LONG_LONG 1++/* Define to 1 if the system has the type `unsigned long long int'. */+#define HAVE_UNSIGNED_LONG_LONG_INT 1++/* Define to 1 if you have the `utime' function. */+#define HAVE_UTIME 1++/* Define to 1 if you have the `utimensat' function. */+#define HAVE_UTIMENSAT 1++/* Define to 1 if you have the `utimes' function. */+#define HAVE_UTIMES 1++/* Define to 1 if you have the <utime.h> header file. */+#define HAVE_UTIME_H 1++/* Define to 1 if you have the `vfork' function. */+#define HAVE_VFORK 1++/* Define to 1 if you have the `vprintf' function. */+#define HAVE_VPRINTF 1++/* Define to 1 if you have the <wchar.h> header file. */+#define HAVE_WCHAR_H 1++/* Define to 1 if the system has the type `wchar_t'. */+#define HAVE_WCHAR_T 1++/* Define to 1 if you have the `wcrtomb' function. */+#define HAVE_WCRTOMB 1++/* Define to 1 if you have the `wcscmp' function. */+#define HAVE_WCSCMP 1++/* Define to 1 if you have the `wcscpy' function. */+#define HAVE_WCSCPY 1++/* Define to 1 if you have the `wcslen' function. */+#define HAVE_WCSLEN 1++/* Define to 1 if you have the `wctomb' function. */+#define HAVE_WCTOMB 1++/* Define to 1 if you have the <wctype.h> header file. */+#define HAVE_WCTYPE_H 1++/* Define to 1 if you have the <wincrypt.h> header file. */+/* #undef HAVE_WINCRYPT_H */++/* Define to 1 if you have the <windows.h> header file. */+/* #undef HAVE_WINDOWS_H */++/* Define to 1 if you have the <winioctl.h> header file. */+/* #undef HAVE_WINIOCTL_H */++/* Define to 1 if you have the `wmemcmp' function. */+#define HAVE_WMEMCMP 1++/* Define to 1 if you have the `wmemcpy' function. */+#define HAVE_WMEMCPY 1++/* Define to 1 if you have the `wmemmove' function. */+#define HAVE_WMEMMOVE 1++/* Define to 1 if you have a working EXT2_IOC_GETFLAGS */+#define HAVE_WORKING_EXT2_IOC_GETFLAGS 1++/* Define to 1 if you have a working FS_IOC_GETFLAGS */+#define HAVE_WORKING_FS_IOC_GETFLAGS 1++/* Define to 1 if you have the <zlib.h> header file. */+/* #undef HAVE_ZLIB_H */++/* Define to 1 if you have the <zstd.h> header file. */+/* #undef HAVE_ZSTD_H */++/* Define to 1 if you have the `_ctime64_s' function. */+/* #undef HAVE__CTIME64_S */++/* Define to 1 if you have the `_fseeki64' function. */+/* #undef HAVE__FSEEKI64 */++/* Define to 1 if you have the `_get_timezone' function. */+/* #undef HAVE__GET_TIMEZONE */++/* Define to 1 if you have the `_gmtime64_s' function. */+/* #undef HAVE__GMTIME64_S */++/* Define to 1 if you have the `_localtime64_s' function. */+/* #undef HAVE__LOCALTIME64_S */++/* Define to 1 if you have the `_mkgmtime64' function. */+/* #undef HAVE__MKGMTIME64 */++/* Define as const if the declaration of iconv() needs const. */+/* #undef ICONV_CONST */++/* Version number of libarchive as a single integer */+#define LIBARCHIVE_VERSION_NUMBER "3004003"++/* Version number of libarchive */+#define LIBARCHIVE_VERSION_STRING "3.4.3"++/* Define to 1 if `lstat' dereferences a symlink specified with a trailing+   slash. */+#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1++/* Define to the sub-directory where libtool stores uninstalled libraries. */+#define LT_OBJDIR ".libs/"++/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.+   */+/* #undef MAJOR_IN_MKDEV */++/* Define to 1 if `major', `minor', and `makedev' are declared in+   <sysmacros.h>. */+#define MAJOR_IN_SYSMACROS 1++/* Define to '0x05020000' for Windows Server 2003 APIs. */+/* #undef NTDDI_VERSION */++/* Name of package */+#define PACKAGE "libarchive"++/* Define to the address where bug reports for this package should be sent. */+#define PACKAGE_BUGREPORT "libarchive-discuss@googlegroups.com"++/* Define to the full name of this package. */+#define PACKAGE_NAME "libarchive"++/* Define to the full name and version of this package. */+#define PACKAGE_STRING "libarchive 3.4.3"++/* Define to the one symbol short name of this package. */+#define PACKAGE_TARNAME "libarchive"++/* Define to the home page for this package. */+#define PACKAGE_URL ""++/* Define to the version of this package. */+#define PACKAGE_VERSION "3.4.3"++/* Define to 1 if PCRE_STATIC needs to be defined. */+/* #undef PCRE_STATIC */++/* The size of `wchar_t', as computed by sizeof. */+#define SIZEOF_WCHAR_T 4++/* Define to 1 if you have the ANSI C header files. */+#define STDC_HEADERS 1++/* Define to 1 if strerror_r returns char *. */+#define STRERROR_R_CHAR_P 1++/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */+#define TIME_WITH_SYS_TIME 1++/* Enable extensions on AIX 3, Interix.  */+#ifndef _ALL_SOURCE+# define _ALL_SOURCE 1+#endif+/* Enable GNU extensions on systems that have them.  */+#ifndef _GNU_SOURCE+# define _GNU_SOURCE 1+#endif+/* Enable threading extensions on Solaris.  */+#ifndef _POSIX_PTHREAD_SEMANTICS+# define _POSIX_PTHREAD_SEMANTICS 1+#endif+/* Enable extensions on HP NonStop.  */+#ifndef _TANDEM_SOURCE+# define _TANDEM_SOURCE 1+#endif+/* Enable general extensions on Solaris.  */+#ifndef __EXTENSIONS__+# define __EXTENSIONS__ 1+#endif+++/* Version number of package */+#define VERSION "3.4.3"++/* Define to '0x0502' for Windows Server 2003 APIs. */+/* #undef WINVER */++/* Enable large inode numbers on Mac OS X 10.5.  */+#ifndef _DARWIN_USE_64_BIT_INODE+# define _DARWIN_USE_64_BIT_INODE 1+#endif++/* Number of bits in a file offset, on hosts where this is settable. */+/* #undef _FILE_OFFSET_BITS */++/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */+/* #undef _LARGEFILE_SOURCE */++/* Define for large files, on AIX-style hosts. */+/* #undef _LARGE_FILES */++/* Define to 1 if on MINIX. */+/* #undef _MINIX */++/* Define to 2 if the system does not provide POSIX.1 features except with+   this defined. */+/* #undef _POSIX_1_SOURCE */++/* Define to 1 if you need to in order for `stat' and other things to work. */+/* #undef _POSIX_SOURCE */++/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the+   #define below would cause a syntax error. */+/* #undef _UINT32_T */++/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the+   #define below would cause a syntax error. */+/* #undef _UINT64_T */++/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the+   #define below would cause a syntax error. */+/* #undef _UINT8_T */++/* Define to '0x0502' for Windows Server 2003 APIs. */+/* #undef _WIN32_WINNT */++/* Define to empty if `const' does not conform to ANSI C. */+/* #undef const */++/* Define to match typeof st_gid field of struct stat if <sys/types.h> doesn't+   define. */+/* #undef gid_t */++/* Define to `unsigned long' if <sys/types.h> does not define. */+/* #undef id_t */++/* Define to the type of a signed integer type of width exactly 16 bits if+   such a type exists and the standard includes do not define it. */+/* #undef int16_t */++/* Define to the type of a signed integer type of width exactly 32 bits if+   such a type exists and the standard includes do not define it. */+/* #undef int32_t */++/* Define to the type of a signed integer type of width exactly 64 bits if+   such a type exists and the standard includes do not define it. */+/* #undef int64_t */++/* Define to the widest signed integer type if <stdint.h> and <inttypes.h> do+   not define. */+/* #undef intmax_t */++/* Define to `int' if <sys/types.h> does not define. */+/* #undef mode_t */++/* Define to `long long' if <sys/types.h> does not define. */+/* #undef off_t */++/* Define to `unsigned int' if <sys/types.h> does not define. */+/* #undef size_t */++/* Define to match typeof st_uid field of struct stat if <sys/types.h> doesn't+   define. */+/* #undef uid_t */++/* Define to the type of an unsigned integer type of width exactly 16 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint16_t */++/* Define to the type of an unsigned integer type of width exactly 32 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint32_t */++/* Define to the type of an unsigned integer type of width exactly 64 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint64_t */++/* Define to the type of an unsigned integer type of width exactly 8 bits if+   such a type exists and the standard includes do not define it. */+/* #undef uint8_t */++/* Define to the widest unsigned integer type if <stdint.h> and <inttypes.h>+   do not define. */+/* #undef uintmax_t */++/* Define to `unsigned int' if <sys/types.h> does not define. */+/* #undef uintptr_t */
+ c/config_freebsd.h view
@@ -0,0 +1,260 @@+/*-+ * Copyright (c) 2003-2007 Tim Kientzle+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD$+ */++#include <osreldate.h>++/* FreeBSD 5.0 and later has ACL and extattr support. */+#if __FreeBSD__ > 4+#define ARCHIVE_ACL_FREEBSD 1+#define HAVE_ACL_GET_PERM_NP 1+#define HAVE_ARC4RANDOM_BUF 1+#define HAVE_EXTATTR_GET_FILE 1+#define HAVE_EXTATTR_LIST_FILE 1+#define HAVE_EXTATTR_SET_FD 1+#define HAVE_EXTATTR_SET_FILE 1+#define HAVE_STRUCT_XVFSCONF 1+#define HAVE_SYS_ACL_H 1+#define HAVE_SYS_EXTATTR_H 1+#if __FreeBSD__ > 7+/* FreeBSD 8.0 and later has NFSv4 ACL support */+#define ARCHIVE_ACL_FREEBSD_NFS4 1+#define HAVE_ACL_GET_LINK_NP 1+#define HAVE_ACL_IS_TRIVIAL_NP 1+#define HAVE_ACL_SET_LINK_NP 1+#endif /* __FreeBSD__ > 7 */+#endif /* __FreeBSD__ > 4 */++#ifdef WITH_OPENSSL+#define HAVE_LIBCRYPTO 1+#define HAVE_OPENSSL_EVP_H 1+#define HAVE_OPENSSL_MD5_H 1+#define HAVE_OPENSSL_RIPEMD_H 1+#define HAVE_OPENSSL_SHA_H 1+#define HAVE_OPENSSL_SHA256_INIT 1+#define HAVE_OPENSSL_SHA384_INIT 1+#define HAVE_OPENSSL_SHA512_INIT 1+#define HAVE_PKCS5_PBKDF2_HMAC_SHA1 1+#define HAVE_SHA256 1+#define HAVE_SHA384 1+#define HAVE_SHA512 1+#else+#define HAVE_LIBMD 1+#define HAVE_MD5_H 1+#define HAVE_MD5INIT 1+#define HAVE_RIPEMD_H 1+#define HAVE_SHA_H 1+#define HAVE_SHA1 1+#define HAVE_SHA1_INIT 1+#define HAVE_SHA256 1+#define HAVE_SHA256_H 1+#define HAVE_SHA256_INIT 1+#define HAVE_SHA512 1+#define HAVE_SHA512_H 1+#define HAVE_SHA512_INIT 1+#endif++#define HAVE_BSDXML_H 1+#define HAVE_BZLIB_H 1+#define HAVE_CHFLAGS 1+#define HAVE_CHOWN 1+#define HAVE_CHROOT 1+#define HAVE_CTIME_R 1+#define HAVE_CTYPE_H 1+#define HAVE_DECL_EXTATTR_NAMESPACE_USER 1+#define HAVE_DECL_INT32_MAX 1+#define HAVE_DECL_INT32_MIN 1+#define HAVE_DECL_INT64_MAX 1+#define HAVE_DECL_INT64_MIN 1+#define HAVE_DECL_INTMAX_MAX 1+#define HAVE_DECL_INTMAX_MIN 1+#define HAVE_DECL_SIZE_MAX 1+#define HAVE_DECL_SSIZE_MAX 1+#define HAVE_DECL_STRERROR_R 1+#define HAVE_DECL_UINT32_MAX 1+#define HAVE_DECL_UINT64_MAX 1+#define HAVE_DECL_UINTMAX_MAX 1+#define HAVE_DIRENT_H 1+#define HAVE_DLFCN_H 1+#define HAVE_D_MD_ORDER 1+#define HAVE_EFTYPE 1+#define HAVE_EILSEQ 1+#define HAVE_ERRNO_H 1+#define HAVE_FCHDIR 1+#define HAVE_FCHFLAGS 1+#define HAVE_FCHMOD 1+#define HAVE_FCHOWN 1+#define HAVE_FCNTL 1+#define HAVE_FCNTL_H 1+#define HAVE_FDOPENDIR 1+#define HAVE_FORK 1+#define HAVE_FSEEKO 1+#define HAVE_FSTAT 1+#define HAVE_FSTATAT 1+#define HAVE_FSTATFS 1+#define HAVE_FSTATVFS 1+#define HAVE_FTRUNCATE 1+#define HAVE_FUTIMES 1+#define HAVE_FUTIMESAT 1+#define HAVE_GETEUID 1+#define HAVE_GETGRGID_R 1+#define HAVE_GETGRNAM_R 1+#define HAVE_GETPID 1+#define HAVE_GETPWNAM_R 1+#define HAVE_GETPWUID_R 1+#define HAVE_GETVFSBYNAME 1+#define HAVE_GMTIME_R 1+#define HAVE_GRP_H 1+#define HAVE_INTMAX_T 1+#define HAVE_INTTYPES_H 1+#define HAVE_LANGINFO_H 1+#define HAVE_LCHFLAGS 1+#define HAVE_LCHMOD 1+#define HAVE_LCHOWN 1+#define HAVE_LIBZ 1+#define HAVE_LIMITS_H 1+#define HAVE_LINK 1+#define HAVE_LOCALE_H 1+#define HAVE_LOCALTIME_R 1+#define HAVE_LONG_LONG_INT 1+#define HAVE_LSTAT 1+#define HAVE_LUTIMES 1+#define HAVE_MBRTOWC 1+#define HAVE_MEMMOVE 1+#define HAVE_MEMORY_H 1+#define HAVE_MEMSET 1+#define HAVE_MKDIR 1+#define HAVE_MKFIFO 1+#define HAVE_MKNOD 1+#define HAVE_MKSTEMP 1+#define HAVE_NL_LANGINFO 1+#define HAVE_OPENAT 1+#define HAVE_PATHS_H 1+#define HAVE_PIPE 1+#define HAVE_POLL 1+#define HAVE_POLL_H 1+#define HAVE_POSIX_SPAWNP 1+#define HAVE_PTHREAD_H 1+#define HAVE_PWD_H 1+#define HAVE_READDIR_R 1+#define HAVE_READLINK 1+#define HAVE_READLINKAT 1+#define HAVE_READPASSPHRASE 1+#define HAVE_READPASSPHRASE_H 1+#define HAVE_REGEX_H 1+#define HAVE_SELECT 1+#define HAVE_SETENV 1+#define HAVE_SETLOCALE 1+#define HAVE_SIGACTION 1+#define HAVE_SIGNAL_H 1+#define HAVE_SPAWN_H 1+#define HAVE_STATFS 1+#define HAVE_STATVFS 1+#define HAVE_STDARG_H 1+#define HAVE_STDINT_H 1+#define HAVE_STDLIB_H 1+#define HAVE_STRCHR 1+#define HAVE_STRDUP 1+#define HAVE_STRERROR 1+#define HAVE_STRERROR_R 1+#define HAVE_STRFTIME 1+#define HAVE_STRINGS_H 1+#define HAVE_STRING_H 1+#define HAVE_STRRCHR 1+#define HAVE_STRUCT_STATFS_F_NAMEMAX 1+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1+#define HAVE_STRUCT_STAT_ST_FLAGS 1+#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1+#define HAVE_STRUCT_TM_TM_GMTOFF 1+#define HAVE_SYMLINK 1+#define HAVE_SYS_CDEFS_H 1+#define HAVE_SYS_IOCTL_H 1+#define HAVE_SYS_MOUNT_H 1+#define HAVE_SYS_PARAM_H 1+#define HAVE_SYS_POLL_H 1+#define HAVE_SYS_SELECT_H 1+#define HAVE_SYS_STATVFS_H 1+#define HAVE_SYS_STAT_H 1+#define HAVE_SYS_TIME_H 1+#define HAVE_SYS_TYPES_H 1+#define HAVE_SYS_UTSNAME_H 1+#define HAVE_SYS_WAIT_H 1+#define HAVE_TIMEGM 1+#define HAVE_TIME_H 1+#define HAVE_TZSET 1+#define HAVE_UINTMAX_T 1+#define HAVE_UNISTD_H 1+#define HAVE_UNLINKAT 1+#define HAVE_UNSETENV 1+#define HAVE_UNSIGNED_LONG_LONG 1+#define HAVE_UNSIGNED_LONG_LONG_INT 1+#define HAVE_UTIME 1+#define HAVE_UTIMES 1+#define HAVE_UTIME_H 1+#define HAVE_VFORK 1+#define HAVE_VPRINTF 1+#define HAVE_WCHAR_H 1+#define HAVE_WCHAR_T 1+#define HAVE_WCRTOMB 1+#define HAVE_WCSCMP 1+#define HAVE_WCSCPY 1+#define HAVE_WCSLEN 1+#define HAVE_WCTOMB 1+#define HAVE_WCTYPE_H 1+#define HAVE_WMEMCMP 1+#define HAVE_WMEMCPY 1+#define HAVE_WMEMMOVE 1+#define HAVE_ZLIB_H 1+#define TIME_WITH_SYS_TIME 1++#if __FreeBSD_version >= 1100056+#define HAVE_FUTIMENS 1+#define HAVE_UTIMENSAT 1+#endif++/* FreeBSD 4 and earlier lack intmax_t/uintmax_t */+#if __FreeBSD__ < 5+#define intmax_t int64_t+#define uintmax_t uint64_t+#endif++/* FreeBSD defines for archive_hash.h */+#ifdef WITH_OPENSSL+#define ARCHIVE_CRYPTO_MD5_OPENSSL 1+#define ARCHIVE_CRYPTO_RMD160_OPENSSL 1+#define ARCHIVE_CRYPTO_SHA1_OPENSSL+#define ARCHIVE_CRYPTO_SHA256_OPENSSL 1+#define ARCHIVE_CRYPTO_SHA384_OPENSSL 1+#define ARCHIVE_CRYPTO_SHA512_OPENSSL 1+#else+#define ARCHIVE_CRYPTO_MD5_LIBMD 1+#define ARCHIVE_CRYPTO_SHA1_LIBMD 1+#define ARCHIVE_CRYPTO_SHA256_LIBMD 1+#define ARCHIVE_CRYPTO_SHA512_LIBMD 1+#endif
+ c/filter_fork.h view
@@ -0,0 +1,46 @@+/*-+ * Copyright (c) 2007 Joerg Sonnenberger+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ *+ * $FreeBSD: head/lib/libarchive/filter_fork.h 201087 2009-12-28 02:18:26Z kientzle $+ */++#ifndef FILTER_FORK_H+#define FILTER_FORK_H++#ifndef __LIBARCHIVE_BUILD+#error This header is only to be used internally to libarchive.+#endif++int+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,+#if defined(_WIN32) && !defined(__CYGWIN__)+	HANDLE *out_child);+#else+	pid_t *out_child);+#endif++void+__archive_check_child(int in, int out);++#endif
+ c/filter_fork_posix.c view
@@ -0,0 +1,240 @@+/*-+ * Copyright (c) 2007 Joerg Sonnenberger+ * Copyright (c) 2012 Michihiro NAKAJIMA + * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++/* This capability is only available on POSIX systems. */+#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \+    (defined(HAVE_FORK) || defined(HAVE_VFORK) || defined(HAVE_POSIX_SPAWNP))++__FBSDID("$FreeBSD: head/lib/libarchive/filter_fork.c 182958 2008-09-12 05:33:00Z kientzle $");++#if defined(HAVE_SYS_TYPES_H)+#  include <sys/types.h>+#endif+#ifdef HAVE_ERRNO_H+#  include <errno.h>+#endif+#ifdef HAVE_STRING_H+#  include <string.h>+#endif+#if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))+#  if defined(HAVE_POLL_H)+#    include <poll.h>+#  elif defined(HAVE_SYS_POLL_H)+#    include <sys/poll.h>+#  endif+#elif defined(HAVE_SELECT)+#  if defined(HAVE_SYS_SELECT_H)+#    include <sys/select.h>+#  elif defined(HAVE_UNISTD_H)+#    include <unistd.h>+#  endif+#endif+#ifdef HAVE_FCNTL_H+#  include <fcntl.h>+#endif+#ifdef HAVE_SPAWN_H+#  include <spawn.h>+#endif+#ifdef HAVE_STDLIB_H+#  include <stdlib.h>+#endif+#ifdef HAVE_UNISTD_H+#  include <unistd.h>+#endif++#include "archive.h"+#include "archive_cmdline_private.h"++#include "filter_fork.h"++int+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,+		pid_t *out_child)+{+	pid_t child;+	int stdin_pipe[2], stdout_pipe[2], tmp;+#if HAVE_POSIX_SPAWNP+	posix_spawn_file_actions_t actions;+	int r;+#endif+	struct archive_cmdline *cmdline;++	cmdline = __archive_cmdline_allocate();+	if (cmdline == NULL)+		goto state_allocated;+	if (__archive_cmdline_parse(cmdline, cmd) != ARCHIVE_OK)+		goto state_allocated;++	if (pipe(stdin_pipe) == -1)+		goto state_allocated;+	if (stdin_pipe[0] == 1 /* stdout */) {+		if ((tmp = dup(stdin_pipe[0])) == -1)+			goto stdin_opened;+		close(stdin_pipe[0]);+		stdin_pipe[0] = tmp;+	}+	if (pipe(stdout_pipe) == -1)+		goto stdin_opened;+	if (stdout_pipe[1] == 0 /* stdin */) {+		if ((tmp = dup(stdout_pipe[1])) == -1)+			goto stdout_opened;+		close(stdout_pipe[1]);+		stdout_pipe[1] = tmp;+	}++#if HAVE_POSIX_SPAWNP++	r = posix_spawn_file_actions_init(&actions);+	if (r != 0) {+		errno = r;+		goto stdout_opened;+	}+	r = posix_spawn_file_actions_addclose(&actions, stdin_pipe[1]);+	if (r != 0)+		goto actions_inited;+	r = posix_spawn_file_actions_addclose(&actions, stdout_pipe[0]);+	if (r != 0)+		goto actions_inited;+	/* Setup for stdin. */+	r = posix_spawn_file_actions_adddup2(&actions, stdin_pipe[0], 0);+	if (r != 0)+		goto actions_inited;+	if (stdin_pipe[0] != 0 /* stdin */) {+		r = posix_spawn_file_actions_addclose(&actions, stdin_pipe[0]);+		if (r != 0)+			goto actions_inited;+	}+	/* Setup for stdout. */+	r = posix_spawn_file_actions_adddup2(&actions, stdout_pipe[1], 1);+	if (r != 0)+		goto actions_inited;+	if (stdout_pipe[1] != 1 /* stdout */) {+		r = posix_spawn_file_actions_addclose(&actions, stdout_pipe[1]);+		if (r != 0)+			goto actions_inited;+	}+	r = posix_spawnp(&child, cmdline->path, &actions, NULL,+		cmdline->argv, NULL);+	if (r != 0)+		goto actions_inited;+	posix_spawn_file_actions_destroy(&actions);++#else /* HAVE_POSIX_SPAWNP */++#if HAVE_VFORK+	child = vfork();+#else+	child = fork();+#endif+	if (child == -1)+		goto stdout_opened;+	if (child == 0) {+		close(stdin_pipe[1]);+		close(stdout_pipe[0]);+		if (dup2(stdin_pipe[0], 0 /* stdin */) == -1)+			_exit(254);+		if (stdin_pipe[0] != 0 /* stdin */)+			close(stdin_pipe[0]);+		if (dup2(stdout_pipe[1], 1 /* stdout */) == -1)+			_exit(254);+		if (stdout_pipe[1] != 1 /* stdout */)+			close(stdout_pipe[1]);+		execvp(cmdline->path, cmdline->argv);+		_exit(254);+	}+#endif /* HAVE_POSIX_SPAWNP */++	close(stdin_pipe[0]);+	close(stdout_pipe[1]);++	*child_stdin = stdin_pipe[1];+	fcntl(*child_stdin, F_SETFL, O_NONBLOCK);+	*child_stdout = stdout_pipe[0];+	fcntl(*child_stdout, F_SETFL, O_NONBLOCK);+	__archive_cmdline_free(cmdline);++	*out_child = child;+	return ARCHIVE_OK;++#if HAVE_POSIX_SPAWNP+actions_inited:+	errno = r;+	posix_spawn_file_actions_destroy(&actions);+#endif+stdout_opened:+	close(stdout_pipe[0]);+	close(stdout_pipe[1]);+stdin_opened:+	close(stdin_pipe[0]);+	close(stdin_pipe[1]);+state_allocated:+	__archive_cmdline_free(cmdline);+	return ARCHIVE_FAILED;+}++void+__archive_check_child(int in, int out)+{+#if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))+	struct pollfd fds[2];+	int idx;++	idx = 0;+	if (in != -1) {+		fds[idx].fd = in;+		fds[idx].events = POLLOUT;+		++idx;+	}+	if (out != -1) {+		fds[idx].fd = out;+		fds[idx].events = POLLIN;+		++idx;+	}++	poll(fds, idx, -1); /* -1 == INFTIM, wait forever */+#elif defined(HAVE_SELECT)+	fd_set fds_in, fds_out, fds_error;++	FD_ZERO(&fds_in);+	FD_ZERO(&fds_out);+	FD_ZERO(&fds_error);+	if (out != -1) {+		FD_SET(out, &fds_in);+		FD_SET(out, &fds_error);+	}+	if (in != -1) {+		FD_SET(in, &fds_out);+		FD_SET(in, &fds_error);+	}+	select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL);+#else+	sleep(1);+#endif+}++#endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */
+ c/filter_fork_windows.c view
@@ -0,0 +1,199 @@+/*-+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "archive_platform.h"++#if defined(_WIN32) && !defined(__CYGWIN__)+#include "archive_cmdline_private.h"+#include "archive_string.h"++#include "filter_fork.h"++int+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,+		HANDLE *out_child)+{+	HANDLE childStdout[2], childStdin[2],childStderr;+	SECURITY_ATTRIBUTES secAtts;+	STARTUPINFOA staInfo;+	PROCESS_INFORMATION childInfo;+	struct archive_string cmdline;+	struct archive_string fullpath;+	struct archive_cmdline *acmd;+	char *arg0, *ext;+	int i, l;+	DWORD fl, fl_old;+	HANDLE child;++	childStdout[0] = childStdout[1] = INVALID_HANDLE_VALUE;+	childStdin[0] = childStdin[1] = INVALID_HANDLE_VALUE;+	childStderr = INVALID_HANDLE_VALUE;+	archive_string_init(&cmdline);+	archive_string_init(&fullpath);++	acmd = __archive_cmdline_allocate();+	if (acmd == NULL)+		goto fail;+	if (__archive_cmdline_parse(acmd, cmd) != ARCHIVE_OK)+		goto fail;++	/*+	 * Search the full path of 'path'.+	 * NOTE: This does not need if we give CreateProcessA 'path' as+	 * a part of the cmdline and give CreateProcessA NULL as first+	 * parameter, but I do not like that way.+	 */+	ext = strrchr(acmd->path, '.');+	if (ext == NULL || strlen(ext) > 4)+		/* 'path' does not have a proper extension, so we have to+		 * give SearchPath() ".exe" as the extension. */+		ext = ".exe";+	else+		ext = NULL;/* 'path' has an extension. */++	fl = MAX_PATH;+	do {+		if (archive_string_ensure(&fullpath, fl) == NULL)+			goto fail;+		fl_old = fl;+		fl = SearchPathA(NULL, acmd->path, ext, fl, fullpath.s,+			&arg0);+	} while (fl != 0 && fl > fl_old);+	if (fl == 0)+		goto fail;++	/*+	 * Make a command line.+	 */+	for (l = 0, i = 0;  acmd->argv[i] != NULL; i++) {+		if (i == 0)+			continue;+		l += (int)strlen(acmd->argv[i]) + 1;+	}+	if (archive_string_ensure(&cmdline, l + 1) == NULL)+		goto fail;+	for (i = 0;  acmd->argv[i] != NULL; i++) {+		if (i == 0) {+			const char *p, *sp;++			if ((p = strchr(acmd->argv[i], '/')) != NULL ||+			    (p = strchr(acmd->argv[i], '\\')) != NULL)+				p++;+			else+				p = acmd->argv[i];+			if ((sp = strchr(p, ' ')) != NULL)+				archive_strappend_char(&cmdline, '"');+			archive_strcat(&cmdline, p);+			if (sp != NULL)+				archive_strappend_char(&cmdline, '"');+		} else {+			archive_strappend_char(&cmdline, ' ');+			archive_strcat(&cmdline, acmd->argv[i]);+		}+	}+	if (i <= 1) {+		const char *sp;++		if ((sp = strchr(arg0, ' ')) != NULL)+			archive_strappend_char(&cmdline, '"');+		archive_strcat(&cmdline, arg0);+		if (sp != NULL)+			archive_strappend_char(&cmdline, '"');+	}++	secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);+	secAtts.bInheritHandle = TRUE;+	secAtts.lpSecurityDescriptor = NULL;+	if (CreatePipe(&childStdout[0], &childStdout[1], &secAtts, 0) == 0)+		goto fail;+	if (!SetHandleInformation(childStdout[0], HANDLE_FLAG_INHERIT, 0))+		goto fail;+	if (CreatePipe(&childStdin[0], &childStdin[1], &secAtts, 0) == 0)+		goto fail;+	if (!SetHandleInformation(childStdin[1], HANDLE_FLAG_INHERIT, 0))+		goto fail;+	if (DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE),+	    GetCurrentProcess(), &childStderr, 0, TRUE,+	    DUPLICATE_SAME_ACCESS) == 0)+		goto fail;++	memset(&staInfo, 0, sizeof(staInfo));+	staInfo.cb = sizeof(staInfo);+	staInfo.hStdError = childStderr;+	staInfo.hStdOutput = childStdout[1];+	staInfo.hStdInput = childStdin[0];+	staInfo.wShowWindow = SW_HIDE;+	staInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;+	if (CreateProcessA(fullpath.s, cmdline.s, NULL, NULL, TRUE, 0,+	      NULL, NULL, &staInfo, &childInfo) == 0)+		goto fail;+	WaitForInputIdle(childInfo.hProcess, INFINITE);+	CloseHandle(childInfo.hProcess);+	CloseHandle(childInfo.hThread);++	*child_stdout = _open_osfhandle((intptr_t)childStdout[0], _O_RDONLY);+	*child_stdin = _open_osfhandle((intptr_t)childStdin[1], _O_WRONLY);+	+	child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,+		childInfo.dwProcessId);+	if (child == NULL) // INVALID_HANDLE_VALUE ?+		goto fail;++	*out_child = child;++	CloseHandle(childStdout[1]);+	CloseHandle(childStdin[0]);++	archive_string_free(&cmdline);+	archive_string_free(&fullpath);+	__archive_cmdline_free(acmd);+	return ARCHIVE_OK;++fail:+	if (childStdout[0] != INVALID_HANDLE_VALUE)+		CloseHandle(childStdout[0]);+	if (childStdout[1] != INVALID_HANDLE_VALUE)+		CloseHandle(childStdout[1]);+	if (childStdin[0] != INVALID_HANDLE_VALUE)+		CloseHandle(childStdin[0]);+	if (childStdin[1] != INVALID_HANDLE_VALUE)+		CloseHandle(childStdin[1]);+	if (childStderr != INVALID_HANDLE_VALUE)+		CloseHandle(childStderr);+	archive_string_free(&cmdline);+	archive_string_free(&fullpath);+	__archive_cmdline_free(acmd);+	return ARCHIVE_FAILED;+}++void+__archive_check_child(int in, int out)+{+	(void)in; /* UNUSED */+	(void)out; /* UNUSED */+	Sleep(100);+}++#endif /* _WIN32 && !__CYGWIN__ */
+ c/xxhash.c view
@@ -0,0 +1,521 @@+/*+xxHash - Fast Hash algorithm+Copyright (C) 2012-2014, Yann Collet.+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++* Redistributions of source code must retain the above copyright+notice, this list of conditions and the following disclaimer.+* Redistributions in binary form must reproduce the above+copyright notice, this list of conditions and the following disclaimer+in the documentation and/or other materials provided with the+distribution.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.++You can contact the author at :+- xxHash source repository : http://code.google.com/p/xxhash/+*/+#include "archive_platform.h"++#include <stdlib.h>+#include <string.h>++#include "archive_xxhash.h"++#ifdef HAVE_LIBLZ4++/***************************************+** Tuning parameters+****************************************/+/* Unaligned memory access is automatically enabled for "common" CPU, such as x86.+** For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected.+** If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance.+** You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32).+*/+#if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)+#  define XXH_USE_UNALIGNED_ACCESS 1+#endif++/* XXH_ACCEPT_NULL_INPUT_POINTER :+** If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.+** When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.+** This option has a very small performance cost (only measurable on small inputs).+** By default, this option is disabled. To enable it, uncomment below define :+** #define XXH_ACCEPT_NULL_INPUT_POINTER 1++** XXH_FORCE_NATIVE_FORMAT :+** By default, xxHash library provides endian-independent Hash values, based on little-endian convention.+** Results are therefore identical for little-endian and big-endian CPU.+** This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.+** Should endian-independence be of no importance for your application, you may set the #define below to 1.+** It will improve speed for Big-endian CPU.+** This option has no impact on Little_Endian CPU.+*/+#define XXH_FORCE_NATIVE_FORMAT 0++/***************************************+** Compiler Specific Options+****************************************/+/* Disable some Visual warning messages */+#ifdef _MSC_VER  /* Visual Studio */+#  pragma warning(disable : 4127)      /* disable: C4127: conditional expression is constant */+#endif++#ifdef _MSC_VER    /* Visual Studio */+#  define FORCE_INLINE __forceinline+#else+#  ifdef __GNUC__+#    define FORCE_INLINE inline __attribute__((always_inline))+#  else+#    define FORCE_INLINE inline+#  endif+#endif++/***************************************+** Includes & Memory related functions+****************************************/+#define XXH_malloc malloc+#define XXH_free free+#define XXH_memcpy memcpy+++static unsigned int	  XXH32 (const void*, unsigned int, unsigned int);+static void*		  XXH32_init   (unsigned int);+static XXH_errorcode	  XXH32_update (void*, const void*, unsigned int);+static unsigned int	  XXH32_digest (void*);+/*static int		  XXH32_sizeofState(void);*/+static XXH_errorcode	  XXH32_resetState(void*, unsigned int);+#define       XXH32_SIZEOFSTATE 48+typedef struct { long long ll[(XXH32_SIZEOFSTATE+(sizeof(long long)-1))/sizeof(long long)]; } XXH32_stateSpace_t;+static unsigned int	  XXH32_intermediateDigest (void*);++/***************************************+** Basic Types+****************************************/+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */+# include <stdint.h>+  typedef uint8_t  BYTE;+  typedef uint16_t U16;+  typedef uint32_t U32;+  typedef  int32_t S32;+  typedef uint64_t U64;+#else+  typedef unsigned char      BYTE;+  typedef unsigned short     U16;+  typedef unsigned int       U32;+  typedef   signed int       S32;+  typedef unsigned long long U64;+#endif++#if defined(__GNUC__)  && !defined(XXH_USE_UNALIGNED_ACCESS)+#  define _PACKED __attribute__ ((packed))+#else+#  define _PACKED+#endif++#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)+#  ifdef __IBMC__+#    pragma pack(1)+#  else+#    pragma pack(push, 1)+#  endif+#endif++typedef struct _U32_S { U32 v; } _PACKED U32_S;++#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)+#  pragma pack(pop)+#endif+++/****************************************+** Compiler-specific Functions and Macros+*****************************************/+#define GCC_VERSION ((__GNUC__-0) * 100 + (__GNUC_MINOR__ - 0))++#if GCC_VERSION >= 409+__attribute__((__no_sanitize_undefined__))+#endif+static inline U32 A32(const void * x)+{+    return (((const U32_S *)(x))->v);+}++/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */+#if defined(_MSC_VER)+#  define XXH_rotl32(x,r) _rotl(x,r)+#else+#  define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))+#endif++#if defined(_MSC_VER)     /* Visual Studio */+#  define XXH_swap32 _byteswap_ulong+#elif GCC_VERSION >= 403+#  define XXH_swap32 __builtin_bswap32+#else+static inline U32 XXH_swap32 (U32 x) {+    return  ((x << 24) & 0xff000000 ) |+			((x <<  8) & 0x00ff0000 ) |+			((x >>  8) & 0x0000ff00 ) |+			((x >> 24) & 0x000000ff );}+#endif+++/***************************************+** Constants+****************************************/+#define PRIME32_1   2654435761U+#define PRIME32_2   2246822519U+#define PRIME32_3   3266489917U+#define PRIME32_4    668265263U+#define PRIME32_5    374761393U+++/***************************************+** Architecture Macros+****************************************/+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;+#ifndef XXH_CPU_LITTLE_ENDIAN   /* It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch */+    static const int one = 1;+#   define XXH_CPU_LITTLE_ENDIAN   (*(const char*)(&one))+#endif+++/***************************************+** Macros+****************************************/+#define XXH_STATIC_ASSERT(c)   { enum { XXH_static_assert = 1/(!!(c)) }; }    /* use only *after* variable declarations */+++/*****************************+** Memory reads+******************************/+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;++static+FORCE_INLINE U32 XXH_readLE32_align(const U32* ptr, XXH_endianess endian, XXH_alignment align)+{+    if (align==XXH_unaligned)+        return endian==XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr));+    else+        return endian==XXH_littleEndian ? *ptr : XXH_swap32(*ptr);+}++static+FORCE_INLINE U32 XXH_readLE32(const U32* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); }+++/*****************************+** Simple Hash Functions+******************************/+static+FORCE_INLINE U32 XXH32_endian_align(const void* input, unsigned int len, U32 seed, XXH_endianess endian, XXH_alignment align)+{+    const BYTE* p = (const BYTE*)input;+    const BYTE* bEnd = p + len;+    U32 h32;+#define XXH_get32bits(p) XXH_readLE32_align((const U32*)p, endian, align)++#ifdef XXH_ACCEPT_NULL_INPUT_POINTER+    if (p==NULL) { len=0; bEnd=p=(const BYTE*)(size_t)16; }+#endif++    if (len>=16)+    {+        const BYTE* const limit = bEnd - 16;+        U32 v1 = seed + PRIME32_1 + PRIME32_2;+        U32 v2 = seed + PRIME32_2;+        U32 v3 = seed + 0;+        U32 v4 = seed - PRIME32_1;++        do+        {+            v1 += XXH_get32bits(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;+            v2 += XXH_get32bits(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;+            v3 += XXH_get32bits(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;+            v4 += XXH_get32bits(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;+        } while (p<=limit);++        h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);+    }+    else+    {+        h32  = seed + PRIME32_5;+    }++    h32 += (U32) len;++    while (p<=bEnd-4)+    {+        h32 += XXH_get32bits(p) * PRIME32_3;+        h32  = XXH_rotl32(h32, 17) * PRIME32_4 ;+        p+=4;+    }++    while (p<bEnd)+    {+        h32 += (*p) * PRIME32_5;+        h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;+        p++;+    }++    h32 ^= h32 >> 15;+    h32 *= PRIME32_2;+    h32 ^= h32 >> 13;+    h32 *= PRIME32_3;+    h32 ^= h32 >> 16;++    return h32;+}+++U32 XXH32(const void* input, unsigned int len, U32 seed)+{+#if 0+    // Simple version, good for code maintenance, but unfortunately slow for small inputs+    void* state = XXH32_init(seed);+    XXH32_update(state, input, len);+    return XXH32_digest(state);+#else+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++#  if !defined(XXH_USE_UNALIGNED_ACCESS)+    if ((((size_t)input) & 3) == 0)   /* Input is aligned, let's leverage the speed advantage */+    {+        if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+            return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);+        else+            return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);+    }+#  endif++    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+        return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);+    else+        return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);+#endif+}++/*****************************+** Advanced Hash Functions+******************************/++struct XXH_state32_t+{+    U64 total_len;+    U32 seed;+    U32 v1;+    U32 v2;+    U32 v3;+    U32 v4;+    int memsize;+    char memory[16];+};++#if 0+static+int XXH32_sizeofState(void)+{+    XXH_STATIC_ASSERT(XXH32_SIZEOFSTATE >= sizeof(struct XXH_state32_t));   /* A compilation error here means XXH32_SIZEOFSTATE is not large enough */+    return sizeof(struct XXH_state32_t);+}+#endif++static+XXH_errorcode XXH32_resetState(void* state_in, U32 seed)+{+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;+    state->seed = seed;+    state->v1 = seed + PRIME32_1 + PRIME32_2;+    state->v2 = seed + PRIME32_2;+    state->v3 = seed + 0;+    state->v4 = seed - PRIME32_1;+    state->total_len = 0;+    state->memsize = 0;+    return XXH_OK;+}++static+void* XXH32_init (U32 seed)+{+    void* state = XXH_malloc (sizeof(struct XXH_state32_t));+    XXH32_resetState(state, seed);+    return state;+}++static+FORCE_INLINE XXH_errorcode XXH32_update_endian (void* state_in, const void* input, int len, XXH_endianess endian)+{+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;+    const BYTE* p = (const BYTE*)input;+    const BYTE* const bEnd = p + len;++#ifdef XXH_ACCEPT_NULL_INPUT_POINTER+    if (input==NULL) return XXH_ERROR;+#endif++    state->total_len += len;++    if (state->memsize + len < 16)   /* fill in tmp buffer */+    {+        XXH_memcpy(state->memory + state->memsize, input, len);+        state->memsize +=  len;+        return XXH_OK;+    }++    if (state->memsize)   /* some data left from previous update */+    {+        XXH_memcpy(state->memory + state->memsize, input, 16-state->memsize);+        {+            const U32* p32 = (const U32*)state->memory;+            state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++;+            state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++;+            state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++;+            state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++;+        }+        p += 16-state->memsize;+        state->memsize = 0;+    }++    if (p <= bEnd-16)+    {+        const BYTE* const limit = bEnd - 16;+        U32 v1 = state->v1;+        U32 v2 = state->v2;+        U32 v3 = state->v3;+        U32 v4 = state->v4;++        do+        {+            v1 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;+            v2 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;+            v3 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;+            v4 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;+        } while (p<=limit);++        state->v1 = v1;+        state->v2 = v2;+        state->v3 = v3;+        state->v4 = v4;+    }++    if (p < bEnd)+    {+        XXH_memcpy(state->memory, p, bEnd-p);+        state->memsize = (int)(bEnd-p);+    }++    return XXH_OK;+}++static+XXH_errorcode XXH32_update (void* state_in, const void* input, unsigned int len)+{+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+        return XXH32_update_endian(state_in, input, len, XXH_littleEndian);+    else+        return XXH32_update_endian(state_in, input, len, XXH_bigEndian);+}++++static+FORCE_INLINE U32 XXH32_intermediateDigest_endian (void* state_in, XXH_endianess endian)+{+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;+    const BYTE * p = (const BYTE*)state->memory;+    BYTE* bEnd = (BYTE*)state->memory + state->memsize;+    U32 h32;++    if (state->total_len >= 16)+    {+        h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);+    }+    else+    {+        h32  = state->seed + PRIME32_5;+    }++    h32 += (U32) state->total_len;++    while (p<=bEnd-4)+    {+        h32 += XXH_readLE32((const U32*)p, endian) * PRIME32_3;+        h32  = XXH_rotl32(h32, 17) * PRIME32_4;+        p+=4;+    }++    while (p<bEnd)+    {+        h32 += (*p) * PRIME32_5;+        h32 = XXH_rotl32(h32, 11) * PRIME32_1;+        p++;+    }++    h32 ^= h32 >> 15;+    h32 *= PRIME32_2;+    h32 ^= h32 >> 13;+    h32 *= PRIME32_3;+    h32 ^= h32 >> 16;++    return h32;+}++static+U32 XXH32_intermediateDigest (void* state_in)+{+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+        return XXH32_intermediateDigest_endian(state_in, XXH_littleEndian);+    else+        return XXH32_intermediateDigest_endian(state_in, XXH_bigEndian);+}++static+U32 XXH32_digest (void* state_in)+{+    U32 h32 = XXH32_intermediateDigest(state_in);++    XXH_free(state_in);++    return h32;+}++const+struct archive_xxhash __archive_xxhash = {+	XXH32,+	XXH32_init,+	XXH32_update,+	XXH32_digest+};+#else++/*+ * Define an empty version of the struct if we aren't using the LZ4 library.+ */+const+struct archive_xxhash __archive_xxhash = {+	NULL,+	NULL,+	NULL,+	NULL+};++#endif /* HAVE_LIBLZ4 */
libarchive.cabal view
@@ -1,19 +1,26 @@-cabal-version:   1.18-name:            libarchive-version:         2.2.5.0-license:         BSD3-license-file:    LICENSE-copyright:       Copyright: (c) 2018-2020 Vanessa McHale-maintainer:      vamchale@gmail.com-author:          Vanessa McHale-tested-with:     ghc ==8.4.4 ghc ==8.6.5 ghc ==8.8.3 ghc ==8.10.1-bug-reports:     https://github.com/vmchale/libarchive/issues-synopsis:        Haskell interface to libarchive+cabal-version:      3.0+name:               libarchive+version:            2.2.5.1+license:            BSD-3-Clause+license-file:       LICENSE+copyright:          Copyright: (c) 2018-2020 Vanessa McHale+maintainer:         vamchale@gmail.com+author:             Vanessa McHale+tested-with:        ghc ==8.4.4 ghc ==8.6.5 ghc ==8.8.3 ghc ==8.10.1+bug-reports:        https://github.com/vmchale/libarchive/issues+synopsis:           Haskell interface to libarchive description:     Haskell bindings for [libarchive](https://www.libarchive.org/). Provides the ability to unpack archives, including the ability to unpack archives lazily. -category:        Codec-build-type:      Custom+category:           Codec+build-type:         Custom+extra-source-files:+    c/autoconf-darwin/config.h+    c/autoconf-linux/config.h+    c/autoconf-freebsd/config.h+    c/*.c+    c/*.h+ extra-doc-files:     README.md     CHANGELOG.md@@ -37,6 +44,13 @@     description: Run low-memory version of test suite     default:     False +flag static+    description:+        Use the bundled sources (this is default for windows and darwin)++    default:     False+    manual:      True+ library     exposed-modules:         Codec.Archive@@ -44,8 +58,7 @@         Codec.Archive.Foreign.Archive         Codec.Archive.Foreign.ArchiveEntry -    pkgconfig-depends: libarchive (==3.4.0 || >3.4.0) && <4.0-    hs-source-dirs:    src+    hs-source-dirs:   src     other-modules:         Codec.Archive.Foreign.Archive.Macros         Codec.Archive.Foreign.ArchiveEntry.Macros@@ -60,8 +73,8 @@         Codec.Archive.Common         Codec.Archive.Monad -    default-language:  Haskell2010-    other-extensions:  DeriveGeneric DeriveAnyClass+    default-language: Haskell2010+    other-extensions: DeriveGeneric DeriveAnyClass     ghc-options:         -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates         -Wredundant-constraints@@ -84,6 +97,158 @@      if impl(ghc >=8.10)         ghc-options: -Wunused-packages++    if flag(static)+        cc-options:   -std=gnu99 -DHAVE_CONFIG_H+        c-sources:+            c/archive_acl.c+            c/archive_blake2sp_ref.c+            c/archive_blake2s_ref.c+            c/archive_check_magic.c+            c/archive_cmdline.c+            c/archive_cryptor.c+            c/archive_digest.c+            c/archive_disk_acl_darwin.c+            c/archive_disk_acl_freebsd.c+            c/archive_disk_acl_linux.c+            c/archive_disk_acl_sunos.c+            c/archive_entry.c+            c/archive_entry_copy_bhfi.c+            c/archive_entry_copy_stat.c+            c/archive_entry_link_resolver.c+            c/archive_entry_sparse.c+            c/archive_entry_stat.c+            c/archive_entry_strmode.c+            c/archive_entry_xattr.c+            c/archive_getdate.c+            c/archive_hmac.c+            c/archive_match.c+            c/archive_options.c+            c/archive_pack_dev.c+            c/archive_pathmatch.c+            c/archive_ppmd7.c+            c/archive_ppmd8.c+            c/archive_random.c+            c/archive_rb.c+            c/archive_read_add_passphrase.c+            c/archive_read_append_filter.c+            c/archive_read.c+            c/archive_read_data_into_fd.c+            c/archive_read_disk_entry_from_file.c+            c/archive_read_disk_posix.c+            c/archive_read_disk_set_standard_lookup.c+            c/archive_read_disk_windows.c+            c/archive_read_extract2.c+            c/archive_read_extract.c+            c/archive_read_open_fd.c+            c/archive_read_open_file.c+            c/archive_read_open_filename.c+            c/archive_read_open_memory.c+            c/archive_read_set_format.c+            c/archive_read_set_options.c+            c/archive_read_support_filter_all.c+            c/archive_read_support_filter_bzip2.c+            c/archive_read_support_filter_compress.c+            c/archive_read_support_filter_grzip.c+            c/archive_read_support_filter_gzip.c+            c/archive_read_support_filter_lrzip.c+            c/archive_read_support_filter_lz4.c+            c/archive_read_support_filter_lzop.c+            c/archive_read_support_filter_none.c+            c/archive_read_support_filter_program.c+            c/archive_read_support_filter_rpm.c+            c/archive_read_support_filter_uu.c+            c/archive_read_support_filter_xz.c+            c/archive_read_support_filter_zstd.c+            c/archive_read_support_format_7zip.c+            c/archive_read_support_format_all.c+            c/archive_read_support_format_ar.c+            c/archive_read_support_format_by_code.c+            c/archive_read_support_format_cab.c+            c/archive_read_support_format_cpio.c+            c/archive_read_support_format_empty.c+            c/archive_read_support_format_iso9660.c+            c/archive_read_support_format_lha.c+            c/archive_read_support_format_mtree.c+            c/archive_read_support_format_rar5.c+            c/archive_read_support_format_rar.c+            c/archive_read_support_format_raw.c+            c/archive_read_support_format_tar.c+            c/archive_read_support_format_warc.c+            c/archive_read_support_format_xar.c+            c/archive_read_support_format_zip.c+            c/archive_string.c+            c/archive_string_sprintf.c+            c/archive_util.c+            c/archive_version_details.c+            c/archive_virtual.c+            c/archive_windows.c+            c/archive_write_add_filter_b64encode.c+            c/archive_write_add_filter_by_name.c+            c/archive_write_add_filter_bzip2.c+            c/archive_write_add_filter.c+            c/archive_write_add_filter_compress.c+            c/archive_write_add_filter_grzip.c+            c/archive_write_add_filter_gzip.c+            c/archive_write_add_filter_lrzip.c+            c/archive_write_add_filter_lz4.c+            c/archive_write_add_filter_lzop.c+            c/archive_write_add_filter_none.c+            c/archive_write_add_filter_program.c+            c/archive_write_add_filter_uuencode.c+            c/archive_write_add_filter_xz.c+            c/archive_write_add_filter_zstd.c+            c/archive_write.c+            c/archive_write_disk_posix.c+            c/archive_write_disk_set_standard_lookup.c+            c/archive_write_disk_windows.c+            c/archive_write_open_fd.c+            c/archive_write_open_file.c+            c/archive_write_open_filename.c+            c/archive_write_open_memory.c+            c/archive_write_set_format_7zip.c+            c/archive_write_set_format_ar.c+            c/archive_write_set_format_by_name.c+            c/archive_write_set_format.c+            c/archive_write_set_format_cpio.c+            c/archive_write_set_format_cpio_newc.c+            c/archive_write_set_format_filter_by_ext.c+            c/archive_write_set_format_gnutar.c+            c/archive_write_set_format_iso9660.c+            c/archive_write_set_format_mtree.c+            c/archive_write_set_format_pax.c+            c/archive_write_set_format_raw.c+            c/archive_write_set_format_shar.c+            c/archive_write_set_format_ustar.c+            c/archive_write_set_format_v7tar.c+            c/archive_write_set_format_warc.c+            c/archive_write_set_format_xar.c+            c/archive_write_set_format_zip.c+            c/archive_write_set_options.c+            c/archive_write_set_passphrase.c+            c/filter_fork_posix.c+            c/filter_fork_windows.c+            c/xxhash.c++        include-dirs: c/++        if os(osx)+            include-dirs: c/autoconf-darwin++        else+            if os(linux)+                include-dirs: c/autoconf-linux++            else+                if os(freebsd)+                    include-dirs: c/autoconf-freebsd++                else+                    buildable:     False+                    build-depends: unbuildable <0++    else+        pkgconfig-depends: libarchive (==3.4.0 || >3.4.0) && <4.0  test-suite libarchive-test     type:               exitcode-stdio-1.0
mem/Mem.hs view
@@ -10,5 +10,5 @@ main = readArc  readArc :: IO ()-readArc = either throw forceList =<< runArchiveM+readArc = forceList =<< throwArchiveM     (readArchiveFile "test/data/llvm-9.0.0.src.tar")
src/Codec/Archive/Pack/Lazy.hs view
@@ -101,3 +101,4 @@             modifyIORef' bsRef (`DL.snoc` bsl)             pure bytesRead           doNothing _ _ = pure ArchiveOk+          -- FIXME: this part isn't sufficiently lazy
src/Codec/Archive/Types/Foreign.chs view
@@ -35,6 +35,7 @@ import           Control.DeepSeq    (NFData) import           Control.Exception  (Exception) import           Data.Bits          (Bits (..))+import           Data.Semigroup     (Semigroup (..)) import           Foreign.C.String   (CString) import           Foreign.C.Types    (CInt, CSize) import           Foreign.Ptr        (Ptr)