]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Add the ability to decode LZX, which is one of compression type of MS Cabinet file.
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Wed, 29 Dec 2010 14:38:03 +0000 (09:38 -0500)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Wed, 29 Dec 2010 14:38:03 +0000 (09:38 -0500)
SVN-Revision: 2838

Makefile.am
libarchive/archive_read_support_format_cab.c
libarchive/test/test_fuzz.c
libarchive/test/test_fuzz.cab.uu [new file with mode: 0644]
libarchive/test/test_read_format_cab.c
libarchive/test/test_read_format_cab_3.cab.uu [new file with mode: 0644]

index 87bb7a713df17d42671f965e3404978d3d8675f1..2ed7c1be73be7133ff00adc59e6614eb3797ea7e 100644 (file)
@@ -396,10 +396,12 @@ libarchive_test_EXTRA_DIST=\
        libarchive/test/test_compat_zip_1.zip.uu                        \
        libarchive/test/test_compat_zip_2.zip.uu                        \
        libarchive/test/test_fuzz_1.iso.Z.uu                            \
+       libarchive/test/test_fuzz.cab.uu                                \
        libarchive/test/test_pax_filename_encoding.tar.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                   \
+       libarchive/test/test_read_format_cab_3.cab.uu                   \
        libarchive/test/test_read_format_cpio_bin_be.cpio.uu            \
        libarchive/test/test_read_format_cpio_svr4_bzip2_rpm.rpm.uu     \
        libarchive/test/test_read_format_cpio_svr4_gzip_rpm.rpm.uu      \
index d5d68615599bad94a9104af8cefe473b6b3bb000..1e7fc7bca8279ab1453be33f70c4a5d232b0bd40 100644 (file)
 #include "archive_read_private.h"
 #include "archive_endian.h"
 
+
+struct lzx_dec {
+       /* Decoding status. */
+       int                      state;
+
+       /*
+        * Window to see last decoded data, from 32KBi to 2MBi.
+        */
+       int                      w_size;
+       int                      w_mask;
+       /* Window buffer, which is a loop buffer. */
+       unsigned char           *w_buff;
+       /* The insert position to the window. */
+       int                      w_pos;
+       /* The position where we can copy decoded code from the window. */
+       int                      copy_pos;
+       /* The length how many bytes we can copy decoded code from
+        * the window. */
+       int                      copy_len;
+       /* Translation reversal for x86 proccessor CALL byte sequence(E8).
+        * This is used for LZX only. */
+       uint32_t                 translation_size;
+       char                     translation;
+       char                     block_type;
+#define VERBATIM_BLOCK         1
+#define ALIGNED_OFFSET_BLOCK   2
+#define UNCOMPRESSED_BLOCK     3
+       size_t                   block_size;
+       size_t                   block_bytes_avail;
+       /* Repeated offset. */
+       uint32_t                 r0, r1, r2;
+       unsigned char            rbytes[4];
+       int                      rbytes_avail;
+       int                      length_header;
+       int                      position_slot;
+       int                      offset_bits;
+
+       struct pos_tbl {
+               int              base;
+               int              footer_bits;
+       }                       *pos_tbl;
+       /*
+        * Bit stream reader.
+        */
+       struct {
+#define CACHE_TYPE             uint64_t
+#define CACHE_BITS             (8 * sizeof(CACHE_TYPE))
+               /* Cache buffer. */
+               CACHE_TYPE       cache_buffer;
+               /* Indicates how many bits avail in cache_buffer. */
+               int              cache_avail;
+               unsigned char    odd;
+               char             have_odd;
+       } br;
+
+       /*
+        * Huffman coding.
+        */
+       struct huffman {
+               int              len_size;
+               int              freq[17];
+               unsigned char   *bitlen;
+
+               /*
+                * Use a index table. It's faster than traversing huffman
+                * coding tree, which is a binary tree.
+                * TODO: Reduce the memory used for the index table.
+                */
+               int              max_bits;
+               int              tbl_bits;
+               uint16_t        *tbl;
+       }                        at, lt, mt, pt;
+
+       int                      loop;
+       int                      error;
+};
+
+static int slots[] = {
+       30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290
+};
+#define SLOT_BASE      15
+#define SLOT_MAX       21/*->25*/
+
+struct lzx_stream {
+       const unsigned char     *next_in;
+       int64_t                  avail_in;
+       int64_t                  total_in;
+       unsigned char           *next_out;
+       int64_t                  avail_out;
+       int64_t                  total_out;
+       struct lzx_dec          *ds;
+};
+
+/*
+ * Cabinet file definitions.
+ */
 /* CFHEADER offset */
 #define CFHEADER_signature     0
 #define CFHEADER_cbCabinet     8
@@ -200,6 +296,7 @@ struct cab {
        z_stream                 stream;
        char                     stream_valid;
 #endif
+       struct lzx_stream        xstrm;
 };
 
 static int     archive_read_format_cab_bid(struct archive_read *);
@@ -224,8 +321,20 @@ static const void *cab_read_ahead_cfdata(struct archive_read *, ssize_t *);
 static const void *cab_read_ahead_cfdata_none(struct archive_read *, ssize_t *);
 static const void *cab_read_ahead_cfdata_deflate(struct archive_read *,
                    ssize_t *);
+static const void *cab_read_ahead_cfdata_lzx(struct archive_read *,
+                   ssize_t *);
 static int64_t cab_consume_cfdata(struct archive_read *, int64_t);
 static int64_t cab_minimum_consume_cfdata(struct archive_read *, int64_t);
+static int     lzx_decode_init(struct lzx_stream *, int);
+static void    lzx_decode_free(struct lzx_stream *);
+static void    lzx_translation(struct lzx_stream *, void *, size_t, uint32_t);
+static void    lzx_cleanup_bitstream(struct lzx_stream *);
+static int     lzx_decode(struct lzx_stream *, int);
+static int     lzx_read_pre_tree(struct lzx_stream *);
+static int     lzx_read_bitlen(struct lzx_stream *, struct huffman *, int);
+static int     lzx_huffman_init(struct huffman *, size_t, int);
+static void    lzx_huffman_free(struct huffman *);
+static int     lzx_make_huffman_table(struct huffman *);
 
 
 int
@@ -575,7 +684,7 @@ cab_read_header(struct archive_read *a)
                folder->comptype =
                    archive_le16dec(p+CFFOLDER_typeCompress) & 0x0F;
                folder->compdata =
-                   archive_le16dec(p+CFFOLDER_typeCompress) >> 4;
+                   archive_le16dec(p+CFFOLDER_typeCompress) >> 8;
                /* Get a compression name. */
                if (folder->comptype <
                    sizeof(compression_name) / sizeof(compression_name[0]))
@@ -1068,7 +1177,8 @@ cab_next_cfdata(struct archive_read *a)
                     cab->entry_cffolder->cfdata_count) &&
                       cfdata->uncompressed_size != 0x8000)
                        goto invalid;
-               /* A Compressed data size and an uncompressed data size must
+
+               /* A compressed data size and an uncompressed data size must
                 * be the same in no compression mode. */
                if (cab->entry_cffolder->comptype == COMPTYPE_NONE &&
                    cfdata->compressed_size != cfdata->uncompressed_size)
