]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Add support for 7-Zip.
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sun, 27 Nov 2011 00:58:45 +0000 (19:58 -0500)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sun, 27 Nov 2011 00:58:45 +0000 (19:58 -0500)
This new 7Zip reader supports COPY,Deflate,BZ2,LZMA and LZMA2.
This also supports BCJ + LZMA2, DELTA + LZMA and DELTA + LZMA2.
(BCJ + LZMA is not supported, there is some problems to support it.)

This 7Zip reader cannot work on stream because of its format:
its header is placed at the bottom of the archive file, so we
have to call seek().

SVN-Revision: 3844

22 files changed:
Makefile.am
libarchive/CMakeLists.txt
libarchive/archive.h
libarchive/archive_read_support_format_7zip.c [new file with mode: 0644]
libarchive/archive_read_support_format_all.c
libarchive/test/CMakeLists.txt
libarchive/test/test_archive_read_support.c
libarchive/test/test_fuzz.c
libarchive/test/test_read_format_7zip.c [new file with mode: 0644]
libarchive/test/test_read_format_7zip_bcj_lzma2.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_bzip2.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_copy.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_copy_2.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_deflate.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_delta_lzma1.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_delta_lzma2.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_empty_archive.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_empty_file.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_lzma1.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_lzma1_2.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_lzma1_lzma2.7z.uu [new file with mode: 0644]
libarchive/test/test_read_format_7zip_lzma2.7z.uu [new file with mode: 0644]

index 7bda16b6e7edc326efae1843e07420e063f436da..c75f19397d1e3b6839cc6f6822fef88e4eaf7b78 100644 (file)
@@ -133,6 +133,7 @@ libarchive_la_SOURCES=                                              \
        libarchive/archive_read_support_filter_rpm.c            \
        libarchive/archive_read_support_filter_uu.c             \
        libarchive/archive_read_support_filter_xz.c             \
+       libarchive/archive_read_support_format_7zip.c           \
        libarchive/archive_read_support_format_all.c            \
        libarchive/archive_read_support_format_ar.c             \
        libarchive/archive_read_support_format_cab.c            \
@@ -304,6 +305,7 @@ libarchive_test_SOURCES=                                    \
        libarchive/test/test_read_disk_entry_from_file.c        \
        libarchive/test/test_read_extract.c                     \
        libarchive/test/test_read_file_nonexistent.c            \
+       libarchive/test/test_read_format_7zip.c                 \
        libarchive/test/test_read_format_ar.c                   \
        libarchive/test/test_read_format_cab.c                  \
        libarchive/test/test_read_format_cab_filename.c         \
@@ -452,6 +454,19 @@ libarchive_test_EXTRA_DIST=\
        libarchive/test/test_fuzz.cab.uu                                \
        libarchive/test/test_fuzz.lzh.uu                                \
        libarchive/test/test_pax_filename_encoding.tar.uu               \
+       libarchive/test/test_read_format_7zip_bcj_lzma2.7z.uu           \
+       libarchive/test/test_read_format_7zip_bzip2.7z.uu               \
+       libarchive/test/test_read_format_7zip_copy.7z.uu                \
+       libarchive/test/test_read_format_7zip_copy_2.7z.uu              \
+       libarchive/test/test_read_format_7zip_deflate.7z.uu             \
+       libarchive/test/test_read_format_7zip_delta_lzma1.7z.uu         \
+       libarchive/test/test_read_format_7zip_delta_lzma2.7z.uu         \
+       libarchive/test/test_read_format_7zip_empty_archive.7z.uu       \
+       libarchive/test/test_read_format_7zip_empty_file.7z.uu          \
+       libarchive/test/test_read_format_7zip_lzma1.7z.uu               \
+       libarchive/test/test_read_format_7zip_lzma1_2.7z.uu             \
+       libarchive/test/test_read_format_7zip_lzma1_lzma2.7z.uu         \
+       libarchive/test/test_read_format_7zip_lzma2.7z.uu               \
        libarchive/test/test_read_format_ar.ar.uu                       \
        libarchive/test/test_read_format_cab_1.cab.uu                   \
        libarchive/test/test_read_format_cab_2.cab.uu                   \
