Taken from http://download.savannah.gnu.org/releases/lzip/busybox/ --- archival/Config.src +++ archival/Config.src @@ -21,6 +21,10 @@ bool "Make tar, rpm, modprobe etc understand .gz data" default y +config FEATURE_SEAMLESS_LZ + bool "Make tar, rpm, modprobe etc understand .lz data" + default y + config FEATURE_SEAMLESS_Z bool "Make tar, rpm, modprobe etc understand .Z data" default n # it is ancient --- archival/bbunzip.c +++ archival/bbunzip.c @@ -207,18 +207,31 @@ #if ENABLE_UNCOMPRESS \ || ENABLE_FEATURE_BZIP2_DECOMPRESS \ + || ENABLE_LUNZIP \ || ENABLE_UNLZMA || ENABLE_LZCAT || ENABLE_LZMA \ || ENABLE_UNXZ || ENABLE_XZCAT || ENABLE_XZ static char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext) { char *extension = strrchr(filename, '.'); - if (!extension || strcmp(extension + 1, expected_ext) != 0) { + + if (!extension) + return NULL; + + if (strcmp(extension + 1, expected_ext) == 0) { + *extension = '\0'; + } else if (extension[1] == 't' && strlen(expected_ext) >= 2 && + strcmp(extension + 2, expected_ext) == 0) { + filename = xstrdup(filename); + extension = strrchr(filename, '.'); + extension[2] = 'a'; + extension[3] = 'r'; + extension[4] = '\0'; + } else { /* Mimic GNU gunzip - "real" bunzip2 tries to */ /* unpack file anyway, to file.out */ return NULL; } - *extension = '\0'; return filename; } #endif @@ -461,6 +474,37 @@ } #endif + +//config:config LUNZIP +//config: bool "lunzip" +//config: default y +//config: help +//config: lunzip is used to decompress archives created by lzip. +//config: You can use the '-t' option to test the integrity of an archive, +//config: without decompressing it. + +//applet:IF_LUNZIP(APPLET(lunzip, BB_DIR_USR_BIN, BB_SUID_DROP)) +//kbuild:lib-$(CONFIG_LUNZIP) += bbunzip.o + +//usage:#define lunzip_trivial_usage +//usage: "[-cft] [FILE]..." +//usage:#define lunzip_full_usage "\n\n" +//usage: "Decompress FILEs (or stdin)\n" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -k Keep input files" +//usage: "\n -t Test file integrity" +#if ENABLE_LUNZIP +int lunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int lunzip_main(int argc UNUSED_PARAM, char **argv) +{ + getopt32(argv, "cfkvqdt"); + argv += optind; + + return bbunpack(argv, unpack_lz_stream, make_new_name_generic, "lz"); +} +#endif + /* * Small lzma deflate implementation. --- archival/dpkg.c +++ archival/dpkg.c @@ -1482,6 +1482,9 @@ #if ENABLE_FEATURE_SEAMLESS_XZ llist_add_to(&(ar_handle->accept), (char*)"control.tar.xz"); #endif +#if ENABLE_FEATURE_SEAMLESS_LZ + llist_add_to(&(ar_handle->accept), (char*)"control.tar.lz"); +#endif /* Assign the tar handle as a subarchive of the ar handle */ ar_handle->dpkg__sub_archive = tar_handle; @@ -1509,6 +1512,9 @@ #if ENABLE_FEATURE_SEAMLESS_XZ llist_add_to(&(ar_handle->accept), (char*)"data.tar.xz"); #endif +#if ENABLE_FEATURE_SEAMLESS_LZ + llist_add_to(&(ar_handle->accept), (char*)"data.tar.lz"); +#endif /* Assign the tar handle as a subarchive of the ar handle */ ar_handle->dpkg__sub_archive = tar_handle; --- archival/dpkg_deb.c +++ archival/dpkg_deb.c @@ -70,6 +70,10 @@ llist_add_to(&ar_archive->accept, (char*)"data.tar.bz2"); llist_add_to(&control_tar_llist, (char*)"control.tar.bz2"); #endif +#if ENABLE_FEATURE_SEAMLESS_LZ + llist_add_to(&ar_archive->accept, (char*)"data.tar.lz"); + llist_add_to(&control_tar_llist, (char*)"control.tar.lz"); +#endif #if ENABLE_FEATURE_SEAMLESS_LZMA llist_add_to(&ar_archive->accept, (char*)"data.tar.lzma"); llist_add_to(&control_tar_llist, (char*)"control.tar.lzma"); --- archival/libarchive/Kbuild.src +++ archival/libarchive/Kbuild.src @@ -35,6 +35,7 @@ get_header_tar.o \ get_header_tar_gz.o \ get_header_tar_bz2.o \ + get_header_tar_lz.o \ get_header_tar_lzma.o \ get_header_tar_xz.o \ @@ -54,6 +55,7 @@ # 'bzip2 -d', bunzip2 or bzcat selects FEATURE_BZIP2_DECOMPRESS lib-$(CONFIG_FEATURE_BZIP2_DECOMPRESS) += open_transformer.o decompress_bunzip2.o lib-$(CONFIG_FEATURE_UNZIP_BZIP2) += open_transformer.o decompress_bunzip2.o +lib-$(CONFIG_LUNZIP) += open_transformer.o decompress_lunzip.o lib-$(CONFIG_UNLZMA) += open_transformer.o decompress_unlzma.o lib-$(CONFIG_LZCAT) += open_transformer.o decompress_unlzma.o lib-$(CONFIG_LZMA) += open_transformer.o decompress_unlzma.o @@ -70,6 +72,7 @@ lib-$(CONFIG_RPM) += open_transformer.o decompress_gunzip.o get_header_cpio.o lib-$(CONFIG_GZIP) += open_transformer.o lib-$(CONFIG_BZIP2) += open_transformer.o +lib-$(CONFIG_LZIP) += open_transformer.o lib-$(CONFIG_LZOP) += open_transformer.o lib-$(CONFIG_MAN) += open_transformer.o lib-$(CONFIG_SETFONT) += open_transformer.o @@ -85,6 +88,7 @@ lib-$(CONFIG_FEATURE_SEAMLESS_Z) += open_transformer.o decompress_uncompress.o lib-$(CONFIG_FEATURE_SEAMLESS_GZ) += open_transformer.o decompress_gunzip.o lib-$(CONFIG_FEATURE_SEAMLESS_BZ2) += open_transformer.o decompress_bunzip2.o +lib-$(CONFIG_FEATURE_SEAMLESS_LZ) += open_transformer.o decompress_lunzip.o lib-$(CONFIG_FEATURE_SEAMLESS_LZMA) += open_transformer.o decompress_unlzma.o lib-$(CONFIG_FEATURE_SEAMLESS_XZ) += open_transformer.o decompress_unxz.o lib-$(CONFIG_FEATURE_COMPRESS_USAGE) += open_transformer.o decompress_bunzip2.o --- /dev/null +++ archival/libarchive/decompress_lunzip.c @@ -0,0 +1,502 @@ +/* + * lunzip implementation for busybox + * + * Copyright (C) 2012-2016 Antonio Diaz Diaz. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "bb_archive.h" +#include "lzip.h" + +/* Some functions have been marked with __always_inline because xz does + it, giving the impression that unxz is much faster than lunzip. */ +#ifndef __always_inline +# ifdef __GNUC__ +# define __always_inline \ + inline __attribute__((__always_inline__)) +# else +# define __always_inline inline +# endif +#endif + + +enum { rd_buffer_size = 16384 }; + +struct Range_decoder { + unsigned long long partial_member_pos; + uint8_t *buffer; /* input buffer */ + int pos; /* current pos in buffer */ + int stream_pos; /* when reached, a new block must be read */ + uint32_t code; + uint32_t range; + int infd; /* input file descriptor */ + bool at_stream_end; +}; + + +static bool Rd_read_block(struct Range_decoder *const rdec) +{ + if (!rdec->at_stream_end) { + rdec->stream_pos = + full_read(rdec->infd, rdec->buffer, rd_buffer_size); + rdec->at_stream_end = (rdec->stream_pos < rd_buffer_size); + rdec->partial_member_pos += rdec->pos; + rdec->pos = 0; + } + return rdec->pos < rdec->stream_pos; +} + + +static bool Rd_init(struct Range_decoder *const rdec, const int ifd, + const bool magic_skipped) +{ + rdec->partial_member_pos = (magic_skipped ? 4 : 0); + rdec->buffer = (uint8_t *) malloc(rd_buffer_size); + if (!rdec->buffer) return false; + rdec->pos = 0; + rdec->stream_pos = 0; + rdec->code = 0; + rdec->range = 0xFFFFFFFFU; + rdec->infd = ifd; + rdec->at_stream_end = false; + return true; +} + +static __always_inline bool Rd_finished(struct Range_decoder *const rdec) +{ + return rdec->pos >= rdec->stream_pos && !Rd_read_block(rdec); +} + +static inline unsigned long long +Rd_member_position(const struct Range_decoder *const rdec) +{ + return rdec->partial_member_pos + rdec->pos; +} + +static inline void Rd_reset_member_position(struct Range_decoder *const rdec) +{ + rdec->partial_member_pos = 0; rdec->partial_member_pos -= rdec->pos; +} + +static __always_inline uint8_t Rd_get_byte(struct Range_decoder *const rdec) +{ + /* 0xFF avoids decoder error if member is truncated at EOS marker */ + if (Rd_finished(rdec)) return 0xFF; + return rdec->buffer[rdec->pos++]; +} + +static void Rd_load(struct Range_decoder *const rdec) +{ + int i; + rdec->code = 0; + for (i = 0; i < 5; ++i) + rdec->code = (rdec->code << 8) | Rd_get_byte(rdec); + rdec->range = 0xFFFFFFFFU; +} + +static __always_inline void Rd_normalize(struct Range_decoder *const rdec) +{ + if (rdec->range <= 0x00FFFFFFU) { + rdec->range <<= 8; + rdec->code = (rdec->code << 8) | Rd_get_byte(rdec); + } +} + +static uint32_t Rd_decode(struct Range_decoder *const rdec, + const uint32_t num_bits) +{ + uint32_t symbol = 0; + uint32_t i; + for (i = num_bits; i > 0; --i) { + uint32_t mask; + Rd_normalize(rdec); + rdec->range >>= 1; + /* symbol <<= 1; */ + /* if(rdec->code >= rdec->range) { rdec->code -= rdec->range; symbol |= 1; } */ + mask = 0U - (rdec->code < rdec->range); + rdec->code -= rdec->range; + rdec->code += rdec->range & mask; + symbol = (symbol << 1) + (mask + 1); + } + return symbol; +} + +static __always_inline uint32_t Rd_decode_bit(struct Range_decoder *const rdec, + Bit_model * const probability) +{ + uint32_t bound; + Rd_normalize(rdec); + bound = (rdec->range >> bit_model_total_bits) * *probability; + if (rdec->code < bound) { + rdec->range = bound; + *probability += (bit_model_total - *probability) >> bit_model_move_bits; + return 0; + } else { + rdec->range -= bound; + rdec->code -= bound; + *probability -= *probability >> bit_model_move_bits; + return 1; + } +} + +static __always_inline uint32_t Rd_decode_tree(struct Range_decoder *const rdec, + Bit_model bm[], const uint32_t num_bits) +{ + uint32_t symbol = 1; + uint32_t i; + for (i = num_bits; i > 0; --i) + symbol = (symbol << 1) | Rd_decode_bit(rdec, &bm[symbol]); + return symbol - (1 << num_bits); +} + +static __always_inline uint32_t Rd_decode_tree_reversed(struct Range_decoder *const rdec, + Bit_model bm[], const uint32_t num_bits) +{ + uint32_t model = 1; + uint32_t symbol = 0; + uint32_t i; + for (i = 0; i < num_bits; ++i) { + const uint32_t bit = Rd_decode_bit(rdec, &bm[model]); + model = ( model << 1 ) + bit; + symbol |= ( bit << i ); + } + return symbol; +} + +static uint32_t Rd_decode_matched(struct Range_decoder *const rdec, + Bit_model bm[], uint32_t match_byte) +{ + uint32_t symbol = 1; + uint32_t mask = 0x100; + while(true) { + const uint32_t match_bit = (match_byte <<= 1) & mask; + const uint32_t bit = Rd_decode_bit(rdec, &bm[symbol+match_bit+mask]); + symbol = (symbol << 1) + bit; + if (symbol >= 0x100) return symbol & 0xFF; + mask &= ~(match_bit ^ (bit << 8)); /* if( match_bit != bit ) mask = 0; */ + } +} + +static __always_inline uint32_t Rd_decode_len(struct Range_decoder *const rdec, + struct Len_model * const lm, + const int pos_state) +{ + if (Rd_decode_bit(rdec, &lm->choice1) == 0) + return Rd_decode_tree(rdec, lm->bm_low[pos_state], len_low_bits); + if (Rd_decode_bit(rdec, &lm->choice2) == 0) + return len_low_symbols + + Rd_decode_tree(rdec, lm->bm_mid[pos_state], len_mid_bits); + return len_low_symbols + len_mid_symbols + + Rd_decode_tree(rdec, lm->bm_high, len_high_bits); +} + + +struct LZ_decoder { + unsigned long long partial_data_pos; + struct Range_decoder *rdec; + uint32_t dictionary_size; + uint8_t *buffer; /* output buffer */ + uint32_t pos; /* current pos in buffer */ + uint32_t stream_pos; /* first byte not yet written to file */ + uint32_t crc; + int outfd; /* output file descriptor */ + bool pos_wrapped; + bool write_error; +}; + +static void LZd_flush_data(struct LZ_decoder *const d) +{ + if (d->pos > d->stream_pos) { + const int size = d->pos - d->stream_pos; + d->crc = crc32_block_endian0(d->crc, d->buffer + d->stream_pos, + size, global_crc32_table); + if (d->outfd >= 0 && full_write(d->outfd, + d->buffer + d->stream_pos, size) != size) + d->write_error = true; + if (d->pos >= d->dictionary_size) { + d->partial_data_pos += d->pos; + d->pos = 0; + d->pos_wrapped = true; + } + d->stream_pos = d->pos; + } +} + +static __always_inline uint8_t LZd_peek_prev(const struct LZ_decoder *const d) +{ + const uint32_t i = ((d->pos > 0) ? d->pos : d->dictionary_size) - 1; + return d->buffer[i]; +} + +static __always_inline uint8_t LZd_peek(const struct LZ_decoder *const d, + const uint32_t distance) +{ + uint32_t i = d->pos - distance - 1; + if (d->pos <= distance) i += d->dictionary_size; + return d->buffer[i]; +} + +static __always_inline void LZd_put_byte(struct LZ_decoder *const d, + const uint8_t b) +{ + d->buffer[d->pos] = b; + if (++d->pos >= d->dictionary_size) LZd_flush_data(d); +} + +static void LZd_copy_block(struct LZ_decoder *const d, + const uint32_t distance, uint32_t len) +{ + uint32_t i = d->pos - distance - 1; + bool fast; + if (d->pos <= distance) { + i += d->dictionary_size; + fast = (len <= d->dictionary_size - i && len <= i - d->pos); + } + else + fast = (len < d->dictionary_size - d->pos && len <= d->pos - i); + if( fast ) { /* no wrap, no overlap */ + memcpy(d->buffer + d->pos, d->buffer + i, len); + d->pos += len; + } else for (; len > 0; --len) { + d->buffer[d->pos] = d->buffer[i]; + if (++d->pos >= d->dictionary_size) LZd_flush_data(d); + if (++i >= d->dictionary_size) i = 0; + } +} + +static bool LZd_init(struct LZ_decoder *const d, + struct Range_decoder *const rde, + const uint32_t dict_size, const int ofd) +{ + d->partial_data_pos = 0; + d->rdec = rde; + d->dictionary_size = dict_size; + d->buffer = (uint8_t *) malloc(d->dictionary_size); + if (!d->buffer) return false; + d->pos = 0; + d->stream_pos = 0; + d->crc = 0xFFFFFFFFU; + d->outfd = ofd; + d->pos_wrapped = false; + d->write_error = false; + d->buffer[d->dictionary_size - 1] = 0; /* prev_byte of first byte */ + return true; +} + +static inline uint32_t LZd_crc(const struct LZ_decoder *const d) +{ + return d->crc ^ 0xFFFFFFFFU; +} + +static __always_inline unsigned long long +LZd_data_position(const struct LZ_decoder *const d) +{ + return d->partial_data_pos + d->pos; +} + + +static bool LZd_verify_trailer(struct LZ_decoder *const d) +{ + File_trailer trailer; + int i = 0; + while (i < Ft_size) + trailer[i++] = Rd_get_byte(d->rdec); + + return (Ft_get_data_crc(trailer) == LZd_crc(d) && + Ft_get_data_size(trailer) == LZd_data_position(d) && + Ft_get_member_size(trailer) == Rd_member_position(d->rdec)); +} + + +/* Return value: -1 = write error, 0 = OK, 1 = data error. */ +static int LZd_decode_member(struct LZ_decoder *const d) +{ + struct Range_decoder * const rdec = d->rdec; + Bit_model bm_literal[1 << literal_context_bits][0x300]; + Bit_model bm_match[states][pos_states]; + Bit_model bm_rep[states]; + Bit_model bm_rep0[states]; + Bit_model bm_rep1[states]; + Bit_model bm_rep2[states]; + Bit_model bm_len[states][pos_states]; + Bit_model bm_dis_slot[len_states][1 << dis_slot_bits]; + Bit_model bm_dis[modeled_distances-end_dis_model]; + Bit_model bm_align[dis_align_size]; + struct Len_model match_len_model; + struct Len_model rep_len_model; + uint32_t rep0 = 0; /* rep[0-3] latest four distances */ + uint32_t rep1 = 0; /* used for efficient coding of */ + uint32_t rep2 = 0; /* repeated distances */ + uint32_t rep3 = 0; + State state = 0; + + Bm_array_init( bm_literal[0], (1 << literal_context_bits) * 0x300 ); + Bm_array_init( bm_match[0], states * pos_states ); + Bm_array_init( bm_rep, states ); + Bm_array_init( bm_rep0, states ); + Bm_array_init( bm_rep1, states ); + Bm_array_init( bm_rep2, states ); + Bm_array_init( bm_len[0], states * pos_states ); + Bm_array_init( bm_dis_slot[0], len_states * (1 << dis_slot_bits) ); + Bm_array_init( bm_dis, modeled_distances - end_dis_model ); + Bm_array_init( bm_align, dis_align_size ); + Lm_init(&match_len_model); + Lm_init(&rep_len_model); + + Rd_load(rdec); + while (!Rd_finished(rdec)) { + const int pos_state = LZd_data_position(d) & pos_state_mask; + if (Rd_decode_bit(rdec, &bm_match[state][pos_state]) == 0) { + const uint8_t prev_byte = LZd_peek_prev(d); + if (St_is_char(state)) { + state -= (state < 4) ? state : 3; + LZd_put_byte(d, Rd_decode_tree(rdec, + bm_literal[get_lit_state(prev_byte)], 8)); + } else { + state -= (state < 10) ? 3 : 6; + LZd_put_byte(d, Rd_decode_matched(rdec, + bm_literal[get_lit_state(prev_byte)], + LZd_peek(d, rep0))); + } + } else { + uint32_t len; + if (Rd_decode_bit(rdec, &bm_rep[state]) != 0) { + if (Rd_decode_bit(rdec, &bm_rep0[state]) != 0) { + uint32_t distance; + if (Rd_decode_bit(rdec, &bm_rep1[state]) == 0) + distance = rep1; + else { + if (Rd_decode_bit(rdec, &bm_rep2[state]) == 0) + distance = rep2; + else { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } else { + if (Rd_decode_bit(rdec, &bm_len[state][pos_state]) == 0) { + state = St_set_short_rep(state); + LZd_put_byte(d, LZd_peek(d, rep0)); + continue; + } + } + state = St_set_rep(state); + len = min_match_len + Rd_decode_len(rdec, &rep_len_model, pos_state); + } else { + const uint32_t rep0_saved = rep0; + uint32_t dis_slot; + len = min_match_len + Rd_decode_len(rdec, &match_len_model, pos_state); + dis_slot = Rd_decode_tree(rdec, bm_dis_slot[get_len_state(len)], 6); + if (dis_slot < start_dis_model) rep0 = dis_slot; + else { + const uint32_t direct_bits = (dis_slot >> 1) - 1; + rep0 = (2 | (dis_slot & 1)) << direct_bits; + if (dis_slot < end_dis_model) + rep0 += Rd_decode_tree_reversed(rdec, + bm_dis + rep0 - dis_slot - 1, direct_bits); + else { + rep0 += Rd_decode(rdec, direct_bits - dis_align_bits) << dis_align_bits; + rep0 += Rd_decode_tree_reversed(rdec, bm_align, dis_align_bits); + if (rep0 == 0xFFFFFFFFU) { /* marker found */ + rep0 = rep0_saved; + Rd_normalize(rdec); + LZd_flush_data(d); + if (d->write_error) return -1; + if (len == min_match_len && /* End Of Stream marker */ + LZd_verify_trailer(d)) + return 0; + if (len == min_match_len + 1) { /* Sync Flush marker */ + Rd_load(rdec); + continue; + } + return 1; + } + } + } + rep3 = rep2; + rep2 = rep1; + rep1 = rep0_saved; + state = St_set_match(state); + if (rep0 >= d->dictionary_size || + (rep0 >= d->pos && !d->pos_wrapped)) { + LZd_flush_data(d); + return 1; + } + } + LZd_copy_block(d, rep0, len); + } + } + LZd_flush_data(d); + return 1; +} + + +IF_DESKTOP(long long) int FAST_FUNC +unpack_lz_stream(transformer_state_t *xstate) +{ + IF_DESKTOP(long long) int total = 0; + struct Range_decoder rdec; + bool first_member; + const bool magic_skipped = (xstate->signature_skipped != 0); + + if (!global_crc32_table) + global_crc32_table = crc32_filltable(NULL, 0); + + if (!Rd_init(&rdec, xstate->src_fd, magic_skipped)) + return -1; + + for (first_member = true;; first_member = false) { + int tmp = 0; + File_header header; + struct LZ_decoder decoder; + + if (first_member && magic_skipped) { + Fh_set_magic(header); + tmp = 4; + } else { + Rd_reset_member_position(&rdec); + } + while (tmp < Fh_size) + header[tmp++] = Rd_get_byte(&rdec); + if (Rd_finished(&rdec)) { /* End Of File */ + if (first_member) { + bb_error_msg(bb_msg_read_error); + total = -1; + } + break; + } + tmp = Fh_get_dictionary_size(header); + if (!Fh_verify_magic(header) || tmp < min_dictionary_size || + tmp > max_dictionary_size) { + if (!first_member) + break; /* trailing garbage */ + bb_error_msg("invalid magic"); + total = -1; + break; + } + + if (!LZd_init(&decoder, &rdec, tmp, xstate->dst_fd)) { + bb_error_msg(bb_msg_memory_exhausted); + total = -1; + break; + } + tmp = LZd_decode_member(&decoder); + IF_DESKTOP(total += Rd_member_position(&rdec);) + free(decoder.buffer); + if (tmp != 0) { + if (tmp < 0) + bb_perror_msg(bb_msg_write_error); + else + bb_error_msg("corrupted data"); + total = -1; + break; + } + } + free(rdec.buffer); + return total; +} --- archival/libarchive/filter_accept_list_reassign.c +++ archival/libarchive/filter_accept_list_reassign.c @@ -44,6 +44,12 @@ archive_handle->dpkg__action_data_subarchive = get_header_tar_bz2; return EXIT_SUCCESS; } + if (ENABLE_FEATURE_SEAMLESS_LZ + && strcmp(name_ptr, "lz") == 0 + ) { + archive_handle->dpkg__action_data_subarchive = get_header_tar_lz; + return EXIT_SUCCESS; + } if (ENABLE_FEATURE_SEAMLESS_LZMA && strcmp(name_ptr, "lzma") == 0 ) { --- /dev/null +++ archival/libarchive/get_header_tar_lz.c @@ -0,0 +1,21 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "bb_archive.h" + +char FAST_FUNC get_header_tar_lz(archive_handle_t *archive_handle) +{ + /* Can't lseek over pipes */ + archive_handle->seek = seek_by_read; + + fork_transformer_with_sig(archive_handle->src_fd, unpack_lz_stream, "lunzip"); + archive_handle->offset = 0; + while (get_header_tar(archive_handle) == EXIT_SUCCESS) + continue; + + /* Can only do one file at a time */ + return EXIT_FAILURE; +} --- /dev/null +++ archival/libarchive/lzip.h @@ -0,0 +1,250 @@ +/* Lzip - LZMA lossless data compressor + Copyright (C) 2008-2016 Antonio Diaz Diaz. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +typedef int State; + +enum { states = 12 }; + +static inline bool St_is_char(const State st) { return st < 7; } + +static inline State St_set_char(const State st) +{ + static const State next[states] = { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5 }; + return next[st]; +} + +static inline State St_set_match(const State st) +{ + return ((st < 7) ? 7 : 10); +} + +static inline State St_set_rep(const State st) +{ + return ((st < 7) ? 8 : 11); +} + +static inline State St_set_short_rep(const State st) +{ + return ((st < 7) ? 9 : 11); +} + + +enum { + min_dictionary_bits = 12, + min_dictionary_size = 1 << min_dictionary_bits, /* >= modeled_distances */ + max_dictionary_bits = 29, + max_dictionary_size = 1 << max_dictionary_bits, + literal_context_bits = 3, + pos_state_bits = 2, + pos_states = 1 << pos_state_bits, + pos_state_mask = pos_states - 1, + + len_states = 4, + dis_slot_bits = 6, + start_dis_model = 4, + end_dis_model = 14, + modeled_distances = 1 << (end_dis_model / 2), /* 128 */ + dis_align_bits = 4, + dis_align_size = 1 << dis_align_bits, + + len_low_bits = 3, + len_mid_bits = 3, + len_high_bits = 8, + len_low_symbols = 1 << len_low_bits, + len_mid_symbols = 1 << len_mid_bits, + len_high_symbols = 1 << len_high_bits, + max_len_symbols = len_low_symbols + len_mid_symbols + len_high_symbols, + + min_match_len = 2, /* must be 2 */ + max_match_len = min_match_len + max_len_symbols - 1, /* 273 */ + min_match_len_limit = 5, + + lz_num_models = + ((1 << literal_context_bits) * 0x300) + + (2 * states * pos_states) + + (4 * states) + + (len_states * (1 << dis_slot_bits)) + + (modeled_distances - end_dis_model) + + dis_align_size, +}; + +static inline int get_len_state(const int len) +{ + return MIN(len - min_match_len, len_states - 1); +} + +static inline int get_lit_state(const uint8_t prev_byte) +{ + return (prev_byte >> (8 - literal_context_bits)); +} + + +enum { bit_model_move_bits = 5, + bit_model_total_bits = 11, + bit_model_total = 1 << bit_model_total_bits +}; + +typedef int Bit_model; + +static inline void Bm_init(Bit_model * const probability) +{ + *probability = bit_model_total / 2; +} + +static inline void Bm_array_init(Bit_model * const p, const int size) +{ + int i = 0; + while (i < size) + p[i++] = bit_model_total / 2; +} + +struct Len_model { + Bit_model choice1; + Bit_model choice2; + Bit_model bm_low[pos_states][len_low_symbols]; + Bit_model bm_mid[pos_states][len_mid_symbols]; + Bit_model bm_high[len_high_symbols]; +}; + +static inline void Lm_init(struct Len_model * const lm) +{ + Bm_init(&lm->choice1); + Bm_init(&lm->choice2); + Bm_array_init(lm->bm_low[0], pos_states * len_low_symbols); + Bm_array_init(lm->bm_mid[0], pos_states * len_mid_symbols); + Bm_array_init(lm->bm_high, len_high_symbols); +} + + +static inline int real_bits(unsigned value) +{ + int bits = 0; + while(value > 0) { value >>= 1; ++bits; } + return bits; +} + + +static const uint8_t magic_string[4] = { 0x4C, 0x5A, 0x49, 0x50 }; /* "LZIP" */ + +typedef uint8_t File_header[6]; /* 0-3 magic bytes */ + /* 4 version */ + /* 5 coded_dict_size */ +enum { Fh_size = 6 }; + +static inline void Fh_set_magic(File_header data) +{ + memcpy(data, magic_string, 4); + data[4] = 1; +} + +static inline bool Fh_verify_magic(const File_header data) +{ + return (memcmp(data, magic_string, 4) == 0 && data[4] == 1); +} + +static inline unsigned Fh_get_dictionary_size(const File_header data) +{ + unsigned sz = (1 << (data[5] & 0x1F)); + if (sz > min_dictionary_size) + sz -= (sz / 16) * ((data[5] >> 5) & 7); + return sz; +} + +static inline bool Fh_set_dictionary_size(File_header data, const unsigned sz) +{ + if (sz < min_dictionary_size || sz > max_dictionary_size) return false; + data[5] = real_bits(sz - 1); + if (sz > min_dictionary_size) { + const unsigned base_size = 1 << data[5]; + const unsigned fraction = base_size / 16; + unsigned i; + for (i = 7; i >= 1; --i) + if (base_size - (i * fraction) >= sz) { + data[5] |= (i << 5); + break; + } + } + return true; +} + + +typedef uint8_t File_trailer[20]; + /* 0-3 CRC32 of the uncompressed data */ + /* 4-11 size of the uncompressed data */ + /* 12-19 member size including header and trailer */ + +enum { Ft_size = 20 }; + +static inline unsigned Ft_get_data_crc(const File_trailer data) +{ + unsigned tmp = 0; + int i; + for (i = 3; i >= 0; --i) { + tmp <<= 8; + tmp += data[i]; + } + return tmp; +} + +static inline void Ft_set_data_crc(File_trailer data, unsigned crc) +{ + int i; + for (i = 0; i <= 3; ++i) { + data[i] = (uint8_t)crc; + crc >>= 8; + } +} + +static inline unsigned long long Ft_get_data_size(const File_trailer data) +{ + unsigned long long tmp = 0; + int i; + for (i = 11; i >= 4; --i) { + tmp <<= 8; + tmp += data[i]; + } + return tmp; +} + +static inline void Ft_set_data_size(File_trailer data, unsigned long long sz) +{ + int i; + for (i = 4; i <= 11; ++i) { + data[i] = (uint8_t)sz; + sz >>= 8; + } +} + +static inline unsigned long long Ft_get_member_size(const File_trailer data) +{ + unsigned long long tmp = 0; + int i; + for (i = 19; i >= 12; --i) { + tmp <<= 8; + tmp += data[i]; + } + return tmp; +} + +static inline void Ft_set_member_size(File_trailer data, unsigned long long sz) +{ + int i; + for (i = 12; i <= 19; ++i) { + data[i] = (uint8_t)sz; + sz >>= 8; + } +} --- archival/libarchive/open_transformer.c +++ archival/libarchive/open_transformer.c @@ -195,6 +195,17 @@ USE_FOR_NOMMU(xstate->xformer_prog = "bunzip2";) goto found_magic; } + if (ENABLE_FEATURE_SEAMLESS_LZ + && magic.b16[0] == LZIP_MAGIC1 + ) { + xstate->signature_skipped = 4; + xread(fd, magic.b16, sizeof(magic.b16[0])); + if (magic.b16[0] == LZIP_MAGIC2) { + xstate->xformer = unpack_lz_stream; + USE_FOR_NOMMU(xstate->xformer_prog = "lunzip";) + goto found_magic; + } + } if (ENABLE_FEATURE_SEAMLESS_XZ && magic.b16[0] == XZ_MAGIC1 ) { @@ -211,6 +222,7 @@ if (fail_if_not_compressed) bb_error_msg_and_die("no gzip" IF_FEATURE_SEAMLESS_BZ2("/bzip2") + IF_FEATURE_SEAMLESS_LZ("/lzip") IF_FEATURE_SEAMLESS_XZ("/xz") " magic"); --- /dev/null +++ archival/lzip.c @@ -0,0 +1,1434 @@ +/* + * lzip implementation for busybox + * + * Copyright (C) 2012-2016 Antonio Diaz Diaz. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//config:config LZIP +//config: bool "lzip" +//config: default y +//config: help +//config: lzip is a lossless data compressor with a user interface similar to +//config: the one of gzip or bzip2. lzip can compress about as fast as gzip or +//config: compress most files more than bzip2 (depending on compression level). +//config: Decompression speed is intermediate between gzip and bzip2. lzip is +//config: better than gzip and bzip2 from a data recovery perspective. + +//applet:IF_LZIP(APPLET(lzip, BB_DIR_USR_BIN, BB_SUID_DROP)) +//kbuild:lib-$(CONFIG_LZIP) += lzip.o bbunzip.o + +//usage:#define lzip_trivial_usage +//usage: "[-123456789c" +//usage: IF_LUNZIP("d") "fk" +//usage: IF_LUNZIP("t") +//usage: "] [-m MATCH_LENGTH] [-s DICT_SIZE] [FILE]..." +//usage:#define lzip_full_usage "\n\n" +//usage: "Compress FILEs (or stdin) with lzip algorithm\n" +//usage: "\n -1..9 Compression level" +//usage: "\n -c Write to stdout" +//usage: IF_LUNZIP("\n -d Decompress") +//usage: "\n -f Force" +//usage: "\n -k Keep input files" +//usage: "\n -m Match length limit [36]" +//usage: "\n -s Dictionary size limit [8MiB]" +//usage: IF_LUNZIP("\n -t Test compressed file integrity") + + +#include "libbb.h" +#include "bb_archive.h" +#include "libarchive/lzip.h" + + +#if CHAR_BIT != 8 +#error "Environments where CHAR_BIT != 8 are not supported." +#endif + + +static void CRC32_update_byte(uint32_t * crc, const uint8_t byte) +{ + *crc = global_crc32_table[(*crc ^ byte) & 0xFF] ^ (*crc >> 8); +} + + +enum { max_num_trials = 1 << 12, + price_shift_bits = 6 +}; + + +static uint8_t * dis_slots; + +static void Dis_slots_init(void) +{ + int i, size, slot; + dis_slots = xmalloc((1 << 10) * sizeof dis_slots[0]); + + for (slot = 0; slot < 4; ++slot) dis_slots[slot] = slot; + for (i = 4, size = 2, slot = 4; slot < 20; slot += 2) { + memset(&dis_slots[i], slot, size); + memset(&dis_slots[i + size], slot + 1, size); + size <<= 1; + i += size; + } +} + +static uint8_t get_slot(const unsigned dis) +{ + if (dis < (1 << 10)) return dis_slots[dis]; + if (dis < (1 << 19)) return dis_slots[dis>> 9] + 18; + if (dis < (1 << 28)) return dis_slots[dis>>18] + 36; + return dis_slots[dis>>27] + 54; +} + + +static int * prob_prices; + +static void Prob_prices_init(void) +{ + const int num_bits = (bit_model_total_bits - 2); + int i, j = 1, end = 2; + prob_prices = xmalloc((bit_model_total >> 2) * sizeof prob_prices[0]); + + prob_prices[0] = bit_model_total_bits << price_shift_bits; + for (i = num_bits - 1; i >= 0; --i, end <<= 1) { + for (; j < end; ++j) + prob_prices[j] = (i << price_shift_bits) + + (((end - j) << price_shift_bits) >> (num_bits - i - 1)); + } +} + +static inline int get_price(const int probability) +{ + return prob_prices[probability >> 2]; +} + + +static inline int price0(const Bit_model probability) +{ + return get_price(probability); +} + +static inline int price1(const Bit_model probability) +{ + return get_price(bit_model_total - probability); +} + +static int price_bit(const Bit_model bm, const int bit) +{ + if (bit) return price1(bm); + else return price0(bm); +} + + +static int price_symbol(const Bit_model bm[], int symbol, + const int num_bits) +{ + int price = 0; + symbol |= (1 << num_bits); + while (symbol > 1) { + const int bit = symbol & 1; + symbol >>= 1; + price += price_bit(bm[symbol], bit); + } + return price; +} + + +static int price_symbol_reversed(const Bit_model bm[], int symbol, + const int num_bits) +{ + int price = 0; + int model = 1; + int i; + for (i = num_bits; i > 0; --i) { + const int bit = symbol & 1; + price += price_bit(bm[model], bit); + model = (model << 1) | bit; + symbol >>= 1; + } + return price; +} + + +static int price_matched(const Bit_model bm[], int symbol, int match_byte) +{ + int price = 0; + int mask = 0x100; + symbol |= mask; + + do { + int match_bit, bit; + match_byte <<= 1; + match_bit = match_byte & mask; + symbol <<= 1; + bit = symbol & 0x100; + price += price_bit( bm[match_bit+(symbol>>9)+mask], bit ); + mask &= ~(match_byte ^ symbol); /* if( match_bit != bit ) mask = 0; */ + } + while( symbol < 0x10000 ); + return price; +} + + +enum { /* bytes to keep in buffer before dictionary */ + before_size = max_num_trials + 1, + /* bytes to keep in buffer after pos */ + after_size = max_match_len, + num_prev_positions4 = 1 << 20, + num_prev_positions3 = 1 << 18, + num_prev_positions2 = 1 << 16, + num_prev_positions = num_prev_positions4 + num_prev_positions3 + + num_prev_positions2 +}; + +struct Matchfinder { + unsigned long long partial_data_pos; + uint8_t *buffer; /* input buffer */ + int32_t *prev_positions; /* last seen position of key */ + int32_t *prev_pos_tree; /* previous positions of key */ + int match_len_limit; + int buffer_size; + int dictionary_size; /* bytes to keep in buffer before pos */ + int pos; /* current pos in buffer */ + int cyclic_pos; /* current pos in dictionary */ + int stream_pos; /* first byte not yet read from file */ + int pos_limit; /* when reached, a new block must be read */ + int cycles; + bool at_stream_end; /* stream_pos shows real end of file */ +}; + +static bool Mf_read_block(struct Matchfinder *const mf) +{ + if (!mf->at_stream_end && mf->stream_pos < mf->buffer_size) { + const int size = mf->buffer_size - mf->stream_pos; + const int rd = full_read(STDIN_FILENO, + mf->buffer + mf->stream_pos, size); + mf->stream_pos += rd; + if (rd < size) { + mf->at_stream_end = true; + mf->pos_limit = mf->buffer_size; + } + } + return mf->pos < mf->stream_pos; +} + +static void Mf_normalize_pos(struct Matchfinder *const mf) +{ + if (!mf->at_stream_end) { + int i; + const int offset = mf->pos - mf->dictionary_size - before_size; + const int size = mf->stream_pos - offset; + memmove(mf->buffer, mf->buffer + offset, size); + mf->partial_data_pos += offset; + mf->pos -= offset; + mf->stream_pos -= offset; + for (i = 0; i < num_prev_positions; ++i) + if (mf->prev_positions[i] >= 0) + mf->prev_positions[i] -= offset; + for (i = 0; i < 2 * mf->dictionary_size; ++i) + if (mf->prev_pos_tree[i] >= 0) + mf->prev_pos_tree[i] -= offset; + Mf_read_block(mf); + } +} + +static bool Mf_init(struct Matchfinder *const mf, const int dict_size, + const int match_len_limit) +{ + const int buffer_size_limit = (2 * dict_size) + before_size + after_size; + int i; + + mf->partial_data_pos = 0; + mf->match_len_limit = match_len_limit; + mf->prev_positions = (int32_t *) malloc(num_prev_positions * sizeof(int32_t)); + if (!mf->prev_positions) return false; + mf->pos = 0; + mf->cyclic_pos = 0; + mf->stream_pos = 0; + mf->cycles = (match_len_limit < max_match_len) ? + 16 + (match_len_limit / 2) : 256; + mf->at_stream_end = false; + + for (i = 0; i < num_prev_positions; ++i) + mf->prev_positions[i] = -1; + mf->buffer_size = MAX(65536, dict_size); + mf->buffer = (uint8_t *) malloc(mf->buffer_size); + if (!mf->buffer) { + free(mf->prev_positions); + return false; + } + if (Mf_read_block(mf) && !mf->at_stream_end && + mf->buffer_size < buffer_size_limit) { + uint8_t *tmp; + mf->buffer_size = buffer_size_limit; + tmp = (uint8_t *) realloc(mf->buffer, mf->buffer_size); + if (!tmp) { + free(mf->buffer); + free(mf->prev_positions); + return false; + } + mf->buffer = tmp; + Mf_read_block(mf); + } + if (mf->at_stream_end && mf->stream_pos < dict_size) + mf->dictionary_size = MAX(min_dictionary_size, mf->stream_pos); + else + mf->dictionary_size = dict_size; + mf->pos_limit = mf->buffer_size; + if (!mf->at_stream_end) mf->pos_limit -= after_size; + mf->prev_pos_tree = + (int32_t *) malloc(2 * mf->dictionary_size * sizeof(int32_t)); + if (!mf->prev_pos_tree) { + free(mf->buffer); + free(mf->prev_positions); + return false; + } + return true; +} + +static void Mf_free(struct Matchfinder *const mf) +{ + free(mf->prev_pos_tree); + free(mf->buffer); + free(mf->prev_positions); +} + +static inline uint8_t Mf_peek(const struct Matchfinder *const mf, + const int distance) +{ + return mf->buffer[mf->pos-distance]; +} + +static inline int Mf_available_bytes(const struct Matchfinder *const mf) +{ + return mf->stream_pos - mf->pos; +} + +static inline unsigned long long +Mf_data_position(const struct Matchfinder *const mf) +{ + return mf->partial_data_pos + mf->pos; +} + +static inline bool Mf_finished(const struct Matchfinder *const mf) +{ + return mf->at_stream_end && mf->pos >= mf->stream_pos; +} + +static inline const uint8_t * +Mf_ptr_to_current_pos(const struct Matchfinder *const mf) +{ + return mf->buffer + mf->pos; +} + +static int Mf_true_match_len(const struct Matchfinder *const mf, + const int index, const int distance, int len_limit) +{ + const uint8_t *const data = mf->buffer + mf->pos + index; + int i = 0; + if (index + len_limit > Mf_available_bytes(mf)) + len_limit = Mf_available_bytes(mf) - index; + while (i < len_limit && data[i - distance] == data[i]) ++i; + return i; +} + +static void Mf_move_pos(struct Matchfinder *const mf) +{ + if (++mf->cyclic_pos >= mf->dictionary_size) mf->cyclic_pos = 0; + if (++mf->pos >= mf->pos_limit) Mf_normalize_pos(mf); +} + +static int Mf_longest_match_len(struct Matchfinder *const mf, + int *const distances) +{ + int32_t *ptr0 = mf->prev_pos_tree + (mf->cyclic_pos << 1); + int32_t *ptr1 = ptr0 + 1; + int32_t *newptr; + const uint8_t *newdata; + int len = 0, len0 = 0, len1 = 0; + int maxlen = min_match_len - 1; + const int min_pos = (mf->pos >= mf->dictionary_size) ? + (mf->pos - mf->dictionary_size + 1) : 0; + const uint8_t *const data = mf->buffer + mf->pos; + int count, delta, key2, key3, key4, newpos, tmp; + int len_limit = mf->match_len_limit; + + if (len_limit > Mf_available_bytes(mf)) { + len_limit = Mf_available_bytes(mf); + if (len_limit < 4) return 0; + } + + key2 = num_prev_positions4 + num_prev_positions3 + + (((int) data[0] << 8) | data[1]); + tmp = global_crc32_table[data[0]] ^ data[1] ^ ((uint32_t) data[2] << 8); + key3 = num_prev_positions4 + (int) (tmp & (num_prev_positions3 - 1)); + key4 = (int) ((tmp ^ (global_crc32_table[data[3]] << 5)) & + (num_prev_positions4 - 1)); + + if (distances) { + int np = mf->prev_positions[key2]; + if (np >= min_pos) { + distances[2] = mf->pos - np - 1; + maxlen = 2; + } else + distances[2] = 0x7FFFFFFF; + np = mf->prev_positions[key3]; + if (np >= min_pos && mf->buffer[np] == data[0]) { + distances[3] = mf->pos - np - 1; + maxlen = 3; + } else + distances[3] = 0x7FFFFFFF; + distances[4] = 0x7FFFFFFF; + } + + mf->prev_positions[key2] = mf->pos; + mf->prev_positions[key3] = mf->pos; + newpos = mf->prev_positions[key4]; + mf->prev_positions[key4] = mf->pos; + + for (count = mf->cycles;;) { + if (newpos < min_pos || --count < 0) { + *ptr0 = *ptr1 = -1; + break; + } + newdata = mf->buffer + newpos; + while (len < len_limit && newdata[len] == data[len]) ++len; + + delta = mf->pos - newpos; + if (distances) + while (maxlen < len) + distances[++maxlen] = delta - 1; + + newptr = mf->prev_pos_tree + + ((mf->cyclic_pos - delta + + ((mf->cyclic_pos >= delta) ? 0 : mf->dictionary_size)) << 1); + + if (len < len_limit) { + if (newdata[len] < data[len]) { + *ptr0 = newpos; + ptr0 = newptr + 1; + newpos = *ptr0; + len0 = len; + if (len1 < len) len = len1; + } else { + *ptr1 = newpos; + ptr1 = newptr; + newpos = *ptr1; + len1 = len; + if (len0 < len) len = len0; + } + } else { + *ptr0 = newptr[0]; + *ptr1 = newptr[1]; + break; + } + } + if (distances) { + if (distances[3] > distances[4]) + distances[3] = distances[4]; + if (distances[2] > distances[3]) + distances[2] = distances[3]; + } + return maxlen; +} + + +enum { re_buffer_size = 16384 }; + +struct Range_encoder { + uint64_t low; + unsigned long long partial_member_pos; + uint8_t *buffer; /* output buffer */ + int pos; /* current pos in buffer */ + uint32_t range; + unsigned ff_count; + uint8_t cache; + bool write_error; +}; + +static void Re_flush_data(struct Range_encoder *const renc) +{ + if (renc->pos > 0) { + if (full_write(STDOUT_FILENO, renc->buffer, renc->pos) != renc->pos) + renc->write_error = true; + renc->partial_member_pos += renc->pos; + renc->pos = 0; + } +} + +static void Re_put_byte(struct Range_encoder *const renc, const uint8_t b) +{ + renc->buffer[renc->pos] = b; + if (++renc->pos >= re_buffer_size) Re_flush_data(renc); +} + +static void Re_shift_low(struct Range_encoder *const renc) +{ + const bool carry = (renc->low > 0xFFFFFFFFU); + if (carry || renc->low < 0xFF000000U) { + Re_put_byte(renc, renc->cache + carry); + for (; renc->ff_count > 0; --renc->ff_count) + Re_put_byte(renc, 0xFF + carry); + renc->cache = renc->low >> 24; + } else + ++renc->ff_count; + renc->low = (renc->low & 0x00FFFFFFU) << 8; +} + +static bool Re_init(struct Range_encoder *const renc) +{ + renc->low = 0; + renc->partial_member_pos = 0; + renc->buffer = (uint8_t *) malloc(re_buffer_size); + if (!renc->buffer) return false; + renc->pos = 0; + renc->range = 0xFFFFFFFFU; + renc->ff_count = 0; + renc->cache = 0; + renc->write_error = false; + return true; +} + +static inline void Re_free(struct Range_encoder *const renc) +{ + free(renc->buffer); +} + +static inline unsigned long long +Re_member_position(const struct Range_encoder *const renc) +{ + return renc->partial_member_pos + renc->pos + renc->ff_count; +} + +static void Re_flush(struct Range_encoder *const renc) +{ + int i; + for (i = 0; i < 5; ++i) Re_shift_low(renc); +} + +static void Re_encode(struct Range_encoder *const renc, + const int symbol, const int num_bits) +{ + int i; + for (i = num_bits - 1; i >= 0; --i) { + renc->range >>= 1; + if ((symbol >> i) & 1) renc->low += renc->range; + if (renc->range <= 0x00FFFFFFU) { + renc->range <<= 8; + Re_shift_low(renc); + } + } +} + +static void Re_encode_bit(struct Range_encoder *const renc, + Bit_model * const probability, const int bit) +{ + const uint32_t bound = (renc->range >> bit_model_total_bits) * *probability; + if (!bit) { + renc->range = bound; + *probability += (bit_model_total - *probability) >> bit_model_move_bits; + } else { + renc->low += bound; + renc->range -= bound; + *probability -= *probability >> bit_model_move_bits; + } + if (renc->range <= 0x00FFFFFFU) { + renc->range <<= 8; + Re_shift_low(renc); + } +} + +static void Re_encode_tree(struct Range_encoder *const renc, + Bit_model bm[], const int symbol, + const int num_bits) +{ + int mask = (1 << (num_bits - 1)); + int model = 1; + int i; + for (i = num_bits; i > 0; --i, mask >>= 1) { + const int bit = (symbol & mask); + Re_encode_bit(renc, &bm[model], bit); + model <<= 1; + if (bit) model |= 1; + } +} + +static void Re_encode_tree_reversed(struct Range_encoder *const renc, + Bit_model bm[], int symbol, const int num_bits) +{ + int model = 1; + int i; + for (i = num_bits; i > 0; --i) { + const int bit = symbol & 1; + Re_encode_bit(renc, &bm[model], bit); + model = (model << 1) | bit; + symbol >>= 1; + } +} + +static void Re_encode_matched(struct Range_encoder *const renc, + Bit_model bm[], int symbol, int match_byte) +{ + int mask = 0x100; + symbol |= mask; + + do { + int match_bit, bit; + match_byte <<= 1; + match_bit = match_byte & mask; + symbol <<= 1; + bit = symbol & 0x100; + Re_encode_bit( renc, &bm[match_bit+(symbol>>9)+mask], bit ); + mask &= ~(match_byte ^ symbol); /* if( match_bit != bit ) mask = 0; */ + } + while( symbol < 0x10000 ); +} + +static void Re_encode_len( struct Range_encoder * const renc, + struct Len_model * const lm, + int symbol, const int pos_state ) +{ + bool bit = ( ( symbol -= min_match_len ) >= len_low_symbols ); + Re_encode_bit( renc, &lm->choice1, bit ); + if( !bit ) + Re_encode_tree( renc, lm->bm_low[pos_state], symbol, len_low_bits ); + else { + bit = ( symbol >= len_low_symbols + len_mid_symbols ); + Re_encode_bit( renc, &lm->choice2, bit ); + if( !bit ) + Re_encode_tree( renc, lm->bm_mid[pos_state], + symbol - len_low_symbols, len_mid_bits ); + else + Re_encode_tree( renc, lm->bm_high, + symbol - len_low_symbols - len_mid_symbols, len_high_bits ); + } +} + + +struct Len_encoder { + struct Len_model lm; + int len_symbols; + int prices[pos_states][max_len_symbols]; + int counters[pos_states]; +}; + +static void Lee_update_prices(struct Len_encoder *const le, const int pos_state) +{ + int *const pps = le->prices[pos_state]; + int tmp = price0(le->lm.choice1); + int len = 0; + + for (; len < len_low_symbols && len < le->len_symbols; ++len) + pps[len] = tmp + + price_symbol(le->lm.bm_low[pos_state], len, len_low_bits); + tmp = price1(le->lm.choice1); + for (; len < len_low_symbols + len_mid_symbols && len < le->len_symbols; ++len) + pps[len] = tmp + price0(le->lm.choice2) + + price_symbol(le->lm.bm_mid[pos_state], + len - len_low_symbols, len_mid_bits); + for (; len < le->len_symbols; ++len) + /* using 4 slots per value makes "Lee_price" faster */ + le->prices[3][len] = le->prices[2][len] = + le->prices[1][len] = le->prices[0][len] = + tmp + price1(le->lm.choice2) + + price_symbol(le->lm.bm_high, + len - len_low_symbols - len_mid_symbols, + len_high_bits); + le->counters[pos_state] = le->len_symbols; +} + +static void Lee_init(struct Len_encoder *const le, const int len_limit) +{ + int i; + Lm_init(&le->lm); + le->len_symbols = len_limit + 1 - min_match_len; + for (i = 0; i < pos_states; ++i) Lee_update_prices(le, i); +} + +static void Lee_encode(struct Len_encoder *const le, + struct Range_encoder *const renc, + int symbol, const int pos_state) +{ + Re_encode_len(renc, &le->lm, symbol, pos_state); + if (--le->counters[pos_state] <= 0) + Lee_update_prices(le, pos_state); +} + +static int Lee_price(const struct Len_encoder *const le, + const int symbol, const int pos_state) +{ + return le->prices[pos_state][symbol - min_match_len]; +} + + +enum { infinite_price = 0x0FFFFFFF, + num_rep_distances = 4 /* must be 4 */ +}; + +struct Trial { + State state; + int price; /* dual use var; cumulative price, match length */ + int dis; /* rep index or match distance. (-1 for literal) */ + int prev_index; /* index of prev trial in trials[] */ + int reps[num_rep_distances]; +}; + +static void Tr_update(struct Trial *const trial, const int pr, + const int distance, const int p_i) +{ + if (pr < trial->price) { + trial->price = pr; + trial->dis = distance; + trial->prev_index = p_i; + } +} + + +struct LZ_encoder { + int longest_match_found; + uint32_t crc; + + Bit_model bm_literal[1<align_prices[i] = + price_symbol_reversed(e->bm_align, i, dis_align_bits); + e->align_price_count = dis_align_size; +} + +static bool LZe_init(struct LZ_encoder *const e, + struct Matchfinder *const mf, const File_header header) +{ + int i; + e->longest_match_found = 0; + e->crc = 0xFFFFFFFFU; + Bm_array_init(&e->bm_literal[0][0], lz_num_models); + e->matchfinder = mf; + if (!Re_init(&e->renc)) return false; + Lee_init(&e->match_len_encoder, e->matchfinder->match_len_limit); + Lee_init(&e->rep_len_encoder, e->matchfinder->match_len_limit); + LZe_fill_align_prices(e); + e->num_dis_slots = 2 * real_bits(e->matchfinder->dictionary_size - 1); + for (i = 0; i < Fh_size; ++i) + Re_put_byte(&e->renc, header[i]); + return true; +} + +static inline void LZe_free(struct LZ_encoder *const e) +{ + Re_free(&e->renc); +} + +static inline unsigned LZe_crc(const struct LZ_encoder *const e) +{ + return e->crc ^ 0xFFFFFFFFU; +} + + /* move-to-front dis in/into reps if( dis > 0 ) */ +static void mtf_reps(const int dis, int reps[num_rep_distances]) +{ + int i; + if (dis >= num_rep_distances) { + for (i = num_rep_distances - 1; i > 0; --i) + reps[i] = reps[i - 1]; + reps[0] = dis - num_rep_distances; + } else if (dis > 0) { + const int distance = reps[dis]; + for (i = dis; i > 0; --i) + reps[i] = reps[i - 1]; + reps[0] = distance; + } +} + +static int LZe_price_shortrep(const struct LZ_encoder *const e, + const State state, const int pos_state) +{ + return price0(e->bm_rep0[state]) + price0(e->bm_len[state][pos_state]); +} + +static int LZe_price_rep(const struct LZ_encoder *const e, const int rep, + const State state, const int pos_state) +{ + int price; + if (rep == 0) + return price0(e->bm_rep0[state]) + + price1(e->bm_len[state][pos_state]); + price = price1(e->bm_rep0[state]); + if (rep == 1) + price += price0(e->bm_rep1[state]); + else { + price += price1(e->bm_rep1[state]); + price += price_bit(e->bm_rep2[state], rep - 2); + } + return price; +} + +static int LZe_price_dis(const struct LZ_encoder *const e, + const int dis, const int len_state) +{ + if (dis < modeled_distances) + return e->dis_prices[len_state][dis]; + else + return e->dis_slot_prices[len_state][get_slot(dis)] + + e->align_prices[dis & (dis_align_size - 1)]; +} + +static int LZe_price_pair(const struct LZ_encoder *const e, + const int dis, const int len, + const int pos_state) +{ + if (len <= min_match_len && dis >= modeled_distances) + return infinite_price; + return Lee_price(&e->match_len_encoder, len, pos_state) + + LZe_price_dis(e, dis, get_len_state(len)); +} + +static int LZe_price_literal(const struct LZ_encoder *const e, + uint8_t prev_byte, uint8_t symbol) +{ + return price_symbol(e->bm_literal[get_lit_state(prev_byte)], symbol, 8); +} + +static int LZe_price_matched(const struct LZ_encoder *const e, + uint8_t prev_byte, uint8_t symbol, + uint8_t match_byte) +{ + return price_matched(e->bm_literal[get_lit_state(prev_byte)], symbol, + match_byte); +} + +static void LZe_encode_literal(struct LZ_encoder *const e, + uint8_t prev_byte, uint8_t symbol) +{ + Re_encode_tree(&e->renc, + e->bm_literal[get_lit_state(prev_byte)], symbol, 8); +} + +static void LZe_encode_matched(struct LZ_encoder *const e, + uint8_t prev_byte, uint8_t symbol, + uint8_t match_byte) +{ + Re_encode_matched(&e->renc, e->bm_literal[get_lit_state(prev_byte)], + symbol, match_byte); +} + +static void LZe_encode_pair(struct LZ_encoder *const e, + const unsigned dis, const int len, + const int pos_state) +{ + const int dis_slot = get_slot(dis); + Lee_encode(&e->match_len_encoder, &e->renc, len, pos_state); + Re_encode_tree(&e->renc, e->bm_dis_slot[get_len_state(len)], dis_slot, + dis_slot_bits); + + if (dis_slot >= start_dis_model) { + const int direct_bits = (dis_slot >> 1) - 1; + const unsigned base = (2 | (dis_slot & 1)) << direct_bits; + const unsigned direct_dis = dis - base; + + if (dis_slot < end_dis_model) + Re_encode_tree_reversed(&e->renc, + e->bm_dis + base - dis_slot - 1, + direct_dis, direct_bits); + else { + Re_encode(&e->renc, direct_dis >> dis_align_bits, + direct_bits - dis_align_bits); + Re_encode_tree_reversed(&e->renc, e->bm_align, + direct_dis, dis_align_bits); + if (--e->align_price_count <= 0) + LZe_fill_align_prices(e); + } + } +} + +static int LZe_read_match_distances(struct LZ_encoder *const e) +{ + int len = Mf_longest_match_len(e->matchfinder, e->match_distances); + if (len == e->matchfinder->match_len_limit && len < max_match_len) + len += Mf_true_match_len(e->matchfinder, len, + e->match_distances[len] + 1, + max_match_len - len); + return len; +} + +static void LZe_move_pos(struct LZ_encoder *const e, int n) +{ + while (true) { + Mf_move_pos(e->matchfinder); + if( --n <= 0 ) break; + Mf_longest_match_len(e->matchfinder, 0); + } +} + +static void LZe_backward(struct LZ_encoder *const e, int cur) +{ + int *const dis = &e->trials[cur].dis; + while (cur > 0) { + const int prev_index = e->trials[cur].prev_index; + struct Trial *const prev_trial = &e->trials[prev_index]; + prev_trial->price = cur - prev_index; /* len */ + cur = *dis; + *dis = prev_trial->dis; + prev_trial->dis = cur; + cur = prev_index; + } +} + + /* End Of Stream mark => (dis == 0xFFFFFFFFU, len == min_match_len) */ +static void LZe_full_flush(struct LZ_encoder *const e, const State state) +{ + int i; + const int pos_state = Mf_data_position(e->matchfinder) & pos_state_mask; + File_trailer trailer; + Re_encode_bit(&e->renc, &e->bm_match[state][pos_state], 1); + Re_encode_bit(&e->renc, &e->bm_rep[state], 0); + LZe_encode_pair(e, 0xFFFFFFFFU, min_match_len, pos_state); + Re_flush(&e->renc); + Ft_set_data_crc(trailer, LZe_crc(e)); + Ft_set_data_size(trailer, Mf_data_position(e->matchfinder)); + Ft_set_member_size(trailer, Re_member_position(&e->renc) + Ft_size); + for (i = 0; i < Ft_size; ++i) + Re_put_byte(&e->renc, trailer[i]); + Re_flush_data(&e->renc); +} + + +static void LZe_update_distance_prices(struct LZ_encoder *const e) +{ + int dis, len_state; + for (dis = start_dis_model; dis < modeled_distances; ++dis) { + const int dis_slot = dis_slots[dis]; + const int direct_bits = (dis_slot >> 1) - 1; + const int base = (2 | (dis_slot & 1)) << direct_bits; + const int price = + price_symbol_reversed(e->bm_dis + base - dis_slot - 1, + dis - base, direct_bits); + for (len_state = 0; len_state < len_states; ++len_state) + e->dis_prices[len_state][dis] = price; + } + + for (len_state = 0; len_state < len_states; ++len_state) { + int *const dsp = e->dis_slot_prices[len_state]; + int *const dp = e->dis_prices[len_state]; + const Bit_model *const bmds = e->bm_dis_slot[len_state]; + int slot = 0; + for (; slot < end_dis_model; ++slot) + dsp[slot] = price_symbol(bmds, slot, dis_slot_bits); + for (; slot < e->num_dis_slots; ++slot) + dsp[slot] = price_symbol(bmds, slot, dis_slot_bits) + + ((((slot >> 1) - 1) - dis_align_bits) << price_shift_bits); + + for (dis = 0; dis < start_dis_model; ++dis) + dp[dis] = dsp[dis]; + for (; dis < modeled_distances; ++dis) + dp[dis] += dsp[dis_slots[dis]]; + } +} + + +/* Returns the number of bytes advanced (ahead). + trials[0]..trials[ahead-1] contain the steps to encode. + ( trials[0].dis == -1 && trials[0].price == 1 ) means literal. + A match/rep longer or equal than match_len_limit finishes the sequence. +*/ +static int LZe_sequence_optimizer(struct LZ_encoder *const e, + const int reps[num_rep_distances], + const State state) +{ + int main_len, i, rep, cur = 0, num_trials; + int replens[num_rep_distances]; + int rep_index = 0; + + if (e->longest_match_found > 0) { /* from previous call */ + main_len = e->longest_match_found; + e->longest_match_found = 0; + } else + main_len = LZe_read_match_distances(e); + + for (i = 0; i < num_rep_distances; ++i) { + replens[i] = Mf_true_match_len(e->matchfinder, 0, reps[i] + 1, + max_match_len); + if (replens[i] > replens[rep_index]) rep_index = i; + } + if (replens[rep_index] >= e->matchfinder->match_len_limit) { + e->trials[0].dis = rep_index; + e->trials[0].price = replens[rep_index]; + LZe_move_pos(e, replens[rep_index]); + return replens[rep_index]; + } + + if (main_len >= e->matchfinder->match_len_limit) { + e->trials[0].dis = + e->match_distances[e->matchfinder->match_len_limit] + + num_rep_distances; + e->trials[0].price = main_len; + LZe_move_pos(e, main_len); + return main_len; + } + + { + const int pos_state = Mf_data_position(e->matchfinder) & pos_state_mask; + const int match_price = price1(e->bm_match[state][pos_state]); + const int rep_match_price = match_price + price1(e->bm_rep[state]); + const uint8_t prev_byte = Mf_peek(e->matchfinder, 1); + const uint8_t cur_byte = Mf_peek(e->matchfinder, 0); + const uint8_t match_byte = Mf_peek(e->matchfinder, reps[0] + 1); + + e->trials[0].state = state; + for (i = 0; i < num_rep_distances; ++i) + e->trials[0].reps[i] = reps[i]; + e->trials[1].dis = -1; /* literal */ + e->trials[1].prev_index = 0; + e->trials[1].price = price0(e->bm_match[state][pos_state]); + if (St_is_char(state)) + e->trials[1].price += + LZe_price_literal(e, prev_byte, cur_byte); + else + e->trials[1].price += + LZe_price_matched(e, prev_byte, cur_byte, match_byte); + + if (match_byte == cur_byte) + Tr_update(&e->trials[1], rep_match_price + + LZe_price_shortrep(e, state, pos_state), 0, 0); + + if (main_len < min_match_len) { + e->trials[0].dis = e->trials[1].dis; + e->trials[0].price = 1; + Mf_move_pos(e->matchfinder); + return 1; + } + + if (main_len <= replens[rep_index]) { + int len; + + main_len = replens[rep_index]; + for (len = min_match_len; len <= main_len; ++len) + e->trials[len].price = infinite_price; + } else { + int len; + const int normal_match_price = + match_price + price0(e->bm_rep[state]); + for (len = min_match_len; len <= main_len; ++len) { + e->trials[len].dis = + e->match_distances[len] + num_rep_distances; + e->trials[len].prev_index = 0; + e->trials[len].price = normal_match_price + + LZe_price_pair(e, e->match_distances[len], + len, pos_state); + } + } + + for (rep = 0; rep < num_rep_distances; ++rep) { + const int price = rep_match_price + + LZe_price_rep(e, rep, state, pos_state); + int len; + for (len = min_match_len; len <= replens[rep]; ++len) + Tr_update(&e->trials[len], price + + Lee_price(&e->rep_len_encoder, len, pos_state), + rep, 0); + } + } + + num_trials = main_len; + + while (true) { /* price optimization loop */ + struct Trial *cur_trial, *next_trial; + int newlen, pos_state, prev_index, len_limit; + int next_price, match_price, rep_match_price; + uint8_t prev_byte, cur_byte, match_byte; + + Mf_move_pos(e->matchfinder); + if (++cur >= num_trials) { /* no more initialized trials */ + LZe_backward(e, cur); + return cur; + } + newlen = LZe_read_match_distances(e); + if (newlen >= e->matchfinder->match_len_limit) { + e->longest_match_found = newlen; + LZe_backward(e, cur); + return cur; + } + + /* give final values to current trial */ + cur_trial = &e->trials[cur]; + prev_index = cur_trial->prev_index; + cur_trial->state = e->trials[prev_index].state; + + for (i = 0; i < num_rep_distances; ++i) + cur_trial->reps[i] = e->trials[prev_index].reps[i]; + + if (prev_index == cur - 1) { + if (cur_trial->dis == 0) + cur_trial->state = St_set_short_rep(cur_trial->state); + else + cur_trial->state = St_set_char(cur_trial->state); + } else { + if (cur_trial->dis < num_rep_distances) + cur_trial->state = St_set_rep(cur_trial->state); + else + cur_trial->state = St_set_match(cur_trial->state); + mtf_reps(cur_trial->dis, cur_trial->reps); + } + + pos_state = Mf_data_position(e->matchfinder) & pos_state_mask; + prev_byte = Mf_peek(e->matchfinder, 1); + cur_byte = Mf_peek(e->matchfinder, 0); + match_byte = Mf_peek(e->matchfinder, cur_trial->reps[0] + 1); + + next_price = cur_trial->price + + price0(e->bm_match[cur_trial->state][pos_state]); + if (St_is_char(cur_trial->state)) + next_price += LZe_price_literal(e, prev_byte, cur_byte); + else + next_price += LZe_price_matched(e, prev_byte, cur_byte, + match_byte); + /* try last updates to next trial */ + next_trial = &e->trials[cur + 1]; + + Tr_update(next_trial, next_price, -1, cur); /* literal */ + + match_price = cur_trial->price + + price1(e->bm_match[cur_trial->state][pos_state]); + rep_match_price = match_price + price1(e->bm_rep[cur_trial->state]); + + if (match_byte == cur_byte && next_trial->dis != 0) + Tr_update(next_trial, rep_match_price + + LZe_price_shortrep(e, cur_trial->state, + pos_state), 0, cur); + + len_limit = MIN(MIN(max_num_trials - 1 - cur, + Mf_available_bytes(e->matchfinder)), + e->matchfinder->match_len_limit); + if (len_limit < min_match_len) continue; + + for (rep = 0; rep < num_rep_distances; ++rep) { + const int dis = cur_trial->reps[rep] + 1; + int len = 0; + const uint8_t *const data = + Mf_ptr_to_current_pos(e->matchfinder); + while (len < len_limit && data[len] == data[len - dis]) + ++len; + if (len >= min_match_len) { + const int price = rep_match_price + + LZe_price_rep(e, rep, cur_trial->state, pos_state); + while (num_trials < cur + len) + e->trials[++num_trials].price = infinite_price; + for (; len >= min_match_len; --len) + Tr_update(&e->trials[cur + len], price + + Lee_price(&e->rep_len_encoder, len, + pos_state), rep, cur); + } + } + + if (newlen <= len_limit && + (newlen > min_match_len || + (newlen == min_match_len && + e->match_distances[min_match_len] < modeled_distances))) { + const int normal_match_price = match_price + + price0(e->bm_rep[cur_trial->state]); + int len; + int dis = e->match_distances[min_match_len]; + int len_state = get_len_state(min_match_len); + int dis_price = infinite_price; + + while (num_trials < cur + newlen) + e->trials[++num_trials].price = infinite_price; + + if (dis < modeled_distances) + Tr_update(&e->trials[cur + min_match_len], + normal_match_price + + e->dis_prices[len_state][dis] + + Lee_price(&e->match_len_encoder, + min_match_len, pos_state), + dis + num_rep_distances, cur); + + for (len = min_match_len + 1; len <= newlen; ++len) { + if (dis != e->match_distances[len] || + len_state < len_states - 1) { + dis = e->match_distances[len]; + len_state = get_len_state(len); + dis_price = LZe_price_dis(e, dis, len_state); + } + Tr_update(&e->trials[cur + len], + normal_match_price + dis_price + + Lee_price(&e->match_len_encoder, len, pos_state), + dis + num_rep_distances, cur); + } + } + } +} + + +static bool LZe_encode_member(struct LZ_encoder *const e) +{ + const int dis_price_count = + (e->matchfinder->match_len_limit > 12) ? 512 : 2048; + int dis_price_counter = 0; + int ahead, i; + int reps[num_rep_distances]; + State state = 0; + for (i = 0; i < num_rep_distances; ++i) reps[i] = 0; + + if (!Mf_finished(e->matchfinder)) { /* encode first byte */ + const uint8_t prev_byte = 0; + const uint8_t cur_byte = Mf_peek(e->matchfinder, 0); + Re_encode_bit(&e->renc, &e->bm_match[state][0], 0); + LZe_encode_literal(e, prev_byte, cur_byte); + CRC32_update_byte(&e->crc, cur_byte); + Mf_longest_match_len(e->matchfinder, 0); + Mf_move_pos(e->matchfinder); + } + + while (!Mf_finished(e->matchfinder)) { + if (dis_price_counter <= 0) { + LZe_update_distance_prices(e); + dis_price_counter = dis_price_count; + } + + ahead = LZe_sequence_optimizer(e, reps, state); + dis_price_counter -= ahead; + + for (i = 0; ahead > 0;) { + const int pos_state = + (Mf_data_position(e->matchfinder) - ahead) & pos_state_mask; + const int dis = e->trials[i].dis; + const int len = e->trials[i].price; + + bool bit = (dis < 0 && len == 1); + Re_encode_bit(&e->renc, &e->bm_match[state][pos_state], !bit); + if (bit) { /* literal byte */ + const uint8_t prev_byte = Mf_peek(e->matchfinder, ahead + 1); + const uint8_t cur_byte = Mf_peek(e->matchfinder, ahead); + CRC32_update_byte(&e->crc, cur_byte); + if (St_is_char(state)) + LZe_encode_literal(e, prev_byte, cur_byte); + else { + const uint8_t match_byte = + Mf_peek(e->matchfinder, ahead + reps[0] + 1); + LZe_encode_matched(e, prev_byte, cur_byte, match_byte); + } + state = St_set_char(state); + } else { /* match or repeated match */ + + e->crc = crc32_block_endian0(e->crc, + Mf_ptr_to_current_pos(e->matchfinder) - ahead, + len, global_crc32_table); + mtf_reps(dis, reps); + bit = (dis < num_rep_distances); + Re_encode_bit(&e->renc, &e->bm_rep[state], bit); + if (bit) { /* repeated match */ + bit = (dis == 0); + Re_encode_bit(&e->renc, &e->bm_rep0[state], !bit); + if (bit) + Re_encode_bit(&e->renc, &e->bm_len[state][pos_state], len > 1); + else { + Re_encode_bit(&e->renc, &e->bm_rep1[state], dis > 1); + if (dis > 1) + Re_encode_bit(&e->renc, &e->bm_rep2[state], dis > 2); + } + if (len == 1) + state = St_set_short_rep(state); + else { + Lee_encode(&e->rep_len_encoder, + &e->renc, len, pos_state); + state = St_set_rep(state); + } + } else { /* match */ + LZe_encode_pair(e, dis - num_rep_distances, len, pos_state); + state = St_set_match(state); + } + } + ahead -= len; + i += len; + } + } + LZe_full_flush(e, state); + return !e->renc.write_error; +} + + +struct Lzma_options { + int dictionary_size; /* 4KiB..512MiB */ + int match_len_limit; /* 5..273 */ +} encoder_options; + + +static int getnum(const char *const ptr, const int llimit, const int ulimit) +{ + long result; + char *tail; + errno = 0; + result = strtol(ptr, &tail, 0); + if (tail == ptr || errno) + goto error; + if (tail[0]) { + int factor = (tail[1] == 'i') ? 1024 : 1000; + int exponent = 0, i; + + switch (tail[0]) { + case 'M': + exponent = 2; + break; + case 'K': + if (factor == 1024) { + exponent = 1; + break; + } + goto error; + case 'k': + if (factor == 1000) { + exponent = 1; + break; + } + default: + goto error; + } + for (i = 0; i < exponent; ++i) { + if (LONG_MAX / factor >= labs(result)) + result *= factor; + else + goto error; + } + } + if (result >= llimit && result <= ulimit) + return result; + error: + bb_error_msg_and_die("invalid number"); +} + + +static int get_dict_size(const char *const arg) +{ + char *tail; + long bits = strtol(arg, &tail, 0); + if (bits >= min_dictionary_bits && + bits <= max_dictionary_bits && *tail == 0) + return (1 << bits); + return getnum(arg, min_dictionary_size, max_dictionary_size); +} + + +static IF_DESKTOP(long long) int FAST_FUNC pack_lzip(transformer_state_t *xstate UNUSED_PARAM) +{ + int retval = 0; + File_header header; + struct Matchfinder matchfinder; + struct LZ_encoder * encoder; + + Fh_set_magic(header); + if (!Fh_set_dictionary_size(header, encoder_options.dictionary_size) || + encoder_options.match_len_limit < min_match_len_limit || + encoder_options.match_len_limit > max_match_len) + bb_error_msg_and_die("internal error"); + + if (!Mf_init(&matchfinder, Fh_get_dictionary_size(header), + encoder_options.match_len_limit)) { + bb_error_msg(bb_msg_memory_exhausted); + return -1; + } + Fh_set_dictionary_size(header, matchfinder.dictionary_size); + + encoder = malloc(sizeof(struct LZ_encoder)); + if (!encoder || !LZe_init(encoder, &matchfinder, header)) { + bb_error_msg(bb_msg_memory_exhausted); + retval = -1; + } else { + if (!LZe_encode_member(encoder)) { + bb_perror_msg(bb_msg_write_error); + retval = -1; + } + LZe_free(encoder); + } + free(encoder); + Mf_free(&matchfinder); + return retval; +} + + +int lzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int lzip_main(int argc UNUSED_PARAM, char **argv) +{ + /* Mapping from gzip/bzip2 style 1..9 compression modes + to the corresponding LZMA compression modes. */ + const struct Lzma_options option_mapping[] = { + {1 << 20, 5}, /* -0 */ + {1 << 20, 5}, /* -1 */ + {3 << 19, 6}, /* -2 */ + {1 << 21, 8}, /* -3 */ + {3 << 20, 12}, /* -4 */ + {1 << 22, 20}, /* -5 */ + {1 << 23, 36}, /* -6 */ + {1 << 24, 68}, /* -7 */ + {3 << 23, 132}, /* -8 */ + {1 << 25, 273} /* -9 */ + }; + int i; + char *m_arg; + char *s_arg; + /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */ + uint32_t flags = getopt32(argv, "cfkvqdt0123456789Fm:s:", &m_arg, &s_arg); + + if (flags & 0x60) { // -d and/or -t +#if ENABLE_LUNZIP /* lunzip_main may not be visible... */ + return lunzip_main(argc, argv); +#else + bb_error_msg("decompression is disabled"); + return 1; +#endif + } + flags >>= 7; /* drop "cfkvqdt" bits */ + + encoder_options = option_mapping[6]; /* default = "-6" */ + + for (i = 9; i >= 7; --i) + if (flags & (1 << i)) + encoder_options = option_mapping[i]; + for (i = 0; i <= 6; ++i) + if (flags & (1 << i)) + encoder_options = option_mapping[i]; + if (flags & (1 << 11)) /* -m */ + encoder_options.match_len_limit = + getnum(m_arg, min_match_len_limit, max_match_len); + if (flags & (1 << 12)) /* -s */ + encoder_options.dictionary_size = get_dict_size(s_arg); + /* end process options */ + + argv += optind; + + if (!global_crc32_table) + global_crc32_table = crc32_filltable(NULL, 0); + if (!dis_slots) { + Dis_slots_init(); + Prob_prices_init(); + } + + return bbunpack(argv, pack_lzip, append_ext, "lz"); +} --- archival/tar.c +++ archival/tar.c @@ -62,7 +62,7 @@ //config:config FEATURE_TAR_AUTODETECT //config: bool "Autodetect compressed tarballs" //config: default y -//config: depends on TAR && (FEATURE_SEAMLESS_Z || FEATURE_SEAMLESS_GZ || FEATURE_SEAMLESS_BZ2 || FEATURE_SEAMLESS_LZMA || FEATURE_SEAMLESS_XZ) +//config: depends on TAR && (FEATURE_SEAMLESS_Z || FEATURE_SEAMLESS_GZ || FEATURE_SEAMLESS_BZ2 || FEATURE_SEAMLESS_LZ || FEATURE_SEAMLESS_LZMA || FEATURE_SEAMLESS_XZ) //config: help //config: With this option tar can automatically detect compressed //config: tarballs. Currently it works only on files (not pipes etc). @@ -771,6 +771,7 @@ //usage: IF_FEATURE_SEAMLESS_GZ("z") //usage: IF_FEATURE_SEAMLESS_XZ("J") //usage: IF_FEATURE_SEAMLESS_BZ2("j") +//usage: IF_FEATURE_SEAMLESS_LZ("y") //usage: IF_FEATURE_SEAMLESS_LZMA("a") //usage: IF_FEATURE_TAR_CREATE("h") //usage: IF_FEATURE_TAR_NOPRESERVE_TIME("m") @@ -802,6 +803,9 @@ //usage: IF_FEATURE_SEAMLESS_BZ2( //usage: "\n j (De)compress using bzip2" //usage: ) +//usage: IF_FEATURE_SEAMLESS_LZ( +//usage: "\n y (De)compress using lzip" +//usage: ) //usage: IF_FEATURE_SEAMLESS_LZMA( //usage: "\n a (De)compress using lzma" //usage: ) @@ -846,6 +850,7 @@ IF_FEATURE_TAR_FROM( OPTBIT_EXCLUDE_FROM,) IF_FEATURE_SEAMLESS_GZ( OPTBIT_GZIP ,) IF_FEATURE_SEAMLESS_XZ( OPTBIT_XZ ,) // 16th bit + IF_FEATURE_SEAMLESS_LZ( OPTBIT_LZIP ,) IF_FEATURE_SEAMLESS_Z( OPTBIT_COMPRESS ,) IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,) #if ENABLE_FEATURE_TAR_LONG_OPTIONS @@ -873,6 +878,7 @@ OPT_EXCLUDE_FROM = IF_FEATURE_TAR_FROM( (1 << OPTBIT_EXCLUDE_FROM)) + 0, // X OPT_GZIP = IF_FEATURE_SEAMLESS_GZ( (1 << OPTBIT_GZIP )) + 0, // z OPT_XZ = IF_FEATURE_SEAMLESS_XZ( (1 << OPTBIT_XZ )) + 0, // J + OPT_LZIP = IF_FEATURE_SEAMLESS_LZ( (1 << OPTBIT_LZIP )) + 0, // y OPT_COMPRESS = IF_FEATURE_SEAMLESS_Z( (1 << OPTBIT_COMPRESS )) + 0, // Z OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m OPT_STRIP_COMPONENTS = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_STRIP_COMPONENTS)) + 0, // strip-components @@ -882,7 +888,7 @@ OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions OPT_OVERWRITE = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE )) + 0, // overwrite - OPT_ANY_COMPRESS = (OPT_BZIP2 | OPT_LZMA | OPT_GZIP | OPT_XZ | OPT_COMPRESS), + OPT_ANY_COMPRESS = (OPT_BZIP2 | OPT_LZMA | OPT_GZIP | OPT_XZ | OPT_LZIP | OPT_COMPRESS), }; #if ENABLE_FEATURE_TAR_LONG_OPTIONS static const char tar_longopts[] ALIGN1 = @@ -915,6 +921,9 @@ # if ENABLE_FEATURE_SEAMLESS_GZ "gzip\0" No_argument "z" # endif +# if ENABLE_FEATURE_SEAMLESS_LZ + "lzip\0" No_argument "y" +# endif # if ENABLE_FEATURE_SEAMLESS_XZ "xz\0" No_argument "J" # endif @@ -1026,6 +1035,7 @@ IF_FEATURE_TAR_FROM( "T:*X:*") IF_FEATURE_SEAMLESS_GZ( "z" ) IF_FEATURE_SEAMLESS_XZ( "J" ) + IF_FEATURE_SEAMLESS_LZ( "y" ) IF_FEATURE_SEAMLESS_Z( "Z" ) IF_FEATURE_TAR_NOPRESERVE_TIME("m") IF_FEATURE_TAR_LONG_OPTIONS("\xf9:") // --strip-components @@ -1063,6 +1073,7 @@ showopt(OPT_EXCLUDE_FROM ); showopt(OPT_GZIP ); showopt(OPT_XZ ); + showopt(OPT_LZIP ); showopt(OPT_COMPRESS ); showopt(OPT_NOPRESERVE_TIME ); showopt(OPT_STRIP_COMPONENTS); @@ -1198,6 +1209,8 @@ zipMode = "gzip"; if (opt & OPT_BZIP2) zipMode = "bzip2"; + if (opt & OPT_LZIP) + zipMode = "lzip"; if (opt & OPT_LZMA) zipMode = "lzma"; if (opt & OPT_XZ) @@ -1228,6 +1241,10 @@ USE_FOR_MMU(IF_FEATURE_SEAMLESS_BZ2(xformer = unpack_bz2_stream;)) USE_FOR_NOMMU(xformer_prog = "bunzip2";) } + if (opt & OPT_LZIP) { + USE_FOR_MMU(IF_FEATURE_SEAMLESS_LZ(xformer = unpack_lz_stream;)) + USE_FOR_NOMMU(xformer_prog = "lunzip";) + } if (opt & OPT_LZMA) { USE_FOR_MMU(IF_FEATURE_SEAMLESS_LZMA(xformer = unpack_lzma_stream;)) USE_FOR_NOMMU(xformer_prog = "unlzma";) --- configs/TEST_nommu_defconfig +++ configs/TEST_nommu_defconfig @@ -108,6 +108,7 @@ CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_LZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y @@ -121,6 +122,8 @@ CONFIG_GUNZIP=y CONFIG_GZIP=y CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LUNZIP=y +CONFIG_LZIP=y CONFIG_LZOP=y CONFIG_LZOP_COMPR_HIGH=y CONFIG_RPM2CPIO=y --- configs/TEST_noprintf_defconfig +++ configs/TEST_noprintf_defconfig @@ -119,6 +119,7 @@ CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_LZ=y CONFIG_FEATURE_SEAMLESS_Z=y # CONFIG_AR is not set # CONFIG_FEATURE_AR_LONG_FILENAMES is not set @@ -133,6 +134,8 @@ # CONFIG_GUNZIP is not set # CONFIG_GZIP is not set # CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set +# CONFIG_LUNZIP is not set +# CONFIG_LZIP is not set # CONFIG_LZOP is not set # CONFIG_LZOP_COMPR_HIGH is not set # CONFIG_RPM2CPIO is not set --- configs/TEST_rh9_defconfig +++ configs/TEST_rh9_defconfig @@ -117,6 +117,7 @@ CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_LZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y @@ -131,6 +132,8 @@ CONFIG_GUNZIP=y CONFIG_GZIP=y CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LUNZIP=y +CONFIG_LZIP=y CONFIG_LZOP=y # CONFIG_LZOP_COMPR_HIGH is not set CONFIG_RPM2CPIO=y --- configs/android2_defconfig +++ configs/android2_defconfig @@ -122,6 +122,7 @@ CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_LZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y @@ -136,6 +137,8 @@ CONFIG_GUNZIP=y CONFIG_GZIP=y # CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set +CONFIG_LUNZIP=y +CONFIG_LZIP=y CONFIG_LZOP=y CONFIG_LZOP_COMPR_HIGH=y CONFIG_RPM2CPIO=y --- configs/android_defconfig +++ configs/android_defconfig @@ -142,6 +142,7 @@ CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_LZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y @@ -157,6 +158,8 @@ CONFIG_GZIP=y # CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set CONFIG_GZIP_FAST=0 +CONFIG_LUNZIP=y +CONFIG_LZIP=y CONFIG_LZOP=y CONFIG_LZOP_COMPR_HIGH=y CONFIG_RPM2CPIO=y --- configs/android_ndk_defconfig +++ configs/android_ndk_defconfig @@ -132,6 +132,7 @@ CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_LZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y @@ -154,6 +155,8 @@ # CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set CONFIG_GZIP_FAST=0 # CONFIG_FEATURE_GZIP_LEVELS is not set +CONFIG_LUNZIP=y +CONFIG_LZIP=y CONFIG_LZOP=y CONFIG_LZOP_COMPR_HIGH=y CONFIG_RPM=y --- configs/cygwin_defconfig +++ configs/cygwin_defconfig @@ -122,6 +122,7 @@ CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_LZ=y # CONFIG_FEATURE_SEAMLESS_Z is not set # CONFIG_AR is not set # CONFIG_FEATURE_AR_LONG_FILENAMES is not set @@ -136,6 +137,8 @@ CONFIG_GUNZIP=y CONFIG_GZIP=y CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LUNZIP=y +CONFIG_LZIP=y CONFIG_LZOP=y # CONFIG_LZOP_COMPR_HIGH is not set CONFIG_RPM2CPIO=y --- configs/freebsd_defconfig +++ configs/freebsd_defconfig @@ -120,6 +120,7 @@ CONFIG_FEATURE_SEAMLESS_LZMA=y CONFIG_FEATURE_SEAMLESS_BZ2=y CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_LZ=y CONFIG_FEATURE_SEAMLESS_Z=y CONFIG_AR=y CONFIG_FEATURE_AR_LONG_FILENAMES=y @@ -134,6 +135,8 @@ CONFIG_GUNZIP=y CONFIG_GZIP=y CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LUNZIP=y +CONFIG_LZIP=y CONFIG_LZOP=y # CONFIG_LZOP_COMPR_HIGH is not set CONFIG_RPM2CPIO=y --- include/bb_archive.h +++ include/bb_archive.h @@ -9,6 +9,8 @@ COMPRESS_MAGIC = 0x1f9d, GZIP_MAGIC = 0x1f8b, BZIP2_MAGIC = 256 * 'B' + 'Z', + LZIP_MAGIC1 = 0x4C5A, + LZIP_MAGIC2 = 0x4950, /* .xz signature: 0xfd, '7', 'z', 'X', 'Z', 0x00 */ /* More info at: http://tukaani.org/xz/xz-file-format.txt */ XZ_MAGIC1 = 256 * 0xfd + '7', @@ -21,6 +23,8 @@ COMPRESS_MAGIC = 0x9d1f, GZIP_MAGIC = 0x8b1f, BZIP2_MAGIC = 'B' + 'Z' * 256, + LZIP_MAGIC1 = 0x5A4C, + LZIP_MAGIC2 = 0x5049, XZ_MAGIC1 = 0xfd + '7' * 256, XZ_MAGIC2 = 'z' + ('X' + ('Z' + 0 * 256) * 256) * 256, XZ_MAGIC1a = 0xfd + ('7' + ('z' + 'X' * 256) * 256) * 256, @@ -189,6 +193,7 @@ char get_header_tar(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC; +char get_header_tar_lz(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC; @@ -239,6 +244,7 @@ IF_DESKTOP(long long) int unpack_Z_stream(transformer_state_t *xstate) FAST_FUNC; IF_DESKTOP(long long) int unpack_gz_stream(transformer_state_t *xstate) FAST_FUNC; IF_DESKTOP(long long) int unpack_bz2_stream(transformer_state_t *xstate) FAST_FUNC; +IF_DESKTOP(long long) int unpack_lz_stream(transformer_state_t *xstate) FAST_FUNC; IF_DESKTOP(long long) int unpack_lzma_stream(transformer_state_t *xstate) FAST_FUNC; IF_DESKTOP(long long) int unpack_xz_stream(transformer_state_t *xstate) FAST_FUNC; --- include/libbb.h +++ include/libbb.h @@ -860,12 +860,13 @@ #define SEAMLESS_COMPRESSION (0 \ || ENABLE_FEATURE_SEAMLESS_XZ \ || ENABLE_FEATURE_SEAMLESS_LZMA \ + || ENABLE_FEATURE_SEAMLESS_LZ \ || ENABLE_FEATURE_SEAMLESS_BZ2 \ || ENABLE_FEATURE_SEAMLESS_GZ \ || ENABLE_FEATURE_SEAMLESS_Z) #if SEAMLESS_COMPRESSION -/* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */ +/* Autodetects gzip/bzip2/lzip formats. fd may be in the middle of the file! */ extern int setup_unzip_on_fd(int fd, int fail_if_not_compressed) FAST_FUNC; /* Autodetects .gz etc */ extern int open_zipped(const char *fname, int fail_if_not_compressed) FAST_FUNC; @@ -1290,6 +1291,7 @@ /* Don't need IF_xxx() guard for these */ int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int lunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; #if ENABLE_ROUTE void bb_displayroutes(int noresolve, int netstatfmt) FAST_FUNC; --- miscutils/man.c +++ miscutils/man.c @@ -180,6 +180,11 @@ if (run_pipe(filename_with_zext, man, level)) return 1; #endif +#if ENABLE_FEATURE_SEAMLESS_LZ + strcpy(ext, "lz"); + if (run_pipe(filename_with_zext, man, level)) + return 1; +#endif #if ENABLE_FEATURE_SEAMLESS_BZ2 strcpy(ext, "bz2"); if (run_pipe(filename_with_zext, man, level)) --- scripts/fix_ws.sh +++ scripts/fix_ws.sh @@ -19,6 +19,7 @@ | while read name; do test "YES" = "${name/*.bz2/YES}" && continue test "YES" = "${name/*.gz/YES}" && continue + test "YES" = "${name/*.lz/YES}" && continue test "YES" = "${name/*.png/YES}" && continue test "YES" = "${name/*.gif/YES}" && continue test "YES" = "${name/*.jpg/YES}" && continue --- testsuite/bunzip2.tests +++ testsuite/bunzip2.tests @@ -9,6 +9,9 @@ elif test "${0##*/}" = "bunzip2.tests"; then unpack=bunzip2 ext=bz2 +elif test "${0##*/}" = "lunzip.tests"; then + unpack=lunzip + ext=lz else echo "WTF? argv0='$0'" exit 1 @@ -35,6 +38,13 @@ $ECHO -ne "\x17\x72\x45\x38\x50\x90\x5b\xb8\xe8\xa3" } +hello_lz() { +# Lzipped "HELLO\n" +$ECHO -ne "\x4c\x5a\x49\x50\x01\x0c\x00\x24\x11\x45\xcf\x72\xcd\x3d\x3a\xdf" +$ECHO -ne "\xff\xff\xdd\x12\x00\x00\x6e\xd7\xac\xfd\x06\x00\x00\x00\x00\x00" +$ECHO -ne "\x00\x00\x2a\x00\x00\x00\x00\x00\x00\x00" +} + # We had bunzip2 error on this .bz2 file (fixed in rev 22521) test1_bz2() { @@ -530,6 +540,12 @@ expected="ok\n" prep; check "$unpack: delete src" "${bb}$unpack t2.$ext; test ! -f t2.$ext && echo ok" +expected="ok\n" +rm -f t1.* +hello_$ext > t1.t$ext +check "$unpack: replace .t$ext --> .tar" \ +"${bb}$unpack t1.t$ext && test -f t1.tar && test ! -f t1.t$ext && echo ok" + ) rm -rf testdir --- /dev/null +++ testsuite/lunzip.tests @@ -0,0 +1,3 @@ +#!/bin/sh + +. ./bunzip2.tests --- /dev/null +++ testsuite/lzip.tests @@ -0,0 +1,58 @@ +#!/bin/sh +# Copyright (C) 2012-2016 Antonio Diaz Diaz. +# Licensed under GPLv2 or later, see file LICENSE in this source tree. + +. ./testing.sh + +rm -rf lzip.testdir 2>/dev/null +mkdir lzip.testdir + +# Lzipped "a" +$ECHO -ne "\x4c\x5a\x49\x50\x01\x0c\x00\x30\xc1\xfb\xff\xff\xff\xe0\x00\x00\ +\x00\x43\xbe\xb7\xe8\x01\x00\x00\x00\x00\x00\x00\x00\x25\x00\x00\ +\x00\x00\x00\x00\x00" > lzip.testdir/a.lz + +# Lzipped zero-length file +$ECHO -ne "\x4c\x5a\x49\x50\x01\x0c\x00\x83\xff\xfb\xff\xff\xc0\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x24\x00\x00\x00\ +\x00\x00\x00\x00" > lzip.testdir/zero.lz + + +# testing "test name" "commands" "expected result" "file input" "stdin" + +testing "lzip reads from standard input" \ +"busybox lzip | cmp lzip.testdir/a.lz -" "" "" "a" + +testing "lzip accepts single minus" \ +"busybox lzip - | cmp lzip.testdir/a.lz -" "" "" "a" + +testing "lzip compresses a zero-length file" \ +"> zero ; busybox lzip -c zero | cmp lzip.testdir/zero.lz -" "" "" "" + +testing "lzip replaces original file" \ +"$ECHO foo > lzip.testdir/foo +rm -f lzip.testdir/foo.lz 2>/dev/null +busybox lzip lzip.testdir/foo +test -f lzip.testdir/foo.lz && test ! -f lzip.testdir/foo && echo ok" \ +"ok\n" "" "" + +testing "lzip replaces multiple files" \ +"$ECHO foo > lzip.testdir/foo +$ECHO bar > lzip.testdir/bar +rm -f lzip.testdir/foo.lz 2>/dev/null +rm -f lzip.testdir/bar.lz 2>/dev/null +busybox lzip lzip.testdir/foo lzip.testdir/bar +test -f lzip.testdir/foo.lz && test ! -f lzip.testdir/foo && echo ok1 +test -f lzip.testdir/bar.lz && test ! -f lzip.testdir/bar && echo ok2" \ +"ok1\nok2\n" "" "" + +testing "lzip -c does not remove original file" \ +"$ECHO foo > lzip.testdir/foo +busybox lzip -c lzip.testdir/foo >/dev/null +test -f lzip.testdir/foo && echo ok" "ok\n" "" "" + + +# Clean up +rm -rf lzip.testdir 2>/dev/null + +exit $FAILCOUNT --- testsuite/tar.tests +++ testsuite/tar.tests @@ -218,6 +218,25 @@ "" "" SKIP= +# lzipped tar archive with a file named ustar containing "ustar\n" +optional FEATURE_SEAMLESS_LZ +testing "tar extract tlz" "\ +tar -xyvf - && echo Ok +rm ustar || echo BAD +" "\ +ustar +Ok +" \ +"" "\ +\x4c\x5a\x49\x50\x01\x0c\x00\x3a\x9c\xca\xdd\xdf\xd4\xd1\x20\x5c\ +\xd8\x09\x8b\x76\xed\xd4\xe3\x53\xc0\x91\x33\x1e\x8c\x70\xfe\x9a\ +\xb3\xd9\xab\xbf\x33\xd0\x4e\x42\x90\xee\xa0\xad\xa7\xa3\x8c\xa9\ +\xfb\x94\xce\xf9\xdc\x54\x68\x12\x1d\x1b\x42\x36\x10\x5a\xa9\x15\ +\x59\x4c\x46\xae\x50\xa5\xe7\xe2\x8d\xc0\xf5\x4b\xa3\xfa\xb7\x15\ +\x6e\x33\x92\xc0\x93\xff\xff\xf7\x6b\x7b\x00\x2e\xb1\xb8\xc0\x00\ +\x08\x00\x00\x00\x00\x00\x00\x6f\x00\x00\x00\x00\x00\x00\x00" +SKIP= + # Do we detect XZ-compressed data (even w/o .tar.xz or txz extension)? # (the uuencoded hello_world.txz contains one empty file named "hello_world") optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_XZ