@@ -1131,6 +1241,8 @@ cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail)
                return (cab_read_ahead_cfdata_none(a, avail));
        case COMPTYPE_MSZIP:
                return (cab_read_ahead_cfdata_deflate(a, avail));
+       case COMPTYPE_LZX:
+               return (cab_read_ahead_cfdata_lzx(a, avail));
        default: /* Unsupported compression. */
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                    "Unsupported CAB compression : %s",
@@ -1214,7 +1326,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
                    = (unsigned char *)malloc(cab->uncompressed_buffer_size);
                if (cab->uncompressed_buffer == NULL) {
                        archive_set_error(&a->archive, ENOMEM,
-                           "No memory for CAB decompression");
+                           "No memory for CAB reader");
                        *avail = ARCHIVE_FATAL;
                        return (NULL);
                }
@@ -1338,7 +1450,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
 
        if (uavail < cfdata->uncompressed_size) {
                archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-                   "Invalid decompression size (%d < %d)",
+                   "Invalid uncompressed size (%d < %d)",
                    uavail, cfdata->uncompressed_size);
                *avail = ARCHIVE_FATAL;
                return (NULL);
@@ -1424,6 +1536,129 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
 
 #endif /* HAVE_ZLIB_H */
 
+static const void *
+cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
+{
+       struct cab *cab = (struct cab *)(a->format->data);
+       struct cfdata *cfdata;
+       const void *d;
+       int r;
+       uint16_t uavail;
+
+       cfdata = cab->entry_cfdata;
+       /* If the buffer hasn't been allocated, allocate it now. */
+       if (cab->uncompressed_buffer == NULL) {
+               cab->uncompressed_buffer_size = 0x8000;
+               cab->uncompressed_buffer
+                   = (unsigned char *)malloc(cab->uncompressed_buffer_size);
+               if (cab->uncompressed_buffer == NULL) {
+                       archive_set_error(&a->archive, ENOMEM,
+                           "No memory for CAB reader");
+                       *avail = ARCHIVE_FATAL;
+                       return (NULL);
+               }
+       }
+
+       uavail = cfdata->uncompressed_avail;
+       if (uavail == cfdata->uncompressed_size) {
+               d = cab->uncompressed_buffer + cfdata->read_offset;
+               *avail = uavail - cfdata->read_offset;
+               return (d);
+       }
+
+       if (!cab->entry_cffolder->decompress_init) {
+               r = lzx_decode_init(&cab->xstrm,
+                   cab->entry_cffolder->compdata);
+               if (r != ARCHIVE_OK) {
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                           "Can't initialize LZX decompression.");
+                       *avail = ARCHIVE_FATAL;
+                       return (NULL);
+               }
+               /* We've initialized decompression for this stream. */
+               cab->entry_cffolder->decompress_init = 1;
+       }
+
+       /* Clean up remaining bits of previous CFDATA. */
+       lzx_cleanup_bitstream(&cab->xstrm);
+       cab->xstrm.total_out = uavail;
+       while (cab->xstrm.total_out < cfdata->uncompressed_size) {
+               ssize_t bytes_avail;
+
+               cab->xstrm.next_out =
+                   cab->uncompressed_buffer + cab->xstrm.total_out;
+               cab->xstrm.avail_out =
+                   cfdata->uncompressed_size - cab->xstrm.total_out;
+
+               d = __archive_read_ahead(a, 1, &bytes_avail);
+               if (bytes_avail <= 0) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Truncated CAB file data");
+                       *avail = ARCHIVE_FATAL;
+                       return (NULL);
+               }
+               if (bytes_avail > cfdata->compressed_bytes_remaining)
+                       bytes_avail = cfdata->compressed_bytes_remaining;
+
+               cab->xstrm.next_in = d;
+               cab->xstrm.avail_in = bytes_avail;
+               cab->xstrm.total_in = 0;
+               r = lzx_decode(&cab->xstrm,
+                   cfdata->compressed_bytes_remaining == bytes_avail);
+               switch (r) {
+               case ARCHIVE_OK:
+               case ARCHIVE_EOF:
+                       break;
+               default:
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                           "LZX decompression failed (%d)", r);
+                       *avail = ARCHIVE_FATAL;
+                       return (NULL);
+               }
+               cfdata->unconsumed = cab->xstrm.total_in;
+               cfdata->sum_ptr = d;
+               if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+                       *avail = ARCHIVE_FATAL;
+                       return (NULL);
+               }
+       }
+
+       uavail = cab->xstrm.total_out;
+       /*
+        * Make sure a read pointer advances to next CFDATA.
+        */
+       if (cfdata->compressed_bytes_remaining > 0) {
+               ssize_t bytes_avail;
+
+               d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining,
+                   &bytes_avail);
+               if (bytes_avail <= 0) {
+                       *avail = truncated_error(a);
+                       return (NULL);
+               }
+               cfdata->unconsumed = cfdata->compressed_bytes_remaining;
+               cfdata->sum_ptr = d;
+               if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+                       *avail = ARCHIVE_FATAL;
+                       return (NULL);
+               }
+       }
+
+       /*
+        * Translation reversal of x86 proccessor CALL byte sequence(E8).
+        */
+       lzx_translation(&cab->xstrm, cab->uncompressed_buffer,
+           cfdata->uncompressed_size,
+           (cab->entry_cffolder->cfdata_index-1) * 0x8000);
+
+       d = cab->uncompressed_buffer + cfdata->read_offset;
+       *avail = uavail - cfdata->read_offset;
+       cfdata->uncompressed_avail = uavail;
+
+       return (d);
+}
+
 /*
  * Consume CFDATA.
  * We always decompress CFDATA to consume CFDATA as much as we need
@@ -1682,6 +1917,7 @@ archive_read_format_cab_cleanup(struct archive_read *a)
        if (cab->stream_valid)
                inflateEnd(&cab->stream);
 #endif
+       lzx_decode_free(&cab->xstrm);
        archive_wstring_free(&cab->ws);
        archive_string_free(&cab->mbs);
        free(cab->uncompressed_buffer);
@@ -1711,3 +1947,1001 @@ cab_dos_time(const unsigned char *p)
        return (mktime(&ts));
 }
 
+/*****************************************************************
+ *
+ * LZX decompression code.
+ *
+ *****************************************************************/
+
+/*
+ * Initialize LZX decoder.
+ *
+ * Returns ARCHIVE_OK if initialization was successful.
+ * Returns ARCHIVE_FAILED if w_bits has unsupported value.
+ * Returns ARCHIVE_FATAL if initialization failed; memory allocation
+ * error occurred.
+ */
+static int
+lzx_decode_init(struct lzx_stream *strm, int w_bits)
+{
+       struct lzx_dec *ds;
+       int slot, w_size, w_slot;
+       int base, footer;
+
+       if (strm->ds == NULL) {
+               strm->ds = calloc(1, sizeof(*strm->ds));
+               if (strm->ds == NULL)
+                       return (ARCHIVE_FATAL);
+       }
+       ds = strm->ds;
+       ds->error = ARCHIVE_FAILED;
+
+       /* Allow bits from 15(32KBi) up to 21(2MBi) */
+       if (w_bits < SLOT_BASE || w_bits > SLOT_MAX)
+               return (ARCHIVE_FAILED);
+
+       ds->error = ARCHIVE_FATAL;
+
+       /*
+        * Alloc window
+        */
+       w_size = ds->w_size;
+       w_slot = slots[w_bits - SLOT_BASE];
+       ds->w_size = 1U << w_bits;
+       ds->w_mask = ds->w_size -1;
+       if (ds->w_buff == NULL || w_size != ds->w_size) {
+               free(ds->w_buff);
+               ds->w_buff = malloc(ds->w_size);
+               if (ds->w_buff == NULL)
+                       return (ARCHIVE_FATAL);
+               free(ds->pos_tbl);
+               ds->pos_tbl = malloc(sizeof(ds->pos_tbl[0]) * w_slot);
+               if (ds->pos_tbl == NULL)
+                       return (ARCHIVE_FATAL);
+               lzx_huffman_free(&(strm->ds->mt));
+       }
+
+       base = footer = 0;
+       for (slot = 0; slot < w_slot; slot++) {
+               int n;
+               if (footer == 0)
+                       base = slot;
+               else
+                       base += 1 << footer;
+               if (footer < 17) {
+                       footer = -2;
+                       for (n = base; n; n >>= 1)
+                               footer++;
+                       if (footer <= 0)
+                               footer = 0;
+               }
+               ds->pos_tbl[slot].base = base;
+               ds->pos_tbl[slot].footer_bits = footer;
+       }
+
+       ds->w_pos = 0;
+       ds->state = 0;
+       ds->br.cache_buffer = 0;
+       ds->br.cache_avail = 0;
+       ds->r0 = ds->r1 = ds->r2 = 1;
+
+       /* Initialize aligned offset tree. */
+       if (lzx_huffman_init(&(ds->at), 8, 8) != ARCHIVE_OK)
+               return (ARCHIVE_FATAL);
+
+       /* Initialize pre-tree. */
+       if (lzx_huffman_init(&(ds->pt), 20, 10) != ARCHIVE_OK)
+               return (ARCHIVE_FATAL);
+
+       /* Initialize Main tree. */
+       if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16)
+           != ARCHIVE_OK)
+               return (ARCHIVE_FATAL);
+
+       /* Initialize Length tree. */
+       if (lzx_huffman_init(&(ds->lt), 249, 16) != ARCHIVE_OK)
+               return (ARCHIVE_FATAL);
+
+       ds->error = 0;
+
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Release LZX decoder.
+ */
+static void
+lzx_decode_free(struct lzx_stream *strm)
+{
+
+       if (strm->ds == NULL)
+               return;
+       free(strm->ds->w_buff);
+       lzx_huffman_free(&(strm->ds->at));
+       lzx_huffman_free(&(strm->ds->pt));
+       lzx_huffman_free(&(strm->ds->mt));
+       lzx_huffman_free(&(strm->ds->lt));
+       free(strm->ds);
+       strm->ds = NULL;
+}
+
+/*
+ * E8 Call Translation reversal.
+ */
+static void
+lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset)
+{
+       struct lzx_dec *ds = strm->ds;
+       unsigned char *b, *end;
+
+       if (!ds->translation || size <= 10)
+               return;
+       b = p;
+       end = b + size - 10;
+       while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) {
+               size_t i = b - (unsigned char *)p;
+               long cp, displacement, value;
+
+               cp = offset + i;
+               value = archive_le32dec(&b[1]);
+               if (value >= -cp && value < (long)ds->translation_size) {
+                       if (value >= 0)
+                               displacement = value - cp;
+                       else
+                               displacement = value + ds->translation_size;
+                       archive_le32enc(&b[1], (uint32_t)displacement);
+               }
+               b += 5;
+       }
+}
+
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define lzx_br_has(strm, n)    ((strm)->ds->br.cache_avail >= n)
+/* Get compressed data by bit. */
+#define lzx_br_bits(strm, n)                           \
+       (((uint16_t)((strm)->ds->br.cache_buffer >>     \
+               ((strm)->ds->br.cache_avail - (n)))) & cache_masks[n])
+#define lzx_br_bits_forced(strm, n)                    \
+       (((uint16_t)((strm)->ds->br.cache_buffer <<     \
+               ((n) - (strm)->ds->br.cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ *  True  : completed, there is enough data in the cache buffer.
+ *  False : we met that strm->next_in is empty, we have to get following
+ *          bytes. */
+#define lzx_br_read_ahead(strm, n)     \
+       (lzx_br_has((strm), (n)) || lzx_br_fillup(strm))
+/*  True  : the cache buffer has some bits as much as we need.
+ *  False : there are no enough bits in the cache buffer to be used,
+ *          we have to get following bytes if we could. */
+#define lzx_br_ensure(strm, n)         \
+       (lzx_br_read_ahead((strm), (n)) || lzx_br_has((strm), (n)))
+
+/* Notify how man bits we consumed. */
+#define lzx_br_consume(strm, n)        ((strm)->ds->br.cache_avail -= (n))
+#define lzx_br_consume_unalined_bits(strm)     \
+       ((strm)->ds->br.cache_avail &= ~0x0f)
+
+static uint16_t cache_masks[] = {
+       0x0000, 0x0001, 0x0003, 0x0007,
+       0x000F, 0x001F, 0x003F, 0x007F,
+       0x00FF, 0x01FF, 0x03FF, 0x07FF,
+       0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,
+       0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+lzx_br_fillup(struct lzx_stream *strm)
+{
+       struct lzx_dec *ds = strm->ds;
+       int n = CACHE_BITS - ds->br.cache_avail;
+
+       if (ds->br.have_odd && n >= 16 && strm->avail_in > 0) {
+               ds->br.cache_buffer =
+                  (ds->br.cache_buffer << 16) |
+                  ((uint16_t)(*strm->next_in)) << 8 |
+                   ds->br.odd;
+               strm->next_in++;
+               strm->avail_in--;
+               strm->total_in++;
+               ds->br.cache_avail += 16;
+               n -= 16;
+               ds->br.have_odd = 0;
+       }
+       while (n >= 16) {
+               if (strm->avail_in < 2) {
+                       /* There is not enough compressed data to fill up the
+                        * cache buffer. */
+                       if (strm->avail_in == 1) {
+                               ds->br.odd = *strm->next_in++;
+                               strm->avail_in--;
+                               strm->total_in++;
+                               ds->br.have_odd = 1;
+                       }
+                       return (0);
+               }
+               if (strm->avail_in >= 8) {
+                       int words;
+                       switch (words = (n >> 4)) {
+                       case 4:
+                               ds->br.cache_buffer =
+                                   (((uint64_t)archive_le16dec(
+                                       strm->next_in)) << 48) |
+                                   (((uint64_t)archive_le16dec(
+                                       strm->next_in+2)) << 32) |
+                                   (((uint32_t)archive_le16dec(
+                                       strm->next_in+4)) << 16) |
+                                   archive_le16dec(strm->next_in+6);
+                               break;
+                       case 3:
+                               ds->br.cache_buffer =
+                                  (ds->br.cache_buffer << 48) |
+                                   (((uint64_t)archive_le16dec(
+                                       strm->next_in)) << 32) |
+                                   (((uint32_t)archive_le16dec(
+                                       strm->next_in+2)) << 16) |
+                                   archive_le16dec(strm->next_in+4);
+                               break;
+                       default:
+                               words = 0;
+                               break;
+                       }
+                       if (words) {
+                               strm->next_in += words << 1;
+                               strm->avail_in -= words << 1;
+                               strm->total_in += words << 1;
+                               ds->br.cache_avail += words << 4;
+                               return (1);
+                       }
+               }
+               ds->br.cache_buffer =
+                  (ds->br.cache_buffer << 16) |
+                   archive_le16dec(strm->next_in);
+               strm->next_in += 2;
+               strm->avail_in -= 2;
+               strm->total_in += 2;
+               ds->br.cache_avail += 16;
+               n -= 16;
+       }
+       return (1);
+}
+
+static void
+lzx_cleanup_bitstream(struct lzx_stream *strm)
+{
+       strm->ds->br.cache_avail = 0;
+       strm->ds->br.have_odd = 0;
+}
+
+/*
+ * Decode LZX.
+ *
+ * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty.
+ *    Please set available buffer and call this function again.
+ * 2. Returns ARCHIVE_EOF if decompression has been completed.
+ * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
+ *    is broken or you do not set 'last' flag properly.
+ */
+static int
+lzx_decode(struct lzx_stream *strm, int last)
+{
+       struct lzx_dec *ds = strm->ds;
+       int c, i;
+#define ST_RD_TRANSLATION      0
+#define ST_RD_TRANSLATION_SIZE 1
+#define ST_RD_BLOCK_TYPE       2
+#define ST_RD_BLOCK_SIZE       3
+#define ST_RD_R0               4
+#define ST_RD_R1               5
+#define ST_RD_R2               6
+#define ST_COPY_UNCOMP1                7
+#define ST_COPY_UNCOMP2                8
+#define ST_RD_ALIGNED_OFFSET   9
+#define ST_RD_VERBATIM         10
+#define ST_RD_PRE_MAIN_TREE_256        11
+#define ST_MAIN_TREE_256       12
+#define ST_RD_PRE_MAIN_TREE_REM        13
+#define ST_MAIN_TREE_REM       14
+#define ST_RD_PRE_LENGTH_TREE  15
+#define ST_LENGTH_TREE         16
+#define ST_MAIN                        17
+#define ST_LENGTH              18
+#define ST_OFFSET              19
+#define ST_REAL_POS            20
+#define ST_COPY                        21
+
+       if (ds->error)
+               return (ds->error);
+
+       for (;;) {
+               switch (ds->state) {
+               case ST_RD_TRANSLATION:
+                       if (!lzx_br_read_ahead(strm, 1)) {
+                               ds->state = ST_RD_TRANSLATION;
+                               if (last)
+                                       goto failed;
+                               return (ARCHIVE_OK);
+                       }
+                       ds->translation = lzx_br_bits(strm, 1);
+                       lzx_br_consume(strm, 1);
+                       /* FALL THROUGH */
+               case ST_RD_TRANSLATION_SIZE:
+                       if (ds->translation) {
+                               if (!lzx_br_read_ahead(strm, 32)) {
+                                       ds->state = ST_RD_TRANSLATION_SIZE;
+                                       if (last)
+                                               goto failed;
+                                       return (ARCHIVE_OK);
+                               }
+                               ds->translation_size = lzx_br_bits(strm, 16);
+                               lzx_br_consume(strm, 16);
+                               ds->translation_size <<= 16;
+                               ds->translation_size |= lzx_br_bits(strm, 16);
+                               lzx_br_consume(strm, 16);
+                       }
+                       /* FALL THROUGH */
+               case ST_RD_BLOCK_TYPE:
+                       if (!lzx_br_read_ahead(strm, 3)) {
+                               ds->state = ST_RD_BLOCK_TYPE;
+                               if (last)
+                                       goto failed;
+                               return (ARCHIVE_OK);
+                       }
+                       ds->block_type = lzx_br_bits(strm, 3);
+                       lzx_br_consume(strm, 3);
+                       /* Check a block type. */
+                       switch (ds->block_type) {
+                       case VERBATIM_BLOCK:
+                       case ALIGNED_OFFSET_BLOCK:
+                       case UNCOMPRESSED_BLOCK:
+                               break;
+                       default:
+                               goto failed;/* Invalid */
+                       }
+                       /* FALL THROUGH */
+               case ST_RD_BLOCK_SIZE:
+                       if (!lzx_br_read_ahead(strm, 24)) {
+                               ds->state = ST_RD_BLOCK_SIZE;
+                               if (last)
+                                       goto failed;
+                               return (ARCHIVE_OK);
+                       }
+                       ds->block_size = lzx_br_bits(strm, 8);
+                       lzx_br_consume(strm, 8);
+                       ds->block_size <<= 16;
+                       ds->block_size |= lzx_br_bits(strm, 16);
+                       lzx_br_consume(strm, 16);
+                       if (ds->block_size == 0)
+                               goto failed;
+                       ds->block_bytes_avail = ds->block_size;
+                       if (ds->block_type != UNCOMPRESSED_BLOCK) {
+                               if (ds->block_type == VERBATIM_BLOCK)
+                                       ds->state = ST_RD_VERBATIM;
+                               else
+                                       ds->state = ST_RD_ALIGNED_OFFSET;
+                               break;
+                       }
+                       /*
+                        * Handle an Uncompressed Block.
+                        */
+                       /* Skip padding to align following field on
+                        * 16-bit boundary. */
+                       lzx_br_consume_unalined_bits(strm);
+                       /* Preparation to read repeated offsets R0,R1 and R2. */
+                       ds->rbytes_avail = 0;
+                       ds->state = ST_RD_R0;
+                       /* FALL THROUGH */
+               case ST_RD_R0:
+               case ST_RD_R1:
+               case ST_RD_R2:
+                       do {
+                               uint16_t u16;
+                               /* Drain bits in the cache buffer of
+                                * bit-stream. */
+                               if (lzx_br_has(strm, 32)) {
+                                       u16 = lzx_br_bits(strm, 16);
+                                       lzx_br_consume(strm, 16);
+                                       archive_le16enc(ds->rbytes, u16);
+                                       u16 = lzx_br_bits(strm, 16);
+                                       lzx_br_consume(strm, 16);
+                                       archive_le16enc(ds->rbytes+2, u16);
+                                       ds->rbytes_avail = 4;
+                               } else if (lzx_br_has(strm, 16)) {
+                                       u16 = lzx_br_bits(strm, 16);
+                                       lzx_br_consume(strm, 16);
+                                       archive_le16enc(ds->rbytes, u16);
+                                       ds->rbytes_avail = 2;
+                               } else
+                                       ds->rbytes_avail = 0;
+                               if (ds->rbytes_avail < 4 && ds->br.have_odd) {
+                                       ds->rbytes[ds->rbytes_avail++] =
+                                           ds->br.odd;
+                                       ds->br.have_odd = 0;
+                               }
+                               while (ds->rbytes_avail < 4) {
+                                       if (strm->avail_in <= 0) {
+                                               if (last)
+                                                       goto failed;
+                                               return (ARCHIVE_OK);
+                                       }
+                                       ds->rbytes[ds->rbytes_avail++] =
+                                           *strm->next_in++;
+                                       strm->avail_in--;
+                                       strm->total_in++;
+                               }
+                               if (ds->state == ST_RD_R0) {
+                                       ds->r0 = archive_le32dec(ds->rbytes);
+                                       ds->state = ST_RD_R1;
+                               } else if (ds->state == ST_RD_R1) {
+                                       ds->r1 = archive_le32dec(ds->rbytes);
+                                       ds->state = ST_RD_R2;
+                               } else if (ds->state == ST_RD_R2) {
+                                       ds->r2 = archive_le32dec(ds->rbytes);
+                                       /* We've gotten all repeated offsets. */
+                                       ds->state = ST_COPY_UNCOMP1;
+                               }
+                       } while (ds->state != ST_COPY_UNCOMP1);
+                       /* FALL THROUGH */
+               case ST_COPY_UNCOMP1:
+                       /*
+                        * Copy bytes form next_in to next_out directly.
+                        */
+                       while (ds->block_bytes_avail) {
+                               unsigned char *d;
+                               int l,ll;
+
+                               if (strm->avail_out <= 0)
+                                       /* Output buffer is empty. */
+                                       return (ARCHIVE_OK);
+                               if (strm->avail_in <= 0) {
+                                       /* Input buffer is empty. */
+                                       if (last)
+                                               goto failed;
+                                       return (ARCHIVE_OK);
+                               }
+                               l = ds->block_bytes_avail;
+                               if (l > ds->w_size - ds->w_pos)
+                                       l = ds->w_size - ds->w_pos;
+                               if (l > strm->avail_out)
+                                       l = (int)strm->avail_out;
+                               if (l > strm->avail_in)
+                                       l = (int)strm->avail_in;
+                               ll = l;
+                               d = &(ds->w_buff[ds->w_pos]);
+                               while (--l >= 0) {
+                                       *strm->next_out++ = *strm->next_in;
+                                       *d++ = *strm->next_in++;
+                               }
+                               strm->avail_out -= ll;
+                               strm->total_out += ll;
+                               strm->avail_in -= ll;
+                               strm->total_in += ll;
+                               ds->w_pos = (ds->w_pos + ll) & ds->w_mask;
+                               ds->block_bytes_avail -= ll;
+                       }
+                       /* FALL THROUGH */
+               case ST_COPY_UNCOMP2:
+                       /* Re-align; skip padding byte. */
+                       if (ds->block_size & 1) {
+                               if (strm->avail_in <= 0) {
+                                       /* Input buffer is empty. */
+                                       ds->state = ST_COPY_UNCOMP2;
+                                       if (last)
+                                               goto failed;
+                                       return (ARCHIVE_OK);
+                               }
+                               strm->next_in++;
+                               strm->avail_in --;
+                               strm->total_in ++;
+                       }
+                       /* This block ended. */
+                       ds->state = ST_RD_BLOCK_TYPE;
+                       return (ARCHIVE_EOF);
+                       /********************/
+               case ST_RD_ALIGNED_OFFSET:
+                       /*
+                        * Read Aligned offset tree.
+                        */
+                       if (!lzx_br_read_ahead(strm, 3 * ds->at.len_size)) {
+                               ds->state = ST_RD_ALIGNED_OFFSET;
+                               if (last)
+                                       goto failed;
+                               return (ARCHIVE_OK);
+                       }
+                       memset(ds->at.freq, 0, sizeof(ds->at.freq));
+                       for (i = 0; i < ds->at.len_size; i++) {
+                               ds->at.bitlen[i] = lzx_br_bits(strm, 3);
+                               ds->at.freq[ds->at.bitlen[i]]++;
+                               lzx_br_consume(strm, 3);
+                       }
+                       if (!lzx_make_huffman_table(&ds->at))
+                               goto failed;
+                       /* FALL THROUGH */
+               case ST_RD_VERBATIM:
+                       ds->loop = 0;
+                       /* FALL THROUGH */
+               case ST_RD_PRE_MAIN_TREE_256:
+                       /*
+                        * Read Pre-tree for first 256 elements of main tree.
+                        */
+                       if (!lzx_read_pre_tree(strm)) {
+                               ds->state = ST_RD_PRE_MAIN_TREE_256;
+                               if (last)
+                                       goto failed;
+                               return (ARCHIVE_OK);
+                       }
+                       if (!lzx_make_huffman_table(&ds->pt))
+                               goto failed;
+                       ds->loop = 0;
+                       /* FALL THROUGH */
+               case ST_MAIN_TREE_256:
+                       /*
+                        * Get path lengths of first 256 elements of main tree.
+                        */
+                       if (!lzx_read_bitlen(strm, &ds->mt, 256)) {
+                               ds->state = ST_MAIN_TREE_256;
+                               if (last)
+                                       goto failed;
+                               return (ARCHIVE_OK);
+                       }
+                       ds->loop = 0;
+                       /* FALL THROUGH */
+               case ST_RD_PRE_MAIN_TREE_REM:
+                       /*
+                        * Read Pre-tree for remaining elements of main tree.
+                        */
+                       if (!lzx_read_pre_tree(strm)) {
+                               ds->state = ST_RD_PRE_MAIN_TREE_REM;
+                               if (last)
+                                       goto failed;
+                               return (ARCHIVE_OK);
+                       }
+                       if (!lzx_make_huffman_table(&ds->pt))
+                               goto failed;
+                       ds->loop = 256;
+                       /* FALL THROUGH */
+               case ST_MAIN_TREE_REM:
+                       /*
+                        * Get path lengths of remaining elements of main tree.
+                        */
+                       if (!lzx_read_bitlen(strm, &ds->mt, -1)) {
+                               ds->state = ST_MAIN_TREE_REM;
+                               if (last)
+                                       goto failed;
+                               return (ARCHIVE_OK);
+                       }
+                       if (!lzx_make_huffman_table(&ds->mt))
+                               goto failed;
+                       ds->loop = 0;
+                       /* FALL THROUGH */
+               case ST_RD_PRE_LENGTH_TREE:
+                       /*
+                        * Read Pre-tree for remaining elements of main tree.
+                        */
+                       if (!lzx_read_pre_tree(strm)) {
+                               ds->state = ST_RD_PRE_LENGTH_TREE;
+                               if (last)
+                                       goto failed;
+                               return (ARCHIVE_OK);
+                       }
+                       if (!lzx_make_huffman_table(&ds->pt))
+                               goto failed;
+                       ds->loop = 0;
+                       /* FALL THROUGH */
+               case ST_LENGTH_TREE:
+                       /*
+                        * Get path lengths of remaining elements of main tree.
+                        */
+                       if (!lzx_read_bitlen(strm, &ds->lt, -1)) {
+                               ds->state = ST_LENGTH_TREE;
+                               if (last)
+                                       goto failed;
+                               return (ARCHIVE_OK);
+                       }
+                       if (!lzx_make_huffman_table(&ds->lt))
+                               goto failed;
+                       ds->state = ST_MAIN;
+                       /* FALL THROUGH */
+               case ST_MAIN:
+                       for (;;) {
+                               if (ds->block_bytes_avail == 0) {
+                                       /* This block ended. */
+                                       ds->state = ST_RD_BLOCK_TYPE;
+                                       return (ARCHIVE_EOF);
+                               }
+                               if (strm->avail_out <= 0)
+                                       /* Output buffer is empty. */
+                                       return (ARCHIVE_OK);
+
+                               if (!lzx_br_ensure(strm,
+                                   ds->mt.max_bits)) {
+                                       if (!last)
+                                               return (ARCHIVE_OK);
+                                       /* Remaining bits are less than
+                                        * maximum bits(mt.max_bits) but maybe
+                                        * it still remains as much as we need,
+                                        * so we should try to use it with
+                                        * dummy bits. */
+                                       c = ds->mt.tbl[lzx_br_bits_forced(
+                                           strm, ds->mt.max_bits)];
+                                       lzx_br_consume(strm, ds->mt.bitlen[c]);
+                                       if (!lzx_br_has(strm, 0))
+                                               goto failed;/* Over read. */
+                               } else {
+                                       c = ds->mt.tbl[lzx_br_bits(strm,
+                                           ds->mt.max_bits)];
+                                       lzx_br_consume(strm, ds->mt.bitlen[c]);
+                               }
+                               if (c > UCHAR_MAX)
+                                       break;
+                               /*
+                                * 'c' is exactly literal code.
+                                */
+                               /* Save a decoded code to reference it
+                                * afterward. */
+                               strm->ds->w_buff[ds->w_pos] = c;
+                               strm->ds->w_pos = (ds->w_pos + 1)
+                                   & strm->ds->w_mask;
+                               /* Store the decoded code to output buffer. */
+                               *strm->next_out++ = c;
+                               strm->avail_out--;
+                               strm->total_out++;
+                               ds->block_bytes_avail--;
+                       }
+                       /*
+                        * Get a match code, its length and offset.
+                        */
+                       c -= UCHAR_MAX + 1;
+                       ds->length_header = c & 7;
+                       ds->position_slot = c >> 3;
+                       /* FALL THROUGH */
+               case ST_LENGTH:
+                       /*
+                        * Get a length.
+                        */
+                       if (ds->length_header == 7) {
+                               if (!lzx_br_ensure(strm, ds->lt.max_bits)) {
+                                       if (!last) {
+                                               ds->state = ST_LENGTH;
+                                               return (ARCHIVE_OK);
+                                       }
+                                       c = ds->lt.tbl[lzx_br_bits_forced(
+                                           strm, ds->lt.max_bits)];
+                                       lzx_br_consume(strm, ds->lt.bitlen[c]);
+                                       if (!lzx_br_has(strm, 0))
+                                               goto failed;/* Over read. */
+                               } else {
+                                       c = ds->lt.tbl[lzx_br_bits(strm,
+                                           ds->lt.max_bits)];
+                                       lzx_br_consume(strm, ds->lt.bitlen[c]);
+                               }
+                               ds->copy_len = c + 7 + 2;
+                       } else
+                               ds->copy_len = ds->length_header + 2;
+                       /*
+                        * Get an offset.
+                        */
+                       switch (ds->position_slot) {
+                       case 0: /* Use repeated offset 0. */
+                               ds->copy_pos = ds->r0;
+                               break;
+                       case 1: /* Use repeated offset 1. */
+                               ds->copy_pos = ds->r1;
+                               /* Swap repeated offset. */
+                               ds->r1 = ds->r0;
+                               ds->r0 = ds->copy_pos;
+                               break;
+                       case 2: /* Use repeated offset 2. */
+                               ds->copy_pos = ds->r2;
+                               /* Swap repeated offset. */
+                               ds->r2 = ds->r0;
+                               ds->r0 = ds->copy_pos;
+                               break;
+                       default:
+                               ds->offset_bits =
+                                   ds->pos_tbl[ds->position_slot].footer_bits;
+                               break;
+                       }
+                       if (ds->position_slot <= 2) {
+                               ds->state = ST_REAL_POS;
+                               break;
+                       }
+                       /* FALL THROUGH */
+               case ST_OFFSET:
+                       /*
+                        * Get the offset, which is a distance from
+                        * current window position.
+                        */
+                       if (ds->block_type == ALIGNED_OFFSET_BLOCK &&
+                           ds->offset_bits >= 3) {
+                               int offbits = ds->offset_bits - 3;
+
+                               if (!lzx_br_ensure(strm, offbits)) {
+                                       ds->state = ST_OFFSET;
+                                       if (last)
+                                               goto failed;
+                                       return (ARCHIVE_OK);
+                               }
+                               c = lzx_br_bits(strm, offbits);
+                               ds->copy_pos = c << 3;
+
+                               /* Get an aligned number. */
+                               if (!lzx_br_ensure(strm,
+                                   offbits + ds->at.max_bits)) {
+                                       if (!last) {
+                                               ds->state = ST_OFFSET;
+                                               return (ARCHIVE_OK);
+                                       }
+                                       lzx_br_consume(strm, offbits);
+                                       c = ds->at.tbl[lzx_br_bits_forced(strm,
+                                           ds->at.max_bits)];
+                                       lzx_br_consume(strm, ds->at.bitlen[c]);
+                                       if (!lzx_br_has(strm, 0))
+                                               goto failed;/* Over read. */
+                               } else {
+                                       lzx_br_consume(strm, offbits);
+                                       c = ds->at.tbl[lzx_br_bits(strm,
+                                           ds->at.max_bits)];
+                                       lzx_br_consume(strm, ds->at.bitlen[c]);
+                               }
+                               /* Add an aligned number. */
+                               ds->copy_pos += c;
+                       } else {
+                               if (!lzx_br_ensure(strm, ds->offset_bits)) {
+                                       ds->state = ST_OFFSET;
+                                       if (last)
+                                               goto failed;
+                                       return (ARCHIVE_OK);
+                               }
+                               c = lzx_br_bits(strm, ds->offset_bits);
+                               lzx_br_consume(strm, ds->offset_bits);
+                               ds->copy_pos = c;
+                       }
+                       ds->copy_pos += ds->pos_tbl[ds->position_slot].base -2;
+
+                       /* Update repeated offset LRU queue. */
+                       ds->r2 = ds->r1;
+                       ds->r1 = ds->r0;
+                       ds->r0 = ds->copy_pos;
+                       /* FALL THROUGH */
+               case ST_REAL_POS:
+                       /*
+                        * Compute a real position in window.
+                        */
+                       ds->copy_pos = (ds->w_pos - ds->copy_pos) & ds->w_mask;
+                       /* FALL THROUGH */
+               case ST_COPY:
+                       /*
+                        * Copy several bytes as extracted data from the window
+                        * into the output buffer.
+                        */
+                       if ((size_t)ds->copy_len > ds->block_bytes_avail)
+                               goto failed;
+                       do {
+                               const unsigned char *s;
+                               unsigned char *d;
+                               int l,ll;
+                               if (strm->avail_out <= 0) {
+                                       /* Output buffer is empty. */
+                                       ds->state = ST_COPY;
+                                       return (ARCHIVE_OK);
+                               }
+                               l = ds->copy_len;
+                               if (l > ds->w_size - ds->copy_pos)
+                                       l = ds->w_size - ds->copy_pos;
+                               if (l > ds->w_size - ds->w_pos)
+                                       l = ds->w_size - ds->w_pos;
+                               if (l > strm->avail_out)
+                                       l = (int)strm->avail_out;
+                               ll = l;
+                               s = &(ds->w_buff[ds->copy_pos]);
+                               d = &(ds->w_buff[ds->w_pos]);
+                               while (--l >= 0) {
+                                       *strm->next_out++ = *s;
+                                       *d++ = *s++;
+                               }
+                               strm->avail_out -= ll;
+                               strm->total_out += ll;
+                               ds->copy_pos =
+                                   (ds->copy_pos + ll) & ds->w_mask;
+                               ds->w_pos = (ds->w_pos + ll) & ds->w_mask;
+                               ds->copy_len -= ll;
+                               ds->block_bytes_avail -= ll;
+                       } while (ds->copy_len > 0);
+                       ds->state = ST_MAIN;
+                       break;
+               }
+       }
+failed:
+       return (ds->error = ARCHIVE_FAILED);
+}
+
+static int
+lzx_read_pre_tree(struct lzx_stream *strm)
+{
+       struct lzx_dec *ds = strm->ds;
+       int i;
+
+       if (ds->loop == 0)
+               memset(ds->pt.freq, 0, sizeof(ds->pt.freq));
+       for (i = ds->loop; i < ds->pt.len_size; i++) {
+               if (!lzx_br_read_ahead(strm, 4)) {
+                       ds->loop = i;
+                       return (0);
+               }
+               ds->pt.bitlen[i] = lzx_br_bits(strm, 4);
+               ds->pt.freq[ds->pt.bitlen[i]]++;
+               lzx_br_consume(strm, 4);
+       }
+       ds->loop = i;
+       return (1);
+}
+
+/*
+ * Read a bunch of bit-lengths from pre-tree.
+ */
+static int
+lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end)
+{
+       struct lzx_dec *ds = strm->ds;
+       int c, i, j, ret, same;
+
+       i = ds->loop;
+       if (i == 0)
+               memset(d->freq, 0, sizeof(d->freq));
+       ret = 0;
+       if (end < 0)
+               end = d->len_size;
+       while (i < end) {
+               ds->loop = i;
+               if (!lzx_br_read_ahead(strm, ds->pt.max_bits))
+                       goto getdata;
+               c = ds->pt.tbl[lzx_br_bits(strm, ds->pt.max_bits)];
+               switch (c) {
+               case 17:/* several zero lengths, from 4 to 19. */
+                       if (!lzx_br_read_ahead(strm, ds->pt.bitlen[c]+4))
+                               goto getdata;
+                       lzx_br_consume(strm, ds->pt.bitlen[c]);
+                       same = lzx_br_bits(strm, 4) + 4;
+                       lzx_br_consume(strm, 4);
+                       for (j = 0; j < same; j++)
+                               d->bitlen[i++] = 0;
+                       break;
+               case 18:/* many zero lengths, from 20 to 51. */
+                       if (!lzx_br_read_ahead(strm, ds->pt.bitlen[c]+5))
+                               goto getdata;
+                       lzx_br_consume(strm, ds->pt.bitlen[c]);
+                       same = lzx_br_bits(strm, 5) + 20;
+                       lzx_br_consume(strm, 5);
+                       memset(d->bitlen + i, 0, same);
+                       i += same;
+                       break;
+               case 19:/* a few same lengths. */
+                       if (!lzx_br_read_ahead(strm,
+                           ds->pt.bitlen[c]+1+ds->pt.max_bits))
+                               goto getdata;
+                       lzx_br_consume(strm, ds->pt.bitlen[c]);
+                       same = lzx_br_bits(strm, 1) + 4;
+                       lzx_br_consume(strm, 1);
+                       c = ds->pt.tbl[lzx_br_bits(strm, ds->pt.max_bits)];
+                       lzx_br_consume(strm, ds->pt.bitlen[c]);
+                       c = (d->bitlen[i] - c + 17) % 17;
+                       for (j = 0; j < same; j++)
+                               d->bitlen[i++] = c;
+                       d->freq[c] += same;
+                       break;
+               default:
+                       lzx_br_consume(strm, ds->pt.bitlen[c]);
+                       c = (d->bitlen[i] - c + 17) % 17;
+                       d->freq[c]++;
+                       d->bitlen[i++] = c;
+                       break;
+               }
+       }
+       ret = 1;
+getdata:
+       ds->loop = i;
+       return (ret);
+}
+
+static int
+lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
+{
+
+       if (hf->bitlen == NULL || hf->len_size != len_size) {
+               free(hf->bitlen);
+               hf->bitlen = calloc(len_size,  sizeof(hf->bitlen[0]));
+               if (hf->bitlen == NULL)
+                       return (ARCHIVE_FATAL);
+               hf->len_size = len_size;
+       } else
+               memset(hf->bitlen, 0, len_size *  sizeof(hf->bitlen[0]));
+       if (hf->tbl == NULL) {
+               hf->tbl = malloc((1 << tbl_bits) * sizeof(hf->tbl[0]));
+               if (hf->tbl == NULL)
+                       return (ARCHIVE_FATAL);
+               hf->tbl_bits = tbl_bits;
+       }
+       return (ARCHIVE_OK);
+}
+
+static void
+lzx_huffman_free(struct huffman *hf)
+{
+       free(hf->bitlen);
+       free(hf->tbl);
+}
+
+/*
+ * Make a huffman coding table.
+ */
+static int
+lzx_make_huffman_table(struct huffman *hf)
+{
+       int bitptn[17], weight[17];
+       int i, maxbits = 0, ptn, tbl_size, w;
+
+       /*
+        * Initialize bit patterns.
+        */
+       ptn = 0;
+       for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {
+               bitptn[i] = ptn;
+               weight[i] = w;
+               if (hf->freq[i]) {
+                       ptn += hf->freq[i] * w;
+                       maxbits = i;
+               }
+       }
+       if ((ptn & 0xffff) != 0 || maxbits > hf->tbl_bits)
+               return (0);/* Invalid */
+
+       hf->max_bits = maxbits;
+       tbl_size = 1 << maxbits;
+
+       /*
+        * Cut out extra bits which we won't house in the table.
+        * This preparation reduces the same calculation in the for-loop
+        * making the table.
+        */
+       if (maxbits < 16) {
+               int ebits = 16 - maxbits;
+               for (i = 1; i <= maxbits; i++) {
+                       bitptn[i] >>= ebits;
+                       weight[i] >>= ebits;
+               }
+       }
+
+       /*
+        * Make the table.
+        */
+       for (i = 0; i < hf->len_size; i++) {
+               uint16_t *p;
+               int len, cnt;
+
+               if ((len = hf->bitlen[i]) == 0)
+                       continue;
+               /* Get a bit pattern */
+               ptn = bitptn[len];
+               cnt = weight[len];
+               /* Calculate next bit pattern */
+               if ((bitptn[len] += cnt) > tbl_size)
+                       return (0);/* Invalid */
+               /* Update the table */
+               p = &(hf->tbl[ptn]);
+               while (--cnt >= 0)
+                       *p++ = (uint16_t)i;
+       }
+       return (1);
+}
+
index 79d732c73351f640ff34c24b4c09d0fd1b796b1c..118e58ad115f6125fe8c9c663d7c535004fa35ad 100644 (file)
@@ -52,6 +52,7 @@ static struct {
 } files[] = {
        {0, "test_fuzz_1.iso.Z"}, /* Exercise compress decompressor. */
        {1, "test_fuzz_1.iso.Z"},
+       {0, "test_fuzz.cab"},
        {0, "test_fuzz.lzh"},
        {0, "test_compat_bzip2_1.tbz"}, /* Exercise bzip2 decompressor. */
        {1, "test_compat_bzip2_1.tbz"},
diff --git a/libarchive/test/test_fuzz.cab.uu b/libarchive/test/test_fuzz.cab.uu
new file mode 100644 (file)
index 0000000..4cfa4fb
--- /dev/null
@@ -0,0 +1,49 @@
+begin 644 test_fuzz.cab
+M35-#1@`````1"````````"P``````````P$!``0```#X!P``A0````$``Q)K
+M````````````G3U:L2``9FEL90#I!```:P``````G3TYL2``24Y35$%,3```
+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
+H^+5YR;W&3(M6?E57A59NO4H-K"3>576O"ZQ6R!-&3>Z7&K;GPA9\V```
+`
+end
index a534dd1162f53ab8a5af0798c3c1e4b939dad085..80c74bc6a6385c09e8bacb454521c1ae36bab840 100644 (file)
@@ -27,9 +27,21 @@ __FBSDID("$FreeBSD");
 
 /*
 Execute the following command to rebuild the data for this program:
-   tail -n +32 test_read_format_cab.c | /bin/sh
+   tail -n +44 test_read_format_cab.c | /bin/sh
+And following works are:
+1. Move /tmp/cab/cab.zip to Windows PC
+2. Extract cab.zip
+3. Open command prompt and change current directory where you extracted cab.zip
+4. Execute cab.bat
+5. Then you will see that there is a cabinet file, test.cab
+6. Move test.cab to posix platform
+7. Extract test.cab with this version of bsdtar
+8. Excecute the following command to make uuencoded files.
+ uuencode test_read_format_cab_1.cab test_read_format_cab_1.cab > test_read_format_cab_1.cab.uu
+ uuencode test_read_format_cab_2.cab test_read_format_cab_2.cab > test_read_format_cab_2.cab.uu
+ uuencode test_read_format_cab_3.cab test_read_format_cab_3.cab > test_read_format_cab_3.cab.uu
 
-#/bin/sh
+#!/bin/sh
 #
 # How to make test data.
 #
@@ -69,6 +81,8 @@ dd if=/dev/zero of=${base}/zero bs=1 count=33000 > /dev/null 2>&1
 #
 cab1=test_read_format_cab_1.cab
 cab2=test_read_format_cab_2.cab
+cab3=test_read_format_cab_3.cab
+#
 #
 cat > ${base}/mkcab1 << END
 .Set Compress=OFF
@@ -98,40 +112,42 @@ dir2/file2
 END
 #
 cat > ${base}/mkcab3 << END
+.Set CompressionType=LZX
+.Set DiskDirectory1=.
+.Set InfDate=1980-01-02
+.Set InfTime=00:00:00
+.Set CabinetName1=${cab3}
+empty
+zero
+.Set DestinationDir=dir1
+dir1/file1
+.Set DestinationDir=dir2
+dir2/file2
+END
+#
+cat > ${base}/mkcab4 << END
 .Set CompressionType=MSZIP
 .Set DiskDirectory1=.
 .Set CabinetName1=test.cab
 ${cab1}
 ${cab2}
+${cab3}
 END
 #
 cat > ${base}/cab.bat << END
 makecab.exe /F mkcab1
 makecab.exe /F mkcab2
 makecab.exe /F mkcab3
+makecab.exe /F mkcab4
 del setup.inf setup.rpt
-del empty zero dir1\file1 dir2\file2 mkcab1 mkcab2 mkcab3
-del ${cab1} ${cab2}
+del empty zero dir1\file1 dir2\file2 mkcab1 mkcab2 mkcab3 mkcab4
+del ${cab1} ${cab2} ${cab3}
 rmdir dir1 dir2
 END
 #
 f=cab.zip
-(cd ${base}; zip -q -c $f empty zero dir1/file1 dir2/file2 mkcab1 mkcab2 mkcab3 cab.bat)
-#
-echo
-echo "You need some more work to make sample files for cab reader test."
-echo
-echo "1. Move ${base}/${f} to Windows PC"
-echo "2. Extract ${base}/${f}"
-echo "3. Open command prompt and change current directory where you extracted ${f}"
-
-echo "4. Execute cab.bat"
-echo "5. Then you will see what there is a cabinet file, test.cab"
-echo "6. Move test.cab to posix platform"
-echo "7. Excuete bsdtar xf test.cab"
-echo "8. Execute uuencode ${cab1} ${cab1} > ${cab1}.uu"
-echo "9. Execute uuencode ${cab2} ${cab2} > ${cab2}.uu"
-
+(cd ${base}; zip -q -c $f empty zero dir1/file1 dir2/file2 mkcab1 mkcab2 mkcab3 mkcab4 cab.bat)
+#
 exit 1
 */
 
@@ -236,5 +252,7 @@ DEFINE_TEST(test_read_format_cab)
        verify("test_read_format_cab_1.cab", 0);
        /* Verify Cabinet file in MSZIP. */
        verify("test_read_format_cab_2.cab", 1);
+       /* Verify Cabinet file in LZX. */
+       verify("test_read_format_cab_3.cab", 1);
 }
 
diff --git a/libarchive/test/test_read_format_cab_3.cab.uu b/libarchive/test/test_read_format_cab_3.cab.uu
new file mode 100644 (file)
index 0000000..9eff338
--- /dev/null
@@ -0,0 +1,10 @@
+begin 755 test_read_format_cab_3.cab
+M35-#1@`````A`0```````"P``````````P$!``0````K"P``C0````(``Q(`
+M````````````(@```"``96UP='D`Z(```````````"(````@`'IE<F\`/```
+M`.B``````"(````@`&1I<C%<9FEL93$`3@```"2!`````"(````@`&1I<C)<
+M9FEL93(`CH]5G'```(!;@("-""`B%P```($`````0#-#`.@+#IA)^XZV#HDL
+M6M_?$-````````$9`'*0(3<P]I!&O]^0?@`@`````&`&8(@]$<$7]7=_TYA\
+ML&X````````````````````````````````````````.``"PPU!^C10`<@$G
+3?%0=L<8OOOO<G,Y6S<,^[/LHWP``
+`
+end