From: Michihiro NAKAJIMA Date: Sun, 27 Nov 2011 00:58:45 +0000 (-0500) Subject: Add support for 7-Zip. X-Git-Tag: v3.0.1b~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e58ec44597f9d9d2ff237f5878a22e658bd80434;p=thirdparty%2Flibarchive.git Add support for 7-Zip. 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 --- diff --git a/Makefile.am b/Makefile.am index 7bda16b6e..c75f19397 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index 28ee09905..80c1158a7 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -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 diff --git a/libarchive/archive.h b/libarchive/archive.h index c0bb26d74..c509b3603 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -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 index 000000000..f93c3bed7 --- /dev/null +++ b/libarchive/archive_read_support_format_7zip.c @@ -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 +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#ifdef HAVE_BZLIB_H +#include +#endif +#ifdef HAVE_LZMA_H +#include +#endif +#ifdef HAVE_ZLIB_H +#include +#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); +} + diff --git a/libarchive/archive_read_support_format_all.c b/libarchive/archive_read_support_format_all.c index 270a0420d..53fe6fa39 100644 --- a/libarchive/archive_read_support_format_all.c +++ b/libarchive/archive_read_support_format_all.c @@ -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); diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index a07466f6a..584dde106 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -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 diff --git a/libarchive/test/test_archive_read_support.c b/libarchive/test/test_archive_read_support.c index 7b110c68c..1619b0729 100644 --- a/libarchive/test/test_archive_read_support.c +++ b/libarchive/test/test_archive_read_support.c @@ -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); diff --git a/libarchive/test/test_fuzz.c b/libarchive/test/test_fuzz.c index 35af4e2b2..d80c0da74 100644 --- a/libarchive/test/test_fuzz.c +++ b/libarchive/test/test_fuzz.c @@ -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 index 000000000..64cc6c442 --- /dev/null +++ b/libarchive/test/test_read_format_7zip.c @@ -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 index 000000000..a61aa7033 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_bcj_lzma2.7z.uu @@ -0,0 +1,245 @@ +begin 644 test_read_format_7zip_bcj_lzma2.7z +M-WJ\KR<<``.X#6P&!BH```````!3`````````-AGR._@:K\I_ET`/Y%%A&@[ +MWP)!,(6+]:YDCG/TF9,6Y4@I6&:W,\L.GXAK)SG^7$ASM;Y]KP;]Q:,S]93R +M>,&)>5<,&FZE"`#6^N9W.O9$'UE^T-50DI_AD]BN$0G/D?)@D^5S4+=>0I33 +M8+60+5WO?S\=&H#(CWK#P8A*.F39U-N85;*`7:QH4MPY]Z"R4@0-G[PN0P/I +M&4U*T@DV'"`CQ@7=GL@J7_2-A[.]&`<]D>$^(9=0(?Z*\Z.%M#RG1BD;5#6( +M4_PL?`47!00BJ=5&HZ7@@B$C!NO::D+1C%8P).\)*UDX0P@<+::RMJNW=<@+:$ZER\IE^S3 +M<>=HCZTGM[:O[2?'(:Q@840D]\]5Y73NXY[]$A&S9_<=T[^)S9E-I@,/"Z%N +M3%P8EQ8N.<>T1U:G?9*=LX1R,65>5\LCE.PG__FU`+L]UUEKFS13]3C4N`J/ +M2*#MK;D6R_YC0[KZK#6ZDVED3OA(WO_#+'G.!7!DNT&:A<:N8%2S1T,0<.CX +MWI?5110E(]/(V5U26WQEEBY"+^%%%QQ*$$LVGS;&M##OO=PE)#7IOV]4!T0> +MMCVFR7A^)`*]D,D*8(TTY<\22+QK022#]K1P8Q>ZDOE"5%*XR1S_RD)#8:OF +MV8PM<:&:XO9&=U7J?O95W3V[0_>S$IA67];6D(/WJJ:Q>6%"L4.3$3?0E!!$ +M%?%!2/7&XB4BNO1`#[FYN$MKM,TL^'./,4GY) +MNO_;YJ7P.<8Z"R!*LK]S`B1=P4X(^JK3]7X8N(%H?=H&K>KB'G7(0C'R=1Z0 +MZ`IBTR?O>DKYV:WR$Q0MRO.CI2E[0;'KUGX`,?^8H_"$EOK=81Z5Z4VZQ'O; +M;O;-'3N/M(@343API(#U5#X1=PK%4)/F?[I"DE\39HS<'P)7H49\16$$ES92 +MA@%4O+Y4AD=?Q`Q6L[^+YH+AJ-Z!]8./"Y'^Q"*$TJ#,%/ESOJCC)X:D@0R_ +M?J)%YC&F):9`]N(:_,O,>0]$-QL,-?ZE@EH7Z=TEA\M7949(]"*65FN\?-CF(U?7P0?R&\DOWV5DVFK[.JXEP>WU1IL?6K&/C +MWJ(7%PM9ZI:B)8)Q$N-53QL+1O[;'7O/7/O^;3JY[WU7(!DWS`YXM'QL?+>( +M"XZ7)FJ3#T4640CH[0Q8U319C-]L^2259=A(C)[[!/*9P+!6MXIH$N)1*Z:& +MQIZ7]E*8%(B.D'_@P=8!H(?*X?N@HB+MAKFN3F][.%W':*1!5I4=/"TFY>,\ +M=4<$EI/)\S[27U*=0/;8!0P4W/=5+RC"*W]L[Z>GIXO;=$L;^CKMC.FK$GA0?W<=3^K@8Y^D_FAZ!W7WZ#.6HO6/_: +MM&X<5E:RAU)=J;SF.%&+0R;V*[FQB?;-D6E*-E9A9B[,4F',0KL8S]AZ3/3, +MAZ!6('R])>:,3R!'4,\HCK1#'3_?PE4$NKGPX,R?.IK6$:/[E%8278\-K\&D +M4\:14"/(J(V,!$2BJH@D@[TOT`^<-9#'>!Y!%G[3Q4L[4EU[56_:"*>E#X'= +ME='@D%RN4=E!\F6P+B>NF-E +M.')J;D/S>](@#H.CZB,"S;X1F.)O-8.%W0+\OBW-QVYBLHRW$*]^CP(Y/U\G +M<3B;CDT0]"EG+*#EDEZI35$5+9SLPO#7YC4( +M/'^E+!1?^*3VBP3&G9$9CU2`[V+^+*?N^CKX/5VXN]BITG#,SZN85C91X7OQ +MC*NK9_X/LBT1D8WVS!OCD4C7_3>4N>6Y[.58AS6TD<_W-2HXTD?5O2$`?JW4 +M#>E1DA(`HQ=#I"0OK1YBL'?/Y$@>3"0_Y+,WS'D:N2A +M4EH3`]F2VFSKLME)Z,E9_,4[!'G+XCO +M\^^`Z\^_!QY_7[O&*@MP8&R9/?%Q"XE@16@O\T+PU]?>]+ +MQO%2RW(E^$A@,C_K8V6Q,(\<*T?3=QQ4D)]CL-8JTO%EV64)I,+2`_,\J._L +M2^N5B1N7PJ])*IU6?/,23U8+;W*OE:A@V<`;](1`&8U>JD6(DW,D!F]^H:VF +M;L,-_%X>,)O/C?JM)C=5^AL,$UQR5IE^&Z1LE"T4OCT2A?W*V(0FZ=[^9O=[ +M0L[DD:O6TE:IM]=YK:4\#5<6Y>]VU@73]UZ689)7!_EKHD0T^P))9P%J]^#P +M><2"YV.%>U#Q<%/(S*4JZ+.0NU!_ZK^5M7!QAIZX:/Z32G6WM(_6?(8E9)/O +MGY..D_/.%.`#A_`A'J3"O.005L(]?6EP;F +MQH7HX1HFCAR1ZQM^6(:4S7GX!F9"T=U-C/O+ +MM%,($IH!`2&G1F-=33R'N8/38:0'UK8R-M.OE^?B@GK<]_L?9*.CSS19I'B[TRS5DP +MH]XZ*@=4'N0J@P[$RZJX07]PA()D$BJ>JM2B14.HY/8P)=2E8[1@\';&"H3/ +M)K\^2;9WJMT1I_^!-`'/V15^*=!%]QH43#I-"Y#'9@90N#0 +MPDZ7ZG*X0/K*DSL[`\0HY64M4W(^[Q"I"\HMJ\:D/N7K3YEO^MPZJPEH2V,*E=IMWQ8"E;5T[__9''NQO$.S;KOSY&Y?/G:5J +M#A\^T)?$'+?-WQ$Z`'JWI3Y6^9:>JX)Q";!98%$S/HR**LS"4XW8"A%>9BDO +MRAIC[W%MCJ#K];":3B4`,5HG,6OW`)L)@!N][4&Q\=V,!V`H=CUN$8->`C$7 +M%9!.USIY%$TS2]-DLCFIJ/U,M32BDU!R@0)&:<1L7G:H>H#$GO#/7J>FABW= +M>4K(-;FM(PYL[]WV;8O,LY8:J2@V$53&XU`0`2]F/"[->8L@I4%MFH#ZVLDC +M/@T#HOKOQ3B_.`*!AW+0>7---V)6VLC%/Q8P(?K*&G7;RPF!6=-6/1=4MNA] +M7W<>H%2)6-5]EI]E8CK>-9?A/04EG"9L'E#\;IO)#E1-^C!(ZO0H&NE^W@N(3UFG?:I:3P,"#DQ41-K1@*#QE7; +M]W+$_3L5:1$\.("#"CK--V!Q34.3GJ5-9SKJQRW^4W6SU*"-9<7U8=SX2)+K +MW'LG]DD>@FUA#W_-)WK%\.&\#-_?W.YZ6Y]/1;Q\5),6=LX8F\5?.%]X6<71 +M?;:-^U4`M)T(J'EN".ZP>>ZX>;'B_*JQ3QJN?0<,D.5:AB.FA[E5-=-"@O4! +M(F^R\05T8`E-4F!NS5LV(VL-(M#[6!("]1#8;-@LE^V\4*!U:SXZ5@'PPWSI +M0M.I+($7SJ._H<]-!AJ7>BW$J_#44W$;-7S9L/8*SBN=U[)6I$)(U6E&\8D6 +MB7@5F\D)&:U?,]>Z'[6^8E&1Q91HLMEX-7SQM&/P4;ZH/\AF3]9;CL1[ZXSL +MI/3,Q,VTB6BM;E*X,404UJ]\M,L`_R!NT+M``JB>+=-*+;8++B:CLZTF%IME +M1)&SI`2A_;*=>Z[VZ)([-+/O<-8[!@QP"`(J#I3+6R9KMD;<`B&;;Z,TJ,(4 +M7QZEP5>BF,0.XPN@FLPW0,#7KZ"_`.7^R-JS#H@@6\3N_S-OWF=^J+!Z-'?M +M5*ZIA=F_>\[088#:U,JEEAE6PM[PX2+CQS13SL/9X%LXF%Z@UB$T'(ZU?^`SO5Z7\0,%I[8@6 +MGD8'03Z>:YJ:R>N%?0Y$_O[6LZ?BNRCA=37A,R/,$<>LH35**HTHR$&\Z]1& +M[TRX0#S1FB"EU2??5S'ZP=^@VVD@,%Z/3C?/R&5PT(J.4;GTKD;]].XMT8G\ +MF\QQ7:-/`*V+!74(GVTBGJN/LYO[@/`Q8@X9N5DZ^;S[9'("+9I\$R&('_CI +M04"%BKKU$8;RDI/->GO2X-)JR/LL`;#\F[X/ILE)[S,7$=OI49NK*YZK_H[C +M;(U!AU0$6+3;W"1A!/DY/X7G0J@FVI[_Z2[R?2-YS#=2.A9`\[";'@_BJ_8@ +MZS3*LW]:,0(83P2BHK#=<\)9>-J3&3V.R0MAB;Q=6Y_ON<`S-@Y6*H3UDD7B +M;-Q2?F:D--15N@/$^-]81!-&0-4@?-_7ZOP[4228AV`5VGVSOM'']2\Y +M?`%?^?![KCTI]H[V7VAS[QX2;5XE["D^I\5[O0AN[!#6.P7H@4W: +M85X`7W8[7=.^#]'OH0JBCP58C':P-3BIP4#OB?%4K2A;:$E8$C/CN!#!6DB.'ZX&HE5SIQ'V_<&HTDT=.P'>G[9U&,^;,3X:OO_!-[ +M;UQ8*Y/GD2]*"H-R3*+B!%(L:9&<`5]0]6`KR/<2ATPN8A]/"#O*.O@;QDQ=:+&<.$@KK)K/PKZ90:#3X^\!S)474V8(Q/S +M0I%"A^O_6M"EW9EC=(ET]FT[3E=A=OA=@T"KA@8@1@%D[B.60ONF^,$KDWGX +MGJ3UH-]XI=BWT5P*"`/+V$U.1B3$5GB_KM>W%?5S(3F=VO^R-%4K2!/M1[FX +M7D36Y&Y`U-N>@6D:>QJPZTL^/R10SZFD2NF`G*P[3]R(9JFF@KL(4&?!#`/H +M[O]9-9"L3:^?RYBI:*V1)-^@C./??0=5*_(QA_CD1M28HUI#&QI]ZEJUT/KD +M>I"*2]L!?Z4+V/`23D=D.(I+9OSG4D='N_ZD''(6034^793JDG'SP7R:CL_Z +MBH%@,>25J5=7-H5])9HT8(:!J;FP?90%OR_E +MX.&[K87]AY:`OJI"ICPI)&/6=._AI"AYHM8V/,87`C' +MO!0'$7OS@##S9B&,,KHM9@::[=#9;TVMYW(QO0T\@SMFFAN!=W/U?VPY41)+ +M4CY[?_!IV",=Y.8)[W]U8Y;3IBC/^N#/`M#(5#P%Q9L +M#[.ML0%'98I%"`G*I&"M`$`7.D,=)!=4]`>H8M9(4I0A#O\]'3(PJ46Q)-;/K-/`5JN",&%"+Y,<-MWE#2-(%V&#>H\\.RHP. +M]S0-I#Y>E@%6*7Q6FY#*Q&JJ]UW@4WA+,%QE3PPE$8$AS?0O7E<./2QA +M"5TV%"1%(A'0&.[WUV%$,*3.B*:N"8VF*#AU5\Y^8P3WP#7MPTG)VPP#CY9/ +M>N6R9>0]).7-KI?S&LC-D.R0!>&^ECY%Y:#?W#HK,#H:3NE5X;M(Q$^U;\%% +MEBMP@/MIS:[EF^7_SUQJ,Z1VF*;),:QLT]CG4C1\)#W\C'4SBJIEUX +M-)C!Y\6P(ZR)/5`:/6_"!UQPQD"^8):+>OG1JHV,3AV%-NN^6R<&L +MYDJNT3G3\+,?-^B#/?U[/)WSW;&$JD0^I>"MST3DTA4,92>9$3W*HE3E=<`T^@J]\"\I_1@$8P?LYX2=&>TK(H +M[IDX%#;#/052%71UXW$ZM=B7/C./*XPJN8ZV1+&IG$`.V/A"1SL]X@]BN6$Z +M\L81365TRE8"[=YV)6H9BJD@6Y'F`5@R(N6WU+?]VP^US7$WD/&2?ZB:"W=0 +M;ZN0D^>\7`!WCK\DV`DLQ2Q/].IURR&!;Y6)0XVIZ[_3K%QH@_/$Y=U[T`A" +M[/A:#NNEY(59/4G6UVR]J1UV\>)UAD#/V*:1%61;[\F>802&'<-ICN^PP2FH +MR>6NZNWNAH27%QM4#+H'N]+1^%CG`[:[9>S=%M1$$KOK_]JL0+]6+-:]9>AY=+2U\NR4`C3@E, +M#WWFW1DVA*E'.T5\,EV+<:]-A=X!:+G!`F!(9I#4'T^GFR]6D^Q%CR)L`B!0 +MO)0(OF>\3#U@%O^#US4`_Q_@A8*Z1)7%(ZQ%J?6M-$8'\NG=)!4C`MPGVB3& +M2E[9$38(/3@US1,"70*H&87;S[!UC;\*'*>3B(?@G;I3FAPQHWIU-1Z0<22D +M_[R=_8I-;V5*Y;(S,-]<%<6I`?>M!6@.'?)8-U\$.QK9/5Z)QT[JDIIW?/5R +MJ9([\+W2Z_3!GE7JO@]RBM/C]\_O?J.*]M2KMQB-5/.3<1`\H\RQ2O(7,09O +M0?X-9*[Q"B#HGK\B5.FYNX:5QE+7C!.:C]9,$[KPXR#I6LR2ID1EJR/,)OW% +MAE7[+>'NI%$H907$J5)49 +MIXH#DNA!D--=$KY]0&N]Z'<,+_>A(YZ?YK'4`@,^:N?-3@A)