index 28ee099058246409302609f0b67b6bb67252bd07..80c1158a72f42cd8aabf8ca8f5b063c4ac5da4d0 100644 (file)
@@ -59,6 +59,7 @@ SET(libarchive_SOURCES
   archive_read_support_filter_rpm.c
   archive_read_support_filter_uu.c
   archive_read_support_filter_xz.c
+  archive_read_support_format_7zip.c
   archive_read_support_format_all.c
   archive_read_support_format_ar.c
   archive_read_support_format_cab.c
index c0bb26d74ba516b90bca5ee46a1e883ae6044fda..c509b360390941322565a43bf61e2f95ccf5c06b 100644 (file)
@@ -286,6 +286,7 @@ typedef int archive_close_callback(struct archive *, void *_client_data);
 #define        ARCHIVE_FORMAT_LHA                      0xB0000
 #define        ARCHIVE_FORMAT_CAB                      0xC0000
 #define        ARCHIVE_FORMAT_RAR                      0xD0000
+#define        ARCHIVE_FORMAT_7ZIP                     0xE0000
 
 /*-
  * Basic outline for reading an archive:
@@ -345,6 +346,7 @@ __LA_DECL int archive_read_support_filter_rpm(struct archive *);
 __LA_DECL int archive_read_support_filter_uu(struct archive *);
 __LA_DECL int archive_read_support_filter_xz(struct archive *);
 
+__LA_DECL int archive_read_support_format_7zip(struct archive *);
 __LA_DECL int archive_read_support_format_all(struct archive *);
 __LA_DECL int archive_read_support_format_ar(struct archive *);
 __LA_DECL int archive_read_support_format_cab(struct archive *);
diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c
new file mode 100644 (file)
index 0000000..f93c3be
--- /dev/null
@@ -0,0 +1,2574 @@
+/*-
+ * 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
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <time.h>
+#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_private.h"
+#include "archive_read_private.h"
+#include "archive_endian.h"
+
+#ifndef HAVE_ZLIB_H
+#include "archive_crc32.h"
+#endif
+
+
+#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
+
+struct _7z_digests {
+       unsigned char   *defineds;
+       uint32_t        *digests;
+};
+
+
+struct _7z_folder {
+       uint64_t                 numCoders;
+       struct {
+               size_t           codecIdSize;
+               unsigned char   *codecId;
+               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;
+       uint64_t                *positions;
+};
+
+struct _7z_substream_info {
+       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;
+};
+
+enum _7z_codec {
+       _7Z_COPY,
+       _7Z_LZMA,
+       _7Z_LZMA2,
+       _7Z_DEFLATE,
+       _7Z_BZ2,
+       _7Z_PPMD,
+       _7Z_DELTA,
+       _7Z_CRYPTO,
+       _7Z_X86,
+       _7Z_POWERPC,
+       _7Z_IA64,
+       _7Z_ARM,
+       _7Z_ARMTHUMB,
+       _7Z_SPARC,
+       /**/
+       _7Z_UNKNOWN,
+};
+
+struct _7zip_entry {
+       size_t                   name_len;
+       const 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;
+       uint64_t                 header_offset;
+
+       /* 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;
+       size_t                   entry_bytes_unconsumed;
+       uint64_t                 entry_bytes_remaining;
+
+       /* * */
+       int64_t                  offset;
+       unsigned                 folder_index;
+       unsigned                 pack_stream_index;
+       uint64_t                 pack_stream_remaining;
+       uint64_t                 pack_stream_bytes_remaining;
+       uint64_t                 unpack_stream_bytes_remaining;
+       uint64_t                 uncompressed_buffer_bytes_remaining;
+       unsigned char           *uncompressed_buff_pointer;
+
+       /* These count the number of bytes actually read for the entry. */
+       int64_t                  entry_uncompressed_bytes_read;
+
+       /* Running CRC32 of the decompressed data */
+       unsigned long            entry_crc32;
+
+       /* Flags to mark progress of decompression. */
+       char                     end_of_entry;
+
+       unsigned char           *uncompressed_buffer;
+       size_t                   uncompressed_buffer_size;
+       enum _7z_codec           codec;
+#ifdef HAVE_LZMA_H
+       lzma_stream              lzstream;
+       int                      lzstream_valid;
+#endif
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+       bz_stream                bzstream;
+       int                      bzstream_valid;
+#endif
+#ifdef HAVE_ZLIB_H
+       z_stream                 stream;
+       int                      stream_valid;
+#endif
+
+       struct archive_string_conv *sconv;
+       char                     format_name[64];
+};
+
+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 enum _7z_codec decode_codec_id(const unsigned char *, size_t);
+static int     decompress(struct archive_read *, struct _7zip *,
+                   void *, size_t *, const void *, size_t *);
+static ssize_t extract_pack_stream(struct archive_read *);
+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);
+static int     init_decompression(struct archive_read *, struct _7zip *,
+                   struct _7z_folder *);
+static int     parse_7zip_uint64(const unsigned char *, size_t, uint64_t *);
+static int     read_Bools(unsigned char *, size_t, const unsigned char *,
+                   size_t);
+static int     read_CodersInfo(struct _7z_coders_info *,
+                   const unsigned char *, size_t);
+static int     read_Digests(struct _7z_digests *, size_t,
+                   const unsigned char *, size_t);
+static int     read_Folder(struct _7z_folder *, const unsigned char *,
+                   size_t);
+static int     read_Header(struct _7zip *, struct _7z_header_info *,
+                   const unsigned char *, size_t);
+static int     read_PackInfo(struct _7z_pack_info *, const unsigned char *,
+                   size_t);
+static int     read_StreamsInfo(struct _7zip *, struct _7z_stream_info *,
+                   const unsigned char *, size_t);
+static int     read_SubStreamsInfo(struct _7z_substream_info *,
+                   struct _7z_folder *, size_t, const unsigned char *,
+                   size_t);
+static int     read_Times(struct _7zip *, struct _7z_header_info *, int,
+                   const unsigned char *, size_t);
+static ssize_t read_stream(struct archive_read *, const void **, size_t);
+static int64_t skip_stream(struct archive_read *, size_t);
+static int     slurp_central_directory(struct archive_read *, struct _7zip *,
+                   struct _7z_header_info *);
+static int     unknown_codec(struct archive_read *, const unsigned char *,
+                   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);
+       }
+
+       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,
+           archive_read_format_7zip_cleanup);
+
+       if (r != ARCHIVE_OK)
+               free(zip);
+       return (ARCHIVE_OK);
+}
+
+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 (memcmp(p, "7z\xBC\xAF\x27\x1C", 6) != 0)
+               return (0);
+
+       /* This is just a tiny bit higher than the maximum returned by
+          the streaming 7Zip bidder.  This ensures that the more accurate
+          seeking 7Zip parser wins whenever seek is available. */
+       return (32);
+}
+
+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;
+
+       a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;
+       if (a->archive.archive_format_name == NULL)
+               a->archive.archive_format_name = "7Zip";
+
+       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 = zip->numFiles;
+               zip->entry = zip->entries;
+       } else {
+               ++zip->entry;
+       }
+       zip_entry = zip->entry;
+
+       if (zip->entries_remaining <= 0)
+               return ARCHIVE_EOF;
+       --zip->entries_remaining;
+
+       zip->entry_offset = 0;
+       zip->end_of_entry = 0;
+       zip->entry_uncompressed_bytes_read = 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);
+       }
+
+       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 != -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;
+
+       /* Set up a more descriptive format name. */
+       sprintf(zip->format_name, "7Zip");
+       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;
+
+       zip = (struct _7zip *)(a->format->data);
+
+       if (zip->entry_bytes_unconsumed) {
+               __archive_read_consume(a, zip->entry_bytes_unconsumed);
+               zip->offset += zip->entry_bytes_unconsumed;
+               zip->entry_bytes_unconsumed = 0;
+       }
+
+       /*
+        * If we hit end-of-entry last time, clean up and return
+        * ARCHIVE_EOF this time.
+        */
+       if (zip->end_of_entry) {
+               *offset = zip->entry_uncompressed_bytes_read;
+               *size = 0;
+               *buff = NULL;
+               return (ARCHIVE_EOF);
+       }
+
+       bytes = read_stream(a, buff, zip->entry_bytes_remaining);
+       if (bytes < 0)
+               return ((int)bytes);
+       if (bytes == 0) {
+               archive_set_error(&a->archive,
+                   ARCHIVE_ERRNO_FILE_FORMAT,
+                   "Truncated 7Zip 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, 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,
+                           "7Zip bad CRC: 0x%lx should be 0x%lx",
+                           (unsigned long)zip->entry_crc32,
+                           (unsigned long)zip->si.ss.digests[
+                                       zip->entry->ssIndex]);
+                       return (ARCHIVE_WARN);
+               }
+       }
+
+       *size = bytes;
+       *offset = zip->entry_offset;
+       zip->entry_offset += bytes;
+       zip->entry_uncompressed_bytes_read += bytes;
+
+       return (ARCHIVE_OK);
+}
+
+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->entry_bytes_unconsumed) {
+               __archive_read_consume(a, zip->entry_bytes_unconsumed);
+               zip->offset += zip->entry_bytes_unconsumed;
+               zip->entry_bytes_unconsumed = 0;
+       }
+
+       /* 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, 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);
+       (a->format->data) = NULL;
+       return (ARCHIVE_OK);
+}
+
+#ifdef HAVE_LZMA_H
+
+/*
+ * Set an error code and choose an error message
+ */
+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 enum _7z_codec
+decode_codec_id(const unsigned char *codecId, size_t id_size)
+{
+
+       if (id_size == 1) {
+               if (codecId[0] == 0)
+                       return (_7Z_COPY);
+               if (codecId[0] == 0x21)
+                       return (_7Z_LZMA2);
+               if (codecId[0] == 0x03)
+                       return (_7Z_DELTA);
+       } else if (id_size == 3) {
+               if (memcmp(codecId, "\x03\x01\x01", 3) == 0)
+                       return (_7Z_LZMA);
+               if (memcmp(codecId, "\x04\x02\x02", 3) == 0)
+                       return (_7Z_BZ2);
+               if (memcmp(codecId, "\x04\x01\x08", 3) == 0)
+                       return (_7Z_DEFLATE);
+               if (memcmp(codecId, "\x03\x04\x01", 3) == 0)
+                       return (_7Z_PPMD);
+       } else if (id_size == 4) {
+               if (memcmp(codecId, "\x06\xf1\x07\x01", 4) == 0)
+                       return (_7Z_CRYPTO);
+               if (memcmp(codecId, "\x03\x03\x01\x03", 4) == 0)
+                       return (_7Z_X86);
+               if (memcmp(codecId, "\x03\x03\x02\x05", 4) == 0)
+                       return (_7Z_POWERPC);
+               if (memcmp(codecId, "\x03\x03\x04\x01", 4) == 0)
+                       return (_7Z_IA64);
+               if (memcmp(codecId, "\x03\x03\x05\x01", 4) == 0)
+                       return (_7Z_ARM);
+               if (memcmp(codecId, "\x03\x03\x07\x01", 4) == 0)
+                       return (_7Z_ARMTHUMB);
+               if (memcmp(codecId, "\x03\x03\x08\x05", 4) == 0)
+                       return (_7Z_SPARC);
+       }
+       return (_7Z_UNKNOWN);
+}
+
+static int
+unknown_codec(struct archive_read *a, const unsigned char *codecId,
+    size_t id_size)
+{
+       unsigned char tmp[4];
+       unsigned i;
+
+       memset(tmp, 0, sizeof(tmp));
+       for (i = 0; i < sizeof(tmp); i++) {
+               if (i >= id_size)
+                       break;
+               tmp[i] = codecId[i];
+       }
+       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+           "Unknown supported filter ID %d bytes %02X %02X %02X %02X",
+           id_size, tmp[0], tmp[1], tmp[2], tmp[3]);
+       return (ARCHIVE_FAILED);
+}
+
+static int
+init_decompression(struct archive_read *a, struct _7zip *zip,
+    struct _7z_folder *folder)
+{
+       int r;
+       enum _7z_codec codec;
+
+       codec = decode_codec_id(folder->coders[0].codecId,
+                   folder->coders[0].codecIdSize);
+
+       switch (zip->codec = codec) {
+       case _7Z_COPY:
+               return (ARCHIVE_OK);
+
+       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];
+               int fi = 0;
+
+               if (zip->lzstream_valid) {
+                       lzma_end(&(zip->lzstream));
+                       zip->lzstream_valid = 0;
+               }
+
+               /*
+                * NOTE: BCJ+LZMA2 is OK, but it seems BCJ+LZMA does not
+                * correctly work. With BCJ, liblzma did not return all
+                * bytes I expected; it was four or three bytes lost.
+                */
+               if (folder->numCoders >= 2) {
+                       codec = decode_codec_id(folder->coders[1].codecId,
+                                   folder->coders[1].codecIdSize);
+
+                       filters[fi].options = NULL;
+                       switch (codec) {
+                       case _7Z_X86:
+#if 1
+                               if (zip->codec == _7Z_LZMA) {
+                                       archive_set_error(&a->archive,
+                                           ARCHIVE_ERRNO_MISC,
+                                           "LZMA + BCJ is unsupported");
+                                       return (ARCHIVE_FAILED);
+                               }
+#endif
+                               filters[fi].id = LZMA_FILTER_X86;
+                               break;
+                       case _7Z_DELTA:
+                               filters[fi].id = LZMA_FILTER_DELTA;
+                               memset(&delta_opt, 0, sizeof(delta_opt));
+                               delta_opt.type = LZMA_DELTA_TYPE_BYTE;
+                               delta_opt.dist = 1;
+                               filters[fi].options = &delta_opt;
+                               break;
+                       /* Following filters have not been tested yet. */
+                       case _7Z_POWERPC:
+                               filters[fi].id = LZMA_FILTER_POWERPC;
+                               break;
+                       case _7Z_IA64:
+                               filters[fi].id = LZMA_FILTER_IA64;
+                               break;
+                       case _7Z_ARM:
+                               filters[fi].id = LZMA_FILTER_ARM;
+                               break;
+                       case _7Z_ARMTHUMB:
+                               filters[fi].id = LZMA_FILTER_ARMTHUMB;
+                               break;
+                       case _7Z_SPARC:
+                               filters[fi].id = LZMA_FILTER_SPARC;
+                               break;
+                       default:
+                               return unknown_codec(a,
+                                   folder->coders[1].codecId,
+                                   folder->coders[1].codecIdSize);
+                       }
+                       fi++;
+               }
+
+               if (zip->codec == _7Z_LZMA2)
+                       filters[fi].id = LZMA_FILTER_LZMA2;
+               else
+                       filters[fi].id = LZMA_FILTER_LZMA1;
+               filters[fi].options = NULL;
+               r = lzma_properties_decode(&filters[fi], NULL,
+                   folder->coders[0].properties,
+                   folder->coders[0].propertiesSize);
+               if (r != LZMA_OK) {
+                       set_error(a, r);
+                       return (ARCHIVE_FAILED);
+               }
+               fi++;
+
+               filters[fi].id = LZMA_VLI_UNKNOWN;
+               filters[fi].options = NULL;
+               r = lzma_raw_decoder(&(zip->lzstream), filters);
+               if (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 (folder->numCoders >= 2) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_MISC,
+                           "BZ2 + FILTER such as BCJ is unsupported");
+                       return (ARCHIVE_FAILED);
+               }
+               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 (folder->numCoders >= 2) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_MISC,
+                           "DEFLATE + FILTER such as BCJ is unsupported");
+                       return (ARCHIVE_FAILED);
+               }
+               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:
+               /* TODO: Can we use archive_ppmd7.c ? */
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "PPMD codec is unsupported");
+               return (ARCHIVE_FAILED);
+       case _7Z_X86:
+       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 filter ID");
+               return (ARCHIVE_FAILED);
+       default:
+               return unknown_codec(a, folder->coders[0].codecId,
+                           folder->coders[0].codecIdSize);
+       }
+
+       return (ARCHIVE_OK);
+}
+
+static int
+decompress(struct archive_read *a, struct _7zip *zip,
+    void *buff, size_t *outbytes, const void *b, size_t *used)
+{
+       size_t avail_in, avail_out;
+       int r, ret = ARCHIVE_OK;
+
+       avail_in = *used;
+       avail_out = *outbytes;
+
+       switch (zip->codec) {
+#ifdef HAVE_LZMA_H
+       case _7Z_LZMA: case _7Z_LZMA2:
+               zip->lzstream.next_in = b;
+               zip->lzstream.avail_in = avail_in;
+               zip->lzstream.next_out = buff;
+               zip->lzstream.avail_out = 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);
+               }
+               *used = avail_in - zip->lzstream.avail_in;
+               *outbytes = 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)b;
+               zip->bzstream.avail_in = avail_in;
+               zip->bzstream.next_out = buff;
+               zip->bzstream.avail_out = 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);
+               }
+               *used = avail_in - zip->bzstream.avail_in;
+               *outbytes = avail_out - zip->bzstream.avail_out;
+               break;
+#endif
+#ifdef HAVE_ZLIB_H
+       case _7Z_DEFLATE:
+               zip->stream.next_in = (Bytef *)(uintptr_t)b;
+               zip->stream.avail_in = avail_in;
+               zip->stream.next_out = buff;
+               zip->stream.avail_out = 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);
+               }
+               *used = avail_in - zip->stream.avail_in;
+               *outbytes = avail_out - zip->stream.avail_out;
+               break;
+#endif
+       default:
+               archive_set_error(&(a->archive),
+                   ARCHIVE_ERRNO_MISC,
+                   "Decompression internal error");
+               return (ARCHIVE_FAILED);
+       }
+       return (ret);
+}
+
+static int
+free_decompression(struct archive_read *a, struct _7zip *zip)
+{
+       int r = ARCHIVE_OK;
+
+#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;
+               }
+       }
+#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;
+               }
+       }
+#endif
+       return (r);
+}
+
+static int
+parse_7zip_uint64(const unsigned char *p, size_t len, uint64_t *val)
+{
+       const unsigned char *_p = p;
+       unsigned char avail, mask;
+       int i;
+
+       if (len-- == 0)
+               return (-1);
+       avail = *p++;
+       mask = 0x80;
+       *val = 0;
+       for (i = 0; i < 8; i++) {
+               if (avail & mask) {
+                       if (len-- == 0)
+                               return (-1);
+                       *val |= ((uint64_t)*p++) << (8 * i);
+                       mask >>= 1;
+                       continue;
+               }
+               *val += (avail & (mask -1)) << (8 * i);
+               break;
+       }
+       return (p - _p);
+}
+
+static int
+read_Bools(unsigned char *data, size_t num, const unsigned char *p, size_t len)
+{
+       const unsigned char *_p = p;
+       unsigned i, mask = 0, avail;
+
+       for (i = 0; i < num; i++) {
+               if (mask == 0) {
+                       if (len == 0)
+                               return (-1);
+                       avail = *p++;
+                       len--;
+                       mask = 0x80;
+               }
+               data[i] = (avail & mask)?1:0;
+               mask >>= 1;
+       }
+       return (p - _p);
+}
+
+static void
+free_Digest(struct _7z_digests *d)
+{
+       free(d->defineds);
+       free(d->digests);
+}
+
+static int
+read_Digests(struct _7z_digests *d, size_t num, const unsigned char *p,
+    size_t len)
+{
+       const unsigned char *_p = p;
+       unsigned i;
+
+       memset(d, 0, sizeof(*d));
+
+       if (len == 0)
+               return (-1);
+
+       d->defineds = malloc(num);
+       if (d->defineds == NULL)
+               return (-1);
+       /*
+        * Read Bools.
+        */
+       len--;
+       if (*p++ == 0) {
+               int r = read_Bools(d->defineds, num, p, len);
+               if (r < 0)
+                       return (-1);
+               p += r;
+               len -= r;
+       } 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 (len < 4)
+                               return (-1);
+                       d->digests[i] = archive_le32dec(p);
+                       p += 4;
+                       len -= 4;
+               }
+       }
+
+       return (p - _p);
+}
+
+static void
+free_PackInfo(struct _7z_pack_info *pi)
+{
+       free(pi->sizes);
+       free(pi->positions);
+       free_Digest(&(pi->digest));
+}
+
+static int
+read_PackInfo(struct _7z_pack_info *pi, const unsigned char *p, size_t len)
+{
+       const unsigned char *_p = p;
+       unsigned i;
+       int r;
+
+       memset(pi, 0, sizeof(*pi));
+
+       if (len < 3 || *p++ != kPackInfo)
+               return (-1);
+       --len;
+
+       /*
+        * Read PackPos.
+        */
+       r = parse_7zip_uint64(p, len, &(pi->pos));
+       if (r < 0)
+               return (r);
+       p += r;
+       len -= r;
+
+       /*
+        * Read NumPackStreams.
+        */
+       r = parse_7zip_uint64(p, len, &(pi->numPackStreams));
+       if (r < 0 || pi->numPackStreams == 0)
+               return (r);
+       p += r;
+       len -= r;
+
+       /*
+        * Read PackSizes[num]
+        */
+       if (len >= 1 && *p == kEnd)
+               /* PackSizes[num] are not present. */
+               return (p - _p + 1);
+       if (len < 1 + pi->numPackStreams || *p++ != kSize)
+               return (-1);
+       --len;
+       pi->sizes = calloc(pi->numPackStreams, sizeof(uint64_t));
+       pi->positions = calloc(pi->numPackStreams, sizeof(uint64_t));
+       if (pi->sizes == NULL || pi->positions == NULL)
+               return (-1);
+
+       for (i = 0; i < pi->numPackStreams; i++) {
+               r = parse_7zip_uint64(p, len, &(pi->sizes[i]));
+               if (r < 0)
+                       return (-1);
+               p += r;
+               len -= r;
+       }
+
+       /*
+        * Read PackStreamDigests[num]
+        */
+       if (len >= 1 && *p == kEnd) {
+               /* PackStreamDigests[num] are not present. */
+               pi->digest.defineds =
+                   calloc(pi->numPackStreams, sizeof(*pi->digest.defineds));
+               pi->digest.digests =
+                   calloc(pi->numPackStreams, sizeof(*pi->digest.digests));
+               if (pi->digest.defineds == NULL || pi->digest.digests == NULL)
+                       return (-1);
+               return (p - _p + 1);
+       }
+
+       if (len < 1 + pi->numPackStreams || *p++ != kSize)
+               return (-1);
+       --len;
+
+       r = read_Digests(&(pi->digest), pi->numPackStreams, p, len);
+       if (r < 0)
+               return (-1);
+       p += r;
+       len -= r;
+
+       /*
+        *  Must be marked by kEnd.
+        */
+       if (len == 0 || *p++ != kEnd)
+               return (-1);
+       return (p - _p);
+}
+
+static void
+free_Folder(struct _7z_folder *f)
+{
+       unsigned i;
+
+       if (f->coders) {
+               for (i = 0; i< f->numCoders; i++) {
+                       free(f->coders[i].codecId);
+                       free(f->coders[i].properties);
+               }
+               free(f->coders);
+       }
+       free(f->bindPairs);
+       free(f->packedStreams);
+       free(f->unPackSize);
+}
+
+static int
+read_Folder(struct _7z_folder *f, const unsigned char *p, size_t len)
+{
+       const unsigned char *_p = p;
+       uint64_t numInStreamsTotal = 0;
+       uint64_t numOutStreamsTotal = 0;
+       int r;
+       unsigned i;
+
+       memset(f, 0, sizeof(*f));
+
+       /*
+        * Read NumCoders.
+        */
+       r = parse_7zip_uint64(p, len, &(f->numCoders));
+       if (r < 0)
+               return (-1);
+       p += r;
+       len -= r;
+
+       f->coders = calloc(f->numCoders, sizeof(*f->coders));
+       if (f->coders == NULL)
+               return (-1);
+       for (i = 0; i< f->numCoders; i++) {
+               int simple, attr;
+
+               if (len == 0)
+                       return (-1);
+               /*
+                * 0:3 CodecIdSize
+                * 4:  0 - IsSimple
+                *     1 - Is not Simple
+                * 5:  0 - No Attributes
+                *     1 - There are Attributes;
+                * 7:  Must be zero.
+                */
+               f->coders[i].codecIdSize = *p & 0xf;
+               simple = (*p & 0x10)?0:1;
+               attr = *p & 0x20;
+               if (*p & 0x80)
+                       return (-1);/* Not supported. */
+               p++;
+               len--;
+
+               /*
+                * Read Decompression Method IDs.
+                */
+               if (len < f->coders[i].codecIdSize)
+                       return (-1);
+               f->coders[i].codecId =
+                       calloc(f->coders[i].codecIdSize,
+                     sizeof(*f->coders->codecId));
+               if (f->coders[i].codecId == NULL)
+                       return (-1);
+               memcpy(f->coders[i].codecId, p,
+                   f->coders[i].codecIdSize);
+               p += f->coders[i].codecIdSize;
+               len -= f->coders[i].codecIdSize;
+
+               if (simple) {
+                       f->coders[i].numInStreams = 1;
+                       f->coders[i].numOutStreams = 1;
+               } else {
+                       r = parse_7zip_uint64(p, len,
+                           &(f->coders[i].numInStreams));
+                       if (r < 0)
+                               return (-1);
+                       p += r;
+                       len -= r;
+                       r = parse_7zip_uint64(p, len,
+                           &(f->coders[i].numOutStreams));
+                       if (r < 0)
+                               return (-1);
+                       p += r;
+                       len -= r;
+               }
+
+               if (attr) {
+                       r = parse_7zip_uint64(p, len,
+                               &(f->coders[i].propertiesSize));
+                       if (r < 0)
+                               return (-1);
+                       p += r;
+                       len -= r;
+
+                       if (len < f->coders[i].propertiesSize)
+                               return (-1);
+                       f->coders[i].properties =
+                           malloc(f->coders[i].propertiesSize);
+                       if (f->coders[i].properties == NULL)
+                               return (-1);
+                       memcpy(f->coders[i].properties, p,
+                           f->coders[i].propertiesSize);
+                       p += f->coders[i].propertiesSize;
+                       len -= 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;
+       f->bindPairs = calloc(f->numBindPairs, sizeof(*f->bindPairs));
+       if (f->bindPairs == NULL)
+               return (-1);
+       for (i = 0; i < f->numBindPairs; i++) {
+               r = parse_7zip_uint64(p, len, &(f->bindPairs[i].inIndex));
+               if (r < 0)
+                       return (-1);
+               p += r;
+               len -= r;
+               r = parse_7zip_uint64(p, len, &(f->bindPairs[i].outIndex));
+               if (r < 0)
+                       return (-1);
+               p += r;
+               len -= r;
+       }
+
+       f->numPackedStreams = numInStreamsTotal - f->numBindPairs;
+       f->packedStreams =
+           calloc(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++) {
+                       r = parse_7zip_uint64(p, len, &(f->packedStreams[i]));
+                       if (r < 0)
+                               return (-1);
+                       p += r;
+                       len -= r;
+               }
+       }
+       f->numInStreams = numInStreamsTotal;
+       f->numOutStreams = numOutStreamsTotal;
+
+       return (p - _p);
+}
+
+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 _7z_coders_info *ci, const unsigned char *p, size_t len)
+{
+       const unsigned char *_p = p;
+       struct _7z_digests digest;
+       unsigned i, external;
+       int r;
+
+       memset(ci, 0, sizeof(*ci));
+       memset(&digest, 0, sizeof(digest));
+
+       if (len < 3 || *p++ != kUnPackInfo)
+               goto failed;
+       --len;
+
+       if (len < 3 || *p++ != kFolder)
+               goto failed;
+       --len;
+
+       /*
+        * Read NumFolders.
+        */
+       r = parse_7zip_uint64(p, len, &(ci->numFolders));
+       if (r < 0)
+               goto failed;
+       p += r;
+       len -= r;
+
+       /*
+        * Read External.
+        */
+       if (len == 0)
+               goto failed;
+       external = *p++;
+       len --;
+       switch (external) {
+       case 0:
+               ci->folders = calloc(ci->numFolders, sizeof(*ci->folders));
+               if (ci->folders == NULL)
+                       return (-1);
+               for (i = 0; i < ci->numFolders; i++) {
+                       r = read_Folder(&(ci->folders[i]), p, len);
+                       if (r < 0)
+                               goto failed;
+                       p += r;
+                       len -= r;
+               }
+               break;
+       case 1:
+               r = parse_7zip_uint64(p, len, &(ci->dataStreamIndex));
+               if (r < 0)
+                       return (r);
+               p += r;
+               len -= r;
+               break;
+       }
+
+       if (len < 1 + ci->numFolders || *p++ != kCodersUnPackSize)
+               goto failed;
+       --len;
+
+       for (i = 0; i < ci->numFolders; i++) {
+               unsigned j;
+
+               ci->folders[i].unPackSize =
+                   calloc(ci->folders[i].numOutStreams,
+                       sizeof(*ci->folders[i].unPackSize));
+               if (ci->folders[i].unPackSize == NULL)
+                       goto failed;
+               for (j = 0; j < ci->folders[i].numOutStreams; j++) {
+                       r = parse_7zip_uint64(p, len,
+                             &(ci->folders[i].unPackSize[j]));
+                       if (r < 0)
+                               goto failed;
+                       p += r;
+                       len -= r;
+               }
+       }
+
+       /*
+        * Read CRCs.
+        */
+       if (len == 0)
+               goto failed;
+       if (*p == kEnd)
+               return (p - _p + 1);
+       if (len < 1 + ci->numFolders || *p++ != kCRC)
+               goto failed;
+       --len;
+       r = read_Digests(&digest, ci->numFolders, p, len);
+       if (r < 0)
+               goto failed;
+       p += r;
+       len -= r;
+       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 (len == 0 || *p++ != kEnd)
+               goto failed;
+       free_Digest(&digest);
+       return (p - _p);
+failed:
+       free_Digest(&digest);
+       return (-1);
+}
+
+static uint64_t
+folder_uncompressed_size(struct _7z_folder *f)
+{
+       int n = f->numOutStreams;
+       unsigned pairs = f->numBindPairs;
+
+       while (--n >= 0) {
+               unsigned i;
+               for (i = 0; i < pairs; i++) {
+                       if (f->bindPairs[i].outIndex == 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 _7z_substream_info *ss, struct _7z_folder *f,
+    size_t numFolders, const unsigned char *p, size_t len)
+{
+       const unsigned char *_p = p;
+       uint64_t *usizes;
+       size_t unpack_streams;
+       int r, type;
+       unsigned i;
+       uint32_t numDigests;
+
+       memset(ss, 0, sizeof(*ss));
+
+       if (len < 2 || *p++ != kSubStreamsInfo)
+               return (-1);
+       --len;
+
+       for (i = 0; i < numFolders; i++)
+               f[i].numUnpackStreams = 1;
+
+       if (len < 1)
+               return (-1);
+       type = *p++;
+       --len;
+
+       if (type == kNumUnPackStream) {
+               unpack_streams = 0;
+               for (i = 0; i < numFolders; i++) {
+                       r = parse_7zip_uint64(p, len,
+                             &(f[i].numUnpackStreams));
+                       if (r < 0)
+                               return (-1);
+                       p += r;
+                       len -= r;
+                       unpack_streams += f[i].numUnpackStreams;
+               }
+               if (len < 1)
+                       return (-1);
+               type = *p++;
+               --len;
+       } else
+               unpack_streams = numFolders;
+
+       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++) {
+                               r = parse_7zip_uint64(p, len, usizes);
+                               if (r < 0)
+                                       return (-1);
+                               p += r;
+                               len -= r;
+                               sum += *usizes++;
+                       }
+               }
+               *usizes++ = folder_uncompressed_size(&f[i]) - sum;
+       }
+
+       if (type == kSize) {
+               if (len < 1)
+                       return (-1);
+               type = *p++;
+               --len;
+       }
+
+       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 += 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));
+               r = read_Digests(&(tmpDigests), numDigests, p, len);
+               if (r < 0) {
+                       free_Digest(&tmpDigests);
+                       return (-1);
+               }
+               p += r;
+               len -= r;
+               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 (len < 1)
+                       return (-1);
+               type = *p++;
+               --len;
+       }
+
+       /*
+        *  Must be kEnd.
+        */
+       if (type != kEnd)
+               return (-1);
+       return (p - _p);
+}
+
+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 _7zip *zip, struct _7z_stream_info *si,
+    const unsigned char *p, size_t len)
+{
+       const unsigned char *_p = p;
+       unsigned i;
+       int r;
+
+       memset(si, 0, sizeof(*si));
+
+       if (len > 0 && *p == kPackInfo) {
+               uint64_t packPos;
+
+               r = read_PackInfo(&(si->pi), p, len);
+               if (r < 0)
+                       return (-1);
+               p += r;
+               len -= r;
+
+               /*
+                * Calculate packed stream positions.
+                */
+               packPos = 0;
+               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 (len > 0 && *p == kUnPackInfo) {
+               uint32_t packIndex;
+               struct _7z_folder *f;
+
+               r = read_CodersInfo(&(si->ci), p, len);
+               if (r < 0)
+                       return (-1);
+               p += r;
+               len -= r;
+
+               /*
+                * Calculate packed stream indexes.
+                */
+               packIndex = 0;
+               f = si->ci.folders;
+               for (i = 0; i < si->ci.numFolders; i++) {
+                       f[i].packIndex = packIndex;
+                       packIndex += f[i].numPackedStreams;
+                       if (packIndex > si->pi.numPackStreams)
+                               return (-1);
+               }
+       }
+       if (len > 0 && *p == kSubStreamsInfo) {
+               r = read_SubStreamsInfo(&(si->ss),
+                   si->ci.folders, si->ci.numFolders, p, len);
+               if (r < 0)
+                       return (-1);
+               p += r;
+               len -= r;
+       }
+
+       /*
+        *  Must be kEnd.
+        */
+       if (len == 0 || *p++ != kEnd)
+               return (-1);
+       return (p - _p);
+}
+
+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 _7zip *zip, struct _7z_header_info *h,
+    const unsigned char *p, size_t len)
+{
+       const unsigned char *_p = 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, external, r, sindex;
+
+       if (len < 2 || *p++ != kHeader)
+               return (-1);
+       len--;
+
+       /*
+        * Read ArchiveProperties.
+        */
+       if (*p == kArchiveProperties) {
+               p++;
+               len--;
+
+               for (;;) {
+                       uint64_t size;
+                       int atype = *p++;
+                       len--;
+                       if (atype == 0)
+                               break;
+                       r = parse_7zip_uint64(p, len, &size);
+                       if (r < 0 || len < r + size)
+                               return (-1);
+                       p += r + size;
+                       len -= r + size;
+               }
+       }
+
+       /*
+        * Read MainStreamsInfo.
+        */
+       if (*p == kMainStreamsInfo) {
+               p++;
+               len--;
+               r = read_StreamsInfo(zip, &(zip->si), p, len);
+               if (r < 0)
+                       return (-1);
+               p += r;
+               len -= r;
+       }
+       if (len == 0)
+               return (-1);
+       if (*p == kEnd)
+               return (p - _p + 1);
+
+       /*
+        * Read FilesInfo.
+        */
+       if (len < 2 || *p++ != kFilesInfo)
+               return (-1);
+       len--;
+
+       r = parse_7zip_uint64(p, len, &(zip->numFiles));
+       if (r < 0)
+               return (-1);
+       p += r;
+       len -= r;
+
+       zip->entries = calloc(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 (len < 1)
+                       return (-1);
+               type = *p++;
+               len--;
+               if (type == kEnd)
+                       break;
+
+               r = parse_7zip_uint64(p, len, &size);
+               if (r < 0 || len < size)
+                       return (-1);
+               p += r;
+               len -= r;
+               ll = (size_t)size;
+               len -= ll;
+
+               switch (type) {
+               case kEmptyStream:
+                       h->emptyStreamBools = calloc(zip->numFiles,
+                           sizeof(*h->emptyStreamBools));
+                       if (h->emptyStreamBools == NULL)
+                               return (-1);
+                       r = read_Bools(h->emptyStreamBools, zip->numFiles,
+                           p, ll);
+                       if (r < 0)
+                               return (-1);
+                       p += r;
+                       ll -= r;
+                       empty_streams = 0;
+                       for (i = 0; i < zip->numFiles; i++) {
+                               if (h->emptyStreamBools[i])
+                                       empty_streams++;
+                       }
+                       break;
+               case kEmptyFile:
+                       h->emptyFileBools = calloc(empty_streams,
+                           sizeof(*h->emptyFileBools));
+                       if (h->emptyFileBools == NULL)
+                               return (-1);
+                       r = read_Bools(h->emptyFileBools, empty_streams,
+                           p, len);
+                       if (r < 0)
+                               return (-1);
+                       p += r;
+                       ll -= r;
+                       break;
+               case kAnti:
+                       h->antiBools = calloc(empty_streams,
+                           sizeof(*h->antiBools));
+                       if (h->antiBools == NULL)
+                               return (-1);
+                       r = read_Bools(h->antiBools, empty_streams, p, len);
+                       if (r < 0)
+                               return (-1);
+                       p += r;
+                       ll -= r;
+                       break;
+               case kCTime:
+               case kATime:
+               case kMTime:
+                       r = read_Times(zip, h, type, p, ll);
+                       if (r < 0)
+                               return (-1);
+                       p += r;
+                       ll -= r;
+                       break;
+               case kName:
+               {
+                       unsigned char *np;
+                       size_t nl;
+
+                       if (ll < 1)
+                               return (-1);
+                       external = *p++;
+                       ll--;
+                       if ((ll & 1) || ll < zip->numFiles * 4)
+                               return (-1);
+
+                       zip->entry_names = malloc(ll);
+                       if (zip->entry_names == NULL)
+                               return (-1);
+                       memcpy(zip->entry_names, p, ll);
+                       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 (ll < 2)
+                               return (-1);
+                       allAreDefined = *p++;
+                       --ll;
+                       external = *p++;
+                       --ll;
+                       h->attrBools = calloc(zip->numFiles,
+                           sizeof(*h->attrBools));
+                       if (h->attrBools == NULL)
+                               return (-1);
+                       if (allAreDefined)
+                               memset(h->attrBools, 1, zip->numFiles);
+                       else {
+                               r = read_Bools(h->attrBools,
+                                     zip->numFiles, p, ll);
+                               if (r < 0)
+                                       return (-1);
+                               p += r;
+                               ll -= r;
+                       }
+                       for (i = 0; i < zip->numFiles; i++) {
+                               if (h->attrBools[i]) {
+                                       if (ll < 4)
+                                               return (-1);
+                                       entries[i].attr = archive_le32dec(p);
+                                       p += 4;
+                                       ll -= 4;
+                               }
+                       }
+                       break;
+               }
+               default:
+                       break;
+               }
+               /* Skip remaining data. */
+               p += ll;
+       }
+
+       /*
+        * 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;
+               if (entries[i].flg & HAS_STREAM) {
+                       entries[i].mode = AE_IFREG | 0777;
+                       if (si->ss.digestsDefined[sindex])
+                               entries[i].flg |= CRC32_IS_SET;
+                       entries[i].ssIndex = sindex;
+                       sindex++;
+               } else {
+                       if (h->emptyFileBools == NULL)
+                               entries[i].mode = AE_IFDIR | 0777;
+                       else {
+                               if (h->emptyFileBools[eindex])
+                                       entries[i].mode = AE_IFREG | 0777;
+                               else
+                                       entries[i].mode = AE_IFDIR | 0777;
+                               eindex++;
+                       }
+                       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 (p - _p);
+}
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static void
+fileTimeToUtc(uint64_t fileTime, time_t *time, long *ns)
+{
+
+       if (fileTime >= EPOC_TIME) {
+               fileTime -= EPOC_TIME;
+               /* milli seconds base */
+               *time = (time_t)(fileTime / 10000000);
+               /* nano seconds base */
+               *ns = (long)(fileTime % 10000000) * 100;
+       } else {
+               *time = 0;
+               *ns = 0;
+       }
+}
+
+static int
+read_Times(struct _7zip *zip, struct _7z_header_info *h, int type,
+    const unsigned char *p, size_t len)
+{
+       const unsigned char *_p = p;
+       struct _7zip_entry *entries = zip->entries;
+       unsigned char *timeBools;
+       int r;
+       int allAreDefined, external;
+       unsigned i;
+
+       timeBools = calloc(zip->numFiles, sizeof(*timeBools));
+       if (timeBools == NULL)
+               return (-1);
+
+       if (len < 1)
+               goto failed;
+       allAreDefined = *p++;
+       len--;
+       if (allAreDefined)
+               memset(timeBools, 1, zip->numFiles);
+       else {
+               r = read_Bools(timeBools, zip->numFiles, p, len);
+               if (r < 0)
+                       goto failed;
+               p += r;
+               len -= r;
+       }
+
+       if (len < 1)
+               goto failed;
+       external = *p++;
+       len--;
+       if (external) {
+               r = parse_7zip_uint64(p, len, &(h->dataIndex));
+               if (r < 0)
+                       goto failed;
+               p += r;
+               len -= r;
+       }
+
+       for (i = 0; i < zip->numFiles; i++) {
+               if (len < 8)
+                       goto failed;
+               if (timeBools[i]) {
+                       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;
+                       }
+               }
+               p += 8;
+               len -= 8;
+       }
+
+       free(timeBools);
+       return (p - _p);
+failed:
+       free(timeBools);
+       return (-1);
+}
+
+static int
+slurp_central_directory(struct archive_read *a, struct _7zip *zip,
+    struct _7z_header_info *header)
+{
+       const unsigned char *p;
+       unsigned char *v;
+       struct _7z_stream_info *si;
+       uint64_t len;
+       uint64_t next_header_offset;
+       uint64_t next_header_size;
+       uint32_t next_header_crc;
+       size_t vsize, remaining;
+       int r;
+
+       if ((p = __archive_read_ahead(a, 32, NULL)) == NULL)
+               return (ARCHIVE_FATAL);
+
+       if (memcmp(p, "7z\xBC\xAF\x27\x1C", 6) != 0) {
+               archive_set_error(&a->archive, -1, "Not 7Zip archive file");
+               return (ARCHIVE_FATAL);
+       }
+
+       /* CRC check. */
+       if (crc32(0, (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 7Zip archive");
+               return (ARCHIVE_FATAL);
+       }
+       if (__archive_read_seek(a, next_header_offset + 32, SEEK_SET) < 0)
+               return (ARCHIVE_FATAL);
+       zip->header_offset = next_header_offset;
+
+       if ((p = __archive_read_ahead(a, next_header_size, NULL)) == NULL)
+               return (ARCHIVE_FATAL);
+
+       if (crc32(0, p, next_header_size) != next_header_crc) {
+               archive_set_error(&a->archive, -1, "Damaged 7Zip archive");
+               return (ARCHIVE_FATAL);
+       }
+
+       len = next_header_size;
+       /* Parse ArchiveProperties. */
+       switch (p[0]) {
+       case kHeader:
+               errno = 0;
+               r = read_Header(zip, header, p, len);
+               if (r < 0) {
+                       if (errno == ENOMEM)
+                               archive_set_error(&a->archive, -1,
+                                   "Couldn't allocate memory");
+                       else
+                               archive_set_error(&a->archive, -1,
+                                   "Damaged 7Zip archive");
+                       return (ARCHIVE_FATAL);
+               }
+               if (len - r == 0 || p[r] != kEnd) {
+                       archive_set_error(&a->archive, -1,
+                           "Damaged 7Zip archive");
+                       return (ARCHIVE_FATAL);
+               }
+               break;
+       case kEncodedHeader:
+               p++;
+               len--;
+               errno = 0;
+
+               si = &(zip->si);
+               r = read_StreamsInfo(zip, si, p, len);
+               if (r < 0) {
+                       if (errno == ENOMEM)
+                               archive_set_error(&a->archive, -1,
+                                   "Couldn't allocate memory");
+                       else
+                               archive_set_error(&a->archive, -1,
+                                   "Malformed 7Zip archive");
+                       return (ARCHIVE_FATAL);
+               }
+               p += r;
+               len -= r;
+
+               if (si->pi.numPackStreams == 0 || si->ci.numFolders == 0) {
+                       archive_set_error(&a->archive, -1,
+                           "Malformed 7Zip archive");
+                       return (ARCHIVE_FATAL);
+               }
+
+               if (next_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);
+               }
+
+               if (__archive_read_seek(a, si->pi.pos + 32, SEEK_SET) < 0)
+                       return (ARCHIVE_FATAL);
+               zip->header_offset = si->pi.pos;
+
+               p = __archive_read_ahead(a, si->pi.sizes[0], NULL);
+               if (p == NULL)
+                       return (ARCHIVE_FATAL);
+
+               r = init_decompression(a, zip, si->ci.folders);
+               if (r != ARCHIVE_OK)
+                       return (ARCHIVE_FATAL);
+
+               /* Get an unpacked header size. */
+               vsize = (size_t)folder_uncompressed_size(si->ci.folders);
+               if ((v = malloc(vsize)) == NULL) {
+                       archive_set_error(&a->archive, -1,
+                           "Couldn't allocate memory");
+                       return (ARCHIVE_FATAL);
+               }
+
+               remaining = si->pi.sizes[0];
+               if (zip->codec == _7Z_COPY) {
+                       /*
+                        * The header is not compressed.
+                        * This case won't happen ?
+                        */
+                       if (vsize > remaining) {
+                               free(v);
+                               archive_set_error(&a->archive, -1,
+                                   "Invalid Header data");
+                               return (ARCHIVE_FATAL);
+                       }
+                       memcpy(v, p, vsize);
+               } else {
+                       /*
+                        * Decompress the header.
+                        */
+                       r = decompress(a, zip, v, &vsize, p, &remaining);
+                       if (r != ARCHIVE_OK) {
+                               free(v);
+                               return (ARCHIVE_FATAL);
+                       }
+               }
+               if (si->ci.folders[0].digest_defined){
+                       uint32_t c = crc32(0, v, vsize);
+                       if (c != si->ci.folders[0].digest) {
+                               free(v);
+                               archive_set_error(&a->archive, -1,
+                                   "Header CRC error");
+                               return (ARCHIVE_FATAL);
+                       }
+               }
+               free_StreamsInfo(si);
+               memset(si, 0, sizeof(*si));
+
+               errno = 0;
+               r = read_Header(zip, header, v, vsize);
+               /* Must be kEnd. */
+               if (r < 0 || vsize - r == 0 || v[r] != kEnd) {
+                       free(v);
+                       if (errno == ENOMEM)
+                               archive_set_error(&a->archive, -1,
+                                   "Couldn't allocate memory");
+                       else
+                               archive_set_error(&a->archive, -1,
+                                   "Malformed 7Zip archive");
+                       return (ARCHIVE_FATAL);
+               }
+               free(v);
+               break;
+       default:
+               archive_set_error(&a->archive, -1,
+                   "Unexpected Property ID = %02X", p[0]);
+               return (ARCHIVE_FATAL);
+       }
+
+       return (ARCHIVE_OK);
+}
+
+static ssize_t
+get_uncompressed_data(struct archive_read *a, const void **buff, size_t size)
+{
+       struct _7zip *zip = (struct _7zip *)a->format->data;
+       ssize_t bytes_avail;
+
+       if (zip->codec == _7Z_COPY) {
+               /* Copy mode. */
+
+               /*
+                * Note: '1' here is a performance optimization.
+                * Recall that the decompression layer returns a count of
+                * available bytes; asking for more than that forces the
+                * decompressor to combine reads by copying data.
+                */
+               *buff = __archive_read_ahead(a, 1, &bytes_avail);
+               if (bytes_avail <= 0) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Truncated 7Zip 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->entry_bytes_unconsumed = bytes_avail;
+       } else {
+               /* Packed mode. */
+               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_buff_pointer;
+               zip->uncompressed_buff_pointer += bytes_avail;
+       }
+       zip->uncompressed_buffer_bytes_remaining -= bytes_avail;
+       return (bytes_avail);
+}
+
+static ssize_t
+extract_pack_stream(struct archive_read *a)
+{
+       struct _7zip *zip = (struct _7zip *)a->format->data;
+       ssize_t bytes_avail;
+       int r;
+
+       if (zip->codec == _7Z_COPY) {
+               zip->uncompressed_buffer_bytes_remaining =
+                   zip->pack_stream_bytes_remaining;
+               zip->pack_stream_bytes_remaining = 0;
+               zip->unpack_stream_bytes_remaining = 0;
+       } else {
+               size_t bytes_in, bytes_out;
+               const void *compressed_buff;
+
+               /* If the buffer hasn't been allocated, allocate it now. */
+               if (zip->uncompressed_buffer == NULL) {
+                       zip->uncompressed_buffer_size = 64 * 1024;
+                       zip->uncompressed_buffer =
+                           malloc(zip->uncompressed_buffer_size);
+                       if (zip->uncompressed_buffer == NULL) {
+                               archive_set_error(&a->archive, ENOMEM,
+                                   "No memory for 7Zip decompression");
+                               return (ARCHIVE_FATAL);
+                       }
+               }
+               /*
+                * 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 = __archive_read_ahead(a, 1, &bytes_avail);
+               if (bytes_avail <= 0) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Truncated 7Zip file body");
+                       return (ARCHIVE_FATAL);
+               }
+
+               bytes_out = zip->uncompressed_buffer_size;
+               bytes_in = bytes_avail;
+               if (bytes_in > zip->pack_stream_bytes_remaining)
+                       bytes_in = zip->pack_stream_bytes_remaining;
+               r = decompress(a, zip, zip->uncompressed_buffer,
+                   &bytes_out, compressed_buff, &bytes_in);
+               switch (r) {
+               case ARCHIVE_OK:
+               case ARCHIVE_EOF:
+                       break;
+               default:
+                       return (ARCHIVE_FATAL);
+               }
+               zip->pack_stream_bytes_remaining -= bytes_in;
+               if (bytes_out > zip->unpack_stream_bytes_remaining)
+                       bytes_out = zip->unpack_stream_bytes_remaining;
+               zip->unpack_stream_bytes_remaining -= bytes_out;
+               zip->uncompressed_buffer_bytes_remaining = bytes_out;
+               zip->uncompressed_buff_pointer = zip->uncompressed_buffer;
+               zip->entry_bytes_unconsumed = bytes_in;
+       }
+       return (ARCHIVE_OK);
+}
+
+static ssize_t
+read_stream(struct archive_read *a, const void **buff, size_t size)
+{
+       struct _7zip *zip = (struct _7zip *)a->format->data;
+       uint64_t skip_bytes = 0;
+       int r;
+
+       if (zip->uncompressed_buffer_bytes_remaining == 0) {
+               if (zip->pack_stream_bytes_remaining > 0) {
+                       r = extract_pack_stream(a);
+                       if (r < 0)
+                               return (r);
+                       return (get_uncompressed_data(a, buff, size));
+               } else if (zip->unpack_stream_bytes_remaining > 0) {
+                       /* Extract a new pack stream. */
+                       r = extract_pack_stream(a);
+                       if (r < 0)
+                               return (r);
+                       return (get_uncompressed_data(a, buff, size));
+               }
+       } else
+               return (get_uncompressed_data(a, buff, size));
+
+       /*
+        * Current pack stream has been consumed.
+        */
+       if (zip->pack_stream_remaining == 0) {
+               /*
+                * 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);
+               }
+               zip->pack_stream_remaining =
+                   zip->si.ci.folders[zip->folder_index].numPackedStreams;
+               zip->pack_stream_index =
+                   zip->si.ci.folders[zip->folder_index].packIndex;
+               zip->unpack_stream_bytes_remaining =
+                       zip->si.ci.folders[zip->folder_index].unPackSize[0];
+               zip->uncompressed_buffer_bytes_remaining = 0;
+               /*
+                * We have to initialize the decompressor for
+                * the new folder's pack streams.
+                */
+               r = init_decompression(a, zip,
+                   &(zip->si.ci.folders[zip->folder_index]));
+               if (r != ARCHIVE_OK)
+                       return (ARCHIVE_FATAL);
+
+               zip->folder_index++;
+       }
+
+       /*
+        * Switch to next pack stream.
+        */
+       zip->pack_stream_bytes_remaining =
+           zip->si.pi.sizes[zip->pack_stream_index];
+       if (zip->offset != zip->si.pi.positions[zip->pack_stream_index] + 32) {
+               if (0 > __archive_read_seek(a, 
+                   zip->si.pi.positions[zip->pack_stream_index] + 32,
+                   SEEK_SET))
+                       return (ARCHIVE_FATAL);
+               zip->offset = zip->si.pi.positions[zip->pack_stream_index] + 32;
+       }
+       zip->pack_stream_index++;
+       zip->pack_stream_remaining--;
+
+       /* Extract a new pack stream. */
+       r = extract_pack_stream(a);
+       if (r < 0)
+               return (r);
+
+       /*
+        * Skip the bytes we alrady has skipped in skip_stream(). 
+        */
+       while (skip_bytes) {
+               ssize_t skipped;
+
+               skipped = get_uncompressed_data(a, buff, skip_bytes);
+               if (skipped < 0)
+                       return (skipped);
+               skip_bytes -= skipped;
+               if (zip->entry_bytes_unconsumed) {
+                       __archive_read_consume(a, zip->entry_bytes_unconsumed);
+                       zip->offset += zip->entry_bytes_unconsumed;
+                       zip->entry_bytes_unconsumed = 0;
+               }
+       }
+
+       return (get_uncompressed_data(a, buff, size));
+}
+
+static int64_t
+skip_stream(struct archive_read *a, size_t skip_bytes)
+{
+       struct _7zip *zip = (struct _7zip *)a->format->data;
+       const void *p;
+       int64_t skipped_bytes;
+       size_t bytes = skip_bytes;
+
+       if (zip->folder_index == 0) {
+               /*
+                * Optimization for a list mode.
+                * Avoid unncecessary decoding operations.
+                */
+               zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
+                   += skip_bytes;
+               return (skip_bytes);
+       }
+
+       while (bytes) {
+               skipped_bytes = read_stream(a, &p, bytes);
+               if (skipped_bytes < 0)
+                       return (skipped_bytes);
+               if (skipped_bytes == 0) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Truncated 7Zip file body");
+                       return (ARCHIVE_FATAL);
+               }
+               bytes -= skipped_bytes;
+               if (zip->entry_bytes_unconsumed) {
+                       __archive_read_consume(a, zip->entry_bytes_unconsumed);
+                       zip->offset += zip->entry_bytes_unconsumed;
+                       zip->entry_bytes_unconsumed = 0;
+               }
+       }
+       return (skip_bytes);
+}
+
index 270a0420dbb98ab0f6bf369d7b0104c9473055cb..53fe6fa391a2d9271ab6dff1f63ce4aa0d7da149 100644 (file)
@@ -68,6 +68,7 @@ archive_read_support_format_all(struct archive *a)
         * 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_iso9660(a);