;`2,&](DA-\+6/.WM!X0,.T8[ +MF'-?PAX#0L?`J#M!ALYFK*I_(_P^(Q^*W4MC((OYFP@Q7RI'W1*IR/8R(\7- +MA%IZ-S@\XSB/G*3=?/Z5S;*RL7+@:#=0M/.;(5EFT4!R"M6A)YM3U(/+>OG_ +M0)M[[CD'KM;*8UGWC!D5I7GPX"@?!$[RK_+AR7;=O/-/^T]&OE?RM_N%..M^ +MT95Q0#0\LX%LA(05LDBT2UJ5(0UFL[6_Z_F!KAW@2!A^I25> +MBAF8XNXG1V?8;M,'%TZRVV\0*!!4NFVG!A*@'_(WU'=8.7<%)XI3^8.N4 +M#3-LW<(^*PNNKKY&I/3Y69"B2=V.W0$BTD>3S$C1F]!T0QNDP8:;Q&S7*`?G +M+GU7=_HND#LS58$JQMGR`RT81!J8@XZ_.D1)<0IUO8.'@ +M9I!6#.B.@\2WO'G4NP,5N?JT?0W.,UZ70[3%(UJ`O7%!5$22^4F[&9A=NZ6T +MBP+L9@9""H[/I;A2*\Q1P/1T$NC@$PKB]0_9N<:]Y_5YF=&M@6/R>6/2MHA] +M;6.[EQ*DK'7!P6?8PN@_(N5PRW4M[A=*#X?C&5;-3"82;2VM]46--D'^+$N0F$A"2OC'&8LCB[,0TY=SP11Y57AOOV^%I^Q +MJK?+.UP86?:'9U*'*1*QYX?:[&[+X.[@RD*A6BUDW09G'33JZQ`BDN_F>DUY +MP9I,OPM5(RBT/&BIEIJ4=,;#2^`:VZ#-,]NR[!KXDKC;3:7X8P"4(L15=&/W +MYL#*W>/VG'18))_@*80Z!3QR?DN05D#)$320O#Y)4\.0PME&.N1IZ2Z[0.6, +M!.E_O&L$G!\UWULP8^IW_W*.O^UK'OW(!GTU@G!2HXZ0YR8\"5X4 +M"9VB,\N_OW&P,^];7*Y8BC#P&JV0`/>4<&2-$=^4F(\\ +M3:!!$GN9\NG!"),B+35?.J0@)Y*##"4/2%SV7__/,;8;Z8*ZER_S`T"%S]_1 +MY)V+]!Y_YNOOV%+(QZ$;\V7^GU3T'%X7^G1LFW(5`")S?)"ZY"VV:08!;)=@ +MB(-+SEYB>#7ZHN301+UFJMH#DYKTH[>)I9&`C=>KV_/B#7D:0-N\#.;BQ_"` +M/(V!RGSIR[40/"&/U'JB=^Z5#$^R7Y]0M$,C@IUI.^P9"M/E.!0:<+PB^_"H +MD[GI;X@0!52M![84"KTU*3'MZ+]@[691I>:X,(S1@L>4$U%7Q27("H2^J2/)`M#8JV-8RYP!>[=8*0']+B3"R`]S\$&'I +M[0&>VGJ//Z#RNQ[F8X:I?;P$W]IRSJK$>+\C);]&ZC\H)O`Z0%$147&IK..R +M.I$_2$F'O4Z&,&YMVP"L"NN@2A[A4@,<++T))G&/^,->Z=^M%4\?_XMNBLX) +M8[M[OCQ@=T-BJ@TLP)[;CV!F&@YN@:/VR=*&U%T#0?T&(IO2FJ\[Z4/$U_V.-/CWO:U%S$^ +MH^'*,TT?@<.0:LH+MP*>KD;02H58$)9VW7P-"KF-1GE3;R>\O-OT,^*G\*"I +MZP2X**1:T0,F#J_QAF3-NEOTO8S'(<(O@7*?,K;)3:_.K")@Y[&;IR,@;[^! +MU%*\GK\E2C.G=3..B8KJC10CDI@>4&73_CW0]^&VUKU1[`&`HZ@FVZ;ND%:@ +M.,-9#Y7<-.#4)^^EZV=?7$354;O:D0/>?3'&47!!571? +M:./5N/XE>@F.?7-[6!=B*`M=`Z$8%(-8-MW:$_2WB@12SV,!$P%(&!H8H]UNM>=5G_>GM +MN$W^Z"FG<0K1N@P[_@-TO/DJ\A%7@G4OM^Q$H?[WG%C:ELB$M2UE.U1TB-<2` +M$MI``@*-5,#;]1%S1$241WG.SBJ`BR#5(6DTF==42ZA2[68+0#VN]'1?9M," +MGM\^H?YHC%GH&!'GZO1K+PXY$#[L'=6"?)X;W`*9`Y@,]D;/:Q!0=N5<#[GT +MIVWEJEC8\[!OMKUNS,/N1"OO+W"";$9\Z;+12Y,R8K-!+T&2"WU>G:_7T06I +MGB@@_7DW!>A]`\3>"Q_TV&9=`==*MAY?RR@5EWM<: +M)>2FTKEVL3[GSW>S3=>[2EP!F?F5].N/*_(2^GWP`26*S=3`%0F`.WP5V0YD +M/1K:U"HC3ZII\^%@FE@I2)LKKWMW-YR+EB\H%MF +MF$Z3TL6=+K;Z>EJ=G'VZZXPR,<`K^]F89P%*8%S6#J`U836CGIU[P(?7^!3\ +MND`<4K(\]:.KX3O\!W!IOYK"K0U9,D=!270*T)?GU?,L>)1T_C#3D:*Y!ZOJ +MCF=HX[("/=Q('%YX.:V!PQ;U2F-"BH2P6$G%6_(8MGO:)Q,+8HO +MM:<#%;JI:XVG-%^\G70`LFB5>24!<]N?^%?.W>?&FEGA+BVC%.6/"P'=<%K^ +M(8BM723"Y!&*Z)<>^ZY-6S?QB"43$Y)C'A@^B?`L!/@D0=H2\$ +M*ALMV(&'FV6)=PC[[8>!84`9(:STD8TW:;/N"4IM]U'9FFL>I^J:4Z\H[#?Z +M^6N%/DU9(7[NY"SF+V?$N@_G'(RLM'2%V423A3XFY/&WRNZN\;$068UBMF)< +MFD;<":E*@8N:(UC]IVTWBM^BL*VR8D4QJW4$QD_U[52VC"%QYQ4[-K9V43U]?W:O=!2%]HR]6LK.PJW?1)P*GUO!"(`("4TT +M2J>0ATND>DE7.D9-SW.K1*E7"&6:.`L]6\VIH4\*X7^3`:8]QC*\!5D0!^J_ +MISZG0#N\GQQ,IU?*50;I4)#T\'?YR@V1BQ=60^[1O,2+1H#8"=C#Y3LW7N@M +MW!SP*%J8_@P5B)OD.V5P7.RZ%WV\H@]9%1V_`F"B&OJR[^HYP--,N?N;GQUR +MPIU6.FOO\.I1(?[.E30]7RR-;7I!WDFM?A&%6/]XI^F-Y0WM[M'EK#- +MD&S`21RIQ7'A!P"94]4RS6L)AL,(7HDW4MV6`WI8::90W2_'8W3!]BN<$S/K.C41\9X'YTAYYA1FV;&*)%(T +MH:U6;@.Z<8OS'A4H`]7>7I!K&Y9ZH +MA)^,MAL^H9N'U(G(ZWF-;_XY*SYGB,.Y>U$YY+Q(RAD1V7>6.^[".?8P/1(C +MWBCE?Y(>Y,GU*$9P=9_V/$Z=-?VD3'O#F,=`8L-;(QJ&YM9O\J>)E(DC[J\" +M@;>0*'84X\F2Z?,:(*3,Q'XET;J]O +M9N2A43DKBK`!N"+>+B2YC?G8OJ&,B19LT2$JA6T7*;$CKV,`:`4O$!RQ*%J\ +MBKE9+1TN^:'"LO%,25X$Y]T.A\29P<3%IL!7&7$!3B!EPWTQJ8.WV,[9)FOF +M&9B."!^E;KJ*DR_G9F*(VN'K[\;%#\:RW60'%5-5;W@OFSL";N--CN[0J-M;O/@/0G +MS>MFP;N(TX785X)P#R%J$SN#YTE5K6CNQ8<84!T(/']&.P,<=I3^O86VP?ZA +M=Z<@\!L>]8]_/.DR4B!9X/&L&=]N1B5D_196!UGJ +M\]4%>:I_J@)>@LN+S]!-?QHT+]D5E_>#D:UYK6.WR(/>([$:3)<4.%I*-@A/ +MIGNY:RS*3S2EDLW_2[LV2;LYCM&3\ +M?.=84'RAUR=WVX:;A"2DFQ!E8S-I^1<$R`HZWV3?-G]8:Y#,.>A@=`^A;Q?C +M.V(S=_;(,[?6(:F[[");ZA>\H47?VSVG!PA;)3(/MIYZ>'#K`;$8/=OK*W7] +MY)+=I-O&NZ\B+.&*/D=Z"$T:D^3DO%T1$$``(#(NTHB8(1)H(X;#B\$T[$X37,SL]?XDR'KO62?\%3>ST&[ME+XJ(\Y?T?PS9CZ5& +M,@$E'GCZ>3;7RUCFF[GPTS:A;AH>/&+EL;?#K#A^NGCWA>/$M").DS@4 +MPZ3\;T96@VW+?A-+_Q*=HL,2<+\R1G3.#(RC`Z&L]^227?>G]"A%\F]3B5WP +MD:I12I&V<,(N6,.',^$%^X;]L%NZ9H'Z!KU!@+E#R[K1(?M]><;/FQAPLS?> +MN=XKTYELN-PIY&*VB$-%G-%6-XZNICMSXD6&,EV(J@@.-R`I`XR2`G^,^T,0G++HSITP.5B/&132Q\O0/2LH#C:;Y=.'<;S8>X: +MKKEX%(3(25#P.\@!K]%$*O$UII=CHST9VI[VNC=DB1Y^[ +MX31T*GTGV/_Z9@%CORE;KG^;=;SZVXUT#:,W$=9:TO$H%I^!34,9%O3F&AET +M@#7+XV?Y)9$Q7;A@Z'%#;$CF]%6R28_.XCPCJGL@B+^[T@`!!`8``0FJ!@`' +M"P$``B$A`0@$`P,!`P$`#,#`:L#`:@`("@$]?9*5```%`1$/`'@`.``V`&4` +<>`!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 index 000000000..07d231ef1 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_bzip2.7z.uu @@ -0,0 +1,37 @@ +begin 644 test_read_format_7zip_bzip2.7z +M-WJ\KR<<``.O"&:?B`4```````!&`````````%\J8&Q"6F@Y,4%9)E-9W9C" +M^0`",E^``!!`]_H]/^__X/__W_!0!>&V6UMJ7NU;KNQ4EW6ZT)32$R9`3*3T +MFIM-,*>H/)J:/)/4!ZGJ`"43""FC23]2&@-`&@``!ZFT`)3))E,AJF:GM*/4 +M'HFQ(#0`!H`!P``,`````````)$@C4TT94S94TT;4-``````4/DD[A^'IM)O +M4?;TRQ^=[=^`4PYH41W1A=WR^]==U?7AJZT?W^$+2+C,Y+0@8D][(7^]64YE]_LI/:HISLZ?Y64ZGYC@P +MS*=_?2LN-UY*JV%5P0;L##"6UP:G==&QL\DDAK:=U6@2DV*#;7U,M*%L[[XG +MED];.U`1615GK$ZU;&)0DY.CIX3F4>.3Z=,88NYAOA`BGC+*-U\EMPS.A2K. +MEGU+H86+IM8H0T:(D98'LKYE*3>>"SC9UCCY,<8BN&W=; +M@J9@5.CB/?6G/Q73=1SS;,-N0XZCUB'NH?O;1=NLIUMX^,BWPF> +M)[2>L'[WKK9_:/=]JY3JCFGX'>H7>?:5AV&%5UB1\I^]NJI76^C0HZY!(XT; +M+Q/HJ22Y6GFO$2JJJH4&@ND.36VTQGP,W[C-QR/;LY#@J&9OG=Z>E9/?PPM% +MY6C"U%C@82X'(C=;T40TA%@UT65V&A:0,$%,%E$W5Y,:.EQ)5Y!9R%Z$?H.5 +M-V>CCZB?'#'8^93`*&+HSY..MS.;&:PL[0R==&-#V,E-L*0J9W`\E6I57)DH +M-ECDI8S*">01&1AIQY5B5-%GHU12>*;9%=022."#&9&+:D\;HNT')3 +M><"$.%[1,";-,`U*&V5L(8\MA:Q"3>H'$J'V4O8WH;D=)4QSG(8Z"$JUG93TP'1Y(T_F5=HH87RNA2B>,\-#E\K`W$M$C-F8P(9.8@EQD(A]&_(&SKWPETM(:8VG +M.$V)@?(G(S>\1ER":3^\P@4L]2S#_;.%]PBW$?5:%TUZ8&M(%6$F%[CJWB&) +M>'ZF_-`B.ILR9Z.Z,@[I1F8I.ASC<[L9L6CBM\LT@O*$&<"#A/@0D8X2FI0MI>6 +M3M-W2RD4-Q%U0:-[!&`R&MSG,,:2BKDDD[1U( +M-B9%;RND4Q&Z_/<&)=.:(5!$FD#4X82S4(D"&3&WV;7\*G*M1-39@QS_\7FPNRIS`$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 index 000000000..2429834c2 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_copy.7z.uu @@ -0,0 +1,7 @@ +begin 644 test_read_format_7zip_none.7z +M-WJ\KR<<``-!QGV(/`````````!"`````````(/;BV,@("`@("`@("`@("`@ +M("`@("`@("`@("`@(&9I;&4@,2!C;VYT96YTN;;\I7YC.>1BQN+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 index 000000000..bf4f051cf --- /dev/null +++ b/libarchive/test/test_read_format_7zip_deflate.7z.uu @@ -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::>VZ#` +ME.(^H0L4&_FA'A:9OI;TAUBKI +MQN0YU1!KKG9D'#(3+F+SW-YP5=-XIG2ZS*#563A3%*HV/_2Q,1VOMADXW)?< +MO/'X5*4.)P<)#>)N*:-!-1V;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%?FNN612&M3N68T3(YJOYO1O-N8%@PDH_`FH\2-R72^HRT*,.)AD6%-T6#;EGT_TXU. +MO_VR-QAW=!%U#I<%5150$;*S+8934K_#/!/= +M[I3ZIC>5J6<:D2R+"P\4=;]IG04;6V%0M[KVG"DXEM=EOT/K+YU>S.I?W?=SWP8/Z`@4MJA6GUP^;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]?Y*\,<=W/B<]L?4PIX7"^AG-Z+K65;TK`(.03&5,<7B6?/(C07A> +M1N%'.1$3.KV@9"K(7R73,'H8G]"7+WZ,B`3)^7(F +MD84RD;](I(A')!?!;#61B_,1G:X26H0)S>1<)@A+PE%7_=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"!:_?N:6>7@U4DYDP.@N/5:9S(9)4(.@_# +M24<_-T```4!$0T`9@!I`&P`90`Q````%`H!`(#WIL+LJ]Z/8&Q;PX#$!$?ZQ?PR&S?06__\,PN2SWN6PB<',Z:`2?+W5NIZQP1?:,X- +M*G`\OL';&5F`W80F)['CE68*4/!?&M>Q_R#$Q;@`Q@F5(I[U[F/8COQ0BL\Z +MA6GS"(1>NNT7UW[=_>9;7AG*PCEPPQEG^3!\L_BGG$>(P!]1=3USVU32.):[0H>JF(9*O[N7&AHVJBS +M+_&-5L$(`LTXLA>O"]0KI-^&2EL>6`T68&4T4AV?J'CYW/4JT7.9.]2-G]B` +M'$Z\$3P'1!].A&N'IZW.+$-/U@QTY0DOB0"/L0"BO_7;OC9A1#M3DKY_.!]1 +M@3UR451\>I/;:`.VS3SJ_MO_/E3`@#\&]@Y4][!$,U$05+X.O4U<%Z5*<`_P +M\X1L?(("JO0$4+7GCN/AC5<9]03!N8NSHL=P`_H>=M-A>$-:*G]-VZ>[H'XZ +M*?@%RJ8@E?SWB^`3Q3W[5__+&^[9`2E.A0WT,QG2>)P$BOJT=@0*>."DT!.: +MNO#(K.M&!)"@[;<'(._$-_+HDP67X)2/Z@UMT`^&8/(YGP%A%XL'FS85[J*&VTE7SDZ+@]K<#2Q]BR^!: +M$0&$?Z71(7DJ,*];&?AJ,8>3!`!J'4O*I=CQA:$X1-_@(=NAG>4N`#LXE:52 +MOI"G\%QQ[MUPDA3A5"".^SSO/RAV[IF3*EC2`$*^)I?I?:Q$X'PJ$[O&[H1S +MET$1SXX\SSK&LAYG4QWR51.W["K]U5Q+C!C2(3/]/K2$*[*UU%\M7;$%2K<+--6SY8!=SBR^I6 +M#[I2UTLHL]J*Y,UD8UL!AMC!SG(/X6[*S3G_DP3'.R2%0%_L@ +M-\X(DBY',.J?&`)$+Y"EH%HR_5Q6-!;S)O"4WEJK:_O>*XL9?1ZO1LSTLU=* +M2\`I_R0L;]GSS4X75G>R%$?VWSE8O,U>P-VV#!H7-.4P]1C.NZ>GXP`,X5.N +M.%?[DGZ2XC$*1_V#]G(_RCD#M^-8%?(D83N#1SY-3)(EFYT^H<&HCX60=X$# +M#3LE/JGBC/:$DQ@ZY!+:5GYP:9\-H)9&E%P(<7ORV2P*\^NJFD6@\YX:SQ_K +M_?E2=;*.EQ]Z.4HKBRP<+*N3>-ZR#UW,R?>/C;A'K^/WA=(X`XCDYXD( +MI`H%!NUJ5A/J^8Q[,PH3(F*9!498.OS4%&Q[)(!=%B(<4;MT0.+>_9OZ;PI0 +M/7G@A%0>MN)310Q1^^-KXW!@913&8Z!;=.:6'0GMLNJ0^@DYO/Q3QLQ]]9RM +MX>2?<>)66*R$?+F$S_C#69"K9\P):$TB +MYMG*S2@AR2;12;=1YGI\<+5(>F#R9'VE[_NA\#RQL9Y"K%,AL$+34Y:I9P/G +M%%M;X+0K#Y>T]K5FN=\,2?RQ^$Q77](@_'6HOQ3HY%CVIUD_OR(IO1>>(9_UJ4DSL!D=]V'^ +M*+UYF\S&``SA(AO589;-MK#V>AC)I=/#=B49L!+,MAJ(#KV;>82#S>=@@.8% +M`^C.[R$H2`F1-@5'@3BW: +M772[UL#!!3DRS^&#&$9-ADFJ@R%S*?T&#%HB"HMQ9(%RRH]&QCTT3'#:?!B0 +M7`!&`CR`T?-ZT-JOK07K,;!D:L*L47U5[-='$_"\]12X(]Q>0Z0$P553`?YZQA.U,'_!NT:K3KZHKN/!+MT2>HRWQ@X%1H@_ +M[.:Q[!S?0=GU+>RQU]Z;VYP87DO)3R.&(F`K"6]*JC3QB*;=Z]6B)M'9N%`* +M&@D6+AZ-B%KC2FR(D6,(X+,=94.(6T7R-Y\3;`3+G5@[A8BS'N>Z(\+6:IAF'=6(/B&5`P>P/ +M-."-*9K820*=E>S#.6EON]"QL,K@:O4K(V=U9V*MF?U:2=OVU!CG-@]1H95\ +MMRNM=X/#2TBEP\7'*D7IRCFMJ3TN\;UES!;;"8?=Y'BA>.A?6$ZW3'9<,@0, +M3-B(K'*F7E)/QUUX"#`]^\"H,OD*C'0@;AYZ=!K;_+"4@$MT_7$/(]UGE5&+ +M@CWVE11WZ)6UETE,BJME(D`T#SAT_7&(B/9D)W>N60ERCMI^Y>'HP/S\Y*C6 +M+3"&90U<5:FW:7YPWB/WTL+\`/=38B>"V(DFG#:V'NFK`WM03Z]\.K8 +MSXZS+W!'W7P@@@=AS^DH[]Y65 +MR6I-C7,%B;-$BB(Z\0(9V;,W%C4L-US:PMM[C[/;\+QC.+&PT1H@6AQGSX2! +MKPB5,/21QJ"9;SP2$*OM5)&L.D2)!;O6J?L]K``#(F^F\1]7XLX=66`SU>W0 +M>IY:WK.UY;)\UFMV3L%G/3:S;1J&4L7I#2)^":\8S`-U+OG)6*@ +MA6DM,XBJ>1F0HT=E@#K5CT`,C,&&O]X"97"W4Z;B_0R;I-X$VFI_\%\!8F],IUUJR/4*XD*CE#E:1#$V!I!GZ*6-K=W +M:X;?]7+(3BC8'CR<-CK2_CS:N&2$OW#7++#94=YH>78TYTI+&N*&9&(!E;(@,4WA>5Y^^TKMRON#') +MN70FKU(AD%EOY,,*ENSMLD)3=**-F@@8ZW&7*?O^+]<*;N>Q_,'^TI4"YJZU +M':R5E$A_R+G-IO]4^U41ARZM;8][Z$OA<-!S[A`5`IL<&1'M4^*#2-X&L$P5 +M=D[N+M9L4^!3S2-`%@"V;-[(&)S/1Z1.5)`(R*6S[#UE_ISL@]6''(DO +M7:ETF;UD^7+,;+?%14BJ5DA,(37%:H=3^M4B&P'O^J/Q]'6Z4^>7:4N'[&BN +ML?*1R#,X2P$BSI3$G*=(9='']16_)+@B)F%7L#GOU_0O-D)A_N4L#HXX?+M> +MDJM\^V=?ZL;4^9J@-6<_0S'.AU&ZT6RE/Z%Q>3?<%[R_0JAW[<=FSX-U&B$` +MFKU@I7O<$-QW)SSXQQ4G4[`?$\3R"7'\#]+'D:H^\U-%@MLL*O,R3,%O +MS>P&'=_R;'$&B^N^9\P;+67ZF;JW!XQ4;(9SO[E?-`$J+%^%G1%PWJ+()C0K +M4U&=P(D#^-16""L81BR;?0MSFXM;O@ET.0'@&S+/J(#U$/=9FN!&,`;UDWDO-`/"NRLAA[RD&LO*U$2'D>`O65#+7$!#XQD.2^N&%7O(Y?2*XX: +M#-B*1D>U_USYJLJ*;98ORW0AK]':_E+5T=NDIKO3MP0!A)0DC3YR-Z).T@.E +M`U]L+U76`C894>4N"]@%4WU:0AS\G#RW!MX\?T%\S$K,DIHTP8@:K`*X2;`- +M7E`B?7.@W+Y%AV&KPCR9L%9U!,"0>GRBA.X@U3GI[=A\?D +MF/&E@CB3!3!=\0ARGD_MPR&&*@DI;E.S"L7M+]$>O/,B\\6E*A;C*Q3SF@,/ +MIEV;SU9="P)31] +MOM;"7LB+2*:K-SV>K$WC+\IE`8I]`81U\R2ML.T`L*%,Y]JI.SL.YOTZ_S!) +M%60%EQL%V3GT$MT[5PA6T0M(7>6T`)5K1:O-`EJO)@)<4CG[J;0`2E.+1%,/ +M[8=M\I8-+^S<:H84WJTQ.F^R';X[=^:]'_QD[,W@NC%R=#XPG_'-R#E*7J6O +M!""049K6@5[`6_MHF$R"-K:,K>;CK@3KT&XE5;QU62HL>*[\?/24#1@5:+8E +MB,#1F-L):1JFY.Z;8&\-D%$F5>DZ&8[!"Q<[+CQH*U(UUX-5G$MK7N:63Y!32\EM.;C^-:5 +M%Z8WV$?E?O2PBNSR_N[5%;IW?AL+'M3M`X7@_?`_R6G*__"0:@J:B/VK?K"J +M4I912,:+!K(6:ZE`K$$)`>8>Z<<9VGCQN\3*QIH_KU)P$7,VIH=LCJ55-N5Y +MK.DW($-*L^CGL=-8DU"E$^^E"N[=S9%*&%]I1[)6K3]C\')R"6;B6W%_1-,+ +M.]\D"\[H#%F#Y$0T"$Q\YM=1"<7=Q7E=7++JM>_'IF%?CU[?P=33.#_?U.:6 +M32_T[CK:@6\;/D?!UW]V/D_Q,]A6.3ZC$-\@R!&=/MX:1(="+K[TU0H^V37W5XXR;4JKT@#"76N@P0,]\;$L)BX3*3QBK +MXOP$7J\6*[$!>F=+9<)Y+PN)C=49&/&H$8E7/V/_J6O4?@)!DZ& +M*$,23=I]VY08XC)Y@DH;"BFX;XT4G@HYSYJ'? +ME3@`-T*%BL5A+3:Y#A/>=%M@&^1!;;HT:Z"XJ&C'#*D^X7!=LV%4VV=D?B// +M>3JDTGTN0+FM#&L4'TE1U743(RFT/&7=,#$?5?5"*6J/7]25?4>U[;8&/I[Z +M;M"*%U"R[;3HKIPBB[]G@CYT,F<$X85.FC\J,DOT.\/_?>8(%HJCW/CTA_^. +M9EP7KNW)2<3#'R'.Q<\QX*@JI>LD;/_.-X?55P2>WO_TP0C3P@AYT?AEW'5, +M8SH+/[@F(X`I>:VUHH-@;,T?.LS3W%M*>J-B>WAY[]!"2O,.3QK_88V_-)0N +M=H6,LCQ#]FJ!<&M<6(4B6J?:NW'(K@/#^O9(I7D\L5@QQM8VM8I5==&#HFA* +M,*=".PHO$L`@F)W4C5.+-J$@)L3UU3/ZO2^S$+0KTX:-2;PG[3Y1(;6462]( +M-`H;2%K^>"1``(JAO6EKN>5*W5:B_(>)=60]SG]L#@\H@V3@WJ+%]*S4:/[&N6&=DX:<%;;4O7K_UWIF +M-9,X?NA@_E+G&3[N6B:)&^@BN%#MT#UF=3 +MQ#<5?UYVZ:A$O%#+(D[?W*>`8TL,>`,G6?OUV#R*V83#FE8SWU&!I,=73_9_ +M6^OD&5Y/D"H<6;Z7Q9[(#Q&Y2@\YH8#Y'Z+K:B+,4TO1^(JM+QM('M4X2O=/ +M5Z!$[<;SIYT:CSTK7[<\_9T'M/:HLHP`#$ILHW9FML'GJG8%R!A.O<5$ +MQ%G9!K\1U7&ZA?0&GPG7S5YX.0PD;^(=V/<&Z6ZJAWR5A1DUV@T'QQI6-/F, +MG1!0",MJ@A4@;I8D=O\,>@#\B>'8X&T0$)8;3+0K] +M=>G@K4:()T38G$_(A16)CG2U9'0H_O(1O1EIU-<$E:S22MY1`MH&%FLMWE0G +M$B0[QBLL$T_C3""DWAIW`V"C30&K2Q8=-;/FN5X;"7?;-]\7KCX0[0IW0%!T +M(E#UTN%4;1$Q$G4=XKOH:C+%7$0#*TO9I*'EC0SOZ_3VR[N8S#F_"J\A!6]B +MG>"TP`^7,JKA6K>D:[5:1(OZN5"EAML%U$V?$)D1U$:-SRFT\Q0*$G[\;A6' +ML9Z\76EC'T@W,,CEEE##.@!NOE%Q^-Y00=37MC!=(J;"M(FU#5,%-5'1=DN. +M?90=HS_6.G!*^LB>+::+K%LCO'S-U?Q>YB7)6((& +MBMCGGW)ZJY].)LYJ1B5%@9Q(`V +M>I0<-MJPI,W-8_<"/J\"E^M6Y[=F4CE]CC@U:7%+S*,@?'X +MP7C?=C]S77H47<`;$-+AB2'?5SII!&#'!0-3:`)#9.+`\ZAH>VY;.X'N!\*M +M*9G5FQU3S]:P@]-`OME.K`N,,2^.Y;TTGVK]#\%X1+F`Z^*.G#0['7+5BR:G +M4LJ#"&Y&T`W6:82SA[C0.)`W*:N'Y*@9D`.?G8/`%Q9#31VG7SCG@+7L&4+C +M<@?'@MRBJGK7_TH;@F,9)G1D`XC)P'J9@N7H??+GVV:M3E`G"?V@>%71:O5* +MX;'7IIMSJKH\N$^.18QUA$H5<8WZZ:,NZM6(^:#F*15I[J5%NIM +M>$,DF7E!B,MX0C:E-F^M$GLB(RP#[]Y.R^9Y>1US#=4@]*&Y3%F>:]1C(?]'#4O")9CM2?%>02'.)_HJ +MR\OMKZH#5^S0[.185IWTBPD6KV3W-O"=5_O)4LE=9RW98[@RTRMEDR5Y7C-A +M%;R1LE.%#YU16M!O\4TY!S#N<@_*$I(1&6,XGIDUU>ZW%LU,F;<`Y2TY:,1( +M!2;]WV=*NQ81O//B'!H<=]H1];IJ13/ +MZ_LD<3IT@4V;^/0R5R`L`>!-Q`[8E*@U$#T2H97`M\N]^Q705Y)3YH=[=4C` +M@:C-,E@Q+8.XV9OS`^,H&DKQOTMW8LB61D3)Y\&7;ME9\>5#/_?:X!%T +MA6I4ZU5\=2>(&H\;L0CY*)`>A-]84HU]\42D+8(I=)GPV-&55@%U\8-(#$HK +M6[;,I:!ZG'L"TFL*(VA],B]Y6LR2 +MRSQ;GQ"2H.NPZ8[YL6Z>FVC9T:;^/0I\:#:]>VS&Z3^$9[ZHXVAZ:AYS+W*/ +M)!<^L"H!,<:C")1:VME$JBGG]L59CZEW[C$9'MSRQ@BD'BCL.*,7=+*8$K/W +M^#':];GD-[YJ;;0UL\D+W+Z+-C0$[=M6S.Q7/.\KR\[60([1L(0*PQS\\7Y9 +M4(?#O)X;S)S44M\#69LR"LQ&*[HGATFI4V8%X6H#8<(2&\6$E5"!QOE]LV[M +ML&GJE2R@):=\U@*CY^9F"Z[:6]SGQ&5.T"+*DR5JP5\W[0[#4>9"75+,2TSZ +M>R:XE-B1QD4Z4A#L)X +MM34\;DDCWY`"`;Q0@_"^;=LL(F0[YE0H+']L=/&HQ7XXQDTZY?"DDV`6-0J7 +M#878D'C*^``8"B(E,+(:FX.EU.]C&H<(M44S9TS>:0@OP,FFY_U"CNGE3M2^ +M,2_L]^LR@S*[=1$A5FXJC5>(G@4B=!"Q3[:5$>!<[0(1&XUNPHM061.DG.#( +M-0MZ['KBN@G)%XL#L`S_O<,A-?[(>2A/$YAFRL.S\TDF';WP2J$8-PXFZ_@3 +MZU-:WQQ[TD*0847@^')SZ.8E63.PL]@("!,7A0K:CG5?S$OI:_F'X5O$_:@5 +M:B\3=G6ZG&AES$6PK`.MP,STG/J!_O9CEBB#&O/R0PA64 +ME+Y+]OK`!'$OXMOF+B$&*XVLP2/CW*>D<.1W\U?FTWRA(C^,`@MF1/_B'VN. +M-%WLVJU[EA"E;1(6H@ +M3*)"`[36&@[?IB-&?I5ZB.'D!6V_ODT?WKNJYY(%O:GDE&>+I<`>ZY6$-VJ4"A6>-A87.:TSL1R;U7V+*>Y#B6$S0RNZK0]3G<2D1Q8D923C.IJTG. +M8"1_+Y!%\$J@!G3;^P6SJ4A*OHZI"5AOA_QG&LVB0VH;4EC*TU_EV;@1,E_* +M#!MTD^7[B8;P*"^+K#G?!UDZDI49QL+\,")7-ET$-/`,6C8+!^I2:)DC8\+! +MW]/64DJ!3W.NG:4>>U:B>](T^4@NU4N(]MP)3%&K,6D=R<)&P[`A^]VV!F6\ +MT^P@5:@NNJF%]ICXND]T`*POO7<:S85Z>7*_I_+(N;VN:$Y."U*7Q%MIG!2_ +MU0<:9,G!YG-DFSTT`&0J +M!\7!!:20-*-0?F$P=L3)'7IO\)SFM3X=%?!__R]ET +MALQ@!*Q0([\V2>(I='370+W;>YW$KV[Z^DGC*`H@S5\G"V0U_Z"@-Z-5:-MU +MIHM)]]3U3@#Q,Q+_CYZ]T\@"INZ;KX?9JE\WA:0_DZ%MWB*N=OQ!,A)*YIC& +M2XQ(/]>NE^<%>NY# +M)BA].AF]S:[:L9W'_1N$$^[2EPGQJ3G`"X^R52LXR +MM!!"9#E2+AM(*D=N,>!"A=.Q57^Z&P`6H'0]R&-B410DS8`BV.:WF])S>NO& +MWZ9]J)69UT6+XZ6_3%+_ORUKJ#C80_`F-(G:(MN*(:H#H/$;3:R&EAG/M[JP +MYFQ([TA9R.3'.1?PSKQKERD<*%24DBJ<53[E5U@D!8"Y`KRY:I`>ANI%B_>5 +M/]C\D6K,;RYX)IF51K*"57#8CS#J5I,6D!-IZ87S<(':=1/%\EH_W"T(B#.6 +M_PX)C@(D?+#Z&A#OOF"!%Q=BPN`B>S]JI0I/@SYSUXPN/A +M+Z.EVEKRE!G+E["AW`%V4]8I/J6Y-$J@5AM+SOY`^KKHY9-;#!(P7K0HM5"^ +M!T13A%X-1LJ%+\G1FLP;#Z9+*/P$F51YI=?]@O'#\&0ZC,]=HX=+N+L@3=,U +MEX]S%$"*KI3324[R7<7?1X<66:2)NE=_>T\OOJ$G3L2-U*TN]`=I!`9#\7G@ +M=S"U$JD4W#C]V^A!^^$AF]I_''@S\52C(006+;YEKX42BSKPU<7\E,];'CPJ!*7&?,)J%K%WWLI!2M6,RU1#VAGJ]J#:^G<`&@/2*D0,%[Q2@X%?YA:^ +M$&P(CXNA9Q-8BGR&BEY@2)D5=/5'8EJF/ +MN7I3E8!>!Q%*5T(S.1$NMD7L5=]=\N4I)V)'W5^+#SI#;NGX-8?`W23W>P#! +M38U$:\Y4K<38[`WU_?!NSQL)OP80;:E6WJ'$&ZTPB%64L,1^`4AK*!B66B]. +M*R:X3J)?YPU'-',Z]TC%)27C6.E:[==+^5GE#\B,C'?93H?HSY_OY4N47LKT +MQ?6A-47HE>4OD%450&B`+6+$`]<[.A9#F>?),P3@/8M%S7$WH&\4;3\;FT/> +M!!E(J__9P?+7S[WL#$.=1A.931E8*:"#[%5=2Q'X=W*O9JD0)TF3S.(;OX&S +MA;LYB!!;<>/V%%Y[?71\,<'P-A,0(FY.E+/PTE@EJ_MT:A/YO\<@^]]A'!M7 +MQAG4W_+,48ZS:@:XL%!H-&&!@]X7*N9BU9^1ZM@4%PA=0+801N,(RNC+A2.- +MZ$K,2NN80#26H("4[7-,DY-/WG[),^G9)3R?L`U8,4V%PF!&\* +MU5AD9/X(.(LSZFJ5,1';1?,70G6L'E(**;*FZZ,Q,^,GR>C3:)AISAENP$.^ +M3F\^F8MT)B(+"W+%U>DZAF=#)<(-#CR3SV>J&-4F"YW<)0HDQSR!^"J(T\]M +M(G-T`U!;U6J>P1;YX`F6K^KL,HD&48,_+5*X/W,M_12A1:OCP&!,>US&I7(V +M"?9$/D0AI<4C#D#G&9N39K6<%-9BN!?MNF7ER^05`,]J +M_^Y#V.R)TW-%&Q>A^'UQF0S]'U(=VMGY?*F_VB]AR*!5*5-UL`>5`(\1%UUI +M!M8(D%1C8[KC_WXX:#^,TRN"W(A'1]UAUG:./MC?4]BN-7J'5'%*S"&\Q]#O/]Q(R;&6+T!1K +M=_N`.18AF-[<.9F(8'^[>@;(:@KJ%">P7_OIC)(QH%'0QPA&8HG +MB(:/H,6[?5:,+C^C5$JQ`;BF\)J9A^VW%/W5YG5R.]H5_/"+`,44$56V>'PE +M0I7;BKO6N/!@\?TRF[9M4&01^#'>6@VK=AWCTNO;=8F*U-^<9A[8ZZ5?L(\; +M[:9QT^:P5:X-;6,F-YJ]Z=((0C(M2*,F4V;_%`+E?N,I56]7`DA%S5MR];Y5 +M^S84E?MKPXP!5.5A:M2GE8L@+P>2JAJV6XUOSKT=)Z&MO(U0D,V9IGD@>E,- +MLCYZB1RS#W +M]4+!XN]$&NC+8:TCKMD@H/P6YS@S!7R)/Z-7B?G,\C\])9E).3;]:.*R[KBF +M$T??^@]CQ']B,_U(`<0BM>S/UY)+UMQP[>VN!\83`P^?G>2,304F)Y600:,7 +M@ZNG+3$$*`.83-NP[%5<&.D,/R"`7&4GL[\XE)"+:>V%J+-EW*>UQJ6FY!P@ +MF>[N"QQ$XDUD*-K&*Y&KDG516QKKF/_I\]^E7+T;6U#\H%JFO\@5I-[Y$IU_ +M&[<.2@R6K`"*^PL?5^SUZ*8I'+`PJ@J06!WW#"C26D.6[K_M0P@QN#X8?V== +M@GI$8)NDQ2U?C4FQ%B>K](U$;>`#E8VV='OG,]TK"ZGBU$A'X86A+T;IMYE` +M7:U[V\*0ZD@$N>Q^G]IIVV)(W1UHUQ%D%*\`E,?+XRR9%ECFXS5_7<\T%_\Y +MG5PO[#'OVH*\:ZF?2VB@TLN\(X[]W7!F@#=4^=>!HY4%$P=`V*QV-!N-#@^+ +MMVOUM,5K`8#13E7*R$Q)ZLL/`K;WH[U6PH6E]PI.OQQP;5Z4NCSL+,4+Y3KY?ZJOU_NF +M5.?8;CYK6:7-CV(=8RXL:86)LACKZ@/S5N8!*4/(Y03K:,8\/D'V*(S<95WM0 +MQOYX_HTQL1N5*+`Q6>'(PRD=2P^`E`P)J;8,SP%0!W#H#BQC!F`04ZG:U;L% +M*\C)(;!PVF/(+Z-]J4ZW#2^^48>.BR+AB-7"LORBGOWY5`H[BA$N)W3Y_R'( +M=**C^G91I%XJT=,_GA<>JD+3:(#LFGJB*LW\5&CLB:>.#F8E%#6%)]:1X]0] +M!Q>C[GS26W)#J-E?W@Y,W:76#:M3O+FEV4\)OVL1C2ZI4[>$`'*__O;+ADT* +MSS>RI1G]1Q7`VTP(4UE!K'#)BSO^6KM4EP(#0^9AL)H#"T2>C/^UG:@V,$8(P(TD;8UDH\I6X%##=P$YP>=1F?/_FCF5MR3$09%F7KE_S_._VR@RMIN6PY'OQ;_S-F\;%Z!1*FS706[ +M`K^VYM[A&%VSP?$YECDV4C7C"1/PW7^$QE08@5=2IXY\)\/DBOI7X%-*O*PF +M\VIEOS.DAF5'PZ@`:/VEH#(C4IS;`./"23BL0!(IH`'V^@X_9-359J@[T]DC +MH!8.(H=OT(K'&.@&0OM7`;NO(_SB/[WUFT`?:*1`Z:?'[(O\QU*CYG&.^IIE +MO3AT`LXBX.GRVW.+-GC$&T-)&J/TYB%<7:8$V,DBJ0ORC2$`D2_2#[9GB.99 +M1P)9L:"XMY09G0SWX)!"4=BHQMQJ+1O!H.:Z1$1VU?5O).[A#""/!6H4OV3T +M%-EZZXN>YX!S\MJ]2+:3<;^ZL"@T-'%''*\9T.I!S28U50QV_?"JK[-B$E;O(ZV8[]K7E#CAW +MT'@3)`[]"JF2#DPYOD9B8MTK)0G),(W>J;@-IM)X*C.1;:_S>L+.8?M;?J78 +M[-L1R]6R2+%^2@KD^7W'ZA\5UB05TXU@B3CBJ( +M7!\/AQ5H\"_\SA +M=:F;M`=D%61]2_7LS#":2[P7TS^$H&GU$*R:!=#4ZPB1CA^<5BR#4PNF1%!5 +M'Z;/4>,#S:M_C@;_YT.(0W6)LD0O$&.Y1-;G;T\F5"G1@00RT$=Q2=`X[9Y^ +M820^U%%$.#V32Q:..ZE4O;;B!/5)UDZI6]P*>N]@^4]+A?JV3& +MIWD_]]F0RO)'#!#]:(<7^A:'S$?@5A%FN;@:-(I\P/%%5$;2]=[LX/-G +MCV^6$A\Y=NI'-?'3$*=#FO1G,P6#%Q^>.W@RR-J\O#MS`YIHR/?H:4L*Q65T +M5D%MZBZ]X1`\)@_P#Q)N[PW0KDL1OY?#&$([VH2-^&E)&4H;KNS)7$!(OI"- +M>O84#:X+5*N`1V&G;ZQSMCAZ/RM*W$,'(=7^)772Q.\%Y^*L]Z/8&Q;PX#$!$?ZQ?PR&S?06__\,PN2SWN6PB<',Z:`2?+W5NIZ +MQP1?:,X-*G`\OL';&5F`W80F)['CE68*4/!?&M>Q_R#$Q;@`Q@F5(I[U[F/8 +MCOQ0BL\ZA6GS"(1>NNT7UW[=_>9;7AG*PCEPPQEG^3!\L_BGG$>(P!]1=3U +MSVU32.):[0H>JF(9*O[N +M7&AHVJBS+_&-5L$(`LTXLA>O"]0KI-^&2EL>6`T68&4T4AV?J'CYW/4JT7.9 +M.]2-G]B`'$Z\$3P'1!].A&N'IZW.+$-/U@QTY0DOB0"/L0"BO_7;OC9A1#M3 +MDKY_.!]1@3UR451\>I/;:`.VS3SJ_MO_/E3`@#\&]@Y4][!$,U$05+X.O4U< +M%Z5*<`_P\X1L?(("JO0$4+7GCN/AC5<9]03!N8NSHL=P`_H>=M-A>$-:*G]- +MVZ>[H'XZ*?@%RJ8@E?SWB^`3Q3W[5__+&^[9`2E.A0WT,QG2>)P$BOJT=@0* +M>."DT!.:NO#(K.M&!)"@[;<'(._$-_+HDP67X)2/Z@UMT`^&8/(YGP%A%XL'FS85[J*&VTE7SDZ+@]K<# +M2Q]BR^!:$0&$?Z71(7DJ,*];&?AJ,8>3!`!J'4O*I=CQA:$X1-_@(=NAG>4N +M`#LXE:52OI"G\%QQ[MUPDA3A5"".^SSO/RAV[IF3*EC2`$*^)I?I?:Q$X'PJ +M$[O&[H1SET$1SXX\SSK&LAYG4QWR51.W["K]U5Q+C!C2(3/]/K2$*[*UU%\M7;$%2K<+--6SY8 +M!=SBR^I6#[I2UTLHL]J*Y,UD8UL!AMC +M!SG(/X6[*S3G_DP3'. +MR2%0%_L@-\X(DBY',.J?&`)$+Y"EH%HR_5Q6-!;S)O"4WEJK:_O>*XL9?1ZO +M1LSTLU=*2\`I_R0L;]GSS4X75G>R%$?VWSE8O,U>P-VV#!H7-.4P]1C.NZ>G +MXP`,X5.N.%?[DGZ2XC$*1_V#]G(_RCD#M^-8%?(D83N#1SY-3)(EFYT^H<&H +MCX60=X$##3LE/JGBC/:$DQ@ZY!+:5GYP:9\-H)9&E%P(<7ORV2P*\^NJFD6@ +M\YX:SQ_K_?E2=;*.EQ]Z.4HKBRP<+*N3>-ZR#UW,R?>/C;A'K^/WA=(X`XCDYXD(I`H%!NUJ5A/J^8Q[,PH3(F*9!498.OS4%&Q[)(!=%B(<4;MT0.+> +M_9OZ;PI0/7G@A%0>MN)310Q1^^-KXW!@913&8Z!;=.:6'0GMLNJ0^@DYO/Q3 +MQLQ]]9RMX>2?<>)66*R$?+F$S_C#69"K +M9\P):$TBYMG*S2@AR2;12;=1YGI\<+5(>F#R9'VE[_NA\#RQL9Y"K%,AL$+3 +M4Y:I9P/G%%M;X+0K#Y>T]K5FN=\,2?RQ^$Q77](@_'6HOQ3HY%CVIUD_OR(IO1>>(9_UJ4DS +ML!D=]V'^*+UYF\S&``SA(AO589;-MK#V>AC)I=/#=B49L!+,MAJ(#KV;>82# +MS>=@@.8%`^C.[R$H2`F1- +M@5'@3BW:772[UL#!!3DRS^&#&$9-ADFJ@R%S*?T&#%HB"HMQ9(%RRH]&QCTT +M3'#:?!B07`!&`CR`T?-ZT-JOK07K,;!D:L*L47U5[-='$_"\]12X(]Q>0Z0< +MR!0]$;@AW;DC'-S)FN9,@BU!RG26IWA;(4UK3$,0R):9:)[H!'P^P0;,&.`4 +MJ'\M11\ET?&^&P_1BU":9)5*1$U1!@UP^[&<[V'_(#F@V&KCC6K6"$P553`?YZQA.U,'_!NT:K3KZHKN/!+MT2>HRW +MQ@X%1H@_[.:Q[!S?0=GU+>RQU]Z;VYP87DO)3R.&(F`K"6]*JC3QB*;=Z]6B +M)M'9N%`*&@D6+AZ-B%KC2FR(D6,(X+,=94.(6T7R-Y\3;`3+G5@[A8BS'N>Z +M(\+6:IAF'=6(/ +MB&5`P>P/-."-*9K820*=E>S#.6EON]"QL,K@:O4K(V=U9V*MF?U:2=OVU!CG +M-@]1H95\MRNM=X/#2TBEP\7'*D7IRCFMJ3TN\;UES!;;"8?=Y'BA>.A?6$ZW +M3'9<,@0,3-B(K'*F7E)/QUUX"#`]^\"H,OD*C'0@;AYZ=!K;_+"4@$MT_7$/ +M(]UGE5&+@CWVE11WZ)6UETE,BJME(D`T#SAT_7&(B/9D)W>N60ERCMI^Y>'H +MP/S\Y*C6+3"&90U<5:FW:7YPWB/WTL+\`/=38B>"V(DFG#:V'NFK`WM +M03Z]\.K8SXZS+W!'W7P@@@=AS^DH[]Y65R6I-C7,%B;-$BB(Z\0(9V;,W%C4L-US:PMM[C[/;\+QC.+&PT1H@ +M6AQGSX2!KPB5,/21QJ"9;SP2$*OM5)&L.D2)!;O6J?L]K``#(F^F\1]7XLX= +M66`SU>W0>IY:WK.UY;)\UFMV3L%G/3:S;1J&4L7I#2)^":\8S`-U+OG)6*@A6DM,XBJ>1F0HT=E@#K5CT`,C,&&O]X"97"W4Z;B_0R;I-X$VFI_ +M\%\!8F],IUUJR/4*XD*CE#E:1#$V!I! +MGZ*6-K=W:X;?]7+(3BC8'CR<-CK2_CS:N&2$OW#7++#94=YH>78TYTI+&N*&9&(!E;(@,4WA>5Y^ +M^T +MKMRON#')N70FKU(AD%EOY,,*ENSMLD)3=**-F@@8ZW&7*?O^+]<*;N>Q_,'^ +MTI4"YJZU':R5E$A_R+G-IO]4^U41ARZM;8][Z$OA<-!S[A`5`IL<&1'M4^*# +M2-X&L$P5=D[N+M9L4^!3S2-`%@"V;-[(&)S/1Z1.5)`(R*6S[#UE_ISL@]6' +M'(DO7:ETF;UD^7+,;+?%14BJ5DA,(37%:H=3^M4B&P'O^J/Q]'6Z4^>7 +M:4N'[&BNL?*1R#,X2P$BSI3$G*=(9='']16_)+@B)F%7L#GOU_0O-D)A_N4L +M#HXX?+M>DJM\^V=?ZL;4^9J@-6<_0S'.AU&ZT6RE/Z%Q>3?<%[R_0JAW[<=F +MSX-U&B$`FKU@I7O<$-QW)SSXQQ4G4[`?$\3R"7'\#]+'D:H^\U-%@MLL +M*O,R3,%OS>P&'=_R;'$&B^N^9\P;+67ZF;JW!XQ4;(9SO[E?-`$J+%^%G1%P +MWJ+()C0K4U&=P(D#^-16""L81BR;?0MSFXM;O@ET.0'@&S+/J(#U$/=9FN!& +M,`;UDWDO-`/"NRLAA[RD&LO*U$2'D>`O65#+7$!#XQD.2^N&%7O +M(Y?2*XX:#-B*1D>U_USYJLJ*;98ORW0AK]':_E+5T=NDIKO3MP0!A)0DC3YR +M-Z).T@.E`U]L+U76`C894>4N"]@%4WU:0AS\G#RW!MX\?T%\S$K,DIHTP8@: +MK`*X2;`-7E`B?7.@W+Y%AV&KPCR9L%9U!,"0>GRBA.X@U3 +MGI[=A\?DF/&E@CB3!3!=\0ARGD_MPR&&*@DI;E.S"L7M+]$>O/,B\\6E*A;C +M*Q3SF@,/IEV;SU9="P)31]OM;"7LB+2*:K-SV>K$WC+\IE`8I]`81U\R2ML.T`L*%,Y]JI.SL. +MYOTZ_S!)%60%EQL%V3GT$MT[5PA6T0M(7>6T`)5K1:O-`EJO)@)<4CG[J;0` +M2E.+1%,/[8=M\I8-+^S<:H84WJTQ.F^R';X[=^:]'_QD[,W@NC%R=#XPG_'- +MR#E*7J6O!""049K6@5[`6_MHF$R"-K:,K>;CK@3KT&XE5;QU62HL>*[\?/24 +M#1@5:+8EB,#1F-L):1JFY.Z;8&\-D%$F5>DZ&8[!"Q<[+CQH*U(UUX-5G$MK7N:63Y!32\E +MM.;C^-:5%Z8WV$?E?O2PBNSR_N[5%;IW?AL+'M3M`X7@_?`_R6G*__"0:@J: +MB/VK?K"J4I912,:+!K(6:ZE`K$$)`>8>Z<<9VGCQN\3*QIH_KU)P$7,VIH=L +MCJ55-N5YK.DW($-*L^CGL=-8DU"E$^^E"N[=S9%*&%]I1[)6K3]C\')R"6;B +M6W%_1-,+.]\D"\[H#%F#Y$0T"$Q\YM=1"<7=Q7E=7++JM>_'IF%?CU[?P=33 +M.#_?U.:632_T[CK:@6\;/D?!UW]V/D_Q,]A6.3ZC$-\@R!&=/MX:1(="+K[T +MU0H^V37W5XXR;4JKT@#"76N@P0,]\;$L)BX3*3QBKXOP$7J\6*[$!>F=+9<)Y+PN)C=49&/&H$8E7/V/_J6O +M4?@)!DZ&*$,23=I]VY08XC)Y@DH;"BFX;XT4G +M@HYSYJ'?E3@`-T*%BL5A+3:Y#A/>=%M@&^1!;;HT:Z"XJ&C'#*D^X7!=LV%4 +MVV=D?B//>3JDTGTN0+FM#&L4'TE1U743(RFT/&7=,#$?5?5"*6J/7]25?4>U +M[;8&/I[Z;M"*%U"R[;3HKIPBB[]G@CYT,F<$X85.FC\J,DOT.\/_?>8(%HJC +MW/CTA_^.9EP7KNW)2<3#'R'.Q<\QX*@JI>LD;/_.-X?55P2>WO_TP0C3P@AY +MT?AEW'5,8SH+/[@F(X`I>:VUHH-@;,T?.LS3W%M*>J-B>WAY[]!"2O,.3QK_ +M88V_-)0N=H6,LCQ#]FJ!<&M<6(4B6J?:NW'(K@/#^O9(I7D\L5@QQM8VM8I5 +M==&#HFA*,*=".PHO$L`@F)W4C5.+-J$@)L3UU3/ZO2^S$+0KTX:-2;PG[3Y1 +M(;6462](-`H;2%K^>"1``(JAO6EKN>5*W5:B_(>)=60]SG]L#@\H@V3@W +MJ+%]*S4:/[&N6&=DX:<%;;4 +MO7K_UWIF-9,X?NA@_E+G&3[N6B:)&^@BN% +M#MT#UF=3Q#<5?UYVZ:A$O%#+(D[?W*>`8TL,>`,G6?OUV#R*V83#FE8SWU&! +MI,=73_9_6^OD&5Y/D"H<6;Z7Q9[(#Q&Y2@\YH8#Y'Z+K:B+,4TO1^(JM+QM( +M'M4X2O=/5Z!$[<;SIYT:CSTK7[<\_9T'M/:HLHP`#$ILHW9FML'GJG8% +MR!A.O<5$Q%G9!K\1U7&ZA?0&GPG7S5YX.0PD;^(=V/<&Z6ZJAWR5A1DUV@T' +MQQI6-/F,G1!0",MJ@A4@;I8D=O\,>@#\B>'8X&T0$ +M)8;3+0K]=>G@K4:()T38G$_(A16)CG2U9'0H_O(1O1EIU-<$E:S22MY1`MH& +M%FLMWE0G$B0[QBLL$T_C3""DWAIW`V"C30&K2Q8=-;/FN5X;"7?;-]\7KCX0 +M[0IW0%!T(E#UTN%4;1$Q$G4=XKOH:C+%7$0#*TO9I*'EC0SOZ_3VR[N8S#F_ +M"J\A!6]BG>"TP`^7,JKA6K>D:[5:1(OZN5"EAML%U$V?$)D1U$:-SRFT\Q0* +M$G[\;A6'L9Z\76EC'T@W,,CEEE##.@!NOE%Q^-Y00=37MC!=(J;"M(FU#5,% +M-5'1=DN.?90=HS_6.G!*^LB>+::+K%LCO'S-U?Q> +MYB7)6((&BMCGGW)ZJY].)LYJ1B +M5%@9Q(`V>I0<-MJPI,W-8_<"/J\"E^M6Y[=F4CE]CC@U:7% +M+S*,@?'XP7C?=C]S77H47<`;$-+AB2'?5SII!&#'!0-3:`)#9.+`\ZAH>VY; +M.X'N!\*M*9G5FQU3S]:P@]-`OME.K`N,,2^.Y;TTGVK]#\%X1+F`Z^*.G#0[ +M'7+5BR:G4LJ#"&Y&T`W6:82SA[C0.)`W*:N'Y*@9D`.?G8/`%Q9#31VG7SCG +M@+7L&4+C<@?'@MRBJGK7_TH;@F,9)G1D`XC)P'J9@N7H??+GVV:M3E`G"?V@ +M>%71:O5*X;'7IIMSJKH\N$^.18QUA$H5<8WZZ:,NZM6(^:#F*15 +MI[J5%NIM>$,DF7E!B,MX0C:E-F^M$GLB(RP#[]Y.R^9Y>1US#=4@]*&Y3%F>:]1C(?]'#4O")9CM2?%> +M02'.)_HJR\OMKZH#5^S0[.185IWTBPD6KV3W-O"=5_O)4LE=9RW98[@RTRME +MDR5Y7C-A%;R1LE.%#YU16M!O\4TY!S#N<@_*$I(1&6,XGIDUU>ZW%LU,F;<` +MY2TY:,1(!2;]WV=*NQ81O//B'!H<=]H +M1];IJ13/Z_LD<3IT@4V;^/0R5R`L`>!-Q`[8E*@U$#T2H97`M\N]^Q705Y)3 +MYH=[=4C`@:C-,E@Q+8.XV9OS`^,H&DKQOTMW8LB61D3)Y\&7;ME9\>5#/_?: +MX!%TA6I4ZU5\=2>(&H\;L0CY*)`>A-]84HU]\42D+8(I=)GPV-&55@%U +M\8-(#$HK6[;,I:!ZG'L"TFL*(VA] +M,B]Y6LR2RSQ;GQ"2H.NPZ8[YL6Z>FVC9T:;^/0I\:#:]>VS&Z3^$9[ZHXVAZ +M:AYS+W*/)!<^L"H!,<:C")1:VME$JBGG]L59CZEW[C$9'MSRQ@BD'BCL.*,7 +M=+*8$K/W^#':];GD-[YJ;;0UL\D+W+Z+-C0$[=M6S.Q7/.\KR\[60([1L(0* +MPQS\\7Y94(?#O)X;S)S44M\#69LR"LQ&*[HGATFI4V8%X6H#8<(2&\6$E5"! +MQOE]LV[ML&GJE2R@):=\U@*CY^9F"Z[:6]SGQ&5.T"+*DR5JP5\W[0[#4>9" +M75+,2TSZ>R:XE-B1QD4Z4A#L)XM34\;DDCWY`"`;Q0@_"^;=LL(F0[YE0H+']L=/&HQ7XXQDTZY?"D +MDV`6-0J7#878D'C*^``8"B(E,+(:FX.EU.]C&H<(M44S9TS>:0@OP,FFY_U" +MCNGE3M2^,2_L]^LR@S*[=1$A5FXJC5>(G@4B=!"Q3[:5$>!<[0(1&XUNPHM0 +M61.DG.#(-0MZ['KBN@G)%XL#L`S_O<,A-?[(>2A/$YAFRL.S\TDF';WP2J$8 +M-PXFZ_@3ZU-:WQQ[TD*0847@^')SZ.8E63.PL]@("!,7A0K:CG5?S$OI:_F' +MX5O$_:@5:B\3=G6ZG&AES$6PK`.MP,STG/J!_O9CEBB#& +MO/R0PA64E+Y+]OK`!'$OXMOF+B$&*XVLP2/CW*>D<.1W\U?FTWRA(C^,`@MF +M1/_B'VN.-%WLVJU[EA"E;1(6H@3*)"`[36&@[?IB-&?I5ZB.'D!6V_ODT?WKNJYY(%O:GDE&>+I<`> +MZY6$-VJ4"A6>-A87.:TSL1R;U7V+*>Y#B6$S0RNZK0]3G<2D1Q8D92 +M3C.IJTG.8"1_+Y!%\$J@!G3;^P6SJ4A*OHZI"5AOA_QG&LVB0VH;4EC*TU_E +MV;@1,E_*#!MTD^7[B8;P*"^+K#G?!UDZDI49QL+\,")7-ET$-/`,6C8+!^I2 +M:)DC8\+!W]/64DJ!3W.NG:4>>U:B>](T^4@NU4N(]MP)3%&K,6D=R<)&P[`A +M^]VV!F6\T^P@5:@NNJF%]ICXND]T`*POO7<:S85Z>7*_I_+(N;VN:$Y."U*7 +MQ%MIG!2_U0<:9,G!YG-DFSTT`&0J!\7!!:20-*-0?F$P=L3)'7IO\)SFM3X=% +M?!__R]ETALQ@!*Q0([\V2>(I='370+W;>YW$KV[Z^DGC*`H@S5\G"V0U_Z"@ +M-Z-5:-MUIHM)]]3U3@#Q,Q+_CYZ]T\@"INZ;KX?9JE\WA:0_DZ%MWB*N=OQ! +M,A)*YIC&2XQ(/]>NE^<%>NY#)BA].AF]S:[:L9W'_1N$$^[2EPGQJ3G`" +MX^R52LXRM!!"9#E2+AM(*D=N,>!"A=.Q57^Z&P`6H'0]R&-B410DS8`BV.:W +MF])S>NO&WZ9]J)69UT6+XZ6_3%+_ORUKJ#C80_`F-(G:(MN*(:H#H/$;3:R& +MEAG/M[JPYFQ([TA9R.3'.1?PSKQKERD<*%24DBJ<53[E5U@D!8"Y`KRY:I`> +MANI%B_>5/]C\D6K,;RYX)IF51K*"57#8CS#J5I,6D!-IZ87S<(':=1/%\EH_ +MW"T(B#.6_PX)C@(D?+#Z&A#OOF"!%Q=BPN`B>S]JI0I/@S +MYSUXPN/A+Z.EVEKRE!G+E["AW`%V4]8I/J6Y-$J@5AM+SOY`^KKHY9-;#!(P +M7K0HM5"^!T13A%X-1LJ%+\G1FLP;#Z9+*/P$F51YI=?]@O'#\&0ZC,]=HX=+ +MN+L@3=,UEX]S%$"*KI3324[R7<7?1X<66:2)NE=_>T\OOJ$G3L2-U*TN]`=I +M!`9#\7G@=S"U$JD4W#C]V^A!^^$AF]I_''@S\52C(006+;YEKX42BSKPU<7\E,];'CPJ!*7&?, +M)J%K%WWLI!2M6,RU1#VAGJ]J#:^G<`&@/2*D0,%[Q2 +M@X%?YA:^$&P(CXNA9Q-8BGR&BEY@2)D5= +M/5'8EJF/N7I3E8!>!Q%*5T(S.1$NMD7L5=]=\N4I)V)'W5^+#SI#;NGX-8?` +MW23W>P#!38U$:\Y4K<38[`WU_?!NSQL)OP80;:E6WJ'$&ZTPB%64L,1^`4AK +M*!B66B].*R:X3J)?YPU'-',Z]TC%)27C6.E:[==+^5GE#\B,C'?93H?HSY_O +MY4N47LKTQ?6A-47HE>4OD%450&B`+6+$`]<[.A9#F>?),P3@/8M%S7$WH&\4 +M;3\;FT/>!!E(J__9P?+7S[WL#$.=1A.931E8*:"#[%5=2Q'X=W*O9JD0)TF3 +MS.(;OX&SA;LYB!!;<>/V%%Y[?71\,<'P-A,0(FY.E+/PTE@EJ_MT:A/YO\<@ +M^]]A'!M7QAG4W_+,48ZS:@:XL%!H-&&!@]X7*N9BU9^1ZM@4%PA=0+801N,( +MRNC+A2.-Z$K,2NN80#26H("4[7-,DY-/WG[),^G9)3R?L`U8,4 +MV%PF!&\*U5AD9/X(.(LSZFJ5,1';1?,70G6L'E(**;*FZZ,Q,^,GR>C3:)AI +MSAENP$.^3F\^F8MT)B(+"W+%U>DZAF=#)<(-#CR3SV>J&-4F"YW<)0HDQSR! +M^"J(T\]M(G-T`U!;U6J>P1;YX`F6K^KL,HD&48,_+5*X/W,M_12A1:OCP&!, +M>US&I7(V"?9$/D0AI<4C#D#G&9N39K6<%-9BN!?MNF7E +MR^05`,]J_^Y#V.R)TW-%&Q>A^'UQF0S]'U(=VMGY?*F_VB]AR*!5*5-UL`>5 +M`(\1%UUI!M8(D%1C8[KC_WXX:#^,TRN"W(A'1]UAUG:./MC?4]BN-7J'5'%*S"&\Q]#O/]Q(R +M;&6+T!1K=_N`.18AF-[<.9F(8'^[>@;(:@KJ%">P7_OIC)(QH%'0QPA&8HGB(:/H,6[?5:,+C^C5$JQ`;BF\)J9A^VW%/W5YG5R.]H5_/"+`,44 +M$56V>'PE0I7;BKO6N/!@\?TRF[9M4&01^#'>6@VK=AWCTNO;=8F*U-^<9A[8 +MZZ5?L(\;[:9QT^:P5:X-;6,F-YJ]Z=((0C(M2*,F4V;_%`+E?N,I56]7`DA% +MS5MR];Y5^S84E?MKPXP!5.5A:M2GE8L@+P>2JAJV6XUOSKT=)Z&MO(U0D,V9 +MIGD@>E,-LC +MYZB1RS#W]4+!XN]$&NC+8:TCKMD@H/P6YS@S!7R)/Z-7B?G,\C\])9E).3;] +M:.*R[KBF$T??^@]CQ']B,_U(`<0BM>S/UY)+UMQP[>VN!\83`P^?G>2,304F +M)Y600:,7@ZNG+3$$*`.83-NP[%5<&.D,/R"`7&4GL[\XE)"+:>V%J+-EW*>U +MQJ6FY!P@F>[N"QQ$XDUD*-K&*Y&KDG516QKKF/_I\]^E7+T;6U#\H%JFO\@5 +MI-[Y$IU_&[<.2@R6K`"*^PL?5^SUZ*8I'+`PJ@J06!WW#"C26D.6[K_M0P@Q +MN#X8?V==@GI$8)NDQ2U?C4FQ%B>K](U$;>`#E8VV='OG,]TK"ZGBU$A'X86A +M+T;IMYE`7:U[V\*0ZD@$N>Q^G]IIVV)(W1UHUQ%D%*\`E,?+XRR9%ECFXS5_ +M7<\T%_\YG5PO[#'OVH*\:ZF?2VB@TLN\(X[]W7!F@#=4^=>!HY4%$P=`V*QV +M-!N-#@^+MVOUM,5K`8#13E7*R$Q)ZLL/`K;WH[U6PH6E]PI.OQQP;5Z4NCSL+,4+Y3KY +M?ZJOU_NF5.?8;CYK6:7-CV(=8RXL:86)LACKZ@/S5N8!*4/(Y03K:,8\/D'V* +M(S<95WM0QOYX_HTQL1N5*+`Q6>'(PRD=2P^`E`P)J;8,SP%0!W#H#BQC!F`0 +M4ZG:U;L%*\C)(;!PVF/(+Z-]J4ZW#2^^48>.BR+AB-7"LORBGOWY5`H[BA$N +M)W3Y_R'(=**C^G91I%XJT=,_GA<>JD+3:(#LFGJB*LW\5&CLB:>.#F8E%#6% +M)]:1X]0]!Q>C[GS26W)#J-E?W@Y,W:76#:M3O+FEV4\)OVL1C2ZI4[>$`'*_ +M_O;+ADT*SS>RI1G]1Q7`VTP(4UE!K'#)BSO^6KM4EP(#0^9AL)H#"T2>C/^U +MG:@V,$8(P(TD;8UDH\I6X%##=P$YP>=1F?/_FCF5MR3$09%F +M7KE_S_._VR@RMIN6PY'OQ;_S-F\;%Z!1*FS706[`K^VYM[A&%VSP?$YECDV4C7C"1/PW7^$QE08@5=2IXY\)\/DBOI7 +MX%-*O*PF\VIEOS.DAF5'PZ@`:/VEH#(C4IS;`./"23BL0!(IH`'V^@X_9-35 +M9J@[T]DCH!8.(H=OT(K'&.@&0OM7`;NO(_SB/[WUFT`?:*1`Z:?'[(O\QU*C +MYG&.^IIEO3AT`LXBX.GRVW.+-GC$&T-)&J/TYB%<7:8$V,DBJ0ORC2$`D2_2 +M#[9GB.991P)9L:"XMY09G0SWX)!"4=BHQMQJ+1O!H.:Z1$1VU?5O).[A#""/ +M!6H4OV3T%-EZZXN>YX!S\MJ]2+:3<;^ZL"@T-'%''*\9T.I!S28U50QV_?"J +MK[-B$E;O(ZV8[ +M]K7E#CAWT'@3)`[]"JF2#DPYOD9B8MTK)0G),(W>J;@-IM)X*C.1;:_S>L+. +M8?M;?J78[-L1R]6R2+%^2@KD^7W'ZA\5UB05TXU@B3CBJ(7!\/AQ5H\"_\SA=:F;M`=D%61]2_7LS#":2[P7TS^$H&GU$*R:!=#4ZPB1CA^<5BR# +M4PNF1%!5'Z;/4>,#S:M_C@;_YT.(0W6)LD0O$&.Y1-;G;T\F5"G1@00RT$=Q +M2=`X[9Y^820^U%%$.#V32Q:..ZE4O;;B!/5)UDZI6]P*>N]@^4 +M]+A?JV3&IWD_]]F0RO)'#!#]:(<7^A:'S$?@5A%FN;@:-(I\P/%%5$;2 +M]=[LX/-GCV^6$A\Y=NI'-?'3$*=#FO1G,P6#%Q^>.W@RR-J\O#MS`YIHR/?H +M:4L*Q65T5D%MZBZ]X1`\)@_P#Q)N[PW0KDL1OY?#&$([VH2-^&E)&4H;KNS) +M7$!(OI"->O84#:X+5*N`1V&G;ZQSMCAZ/RM*W$,'(=7^)772Q.\%Y^*L`@;QG5 +M&B5-,5-_QRM0L>#MM0W$-S'BJH_8IY"*#4L;0'F!$MLGK8$6R7(_0JIF4F188LW.NQMITP8HMUWF\XR7X"-6 +M&\D7'8\P-Y0ID%$5:_E*6FTAD,UZ*C8?&[QBA +MQ(`^4DD*>=?33B/"4^$0QU4+CRBV<#Q%I'ES]CDV4..(`?$2ZB3)Y9=*8YWP +MKD55I7'(RB6:N.K<<#K;4`K?8S5S74$=%X:A@F!?`%"G+4V*7>T.:+#D%NX- +MO"&K@R&0W=5=#NWE4]:?]-1"<@XD,`PD^@*?QWL.PCMHD':HB^7%580>+:B: +MO-PW#(>)-?"@^,U>D1Y=!9F0\6.0SH[GLR.<#T"+F'3+T``?$Z)4;/6X\AS`:@KC>Q'$IU70=EY/" +MI070FD?O5Z0^K4N`T=F%GBW`6JH+RR8H+@R/JYB?/IC01HJV+DQI**WD\"]) +MFE)I[P]9(OI08OF3`L#+:(H)F&6Q[Y+0GKAC:L!I6*NP(+ALQC@TQ[ALSYJ] +M1B`"6XJH3U%?E3+DA&F$]B0PH*]BYZ%_(8.P1]C1PODZCT1OR.:UM>7>W);S +M5IG800B9F[_=KY9L->M0JZ'C$?"&8X]Q+FU)*)I]F=ZH:2:[\.>OAP1IS +MS/%E3R69K`@/-\N#`"&QKEICZ;24_0BUA0CAEC&AHVWNM5/^CN&0 +MI;+M\"W1W@6-<,_&_OFGHG!&G+9(RA+9>OS697XLAU-((X>LQ^ZVT@F40:88 +MKTV4E(**WG*)U$-F%DT:#XT"I%>D3H<&/>)9Z8C%7 +MQ^#L9(.[[6#NB@B3J2('BE5;T*DM[/[UM+[O:MO6H_/>I=T]Q.)Y``#%>W!N +MA0,"RXLKB"M\7A$C!/;J+>F.U)D&3*>[=BTFB#4""R:)G=90L/;3[OZ\\5C%]"RAMQM_U,38(.3V- +MY[%HZO,JYWMJ="AK?7_"$8PI_US?N!%-+/2&VP13 +MKI2,&K,>[J2_X%LN:)1"&*"R(U[4$O._I5^`W +M0Y9^ZD6\Z7$!@B4T77'F +M$\[ZG7\FM@"?`-[S"MF4PY^:C$H!!`8``0F%9P`'"P$``2,#`0$%70```0`, +MBQP`"`H!Y1S\W0``!0$1#0!F`&D`;`!E`#$````4"@$`@/>FPNRIS`$5!@$` +&(("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 index 000000000..70b03bd3b --- /dev/null +++ b/libarchive/test/test_read_format_7zip_lzma1_2.7z.uu @@ -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=)!5AVU_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 index 000000000..4de527e4b --- /dev/null +++ b/libarchive/test/test_read_format_7zip_lzma1_lzma2.7z.uu @@ -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(*P-]#*^T-I.U$V\S<> +MW,KQ]&30$$K2G.A@ZB\TJ2%=E9LVPJES']:2`CL-F<22BZ>*M00K1&/TM`@;QG5&B5-,5-_QRM0L>#MM0W$-S'BJH_8IY"*#4L;0'F!$MLGK8$6R7(_0JIF4F188LW.NQMITP8HMUWF +M\XR7X"-6&\D7'8\P-Y0ID%$5:_E*6FTAD,UZ* +MC8?&[QBAQ(`^4DD*>=?33B/"4^$0QU4+CRBV<#Q%I'ES]CDV4..(`?$2ZB3) +MY9=*8YWPKD55I7'(RB6:N.K<<#K;4`K?8S5S74$=%X:A@F!?`%"G+4V*7>T. +M:+#D%NX-O"&K@R&0W=5=#NWE4]:?]-1"<@XD,`PD^@*?QWL.PCMHD':HB^7% +M580>+:B:O-PW#(>)-?"@^,U>D1Y=!9F0\6.0SH[GLR.<#T"+F'3+T``?$Z)4;/6X\AS`:@KC>Q'$I +MU70=EY/"I070FD?O5Z0^K4N`T=F%GBW`6JH+RR8H+@R/JYB?/IC01HJV+DQI +M**WD\"])FE)I[P]9(OI08OF3`L#+:(H)F&6Q[Y+0GKAC:L!I6*NP(+ALQC@T +MQ[ALSYJ]1B`"6XJH3U%?E3+DA&F$]B0PH*]BYZ%_(8.P1]C1PODZCT1OR.:U +MM>7>W);S5IG800B9F[_=KY9L->M0JZ'C$?"&8X]Q+FU)*)I]F=ZH:2:[\ +M.>OAP1ISS/%E3R69K`@/-\N#`"&QKEICZ;24_0BUA0CAEC&AHVWN +MM5/^CN&0I;+M\"W1W@6-<,_&_OFGHG!&G+9(RA+9>OS697XLAU-((X>LQ^ZV +MT@F40:88KTV4E(**WG*)U$-F%DT:#XT"I%>D3H<&/ +M>)9Z8C%7Q^#L9(.[[6#NB@B3J2('BE5;T*DM[/[UM+[O:MO6H_/>I=T]Q.)Y +M``#%>W!NA0,"RXLKB"M\7A$C!/;J+>F.U)D&3*>[=BTFB#4""R:)G=90L/;3 +M[OZ\\5C%]"RAMQM_U +M,38(.3V-Y[%HZO,JYWMJ="AK?7_"$8PI_US?N!%-+/2&VP13 +MKI2,&K,>[J2_X%LN:)1"&*"R(U[4$ +MO._I5^`W0Y9^ZD6\Z7$! +M@B4T77'F$\[ZG7\FM@"?`-[S"MF4PY^:C$H``00&``$)A6X`!PL!``$A(0$( +M#(L<``@*`>4<_-T```4!$0T`9@!I`&P`90`Q````%`H!`(#WIL+LJ