index a07466f6a4de9479752cedb0521ab1be0f394820..584dde1066bced64f7035433f6c64499ecd49ea4 100644 (file)
@@ -67,6 +67,7 @@ IF(ENABLE_TEST)
     test_read_disk_entry_from_file.c
     test_read_extract.c
     test_read_file_nonexistent.c
+    test_read_format_7zip.c
     test_read_format_ar.c
     test_read_format_cab.c
     test_read_format_cab_filename.c
index 7b110c68c3867f00e3b5ae8dd9b2a8d319bd7b60..1619b0729e94d7d232a4d2047641530b16aefee6 100644 (file)
@@ -72,6 +72,7 @@ test_filter_or_format(enabler enable)
 
 DEFINE_TEST(test_archive_read_support)
 {
+       test_filter_or_format(archive_read_support_format_7zip);
        test_filter_or_format(archive_read_support_format_all);
        test_filter_or_format(archive_read_support_format_ar);
        test_filter_or_format(archive_read_support_format_cab);
index 35af4e2b2ff0c1d46bc6c1708b0b4a64c4d5457f..d80c0da74f8159c8f90d081e0c65221e2cadb926 100644 (file)
@@ -62,6 +62,12 @@ static struct {
        {0, "test_compat_tar_hardlink_1.tar"},
        {0, "test_compat_xz_1.txz"}, /* Exercise xz decompressor. */
        {0, "test_compat_zip_1.zip"},
+       {0, "test_read_format_7zip_copy.7z"},
+       {0, "test_read_format_7zip_bcj_lzma2.7z"},
+       {0, "test_read_format_7zip_deflate.7z"},
+       {0, "test_read_format_7zip_bzip2.7z"},
+       {0, "test_read_format_7zip_lzma1.7z"},
+       {0, "test_read_format_7zip_lzma1_lzma2.7z"},
        {0, "test_read_format_ar.ar"},
        {0, "test_read_format_cpio_bin_be.cpio"},
        {0, "test_read_format_cpio_svr4_gzip_rpm.rpm"}, /* Test RPM unwrapper */
diff --git a/libarchive/test/test_read_format_7zip.c b/libarchive/test/test_read_format_7zip.c
new file mode 100644 (file)
index 0000000..64cc6c4
--- /dev/null
@@ -0,0 +1,587 @@
+/*-
+ * 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 "test.h"
+__FBSDID("$FreeBSD");
+
+/*
+ * Extract a non-encorded file.
+ * The header of the 7z archive files is not encdoed.
+ */
+static void
+test_copy()
+{
+       const char *refname = "test_read_format_7zip_copy.7z";
+       struct archive_entry *ae;
+       struct archive *a;
+       char buff[128];
+
+       extract_reference_file(refname);
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_filename(a, refname, 10240));
+
+       /* Verify regular file1. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file1", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(60, archive_entry_size(ae));
+       assertEqualInt(60, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff, "    ", 4);
+
+       assertEqualInt(1, archive_file_count(a));
+
+       /* End of archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       /* Verify archive format. */
+       assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a));
+       assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a));
+
+       /* Close the archive. */
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/*
+ * An archive file has no entry.
+ */
+static void
+test_empty_archive()
+{
+       const char *refname = "test_read_format_7zip_empty_archive.7z";
+       struct archive_entry *ae;
+       struct archive *a;
+
+       extract_reference_file(refname);
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_filename(a, refname, 10240));
+
+       /* End of archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       assertEqualInt(0, archive_file_count(a));
+
+       /* Verify archive format. */
+       assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a));
+       assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a));
+
+       /* Close the archive. */
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/*
+ * An archive file has one empty file. It means there is no content
+ * in the archive file except for a header.
+ */
+static void
+test_empty_file()
+{
+       const char *refname = "test_read_format_7zip_empty_file.7z";
+       struct archive_entry *ae;
+       struct archive *a;
+
+       extract_reference_file(refname);
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_filename(a, refname, 10240));
+
+       /* Verify regular empty. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("empty", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_size(ae));
+
+       assertEqualInt(1, archive_file_count(a));
+
+       /* End of archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       /* Verify archive format. */
+       assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a));
+       assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a));
+
+       /* Close the archive. */
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/*
+ * Extract an encoded file.
+ * The header of the 7z archive files is not encdoed.
+ */
+static void
+text_plain_header(const char *refname)
+{
+       struct archive_entry *ae;
+       struct archive *a;
+       char buff[128];
+
+       extract_reference_file(refname);
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_filename(a, refname, 10240));
+
+       /* Verify regular file1. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file1", archive_entry_pathname(ae));
+       assertEqualInt(1322058763, archive_entry_mtime(ae));
+       assertEqualInt(2844, archive_entry_size(ae));
+       assertEqualInt(sizeof(buff), archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff, "The libarchive distribution ", 28);
+
+       assertEqualInt(1, archive_file_count(a));
+
+       /* End of archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       /* Verify archive format. */
+       assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a));
+       assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a));
+
+       /* Close the archive. */
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/*
+ * Extract multi files.
+ * The header of the 7z archive files is encdoed with LZMA.
+ */
+static void
+test_extract_all_files(const char *refname)
+{
+       struct archive_entry *ae;
+       struct archive *a;
+       char buff[128];
+
+       extract_reference_file(refname);
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_filename(a, refname, 10240));
+
+       /* Verify regular file1. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("dir1/file1", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(13, archive_entry_size(ae));
+       assertEqualInt(13, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff, "aaaaaaaaaaaa\n", 13);
+
+       /* Verify regular file2. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file2", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(26, archive_entry_size(ae));
+       assertEqualInt(26, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\n", 26);
+
+       /* Verify regular file3. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file3", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(39, archive_entry_size(ae));
+       assertEqualInt(39, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\n", 39);
+
+       /* Verify regular file4. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file4", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(52, archive_entry_size(ae));
+       assertEqualInt(52, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff,
+           "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\ndddddddddddd\n", 52);
+
+       /* Verify directory dir1. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFDIR | 0777), archive_entry_mode(ae));
+       assertEqualString("dir1", archive_entry_pathname(ae));
+       assertEqualInt(2764801, archive_entry_mtime(ae));
+
+       assertEqualInt(5, archive_file_count(a));
+
+       /* End of archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       /* Verify archive format. */
+       assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a));
+       assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a));
+
+       /* Close the archive. */
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/*
+ * Extract last file.
+ * The header of the 7z archive files is encdoed with LZMA.
+ */
+static void
+test_extract_last_file(const char *refname)
+{
+       struct archive_entry *ae;
+       struct archive *a;
+       char buff[128];
+
+       extract_reference_file(refname);
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_filename(a, refname, 10240));
+
+       /* Verify regular file1. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("dir1/file1", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(13, archive_entry_size(ae));
+
+       /* Verify regular file2. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file2", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(26, archive_entry_size(ae));
+
+       /* Verify regular file3. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file3", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(39, archive_entry_size(ae));
+
+       /* Verify regular file4. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file4", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(52, archive_entry_size(ae));
+       assertEqualInt(52, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff,
+           "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\ndddddddddddd\n", 52);
+
+       /* Verify directory dir1. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFDIR | 0777), archive_entry_mode(ae));
+       assertEqualString("dir1", archive_entry_pathname(ae));
+       assertEqualInt(2764801, archive_entry_mtime(ae));
+
+       assertEqualInt(5, archive_file_count(a));
+
+       /* End of archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       /* Verify archive format. */
+       assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a));
+       assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a));
+
+       /* Close the archive. */
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/*
+ * Extract a mixed archive file which has both  LZMA and LZMA2 encoded files.
+ *  LZMA: file1, file2, file3, file4
+ *  LZMA2: zfile1, zfile2, zfile3, zfile4
+ */
+static void
+test_extract_all_files2(const char *refname)
+{
+       struct archive_entry *ae;
+       struct archive *a;
+       char buff[128];
+
+       extract_reference_file(refname);
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_filename(a, refname, 10240));
+
+       /* Verify regular file1. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("dir1/file1", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(13, archive_entry_size(ae));
+       assertEqualInt(13, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff, "aaaaaaaaaaaa\n", 13);
+
+       /* Verify regular file2. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file2", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(26, archive_entry_size(ae));
+       assertEqualInt(26, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\n", 26);
+
+       /* Verify regular file3. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file3", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(39, archive_entry_size(ae));
+       assertEqualInt(39, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\n", 39);
+
+       /* Verify regular file4. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file4", archive_entry_pathname(ae));
+       assertEqualInt(86401, archive_entry_mtime(ae));
+       assertEqualInt(52, archive_entry_size(ae));
+       assertEqualInt(52, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff,
+           "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\ndddddddddddd\n", 52);
+
+       /* Verify regular zfile1. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("dir1/zfile1", archive_entry_pathname(ae));
+       assertEqualInt(5184001, archive_entry_mtime(ae));
+       assertEqualInt(13, archive_entry_size(ae));
+       assertEqualInt(13, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff, "aaaaaaaaaaaa\n", 13);
+
+       /* Verify regular zfile2. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("zfile2", archive_entry_pathname(ae));
+       assertEqualInt(5184001, archive_entry_mtime(ae));
+       assertEqualInt(26, archive_entry_size(ae));
+       assertEqualInt(26, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\n", 26);
+
+       /* Verify regular zfile3. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("zfile3", archive_entry_pathname(ae));
+       assertEqualInt(5184001, archive_entry_mtime(ae));
+       assertEqualInt(39, archive_entry_size(ae));
+       assertEqualInt(39, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\n", 39);
+
+       /* Verify regular zfile4. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("zfile4", archive_entry_pathname(ae));
+       assertEqualInt(5184001, archive_entry_mtime(ae));
+       assertEqualInt(52, archive_entry_size(ae));
+       assertEqualInt(52, archive_read_data(a, buff, sizeof(buff)));
+       assertEqualMem(buff,
+           "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\ndddddddddddd\n", 52);
+
+       /* Verify directory dir1. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFDIR | 0777), archive_entry_mode(ae));
+       assertEqualString("dir1", archive_entry_pathname(ae));
+       assertEqualInt(2764801, archive_entry_mtime(ae));
+
+       assertEqualInt(9, archive_file_count(a));
+
+       /* End of archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       /* Verify archive format. */
+       assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a));
+       assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a));
+
+       /* Close the archive. */
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/*
+ * Extract a file compressed with DELTA + LZMA[12].
+ */
+static void
+test_delta_lzma(const char *refname)
+{
+       struct archive_entry *ae;
+       struct archive *a;
+       size_t remaining;
+       ssize_t bytes;
+       char buff[128];
+
+       extract_reference_file(refname);
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_filename(a, refname, 10240));
+
+       /* Verify regular file1. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0777), archive_entry_mode(ae));
+       assertEqualString("file1", archive_entry_pathname(ae));
+       assertEqualInt(172802, archive_entry_mtime(ae));
+       assertEqualInt(27627, archive_entry_size(ae));
+       remaining = (size_t)archive_entry_size(ae);
+       while (remaining) {
+               if (remaining < sizeof(buff))
+                       assertEqualInt(remaining,
+                           bytes = archive_read_data(a, buff, sizeof(buff)));
+               else
+                       assertEqualInt(sizeof(buff),
+                           bytes = archive_read_data(a, buff, sizeof(buff)));
+               if (bytes > 0)
+                       remaining -= bytes;
+               else
+                       break;
+       }
+       assertEqualInt(0, remaining);
+
+       assertEqualInt(1, archive_file_count(a));
+
+       /* End of archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       /* Verify archive format. */
+       assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a));
+       assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a));
+
+       /* Close the archive. */
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/*
+ * Extract a file compressed with BCJ + LZMA2.
+ */
+static void
+test_bcj_lzma(const char *refname)
+{
+       struct archive_entry *ae;
+       struct archive *a;
+       size_t remaining;
+       ssize_t bytes;
+       char buff[128];
+
+       extract_reference_file(refname);
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_filename(a, refname, 10240));
+
+       /* Verify regular x86exe. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt((AE_IFREG | 0555), archive_entry_mode(ae));
+       assertEqualString("x86exe", archive_entry_pathname(ae));
+       assertEqualInt(172802, archive_entry_mtime(ae));
+       assertEqualInt(27328, archive_entry_size(ae));
+       remaining = (size_t)archive_entry_size(ae);
+       while (remaining) {
+               if (remaining < sizeof(buff))
+                       assertEqualInt(remaining,
+                           bytes = archive_read_data(a, buff, sizeof(buff)));
+               else
+                       assertEqualInt(sizeof(buff),
+                           bytes = archive_read_data(a, buff, sizeof(buff)));
+               if (bytes > 0)
+                       remaining -= bytes;
+               else
+                       break;
+       }
+       assertEqualInt(0, remaining);
+
+       assertEqualInt(1, archive_file_count(a));
+
+       /* End of archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       /* Verify archive format. */
+       assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a));
+       assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a));
+
+       /* Close the archive. */
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_format_7zip)
+{
+       struct archive *a;
+
+       test_copy();
+       test_empty_archive();
+       test_empty_file();
+
+       assert((a = archive_read_new()) != NULL);
+
+       /* Extracting with libbzip2 */
+       if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) {
+               skipping("7zip:bzip2 decoding is not supported on this platform");
+       } else {
+               text_plain_header("test_read_format_7zip_bzip2.7z");
+       }
+
+       /* Extracting with libz */
+       if (ARCHIVE_OK != archive_read_support_filter_gzip(a)) {
+               skipping("7zip:deflate decoding is not supported on this platform");
+       } else {
+               text_plain_header("test_read_format_7zip_deflate.7z");
+       }
+
+       /* Extracting with liblzma */
+       if (ARCHIVE_OK != archive_read_support_filter_xz(a)) {
+               skipping("7zip:lzma decoding is not supported on this platform");
+       } else {
+               text_plain_header("test_read_format_7zip_lzma1.7z");
+               text_plain_header("test_read_format_7zip_lzma2.7z");
+               test_extract_all_files("test_read_format_7zip_copy_2.7z");
+               test_extract_all_files("test_read_format_7zip_lzma1_2.7z");
+               test_extract_last_file("test_read_format_7zip_copy_2.7z");
+               test_extract_last_file("test_read_format_7zip_lzma1_2.7z");
+               test_extract_all_files2("test_read_format_7zip_lzma1_lzma2.7z");
+               test_bcj_lzma("test_read_format_7zip_bcj_lzma2.7z");
+               test_delta_lzma("test_read_format_7zip_delta_lzma1.7z");
+               test_delta_lzma("test_read_format_7zip_delta_lzma2.7z");
+       }
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
diff --git a/libarchive/test/test_read_format_7zip_bcj_lzma2.7z.uu b/libarchive/test/test_read_format_7zip_bcj_lzma2.7z.uu
new file mode 100644 (file)
index 0000000..a61aa70
--- /dev/null
@@ -0,0 +1,245 @@
+begin 644 test_read_format_7zip_bcj_lzma2.7z
+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
+<>`!E````%`H!```M0RMQLYT!%08!`"&`;8$`````
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_bzip2.7z.uu b/libarchive/test/test_read_format_7zip_bzip2.7z.uu
new file mode 100644 (file)
index 0000000..07d231e
--- /dev/null
@@ -0,0 +1,37 @@
+begin 644 test_read_format_7zip_bzip2.7z
+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
+A`&D`;`!E`#$````4"@$`@/>FPNRIS`$5!@$`(("D@0``
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_copy.7z.uu b/libarchive/test/test_read_format_7zip_copy.7z.uu
new file mode 100644 (file)
index 0000000..2429834
--- /dev/null
@@ -0,0 +1,7 @@
+begin 644 test_read_format_7zip_none.7z
+M-WJ\KR<<``-!QGV(/`````````!"`````````(/;BV,@("`@("`@("`@("`@
+M("`@("`@("`@("`@(&9I;&4@,2!C;VYT96YT<PIH96QL;PIH96QL;PIH96QL
+M;PH!!`8``0D\``<+`0`!`0`,/``("@&J'=X/```%`1$-`&8`:0!L`&4`,0``
+7`!0*`0"`UD``J+*=`14&`0`@````````
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_copy_2.7z.uu b/libarchive/test/test_read_format_7zip_copy_2.7z.uu
new file mode 100644 (file)
index 0000000..42dda2d
--- /dev/null
@@ -0,0 +1,11 @@
+begin 644 test_read_format_7zip_copy_2.7z
+M-WJ\KR<<``,UN4V,_``````````B`````````)#)!7AA86%A86%A86%A86$*
+M86%A86%A86%A86%A"F)B8F)B8F)B8F)B8@IA86%A86%A86%A86$*8F)B8F)B
+M8F)B8F)B"F-C8V-C8V-C8V-C8PIA86%A86%A86%A86$*8F)B8F)B8F)B8F)B
+M"F-C8V-C8V-C8V-C8PID9&1D9&1D9&1D9&0*``"!,P>N;;\I7YC.>1<DC8$\
+M>BQN+EC0JP:,/8'I^CO'+,+O1BT_V!J25R+>!(6$RN.N7/`:E<835()Z+\30
+M7HIV[7[>>JA7V-Q4B,BA%R4UM#?HQF-[B9DE>M/_1#ICKKVX_%8F!1KJ*HFC
+M3WIYZ-+^$,6\V``````7!H""`0EZ``<+`0`!(P,!`05=`!````R`R`H!@8KI
+#-P``
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_deflate.7z.uu b/libarchive/test/test_read_format_7zip_deflate.7z.uu
new file mode 100644 (file)
index 0000000..bf4f051
--- /dev/null
@@ -0,0 +1,36 @@
+begin 644 test_read_format_7zip_deflate.7z
+M-WJ\KR<<``/.MOKA/@4```````!&`````````#5T;:VU54MSTS`0OOM7[`W*
+MA/`:#CR&&==1&PU)'&P'Z*DHMMH(;,MCRRGAU_.M[31-&08X<&CCR+O[/797
+M23::<K-6=;HQ6TV9:5QMUJTSMB35D**;C<TUF88"6^UJ<[UQM-Y18@IZ;W3I
+M?N3:4V7&`4V[_JI31\Z20]7T-KZTSJ2::EW5-FM3G9%R7<C:.F<+LE>>VZ#`
+ME<GUV/.$2C=DRLQL3=:JO#O&=^*88X+-QK9Y1ANUU6":YEK5WBWLDQR@96/*
+M:VJ<<KH`VUM@?6W*$J^`S0<]-)&\(E7N*+,>.(^H0L4&_FA'A:9OI;TAUBKI
+MQN0YU1!KKG9D'#(3+F+SW-YP5=-XIG2ZS*#563A3%*HV/_2Q,1VOMADXW)?<
+MO/'X5*4.)P<)#>)N*:-!-1<L70UL(,.^1R2^I[IRW+X<;ND,<L%K1`JD`VIL
+M6Z?(?&C*-&\SICM..UWC35_TQ*/N>V;3EB$5FWW`^X=&H]#]5M/=5C\Z]FW@
+M=H`"Y<;N\2!\F$9;\W.E:N;`&/3B<9JK%KU:!13I:_;IP.M-WT@FIK)[5@^0
+M*-*#7J%VIITR>?,:AW=VX\GP>8GB]6Z<_N8M8UPV;579VEVBIM/U96J+JM9-
+M\]NDF]HX?=GH/V<4KM9Z_))/G:J?%,KA''U/_\+,87"J=HW50'<+9<K7^TK7
+MVF48L4.A=6ORK,]^_/CQ85CFZION3D<\>%?FNN612&M3N68T3(YJOYO<J'JW
+M?T'H#8Z;KFG[@6>1O-N8%@PDH_`FH\2-R72^HRT*,.)AD6%-T6#;EGT_TXU.
+MO_VR-QAW=!%U#I<%<AFQZ)91PPDF8+N)X(B4!QP>5150$;*S+8934K_#/!/=
+M[I3ZIC>5J6<:D2R+"P\4=;]IG04;6V%0M[KVG"DXEM<B5T,N)+H-6:36^]RF
+M.^M`3(&!O=V\!@C*>=EOT/K+YU>S.I?W?=SWP8/Z`@4M<D&3VE)MK<G4&FTH
+MT`/(]@X7_</TA)X_??KB,?Z]HK>JA6GUP^;DG>?#CBZF80MUO=494B-]_/M1
+M9AUE4PZV=2=K4X(JFUHTH\&'NONTK?,*FYDKDW;"1]W45NBY<;C&>$[09SRP
+M'_AW=]A3BRGHW/*0!"WNM?=L3,>,^*H]ZF#1-@X"G!I60ZWM]LZ%AA$:[K01
+M7UO]?<HU#F@LZ1X5(.(R0M-KY-_^<%6V,0=7RG2CRFLV[?DO))%RUZ2.Y.%.
+M_1\\>Y*\,<=W/B<]L?4PIX7"^AG<R+>-Z+K65;TK`(.03&5,<7B6?/(C07A>
+M1N%'.1$3.KV@9"K(7R73,'H8G]"7+WZ,B`</R%],\'=!XO,R$G%,8>3)^7(F
+MD84RD;](I(A')!?!;#61B_,1G:X26H0)S>1<)@A+PE%7_=<T+SRCN8B"*;[Z
+MIW(FDXL.[TPF"\8Z"R/R:>E'B0Q6,S^BY2I:AK$@YC^1<3#SY5Q,QIY<`)'$
+M1[%(*)[ZL]D].:<";/S3F>"2+`?9D0@2YCT\H48`+T!D-J)X*0*)!Z@6\R60
+M+T:$Q"!<Q.+#"D%X21-_[I^+F!X>:_?N:6>7@U4DYDP.@N/5:9S(9)4(.@_#
+M2<R%8Q%]E(&(W]`LC&..6L5BY$W\Q&=@+@%/\!K/IZM8LCN@GH@H6BT3&2Y.
+M:!I^@GYP])$ZZ6P,%RS5@Q5A=(&B8-6[##6?I@+G$8JPJB3RV8PXB620',(8
+M&Q*BQ#MHI(4XG\ESL0@$OPVYRB<9BQ,T1<8<(#M8M!F8JTXR8E@0'H]G<-2U
+MC.09^9./J#'9!R_A@NPY])8%T\'NL?<3`00&``$)A3X`!PL!``$#!`$(#(L<
+M``@*`>4<_-T```4!$0T`9@!I`&P`90`Q````%`H!`(#WIL+LJ<P!%08!`""`
+$I($`````
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_delta_lzma1.7z.uu b/libarchive/test/test_read_format_7zip_delta_lzma1.7z.uu
new file mode 100644 (file)
index 0000000..d1e40f6
--- /dev/null
@@ -0,0 +1,280 @@
+begin 644 test_read_format_7zip_delta_lzma1.7z
+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
+D#0!F`&D`;`!E`#$````4"@$``"U#*W&SG0$5!@$`(("D@0``
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_delta_lzma2.7z.uu b/libarchive/test/test_read_format_7zip_delta_lzma2.7z.uu
new file mode 100644 (file)
index 0000000..c8da4cf
--- /dev/null
@@ -0,0 +1,280 @@
+begin 644 test_read_format_7zip_delta_lzma2.7z
+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
+E$0T`9@!I`&P`90`Q````%`H!```M0RMQLYT!%08!`""`I($`````
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_empty_archive.7z.uu b/libarchive/test/test_read_format_7zip_empty_archive.7z.uu
new file mode 100644 (file)
index 0000000..88831ca
--- /dev/null
@@ -0,0 +1,4 @@
+begin 644 test_read_format_7zip_empty_archive.7z
+@-WJ\KR<<``.-F]4/````````````````````````````
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_empty_file.7z.uu b/libarchive/test/test_read_format_7zip_empty_file.7z.uu
new file mode 100644 (file)
index 0000000..ab9cf65
--- /dev/null
@@ -0,0 +1,5 @@
+begin 644 test_read_format_7zip_empty_file.7z
+M-WJ\KR<<``,%;\HA```````````N`````````,Z^!W,!!0$.`8`/`8`1#0!E
+A`&T`<`!T`'D````4"@$`@-9``*BRG0$5!@$`(("D@0``
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_lzma1.7z.uu b/libarchive/test/test_read_format_7zip_lzma1.7z.uu
new file mode 100644 (file)
index 0000000..1ffb08f
--- /dev/null
@@ -0,0 +1,37 @@
+begin 644 test_read_format_7zip_lzma1.7z
+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
+&(("D@0``
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_lzma1_2.7z.uu b/libarchive/test/test_read_format_7zip_lzma1_2.7z.uu
new file mode 100644 (file)
index 0000000..70b03bd
--- /dev/null
@@ -0,0 +1,8 @@
+begin 644 test_read_format_7zip_lzma1_2.7z
+M-WJ\KR<<``-GH*;IF@`````````B`````````--W(LL`,.P]5,<,713`O:V<
+M)`.JE)L$6^G@````@3,'K@_/;NZ,!\A#@(.!6_^L@!U0&A2&3>R/[PWL=[H6
+MI6B>-7,"LEFSPJXL&_5`VI+39=)!5A<B\LC\HNBIA&&!?>VU_MRZJ-'/$T<4
+M$.`BV?:<->IQ$-K,-DX=!*T>*)Q&M+J="/NB=:8'E1X3/?KZT!XEN'99V!2D
+HL??E````%P87`0F`@P`'"P$``2,#`0$%70`0```,@,@*`<_EW0D`````
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_lzma1_lzma2.7z.uu b/libarchive/test/test_read_format_7zip_lzma1_lzma2.7z.uu
new file mode 100644 (file)
index 0000000..4de527e
--- /dev/null
@@ -0,0 +1,10 @@
+begin 644 test_read_format_7zip_lzma1_lzma2.7z
+M-WJ\KR<<``.;^9[-V0`````````B``````````(\.`H`,.P]5,<,713`O:V<
+M)`.JE)L$6^G@`.``@0`670`P[#U4QPQ=%,"]K9PD`ZJ4FP1;Z>``````@3,'
+MKC&9Q;Z1",.ZTR=H7?D</%DDB!LX-2ML$Z:8JM^%!8@O`.5,A[\=PZ*`*GB1
+MZ`J5+PF#Q6VZYNX_+&CIC]X'IVM\#IRF6A@O"L4)>(*P-]#*^T-I.U$V\S<>
+MW,KQ]&30$$K2G.A@ZB\TJ2%=E9LVPJES']:2`CL-F<22BZ>*M00K1&/TM<R`
+MS(NWRIW:=%C$SOHE:-:A'S_GP0X`````%P8U`0F`I``'"P$``2,#`0$%70`0
+-```,@5@*`6![G$@`````
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_lzma2.7z.uu b/libarchive/test/test_read_format_7zip_lzma2.7z.uu
new file mode 100644 (file)
index 0000000..ee740fb
--- /dev/null
@@ -0,0 +1,37 @@
+begin 644 test_read_format_7zip_lzma2.7z
+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
+'`""`I($`````
+`
+end