From 3c1ff97ddc8175f330f30f9e832749e042d8a845 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 17 Feb 2025 22:21:37 +0100 Subject: [PATCH 01/10] Adding upstream version 1.8. Signed-off-by: Daniel Baumann --- ChangeLog | 19 +++-- INSTALL | 4 +- Makefile.in | 9 ++- NEWS | 19 ++++- README | 16 ++-- carg_parser.c | 2 +- carg_parser.h | 2 +- configure | 14 ++-- decoder.c | 51 ++++++------ decoder.h | 20 +++-- doc/lunzip.1 | 25 +++--- lzip.h | 19 ++++- main.c | 191 +++++++++++++++++++++++++++++---------------- testsuite/check.sh | 91 +++++++++++++++------ 14 files changed, 315 insertions(+), 167 deletions(-) diff --git a/ChangeLog b/ChangeLog index 18d00a2..89bdf05 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,17 @@ -2015-07-07 Antonio Diaz Diaz +2016-05-12 Antonio Diaz Diaz - * Version 1.8-pre1 released. + * Version 1.8 released. + * main.c: Added new option '-a, --trailing-error'. * main.c (main): With '-u', verify that output file is regular. - * Error messages synced with lzip-1.17. + * main.c (decompress): Print up to 6 bytes of trailing data + when '-vvvv' is specified. + * decoder.c (LZd_verify_trailer): Removed test of final code. + * main.c (main): Delete '--output' file if infd is a terminal. + * main.c (main): Don't use stdin more than once. + * Error messages synced with lzip-1.18. + * configure: Avoid warning on some shells when testing for gcc. + * testsuite/check.sh: A POSIX shell is required to run the tests. + * testsuite/check.sh: Don't check error messages. 2015-05-27 Antonio Diaz Diaz @@ -24,7 +33,7 @@ 2013-09-17 Antonio Diaz Diaz * Version 1.4 released. - * main.c (show_header): Do not show header version. + * main.c (show_header): Don't show header version. * Minor fixes. 2013-06-18 Antonio Diaz Diaz @@ -60,7 +69,7 @@ * Created from the decompression code of clzip 1.1. -Copyright (C) 2010-2015 Antonio Diaz Diaz. +Copyright (C) 2010-2016 Antonio Diaz Diaz. This file is a collection of facts, and thus it is not copyrightable, but just in case, you have unlimited permission to copy, distribute and diff --git a/INSTALL b/INSTALL index c1a9d9a..89ff941 100644 --- a/INSTALL +++ b/INSTALL @@ -1,7 +1,7 @@ Requirements ------------ You will need a C compiler. -I use gcc 4.9.1 and 4.1.2, but the code should compile with any +I use gcc 5.3.0 and 4.1.2, but the code should compile with any standards compliant compiler. Gcc is available at http://gcc.gnu.org. @@ -61,7 +61,7 @@ After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2010-2015 Antonio Diaz Diaz. +Copyright (C) 2010-2016 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute and modify it. diff --git a/Makefile.in b/Makefile.in index 1ec2aaa..e4ff366 100644 --- a/Makefile.in +++ b/Makefile.in @@ -5,6 +5,7 @@ INSTALL_PROGRAM = $(INSTALL) -m 755 INSTALL_DATA = $(INSTALL) -m 644 INSTALL_DIR = $(INSTALL) -d -m 755 SHELL = /bin/sh +CAN_RUN_INSTALLINFO = $(SHELL) -c "install-info --version" > /dev/null 2>&1 objs = carg_parser.o decoder.o main.o @@ -66,7 +67,9 @@ install-info : if [ ! -d "$(DESTDIR)$(infodir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(infodir)" ; fi -rm -f "$(DESTDIR)$(infodir)/$(pkgname).info"* $(INSTALL_DATA) $(VPATH)/doc/$(pkgname).info "$(DESTDIR)$(infodir)/$(pkgname).info" - -install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(pkgname).info" + -if $(CAN_RUN_INSTALLINFO) ; then \ + install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(pkgname).info" ; \ + fi install-info-compress : install-info lzip -v -9 "$(DESTDIR)$(infodir)/$(pkgname).info" @@ -89,7 +92,9 @@ uninstall-bin : -rm -f "$(DESTDIR)$(bindir)/$(progname)" uninstall-info : - -install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(pkgname).info" + -if $(CAN_RUN_INSTALLINFO) ; then \ + install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(pkgname).info" ; \ + fi -rm -f "$(DESTDIR)$(infodir)/$(pkgname).info"* uninstall-man : diff --git a/NEWS b/NEWS index b9a07e1..8727a19 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,24 @@ Changes in version 1.8: +The option "-a, --trailing-error", which makes lunzip exit with error +status 2 if any remaining input is detected after decompressing the last +member, has been added. + Lunzip now verifies that the output file is regular when "low memory" mode is requested. +Up to 6 bytes of trailing data are printed if "-vvvv" is specified. + +The test of the value remaining in the range decoder has been removed. +(After extensive testing it has been found useless to detect corruption +in the decompressed data. Eliminating it reduces the number of false +positives for corruption and makes error detection more accurate). + +When decompressing, the file specified with the '--output' option is now +deleted if the input is a terminal. + Some error messages have been adjusted to be identical to those of -lzip-1.17. +lzip-1.18. + +A harmless check failure on Windows, caused by the failed comparison of +a message in text mode, has been fixed. diff --git a/README b/README index 052751f..793bfb1 100644 --- a/README +++ b/README @@ -2,7 +2,7 @@ Description Lunzip is a decompressor for the lzip format. It is written in C and its small size makes it well suited for embedded devices or software -installers that need to decompress files but do not need compression +installers that need to decompress files but don't need compression capabilities. Lunzip is fully compatible with lzip-1.4 or newer. The lzip file format is designed for data sharing and long-term @@ -40,12 +40,12 @@ little memory as 50 kB, irrespective of the dictionary size used to compress the file. To activate it, specify the size of the output buffer with the "--buffer-size" option and lunzip will use the decompressed file as dictionary for distances beyond the buffer size. Of course, the -smaller the output buffer size used in relation to the dictionary size, -the more accesses to disk are needed and the slower the decompression -is. This "low memory" mode only works when decompressing to a regular -file and is intended for systems without enough memory (RAM + swap) to -keep the whole dictionary at once. It has been tested on a laptop with a -486 processor and 4 MiB of RAM. +smaller the buffer size used in relation to the dictionary size, the +more accesses to disk are needed and the slower the decompression is. +This "low memory" mode only works when decompressing to a regular file +and is intended for systems without enough memory (RAM + swap) to keep +the whole dictionary at once. It has been tested on a laptop with a 486 +processor and 4 MiB of RAM. The amount of memory required by lunzip to decompress a file is about 46 kB larger than the dictionary size used to compress that file, unless @@ -83,7 +83,7 @@ range encoding), Igor Pavlov (for putting all the above together in LZMA), and Julian Seward (for bzip2's CLI). -Copyright (C) 2010-2015 Antonio Diaz Diaz. +Copyright (C) 2010-2016 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute and modify it. diff --git a/carg_parser.c b/carg_parser.c index 8d74ea6..3d4e89f 100644 --- a/carg_parser.c +++ b/carg_parser.c @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2015 Antonio Diaz Diaz. + Copyright (C) 2006-2016 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/carg_parser.h b/carg_parser.h index ed4d9c5..e918942 100644 --- a/carg_parser.h +++ b/carg_parser.h @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2015 Antonio Diaz Diaz. + Copyright (C) 2006-2016 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/configure b/configure index 8609260..37541c5 100755 --- a/configure +++ b/configure @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2015 Antonio Diaz Diaz. +# Copyright (C) 2010-2016 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission # to copy, distribute and modify it. pkgname=lunzip -pkgversion=1.8-pre1 +pkgversion=1.8 progname=lunzip srctrigger=doc/${progname}.1 @@ -26,8 +26,8 @@ CFLAGS='-Wall -W -O2' LDFLAGS= # checking whether we are using GNU C. -${CC} --version > /dev/null 2>&1 -if [ $? != 0 ] ; then +if /bin/sh -c "${CC} --version" > /dev/null 2>&1 ; then true +else CC=cc CFLAGS='-W -O2' fi @@ -139,7 +139,7 @@ if [ -z "${no_create}" ] ; then rm -f config.status cat > config.status << EOF #! /bin/sh -# This file was generated automatically by configure. Do not edit. +# This file was generated automatically by configure. Don't edit. # Run this file to recreate the current configuration. # # This script is free software: you have unlimited permission @@ -165,8 +165,8 @@ echo "LDFLAGS = ${LDFLAGS}" rm -f Makefile cat > Makefile << EOF # Makefile for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2015 Antonio Diaz Diaz. -# This file was generated automatically by configure. Do not edit. +# Copyright (C) 2010-2016 Antonio Diaz Diaz. +# This file was generated automatically by configure. Don't edit. # # This Makefile is free software: you have unlimited permission # to copy, distribute and modify it. diff --git a/decoder.c b/decoder.c index 3511f9a..20364c0 100644 --- a/decoder.c +++ b/decoder.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2015 Antonio Diaz Diaz. + Copyright (C) 2010-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 @@ -38,10 +38,11 @@ void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ) { if( pp->first_post ) { - int i, len = pp->longest_name - strlen( pp->name ); + unsigned i; pp->first_post = false; fprintf( stderr, " %s: ", pp->name ); - for( i = 0; i < len; ++i ) fputc( ' ', stderr ); + for( i = strlen( pp->name ); i < pp->longest_name; ++i ) + fputc( ' ', stderr ); if( !msg ) fflush( stderr ); } if( msg ) fprintf( stderr, "%s\n", msg ); @@ -120,7 +121,8 @@ void LZd_flush_data( struct LZ_decoder * const d ) writeblock( d->outfd, d->buffer + d->stream_pos, size ) != size ) { show_error( "Write error", errno, false ); cleanup_and_fail( 1 ); } if( d->pos >= d->buffer_size ) - { d->partial_data_pos += d->pos; d->pos = 0; } + { d->partial_data_pos += d->pos; d->pos = 0; + if( d->partial_data_pos >= d->dictionary_size ) d->pos_wrapped = true; } d->stream_pos = d->pos; } } @@ -130,13 +132,11 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, struct Pretty_print * const pp ) { File_trailer trailer; - const unsigned long long member_size = Rd_member_position( d->rdec ) + Ft_size; - unsigned long long trailer_data_size; - unsigned long long trailer_member_size; - unsigned trailer_crc; + int size = Rd_read_data( d->rdec, trailer, Ft_size ); + const unsigned long long data_size = LZd_data_position( d ); + const unsigned long long member_size = Rd_member_position( d->rdec ); bool error = false; - int size = Rd_read_data( d->rdec, trailer, Ft_size ); if( size < Ft_size ) { error = true; @@ -149,52 +149,44 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, while( size < Ft_size ) trailer[size++] = 0; } - if( d->rdec->code != 0 ) - { - error = true; - Pp_show_msg( pp, "Range decoder final code is not zero." ); - } - trailer_crc = Ft_get_data_crc( trailer ); - if( trailer_crc != LZd_crc( d ) ) + if( Ft_get_data_crc( trailer ) != LZd_crc( d ) ) { error = true; if( verbosity >= 0 ) { Pp_show_msg( pp, 0 ); fprintf( stderr, "CRC mismatch; trailer says %08X, data CRC is %08X\n", - trailer_crc, LZd_crc( d ) ); + Ft_get_data_crc( trailer ), LZd_crc( d ) ); } } - trailer_data_size = Ft_get_data_size( trailer ); - if( trailer_data_size != LZd_data_position( d ) ) + if( Ft_get_data_size( trailer ) != data_size ) { error = true; if( verbosity >= 0 ) { Pp_show_msg( pp, 0 ); fprintf( stderr, "Data size mismatch; trailer says %llu, data size is %llu (0x%llX)\n", - trailer_data_size, LZd_data_position( d ), LZd_data_position( d ) ); + Ft_get_data_size( trailer ), data_size, data_size ); } } - trailer_member_size = Ft_get_member_size( trailer ); - if( trailer_member_size != member_size ) + if( Ft_get_member_size( trailer ) != member_size ) { error = true; if( verbosity >= 0 ) { Pp_show_msg( pp, 0 ); fprintf( stderr, "Member size mismatch; trailer says %llu, member size is %llu (0x%llX)\n", - trailer_member_size, member_size, member_size ); + Ft_get_member_size( trailer ), member_size, member_size ); } } - if( !error && verbosity >= 2 && LZd_data_position( d ) > 0 && member_size > 0 ) + if( !error && verbosity >= 2 && data_size > 0 && member_size > 0 ) fprintf( stderr, "%6.3f:1, %6.3f bits/byte, %5.2f%% saved. ", - (double)LZd_data_position( d ) / member_size, - ( 8.0 * member_size ) / LZd_data_position( d ), - 100.0 * ( 1.0 - ( (double)member_size / LZd_data_position( d ) ) ) ); + (double)data_size / member_size, + ( 8.0 * member_size ) / data_size, + 100.0 * ( 1.0 - ( (double)member_size / data_size ) ) ); if( !error && verbosity >= 4 ) fprintf( stderr, "data CRC %08X, data size %9llu, member size %8llu. ", - trailer_crc, trailer_data_size, trailer_member_size ); + LZd_crc( d ), data_size, member_size ); return !error; } @@ -308,7 +300,8 @@ int LZd_decode_member( struct LZ_decoder * const d, } rep3 = rep2; rep2 = rep1; rep1 = rep0_saved; state = St_set_match( state ); - if( rep0 >= d->dictionary_size || rep0 >= LZd_data_position( d ) ) + if( rep0 >= d->dictionary_size || + ( rep0 >= LZd_data_position( d ) && !d->pos_wrapped ) ) { LZd_flush_data( d ); return 1; } } copy_block( d, rep0, len ); diff --git a/decoder.h b/decoder.h index 10195b8..922426c 100644 --- a/decoder.h +++ b/decoder.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2015 Antonio Diaz Diaz. + Copyright (C) 2010-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 @@ -60,7 +60,8 @@ static inline void Rd_reset_member_position( struct Range_decoder * const rdec ) static inline uint8_t Rd_get_byte( struct Range_decoder * const rdec ) { - if( Rd_finished( rdec ) ) return 0xAA; /* make code != 0 */ + /* 0xFF avoids decoder error if member is truncated at EOS marker */ + if( Rd_finished( rdec ) ) return 0xFF; return rdec->buffer[rdec->pos++]; } @@ -238,6 +239,7 @@ struct LZ_decoder int stream_pos; /* first byte not yet written to file */ uint32_t crc; int outfd; /* output file descriptor */ + bool pos_wrapped; Bit_model bm_literal[1<pos - distance - 1; - if( i < 0 ) i += d->buffer_size; - if( len < d->buffer_size - max( d->pos, i ) && len <= abs( d->pos - i ) ) + bool fast; + if( i < 0 ) + { i += d->buffer_size; + fast = ( len <= d->buffer_size - i && len <= i - d->pos ); } + else + fast = ( len < d->buffer_size - d->pos && len <= d->pos - i ); + if( fast ) /* no wrap, no overlap */ { - memcpy( d->buffer + d->pos, d->buffer + i, len ); /* no wrap, no overlap */ + memcpy( d->buffer + d->pos, d->buffer + i, len ); d->pos += len; } else for( ; len > 0; --len ) @@ -330,13 +337,14 @@ static inline bool LZd_init( struct LZ_decoder * const d, d->partial_data_pos = 0; d->rdec = rde; d->dictionary_size = dict_size; - d->buffer_size = min( buffer_size, max( 65536, dict_size ) ); + d->buffer_size = min( buffer_size, dict_size ); d->buffer = (uint8_t *)malloc( d->buffer_size ); if( !d->buffer ) return false; d->pos = 0; d->stream_pos = 0; d->crc = 0xFFFFFFFFU; d->outfd = ofd; + d->pos_wrapped = false; Bm_array_init( d->bm_literal[0], (1 << literal_context_bits) * 0x300 ); Bm_array_init( d->bm_match[0], states * pos_states ); diff --git a/doc/lunzip.1 b/doc/lunzip.1 index 7a35eb5..53b3faf 100644 --- a/doc/lunzip.1 +++ b/doc/lunzip.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. -.TH LUNZIP "1" "July 2015" "lunzip 1.8-pre1" "User Commands" +.TH LUNZIP "1" "May 2016" "lunzip 1.8" "User Commands" .SH NAME lunzip \- decompressor for the lzip format .SH SYNOPSIS @@ -8,7 +8,7 @@ lunzip \- decompressor for the lzip format .SH DESCRIPTION Lunzip is a decompressor for the lzip format. It is written in C and its small size makes it well suited for embedded devices or software -installers that need to decompress files but do not need compression +installers that need to decompress files but don't need compression capabilities. Lunzip is fully compatible with lzip\-1.4 or newer. .PP Lunzip provides a 'low memory' mode able to decompress any file using as @@ -16,9 +16,11 @@ little memory as 50 kB, irrespective of the dictionary size used to compress the file. To activate it, specify the size of the output buffer with the '\-\-buffer\-size' option and lunzip will use the decompressed file as dictionary for distances beyond the buffer size. Of course, the -smaller the output buffer size used in relation to the dictionary size, -the more accesses to disk are needed and the slower the decompression is. -This 'low memory' mode only works when decompressing to a regular file. +smaller the buffer size used in relation to the dictionary size, the +more accesses to disk are needed and the slower the decompression is. +This 'low memory' mode only works when decompressing to a regular file +and is intended for systems without enough memory (RAM + swap) to keep +the whole dictionary at once. .SH OPTIONS .TP \fB\-h\fR, \fB\-\-help\fR @@ -27,8 +29,11 @@ display this help and exit \fB\-V\fR, \fB\-\-version\fR output version information and exit .TP +\fB\-a\fR, \fB\-\-trailing\-error\fR +exit with error status if trailing data +.TP \fB\-c\fR, \fB\-\-stdout\fR -send output to standard output +write to standard output, keep input files .TP \fB\-d\fR, \fB\-\-decompress\fR decompress (this is the default) @@ -40,7 +45,7 @@ overwrite existing output files keep (don't delete) input files .TP \fB\-o\fR, \fB\-\-output=\fR -if reading stdin, place the output into +if reading standard input, write to .TP \fB\-q\fR, \fB\-\-quiet\fR suppress all messages @@ -54,8 +59,8 @@ set output buffer size in bytes \fB\-v\fR, \fB\-\-verbose\fR be verbose (a 2nd \fB\-v\fR gives more) .PP -If no file names are given, lunzip decompresses from standard input to -standard output. +If no file names are given, or if a file is '\-', lunzip decompresses +from standard input to standard output. Numbers may be followed by a multiplier: k = kB = 10^3 = 1000, Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc... .PP @@ -68,7 +73,7 @@ Report bugs to lzip\-bug@nongnu.org .br Lunzip home page: http://www.nongnu.org/lzip/lunzip.html .SH COPYRIGHT -Copyright \(co 2015 Antonio Diaz Diaz. +Copyright \(co 2016 Antonio Diaz Diaz. License GPLv2+: GNU GPL version 2 or later .br This is free software: you are free to change and redistribute it. diff --git a/lzip.h b/lzip.h index 8f4120a..2970cb3 100644 --- a/lzip.h +++ b/lzip.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2015 Antonio Diaz Diaz. + Copyright (C) 2010-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 @@ -50,6 +50,7 @@ enum { max_dictionary_bits = 29, max_dictionary_size = 1 << max_dictionary_bits, literal_context_bits = 3, + literal_pos_state_bits = 0, /* not used */ pos_state_bits = 2, pos_states = 1 << pos_state_bits, pos_state_mask = pos_states - 1, @@ -90,8 +91,8 @@ 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; } +static inline void Bm_array_init( Bit_model bm[], const int size ) + { int i; for( i = 0; i < size; ++i ) Bm_init( &bm[i] ); } struct Len_model { @@ -121,7 +122,8 @@ struct Pretty_print }; static inline void Pp_init( struct Pretty_print * const pp, - const char * const filenames[], const int num_filenames ) + const char * const filenames[], + const int num_filenames, const int verbosity ) { unsigned stdin_name_len; int i; @@ -131,6 +133,7 @@ static inline void Pp_init( struct Pretty_print * const pp, pp->first_post = false; stdin_name_len = strlen( pp->stdin_name ); + if( verbosity <= 0 ) return; for( i = 0; i < num_filenames; ++i ) { const char * const s = filenames[i]; @@ -191,6 +194,14 @@ enum { Fh_size = 6 }; static inline bool Fh_verify_magic( const File_header data ) { return ( memcmp( data, magic_string, 4 ) == 0 ); } +/* detect truncated header */ +static inline bool Fh_verify_prefix( const File_header data, const int size ) + { + int i; for( i = 0; i < size && i < 4; ++i ) + if( data[i] != magic_string[i] ) return false; + return ( size > 0 ); + } + static inline uint8_t Fh_version( const File_header data ) { return data[4]; } diff --git a/main.c b/main.c index 0b220da..8737fed 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2015 Antonio Diaz Diaz. + Copyright (C) 2010-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 @@ -23,6 +23,7 @@ #define _FILE_OFFSET_BITS 64 +#include #include #include #include @@ -62,10 +63,11 @@ #error "Environments where CHAR_BIT != 8 are not supported." #endif +int verbosity = 0; const char * const Program_name = "Lunzip"; const char * const program_name = "lunzip"; -const char * const program_year = "2015"; +const char * const program_year = "2016"; const char * invocation_name = 0; struct { const char * from; const char * to; } const known_extensions[] = { @@ -75,10 +77,6 @@ struct { const char * from; const char * to; } const known_extensions[] = { char * output_filename = 0; int outfd = -1; -int verbosity = 0; -const mode_t usr_rw = S_IRUSR | S_IWUSR; -const mode_t all_rw = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; -mode_t outfd_mode = S_IRUSR | S_IWUSR; bool delete_output_on_interrupt = false; @@ -86,31 +84,34 @@ static void show_help( void ) { printf( "Lunzip is a decompressor for the lzip format. It is written in C and its\n" "small size makes it well suited for embedded devices or software\n" - "installers that need to decompress files but do not need compression\n" + "installers that need to decompress files but don't need compression\n" "capabilities. Lunzip is fully compatible with lzip-1.4 or newer.\n" "\nLunzip provides a 'low memory' mode able to decompress any file using as\n" "little memory as 50 kB, irrespective of the dictionary size used to\n" "compress the file. To activate it, specify the size of the output buffer\n" "with the '--buffer-size' option and lunzip will use the decompressed\n" "file as dictionary for distances beyond the buffer size. Of course, the\n" - "smaller the output buffer size used in relation to the dictionary size,\n" - "the more accesses to disk are needed and the slower the decompression is.\n" - "This 'low memory' mode only works when decompressing to a regular file.\n" + "smaller the buffer size used in relation to the dictionary size, the\n" + "more accesses to disk are needed and the slower the decompression is.\n" + "This 'low memory' mode only works when decompressing to a regular file\n" + "and is intended for systems without enough memory (RAM + swap) to keep\n" + "the whole dictionary at once.\n" "\nUsage: %s [options] [files]\n", invocation_name ); printf( "\nOptions:\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" - " -c, --stdout send output to standard output\n" + " -a, --trailing-error exit with error status if trailing data\n" + " -c, --stdout write to standard output, keep input files\n" " -d, --decompress decompress (this is the default)\n" " -f, --force overwrite existing output files\n" " -k, --keep keep (don't delete) input files\n" - " -o, --output= if reading stdin, place the output into \n" + " -o, --output= if reading standard input, write to \n" " -q, --quiet suppress all messages\n" " -t, --test test compressed file integrity\n" " -u, --buffer-size= set output buffer size in bytes\n" " -v, --verbose be verbose (a 2nd -v gives more)\n" - "If no file names are given, lunzip decompresses from standard input to\n" - "standard output.\n" + "If no file names are given, or if a file is '-', lunzip decompresses\n" + "from standard input to standard output.\n" "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n" @@ -169,11 +170,10 @@ static unsigned long getnum( const char * const ptr, if( !errno && tail[0] ) { const int factor = ( tail[1] == 'i' ) ? 1024 : 1000; - int exponent = 0, i; - bool bad_multiplier = false; + int exponent = 0; /* 0 = bad multiplier */ + int i; switch( tail[0] ) { - case ' ': break; case 'Y': exponent = 8; break; case 'Z': exponent = 7; break; case 'E': exponent = 6; break; @@ -181,13 +181,10 @@ static unsigned long getnum( const char * const ptr, case 'T': exponent = 4; break; case 'G': exponent = 3; break; case 'M': exponent = 2; break; - case 'K': if( factor == 1024 ) exponent = 1; else bad_multiplier = true; - break; - case 'k': if( factor == 1000 ) exponent = 1; else bad_multiplier = true; - break; - default : bad_multiplier = true; + case 'K': if( factor == 1024 ) exponent = 1; break; + case 'k': if( factor == 1000 ) exponent = 1; break; } - if( bad_multiplier ) + if( exponent <= 0 ) { show_error( "Bad multiplier in numerical argument.", 0, true ); exit( 1 ); @@ -306,13 +303,17 @@ static void set_d_outname( const char * const name, const int i ) } -static bool open_outstream( const bool force ) +static bool open_outstream( const bool force, const bool from_stdin ) { + const mode_t usr_rw = S_IRUSR | S_IWUSR; + const mode_t all_rw = usr_rw | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + const mode_t outfd_mode = from_stdin ? all_rw : usr_rw; int flags = O_APPEND | O_CREAT | O_RDWR | O_BINARY; if( force ) flags |= O_TRUNC; else flags |= O_EXCL; outfd = open( output_filename, flags, outfd_mode ); - if( outfd < 0 && verbosity >= 0 ) + if( outfd >= 0 ) delete_output_on_interrupt = true; + else if( verbosity >= 0 ) { if( errno == EEXIST ) fprintf( stderr, "%s: Output file '%s' already exists, skipping.\n", @@ -356,7 +357,11 @@ static void close_and_set_permissions( const struct stat * const in_statsp ) fchmod( outfd, mode & ~( S_ISUID | S_ISGID | S_ISVTX ) ) != 0 ) warning = true; } - if( close( outfd ) != 0 ) cleanup_and_fail( 1 ); + if( close( outfd ) != 0 ) + { + show_error( "Error closing output file", errno, false ); + cleanup_and_fail( 1 ); + } outfd = -1; delete_output_on_interrupt = false; if( in_statsp ) @@ -371,8 +376,52 @@ static void close_and_set_permissions( const struct stat * const in_statsp ) } +static unsigned char xdigit( const int value ) + { + if( value >= 0 && value <= 9 ) return '0' + value; + if( value >= 10 && value <= 15 ) return 'A' + value - 10; + return 0; + } + + +static bool show_trailing_data( const uint8_t * const data, const int size, + struct Pretty_print * const pp, const bool all, + const bool ignore_trailing ) + { + if( verbosity >= 4 || !ignore_trailing ) + { + int i; + char buf[80]; + int len = snprintf( buf, sizeof buf, "%strailing data = ", + all ? "" : "first bytes of " ); + bool text = true; + for( i = 0; i < size; ++i ) + if( !isprint( data[i] ) ) { text = false; break; } + if( text ) + { + if( len > 0 && len < (int)sizeof buf ) + snprintf( buf + len, sizeof buf - len, "'%.*s'", size, (const char *)data ); + } + else + { + for( i = 0; i < size && len > 0 && len + 3 < (int)sizeof buf; ++i ) + { + if( i > 0 ) buf[len++] = ' '; + buf[len++] = xdigit( data[i] >> 4 ); + buf[len++] = xdigit( data[i] & 0x0F ); + buf[len] = 0; + } + } + Pp_show_msg( pp, buf ); + if( !ignore_trailing ) show_error( "Trailing data not allowed.", 0, false ); + } + return ignore_trailing; + } + + static int decompress( const int infd, struct Pretty_print * const pp, - const int buffer_size, const bool testing ) + const int buffer_size, + const bool ignore_trailing, const bool testing ) { unsigned long long partial_file_pos = 0; struct Range_decoder rdec; @@ -386,24 +435,30 @@ static int decompress( const int infd, struct Pretty_print * const pp, for( first_member = true; ; first_member = false ) { - int result; + int result, size; unsigned dictionary_size; File_header header; struct LZ_decoder decoder; Rd_reset_member_position( &rdec ); - Rd_read_data( &rdec, header, Fh_size ); + size = Rd_read_data( &rdec, header, Fh_size ); if( Rd_finished( &rdec ) ) /* End Of File */ { - if( first_member ) + if( first_member || Fh_verify_prefix( header, size ) ) { Pp_show_msg( pp, "File ends unexpectedly at member header." ); retval = 2; } + else if( size > 0 && !show_trailing_data( header, size, pp, + true, ignore_trailing ) ) + retval = 2; break; } if( !Fh_verify_magic( header ) ) { - if( !first_member ) break; /* trailing garbage */ - Pp_show_msg( pp, "Bad magic number (file not in lzip format)." ); - retval = 2; break; + if( first_member ) + { Pp_show_msg( pp, "Bad magic number (file not in lzip format)." ); + retval = 2; } + else if( !show_trailing_data( header, size, pp, false, ignore_trailing ) ) + retval = 2; + break; } if( !Fh_verify_version( header ) ) { @@ -424,8 +479,8 @@ static int decompress( const int infd, struct Pretty_print * const pp, if( !LZd_init( &decoder, &rdec, buffer_size, dictionary_size, outfd ) ) { - show_error( "Not enough memory. Try a smaller output buffer size.", 0, false ); - cleanup_and_fail( 1 ); + Pp_show_msg( pp, "Not enough memory. Try a smaller output buffer size." ); + retval = 1; break; } result = LZd_decode_member( &decoder, pp ); partial_file_pos += Rd_member_position( &rdec ); @@ -468,18 +523,16 @@ static void set_signals( void ) void show_error( const char * const msg, const int errcode, const bool help ) { - if( verbosity >= 0 ) + if( verbosity < 0 ) return; + if( msg && msg[0] ) { - if( msg && msg[0] ) - { - fprintf( stderr, "%s: %s", program_name, msg ); - if( errcode > 0 ) fprintf( stderr, ": %s", strerror( errcode ) ); - fputc( '\n', stderr ); - } - if( help ) - fprintf( stderr, "Try '%s --help' for more information.\n", - invocation_name ); + fprintf( stderr, "%s: %s", program_name, msg ); + if( errcode > 0 ) fprintf( stderr, ": %s", strerror( errcode ) ); + fputc( '\n', stderr ); } + if( help ) + fprintf( stderr, "Try '%s --help' for more information.\n", + invocation_name ); } @@ -504,13 +557,16 @@ int main( const int argc, const char * const argv[] ) int i; bool filenames_given = false; bool force = false; + bool ignore_trailing = true; bool keep_input_files = false; + bool stdin_used = false; bool testing = false; bool to_stdout = false; struct Pretty_print pp; const struct ap_Option options[] = { + { 'a', "trailing-error", ap_no }, { 'c', "stdout", ap_no }, { 'd', "decompress", ap_no }, { 'f', "force", ap_no }, @@ -542,6 +598,7 @@ int main( const int argc, const char * const argv[] ) if( !code ) break; /* no more options */ switch( code ) { + case 'a': ignore_trailing = false; break; case 'c': to_stdout = true; break; case 'd': testing = false; break; case 'f': force = true; break; @@ -578,7 +635,6 @@ int main( const int argc, const char * const argv[] ) if( buffer_size < max_dictionary_size ) { - struct stat st; bool from_stdin = false; if( to_stdout || testing ) { show_error( "'--buffer-size' is incompatible with '--stdout' and '--test'.", 0, false ); @@ -587,24 +643,15 @@ int main( const int argc, const char * const argv[] ) if( !filenames[i][0] || strcmp( filenames[i], "-" ) == 0 ) { from_stdin = true; break; } if( from_stdin && !default_output_filename[0] ) - { show_error( "Output file must be specified when decompressing from stdin with a\n" - " reduced buffer size.", 0, false ); return 1; } - if( from_stdin && default_output_filename[0] && - stat( default_output_filename, &st ) == 0 && !S_ISREG( st.st_mode ) ) - { - if( verbosity >= 0 ) - fprintf( stderr, "%s: Output file '%s' is not a regular file,\n" - " and 'low memory' mode has been requested.\n", - program_name, default_output_filename ); - return 1; - } + { show_error( "Output file must be specified when decompressing from standard input\n" + " with a reduced buffer size.", 0, false ); return 1; } } if( !to_stdout && !testing && ( filenames_given || default_output_filename[0] ) ) set_signals(); - Pp_init( &pp, filenames, num_filenames ); + Pp_init( &pp, filenames, num_filenames, verbosity ); output_filename = resize_buffer( output_filename, 1 ); for( i = 0; i < num_filenames; ++i ) @@ -616,6 +663,7 @@ int main( const int argc, const char * const argv[] ) if( !filenames[i][0] || strcmp( filenames[i], "-" ) == 0 ) { + if( stdin_used ) continue; else stdin_used = true; input_filename = ""; infd = STDIN_FILENO; if( !testing ) @@ -627,8 +675,7 @@ int main( const int argc, const char * const argv[] ) output_filename = resize_buffer( output_filename, strlen( default_output_filename ) + 1 ); strcpy( output_filename, default_output_filename ); - outfd_mode = all_rw; - if( !open_outstream( force ) ) + if( !open_outstream( force, true ) ) { if( retval < 1 ) retval = 1; close( infd ); infd = -1; @@ -648,8 +695,7 @@ int main( const int argc, const char * const argv[] ) else { set_d_outname( input_filename, extension_index( input_filename ) ); - outfd_mode = usr_rw; - if( !open_outstream( force ) ) + if( !open_outstream( force, false ) ) { if( retval < 1 ) retval = 1; close( infd ); infd = -1; @@ -662,14 +708,27 @@ int main( const int argc, const char * const argv[] ) if( isatty( infd ) ) { show_error( "I won't read compressed data from a terminal.", 0, true ); - return 1; + if( retval < 1 ) retval = 1; + cleanup_and_fail( retval ); + } + + if( delete_output_on_interrupt && buffer_size < max_dictionary_size ) + { + struct stat st; + if( fstat( outfd, &st ) != 0 || !S_ISREG( st.st_mode ) ) + { + if( verbosity >= 0 ) + fprintf( stderr, "%s: Output file '%s' is not a regular file,\n" + " and 'low memory' mode has been requested.\n", + program_name, output_filename ); + if( retval < 1 ) retval = 1; + cleanup_and_fail( retval ); + } } - if( output_filename[0] && !to_stdout && !testing ) - delete_output_on_interrupt = true; in_statsp = input_filename[0] ? &in_stats : 0; Pp_set_name( &pp, input_filename ); - tmp = decompress( infd, &pp, buffer_size, testing ); + tmp = decompress( infd, &pp, buffer_size, ignore_trailing, testing ); if( tmp > retval ) retval = tmp; if( tmp && !testing ) cleanup_and_fail( retval ); diff --git a/testsuite/check.sh b/testsuite/check.sh index f647d6c..f5b2088 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # check script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2015 Antonio Diaz Diaz. +# Copyright (C) 2010-2016 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission # to copy, distribute and modify it. @@ -17,9 +17,16 @@ if [ ! -f "${LZIP}" ] || [ ! -x "${LZIP}" ] ; then exit 1 fi +if [ -e "${LZIP}" ] 2> /dev/null ; then true +else + echo "$0: a POSIX shell is required to run the tests" + echo "Try bash -c \"$0 $1 $2\"" + exit 1 +fi + if [ -d tmp ] ; then rm -rf tmp ; fi mkdir tmp -cd "${objdir}"/tmp +cd "${objdir}"/tmp || framework_failure cat "${testdir}"/test.txt > in || framework_failure in_lz="${testdir}"/test.txt.lz @@ -27,21 +34,20 @@ fail=0 printf "testing lunzip-%s..." "$2" -"${LZIP}" -cqu-1 "${in_lz}" > /dev/null -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cqu0 "${in_lz}" > /dev/null -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cqu4095 "${in_lz}" > /dev/null -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cqu513MiB "${in_lz}" > /dev/null -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -printf " in: Bad magic number (file not in lzip format).\n" > msg -"${LZIP}" -t in 2> out -if [ $? = 2 ] && cmp out msg ; then printf . ; else printf - ; fail=1 ; fi -printf " (stdin): Bad magic number (file not in lzip format).\n" > msg -"${LZIP}" -t < in 2> out -if [ $? = 2 ] && cmp out msg ; then printf . ; else printf - ; fail=1 ; fi -rm -f out msg +cat "${testdir}"/test.txt.lz > uin.lz || framework_failure +"${LZIP}" -dfkqu-1 uin.lz +if [ $? = 1 ] && [ ! -e uin ] ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -dfkqu0 uin.lz +if [ $? = 1 ] && [ ! -e uin ] ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -dfkqu4095 uin.lz +if [ $? = 1 ] && [ ! -e uin ] ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -dfkqu513MiB uin.lz +if [ $? = 1 ] && [ ! -e uin ] ; then printf . ; else printf - ; fail=1 ; fi +rm -f uin.lz +"${LZIP}" -tq in +if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -tq < in +if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi "${LZIP}" -cdq in if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi "${LZIP}" -cdq < in @@ -51,26 +57,53 @@ if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi dd if="${in_lz}" bs=1 count=20 2> /dev/null | "${LZIP}" -tq if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -t "${in_lz}" || fail=1 +printf "\ntesting decompression..." + +"${LZIP}" -t "${in_lz}" +if [ $? = 0 ] ; then printf . ; else printf - ; fail=1 ; fi "${LZIP}" -cd "${in_lz}" > copy || fail=1 cmp in copy || fail=1 printf . +rm -f copy cat "${in_lz}" > copy.lz || framework_failure -printf "to be overwritten" > copy || framework_failure -"${LZIP}" -df copy.lz || fail=1 +"${LZIP}" -dk copy.lz || fail=1 cmp in copy || fail=1 -printf . +printf "to be overwritten" > copy || framework_failure +"${LZIP}" -dq copy.lz +if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -df copy.lz +if [ $? = 0 ] && [ ! -e copy.lz ] && cmp in copy ; then + printf . ; else printf - ; fail=1 ; fi printf "to be overwritten" > copy || framework_failure "${LZIP}" -df -o copy < "${in_lz}" || fail=1 cmp in copy || fail=1 printf . +rm -f copy cat "${in_lz}" > anyothername || framework_failure -"${LZIP}" -d anyothername || fail=1 -cmp in anyothername.out || fail=1 -printf . +"${LZIP}" -d -o copy - anyothername - < "${in_lz}" +if [ $? = 0 ] && cmp in copy && cmp in anyothername.out ; then + printf . ; else printf - ; fail=1 ; fi +rm -f copy anyothername.out + +"${LZIP}" -tq in "${in_lz}" +if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -tq foo.lz "${in_lz}" +if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -cdq in "${in_lz}" > copy +if [ $? = 2 ] && cat copy in | cmp in - ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -cdq foo.lz "${in_lz}" > copy +if [ $? = 1 ] && cmp in copy ; then printf . ; else printf - ; fail=1 ; fi +rm -f copy +cat "${in_lz}" > copy.lz || framework_failure +"${LZIP}" -dq in copy.lz +if [ $? = 2 ] && [ -e copy.lz ] && [ ! -e copy ] && [ ! -e in.out ] ; then + printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -dq foo.lz copy.lz +if [ $? = 1 ] && [ ! -e copy.lz ] && [ ! -e foo ] && cmp in copy ; then + printf . ; else printf - ; fail=1 ; fi cat in in > in2 || framework_failure cat "${in_lz}" "${in_lz}" > copy2.lz || framework_failure @@ -80,12 +113,20 @@ cmp in2 copy2 || fail=1 printf . printf "garbage" >> copy2.lz || framework_failure +rm -f copy2 +"${LZIP}" -atq copy2.lz +if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -atq < copy2.lz +if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -adkq copy2.lz +if [ $? = 2 ] && [ ! -e copy2 ] ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -adkq -o copy2 < copy2.lz +if [ $? = 2 ] && [ ! -e copy2 ] ; then printf . ; else printf - ; fail=1 ; fi printf "to be overwritten" > copy2 || framework_failure "${LZIP}" -df copy2.lz || fail=1 cmp in2 copy2 || fail=1 printf . -rm -f copy for i in 12 4096 4Ki 29 512KiB ; do printf "to be overwritten" > copy || framework_failure "${LZIP}" -df -u$i -o copy < "${in_lz}" || fail=1 From ff22cd65e09e559bc8916e70ac5a3c5a8acd8a2b Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 17 Feb 2025 22:23:33 +0100 Subject: [PATCH 02/10] Adding upstream version 1.9. Signed-off-by: Daniel Baumann --- ChangeLog | 13 +- INSTALL | 2 +- Makefile.in | 13 +- NEWS | 28 ++--- README | 12 +- carg_parser.c | 9 +- carg_parser.h | 2 +- configure | 21 ++-- decoder.c | 108 ++++++++++------- decoder.h | 273 +++++++++++++++++++----------------------- doc/lunzip.1 | 9 +- file_index.c | 268 +++++++++++++++++++++++++++++++++++++++++ file_index.h | 90 ++++++++++++++ list.c | 123 +++++++++++++++++++ lzip.h | 33 ++++- main.c | 213 ++++++++++++++++++-------------- testsuite/check.sh | 199 ++++++++++++++++++++---------- testsuite/test.txt.lz | Bin 7376 -> 7376 bytes 18 files changed, 1019 insertions(+), 397 deletions(-) create mode 100644 file_index.c create mode 100644 file_index.h create mode 100644 list.c diff --git a/ChangeLog b/ChangeLog index 89bdf05..83c4648 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2017-04-13 Antonio Diaz Diaz + + * Version 1.9 released. + * The option '-l, --list' has been ported from lziprecover. + * Don't allow mixing different operations (-d, -l or -t). + * Decompression time has been reduced by 7%. + * main.c: Continue testing if any input file is a terminal. + * main.c: Show trailing data in both hexadecimal and ASCII. + * file_index.c: Improve detection of bad dict and trailing data. + * lzip.h: Unified messages for bad magic, trailing data, etc. + 2016-05-12 Antonio Diaz Diaz * Version 1.8 released. @@ -69,7 +80,7 @@ * Created from the decompression code of clzip 1.1. -Copyright (C) 2010-2016 Antonio Diaz Diaz. +Copyright (C) 2010-2017 Antonio Diaz Diaz. This file is a collection of facts, and thus it is not copyrightable, but just in case, you have unlimited permission to copy, distribute and diff --git a/INSTALL b/INSTALL index 89ff941..59892e4 100644 --- a/INSTALL +++ b/INSTALL @@ -61,7 +61,7 @@ After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2010-2016 Antonio Diaz Diaz. +Copyright (C) 2010-2017 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute and modify it. diff --git a/Makefile.in b/Makefile.in index e4ff366..4c0d3a5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -7,13 +7,14 @@ INSTALL_DIR = $(INSTALL) -d -m 755 SHELL = /bin/sh CAN_RUN_INSTALLINFO = $(SHELL) -c "install-info --version" > /dev/null 2>&1 -objs = carg_parser.o decoder.o main.o +objs = carg_parser.o file_index.o list.o decoder.o main.o .PHONY : all install install-bin install-info install-man \ install-strip install-compress install-strip-compress \ install-bin-strip install-info-compress install-man-compress \ - install-as-lzip uninstall uninstall-bin uninstall-info uninstall-man \ + install-as-lzip \ + uninstall uninstall-bin uninstall-info uninstall-man \ doc info man check dist clean distclean all : $(progname) @@ -30,6 +31,8 @@ main.o : main.c $(objs) : Makefile carg_parser.o : carg_parser.h decoder.o : lzip.h decoder.h +file_index.o : lzip.h file_index.h +list.o : lzip.h file_index.h main.o : carg_parser.h lzip.h decoder.h @@ -112,11 +115,11 @@ dist : doc $(DISTNAME)/README \ $(DISTNAME)/configure \ $(DISTNAME)/doc/$(progname).1 \ + $(DISTNAME)/*.h \ + $(DISTNAME)/*.c \ $(DISTNAME)/testsuite/check.sh \ $(DISTNAME)/testsuite/test.txt \ - $(DISTNAME)/testsuite/test.txt.lz \ - $(DISTNAME)/*.h \ - $(DISTNAME)/*.c + $(DISTNAME)/testsuite/test.txt.lz rm -f $(DISTNAME) lzip -v -9 $(DISTNAME).tar diff --git a/NEWS b/NEWS index 8727a19..57ec920 100644 --- a/NEWS +++ b/NEWS @@ -1,24 +1,14 @@ -Changes in version 1.8: +Changes in version 1.9: -The option "-a, --trailing-error", which makes lunzip exit with error -status 2 if any remaining input is detected after decompressing the last -member, has been added. +The option '-l, --list' has been ported from lziprecover. -Lunzip now verifies that the output file is regular when "low memory" -mode is requested. +It is now an error to specify two or more different operations in the +command line (--decompress, --list or --test). -Up to 6 bytes of trailing data are printed if "-vvvv" is specified. +Decompression time has been reduced by 7%. -The test of the value remaining in the range decoder has been removed. -(After extensive testing it has been found useless to detect corruption -in the decompressed data. Eliminating it reduces the number of false -positives for corruption and makes error detection more accurate). +In test mode, lunzip now continues checking the rest of the files if any +input file is a terminal. -When decompressing, the file specified with the '--output' option is now -deleted if the input is a terminal. - -Some error messages have been adjusted to be identical to those of -lzip-1.18. - -A harmless check failure on Windows, caused by the failed comparison of -a message in text mode, has been fixed. +Trailing data are now shown both in hexadecimal and as a string of +printable ASCII characters. diff --git a/README b/README index 793bfb1..ccd9b7b 100644 --- a/README +++ b/README @@ -16,11 +16,11 @@ availability: merging of damaged copies of a file. * The lzip format is as simple as possible (but not simpler). The - lzip manual provides the code of a simple decompressor along with a - detailed explanation of how it works, so that with the only help of - the lzip manual it would be possible for a digital archaeologist to - extract the data from a lzip file long after quantum computers - eventually render LZMA obsolete. + lzip manual provides the source code of a simple decompressor along + with a detailed explanation of how it works, so that with the only + help of the lzip manual it would be possible for a digital + archaeologist to extract the data from a lzip file long after + quantum computers eventually render LZMA obsolete. * Additionally the lzip reference implementation is copylefted, which guarantees that it will remain free forever. @@ -83,7 +83,7 @@ range encoding), Igor Pavlov (for putting all the above together in LZMA), and Julian Seward (for bzip2's CLI). -Copyright (C) 2010-2016 Antonio Diaz Diaz. +Copyright (C) 2010-2017 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute and modify it. diff --git a/carg_parser.c b/carg_parser.c index 3d4e89f..6850643 100644 --- a/carg_parser.c +++ b/carg_parser.c @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2016 Antonio Diaz Diaz. + Copyright (C) 2006-2017 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided @@ -94,7 +94,7 @@ static char parse_long_option( struct Arg_parser * const ap, else if( index < 0 ) index = i; /* First nonexact match found */ else if( options[index].code != options[i].code || options[index].has_arg != options[i].has_arg ) - ambig = 1; /* Second or later nonexact match found */ + ambig = 1; /* Second or later nonexact match found */ } if( ambig && !exact ) @@ -230,7 +230,9 @@ char ap_init( struct Arg_parser * const ap, } else { - if( !in_order ) + if( in_order ) + { if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; } + else { void * tmp = ap_resize_buffer( non_options, ( non_options_size + 1 ) * sizeof *non_options ); @@ -238,7 +240,6 @@ char ap_init( struct Arg_parser * const ap, non_options = (const char **)tmp; non_options[non_options_size++] = argv[argind++]; } - else if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; } } if( ap->error ) free_data( ap ); diff --git a/carg_parser.h b/carg_parser.h index e918942..c4ce31d 100644 --- a/carg_parser.h +++ b/carg_parser.h @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2016 Antonio Diaz Diaz. + Copyright (C) 2006-2017 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/configure b/configure index 37541c5..53fcaae 100755 --- a/configure +++ b/configure @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2016 Antonio Diaz Diaz. +# Copyright (C) 2010-2017 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission # to copy, distribute and modify it. pkgname=lunzip -pkgversion=1.8 +pkgversion=1.9 progname=lunzip srctrigger=doc/${progname}.1 @@ -26,11 +26,11 @@ CFLAGS='-Wall -W -O2' LDFLAGS= # checking whether we are using GNU C. -if /bin/sh -c "${CC} --version" > /dev/null 2>&1 ; then true -else +/bin/sh -c "${CC} --version" > /dev/null 2>&1 || + { CC=cc - CFLAGS='-W -O2' -fi + CFLAGS=-O2 + } # Loop over all args args= @@ -52,9 +52,12 @@ while [ $# != 0 ] ; do # Process the options case ${option} in --help | -h) - echo "Usage: configure [options]" + echo "Usage: $0 [OPTION]... [VAR=VALUE]..." echo - echo "Options: [defaults in brackets]" + echo "To assign makefile variables (e.g., CC, CFLAGS...), specify them as" + echo "arguments to configure in the form VAR=VALUE." + echo + echo "Options and variables: [defaults in brackets]" echo " -h, --help display this help and exit" echo " -V, --version output version information and exit" echo " --srcdir=DIR find the sources in DIR [. or ..]" @@ -165,7 +168,7 @@ echo "LDFLAGS = ${LDFLAGS}" rm -f Makefile cat > Makefile << EOF # Makefile for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2016 Antonio Diaz Diaz. +# Copyright (C) 2010-2017 Antonio Diaz Diaz. # This file was generated automatically by configure. Don't edit. # # This Makefile is free software: you have unlimited permission diff --git a/decoder.c b/decoder.c index 20364c0..61777b5 100644 --- a/decoder.c +++ b/decoder.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2016 Antonio Diaz Diaz. + Copyright (C) 2010-2017 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 @@ -53,7 +53,7 @@ void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ) /* Returns the number of bytes really read. If (returned value < size) and (errno == 0), means EOF was reached. */ -static int readblock( const int fd, uint8_t * const buf, const int size ) +int readblock( const int fd, uint8_t * const buf, const int size ) { int sz = 0; errno = 0; @@ -87,10 +87,10 @@ static int writeblock( const int fd, const uint8_t * const buf, const int size ) } -int seek_read( const int fd, uint8_t * const buf, const int size, - const int offset ) +unsigned seek_read_back( const int fd, uint8_t * const buf, const int size, + const int offset ) { - if( lseek( fd, offset, SEEK_END ) >= 0 ) + if( lseek( fd, -offset, SEEK_END ) >= 0 ) return readblock( fd, buf, size ); return 0; } @@ -121,8 +121,9 @@ void LZd_flush_data( struct LZ_decoder * const d ) writeblock( d->outfd, d->buffer + d->stream_pos, size ) != size ) { show_error( "Write error", errno, false ); cleanup_and_fail( 1 ); } if( d->pos >= d->buffer_size ) - { d->partial_data_pos += d->pos; d->pos = 0; - if( d->partial_data_pos >= d->dictionary_size ) d->pos_wrapped = true; } + { d->partial_data_pos += d->pos; d->pos = 0; d->pos_wrapped = true; + if( d->partial_data_pos >= d->dictionary_size ) + d->pos_wrapped_dic = true; } d->stream_pos = d->pos; } } @@ -185,7 +186,7 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, ( 8.0 * member_size ) / data_size, 100.0 * ( 1.0 - ( (double)member_size / data_size ) ) ); if( !error && verbosity >= 4 ) - fprintf( stderr, "data CRC %08X, data size %9llu, member size %8llu. ", + fprintf( stderr, "CRC %08X, decompressed %9llu, compressed %8llu. ", LZd_crc( d ), data_size, member_size ); return !error; } @@ -198,49 +199,77 @@ int LZd_decode_member( struct LZ_decoder * const d, { struct Range_decoder * const rdec = d->rdec; void (* const copy_block) - ( struct LZ_decoder * const d, const int distance, int len ) = - ( (unsigned)d->buffer_size >= d->dictionary_size ) ? + ( struct LZ_decoder * const d, const unsigned distance, unsigned len ) = + ( d->buffer_size >= d->dictionary_size ) ? &LZd_copy_block : &LZd_copy_block2; + Bit_model bm_literal[1<bm_match[state][pos_state] ) == 0 ) /* 1st bit */ + if( Rd_decode_bit( rdec, &bm_match[state][pos_state] ) == 0 ) /* 1st bit */ { - const uint8_t prev_byte = LZd_peek_prev( d ); + Bit_model * const bm = bm_literal[get_lit_state(LZd_peek_prev( d ))]; if( St_is_char( state ) ) { state -= ( state < 4 ) ? state : 3; - LZd_put_byte( d, Rd_decode_tree( rdec, - d->bm_literal[get_lit_state(prev_byte)], 8 ) ); + LZd_put_byte( d, Rd_decode_tree8( rdec, bm ) ); } else { state -= ( state < 10 ) ? 3 : 6; - LZd_put_byte( d, Rd_decode_matched( rdec, - d->bm_literal[get_lit_state(prev_byte)], - LZd_peek( d, rep0 ) ) ); + LZd_put_byte( d, Rd_decode_matched( rdec, bm, LZd_peek( d, rep0 ) ) ); } } else /* match or repeated match */ { int len; - if( Rd_decode_bit( rdec, &d->bm_rep[state] ) != 0 ) /* 2nd bit */ + if( Rd_decode_bit( rdec, &bm_rep[state] ) != 0 ) /* 2nd bit */ { - if( Rd_decode_bit( rdec, &d->bm_rep0[state] ) != 0 ) /* 3rd bit */ + if( Rd_decode_bit( rdec, &bm_rep0[state] ) == 0 ) /* 3rd bit */ + { + if( Rd_decode_bit( rdec, &bm_len[state][pos_state] ) == 0 ) /* 4th bit */ + { state = St_set_short_rep( state ); + LZd_put_byte( d, LZd_peek( d, rep0 ) ); continue; } + } + else { unsigned distance; - if( Rd_decode_bit( rdec, &d->bm_rep1[state] ) == 0 ) /* 4th bit */ + if( Rd_decode_bit( rdec, &bm_rep1[state] ) == 0 ) /* 4th bit */ distance = rep1; else { - if( Rd_decode_bit( rdec, &d->bm_rep2[state] ) == 0 ) /* 5th bit */ + if( Rd_decode_bit( rdec, &bm_rep2[state] ) == 0 ) /* 5th bit */ distance = rep2; else { distance = rep3; rep3 = rep2; } @@ -249,36 +278,29 @@ int LZd_decode_member( struct LZ_decoder * const d, rep1 = rep0; rep0 = distance; } - else - { - if( Rd_decode_bit( rdec, &d->bm_len[state][pos_state] ) == 0 ) /* 4th bit */ - { 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, &d->rep_len_model, pos_state ); + len = min_match_len + Rd_decode_len( rdec, &rep_len_model, pos_state ); } else /* match */ { - int dis_slot; - const unsigned rep0_saved = rep0; - len = min_match_len + Rd_decode_len( rdec, &d->match_len_model, pos_state ); - dis_slot = Rd_decode_tree6( rdec, d->bm_dis_slot[get_len_state(len)] ); - if( dis_slot < start_dis_model ) rep0 = dis_slot; - else + unsigned distance; + len = min_match_len + Rd_decode_len( rdec, &match_len_model, pos_state ); + distance = Rd_decode_tree6( rdec, bm_dis_slot[get_len_state(len)] ); + if( distance >= start_dis_model ) { + const unsigned dis_slot = distance; const int direct_bits = ( dis_slot >> 1 ) - 1; - rep0 = ( 2 | ( dis_slot & 1 ) ) << direct_bits; + distance = ( 2 | ( dis_slot & 1 ) ) << direct_bits; if( dis_slot < end_dis_model ) - rep0 += Rd_decode_tree_reversed( rdec, - d->bm_dis + rep0 - dis_slot - 1, direct_bits ); + distance += Rd_decode_tree_reversed( rdec, + bm_dis + ( distance - dis_slot ), direct_bits ); else { - rep0 += Rd_decode( rdec, direct_bits - dis_align_bits ) << dis_align_bits; - rep0 += Rd_decode_tree_reversed4( rdec, d->bm_align ); - if( rep0 == 0xFFFFFFFFU ) /* marker found */ + distance += + Rd_decode( rdec, direct_bits - dis_align_bits ) << dis_align_bits; + distance += Rd_decode_tree_reversed4( rdec, bm_align ); + if( distance == 0xFFFFFFFFU ) /* marker found */ { - rep0 = rep0_saved; Rd_normalize( rdec ); LZd_flush_data( d ); if( len == min_match_len ) /* End Of Stream marker */ @@ -298,10 +320,10 @@ int LZd_decode_member( struct LZ_decoder * const d, } } } - rep3 = rep2; rep2 = rep1; rep1 = rep0_saved; + rep3 = rep2; rep2 = rep1; rep1 = rep0; rep0 = distance; state = St_set_match( state ); if( rep0 >= d->dictionary_size || - ( rep0 >= LZd_data_position( d ) && !d->pos_wrapped ) ) + ( rep0 >= LZd_data_position( d ) && !d->pos_wrapped_dic ) ) { LZd_flush_data( d ); return 1; } } copy_block( d, rep0, len ); diff --git a/decoder.h b/decoder.h index 922426c..e3a4f1c 100644 --- a/decoder.h +++ b/decoder.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2016 Antonio Diaz Diaz. + Copyright (C) 2010-2017 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 @@ -56,7 +56,7 @@ 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 = -rdec->pos; } + { rdec->partial_member_pos = 0; rdec->partial_member_pos -= rdec->pos; } static inline uint8_t Rd_get_byte( struct Range_decoder * const rdec ) { @@ -68,23 +68,22 @@ static inline uint8_t Rd_get_byte( struct Range_decoder * const rdec ) static inline int Rd_read_data( struct Range_decoder * const rdec, uint8_t * const outbuf, const int size ) { - int rest = size; - while( rest > 0 && !Rd_finished( rdec ) ) + int sz = 0; + while( sz < size && !Rd_finished( rdec ) ) { - const int rd = min( rest, rdec->stream_pos - rdec->pos ); - memcpy( outbuf + size - rest, rdec->buffer + rdec->pos, rd ); + const int rd = min( size - sz, rdec->stream_pos - rdec->pos ); + memcpy( outbuf + sz, rdec->buffer + rdec->pos, rd ); rdec->pos += rd; - rest -= rd; + sz += rd; } - return size - rest; + return sz; } static inline 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 ); + for( i = 0; i < 5; ++i ) rdec->code = (rdec->code << 8) | Rd_get_byte( rdec ); rdec->range = 0xFFFFFFFFU; rdec->code &= rdec->range; /* make sure that first byte is discarded */ } @@ -92,34 +91,30 @@ static inline void Rd_load( struct Range_decoder * const rdec ) static 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 ); - } + { rdec->range <<= 8; rdec->code = (rdec->code << 8) | Rd_get_byte( rdec ); } } -static inline int Rd_decode( struct Range_decoder * const rdec, - const int num_bits ) +static inline unsigned Rd_decode( struct Range_decoder * const rdec, + const int num_bits ) { - int symbol = 0; + unsigned symbol = 0; int i; for( i = num_bits; i > 0; --i ) { - uint32_t mask; + bool bit; 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); + bit = ( rdec->code >= rdec->range ); + symbol = ( symbol << 1 ) + bit; + rdec->code -= rdec->range & ( 0U - bit ); } return symbol; } -static inline int Rd_decode_bit( struct Range_decoder * const rdec, - Bit_model * const probability ) +static inline unsigned Rd_decode_bit( struct Range_decoder * const rdec, + Bit_model * const probability ) { uint32_t bound; Rd_normalize( rdec ); @@ -139,20 +134,20 @@ static inline int Rd_decode_bit( struct Range_decoder * const rdec, } } -static inline int Rd_decode_tree( struct Range_decoder * const rdec, - Bit_model bm[], const int num_bits ) +static inline unsigned Rd_decode_tree3( struct Range_decoder * const rdec, + Bit_model bm[] ) { - int symbol = 1; - int i; - for( i = num_bits; i > 0; --i ) - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); - return symbol - (1 << num_bits); + unsigned symbol = 1; + symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); + symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); + symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); + return symbol & 7; } -static inline int Rd_decode_tree6( struct Range_decoder * const rdec, - Bit_model bm[] ) +static inline unsigned Rd_decode_tree6( struct Range_decoder * const rdec, + Bit_model bm[] ) { - int symbol = 1; + unsigned symbol = 1; symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); @@ -162,69 +157,69 @@ static inline int Rd_decode_tree6( struct Range_decoder * const rdec, return symbol & 0x3F; } -static inline int Rd_decode_tree_reversed( struct Range_decoder * const rdec, - Bit_model bm[], const int num_bits ) +static inline unsigned Rd_decode_tree8( struct Range_decoder * const rdec, + Bit_model bm[] ) { - int model = 1; - int symbol = 0; + unsigned symbol = 1; int i; - for( i = 0; i < num_bits; ++i ) - { - const bool bit = Rd_decode_bit( rdec, &bm[model] ); - model <<= 1; - if( bit ) { ++model; symbol |= (1 << i); } - } - return symbol; - } - -static inline int Rd_decode_tree_reversed4( struct Range_decoder * const rdec, - Bit_model bm[] ) - { - int model = 1; - int symbol = Rd_decode_bit( rdec, &bm[model] ); - int bit; - model = (model << 1) + symbol; - bit = Rd_decode_bit( rdec, &bm[model] ); - model = (model << 1) + bit; symbol |= (bit << 1); - bit = Rd_decode_bit( rdec, &bm[model] ); - model = (model << 1) + bit; symbol |= (bit << 2); - if( Rd_decode_bit( rdec, &bm[model] ) ) symbol |= 8; - return symbol; - } - -static inline int Rd_decode_matched( struct Range_decoder * const rdec, - Bit_model bm[], int match_byte ) - { - Bit_model * const bm1 = bm + 0x100; - int symbol = 1; - while( symbol < 0x100 ) - { - int match_bit, bit; - match_byte <<= 1; - match_bit = match_byte & 0x100; - bit = Rd_decode_bit( rdec, &bm1[match_bit+symbol] ); - symbol = ( symbol << 1 ) | bit; - if( match_bit != bit << 8 ) - { - while( symbol < 0x100 ) - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); - break; - } - } + for( i = 0; i < 8; ++i ) + symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); return symbol & 0xFF; } -static inline int Rd_decode_len( struct Range_decoder * const rdec, - struct Len_model * const lm, - const int pos_state ) +static inline unsigned +Rd_decode_tree_reversed( struct Range_decoder * const rdec, + Bit_model bm[], const int num_bits ) + { + unsigned model = 1; + unsigned symbol = 0; + int i; + for( i = 0; i < num_bits; ++i ) + { + const unsigned bit = Rd_decode_bit( rdec, &bm[model] ); + model = ( model << 1 ) + bit; + symbol |= ( bit << i ); + } + return symbol; + } + +static inline unsigned +Rd_decode_tree_reversed4( struct Range_decoder * const rdec, Bit_model bm[] ) + { + unsigned symbol = Rd_decode_bit( rdec, &bm[1] ); + unsigned model = 2 + symbol; + unsigned bit = Rd_decode_bit( rdec, &bm[model] ); + model = ( model << 1 ) + bit; symbol |= ( bit << 1 ); + bit = Rd_decode_bit( rdec, &bm[model] ); + model = ( model << 1 ) + bit; symbol |= ( bit << 2 ); + symbol |= ( Rd_decode_bit( rdec, &bm[model] ) << 3 ); + return symbol; + } + +static inline unsigned Rd_decode_matched( struct Range_decoder * const rdec, + Bit_model bm[], unsigned match_byte ) + { + unsigned symbol = 1; + unsigned mask = 0x100; + while( true ) + { + const unsigned match_bit = ( match_byte <<= 1 ) & mask; + const unsigned bit = Rd_decode_bit( rdec, &bm[symbol+match_bit+mask] ); + symbol = ( symbol << 1 ) + bit; + if( symbol > 0xFF ) return symbol & 0xFF; + mask &= ~(match_bit ^ (bit << 8)); /* if( match_bit != bit ) mask = 0; */ + } + } + +static inline unsigned 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 ); + return Rd_decode_tree3( rdec, lm->bm_low[pos_state] ); 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 ); + return len_low_symbols + Rd_decode_tree3( rdec, lm->bm_mid[pos_state] ); + return len_low_symbols + len_mid_symbols + Rd_decode_tree8( rdec, lm->bm_high ); } @@ -233,49 +228,37 @@ struct LZ_decoder unsigned long long partial_data_pos; struct Range_decoder * rdec; unsigned dictionary_size; - int buffer_size; + unsigned buffer_size; uint8_t * buffer; /* output buffer */ - int pos; /* current pos in buffer */ - int stream_pos; /* first byte not yet written to file */ + unsigned pos; /* current pos in buffer */ + unsigned stream_pos; /* first byte not yet written to file */ uint32_t crc; int outfd; /* output file descriptor */ bool pos_wrapped; - - Bit_model bm_literal[1<pos > 0 ) ? d->pos : d->buffer_size ) - 1; - return d->buffer[i]; + if( d->pos > 0 ) return d->buffer[d->pos-1]; + if( d->pos_wrapped ) return d->buffer[d->buffer_size-1]; + return 0; /* prev_byte of first byte */ } static inline uint8_t LZd_peek( const struct LZ_decoder * const d, - const int distance ) + const unsigned distance ) { uint8_t b; - const int i = d->pos - distance - 1; - if( i >= 0 ) b = d->buffer[i]; - else if( i + d->buffer_size >= d->pos ) - b = d->buffer[i+d->buffer_size]; - else if( seek_read( d->outfd, &b, 1, i - d->stream_pos ) != 1 ) + if( d->pos > distance ) b = d->buffer[d->pos-distance-1]; + else if( d->buffer_size > distance ) + b = d->buffer[d->buffer_size+d->pos-distance-1]; + else if( seek_read_back( d->outfd, &b, 1, + distance + 1 + d->stream_pos - d->pos ) != 1 ) { show_error( "Seek error", errno, false ); cleanup_and_fail( 1 ); } return b; } @@ -287,19 +270,28 @@ static inline void LZd_put_byte( struct LZ_decoder * const d, const uint8_t b ) } static inline void LZd_copy_block( struct LZ_decoder * const d, - const int distance, int len ) + const unsigned distance, unsigned len ) { - int i = d->pos - distance - 1; - bool fast; - if( i < 0 ) - { i += d->buffer_size; - fast = ( len <= d->buffer_size - i && len <= i - d->pos ); } - else - fast = ( len < d->buffer_size - d->pos && len <= d->pos - i ); - if( fast ) /* no wrap, no overlap */ + unsigned lpos = d->pos, i = lpos - distance - 1; + bool fast, fast2; + if( lpos > distance ) + { + fast = ( len < d->buffer_size - lpos ); + fast2 = ( fast && len <= lpos - i ); + } + else + { + i += d->buffer_size; + fast = ( len < d->buffer_size - i ); /* (i == pos) may happen */ + fast2 = ( fast && len <= i - lpos ); + } + if( fast ) /* no wrap */ { - memcpy( d->buffer + d->pos, d->buffer + i, len ); d->pos += len; + if( fast2 ) /* no wrap, no overlap */ + memcpy( d->buffer + lpos, d->buffer + i, len ); + else + for( ; len > 0; --len ) d->buffer[lpos++] = d->buffer[i++]; } else for( ; len > 0; --len ) { @@ -310,16 +302,16 @@ static inline void LZd_copy_block( struct LZ_decoder * const d, } static inline void LZd_copy_block2( struct LZ_decoder * const d, - const int distance, int len ) + const unsigned distance, unsigned len ) { - if( distance < d->buffer_size ) /* block is in buffer */ + if( d->buffer_size > distance ) /* block is in buffer */ { LZd_copy_block( d, distance, len ); return; } if( len < d->buffer_size - d->pos ) /* no wrap */ { - const int offset = d->pos - d->stream_pos - distance - 1; - if( len <= -offset ) /* block is in file */ + const unsigned offset = distance + 1 + d->stream_pos - d->pos; + if( len <= offset ) /* block is in file */ { - if( seek_read( d->outfd, d->buffer + d->pos, len, offset ) != len ) + if( seek_read_back( d->outfd, d->buffer + d->pos, len, offset ) != len ) { show_error( "Seek error", errno, false ); cleanup_and_fail( 1 ); } d->pos += len; return; @@ -331,8 +323,8 @@ static inline void LZd_copy_block2( struct LZ_decoder * const d, static inline bool LZd_init( struct LZ_decoder * const d, struct Range_decoder * const rde, - const int buffer_size, - const int dict_size, const int ofd ) + const unsigned buffer_size, + const unsigned dict_size, const int ofd ) { d->partial_data_pos = 0; d->rdec = rde; @@ -345,20 +337,7 @@ static inline bool LZd_init( struct LZ_decoder * const d, d->crc = 0xFFFFFFFFU; d->outfd = ofd; d->pos_wrapped = false; - - Bm_array_init( d->bm_literal[0], (1 << literal_context_bits) * 0x300 ); - Bm_array_init( d->bm_match[0], states * pos_states ); - Bm_array_init( d->bm_rep, states ); - Bm_array_init( d->bm_rep0, states ); - Bm_array_init( d->bm_rep1, states ); - Bm_array_init( d->bm_rep2, states ); - Bm_array_init( d->bm_len[0], states * pos_states ); - Bm_array_init( d->bm_dis_slot[0], len_states * (1 << dis_slot_bits) ); - Bm_array_init( d->bm_dis, modeled_distances - end_dis_model ); - Bm_array_init( d->bm_align, dis_align_size ); - Lm_init( &d->match_len_model ); - Lm_init( &d->rep_len_model ); - d->buffer[d->buffer_size-1] = 0; /* prev_byte of first byte */ + d->pos_wrapped_dic = false; return true; } diff --git a/doc/lunzip.1 b/doc/lunzip.1 index 53b3faf..c83f901 100644 --- a/doc/lunzip.1 +++ b/doc/lunzip.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. -.TH LUNZIP "1" "May 2016" "lunzip 1.8" "User Commands" +.TH LUNZIP "1" "April 2017" "lunzip 1.9" "User Commands" .SH NAME lunzip \- decompressor for the lzip format .SH SYNOPSIS @@ -44,6 +44,9 @@ overwrite existing output files \fB\-k\fR, \fB\-\-keep\fR keep (don't delete) input files .TP +\fB\-l\fR, \fB\-\-list\fR +print (un)compressed file sizes +.TP \fB\-o\fR, \fB\-\-output=\fR if reading standard input, write to .TP @@ -63,6 +66,8 @@ If no file names are given, or if a file is '\-', lunzip decompresses from standard input to standard output. Numbers may be followed by a multiplier: k = kB = 10^3 = 1000, Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc... +Buffer sizes 12 to 29 are interpreted as powers of two, meaning 2^12 +to 2^29 bytes. .PP Exit status: 0 for a normal exit, 1 for environmental problems (file not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or @@ -73,7 +78,7 @@ Report bugs to lzip\-bug@nongnu.org .br Lunzip home page: http://www.nongnu.org/lzip/lunzip.html .SH COPYRIGHT -Copyright \(co 2016 Antonio Diaz Diaz. +Copyright \(co 2017 Antonio Diaz Diaz. License GPLv2+: GNU GPL version 2 or later .br This is free software: you are free to change and redistribute it. diff --git a/file_index.c b/file_index.c new file mode 100644 index 0000000..e737608 --- /dev/null +++ b/file_index.c @@ -0,0 +1,268 @@ +/* Lunzip - Decompressor for the lzip format + Copyright (C) 2010-2017 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 . +*/ + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include + +#include "lzip.h" +#include "file_index.h" + + +static int seek_read( const int fd, uint8_t * const buf, const int size, + const long long pos ) + { + if( lseek( fd, pos, SEEK_SET ) == pos ) + return readblock( fd, buf, size ); + return 0; + } + + +static bool add_error( struct File_index * const fi, const char * const msg ) + { + const int len = strlen( msg ); + void * tmp = resize_buffer( fi->error, fi->error_size + len + 1 ); + if( !tmp ) return false; + fi->error = (char *)tmp; + strncpy( fi->error + fi->error_size, msg, len + 1 ); + fi->error_size += len; + return true; + } + + +static bool push_back_member( struct File_index * const fi, + const long long dp, const long long ds, + const long long mp, const long long ms, + const unsigned dict_size ) + { + struct Member * p; + void * tmp = resize_buffer( fi->member_vector, + ( fi->members + 1 ) * sizeof fi->member_vector[0] ); + if( !tmp ) + { add_error( fi, "Not enough memory." ); fi->retval = 1; return false; } + fi->member_vector = (struct Member *)tmp; + p = &(fi->member_vector[fi->members]); + init_member( p, dp, ds, mp, ms, dict_size ); + ++fi->members; + return true; + } + + +static void Fi_free_member_vector( struct File_index * const fi ) + { + if( fi->member_vector ) + { free( fi->member_vector ); fi->member_vector = 0; } + fi->members = 0; + } + + +static void Fi_reverse_member_vector( struct File_index * const fi ) + { + struct Member tmp; + long i; + for( i = 0; i < fi->members / 2; ++i ) + { + tmp = fi->member_vector[i]; + fi->member_vector[i] = fi->member_vector[fi->members-i-1]; + fi->member_vector[fi->members-i-1] = tmp; + } + } + + +static void Fi_set_errno_error( struct File_index * const fi, + const char * const msg ) + { + add_error( fi, msg ); add_error( fi, strerror( errno ) ); + fi->retval = 1; + } + +static void Fi_set_num_error( struct File_index * const fi, + const char * const msg, unsigned long long num ) + { + char buf[80]; + snprintf( buf, sizeof buf, "%s%llu", msg, num ); + add_error( fi, buf ); + fi->retval = 2; + } + + +/* If successful, push last member and set pos to member header. */ +static bool Fi_skip_trailing_data( struct File_index * const fi, + const int fd, long long * const pos ) + { + enum { block_size = 16384, + buffer_size = block_size + Ft_size - 1 + Fh_size }; + uint8_t buffer[buffer_size]; + int bsize = *pos % block_size; /* total bytes in buffer */ + int search_size, rd_size; + unsigned long long ipos; + int i; + if( bsize <= buffer_size - block_size ) bsize += block_size; + search_size = bsize; /* bytes to search for trailer */ + rd_size = bsize; /* bytes to read from file */ + ipos = *pos - rd_size; /* aligned to block_size */ + if( *pos < min_member_size ) return false; + + while( true ) + { + const uint8_t max_msb = ( ipos + search_size ) >> 56; + if( seek_read( fd, buffer, rd_size, ipos ) != rd_size ) + { Fi_set_errno_error( fi, "Error seeking member trailer: " ); + return false; } + for( i = search_size; i >= Ft_size; --i ) + if( buffer[i-1] <= max_msb ) /* most significant byte of member_size */ + { + File_header header; + File_trailer * trailer = (File_trailer *)( buffer + i - Ft_size ); + const unsigned long long member_size = Ft_get_member_size( *trailer ); + unsigned dictionary_size; + if( member_size == 0 ) + { while( i > Ft_size && buffer[i-9] == 0 ) --i; continue; } + if( member_size < min_member_size || member_size > ipos + i ) + continue; + if( seek_read( fd, header, Fh_size, + ipos + i - member_size ) != Fh_size ) + { Fi_set_errno_error( fi, "Error reading member header: " ); + return false; } + dictionary_size = Fh_get_dictionary_size( header ); + if( !Fh_verify_magic( header ) || !Fh_verify_version( header ) || + !isvalid_ds( dictionary_size ) ) continue; + if( Fh_verify_prefix( buffer + i, bsize - i ) ) + { + add_error( fi, "Last member in input file is truncated or corrupt." ); + fi->retval = 2; return false; + } + *pos = ipos + i - member_size; + return push_back_member( fi, 0, Ft_get_data_size( *trailer ), *pos, + member_size, dictionary_size ); + } + if( ipos <= 0 ) + { Fi_set_num_error( fi, "Member size in trailer is corrupt at pos ", + *pos - 8 ); + return false; } + bsize = buffer_size; + search_size = bsize - Fh_size; + rd_size = block_size; + ipos -= rd_size; + memcpy( buffer + rd_size, buffer, buffer_size - rd_size ); + } + } + + +bool Fi_init( struct File_index * const fi, const int infd, + const bool ignore_trailing ) + { + File_header header; + long long pos; + long i; + fi->member_vector = 0; + fi->error = 0; + fi->isize = lseek( infd, 0, SEEK_END ); + fi->members = 0; + fi->error_size = 0; + fi->retval = 0; + if( fi->isize < 0 ) + { Fi_set_errno_error( fi, "Input file is not seekable: " ); return false; } + if( fi->isize < min_member_size ) + { add_error( fi, "Input file is too short." ); fi->retval = 2; + return false; } + if( fi->isize > INT64_MAX ) + { add_error( fi, "Input file is too long (2^63 bytes or more)." ); + fi->retval = 2; return false; } + + if( seek_read( infd, header, Fh_size, 0 ) != Fh_size ) + { Fi_set_errno_error( fi, "Error reading member header: " ); return false; } + if( !Fh_verify_magic( header ) ) + { add_error( fi, bad_magic_msg ); fi->retval = 2; return false; } + if( !Fh_verify_version( header ) ) + { add_error( fi, bad_version( Fh_version( header ) ) ); fi->retval = 2; + return false; } + if( !isvalid_ds( Fh_get_dictionary_size( header ) ) ) + { add_error( fi, bad_dict_msg ); fi->retval = 2; return false; } + + pos = fi->isize; /* always points to a header or to EOF */ + while( pos >= min_member_size ) + { + File_trailer trailer; + unsigned long long member_size; + unsigned dictionary_size; + if( seek_read( infd, trailer, Ft_size, pos - Ft_size ) != Ft_size ) + { Fi_set_errno_error( fi, "Error reading member trailer: " ); break; } + member_size = Ft_get_member_size( trailer ); + if( member_size < min_member_size || member_size > (unsigned long long)pos ) + { + if( fi->members > 0 ) + Fi_set_num_error( fi, "Member size in trailer is corrupt at pos ", + pos - 8 ); + else if( Fi_skip_trailing_data( fi, infd, &pos ) ) + { if( ignore_trailing ) continue; + add_error( fi, trailing_msg ); fi->retval = 2; return false; } + break; + } + if( seek_read( infd, header, Fh_size, pos - member_size ) != Fh_size ) + { Fi_set_errno_error( fi, "Error reading member header: " ); break; } + dictionary_size = Fh_get_dictionary_size( header ); + if( !Fh_verify_magic( header ) || !Fh_verify_version( header ) || + !isvalid_ds( dictionary_size ) ) + { + if( fi->members > 0 ) + Fi_set_num_error( fi, "Bad header at pos ", pos - member_size ); + else if( Fi_skip_trailing_data( fi, infd, &pos ) ) + { if( ignore_trailing ) continue; + add_error( fi, trailing_msg ); fi->retval = 2; return false; } + break; + } + pos -= member_size; + if( !push_back_member( fi, 0, Ft_get_data_size( trailer ), pos, + member_size, dictionary_size ) ) + return false; + } + if( pos != 0 || fi->members <= 0 ) + { + Fi_free_member_vector( fi ); + if( fi->retval == 0 ) + { add_error( fi, "Can't create file index." ); fi->retval = 2; } + return false; + } + Fi_reverse_member_vector( fi ); + for( i = 0; i < fi->members - 1; ++i ) + { + const long long end = block_end( fi->member_vector[i].dblock ); + if( end < 0 || end > INT64_MAX ) + { + Fi_free_member_vector( fi ); + add_error( fi, "Data in input file is too long (2^63 bytes or more)." ); + fi->retval = 2; return false; + } + fi->member_vector[i+1].dblock.pos = end; + } + return true; + } + + +void Fi_free( struct File_index * const fi ) + { + Fi_free_member_vector( fi ); + if( fi->error ) { free( fi->error ); fi->error = 0; } + fi->error_size = 0; + } diff --git a/file_index.h b/file_index.h new file mode 100644 index 0000000..38f0246 --- /dev/null +++ b/file_index.h @@ -0,0 +1,90 @@ +/* Lunzip - Decompressor for the lzip format + Copyright (C) 2010-2017 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 . +*/ + +#ifndef INT64_MAX +#define INT64_MAX 0x7FFFFFFFFFFFFFFFLL +#endif + + +struct Block + { + long long pos, size; /* pos + size <= INT64_MAX */ + }; + +static inline void init_block( struct Block * const b, + const long long p, const long long s ) + { b->pos = p; b->size = s; } + +static inline long long block_end( const struct Block b ) + { return b.pos + b.size; } + + +struct Member + { + struct Block dblock, mblock; /* data block, member block */ + unsigned dictionary_size; + }; + +static inline void init_member( struct Member * const m, + const long long dp, const long long ds, + const long long mp, const long long ms, + const unsigned dict_size ) + { init_block( &m->dblock, dp, ds ); init_block( &m->mblock, mp, ms ); + m->dictionary_size = dict_size; } + +struct File_index + { + struct Member * member_vector; + char * error; + long long isize; + long members; + int error_size; + int retval; + }; + +bool Fi_init( struct File_index * const fi, const int infd, + const bool ignore_trailing ); + +void Fi_free( struct File_index * const fi ); + +static inline long long Fi_udata_size( const struct File_index * const fi ) + { + if( fi->members <= 0 ) return 0; + return block_end( fi->member_vector[fi->members-1].dblock ); + } + +static inline long long Fi_cdata_size( const struct File_index * const fi ) + { + if( fi->members <= 0 ) return 0; + return block_end( fi->member_vector[fi->members-1].mblock ); + } + + /* total size including trailing data (if any) */ +static inline long long Fi_file_size( const struct File_index * const fi ) + { if( fi->isize >= 0 ) return fi->isize; else return 0; } + +static inline const struct Block * Fi_dblock( const struct File_index * const fi, + const long i ) + { return &fi->member_vector[i].dblock; } + +static inline const struct Block * Fi_mblock( const struct File_index * const fi, + const long i ) + { return &fi->member_vector[i].mblock; } + +static inline unsigned Fi_dictionary_size( const struct File_index * const fi, + const long i ) + { return fi->member_vector[i].dictionary_size; } diff --git a/list.c b/list.c new file mode 100644 index 0000000..6533bce --- /dev/null +++ b/list.c @@ -0,0 +1,123 @@ +/* Lunzip - Decompressor for the lzip format + Copyright (C) 2010-2017 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 . +*/ + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include + +#include "lzip.h" +#include "file_index.h" + + +static void list_line( const unsigned long long uncomp_size, + const unsigned long long comp_size, + const char * const input_filename ) + { + if( uncomp_size > 0 ) + printf( "%15llu %15llu %6.2f%% %s\n", uncomp_size, comp_size, + 100.0 * ( 1.0 - ( (double)comp_size / uncomp_size ) ), + input_filename ); + else + printf( "%15llu %15llu -INF%% %s\n", uncomp_size, comp_size, + input_filename ); + } + + +int list_files( const char * const filenames[], const int num_filenames, + const bool ignore_trailing ) + { + unsigned long long total_comp = 0, total_uncomp = 0; + int files = 0, retval = 0; + int i; + bool first_post = true; + bool stdin_used = false; + for( i = 0; i < num_filenames; ++i ) + { + const char * input_filename; + struct File_index file_index; + struct stat in_stats; /* not used */ + int infd; + const bool from_stdin = ( strcmp( filenames[i], "-" ) == 0 ); + if( from_stdin ) { if( stdin_used ) continue; else stdin_used = true; } + input_filename = from_stdin ? "(stdin)" : filenames[i]; + infd = from_stdin ? STDIN_FILENO : + open_instream( input_filename, &in_stats, true, true ); + if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } + + Fi_init( &file_index, infd, ignore_trailing ); + close( infd ); + if( file_index.retval != 0 ) + { + show_file_error( input_filename, file_index.error, 0 ); + if( retval < file_index.retval ) retval = file_index.retval; + Fi_free( &file_index ); continue; + } + if( verbosity >= 0 ) + { + const unsigned long long udata_size = Fi_udata_size( &file_index ); + const unsigned long long cdata_size = Fi_cdata_size( &file_index ); + total_comp += cdata_size; total_uncomp += udata_size; ++files; + if( first_post ) + { + first_post = false; + if( verbosity >= 1 ) fputs( " dict memb trail ", stdout ); + fputs( " uncompressed compressed saved name\n", stdout ); + } + if( verbosity >= 1 ) + { + long long trailing_size; + unsigned dictionary_size = 0; + long i; + for( i = 0; i < file_index.members; ++i ) + dictionary_size = + max( dictionary_size, Fi_dictionary_size( &file_index, i ) ); + trailing_size = Fi_file_size( &file_index ) - cdata_size; + printf( "%s %5ld %6lld ", format_ds( dictionary_size ), + file_index.members, trailing_size ); + } + list_line( udata_size, cdata_size, input_filename ); + + if( verbosity >= 2 && file_index.members > 1 ) + { + long i; + fputs( " member data_pos data_size member_pos member_size\n", stdout ); + for( i = 0; i < file_index.members; ++i ) + { + const struct Block * db = Fi_dblock( &file_index, i ); + const struct Block * mb = Fi_mblock( &file_index, i ); + printf( "%5ld %15llu %15llu %15llu %15llu\n", + i + 1, db->pos, db->size, mb->pos, mb->size ); + } + first_post = true; /* reprint heading after list of members */ + } + fflush( stdout ); + } + Fi_free( &file_index ); + } + if( verbosity >= 0 && files > 1 ) + { + if( verbosity >= 1 ) fputs( " ", stdout ); + list_line( total_uncomp, total_comp, "(totals)" ); + fflush( stdout ); + } + return retval; + } diff --git a/lzip.h b/lzip.h index 2970cb3..1e6cdd2 100644 --- a/lzip.h +++ b/lzip.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2016 Antonio Diaz Diaz. + Copyright (C) 2010-2017 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 @@ -49,6 +49,7 @@ enum { min_dictionary_size = 1 << min_dictionary_bits, /* >= modeled_distances */ max_dictionary_bits = 29, max_dictionary_size = 1 << max_dictionary_bits, + min_member_size = 36, literal_context_bits = 3, literal_pos_state_bits = 0, /* not used */ pos_state_bits = 2, @@ -131,9 +132,9 @@ static inline void Pp_init( struct Pretty_print * const pp, pp->stdin_name = "(stdin)"; pp->longest_name = 0; pp->first_post = false; - stdin_name_len = strlen( pp->stdin_name ); if( verbosity <= 0 ) return; + stdin_name_len = strlen( pp->stdin_name ); for( i = 0; i < num_filenames; ++i ) { const char * const s = filenames[i]; @@ -179,11 +180,18 @@ static inline void CRC32_update_buf( uint32_t * const crc, const int size ) { int i; + uint32_t c = *crc; for( i = 0; i < size; ++i ) - *crc = crc32[(*crc^buffer[i])&0xFF] ^ ( *crc >> 8 ); + c = crc32[(c^buffer[i])&0xFF] ^ ( c >> 8 ); + *crc = c; } +static inline bool isvalid_ds( const unsigned dictionary_size ) + { return ( dictionary_size >= min_dictionary_size && + dictionary_size <= max_dictionary_size ); } + + static const uint8_t magic_string[4] = { 0x4C, 0x5A, 0x49, 0x50 }; /* "LZIP" */ typedef uint8_t File_header[6]; /* 0-3 magic bytes */ @@ -246,7 +254,26 @@ static inline unsigned long long Ft_get_member_size( const File_trailer data ) } +static const char * const bad_magic_msg = "Bad magic number (file not in lzip format)."; +static const char * const bad_dict_msg = "Invalid dictionary size in member header."; +static const char * const trailing_msg = "Trailing data not allowed."; + +/* defined in decoder.c */ +int readblock( const int fd, uint8_t * const buf, const int size ); + +/* defined in list.c */ +int list_files( const char * const filenames[], const int num_filenames, + const bool ignore_trailing ); + /* defined in main.c */ extern int verbosity; +struct stat; +const char * bad_version( const unsigned version ); +const char * format_ds( const unsigned dictionary_size ); +int open_instream( const char * const name, struct stat * const in_statsp, + const bool no_ofile, const bool reg_only ); +void * resize_buffer( void * buf, const unsigned min_size ); void cleanup_and_fail( const int retval ); void show_error( const char * const msg, const int errcode, const bool help ); +void show_file_error( const char * const filename, const char * const msg, + const int errcode ); diff --git a/main.c b/main.c index 8737fed..47a72d6 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2016 Antonio Diaz Diaz. + Copyright (C) 2010-2017 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 @@ -67,14 +67,16 @@ int verbosity = 0; const char * const Program_name = "Lunzip"; const char * const program_name = "lunzip"; -const char * const program_year = "2016"; +const char * const program_year = "2017"; const char * invocation_name = 0; -struct { const char * from; const char * to; } const known_extensions[] = { +const struct { const char * from; const char * to; } known_extensions[] = { { ".lz", "" }, { ".tlz", ".tar" }, { 0, 0 } }; +enum Mode { m_compress, m_decompress, m_list, m_test }; + char * output_filename = 0; int outfd = -1; bool delete_output_on_interrupt = false; @@ -105,6 +107,7 @@ static void show_help( void ) " -d, --decompress decompress (this is the default)\n" " -f, --force overwrite existing output files\n" " -k, --keep keep (don't delete) input files\n" + " -l, --list print (un)compressed file sizes\n" " -o, --output= if reading standard input, write to \n" " -q, --quiet suppress all messages\n" " -t, --test test compressed file integrity\n" @@ -114,6 +117,8 @@ static void show_help( void ) "from standard input to standard output.\n" "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" + "Buffer sizes 12 to 29 are interpreted as powers of two, meaning 2^12\n" + "to 2^29 bytes.\n" "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n" "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n" "invalid input file, 3 for an internal consistency error (eg, bug) which\n" @@ -133,23 +138,38 @@ static void show_version( void ) } +const char * bad_version( const unsigned version ) + { + static char buf[80]; + snprintf( buf, sizeof buf, "Version %u member format not supported.", + version ); + return buf; + } + + +const char * format_ds( const unsigned dictionary_size ) + { + enum { bufsize = 16, factor = 1024 }; + static char buf[bufsize]; + const char * const prefix[8] = + { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; + const char * p = ""; + const char * np = " "; + unsigned num = dictionary_size, i; + bool exact = ( num % factor == 0 ); + + for( i = 0; i < 8 && ( num > 9999 || ( exact && num >= factor ) ); ++i ) + { num /= factor; if( num % factor != 0 ) exact = false; + p = prefix[i]; np = ""; } + snprintf( buf, bufsize, "%s%4u %sB", np, num, p ); + return buf; + } + + static void show_header( const unsigned dictionary_size ) { if( verbosity >= 3 ) - { - const char * const prefix[8] = - { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; - enum { factor = 1024 }; - const char * p = ""; - const char * np = " "; - unsigned num = dictionary_size, i; - bool exact = ( num % factor == 0 ); - - for( i = 0; i < 8 && ( num > 9999 || ( exact && num >= factor ) ); ++i ) - { num /= factor; if( num % factor != 0 ) exact = false; - p = prefix[i]; np = ""; } - fprintf( stderr, "dictionary size %s%4u %sB. ", np, num, p ); - } + fprintf( stderr, "dictionary %s. ", format_ds( dictionary_size ) ); } @@ -169,7 +189,7 @@ static unsigned long getnum( const char * const ptr, if( !errno && tail[0] ) { - const int factor = ( tail[1] == 'i' ) ? 1024 : 1000; + const unsigned factor = ( tail[1] == 'i' ) ? 1024 : 1000; int exponent = 0; /* 0 = bad multiplier */ int i; switch( tail[0] ) @@ -208,7 +228,7 @@ static unsigned long getnum( const char * const ptr, static int get_dict_size( const char * const arg ) { char * tail; - const int bits = strtol( arg, &tail, 0 ); + const long bits = strtol( arg, &tail, 0 ); if( bits >= min_dictionary_bits && bits <= max_dictionary_bits && *tail == 0 ) return ( 1 << bits ); @@ -216,37 +236,44 @@ static int get_dict_size( const char * const arg ) } +void set_mode( enum Mode * const program_modep, const enum Mode new_mode ) + { + if( *program_modep != m_compress && *program_modep != new_mode ) + { + show_error( "Only one operation can be specified.", 0, true ); + exit( 1 ); + } + *program_modep = new_mode; + } + + static int extension_index( const char * const name ) { - int i; - for( i = 0; known_extensions[i].from; ++i ) + int eindex; + for( eindex = 0; known_extensions[eindex].from; ++eindex ) { - const char * const ext = known_extensions[i].from; + const char * const ext = known_extensions[eindex].from; const unsigned name_len = strlen( name ); const unsigned ext_len = strlen( ext ); if( name_len > ext_len && strncmp( name + name_len - ext_len, ext, ext_len ) == 0 ) - return i; + return eindex; } return -1; } -static int open_instream( const char * const name, struct stat * const in_statsp, - const bool no_ofile ) +int open_instream( const char * const name, struct stat * const in_statsp, + const bool no_ofile, const bool reg_only ) { int infd = open( name, O_RDONLY | O_BINARY ); if( infd < 0 ) - { - if( verbosity >= 0 ) - fprintf( stderr, "%s: Can't open input file '%s': %s\n", - program_name, name, strerror( errno ) ); - } + show_file_error( name, "Can't open input file", errno ); else { const int i = fstat( infd, in_statsp ); const mode_t mode = in_statsp->st_mode; - const bool can_read = ( i == 0 && + const bool can_read = ( i == 0 && !reg_only && ( S_ISBLK( mode ) || S_ISCHR( mode ) || S_ISFIFO( mode ) || S_ISSOCK( mode ) ) ); if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || !no_ofile ) ) ) @@ -265,7 +292,7 @@ static int open_instream( const char * const name, struct stat * const in_statsp /* assure at least a minimum size for buffer 'buf' */ -static void * resize_buffer( void * buf, const int min_size ) +void * resize_buffer( void * buf, const unsigned min_size ) { if( buf ) buf = realloc( buf, min_size ); else buf = malloc( min_size ); @@ -278,19 +305,19 @@ static void * resize_buffer( void * buf, const int min_size ) } -static void set_d_outname( const char * const name, const int i ) +static void set_d_outname( const char * const name, const int eindex ) { const unsigned name_len = strlen( name ); - if( i >= 0 ) + if( eindex >= 0 ) { - const char * const from = known_extensions[i].from; + const char * const from = known_extensions[eindex].from; const unsigned from_len = strlen( from ); if( name_len > from_len ) { output_filename = resize_buffer( output_filename, name_len + - strlen( known_extensions[0].to ) + 1 ); + strlen( known_extensions[eindex].to ) + 1 ); strcpy( output_filename, name ); - strcpy( output_filename + name_len - from_len, known_extensions[i].to ); + strcpy( output_filename + name_len - from_len, known_extensions[eindex].to ); return; } } @@ -376,10 +403,10 @@ static void close_and_set_permissions( const struct stat * const in_statsp ) } -static unsigned char xdigit( const int value ) +static unsigned char xdigit( const unsigned value ) { - if( value >= 0 && value <= 9 ) return '0' + value; - if( value >= 10 && value <= 15 ) return 'A' + value - 10; + if( value <= 9 ) return '0' + value; + if( value <= 15 ) return 'A' + value - 10; return 0; } @@ -392,35 +419,28 @@ static bool show_trailing_data( const uint8_t * const data, const int size, { int i; char buf[80]; - int len = snprintf( buf, sizeof buf, "%strailing data = ", - all ? "" : "first bytes of " ); - bool text = true; - for( i = 0; i < size; ++i ) - if( !isprint( data[i] ) ) { text = false; break; } - if( text ) + unsigned len = max( 0, snprintf( buf, sizeof buf, "%strailing data = ", + all ? "" : "first bytes of " ) ); + for( i = 0; i < size && len + 2 < sizeof buf; ++i ) { - if( len > 0 && len < (int)sizeof buf ) - snprintf( buf + len, sizeof buf - len, "'%.*s'", size, (const char *)data ); - } - else - { - for( i = 0; i < size && len > 0 && len + 3 < (int)sizeof buf; ++i ) - { - if( i > 0 ) buf[len++] = ' '; - buf[len++] = xdigit( data[i] >> 4 ); - buf[len++] = xdigit( data[i] & 0x0F ); - buf[len] = 0; - } + buf[len++] = xdigit( data[i] >> 4 ); + buf[len++] = xdigit( data[i] & 0x0F ); + buf[len++] = ' '; } + if( len < sizeof buf ) buf[len++] = '\''; + for( i = 0; i < size && len < sizeof buf; ++i ) + { if( isprint( data[i] ) ) buf[len++] = data[i]; else buf[len++] = '.'; } + if( len < sizeof buf ) buf[len++] = '\''; + if( len < sizeof buf ) buf[len] = 0; else buf[sizeof buf - 1] = 0; Pp_show_msg( pp, buf ); - if( !ignore_trailing ) show_error( "Trailing data not allowed.", 0, false ); + if( !ignore_trailing ) show_file_error( pp->name, trailing_msg, 0 ); } return ignore_trailing; } static int decompress( const int infd, struct Pretty_print * const pp, - const int buffer_size, + const unsigned buffer_size, const bool ignore_trailing, const bool testing ) { unsigned long long partial_file_pos = 0; @@ -454,25 +474,19 @@ static int decompress( const int infd, struct Pretty_print * const pp, if( !Fh_verify_magic( header ) ) { if( first_member ) - { Pp_show_msg( pp, "Bad magic number (file not in lzip format)." ); - retval = 2; } + { show_file_error( pp->name, bad_magic_msg, 0 ); retval = 2; } else if( !show_trailing_data( header, size, pp, false, ignore_trailing ) ) retval = 2; break; } if( !Fh_verify_version( header ) ) { - if( verbosity >= 0 ) - { Pp_show_msg( pp, 0 ); - fprintf( stderr, "Version %d member format not supported.\n", - Fh_version( header ) ); } + Pp_show_msg( pp, bad_version( Fh_version( header ) ) ); retval = 2; break; } dictionary_size = Fh_get_dictionary_size( header ); - if( dictionary_size < min_dictionary_size || - dictionary_size > max_dictionary_size ) - { Pp_show_msg( pp, "Invalid dictionary size in member header." ); - retval = 2; break; } + if( !isvalid_ds( dictionary_size ) ) + { Pp_show_msg( pp, bad_dict_msg ); retval = 2; break; } if( verbosity >= 2 || ( verbosity == 1 && first_member ) ) { Pp_show_msg( pp, 0 ); show_header( dictionary_size ); } @@ -536,6 +550,16 @@ void show_error( const char * const msg, const int errcode, const bool help ) } +void show_file_error( const char * const filename, const char * const msg, + const int errcode ) + { + if( verbosity < 0 ) return; + fprintf( stderr, "%s: %s: %s", program_name, filename, msg ); + if( errcode > 0 ) fprintf( stderr, ": %s", strerror( errcode ) ); + fputc( '\n', stderr ); + } + + void internal_error( const char * const msg ) { if( verbosity >= 0 ) @@ -546,12 +570,12 @@ void internal_error( const char * const msg ) int main( const int argc, const char * const argv[] ) { - const char * input_filename = ""; const char * default_output_filename = ""; const char ** filenames = 0; int num_filenames = 0; - int buffer_size = max_dictionary_size; + unsigned buffer_size = max_dictionary_size; int infd = -1; + enum Mode program_mode = m_compress; int argind = 0; int retval = 0; int i; @@ -560,7 +584,6 @@ int main( const int argc, const char * const argv[] ) bool ignore_trailing = true; bool keep_input_files = false; bool stdin_used = false; - bool testing = false; bool to_stdout = false; struct Pretty_print pp; @@ -572,6 +595,7 @@ int main( const int argc, const char * const argv[] ) { 'f', "force", ap_no }, { 'h', "help", ap_no }, { 'k', "keep", ap_no }, + { 'l', "list", ap_no }, { 'n', "threads", ap_yes }, { 'o', "output", ap_yes }, { 'q', "quiet", ap_no }, @@ -600,14 +624,15 @@ int main( const int argc, const char * const argv[] ) { case 'a': ignore_trailing = false; break; case 'c': to_stdout = true; break; - case 'd': testing = false; break; + case 'd': set_mode( &program_mode, m_decompress ); break; case 'f': force = true; break; case 'h': show_help(); return 0; case 'k': keep_input_files = true; break; + case 'l': set_mode( &program_mode, m_list ); break; case 'n': break; case 'o': default_output_filename = arg; break; case 'q': verbosity = -1; break; - case 't': testing = true; break; + case 't': set_mode( &program_mode, m_test ); break; case 'u': buffer_size = get_dict_size( arg ); break; case 'v': if( verbosity < 4 ) ++verbosity; break; case 'V': show_version(); return 0; @@ -620,9 +645,6 @@ int main( const int argc, const char * const argv[] ) setmode( STDOUT_FILENO, O_BINARY ); #endif - if( testing ) - outfd = -1; - num_filenames = max( 1, ap_arguments( &parser ) - argind ); filenames = resize_buffer( filenames, num_filenames * sizeof filenames[0] ); filenames[0] = "-"; @@ -633,10 +655,18 @@ int main( const int argc, const char * const argv[] ) if( strcmp( filenames[i], "-" ) != 0 ) filenames_given = true; } + if( program_mode == m_list ) + return list_files( filenames, num_filenames, ignore_trailing ); + + if( program_mode == m_test ) + outfd = -1; + else if( program_mode == m_compress ) + program_mode = m_decompress; /* default mode */ + if( buffer_size < max_dictionary_size ) { bool from_stdin = false; - if( to_stdout || testing ) + if( to_stdout || program_mode == m_test ) { show_error( "'--buffer-size' is incompatible with '--stdout' and '--test'.", 0, false ); return 1; } for( i = 0; i < num_filenames; ++i ) @@ -647,7 +677,7 @@ int main( const int argc, const char * const argv[] ) " with a reduced buffer size.", 0, false ); return 1; } } - if( !to_stdout && !testing && + if( !to_stdout && program_mode != m_test && ( filenames_given || default_output_filename[0] ) ) set_signals(); @@ -656,6 +686,7 @@ int main( const int argc, const char * const argv[] ) output_filename = resize_buffer( output_filename, 1 ); for( i = 0; i < num_filenames; ++i ) { + const char * input_filename = ""; int tmp; struct stat in_stats; const struct stat * in_statsp; @@ -664,16 +695,15 @@ int main( const int argc, const char * const argv[] ) if( !filenames[i][0] || strcmp( filenames[i], "-" ) == 0 ) { if( stdin_used ) continue; else stdin_used = true; - input_filename = ""; infd = STDIN_FILENO; - if( !testing ) + if( program_mode != m_test ) { if( to_stdout || !default_output_filename[0] ) outfd = STDOUT_FILENO; else { output_filename = resize_buffer( output_filename, - strlen( default_output_filename ) + 1 ); + strlen( default_output_filename ) + 1 ); strcpy( output_filename, default_output_filename ); if( !open_outstream( force, true ) ) { @@ -687,9 +717,10 @@ int main( const int argc, const char * const argv[] ) else { input_filename = filenames[i]; - infd = open_instream( input_filename, &in_stats, to_stdout || testing ); + infd = open_instream( input_filename, &in_stats, + to_stdout || program_mode == m_test, false ); if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } - if( !testing ) + if( program_mode != m_test ) { if( to_stdout ) outfd = STDOUT_FILENO; else @@ -705,10 +736,13 @@ int main( const int argc, const char * const argv[] ) } } + Pp_set_name( &pp, input_filename ); if( isatty( infd ) ) { - show_error( "I won't read compressed data from a terminal.", 0, true ); + show_file_error( pp.name, + "I won't read compressed data from a terminal.", 0 ); if( retval < 1 ) retval = 1; + if( program_mode == m_test ) { close( infd ); infd = -1; continue; } cleanup_and_fail( retval ); } @@ -727,17 +761,16 @@ int main( const int argc, const char * const argv[] ) } in_statsp = input_filename[0] ? &in_stats : 0; - Pp_set_name( &pp, input_filename ); - tmp = decompress( infd, &pp, buffer_size, ignore_trailing, testing ); + tmp = decompress( infd, &pp, buffer_size, ignore_trailing, program_mode == m_test ); if( tmp > retval ) retval = tmp; - if( tmp && !testing ) cleanup_and_fail( retval ); + if( tmp && program_mode != m_test ) cleanup_and_fail( retval ); if( delete_output_on_interrupt ) close_and_set_permissions( in_statsp ); if( input_filename[0] ) { close( infd ); infd = -1; - if( !keep_input_files && !to_stdout && !testing ) + if( !keep_input_files && !to_stdout && program_mode != m_test ) remove( input_filename ); } } diff --git a/testsuite/check.sh b/testsuite/check.sh index f5b2088..334cb71 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # check script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2016 Antonio Diaz Diaz. +# Copyright (C) 2010-2017 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission # to copy, distribute and modify it. @@ -17,12 +17,12 @@ if [ ! -f "${LZIP}" ] || [ ! -x "${LZIP}" ] ; then exit 1 fi -if [ -e "${LZIP}" ] 2> /dev/null ; then true -else +[ -e "${LZIP}" ] 2> /dev/null || + { echo "$0: a POSIX shell is required to run the tests" echo "Try bash -c \"$0 $1 $2\"" exit 1 -fi + } if [ -d tmp ] ; then rm -rf tmp ; fi mkdir tmp @@ -31,109 +31,176 @@ cd "${objdir}"/tmp || framework_failure cat "${testdir}"/test.txt > in || framework_failure in_lz="${testdir}"/test.txt.lz fail=0 +test_failed() { fail=1 ; printf " $1" ; [ -z "$2" ] || printf "($2)" ; } printf "testing lunzip-%s..." "$2" -cat "${testdir}"/test.txt.lz > uin.lz || framework_failure -"${LZIP}" -dfkqu-1 uin.lz -if [ $? = 1 ] && [ ! -e uin ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -dfkqu0 uin.lz -if [ $? = 1 ] && [ ! -e uin ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -dfkqu4095 uin.lz -if [ $? = 1 ] && [ ! -e uin ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -dfkqu513MiB uin.lz -if [ $? = 1 ] && [ ! -e uin ] ; then printf . ; else printf - ; fail=1 ; fi +cat "${in_lz}" > uin.lz || framework_failure +for i in bad_size -1 0 4095 513MiB 1G 1T 1P 1E 1Z 1Y 10KB ; do + "${LZIP}" -dfkqu $i uin.lz + { [ $? = 1 ] && [ ! -e uin ] ; } || test_failed $LINENO $i +done rm -f uin.lz +"${LZIP}" -lq in +[ $? = 2 ] || test_failed $LINENO "${LZIP}" -tq in -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +[ $? = 2 ] || test_failed $LINENO "${LZIP}" -tq < in -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +[ $? = 2 ] || test_failed $LINENO "${LZIP}" -cdq in -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +[ $? = 2 ] || test_failed $LINENO "${LZIP}" -cdq < in -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi -dd if="${in_lz}" bs=1 count=6 2> /dev/null | "${LZIP}" -tq -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi -dd if="${in_lz}" bs=1 count=20 2> /dev/null | "${LZIP}" -tq -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +[ $? = 2 ] || test_failed $LINENO +# these are for code coverage +"${LZIP}" -lt "${in_lz}" 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -cdl "${in_lz}" > out 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -cdt "${in_lz}" > out 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -t -- nx_file 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --help > /dev/null || test_failed $LINENO +"${LZIP}" -n1 -V > /dev/null || test_failed $LINENO +"${LZIP}" -m 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -z 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --bad_option 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --t 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --test=2 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --output= 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" --output 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +printf "LZIP\001-.............................." | "${LZIP}" -t 2> /dev/null +printf "LZIP\002-.............................." | "${LZIP}" -t 2> /dev/null +printf "LZIP\001+.............................." | "${LZIP}" -t 2> /dev/null printf "\ntesting decompression..." -"${LZIP}" -t "${in_lz}" -if [ $? = 0 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cd "${in_lz}" > copy || fail=1 -cmp in copy || fail=1 -printf . +"${LZIP}" -lq "${in_lz}" || test_failed $LINENO +"${LZIP}" -t "${in_lz}" || test_failed $LINENO +"${LZIP}" -cd "${in_lz}" > copy || test_failed $LINENO +cmp in copy || test_failed $LINENO rm -f copy cat "${in_lz}" > copy.lz || framework_failure -"${LZIP}" -dk copy.lz || fail=1 -cmp in copy || fail=1 +"${LZIP}" -dk copy.lz || test_failed $LINENO +cmp in copy || test_failed $LINENO printf "to be overwritten" > copy || framework_failure -"${LZIP}" -dq copy.lz -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -d copy.lz 2> /dev/null +[ $? = 1 ] || test_failed $LINENO "${LZIP}" -df copy.lz -if [ $? = 0 ] && [ ! -e copy.lz ] && cmp in copy ; then - printf . ; else printf - ; fail=1 ; fi +{ [ $? = 0 ] && [ ! -e copy.lz ] && cmp in copy ; } || test_failed $LINENO printf "to be overwritten" > copy || framework_failure -"${LZIP}" -df -o copy < "${in_lz}" || fail=1 -cmp in copy || fail=1 -printf . +"${LZIP}" -df -o copy < "${in_lz}" || test_failed $LINENO +cmp in copy || test_failed $LINENO rm -f copy cat "${in_lz}" > anyothername || framework_failure -"${LZIP}" -d -o copy - anyothername - < "${in_lz}" -if [ $? = 0 ] && cmp in copy && cmp in anyothername.out ; then - printf . ; else printf - ; fail=1 ; fi +"${LZIP}" -dv --output copy - anyothername - < "${in_lz}" 2> /dev/null +{ [ $? = 0 ] && cmp in copy && cmp in anyothername.out ; } || + test_failed $LINENO rm -f copy anyothername.out +"${LZIP}" -lq in "${in_lz}" +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -lq nx_file.lz "${in_lz}" +[ $? = 1 ] || test_failed $LINENO "${LZIP}" -tq in "${in_lz}" -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -tq foo.lz "${in_lz}" -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -tq nx_file.lz "${in_lz}" +[ $? = 1 ] || test_failed $LINENO "${LZIP}" -cdq in "${in_lz}" > copy -if [ $? = 2 ] && cat copy in | cmp in - ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cdq foo.lz "${in_lz}" > copy -if [ $? = 1 ] && cmp in copy ; then printf . ; else printf - ; fail=1 ; fi +{ [ $? = 2 ] && cat copy in | cmp in - ; } || test_failed $LINENO +"${LZIP}" -cdq nx_file.lz "${in_lz}" > copy +{ [ $? = 1 ] && cmp in copy ; } || test_failed $LINENO rm -f copy cat "${in_lz}" > copy.lz || framework_failure +for i in 1 2 3 4 5 6 7 ; do + printf "g" >> copy.lz || framework_failure + "${LZIP}" -alvv copy.lz "${in_lz}" > /dev/null 2>&1 + [ $? = 2 ] || test_failed $LINENO $i + "${LZIP}" -atvvvv copy.lz "${in_lz}" 2> /dev/null + [ $? = 2 ] || test_failed $LINENO $i +done "${LZIP}" -dq in copy.lz -if [ $? = 2 ] && [ -e copy.lz ] && [ ! -e copy ] && [ ! -e in.out ] ; then - printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -dq foo.lz copy.lz -if [ $? = 1 ] && [ ! -e copy.lz ] && [ ! -e foo ] && cmp in copy ; then - printf . ; else printf - ; fail=1 ; fi +{ [ $? = 2 ] && [ -e copy.lz ] && [ ! -e copy ] && [ ! -e in.out ] ; } || + test_failed $LINENO +"${LZIP}" -dq nx_file.lz copy.lz +{ [ $? = 1 ] && [ ! -e copy.lz ] && [ ! -e nx_file ] && cmp in copy ; } || + test_failed $LINENO cat in in > in2 || framework_failure -cat "${in_lz}" "${in_lz}" > copy2.lz || framework_failure -"${LZIP}" -t copy2.lz || fail=1 -"${LZIP}" -cd copy2.lz > copy2 || fail=1 -cmp in2 copy2 || fail=1 -printf . +cat "${in_lz}" "${in_lz}" > in2.lz || framework_failure +"${LZIP}" -lq in2.lz || test_failed $LINENO +"${LZIP}" -t in2.lz || test_failed $LINENO +"${LZIP}" -cd in2.lz > copy2 || test_failed $LINENO +cmp in2 copy2 || test_failed $LINENO -printf "garbage" >> copy2.lz || framework_failure +cat in2.lz > copy2.lz || framework_failure +printf "\ngarbage" >> copy2.lz || framework_failure +"${LZIP}" -tvvvv copy2.lz 2> /dev/null || test_failed $LINENO rm -f copy2 +"${LZIP}" -alq copy2.lz +[ $? = 2 ] || test_failed $LINENO "${LZIP}" -atq copy2.lz -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +[ $? = 2 ] || test_failed $LINENO "${LZIP}" -atq < copy2.lz -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +[ $? = 2 ] || test_failed $LINENO "${LZIP}" -adkq copy2.lz -if [ $? = 2 ] && [ ! -e copy2 ] ; then printf . ; else printf - ; fail=1 ; fi +{ [ $? = 2 ] && [ ! -e copy2 ] ; } || test_failed $LINENO "${LZIP}" -adkq -o copy2 < copy2.lz -if [ $? = 2 ] && [ ! -e copy2 ] ; then printf . ; else printf - ; fail=1 ; fi +{ [ $? = 2 ] && [ ! -e copy2 ] ; } || test_failed $LINENO printf "to be overwritten" > copy2 || framework_failure -"${LZIP}" -df copy2.lz || fail=1 -cmp in2 copy2 || fail=1 -printf . +"${LZIP}" -df copy2.lz || test_failed $LINENO +cmp in2 copy2 || test_failed $LINENO -for i in 12 4096 4Ki 29 512KiB ; do +for i in 12 5120 6Ki 29 512KiB ; do printf "to be overwritten" > copy || framework_failure - "${LZIP}" -df -u$i -o copy < "${in_lz}" || fail=1 - cmp in copy || fail=1 - printf . + "${LZIP}" -df -u$i -o copy < "${in_lz}" || test_failed $LINENO $i + cmp in copy || test_failed $LINENO $i done +printf "\ntesting bad input..." + +cat "${in_lz}" "${in_lz}" "${in_lz}" > in3.lz || framework_failure +if dd if=in3.lz of=trunc.lz bs=14752 count=1 2> /dev/null && + [ -e trunc.lz ] && cmp in2.lz trunc.lz > /dev/null 2>&1 ; then + for i in 6 20 14734 14753 14754 14755 14756 14757 14758 ; do + dd if=in3.lz of=trunc.lz bs=$i count=1 2> /dev/null + "${LZIP}" -lq trunc.lz + [ $? = 2 ] || test_failed $LINENO $i + "${LZIP}" -t trunc.lz 2> /dev/null + [ $? = 2 ] || test_failed $LINENO $i + "${LZIP}" -tq < trunc.lz + [ $? = 2 ] || test_failed $LINENO $i + "${LZIP}" -cdq trunc.lz > out + [ $? = 2 ] || test_failed $LINENO $i + "${LZIP}" -dq < trunc.lz > out + [ $? = 2 ] || test_failed $LINENO $i + done +else + printf "\nwarning: skipping truncation test: 'dd' does not work on your system." +fi + +cat "${in_lz}" > ingin.lz || framework_failure +printf "g" >> ingin.lz || framework_failure +cat "${in_lz}" >> ingin.lz || framework_failure +"${LZIP}" -lq ingin.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -t ingin.lz || test_failed $LINENO +"${LZIP}" -cd ingin.lz > copy || test_failed $LINENO +cmp in copy || test_failed $LINENO +"${LZIP}" -t < ingin.lz || test_failed $LINENO +"${LZIP}" -d < ingin.lz > copy || test_failed $LINENO +cmp in copy || test_failed $LINENO + echo if [ ${fail} = 0 ] ; then echo "tests completed successfully." diff --git a/testsuite/test.txt.lz b/testsuite/test.txt.lz index 41d2e39999a92cd871d646a4ae00ba25ef067cc4..22cea6e4d2a58b4dcb006541dcdb8defabba0cbb 100644 GIT binary patch delta 15 Wcmca$dBKv+C(1K`@%=`&Q!)TBtOex& delta 15 Wcmca$dBKv+C(1K`@xw;8Q!)TBu?6M; From 9ac634baad3ec68a008441ae2a7810e5e9ba6eb9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 17 Feb 2025 22:24:43 +0100 Subject: [PATCH 03/10] Adding upstream version 1.10. Signed-off-by: Daniel Baumann --- ChangeLog | 19 +++- INSTALL | 2 +- NEWS | 36 +++++-- README | 4 +- carg_parser.c | 2 +- carg_parser.h | 2 +- configure | 6 +- decoder.c | 70 +++++++------ decoder.h | 10 +- doc/lunzip.1 | 7 +- file_index.c | 32 +++--- file_index.h | 4 +- list.c | 8 +- lzip.h | 49 ++++++++-- main.c | 239 +++++++++++++++++++++++++++++---------------- testsuite/check.sh | 43 +++++++- 16 files changed, 352 insertions(+), 181 deletions(-) diff --git a/ChangeLog b/ChangeLog index 83c4648..54b4b03 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2018-02-05 Antonio Diaz Diaz + + * Version 1.10 released. + * main.c: Added new option '--loose-trailing'. + * Improved corrupt header detection to HD=3. + * main.c: Show corrupt or truncated header in multimember file. + * Replaced 'bits/byte' with inverse compression ratio in output. + * Show progress of decompression at verbosity level 2 (-vv). + * Show progress of decompression only if stderr is a terminal. + * main.c: Show final diagnostic when testing multiple files. + * decoder.c (LZd_verify_trailer): Show stored sizes also in hex. + Show dictionary size at verbosity level 4 (-vvvv). + 2017-04-13 Antonio Diaz Diaz * Version 1.9 released. @@ -21,8 +34,8 @@ * main.c (main): Don't use stdin more than once. * Error messages synced with lzip-1.18. * configure: Avoid warning on some shells when testing for gcc. - * testsuite/check.sh: A POSIX shell is required to run the tests. - * testsuite/check.sh: Don't check error messages. + * check.sh: A POSIX shell is required to run the tests. + * check.sh: Don't check error messages. 2015-05-27 Antonio Diaz Diaz @@ -80,7 +93,7 @@ * Created from the decompression code of clzip 1.1. -Copyright (C) 2010-2017 Antonio Diaz Diaz. +Copyright (C) 2010-2018 Antonio Diaz Diaz. This file is a collection of facts, and thus it is not copyrightable, but just in case, you have unlimited permission to copy, distribute and diff --git a/INSTALL b/INSTALL index 59892e4..4543b8d 100644 --- a/INSTALL +++ b/INSTALL @@ -61,7 +61,7 @@ After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2010-2017 Antonio Diaz Diaz. +Copyright (C) 2010-2018 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute and modify it. diff --git a/NEWS b/NEWS index 57ec920..cd904d7 100644 --- a/NEWS +++ b/NEWS @@ -1,14 +1,32 @@ -Changes in version 1.9: +Changes in version 1.10: -The option '-l, --list' has been ported from lziprecover. +The option '--loose-trailing', has been added. -It is now an error to specify two or more different operations in the -command line (--decompress, --list or --test). +The test used by lunzip to discriminate trailing data from a corrupt +header in multimember or concatenated files has been improved to a +Hamming distance (HD) of 3, and the 3 bit flips must happen in different +magic bytes for the test to fail. As a consequence some kinds of files +no longer can be appended to a lzip file as trailing data unless the +'--loose-trailing' option is used when decompressing. +Lziprecover can be used to remove conflicting trailing data from a file. -Decompression time has been reduced by 7%. +The contents of a corrupt or truncated header found in a multimember +file is now shown, after the error message, in the same format as +trailing data. -In test mode, lunzip now continues checking the rest of the files if any -input file is a terminal. +The 'bits/byte' ratio has been replaced with the inverse compression +ratio in the output. -Trailing data are now shown both in hexadecimal and as a string of -printable ASCII characters. +The progress of decompression is now shown at verbosity level 2 (-vv) or +higher. + +Progress of decompression is only shown if stderr is a terminal. + +A final diagnostic is now shown at verbosity level 1 (-v) or higher if +any file fails the test when testing multiple files. + +In case of (de)compressed size mismatch, the stored size is now also +shown in hexadecimal to ease visual comparison. + +The dictionary size is now shown at verbosity level 4 (-vvvv) when +decompressing or testing. diff --git a/README b/README index ccd9b7b..947dc24 100644 --- a/README +++ b/README @@ -73,7 +73,7 @@ input from a terminal. Lunzip will correctly decompress a file which is the concatenation of two or more compressed files. The result is the concatenation of the -corresponding uncompressed files. Integrity testing of concatenated +corresponding decompressed files. Integrity testing of concatenated compressed files is also supported. The ideas embodied in lunzip are due to (at least) the following people: @@ -83,7 +83,7 @@ range encoding), Igor Pavlov (for putting all the above together in LZMA), and Julian Seward (for bzip2's CLI). -Copyright (C) 2010-2017 Antonio Diaz Diaz. +Copyright (C) 2010-2018 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute and modify it. diff --git a/carg_parser.c b/carg_parser.c index 6850643..10ad4dc 100644 --- a/carg_parser.c +++ b/carg_parser.c @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2017 Antonio Diaz Diaz. + Copyright (C) 2006-2018 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/carg_parser.h b/carg_parser.h index c4ce31d..e1c70dd 100644 --- a/carg_parser.h +++ b/carg_parser.h @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2017 Antonio Diaz Diaz. + Copyright (C) 2006-2018 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/configure b/configure index 53fcaae..20fa969 100755 --- a/configure +++ b/configure @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2017 Antonio Diaz Diaz. +# Copyright (C) 2010-2018 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission # to copy, distribute and modify it. pkgname=lunzip -pkgversion=1.9 +pkgversion=1.10 progname=lunzip srctrigger=doc/${progname}.1 @@ -168,7 +168,7 @@ echo "LDFLAGS = ${LDFLAGS}" rm -f Makefile cat > Makefile << EOF # Makefile for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2017 Antonio Diaz Diaz. +# Copyright (C) 2010-2018 Antonio Diaz Diaz. # This file was generated automatically by configure. Don't edit. # # This Makefile is free software: you have unlimited permission diff --git a/decoder.c b/decoder.c index 61777b5..a995da3 100644 --- a/decoder.c +++ b/decoder.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2017 Antonio Diaz Diaz. + Copyright (C) 2010-2018 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 @@ -32,24 +32,6 @@ CRC32 crc32; -void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ) - { - if( verbosity >= 0 ) - { - if( pp->first_post ) - { - unsigned i; - pp->first_post = false; - fprintf( stderr, " %s: ", pp->name ); - for( i = strlen( pp->name ); i < pp->longest_name; ++i ) - fputc( ' ', stderr ); - if( !msg ) fflush( stderr ); - } - if( msg ) fprintf( stderr, "%s\n", msg ); - } - } - - /* Returns the number of bytes really read. If (returned value < size) and (errno == 0), means EOF was reached. */ @@ -106,6 +88,7 @@ bool Rd_read_block( struct Range_decoder * const rdec ) rdec->at_stream_end = ( rdec->stream_pos < rd_buffer_size ); rdec->partial_member_pos += rdec->pos; rdec->pos = 0; + show_dprogress( 0, 0, 0, 0 ); } return rdec->pos < rdec->stream_pos; } @@ -136,6 +119,8 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, int size = Rd_read_data( d->rdec, trailer, Ft_size ); const unsigned long long data_size = LZd_data_position( d ); const unsigned long long member_size = Rd_member_position( d->rdec ); + unsigned td_crc; + unsigned long long td_size, tm_size; bool error = false; if( size < Ft_size ) @@ -150,45 +135,56 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, while( size < Ft_size ) trailer[size++] = 0; } - if( Ft_get_data_crc( trailer ) != LZd_crc( d ) ) + td_crc = Ft_get_data_crc( trailer ); + if( td_crc != LZd_crc( d ) ) { error = true; if( verbosity >= 0 ) { Pp_show_msg( pp, 0 ); - fprintf( stderr, "CRC mismatch; trailer says %08X, data CRC is %08X\n", - Ft_get_data_crc( trailer ), LZd_crc( d ) ); + fprintf( stderr, "CRC mismatch; stored %08X, computed %08X\n", + td_crc, LZd_crc( d ) ); } } - if( Ft_get_data_size( trailer ) != data_size ) + td_size = Ft_get_data_size( trailer ); + if( td_size != data_size ) { error = true; if( verbosity >= 0 ) { Pp_show_msg( pp, 0 ); - fprintf( stderr, "Data size mismatch; trailer says %llu, data size is %llu (0x%llX)\n", - Ft_get_data_size( trailer ), data_size, data_size ); + fprintf( stderr, "Data size mismatch; stored %llu (0x%llX), computed %llu (0x%llX)\n", + td_size, td_size, data_size, data_size ); } } - if( Ft_get_member_size( trailer ) != member_size ) + tm_size = Ft_get_member_size( trailer ); + if( tm_size != member_size ) { error = true; if( verbosity >= 0 ) { Pp_show_msg( pp, 0 ); - fprintf( stderr, "Member size mismatch; trailer says %llu, member size is %llu (0x%llX)\n", - Ft_get_member_size( trailer ), member_size, member_size ); + fprintf( stderr, "Member size mismatch; stored %llu (0x%llX), computed %llu (0x%llX)\n", + tm_size, tm_size, member_size, member_size ); } } - if( !error && verbosity >= 2 && data_size > 0 && member_size > 0 ) - fprintf( stderr, "%6.3f:1, %6.3f bits/byte, %5.2f%% saved. ", - (double)data_size / member_size, - ( 8.0 * member_size ) / data_size, - 100.0 * ( 1.0 - ( (double)member_size / data_size ) ) ); - if( !error && verbosity >= 4 ) - fprintf( stderr, "CRC %08X, decompressed %9llu, compressed %8llu. ", - LZd_crc( d ), data_size, member_size ); - return !error; + if( error ) return false; + if( verbosity >= 2 ) + { + if( verbosity >= 4 ) show_header( d->dictionary_size ); + if( data_size == 0 || member_size == 0 ) + fputs( "no data compressed. ", stderr ); + else + fprintf( stderr, "%6.3f:1, %5.2f%% ratio, %5.2f%% saved. ", + (double)data_size / member_size, + ( 100.0 * member_size ) / data_size, + 100.0 - ( ( 100.0 * member_size ) / data_size ) ); + if( verbosity >= 4 ) fprintf( stderr, "CRC %08X, ", td_crc ); + if( verbosity >= 3 ) + fprintf( stderr, "decompressed %9llu, compressed %8llu. ", + data_size, member_size ); + } + return true; } diff --git a/decoder.h b/decoder.h index e3a4f1c..7ec3133 100644 --- a/decoder.h +++ b/decoder.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2017 Antonio Diaz Diaz. + Copyright (C) 2010-2018 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 @@ -244,11 +244,7 @@ unsigned seek_read_back( const int fd, uint8_t * const buf, const int size, const int offset ); static inline uint8_t LZd_peek_prev( const struct LZ_decoder * const d ) - { - if( d->pos > 0 ) return d->buffer[d->pos-1]; - if( d->pos_wrapped ) return d->buffer[d->buffer_size-1]; - return 0; /* prev_byte of first byte */ - } + { return d->buffer[((d->pos > 0) ? d->pos : d->buffer_size)-1]; } static inline uint8_t LZd_peek( const struct LZ_decoder * const d, const unsigned distance ) @@ -338,6 +334,8 @@ static inline bool LZd_init( struct LZ_decoder * const d, d->outfd = ofd; d->pos_wrapped = false; d->pos_wrapped_dic = false; + /* prev_byte of first byte; also for LZd_peek( 0 ) on corrupt file */ + d->buffer[d->buffer_size-1] = 0; return true; } diff --git a/doc/lunzip.1 b/doc/lunzip.1 index c83f901..ffcd8a3 100644 --- a/doc/lunzip.1 +++ b/doc/lunzip.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. -.TH LUNZIP "1" "April 2017" "lunzip 1.9" "User Commands" +.TH LUNZIP "1" "February 2018" "lunzip 1.10" "User Commands" .SH NAME lunzip \- decompressor for the lzip format .SH SYNOPSIS @@ -61,6 +61,9 @@ set output buffer size in bytes .TP \fB\-v\fR, \fB\-\-verbose\fR be verbose (a 2nd \fB\-v\fR gives more) +.TP +\fB\-\-loose\-trailing\fR +allow trailing data seeming corrupt header .PP If no file names are given, or if a file is '\-', lunzip decompresses from standard input to standard output. @@ -78,7 +81,7 @@ Report bugs to lzip\-bug@nongnu.org .br Lunzip home page: http://www.nongnu.org/lzip/lunzip.html .SH COPYRIGHT -Copyright \(co 2017 Antonio Diaz Diaz. +Copyright \(co 2018 Antonio Diaz Diaz. License GPLv2+: GNU GPL version 2 or later .br This is free software: you are free to change and redistribute it. diff --git a/file_index.c b/file_index.c index e737608..1872d67 100644 --- a/file_index.c +++ b/file_index.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2017 Antonio Diaz Diaz. + Copyright (C) 2010-2018 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 @@ -108,7 +108,9 @@ static void Fi_set_num_error( struct File_index * const fi, /* If successful, push last member and set pos to member header. */ static bool Fi_skip_trailing_data( struct File_index * const fi, - const int fd, long long * const pos ) + const int fd, long long * const pos, + const bool ignore_trailing, + const bool loose_trailing ) { enum { block_size = 16384, buffer_size = block_size + Ft_size - 1 + Fh_size }; @@ -152,6 +154,11 @@ static bool Fi_skip_trailing_data( struct File_index * const fi, add_error( fi, "Last member in input file is truncated or corrupt." ); fi->retval = 2; return false; } + if( !loose_trailing && bsize - i >= Fh_size && + Fh_verify_corrupt( buffer + i ) ) + { add_error( fi, corrupt_mm_msg ); fi->retval = 2; return false; } + if( !ignore_trailing ) + { add_error( fi, trailing_msg ); fi->retval = 2; return false; } *pos = ipos + i - member_size; return push_back_member( fi, 0, Ft_get_data_size( *trailer ), *pos, member_size, dictionary_size ); @@ -170,7 +177,7 @@ static bool Fi_skip_trailing_data( struct File_index * const fi, bool Fi_init( struct File_index * const fi, const int infd, - const bool ignore_trailing ) + const bool ignore_trailing, const bool loose_trailing ) { File_header header; long long pos; @@ -211,12 +218,10 @@ bool Fi_init( struct File_index * const fi, const int infd, member_size = Ft_get_member_size( trailer ); if( member_size < min_member_size || member_size > (unsigned long long)pos ) { - if( fi->members > 0 ) - Fi_set_num_error( fi, "Member size in trailer is corrupt at pos ", - pos - 8 ); - else if( Fi_skip_trailing_data( fi, infd, &pos ) ) - { if( ignore_trailing ) continue; - add_error( fi, trailing_msg ); fi->retval = 2; return false; } + if( fi->members <= 0 ) + { if( Fi_skip_trailing_data( fi, infd, &pos, ignore_trailing, + loose_trailing ) ) continue; else return false; } + Fi_set_num_error( fi, "Member size in trailer is corrupt at pos ", pos - 8 ); break; } if( seek_read( infd, header, Fh_size, pos - member_size ) != Fh_size ) @@ -225,11 +230,10 @@ bool Fi_init( struct File_index * const fi, const int infd, if( !Fh_verify_magic( header ) || !Fh_verify_version( header ) || !isvalid_ds( dictionary_size ) ) { - if( fi->members > 0 ) - Fi_set_num_error( fi, "Bad header at pos ", pos - member_size ); - else if( Fi_skip_trailing_data( fi, infd, &pos ) ) - { if( ignore_trailing ) continue; - add_error( fi, trailing_msg ); fi->retval = 2; return false; } + if( fi->members <= 0 ) + { if( Fi_skip_trailing_data( fi, infd, &pos, ignore_trailing, + loose_trailing ) ) continue; else return false; } + Fi_set_num_error( fi, "Bad header at pos ", pos - member_size ); break; } pos -= member_size; diff --git a/file_index.h b/file_index.h index 38f0246..49b6519 100644 --- a/file_index.h +++ b/file_index.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2017 Antonio Diaz Diaz. + Copyright (C) 2010-2018 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 @@ -57,7 +57,7 @@ struct File_index }; bool Fi_init( struct File_index * const fi, const int infd, - const bool ignore_trailing ); + const bool ignore_trailing, const bool loose_trailing ); void Fi_free( struct File_index * const fi ); diff --git a/list.c b/list.c index 6533bce..b0dfef7 100644 --- a/list.c +++ b/list.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2017 Antonio Diaz Diaz. + Copyright (C) 2010-2018 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 @@ -34,7 +34,7 @@ static void list_line( const unsigned long long uncomp_size, { if( uncomp_size > 0 ) printf( "%15llu %15llu %6.2f%% %s\n", uncomp_size, comp_size, - 100.0 * ( 1.0 - ( (double)comp_size / uncomp_size ) ), + 100.0 - ( ( 100.0 * comp_size ) / uncomp_size ), input_filename ); else printf( "%15llu %15llu -INF%% %s\n", uncomp_size, comp_size, @@ -43,7 +43,7 @@ static void list_line( const unsigned long long uncomp_size, int list_files( const char * const filenames[], const int num_filenames, - const bool ignore_trailing ) + const bool ignore_trailing, const bool loose_trailing ) { unsigned long long total_comp = 0, total_uncomp = 0; int files = 0, retval = 0; @@ -63,7 +63,7 @@ int list_files( const char * const filenames[], const int num_filenames, open_instream( input_filename, &in_stats, true, true ); if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } - Fi_init( &file_index, infd, ignore_trailing ); + Fi_init( &file_index, infd, ignore_trailing, loose_trailing ); close( infd ); if( file_index.retval != 0 ) { diff --git a/lzip.h b/lzip.h index 1e6cdd2..01bf942 100644 --- a/lzip.h +++ b/lzip.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2017 Antonio Diaz Diaz. + Copyright (C) 2010-2018 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 @@ -22,6 +22,8 @@ #define min(x,y) ((x) <= (y) ? (x) : (y)) #endif +void * resize_buffer( void * buf, const unsigned min_size ); + typedef int State; enum { states = 12 }; @@ -114,9 +116,13 @@ static inline void Lm_init( struct Len_model * const lm ) } +/* defined in main.c */ +extern int verbosity; + struct Pretty_print { const char * name; + char * padded_name; const char * stdin_name; unsigned longest_name; bool first_post; @@ -124,11 +130,12 @@ struct Pretty_print static inline void Pp_init( struct Pretty_print * const pp, const char * const filenames[], - const int num_filenames, const int verbosity ) + const int num_filenames ) { unsigned stdin_name_len; int i; pp->name = 0; + pp->padded_name = 0; pp->stdin_name = "(stdin)"; pp->longest_name = 0; pp->first_post = false; @@ -147,9 +154,19 @@ static inline void Pp_init( struct Pretty_print * const pp, static inline void Pp_set_name( struct Pretty_print * const pp, const char * const filename ) { + unsigned name_len, padded_name_len, i = 0; + if( filename && filename[0] && strcmp( filename, "-" ) != 0 ) pp->name = filename; else pp->name = pp->stdin_name; + name_len = strlen( pp->name ); + padded_name_len = max( name_len, pp->longest_name ) + 4; + pp->padded_name = resize_buffer( pp->padded_name, padded_name_len + 1 ); + while( i < 2 ) pp->padded_name[i++] = ' '; + while( i < name_len + 2 ) { pp->padded_name[i] = pp->name[i-2]; ++i; } + pp->padded_name[i++] = ':'; + while( i < padded_name_len ) pp->padded_name[i++] = ' '; + pp->padded_name[i] = 0; pp->first_post = true; } @@ -202,12 +219,21 @@ enum { Fh_size = 6 }; static inline bool Fh_verify_magic( const File_header data ) { return ( memcmp( data, magic_string, 4 ) == 0 ); } -/* detect truncated header */ -static inline bool Fh_verify_prefix( const File_header data, const int size ) +/* detect (truncated) header */ +static inline bool Fh_verify_prefix( const File_header data, const int sz ) { - int i; for( i = 0; i < size && i < 4; ++i ) + int i; for( i = 0; i < sz && i < 4; ++i ) if( data[i] != magic_string[i] ) return false; - return ( size > 0 ); + return ( sz > 0 ); + } + +/* detect corrupt header */ +static inline bool Fh_verify_corrupt( const File_header data ) + { + int matches = 0; + int i; for( i = 0; i < 4; ++i ) + if( data[i] == magic_string[i] ) ++matches; + return ( matches > 1 && matches < 4 ); } static inline uint8_t Fh_version( const File_header data ) @@ -256,6 +282,7 @@ static inline unsigned long long Ft_get_member_size( const File_trailer data ) static const char * const bad_magic_msg = "Bad magic number (file not in lzip format)."; static const char * const bad_dict_msg = "Invalid dictionary size in member header."; +static const char * const corrupt_mm_msg = "Corrupt header in multimember file."; static const char * const trailing_msg = "Trailing data not allowed."; /* defined in decoder.c */ @@ -263,17 +290,21 @@ int readblock( const int fd, uint8_t * const buf, const int size ); /* defined in list.c */ int list_files( const char * const filenames[], const int num_filenames, - const bool ignore_trailing ); + const bool ignore_trailing, const bool loose_trailing ); /* defined in main.c */ -extern int verbosity; struct stat; const char * bad_version( const unsigned version ); const char * format_ds( const unsigned dictionary_size ); +void show_header( const unsigned dictionary_size ); int open_instream( const char * const name, struct stat * const in_statsp, const bool no_ofile, const bool reg_only ); -void * resize_buffer( void * buf, const unsigned min_size ); void cleanup_and_fail( const int retval ); void show_error( const char * const msg, const int errcode, const bool help ); void show_file_error( const char * const filename, const char * const msg, const int errcode ); +struct Range_decoder; +void show_dprogress( const unsigned long long cfile_size, + const unsigned long long partial_size, + const struct Range_decoder * const d, + struct Pretty_print * const p ); diff --git a/main.c b/main.c index 47a72d6..9a14137 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2017 Antonio Diaz Diaz. + Copyright (C) 2010-2018 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 @@ -67,7 +67,7 @@ int verbosity = 0; const char * const Program_name = "Lunzip"; const char * const program_name = "lunzip"; -const char * const program_year = "2017"; +const char * const program_year = "2018"; const char * invocation_name = 0; const struct { const char * from; const char * to; } known_extensions[] = { @@ -113,6 +113,7 @@ static void show_help( void ) " -t, --test test compressed file integrity\n" " -u, --buffer-size= set output buffer size in bytes\n" " -v, --verbose be verbose (a 2nd -v gives more)\n" + " --loose-trailing allow trailing data seeming corrupt header\n" "If no file names are given, or if a file is '-', lunzip decompresses\n" "from standard input to standard output.\n" "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" @@ -138,6 +139,35 @@ static void show_version( void ) } +/* assure at least a minimum size for buffer 'buf' */ +void * resize_buffer( void * buf, const unsigned min_size ) + { + if( buf ) buf = realloc( buf, min_size ); + else buf = malloc( min_size ); + if( !buf ) + { + show_error( "Not enough memory.", 0, false ); + cleanup_and_fail( 1 ); + } + return buf; + } + + +void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ) + { + if( verbosity >= 0 ) + { + if( pp->first_post ) + { + pp->first_post = false; + fputs( pp->padded_name, stderr ); + if( !msg ) fflush( stderr ); + } + if( msg ) fprintf( stderr, "%s\n", msg ); + } + } + + const char * bad_version( const unsigned version ) { static char buf[80]; @@ -155,10 +185,10 @@ const char * format_ds( const unsigned dictionary_size ) { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; const char * p = ""; const char * np = " "; - unsigned num = dictionary_size, i; + unsigned num = dictionary_size; bool exact = ( num % factor == 0 ); - for( i = 0; i < 8 && ( num > 9999 || ( exact && num >= factor ) ); ++i ) + int i; for( i = 0; i < 8 && ( num > 9999 || ( exact && num >= factor ) ); ++i ) { num /= factor; if( num % factor != 0 ) exact = false; p = prefix[i]; np = ""; } snprintf( buf, bufsize, "%s%4u %sB", np, num, p ); @@ -166,10 +196,9 @@ const char * format_ds( const unsigned dictionary_size ) } -static void show_header( const unsigned dictionary_size ) +void show_header( const unsigned dictionary_size ) { - if( verbosity >= 3 ) - fprintf( stderr, "dictionary %s. ", format_ds( dictionary_size ) ); + fprintf( stderr, "dictionary %s, ", format_ds( dictionary_size ) ); } @@ -263,6 +292,31 @@ static int extension_index( const char * const name ) } +static void set_d_outname( const char * const name, const int eindex ) + { + const unsigned name_len = strlen( name ); + if( eindex >= 0 ) + { + const char * const from = known_extensions[eindex].from; + const unsigned from_len = strlen( from ); + if( name_len > from_len ) + { + output_filename = resize_buffer( output_filename, name_len + + strlen( known_extensions[eindex].to ) + 1 ); + strcpy( output_filename, name ); + strcpy( output_filename + name_len - from_len, known_extensions[eindex].to ); + return; + } + } + output_filename = resize_buffer( output_filename, name_len + 4 + 1 ); + strcpy( output_filename, name ); + strcat( output_filename, ".out" ); + if( verbosity >= 1 ) + fprintf( stderr, "%s: Can't guess original name for '%s' -- using '%s'\n", + program_name, name, output_filename ); + } + + int open_instream( const char * const name, struct stat * const in_statsp, const bool no_ofile, const bool reg_only ) { @@ -291,45 +345,6 @@ int open_instream( const char * const name, struct stat * const in_statsp, } -/* assure at least a minimum size for buffer 'buf' */ -void * resize_buffer( void * buf, const unsigned min_size ) - { - if( buf ) buf = realloc( buf, min_size ); - else buf = malloc( min_size ); - if( !buf ) - { - show_error( "Not enough memory.", 0, false ); - cleanup_and_fail( 1 ); - } - return buf; - } - - -static void set_d_outname( const char * const name, const int eindex ) - { - const unsigned name_len = strlen( name ); - if( eindex >= 0 ) - { - const char * const from = known_extensions[eindex].from; - const unsigned from_len = strlen( from ); - if( name_len > from_len ) - { - output_filename = resize_buffer( output_filename, name_len + - strlen( known_extensions[eindex].to ) + 1 ); - strcpy( output_filename, name ); - strcpy( output_filename + name_len - from_len, known_extensions[eindex].to ); - return; - } - } - output_filename = resize_buffer( output_filename, name_len + 4 + 1 ); - strcpy( output_filename, name ); - strcat( output_filename, ".out" ); - if( verbosity >= 1 ) - fprintf( stderr, "%s: Can't guess original name for '%s' -- using '%s'\n", - program_name, name, output_filename ); - } - - static bool open_outstream( const bool force, const bool from_stdin ) { const mode_t usr_rw = S_IRUSR | S_IWUSR; @@ -413,9 +428,9 @@ static unsigned char xdigit( const unsigned value ) static bool show_trailing_data( const uint8_t * const data, const int size, struct Pretty_print * const pp, const bool all, - const bool ignore_trailing ) + const int ignore_trailing ) /* -1 = show */ { - if( verbosity >= 4 || !ignore_trailing ) + if( verbosity >= 4 || ignore_trailing <= 0 ) { int i; char buf[80]; @@ -433,15 +448,16 @@ static bool show_trailing_data( const uint8_t * const data, const int size, if( len < sizeof buf ) buf[len++] = '\''; if( len < sizeof buf ) buf[len] = 0; else buf[sizeof buf - 1] = 0; Pp_show_msg( pp, buf ); - if( !ignore_trailing ) show_file_error( pp->name, trailing_msg, 0 ); + if( ignore_trailing == 0 ) show_file_error( pp->name, trailing_msg, 0 ); } - return ignore_trailing; + return ( ignore_trailing > 0 ); } -static int decompress( const int infd, struct Pretty_print * const pp, - const unsigned buffer_size, - const bool ignore_trailing, const bool testing ) +static int decompress( const unsigned long long cfile_size, const int infd, + struct Pretty_print * const pp, const unsigned buffer_size, + const bool ignore_trailing, const bool loose_trailing, + const bool testing ) { unsigned long long partial_file_pos = 0; struct Range_decoder rdec; @@ -463,8 +479,12 @@ static int decompress( const int infd, struct Pretty_print * const pp, size = Rd_read_data( &rdec, header, Fh_size ); if( Rd_finished( &rdec ) ) /* End Of File */ { - if( first_member || Fh_verify_prefix( header, size ) ) - { Pp_show_msg( pp, "File ends unexpectedly at member header." ); + if( first_member ) + { show_file_error( pp->name, "File ends unexpectedly at member header.", 0 ); + retval = 2; } + else if( Fh_verify_prefix( header, size ) ) + { Pp_show_msg( pp, "Truncated header in multimember file." ); + show_trailing_data( header, size, pp, true, -1 ); retval = 2; } else if( size > 0 && !show_trailing_data( header, size, pp, true, ignore_trailing ) ) @@ -475,27 +495,30 @@ static int decompress( const int infd, struct Pretty_print * const pp, { if( first_member ) { show_file_error( pp->name, bad_magic_msg, 0 ); retval = 2; } + else if( !loose_trailing && Fh_verify_corrupt( header ) ) + { Pp_show_msg( pp, corrupt_mm_msg ); + show_trailing_data( header, size, pp, false, -1 ); + retval = 2; } else if( !show_trailing_data( header, size, pp, false, ignore_trailing ) ) retval = 2; break; } if( !Fh_verify_version( header ) ) - { - Pp_show_msg( pp, bad_version( Fh_version( header ) ) ); - retval = 2; break; - } + { Pp_show_msg( pp, bad_version( Fh_version( header ) ) ); + retval = 2; break; } dictionary_size = Fh_get_dictionary_size( header ); if( !isvalid_ds( dictionary_size ) ) { Pp_show_msg( pp, bad_dict_msg ); retval = 2; break; } if( verbosity >= 2 || ( verbosity == 1 && first_member ) ) - { Pp_show_msg( pp, 0 ); show_header( dictionary_size ); } + Pp_show_msg( pp, 0 ); if( !LZd_init( &decoder, &rdec, buffer_size, dictionary_size, outfd ) ) { Pp_show_msg( pp, "Not enough memory. Try a smaller output buffer size." ); retval = 1; break; } + show_dprogress( cfile_size, partial_file_pos, &rdec, pp ); /* init */ result = LZd_decode_member( &decoder, pp ); partial_file_pos += Rd_member_position( &rdec ); LZd_free( &decoder ); @@ -568,42 +591,76 @@ void internal_error( const char * const msg ) } +void show_dprogress( const unsigned long long cfile_size, + const unsigned long long partial_size, + const struct Range_decoder * const d, + struct Pretty_print * const p ) + { + static unsigned long long csize = 0; /* file_size / 100 */ + static unsigned long long psize = 0; + static const struct Range_decoder * rdec = 0; + static struct Pretty_print * pp = 0; + static int counter = 0; + static bool enabled = true; + + if( !enabled ) return; + if( p ) /* initialize static vars */ + { + if( verbosity < 2 || !isatty( STDERR_FILENO ) ) { enabled = false; return; } + csize = cfile_size; psize = partial_size; rdec = d; pp = p; counter = 0; + } + if( rdec && pp && --counter <= 0 ) + { + const unsigned long long pos = psize + Rd_member_position( rdec ); + counter = 7; /* update display every 114688 bytes */ + if( csize > 0 ) + fprintf( stderr, "%4llu%% %.1f MB\r", pos / csize, pos / 1000000.0 ); + else + fprintf( stderr, " %.1f MB\r", pos / 1000000.0 ); + Pp_reset( pp ); Pp_show_msg( pp, 0 ); /* restore cursor position */ + } + } + + int main( const int argc, const char * const argv[] ) { const char * default_output_filename = ""; const char ** filenames = 0; int num_filenames = 0; unsigned buffer_size = max_dictionary_size; - int infd = -1; enum Mode program_mode = m_compress; int argind = 0; + int failed_tests = 0; int retval = 0; int i; bool filenames_given = false; bool force = false; bool ignore_trailing = true; bool keep_input_files = false; + bool loose_trailing = false; bool stdin_used = false; bool to_stdout = false; struct Pretty_print pp; + enum { opt_lt = 256 }; const struct ap_Option options[] = { - { 'a', "trailing-error", ap_no }, - { 'c', "stdout", ap_no }, - { 'd', "decompress", ap_no }, - { 'f', "force", ap_no }, - { 'h', "help", ap_no }, - { 'k', "keep", ap_no }, - { 'l', "list", ap_no }, - { 'n', "threads", ap_yes }, - { 'o', "output", ap_yes }, - { 'q', "quiet", ap_no }, - { 't', "test", ap_no }, - { 'u', "buffer-size", ap_yes }, - { 'v', "verbose", ap_no }, - { 'V', "version", ap_no }, - { 0 , 0, ap_no } }; + { 'a', "trailing-error", ap_no }, + { 'c', "stdout", ap_no }, + { 'd', "decompress", ap_no }, + { 'f', "force", ap_no }, + { 'h', "help", ap_no }, + { 'k', "keep", ap_no }, + { 'l', "list", ap_no }, + { 'n', "threads", ap_yes }, + { 'o', "output", ap_yes }, + { 'q', "quiet", ap_no }, + { 't', "test", ap_no }, + { 'u', "buffer-size", ap_yes }, + { 'v', "verbose", ap_no }, + { 'V', "version", ap_no }, + { opt_lt, "loose-trailing", ap_no }, + { 0 , 0, ap_no } }; struct Arg_parser parser; @@ -636,6 +693,7 @@ int main( const int argc, const char * const argv[] ) case 'u': buffer_size = get_dict_size( arg ); break; case 'v': if( verbosity < 4 ) ++verbosity; break; case 'V': show_version(); return 0; + case opt_lt: loose_trailing = true; break; default : internal_error( "uncaught option." ); } } /* end process options */ @@ -656,7 +714,7 @@ int main( const int argc, const char * const argv[] ) } if( program_mode == m_list ) - return list_files( filenames, num_filenames, ignore_trailing ); + return list_files( filenames, num_filenames, ignore_trailing, loose_trailing ); if( program_mode == m_test ) outfd = -1; @@ -681,12 +739,14 @@ int main( const int argc, const char * const argv[] ) ( filenames_given || default_output_filename[0] ) ) set_signals(); - Pp_init( &pp, filenames, num_filenames, verbosity ); + Pp_init( &pp, filenames, num_filenames ); output_filename = resize_buffer( output_filename, 1 ); for( i = 0; i < num_filenames; ++i ) { + unsigned long long cfile_size; const char * input_filename = ""; + int infd; int tmp; struct stat in_stats; const struct stat * in_statsp; @@ -708,7 +768,7 @@ int main( const int argc, const char * const argv[] ) if( !open_outstream( force, true ) ) { if( retval < 1 ) retval = 1; - close( infd ); infd = -1; + close( infd ); continue; } } @@ -729,7 +789,7 @@ int main( const int argc, const char * const argv[] ) if( !open_outstream( force, false ) ) { if( retval < 1 ) retval = 1; - close( infd ); infd = -1; + close( infd ); continue; } } @@ -742,7 +802,7 @@ int main( const int argc, const char * const argv[] ) show_file_error( pp.name, "I won't read compressed data from a terminal.", 0 ); if( retval < 1 ) retval = 1; - if( program_mode == m_test ) { close( infd ); infd = -1; continue; } + if( program_mode == m_test ) { close( infd ); continue; } cleanup_and_fail( retval ); } @@ -761,24 +821,33 @@ int main( const int argc, const char * const argv[] ) } in_statsp = input_filename[0] ? &in_stats : 0; - tmp = decompress( infd, &pp, buffer_size, ignore_trailing, program_mode == m_test ); + cfile_size = ( in_statsp && S_ISREG( in_statsp->st_mode ) ) ? + ( in_statsp->st_size + 99 ) / 100 : 0; + tmp = decompress( cfile_size, infd, &pp, buffer_size, ignore_trailing, + loose_trailing, program_mode == m_test ); if( tmp > retval ) retval = tmp; - if( tmp && program_mode != m_test ) cleanup_and_fail( retval ); + if( tmp ) + { if( program_mode != m_test ) cleanup_and_fail( retval ); + else ++failed_tests; } if( delete_output_on_interrupt ) close_and_set_permissions( in_statsp ); if( input_filename[0] ) { - close( infd ); infd = -1; + close( infd ); if( !keep_input_files && !to_stdout && program_mode != m_test ) remove( input_filename ); } } if( outfd >= 0 && close( outfd ) != 0 ) { - show_error( "Can't close stdout", errno, false ); + show_error( "Error closing stdout", errno, false ); if( retval < 1 ) retval = 1; } + if( failed_tests > 0 && verbosity >= 1 && num_filenames > 1 ) + fprintf( stderr, "%s: warning: %d %s failed the test.\n", + program_name, failed_tests, + ( failed_tests == 1 ) ? "file" : "files" ); free( output_filename ); free( filenames ); ap_free( &parser ); diff --git a/testsuite/check.sh b/testsuite/check.sh index 334cb71..728c7c6 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # check script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2017 Antonio Diaz Diaz. +# Copyright (C) 2010-2018 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission # to copy, distribute and modify it. @@ -51,6 +51,8 @@ rm -f uin.lz [ $? = 2 ] || test_failed $LINENO "${LZIP}" -cdq < in [ $? = 2 ] || test_failed $LINENO +"${LZIP}" -dq -o in < "${in_lz}" +[ $? = 1 ] || test_failed $LINENO # these are for code coverage "${LZIP}" -lt "${in_lz}" 2> /dev/null [ $? = 1 ] || test_failed $LINENO @@ -169,6 +171,41 @@ done printf "\ntesting bad input..." +headers='LZIp LZiP LZip LzIP LzIp LziP lZIP lZIp lZiP lzIP' +body='\001\014\000\203\377\373\377\377\300\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000$\000\000\000\000\000\000\000' +cat "${in_lz}" > in0.lz +printf "LZIP${body}" >> in0.lz +if "${LZIP}" -tq in0.lz ; then + for header in ${headers} ; do + printf "${header}${body}" > in0.lz # first member + "${LZIP}" -lq in0.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq in0.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -lq --loose-trailing in0.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq --loose-trailing in0.lz + [ $? = 2 ] || test_failed $LINENO ${header} + cat "${in_lz}" > in0.lz + printf "${header}${body}" >> in0.lz # trailing data + "${LZIP}" -lq in0.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq in0.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -lq --loose-trailing in0.lz + [ $? = 0 ] || test_failed $LINENO ${header} + "${LZIP}" -t --loose-trailing in0.lz + [ $? = 0 ] || test_failed $LINENO ${header} + "${LZIP}" -lq --loose-trailing --trailing-error in0.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq --loose-trailing --trailing-error in0.lz + [ $? = 2 ] || test_failed $LINENO ${header} + done +else + printf "\nwarning: skipping header test: 'printf' does not work on your system." +fi +rm -f in0.lz + cat "${in_lz}" "${in_lz}" "${in_lz}" > in3.lz || framework_failure if dd if=in3.lz of=trunc.lz bs=14752 count=1 2> /dev/null && [ -e trunc.lz ] && cmp in2.lz trunc.lz > /dev/null 2>&1 ; then @@ -176,7 +213,7 @@ if dd if=in3.lz of=trunc.lz bs=14752 count=1 2> /dev/null && dd if=in3.lz of=trunc.lz bs=$i count=1 2> /dev/null "${LZIP}" -lq trunc.lz [ $? = 2 ] || test_failed $LINENO $i - "${LZIP}" -t trunc.lz 2> /dev/null + "${LZIP}" -tq trunc.lz [ $? = 2 ] || test_failed $LINENO $i "${LZIP}" -tq < trunc.lz [ $? = 2 ] || test_failed $LINENO $i @@ -188,6 +225,7 @@ if dd if=in3.lz of=trunc.lz bs=14752 count=1 2> /dev/null && else printf "\nwarning: skipping truncation test: 'dd' does not work on your system." fi +rm -f in3.lz trunc.lz cat "${in_lz}" > ingin.lz || framework_failure printf "g" >> ingin.lz || framework_failure @@ -200,6 +238,7 @@ cmp in copy || test_failed $LINENO "${LZIP}" -t < ingin.lz || test_failed $LINENO "${LZIP}" -d < ingin.lz > copy || test_failed $LINENO cmp in copy || test_failed $LINENO +rm -f ingin.lz echo if [ ${fail} = 0 ] ; then From afb468092e6a1a2decd17ed447f674b76660152d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 17 Feb 2025 22:26:33 +0100 Subject: [PATCH 04/10] Adding upstream version 1.11. Signed-off-by: Daniel Baumann --- ChangeLog | 15 +- INSTALL | 14 +- Makefile.in | 6 +- NEWS | 36 ++--- README | 46 +++--- carg_parser.c | 2 +- carg_parser.h | 2 +- configure | 16 +- decoder.c | 144 +++++++++--------- decoder.h | 2 +- doc/lunzip.1 | 4 +- file_index.c | 272 ---------------------------------- list.c | 38 ++--- lzip.h | 56 ++++--- lzip_index.c | 273 +++++++++++++++++++++++++++++++++++ file_index.h => lzip_index.h | 38 ++--- main.c | 105 ++++++++------ testsuite/check.sh | 114 +++++++++------ 18 files changed, 621 insertions(+), 562 deletions(-) delete mode 100644 file_index.c create mode 100644 lzip_index.c rename file_index.h => lzip_index.h (65%) diff --git a/ChangeLog b/ChangeLog index 54b4b03..ba6ef85 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2019-01-01 Antonio Diaz Diaz + + * Version 1.11 released. + * File_* renamed to Lzip_*. + * lzip.h (Lzip_trailer): New function 'Lt_verify_consistency'. + * lzip_index.c: Detect some kinds of corrupt trailers. + * main.c (main): Check return value of close( infd ). + * main.c: Compile on DOS with DJGPP. + * configure: Accept appending to CFLAGS, 'CFLAGS+=OPTIONS'. + * INSTALL: Document use of CFLAGS+='-D __USE_MINGW_ANSI_STDIO'. + 2018-02-05 Antonio Diaz Diaz * Version 1.10 released. @@ -19,7 +30,7 @@ * Decompression time has been reduced by 7%. * main.c: Continue testing if any input file is a terminal. * main.c: Show trailing data in both hexadecimal and ASCII. - * file_index.c: Improve detection of bad dict and trailing data. + * lzip_index.c: Improve detection of bad dict and trailing data. * lzip.h: Unified messages for bad magic, trailing data, etc. 2016-05-12 Antonio Diaz Diaz @@ -93,7 +104,7 @@ * Created from the decompression code of clzip 1.1. -Copyright (C) 2010-2018 Antonio Diaz Diaz. +Copyright (C) 2010-2019 Antonio Diaz Diaz. This file is a collection of facts, and thus it is not copyrightable, but just in case, you have unlimited permission to copy, distribute and diff --git a/INSTALL b/INSTALL index 4543b8d..2652bad 100644 --- a/INSTALL +++ b/INSTALL @@ -1,10 +1,14 @@ Requirements ------------ You will need a C compiler. -I use gcc 5.3.0 and 4.1.2, but the code should compile with any -standards compliant compiler. +I use gcc 5.3.0 and 4.1.2, but the code should compile with any standards +compliant compiler. Gcc is available at http://gcc.gnu.org. +The operating system must allow signal handlers read access to objects with +static storage duration so that the cleanup handler for Control-C can delete +the partial output file. + Procedure --------- @@ -23,6 +27,10 @@ the main archive. cd lunzip[version] ./configure + If you are compiling on MinGW, use: + + ./configure CFLAGS+='-D __USE_MINGW_ANSI_STDIO' + 3. Run make. make @@ -61,7 +69,7 @@ After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2010-2018 Antonio Diaz Diaz. +Copyright (C) 2010-2019 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute and modify it. diff --git a/Makefile.in b/Makefile.in index 4c0d3a5..691bd14 100644 --- a/Makefile.in +++ b/Makefile.in @@ -7,7 +7,7 @@ INSTALL_DIR = $(INSTALL) -d -m 755 SHELL = /bin/sh CAN_RUN_INSTALLINFO = $(SHELL) -c "install-info --version" > /dev/null 2>&1 -objs = carg_parser.o file_index.o list.o decoder.o main.o +objs = carg_parser.o lzip_index.o list.o decoder.o main.o .PHONY : all install install-bin install-info install-man \ @@ -31,8 +31,8 @@ main.o : main.c $(objs) : Makefile carg_parser.o : carg_parser.h decoder.o : lzip.h decoder.h -file_index.o : lzip.h file_index.h -list.o : lzip.h file_index.h +list.o : lzip.h lzip_index.h +lzip_index.o : lzip.h lzip_index.h main.o : carg_parser.h lzip.h decoder.h diff --git a/NEWS b/NEWS index cd904d7..d49ddba 100644 --- a/NEWS +++ b/NEWS @@ -1,32 +1,14 @@ -Changes in version 1.10: +Changes in version 1.11: -The option '--loose-trailing', has been added. +Detection of forbidden combinations of characters in trailing data has +been improved. -The test used by lunzip to discriminate trailing data from a corrupt -header in multimember or concatenated files has been improved to a -Hamming distance (HD) of 3, and the 3 bit flips must happen in different -magic bytes for the test to fail. As a consequence some kinds of files -no longer can be appended to a lzip file as trailing data unless the -'--loose-trailing' option is used when decompressing. -Lziprecover can be used to remove conflicting trailing data from a file. +Errors are now also checked when closing the input file. -The contents of a corrupt or truncated header found in a multimember -file is now shown, after the error message, in the same format as -trailing data. +Lunzip now compiles on DOS with DJGPP. (Patch from Robert Riebisch). -The 'bits/byte' ratio has been replaced with the inverse compression -ratio in the output. +The configure script now accepts appending options to CFLAGS using the +syntax 'CFLAGS+=OPTIONS'. -The progress of decompression is now shown at verbosity level 2 (-vv) or -higher. - -Progress of decompression is only shown if stderr is a terminal. - -A final diagnostic is now shown at verbosity level 1 (-v) or higher if -any file fails the test when testing multiple files. - -In case of (de)compressed size mismatch, the stored size is now also -shown in hexadecimal to ease visual comparison. - -The dictionary size is now shown at verbosity level 4 (-vvvv) when -decompressing or testing. +It has been documented in INSTALL the use of +CFLAGS+='-D __USE_MINGW_ANSI_STDIO' when compiling on MinGW. diff --git a/README b/README index 947dc24..b86ff58 100644 --- a/README +++ b/README @@ -5,22 +5,21 @@ small size makes it well suited for embedded devices or software installers that need to decompress files but don't need compression capabilities. Lunzip is fully compatible with lzip-1.4 or newer. -The lzip file format is designed for data sharing and long-term -archiving, taking into account both data integrity and decoder -availability: +The lzip file format is designed for data sharing and long-term archiving, +taking into account both data integrity and decoder availability: * The lzip format provides very safe integrity checking and some data - recovery means. The lziprecover program can repair bit-flip errors + recovery means. The lziprecover program can repair bit flip errors (one of the most common forms of data corruption) in lzip files, and provides data recovery capabilities, including error-checked merging of damaged copies of a file. * The lzip format is as simple as possible (but not simpler). The - lzip manual provides the source code of a simple decompressor along - with a detailed explanation of how it works, so that with the only - help of the lzip manual it would be possible for a digital - archaeologist to extract the data from a lzip file long after - quantum computers eventually render LZMA obsolete. + lzip manual provides the source code of a simple decompressor + along with a detailed explanation of how it works, so that with + the only help of the lzip manual it would be possible for a + digital archaeologist to extract the data from a lzip file long + after quantum computers eventually render LZMA obsolete. * Additionally the lzip reference implementation is copylefted, which guarantees that it will remain free forever. @@ -30,26 +29,25 @@ repair the nearer it is from the beginning of the file. Therefore, with the help of lziprecover, losing an entire archive just because of a corrupt byte near the beginning is a thing of the past. -Lunzip uses the same well-defined exit status values used by lzip and -bzip2, which makes it safer than decompressors returning ambiguous -warning values (like gunzip) when it is used as a back end for other -programs like tar or zutils. +Lunzip uses the same well-defined exit status values used by lzip, which +makes it safer than decompressors returning ambiguous warning values (like +gunzip) when it is used as a back end for other programs like tar or zutils. -Lunzip provides a "low memory" mode able to decompress any file using as +Lunzip provides a 'low memory' mode able to decompress any file using as little memory as 50 kB, irrespective of the dictionary size used to compress the file. To activate it, specify the size of the output buffer -with the "--buffer-size" option and lunzip will use the decompressed +with the '--buffer-size' option and lunzip will use the decompressed file as dictionary for distances beyond the buffer size. Of course, the smaller the buffer size used in relation to the dictionary size, the more accesses to disk are needed and the slower the decompression is. -This "low memory" mode only works when decompressing to a regular file +This 'low memory' mode only works when decompressing to a regular file and is intended for systems without enough memory (RAM + swap) to keep the whole dictionary at once. It has been tested on a laptop with a 486 processor and 4 MiB of RAM. The amount of memory required by lunzip to decompress a file is about 46 kB larger than the dictionary size used to compress that file, unless -the "--buffer-size" option is specified. +the '--buffer-size' option is specified. Lunzip attempts to guess the name for the decompressed file from that of the compressed file as follows: @@ -60,21 +58,21 @@ anyothername becomes anyothername.out Decompressing a file is much like copying or moving it; therefore lunzip preserves the access and modification dates, permissions, and, when -possible, ownership of the file just as "cp -p" does. (If the user ID or +possible, ownership of the file just as 'cp -p' does. (If the user ID or the group ID can't be duplicated, the file permission bits S_ISUID and S_ISGID are cleared). Lunzip is able to read from some types of non regular files if the -"--stdout" option is specified. +'--stdout' option is specified. If no file names are specified, lunzip decompresses from standard input to standard output. In this case, lunzip will decline to read compressed input from a terminal. -Lunzip will correctly decompress a file which is the concatenation of -two or more compressed files. The result is the concatenation of the -corresponding decompressed files. Integrity testing of concatenated -compressed files is also supported. +Lunzip will correctly decompress a file which is the concatenation of two or +more compressed files. The result is the concatenation of the corresponding +decompressed files. Integrity testing of concatenated compressed files is +also supported. The ideas embodied in lunzip are due to (at least) the following people: Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for @@ -83,7 +81,7 @@ range encoding), Igor Pavlov (for putting all the above together in LZMA), and Julian Seward (for bzip2's CLI). -Copyright (C) 2010-2018 Antonio Diaz Diaz. +Copyright (C) 2010-2019 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute and modify it. diff --git a/carg_parser.c b/carg_parser.c index 10ad4dc..ce01d7b 100644 --- a/carg_parser.c +++ b/carg_parser.c @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2018 Antonio Diaz Diaz. + Copyright (C) 2006-2019 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/carg_parser.h b/carg_parser.h index e1c70dd..dcae2de 100644 --- a/carg_parser.h +++ b/carg_parser.h @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2018 Antonio Diaz Diaz. + Copyright (C) 2006-2019 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/configure b/configure index 20fa969..1098e99 100755 --- a/configure +++ b/configure @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2018 Antonio Diaz Diaz. +# Copyright (C) 2010-2019 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission # to copy, distribute and modify it. pkgname=lunzip -pkgversion=1.10 +pkgversion=1.11 progname=lunzip srctrigger=doc/${progname}.1 @@ -70,6 +70,7 @@ while [ $# != 0 ] ; do echo " CC=COMPILER C compiler to use [${CC}]" echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]" echo " CFLAGS=OPTIONS command line options for the C compiler [${CFLAGS}]" + echo " CFLAGS+=OPTIONS append options to the current value of CFLAGS" echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]" echo exit 0 ;; @@ -93,10 +94,11 @@ while [ $# != 0 ] ; do --mandir=*) mandir=${optarg} ;; --no-create) no_create=yes ;; - CC=*) CC=${optarg} ;; - CPPFLAGS=*) CPPFLAGS=${optarg} ;; - CFLAGS=*) CFLAGS=${optarg} ;; - LDFLAGS=*) LDFLAGS=${optarg} ;; + CC=*) CC=${optarg} ;; + CPPFLAGS=*) CPPFLAGS=${optarg} ;; + CFLAGS=*) CFLAGS=${optarg} ;; + CFLAGS+=*) CFLAGS="${CFLAGS} ${optarg}" ;; + LDFLAGS=*) LDFLAGS=${optarg} ;; --*) echo "configure: WARNING: unrecognized option: '${option}'" 1>&2 ;; @@ -168,7 +170,7 @@ echo "LDFLAGS = ${LDFLAGS}" rm -f Makefile cat > Makefile << EOF # Makefile for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2018 Antonio Diaz Diaz. +# Copyright (C) 2010-2019 Antonio Diaz Diaz. # This file was generated automatically by configure. Don't edit. # # This Makefile is free software: you have unlimited permission diff --git a/decoder.c b/decoder.c index a995da3..3095015 100644 --- a/decoder.c +++ b/decoder.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2018 Antonio Diaz Diaz. + Copyright (C) 2010-2019 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 @@ -115,15 +115,15 @@ void LZd_flush_data( struct LZ_decoder * const d ) static bool LZd_verify_trailer( struct LZ_decoder * const d, struct Pretty_print * const pp ) { - File_trailer trailer; - int size = Rd_read_data( d->rdec, trailer, Ft_size ); + Lzip_trailer trailer; + int size = Rd_read_data( d->rdec, trailer, Lt_size ); const unsigned long long data_size = LZd_data_position( d ); const unsigned long long member_size = Rd_member_position( d->rdec ); unsigned td_crc; unsigned long long td_size, tm_size; bool error = false; - if( size < Ft_size ) + if( size < Lt_size ) { error = true; if( verbosity >= 0 ) @@ -132,10 +132,10 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, fprintf( stderr, "Trailer truncated at trailer position %d;" " some checks may fail.\n", size ); } - while( size < Ft_size ) trailer[size++] = 0; + while( size < Lt_size ) trailer[size++] = 0; } - td_crc = Ft_get_data_crc( trailer ); + td_crc = Lt_get_data_crc( trailer ); if( td_crc != LZd_crc( d ) ) { error = true; @@ -146,7 +146,7 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, td_crc, LZd_crc( d ) ); } } - td_size = Ft_get_data_size( trailer ); + td_size = Lt_get_data_size( trailer ); if( td_size != data_size ) { error = true; @@ -157,7 +157,7 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, td_size, td_size, data_size, data_size ); } } - tm_size = Ft_get_member_size( trailer ); + tm_size = Lt_get_member_size( trailer ); if( tm_size != member_size ) { error = true; @@ -232,9 +232,11 @@ int LZd_decode_member( struct LZ_decoder * const d, Rd_load( rdec ); while( !Rd_finished( rdec ) ) { + int len; const int pos_state = LZd_data_position( d ) & pos_state_mask; - if( Rd_decode_bit( rdec, &bm_match[state][pos_state] ) == 0 ) /* 1st bit */ + if( Rd_decode_bit( rdec, &bm_match[state][pos_state] ) == 0 ) /* 1st bit */ { + /* literal byte */ Bit_model * const bm = bm_literal[get_lit_state(LZd_peek_prev( d ))]; if( St_is_char( state ) ) { @@ -246,84 +248,82 @@ int LZd_decode_member( struct LZ_decoder * const d, state -= ( state < 10 ) ? 3 : 6; LZd_put_byte( d, Rd_decode_matched( rdec, bm, LZd_peek( d, rep0 ) ) ); } + continue; } - else /* match or repeated match */ + /* match or repeated match */ + if( Rd_decode_bit( rdec, &bm_rep[state] ) != 0 ) /* 2nd bit */ { - int len; - if( Rd_decode_bit( rdec, &bm_rep[state] ) != 0 ) /* 2nd bit */ + if( Rd_decode_bit( rdec, &bm_rep0[state] ) == 0 ) /* 3rd bit */ { - if( Rd_decode_bit( rdec, &bm_rep0[state] ) == 0 ) /* 3rd bit */ - { - if( Rd_decode_bit( rdec, &bm_len[state][pos_state] ) == 0 ) /* 4th bit */ - { state = St_set_short_rep( state ); - LZd_put_byte( d, LZd_peek( d, rep0 ) ); continue; } - } - else - { - unsigned distance; - if( Rd_decode_bit( rdec, &bm_rep1[state] ) == 0 ) /* 4th bit */ - distance = rep1; - else - { - if( Rd_decode_bit( rdec, &bm_rep2[state] ) == 0 ) /* 5th bit */ - distance = rep2; - else - { distance = rep3; rep3 = rep2; } - rep2 = rep1; - } - rep1 = rep0; - rep0 = distance; - } - state = St_set_rep( state ); - len = min_match_len + Rd_decode_len( rdec, &rep_len_model, pos_state ); + if( Rd_decode_bit( rdec, &bm_len[state][pos_state] ) == 0 ) /* 4th bit */ + { state = St_set_short_rep( state ); + LZd_put_byte( d, LZd_peek( d, rep0 ) ); continue; } } - else /* match */ + else { unsigned distance; - len = min_match_len + Rd_decode_len( rdec, &match_len_model, pos_state ); - distance = Rd_decode_tree6( rdec, bm_dis_slot[get_len_state(len)] ); - if( distance >= start_dis_model ) + if( Rd_decode_bit( rdec, &bm_rep1[state] ) == 0 ) /* 4th bit */ + distance = rep1; + else { - const unsigned dis_slot = distance; - const int direct_bits = ( dis_slot >> 1 ) - 1; - distance = ( 2 | ( dis_slot & 1 ) ) << direct_bits; - if( dis_slot < end_dis_model ) - distance += Rd_decode_tree_reversed( rdec, - bm_dis + ( distance - dis_slot ), direct_bits ); + if( Rd_decode_bit( rdec, &bm_rep2[state] ) == 0 ) /* 5th bit */ + distance = rep2; else + { distance = rep3; rep3 = rep2; } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = St_set_rep( state ); + len = min_match_len + Rd_decode_len( rdec, &rep_len_model, pos_state ); + } + else /* match */ + { + unsigned distance; + len = min_match_len + Rd_decode_len( rdec, &match_len_model, pos_state ); + distance = Rd_decode_tree6( rdec, bm_dis_slot[get_len_state(len)] ); + if( distance >= start_dis_model ) + { + const unsigned dis_slot = distance; + const int direct_bits = ( dis_slot >> 1 ) - 1; + distance = ( 2 | ( dis_slot & 1 ) ) << direct_bits; + if( dis_slot < end_dis_model ) + distance += Rd_decode_tree_reversed( rdec, + bm_dis + ( distance - dis_slot ), direct_bits ); + else + { + distance += + Rd_decode( rdec, direct_bits - dis_align_bits ) << dis_align_bits; + distance += Rd_decode_tree_reversed4( rdec, bm_align ); + if( distance == 0xFFFFFFFFU ) /* marker found */ { - distance += - Rd_decode( rdec, direct_bits - dis_align_bits ) << dis_align_bits; - distance += Rd_decode_tree_reversed4( rdec, bm_align ); - if( distance == 0xFFFFFFFFU ) /* marker found */ + Rd_normalize( rdec ); + LZd_flush_data( d ); + if( len == min_match_len ) /* End Of Stream marker */ { - Rd_normalize( rdec ); - LZd_flush_data( d ); - if( len == min_match_len ) /* End Of Stream marker */ - { - if( LZd_verify_trailer( d, pp ) ) return 0; else return 3; - } - if( len == min_match_len + 1 ) /* Sync Flush marker */ - { - Rd_load( rdec ); continue; - } - if( verbosity >= 0 ) - { - Pp_show_msg( pp, 0 ); - fprintf( stderr, "Unsupported marker code '%d'\n", len ); - } - return 4; + if( LZd_verify_trailer( d, pp ) ) return 0; else return 3; } + if( len == min_match_len + 1 ) /* Sync Flush marker */ + { + Rd_load( rdec ); continue; + } + if( verbosity >= 0 ) + { + Pp_show_msg( pp, 0 ); + fprintf( stderr, "Unsupported marker code '%d'\n", len ); + } + return 4; } } - rep3 = rep2; rep2 = rep1; rep1 = rep0; rep0 = distance; - state = St_set_match( state ); - if( rep0 >= d->dictionary_size || - ( rep0 >= LZd_data_position( d ) && !d->pos_wrapped_dic ) ) - { LZd_flush_data( d ); return 1; } } - copy_block( d, rep0, len ); + rep3 = rep2; rep2 = rep1; rep1 = rep0; rep0 = distance; + state = St_set_match( state ); + if( rep0 >= d->dictionary_size || + ( rep0 >= LZd_data_position( d ) && !d->pos_wrapped_dic ) ) + { LZd_flush_data( d ); return 1; } } + copy_block( d, rep0, len ); } LZd_flush_data( d ); return 2; diff --git a/decoder.h b/decoder.h index 7ec3133..ba4adb0 100644 --- a/decoder.h +++ b/decoder.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2018 Antonio Diaz Diaz. + Copyright (C) 2010-2019 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 diff --git a/doc/lunzip.1 b/doc/lunzip.1 index ffcd8a3..1198723 100644 --- a/doc/lunzip.1 +++ b/doc/lunzip.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. -.TH LUNZIP "1" "February 2018" "lunzip 1.10" "User Commands" +.TH LUNZIP "1" "January 2019" "lunzip 1.11" "User Commands" .SH NAME lunzip \- decompressor for the lzip format .SH SYNOPSIS @@ -81,7 +81,7 @@ Report bugs to lzip\-bug@nongnu.org .br Lunzip home page: http://www.nongnu.org/lzip/lunzip.html .SH COPYRIGHT -Copyright \(co 2018 Antonio Diaz Diaz. +Copyright \(co 2019 Antonio Diaz Diaz. License GPLv2+: GNU GPL version 2 or later .br This is free software: you are free to change and redistribute it. diff --git a/file_index.c b/file_index.c deleted file mode 100644 index 1872d67..0000000 --- a/file_index.c +++ /dev/null @@ -1,272 +0,0 @@ -/* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2018 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 . -*/ - -#define _FILE_OFFSET_BITS 64 - -#include -#include -#include -#include -#include -#include -#include - -#include "lzip.h" -#include "file_index.h" - - -static int seek_read( const int fd, uint8_t * const buf, const int size, - const long long pos ) - { - if( lseek( fd, pos, SEEK_SET ) == pos ) - return readblock( fd, buf, size ); - return 0; - } - - -static bool add_error( struct File_index * const fi, const char * const msg ) - { - const int len = strlen( msg ); - void * tmp = resize_buffer( fi->error, fi->error_size + len + 1 ); - if( !tmp ) return false; - fi->error = (char *)tmp; - strncpy( fi->error + fi->error_size, msg, len + 1 ); - fi->error_size += len; - return true; - } - - -static bool push_back_member( struct File_index * const fi, - const long long dp, const long long ds, - const long long mp, const long long ms, - const unsigned dict_size ) - { - struct Member * p; - void * tmp = resize_buffer( fi->member_vector, - ( fi->members + 1 ) * sizeof fi->member_vector[0] ); - if( !tmp ) - { add_error( fi, "Not enough memory." ); fi->retval = 1; return false; } - fi->member_vector = (struct Member *)tmp; - p = &(fi->member_vector[fi->members]); - init_member( p, dp, ds, mp, ms, dict_size ); - ++fi->members; - return true; - } - - -static void Fi_free_member_vector( struct File_index * const fi ) - { - if( fi->member_vector ) - { free( fi->member_vector ); fi->member_vector = 0; } - fi->members = 0; - } - - -static void Fi_reverse_member_vector( struct File_index * const fi ) - { - struct Member tmp; - long i; - for( i = 0; i < fi->members / 2; ++i ) - { - tmp = fi->member_vector[i]; - fi->member_vector[i] = fi->member_vector[fi->members-i-1]; - fi->member_vector[fi->members-i-1] = tmp; - } - } - - -static void Fi_set_errno_error( struct File_index * const fi, - const char * const msg ) - { - add_error( fi, msg ); add_error( fi, strerror( errno ) ); - fi->retval = 1; - } - -static void Fi_set_num_error( struct File_index * const fi, - const char * const msg, unsigned long long num ) - { - char buf[80]; - snprintf( buf, sizeof buf, "%s%llu", msg, num ); - add_error( fi, buf ); - fi->retval = 2; - } - - -/* If successful, push last member and set pos to member header. */ -static bool Fi_skip_trailing_data( struct File_index * const fi, - const int fd, long long * const pos, - const bool ignore_trailing, - const bool loose_trailing ) - { - enum { block_size = 16384, - buffer_size = block_size + Ft_size - 1 + Fh_size }; - uint8_t buffer[buffer_size]; - int bsize = *pos % block_size; /* total bytes in buffer */ - int search_size, rd_size; - unsigned long long ipos; - int i; - if( bsize <= buffer_size - block_size ) bsize += block_size; - search_size = bsize; /* bytes to search for trailer */ - rd_size = bsize; /* bytes to read from file */ - ipos = *pos - rd_size; /* aligned to block_size */ - if( *pos < min_member_size ) return false; - - while( true ) - { - const uint8_t max_msb = ( ipos + search_size ) >> 56; - if( seek_read( fd, buffer, rd_size, ipos ) != rd_size ) - { Fi_set_errno_error( fi, "Error seeking member trailer: " ); - return false; } - for( i = search_size; i >= Ft_size; --i ) - if( buffer[i-1] <= max_msb ) /* most significant byte of member_size */ - { - File_header header; - File_trailer * trailer = (File_trailer *)( buffer + i - Ft_size ); - const unsigned long long member_size = Ft_get_member_size( *trailer ); - unsigned dictionary_size; - if( member_size == 0 ) - { while( i > Ft_size && buffer[i-9] == 0 ) --i; continue; } - if( member_size < min_member_size || member_size > ipos + i ) - continue; - if( seek_read( fd, header, Fh_size, - ipos + i - member_size ) != Fh_size ) - { Fi_set_errno_error( fi, "Error reading member header: " ); - return false; } - dictionary_size = Fh_get_dictionary_size( header ); - if( !Fh_verify_magic( header ) || !Fh_verify_version( header ) || - !isvalid_ds( dictionary_size ) ) continue; - if( Fh_verify_prefix( buffer + i, bsize - i ) ) - { - add_error( fi, "Last member in input file is truncated or corrupt." ); - fi->retval = 2; return false; - } - if( !loose_trailing && bsize - i >= Fh_size && - Fh_verify_corrupt( buffer + i ) ) - { add_error( fi, corrupt_mm_msg ); fi->retval = 2; return false; } - if( !ignore_trailing ) - { add_error( fi, trailing_msg ); fi->retval = 2; return false; } - *pos = ipos + i - member_size; - return push_back_member( fi, 0, Ft_get_data_size( *trailer ), *pos, - member_size, dictionary_size ); - } - if( ipos <= 0 ) - { Fi_set_num_error( fi, "Member size in trailer is corrupt at pos ", - *pos - 8 ); - return false; } - bsize = buffer_size; - search_size = bsize - Fh_size; - rd_size = block_size; - ipos -= rd_size; - memcpy( buffer + rd_size, buffer, buffer_size - rd_size ); - } - } - - -bool Fi_init( struct File_index * const fi, const int infd, - const bool ignore_trailing, const bool loose_trailing ) - { - File_header header; - long long pos; - long i; - fi->member_vector = 0; - fi->error = 0; - fi->isize = lseek( infd, 0, SEEK_END ); - fi->members = 0; - fi->error_size = 0; - fi->retval = 0; - if( fi->isize < 0 ) - { Fi_set_errno_error( fi, "Input file is not seekable: " ); return false; } - if( fi->isize < min_member_size ) - { add_error( fi, "Input file is too short." ); fi->retval = 2; - return false; } - if( fi->isize > INT64_MAX ) - { add_error( fi, "Input file is too long (2^63 bytes or more)." ); - fi->retval = 2; return false; } - - if( seek_read( infd, header, Fh_size, 0 ) != Fh_size ) - { Fi_set_errno_error( fi, "Error reading member header: " ); return false; } - if( !Fh_verify_magic( header ) ) - { add_error( fi, bad_magic_msg ); fi->retval = 2; return false; } - if( !Fh_verify_version( header ) ) - { add_error( fi, bad_version( Fh_version( header ) ) ); fi->retval = 2; - return false; } - if( !isvalid_ds( Fh_get_dictionary_size( header ) ) ) - { add_error( fi, bad_dict_msg ); fi->retval = 2; return false; } - - pos = fi->isize; /* always points to a header or to EOF */ - while( pos >= min_member_size ) - { - File_trailer trailer; - unsigned long long member_size; - unsigned dictionary_size; - if( seek_read( infd, trailer, Ft_size, pos - Ft_size ) != Ft_size ) - { Fi_set_errno_error( fi, "Error reading member trailer: " ); break; } - member_size = Ft_get_member_size( trailer ); - if( member_size < min_member_size || member_size > (unsigned long long)pos ) - { - if( fi->members <= 0 ) - { if( Fi_skip_trailing_data( fi, infd, &pos, ignore_trailing, - loose_trailing ) ) continue; else return false; } - Fi_set_num_error( fi, "Member size in trailer is corrupt at pos ", pos - 8 ); - break; - } - if( seek_read( infd, header, Fh_size, pos - member_size ) != Fh_size ) - { Fi_set_errno_error( fi, "Error reading member header: " ); break; } - dictionary_size = Fh_get_dictionary_size( header ); - if( !Fh_verify_magic( header ) || !Fh_verify_version( header ) || - !isvalid_ds( dictionary_size ) ) - { - if( fi->members <= 0 ) - { if( Fi_skip_trailing_data( fi, infd, &pos, ignore_trailing, - loose_trailing ) ) continue; else return false; } - Fi_set_num_error( fi, "Bad header at pos ", pos - member_size ); - break; - } - pos -= member_size; - if( !push_back_member( fi, 0, Ft_get_data_size( trailer ), pos, - member_size, dictionary_size ) ) - return false; - } - if( pos != 0 || fi->members <= 0 ) - { - Fi_free_member_vector( fi ); - if( fi->retval == 0 ) - { add_error( fi, "Can't create file index." ); fi->retval = 2; } - return false; - } - Fi_reverse_member_vector( fi ); - for( i = 0; i < fi->members - 1; ++i ) - { - const long long end = block_end( fi->member_vector[i].dblock ); - if( end < 0 || end > INT64_MAX ) - { - Fi_free_member_vector( fi ); - add_error( fi, "Data in input file is too long (2^63 bytes or more)." ); - fi->retval = 2; return false; - } - fi->member_vector[i+1].dblock.pos = end; - } - return true; - } - - -void Fi_free( struct File_index * const fi ) - { - Fi_free_member_vector( fi ); - if( fi->error ) { free( fi->error ); fi->error = 0; } - fi->error_size = 0; - } diff --git a/list.c b/list.c index b0dfef7..97adb50 100644 --- a/list.c +++ b/list.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2018 Antonio Diaz Diaz. + Copyright (C) 2010-2019 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 @@ -25,7 +25,7 @@ #include #include "lzip.h" -#include "file_index.h" +#include "lzip_index.h" static void list_line( const unsigned long long uncomp_size, @@ -53,7 +53,7 @@ int list_files( const char * const filenames[], const int num_filenames, for( i = 0; i < num_filenames; ++i ) { const char * input_filename; - struct File_index file_index; + struct Lzip_index lzip_index; struct stat in_stats; /* not used */ int infd; const bool from_stdin = ( strcmp( filenames[i], "-" ) == 0 ); @@ -63,18 +63,18 @@ int list_files( const char * const filenames[], const int num_filenames, open_instream( input_filename, &in_stats, true, true ); if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } - Fi_init( &file_index, infd, ignore_trailing, loose_trailing ); + Li_init( &lzip_index, infd, ignore_trailing, loose_trailing ); close( infd ); - if( file_index.retval != 0 ) + if( lzip_index.retval != 0 ) { - show_file_error( input_filename, file_index.error, 0 ); - if( retval < file_index.retval ) retval = file_index.retval; - Fi_free( &file_index ); continue; + show_file_error( input_filename, lzip_index.error, 0 ); + if( retval < lzip_index.retval ) retval = lzip_index.retval; + Li_free( &lzip_index ); continue; } if( verbosity >= 0 ) { - const unsigned long long udata_size = Fi_udata_size( &file_index ); - const unsigned long long cdata_size = Fi_cdata_size( &file_index ); + const unsigned long long udata_size = Li_udata_size( &lzip_index ); + const unsigned long long cdata_size = Li_cdata_size( &lzip_index ); total_comp += cdata_size; total_uncomp += udata_size; ++files; if( first_post ) { @@ -87,23 +87,23 @@ int list_files( const char * const filenames[], const int num_filenames, long long trailing_size; unsigned dictionary_size = 0; long i; - for( i = 0; i < file_index.members; ++i ) + for( i = 0; i < lzip_index.members; ++i ) dictionary_size = - max( dictionary_size, Fi_dictionary_size( &file_index, i ) ); - trailing_size = Fi_file_size( &file_index ) - cdata_size; + max( dictionary_size, Li_dictionary_size( &lzip_index, i ) ); + trailing_size = Li_file_size( &lzip_index ) - cdata_size; printf( "%s %5ld %6lld ", format_ds( dictionary_size ), - file_index.members, trailing_size ); + lzip_index.members, trailing_size ); } list_line( udata_size, cdata_size, input_filename ); - if( verbosity >= 2 && file_index.members > 1 ) + if( verbosity >= 2 && lzip_index.members > 1 ) { long i; fputs( " member data_pos data_size member_pos member_size\n", stdout ); - for( i = 0; i < file_index.members; ++i ) + for( i = 0; i < lzip_index.members; ++i ) { - const struct Block * db = Fi_dblock( &file_index, i ); - const struct Block * mb = Fi_mblock( &file_index, i ); + const struct Block * db = Li_dblock( &lzip_index, i ); + const struct Block * mb = Li_mblock( &lzip_index, i ); printf( "%5ld %15llu %15llu %15llu %15llu\n", i + 1, db->pos, db->size, mb->pos, mb->size ); } @@ -111,7 +111,7 @@ int list_files( const char * const filenames[], const int num_filenames, } fflush( stdout ); } - Fi_free( &file_index ); + Li_free( &lzip_index ); } if( verbosity >= 0 && files > 1 ) { diff --git a/lzip.h b/lzip.h index 01bf942..23db964 100644 --- a/lzip.h +++ b/lzip.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2018 Antonio Diaz Diaz. + Copyright (C) 2010-2019 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 @@ -119,7 +119,7 @@ static inline void Lm_init( struct Len_model * const lm ) /* defined in main.c */ extern int verbosity; -struct Pretty_print +struct Pretty_print /* requires global var 'int verbosity' */ { const char * name; char * padded_name; @@ -146,7 +146,7 @@ static inline void Pp_init( struct Pretty_print * const pp, { const char * const s = filenames[i]; const unsigned len = (strcmp( s, "-" ) == 0) ? stdin_name_len : strlen( s ); - if( len > pp->longest_name ) pp->longest_name = len; + if( pp->longest_name < len ) pp->longest_name = len; } if( pp->longest_name == 0 ) pp->longest_name = stdin_name_len; } @@ -209,40 +209,40 @@ static inline bool isvalid_ds( const unsigned dictionary_size ) dictionary_size <= max_dictionary_size ); } -static const uint8_t magic_string[4] = { 0x4C, 0x5A, 0x49, 0x50 }; /* "LZIP" */ +static const uint8_t lzip_magic[4] = { 0x4C, 0x5A, 0x49, 0x50 }; /* "LZIP" */ -typedef uint8_t File_header[6]; /* 0-3 magic bytes */ +typedef uint8_t Lzip_header[6]; /* 0-3 magic bytes */ /* 4 version */ /* 5 coded_dict_size */ -enum { Fh_size = 6 }; +enum { Lh_size = 6 }; -static inline bool Fh_verify_magic( const File_header data ) - { return ( memcmp( data, magic_string, 4 ) == 0 ); } +static inline bool Lh_verify_magic( const Lzip_header data ) + { return ( memcmp( data, lzip_magic, 4 ) == 0 ); } /* detect (truncated) header */ -static inline bool Fh_verify_prefix( const File_header data, const int sz ) +static inline bool Lh_verify_prefix( const Lzip_header data, const int sz ) { int i; for( i = 0; i < sz && i < 4; ++i ) - if( data[i] != magic_string[i] ) return false; + if( data[i] != lzip_magic[i] ) return false; return ( sz > 0 ); } /* detect corrupt header */ -static inline bool Fh_verify_corrupt( const File_header data ) +static inline bool Lh_verify_corrupt( const Lzip_header data ) { int matches = 0; int i; for( i = 0; i < 4; ++i ) - if( data[i] == magic_string[i] ) ++matches; + if( data[i] == lzip_magic[i] ) ++matches; return ( matches > 1 && matches < 4 ); } -static inline uint8_t Fh_version( const File_header data ) +static inline uint8_t Lh_version( const Lzip_header data ) { return data[4]; } -static inline bool Fh_verify_version( const File_header data ) +static inline bool Lh_verify_version( const Lzip_header data ) { return ( data[4] == 1 ); } -static inline unsigned Fh_get_dictionary_size( const File_header data ) +static inline unsigned Lh_get_dictionary_size( const Lzip_header data ) { unsigned sz = ( 1 << ( data[5] & 0x1F ) ); if( sz > min_dictionary_size ) @@ -251,34 +251,48 @@ static inline unsigned Fh_get_dictionary_size( const File_header data ) } -typedef uint8_t File_trailer[20]; +typedef uint8_t Lzip_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 { Lt_size = 20 }; -enum { Ft_size = 20 }; - -static inline unsigned Ft_get_data_crc( const File_trailer data ) +static inline unsigned Lt_get_data_crc( const Lzip_trailer data ) { unsigned tmp = 0; int i; for( i = 3; i >= 0; --i ) { tmp <<= 8; tmp += data[i]; } return tmp; } -static inline unsigned long long Ft_get_data_size( const File_trailer data ) +static inline unsigned long long Lt_get_data_size( const Lzip_trailer data ) { unsigned long long tmp = 0; int i; for( i = 11; i >= 4; --i ) { tmp <<= 8; tmp += data[i]; } return tmp; } -static inline unsigned long long Ft_get_member_size( const File_trailer data ) +static inline unsigned long long Lt_get_member_size( const Lzip_trailer data ) { unsigned long long tmp = 0; int i; for( i = 19; i >= 12; --i ) { tmp <<= 8; tmp += data[i]; } return tmp; } +/* check internal consistency */ +static inline bool Lt_verify_consistency( const Lzip_trailer data ) + { + const unsigned crc = Lt_get_data_crc( data ); + const unsigned long long dsize = Lt_get_data_size( data ); + const unsigned long long msize = Lt_get_member_size( data ); + const unsigned long long mlimit = ( 9 * dsize + 7 ) / 8 + min_member_size; + const unsigned long long dlimit = 7090 * ( msize - 26 ) - 1; + if( ( crc == 0 ) != ( dsize == 0 ) ) return false; + if( msize < min_member_size ) return false; + if( mlimit > dsize && msize > mlimit ) return false; + if( dlimit > msize && dsize > dlimit ) return false; + return true; + } + static const char * const bad_magic_msg = "Bad magic number (file not in lzip format)."; static const char * const bad_dict_msg = "Invalid dictionary size in member header."; diff --git a/lzip_index.c b/lzip_index.c new file mode 100644 index 0000000..44c7b17 --- /dev/null +++ b/lzip_index.c @@ -0,0 +1,273 @@ +/* Lunzip - Decompressor for the lzip format + Copyright (C) 2010-2019 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 . +*/ + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include + +#include "lzip.h" +#include "lzip_index.h" + + +static int seek_read( const int fd, uint8_t * const buf, const int size, + const long long pos ) + { + if( lseek( fd, pos, SEEK_SET ) == pos ) + return readblock( fd, buf, size ); + return 0; + } + + +static bool add_error( struct Lzip_index * const li, const char * const msg ) + { + const int len = strlen( msg ); + void * tmp = resize_buffer( li->error, li->error_size + len + 1 ); + if( !tmp ) return false; + li->error = (char *)tmp; + strncpy( li->error + li->error_size, msg, len + 1 ); + li->error_size += len; + return true; + } + + +static bool push_back_member( struct Lzip_index * const li, + const long long dp, const long long ds, + const long long mp, const long long ms, + const unsigned dict_size ) + { + struct Member * p; + void * tmp = resize_buffer( li->member_vector, + ( li->members + 1 ) * sizeof li->member_vector[0] ); + if( !tmp ) + { add_error( li, "Not enough memory." ); li->retval = 1; return false; } + li->member_vector = (struct Member *)tmp; + p = &(li->member_vector[li->members]); + init_member( p, dp, ds, mp, ms, dict_size ); + ++li->members; + return true; + } + + +static void Li_free_member_vector( struct Lzip_index * const li ) + { + if( li->member_vector ) + { free( li->member_vector ); li->member_vector = 0; } + li->members = 0; + } + + +static void Li_reverse_member_vector( struct Lzip_index * const li ) + { + struct Member tmp; + long i; + for( i = 0; i < li->members / 2; ++i ) + { + tmp = li->member_vector[i]; + li->member_vector[i] = li->member_vector[li->members-i-1]; + li->member_vector[li->members-i-1] = tmp; + } + } + + +static void Li_set_errno_error( struct Lzip_index * const li, + const char * const msg ) + { + add_error( li, msg ); add_error( li, strerror( errno ) ); + li->retval = 1; + } + +static void Li_set_num_error( struct Lzip_index * const li, + const char * const msg, unsigned long long num ) + { + char buf[80]; + snprintf( buf, sizeof buf, "%s%llu", msg, num ); + add_error( li, buf ); + li->retval = 2; + } + + +/* If successful, push last member and set pos to member header. */ +static bool Li_skip_trailing_data( struct Lzip_index * const li, + const int fd, long long * const pos, + const bool ignore_trailing, + const bool loose_trailing ) + { + enum { block_size = 16384, + buffer_size = block_size + Lt_size - 1 + Lh_size }; + uint8_t buffer[buffer_size]; + int bsize = *pos % block_size; /* total bytes in buffer */ + int search_size, rd_size; + unsigned long long ipos; + int i; + if( *pos < min_member_size ) return false; + if( bsize <= buffer_size - block_size ) bsize += block_size; + search_size = bsize; /* bytes to search for trailer */ + rd_size = bsize; /* bytes to read from file */ + ipos = *pos - rd_size; /* aligned to block_size */ + + while( true ) + { + const uint8_t max_msb = ( ipos + search_size ) >> 56; + if( seek_read( fd, buffer, rd_size, ipos ) != rd_size ) + { Li_set_errno_error( li, "Error seeking member trailer: " ); + return false; } + for( i = search_size; i >= Lt_size; --i ) + if( buffer[i-1] <= max_msb ) /* most significant byte of member_size */ + { + Lzip_header header; + const Lzip_trailer * const trailer = + (const Lzip_trailer *)( buffer + i - Lt_size ); + const unsigned long long member_size = Lt_get_member_size( *trailer ); + unsigned dictionary_size; + if( member_size == 0 ) /* skip trailing zeros */ + { while( i > Lt_size && buffer[i-9] == 0 ) --i; continue; } + if( member_size > ipos + i || !Lt_verify_consistency( *trailer ) ) + continue; + if( seek_read( fd, header, Lh_size, + ipos + i - member_size ) != Lh_size ) + { Li_set_errno_error( li, "Error reading member header: " ); + return false; } + dictionary_size = Lh_get_dictionary_size( header ); + if( !Lh_verify_magic( header ) || !Lh_verify_version( header ) || + !isvalid_ds( dictionary_size ) ) continue; + if( Lh_verify_prefix( buffer + i, bsize - i ) ) + { + add_error( li, "Last member in input file is truncated or corrupt." ); + li->retval = 2; return false; + } + if( !loose_trailing && bsize - i >= Lh_size && + Lh_verify_corrupt( buffer + i ) ) + { add_error( li, corrupt_mm_msg ); li->retval = 2; return false; } + if( !ignore_trailing ) + { add_error( li, trailing_msg ); li->retval = 2; return false; } + *pos = ipos + i - member_size; + return push_back_member( li, 0, Lt_get_data_size( *trailer ), *pos, + member_size, dictionary_size ); + } + if( ipos <= 0 ) + { Li_set_num_error( li, "Bad trailer at pos ", *pos - Lt_size ); + return false; } + bsize = buffer_size; + search_size = bsize - Lh_size; + rd_size = block_size; + ipos -= rd_size; + memcpy( buffer + rd_size, buffer, buffer_size - rd_size ); + } + } + + +bool Li_init( struct Lzip_index * const li, const int infd, + const bool ignore_trailing, const bool loose_trailing ) + { + Lzip_header header; + long long pos; + long i; + li->member_vector = 0; + li->error = 0; + li->insize = lseek( infd, 0, SEEK_END ); + li->members = 0; + li->error_size = 0; + li->retval = 0; + if( li->insize < 0 ) + { Li_set_errno_error( li, "Input file is not seekable: " ); return false; } + if( li->insize < min_member_size ) + { add_error( li, "Input file is too short." ); li->retval = 2; + return false; } + if( li->insize > INT64_MAX ) + { add_error( li, "Input file is too long (2^63 bytes or more)." ); + li->retval = 2; return false; } + + if( seek_read( infd, header, Lh_size, 0 ) != Lh_size ) + { Li_set_errno_error( li, "Error reading member header: " ); return false; } + if( !Lh_verify_magic( header ) ) + { add_error( li, bad_magic_msg ); li->retval = 2; return false; } + if( !Lh_verify_version( header ) ) + { add_error( li, bad_version( Lh_version( header ) ) ); li->retval = 2; + return false; } + if( !isvalid_ds( Lh_get_dictionary_size( header ) ) ) + { add_error( li, bad_dict_msg ); li->retval = 2; return false; } + + pos = li->insize; /* always points to a header or to EOF */ + while( pos >= min_member_size ) + { + Lzip_trailer trailer; + unsigned long long member_size; + unsigned dictionary_size; + if( seek_read( infd, trailer, Lt_size, pos - Lt_size ) != Lt_size ) + { Li_set_errno_error( li, "Error reading member trailer: " ); break; } + member_size = Lt_get_member_size( trailer ); + if( member_size > (unsigned long long)pos || !Lt_verify_consistency( trailer ) ) + { + if( li->members <= 0 ) + { if( Li_skip_trailing_data( li, infd, &pos, ignore_trailing, + loose_trailing ) ) continue; else return false; } + Li_set_num_error( li, "Bad trailer at pos ", pos - Lt_size ); + break; + } + if( seek_read( infd, header, Lh_size, pos - member_size ) != Lh_size ) + { Li_set_errno_error( li, "Error reading member header: " ); break; } + dictionary_size = Lh_get_dictionary_size( header ); + if( !Lh_verify_magic( header ) || !Lh_verify_version( header ) || + !isvalid_ds( dictionary_size ) ) + { + if( li->members <= 0 ) + { if( Li_skip_trailing_data( li, infd, &pos, ignore_trailing, + loose_trailing ) ) continue; else return false; } + Li_set_num_error( li, "Bad header at pos ", pos - member_size ); + break; + } + pos -= member_size; + if( !push_back_member( li, 0, Lt_get_data_size( trailer ), pos, + member_size, dictionary_size ) ) + return false; + } + if( pos != 0 || li->members <= 0 ) + { + Li_free_member_vector( li ); + if( li->retval == 0 ) + { add_error( li, "Can't create file index." ); li->retval = 2; } + return false; + } + Li_reverse_member_vector( li ); + for( i = 0; ; ++i ) + { + const long long end = block_end( li->member_vector[i].dblock ); + if( end < 0 || end > INT64_MAX ) + { + Li_free_member_vector( li ); + add_error( li, "Data in input file is too long (2^63 bytes or more)." ); + li->retval = 2; return false; + } + if( i + 1 >= li->members ) break; + li->member_vector[i+1].dblock.pos = end; + } + return true; + } + + +void Li_free( struct Lzip_index * const li ) + { + Li_free_member_vector( li ); + if( li->error ) { free( li->error ); li->error = 0; } + li->error_size = 0; + } diff --git a/file_index.h b/lzip_index.h similarity index 65% rename from file_index.h rename to lzip_index.h index 49b6519..03be274 100644 --- a/file_index.h +++ b/lzip_index.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2018 Antonio Diaz Diaz. + Copyright (C) 2010-2019 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 @@ -46,45 +46,45 @@ static inline void init_member( struct Member * const m, { init_block( &m->dblock, dp, ds ); init_block( &m->mblock, mp, ms ); m->dictionary_size = dict_size; } -struct File_index +struct Lzip_index { struct Member * member_vector; char * error; - long long isize; + long long insize; long members; int error_size; int retval; }; -bool Fi_init( struct File_index * const fi, const int infd, +bool Li_init( struct Lzip_index * const li, const int infd, const bool ignore_trailing, const bool loose_trailing ); -void Fi_free( struct File_index * const fi ); +void Li_free( struct Lzip_index * const li ); -static inline long long Fi_udata_size( const struct File_index * const fi ) +static inline long long Li_udata_size( const struct Lzip_index * const li ) { - if( fi->members <= 0 ) return 0; - return block_end( fi->member_vector[fi->members-1].dblock ); + if( li->members <= 0 ) return 0; + return block_end( li->member_vector[li->members-1].dblock ); } -static inline long long Fi_cdata_size( const struct File_index * const fi ) +static inline long long Li_cdata_size( const struct Lzip_index * const li ) { - if( fi->members <= 0 ) return 0; - return block_end( fi->member_vector[fi->members-1].mblock ); + if( li->members <= 0 ) return 0; + return block_end( li->member_vector[li->members-1].mblock ); } /* total size including trailing data (if any) */ -static inline long long Fi_file_size( const struct File_index * const fi ) - { if( fi->isize >= 0 ) return fi->isize; else return 0; } +static inline long long Li_file_size( const struct Lzip_index * const li ) + { if( li->insize >= 0 ) return li->insize; else return 0; } -static inline const struct Block * Fi_dblock( const struct File_index * const fi, +static inline const struct Block * Li_dblock( const struct Lzip_index * const li, const long i ) - { return &fi->member_vector[i].dblock; } + { return &li->member_vector[i].dblock; } -static inline const struct Block * Fi_mblock( const struct File_index * const fi, +static inline const struct Block * Li_mblock( const struct Lzip_index * const li, const long i ) - { return &fi->member_vector[i].mblock; } + { return &li->member_vector[i].mblock; } -static inline unsigned Fi_dictionary_size( const struct File_index * const fi, +static inline unsigned Li_dictionary_size( const struct Lzip_index * const li, const long i ) - { return fi->member_vector[i].dictionary_size; } + { return li->member_vector[i].dictionary_size; } diff --git a/main.c b/main.c index 9a14137..2ff4a15 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2018 Antonio Diaz Diaz. + Copyright (C) 2010-2019 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 @@ -36,19 +36,24 @@ #include #include #include -#if defined(__MSVCRT__) +#if defined(__MSVCRT__) || defined(__OS2__) || defined(__DJGPP__) #include +#if defined(__MSVCRT__) #define fchmod(x,y) 0 #define fchown(x,y,z) 0 #define SIGHUP SIGTERM #define S_ISSOCK(x) 0 +#ifndef S_IRGRP #define S_IRGRP 0 #define S_IWGRP 0 #define S_IROTH 0 #define S_IWOTH 0 #endif -#if defined(__OS2__) -#include +#endif +#if defined(__DJGPP__) +#define S_ISSOCK(x) 0 +#define S_ISVTX 0 +#endif #endif #include "carg_parser.h" @@ -65,9 +70,8 @@ int verbosity = 0; -const char * const Program_name = "Lunzip"; const char * const program_name = "lunzip"; -const char * const program_year = "2018"; +const char * const program_year = "2019"; const char * invocation_name = 0; const struct { const char * from; const char * to; } known_extensions[] = { @@ -77,6 +81,8 @@ const struct { const char * from; const char * to; } known_extensions[] = { enum Mode { m_compress, m_decompress, m_list, m_test }; +/* Variables used in signal handler context. + They are not declared volatile because the handler never returns. */ char * output_filename = 0; int outfd = -1; bool delete_output_on_interrupt = false; @@ -260,7 +266,7 @@ static int get_dict_size( const char * const arg ) const long bits = strtol( arg, &tail, 0 ); if( bits >= min_dictionary_bits && bits <= max_dictionary_bits && *tail == 0 ) - return ( 1 << bits ); + return 1 << bits; return getnum( arg, min_dictionary_size, max_dictionary_size ); } @@ -368,8 +374,17 @@ static bool open_outstream( const bool force, const bool from_stdin ) } +static void set_signals( void (*action)(int) ) + { + signal( SIGHUP, action ); + signal( SIGINT, action ); + signal( SIGTERM, action ); + } + + void cleanup_and_fail( const int retval ) { + set_signals( SIG_IGN ); /* ignore signals */ if( delete_output_on_interrupt ) { delete_output_on_interrupt = false; @@ -384,6 +399,14 @@ void cleanup_and_fail( const int retval ) } +void signal_handler( int sig ) + { + if( sig ) {} /* keep compiler happy */ + show_error( "Control-C or similar caught, quitting.", 0, false ); + cleanup_and_fail( 1 ); + } + + /* Set permissions, owner and times. */ static void close_and_set_permissions( const struct stat * const in_statsp ) { @@ -455,9 +478,9 @@ static bool show_trailing_data( const uint8_t * const data, const int size, static int decompress( const unsigned long long cfile_size, const int infd, - struct Pretty_print * const pp, const unsigned buffer_size, - const bool ignore_trailing, const bool loose_trailing, - const bool testing ) + struct Pretty_print * const pp, const unsigned buffer_size, + const bool ignore_trailing, const bool loose_trailing, + const bool testing ) { unsigned long long partial_file_pos = 0; struct Range_decoder rdec; @@ -473,16 +496,16 @@ static int decompress( const unsigned long long cfile_size, const int infd, { int result, size; unsigned dictionary_size; - File_header header; + Lzip_header header; struct LZ_decoder decoder; Rd_reset_member_position( &rdec ); - size = Rd_read_data( &rdec, header, Fh_size ); + size = Rd_read_data( &rdec, header, Lh_size ); if( Rd_finished( &rdec ) ) /* End Of File */ { if( first_member ) { show_file_error( pp->name, "File ends unexpectedly at member header.", 0 ); retval = 2; } - else if( Fh_verify_prefix( header, size ) ) + else if( Lh_verify_prefix( header, size ) ) { Pp_show_msg( pp, "Truncated header in multimember file." ); show_trailing_data( header, size, pp, true, -1 ); retval = 2; } @@ -491,11 +514,11 @@ static int decompress( const unsigned long long cfile_size, const int infd, retval = 2; break; } - if( !Fh_verify_magic( header ) ) + if( !Lh_verify_magic( header ) ) { if( first_member ) { show_file_error( pp->name, bad_magic_msg, 0 ); retval = 2; } - else if( !loose_trailing && Fh_verify_corrupt( header ) ) + else if( !loose_trailing && Lh_verify_corrupt( header ) ) { Pp_show_msg( pp, corrupt_mm_msg ); show_trailing_data( header, size, pp, false, -1 ); retval = 2; } @@ -503,10 +526,10 @@ static int decompress( const unsigned long long cfile_size, const int infd, retval = 2; break; } - if( !Fh_verify_version( header ) ) - { Pp_show_msg( pp, bad_version( Fh_version( header ) ) ); + if( !Lh_verify_version( header ) ) + { Pp_show_msg( pp, bad_version( Lh_version( header ) ) ); retval = 2; break; } - dictionary_size = Fh_get_dictionary_size( header ); + dictionary_size = Lh_get_dictionary_size( header ); if( !isvalid_ds( dictionary_size ) ) { Pp_show_msg( pp, bad_dict_msg ); retval = 2; break; } @@ -528,7 +551,8 @@ static int decompress( const unsigned long long cfile_size, const int infd, { Pp_show_msg( pp, 0 ); fprintf( stderr, "%s at pos %llu\n", ( result == 2 ) ? - "File ends unexpectedly" : "Decoder error", partial_file_pos ); + "File ends unexpectedly" : "Decoder error", + partial_file_pos ); } retval = 2; break; } @@ -542,31 +566,13 @@ static int decompress( const unsigned long long cfile_size, const int infd, } -void signal_handler( int sig ) - { - if( sig ) {} /* keep compiler happy */ - show_error( "Control-C or similar caught, quitting.", 0, false ); - cleanup_and_fail( 1 ); - } - - -static void set_signals( void ) - { - signal( SIGHUP, signal_handler ); - signal( SIGINT, signal_handler ); - signal( SIGTERM, signal_handler ); - } - - void show_error( const char * const msg, const int errcode, const bool help ) { if( verbosity < 0 ) return; if( msg && msg[0] ) - { - fprintf( stderr, "%s: %s", program_name, msg ); - if( errcode > 0 ) fprintf( stderr, ": %s", strerror( errcode ) ); - fputc( '\n', stderr ); - } + fprintf( stderr, "%s: %s%s%s\n", program_name, msg, + ( errcode > 0 ) ? ": " : "", + ( errcode > 0 ) ? strerror( errcode ) : "" ); if( help ) fprintf( stderr, "Try '%s --help' for more information.\n", invocation_name ); @@ -576,10 +582,10 @@ void show_error( const char * const msg, const int errcode, const bool help ) void show_file_error( const char * const filename, const char * const msg, const int errcode ) { - if( verbosity < 0 ) return; - fprintf( stderr, "%s: %s: %s", program_name, filename, msg ); - if( errcode > 0 ) fprintf( stderr, ": %s", strerror( errcode ) ); - fputc( '\n', stderr ); + if( verbosity >= 0 ) + fprintf( stderr, "%s: %s: %s%s%s\n", program_name, filename, msg, + ( errcode > 0 ) ? ": " : "", + ( errcode > 0 ) ? strerror( errcode ) : "" ); } @@ -698,7 +704,7 @@ int main( const int argc, const char * const argv[] ) } } /* end process options */ -#if defined(__MSVCRT__) || defined(__OS2__) +#if defined(__MSVCRT__) || defined(__OS2__) || defined(__DJGPP__) setmode( STDIN_FILENO, O_BINARY ); setmode( STDOUT_FILENO, O_BINARY ); #endif @@ -737,7 +743,7 @@ int main( const int argc, const char * const argv[] ) if( !to_stdout && program_mode != m_test && ( filenames_given || default_output_filename[0] ) ) - set_signals(); + set_signals( signal_handler ); Pp_init( &pp, filenames, num_filenames ); @@ -825,6 +831,12 @@ int main( const int argc, const char * const argv[] ) ( in_statsp->st_size + 99 ) / 100 : 0; tmp = decompress( cfile_size, infd, &pp, buffer_size, ignore_trailing, loose_trailing, program_mode == m_test ); + if( close( infd ) != 0 ) + { + show_error( input_filename[0] ? "Error closing input file" : + "Error closing stdin", errno, false ); + if( tmp < 1 ) tmp = 1; + } if( tmp > retval ) retval = tmp; if( tmp ) { if( program_mode != m_test ) cleanup_and_fail( retval ); @@ -834,7 +846,6 @@ int main( const int argc, const char * const argv[] ) close_and_set_permissions( in_statsp ); if( input_filename[0] ) { - close( infd ); if( !keep_input_files && !to_stdout && program_mode != m_test ) remove( input_filename ); } diff --git a/testsuite/check.sh b/testsuite/check.sh index 728c7c6..cf7bb72 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # check script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2018 Antonio Diaz Diaz. +# Copyright (C) 2010-2019 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission # to copy, distribute and modify it. @@ -38,9 +38,10 @@ printf "testing lunzip-%s..." "$2" cat "${in_lz}" > uin.lz || framework_failure for i in bad_size -1 0 4095 513MiB 1G 1T 1P 1E 1Z 1Y 10KB ; do "${LZIP}" -dfkqu $i uin.lz - { [ $? = 1 ] && [ ! -e uin ] ; } || test_failed $LINENO $i + [ $? = 1 ] || test_failed $LINENO $i + [ ! -e uin ] || test_failed $LINENO $i done -rm -f uin.lz +rm -f uin.lz || framework_failure "${LZIP}" -lq in [ $? = 2 ] || test_failed $LINENO "${LZIP}" -tq in @@ -89,26 +90,28 @@ printf "\ntesting decompression..." "${LZIP}" -cd "${in_lz}" > copy || test_failed $LINENO cmp in copy || test_failed $LINENO -rm -f copy +rm -f copy || framework_failure cat "${in_lz}" > copy.lz || framework_failure "${LZIP}" -dk copy.lz || test_failed $LINENO cmp in copy || test_failed $LINENO printf "to be overwritten" > copy || framework_failure "${LZIP}" -d copy.lz 2> /dev/null [ $? = 1 ] || test_failed $LINENO -"${LZIP}" -df copy.lz -{ [ $? = 0 ] && [ ! -e copy.lz ] && cmp in copy ; } || test_failed $LINENO +"${LZIP}" -df copy.lz || test_failed $LINENO +[ ! -e copy.lz ] || test_failed $LINENO +cmp in copy || test_failed $LINENO printf "to be overwritten" > copy || framework_failure "${LZIP}" -df -o copy < "${in_lz}" || test_failed $LINENO cmp in copy || test_failed $LINENO -rm -f copy +rm -f copy || framework_failure cat "${in_lz}" > anyothername || framework_failure -"${LZIP}" -dv --output copy - anyothername - < "${in_lz}" 2> /dev/null -{ [ $? = 0 ] && cmp in copy && cmp in anyothername.out ; } || +"${LZIP}" -dv --output copy - anyothername - < "${in_lz}" 2> /dev/null || test_failed $LINENO -rm -f copy anyothername.out +cmp in copy || test_failed $LINENO +cmp in anyothername.out || test_failed $LINENO +rm -f copy anyothername.out || framework_failure "${LZIP}" -lq in "${in_lz}" [ $? = 2 ] || test_failed $LINENO @@ -119,10 +122,12 @@ rm -f copy anyothername.out "${LZIP}" -tq nx_file.lz "${in_lz}" [ $? = 1 ] || test_failed $LINENO "${LZIP}" -cdq in "${in_lz}" > copy -{ [ $? = 2 ] && cat copy in | cmp in - ; } || test_failed $LINENO +[ $? = 2 ] || test_failed $LINENO +cat copy in | cmp in - || test_failed $LINENO "${LZIP}" -cdq nx_file.lz "${in_lz}" > copy -{ [ $? = 1 ] && cmp in copy ; } || test_failed $LINENO -rm -f copy +[ $? = 1 ] || test_failed $LINENO +cmp in copy || test_failed $LINENO +rm -f copy || framework_failure cat "${in_lz}" > copy.lz || framework_failure for i in 1 2 3 4 5 6 7 ; do printf "g" >> copy.lz || framework_failure @@ -132,11 +137,15 @@ for i in 1 2 3 4 5 6 7 ; do [ $? = 2 ] || test_failed $LINENO $i done "${LZIP}" -dq in copy.lz -{ [ $? = 2 ] && [ -e copy.lz ] && [ ! -e copy ] && [ ! -e in.out ] ; } || - test_failed $LINENO +[ $? = 2 ] || test_failed $LINENO +[ -e copy.lz ] || test_failed $LINENO +[ ! -e copy ] || test_failed $LINENO +[ ! -e in.out ] || test_failed $LINENO "${LZIP}" -dq nx_file.lz copy.lz -{ [ $? = 1 ] && [ ! -e copy.lz ] && [ ! -e nx_file ] && cmp in copy ; } || - test_failed $LINENO +[ $? = 1 ] || test_failed $LINENO +[ ! -e copy.lz ] || test_failed $LINENO +[ ! -e nx_file ] || test_failed $LINENO +cmp in copy || test_failed $LINENO cat in in > in2 || framework_failure cat "${in_lz}" "${in_lz}" > in2.lz || framework_failure @@ -148,7 +157,7 @@ cmp in2 copy2 || test_failed $LINENO cat in2.lz > copy2.lz || framework_failure printf "\ngarbage" >> copy2.lz || framework_failure "${LZIP}" -tvvvv copy2.lz 2> /dev/null || test_failed $LINENO -rm -f copy2 +rm -f copy2 || framework_failure "${LZIP}" -alq copy2.lz [ $? = 2 ] || test_failed $LINENO "${LZIP}" -atq copy2.lz @@ -156,12 +165,15 @@ rm -f copy2 "${LZIP}" -atq < copy2.lz [ $? = 2 ] || test_failed $LINENO "${LZIP}" -adkq copy2.lz -{ [ $? = 2 ] && [ ! -e copy2 ] ; } || test_failed $LINENO +[ $? = 2 ] || test_failed $LINENO +[ ! -e copy2 ] || test_failed $LINENO "${LZIP}" -adkq -o copy2 < copy2.lz -{ [ $? = 2 ] && [ ! -e copy2 ] ; } || test_failed $LINENO +[ $? = 2 ] || test_failed $LINENO +[ ! -e copy2 ] || test_failed $LINENO printf "to be overwritten" > copy2 || framework_failure "${LZIP}" -df copy2.lz || test_failed $LINENO cmp in2 copy2 || test_failed $LINENO +rm -f in2 copy2 || framework_failure for i in 12 5120 6Ki 29 512KiB ; do printf "to be overwritten" > copy || framework_failure @@ -173,38 +185,58 @@ printf "\ntesting bad input..." headers='LZIp LZiP LZip LzIP LzIp LziP lZIP lZIp lZiP lzIP' body='\001\014\000\203\377\373\377\377\300\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000$\000\000\000\000\000\000\000' -cat "${in_lz}" > in0.lz -printf "LZIP${body}" >> in0.lz -if "${LZIP}" -tq in0.lz ; then +cat "${in_lz}" > int.lz +printf "LZIP${body}" >> int.lz +if "${LZIP}" -tq int.lz ; then for header in ${headers} ; do - printf "${header}${body}" > in0.lz # first member - "${LZIP}" -lq in0.lz + printf "${header}${body}" > int.lz # first member + "${LZIP}" -lq int.lz [ $? = 2 ] || test_failed $LINENO ${header} - "${LZIP}" -tq in0.lz + "${LZIP}" -tq int.lz [ $? = 2 ] || test_failed $LINENO ${header} - "${LZIP}" -lq --loose-trailing in0.lz + "${LZIP}" -tq < int.lz [ $? = 2 ] || test_failed $LINENO ${header} - "${LZIP}" -tq --loose-trailing in0.lz + "${LZIP}" -cdq int.lz > /dev/null [ $? = 2 ] || test_failed $LINENO ${header} - cat "${in_lz}" > in0.lz - printf "${header}${body}" >> in0.lz # trailing data - "${LZIP}" -lq in0.lz + "${LZIP}" -lq --loose-trailing int.lz [ $? = 2 ] || test_failed $LINENO ${header} - "${LZIP}" -tq in0.lz + "${LZIP}" -tq --loose-trailing int.lz [ $? = 2 ] || test_failed $LINENO ${header} - "${LZIP}" -lq --loose-trailing in0.lz - [ $? = 0 ] || test_failed $LINENO ${header} - "${LZIP}" -t --loose-trailing in0.lz - [ $? = 0 ] || test_failed $LINENO ${header} - "${LZIP}" -lq --loose-trailing --trailing-error in0.lz + "${LZIP}" -tq --loose-trailing < int.lz [ $? = 2 ] || test_failed $LINENO ${header} - "${LZIP}" -tq --loose-trailing --trailing-error in0.lz + "${LZIP}" -cdq --loose-trailing int.lz > /dev/null + [ $? = 2 ] || test_failed $LINENO ${header} + cat "${in_lz}" > int.lz + printf "${header}${body}" >> int.lz # trailing data + "${LZIP}" -lq int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq < int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -cdq int.lz > /dev/null + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -lq --loose-trailing int.lz || + test_failed $LINENO ${header} + "${LZIP}" -t --loose-trailing int.lz || + test_failed $LINENO ${header} + "${LZIP}" -t --loose-trailing < int.lz || + test_failed $LINENO ${header} + "${LZIP}" -cd --loose-trailing int.lz > /dev/null || + test_failed $LINENO ${header} + "${LZIP}" -lq --loose-trailing --trailing-error int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq --loose-trailing --trailing-error int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -tq --loose-trailing --trailing-error < int.lz + [ $? = 2 ] || test_failed $LINENO ${header} + "${LZIP}" -cdq --loose-trailing --trailing-error int.lz > /dev/null [ $? = 2 ] || test_failed $LINENO ${header} done else printf "\nwarning: skipping header test: 'printf' does not work on your system." fi -rm -f in0.lz +rm -f int.lz || framework_failure cat "${in_lz}" "${in_lz}" "${in_lz}" > in3.lz || framework_failure if dd if=in3.lz of=trunc.lz bs=14752 count=1 2> /dev/null && @@ -225,7 +257,7 @@ if dd if=in3.lz of=trunc.lz bs=14752 count=1 2> /dev/null && else printf "\nwarning: skipping truncation test: 'dd' does not work on your system." fi -rm -f in3.lz trunc.lz +rm -f in2.lz in3.lz trunc.lz out || framework_failure cat "${in_lz}" > ingin.lz || framework_failure printf "g" >> ingin.lz || framework_failure @@ -238,7 +270,7 @@ cmp in copy || test_failed $LINENO "${LZIP}" -t < ingin.lz || test_failed $LINENO "${LZIP}" -d < ingin.lz > copy || test_failed $LINENO cmp in copy || test_failed $LINENO -rm -f ingin.lz +rm -f copy ingin.lz || framework_failure echo if [ ${fail} = 0 ] ; then From 3e2c48fe405434402700e3459aecdf834a389b8e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 17 Feb 2025 22:29:09 +0100 Subject: [PATCH 05/10] Adding upstream version 1.12. Signed-off-by: Daniel Baumann --- AUTHORS | 8 +- ChangeLog | 43 +++--- INSTALL | 22 +-- Makefile.in | 9 +- NEWS | 32 +++-- README | 78 +++++----- carg_parser.c | 26 ++-- carg_parser.h | 66 ++++----- configure | 25 ++-- decoder.c | 38 +++-- decoder.h | 47 +++---- doc/lunzip.1 | 29 ++-- list.c | 55 +++----- lzip.h | 105 ++++---------- lzip_index.c | 113 ++++++++------- lzip_index.h | 25 ++-- main.c | 297 ++++++++++++++++++++++----------------- testsuite/check.sh | 104 +++++++++++--- testsuite/fox.lz | Bin 0 -> 80 bytes testsuite/fox_bcrc.lz | Bin 0 -> 80 bytes testsuite/fox_crc0.lz | Bin 0 -> 80 bytes testsuite/fox_das46.lz | Bin 0 -> 80 bytes testsuite/fox_de20.lz | Bin 0 -> 80 bytes testsuite/fox_mes81.lz | Bin 0 -> 80 bytes testsuite/fox_s11.lz | Bin 0 -> 80 bytes testsuite/fox_v2.lz | Bin 0 -> 80 bytes testsuite/test_em.txt.lz | Bin 0 -> 14024 bytes 27 files changed, 612 insertions(+), 510 deletions(-) create mode 100644 testsuite/fox.lz create mode 100644 testsuite/fox_bcrc.lz create mode 100644 testsuite/fox_crc0.lz create mode 100644 testsuite/fox_das46.lz create mode 100644 testsuite/fox_de20.lz create mode 100644 testsuite/fox_mes81.lz create mode 100644 testsuite/fox_s11.lz create mode 100644 testsuite/fox_v2.lz create mode 100644 testsuite/test_em.txt.lz diff --git a/AUTHORS b/AUTHORS index d3cc828..e39119d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,7 +1,7 @@ Lunzip was written by Antonio Diaz Diaz. The ideas embodied in lunzip are due to (at least) the following people: -Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for -the definition of Markov chains), G.N.N. Martin (for the definition of -range encoding), Igor Pavlov (for putting all the above together in -LZMA), and Julian Seward (for bzip2's CLI). +Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for the +definition of Markov chains), G.N.N. Martin (for the definition of range +encoding), Igor Pavlov (for putting all the above together in LZMA), and +Julian Seward (for bzip2's CLI). diff --git a/ChangeLog b/ChangeLog index ba6ef85..08e3c0a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,19 @@ +2021-01-01 Antonio Diaz Diaz + + * Version 1.12 released. + * main.c (main): Report an error if a file name is empty. + Make '-o' behave like '-c', but writing to file instead of stdout. + Do not open output if input is a terminal. + * Replace 'decompressed', 'compressed' with 'out', 'in' in output. + * lzip_index.c: Improve messages for corruption in last header. + * main.c: Set a valid invocation_name even if argc == 0. + * Document extraction from tar.lz in '--help' output and man page. + * testsuite: Add 9 new test files. + 2019-01-01 Antonio Diaz Diaz * Version 1.11 released. - * File_* renamed to Lzip_*. + * Rename File_* to Lzip_*. * lzip.h (Lzip_trailer): New function 'Lt_verify_consistency'. * lzip_index.c: Detect some kinds of corrupt trailers. * main.c (main): Check return value of close( infd ). @@ -12,10 +24,10 @@ 2018-02-05 Antonio Diaz Diaz * Version 1.10 released. - * main.c: Added new option '--loose-trailing'. - * Improved corrupt header detection to HD=3. + * main.c: New option '--loose-trailing'. + * Improve corrupt header detection to HD=3. * main.c: Show corrupt or truncated header in multimember file. - * Replaced 'bits/byte' with inverse compression ratio in output. + * Replace 'bits/byte' with inverse compression ratio in output. * Show progress of decompression at verbosity level 2 (-vv). * Show progress of decompression only if stderr is a terminal. * main.c: Show final diagnostic when testing multiple files. @@ -31,16 +43,16 @@ * main.c: Continue testing if any input file is a terminal. * main.c: Show trailing data in both hexadecimal and ASCII. * lzip_index.c: Improve detection of bad dict and trailing data. - * lzip.h: Unified messages for bad magic, trailing data, etc. + * lzip.h: Unify messages for bad magic, trailing data, etc. 2016-05-12 Antonio Diaz Diaz * Version 1.8 released. - * main.c: Added new option '-a, --trailing-error'. + * main.c: New option '-a, --trailing-error'. * main.c (main): With '-u', verify that output file is regular. * main.c (decompress): Print up to 6 bytes of trailing data when '-vvvv' is specified. - * decoder.c (LZd_verify_trailer): Removed test of final code. + * decoder.c (LZd_verify_trailer): Remove test of final code. * main.c (main): Delete '--output' file if infd is a terminal. * main.c (main): Don't use stdin more than once. * Error messages synced with lzip-1.18. @@ -52,17 +64,17 @@ * Version 1.7 released. * Minor changes. - * Makefile.in: Added new targets 'install*-compress'. + * Makefile.in: New targets 'install*-compress'. 2014-07-01 Antonio Diaz Diaz * Version 1.6 released. - * License changed to GPL version 2 or later. + * Change license to GPL version 2 or later. 2014-04-11 Antonio Diaz Diaz * Version 1.5 released. - * main.c: Added new option '-u, --buffer-size' (low memory mode). + * main.c: New option '-u, --buffer-size' (low memory mode). * main.c (close_and_set_permissions): Behave like 'cp -p'. 2013-09-17 Antonio Diaz Diaz @@ -83,8 +95,7 @@ * Version 1.2 released. * Decompression time has been reduced by 12%. - * Makefile.in: Added new target 'install-as-lzip'. - * Makefile.in: Added new target 'install-bin'. + * Makefile.in: New targets 'install-as-lzip' and 'install-bin'. * main.c: Use 'setmode' instead of '_setmode' on Windows and OS/2. 2012-02-26 Antonio Diaz Diaz @@ -94,8 +105,8 @@ multi-member file when only one '-v' is specified. * main.c (close_and_set_permissions): Inability to change output file attributes has been downgraded from error to warning. - * Changed quote characters in messages as advised by GNU Standards. - * configure: 'datadir' renamed to 'datarootdir'. + * Change quote characters in messages as advised by GNU Standards. + * configure: Rename 'datadir' to 'datarootdir'. 2011-01-17 Antonio Diaz Diaz @@ -104,8 +115,8 @@ * Created from the decompression code of clzip 1.1. -Copyright (C) 2010-2019 Antonio Diaz Diaz. +Copyright (C) 2010-2021 Antonio Diaz Diaz. This file is a collection of facts, and thus it is not copyrightable, -but just in case, you have unlimited permission to copy, distribute and +but just in case, you have unlimited permission to copy, distribute, and modify it. diff --git a/INSTALL b/INSTALL index 2652bad..313b7dc 100644 --- a/INSTALL +++ b/INSTALL @@ -1,7 +1,7 @@ Requirements ------------ -You will need a C compiler. -I use gcc 5.3.0 and 4.1.2, but the code should compile with any standards +You will need a C99 compiler. (gcc 3.3.6 or newer is recommended). +I use gcc 6.1.0 and 4.1.2, but the code should compile with any standards compliant compiler. Gcc is available at http://gcc.gnu.org. @@ -41,11 +41,11 @@ the main archive. documentation. Or type 'make install-compress', which additionally compresses the - man page after installation. (Installing compressed docs may become - the default in the future). + man page after installation. + (Installing compressed docs may become the default in the future). - You can install only the program or the man page by typing 'make - install-bin' or 'make install-man' respectively. + You can install only the program or the man page by typing + 'make install-bin' or 'make install-man' respectively. Instead of 'make install', you can type 'make install-as-lzip' to install the program and any data files and documentation, and link @@ -55,10 +55,10 @@ the main archive. Another way ----------- You can also compile lunzip into a separate directory. -To do this, you must use a version of 'make' that supports the 'VPATH' -variable, such as GNU 'make'. 'cd' to the directory where you want the +To do this, you must use a version of 'make' that supports the variable +'VPATH', such as GNU 'make'. 'cd' to the directory where you want the object files and executables to go and run the 'configure' script. -'configure' automatically checks for the source code in '.', in '..' and +'configure' automatically checks for the source code in '.', in '..', and in the directory that 'configure' is in. 'configure' recognizes the option '--srcdir=DIR' to control where to @@ -69,7 +69,7 @@ After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2010-2019 Antonio Diaz Diaz. +Copyright (C) 2010-2021 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, -distribute and modify it. +distribute, and modify it. diff --git a/Makefile.in b/Makefile.in index 691bd14..aff94d4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -71,7 +71,7 @@ install-info : -rm -f "$(DESTDIR)$(infodir)/$(pkgname).info"* $(INSTALL_DATA) $(VPATH)/doc/$(pkgname).info "$(DESTDIR)$(infodir)/$(pkgname).info" -if $(CAN_RUN_INSTALLINFO) ; then \ - install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(pkgname).info" ; \ + install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(pkgname).info" ; \ fi install-info-compress : install-info @@ -96,7 +96,7 @@ uninstall-bin : uninstall-info : -if $(CAN_RUN_INSTALLINFO) ; then \ - install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(pkgname).info" ; \ + install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(pkgname).info" ; \ fi -rm -f "$(DESTDIR)$(infodir)/$(pkgname).info"* @@ -119,7 +119,10 @@ dist : doc $(DISTNAME)/*.c \ $(DISTNAME)/testsuite/check.sh \ $(DISTNAME)/testsuite/test.txt \ - $(DISTNAME)/testsuite/test.txt.lz + $(DISTNAME)/testsuite/fox.lz \ + $(DISTNAME)/testsuite/fox_*.lz \ + $(DISTNAME)/testsuite/test.txt.lz \ + $(DISTNAME)/testsuite/test_em.txt.lz rm -f $(DISTNAME) lzip -v -9 $(DISTNAME).tar diff --git a/NEWS b/NEWS index d49ddba..98e59f3 100644 --- a/NEWS +++ b/NEWS @@ -1,14 +1,28 @@ -Changes in version 1.11: +Changes in version 1.12: -Detection of forbidden combinations of characters in trailing data has -been improved. +Lunzip now reports an error if a file name is empty (lunzip -t ""). -Errors are now also checked when closing the input file. +Option '-o, --output' now behaves like '-c, --stdout', but sending the +output unconditionally to a file instead of to standard output. See the new +description of '-o' in the manual. This change is backwards compatible only +when decompressing from standard input alone. Therefore commands like: + lunzip -d -o foo - bar.lz < foo.lz +must now be split into: + lunzip -d -o foo - < foo.lz + lunzip -d bar.lz +or rewritten as: + lunzip -d - bar.lz < foo.lz > foo -Lunzip now compiles on DOS with DJGPP. (Patch from Robert Riebisch). +Lunzip now does not even open the output file if the input file is a terminal. -The configure script now accepts appending options to CFLAGS using the -syntax 'CFLAGS+=OPTIONS'. +The words 'decompressed' and 'compressed' have been replaced with the +shorter 'out' and 'in' in the verbose output when decompressing or testing. -It has been documented in INSTALL the use of -CFLAGS+='-D __USE_MINGW_ANSI_STDIO' when compiling on MinGW. +Option '--list' now reports corruption or truncation of the last header in a +multimenber file specifically instead of showing the generic message "Last +member in input file is truncated or corrupt." + +The commands needed to extract files from a tar.lz archive have been +documented in the output of '--help' and in the man page. + +9 new test files have been added to the testsuite. diff --git a/README b/README index b86ff58..b09c908 100644 --- a/README +++ b/README @@ -1,53 +1,57 @@ Description -Lunzip is a decompressor for the lzip format. It is written in C and its -small size makes it well suited for embedded devices or software -installers that need to decompress files but don't need compression -capabilities. Lunzip is fully compatible with lzip-1.4 or newer. +Lunzip is a decompressor for the lzip format written in C. Its small size +makes it well suited for embedded devices or software installers that need +to decompress files but don't need compression capabilities. Lunzip is fully +compatible with lzip 1.4 or newer. The lzip file format is designed for data sharing and long-term archiving, taking into account both data integrity and decoder availability: * The lzip format provides very safe integrity checking and some data - recovery means. The lziprecover program can repair bit flip errors - (one of the most common forms of data corruption) in lzip files, - and provides data recovery capabilities, including error-checked - merging of damaged copies of a file. + recovery means. The program lziprecover can repair bit flip errors + (one of the most common forms of data corruption) in lzip files, and + provides data recovery capabilities, including error-checked merging + of damaged copies of a file. - * The lzip format is as simple as possible (but not simpler). The - lzip manual provides the source code of a simple decompressor - along with a detailed explanation of how it works, so that with - the only help of the lzip manual it would be possible for a - digital archaeologist to extract the data from a lzip file long - after quantum computers eventually render LZMA obsolete. + * The lzip format is as simple as possible (but not simpler). The lzip + manual provides the source code of a simple decompressor along with a + detailed explanation of how it works, so that with the only help of the + lzip manual it would be possible for a digital archaeologist to extract + the data from a lzip file long after quantum computers eventually + render LZMA obsolete. * Additionally the lzip reference implementation is copylefted, which guarantees that it will remain free forever. -A nice feature of the lzip format is that a corrupt byte is easier to -repair the nearer it is from the beginning of the file. Therefore, with -the help of lziprecover, losing an entire archive just because of a -corrupt byte near the beginning is a thing of the past. +A nice feature of the lzip format is that a corrupt byte is easier to repair +the nearer it is from the beginning of the file. Therefore, with the help of +lziprecover, losing an entire archive just because of a corrupt byte near +the beginning is a thing of the past. -Lunzip uses the same well-defined exit status values used by lzip, which +Lunzip uses the same well-defined exit status values used by bzip2, which makes it safer than decompressors returning ambiguous warning values (like gunzip) when it is used as a back end for other programs like tar or zutils. Lunzip provides a 'low memory' mode able to decompress any file using as little memory as 50 kB, irrespective of the dictionary size used to compress the file. To activate it, specify the size of the output buffer -with the '--buffer-size' option and lunzip will use the decompressed +with the option '--buffer-size' and lunzip will use the decompressed file as dictionary for distances beyond the buffer size. Of course, the -smaller the buffer size used in relation to the dictionary size, the +larger the difference between the buffer size and the dictionary size, the more accesses to disk are needed and the slower the decompression is. This 'low memory' mode only works when decompressing to a regular file and is intended for systems without enough memory (RAM + swap) to keep the whole dictionary at once. It has been tested on a laptop with a 486 processor and 4 MiB of RAM. -The amount of memory required by lunzip to decompress a file is about -46 kB larger than the dictionary size used to compress that file, unless -the '--buffer-size' option is specified. +The option '--buffer-size' may help to decompress a file erroneously created +with a dictionary size much larger than the uncompressed size. (Lzip adjusts +the dictionary size to the uncompressed size, but third-party tools may not). + +The amount of memory required by lunzip to decompress a file is about 46 kB +larger than the dictionary size used to compress that file, unless +'--buffer-size' is specified. Lunzip attempts to guess the name for the decompressed file from that of the compressed file as follows: @@ -62,12 +66,12 @@ possible, ownership of the file just as 'cp -p' does. (If the user ID or the group ID can't be duplicated, the file permission bits S_ISUID and S_ISGID are cleared). -Lunzip is able to read from some types of non regular files if the -'--stdout' option is specified. +Lunzip is able to read from some types of non-regular files if either the +option '-c' or the option '-o' is specified. -If no file names are specified, lunzip decompresses from standard input -to standard output. In this case, lunzip will decline to read compressed -input from a terminal. +If no file names are specified, lunzip decompresses from standard input to +standard output. In this case, lunzip will refuse to read compressed input +from a terminal, as this might leave the terminal in an abnormal state. Lunzip will correctly decompress a file which is the concatenation of two or more compressed files. The result is the concatenation of the corresponding @@ -75,16 +79,20 @@ decompressed files. Integrity testing of concatenated compressed files is also supported. The ideas embodied in lunzip are due to (at least) the following people: -Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for -the definition of Markov chains), G.N.N. Martin (for the definition of -range encoding), Igor Pavlov (for putting all the above together in -LZMA), and Julian Seward (for bzip2's CLI). +Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for the +definition of Markov chains), G.N.N. Martin (for the definition of range +encoding), Igor Pavlov (for putting all the above together in LZMA), and +Julian Seward (for bzip2's CLI). + +LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never have +been compressed. Decompressed is used to refer to data which have undergone +the process of decompression. -Copyright (C) 2010-2019 Antonio Diaz Diaz. +Copyright (C) 2010-2021 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, -distribute and modify it. +distribute, and modify it. The file Makefile.in is a data file used by configure to produce the Makefile. It has the same copyright owner and permissions that configure diff --git a/carg_parser.c b/carg_parser.c index ce01d7b..d0c05d5 100644 --- a/carg_parser.c +++ b/carg_parser.c @@ -1,20 +1,20 @@ -/* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2019 Antonio Diaz Diaz. +/* Arg_parser - POSIX/GNU command line argument parser. (C version) + Copyright (C) 2006-2021 Antonio Diaz Diaz. - This library is free software. Redistribution and use in source and - binary forms, with or without modification, are permitted provided - that the following conditions are met: + This library is free software. Redistribution and use in source and + binary forms, with or without modification, are permitted provided + that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. - This library 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. + This library 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. */ #include diff --git a/carg_parser.h b/carg_parser.h index dcae2de..c5f2352 100644 --- a/carg_parser.h +++ b/carg_parser.h @@ -1,44 +1,44 @@ -/* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2019 Antonio Diaz Diaz. +/* Arg_parser - POSIX/GNU command line argument parser. (C version) + Copyright (C) 2006-2021 Antonio Diaz Diaz. - This library is free software. Redistribution and use in source and - binary forms, with or without modification, are permitted provided - that the following conditions are met: + This library is free software. Redistribution and use in source and + binary forms, with or without modification, are permitted provided + that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. - This library 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. + This library 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. */ -/* Arg_parser reads the arguments in 'argv' and creates a number of - option codes, option arguments and non-option arguments. +/* Arg_parser reads the arguments in 'argv' and creates a number of + option codes, option arguments, and non-option arguments. - In case of error, 'ap_error' returns a non-null pointer to an error - message. + In case of error, 'ap_error' returns a non-null pointer to an error + message. - 'options' is an array of 'struct ap_Option' terminated by an element - containing a code which is zero. A null name means a short-only - option. A code value outside the unsigned char range means a - long-only option. + 'options' is an array of 'struct ap_Option' terminated by an element + containing a code which is zero. A null name means a short-only + option. A code value outside the unsigned char range means a + long-only option. - Arg_parser normally makes it appear as if all the option arguments - were specified before all the non-option arguments for the purposes - of parsing, even if the user of your program intermixed option and - non-option arguments. If you want the arguments in the exact order - the user typed them, call 'ap_init' with 'in_order' = true. + Arg_parser normally makes it appear as if all the option arguments + were specified before all the non-option arguments for the purposes + of parsing, even if the user of your program intermixed option and + non-option arguments. If you want the arguments in the exact order + the user typed them, call 'ap_init' with 'in_order' = true. - The argument '--' terminates all options; any following arguments are - treated as non-option arguments, even if they begin with a hyphen. + The argument '--' terminates all options; any following arguments are + treated as non-option arguments, even if they begin with a hyphen. - The syntax for optional option arguments is '-' - (without whitespace), or '--='. + The syntax for optional option arguments is '-' + (without whitespace), or '--='. */ #ifdef __cplusplus @@ -79,11 +79,11 @@ void ap_free( struct Arg_parser * const ap ); const char * ap_error( const struct Arg_parser * const ap ); - /* The number of arguments parsed (may be different from argc) */ +/* The number of arguments parsed. May be different from argc. */ int ap_arguments( const struct Arg_parser * const ap ); - /* If ap_code( i ) is 0, ap_argument( i ) is a non-option. - Else ap_argument( i ) is the option's argument (or empty). */ +/* If ap_code( i ) is 0, ap_argument( i ) is a non-option. + Else ap_argument( i ) is the option's argument (or empty). */ int ap_code( const struct Arg_parser * const ap, const int i ); const char * ap_argument( const struct Arg_parser * const ap, const int i ); diff --git a/configure b/configure index 1098e99..54fb2fb 100755 --- a/configure +++ b/configure @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2019 Antonio Diaz Diaz. +# Copyright (C) 2010-2021 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission -# to copy, distribute and modify it. +# to copy, distribute, and modify it. pkgname=lunzip -pkgversion=1.11 +pkgversion=1.12 progname=lunzip srctrigger=doc/${progname}.1 @@ -26,11 +26,7 @@ CFLAGS='-Wall -W -O2' LDFLAGS= # checking whether we are using GNU C. -/bin/sh -c "${CC} --version" > /dev/null 2>&1 || - { - CC=cc - CFLAGS=-O2 - } +/bin/sh -c "${CC} --version" > /dev/null 2>&1 || { CC=cc ; CFLAGS=-O2 ; } # Loop over all args args= @@ -42,11 +38,12 @@ while [ $# != 0 ] ; do shift # Add the argument quoted to args - args="${args} \"${option}\"" + if [ -z "${args}" ] ; then args="\"${option}\"" + else args="${args} \"${option}\"" ; fi # Split out the argument for options that take them case ${option} in - *=*) optarg=`echo ${option} | sed -e 's,^[^=]*=,,;s,/$,,'` ;; + *=*) optarg=`echo "${option}" | sed -e 's,^[^=]*=,,;s,/$,,'` ;; esac # Process the options @@ -125,7 +122,7 @@ if [ -z "${srcdir}" ] ; then if [ ! -r "${srcdir}/${srctrigger}" ] ; then srcdir=.. ; fi if [ ! -r "${srcdir}/${srctrigger}" ] ; then ## the sed command below emulates the dirname command - srcdir=`echo $0 | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + srcdir=`echo "$0" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` fi fi @@ -148,7 +145,7 @@ if [ -z "${no_create}" ] ; then # Run this file to recreate the current configuration. # # This script is free software: you have unlimited permission -# to copy, distribute and modify it. +# to copy, distribute, and modify it. exec /bin/sh $0 ${args} --no-create EOF @@ -170,11 +167,11 @@ echo "LDFLAGS = ${LDFLAGS}" rm -f Makefile cat > Makefile << EOF # Makefile for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2019 Antonio Diaz Diaz. +# Copyright (C) 2010-2021 Antonio Diaz Diaz. # This file was generated automatically by configure. Don't edit. # # This Makefile is free software: you have unlimited permission -# to copy, distribute and modify it. +# to copy, distribute, and modify it. pkgname = ${pkgname} pkgversion = ${pkgversion} diff --git a/decoder.c b/decoder.c index 3095015..63d30ea 100644 --- a/decoder.c +++ b/decoder.c @@ -1,18 +1,18 @@ -/* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2019 Antonio Diaz Diaz. +/* Lunzip - Decompressor for the lzip format + Copyright (C) 2010-2021 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #define _FILE_OFFSET_BITS 64 @@ -104,9 +104,8 @@ void LZd_flush_data( struct LZ_decoder * const d ) writeblock( d->outfd, d->buffer + d->stream_pos, size ) != size ) { show_error( "Write error", errno, false ); cleanup_and_fail( 1 ); } if( d->pos >= d->buffer_size ) - { d->partial_data_pos += d->pos; d->pos = 0; d->pos_wrapped = true; - if( d->partial_data_pos >= d->dictionary_size ) - d->pos_wrapped_dic = true; } + { d->partial_data_pos += d->pos; d->pos = 0; + if( d->partial_data_pos >= d->dictionary_size ) d->pos_wrapped = true; } d->stream_pos = d->pos; } } @@ -173,16 +172,15 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, { if( verbosity >= 4 ) show_header( d->dictionary_size ); if( data_size == 0 || member_size == 0 ) - fputs( "no data compressed. ", stderr ); + fputs( "no data compressed. ", stderr ); else - fprintf( stderr, "%6.3f:1, %5.2f%% ratio, %5.2f%% saved. ", + fprintf( stderr, "%6.3f:1, %5.2f%% ratio, %5.2f%% saved. ", (double)data_size / member_size, ( 100.0 * member_size ) / data_size, 100.0 - ( ( 100.0 * member_size ) / data_size ) ); if( verbosity >= 4 ) fprintf( stderr, "CRC %08X, ", td_crc ); if( verbosity >= 3 ) - fprintf( stderr, "decompressed %9llu, compressed %8llu. ", - data_size, member_size ); + fprintf( stderr, "%9llu out, %8llu in. ", data_size, member_size ); } return true; } @@ -320,7 +318,7 @@ int LZd_decode_member( struct LZ_decoder * const d, rep3 = rep2; rep2 = rep1; rep1 = rep0; rep0 = distance; state = St_set_match( state ); if( rep0 >= d->dictionary_size || - ( rep0 >= LZd_data_position( d ) && !d->pos_wrapped_dic ) ) + ( !d->pos_wrapped && rep0 >= LZd_data_position( d ) ) ) { LZd_flush_data( d ); return 1; } } copy_block( d, rep0, len ); diff --git a/decoder.h b/decoder.h index ba4adb0..0fe0110 100644 --- a/decoder.h +++ b/decoder.h @@ -1,18 +1,18 @@ -/* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2019 Antonio Diaz Diaz. +/* Lunzip - Decompressor for the lzip format + Copyright (C) 2010-2021 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ enum { rd_buffer_size = 16384 }; @@ -107,7 +107,7 @@ static inline unsigned Rd_decode( struct Range_decoder * const rdec, /* symbol <<= 1; */ /* if( rdec->code >= rdec->range ) { rdec->code -= rdec->range; symbol |= 1; } */ bit = ( rdec->code >= rdec->range ); - symbol = ( symbol << 1 ) + bit; + symbol <<= 1; symbol += bit; rdec->code -= rdec->range & ( 0U - bit ); } return symbol; @@ -137,8 +137,7 @@ static inline unsigned Rd_decode_bit( struct Range_decoder * const rdec, static inline unsigned Rd_decode_tree3( struct Range_decoder * const rdec, Bit_model bm[] ) { - unsigned symbol = 1; - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); + unsigned symbol = 2 | Rd_decode_bit( rdec, &bm[1] ); symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); return symbol & 7; @@ -147,8 +146,7 @@ static inline unsigned Rd_decode_tree3( struct Range_decoder * const rdec, static inline unsigned Rd_decode_tree6( struct Range_decoder * const rdec, Bit_model bm[] ) { - unsigned symbol = 1; - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); + unsigned symbol = 2 | Rd_decode_bit( rdec, &bm[1] ); symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); @@ -177,7 +175,7 @@ Rd_decode_tree_reversed( struct Range_decoder * const rdec, for( i = 0; i < num_bits; ++i ) { const unsigned bit = Rd_decode_bit( rdec, &bm[model] ); - model = ( model << 1 ) + bit; + model <<= 1; model += bit; symbol |= ( bit << i ); } return symbol; @@ -187,12 +185,9 @@ static inline unsigned Rd_decode_tree_reversed4( struct Range_decoder * const rdec, Bit_model bm[] ) { unsigned symbol = Rd_decode_bit( rdec, &bm[1] ); - unsigned model = 2 + symbol; - unsigned bit = Rd_decode_bit( rdec, &bm[model] ); - model = ( model << 1 ) + bit; symbol |= ( bit << 1 ); - bit = Rd_decode_bit( rdec, &bm[model] ); - model = ( model << 1 ) + bit; symbol |= ( bit << 2 ); - symbol |= ( Rd_decode_bit( rdec, &bm[model] ) << 3 ); + symbol += Rd_decode_bit( rdec, &bm[2+symbol] ) << 1; + symbol += Rd_decode_bit( rdec, &bm[4+symbol] ) << 2; + symbol += Rd_decode_bit( rdec, &bm[8+symbol] ) << 3; return symbol; } @@ -205,7 +200,7 @@ static inline unsigned Rd_decode_matched( struct Range_decoder * const rdec, { const unsigned match_bit = ( match_byte <<= 1 ) & mask; const unsigned bit = Rd_decode_bit( rdec, &bm[symbol+match_bit+mask] ); - symbol = ( symbol << 1 ) + bit; + symbol <<= 1; symbol += bit; if( symbol > 0xFF ) return symbol & 0xFF; mask &= ~(match_bit ^ (bit << 8)); /* if( match_bit != bit ) mask = 0; */ } @@ -235,7 +230,6 @@ struct LZ_decoder uint32_t crc; int outfd; /* output file descriptor */ bool pos_wrapped; - bool pos_wrapped_dic; }; void LZd_flush_data( struct LZ_decoder * const d ); @@ -333,7 +327,6 @@ static inline bool LZd_init( struct LZ_decoder * const d, d->crc = 0xFFFFFFFFU; d->outfd = ofd; d->pos_wrapped = false; - d->pos_wrapped_dic = false; /* prev_byte of first byte; also for LZd_peek( 0 ) on corrupt file */ d->buffer[d->buffer_size-1] = 0; return true; diff --git a/doc/lunzip.1 b/doc/lunzip.1 index 1198723..f96b431 100644 --- a/doc/lunzip.1 +++ b/doc/lunzip.1 @@ -1,22 +1,22 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. -.TH LUNZIP "1" "January 2019" "lunzip 1.11" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16. +.TH LUNZIP "1" "January 2021" "lunzip 1.12" "User Commands" .SH NAME lunzip \- decompressor for the lzip format .SH SYNOPSIS .B lunzip [\fI\,options\/\fR] [\fI\,files\/\fR] .SH DESCRIPTION -Lunzip is a decompressor for the lzip format. It is written in C and its -small size makes it well suited for embedded devices or software -installers that need to decompress files but don't need compression -capabilities. Lunzip is fully compatible with lzip\-1.4 or newer. +Lunzip is a decompressor for the lzip format written in C. Its small size +makes it well suited for embedded devices or software installers that need +to decompress files but don't need compression capabilities. Lunzip is fully +compatible with lzip 1.4 or newer. .PP Lunzip provides a 'low memory' mode able to decompress any file using as little memory as 50 kB, irrespective of the dictionary size used to compress the file. To activate it, specify the size of the output buffer -with the '\-\-buffer\-size' option and lunzip will use the decompressed +with the option \fB\-\-buffer\-size\fR and lunzip will use the decompressed file as dictionary for distances beyond the buffer size. Of course, the -smaller the buffer size used in relation to the dictionary size, the +larger the difference between the buffer size and the dictionary size, the more accesses to disk are needed and the slower the decompression is. This 'low memory' mode only works when decompressing to a regular file and is intended for systems without enough memory (RAM + swap) to keep @@ -48,7 +48,7 @@ keep (don't delete) input files print (un)compressed file sizes .TP \fB\-o\fR, \fB\-\-output=\fR -if reading standard input, write to +write to , keep input files .TP \fB\-q\fR, \fB\-\-quiet\fR suppress all messages @@ -72,16 +72,25 @@ Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc... Buffer sizes 12 to 29 are interpreted as powers of two, meaning 2^12 to 2^29 bytes. .PP +To extract all the files from archive 'foo.tar.lz', use the commands +\&'tar \fB\-xf\fR foo.tar.lz' or 'lunzip \fB\-cd\fR foo.tar.lz | tar \fB\-xf\fR \-'. +.PP Exit status: 0 for a normal exit, 1 for environmental problems (file not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or invalid input file, 3 for an internal consistency error (eg, bug) which caused lunzip to panic. +.PP +The ideas embodied in lunzip are due to (at least) the following people: +Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for the +definition of Markov chains), G.N.N. Martin (for the definition of range +encoding), Igor Pavlov (for putting all the above together in LZMA), and +Julian Seward (for bzip2's CLI). .SH "REPORTING BUGS" Report bugs to lzip\-bug@nongnu.org .br Lunzip home page: http://www.nongnu.org/lzip/lunzip.html .SH COPYRIGHT -Copyright \(co 2019 Antonio Diaz Diaz. +Copyright \(co 2021 Antonio Diaz Diaz. License GPLv2+: GNU GPL version 2 or later .br This is free software: you are free to change and redistribute it. diff --git a/list.c b/list.c index 97adb50..8ab8c44 100644 --- a/list.c +++ b/list.c @@ -1,26 +1,26 @@ -/* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2019 Antonio Diaz Diaz. +/* Lunzip - Decompressor for the lzip format + Copyright (C) 2010-2021 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #define _FILE_OFFSET_BITS 64 #include +#include #include #include -#include #include #include @@ -33,11 +33,11 @@ static void list_line( const unsigned long long uncomp_size, const char * const input_filename ) { if( uncomp_size > 0 ) - printf( "%15llu %15llu %6.2f%% %s\n", uncomp_size, comp_size, + printf( "%14llu %14llu %6.2f%% %s\n", uncomp_size, comp_size, 100.0 - ( ( 100.0 * comp_size ) / uncomp_size ), input_filename ); else - printf( "%15llu %15llu -INF%% %s\n", uncomp_size, comp_size, + printf( "%14llu %14llu -INF%% %s\n", uncomp_size, comp_size, input_filename ); } @@ -60,15 +60,15 @@ int list_files( const char * const filenames[], const int num_filenames, if( from_stdin ) { if( stdin_used ) continue; else stdin_used = true; } input_filename = from_stdin ? "(stdin)" : filenames[i]; infd = from_stdin ? STDIN_FILENO : - open_instream( input_filename, &in_stats, true, true ); - if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } + open_instream( input_filename, &in_stats, false, true ); + if( infd < 0 ) { set_retval( &retval, 1 ); continue; } Li_init( &lzip_index, infd, ignore_trailing, loose_trailing ); close( infd ); if( lzip_index.retval != 0 ) { show_file_error( input_filename, lzip_index.error, 0 ); - if( retval < lzip_index.retval ) retval = lzip_index.retval; + set_retval( &retval, lzip_index.retval ); Li_free( &lzip_index ); continue; } if( verbosity >= 0 ) @@ -80,31 +80,22 @@ int list_files( const char * const filenames[], const int num_filenames, { first_post = false; if( verbosity >= 1 ) fputs( " dict memb trail ", stdout ); - fputs( " uncompressed compressed saved name\n", stdout ); + fputs( " uncompressed compressed saved name\n", stdout ); } if( verbosity >= 1 ) - { - long long trailing_size; - unsigned dictionary_size = 0; - long i; - for( i = 0; i < lzip_index.members; ++i ) - dictionary_size = - max( dictionary_size, Li_dictionary_size( &lzip_index, i ) ); - trailing_size = Li_file_size( &lzip_index ) - cdata_size; - printf( "%s %5ld %6lld ", format_ds( dictionary_size ), - lzip_index.members, trailing_size ); - } + printf( "%s %5ld %6lld ", format_ds( lzip_index.dictionary_size ), + lzip_index.members, Li_file_size( &lzip_index ) - cdata_size ); list_line( udata_size, cdata_size, input_filename ); if( verbosity >= 2 && lzip_index.members > 1 ) { long i; - fputs( " member data_pos data_size member_pos member_size\n", stdout ); + fputs( " member data_pos data_size member_pos member_size\n", stdout ); for( i = 0; i < lzip_index.members; ++i ) { const struct Block * db = Li_dblock( &lzip_index, i ); const struct Block * mb = Li_mblock( &lzip_index, i ); - printf( "%5ld %15llu %15llu %15llu %15llu\n", + printf( "%6ld %14llu %14llu %14llu %14llu\n", i + 1, db->pos, db->size, mb->pos, mb->size ); } first_post = true; /* reprint heading after list of members */ diff --git a/lzip.h b/lzip.h index 23db964..3961c33 100644 --- a/lzip.h +++ b/lzip.h @@ -1,18 +1,18 @@ -/* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2019 Antonio Diaz Diaz. +/* Lunzip - Decompressor for the lzip format + Copyright (C) 2010-2021 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #ifndef max @@ -22,8 +22,6 @@ #define min(x,y) ((x) <= (y) ? (x) : (y)) #endif -void * resize_buffer( void * buf, const unsigned min_size ); - typedef int State; enum { states = 12 }; @@ -82,7 +80,7 @@ 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 ) ); } + { return prev_byte >> ( 8 - literal_context_bits ); } enum { bit_model_move_bits = 5, @@ -116,65 +114,6 @@ static inline void Lm_init( struct Len_model * const lm ) } -/* defined in main.c */ -extern int verbosity; - -struct Pretty_print /* requires global var 'int verbosity' */ - { - const char * name; - char * padded_name; - const char * stdin_name; - unsigned longest_name; - bool first_post; - }; - -static inline void Pp_init( struct Pretty_print * const pp, - const char * const filenames[], - const int num_filenames ) - { - unsigned stdin_name_len; - int i; - pp->name = 0; - pp->padded_name = 0; - pp->stdin_name = "(stdin)"; - pp->longest_name = 0; - pp->first_post = false; - - if( verbosity <= 0 ) return; - stdin_name_len = strlen( pp->stdin_name ); - for( i = 0; i < num_filenames; ++i ) - { - const char * const s = filenames[i]; - const unsigned len = (strcmp( s, "-" ) == 0) ? stdin_name_len : strlen( s ); - if( pp->longest_name < len ) pp->longest_name = len; - } - if( pp->longest_name == 0 ) pp->longest_name = stdin_name_len; - } - -static inline void Pp_set_name( struct Pretty_print * const pp, - const char * const filename ) - { - unsigned name_len, padded_name_len, i = 0; - - if( filename && filename[0] && strcmp( filename, "-" ) != 0 ) - pp->name = filename; - else pp->name = pp->stdin_name; - name_len = strlen( pp->name ); - padded_name_len = max( name_len, pp->longest_name ) + 4; - pp->padded_name = resize_buffer( pp->padded_name, padded_name_len + 1 ); - while( i < 2 ) pp->padded_name[i++] = ' '; - while( i < name_len + 2 ) { pp->padded_name[i] = pp->name[i-2]; ++i; } - pp->padded_name[i++] = ':'; - while( i < padded_name_len ) pp->padded_name[i++] = ' '; - pp->padded_name[i] = 0; - pp->first_post = true; - } - -static inline void Pp_reset( struct Pretty_print * const pp ) - { if( pp->name && pp->name[0] ) pp->first_post = true; } -void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ); - - typedef uint32_t CRC32[256]; /* Table of CRCs of all 8-bit messages. */ extern CRC32 crc32; @@ -213,7 +152,7 @@ static const uint8_t lzip_magic[4] = { 0x4C, 0x5A, 0x49, 0x50 }; /* "LZIP" */ typedef uint8_t Lzip_header[6]; /* 0-3 magic bytes */ /* 4 version */ - /* 5 coded_dict_size */ + /* 5 coded dictionary size */ enum { Lh_size = 6 }; static inline bool Lh_verify_magic( const Lzip_header data ) @@ -250,6 +189,12 @@ static inline unsigned Lh_get_dictionary_size( const Lzip_header data ) return sz; } +static inline bool Lh_verify( const Lzip_header data ) + { + return Lh_verify_magic( data ) && Lh_verify_version( data ) && + isvalid_ds( Lh_get_dictionary_size( data ) ); + } + typedef uint8_t Lzip_trailer[20]; /* 0-3 CRC32 of the uncompressed data */ @@ -294,10 +239,14 @@ static inline bool Lt_verify_consistency( const Lzip_trailer data ) } +static inline void set_retval( int * retval, const int new_val ) + { if( *retval < new_val ) *retval = new_val; } + static const char * const bad_magic_msg = "Bad magic number (file not in lzip format)."; static const char * const bad_dict_msg = "Invalid dictionary size in member header."; static const char * const corrupt_mm_msg = "Corrupt header in multimember file."; static const char * const trailing_msg = "Trailing data not allowed."; +static const char * const mem_msg = "Not enough memory."; /* defined in decoder.c */ int readblock( const int fd, uint8_t * const buf, const int size ); @@ -308,11 +257,15 @@ int list_files( const char * const filenames[], const int num_filenames, /* defined in main.c */ struct stat; +struct Pretty_print; +extern int verbosity; +void * resize_buffer( void * buf, const unsigned min_size ); +void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ); const char * bad_version( const unsigned version ); const char * format_ds( const unsigned dictionary_size ); void show_header( const unsigned dictionary_size ); int open_instream( const char * const name, struct stat * const in_statsp, - const bool no_ofile, const bool reg_only ); + const bool one_to_one, const bool reg_only ); void cleanup_and_fail( const int retval ); void show_error( const char * const msg, const int errcode, const bool help ); void show_file_error( const char * const filename, const char * const msg, diff --git a/lzip_index.c b/lzip_index.c index 44c7b17..ca4df8d 100644 --- a/lzip_index.c +++ b/lzip_index.c @@ -1,28 +1,28 @@ -/* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2019 Antonio Diaz Diaz. +/* Lunzip - Decompressor for the lzip format + Copyright (C) 2010-2021 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #define _FILE_OFFSET_BITS 64 #include #include -#include -#include #include +#include #include +#include #include #include "lzip.h" @@ -58,8 +58,7 @@ static bool push_back_member( struct Lzip_index * const li, struct Member * p; void * tmp = resize_buffer( li->member_vector, ( li->members + 1 ) * sizeof li->member_vector[0] ); - if( !tmp ) - { add_error( li, "Not enough memory." ); li->retval = 1; return false; } + if( !tmp ) { add_error( li, mem_msg ); li->retval = 1; return false; } li->member_vector = (struct Member *)tmp; p = &(li->member_vector[li->members]); init_member( p, dp, ds, mp, ms, dict_size ); @@ -89,6 +88,19 @@ static void Li_reverse_member_vector( struct Lzip_index * const li ) } +static bool Li_check_header_error( struct Lzip_index * const li, + const Lzip_header header ) + { + if( !Lh_verify_magic( header ) ) + { add_error( li, bad_magic_msg ); li->retval = 2; return true; } + if( !Lh_verify_version( header ) ) + { add_error( li, bad_version( Lh_version( header ) ) ); li->retval = 2; + return true; } + if( !isvalid_ds( Lh_get_dictionary_size( header ) ) ) + { add_error( li, bad_dict_msg ); li->retval = 2; return true; } + return false; + } + static void Li_set_errno_error( struct Lzip_index * const li, const char * const msg ) { @@ -106,9 +118,18 @@ static void Li_set_num_error( struct Lzip_index * const li, } +static bool Li_read_header( struct Lzip_index * const li, const int fd, + Lzip_header header, const long long pos ) + { + if( seek_read( fd, header, Lh_size, pos ) != Lh_size ) + { Li_set_errno_error( li, "Error reading member header: " ); return false; } + return true; + } + + /* If successful, push last member and set pos to member header. */ -static bool Li_skip_trailing_data( struct Lzip_index * const li, - const int fd, long long * const pos, +static bool Li_skip_trailing_data( struct Lzip_index * const li, const int fd, + unsigned long long * const pos, const bool ignore_trailing, const bool loose_trailing ) { @@ -135,36 +156,40 @@ static bool Li_skip_trailing_data( struct Lzip_index * const li, if( buffer[i-1] <= max_msb ) /* most significant byte of member_size */ { Lzip_header header; + const Lzip_header * header2; const Lzip_trailer * const trailer = (const Lzip_trailer *)( buffer + i - Lt_size ); const unsigned long long member_size = Lt_get_member_size( *trailer ); unsigned dictionary_size; + bool full_h2; if( member_size == 0 ) /* skip trailing zeros */ { while( i > Lt_size && buffer[i-9] == 0 ) --i; continue; } if( member_size > ipos + i || !Lt_verify_consistency( *trailer ) ) continue; - if( seek_read( fd, header, Lh_size, - ipos + i - member_size ) != Lh_size ) - { Li_set_errno_error( li, "Error reading member header: " ); - return false; } - dictionary_size = Lh_get_dictionary_size( header ); - if( !Lh_verify_magic( header ) || !Lh_verify_version( header ) || - !isvalid_ds( dictionary_size ) ) continue; - if( Lh_verify_prefix( buffer + i, bsize - i ) ) + if( !Li_read_header( li, fd, header, ipos + i - member_size ) ) + return false; + if( !Lh_verify( header ) ) continue; + header2 = (const Lzip_header *)( buffer + i ); + full_h2 = bsize - i >= Lh_size; + if( Lh_verify_prefix( *header2, bsize - i ) ) /* last member */ { - add_error( li, "Last member in input file is truncated or corrupt." ); + if( !full_h2 ) add_error( li, "Last member in input file is truncated." ); + else if( !Li_check_header_error( li, *header2 ) ) + add_error( li, "Last member in input file is truncated or corrupt." ); li->retval = 2; return false; } - if( !loose_trailing && bsize - i >= Lh_size && - Lh_verify_corrupt( buffer + i ) ) + if( !loose_trailing && full_h2 && Lh_verify_corrupt( *header2 ) ) { add_error( li, corrupt_mm_msg ); li->retval = 2; return false; } if( !ignore_trailing ) { add_error( li, trailing_msg ); li->retval = 2; return false; } *pos = ipos + i - member_size; + dictionary_size = Lh_get_dictionary_size( header ); + if( li->dictionary_size < dictionary_size ) + li->dictionary_size = dictionary_size; return push_back_member( li, 0, Lt_get_data_size( *trailer ), *pos, member_size, dictionary_size ); } - if( ipos <= 0 ) + if( ipos == 0 ) { Li_set_num_error( li, "Bad trailer at pos ", *pos - Lt_size ); return false; } bsize = buffer_size; @@ -180,7 +205,7 @@ bool Li_init( struct Lzip_index * const li, const int infd, const bool ignore_trailing, const bool loose_trailing ) { Lzip_header header; - long long pos; + unsigned long long pos; long i; li->member_vector = 0; li->error = 0; @@ -188,6 +213,7 @@ bool Li_init( struct Lzip_index * const li, const int infd, li->members = 0; li->error_size = 0; li->retval = 0; + li->dictionary_size = 0; if( li->insize < 0 ) { Li_set_errno_error( li, "Input file is not seekable: " ); return false; } if( li->insize < min_member_size ) @@ -197,15 +223,8 @@ bool Li_init( struct Lzip_index * const li, const int infd, { add_error( li, "Input file is too long (2^63 bytes or more)." ); li->retval = 2; return false; } - if( seek_read( infd, header, Lh_size, 0 ) != Lh_size ) - { Li_set_errno_error( li, "Error reading member header: " ); return false; } - if( !Lh_verify_magic( header ) ) - { add_error( li, bad_magic_msg ); li->retval = 2; return false; } - if( !Lh_verify_version( header ) ) - { add_error( li, bad_version( Lh_version( header ) ) ); li->retval = 2; - return false; } - if( !isvalid_ds( Lh_get_dictionary_size( header ) ) ) - { add_error( li, bad_dict_msg ); li->retval = 2; return false; } + if( !Li_read_header( li, infd, header, 0 ) ) return false; + if( Li_check_header_error( li, header ) ) return false; pos = li->insize; /* always points to a header or to EOF */ while( pos >= min_member_size ) @@ -216,19 +235,16 @@ bool Li_init( struct Lzip_index * const li, const int infd, if( seek_read( infd, trailer, Lt_size, pos - Lt_size ) != Lt_size ) { Li_set_errno_error( li, "Error reading member trailer: " ); break; } member_size = Lt_get_member_size( trailer ); - if( member_size > (unsigned long long)pos || !Lt_verify_consistency( trailer ) ) - { + if( member_size > pos || !Lt_verify_consistency( trailer ) ) + { /* bad trailer */ if( li->members <= 0 ) { if( Li_skip_trailing_data( li, infd, &pos, ignore_trailing, loose_trailing ) ) continue; else return false; } Li_set_num_error( li, "Bad trailer at pos ", pos - Lt_size ); break; } - if( seek_read( infd, header, Lh_size, pos - member_size ) != Lh_size ) - { Li_set_errno_error( li, "Error reading member header: " ); break; } - dictionary_size = Lh_get_dictionary_size( header ); - if( !Lh_verify_magic( header ) || !Lh_verify_version( header ) || - !isvalid_ds( dictionary_size ) ) + if( !Li_read_header( li, infd, header, pos - member_size ) ) break; + if( !Lh_verify( header ) ) /* bad header */ { if( li->members <= 0 ) { if( Li_skip_trailing_data( li, infd, &pos, ignore_trailing, @@ -237,6 +253,9 @@ bool Li_init( struct Lzip_index * const li, const int infd, break; } pos -= member_size; + dictionary_size = Lh_get_dictionary_size( header ); + if( li->dictionary_size < dictionary_size ) + li->dictionary_size = dictionary_size; if( !push_back_member( li, 0, Lt_get_data_size( trailer ), pos, member_size, dictionary_size ) ) return false; diff --git a/lzip_index.h b/lzip_index.h index 03be274..4e9cd44 100644 --- a/lzip_index.h +++ b/lzip_index.h @@ -1,18 +1,18 @@ -/* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2019 Antonio Diaz Diaz. +/* Lunzip - Decompressor for the lzip format + Copyright (C) 2010-2021 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #ifndef INT64_MAX @@ -54,6 +54,7 @@ struct Lzip_index long members; int error_size; int retval; + unsigned dictionary_size; /* largest dictionary size in the file */ }; bool Li_init( struct Lzip_index * const li, const int infd, diff --git a/main.c b/main.c index 2ff4a15..b5e458e 100644 --- a/main.c +++ b/main.c @@ -1,24 +1,24 @@ -/* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2019 Antonio Diaz Diaz. +/* Lunzip - Decompressor for the lzip format + Copyright (C) 2010-2021 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ /* - Exit status: 0 for a normal exit, 1 for environmental problems - (file not found, invalid flags, I/O errors, etc), 2 to indicate a - corrupt or invalid input file, 3 for an internal consistency error - (eg, bug) which caused lunzip to panic. + Exit status: 0 for a normal exit, 1 for environmental problems + (file not found, invalid flags, I/O errors, etc), 2 to indicate a + corrupt or invalid input file, 3 for an internal consistency error + (eg, bug) which caused lunzip to panic. */ #define _FILE_OFFSET_BITS 64 @@ -70,11 +70,11 @@ int verbosity = 0; -const char * const program_name = "lunzip"; -const char * const program_year = "2019"; -const char * invocation_name = 0; +static const char * const program_name = "lunzip"; +static const char * const program_year = "2021"; +static const char * invocation_name = "lunzip"; /* default value */ -const struct { const char * from; const char * to; } known_extensions[] = { +static const struct { const char * from; const char * to; } known_extensions[] = { { ".lz", "" }, { ".tlz", ".tar" }, { 0, 0 } }; @@ -83,23 +83,23 @@ enum Mode { m_compress, m_decompress, m_list, m_test }; /* Variables used in signal handler context. They are not declared volatile because the handler never returns. */ -char * output_filename = 0; -int outfd = -1; -bool delete_output_on_interrupt = false; +static char * output_filename = 0; +static int outfd = -1; +static bool delete_output_on_interrupt = false; static void show_help( void ) { - printf( "Lunzip is a decompressor for the lzip format. It is written in C and its\n" - "small size makes it well suited for embedded devices or software\n" - "installers that need to decompress files but don't need compression\n" - "capabilities. Lunzip is fully compatible with lzip-1.4 or newer.\n" + printf( "Lunzip is a decompressor for the lzip format written in C. Its small size\n" + "makes it well suited for embedded devices or software installers that need\n" + "to decompress files but don't need compression capabilities. Lunzip is fully\n" + "compatible with lzip 1.4 or newer.\n" "\nLunzip provides a 'low memory' mode able to decompress any file using as\n" "little memory as 50 kB, irrespective of the dictionary size used to\n" "compress the file. To activate it, specify the size of the output buffer\n" - "with the '--buffer-size' option and lunzip will use the decompressed\n" + "with the option --buffer-size and lunzip will use the decompressed\n" "file as dictionary for distances beyond the buffer size. Of course, the\n" - "smaller the buffer size used in relation to the dictionary size, the\n" + "larger the difference between the buffer size and the dictionary size, the\n" "more accesses to disk are needed and the slower the decompression is.\n" "This 'low memory' mode only works when decompressing to a regular file\n" "and is intended for systems without enough memory (RAM + swap) to keep\n" @@ -114,22 +114,29 @@ static void show_help( void ) " -f, --force overwrite existing output files\n" " -k, --keep keep (don't delete) input files\n" " -l, --list print (un)compressed file sizes\n" - " -o, --output= if reading standard input, write to \n" + " -o, --output= write to , keep input files\n" " -q, --quiet suppress all messages\n" " -t, --test test compressed file integrity\n" " -u, --buffer-size= set output buffer size in bytes\n" " -v, --verbose be verbose (a 2nd -v gives more)\n" " --loose-trailing allow trailing data seeming corrupt header\n" - "If no file names are given, or if a file is '-', lunzip decompresses\n" + "\nIf no file names are given, or if a file is '-', lunzip decompresses\n" "from standard input to standard output.\n" "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" "Buffer sizes 12 to 29 are interpreted as powers of two, meaning 2^12\n" "to 2^29 bytes.\n" + "\nTo extract all the files from archive 'foo.tar.lz', use the commands\n" + "'tar -xf foo.tar.lz' or 'lunzip -cd foo.tar.lz | tar -xf -'.\n" "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n" "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n" "invalid input file, 3 for an internal consistency error (eg, bug) which\n" "caused lunzip to panic.\n" + "\nThe ideas embodied in lunzip are due to (at least) the following people:\n" + "Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for the\n" + "definition of Markov chains), G.N.N. Martin (for the definition of range\n" + "encoding), Igor Pavlov (for putting all the above together in LZMA), and\n" + "Julian Seward (for bzip2's CLI).\n" "\nReport bugs to lzip-bug@nongnu.org\n" "Lunzip home page: http://www.nongnu.org/lzip/lunzip.html\n" ); } @@ -150,15 +157,64 @@ void * resize_buffer( void * buf, const unsigned min_size ) { if( buf ) buf = realloc( buf, min_size ); else buf = malloc( min_size ); - if( !buf ) - { - show_error( "Not enough memory.", 0, false ); - cleanup_and_fail( 1 ); - } + if( !buf ) { show_error( mem_msg, 0, false ); cleanup_and_fail( 1 ); } return buf; } +struct Pretty_print + { + const char * name; + char * padded_name; + const char * stdin_name; + unsigned longest_name; + bool first_post; + }; + +static void Pp_init( struct Pretty_print * const pp, + const char * const filenames[], const int num_filenames ) + { + unsigned stdin_name_len; + int i; + pp->name = 0; + pp->padded_name = 0; + pp->stdin_name = "(stdin)"; + pp->longest_name = 0; + pp->first_post = false; + + if( verbosity <= 0 ) return; + stdin_name_len = strlen( pp->stdin_name ); + for( i = 0; i < num_filenames; ++i ) + { + const char * const s = filenames[i]; + const unsigned len = (strcmp( s, "-" ) == 0) ? stdin_name_len : strlen( s ); + if( pp->longest_name < len ) pp->longest_name = len; + } + if( pp->longest_name == 0 ) pp->longest_name = stdin_name_len; + } + +static void Pp_set_name( struct Pretty_print * const pp, + const char * const filename ) + { + unsigned name_len, padded_name_len, i = 0; + + if( filename && filename[0] && strcmp( filename, "-" ) != 0 ) + pp->name = filename; + else pp->name = pp->stdin_name; + name_len = strlen( pp->name ); + padded_name_len = max( name_len, pp->longest_name ) + 4; + pp->padded_name = resize_buffer( pp->padded_name, padded_name_len + 1 ); + while( i < 2 ) pp->padded_name[i++] = ' '; + while( i < name_len + 2 ) { pp->padded_name[i] = pp->name[i-2]; ++i; } + pp->padded_name[i++] = ':'; + while( i < padded_name_len ) pp->padded_name[i++] = ' '; + pp->padded_name[i] = 0; + pp->first_post = true; + } + +static void Pp_reset( struct Pretty_print * const pp ) + { if( pp->name && pp->name[0] ) pp->first_post = true; } + void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ) { if( verbosity >= 0 ) @@ -204,7 +260,7 @@ const char * format_ds( const unsigned dictionary_size ) void show_header( const unsigned dictionary_size ) { - fprintf( stderr, "dictionary %s, ", format_ds( dictionary_size ) ); + fprintf( stderr, "dict %s, ", format_ds( dictionary_size ) ); } @@ -271,7 +327,7 @@ static int get_dict_size( const char * const arg ) } -void set_mode( enum Mode * const program_modep, const enum Mode new_mode ) +static void set_mode( enum Mode * const program_modep, const enum Mode new_mode ) { if( *program_modep != m_compress && *program_modep != new_mode ) { @@ -324,7 +380,7 @@ static void set_d_outname( const char * const name, const int eindex ) int open_instream( const char * const name, struct stat * const in_statsp, - const bool no_ofile, const bool reg_only ) + const bool one_to_one, const bool reg_only ) { int infd = open( name, O_RDONLY | O_BINARY ); if( infd < 0 ) @@ -336,13 +392,12 @@ int open_instream( const char * const name, struct stat * const in_statsp, const bool can_read = ( i == 0 && !reg_only && ( S_ISBLK( mode ) || S_ISCHR( mode ) || S_ISFIFO( mode ) || S_ISSOCK( mode ) ) ); - if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || !no_ofile ) ) ) + if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || one_to_one ) ) ) { if( verbosity >= 0 ) fprintf( stderr, "%s: Input file '%s' is not a regular file%s.\n", - program_name, name, - ( can_read && !no_ofile ) ? - ",\n and '--stdout' was not specified" : "" ); + program_name, name, ( can_read && one_to_one ) ? + ",\n and neither '-c' nor '-o' were specified" : "" ); close( infd ); infd = -1; } @@ -351,11 +406,11 @@ int open_instream( const char * const name, struct stat * const in_statsp, } -static bool open_outstream( const bool force, const bool from_stdin ) +static bool open_outstream( const bool force, const bool protect ) { const mode_t usr_rw = S_IRUSR | S_IWUSR; const mode_t all_rw = usr_rw | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; - const mode_t outfd_mode = from_stdin ? all_rw : usr_rw; + const mode_t outfd_mode = protect ? usr_rw : all_rw; int flags = O_APPEND | O_CREAT | O_RDWR | O_BINARY; if( force ) flags |= O_TRUNC; else flags |= O_EXCL; @@ -399,7 +454,7 @@ void cleanup_and_fail( const int retval ) } -void signal_handler( int sig ) +static void signal_handler( int sig ) { if( sig ) {} /* keep compiler happy */ show_error( "Control-C or similar caught, quitting.", 0, false ); @@ -407,7 +462,20 @@ void signal_handler( int sig ) } - /* Set permissions, owner and times. */ +static bool check_tty_in( const char * const input_filename, const int infd, + const enum Mode program_mode, int * const retval ) + { + if( isatty( infd ) ) /* for example /dev/tty */ + { show_file_error( input_filename, + "I won't read compressed data from a terminal.", 0 ); + close( infd ); set_retval( retval, 1 ); + if( program_mode != m_test ) cleanup_and_fail( *retval ); + return false; } + return true; + } + + +/* Set permissions, owner, and times. */ static void close_and_set_permissions( const struct stat * const in_statsp ) { bool warning = false; @@ -487,10 +555,7 @@ static int decompress( const unsigned long long cfile_size, const int infd, int retval = 0; bool first_member; if( !Rd_init( &rdec, infd ) ) - { - show_error( "Not enough memory.", 0, false ); - cleanup_and_fail( 1 ); - } + { show_error( mem_msg, 0, false ); cleanup_and_fail( 1 ); } for( first_member = true; ; first_member = false ) { @@ -589,7 +654,7 @@ void show_file_error( const char * const filename, const char * const msg, } -void internal_error( const char * const msg ) +static void internal_error( const char * const msg ) { if( verbosity >= 0 ) fprintf( stderr, "%s: internal error: %s\n", program_name, msg ); @@ -631,7 +696,9 @@ void show_dprogress( const unsigned long long cfile_size, int main( const int argc, const char * const argv[] ) { const char * default_output_filename = ""; - const char ** filenames = 0; + static struct Arg_parser parser; /* static because valgrind complains */ + static struct Pretty_print pp; /* and memory management in C sucks */ + static const char ** filenames = 0; int num_filenames = 0; unsigned buffer_size = max_dictionary_size; enum Mode program_mode = m_compress; @@ -646,7 +713,6 @@ int main( const int argc, const char * const argv[] ) bool loose_trailing = false; bool stdin_used = false; bool to_stdout = false; - struct Pretty_print pp; enum { opt_lt = 256 }; const struct ap_Option options[] = @@ -668,13 +734,11 @@ int main( const int argc, const char * const argv[] ) { opt_lt, "loose-trailing", ap_no }, { 0 , 0, ap_no } }; - struct Arg_parser parser; - - invocation_name = argv[0]; + if( argc > 0 ) invocation_name = argv[0]; CRC32_init(); if( !ap_init( &parser, argc, argv, options, 0 ) ) - { show_error( "Not enough memory.", 0, false ); return 1; } + { show_error( mem_msg, 0, false ); return 1; } if( ap_error( &parser ) ) /* bad option */ { show_error( ap_error( &parser ), 0, true ); return 1; } @@ -693,7 +757,8 @@ int main( const int argc, const char * const argv[] ) case 'k': keep_input_files = true; break; case 'l': set_mode( &program_mode, m_list ); break; case 'n': break; - case 'o': default_output_filename = arg; break; + case 'o': if( strcmp( arg, "-" ) == 0 ) to_stdout = true; + else { default_output_filename = arg; } break; case 'q': verbosity = -1; break; case 't': set_mode( &program_mode, m_test ); break; case 'u': buffer_size = get_dict_size( arg ); break; @@ -722,17 +787,17 @@ int main( const int argc, const char * const argv[] ) if( program_mode == m_list ) return list_files( filenames, num_filenames, ignore_trailing, loose_trailing ); - if( program_mode == m_test ) - outfd = -1; - else if( program_mode == m_compress ) - program_mode = m_decompress; /* default mode */ + if( program_mode == m_compress ) + program_mode = m_decompress; /* default mode */ + if( program_mode == m_test ) to_stdout = false; /* apply overrides */ + if( program_mode == m_test || to_stdout ) default_output_filename = ""; if( buffer_size < max_dictionary_size ) { bool from_stdin = false; if( to_stdout || program_mode == m_test ) - { show_error( "'--buffer-size' is incompatible with '--stdout' and '--test'.", 0, false ); - return 1; } + { show_error( "'--buffer-size' is incompatible with '--stdout' and '--test'.", + 0, false ); return 1; } for( i = 0; i < num_filenames; ++i ) if( !filenames[i][0] || strcmp( filenames[i], "-" ) == 0 ) { from_stdin = true; break; } @@ -741,13 +806,19 @@ int main( const int argc, const char * const argv[] ) " with a reduced buffer size.", 0, false ); return 1; } } - if( !to_stdout && program_mode != m_test && - ( filenames_given || default_output_filename[0] ) ) + output_filename = resize_buffer( output_filename, 1 ); + output_filename[0] = 0; + if( to_stdout && program_mode != m_test ) outfd = STDOUT_FILENO; + else outfd = -1; + + const bool to_file = !to_stdout && program_mode != m_test && + default_output_filename[0]; + if( !to_stdout && program_mode != m_test && ( filenames_given || to_file ) ) set_signals( signal_handler ); Pp_init( &pp, filenames, num_filenames ); - output_filename = resize_buffer( output_filename, 1 ); + const bool one_to_one = !to_stdout && program_mode != m_test && !to_file; for( i = 0; i < num_filenames; ++i ) { unsigned long long cfile_size; @@ -756,60 +827,35 @@ int main( const int argc, const char * const argv[] ) int tmp; struct stat in_stats; const struct stat * in_statsp; - output_filename[0] = 0; - if( !filenames[i][0] || strcmp( filenames[i], "-" ) == 0 ) + Pp_set_name( &pp, filenames[i] ); + if( strcmp( filenames[i], "-" ) == 0 ) { if( stdin_used ) continue; else stdin_used = true; infd = STDIN_FILENO; - if( program_mode != m_test ) - { - if( to_stdout || !default_output_filename[0] ) - outfd = STDOUT_FILENO; - else - { - output_filename = resize_buffer( output_filename, - strlen( default_output_filename ) + 1 ); - strcpy( output_filename, default_output_filename ); - if( !open_outstream( force, true ) ) - { - if( retval < 1 ) retval = 1; - close( infd ); - continue; - } - } - } + if( !check_tty_in( pp.name, infd, program_mode, &retval ) ) continue; + if( one_to_one ) { outfd = STDOUT_FILENO; output_filename[0] = 0; } } else { input_filename = filenames[i]; - infd = open_instream( input_filename, &in_stats, - to_stdout || program_mode == m_test, false ); - if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } - if( program_mode != m_test ) + infd = open_instream( input_filename, &in_stats, one_to_one, false ); + if( infd < 0 ) { set_retval( &retval, 1 ); continue; } + if( !check_tty_in( pp.name, infd, program_mode, &retval ) ) continue; + if( one_to_one ) /* open outfd after verifying infd */ { - if( to_stdout ) outfd = STDOUT_FILENO; - else - { - set_d_outname( input_filename, extension_index( input_filename ) ); - if( !open_outstream( force, false ) ) - { - if( retval < 1 ) retval = 1; - close( infd ); - continue; - } - } + set_d_outname( input_filename, extension_index( input_filename ) ); + if( !open_outstream( force, true ) ) + { close( infd ); set_retval( &retval, 1 ); continue; } } } - Pp_set_name( &pp, input_filename ); - if( isatty( infd ) ) + if( to_file && outfd < 0 ) /* open outfd after verifying infd */ { - show_file_error( pp.name, - "I won't read compressed data from a terminal.", 0 ); - if( retval < 1 ) retval = 1; - if( program_mode == m_test ) { close( infd ); continue; } - cleanup_and_fail( retval ); + output_filename = resize_buffer( output_filename, + strlen( default_output_filename ) + 1 ); + strcpy( output_filename, default_output_filename ); + if( !open_outstream( force, false ) ) return 1; } if( delete_output_on_interrupt && buffer_size < max_dictionary_size ) @@ -821,39 +867,34 @@ int main( const int argc, const char * const argv[] ) fprintf( stderr, "%s: Output file '%s' is not a regular file,\n" " and 'low memory' mode has been requested.\n", program_name, output_filename ); - if( retval < 1 ) retval = 1; - cleanup_and_fail( retval ); + set_retval( &retval, 1 ); + return retval; /* don't try to delete a non-regular file */ } } - in_statsp = input_filename[0] ? &in_stats : 0; - cfile_size = ( in_statsp && S_ISREG( in_statsp->st_mode ) ) ? - ( in_statsp->st_size + 99 ) / 100 : 0; + in_statsp = ( input_filename[0] && one_to_one ) ? &in_stats : 0; + cfile_size = ( input_filename[0] && S_ISREG( in_stats.st_mode ) ) ? + ( in_stats.st_size + 99 ) / 100 : 0; tmp = decompress( cfile_size, infd, &pp, buffer_size, ignore_trailing, loose_trailing, program_mode == m_test ); if( close( infd ) != 0 ) - { - show_error( input_filename[0] ? "Error closing input file" : - "Error closing stdin", errno, false ); - if( tmp < 1 ) tmp = 1; - } - if( tmp > retval ) retval = tmp; + { show_file_error( pp.name, "Error closing input file", errno ); + set_retval( &tmp, 1 ); } + set_retval( &retval, tmp ); if( tmp ) { if( program_mode != m_test ) cleanup_and_fail( retval ); else ++failed_tests; } - if( delete_output_on_interrupt ) + if( delete_output_on_interrupt && one_to_one ) close_and_set_permissions( in_statsp ); - if( input_filename[0] ) - { - if( !keep_input_files && !to_stdout && program_mode != m_test ) - remove( input_filename ); - } + if( input_filename[0] && !keep_input_files && one_to_one ) + remove( input_filename ); } - if( outfd >= 0 && close( outfd ) != 0 ) + if( delete_output_on_interrupt ) close_and_set_permissions( 0 ); /* -o */ + else if( outfd >= 0 && close( outfd ) != 0 ) /* -c */ { show_error( "Error closing stdout", errno, false ); - if( retval < 1 ) retval = 1; + set_retval( &retval, 1 ); } if( failed_tests > 0 && verbosity >= 1 && num_filenames > 1 ) fprintf( stderr, "%s: warning: %d %s failed the test.\n", diff --git a/testsuite/check.sh b/testsuite/check.sh index cf7bb72..19928dd 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,9 +1,9 @@ #! /bin/sh # check script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2019 Antonio Diaz Diaz. +# Copyright (C) 2010-2021 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission -# to copy, distribute and modify it. +# to copy, distribute, and modify it. LC_ALL=C export LC_ALL @@ -30,6 +30,8 @@ cd "${objdir}"/tmp || framework_failure cat "${testdir}"/test.txt > in || framework_failure in_lz="${testdir}"/test.txt.lz +in_em="${testdir}"/test_em.txt.lz +fox_lz="${testdir}"/fox.lz fail=0 test_failed() { fail=1 ; printf " $1" ; [ -z "$2" ] || printf "($2)" ; } @@ -37,7 +39,7 @@ printf "testing lunzip-%s..." "$2" cat "${in_lz}" > uin.lz || framework_failure for i in bad_size -1 0 4095 513MiB 1G 1T 1P 1E 1Z 1Y 10KB ; do - "${LZIP}" -dfkqu $i uin.lz + "${LZIP}" -dfkq -u $i uin.lz [ $? = 1 ] || test_failed $LINENO $i [ ! -e uin ] || test_failed $LINENO $i done @@ -54,6 +56,11 @@ rm -f uin.lz || framework_failure [ $? = 2 ] || test_failed $LINENO "${LZIP}" -dq -o in < "${in_lz}" [ $? = 1 ] || test_failed $LINENO +"${LZIP}" -dq -o in "${in_lz}" +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -dq -o out nx_file.lz +[ $? = 1 ] || test_failed $LINENO +[ ! -e out ] || test_failed $LINENO # these are for code coverage "${LZIP}" -lt "${in_lz}" 2> /dev/null [ $? = 1 ] || test_failed $LINENO @@ -61,7 +68,9 @@ rm -f uin.lz || framework_failure [ $? = 1 ] || test_failed $LINENO "${LZIP}" -cdt "${in_lz}" > out 2> /dev/null [ $? = 1 ] || test_failed $LINENO -"${LZIP}" -t -- nx_file 2> /dev/null +"${LZIP}" -t -- nx_file.lz 2> /dev/null +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -t "" < /dev/null 2> /dev/null [ $? = 1 ] || test_failed $LINENO "${LZIP}" --help > /dev/null || test_failed $LINENO "${LZIP}" -n1 -V > /dev/null || test_failed $LINENO @@ -85,12 +94,26 @@ printf "LZIP\001+.............................." | "${LZIP}" -t 2> /dev/null printf "\ntesting decompression..." -"${LZIP}" -lq "${in_lz}" || test_failed $LINENO -"${LZIP}" -t "${in_lz}" || test_failed $LINENO -"${LZIP}" -cd "${in_lz}" > copy || test_failed $LINENO -cmp in copy || test_failed $LINENO +for i in "${in_lz}" "${in_em}" ; do + "${LZIP}" -lq "$i" || test_failed $LINENO "$i" + "${LZIP}" -t "$i" || test_failed $LINENO "$i" + "${LZIP}" -d "$i" -o copy || test_failed $LINENO "$i" + cmp in copy || test_failed $LINENO "$i" + "${LZIP}" -cd "$i" > copy || test_failed $LINENO "$i" + cmp in copy || test_failed $LINENO "$i" + "${LZIP}" -d "$i" -o - > copy || test_failed $LINENO "$i" + cmp in copy || test_failed $LINENO "$i" + "${LZIP}" -d < "$i" > copy || test_failed $LINENO "$i" + cmp in copy || test_failed $LINENO "$i" + rm -f copy || framework_failure +done + +lines=$("${LZIP}" -tvv "${in_em}" 2>&1 | wc -l) || test_failed $LINENO +[ "${lines}" -eq 8 ] || test_failed $LINENO "${lines}" + +lines=$("${LZIP}" -lvv "${in_em}" | wc -l) || test_failed $LINENO +[ "${lines}" -eq 11 ] || test_failed $LINENO "${lines}" -rm -f copy || framework_failure cat "${in_lz}" > copy.lz || framework_failure "${LZIP}" -dk copy.lz || test_failed $LINENO cmp in copy || test_failed $LINENO @@ -102,12 +125,20 @@ printf "to be overwritten" > copy || framework_failure cmp in copy || test_failed $LINENO printf "to be overwritten" > copy || framework_failure +"${LZIP}" -d -o copy < "${in_lz}" 2> /dev/null +[ $? = 1 ] || test_failed $LINENO "${LZIP}" -df -o copy < "${in_lz}" || test_failed $LINENO cmp in copy || test_failed $LINENO +rm -f out copy || framework_failure +"${LZIP}" -d -o ./- "${in_lz}" || test_failed $LINENO +cmp in ./- || test_failed $LINENO +rm -f ./- || framework_failure +"${LZIP}" -d -o ./- < "${in_lz}" || test_failed $LINENO +cmp in ./- || test_failed $LINENO +rm -f ./- || framework_failure -rm -f copy || framework_failure cat "${in_lz}" > anyothername || framework_failure -"${LZIP}" -dv --output copy - anyothername - < "${in_lz}" 2> /dev/null || +"${LZIP}" -dv - anyothername - < "${in_lz}" > copy 2> /dev/null || test_failed $LINENO cmp in copy || test_failed $LINENO cmp in anyothername.out || test_failed $LINENO @@ -148,16 +179,19 @@ done cmp in copy || test_failed $LINENO cat in in > in2 || framework_failure -cat "${in_lz}" "${in_lz}" > in2.lz || framework_failure -"${LZIP}" -lq in2.lz || test_failed $LINENO -"${LZIP}" -t in2.lz || test_failed $LINENO -"${LZIP}" -cd in2.lz > copy2 || test_failed $LINENO +"${LZIP}" -lq "${in_lz}" "${in_lz}" || test_failed $LINENO +"${LZIP}" -t "${in_lz}" "${in_lz}" || test_failed $LINENO +"${LZIP}" -cd "${in_lz}" "${in_lz}" -o out > copy2 || test_failed $LINENO +[ ! -e out ] || test_failed $LINENO # override -o cmp in2 copy2 || test_failed $LINENO +rm -f copy2 || framework_failure +"${LZIP}" -d "${in_lz}" "${in_lz}" -o copy2 || test_failed $LINENO +cmp in2 copy2 || test_failed $LINENO +rm -f copy2 || framework_failure -cat in2.lz > copy2.lz || framework_failure +cat "${in_lz}" "${in_lz}" > copy2.lz || framework_failure printf "\ngarbage" >> copy2.lz || framework_failure "${LZIP}" -tvvvv copy2.lz 2> /dev/null || test_failed $LINENO -rm -f copy2 || framework_failure "${LZIP}" -alq copy2.lz [ $? = 2 ] || test_failed $LINENO "${LZIP}" -atq copy2.lz @@ -173,13 +207,20 @@ rm -f copy2 || framework_failure printf "to be overwritten" > copy2 || framework_failure "${LZIP}" -df copy2.lz || test_failed $LINENO cmp in2 copy2 || test_failed $LINENO -rm -f in2 copy2 || framework_failure for i in 12 5120 6Ki 29 512KiB ; do printf "to be overwritten" > copy || framework_failure "${LZIP}" -df -u$i -o copy < "${in_lz}" || test_failed $LINENO $i cmp in copy || test_failed $LINENO $i + rm -f copy || framework_failure + "${LZIP}" -d -u$i -o copy "${in_lz}" || test_failed $LINENO $i + cmp in copy || test_failed $LINENO $i + rm -f copy2 || framework_failure + "${LZIP}" -d -u$i -o copy2 "${in_lz}" "${in_lz}" || + test_failed $LINENO $i + cmp in2 copy2 || test_failed $LINENO $i done +rm -f in2 copy copy2 || framework_failure printf "\ntesting bad input..." @@ -238,6 +279,21 @@ else fi rm -f int.lz || framework_failure +for i in fox_v2.lz fox_s11.lz fox_de20.lz \ + fox_bcrc.lz fox_crc0.lz fox_das46.lz fox_mes81.lz ; do + "${LZIP}" -tq "${testdir}"/$i + [ $? = 2 ] || test_failed $LINENO $i +done + +"${LZIP}" -cd "${fox_lz}" > fox || test_failed $LINENO +for i in fox_bcrc.lz fox_crc0.lz fox_das46.lz fox_mes81.lz ; do + "${LZIP}" -cdq "${testdir}"/$i > out + [ $? = 2 ] || test_failed $LINENO $i + cmp fox out || test_failed $LINENO $i +done +rm -f fox out || framework_failure + +cat "${in_lz}" "${in_lz}" > in2.lz || framework_failure cat "${in_lz}" "${in_lz}" "${in_lz}" > in3.lz || framework_failure if dd if=in3.lz of=trunc.lz bs=14752 count=1 2> /dev/null && [ -e trunc.lz ] && cmp in2.lz trunc.lz > /dev/null 2>&1 ; then @@ -264,13 +320,21 @@ printf "g" >> ingin.lz || framework_failure cat "${in_lz}" >> ingin.lz || framework_failure "${LZIP}" -lq ingin.lz [ $? = 2 ] || test_failed $LINENO +"${LZIP}" -atq ingin.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -atq < ingin.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -acdq ingin.lz > out +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -adq < ingin.lz > out +[ $? = 2 ] || test_failed $LINENO "${LZIP}" -t ingin.lz || test_failed $LINENO +"${LZIP}" -t < ingin.lz || test_failed $LINENO "${LZIP}" -cd ingin.lz > copy || test_failed $LINENO cmp in copy || test_failed $LINENO -"${LZIP}" -t < ingin.lz || test_failed $LINENO "${LZIP}" -d < ingin.lz > copy || test_failed $LINENO cmp in copy || test_failed $LINENO -rm -f copy ingin.lz || framework_failure +rm -f copy ingin.lz out || framework_failure echo if [ ${fail} = 0 ] ; then diff --git a/testsuite/fox.lz b/testsuite/fox.lz new file mode 100644 index 0000000000000000000000000000000000000000..509da8268a3fd054415089a646cdcfd00d3eee7a GIT binary patch literal 80 zcmeZ?@(f_)VbGG|Sj4QF_R+iI=pxqtde3fNQjl6}^5c>03rDkGci1v_+dL64QRjTU cIqmVA>95u)|NqywfFbKlz-wIw2nc}E07J|k>Hq)$ literal 0 HcmV?d00001 diff --git a/testsuite/fox_bcrc.lz b/testsuite/fox_bcrc.lz new file mode 100644 index 0000000000000000000000000000000000000000..8f6a7c4af4b808a1ed873b58d5d9a343a722edcd GIT binary patch literal 80 zcmeZ?@(f_)VbGG|Sj4QF_R+iI=pxqtde3fNQjl6}^5c>03rDkGci1v_+dL64QRjTU cIqmVA>95u)|NqywfFb)#z-wIw2nc}E07Kv&>i_@% literal 0 HcmV?d00001 diff --git a/testsuite/fox_crc0.lz b/testsuite/fox_crc0.lz new file mode 100644 index 0000000000000000000000000000000000000000..1abe92689f2fb57212633f41962e53f4297bd5a2 GIT binary patch literal 80 zcmeZ?@(f_)VbGG|Sj4QF_R+iI=pxqtde3fNQjl6}^5c>03rDkGci1v_+dL64QRjTU bIqmVA>95u)|NqywfB^(_!6XAi0F(v*7bP5d literal 0 HcmV?d00001 diff --git a/testsuite/fox_das46.lz b/testsuite/fox_das46.lz new file mode 100644 index 0000000000000000000000000000000000000000..43ed9f9503c4e8c298e6bf671f669b56e24162a2 GIT binary patch literal 80 zcmeZ?@(f_)VbGG|Sj4QF_R+iI=pxqtde3fNQjl6}^5c>03rDkGci1v_+dL64QRjTU cIqmVA>95u)|NqywfFbKlz-v7Q2nc}E07Kj!>i_@% literal 0 HcmV?d00001 diff --git a/testsuite/fox_de20.lz b/testsuite/fox_de20.lz new file mode 100644 index 0000000000000000000000000000000000000000..10949d888f0e1c630fc860b3a8c8eb0bee7f77bf GIT binary patch literal 80 zcmeZ?@(f_)VbGG|Sj4QF_R+iI=pxpCde3fNQjl6}^5c>03rDkGci1v_+dL64QRjTU cIqmVA>95u)|NqywfFbKlz-wIw2nc}E07H--=>Px# literal 0 HcmV?d00001 diff --git a/testsuite/fox_mes81.lz b/testsuite/fox_mes81.lz new file mode 100644 index 0000000000000000000000000000000000000000..d50ef2e2fa7a5160a77c814344218585e865f266 GIT binary patch literal 80 zcmeZ?@(f_)VbGG|Sj4QF_R+iI=pxqtde3fNQjl6}^5c>03rDkGci1v_+dL64QRjTU cIqmVA>95u)|NqywfFbKlz-wIw2ndAI07KLs>i_@% literal 0 HcmV?d00001 diff --git a/testsuite/fox_s11.lz b/testsuite/fox_s11.lz new file mode 100644 index 0000000000000000000000000000000000000000..dca909c62a44e1b011404dbd5afe0af47c3bb6e7 GIT binary patch literal 80 zcmeZ?@(f_)X3&!2Sj4QF_R+iI=pxqtde3fNQjl6}^5c>03rDkGci1v_+dL64QRjTU cIqmVA>95u)|NqywfFbKlz-wIw2nc}E07HQu=>Px# literal 0 HcmV?d00001 diff --git a/testsuite/fox_v2.lz b/testsuite/fox_v2.lz new file mode 100644 index 0000000000000000000000000000000000000000..8620981714096152fff6673aa04060f251bdad44 GIT binary patch literal 80 zcmeZ?@(f_&VbGG|Sj4QF_R+iI=pxqtde3fNQjl6}^5c>03rDkGci1v_+dL64QRjTU cIqmVA>95u)|NqywfFbKlz-wIw2nc}E07Mub>i_@% literal 0 HcmV?d00001 diff --git a/testsuite/test_em.txt.lz b/testsuite/test_em.txt.lz new file mode 100644 index 0000000000000000000000000000000000000000..7e96250d9ec70bc7d8cb173f438c76be447e6b6f GIT binary patch literal 14024 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 Gt^OBPlQ$#) literal 0 HcmV?d00001 From a7c61ed055bc236534d268285196e75437c1dbb0 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 17 Feb 2025 22:30:20 +0100 Subject: [PATCH 06/10] Adding upstream version 1.13. Signed-off-by: Daniel Baumann --- ChangeLog | 10 ++- INSTALL | 4 +- Makefile.in | 2 +- NEWS | 30 ++------- README | 4 +- carg_parser.c | 110 +++++++++++++++++++++----------- carg_parser.h | 14 +++-- configure | 6 +- decoder.c | 43 +++++-------- decoder.h | 119 ++++++++++++++++++++++++----------- doc/lunzip.1 | 6 +- list.c | 69 ++++++++++---------- lzip.h | 9 +-- lzip_index.c | 43 +++++-------- lzip_index.h | 2 +- main.c | 153 ++++++++++++++++++++++++++++----------------- testsuite/check.sh | 23 ++++--- 17 files changed, 370 insertions(+), 277 deletions(-) diff --git a/ChangeLog b/ChangeLog index 08e3c0a..07d8d6e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2022-01-22 Antonio Diaz Diaz + + * Version 1.13 released. + * Decompression time has been reduced by 5-12% depending on the file. + * main.c (getnum): Show option name and valid range if error. + 2021-01-01 Antonio Diaz Diaz * Version 1.12 released. @@ -18,7 +24,7 @@ * lzip_index.c: Detect some kinds of corrupt trailers. * main.c (main): Check return value of close( infd ). * main.c: Compile on DOS with DJGPP. - * configure: Accept appending to CFLAGS, 'CFLAGS+=OPTIONS'. + * configure: Accept appending to CFLAGS; 'CFLAGS+=OPTIONS'. * INSTALL: Document use of CFLAGS+='-D __USE_MINGW_ANSI_STDIO'. 2018-02-05 Antonio Diaz Diaz @@ -115,7 +121,7 @@ * Created from the decompression code of clzip 1.1. -Copyright (C) 2010-2021 Antonio Diaz Diaz. +Copyright (C) 2010-2022 Antonio Diaz Diaz. This file is a collection of facts, and thus it is not copyrightable, but just in case, you have unlimited permission to copy, distribute, and diff --git a/INSTALL b/INSTALL index 313b7dc..4282b1e 100644 --- a/INSTALL +++ b/INSTALL @@ -1,7 +1,7 @@ Requirements ------------ You will need a C99 compiler. (gcc 3.3.6 or newer is recommended). -I use gcc 6.1.0 and 4.1.2, but the code should compile with any standards +I use gcc 6.1.0 and 3.3.6, but the code should compile with any standards compliant compiler. Gcc is available at http://gcc.gnu.org. @@ -69,7 +69,7 @@ After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2010-2021 Antonio Diaz Diaz. +Copyright (C) 2010-2022 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute, and modify it. diff --git a/Makefile.in b/Makefile.in index aff94d4..ffc4ce8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -20,7 +20,7 @@ objs = carg_parser.o lzip_index.o list.o decoder.o main.o all : $(progname) $(progname) : $(objs) - $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(objs) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(objs) main.o : main.c $(CC) $(CPPFLAGS) $(CFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< diff --git a/NEWS b/NEWS index 98e59f3..fee658c 100644 --- a/NEWS +++ b/NEWS @@ -1,28 +1,6 @@ -Changes in version 1.12: +Changes in version 1.13: -Lunzip now reports an error if a file name is empty (lunzip -t ""). +Decompression time has been reduced by 5-12% depending on the file. -Option '-o, --output' now behaves like '-c, --stdout', but sending the -output unconditionally to a file instead of to standard output. See the new -description of '-o' in the manual. This change is backwards compatible only -when decompressing from standard input alone. Therefore commands like: - lunzip -d -o foo - bar.lz < foo.lz -must now be split into: - lunzip -d -o foo - < foo.lz - lunzip -d bar.lz -or rewritten as: - lunzip -d - bar.lz < foo.lz > foo - -Lunzip now does not even open the output file if the input file is a terminal. - -The words 'decompressed' and 'compressed' have been replaced with the -shorter 'out' and 'in' in the verbose output when decompressing or testing. - -Option '--list' now reports corruption or truncation of the last header in a -multimenber file specifically instead of showing the generic message "Last -member in input file is truncated or corrupt." - -The commands needed to extract files from a tar.lz archive have been -documented in the output of '--help' and in the man page. - -9 new test files have been added to the testsuite. +In case of error in a numerical argument to a command line option, lunzip +now shows the name of the option and the range of valid values. diff --git a/README b/README index b09c908..39cd00d 100644 --- a/README +++ b/README @@ -60,7 +60,7 @@ filename.lz becomes filename filename.tlz becomes filename.tar anyothername becomes anyothername.out -Decompressing a file is much like copying or moving it; therefore lunzip +Decompressing a file is much like copying or moving it. Therefore lunzip preserves the access and modification dates, permissions, and, when possible, ownership of the file just as 'cp -p' does. (If the user ID or the group ID can't be duplicated, the file permission bits S_ISUID and @@ -89,7 +89,7 @@ been compressed. Decompressed is used to refer to data which have undergone the process of decompression. -Copyright (C) 2010-2021 Antonio Diaz Diaz. +Copyright (C) 2010-2022 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute, and modify it. diff --git a/carg_parser.c b/carg_parser.c index d0c05d5..181ba23 100644 --- a/carg_parser.c +++ b/carg_parser.c @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2021 Antonio Diaz Diaz. + Copyright (C) 2006-2022 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided @@ -32,10 +32,10 @@ static void * ap_resize_buffer( void * buf, const int min_size ) } -static char push_back_record( struct Arg_parser * const ap, - const int code, const char * const argument ) +static char push_back_record( struct Arg_parser * const ap, const int code, + const char * const long_name, + const char * const argument ) { - const int len = strlen( argument ); struct ap_Record * p; void * tmp = ap_resize_buffer( ap->data, ( ap->data_size + 1 ) * sizeof (struct ap_Record) ); @@ -43,11 +43,29 @@ static char push_back_record( struct Arg_parser * const ap, ap->data = (struct ap_Record *)tmp; p = &(ap->data[ap->data_size]); p->code = code; - p->argument = 0; - tmp = ap_resize_buffer( p->argument, len + 1 ); - if( !tmp ) return 0; - p->argument = (char *)tmp; - strncpy( p->argument, argument, len + 1 ); + if( long_name ) + { + const int len = strlen( long_name ); + p->parsed_name = (char *)malloc( len + 2 + 1 ); + if( !p->parsed_name ) return 0; + p->parsed_name[0] = p->parsed_name[1] = '-'; + strncpy( p->parsed_name + 2, long_name, len + 1 ); + } + else if( code > 0 && code < 256 ) + { + p->parsed_name = (char *)malloc( 2 + 1 ); + if( !p->parsed_name ) return 0; + p->parsed_name[0] = '-'; p->parsed_name[1] = code; p->parsed_name[2] = 0; + } + else p->parsed_name = 0; + if( argument ) + { + const int len = strlen( argument ); + p->argument = (char *)malloc( len + 1 ); + if( !p->argument ) { free( p->parsed_name ); return 0; } + strncpy( p->argument, argument, len + 1 ); + } + else p->argument = 0; ++ap->data_size; return 1; } @@ -68,12 +86,14 @@ static char add_error( struct Arg_parser * const ap, const char * const msg ) static void free_data( struct Arg_parser * const ap ) { int i; - for( i = 0; i < ap->data_size; ++i ) free( ap->data[i].argument ); + for( i = 0; i < ap->data_size; ++i ) + { free( ap->data[i].argument ); free( ap->data[i].parsed_name ); } if( ap->data ) { free( ap->data ); ap->data = 0; } ap->data_size = 0; } +/* Return 0 only if out of memory. */ static char parse_long_option( struct Arg_parser * const ap, const char * const opt, const char * const arg, const struct ap_Option options[], @@ -87,9 +107,10 @@ static char parse_long_option( struct Arg_parser * const ap, /* Test all long options for either exact match or abbreviated matches. */ for( i = 0; options[i].code != 0; ++i ) - if( options[i].name && strncmp( options[i].name, &opt[2], len ) == 0 ) + if( options[i].long_name && + strncmp( options[i].long_name, &opt[2], len ) == 0 ) { - if( strlen( options[i].name ) == len ) /* Exact match found */ + if( strlen( options[i].long_name ) == len ) /* Exact match found */ { index = i; exact = 1; break; } else if( index < 0 ) index = i; /* First nonexact match found */ else if( options[index].code != options[i].code || @@ -117,35 +138,39 @@ static char parse_long_option( struct Arg_parser * const ap, { if( options[index].has_arg == ap_no ) { - add_error( ap, "option '--" ); add_error( ap, options[index].name ); + add_error( ap, "option '--" ); add_error( ap, options[index].long_name ); add_error( ap, "' doesn't allow an argument" ); return 1; } if( options[index].has_arg == ap_yes && !opt[len+3] ) { - add_error( ap, "option '--" ); add_error( ap, options[index].name ); + add_error( ap, "option '--" ); add_error( ap, options[index].long_name ); add_error( ap, "' requires an argument" ); return 1; } - return push_back_record( ap, options[index].code, &opt[len+3] ); + return push_back_record( ap, options[index].code, + options[index].long_name, &opt[len+3] ); } if( options[index].has_arg == ap_yes ) { if( !arg || !arg[0] ) { - add_error( ap, "option '--" ); add_error( ap, options[index].name ); + add_error( ap, "option '--" ); add_error( ap, options[index].long_name ); add_error( ap, "' requires an argument" ); return 1; } ++*argindp; - return push_back_record( ap, options[index].code, arg ); + return push_back_record( ap, options[index].code, + options[index].long_name, arg ); } - return push_back_record( ap, options[index].code, "" ); + return push_back_record( ap, options[index].code, + options[index].long_name, 0 ); } +/* Return 0 only if out of memory. */ static char parse_short_option( struct Arg_parser * const ap, const char * const opt, const char * const arg, const struct ap_Option options[], @@ -156,13 +181,13 @@ static char parse_short_option( struct Arg_parser * const ap, while( cind > 0 ) { int index = -1, i; - const unsigned char code = opt[cind]; + const unsigned char c = opt[cind]; char code_str[2]; - code_str[0] = code; code_str[1] = 0; + code_str[0] = c; code_str[1] = 0; - if( code != 0 ) + if( c != 0 ) for( i = 0; options[i].code; ++i ) - if( code == options[i].code ) + if( c == options[i].code ) { index = i; break; } if( index < 0 ) @@ -176,7 +201,7 @@ static char parse_short_option( struct Arg_parser * const ap, if( options[index].has_arg != ap_no && cind > 0 && opt[cind] ) { - if( !push_back_record( ap, code, &opt[cind] ) ) return 0; + if( !push_back_record( ap, c, 0, &opt[cind] ) ) return 0; ++*argindp; cind = 0; } else if( options[index].has_arg == ap_yes ) @@ -188,9 +213,9 @@ static char parse_short_option( struct Arg_parser * const ap, return 1; } ++*argindp; cind = 0; - if( !push_back_record( ap, code, arg ) ) return 0; + if( !push_back_record( ap, c, 0, arg ) ) return 0; } - else if( !push_back_record( ap, code, "" ) ) return 0; + else if( !push_back_record( ap, c, 0, 0 ) ) return 0; } return 1; } @@ -203,7 +228,7 @@ char ap_init( struct Arg_parser * const ap, const char ** non_options = 0; /* skipped non-options */ int non_options_size = 0; /* number of skipped non-options */ int argind = 1; /* index in argv */ - int i; + char done = 0; /* false until success */ ap->data = 0; ap->error = 0; @@ -223,20 +248,20 @@ char ap_init( struct Arg_parser * const ap, if( ch2 == '-' ) { if( !argv[argind][2] ) { ++argind; break; } /* we found "--" */ - else if( !parse_long_option( ap, opt, arg, options, &argind ) ) return 0; + else if( !parse_long_option( ap, opt, arg, options, &argind ) ) goto out; } - else if( !parse_short_option( ap, opt, arg, options, &argind ) ) return 0; + else if( !parse_short_option( ap, opt, arg, options, &argind ) ) goto out; if( ap->error ) break; } else { if( in_order ) - { if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; } + { if( !push_back_record( ap, 0, 0, argv[argind++] ) ) goto out; } else { void * tmp = ap_resize_buffer( non_options, ( non_options_size + 1 ) * sizeof *non_options ); - if( !tmp ) return 0; + if( !tmp ) goto out; non_options = (const char **)tmp; non_options[non_options_size++] = argv[argind++]; } @@ -245,13 +270,15 @@ char ap_init( struct Arg_parser * const ap, if( ap->error ) free_data( ap ); else { + int i; for( i = 0; i < non_options_size; ++i ) - if( !push_back_record( ap, 0, non_options[i] ) ) return 0; + if( !push_back_record( ap, 0, 0, non_options[i] ) ) goto out; while( argind < argc ) - if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; + if( !push_back_record( ap, 0, 0, argv[argind++] ) ) goto out; } - if( non_options ) free( non_options ); - return 1; + done = 1; +out: if( non_options ) free( non_options ); + return done; } @@ -273,13 +300,20 @@ int ap_arguments( const struct Arg_parser * const ap ) int ap_code( const struct Arg_parser * const ap, const int i ) { - if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].code; - else return 0; + if( i < 0 || i >= ap_arguments( ap ) ) return 0; + return ap->data[i].code; + } + + +const char * ap_parsed_name( const struct Arg_parser * const ap, const int i ) + { + if( i < 0 || i >= ap_arguments( ap ) || !ap->data[i].parsed_name ) return ""; + return ap->data[i].parsed_name; } const char * ap_argument( const struct Arg_parser * const ap, const int i ) { - if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].argument; - else return ""; + if( i < 0 || i >= ap_arguments( ap ) || !ap->data[i].argument ) return ""; + return ap->data[i].argument; } diff --git a/carg_parser.h b/carg_parser.h index c5f2352..0c64861 100644 --- a/carg_parser.h +++ b/carg_parser.h @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2021 Antonio Diaz Diaz. + Copyright (C) 2006-2022 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided @@ -24,9 +24,9 @@ message. 'options' is an array of 'struct ap_Option' terminated by an element - containing a code which is zero. A null name means a short-only - option. A code value outside the unsigned char range means a - long-only option. + containing a code which is zero. A null long_name means a short-only + option. A code value outside the unsigned char range means a long-only + option. Arg_parser normally makes it appear as if all the option arguments were specified before all the non-option arguments for the purposes @@ -50,7 +50,7 @@ enum ap_Has_arg { ap_no, ap_yes, ap_maybe }; struct ap_Option { int code; /* Short option letter or code ( code != 0 ) */ - const char * name; /* Long option name (maybe null) */ + const char * long_name; /* Long option name (maybe null) */ enum ap_Has_arg has_arg; }; @@ -58,6 +58,7 @@ struct ap_Option struct ap_Record { int code; + char * parsed_name; char * argument; }; @@ -86,6 +87,9 @@ int ap_arguments( const struct Arg_parser * const ap ); Else ap_argument( i ) is the option's argument (or empty). */ int ap_code( const struct Arg_parser * const ap, const int i ); +/* Full name of the option parsed (short or long). */ +const char * ap_parsed_name( const struct Arg_parser * const ap, const int i ); + const char * ap_argument( const struct Arg_parser * const ap, const int i ); #ifdef __cplusplus diff --git a/configure b/configure index 54fb2fb..e241235 100755 --- a/configure +++ b/configure @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2021 Antonio Diaz Diaz. +# Copyright (C) 2010-2022 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission # to copy, distribute, and modify it. pkgname=lunzip -pkgversion=1.12 +pkgversion=1.13 progname=lunzip srctrigger=doc/${progname}.1 @@ -167,7 +167,7 @@ echo "LDFLAGS = ${LDFLAGS}" rm -f Makefile cat > Makefile << EOF # Makefile for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2021 Antonio Diaz Diaz. +# Copyright (C) 2010-2022 Antonio Diaz Diaz. # This file was generated automatically by configure. Don't edit. # # This Makefile is free software: you have unlimited permission diff --git a/decoder.c b/decoder.c index 63d30ea..b52b35f 100644 --- a/decoder.c +++ b/decoder.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2021 Antonio Diaz Diaz. + Copyright (C) 2010-2022 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 @@ -32,8 +32,8 @@ CRC32 crc32; -/* Returns the number of bytes really read. - If (returned value < size) and (errno == 0), means EOF was reached. +/* Return the number of bytes really read. + If (value returned < size) and (errno == 0), means EOF was reached. */ int readblock( const int fd, uint8_t * const buf, const int size ) { @@ -51,8 +51,8 @@ int readblock( const int fd, uint8_t * const buf, const int size ) } -/* Returns the number of bytes really written. - If (returned value < size), it is always an error. +/* Return the number of bytes really written. + If (value returned < size), it is always an error. */ static int writeblock( const int fd, const uint8_t * const buf, const int size ) { @@ -118,8 +118,6 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, int size = Rd_read_data( d->rdec, trailer, Lt_size ); const unsigned long long data_size = LZd_data_position( d ); const unsigned long long member_size = Rd_member_position( d->rdec ); - unsigned td_crc; - unsigned long long td_size, tm_size; bool error = false; if( size < Lt_size ) @@ -134,7 +132,7 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, while( size < Lt_size ) trailer[size++] = 0; } - td_crc = Lt_get_data_crc( trailer ); + const unsigned td_crc = Lt_get_data_crc( trailer ); if( td_crc != LZd_crc( d ) ) { error = true; @@ -145,7 +143,7 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, td_crc, LZd_crc( d ) ); } } - td_size = Lt_get_data_size( trailer ); + const unsigned long long td_size = Lt_get_data_size( trailer ); if( td_size != data_size ) { error = true; @@ -156,7 +154,7 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, td_size, td_size, data_size, data_size ); } } - tm_size = Lt_get_member_size( trailer ); + const unsigned long long tm_size = Lt_get_member_size( trailer ); if( tm_size != member_size ) { error = true; @@ -192,10 +190,6 @@ int LZd_decode_member( struct LZ_decoder * const d, struct Pretty_print * const pp ) { struct Range_decoder * const rdec = d->rdec; - void (* const copy_block) - ( struct LZ_decoder * const d, const unsigned distance, unsigned len ) = - ( d->buffer_size >= d->dictionary_size ) ? - &LZd_copy_block : &LZd_copy_block2; Bit_model bm_literal[1<buffer_size >= d->dictionary_size; Bm_array_init( bm_literal[0], (1 << literal_context_bits) * 0x300 ); Bm_array_init( bm_match[0], states * pos_states ); @@ -230,25 +225,19 @@ int LZd_decode_member( struct LZ_decoder * const d, Rd_load( rdec ); while( !Rd_finished( rdec ) ) { - int len; const int pos_state = LZd_data_position( d ) & pos_state_mask; if( Rd_decode_bit( rdec, &bm_match[state][pos_state] ) == 0 ) /* 1st bit */ { /* literal byte */ Bit_model * const bm = bm_literal[get_lit_state(LZd_peek_prev( d ))]; - if( St_is_char( state ) ) - { - state -= ( state < 4 ) ? state : 3; + if( ( state = St_set_char( state ) ) < 4 ) LZd_put_byte( d, Rd_decode_tree8( rdec, bm ) ); - } else - { - state -= ( state < 10 ) ? 3 : 6; LZd_put_byte( d, Rd_decode_matched( rdec, bm, LZd_peek( d, rep0 ) ) ); - } continue; } /* match or repeated match */ + int len; if( Rd_decode_bit( rdec, &bm_rep[state] ) != 0 ) /* 2nd bit */ { if( Rd_decode_bit( rdec, &bm_rep0[state] ) == 0 ) /* 3rd bit */ @@ -274,13 +263,12 @@ int LZd_decode_member( struct LZ_decoder * const d, rep0 = distance; } state = St_set_rep( state ); - len = min_match_len + Rd_decode_len( rdec, &rep_len_model, pos_state ); + len = Rd_decode_len( rdec, &rep_len_model, pos_state ); } else /* match */ { - unsigned distance; - len = min_match_len + Rd_decode_len( rdec, &match_len_model, pos_state ); - distance = Rd_decode_tree6( rdec, bm_dis_slot[get_len_state(len)] ); + len = Rd_decode_len( rdec, &match_len_model, pos_state ); + unsigned distance = Rd_decode_tree6( rdec, bm_dis_slot[get_len_state(len)] ); if( distance >= start_dis_model ) { const unsigned dis_slot = distance; @@ -321,7 +309,8 @@ int LZd_decode_member( struct LZ_decoder * const d, ( !d->pos_wrapped && rep0 >= LZd_data_position( d ) ) ) { LZd_flush_data( d ); return 1; } } - copy_block( d, rep0, len ); + if( full_buffer || rep0 < d->buffer_size ) LZd_copy_block( d, rep0, len ); + else LZd_copy_block2( d, rep0, len ); } LZd_flush_data( d ); return 2; diff --git a/decoder.h b/decoder.h index 0fe0110..0afdd83 100644 --- a/decoder.h +++ b/decoder.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2021 Antonio Diaz Diaz. + Copyright (C) 2010-2022 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 @@ -101,12 +101,11 @@ static inline unsigned Rd_decode( struct Range_decoder * const rdec, int i; for( i = num_bits; i > 0; --i ) { - bool bit; Rd_normalize( rdec ); rdec->range >>= 1; /* symbol <<= 1; */ /* if( rdec->code >= rdec->range ) { rdec->code -= rdec->range; symbol |= 1; } */ - bit = ( rdec->code >= rdec->range ); + const bool bit = ( rdec->code >= rdec->range ); symbol <<= 1; symbol += bit; rdec->code -= rdec->range & ( 0U - bit ); } @@ -116,42 +115,75 @@ static inline unsigned Rd_decode( struct Range_decoder * const rdec, static inline unsigned 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; + const uint32_t bound = ( rdec->range >> bit_model_total_bits ) * *probability; if( rdec->code < bound ) { rdec->range = bound; - *probability += (bit_model_total - *probability) >> bit_model_move_bits; + *probability += ( bit_model_total - *probability ) >> bit_model_move_bits; return 0; } else { - rdec->range -= bound; rdec->code -= bound; + rdec->range -= bound; *probability -= *probability >> bit_model_move_bits; return 1; } } -static inline unsigned Rd_decode_tree3( struct Range_decoder * const rdec, - Bit_model bm[] ) +static inline void Rd_decode_symbol_bit( struct Range_decoder * const rdec, + Bit_model * const probability, unsigned * symbol ) { - unsigned symbol = 2 | Rd_decode_bit( rdec, &bm[1] ); - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); - return symbol & 7; + Rd_normalize( rdec ); + *symbol <<= 1; + const uint32_t bound = ( rdec->range >> bit_model_total_bits ) * *probability; + if( rdec->code < bound ) + { + rdec->range = bound; + *probability += ( bit_model_total - *probability ) >> bit_model_move_bits; + } + else + { + rdec->code -= bound; + rdec->range -= bound; + *probability -= *probability >> bit_model_move_bits; + *symbol |= 1; + } + } + +static inline void Rd_decode_symbol_bit_reversed( struct Range_decoder * const rdec, + Bit_model * const probability, unsigned * model, + unsigned * symbol, const int i ) + { + Rd_normalize( rdec ); + *model <<= 1; + const uint32_t bound = ( rdec->range >> bit_model_total_bits ) * *probability; + if( rdec->code < bound ) + { + rdec->range = bound; + *probability += ( bit_model_total - *probability ) >> bit_model_move_bits; + } + else + { + rdec->code -= bound; + rdec->range -= bound; + *probability -= *probability >> bit_model_move_bits; + *model |= 1; + *symbol |= 1 << i; + } } static inline unsigned Rd_decode_tree6( struct Range_decoder * const rdec, Bit_model bm[] ) { - unsigned symbol = 2 | Rd_decode_bit( rdec, &bm[1] ); - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); + unsigned symbol = 1; + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); return symbol & 0x3F; } @@ -159,9 +191,14 @@ static inline unsigned Rd_decode_tree8( struct Range_decoder * const rdec, Bit_model bm[] ) { unsigned symbol = 1; - int i; - for( i = 0; i < 8; ++i ) - symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); return symbol & 0xFF; } @@ -173,21 +210,19 @@ Rd_decode_tree_reversed( struct Range_decoder * const rdec, unsigned symbol = 0; int i; for( i = 0; i < num_bits; ++i ) - { - const unsigned bit = Rd_decode_bit( rdec, &bm[model] ); - model <<= 1; model += bit; - symbol |= ( bit << i ); - } + Rd_decode_symbol_bit_reversed( rdec, &bm[model], &model, &symbol, i ); return symbol; } static inline unsigned Rd_decode_tree_reversed4( struct Range_decoder * const rdec, Bit_model bm[] ) { - unsigned symbol = Rd_decode_bit( rdec, &bm[1] ); - symbol += Rd_decode_bit( rdec, &bm[2+symbol] ) << 1; - symbol += Rd_decode_bit( rdec, &bm[4+symbol] ) << 2; - symbol += Rd_decode_bit( rdec, &bm[8+symbol] ) << 3; + unsigned model = 1; + unsigned symbol = 0; + Rd_decode_symbol_bit_reversed( rdec, &bm[model], &model, &symbol, 0 ); + Rd_decode_symbol_bit_reversed( rdec, &bm[model], &model, &symbol, 1 ); + Rd_decode_symbol_bit_reversed( rdec, &bm[model], &model, &symbol, 2 ); + Rd_decode_symbol_bit_reversed( rdec, &bm[model], &model, &symbol, 3 ); return symbol; } @@ -210,11 +245,24 @@ static inline unsigned Rd_decode_len( struct Range_decoder * const rdec, struct Len_model * const lm, const int pos_state ) { + Bit_model * bm; + unsigned mask, offset, symbol = 1; + if( Rd_decode_bit( rdec, &lm->choice1 ) == 0 ) - return Rd_decode_tree3( rdec, lm->bm_low[pos_state] ); + { bm = lm->bm_low[pos_state]; mask = 7; offset = 0; goto len3; } if( Rd_decode_bit( rdec, &lm->choice2 ) == 0 ) - return len_low_symbols + Rd_decode_tree3( rdec, lm->bm_mid[pos_state] ); - return len_low_symbols + len_mid_symbols + Rd_decode_tree8( rdec, lm->bm_high ); + { bm = lm->bm_mid[pos_state]; mask = 7; offset = len_low_symbols; goto len3; } + bm = lm->bm_high; mask = 0xFF; offset = len_low_symbols + len_mid_symbols; + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); +len3: + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + Rd_decode_symbol_bit( rdec, &bm[symbol], &symbol ); + return ( symbol & mask ) + min_match_len + offset; } @@ -291,11 +339,10 @@ static inline void LZd_copy_block( struct LZ_decoder * const d, } } +/* block is (at least partially) outside of the buffer */ static inline void LZd_copy_block2( struct LZ_decoder * const d, const unsigned distance, unsigned len ) { - if( d->buffer_size > distance ) /* block is in buffer */ - { LZd_copy_block( d, distance, len ); return; } if( len < d->buffer_size - d->pos ) /* no wrap */ { const unsigned offset = distance + 1 + d->stream_pos - d->pos; diff --git a/doc/lunzip.1 b/doc/lunzip.1 index f96b431..9aa0300 100644 --- a/doc/lunzip.1 +++ b/doc/lunzip.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16. -.TH LUNZIP "1" "January 2021" "lunzip 1.12" "User Commands" +.TH LUNZIP "1" "January 2022" "lunzip 1.13" "User Commands" .SH NAME lunzip \- decompressor for the lzip format .SH SYNOPSIS @@ -77,7 +77,7 @@ To extract all the files from archive 'foo.tar.lz', use the commands .PP Exit status: 0 for a normal exit, 1 for environmental problems (file not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or -invalid input file, 3 for an internal consistency error (eg, bug) which +invalid input file, 3 for an internal consistency error (e.g., bug) which caused lunzip to panic. .PP The ideas embodied in lunzip are due to (at least) the following people: @@ -90,7 +90,7 @@ Report bugs to lzip\-bug@nongnu.org .br Lunzip home page: http://www.nongnu.org/lzip/lunzip.html .SH COPYRIGHT -Copyright \(co 2021 Antonio Diaz Diaz. +Copyright \(co 2022 Antonio Diaz Diaz. License GPLv2+: GNU GPL version 2 or later .br This is free software: you are free to change and redistribute it. diff --git a/list.c b/list.c index 8ab8c44..33de75c 100644 --- a/list.c +++ b/list.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2021 Antonio Diaz Diaz. + Copyright (C) 2010-2022 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 @@ -52,17 +52,15 @@ int list_files( const char * const filenames[], const int num_filenames, bool stdin_used = false; for( i = 0; i < num_filenames; ++i ) { - const char * input_filename; - struct Lzip_index lzip_index; - struct stat in_stats; /* not used */ - int infd; const bool from_stdin = ( strcmp( filenames[i], "-" ) == 0 ); if( from_stdin ) { if( stdin_used ) continue; else stdin_used = true; } - input_filename = from_stdin ? "(stdin)" : filenames[i]; - infd = from_stdin ? STDIN_FILENO : + const char * const input_filename = from_stdin ? "(stdin)" : filenames[i]; + struct stat in_stats; /* not used */ + const int infd = from_stdin ? STDIN_FILENO : open_instream( input_filename, &in_stats, false, true ); if( infd < 0 ) { set_retval( &retval, 1 ); continue; } + struct Lzip_index lzip_index; Li_init( &lzip_index, infd, ignore_trailing, loose_trailing ); close( infd ); if( lzip_index.retval != 0 ) @@ -71,37 +69,36 @@ int list_files( const char * const filenames[], const int num_filenames, set_retval( &retval, lzip_index.retval ); Li_free( &lzip_index ); continue; } - if( verbosity >= 0 ) + if( verbosity < 0 ) { Li_free( &lzip_index ); continue; } + const unsigned long long udata_size = Li_udata_size( &lzip_index ); + const unsigned long long cdata_size = Li_cdata_size( &lzip_index ); + total_comp += cdata_size; total_uncomp += udata_size; ++files; + const long members = lzip_index.members; + if( first_post ) { - const unsigned long long udata_size = Li_udata_size( &lzip_index ); - const unsigned long long cdata_size = Li_cdata_size( &lzip_index ); - total_comp += cdata_size; total_uncomp += udata_size; ++files; - if( first_post ) - { - first_post = false; - if( verbosity >= 1 ) fputs( " dict memb trail ", stdout ); - fputs( " uncompressed compressed saved name\n", stdout ); - } - if( verbosity >= 1 ) - printf( "%s %5ld %6lld ", format_ds( lzip_index.dictionary_size ), - lzip_index.members, Li_file_size( &lzip_index ) - cdata_size ); - list_line( udata_size, cdata_size, input_filename ); - - if( verbosity >= 2 && lzip_index.members > 1 ) - { - long i; - fputs( " member data_pos data_size member_pos member_size\n", stdout ); - for( i = 0; i < lzip_index.members; ++i ) - { - const struct Block * db = Li_dblock( &lzip_index, i ); - const struct Block * mb = Li_mblock( &lzip_index, i ); - printf( "%6ld %14llu %14llu %14llu %14llu\n", - i + 1, db->pos, db->size, mb->pos, mb->size ); - } - first_post = true; /* reprint heading after list of members */ - } - fflush( stdout ); + first_post = false; + if( verbosity >= 1 ) fputs( " dict memb trail ", stdout ); + fputs( " uncompressed compressed saved name\n", stdout ); } + if( verbosity >= 1 ) + printf( "%s %5ld %6lld ", format_ds( lzip_index.dictionary_size ), + members, Li_file_size( &lzip_index ) - cdata_size ); + list_line( udata_size, cdata_size, input_filename ); + + if( verbosity >= 2 && members > 1 ) + { + long i; + fputs( " member data_pos data_size member_pos member_size\n", stdout ); + for( i = 0; i < members; ++i ) + { + const struct Block * db = Li_dblock( &lzip_index, i ); + const struct Block * mb = Li_mblock( &lzip_index, i ); + printf( "%6ld %14llu %14llu %14llu %14llu\n", + i + 1, db->pos, db->size, mb->pos, mb->size ); + } + first_post = true; /* reprint heading after list of members */ + } + fflush( stdout ); Li_free( &lzip_index ); } if( verbosity >= 0 && files > 1 ) diff --git a/lzip.h b/lzip.h index 3961c33..4b77be8 100644 --- a/lzip.h +++ b/lzip.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2021 Antonio Diaz Diaz. + Copyright (C) 2010-2022 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 @@ -131,6 +131,7 @@ static inline void CRC32_init( void ) } } +/* about as fast as it is possible without messing with endianness */ static inline void CRC32_update_buf( uint32_t * const crc, const uint8_t * const buffer, const int size ) @@ -228,12 +229,12 @@ static inline bool Lt_verify_consistency( const Lzip_trailer data ) { const unsigned crc = Lt_get_data_crc( data ); const unsigned long long dsize = Lt_get_data_size( data ); - const unsigned long long msize = Lt_get_member_size( data ); - const unsigned long long mlimit = ( 9 * dsize + 7 ) / 8 + min_member_size; - const unsigned long long dlimit = 7090 * ( msize - 26 ) - 1; if( ( crc == 0 ) != ( dsize == 0 ) ) return false; + const unsigned long long msize = Lt_get_member_size( data ); if( msize < min_member_size ) return false; + const unsigned long long mlimit = ( 9 * dsize + 7 ) / 8 + min_member_size; if( mlimit > dsize && msize > mlimit ) return false; + const unsigned long long dlimit = 7090 * ( msize - 26 ) - 1; if( dlimit > msize && dsize > dlimit ) return false; return true; } diff --git a/lzip_index.c b/lzip_index.c index ca4df8d..559fd7a 100644 --- a/lzip_index.c +++ b/lzip_index.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2021 Antonio Diaz Diaz. + Copyright (C) 2010-2022 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 @@ -133,44 +133,38 @@ static bool Li_skip_trailing_data( struct Lzip_index * const li, const int fd, const bool ignore_trailing, const bool loose_trailing ) { + if( *pos < min_member_size ) return false; enum { block_size = 16384, buffer_size = block_size + Lt_size - 1 + Lh_size }; uint8_t buffer[buffer_size]; int bsize = *pos % block_size; /* total bytes in buffer */ - int search_size, rd_size; - unsigned long long ipos; - int i; - if( *pos < min_member_size ) return false; if( bsize <= buffer_size - block_size ) bsize += block_size; - search_size = bsize; /* bytes to search for trailer */ - rd_size = bsize; /* bytes to read from file */ - ipos = *pos - rd_size; /* aligned to block_size */ + int search_size = bsize; /* bytes to search for trailer */ + int rd_size = bsize; /* bytes to read from file */ + unsigned long long ipos = *pos - rd_size; /* aligned to block_size */ while( true ) { - const uint8_t max_msb = ( ipos + search_size ) >> 56; if( seek_read( fd, buffer, rd_size, ipos ) != rd_size ) - { Li_set_errno_error( li, "Error seeking member trailer: " ); - return false; } + { Li_set_errno_error( li, "Error seeking member trailer: " ); return false; } + const uint8_t max_msb = ( ipos + search_size ) >> 56; + int i; for( i = search_size; i >= Lt_size; --i ) if( buffer[i-1] <= max_msb ) /* most significant byte of member_size */ { - Lzip_header header; - const Lzip_header * header2; const Lzip_trailer * const trailer = (const Lzip_trailer *)( buffer + i - Lt_size ); const unsigned long long member_size = Lt_get_member_size( *trailer ); - unsigned dictionary_size; - bool full_h2; if( member_size == 0 ) /* skip trailing zeros */ { while( i > Lt_size && buffer[i-9] == 0 ) --i; continue; } if( member_size > ipos + i || !Lt_verify_consistency( *trailer ) ) continue; + Lzip_header header; if( !Li_read_header( li, fd, header, ipos + i - member_size ) ) return false; if( !Lh_verify( header ) ) continue; - header2 = (const Lzip_header *)( buffer + i ); - full_h2 = bsize - i >= Lh_size; + const Lzip_header * header2 = (const Lzip_header *)( buffer + i ); + const bool full_h2 = bsize - i >= Lh_size; if( Lh_verify_prefix( *header2, bsize - i ) ) /* last member */ { if( !full_h2 ) add_error( li, "Last member in input file is truncated." ); @@ -183,7 +177,7 @@ static bool Li_skip_trailing_data( struct Lzip_index * const li, const int fd, if( !ignore_trailing ) { add_error( li, trailing_msg ); li->retval = 2; return false; } *pos = ipos + i - member_size; - dictionary_size = Lh_get_dictionary_size( header ); + const unsigned dictionary_size = Lh_get_dictionary_size( header ); if( li->dictionary_size < dictionary_size ) li->dictionary_size = dictionary_size; return push_back_member( li, 0, Lt_get_data_size( *trailer ), *pos, @@ -204,9 +198,6 @@ static bool Li_skip_trailing_data( struct Lzip_index * const li, const int fd, bool Li_init( struct Lzip_index * const li, const int infd, const bool ignore_trailing, const bool loose_trailing ) { - Lzip_header header; - unsigned long long pos; - long i; li->member_vector = 0; li->error = 0; li->insize = lseek( infd, 0, SEEK_END ); @@ -223,18 +214,17 @@ bool Li_init( struct Lzip_index * const li, const int infd, { add_error( li, "Input file is too long (2^63 bytes or more)." ); li->retval = 2; return false; } + Lzip_header header; if( !Li_read_header( li, infd, header, 0 ) ) return false; if( Li_check_header_error( li, header ) ) return false; - pos = li->insize; /* always points to a header or to EOF */ + unsigned long long pos = li->insize; /* always points to a header or to EOF */ while( pos >= min_member_size ) { Lzip_trailer trailer; - unsigned long long member_size; - unsigned dictionary_size; if( seek_read( infd, trailer, Lt_size, pos - Lt_size ) != Lt_size ) { Li_set_errno_error( li, "Error reading member trailer: " ); break; } - member_size = Lt_get_member_size( trailer ); + const unsigned long long member_size = Lt_get_member_size( trailer ); if( member_size > pos || !Lt_verify_consistency( trailer ) ) { /* bad trailer */ if( li->members <= 0 ) @@ -253,7 +243,7 @@ bool Li_init( struct Lzip_index * const li, const int infd, break; } pos -= member_size; - dictionary_size = Lh_get_dictionary_size( header ); + const unsigned dictionary_size = Lh_get_dictionary_size( header ); if( li->dictionary_size < dictionary_size ) li->dictionary_size = dictionary_size; if( !push_back_member( li, 0, Lt_get_data_size( trailer ), pos, @@ -268,6 +258,7 @@ bool Li_init( struct Lzip_index * const li, const int infd, return false; } Li_reverse_member_vector( li ); + long i; for( i = 0; ; ++i ) { const long long end = block_end( li->member_vector[i].dblock ); diff --git a/lzip_index.h b/lzip_index.h index 4e9cd44..0938533 100644 --- a/lzip_index.h +++ b/lzip_index.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2021 Antonio Diaz Diaz. + Copyright (C) 2010-2022 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 diff --git a/main.c b/main.c index b5e458e..73e29b4 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2021 Antonio Diaz Diaz. + Copyright (C) 2010-2022 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 @@ -18,7 +18,7 @@ Exit status: 0 for a normal exit, 1 for environmental problems (file not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or invalid input file, 3 for an internal consistency error - (eg, bug) which caused lunzip to panic. + (e.g., bug) which caused lunzip to panic. */ #define _FILE_OFFSET_BITS 64 @@ -36,9 +36,9 @@ #include #include #include -#if defined(__MSVCRT__) || defined(__OS2__) || defined(__DJGPP__) +#if defined __MSVCRT__ || defined __OS2__ || defined __DJGPP__ #include -#if defined(__MSVCRT__) +#if defined __MSVCRT__ #define fchmod(x,y) 0 #define fchown(x,y,z) 0 #define SIGHUP SIGTERM @@ -50,7 +50,7 @@ #define S_IWOTH 0 #endif #endif -#if defined(__DJGPP__) +#if defined __DJGPP__ #define S_ISSOCK(x) 0 #define S_ISVTX 0 #endif @@ -68,10 +68,15 @@ #error "Environments where CHAR_BIT != 8 are not supported." #endif +#if ( defined SIZE_MAX && SIZE_MAX < UINT_MAX ) || \ + ( defined SSIZE_MAX && SSIZE_MAX < INT_MAX ) +#error "Environments where 'size_t' is narrower than 'int' are not supported." +#endif + int verbosity = 0; static const char * const program_name = "lunzip"; -static const char * const program_year = "2021"; +static const char * const program_year = "2022"; static const char * invocation_name = "lunzip"; /* default value */ static const struct { const char * from; const char * to; } known_extensions[] = { @@ -130,7 +135,7 @@ static void show_help( void ) "'tar -xf foo.tar.lz' or 'lunzip -cd foo.tar.lz | tar -xf -'.\n" "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n" "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n" - "invalid input file, 3 for an internal consistency error (eg, bug) which\n" + "invalid input file, 3 for an internal consistency error (e.g., bug) which\n" "caused lunzip to panic.\n" "\nThe ideas embodied in lunzip are due to (at least) the following people:\n" "Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for the\n" @@ -174,8 +179,6 @@ struct Pretty_print static void Pp_init( struct Pretty_print * const pp, const char * const filenames[], const int num_filenames ) { - unsigned stdin_name_len; - int i; pp->name = 0; pp->padded_name = 0; pp->stdin_name = "(stdin)"; @@ -183,7 +186,8 @@ static void Pp_init( struct Pretty_print * const pp, pp->first_post = false; if( verbosity <= 0 ) return; - stdin_name_len = strlen( pp->stdin_name ); + const unsigned stdin_name_len = strlen( pp->stdin_name ); + int i; for( i = 0; i < num_filenames; ++i ) { const char * const s = filenames[i]; @@ -217,16 +221,14 @@ static void Pp_reset( struct Pretty_print * const pp ) void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ) { - if( verbosity >= 0 ) + if( verbosity < 0 ) return; + if( pp->first_post ) { - if( pp->first_post ) - { - pp->first_post = false; - fputs( pp->padded_name, stderr ); - if( !msg ) fflush( stderr ); - } - if( msg ) fprintf( stderr, "%s\n", msg ); + pp->first_post = false; + fputs( pp->padded_name, stderr ); + if( !msg ) fflush( stderr ); } + if( msg ) fprintf( stderr, "%s\n", msg ); } @@ -264,17 +266,53 @@ void show_header( const unsigned dictionary_size ) } -static unsigned long getnum( const char * const ptr, +/* separate large numbers >= 100_000 in groups of 3 digits using '_' */ +static const char * format_num3( unsigned long long num ) + { + const char * const si_prefix = "kMGTPEZY"; + const char * const binary_prefix = "KMGTPEZY"; + enum { buffers = 8, bufsize = 4 * sizeof (long long) }; + static char buffer[buffers][bufsize]; /* circle of static buffers for printf */ + static int current = 0; + int i; + char * const buf = buffer[current++]; current %= buffers; + char * p = buf + bufsize - 1; /* fill the buffer backwards */ + *p = 0; /* terminator */ + if( num > 1024 ) + { + char prefix = 0; /* try binary first, then si */ + for( i = 0; i < 8 && num >= 1024 && num % 1024 == 0; ++i ) + { num /= 1024; prefix = binary_prefix[i]; } + if( prefix ) *(--p) = 'i'; + else + for( i = 0; i < 8 && num >= 1000 && num % 1000 == 0; ++i ) + { num /= 1000; prefix = si_prefix[i]; } + if( prefix ) *(--p) = prefix; + } + const bool split = num >= 100000; + + for( i = 0; ; ) + { + *(--p) = num % 10 + '0'; num /= 10; if( num == 0 ) break; + if( split && ++i >= 3 ) { i = 0; *(--p) = '_'; } + } + return p; + } + + +static unsigned long getnum( const char * const arg, + const char * const option_name, const unsigned long llimit, const unsigned long ulimit ) { - unsigned long result; char * tail; errno = 0; - result = strtoul( ptr, &tail, 0 ); - if( tail == ptr ) + unsigned long long result = strtoul( arg, &tail, 0 ); + if( tail == arg ) { - show_error( "Bad or missing numerical argument.", 0, true ); + if( verbosity >= 0 ) + fprintf( stderr, "%s: Bad or missing numerical argument in " + "option '%s'.\n", program_name, option_name ); exit( 1 ); } @@ -297,7 +335,9 @@ static unsigned long getnum( const char * const ptr, } if( exponent <= 0 ) { - show_error( "Bad multiplier in numerical argument.", 0, true ); + if( verbosity >= 0 ) + fprintf( stderr, "%s: Bad multiplier in numerical argument of " + "option '%s'.\n", program_name, option_name ); exit( 1 ); } for( i = 0; i < exponent; ++i ) @@ -309,21 +349,24 @@ static unsigned long getnum( const char * const ptr, if( !errno && ( result < llimit || result > ulimit ) ) errno = ERANGE; if( errno ) { - show_error( "Numerical argument out of limits.", 0, false ); + if( verbosity >= 0 ) + fprintf( stderr, "%s: Numerical argument out of limits [%s,%s] " + "in option '%s'.\n", program_name, format_num3( llimit ), + format_num3( ulimit ), option_name ); exit( 1 ); } return result; } -static int get_dict_size( const char * const arg ) +static int get_dict_size( const char * const arg, const char * const option_name ) { char * tail; const 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 ); + return getnum( arg, option_name, min_dictionary_size, max_dictionary_size ); } @@ -468,7 +511,7 @@ static bool check_tty_in( const char * const input_filename, const int infd, if( isatty( infd ) ) /* for example /dev/tty */ { show_file_error( input_filename, "I won't read compressed data from a terminal.", 0 ); - close( infd ); set_retval( retval, 1 ); + close( infd ); set_retval( retval, 2 ); if( program_mode != m_test ) cleanup_and_fail( *retval ); return false; } return true; @@ -559,12 +602,9 @@ static int decompress( const unsigned long long cfile_size, const int infd, for( first_member = true; ; first_member = false ) { - int result, size; - unsigned dictionary_size; Lzip_header header; - struct LZ_decoder decoder; Rd_reset_member_position( &rdec ); - size = Rd_read_data( &rdec, header, Lh_size ); + const int size = Rd_read_data( &rdec, header, Lh_size ); if( Rd_finished( &rdec ) ) /* End Of File */ { if( first_member ) @@ -594,20 +634,21 @@ static int decompress( const unsigned long long cfile_size, const int infd, if( !Lh_verify_version( header ) ) { Pp_show_msg( pp, bad_version( Lh_version( header ) ) ); retval = 2; break; } - dictionary_size = Lh_get_dictionary_size( header ); + const unsigned dictionary_size = Lh_get_dictionary_size( header ); if( !isvalid_ds( dictionary_size ) ) { Pp_show_msg( pp, bad_dict_msg ); retval = 2; break; } if( verbosity >= 2 || ( verbosity == 1 && first_member ) ) Pp_show_msg( pp, 0 ); + struct LZ_decoder decoder; if( !LZd_init( &decoder, &rdec, buffer_size, dictionary_size, outfd ) ) { Pp_show_msg( pp, "Not enough memory. Try a smaller output buffer size." ); retval = 1; break; } show_dprogress( cfile_size, partial_file_pos, &rdec, pp ); /* init */ - result = LZd_decode_member( &decoder, pp ); + const int result = LZd_decode_member( &decoder, pp ); partial_file_pos += Rd_member_position( &rdec ); LZd_free( &decoder ); if( result != 0 ) @@ -696,23 +737,15 @@ void show_dprogress( const unsigned long long cfile_size, int main( const int argc, const char * const argv[] ) { const char * default_output_filename = ""; - static struct Arg_parser parser; /* static because valgrind complains */ - static struct Pretty_print pp; /* and memory management in C sucks */ - static const char ** filenames = 0; - int num_filenames = 0; unsigned buffer_size = max_dictionary_size; enum Mode program_mode = m_compress; - int argind = 0; - int failed_tests = 0; - int retval = 0; int i; - bool filenames_given = false; bool force = false; bool ignore_trailing = true; bool keep_input_files = false; bool loose_trailing = false; - bool stdin_used = false; bool to_stdout = false; + if( argc > 0 ) invocation_name = argv[0]; enum { opt_lt = 256 }; const struct ap_Option options[] = @@ -734,19 +767,22 @@ int main( const int argc, const char * const argv[] ) { opt_lt, "loose-trailing", ap_no }, { 0 , 0, ap_no } }; - if( argc > 0 ) invocation_name = argv[0]; CRC32_init(); + /* static because valgrind complains and memory management in C sucks */ + static struct Arg_parser parser; if( !ap_init( &parser, argc, argv, options, 0 ) ) { show_error( mem_msg, 0, false ); return 1; } if( ap_error( &parser ) ) /* bad option */ { show_error( ap_error( &parser ), 0, true ); return 1; } + int argind = 0; for( ; argind < ap_arguments( &parser ); ++argind ) { const int code = ap_code( &parser, argind ); - const char * const arg = ap_argument( &parser, argind ); if( !code ) break; /* no more options */ + const char * const pn = ap_parsed_name( &parser, argind ); + const char * const arg = ap_argument( &parser, argind ); switch( code ) { case 'a': ignore_trailing = false; break; @@ -761,7 +797,7 @@ int main( const int argc, const char * const argv[] ) else { default_output_filename = arg; } break; case 'q': verbosity = -1; break; case 't': set_mode( &program_mode, m_test ); break; - case 'u': buffer_size = get_dict_size( arg ); break; + case 'u': buffer_size = get_dict_size( arg, pn ); break; case 'v': if( verbosity < 4 ) ++verbosity; break; case 'V': show_version(); return 0; case opt_lt: loose_trailing = true; break; @@ -769,15 +805,17 @@ int main( const int argc, const char * const argv[] ) } } /* end process options */ -#if defined(__MSVCRT__) || defined(__OS2__) || defined(__DJGPP__) +#if defined __MSVCRT__ || defined __OS2__ || defined __DJGPP__ setmode( STDIN_FILENO, O_BINARY ); setmode( STDOUT_FILENO, O_BINARY ); #endif - num_filenames = max( 1, ap_arguments( &parser ) - argind ); + static const char ** filenames = 0; + int num_filenames = max( 1, ap_arguments( &parser ) - argind ); filenames = resize_buffer( filenames, num_filenames * sizeof filenames[0] ); filenames[0] = "-"; + bool filenames_given = false; for( i = 0; argind + i < ap_arguments( &parser ); ++i ) { filenames[i] = ap_argument( &parser, argind + i ); @@ -816,17 +854,18 @@ int main( const int argc, const char * const argv[] ) if( !to_stdout && program_mode != m_test && ( filenames_given || to_file ) ) set_signals( signal_handler ); + static struct Pretty_print pp; Pp_init( &pp, filenames, num_filenames ); + int failed_tests = 0; + int retval = 0; const bool one_to_one = !to_stdout && program_mode != m_test && !to_file; + bool stdin_used = false; for( i = 0; i < num_filenames; ++i ) { - unsigned long long cfile_size; const char * input_filename = ""; int infd; - int tmp; struct stat in_stats; - const struct stat * in_statsp; Pp_set_name( &pp, filenames[i] ); if( strcmp( filenames[i], "-" ) == 0 ) @@ -872,11 +911,13 @@ int main( const int argc, const char * const argv[] ) } } - in_statsp = ( input_filename[0] && one_to_one ) ? &in_stats : 0; - cfile_size = ( input_filename[0] && S_ISREG( in_stats.st_mode ) ) ? - ( in_stats.st_size + 99 ) / 100 : 0; - tmp = decompress( cfile_size, infd, &pp, buffer_size, ignore_trailing, - loose_trailing, program_mode == m_test ); + const struct stat * const in_statsp = + ( input_filename[0] && one_to_one ) ? &in_stats : 0; + const unsigned long long cfile_size = + ( input_filename[0] && S_ISREG( in_stats.st_mode ) ) ? + ( in_stats.st_size + 99 ) / 100 : 0; + int tmp = decompress( cfile_size, infd, &pp, buffer_size, ignore_trailing, + loose_trailing, program_mode == m_test ); if( close( infd ) != 0 ) { show_file_error( pp.name, "Error closing input file", errno ); set_retval( &tmp, 1 ); } diff --git a/testsuite/check.sh b/testsuite/check.sh index 19928dd..c495ba1 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # check script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2021 Antonio Diaz Diaz. +# Copyright (C) 2010-2022 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission # to copy, distribute, and modify it. @@ -91,6 +91,7 @@ rm -f uin.lz || framework_failure printf "LZIP\001-.............................." | "${LZIP}" -t 2> /dev/null printf "LZIP\002-.............................." | "${LZIP}" -t 2> /dev/null printf "LZIP\001+.............................." | "${LZIP}" -t 2> /dev/null +rm -f out || framework_failure printf "\ntesting decompression..." @@ -114,19 +115,23 @@ lines=$("${LZIP}" -tvv "${in_em}" 2>&1 | wc -l) || test_failed $LINENO lines=$("${LZIP}" -lvv "${in_em}" | wc -l) || test_failed $LINENO [ "${lines}" -eq 11 ] || test_failed $LINENO "${lines}" +"${LZIP}" -cd "${fox_lz}" > fox || test_failed $LINENO cat "${in_lz}" > copy.lz || framework_failure "${LZIP}" -dk copy.lz || test_failed $LINENO cmp in copy || test_failed $LINENO -printf "to be overwritten" > copy || framework_failure -"${LZIP}" -d copy.lz 2> /dev/null +cat fox > copy || framework_failure +cat "${in_lz}" > out.lz || framework_failure +rm -f out || framework_failure +"${LZIP}" -d copy.lz out.lz 2> /dev/null # skip copy, decompress out [ $? = 1 ] || test_failed $LINENO +cmp fox copy || test_failed $LINENO +cmp in out || test_failed $LINENO "${LZIP}" -df copy.lz || test_failed $LINENO [ ! -e copy.lz ] || test_failed $LINENO cmp in copy || test_failed $LINENO +rm -f out || framework_failure printf "to be overwritten" > copy || framework_failure -"${LZIP}" -d -o copy < "${in_lz}" 2> /dev/null -[ $? = 1 ] || test_failed $LINENO "${LZIP}" -df -o copy < "${in_lz}" || test_failed $LINENO cmp in copy || test_failed $LINENO rm -f out copy || framework_failure @@ -154,7 +159,7 @@ rm -f copy anyothername.out || framework_failure [ $? = 1 ] || test_failed $LINENO "${LZIP}" -cdq in "${in_lz}" > copy [ $? = 2 ] || test_failed $LINENO -cat copy in | cmp in - || test_failed $LINENO +cat copy in | cmp in - || test_failed $LINENO # copy must be empty "${LZIP}" -cdq nx_file.lz "${in_lz}" > copy [ $? = 1 ] || test_failed $LINENO cmp in copy || test_failed $LINENO @@ -207,6 +212,7 @@ printf "\ngarbage" >> copy2.lz || framework_failure printf "to be overwritten" > copy2 || framework_failure "${LZIP}" -df copy2.lz || test_failed $LINENO cmp in2 copy2 || test_failed $LINENO +rm -f copy2 || framework_failure for i in 12 5120 6Ki 29 512KiB ; do printf "to be overwritten" > copy || framework_failure @@ -215,12 +221,12 @@ for i in 12 5120 6Ki 29 512KiB ; do rm -f copy || framework_failure "${LZIP}" -d -u$i -o copy "${in_lz}" || test_failed $LINENO $i cmp in copy || test_failed $LINENO $i - rm -f copy2 || framework_failure "${LZIP}" -d -u$i -o copy2 "${in_lz}" "${in_lz}" || test_failed $LINENO $i cmp in2 copy2 || test_failed $LINENO $i + rm -f copy2 || framework_failure done -rm -f in2 copy copy2 || framework_failure +rm -f in2 copy || framework_failure printf "\ntesting bad input..." @@ -285,7 +291,6 @@ for i in fox_v2.lz fox_s11.lz fox_de20.lz \ [ $? = 2 ] || test_failed $LINENO $i done -"${LZIP}" -cd "${fox_lz}" > fox || test_failed $LINENO for i in fox_bcrc.lz fox_crc0.lz fox_das46.lz fox_mes81.lz ; do "${LZIP}" -cdq "${testdir}"/$i > out [ $? = 2 ] || test_failed $LINENO $i From b03599ae9b886b75fa89c80dc998e51c0a0be7ad Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 17 Feb 2025 22:31:54 +0100 Subject: [PATCH 07/10] Adding upstream version 1.14~rc1. Signed-off-by: Daniel Baumann --- AUTHORS | 2 +- ChangeLog | 16 ++- INSTALL | 17 ++- Makefile.in | 9 +- NEWS | 23 +++- README | 18 +-- carg_parser.c | 4 +- carg_parser.h | 4 +- configure | 25 ++-- decoder.c | 62 +++++----- decoder.h | 12 +- doc/lunzip.1 | 26 +++-- list.c | 7 +- lzip.h | 67 ++++++----- lzip_index.c | 90 ++++++++------- lzip_index.h | 14 +-- main.c | 255 +++++++++++++++++++++++------------------ testsuite/check.sh | 206 ++++++++++++++++++--------------- testsuite/fox6.lz | Bin 0 -> 480 bytes testsuite/fox6_mark.lz | Bin 0 -> 480 bytes 20 files changed, 490 insertions(+), 367 deletions(-) create mode 100644 testsuite/fox6.lz create mode 100644 testsuite/fox6_mark.lz diff --git a/AUTHORS b/AUTHORS index e39119d..1ac737d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,7 +1,7 @@ Lunzip was written by Antonio Diaz Diaz. The ideas embodied in lunzip are due to (at least) the following people: -Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for the +Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrei Markov (for the definition of Markov chains), G.N.N. Martin (for the definition of range encoding), Igor Pavlov (for putting all the above together in LZMA), and Julian Seward (for bzip2's CLI). diff --git a/ChangeLog b/ChangeLog index 07d8d6e..264c4a0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2023-12-21 Antonio Diaz Diaz + + * Version 1.14-rc1 released. + * New options '--empty-error' and '--marking-error'. + * main.c: Reformat file diagnostics as 'PROGRAM: FILE: MESSAGE'. + (show_option_error): New function showing argument and option name. + (open_outstream): Create missing intermediate directories. + * lzip.h: Rename verify_* to check_*. + * configure, Makefile.in: New variable 'MAKEINFO'. + * INSTALL: Document use of CFLAGS+='--std=c99 -D_XOPEN_SOURCE=500'. + * testsuite: New test files fox6.lz, fox6_mark.lz. + 2022-01-22 Antonio Diaz Diaz * Version 1.13 released. @@ -55,7 +67,7 @@ * Version 1.8 released. * main.c: New option '-a, --trailing-error'. - * main.c (main): With '-u', verify that output file is regular. + * main.c (main): With '-u', check that output file is regular. * main.c (decompress): Print up to 6 bytes of trailing data when '-vvvv' is specified. * decoder.c (LZd_verify_trailer): Remove test of final code. @@ -121,7 +133,7 @@ * Created from the decompression code of clzip 1.1. -Copyright (C) 2010-2022 Antonio Diaz Diaz. +Copyright (C) 2010-2023 Antonio Diaz Diaz. This file is a collection of facts, and thus it is not copyrightable, but just in case, you have unlimited permission to copy, distribute, and diff --git a/INSTALL b/INSTALL index 4282b1e..b8e9690 100644 --- a/INSTALL +++ b/INSTALL @@ -18,8 +18,8 @@ Procedure or lzip -cd lunzip[version].tar.lz | tar -xf - -This creates the directory ./lunzip[version] containing the source from -the main archive. +This creates the directory ./lunzip[version] containing the source code +extracted from the archive. 2. Change to lunzip directory and run configure. (Try 'configure --help' for usage instructions). @@ -27,6 +27,10 @@ the main archive. cd lunzip[version] ./configure + If you choose a C standard, enable the POSIX features explicitly: + + ./configure CFLAGS+='--std=c99 -D_XOPEN_SOURCE=500' + If you are compiling on MinGW, use: ./configure CFLAGS+='-D __USE_MINGW_ANSI_STDIO' @@ -38,7 +42,8 @@ the main archive. 4. Optionally, type 'make check' to run the tests that come with lunzip. 5. Type 'make install' to install the program and any data files and - documentation. + documentation. You need root privileges to install into a prefix owned + by root. Or type 'make install-compress', which additionally compresses the man page after installation. @@ -61,15 +66,15 @@ object files and executables to go and run the 'configure' script. 'configure' automatically checks for the source code in '.', in '..', and in the directory that 'configure' is in. -'configure' recognizes the option '--srcdir=DIR' to control where to -look for the sources. Usually 'configure' can determine that directory +'configure' recognizes the option '--srcdir=DIR' to control where to look +for the source code. Usually 'configure' can determine that directory automatically. After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2010-2022 Antonio Diaz Diaz. +Copyright (C) 2010-2023 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute, and modify it. diff --git a/Makefile.in b/Makefile.in index ffc4ce8..ea04dd8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -28,6 +28,10 @@ main.o : main.c %.o : %.c $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< +# prevent 'make' from trying to remake source files +$(VPATH)/configure $(VPATH)/Makefile.in $(VPATH)/doc/$(pkgname).texi : ; +%.h %.c : ; + $(objs) : Makefile carg_parser.o : carg_parser.h decoder.o : lzip.h decoder.h @@ -35,13 +39,12 @@ list.o : lzip.h lzip_index.h lzip_index.o : lzip.h lzip_index.h main.o : carg_parser.h lzip.h decoder.h - doc : man info : $(VPATH)/doc/$(pkgname).info $(VPATH)/doc/$(pkgname).info : $(VPATH)/doc/$(pkgname).texi - cd $(VPATH)/doc && makeinfo $(pkgname).texi + cd $(VPATH)/doc && $(MAKEINFO) $(pkgname).texi man : $(VPATH)/doc/$(progname).1 @@ -121,6 +124,8 @@ dist : doc $(DISTNAME)/testsuite/test.txt \ $(DISTNAME)/testsuite/fox.lz \ $(DISTNAME)/testsuite/fox_*.lz \ + $(DISTNAME)/testsuite/fox6.lz \ + $(DISTNAME)/testsuite/fox6_mark.lz \ $(DISTNAME)/testsuite/test.txt.lz \ $(DISTNAME)/testsuite/test_em.txt.lz rm -f $(DISTNAME) diff --git a/NEWS b/NEWS index fee658c..27003a0 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,21 @@ -Changes in version 1.13: +Changes in version 1.14: -Decompression time has been reduced by 5-12% depending on the file. +The option '--empty-error', which forces exit status 2 if any empty member +is found, has been added. -In case of error in a numerical argument to a command line option, lunzip -now shows the name of the option and the range of valid values. +The option '--marking-error', which forces exit status 2 if the first LZMA +byte is non-zero in any member, has been added. + +File diagnostics have been reformatted as 'PROGRAM: FILE: MESSAGE'. + +Diagnostics caused by invalid arguments to command-line options now show the +argument and the name of the option. + +The option '-o, --output' now creates missing intermediate directories when +writing to a file. + +The variable MAKEINFO has been added to configure and Makefile.in. + +It has been documented in INSTALL that when choosing a C standard, the POSIX +features need to be enabled explicitly: + ./configure CFLAGS+='--std=c99 -D_XOPEN_SOURCE=500' diff --git a/README b/README index 39cd00d..90da7af 100644 --- a/README +++ b/README @@ -61,25 +61,25 @@ filename.tlz becomes filename.tar anyothername becomes anyothername.out Decompressing a file is much like copying or moving it. Therefore lunzip -preserves the access and modification dates, permissions, and, when -possible, ownership of the file just as 'cp -p' does. (If the user ID or -the group ID can't be duplicated, the file permission bits S_ISUID and -S_ISGID are cleared). +preserves the access and modification dates, permissions, and, if you have +appropriate privileges, ownership of the file just as 'cp -p' does. (If the +user ID or the group ID can't be duplicated, the file permission bits +S_ISUID and S_ISGID are cleared). Lunzip is able to read from some types of non-regular files if either the option '-c' or the option '-o' is specified. If no file names are specified, lunzip decompresses from standard input to -standard output. In this case, lunzip will refuse to read compressed input -from a terminal, as this might leave the terminal in an abnormal state. +standard output. In this case, lunzip refuses to read compressed input from +a terminal, as this might leave the terminal in an abnormal state. -Lunzip will correctly decompress a file which is the concatenation of two or +Lunzip correctly decompresses a file which is the concatenation of two or more compressed files. The result is the concatenation of the corresponding decompressed files. Integrity testing of concatenated compressed files is also supported. The ideas embodied in lunzip are due to (at least) the following people: -Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for the +Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrei Markov (for the definition of Markov chains), G.N.N. Martin (for the definition of range encoding), Igor Pavlov (for putting all the above together in LZMA), and Julian Seward (for bzip2's CLI). @@ -89,7 +89,7 @@ been compressed. Decompressed is used to refer to data which have undergone the process of decompression. -Copyright (C) 2010-2022 Antonio Diaz Diaz. +Copyright (C) 2010-2023 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute, and modify it. diff --git a/carg_parser.c b/carg_parser.c index 181ba23..8882b97 100644 --- a/carg_parser.c +++ b/carg_parser.c @@ -1,5 +1,5 @@ -/* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2022 Antonio Diaz Diaz. +/* Arg_parser - POSIX/GNU command-line argument parser. (C version) + Copyright (C) 2006-2023 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/carg_parser.h b/carg_parser.h index 0c64861..ec5d3d0 100644 --- a/carg_parser.h +++ b/carg_parser.h @@ -1,5 +1,5 @@ -/* Arg_parser - POSIX/GNU command line argument parser. (C version) - Copyright (C) 2006-2022 Antonio Diaz Diaz. +/* Arg_parser - POSIX/GNU command-line argument parser. (C version) + Copyright (C) 2006-2023 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/configure b/configure index e241235..ad36847 100755 --- a/configure +++ b/configure @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2022 Antonio Diaz Diaz. +# Copyright (C) 2010-2023 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission # to copy, distribute, and modify it. pkgname=lunzip -pkgversion=1.13 +pkgversion=1.14-rc1 progname=lunzip srctrigger=doc/${progname}.1 @@ -24,6 +24,7 @@ CC=gcc CPPFLAGS= CFLAGS='-Wall -W -O2' LDFLAGS= +MAKEINFO=makeinfo # checking whether we are using GNU C. /bin/sh -c "${CC} --version" > /dev/null 2>&1 || { CC=cc ; CFLAGS=-O2 ; } @@ -57,7 +58,7 @@ while [ $# != 0 ] ; do echo "Options and variables: [defaults in brackets]" echo " -h, --help display this help and exit" echo " -V, --version output version information and exit" - echo " --srcdir=DIR find the sources in DIR [. or ..]" + echo " --srcdir=DIR find the source code in DIR [. or ..]" echo " --prefix=DIR install into DIR [${prefix}]" echo " --exec-prefix=DIR base directory for arch-dependent files [${exec_prefix}]" echo " --bindir=DIR user executables directory [${bindir}]" @@ -65,10 +66,11 @@ while [ $# != 0 ] ; do echo " --infodir=DIR info files directory [${infodir}]" echo " --mandir=DIR man pages directory [${mandir}]" echo " CC=COMPILER C compiler to use [${CC}]" - echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]" - echo " CFLAGS=OPTIONS command line options for the C compiler [${CFLAGS}]" + echo " CPPFLAGS=OPTIONS command-line options for the preprocessor [${CPPFLAGS}]" + echo " CFLAGS=OPTIONS command-line options for the C compiler [${CFLAGS}]" echo " CFLAGS+=OPTIONS append options to the current value of CFLAGS" - echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]" + echo " LDFLAGS=OPTIONS command-line options for the linker [${LDFLAGS}]" + echo " MAKEINFO=NAME makeinfo program to use [${MAKEINFO}]" echo exit 0 ;; --version | -V) @@ -96,6 +98,7 @@ while [ $# != 0 ] ; do CFLAGS=*) CFLAGS=${optarg} ;; CFLAGS+=*) CFLAGS="${CFLAGS} ${optarg}" ;; LDFLAGS=*) LDFLAGS=${optarg} ;; + MAKEINFO=*) MAKEINFO=${optarg} ;; --*) echo "configure: WARNING: unrecognized option: '${option}'" 1>&2 ;; @@ -115,7 +118,7 @@ while [ $# != 0 ] ; do fi done -# Find the source files, if location was not specified. +# Find the source code, if location was not specified. srcdirtext= if [ -z "${srcdir}" ] ; then srcdirtext="or . or .." ; srcdir=. @@ -127,7 +130,7 @@ if [ -z "${srcdir}" ] ; then fi if [ ! -r "${srcdir}/${srctrigger}" ] ; then - echo "configure: Can't find sources in ${srcdir} ${srcdirtext}" 1>&2 + echo "configure: Can't find source code in ${srcdir} ${srcdirtext}" 1>&2 echo "configure: (At least ${srctrigger} is missing)." 1>&2 exit 1 fi @@ -147,7 +150,7 @@ if [ -z "${no_create}" ] ; then # This script is free software: you have unlimited permission # to copy, distribute, and modify it. -exec /bin/sh $0 ${args} --no-create +exec /bin/sh "$0" ${args} --no-create EOF chmod +x config.status fi @@ -164,10 +167,11 @@ echo "CC = ${CC}" echo "CPPFLAGS = ${CPPFLAGS}" echo "CFLAGS = ${CFLAGS}" echo "LDFLAGS = ${LDFLAGS}" +echo "MAKEINFO = ${MAKEINFO}" rm -f Makefile cat > Makefile << EOF # Makefile for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2022 Antonio Diaz Diaz. +# Copyright (C) 2010-2023 Antonio Diaz Diaz. # This file was generated automatically by configure. Don't edit. # # This Makefile is free software: you have unlimited permission @@ -187,6 +191,7 @@ CC = ${CC} CPPFLAGS = ${CPPFLAGS} CFLAGS = ${CFLAGS} LDFLAGS = ${LDFLAGS} +MAKEINFO = ${MAKEINFO} EOF cat "${srcdir}/Makefile.in" >> Makefile diff --git a/decoder.c b/decoder.c index b52b35f..ab6a259 100644 --- a/decoder.c +++ b/decoder.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2022 Antonio Diaz Diaz. + Copyright (C) 2010-2023 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 @@ -111,24 +111,21 @@ void LZd_flush_data( struct LZ_decoder * const d ) } -static bool LZd_verify_trailer( struct LZ_decoder * const d, - struct Pretty_print * const pp ) +static int LZd_check_trailer( struct LZ_decoder * const d, + struct Pretty_print * const pp, + const bool ignore_empty ) { Lzip_trailer trailer; int size = Rd_read_data( d->rdec, trailer, Lt_size ); - const unsigned long long data_size = LZd_data_position( d ); - const unsigned long long member_size = Rd_member_position( d->rdec ); bool error = false; if( size < Lt_size ) { error = true; if( verbosity >= 0 ) - { - Pp_show_msg( pp, 0 ); - fprintf( stderr, "Trailer truncated at trailer position %d;" - " some checks may fail.\n", size ); - } + { Pp_show_msg( pp, 0 ); + fprintf( stderr, "Trailer truncated at trailer position %d;" + " some checks may fail.\n", size ); } while( size < Lt_size ) trailer[size++] = 0; } @@ -137,35 +134,32 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, { error = true; if( verbosity >= 0 ) - { - Pp_show_msg( pp, 0 ); - fprintf( stderr, "CRC mismatch; stored %08X, computed %08X\n", - td_crc, LZd_crc( d ) ); - } + { Pp_show_msg( pp, 0 ); + fprintf( stderr, "CRC mismatch; stored %08X, computed %08X\n", + td_crc, LZd_crc( d ) ); } } + const unsigned long long data_size = LZd_data_position( d ); const unsigned long long td_size = Lt_get_data_size( trailer ); if( td_size != data_size ) { error = true; if( verbosity >= 0 ) - { - Pp_show_msg( pp, 0 ); - fprintf( stderr, "Data size mismatch; stored %llu (0x%llX), computed %llu (0x%llX)\n", - td_size, td_size, data_size, data_size ); - } + { Pp_show_msg( pp, 0 ); + fprintf( stderr, "Data size mismatch; stored %llu (0x%llX), computed %llu (0x%llX)\n", + td_size, td_size, data_size, data_size ); } } + const unsigned long long member_size = Rd_member_position( d->rdec ); const unsigned long long tm_size = Lt_get_member_size( trailer ); if( tm_size != member_size ) { error = true; if( verbosity >= 0 ) - { - Pp_show_msg( pp, 0 ); - fprintf( stderr, "Member size mismatch; stored %llu (0x%llX), computed %llu (0x%llX)\n", - tm_size, tm_size, member_size, member_size ); - } + { Pp_show_msg( pp, 0 ); + fprintf( stderr, "Member size mismatch; stored %llu (0x%llX), computed %llu (0x%llX)\n", + tm_size, tm_size, member_size, member_size ); } } - if( error ) return false; + if( error ) return 3; + if( !ignore_empty && data_size == 0 ) return 5; if( verbosity >= 2 ) { if( verbosity >= 4 ) show_header( d->dictionary_size ); @@ -180,13 +174,15 @@ static bool LZd_verify_trailer( struct LZ_decoder * const d, if( verbosity >= 3 ) fprintf( stderr, "%9llu out, %8llu in. ", data_size, member_size ); } - return true; + return 0; } /* Return value: 0 = OK, 1 = decoder error, 2 = unexpected EOF, - 3 = trailer error, 4 = unknown marker found. */ + 3 = trailer error, 4 = unknown marker found, + 5 = empty member found, 6 = marked member found. */ int LZd_decode_member( struct LZ_decoder * const d, + const struct Cl_options * const cl_opts, struct Pretty_print * const pp ) { struct Range_decoder * const rdec = d->rdec; @@ -222,7 +218,7 @@ int LZd_decode_member( struct LZ_decoder * const d, Lm_init( &match_len_model ); Lm_init( &rep_len_model ); - Rd_load( rdec ); + if( !Rd_load( rdec, cl_opts->ignore_marking ) ) return 6; while( !Rd_finished( rdec ) ) { const int pos_state = LZd_data_position( d ) & pos_state_mask; @@ -287,13 +283,9 @@ int LZd_decode_member( struct LZ_decoder * const d, Rd_normalize( rdec ); LZd_flush_data( d ); if( len == min_match_len ) /* End Of Stream marker */ - { - if( LZd_verify_trailer( d, pp ) ) return 0; else return 3; - } + return LZd_check_trailer( d, pp, cl_opts->ignore_empty ); if( len == min_match_len + 1 ) /* Sync Flush marker */ - { - Rd_load( rdec ); continue; - } + { Rd_load( rdec, true ); continue; } if( verbosity >= 0 ) { Pp_show_msg( pp, 0 ); diff --git a/decoder.h b/decoder.h index 0afdd83..0651cc2 100644 --- a/decoder.h +++ b/decoder.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2022 Antonio Diaz Diaz. + Copyright (C) 2010-2023 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 @@ -79,13 +79,16 @@ static inline int Rd_read_data( struct Range_decoder * const rdec, return sz; } -static inline void Rd_load( struct Range_decoder * const rdec ) +static inline bool Rd_load( struct Range_decoder * const rdec, + const bool ignore_marking ) { int i; rdec->code = 0; - for( i = 0; i < 5; ++i ) rdec->code = (rdec->code << 8) | Rd_get_byte( rdec ); rdec->range = 0xFFFFFFFFU; - rdec->code &= rdec->range; /* make sure that first byte is discarded */ + /* check and discard first byte of the LZMA stream */ + if( Rd_get_byte( rdec ) != 0 && !ignore_marking ) return false; + for( i = 0; i < 4; ++i ) rdec->code = (rdec->code << 8) | Rd_get_byte( rdec ); + return true; } static inline void Rd_normalize( struct Range_decoder * const rdec ) @@ -390,4 +393,5 @@ LZd_data_position( const struct LZ_decoder * const d ) { return d->partial_data_pos + d->pos; } int LZd_decode_member( struct LZ_decoder * const d, + const struct Cl_options * const cl_opts, struct Pretty_print * const pp ); diff --git a/doc/lunzip.1 b/doc/lunzip.1 index 9aa0300..3b9fb99 100644 --- a/doc/lunzip.1 +++ b/doc/lunzip.1 @@ -1,5 +1,5 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16. -.TH LUNZIP "1" "January 2022" "lunzip 1.13" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. +.TH LUNZIP "1" "December 2023" "lunzip 1.14-rc1" "User Commands" .SH NAME lunzip \- decompressor for the lzip format .SH SYNOPSIS @@ -62,6 +62,12 @@ set output buffer size in bytes \fB\-v\fR, \fB\-\-verbose\fR be verbose (a 2nd \fB\-v\fR gives more) .TP +\fB\-\-empty\-error\fR +exit with error status if empty member in file +.TP +\fB\-\-marking\-error\fR +exit with error status if 1st LZMA byte not 0 +.TP \fB\-\-loose\-trailing\fR allow trailing data seeming corrupt header .PP @@ -69,19 +75,19 @@ If no file names are given, or if a file is '\-', lunzip decompresses from standard input to standard output. Numbers may be followed by a multiplier: k = kB = 10^3 = 1000, Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc... -Buffer sizes 12 to 29 are interpreted as powers of two, meaning 2^12 -to 2^29 bytes. +Buffer sizes 12 to 29 are interpreted as powers of two, meaning 2^12 to +2^29 bytes. .PP To extract all the files from archive 'foo.tar.lz', use the commands \&'tar \fB\-xf\fR foo.tar.lz' or 'lunzip \fB\-cd\fR foo.tar.lz | tar \fB\-xf\fR \-'. .PP -Exit status: 0 for a normal exit, 1 for environmental problems (file -not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or -invalid input file, 3 for an internal consistency error (e.g., bug) which -caused lunzip to panic. +Exit status: 0 for a normal exit, 1 for environmental problems +(file not found, invalid command\-line options, I/O errors, etc), 2 to +indicate a corrupt or invalid input file, 3 for an internal consistency +error (e.g., bug) which caused lunzip to panic. .PP The ideas embodied in lunzip are due to (at least) the following people: -Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for the +Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrei Markov (for the definition of Markov chains), G.N.N. Martin (for the definition of range encoding), Igor Pavlov (for putting all the above together in LZMA), and Julian Seward (for bzip2's CLI). @@ -90,7 +96,7 @@ Report bugs to lzip\-bug@nongnu.org .br Lunzip home page: http://www.nongnu.org/lzip/lunzip.html .SH COPYRIGHT -Copyright \(co 2022 Antonio Diaz Diaz. +Copyright \(co 2023 Antonio Diaz Diaz. License GPLv2+: GNU GPL version 2 or later .br This is free software: you are free to change and redistribute it. diff --git a/list.c b/list.c index 33de75c..3d4b242 100644 --- a/list.c +++ b/list.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2022 Antonio Diaz Diaz. + Copyright (C) 2010-2023 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 @@ -43,13 +43,14 @@ static void list_line( const unsigned long long uncomp_size, int list_files( const char * const filenames[], const int num_filenames, - const bool ignore_trailing, const bool loose_trailing ) + const struct Cl_options * const cl_opts ) { unsigned long long total_comp = 0, total_uncomp = 0; int files = 0, retval = 0; int i; bool first_post = true; bool stdin_used = false; + for( i = 0; i < num_filenames; ++i ) { const bool from_stdin = ( strcmp( filenames[i], "-" ) == 0 ); @@ -61,7 +62,7 @@ int list_files( const char * const filenames[], const int num_filenames, if( infd < 0 ) { set_retval( &retval, 1 ); continue; } struct Lzip_index lzip_index; - Li_init( &lzip_index, infd, ignore_trailing, loose_trailing ); + Li_init( &lzip_index, infd, cl_opts ); close( infd ); if( lzip_index.retval != 0 ) { diff --git a/lzip.h b/lzip.h index 4b77be8..22c75e5 100644 --- a/lzip.h +++ b/lzip.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2022 Antonio Diaz Diaz. + Copyright (C) 2010-2023 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 @@ -25,7 +25,6 @@ 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 ) @@ -33,15 +32,12 @@ 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 ); } - + { return ( st < 7 ) ? 7 : 10; } static inline State St_set_rep( const State st ) - { return ( ( st < 7 ) ? 8 : 11 ); } - + { return ( st < 7 ) ? 8 : 11; } static inline State St_set_short_rep( const State st ) - { return ( ( st < 7 ) ? 9 : 11 ); } + { return ( st < 7 ) ? 9 : 11; } enum { @@ -60,7 +56,7 @@ enum { dis_slot_bits = 6, start_dis_model = 4, end_dis_model = 14, - modeled_distances = 1 << (end_dis_model / 2), /* 128 */ + modeled_distances = 1 << ( end_dis_model / 2 ), /* 128 */ dis_align_bits = 4, dis_align_size = 1 << dis_align_bits, @@ -145,63 +141,63 @@ static inline void CRC32_update_buf( uint32_t * const crc, static inline bool isvalid_ds( const unsigned dictionary_size ) - { return ( dictionary_size >= min_dictionary_size && - dictionary_size <= max_dictionary_size ); } + { return dictionary_size >= min_dictionary_size && + dictionary_size <= max_dictionary_size; } static const uint8_t lzip_magic[4] = { 0x4C, 0x5A, 0x49, 0x50 }; /* "LZIP" */ -typedef uint8_t Lzip_header[6]; /* 0-3 magic bytes */ +enum { Lh_size = 6 }; +typedef uint8_t Lzip_header[Lh_size]; /* 0-3 magic bytes */ /* 4 version */ /* 5 coded dictionary size */ -enum { Lh_size = 6 }; -static inline bool Lh_verify_magic( const Lzip_header data ) - { return ( memcmp( data, lzip_magic, 4 ) == 0 ); } +static inline bool Lh_check_magic( const Lzip_header data ) + { return memcmp( data, lzip_magic, 4 ) == 0; } /* detect (truncated) header */ -static inline bool Lh_verify_prefix( const Lzip_header data, const int sz ) +static inline bool Lh_check_prefix( const Lzip_header data, const int sz ) { int i; for( i = 0; i < sz && i < 4; ++i ) if( data[i] != lzip_magic[i] ) return false; - return ( sz > 0 ); + return sz > 0; } /* detect corrupt header */ -static inline bool Lh_verify_corrupt( const Lzip_header data ) +static inline bool Lh_check_corrupt( const Lzip_header data ) { int matches = 0; int i; for( i = 0; i < 4; ++i ) if( data[i] == lzip_magic[i] ) ++matches; - return ( matches > 1 && matches < 4 ); + return matches > 1 && matches < 4; } static inline uint8_t Lh_version( const Lzip_header data ) { return data[4]; } -static inline bool Lh_verify_version( const Lzip_header data ) - { return ( data[4] == 1 ); } +static inline bool Lh_check_version( const Lzip_header data ) + { return data[4] == 1; } static inline unsigned Lh_get_dictionary_size( const Lzip_header data ) { - unsigned sz = ( 1 << ( data[5] & 0x1F ) ); + unsigned sz = 1 << ( data[5] & 0x1F ); if( sz > min_dictionary_size ) sz -= ( sz / 16 ) * ( ( data[5] >> 5 ) & 7 ); return sz; } -static inline bool Lh_verify( const Lzip_header data ) +static inline bool Lh_check( const Lzip_header data ) { - return Lh_verify_magic( data ) && Lh_verify_version( data ) && + return Lh_check_magic( data ) && Lh_check_version( data ) && isvalid_ds( Lh_get_dictionary_size( data ) ); } -typedef uint8_t Lzip_trailer[20]; +enum { Lt_size = 20 }; +typedef uint8_t Lzip_trailer[Lt_size]; /* 0-3 CRC32 of the uncompressed data */ /* 4-11 size of the uncompressed data */ /* 12-19 member size including header and trailer */ -enum { Lt_size = 20 }; static inline unsigned Lt_get_data_crc( const Lzip_trailer data ) { @@ -225,7 +221,7 @@ static inline unsigned long long Lt_get_member_size( const Lzip_trailer data ) } /* check internal consistency */ -static inline bool Lt_verify_consistency( const Lzip_trailer data ) +static inline bool Lt_check_consistency( const Lzip_trailer data ) { const unsigned crc = Lt_get_data_crc( data ); const unsigned long long dsize = Lt_get_data_size( data ); @@ -240,12 +236,27 @@ static inline bool Lt_verify_consistency( const Lzip_trailer data ) } +struct Cl_options /* command-line options */ + { + bool ignore_empty; + bool ignore_marking; + bool ignore_trailing; + bool loose_trailing; + }; + +static inline void Cl_options_init( struct Cl_options * cl_opts ) + { cl_opts->ignore_empty = true; cl_opts->ignore_marking = true; + cl_opts->ignore_trailing = true; cl_opts->loose_trailing = false; } + + static inline void set_retval( int * retval, const int new_val ) { if( *retval < new_val ) *retval = new_val; } static const char * const bad_magic_msg = "Bad magic number (file not in lzip format)."; static const char * const bad_dict_msg = "Invalid dictionary size in member header."; static const char * const corrupt_mm_msg = "Corrupt header in multimember file."; +static const char * const empty_msg = "Empty member not allowed."; +static const char * const marking_msg = "Marking data not allowed."; static const char * const trailing_msg = "Trailing data not allowed."; static const char * const mem_msg = "Not enough memory."; @@ -254,7 +265,7 @@ int readblock( const int fd, uint8_t * const buf, const int size ); /* defined in list.c */ int list_files( const char * const filenames[], const int num_filenames, - const bool ignore_trailing, const bool loose_trailing ); + const struct Cl_options * const cl_opts ); /* defined in main.c */ struct stat; diff --git a/lzip_index.c b/lzip_index.c index 559fd7a..688a370 100644 --- a/lzip_index.c +++ b/lzip_index.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2022 Antonio Diaz Diaz. + Copyright (C) 2010-2023 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 @@ -88,17 +88,17 @@ static void Li_reverse_member_vector( struct Lzip_index * const li ) } -static bool Li_check_header_error( struct Lzip_index * const li, - const Lzip_header header ) +static bool Li_check_header( struct Lzip_index * const li, + const Lzip_header header ) { - if( !Lh_verify_magic( header ) ) - { add_error( li, bad_magic_msg ); li->retval = 2; return true; } - if( !Lh_verify_version( header ) ) + if( !Lh_check_magic( header ) ) + { add_error( li, bad_magic_msg ); li->retval = 2; return false; } + if( !Lh_check_version( header ) ) { add_error( li, bad_version( Lh_version( header ) ) ); li->retval = 2; - return true; } + return false; } if( !isvalid_ds( Lh_get_dictionary_size( header ) ) ) - { add_error( li, bad_dict_msg ); li->retval = 2; return true; } - return false; + { add_error( li, bad_dict_msg ); li->retval = 2; return false; } + return true; } static void Li_set_errno_error( struct Lzip_index * const li, @@ -119,10 +119,13 @@ static void Li_set_num_error( struct Lzip_index * const li, static bool Li_read_header( struct Lzip_index * const li, const int fd, - Lzip_header header, const long long pos ) + Lzip_header header, const long long pos, const bool ignore_marking ) { if( seek_read( fd, header, Lh_size, pos ) != Lh_size ) { Li_set_errno_error( li, "Error reading member header: " ); return false; } + uint8_t byte; + if( !ignore_marking && readblock( fd, &byte, 1 ) == 1 && byte != 0 ) + { add_error( li, marking_msg ); li->retval = 2; return false; } return true; } @@ -130,8 +133,7 @@ static bool Li_read_header( struct Lzip_index * const li, const int fd, /* If successful, push last member and set pos to member header. */ static bool Li_skip_trailing_data( struct Lzip_index * const li, const int fd, unsigned long long * const pos, - const bool ignore_trailing, - const bool loose_trailing ) + const struct Cl_options * const cl_opts ) { if( *pos < min_member_size ) return false; enum { block_size = 16384, @@ -157,31 +159,34 @@ static bool Li_skip_trailing_data( struct Lzip_index * const li, const int fd, const unsigned long long member_size = Lt_get_member_size( *trailer ); if( member_size == 0 ) /* skip trailing zeros */ { while( i > Lt_size && buffer[i-9] == 0 ) --i; continue; } - if( member_size > ipos + i || !Lt_verify_consistency( *trailer ) ) + if( member_size > ipos + i || !Lt_check_consistency( *trailer ) ) continue; Lzip_header header; - if( !Li_read_header( li, fd, header, ipos + i - member_size ) ) - return false; - if( !Lh_verify( header ) ) continue; + if( !Li_read_header( li, fd, header, ipos + i - member_size, + cl_opts->ignore_marking ) ) return false; + if( !Lh_check( header ) ) continue; const Lzip_header * header2 = (const Lzip_header *)( buffer + i ); const bool full_h2 = bsize - i >= Lh_size; - if( Lh_verify_prefix( *header2, bsize - i ) ) /* last member */ + if( Lh_check_prefix( *header2, bsize - i ) ) /* last member */ { if( !full_h2 ) add_error( li, "Last member in input file is truncated." ); - else if( !Li_check_header_error( li, *header2 ) ) + else if( Li_check_header( li, *header2 ) ) add_error( li, "Last member in input file is truncated or corrupt." ); li->retval = 2; return false; } - if( !loose_trailing && full_h2 && Lh_verify_corrupt( *header2 ) ) + if( !cl_opts->loose_trailing && full_h2 && Lh_check_corrupt( *header2 ) ) { add_error( li, corrupt_mm_msg ); li->retval = 2; return false; } - if( !ignore_trailing ) + if( !cl_opts->ignore_trailing ) { add_error( li, trailing_msg ); li->retval = 2; return false; } - *pos = ipos + i - member_size; + const unsigned long long data_size = Lt_get_data_size( *trailer ); + if( !cl_opts->ignore_empty && data_size == 0 ) + { add_error( li, empty_msg ); li->retval = 2; return false; } + *pos = ipos + i - member_size; /* good member */ const unsigned dictionary_size = Lh_get_dictionary_size( header ); if( li->dictionary_size < dictionary_size ) li->dictionary_size = dictionary_size; - return push_back_member( li, 0, Lt_get_data_size( *trailer ), *pos, - member_size, dictionary_size ); + return push_back_member( li, 0, data_size, *pos, member_size, + dictionary_size ); } if( ipos == 0 ) { Li_set_num_error( li, "Bad trailer at pos ", *pos - Lt_size ); @@ -196,7 +201,7 @@ static bool Li_skip_trailing_data( struct Lzip_index * const li, const int fd, bool Li_init( struct Lzip_index * const li, const int infd, - const bool ignore_trailing, const bool loose_trailing ) + const struct Cl_options * const cl_opts ) { li->member_vector = 0; li->error = 0; @@ -215,8 +220,8 @@ bool Li_init( struct Lzip_index * const li, const int infd, li->retval = 2; return false; } Lzip_header header; - if( !Li_read_header( li, infd, header, 0 ) ) return false; - if( Li_check_header_error( li, header ) ) return false; + if( !Li_read_header( li, infd, header, 0, cl_opts->ignore_marking ) || + !Li_check_header( li, header ) ) return false; unsigned long long pos = li->insize; /* always points to a header or to EOF */ while( pos >= min_member_size ) @@ -225,32 +230,33 @@ bool Li_init( struct Lzip_index * const li, const int infd, if( seek_read( infd, trailer, Lt_size, pos - Lt_size ) != Lt_size ) { Li_set_errno_error( li, "Error reading member trailer: " ); break; } const unsigned long long member_size = Lt_get_member_size( trailer ); - if( member_size > pos || !Lt_verify_consistency( trailer ) ) + if( member_size > pos || !Lt_check_consistency( trailer ) ) { /* bad trailer */ if( li->members <= 0 ) - { if( Li_skip_trailing_data( li, infd, &pos, ignore_trailing, - loose_trailing ) ) continue; else return false; } - Li_set_num_error( li, "Bad trailer at pos ", pos - Lt_size ); - break; + { if( Li_skip_trailing_data( li, infd, &pos, cl_opts ) ) continue; + return false; } + Li_set_num_error( li, "Bad trailer at pos ", pos - Lt_size ); break; } - if( !Li_read_header( li, infd, header, pos - member_size ) ) break; - if( !Lh_verify( header ) ) /* bad header */ + if( !Li_read_header( li, infd, header, pos - member_size, + cl_opts->ignore_marking ) ) break; + if( !Lh_check( header ) ) /* bad header */ { if( li->members <= 0 ) - { if( Li_skip_trailing_data( li, infd, &pos, ignore_trailing, - loose_trailing ) ) continue; else return false; } - Li_set_num_error( li, "Bad header at pos ", pos - member_size ); - break; + { if( Li_skip_trailing_data( li, infd, &pos, cl_opts ) ) continue; + return false; } + Li_set_num_error( li, "Bad header at pos ", pos - member_size ); break; } - pos -= member_size; + const unsigned long long data_size = Lt_get_data_size( trailer ); + if( !cl_opts->ignore_empty && data_size == 0 ) + { add_error( li, empty_msg ); li->retval = 2; break; } + pos -= member_size; /* good member */ const unsigned dictionary_size = Lh_get_dictionary_size( header ); if( li->dictionary_size < dictionary_size ) li->dictionary_size = dictionary_size; - if( !push_back_member( li, 0, Lt_get_data_size( trailer ), pos, - member_size, dictionary_size ) ) - return false; + if( !push_back_member( li, 0, data_size, pos, member_size, + dictionary_size ) ) return false; } - if( pos != 0 || li->members <= 0 ) + if( pos != 0 || li->members <= 0 || li->retval != 0 ) { Li_free_member_vector( li ); if( li->retval == 0 ) diff --git a/lzip_index.h b/lzip_index.h index 0938533..df2aeca 100644 --- a/lzip_index.h +++ b/lzip_index.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2022 Antonio Diaz Diaz. + Copyright (C) 2010-2023 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 @@ -16,13 +16,13 @@ */ #ifndef INT64_MAX -#define INT64_MAX 0x7FFFFFFFFFFFFFFFLL +#define INT64_MAX 0x7FFFFFFFFFFFFFFFLL #endif struct Block { - long long pos, size; /* pos + size <= INT64_MAX */ + long long pos, size; /* pos >= 0, size >= 0, pos + size <= INT64_MAX */ }; static inline void init_block( struct Block * const b, @@ -40,10 +40,10 @@ struct Member }; static inline void init_member( struct Member * const m, - const long long dp, const long long ds, - const long long mp, const long long ms, + const long long dpos, const long long dsize, + const long long mpos, const long long msize, const unsigned dict_size ) - { init_block( &m->dblock, dp, ds ); init_block( &m->mblock, mp, ms ); + { init_block( &m->dblock, dpos, dsize ); init_block( &m->mblock, mpos, msize ); m->dictionary_size = dict_size; } struct Lzip_index @@ -58,7 +58,7 @@ struct Lzip_index }; bool Li_init( struct Lzip_index * const li, const int infd, - const bool ignore_trailing, const bool loose_trailing ); + const struct Cl_options * const cl_opts ); void Li_free( struct Lzip_index * const li ); diff --git a/main.c b/main.c index 73e29b4..6949e1d 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2022 Antonio Diaz Diaz. + Copyright (C) 2010-2023 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 @@ -16,9 +16,9 @@ */ /* Exit status: 0 for a normal exit, 1 for environmental problems - (file not found, invalid flags, I/O errors, etc), 2 to indicate a - corrupt or invalid input file, 3 for an internal consistency error - (e.g., bug) which caused lunzip to panic. + (file not found, invalid command-line options, I/O errors, etc), 2 to + indicate a corrupt or invalid input file, 3 for an internal consistency + error (e.g., bug) which caused lunzip to panic. */ #define _FILE_OFFSET_BITS 64 @@ -26,10 +26,10 @@ #include #include #include -#include +#include /* SSIZE_MAX */ #include #include -#include +#include /* SIZE_MAX */ #include #include #include @@ -76,7 +76,7 @@ int verbosity = 0; static const char * const program_name = "lunzip"; -static const char * const program_year = "2022"; +static const char * const program_year = "2023"; static const char * invocation_name = "lunzip"; /* default value */ static const struct { const char * from; const char * to; } known_extensions[] = { @@ -124,21 +124,23 @@ static void show_help( void ) " -t, --test test compressed file integrity\n" " -u, --buffer-size= set output buffer size in bytes\n" " -v, --verbose be verbose (a 2nd -v gives more)\n" + " --empty-error exit with error status if empty member in file\n" + " --marking-error exit with error status if 1st LZMA byte not 0\n" " --loose-trailing allow trailing data seeming corrupt header\n" "\nIf no file names are given, or if a file is '-', lunzip decompresses\n" "from standard input to standard output.\n" "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" - "Buffer sizes 12 to 29 are interpreted as powers of two, meaning 2^12\n" - "to 2^29 bytes.\n" + "Buffer sizes 12 to 29 are interpreted as powers of two, meaning 2^12 to\n" + "2^29 bytes.\n" "\nTo extract all the files from archive 'foo.tar.lz', use the commands\n" "'tar -xf foo.tar.lz' or 'lunzip -cd foo.tar.lz | tar -xf -'.\n" - "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n" - "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n" - "invalid input file, 3 for an internal consistency error (e.g., bug) which\n" - "caused lunzip to panic.\n" + "\nExit status: 0 for a normal exit, 1 for environmental problems\n" + "(file not found, invalid command-line options, I/O errors, etc), 2 to\n" + "indicate a corrupt or invalid input file, 3 for an internal consistency\n" + "error (e.g., bug) which caused lunzip to panic.\n" "\nThe ideas embodied in lunzip are due to (at least) the following people:\n" - "Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrey Markov (for the\n" + "Abraham Lempel and Jacob Ziv (for the LZ algorithm), Andrei Markov (for the\n" "definition of Markov chains), G.N.N. Martin (for the definition of range\n" "encoding), Igor Pavlov (for putting all the above together in LZMA), and\n" "Julian Seward (for bzip2's CLI).\n" @@ -243,16 +245,15 @@ const char * bad_version( const unsigned version ) const char * format_ds( const unsigned dictionary_size ) { - enum { bufsize = 16, factor = 1024 }; + enum { bufsize = 16, factor = 1024, n = 3 }; static char buf[bufsize]; - const char * const prefix[8] = - { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; + const char * const prefix[n] = { "Ki", "Mi", "Gi" }; const char * p = ""; const char * np = " "; unsigned num = dictionary_size; bool exact = ( num % factor == 0 ); - int i; for( i = 0; i < 8 && ( num > 9999 || ( exact && num >= factor ) ); ++i ) + int i; for( i = 0; i < n && ( num > 9999 || ( exact && num >= factor ) ); ++i ) { num /= factor; if( num % factor != 0 ) exact = false; p = prefix[i]; np = ""; } snprintf( buf, bufsize, "%s%4u %sB", np, num, p ); @@ -266,12 +267,12 @@ void show_header( const unsigned dictionary_size ) } -/* separate large numbers >= 100_000 in groups of 3 digits using '_' */ +/* separate numbers of 5 or more digits in groups of 3 digits using '_' */ static const char * format_num3( unsigned long long num ) { - const char * const si_prefix = "kMGTPEZY"; - const char * const binary_prefix = "KMGTPEZY"; - enum { buffers = 8, bufsize = 4 * sizeof (long long) }; + enum { buffers = 8, bufsize = 4 * sizeof num, n = 10 }; + const char * const si_prefix = "kMGTPEZYRQ"; + const char * const binary_prefix = "KMGTPEZYRQ"; static char buffer[buffers][bufsize]; /* circle of static buffers for printf */ static int current = 0; int i; @@ -281,15 +282,15 @@ static const char * format_num3( unsigned long long num ) if( num > 1024 ) { char prefix = 0; /* try binary first, then si */ - for( i = 0; i < 8 && num >= 1024 && num % 1024 == 0; ++i ) + for( i = 0; i < n && num != 0 && num % 1024 == 0; ++i ) { num /= 1024; prefix = binary_prefix[i]; } if( prefix ) *(--p) = 'i'; else - for( i = 0; i < 8 && num >= 1000 && num % 1000 == 0; ++i ) + for( i = 0; i < n && num != 0 && num % 1000 == 0; ++i ) { num /= 1000; prefix = si_prefix[i]; } if( prefix ) *(--p) = prefix; } - const bool split = num >= 100000; + const bool split = num >= 10000; for( i = 0; ; ) { @@ -300,6 +301,16 @@ static const char * format_num3( unsigned long long num ) } +void show_option_error( const char * const arg, const char * const msg, + const char * const option_name ) + { + if( verbosity >= 0 ) + fprintf( stderr, "%s: '%s': %s option '%s'.\n", + program_name, arg, msg, option_name ); + } + + +/* Recognized formats: k, Ki, [MGTPEZYRQ][i] */ static unsigned long getnum( const char * const arg, const char * const option_name, const unsigned long llimit, @@ -307,14 +318,10 @@ static unsigned long getnum( const char * const arg, { char * tail; errno = 0; - unsigned long long result = strtoul( arg, &tail, 0 ); + unsigned long result = strtoul( arg, &tail, 0 ); if( tail == arg ) - { - if( verbosity >= 0 ) - fprintf( stderr, "%s: Bad or missing numerical argument in " - "option '%s'.\n", program_name, option_name ); - exit( 1 ); - } + { show_option_error( arg, "Bad or missing numerical argument in", + option_name ); exit( 1 ); } if( !errno && tail[0] ) { @@ -323,6 +330,8 @@ static unsigned long getnum( const char * const arg, int i; switch( tail[0] ) { + case 'Q': exponent = 10; break; + case 'R': exponent = 9; break; case 'Y': exponent = 8; break; case 'Z': exponent = 7; break; case 'E': exponent = 6; break; @@ -334,12 +343,8 @@ static unsigned long getnum( const char * const arg, case 'k': if( factor == 1000 ) exponent = 1; break; } if( exponent <= 0 ) - { - if( verbosity >= 0 ) - fprintf( stderr, "%s: Bad multiplier in numerical argument of " - "option '%s'.\n", program_name, option_name ); - exit( 1 ); - } + { show_option_error( arg, "Bad multiplier in numerical argument of", + option_name ); exit( 1 ); } for( i = 0; i < exponent; ++i ) { if( ulimit / factor >= result ) result *= factor; @@ -350,8 +355,8 @@ static unsigned long getnum( const char * const arg, if( errno ) { if( verbosity >= 0 ) - fprintf( stderr, "%s: Numerical argument out of limits [%s,%s] " - "in option '%s'.\n", program_name, format_num3( llimit ), + fprintf( stderr, "%s: '%s': Value out of limits [%s,%s] in " + "option '%s'.\n", program_name, arg, format_num3( llimit ), format_num3( ulimit ), option_name ); exit( 1 ); } @@ -417,7 +422,7 @@ static void set_d_outname( const char * const name, const int eindex ) strcpy( output_filename, name ); strcat( output_filename, ".out" ); if( verbosity >= 1 ) - fprintf( stderr, "%s: Can't guess original name for '%s' -- using '%s'\n", + fprintf( stderr, "%s: %s: Can't guess original name -- using '%s'\n", program_name, name, output_filename ); } @@ -438,9 +443,9 @@ int open_instream( const char * const name, struct stat * const in_statsp, if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || one_to_one ) ) ) { if( verbosity >= 0 ) - fprintf( stderr, "%s: Input file '%s' is not a regular file%s.\n", + fprintf( stderr, "%s: %s: Input file is not a regular file%s.\n", program_name, name, ( can_read && one_to_one ) ? - ",\n and neither '-c' nor '-o' were specified" : "" ); + ",\n and neither '-c' nor '-o' were specified" : "" ); close( infd ); infd = -1; } @@ -449,6 +454,33 @@ int open_instream( const char * const name, struct stat * const in_statsp, } +static bool make_dirs( const char * const name ) + { + int i = strlen( name ); + while( i > 0 && name[i-1] != '/' ) --i; /* remove last component */ + while( i > 0 && name[i-1] == '/' ) --i; /* remove slash(es) */ + const int dirsize = i; /* size of dirname without trailing slash(es) */ + + for( i = 0; i < dirsize; ) /* if dirsize == 0, dirname is '/' or empty */ + { + while( i < dirsize && name[i] == '/' ) ++i; + const int first = i; + while( i < dirsize && name[i] != '/' ) ++i; + if( first < i ) + { + char partial[i+1]; memcpy( partial, name, i ); partial[i] = 0; + const mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + struct stat st; + if( stat( partial, &st ) == 0 ) + { if( !S_ISDIR( st.st_mode ) ) { errno = ENOTDIR; return false; } } + else if( mkdir( partial, mode ) != 0 && errno != EEXIST ) + return false; /* if EEXIST, another process created the dir */ + } + } + return true; + } + + static bool open_outstream( const bool force, const bool protect ) { const mode_t usr_rw = S_IRUSR | S_IWUSR; @@ -457,18 +489,21 @@ static bool open_outstream( const bool force, const bool protect ) int flags = O_APPEND | O_CREAT | O_RDWR | O_BINARY; if( force ) flags |= O_TRUNC; else flags |= O_EXCL; - outfd = open( output_filename, flags, outfd_mode ); - if( outfd >= 0 ) delete_output_on_interrupt = true; - else if( verbosity >= 0 ) - { + outfd = -1; + const int len = strlen( output_filename ); + if( len > 0 && output_filename[len-1] == '/' ) errno = EISDIR; + else { + if( !protect && !make_dirs( output_filename ) ) + { show_file_error( output_filename, + "Error creating intermediate directory", errno ); return false; } + outfd = open( output_filename, flags, outfd_mode ); + if( outfd >= 0 ) { delete_output_on_interrupt = true; return true; } if( errno == EEXIST ) - fprintf( stderr, "%s: Output file '%s' already exists, skipping.\n", - program_name, output_filename ); - else - fprintf( stderr, "%s: Can't create output file '%s': %s\n", - program_name, output_filename, strerror( errno ) ); + { show_file_error( output_filename, + "Output file already exists, skipping.", 0 ); return false; } } - return ( outfd >= 0 ); + show_file_error( output_filename, "Can't create output file", errno ); + return false; } @@ -486,12 +521,10 @@ void cleanup_and_fail( const int retval ) if( delete_output_on_interrupt ) { delete_output_on_interrupt = false; - if( verbosity >= 0 ) - fprintf( stderr, "%s: Deleting output file '%s', if it exists.\n", - program_name, output_filename ); + show_file_error( output_filename, "Deleting output file, if it exists.", 0 ); if( outfd >= 0 ) { close( outfd ); outfd = -1; } if( remove( output_filename ) != 0 && errno != ENOENT ) - show_error( "WARNING: deletion of output file (apparently) failed.", 0, false ); + show_error( "warning: deletion of output file failed", errno, false ); } exit( retval ); } @@ -534,10 +567,8 @@ static void close_and_set_permissions( const struct stat * const in_statsp ) warning = true; } if( close( outfd ) != 0 ) - { - show_error( "Error closing output file", errno, false ); - cleanup_and_fail( 1 ); - } + { show_file_error( output_filename, "Error closing output file", errno ); + cleanup_and_fail( 1 ); } outfd = -1; delete_output_on_interrupt = false; if( in_statsp ) @@ -548,11 +579,12 @@ static void close_and_set_permissions( const struct stat * const in_statsp ) if( utime( output_filename, &t ) != 0 ) warning = true; } if( warning && verbosity >= 1 ) - show_error( "Can't change output file attributes.", 0, false ); + show_file_error( output_filename, + "warning: can't change output file attributes", errno ); } -static unsigned char xdigit( const unsigned value ) +static unsigned char xdigit( const unsigned value ) /* hex digit for 'value' */ { if( value <= 9 ) return '0' + value; if( value <= 15 ) return 'A' + value - 10; @@ -584,14 +616,14 @@ static bool show_trailing_data( const uint8_t * const data, const int size, Pp_show_msg( pp, buf ); if( ignore_trailing == 0 ) show_file_error( pp->name, trailing_msg, 0 ); } - return ( ignore_trailing > 0 ); + return ignore_trailing > 0; } static int decompress( const unsigned long long cfile_size, const int infd, - struct Pretty_print * const pp, const unsigned buffer_size, - const bool ignore_trailing, const bool loose_trailing, - const bool testing ) + const struct Cl_options * const cl_opts, + struct Pretty_print * const pp, + const unsigned buffer_size, const bool testing ) { unsigned long long partial_file_pos = 0; struct Range_decoder rdec; @@ -610,28 +642,25 @@ static int decompress( const unsigned long long cfile_size, const int infd, if( first_member ) { show_file_error( pp->name, "File ends unexpectedly at member header.", 0 ); retval = 2; } - else if( Lh_verify_prefix( header, size ) ) + else if( Lh_check_prefix( header, size ) ) { Pp_show_msg( pp, "Truncated header in multimember file." ); - show_trailing_data( header, size, pp, true, -1 ); - retval = 2; } - else if( size > 0 && !show_trailing_data( header, size, pp, - true, ignore_trailing ) ) - retval = 2; + show_trailing_data( header, size, pp, true, -1 ); retval = 2; } + else if( size > 0 && !show_trailing_data( header, size, pp, true, + cl_opts->ignore_trailing ) ) retval = 2; break; } - if( !Lh_verify_magic( header ) ) + if( !Lh_check_magic( header ) ) { if( first_member ) { show_file_error( pp->name, bad_magic_msg, 0 ); retval = 2; } - else if( !loose_trailing && Lh_verify_corrupt( header ) ) + else if( !cl_opts->loose_trailing && Lh_check_corrupt( header ) ) { Pp_show_msg( pp, corrupt_mm_msg ); - show_trailing_data( header, size, pp, false, -1 ); - retval = 2; } - else if( !show_trailing_data( header, size, pp, false, ignore_trailing ) ) - retval = 2; + show_trailing_data( header, size, pp, false, -1 ); retval = 2; } + else if( !show_trailing_data( header, size, pp, false, + cl_opts->ignore_trailing ) ) retval = 2; break; } - if( !Lh_verify_version( header ) ) + if( !Lh_check_version( header ) ) { Pp_show_msg( pp, bad_version( Lh_version( header ) ) ); retval = 2; break; } const unsigned dictionary_size = Lh_get_dictionary_size( header ); @@ -648,7 +677,7 @@ static int decompress( const unsigned long long cfile_size, const int infd, retval = 1; break; } show_dprogress( cfile_size, partial_file_pos, &rdec, pp ); /* init */ - const int result = LZd_decode_member( &decoder, pp ); + const int result = LZd_decode_member( &decoder, cl_opts, pp ); partial_file_pos += Rd_member_position( &rdec ); LZd_free( &decoder ); if( result != 0 ) @@ -660,6 +689,8 @@ static int decompress( const unsigned long long cfile_size, const int infd, "File ends unexpectedly" : "Decoder error", partial_file_pos ); } + else if( result == 5 ) Pp_show_msg( pp, empty_msg ); + else if( result == 6 ) Pp_show_msg( pp, marking_msg ); retval = 2; break; } if( verbosity >= 2 ) @@ -740,32 +771,34 @@ int main( const int argc, const char * const argv[] ) unsigned buffer_size = max_dictionary_size; enum Mode program_mode = m_compress; int i; + struct Cl_options cl_opts; /* command-line options */ + Cl_options_init( &cl_opts ); bool force = false; - bool ignore_trailing = true; bool keep_input_files = false; - bool loose_trailing = false; bool to_stdout = false; if( argc > 0 ) invocation_name = argv[0]; - enum { opt_lt = 256 }; + enum { opt_eer = 256, opt_lt, opt_mer }; const struct ap_Option options[] = { - { 'a', "trailing-error", ap_no }, - { 'c', "stdout", ap_no }, - { 'd', "decompress", ap_no }, - { 'f', "force", ap_no }, - { 'h', "help", ap_no }, - { 'k', "keep", ap_no }, - { 'l', "list", ap_no }, - { 'n', "threads", ap_yes }, - { 'o', "output", ap_yes }, - { 'q', "quiet", ap_no }, - { 't', "test", ap_no }, - { 'u', "buffer-size", ap_yes }, - { 'v', "verbose", ap_no }, - { 'V', "version", ap_no }, - { opt_lt, "loose-trailing", ap_no }, - { 0 , 0, ap_no } }; + { 'a', "trailing-error", ap_no }, + { 'c', "stdout", ap_no }, + { 'd', "decompress", ap_no }, + { 'f', "force", ap_no }, + { 'h', "help", ap_no }, + { 'k', "keep", ap_no }, + { 'l', "list", ap_no }, + { 'n', "threads", ap_yes }, + { 'o', "output", ap_yes }, + { 'q', "quiet", ap_no }, + { 't', "test", ap_no }, + { 'u', "buffer-size", ap_yes }, + { 'v', "verbose", ap_no }, + { 'V', "version", ap_no }, + { opt_eer, "empty-error", ap_no }, + { opt_lt, "loose-trailing", ap_no }, + { opt_mer, "marking-error", ap_no }, + { 0, 0, ap_no } }; CRC32_init(); @@ -785,7 +818,7 @@ int main( const int argc, const char * const argv[] ) const char * const arg = ap_argument( &parser, argind ); switch( code ) { - case 'a': ignore_trailing = false; break; + case 'a': cl_opts.ignore_trailing = false; break; case 'c': to_stdout = true; break; case 'd': set_mode( &program_mode, m_decompress ); break; case 'f': force = true; break; @@ -800,8 +833,10 @@ int main( const int argc, const char * const argv[] ) case 'u': buffer_size = get_dict_size( arg, pn ); break; case 'v': if( verbosity < 4 ) ++verbosity; break; case 'V': show_version(); return 0; - case opt_lt: loose_trailing = true; break; - default : internal_error( "uncaught option." ); + case opt_eer: cl_opts.ignore_empty = false; break; + case opt_lt: cl_opts.loose_trailing = true; break; + case opt_mer: cl_opts.ignore_marking = false; break; + default: internal_error( "uncaught option." ); } } /* end process options */ @@ -823,7 +858,7 @@ int main( const int argc, const char * const argv[] ) } if( program_mode == m_list ) - return list_files( filenames, num_filenames, ignore_trailing, loose_trailing ); + return list_files( filenames, num_filenames, &cl_opts ); if( program_mode == m_compress ) program_mode = m_decompress; /* default mode */ @@ -881,7 +916,7 @@ int main( const int argc, const char * const argv[] ) infd = open_instream( input_filename, &in_stats, one_to_one, false ); if( infd < 0 ) { set_retval( &retval, 1 ); continue; } if( !check_tty_in( pp.name, infd, program_mode, &retval ) ) continue; - if( one_to_one ) /* open outfd after verifying infd */ + if( one_to_one ) /* open outfd after checking infd */ { set_d_outname( input_filename, extension_index( input_filename ) ); if( !open_outstream( force, true ) ) @@ -889,7 +924,7 @@ int main( const int argc, const char * const argv[] ) } } - if( to_file && outfd < 0 ) /* open outfd after verifying infd */ + if( to_file && outfd < 0 ) /* open outfd after checking infd */ { output_filename = resize_buffer( output_filename, strlen( default_output_filename ) + 1 ); @@ -903,8 +938,8 @@ int main( const int argc, const char * const argv[] ) if( fstat( outfd, &st ) != 0 || !S_ISREG( st.st_mode ) ) { if( verbosity >= 0 ) - fprintf( stderr, "%s: Output file '%s' is not a regular file,\n" - " and 'low memory' mode has been requested.\n", + fprintf( stderr, "%s: %s: Output file is not a regular file,\n" + " and 'low memory' mode has been requested.\n", program_name, output_filename ); set_retval( &retval, 1 ); return retval; /* don't try to delete a non-regular file */ @@ -916,8 +951,8 @@ int main( const int argc, const char * const argv[] ) const unsigned long long cfile_size = ( input_filename[0] && S_ISREG( in_stats.st_mode ) ) ? ( in_stats.st_size + 99 ) / 100 : 0; - int tmp = decompress( cfile_size, infd, &pp, buffer_size, ignore_trailing, - loose_trailing, program_mode == m_test ); + int tmp = decompress( cfile_size, infd, &cl_opts, &pp, buffer_size, + program_mode == m_test ); if( close( infd ) != 0 ) { show_file_error( pp.name, "Error closing input file", errno ); set_retval( &tmp, 1 ); } diff --git a/testsuite/check.sh b/testsuite/check.sh index c495ba1..16682a7 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # check script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2022 Antonio Diaz Diaz. +# Copyright (C) 2010-2023 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission # to copy, distribute, and modify it. @@ -32,6 +32,8 @@ cat "${testdir}"/test.txt > in || framework_failure in_lz="${testdir}"/test.txt.lz in_em="${testdir}"/test_em.txt.lz fox_lz="${testdir}"/fox.lz +fox6_lz="${testdir}"/fox6.lz +f6mk_lz="${testdir}"/fox6_mark.lz fail=0 test_failed() { fail=1 ; printf " $1" ; [ -z "$2" ] || printf "($2)" ; } @@ -64,9 +66,9 @@ rm -f uin.lz || framework_failure # these are for code coverage "${LZIP}" -lt "${in_lz}" 2> /dev/null [ $? = 1 ] || test_failed $LINENO -"${LZIP}" -cdl "${in_lz}" > out 2> /dev/null +"${LZIP}" -cdl "${in_lz}" 2> /dev/null [ $? = 1 ] || test_failed $LINENO -"${LZIP}" -cdt "${in_lz}" > out 2> /dev/null +"${LZIP}" -cdt "${in_lz}" 2> /dev/null [ $? = 1 ] || test_failed $LINENO "${LZIP}" -t -- nx_file.lz 2> /dev/null [ $? = 1 ] || test_failed $LINENO @@ -91,50 +93,54 @@ rm -f uin.lz || framework_failure printf "LZIP\001-.............................." | "${LZIP}" -t 2> /dev/null printf "LZIP\002-.............................." | "${LZIP}" -t 2> /dev/null printf "LZIP\001+.............................." | "${LZIP}" -t 2> /dev/null -rm -f out || framework_failure printf "\ntesting decompression..." for i in "${in_lz}" "${in_em}" ; do "${LZIP}" -lq "$i" || test_failed $LINENO "$i" "${LZIP}" -t "$i" || test_failed $LINENO "$i" - "${LZIP}" -d "$i" -o copy || test_failed $LINENO "$i" - cmp in copy || test_failed $LINENO "$i" - "${LZIP}" -cd "$i" > copy || test_failed $LINENO "$i" - cmp in copy || test_failed $LINENO "$i" - "${LZIP}" -d "$i" -o - > copy || test_failed $LINENO "$i" - cmp in copy || test_failed $LINENO "$i" - "${LZIP}" -d < "$i" > copy || test_failed $LINENO "$i" - cmp in copy || test_failed $LINENO "$i" - rm -f copy || framework_failure + "${LZIP}" -d "$i" -o out || test_failed $LINENO "$i" + cmp in out || test_failed $LINENO "$i" + "${LZIP}" -cd "$i" > out || test_failed $LINENO "$i" + cmp in out || test_failed $LINENO "$i" + "${LZIP}" -d "$i" -o - > out || test_failed $LINENO "$i" + cmp in out || test_failed $LINENO "$i" + "${LZIP}" -d < "$i" > out || test_failed $LINENO "$i" + cmp in out || test_failed $LINENO "$i" + rm -f out || framework_failure done -lines=$("${LZIP}" -tvv "${in_em}" 2>&1 | wc -l) || test_failed $LINENO +lines=`"${LZIP}" -tvv "${in_em}" 2>&1 | wc -l` || test_failed $LINENO [ "${lines}" -eq 8 ] || test_failed $LINENO "${lines}" +"${LZIP}" -tq "${in_em}" --empty-error +[ $? = 2 ] || test_failed $LINENO -lines=$("${LZIP}" -lvv "${in_em}" | wc -l) || test_failed $LINENO +lines=`"${LZIP}" -lvv "${in_em}" | wc -l` || test_failed $LINENO [ "${lines}" -eq 11 ] || test_failed $LINENO "${lines}" +"${LZIP}" -lq "${in_em}" --empty-error +[ $? = 2 ] || test_failed $LINENO -"${LZIP}" -cd "${fox_lz}" > fox || test_failed $LINENO -cat "${in_lz}" > copy.lz || framework_failure -"${LZIP}" -dk copy.lz || test_failed $LINENO -cmp in copy || test_failed $LINENO -cat fox > copy || framework_failure cat "${in_lz}" > out.lz || framework_failure +"${LZIP}" -dk out.lz || test_failed $LINENO +cmp in out || test_failed $LINENO rm -f out || framework_failure +"${LZIP}" -cd "${fox_lz}" > fox || test_failed $LINENO +cat fox > copy || framework_failure +cat "${in_lz}" > copy.lz || framework_failure "${LZIP}" -d copy.lz out.lz 2> /dev/null # skip copy, decompress out [ $? = 1 ] || test_failed $LINENO +[ ! -e out.lz ] || test_failed $LINENO cmp fox copy || test_failed $LINENO cmp in out || test_failed $LINENO "${LZIP}" -df copy.lz || test_failed $LINENO [ ! -e copy.lz ] || test_failed $LINENO cmp in copy || test_failed $LINENO -rm -f out || framework_failure +rm -f copy out || framework_failure -printf "to be overwritten" > copy || framework_failure -"${LZIP}" -df -o copy < "${in_lz}" || test_failed $LINENO -cmp in copy || test_failed $LINENO -rm -f out copy || framework_failure +printf "to be overwritten" > out || framework_failure +"${LZIP}" -df -o out < "${in_lz}" || test_failed $LINENO +cmp in out || test_failed $LINENO +rm -f out || framework_failure "${LZIP}" -d -o ./- "${in_lz}" || test_failed $LINENO cmp in ./- || test_failed $LINENO rm -f ./- || framework_failure @@ -143,11 +149,11 @@ cmp in ./- || test_failed $LINENO rm -f ./- || framework_failure cat "${in_lz}" > anyothername || framework_failure -"${LZIP}" -dv - anyothername - < "${in_lz}" > copy 2> /dev/null || +"${LZIP}" -dv - anyothername - < "${in_lz}" > out 2> /dev/null || test_failed $LINENO -cmp in copy || test_failed $LINENO +cmp in out || test_failed $LINENO cmp in anyothername.out || test_failed $LINENO -rm -f copy anyothername.out || framework_failure +rm -f out anyothername.out || framework_failure "${LZIP}" -lq in "${in_lz}" [ $? = 2 ] || test_failed $LINENO @@ -157,87 +163,107 @@ rm -f copy anyothername.out || framework_failure [ $? = 2 ] || test_failed $LINENO "${LZIP}" -tq nx_file.lz "${in_lz}" [ $? = 1 ] || test_failed $LINENO -"${LZIP}" -cdq in "${in_lz}" > copy +"${LZIP}" -cdq in "${in_lz}" > out [ $? = 2 ] || test_failed $LINENO -cat copy in | cmp in - || test_failed $LINENO # copy must be empty -"${LZIP}" -cdq nx_file.lz "${in_lz}" > copy +cat out in | cmp in - || test_failed $LINENO # out must be empty +"${LZIP}" -cdq nx_file.lz "${in_lz}" > out # skip nx_file, decompress in [ $? = 1 ] || test_failed $LINENO -cmp in copy || test_failed $LINENO -rm -f copy || framework_failure -cat "${in_lz}" > copy.lz || framework_failure +cmp in out || test_failed $LINENO +rm -f out || framework_failure +cat "${in_lz}" > out.lz || framework_failure for i in 1 2 3 4 5 6 7 ; do - printf "g" >> copy.lz || framework_failure - "${LZIP}" -alvv copy.lz "${in_lz}" > /dev/null 2>&1 + printf "g" >> out.lz || framework_failure + "${LZIP}" -alvv out.lz "${in_lz}" > /dev/null 2>&1 [ $? = 2 ] || test_failed $LINENO $i - "${LZIP}" -atvvvv copy.lz "${in_lz}" 2> /dev/null + "${LZIP}" -atvvvv out.lz "${in_lz}" 2> /dev/null [ $? = 2 ] || test_failed $LINENO $i done -"${LZIP}" -dq in copy.lz +"${LZIP}" -dq in out.lz [ $? = 2 ] || test_failed $LINENO -[ -e copy.lz ] || test_failed $LINENO -[ ! -e copy ] || test_failed $LINENO +[ -e out.lz ] || test_failed $LINENO +[ ! -e out ] || test_failed $LINENO [ ! -e in.out ] || test_failed $LINENO -"${LZIP}" -dq nx_file.lz copy.lz +"${LZIP}" -dq nx_file.lz out.lz [ $? = 1 ] || test_failed $LINENO -[ ! -e copy.lz ] || test_failed $LINENO +[ ! -e out.lz ] || test_failed $LINENO [ ! -e nx_file ] || test_failed $LINENO -cmp in copy || test_failed $LINENO +cmp in out || test_failed $LINENO +rm -f out || framework_failure cat in in > in2 || framework_failure "${LZIP}" -lq "${in_lz}" "${in_lz}" || test_failed $LINENO "${LZIP}" -t "${in_lz}" "${in_lz}" || test_failed $LINENO -"${LZIP}" -cd "${in_lz}" "${in_lz}" -o out > copy2 || test_failed $LINENO +"${LZIP}" -cd "${in_lz}" "${in_lz}" -o out > out2 || test_failed $LINENO [ ! -e out ] || test_failed $LINENO # override -o -cmp in2 copy2 || test_failed $LINENO -rm -f copy2 || framework_failure -"${LZIP}" -d "${in_lz}" "${in_lz}" -o copy2 || test_failed $LINENO -cmp in2 copy2 || test_failed $LINENO -rm -f copy2 || framework_failure +cmp in2 out2 || test_failed $LINENO +rm -f out2 || framework_failure +"${LZIP}" -d "${in_lz}" "${in_lz}" -o out2 || test_failed $LINENO +cmp in2 out2 || test_failed $LINENO +rm -f out2 || framework_failure -cat "${in_lz}" "${in_lz}" > copy2.lz || framework_failure -printf "\ngarbage" >> copy2.lz || framework_failure -"${LZIP}" -tvvvv copy2.lz 2> /dev/null || test_failed $LINENO -"${LZIP}" -alq copy2.lz +cat "${in_lz}" "${in_lz}" > out2.lz || framework_failure +printf "\ngarbage" >> out2.lz || framework_failure +"${LZIP}" -tvvvv out2.lz 2> /dev/null || test_failed $LINENO +"${LZIP}" -alq out2.lz [ $? = 2 ] || test_failed $LINENO -"${LZIP}" -atq copy2.lz +"${LZIP}" -atq out2.lz [ $? = 2 ] || test_failed $LINENO -"${LZIP}" -atq < copy2.lz +"${LZIP}" -atq < out2.lz [ $? = 2 ] || test_failed $LINENO -"${LZIP}" -adkq copy2.lz +"${LZIP}" -adkq out2.lz [ $? = 2 ] || test_failed $LINENO -[ ! -e copy2 ] || test_failed $LINENO -"${LZIP}" -adkq -o copy2 < copy2.lz +[ ! -e out2 ] || test_failed $LINENO +"${LZIP}" -adkq -o out2 < out2.lz [ $? = 2 ] || test_failed $LINENO -[ ! -e copy2 ] || test_failed $LINENO -printf "to be overwritten" > copy2 || framework_failure -"${LZIP}" -df copy2.lz || test_failed $LINENO -cmp in2 copy2 || test_failed $LINENO -rm -f copy2 || framework_failure +[ ! -e out2 ] || test_failed $LINENO +printf "to be overwritten" > out2 || framework_failure +"${LZIP}" -df out2.lz || test_failed $LINENO +cmp in2 out2 || test_failed $LINENO +rm -f out2 || framework_failure for i in 12 5120 6Ki 29 512KiB ; do - printf "to be overwritten" > copy || framework_failure - "${LZIP}" -df -u$i -o copy < "${in_lz}" || test_failed $LINENO $i - cmp in copy || test_failed $LINENO $i - rm -f copy || framework_failure - "${LZIP}" -d -u$i -o copy "${in_lz}" || test_failed $LINENO $i - cmp in copy || test_failed $LINENO $i - "${LZIP}" -d -u$i -o copy2 "${in_lz}" "${in_lz}" || + printf "to be overwritten" > out || framework_failure + "${LZIP}" -df -u$i -o out < "${in_lz}" || test_failed $LINENO $i + cmp in out || test_failed $LINENO $i + rm -f out || framework_failure + "${LZIP}" -d -u$i -o out "${in_lz}" || test_failed $LINENO $i + cmp in out || test_failed $LINENO $i + "${LZIP}" -d -u$i -o out2 "${in_lz}" "${in_lz}" || test_failed $LINENO $i - cmp in2 copy2 || test_failed $LINENO $i - rm -f copy2 || framework_failure + cmp in2 out2 || test_failed $LINENO $i + rm -f out2 || framework_failure done -rm -f in2 copy || framework_failure +rm -f in2 out || framework_failure + +"${LZIP}" -cd "${fox6_lz}" > out || test_failed $LINENO +"${LZIP}" -cd "${f6mk_lz}" > copy || test_failed $LINENO +cmp copy out || test_failed $LINENO +rm -f copy out || framework_failure +"${LZIP}" -lq "${f6mk_lz}" --marking-error +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -tq "${f6mk_lz}" --marking-error +[ $? = 2 ] || test_failed $LINENO + +"${LZIP}" -d "${fox_lz}" -o a/b/c/fox || test_failed $LINENO +cmp fox a/b/c/fox || test_failed $LINENO +rm -rf a || framework_failure +"${LZIP}" -d -o a/b/c/fox < "${fox_lz}" || test_failed $LINENO +cmp fox a/b/c/fox || test_failed $LINENO +rm -rf a || framework_failure +"${LZIP}" -dq "${fox_lz}" -o a/b/c/ +[ $? = 1 ] || test_failed $LINENO +[ ! -e a ] || test_failed $LINENO printf "\ntesting bad input..." headers='LZIp LZiP LZip LzIP LzIp LziP lZIP lZIp lZiP lzIP' body='\001\014\000\203\377\373\377\377\300\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000$\000\000\000\000\000\000\000' -cat "${in_lz}" > int.lz -printf "LZIP${body}" >> int.lz +cat "${in_lz}" > int.lz || framework_failure +printf "LZIP${body}" >> int.lz || framework_failure if "${LZIP}" -tq int.lz ; then for header in ${headers} ; do - printf "${header}${body}" > int.lz # first member - "${LZIP}" -lq int.lz + printf "${header}${body}" > int.lz || framework_failure + "${LZIP}" -lq int.lz # first member [ $? = 2 ] || test_failed $LINENO ${header} "${LZIP}" -tq int.lz [ $? = 2 ] || test_failed $LINENO ${header} @@ -253,9 +279,9 @@ if "${LZIP}" -tq int.lz ; then [ $? = 2 ] || test_failed $LINENO ${header} "${LZIP}" -cdq --loose-trailing int.lz > /dev/null [ $? = 2 ] || test_failed $LINENO ${header} - cat "${in_lz}" > int.lz - printf "${header}${body}" >> int.lz # trailing data - "${LZIP}" -lq int.lz + cat "${in_lz}" > int.lz || framework_failure + printf "${header}${body}" >> int.lz || framework_failure + "${LZIP}" -lq int.lz # trailing data [ $? = 2 ] || test_failed $LINENO ${header} "${LZIP}" -tq int.lz [ $? = 2 ] || test_failed $LINENO ${header} @@ -310,15 +336,15 @@ if dd if=in3.lz of=trunc.lz bs=14752 count=1 2> /dev/null && [ $? = 2 ] || test_failed $LINENO $i "${LZIP}" -tq < trunc.lz [ $? = 2 ] || test_failed $LINENO $i - "${LZIP}" -cdq trunc.lz > out + "${LZIP}" -cdq trunc.lz > /dev/null [ $? = 2 ] || test_failed $LINENO $i - "${LZIP}" -dq < trunc.lz > out + "${LZIP}" -dq < trunc.lz > /dev/null [ $? = 2 ] || test_failed $LINENO $i done else printf "\nwarning: skipping truncation test: 'dd' does not work on your system." fi -rm -f in2.lz in3.lz trunc.lz out || framework_failure +rm -f in2.lz in3.lz trunc.lz || framework_failure cat "${in_lz}" > ingin.lz || framework_failure printf "g" >> ingin.lz || framework_failure @@ -329,17 +355,17 @@ cat "${in_lz}" >> ingin.lz || framework_failure [ $? = 2 ] || test_failed $LINENO "${LZIP}" -atq < ingin.lz [ $? = 2 ] || test_failed $LINENO -"${LZIP}" -acdq ingin.lz > out +"${LZIP}" -acdq ingin.lz > /dev/null [ $? = 2 ] || test_failed $LINENO -"${LZIP}" -adq < ingin.lz > out +"${LZIP}" -adq < ingin.lz > /dev/null [ $? = 2 ] || test_failed $LINENO "${LZIP}" -t ingin.lz || test_failed $LINENO "${LZIP}" -t < ingin.lz || test_failed $LINENO -"${LZIP}" -cd ingin.lz > copy || test_failed $LINENO -cmp in copy || test_failed $LINENO -"${LZIP}" -d < ingin.lz > copy || test_failed $LINENO -cmp in copy || test_failed $LINENO -rm -f copy ingin.lz out || framework_failure +"${LZIP}" -cd ingin.lz > out || test_failed $LINENO +cmp in out || test_failed $LINENO +"${LZIP}" -d < ingin.lz > out || test_failed $LINENO +cmp in out || test_failed $LINENO +rm -f out ingin.lz || framework_failure echo if [ ${fail} = 0 ] ; then diff --git a/testsuite/fox6.lz b/testsuite/fox6.lz new file mode 100644 index 0000000000000000000000000000000000000000..8401b9912049eb24a0ddb778211eba0ec894686c GIT binary patch literal 480 zcmeZ?@(f_)VbGG|Sj4QF_R+iI=pxqtde3fNQjl6}^5c>03rDkGci1v_+dL64QRjTU jIqmVA>95u)|NqywfFbKlz-wIw2nc}EKBMLhs_+803rDkGci1v_+dL64QRjTU vIqmVA>95u)|NqywfFbKlz-wIw2nc}EJ~S~em&)!-q_TNMR5mZ0YUTj|#dEtN literal 0 HcmV?d00001 From 88b11eaf36ed5e2192f3f8b5795db2110ffb74d9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 17 Feb 2025 22:32:17 +0100 Subject: [PATCH 08/10] Adding upstream version 1.14. Signed-off-by: Daniel Baumann --- ChangeLog | 12 ++++++------ INSTALL | 2 +- NEWS | 3 +++ README | 21 ++++++++++++++++----- carg_parser.c | 2 +- carg_parser.h | 2 +- configure | 6 +++--- decoder.c | 2 +- decoder.h | 2 +- doc/lunzip.1 | 18 +++++++++++++++--- list.c | 2 +- lzip.h | 2 +- lzip_index.c | 2 +- lzip_index.h | 2 +- main.c | 25 +++++++++++++++++++------ testsuite/check.sh | 2 +- 16 files changed, 72 insertions(+), 33 deletions(-) diff --git a/ChangeLog b/ChangeLog index 264c4a0..60453fb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,10 @@ -2023-12-21 Antonio Diaz Diaz +2024-01-21 Antonio Diaz Diaz - * Version 1.14-rc1 released. + * Version 1.14 released. * New options '--empty-error' and '--marking-error'. * main.c: Reformat file diagnostics as 'PROGRAM: FILE: MESSAGE'. (show_option_error): New function showing argument and option name. + (main): Make -o preserve date/mode/owner if 1 input file. (open_outstream): Create missing intermediate directories. * lzip.h: Rename verify_* to check_*. * configure, Makefile.in: New variable 'MAKEINFO'. @@ -133,8 +134,7 @@ * Created from the decompression code of clzip 1.1. -Copyright (C) 2010-2023 Antonio Diaz Diaz. +Copyright (C) 2010-2024 Antonio Diaz Diaz. -This file is a collection of facts, and thus it is not copyrightable, -but just in case, you have unlimited permission to copy, distribute, and -modify it. +This file is a collection of facts, and thus it is not copyrightable, but just +in case, you have unlimited permission to copy, distribute, and modify it. diff --git a/INSTALL b/INSTALL index b8e9690..9fe40ef 100644 --- a/INSTALL +++ b/INSTALL @@ -74,7 +74,7 @@ After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2010-2023 Antonio Diaz Diaz. +Copyright (C) 2010-2024 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute, and modify it. diff --git a/NEWS b/NEWS index 27003a0..b7979bd 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,9 @@ File diagnostics have been reformatted as 'PROGRAM: FILE: MESSAGE'. Diagnostics caused by invalid arguments to command-line options now show the argument and the name of the option. +The option '-o, --output' now preserves dates, permissions, and ownership of +the file when decompressing exactly one file. + The option '-o, --output' now creates missing intermediate directories when writing to a file. diff --git a/README b/README index 90da7af..464de88 100644 --- a/README +++ b/README @@ -2,9 +2,21 @@ Description Lunzip is a decompressor for the lzip format written in C. Its small size makes it well suited for embedded devices or software installers that need -to decompress files but don't need compression capabilities. Lunzip is fully +to decompress files but don't need compression capabilities. Lunzip is compatible with lzip 1.4 or newer. +Lzip is a lossless data compressor with a user interface similar to the one +of gzip or bzip2. Lzip uses a simplified form of the 'Lempel-Ziv-Markov +chain-Algorithm' (LZMA) stream format to maximize interoperability. The +maximum dictionary size is 512 MiB so that any lzip file can be decompressed +on 32-bit machines. Lzip provides accurate and robust 3-factor integrity +checking. Lzip can compress about as fast as gzip (lzip -0) or compress most +files more than bzip2 (lzip -9). Decompression speed is intermediate between +gzip and bzip2. Lzip is better than gzip and bzip2 from a data recovery +perspective. Lzip has been designed, written, and tested with great care to +replace gzip and bzip2 as the standard general-purpose compressed format for +Unix-like systems. + The lzip file format is designed for data sharing and long-term archiving, taking into account both data integrity and decoder availability: @@ -89,11 +101,10 @@ been compressed. Decompressed is used to refer to data which have undergone the process of decompression. -Copyright (C) 2010-2023 Antonio Diaz Diaz. +Copyright (C) 2010-2024 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute, and modify it. -The file Makefile.in is a data file used by configure to produce the -Makefile. It has the same copyright owner and permissions that configure -itself. +The file Makefile.in is a data file used by configure to produce the Makefile. +It has the same copyright owner and permissions that configure itself. diff --git a/carg_parser.c b/carg_parser.c index 8882b97..edb4eb9 100644 --- a/carg_parser.c +++ b/carg_parser.c @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command-line argument parser. (C version) - Copyright (C) 2006-2023 Antonio Diaz Diaz. + Copyright (C) 2006-2024 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/carg_parser.h b/carg_parser.h index ec5d3d0..69ce271 100644 --- a/carg_parser.h +++ b/carg_parser.h @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command-line argument parser. (C version) - Copyright (C) 2006-2023 Antonio Diaz Diaz. + Copyright (C) 2006-2024 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/configure b/configure index ad36847..a6e4a93 100755 --- a/configure +++ b/configure @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2023 Antonio Diaz Diaz. +# Copyright (C) 2010-2024 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission # to copy, distribute, and modify it. pkgname=lunzip -pkgversion=1.14-rc1 +pkgversion=1.14 progname=lunzip srctrigger=doc/${progname}.1 @@ -171,7 +171,7 @@ echo "MAKEINFO = ${MAKEINFO}" rm -f Makefile cat > Makefile << EOF # Makefile for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2023 Antonio Diaz Diaz. +# Copyright (C) 2010-2024 Antonio Diaz Diaz. # This file was generated automatically by configure. Don't edit. # # This Makefile is free software: you have unlimited permission diff --git a/decoder.c b/decoder.c index ab6a259..5f1dc5e 100644 --- a/decoder.c +++ b/decoder.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2023 Antonio Diaz Diaz. + Copyright (C) 2010-2024 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 diff --git a/decoder.h b/decoder.h index 0651cc2..8a948e9 100644 --- a/decoder.h +++ b/decoder.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2023 Antonio Diaz Diaz. + Copyright (C) 2010-2024 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 diff --git a/doc/lunzip.1 b/doc/lunzip.1 index 3b9fb99..ce64bc9 100644 --- a/doc/lunzip.1 +++ b/doc/lunzip.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. -.TH LUNZIP "1" "December 2023" "lunzip 1.14-rc1" "User Commands" +.TH LUNZIP "1" "January 2024" "lunzip 1.14" "User Commands" .SH NAME lunzip \- decompressor for the lzip format .SH SYNOPSIS @@ -8,9 +8,21 @@ lunzip \- decompressor for the lzip format .SH DESCRIPTION Lunzip is a decompressor for the lzip format written in C. Its small size makes it well suited for embedded devices or software installers that need -to decompress files but don't need compression capabilities. Lunzip is fully +to decompress files but don't need compression capabilities. Lunzip is compatible with lzip 1.4 or newer. .PP +Lzip is a lossless data compressor with a user interface similar to the one +of gzip or bzip2. Lzip uses a simplified form of the 'Lempel\-Ziv\-Markov +chain\-Algorithm' (LZMA) stream format to maximize interoperability. The +maximum dictionary size is 512 MiB so that any lzip file can be decompressed +on 32\-bit machines. Lzip provides accurate and robust 3\-factor integrity +checking. Lzip can compress about as fast as gzip (lzip \fB\-0\fR) or compress most +files more than bzip2 (lzip \fB\-9\fR). Decompression speed is intermediate between +gzip and bzip2. Lzip is better than gzip and bzip2 from a data recovery +perspective. Lzip has been designed, written, and tested with great care to +replace gzip and bzip2 as the standard general\-purpose compressed format for +Unix\-like systems. +.PP Lunzip provides a 'low memory' mode able to decompress any file using as little memory as 50 kB, irrespective of the dictionary size used to compress the file. To activate it, specify the size of the output buffer @@ -96,7 +108,7 @@ Report bugs to lzip\-bug@nongnu.org .br Lunzip home page: http://www.nongnu.org/lzip/lunzip.html .SH COPYRIGHT -Copyright \(co 2023 Antonio Diaz Diaz. +Copyright \(co 2024 Antonio Diaz Diaz. License GPLv2+: GNU GPL version 2 or later .br This is free software: you are free to change and redistribute it. diff --git a/list.c b/list.c index 3d4b242..c711031 100644 --- a/list.c +++ b/list.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2023 Antonio Diaz Diaz. + Copyright (C) 2010-2024 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 diff --git a/lzip.h b/lzip.h index 22c75e5..91a64b1 100644 --- a/lzip.h +++ b/lzip.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2023 Antonio Diaz Diaz. + Copyright (C) 2010-2024 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 diff --git a/lzip_index.c b/lzip_index.c index 688a370..ef0b0bd 100644 --- a/lzip_index.c +++ b/lzip_index.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2023 Antonio Diaz Diaz. + Copyright (C) 2010-2024 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 diff --git a/lzip_index.h b/lzip_index.h index df2aeca..a4fbd56 100644 --- a/lzip_index.h +++ b/lzip_index.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2023 Antonio Diaz Diaz. + Copyright (C) 2010-2024 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 diff --git a/main.c b/main.c index 6949e1d..059d716 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2023 Antonio Diaz Diaz. + Copyright (C) 2010-2024 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 @@ -76,7 +76,7 @@ int verbosity = 0; static const char * const program_name = "lunzip"; -static const char * const program_year = "2023"; +static const char * const program_year = "2024"; static const char * invocation_name = "lunzip"; /* default value */ static const struct { const char * from; const char * to; } known_extensions[] = { @@ -97,8 +97,19 @@ static void show_help( void ) { printf( "Lunzip is a decompressor for the lzip format written in C. Its small size\n" "makes it well suited for embedded devices or software installers that need\n" - "to decompress files but don't need compression capabilities. Lunzip is fully\n" + "to decompress files but don't need compression capabilities. Lunzip is\n" "compatible with lzip 1.4 or newer.\n" + "\nLzip is a lossless data compressor with a user interface similar to the one\n" + "of gzip or bzip2. Lzip uses a simplified form of the 'Lempel-Ziv-Markov\n" + "chain-Algorithm' (LZMA) stream format to maximize interoperability. The\n" + "maximum dictionary size is 512 MiB so that any lzip file can be decompressed\n" + "on 32-bit machines. Lzip provides accurate and robust 3-factor integrity\n" + "checking. Lzip can compress about as fast as gzip (lzip -0) or compress most\n" + "files more than bzip2 (lzip -9). Decompression speed is intermediate between\n" + "gzip and bzip2. Lzip is better than gzip and bzip2 from a data recovery\n" + "perspective. Lzip has been designed, written, and tested with great care to\n" + "replace gzip and bzip2 as the standard general-purpose compressed format for\n" + "Unix-like systems.\n" "\nLunzip provides a 'low memory' mode able to decompress any file using as\n" "little memory as 50 kB, irrespective of the dictionary size used to\n" "compress the file. To activate it, specify the size of the output buffer\n" @@ -558,7 +569,7 @@ static void close_and_set_permissions( const struct stat * const in_statsp ) if( in_statsp ) { const mode_t mode = in_statsp->st_mode; - /* fchown will in many cases return with EPERM, which can be safely ignored. */ + /* fchown in many cases returns with EPERM, which can be safely ignored. */ if( fchown( outfd, in_statsp->st_uid, in_statsp->st_gid ) == 0 ) { if( fchmod( outfd, mode ) != 0 ) warning = true; } else @@ -896,11 +907,11 @@ int main( const int argc, const char * const argv[] ) int retval = 0; const bool one_to_one = !to_stdout && program_mode != m_test && !to_file; bool stdin_used = false; + struct stat in_stats; for( i = 0; i < num_filenames; ++i ) { const char * input_filename = ""; int infd; - struct stat in_stats; Pp_set_name( &pp, filenames[i] ); if( strcmp( filenames[i], "-" ) == 0 ) @@ -966,7 +977,9 @@ int main( const int argc, const char * const argv[] ) if( input_filename[0] && !keep_input_files && one_to_one ) remove( input_filename ); } - if( delete_output_on_interrupt ) close_and_set_permissions( 0 ); /* -o */ + if( delete_output_on_interrupt ) /* -o */ + close_and_set_permissions( ( retval == 0 && !stdin_used && + filenames_given && num_filenames == 1 ) ? &in_stats : 0 ); else if( outfd >= 0 && close( outfd ) != 0 ) /* -c */ { show_error( "Error closing stdout", errno, false ); diff --git a/testsuite/check.sh b/testsuite/check.sh index 16682a7..1d36069 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # check script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2023 Antonio Diaz Diaz. +# Copyright (C) 2010-2024 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission # to copy, distribute, and modify it. From ce2c936d6bed60ec713b2649fa56efa09c425792 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 17 Feb 2025 22:32:50 +0100 Subject: [PATCH 09/10] Adding upstream version 1.15~rc1. Signed-off-by: Daniel Baumann --- COPYING | 3 +- ChangeLog | 30 ++++--- Makefile.in | 11 ++- NEWS | 27 ++---- README | 27 +++--- carg_parser.c | 60 ++++++------- carg_parser.h | 43 +++++---- configure | 4 +- decoder.c | 63 ++++++-------- decoder.h | 94 ++++++++++---------- doc/lunzip.1 | 31 +++---- list.c | 21 +++-- lzip.h | 34 ++++---- lzip_index.c | 78 +++++++---------- lzip_index.h | 64 ++++++++------ main.c | 183 ++++++++++++++++++++------------------- testsuite/check.sh | 159 +++++++++++++++++++++------------- testsuite/em.lz | Bin 0 -> 36 bytes testsuite/fox6.lz | Bin 480 -> 0 bytes testsuite/fox6_mark.lz | Bin 480 -> 0 bytes testsuite/fox_nz.lz | Bin 0 -> 80 bytes testsuite/test.txt | 6 +- testsuite/test.txt.lz | Bin 7376 -> 7341 bytes testsuite/test_em.txt.lz | Bin 14024 -> 0 bytes 24 files changed, 472 insertions(+), 466 deletions(-) create mode 100644 testsuite/em.lz delete mode 100644 testsuite/fox6.lz delete mode 100644 testsuite/fox6_mark.lz create mode 100644 testsuite/fox_nz.lz delete mode 100644 testsuite/test_em.txt.lz diff --git a/COPYING b/COPYING index 4ad17ae..42fe735 100644 --- a/COPYING +++ b/COPYING @@ -1,8 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/ChangeLog b/ChangeLog index 60453fb..6782d3f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2024-11-17 Antonio Diaz Diaz + + * Version 1.15-rc1 released. + * Remove options '--empty-error' and '--marking-error'. + * main.c (Pp_free): New function. + * decoder.c (LZd_decode_member): Remove support for Sync Flush marker. + * check.sh: Use 'cp' instead of 'cat'. + * testsuite: Add em.lz, fox_nz.lz. + Remove fox6.lz, fox6_mark.lz, test_em.txt.lz. + 2024-01-21 Antonio Diaz Diaz * Version 1.14 released. @@ -43,13 +53,13 @@ 2018-02-05 Antonio Diaz Diaz * Version 1.10 released. - * main.c: New option '--loose-trailing'. + * New option '--loose-trailing'. * Improve corrupt header detection to HD=3. * main.c: Show corrupt or truncated header in multimember file. + (main): Show final diagnostic when testing multiple files. * Replace 'bits/byte' with inverse compression ratio in output. * Show progress of decompression at verbosity level 2 (-vv). * Show progress of decompression only if stderr is a terminal. - * main.c: Show final diagnostic when testing multiple files. * decoder.c (LZd_verify_trailer): Show stored sizes also in hex. Show dictionary size at verbosity level 4 (-vvvv). @@ -67,17 +77,15 @@ 2016-05-12 Antonio Diaz Diaz * Version 1.8 released. - * main.c: New option '-a, --trailing-error'. + * New option '-a, --trailing-error'. * main.c (main): With '-u', check that output file is regular. - * main.c (decompress): Print up to 6 bytes of trailing data - when '-vvvv' is specified. + (main): Delete '--output' file if infd is a terminal. + (main): Don't use stdin more than once. + (decompress): Print 6 bytes of trailing data at verbosity level 4. * decoder.c (LZd_verify_trailer): Remove test of final code. - * main.c (main): Delete '--output' file if infd is a terminal. - * main.c (main): Don't use stdin more than once. * Error messages synced with lzip-1.18. * configure: Avoid warning on some shells when testing for gcc. - * check.sh: A POSIX shell is required to run the tests. - * check.sh: Don't check error messages. + * check.sh: Require a POSIX shell. Don't check error messages. 2015-05-27 Antonio Diaz Diaz @@ -93,7 +101,7 @@ 2014-04-11 Antonio Diaz Diaz * Version 1.5 released. - * main.c: New option '-u, --buffer-size' (low memory mode). + * New option '-u, --buffer-size' (low memory mode). * main.c (close_and_set_permissions): Behave like 'cp -p'. 2013-09-17 Antonio Diaz Diaz @@ -115,7 +123,7 @@ * Version 1.2 released. * Decompression time has been reduced by 12%. * Makefile.in: New targets 'install-as-lzip' and 'install-bin'. - * main.c: Use 'setmode' instead of '_setmode' on Windows and OS/2. + * (main): Use 'setmode' instead of '_setmode' on Windows and OS/2. 2012-02-26 Antonio Diaz Diaz diff --git a/Makefile.in b/Makefile.in index ea04dd8..aed8d09 100644 --- a/Makefile.in +++ b/Makefile.in @@ -2,8 +2,8 @@ DISTNAME = $(pkgname)-$(pkgversion) INSTALL = install INSTALL_PROGRAM = $(INSTALL) -m 755 -INSTALL_DATA = $(INSTALL) -m 644 INSTALL_DIR = $(INSTALL) -d -m 755 +INSTALL_DATA = $(INSTALL) -m 644 SHELL = /bin/sh CAN_RUN_INSTALLINFO = $(SHELL) -c "install-info --version" > /dev/null 2>&1 @@ -30,7 +30,8 @@ main.o : main.c # prevent 'make' from trying to remake source files $(VPATH)/configure $(VPATH)/Makefile.in $(VPATH)/doc/$(pkgname).texi : ; -%.h %.c : ; +MAKEFLAGS += -r +.SUFFIXES : $(objs) : Makefile carg_parser.o : carg_parser.h @@ -122,12 +123,10 @@ dist : doc $(DISTNAME)/*.c \ $(DISTNAME)/testsuite/check.sh \ $(DISTNAME)/testsuite/test.txt \ + $(DISTNAME)/testsuite/em.lz \ $(DISTNAME)/testsuite/fox.lz \ $(DISTNAME)/testsuite/fox_*.lz \ - $(DISTNAME)/testsuite/fox6.lz \ - $(DISTNAME)/testsuite/fox6_mark.lz \ - $(DISTNAME)/testsuite/test.txt.lz \ - $(DISTNAME)/testsuite/test_em.txt.lz + $(DISTNAME)/testsuite/test.txt.lz rm -f $(DISTNAME) lzip -v -9 $(DISTNAME).tar diff --git a/NEWS b/NEWS index b7979bd..fb76246 100644 --- a/NEWS +++ b/NEWS @@ -1,24 +1,9 @@ -Changes in version 1.14: +Changes in version 1.15: -The option '--empty-error', which forces exit status 2 if any empty member -is found, has been added. +lunzip now exits with error status 2 if any empty member is found in a +multimember file. -The option '--marking-error', which forces exit status 2 if the first LZMA -byte is non-zero in any member, has been added. +lunzip now exits with error status 2 if the first byte of the LZMA stream is +not 0. -File diagnostics have been reformatted as 'PROGRAM: FILE: MESSAGE'. - -Diagnostics caused by invalid arguments to command-line options now show the -argument and the name of the option. - -The option '-o, --output' now preserves dates, permissions, and ownership of -the file when decompressing exactly one file. - -The option '-o, --output' now creates missing intermediate directories when -writing to a file. - -The variable MAKEINFO has been added to configure and Makefile.in. - -It has been documented in INSTALL that when choosing a C standard, the POSIX -features need to be enabled explicitly: - ./configure CFLAGS+='--std=c99 -D_XOPEN_SOURCE=500' +Options '--empty-error' and '--marking-error' have been removed. diff --git a/README b/README index 464de88..36784cd 100644 --- a/README +++ b/README @@ -2,20 +2,19 @@ Description Lunzip is a decompressor for the lzip format written in C. Its small size makes it well suited for embedded devices or software installers that need -to decompress files but don't need compression capabilities. Lunzip is -compatible with lzip 1.4 or newer. +to decompress files but don't need compression capabilities. Lzip is a lossless data compressor with a user interface similar to the one -of gzip or bzip2. Lzip uses a simplified form of the 'Lempel-Ziv-Markov -chain-Algorithm' (LZMA) stream format to maximize interoperability. The -maximum dictionary size is 512 MiB so that any lzip file can be decompressed -on 32-bit machines. Lzip provides accurate and robust 3-factor integrity -checking. Lzip can compress about as fast as gzip (lzip -0) or compress most -files more than bzip2 (lzip -9). Decompression speed is intermediate between -gzip and bzip2. Lzip is better than gzip and bzip2 from a data recovery -perspective. Lzip has been designed, written, and tested with great care to -replace gzip and bzip2 as the standard general-purpose compressed format for -Unix-like systems. +of gzip or bzip2. Lzip uses a simplified form of LZMA (Lempel-Ziv-Markov +chain-Algorithm) designed to achieve complete interoperability between +implementations. The maximum dictionary size is 512 MiB so that any lzip +file can be decompressed on 32-bit machines. Lzip provides accurate and +robust 3-factor integrity checking. 'lzip -0' compresses about as fast as +gzip, while 'lzip -9' compresses most files more than bzip2. Decompression +speed is intermediate between gzip and bzip2. Lzip provides better data +recovery capabilities than gzip and bzip2. Lzip has been designed, written, +and tested with great care to replace gzip and bzip2 as general-purpose +compressed format for Unix-like systems. The lzip file format is designed for data sharing and long-term archiving, taking into account both data integrity and decoder availability: @@ -65,8 +64,8 @@ The amount of memory required by lunzip to decompress a file is about 46 kB larger than the dictionary size used to compress that file, unless '--buffer-size' is specified. -Lunzip attempts to guess the name for the decompressed file from that of -the compressed file as follows: +When decompressing, lunzip attempts to guess the name for the decompressed +file from that of the compressed file as follows: filename.lz becomes filename filename.tlz becomes filename.tar diff --git a/carg_parser.c b/carg_parser.c index edb4eb9..9400342 100644 --- a/carg_parser.c +++ b/carg_parser.c @@ -32,15 +32,15 @@ static void * ap_resize_buffer( void * buf, const int min_size ) } -static char push_back_record( struct Arg_parser * const ap, const int code, +static char push_back_record( Arg_parser * const ap, const int code, const char * const long_name, const char * const argument ) { - struct ap_Record * p; + ap_Record * p; void * tmp = ap_resize_buffer( ap->data, - ( ap->data_size + 1 ) * sizeof (struct ap_Record) ); + ( ap->data_size + 1 ) * sizeof (ap_Record) ); if( !tmp ) return 0; - ap->data = (struct ap_Record *)tmp; + ap->data = (ap_Record *)tmp; p = &(ap->data[ap->data_size]); p->code = code; if( long_name ) @@ -71,7 +71,7 @@ static char push_back_record( struct Arg_parser * const ap, const int code, } -static char add_error( struct Arg_parser * const ap, const char * const msg ) +static char add_error( Arg_parser * const ap, const char * const msg ) { const int len = strlen( msg ); void * tmp = ap_resize_buffer( ap->error, ap->error_size + len + 1 ); @@ -83,7 +83,7 @@ static char add_error( struct Arg_parser * const ap, const char * const msg ) } -static void free_data( struct Arg_parser * const ap ) +static void free_data( Arg_parser * const ap ) { int i; for( i = 0; i < ap->data_size; ++i ) @@ -94,10 +94,9 @@ static void free_data( struct Arg_parser * const ap ) /* Return 0 only if out of memory. */ -static char parse_long_option( struct Arg_parser * const ap, +static char parse_long_option( Arg_parser * const ap, const char * const opt, const char * const arg, - const struct ap_Option options[], - int * const argindp ) + const ap_Option options[], int * const argindp ) { unsigned len; int index = -1, i; @@ -148,21 +147,21 @@ static char parse_long_option( struct Arg_parser * const ap, add_error( ap, "' requires an argument" ); return 1; } - return push_back_record( ap, options[index].code, - options[index].long_name, &opt[len+3] ); + return push_back_record( ap, options[index].code, options[index].long_name, + &opt[len+3] ); /* argument may be empty */ } - if( options[index].has_arg == ap_yes ) + if( options[index].has_arg == ap_yes || options[index].has_arg == ap_yme ) { - if( !arg || !arg[0] ) + if( !arg || ( options[index].has_arg == ap_yes && !arg[0] ) ) { add_error( ap, "option '--" ); add_error( ap, options[index].long_name ); add_error( ap, "' requires an argument" ); return 1; } ++*argindp; - return push_back_record( ap, options[index].code, - options[index].long_name, arg ); + return push_back_record( ap, options[index].code, options[index].long_name, + arg ); /* argument may be empty */ } return push_back_record( ap, options[index].code, @@ -171,10 +170,9 @@ static char parse_long_option( struct Arg_parser * const ap, /* Return 0 only if out of memory. */ -static char parse_short_option( struct Arg_parser * const ap, +static char parse_short_option( Arg_parser * const ap, const char * const opt, const char * const arg, - const struct ap_Option options[], - int * const argindp ) + const ap_Option options[], int * const argindp ) { int cind = 1; /* character index in opt */ @@ -204,15 +202,15 @@ static char parse_short_option( struct Arg_parser * const ap, if( !push_back_record( ap, c, 0, &opt[cind] ) ) return 0; ++*argindp; cind = 0; } - else if( options[index].has_arg == ap_yes ) + else if( options[index].has_arg == ap_yes || options[index].has_arg == ap_yme ) { - if( !arg || !arg[0] ) + if( !arg || ( options[index].has_arg == ap_yes && !arg[0] ) ) { add_error( ap, "option requires an argument -- '" ); add_error( ap, code_str ); add_error( ap, "'" ); return 1; } - ++*argindp; cind = 0; + ++*argindp; cind = 0; /* argument may be empty */ if( !push_back_record( ap, c, 0, arg ) ) return 0; } else if( !push_back_record( ap, c, 0, 0 ) ) return 0; @@ -221,9 +219,9 @@ static char parse_short_option( struct Arg_parser * const ap, } -char ap_init( struct Arg_parser * const ap, +char ap_init( Arg_parser * const ap, const int argc, const char * const argv[], - const struct ap_Option options[], const char in_order ) + const ap_Option options[], const char in_order ) { const char ** non_options = 0; /* skipped non-options */ int non_options_size = 0; /* number of skipped non-options */ @@ -282,7 +280,7 @@ out: if( non_options ) free( non_options ); } -void ap_free( struct Arg_parser * const ap ) +void ap_free( Arg_parser * const ap ) { free_data( ap ); if( ap->error ) { free( ap->error ); ap->error = 0; } @@ -290,29 +288,25 @@ void ap_free( struct Arg_parser * const ap ) } -const char * ap_error( const struct Arg_parser * const ap ) - { return ap->error; } +const char * ap_error( const Arg_parser * const ap ) { return ap->error; } +int ap_arguments( const Arg_parser * const ap ) { return ap->data_size; } -int ap_arguments( const struct Arg_parser * const ap ) - { return ap->data_size; } - - -int ap_code( const struct Arg_parser * const ap, const int i ) +int ap_code( const Arg_parser * const ap, const int i ) { if( i < 0 || i >= ap_arguments( ap ) ) return 0; return ap->data[i].code; } -const char * ap_parsed_name( const struct Arg_parser * const ap, const int i ) +const char * ap_parsed_name( const Arg_parser * const ap, const int i ) { if( i < 0 || i >= ap_arguments( ap ) || !ap->data[i].parsed_name ) return ""; return ap->data[i].parsed_name; } -const char * ap_argument( const struct Arg_parser * const ap, const int i ) +const char * ap_argument( const Arg_parser * const ap, const int i ) { if( i < 0 || i >= ap_arguments( ap ) || !ap->data[i].argument ) return ""; return ap->data[i].argument; diff --git a/carg_parser.h b/carg_parser.h index 69ce271..294756c 100644 --- a/carg_parser.h +++ b/carg_parser.h @@ -37,60 +37,65 @@ The argument '--' terminates all options; any following arguments are treated as non-option arguments, even if they begin with a hyphen. - The syntax for optional option arguments is '-' - (without whitespace), or '--='. + The syntax of options with an optional argument is + '-' (without whitespace), or + '--='. + + The syntax of options with an empty argument is '- ""', + '-- ""', or '--=""'. */ #ifdef __cplusplus extern "C" { #endif -enum ap_Has_arg { ap_no, ap_yes, ap_maybe }; +/* ap_yme = yes but maybe empty */ +typedef enum ap_Has_arg { ap_no, ap_yes, ap_maybe, ap_yme } ap_Has_arg; -struct ap_Option +typedef struct ap_Option { int code; /* Short option letter or code ( code != 0 ) */ const char * long_name; /* Long option name (maybe null) */ - enum ap_Has_arg has_arg; - }; + ap_Has_arg has_arg; + } ap_Option; -struct ap_Record +typedef struct ap_Record { int code; char * parsed_name; char * argument; - }; + } ap_Record; -struct Arg_parser +typedef struct Arg_parser { - struct ap_Record * data; + ap_Record * data; char * error; int data_size; int error_size; - }; + } Arg_parser; -char ap_init( struct Arg_parser * const ap, +char ap_init( Arg_parser * const ap, const int argc, const char * const argv[], - const struct ap_Option options[], const char in_order ); + const ap_Option options[], const char in_order ); -void ap_free( struct Arg_parser * const ap ); +void ap_free( Arg_parser * const ap ); -const char * ap_error( const struct Arg_parser * const ap ); +const char * ap_error( const Arg_parser * const ap ); /* The number of arguments parsed. May be different from argc. */ -int ap_arguments( const struct Arg_parser * const ap ); +int ap_arguments( const Arg_parser * const ap ); /* If ap_code( i ) is 0, ap_argument( i ) is a non-option. Else ap_argument( i ) is the option's argument (or empty). */ -int ap_code( const struct Arg_parser * const ap, const int i ); +int ap_code( const Arg_parser * const ap, const int i ); /* Full name of the option parsed (short or long). */ -const char * ap_parsed_name( const struct Arg_parser * const ap, const int i ); +const char * ap_parsed_name( const Arg_parser * const ap, const int i ); -const char * ap_argument( const struct Arg_parser * const ap, const int i ); +const char * ap_argument( const Arg_parser * const ap, const int i ); #ifdef __cplusplus } diff --git a/configure b/configure index a6e4a93..4aea9d0 100755 --- a/configure +++ b/configure @@ -6,7 +6,7 @@ # to copy, distribute, and modify it. pkgname=lunzip -pkgversion=1.14 +pkgversion=1.15-rc1 progname=lunzip srctrigger=doc/${progname}.1 @@ -109,7 +109,7 @@ while [ $# != 0 ] ; do exit 1 ;; esac - # Check if the option took a separate argument + # Check whether the option took a separate argument if [ "${arg2}" = yes ] ; then if [ $# != 0 ] ; then args="${args} \"$1\"" ; shift else echo "configure: Missing argument to '${option}'" 1>&2 diff --git a/decoder.c b/decoder.c index 5f1dc5e..fbd185e 100644 --- a/decoder.c +++ b/decoder.c @@ -78,14 +78,14 @@ unsigned seek_read_back( const int fd, uint8_t * const buf, const int size, } -bool Rd_read_block( struct Range_decoder * const rdec ) +bool Rd_read_block( Range_decoder * const rdec ) { if( !rdec->at_stream_end ) { rdec->stream_pos = readblock( rdec->infd, rdec->buffer, rd_buffer_size ); if( rdec->stream_pos != rd_buffer_size && errno ) { show_error( "Read error", errno, false ); cleanup_and_fail( 1 ); } - rdec->at_stream_end = ( rdec->stream_pos < rd_buffer_size ); + rdec->at_stream_end = rdec->stream_pos < rd_buffer_size; rdec->partial_member_pos += rdec->pos; rdec->pos = 0; show_dprogress( 0, 0, 0, 0 ); @@ -94,7 +94,7 @@ bool Rd_read_block( struct Range_decoder * const rdec ) } -void LZd_flush_data( struct LZ_decoder * const d ) +void LZd_flush_data( LZ_decoder * const d ) { if( d->pos > d->stream_pos ) { @@ -102,7 +102,7 @@ void LZd_flush_data( struct LZ_decoder * const d ) CRC32_update_buf( &d->crc, d->buffer + d->stream_pos, size ); if( d->outfd >= 0 && writeblock( d->outfd, d->buffer + d->stream_pos, size ) != size ) - { show_error( "Write error", errno, false ); cleanup_and_fail( 1 ); } + { show_error( write_error_msg, errno, false ); cleanup_and_fail( 1 ); } if( d->pos >= d->buffer_size ) { d->partial_data_pos += d->pos; d->pos = 0; if( d->partial_data_pos >= d->dictionary_size ) d->pos_wrapped = true; } @@ -111,9 +111,7 @@ void LZd_flush_data( struct LZ_decoder * const d ) } -static int LZd_check_trailer( struct LZ_decoder * const d, - struct Pretty_print * const pp, - const bool ignore_empty ) +static bool LZd_check_trailer( LZ_decoder * const d, Pretty_print * const pp ) { Lzip_trailer trailer; int size = Rd_read_data( d->rdec, trailer, Lt_size ); @@ -158,8 +156,7 @@ static int LZd_check_trailer( struct LZ_decoder * const d, fprintf( stderr, "Member size mismatch; stored %llu (0x%llX), computed %llu (0x%llX)\n", tm_size, tm_size, member_size, member_size ); } } - if( error ) return 3; - if( !ignore_empty && data_size == 0 ) return 5; + if( error ) return false; if( verbosity >= 2 ) { if( verbosity >= 4 ) show_header( d->dictionary_size ); @@ -174,18 +171,16 @@ static int LZd_check_trailer( struct LZ_decoder * const d, if( verbosity >= 3 ) fprintf( stderr, "%9llu out, %8llu in. ", data_size, member_size ); } - return 0; + return true; } /* Return value: 0 = OK, 1 = decoder error, 2 = unexpected EOF, 3 = trailer error, 4 = unknown marker found, - 5 = empty member found, 6 = marked member found. */ -int LZd_decode_member( struct LZ_decoder * const d, - const struct Cl_options * const cl_opts, - struct Pretty_print * const pp ) + 5 = nonzero first LZMA byte found. */ +int LZd_decode_member( LZ_decoder * const d, Pretty_print * const pp ) { - struct Range_decoder * const rdec = d->rdec; + Range_decoder * const rdec = d->rdec; Bit_model bm_literal[1<ignore_marking ) ) return 6; + if( !Rd_load( rdec ) ) return 5; while( !Rd_finished( rdec ) ) { const int pos_state = LZd_data_position( d ) & pos_state_mask; @@ -263,39 +258,33 @@ int LZd_decode_member( struct LZ_decoder * const d, } else /* match */ { + rep3 = rep2; rep2 = rep1; rep1 = rep0; len = Rd_decode_len( rdec, &match_len_model, pos_state ); - unsigned distance = Rd_decode_tree6( rdec, bm_dis_slot[get_len_state(len)] ); - if( distance >= start_dis_model ) + rep0 = Rd_decode_tree6( rdec, bm_dis_slot[get_len_state(len)] ); + if( rep0 >= start_dis_model ) { - const unsigned dis_slot = distance; + const unsigned dis_slot = rep0; const int direct_bits = ( dis_slot >> 1 ) - 1; - distance = ( 2 | ( dis_slot & 1 ) ) << direct_bits; + rep0 = ( 2 | ( dis_slot & 1 ) ) << direct_bits; if( dis_slot < end_dis_model ) - distance += Rd_decode_tree_reversed( rdec, - bm_dis + ( distance - dis_slot ), direct_bits ); + rep0 += Rd_decode_tree_reversed( rdec, bm_dis + ( rep0 - dis_slot ), + direct_bits ); else { - distance += - Rd_decode( rdec, direct_bits - dis_align_bits ) << dis_align_bits; - distance += Rd_decode_tree_reversed4( rdec, bm_align ); - if( distance == 0xFFFFFFFFU ) /* marker found */ + rep0 += Rd_decode( rdec, direct_bits - dis_align_bits ) << dis_align_bits; + rep0 += Rd_decode_tree_reversed4( rdec, bm_align ); + if( rep0 == 0xFFFFFFFFU ) /* marker found */ { Rd_normalize( rdec ); LZd_flush_data( d ); if( len == min_match_len ) /* End Of Stream marker */ - return LZd_check_trailer( d, pp, cl_opts->ignore_empty ); - if( len == min_match_len + 1 ) /* Sync Flush marker */ - { Rd_load( rdec, true ); continue; } - if( verbosity >= 0 ) - { - Pp_show_msg( pp, 0 ); - fprintf( stderr, "Unsupported marker code '%d'\n", len ); - } + { if( LZd_check_trailer( d, pp ) ) return 0; else return 3; } + if( verbosity >= 0 ) { Pp_show_msg( pp, 0 ); + fprintf( stderr, "Unsupported marker code '%d'\n", len ); } return 4; } } } - rep3 = rep2; rep2 = rep1; rep1 = rep0; rep0 = distance; state = St_set_match( state ); if( rep0 >= d->dictionary_size || ( !d->pos_wrapped && rep0 >= LZd_data_position( d ) ) ) diff --git a/decoder.h b/decoder.h index 8a948e9..853c04d 100644 --- a/decoder.h +++ b/decoder.h @@ -29,9 +29,9 @@ struct Range_decoder bool at_stream_end; }; -bool Rd_read_block( struct Range_decoder * const rdec ); +bool Rd_read_block( Range_decoder * const rdec ); -static inline bool Rd_init( struct Range_decoder * const rdec, const int ifd ) +static inline bool Rd_init( Range_decoder * const rdec, const int ifd ) { rdec->partial_member_pos = 0; rdec->buffer = (uint8_t *)malloc( rd_buffer_size ); @@ -45,27 +45,27 @@ static inline bool Rd_init( struct Range_decoder * const rdec, const int ifd ) return true; } -static inline void Rd_free( struct Range_decoder * const rdec ) +static inline void Rd_free( Range_decoder * const rdec ) { free( rdec->buffer ); } -static inline bool Rd_finished( struct Range_decoder * const rdec ) +static inline bool Rd_finished( 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 ) +Rd_member_position( const Range_decoder * const rdec ) { return rdec->partial_member_pos + rdec->pos; } -static inline void Rd_reset_member_position( struct Range_decoder * const rdec ) +static inline void Rd_reset_member_position( Range_decoder * const rdec ) { rdec->partial_member_pos = 0; rdec->partial_member_pos -= rdec->pos; } -static inline uint8_t Rd_get_byte( struct Range_decoder * const rdec ) +static inline uint8_t Rd_get_byte( 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 inline int Rd_read_data( struct Range_decoder * const rdec, +static inline int Rd_read_data( Range_decoder * const rdec, uint8_t * const outbuf, const int size ) { int sz = 0; @@ -79,25 +79,24 @@ static inline int Rd_read_data( struct Range_decoder * const rdec, return sz; } -static inline bool Rd_load( struct Range_decoder * const rdec, - const bool ignore_marking ) +static inline bool Rd_load( Range_decoder * const rdec ) { - int i; rdec->code = 0; rdec->range = 0xFFFFFFFFU; - /* check and discard first byte of the LZMA stream */ - if( Rd_get_byte( rdec ) != 0 && !ignore_marking ) return false; - for( i = 0; i < 4; ++i ) rdec->code = (rdec->code << 8) | Rd_get_byte( rdec ); + /* check first byte of the LZMA stream */ + if( Rd_get_byte( rdec ) != 0 ) return false; + int i; for( i = 0; i < 4; ++i ) + rdec->code = (rdec->code << 8) | Rd_get_byte( rdec ); return true; } -static inline void Rd_normalize( struct Range_decoder * const rdec ) +static inline void Rd_normalize( Range_decoder * const rdec ) { if( rdec->range <= 0x00FFFFFFU ) { rdec->range <<= 8; rdec->code = (rdec->code << 8) | Rd_get_byte( rdec ); } } -static inline unsigned Rd_decode( struct Range_decoder * const rdec, +static inline unsigned Rd_decode( Range_decoder * const rdec, const int num_bits ) { unsigned symbol = 0; @@ -108,14 +107,14 @@ static inline unsigned Rd_decode( struct Range_decoder * const rdec, rdec->range >>= 1; /* symbol <<= 1; */ /* if( rdec->code >= rdec->range ) { rdec->code -= rdec->range; symbol |= 1; } */ - const bool bit = ( rdec->code >= rdec->range ); + const bool bit = rdec->code >= rdec->range; symbol <<= 1; symbol += bit; rdec->code -= rdec->range & ( 0U - bit ); } return symbol; } -static inline unsigned Rd_decode_bit( struct Range_decoder * const rdec, +static inline unsigned Rd_decode_bit( Range_decoder * const rdec, Bit_model * const probability ) { Rd_normalize( rdec ); @@ -135,7 +134,7 @@ static inline unsigned Rd_decode_bit( struct Range_decoder * const rdec, } } -static inline void Rd_decode_symbol_bit( struct Range_decoder * const rdec, +static inline void Rd_decode_symbol_bit( Range_decoder * const rdec, Bit_model * const probability, unsigned * symbol ) { Rd_normalize( rdec ); @@ -155,7 +154,7 @@ static inline void Rd_decode_symbol_bit( struct Range_decoder * const rdec, } } -static inline void Rd_decode_symbol_bit_reversed( struct Range_decoder * const rdec, +static inline void Rd_decode_symbol_bit_reversed( Range_decoder * const rdec, Bit_model * const probability, unsigned * model, unsigned * symbol, const int i ) { @@ -177,7 +176,7 @@ static inline void Rd_decode_symbol_bit_reversed( struct Range_decoder * const r } } -static inline unsigned Rd_decode_tree6( struct Range_decoder * const rdec, +static inline unsigned Rd_decode_tree6( Range_decoder * const rdec, Bit_model bm[] ) { unsigned symbol = 1; @@ -190,7 +189,7 @@ static inline unsigned Rd_decode_tree6( struct Range_decoder * const rdec, return symbol & 0x3F; } -static inline unsigned Rd_decode_tree8( struct Range_decoder * const rdec, +static inline unsigned Rd_decode_tree8( Range_decoder * const rdec, Bit_model bm[] ) { unsigned symbol = 1; @@ -206,7 +205,7 @@ static inline unsigned Rd_decode_tree8( struct Range_decoder * const rdec, } static inline unsigned -Rd_decode_tree_reversed( struct Range_decoder * const rdec, +Rd_decode_tree_reversed( Range_decoder * const rdec, Bit_model bm[], const int num_bits ) { unsigned model = 1; @@ -218,7 +217,7 @@ Rd_decode_tree_reversed( struct Range_decoder * const rdec, } static inline unsigned -Rd_decode_tree_reversed4( struct Range_decoder * const rdec, Bit_model bm[] ) +Rd_decode_tree_reversed4( Range_decoder * const rdec, Bit_model bm[] ) { unsigned model = 1; unsigned symbol = 0; @@ -229,7 +228,7 @@ Rd_decode_tree_reversed4( struct Range_decoder * const rdec, Bit_model bm[] ) return symbol; } -static inline unsigned Rd_decode_matched( struct Range_decoder * const rdec, +static inline unsigned Rd_decode_matched( Range_decoder * const rdec, Bit_model bm[], unsigned match_byte ) { unsigned symbol = 1; @@ -244,8 +243,8 @@ static inline unsigned Rd_decode_matched( struct Range_decoder * const rdec, } } -static inline unsigned Rd_decode_len( struct Range_decoder * const rdec, - struct Len_model * const lm, +static inline unsigned Rd_decode_len( Range_decoder * const rdec, + Len_model * const lm, const int pos_state ) { Bit_model * bm; @@ -269,10 +268,10 @@ len3: } -struct LZ_decoder +typedef struct LZ_decoder { unsigned long long partial_data_pos; - struct Range_decoder * rdec; + Range_decoder * rdec; unsigned dictionary_size; unsigned buffer_size; uint8_t * buffer; /* output buffer */ @@ -281,17 +280,17 @@ struct LZ_decoder uint32_t crc; int outfd; /* output file descriptor */ bool pos_wrapped; - }; + } LZ_decoder; -void LZd_flush_data( struct LZ_decoder * const d ); +void LZd_flush_data( LZ_decoder * const d ); unsigned seek_read_back( const int fd, uint8_t * const buf, const int size, const int offset ); -static inline uint8_t LZd_peek_prev( const struct LZ_decoder * const d ) +static inline uint8_t LZd_peek_prev( const LZ_decoder * const d ) { return d->buffer[((d->pos > 0) ? d->pos : d->buffer_size)-1]; } -static inline uint8_t LZd_peek( const struct LZ_decoder * const d, +static inline uint8_t LZd_peek( const LZ_decoder * const d, const unsigned distance ) { uint8_t b; @@ -304,27 +303,27 @@ static inline uint8_t LZd_peek( const struct LZ_decoder * const d, return b; } -static inline void LZd_put_byte( struct LZ_decoder * const d, const uint8_t b ) +static inline void LZd_put_byte( LZ_decoder * const d, const uint8_t b ) { d->buffer[d->pos] = b; if( ++d->pos >= d->buffer_size ) LZd_flush_data( d ); } -static inline void LZd_copy_block( struct LZ_decoder * const d, +static inline void LZd_copy_block( LZ_decoder * const d, const unsigned distance, unsigned len ) { unsigned lpos = d->pos, i = lpos - distance - 1; bool fast, fast2; if( lpos > distance ) { - fast = ( len < d->buffer_size - lpos ); - fast2 = ( fast && len <= lpos - i ); + fast = len < d->buffer_size - lpos; + fast2 = fast && len <= lpos - i; } else { i += d->buffer_size; - fast = ( len < d->buffer_size - i ); /* (i == pos) may happen */ - fast2 = ( fast && len <= i - lpos ); + fast = len < d->buffer_size - i; /* (i == pos) may happen */ + fast2 = fast && len <= i - lpos; } if( fast ) /* no wrap */ { @@ -342,8 +341,8 @@ static inline void LZd_copy_block( struct LZ_decoder * const d, } } -/* block is (at least partially) outside of the buffer */ -static inline void LZd_copy_block2( struct LZ_decoder * const d, +/* block is (at least partially) outside the buffer */ +static inline void LZd_copy_block2( LZ_decoder * const d, const unsigned distance, unsigned len ) { if( len < d->buffer_size - d->pos ) /* no wrap */ @@ -361,8 +360,7 @@ static inline void LZd_copy_block2( struct LZ_decoder * const d, LZd_put_byte( d, LZd_peek( d, distance ) ); } -static inline bool LZd_init( struct LZ_decoder * const d, - struct Range_decoder * const rde, +static inline bool LZd_init( LZ_decoder * const d, Range_decoder * const rde, const unsigned buffer_size, const unsigned dict_size, const int ofd ) { @@ -382,16 +380,14 @@ static inline bool LZd_init( struct LZ_decoder * const d, return true; } -static inline void LZd_free( struct LZ_decoder * const d ) +static inline void LZd_free( LZ_decoder * const d ) { free( d->buffer ); } -static inline unsigned LZd_crc( const struct LZ_decoder * const d ) +static inline unsigned LZd_crc( const LZ_decoder * const d ) { return d->crc ^ 0xFFFFFFFFU; } static inline unsigned long long -LZd_data_position( const struct LZ_decoder * const d ) +LZd_data_position( const LZ_decoder * const d ) { return d->partial_data_pos + d->pos; } -int LZd_decode_member( struct LZ_decoder * const d, - const struct Cl_options * const cl_opts, - struct Pretty_print * const pp ); +int LZd_decode_member( LZ_decoder * const d, Pretty_print * const pp ); diff --git a/doc/lunzip.1 b/doc/lunzip.1 index ce64bc9..54c098e 100644 --- a/doc/lunzip.1 +++ b/doc/lunzip.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. -.TH LUNZIP "1" "January 2024" "lunzip 1.14" "User Commands" +.TH LUNZIP "1" "November 2024" "lunzip 1.15-rc1" "User Commands" .SH NAME lunzip \- decompressor for the lzip format .SH SYNOPSIS @@ -8,20 +8,19 @@ lunzip \- decompressor for the lzip format .SH DESCRIPTION Lunzip is a decompressor for the lzip format written in C. Its small size makes it well suited for embedded devices or software installers that need -to decompress files but don't need compression capabilities. Lunzip is -compatible with lzip 1.4 or newer. +to decompress files but don't need compression capabilities. .PP Lzip is a lossless data compressor with a user interface similar to the one -of gzip or bzip2. Lzip uses a simplified form of the 'Lempel\-Ziv\-Markov -chain\-Algorithm' (LZMA) stream format to maximize interoperability. The -maximum dictionary size is 512 MiB so that any lzip file can be decompressed -on 32\-bit machines. Lzip provides accurate and robust 3\-factor integrity -checking. Lzip can compress about as fast as gzip (lzip \fB\-0\fR) or compress most -files more than bzip2 (lzip \fB\-9\fR). Decompression speed is intermediate between -gzip and bzip2. Lzip is better than gzip and bzip2 from a data recovery -perspective. Lzip has been designed, written, and tested with great care to -replace gzip and bzip2 as the standard general\-purpose compressed format for -Unix\-like systems. +of gzip or bzip2. Lzip uses a simplified form of LZMA (Lempel\-Ziv\-Markov +chain\-Algorithm) designed to achieve complete interoperability between +implementations. The maximum dictionary size is 512 MiB so that any lzip +file can be decompressed on 32\-bit machines. Lzip provides accurate and +robust 3\-factor integrity checking. 'lzip \fB\-0\fR' compresses about as fast as +gzip, while 'lzip \fB\-9\fR' compresses most files more than bzip2. Decompression +speed is intermediate between gzip and bzip2. Lzip provides better data +recovery capabilities than gzip and bzip2. Lzip has been designed, written, +and tested with great care to replace gzip and bzip2 as general\-purpose +compressed format for Unix\-like systems. .PP Lunzip provides a 'low memory' mode able to decompress any file using as little memory as 50 kB, irrespective of the dictionary size used to @@ -74,12 +73,6 @@ set output buffer size in bytes \fB\-v\fR, \fB\-\-verbose\fR be verbose (a 2nd \fB\-v\fR gives more) .TP -\fB\-\-empty\-error\fR -exit with error status if empty member in file -.TP -\fB\-\-marking\-error\fR -exit with error status if 1st LZMA byte not 0 -.TP \fB\-\-loose\-trailing\fR allow trailing data seeming corrupt header .PP diff --git a/list.c b/list.c index c711031..7ed74ff 100644 --- a/list.c +++ b/list.c @@ -17,6 +17,7 @@ #define _FILE_OFFSET_BITS 64 +#include #include #include #include @@ -43,7 +44,7 @@ static void list_line( const unsigned long long uncomp_size, int list_files( const char * const filenames[], const int num_filenames, - const struct Cl_options * const cl_opts ) + const Cl_options * const cl_opts ) { unsigned long long total_comp = 0, total_uncomp = 0; int files = 0, retval = 0; @@ -53,7 +54,7 @@ int list_files( const char * const filenames[], const int num_filenames, for( i = 0; i < num_filenames; ++i ) { - const bool from_stdin = ( strcmp( filenames[i], "-" ) == 0 ); + const bool from_stdin = strcmp( filenames[i], "-" ) == 0; if( from_stdin ) { if( stdin_used ) continue; else stdin_used = true; } const char * const input_filename = from_stdin ? "(stdin)" : filenames[i]; struct stat in_stats; /* not used */ @@ -61,7 +62,7 @@ int list_files( const char * const filenames[], const int num_filenames, open_instream( input_filename, &in_stats, false, true ); if( infd < 0 ) { set_retval( &retval, 1 ); continue; } - struct Lzip_index lzip_index; + Lzip_index lzip_index; Li_init( &lzip_index, infd, cl_opts ); close( infd ); if( lzip_index.retval != 0 ) @@ -70,6 +71,8 @@ int list_files( const char * const filenames[], const int num_filenames, set_retval( &retval, lzip_index.retval ); Li_free( &lzip_index ); continue; } + const bool multi_empty = !from_stdin && Li_multi_empty( &lzip_index ); + if( multi_empty ) set_retval( &retval, 2 ); if( verbosity < 0 ) { Li_free( &lzip_index ); continue; } const unsigned long long udata_size = Li_udata_size( &lzip_index ); const unsigned long long cdata_size = Li_cdata_size( &lzip_index ); @@ -81,6 +84,8 @@ int list_files( const char * const filenames[], const int num_filenames, if( verbosity >= 1 ) fputs( " dict memb trail ", stdout ); fputs( " uncompressed compressed saved name\n", stdout ); } + if( multi_empty ) + { fflush( stdout ); show_file_error( input_filename, empty_msg, 0 ); } if( verbosity >= 1 ) printf( "%s %5ld %6lld ", format_ds( lzip_index.dictionary_size ), members, Li_file_size( &lzip_index ) - cdata_size ); @@ -92,8 +97,8 @@ int list_files( const char * const filenames[], const int num_filenames, fputs( " member data_pos data_size member_pos member_size\n", stdout ); for( i = 0; i < members; ++i ) { - const struct Block * db = Li_dblock( &lzip_index, i ); - const struct Block * mb = Li_mblock( &lzip_index, i ); + const Block * db = Li_dblock( &lzip_index, i ); + const Block * mb = Li_mblock( &lzip_index, i ); printf( "%6ld %14llu %14llu %14llu %14llu\n", i + 1, db->pos, db->size, mb->pos, mb->size ); } @@ -101,12 +106,16 @@ int list_files( const char * const filenames[], const int num_filenames, } fflush( stdout ); Li_free( &lzip_index ); + if( ferror( stdout ) ) break; } - if( verbosity >= 0 && files > 1 ) + if( verbosity >= 0 && files > 1 && !ferror( stdout ) ) { if( verbosity >= 1 ) fputs( " ", stdout ); list_line( total_uncomp, total_comp, "(totals)" ); fflush( stdout ); } + if( verbosity >= 0 && ( ferror( stdout ) || fclose( stdout ) != 0 ) ) + { show_file_error( "(stdout)", write_error_msg, errno ); + set_retval( &retval, 1 ); } return retval; } diff --git a/lzip.h b/lzip.h index 91a64b1..8f793a8 100644 --- a/lzip.h +++ b/lzip.h @@ -91,16 +91,16 @@ static inline void Bm_init( Bit_model * const probability ) static inline void Bm_array_init( Bit_model bm[], const int size ) { int i; for( i = 0; i < size; ++i ) Bm_init( &bm[i] ); } -struct Len_model +typedef 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]; - }; + } Len_model; -static inline void Lm_init( struct Len_model * const lm ) +static inline void Lm_init( Len_model * const lm ) { Bm_init( &lm->choice1 ); Bm_init( &lm->choice2 ); @@ -236,17 +236,14 @@ static inline bool Lt_check_consistency( const Lzip_trailer data ) } -struct Cl_options /* command-line options */ +typedef struct Cl_options /* command-line options */ { - bool ignore_empty; - bool ignore_marking; bool ignore_trailing; bool loose_trailing; - }; + } Cl_options; -static inline void Cl_options_init( struct Cl_options * cl_opts ) - { cl_opts->ignore_empty = true; cl_opts->ignore_marking = true; - cl_opts->ignore_trailing = true; cl_opts->loose_trailing = false; } +static inline void Cl_options_init( Cl_options * cl_opts ) + { cl_opts->ignore_trailing = true; cl_opts->loose_trailing = false; } static inline void set_retval( int * retval, const int new_val ) @@ -256,23 +253,25 @@ static const char * const bad_magic_msg = "Bad magic number (file not in lzip fo static const char * const bad_dict_msg = "Invalid dictionary size in member header."; static const char * const corrupt_mm_msg = "Corrupt header in multimember file."; static const char * const empty_msg = "Empty member not allowed."; -static const char * const marking_msg = "Marking data not allowed."; -static const char * const trailing_msg = "Trailing data not allowed."; static const char * const mem_msg = "Not enough memory."; +static const char * const nonzero_msg = "Nonzero first LZMA byte."; +static const char * const trailing_msg = "Trailing data not allowed."; +static const char * const write_error_msg = "Write error"; /* defined in decoder.c */ int readblock( const int fd, uint8_t * const buf, const int size ); /* defined in list.c */ int list_files( const char * const filenames[], const int num_filenames, - const struct Cl_options * const cl_opts ); + const Cl_options * const cl_opts ); /* defined in main.c */ struct stat; -struct Pretty_print; +typedef struct Pretty_print Pretty_print; +typedef struct Range_decoder Range_decoder; extern int verbosity; void * resize_buffer( void * buf, const unsigned min_size ); -void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ); +void Pp_show_msg( Pretty_print * const pp, const char * const msg ); const char * bad_version( const unsigned version ); const char * format_ds( const unsigned dictionary_size ); void show_header( const unsigned dictionary_size ); @@ -282,8 +281,7 @@ void cleanup_and_fail( const int retval ); void show_error( const char * const msg, const int errcode, const bool help ); void show_file_error( const char * const filename, const char * const msg, const int errcode ); -struct Range_decoder; void show_dprogress( const unsigned long long cfile_size, const unsigned long long partial_size, - const struct Range_decoder * const d, - struct Pretty_print * const p ); + const Range_decoder * const d, + Pretty_print * const p ); diff --git a/lzip_index.c b/lzip_index.c index ef0b0bd..91a9ec6 100644 --- a/lzip_index.c +++ b/lzip_index.c @@ -38,7 +38,7 @@ static int seek_read( const int fd, uint8_t * const buf, const int size, } -static bool add_error( struct Lzip_index * const li, const char * const msg ) +static bool add_error( Lzip_index * const li, const char * const msg ) { const int len = strlen( msg ); void * tmp = resize_buffer( li->error, li->error_size + len + 1 ); @@ -50,16 +50,15 @@ static bool add_error( struct Lzip_index * const li, const char * const msg ) } -static bool push_back_member( struct Lzip_index * const li, - const long long dp, const long long ds, - const long long mp, const long long ms, - const unsigned dict_size ) +static bool push_back_member( Lzip_index * const li, const long long dp, + const long long ds, const long long mp, + const long long ms, const unsigned dict_size ) { - struct Member * p; + Member * p; void * tmp = resize_buffer( li->member_vector, ( li->members + 1 ) * sizeof li->member_vector[0] ); if( !tmp ) { add_error( li, mem_msg ); li->retval = 1; return false; } - li->member_vector = (struct Member *)tmp; + li->member_vector = (Member *)tmp; p = &(li->member_vector[li->members]); init_member( p, dp, ds, mp, ms, dict_size ); ++li->members; @@ -67,7 +66,7 @@ static bool push_back_member( struct Lzip_index * const li, } -static void Li_free_member_vector( struct Lzip_index * const li ) +static void Li_free_member_vector( Lzip_index * const li ) { if( li->member_vector ) { free( li->member_vector ); li->member_vector = 0; } @@ -75,9 +74,9 @@ static void Li_free_member_vector( struct Lzip_index * const li ) } -static void Li_reverse_member_vector( struct Lzip_index * const li ) +static void Li_reverse_member_vector( Lzip_index * const li ) { - struct Member tmp; + Member tmp; long i; for( i = 0; i < li->members / 2; ++i ) { @@ -88,8 +87,7 @@ static void Li_reverse_member_vector( struct Lzip_index * const li ) } -static bool Li_check_header( struct Lzip_index * const li, - const Lzip_header header ) +static bool Li_check_header( Lzip_index * const li, const Lzip_header header ) { if( !Lh_check_magic( header ) ) { add_error( li, bad_magic_msg ); li->retval = 2; return false; } @@ -101,15 +99,14 @@ static bool Li_check_header( struct Lzip_index * const li, return true; } -static void Li_set_errno_error( struct Lzip_index * const li, - const char * const msg ) +static void Li_set_errno_error( Lzip_index * const li, const char * const msg ) { add_error( li, msg ); add_error( li, strerror( errno ) ); li->retval = 1; } -static void Li_set_num_error( struct Lzip_index * const li, - const char * const msg, unsigned long long num ) +static void Li_set_num_error( Lzip_index * const li, const char * const msg, + unsigned long long num ) { char buf[80]; snprintf( buf, sizeof buf, "%s%llu", msg, num ); @@ -118,22 +115,19 @@ static void Li_set_num_error( struct Lzip_index * const li, } -static bool Li_read_header( struct Lzip_index * const li, const int fd, - Lzip_header header, const long long pos, const bool ignore_marking ) +static bool Li_read_header( Lzip_index * const li, const int fd, + Lzip_header header, const long long pos ) { if( seek_read( fd, header, Lh_size, pos ) != Lh_size ) { Li_set_errno_error( li, "Error reading member header: " ); return false; } - uint8_t byte; - if( !ignore_marking && readblock( fd, &byte, 1 ) == 1 && byte != 0 ) - { add_error( li, marking_msg ); li->retval = 2; return false; } return true; } /* If successful, push last member and set pos to member header. */ -static bool Li_skip_trailing_data( struct Lzip_index * const li, const int fd, +static bool Li_skip_trailing_data( Lzip_index * const li, const int fd, unsigned long long * const pos, - const struct Cl_options * const cl_opts ) + const Cl_options * const cl_opts ) { if( *pos < min_member_size ) return false; enum { block_size = 16384, @@ -162,8 +156,8 @@ static bool Li_skip_trailing_data( struct Lzip_index * const li, const int fd, if( member_size > ipos + i || !Lt_check_consistency( *trailer ) ) continue; Lzip_header header; - if( !Li_read_header( li, fd, header, ipos + i - member_size, - cl_opts->ignore_marking ) ) return false; + if( !Li_read_header( li, fd, header, ipos + i - member_size ) ) + return false; if( !Lh_check( header ) ) continue; const Lzip_header * header2 = (const Lzip_header *)( buffer + i ); const bool full_h2 = bsize - i >= Lh_size; @@ -178,15 +172,12 @@ static bool Li_skip_trailing_data( struct Lzip_index * const li, const int fd, { add_error( li, corrupt_mm_msg ); li->retval = 2; return false; } if( !cl_opts->ignore_trailing ) { add_error( li, trailing_msg ); li->retval = 2; return false; } - const unsigned long long data_size = Lt_get_data_size( *trailer ); - if( !cl_opts->ignore_empty && data_size == 0 ) - { add_error( li, empty_msg ); li->retval = 2; return false; } *pos = ipos + i - member_size; /* good member */ const unsigned dictionary_size = Lh_get_dictionary_size( header ); if( li->dictionary_size < dictionary_size ) li->dictionary_size = dictionary_size; - return push_back_member( li, 0, data_size, *pos, member_size, - dictionary_size ); + return push_back_member( li, 0, Lt_get_data_size( *trailer ), *pos, + member_size, dictionary_size ); } if( ipos == 0 ) { Li_set_num_error( li, "Bad trailer at pos ", *pos - Lt_size ); @@ -200,8 +191,8 @@ static bool Li_skip_trailing_data( struct Lzip_index * const li, const int fd, } -bool Li_init( struct Lzip_index * const li, const int infd, - const struct Cl_options * const cl_opts ) +bool Li_init( Lzip_index * const li, const int infd, + const Cl_options * const cl_opts ) { li->member_vector = 0; li->error = 0; @@ -212,6 +203,10 @@ bool Li_init( struct Lzip_index * const li, const int infd, li->dictionary_size = 0; if( li->insize < 0 ) { Li_set_errno_error( li, "Input file is not seekable: " ); return false; } + Lzip_header header; + if( li->insize >= Lh_size && + ( !Li_read_header( li, infd, header, 0 ) || + !Li_check_header( li, header ) ) ) return false; if( li->insize < min_member_size ) { add_error( li, "Input file is too short." ); li->retval = 2; return false; } @@ -219,10 +214,6 @@ bool Li_init( struct Lzip_index * const li, const int infd, { add_error( li, "Input file is too long (2^63 bytes or more)." ); li->retval = 2; return false; } - Lzip_header header; - if( !Li_read_header( li, infd, header, 0, cl_opts->ignore_marking ) || - !Li_check_header( li, header ) ) return false; - unsigned long long pos = li->insize; /* always points to a header or to EOF */ while( pos >= min_member_size ) { @@ -237,8 +228,7 @@ bool Li_init( struct Lzip_index * const li, const int infd, return false; } Li_set_num_error( li, "Bad trailer at pos ", pos - Lt_size ); break; } - if( !Li_read_header( li, infd, header, pos - member_size, - cl_opts->ignore_marking ) ) break; + if( !Li_read_header( li, infd, header, pos - member_size ) ) break; if( !Lh_check( header ) ) /* bad header */ { if( li->members <= 0 ) @@ -246,15 +236,12 @@ bool Li_init( struct Lzip_index * const li, const int infd, return false; } Li_set_num_error( li, "Bad header at pos ", pos - member_size ); break; } - const unsigned long long data_size = Lt_get_data_size( trailer ); - if( !cl_opts->ignore_empty && data_size == 0 ) - { add_error( li, empty_msg ); li->retval = 2; break; } pos -= member_size; /* good member */ const unsigned dictionary_size = Lh_get_dictionary_size( header ); if( li->dictionary_size < dictionary_size ) li->dictionary_size = dictionary_size; - if( !push_back_member( li, 0, data_size, pos, member_size, - dictionary_size ) ) return false; + if( !push_back_member( li, 0, Lt_get_data_size( trailer ), pos, + member_size, dictionary_size ) ) return false; } if( pos != 0 || li->members <= 0 || li->retval != 0 ) { @@ -264,8 +251,7 @@ bool Li_init( struct Lzip_index * const li, const int infd, return false; } Li_reverse_member_vector( li ); - long i; - for( i = 0; ; ++i ) + long i; for( i = 0; ; ++i ) { const long long end = block_end( li->member_vector[i].dblock ); if( end < 0 || end > INT64_MAX ) @@ -281,7 +267,7 @@ bool Li_init( struct Lzip_index * const li, const int infd, } -void Li_free( struct Lzip_index * const li ) +void Li_free( Lzip_index * const li ) { Li_free_member_vector( li ); if( li->error ) { free( li->error ); li->error = 0; } diff --git a/lzip_index.h b/lzip_index.h index a4fbd56..3fb4f35 100644 --- a/lzip_index.h +++ b/lzip_index.h @@ -20,72 +20,80 @@ #endif -struct Block +typedef struct Block { long long pos, size; /* pos >= 0, size >= 0, pos + size <= INT64_MAX */ - }; + } Block; -static inline void init_block( struct Block * const b, +static inline void init_block( Block * const b, const long long p, const long long s ) { b->pos = p; b->size = s; } -static inline long long block_end( const struct Block b ) - { return b.pos + b.size; } +static inline long long block_end( const Block b ) { return b.pos + b.size; } -struct Member +typedef struct Member { - struct Block dblock, mblock; /* data block, member block */ + Block dblock, mblock; /* data block, member block */ unsigned dictionary_size; - }; + } Member; -static inline void init_member( struct Member * const m, - const long long dpos, const long long dsize, - const long long mpos, const long long msize, - const unsigned dict_size ) - { init_block( &m->dblock, dpos, dsize ); init_block( &m->mblock, mpos, msize ); - m->dictionary_size = dict_size; } +static inline void init_member( Member * const m, const long long dpos, + const long long dsize, const long long mpos, + const long long msize, const unsigned dict_size ) + { init_block( &m->dblock, dpos, dsize ); + init_block( &m->mblock, mpos, msize ); m->dictionary_size = dict_size; } -struct Lzip_index +typedef struct Lzip_index { - struct Member * member_vector; + Member * member_vector; char * error; long long insize; long members; int error_size; int retval; unsigned dictionary_size; /* largest dictionary size in the file */ - }; + } Lzip_index; -bool Li_init( struct Lzip_index * const li, const int infd, - const struct Cl_options * const cl_opts ); +bool Li_init( Lzip_index * const li, const int infd, + const Cl_options * const cl_opts ); -void Li_free( struct Lzip_index * const li ); +void Li_free( Lzip_index * const li ); -static inline long long Li_udata_size( const struct Lzip_index * const li ) +/* multimember file with empty member(s) */ +static inline bool Li_multi_empty( Lzip_index * const li ) + { + long i; + if( li->members > 1 ) + for( i = 0; i < li->members; ++i ) + if( li->member_vector[i].dblock.size == 0 ) return true; + return false; + } + +static inline long long Li_udata_size( const Lzip_index * const li ) { if( li->members <= 0 ) return 0; return block_end( li->member_vector[li->members-1].dblock ); } -static inline long long Li_cdata_size( const struct Lzip_index * const li ) +static inline long long Li_cdata_size( const Lzip_index * const li ) { if( li->members <= 0 ) return 0; return block_end( li->member_vector[li->members-1].mblock ); } /* total size including trailing data (if any) */ -static inline long long Li_file_size( const struct Lzip_index * const li ) +static inline long long Li_file_size( const Lzip_index * const li ) { if( li->insize >= 0 ) return li->insize; else return 0; } -static inline const struct Block * Li_dblock( const struct Lzip_index * const li, - const long i ) +static inline const Block * Li_dblock( const Lzip_index * const li, + const long i ) { return &li->member_vector[i].dblock; } -static inline const struct Block * Li_mblock( const struct Lzip_index * const li, - const long i ) +static inline const Block * Li_mblock( const Lzip_index * const li, + const long i ) { return &li->member_vector[i].mblock; } -static inline unsigned Li_dictionary_size( const struct Lzip_index * const li, +static inline unsigned Li_dictionary_size( const Lzip_index * const li, const long i ) { return li->member_vector[i].dictionary_size; } diff --git a/main.c b/main.c index 059d716..f46af3b 100644 --- a/main.c +++ b/main.c @@ -26,7 +26,7 @@ #include #include #include -#include /* SSIZE_MAX */ +#include /* CHAR_BIT, SSIZE_MAX */ #include #include #include /* SIZE_MAX */ @@ -39,8 +39,10 @@ #if defined __MSVCRT__ || defined __OS2__ || defined __DJGPP__ #include #if defined __MSVCRT__ +#include #define fchmod(x,y) 0 #define fchown(x,y,z) 0 +#define mkdir(name,mode) _mkdir(name) #define SIGHUP SIGTERM #define S_ISSOCK(x) 0 #ifndef S_IRGRP @@ -84,7 +86,7 @@ static const struct { const char * from; const char * to; } known_extensions[] = { ".tlz", ".tar" }, { 0, 0 } }; -enum Mode { m_compress, m_decompress, m_list, m_test }; +typedef enum Mode { m_compress, m_decompress, m_list, m_test } Mode; /* Variables used in signal handler context. They are not declared volatile because the handler never returns. */ @@ -97,19 +99,18 @@ static void show_help( void ) { printf( "Lunzip is a decompressor for the lzip format written in C. Its small size\n" "makes it well suited for embedded devices or software installers that need\n" - "to decompress files but don't need compression capabilities. Lunzip is\n" - "compatible with lzip 1.4 or newer.\n" + "to decompress files but don't need compression capabilities.\n" "\nLzip is a lossless data compressor with a user interface similar to the one\n" - "of gzip or bzip2. Lzip uses a simplified form of the 'Lempel-Ziv-Markov\n" - "chain-Algorithm' (LZMA) stream format to maximize interoperability. The\n" - "maximum dictionary size is 512 MiB so that any lzip file can be decompressed\n" - "on 32-bit machines. Lzip provides accurate and robust 3-factor integrity\n" - "checking. Lzip can compress about as fast as gzip (lzip -0) or compress most\n" - "files more than bzip2 (lzip -9). Decompression speed is intermediate between\n" - "gzip and bzip2. Lzip is better than gzip and bzip2 from a data recovery\n" - "perspective. Lzip has been designed, written, and tested with great care to\n" - "replace gzip and bzip2 as the standard general-purpose compressed format for\n" - "Unix-like systems.\n" + "of gzip or bzip2. Lzip uses a simplified form of LZMA (Lempel-Ziv-Markov\n" + "chain-Algorithm) designed to achieve complete interoperability between\n" + "implementations. The maximum dictionary size is 512 MiB so that any lzip\n" + "file can be decompressed on 32-bit machines. Lzip provides accurate and\n" + "robust 3-factor integrity checking. 'lzip -0' compresses about as fast as\n" + "gzip, while 'lzip -9' compresses most files more than bzip2. Decompression\n" + "speed is intermediate between gzip and bzip2. Lzip provides better data\n" + "recovery capabilities than gzip and bzip2. Lzip has been designed, written,\n" + "and tested with great care to replace gzip and bzip2 as general-purpose\n" + "compressed format for Unix-like systems.\n" "\nLunzip provides a 'low memory' mode able to decompress any file using as\n" "little memory as 50 kB, irrespective of the dictionary size used to\n" "compress the file. To activate it, specify the size of the output buffer\n" @@ -122,22 +123,20 @@ static void show_help( void ) "the whole dictionary at once.\n" "\nUsage: %s [options] [files]\n", invocation_name ); printf( "\nOptions:\n" - " -h, --help display this help and exit\n" - " -V, --version output version information and exit\n" - " -a, --trailing-error exit with error status if trailing data\n" - " -c, --stdout write to standard output, keep input files\n" - " -d, --decompress decompress (this is the default)\n" - " -f, --force overwrite existing output files\n" - " -k, --keep keep (don't delete) input files\n" - " -l, --list print (un)compressed file sizes\n" - " -o, --output= write to , keep input files\n" - " -q, --quiet suppress all messages\n" - " -t, --test test compressed file integrity\n" - " -u, --buffer-size= set output buffer size in bytes\n" - " -v, --verbose be verbose (a 2nd -v gives more)\n" - " --empty-error exit with error status if empty member in file\n" - " --marking-error exit with error status if 1st LZMA byte not 0\n" - " --loose-trailing allow trailing data seeming corrupt header\n" + " -h, --help display this help and exit\n" + " -V, --version output version information and exit\n" + " -a, --trailing-error exit with error status if trailing data\n" + " -c, --stdout write to standard output, keep input files\n" + " -d, --decompress decompress (this is the default)\n" + " -f, --force overwrite existing output files\n" + " -k, --keep keep (don't delete) input files\n" + " -l, --list print (un)compressed file sizes\n" + " -o, --output= write to , keep input files\n" + " -q, --quiet suppress all messages\n" + " -t, --test test compressed file integrity\n" + " -u, --buffer-size= set output buffer size in bytes\n" + " -v, --verbose be verbose (a 2nd -v gives more)\n" + " --loose-trailing allow trailing data seeming corrupt header\n" "\nIf no file names are given, or if a file is '-', lunzip decompresses\n" "from standard input to standard output.\n" "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" @@ -189,7 +188,7 @@ struct Pretty_print bool first_post; }; -static void Pp_init( struct Pretty_print * const pp, +static void Pp_init( Pretty_print * const pp, const char * const filenames[], const int num_filenames ) { pp->name = 0; @@ -210,8 +209,10 @@ static void Pp_init( struct Pretty_print * const pp, if( pp->longest_name == 0 ) pp->longest_name = stdin_name_len; } -static void Pp_set_name( struct Pretty_print * const pp, - const char * const filename ) +void Pp_free( Pretty_print * const pp ) + { if( pp->padded_name ) { free( pp->padded_name ); pp->padded_name = 0; } } + +static void Pp_set_name( Pretty_print * const pp, const char * const filename ) { unsigned name_len, padded_name_len, i = 0; @@ -229,10 +230,10 @@ static void Pp_set_name( struct Pretty_print * const pp, pp->first_post = true; } -static void Pp_reset( struct Pretty_print * const pp ) +static void Pp_reset( Pretty_print * const pp ) { if( pp->name && pp->name[0] ) pp->first_post = true; } -void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ) +void Pp_show_msg( Pretty_print * const pp, const char * const msg ) { if( verbosity < 0 ) return; if( pp->first_post ) @@ -262,7 +263,7 @@ const char * format_ds( const unsigned dictionary_size ) const char * p = ""; const char * np = " "; unsigned num = dictionary_size; - bool exact = ( num % factor == 0 ); + bool exact = num % factor == 0; int i; for( i = 0; i < n && ( num > 9999 || ( exact && num >= factor ) ); ++i ) { num /= factor; if( num % factor != 0 ) exact = false; @@ -278,7 +279,7 @@ void show_header( const unsigned dictionary_size ) } -/* separate numbers of 5 or more digits in groups of 3 digits using '_' */ +/* separate numbers of 6 or more digits in groups of 3 digits using '_' */ static const char * format_num3( unsigned long long num ) { enum { buffers = 8, bufsize = 4 * sizeof num, n = 10 }; @@ -290,7 +291,7 @@ static const char * format_num3( unsigned long long num ) char * const buf = buffer[current++]; current %= buffers; char * p = buf + bufsize - 1; /* fill the buffer backwards */ *p = 0; /* terminator */ - if( num > 1024 ) + if( num > 9999 ) { char prefix = 0; /* try binary first, then si */ for( i = 0; i < n && num != 0 && num % 1024 == 0; ++i ) @@ -301,7 +302,7 @@ static const char * format_num3( unsigned long long num ) { num /= 1000; prefix = si_prefix[i]; } if( prefix ) *(--p) = prefix; } - const bool split = num >= 10000; + const bool split = num >= 100000; for( i = 0; ; ) { @@ -336,7 +337,7 @@ static unsigned long getnum( const char * const arg, if( !errno && tail[0] ) { - const unsigned factor = ( tail[1] == 'i' ) ? 1024 : 1000; + const unsigned factor = (tail[1] == 'i') ? 1024 : 1000; int exponent = 0; /* 0 = bad multiplier */ int i; switch( tail[0] ) @@ -386,7 +387,7 @@ static int get_dict_size( const char * const arg, const char * const option_name } -static void set_mode( enum Mode * const program_modep, const enum Mode new_mode ) +static void set_mode( Mode * const program_modep, const Mode new_mode ) { if( *program_modep != m_compress && *program_modep != new_mode ) { @@ -448,9 +449,9 @@ int open_instream( const char * const name, struct stat * const in_statsp, { const int i = fstat( infd, in_statsp ); const mode_t mode = in_statsp->st_mode; - const bool can_read = ( i == 0 && !reg_only && - ( S_ISBLK( mode ) || S_ISCHR( mode ) || - S_ISFIFO( mode ) || S_ISSOCK( mode ) ) ); + const bool can_read = i == 0 && !reg_only && + ( S_ISBLK( mode ) || S_ISCHR( mode ) || + S_ISFIFO( mode ) || S_ISSOCK( mode ) ); if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || one_to_one ) ) ) { if( verbosity >= 0 ) @@ -479,7 +480,7 @@ static bool make_dirs( const char * const name ) while( i < dirsize && name[i] != '/' ) ++i; if( first < i ) { - char partial[i+1]; memcpy( partial, name, i ); partial[i] = 0; + char partial[i+1]; memcpy( partial, name, i ); partial[i] = 0; /* vla */ const mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; struct stat st; if( stat( partial, &st ) == 0 ) @@ -550,7 +551,7 @@ static void signal_handler( int sig ) static bool check_tty_in( const char * const input_filename, const int infd, - const enum Mode program_mode, int * const retval ) + const Mode program_mode, int * const retval ) { if( isatty( infd ) ) /* for example /dev/tty */ { show_file_error( input_filename, @@ -604,7 +605,7 @@ static unsigned char xdigit( const unsigned value ) /* hex digit for 'value' */ static bool show_trailing_data( const uint8_t * const data, const int size, - struct Pretty_print * const pp, const bool all, + Pretty_print * const pp, const bool all, const int ignore_trailing ) /* -1 = show */ { if( verbosity >= 4 || ignore_trailing <= 0 ) @@ -632,16 +633,17 @@ static bool show_trailing_data( const uint8_t * const data, const int size, static int decompress( const unsigned long long cfile_size, const int infd, - const struct Cl_options * const cl_opts, - struct Pretty_print * const pp, - const unsigned buffer_size, const bool testing ) + const Cl_options * const cl_opts, Pretty_print * const pp, + const unsigned buffer_size, + const bool from_stdin, const bool testing ) { unsigned long long partial_file_pos = 0; - struct Range_decoder rdec; + Range_decoder rdec; int retval = 0; bool first_member; if( !Rd_init( &rdec, infd ) ) { show_error( mem_msg, 0, false ); cleanup_and_fail( 1 ); } + bool empty = false, multi = false; for( first_member = true; ; first_member = false ) { @@ -681,14 +683,14 @@ static int decompress( const unsigned long long cfile_size, const int infd, if( verbosity >= 2 || ( verbosity == 1 && first_member ) ) Pp_show_msg( pp, 0 ); - struct LZ_decoder decoder; + LZ_decoder decoder; if( !LZd_init( &decoder, &rdec, buffer_size, dictionary_size, outfd ) ) { Pp_show_msg( pp, "Not enough memory. Try a smaller output buffer size." ); retval = 1; break; } show_dprogress( cfile_size, partial_file_pos, &rdec, pp ); /* init */ - const int result = LZd_decode_member( &decoder, cl_opts, pp ); + const int result = LZd_decode_member( &decoder, pp ); partial_file_pos += Rd_member_position( &rdec ); LZd_free( &decoder ); if( result != 0 ) @@ -700,16 +702,19 @@ static int decompress( const unsigned long long cfile_size, const int infd, "File ends unexpectedly" : "Decoder error", partial_file_pos ); } - else if( result == 5 ) Pp_show_msg( pp, empty_msg ); - else if( result == 6 ) Pp_show_msg( pp, marking_msg ); + else if( result == 5 ) Pp_show_msg( pp, nonzero_msg ); retval = 2; break; } + if( !from_stdin ) { multi = !first_member; + if( LZd_data_position( &decoder ) == 0 ) empty = true; } if( verbosity >= 2 ) { fputs( testing ? "ok\n" : "done\n", stderr ); Pp_reset( pp ); } } Rd_free( &rdec ); if( verbosity == 1 && retval == 0 ) fputs( testing ? "ok\n" : "done\n", stderr ); + if( empty && multi && retval == 0 ) + { show_file_error( pp->name, empty_msg, 0 ); retval = 2; } return retval; } @@ -747,13 +752,13 @@ static void internal_error( const char * const msg ) void show_dprogress( const unsigned long long cfile_size, const unsigned long long partial_size, - const struct Range_decoder * const d, - struct Pretty_print * const p ) + const Range_decoder * const d, + Pretty_print * const p ) { static unsigned long long csize = 0; /* file_size / 100 */ static unsigned long long psize = 0; - static const struct Range_decoder * rdec = 0; - static struct Pretty_print * pp = 0; + static const Range_decoder * rdec = 0; + static Pretty_print * pp = 0; static int counter = 0; static bool enabled = true; @@ -780,41 +785,38 @@ int main( const int argc, const char * const argv[] ) { const char * default_output_filename = ""; unsigned buffer_size = max_dictionary_size; - enum Mode program_mode = m_compress; - int i; - struct Cl_options cl_opts; /* command-line options */ + Mode program_mode = m_compress; + Cl_options cl_opts; /* command-line options */ Cl_options_init( &cl_opts ); bool force = false; bool keep_input_files = false; bool to_stdout = false; if( argc > 0 ) invocation_name = argv[0]; - enum { opt_eer = 256, opt_lt, opt_mer }; - const struct ap_Option options[] = + enum { opt_lt = 256 }; + const ap_Option options[] = { - { 'a', "trailing-error", ap_no }, - { 'c', "stdout", ap_no }, - { 'd', "decompress", ap_no }, - { 'f', "force", ap_no }, - { 'h', "help", ap_no }, - { 'k', "keep", ap_no }, - { 'l', "list", ap_no }, - { 'n', "threads", ap_yes }, - { 'o', "output", ap_yes }, - { 'q', "quiet", ap_no }, - { 't', "test", ap_no }, - { 'u', "buffer-size", ap_yes }, - { 'v', "verbose", ap_no }, - { 'V', "version", ap_no }, - { opt_eer, "empty-error", ap_no }, - { opt_lt, "loose-trailing", ap_no }, - { opt_mer, "marking-error", ap_no }, - { 0, 0, ap_no } }; + { 'a', "trailing-error", ap_no }, + { 'c', "stdout", ap_no }, + { 'd', "decompress", ap_no }, + { 'f', "force", ap_no }, + { 'h', "help", ap_no }, + { 'k', "keep", ap_no }, + { 'l', "list", ap_no }, + { 'n', "threads", ap_yes }, + { 'o', "output", ap_yes }, + { 'q', "quiet", ap_no }, + { 't', "test", ap_no }, + { 'u', "buffer-size", ap_yes }, + { 'v', "verbose", ap_no }, + { 'V', "version", ap_no }, + { opt_lt, "loose-trailing", ap_no }, + { 0, 0, ap_no } }; CRC32_init(); /* static because valgrind complains and memory management in C sucks */ - static struct Arg_parser parser; + static Arg_parser parser; if( !ap_init( &parser, argc, argv, options, 0 ) ) { show_error( mem_msg, 0, false ); return 1; } if( ap_error( &parser ) ) /* bad option */ @@ -836,7 +838,7 @@ int main( const int argc, const char * const argv[] ) case 'h': show_help(); return 0; case 'k': keep_input_files = true; break; case 'l': set_mode( &program_mode, m_list ); break; - case 'n': break; + case 'n': break; /* ignored */ case 'o': if( strcmp( arg, "-" ) == 0 ) to_stdout = true; else { default_output_filename = arg; } break; case 'q': verbosity = -1; break; @@ -844,9 +846,7 @@ int main( const int argc, const char * const argv[] ) case 'u': buffer_size = get_dict_size( arg, pn ); break; case 'v': if( verbosity < 4 ) ++verbosity; break; case 'V': show_version(); return 0; - case opt_eer: cl_opts.ignore_empty = false; break; - case opt_lt: cl_opts.loose_trailing = true; break; - case opt_mer: cl_opts.ignore_marking = false; break; + case opt_lt: cl_opts.loose_trailing = true; break; default: internal_error( "uncaught option." ); } } /* end process options */ @@ -861,6 +861,7 @@ int main( const int argc, const char * const argv[] ) filenames = resize_buffer( filenames, num_filenames * sizeof filenames[0] ); filenames[0] = "-"; + int i; bool filenames_given = false; for( i = 0; argind + i < ap_arguments( &parser ); ++i ) { @@ -900,7 +901,7 @@ int main( const int argc, const char * const argv[] ) if( !to_stdout && program_mode != m_test && ( filenames_given || to_file ) ) set_signals( signal_handler ); - static struct Pretty_print pp; + static Pretty_print pp; Pp_init( &pp, filenames, num_filenames ); int failed_tests = 0; @@ -912,9 +913,10 @@ int main( const int argc, const char * const argv[] ) { const char * input_filename = ""; int infd; + const bool from_stdin = strcmp( filenames[i], "-" ) == 0; Pp_set_name( &pp, filenames[i] ); - if( strcmp( filenames[i], "-" ) == 0 ) + if( from_stdin ) { if( stdin_used ) continue; else stdin_used = true; infd = STDIN_FILENO; @@ -963,7 +965,7 @@ int main( const int argc, const char * const argv[] ) ( input_filename[0] && S_ISREG( in_stats.st_mode ) ) ? ( in_stats.st_size + 99 ) / 100 : 0; int tmp = decompress( cfile_size, infd, &cl_opts, &pp, buffer_size, - program_mode == m_test ); + from_stdin, program_mode == m_test ); if( close( infd ) != 0 ) { show_file_error( pp.name, "Error closing input file", errno ); set_retval( &tmp, 1 ); } @@ -990,6 +992,7 @@ int main( const int argc, const char * const argv[] ) program_name, failed_tests, ( failed_tests == 1 ) ? "file" : "files" ); free( output_filename ); + Pp_free( &pp ); free( filenames ); ap_free( &parser ); return retval; diff --git a/testsuite/check.sh b/testsuite/check.sh index 1d36069..296e165 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -28,18 +28,17 @@ if [ -d tmp ] ; then rm -rf tmp ; fi mkdir tmp cd "${objdir}"/tmp || framework_failure -cat "${testdir}"/test.txt > in || framework_failure +cp "${testdir}"/test.txt in || framework_failure in_lz="${testdir}"/test.txt.lz -in_em="${testdir}"/test_em.txt.lz +em_lz="${testdir}"/em.lz fox_lz="${testdir}"/fox.lz -fox6_lz="${testdir}"/fox6.lz -f6mk_lz="${testdir}"/fox6_mark.lz +fnz_lz="${testdir}"/fox_nz.lz fail=0 test_failed() { fail=1 ; printf " $1" ; [ -z "$2" ] || printf "($2)" ; } printf "testing lunzip-%s..." "$2" -cat "${in_lz}" > uin.lz || framework_failure +cp "${in_lz}" uin.lz || framework_failure for i in bad_size -1 0 4095 513MiB 1G 1T 1P 1E 1Z 1Y 10KB ; do "${LZIP}" -dfkq -u $i uin.lz [ $? = 1 ] || test_failed $LINENO $i @@ -96,37 +95,25 @@ printf "LZIP\001+.............................." | "${LZIP}" -t 2> /dev/null printf "\ntesting decompression..." -for i in "${in_lz}" "${in_em}" ; do - "${LZIP}" -lq "$i" || test_failed $LINENO "$i" - "${LZIP}" -t "$i" || test_failed $LINENO "$i" - "${LZIP}" -d "$i" -o out || test_failed $LINENO "$i" - cmp in out || test_failed $LINENO "$i" - "${LZIP}" -cd "$i" > out || test_failed $LINENO "$i" - cmp in out || test_failed $LINENO "$i" - "${LZIP}" -d "$i" -o - > out || test_failed $LINENO "$i" - cmp in out || test_failed $LINENO "$i" - "${LZIP}" -d < "$i" > out || test_failed $LINENO "$i" - cmp in out || test_failed $LINENO "$i" - rm -f out || framework_failure -done +"${LZIP}" -l "${in_lz}" > /dev/null || test_failed $LINENO +"${LZIP}" -t "${in_lz}" || test_failed $LINENO +"${LZIP}" -d "${in_lz}" -o out || test_failed $LINENO +cmp in out || test_failed $LINENO +"${LZIP}" -cd "${in_lz}" > out || test_failed $LINENO +cmp in out || test_failed $LINENO +"${LZIP}" -d "${in_lz}" -o - > out || test_failed $LINENO +cmp in out || test_failed $LINENO +"${LZIP}" -d < "${in_lz}" > out || test_failed $LINENO +cmp in out || test_failed $LINENO +rm -f out || framework_failure -lines=`"${LZIP}" -tvv "${in_em}" 2>&1 | wc -l` || test_failed $LINENO -[ "${lines}" -eq 8 ] || test_failed $LINENO "${lines}" -"${LZIP}" -tq "${in_em}" --empty-error -[ $? = 2 ] || test_failed $LINENO - -lines=`"${LZIP}" -lvv "${in_em}" | wc -l` || test_failed $LINENO -[ "${lines}" -eq 11 ] || test_failed $LINENO "${lines}" -"${LZIP}" -lq "${in_em}" --empty-error -[ $? = 2 ] || test_failed $LINENO - -cat "${in_lz}" > out.lz || framework_failure +cp "${in_lz}" out.lz || framework_failure "${LZIP}" -dk out.lz || test_failed $LINENO cmp in out || test_failed $LINENO rm -f out || framework_failure "${LZIP}" -cd "${fox_lz}" > fox || test_failed $LINENO -cat fox > copy || framework_failure -cat "${in_lz}" > copy.lz || framework_failure +cp fox copy || framework_failure +cp "${in_lz}" copy.lz || framework_failure "${LZIP}" -d copy.lz out.lz 2> /dev/null # skip copy, decompress out [ $? = 1 ] || test_failed $LINENO [ ! -e out.lz ] || test_failed $LINENO @@ -140,7 +127,6 @@ rm -f copy out || framework_failure printf "to be overwritten" > out || framework_failure "${LZIP}" -df -o out < "${in_lz}" || test_failed $LINENO cmp in out || test_failed $LINENO -rm -f out || framework_failure "${LZIP}" -d -o ./- "${in_lz}" || test_failed $LINENO cmp in ./- || test_failed $LINENO rm -f ./- || framework_failure @@ -148,12 +134,12 @@ rm -f ./- || framework_failure cmp in ./- || test_failed $LINENO rm -f ./- || framework_failure -cat "${in_lz}" > anyothername || framework_failure +cp "${in_lz}" anyothername || framework_failure "${LZIP}" -dv - anyothername - < "${in_lz}" > out 2> /dev/null || test_failed $LINENO cmp in out || test_failed $LINENO cmp in anyothername.out || test_failed $LINENO -rm -f out anyothername.out || framework_failure +rm -f anyothername.out || framework_failure "${LZIP}" -lq in "${in_lz}" [ $? = 2 ] || test_failed $LINENO @@ -170,7 +156,7 @@ cat out in | cmp in - || test_failed $LINENO # out must be empty [ $? = 1 ] || test_failed $LINENO cmp in out || test_failed $LINENO rm -f out || framework_failure -cat "${in_lz}" > out.lz || framework_failure +cp "${in_lz}" out.lz || framework_failure for i in 1 2 3 4 5 6 7 ; do printf "g" >> out.lz || framework_failure "${LZIP}" -alvv out.lz "${in_lz}" > /dev/null 2>&1 @@ -191,7 +177,7 @@ cmp in out || test_failed $LINENO rm -f out || framework_failure cat in in > in2 || framework_failure -"${LZIP}" -lq "${in_lz}" "${in_lz}" || test_failed $LINENO +"${LZIP}" -l "${in_lz}" "${in_lz}" > /dev/null || test_failed $LINENO "${LZIP}" -t "${in_lz}" "${in_lz}" || test_failed $LINENO "${LZIP}" -cd "${in_lz}" "${in_lz}" -o out > out2 || test_failed $LINENO [ ! -e out ] || test_failed $LINENO # override -o @@ -202,6 +188,11 @@ cmp in2 out2 || test_failed $LINENO rm -f out2 || framework_failure cat "${in_lz}" "${in_lz}" > out2.lz || framework_failure +lines=`"${LZIP}" -tvv out2.lz 2>&1 | wc -l` || test_failed $LINENO +[ "${lines}" -eq 2 ] || test_failed $LINENO "${lines}" +lines=`"${LZIP}" -lvv out2.lz | wc -l` || test_failed $LINENO +[ "${lines}" -eq 5 ] || test_failed $LINENO "${lines}" + printf "\ngarbage" >> out2.lz || framework_failure "${LZIP}" -tvvvv out2.lz 2> /dev/null || test_failed $LINENO "${LZIP}" -alq out2.lz @@ -233,16 +224,6 @@ for i in 12 5120 6Ki 29 512KiB ; do cmp in2 out2 || test_failed $LINENO $i rm -f out2 || framework_failure done -rm -f in2 out || framework_failure - -"${LZIP}" -cd "${fox6_lz}" > out || test_failed $LINENO -"${LZIP}" -cd "${f6mk_lz}" > copy || test_failed $LINENO -cmp copy out || test_failed $LINENO -rm -f copy out || framework_failure -"${LZIP}" -lq "${f6mk_lz}" --marking-error -[ $? = 2 ] || test_failed $LINENO -"${LZIP}" -tq "${f6mk_lz}" --marking-error -[ $? = 2 ] || test_failed $LINENO "${LZIP}" -d "${fox_lz}" -o a/b/c/fox || test_failed $LINENO cmp fox a/b/c/fox || test_failed $LINENO @@ -254,13 +235,61 @@ rm -rf a || framework_failure [ $? = 1 ] || test_failed $LINENO [ ! -e a ] || test_failed $LINENO +touch empty || framework_failure +cp "${em_lz}" em.lz || framework_failure +"${LZIP}" -l em.lz > /dev/null || test_failed $LINENO +"${LZIP}" -dk em.lz || test_failed $LINENO +cmp empty em || test_failed $LINENO +cat em.lz em.lz | "${LZIP}" -t || test_failed $LINENO +cat em.lz em.lz | "${LZIP}" -d > em || test_failed $LINENO +cmp empty em || test_failed $LINENO +cat em.lz "${in_lz}" | "${LZIP}" -t || test_failed $LINENO +cat em.lz "${in_lz}" | "${LZIP}" -d > out || test_failed $LINENO +cmp in out || test_failed $LINENO +cat "${in_lz}" em.lz | "${LZIP}" -t || test_failed $LINENO +cat "${in_lz}" em.lz | "${LZIP}" -d > out || test_failed $LINENO +cmp in out || test_failed $LINENO + printf "\ntesting bad input..." +cat em.lz em.lz > ee.lz || framework_failure +"${LZIP}" -l < ee.lz > /dev/null || test_failed $LINENO +"${LZIP}" -t < ee.lz || test_failed $LINENO +"${LZIP}" -d < ee.lz > em || test_failed $LINENO +cmp empty em || test_failed $LINENO +"${LZIP}" -lq ee.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -tq ee.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -dq ee.lz +[ $? = 2 ] || test_failed $LINENO +[ ! -e ee ] || test_failed $LINENO +"${LZIP}" -cdq ee.lz > em +[ $? = 2 ] || test_failed $LINENO +cmp empty em || test_failed $LINENO +rm -f empty em || framework_failure +cat "${in_lz}" em.lz "${in_lz}" > inein.lz || framework_failure +"${LZIP}" -l < inein.lz > /dev/null || test_failed $LINENO +"${LZIP}" -t < inein.lz || test_failed $LINENO +"${LZIP}" -d < inein.lz > out2 || test_failed $LINENO +cmp in2 out2 || test_failed $LINENO +"${LZIP}" -lq inein.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -tq inein.lz +[ $? = 2 ] || test_failed $LINENO +"${LZIP}" -dq inein.lz +[ $? = 2 ] || test_failed $LINENO +[ ! -e inein ] || test_failed $LINENO +"${LZIP}" -cdq inein.lz > out2 +[ $? = 2 ] || test_failed $LINENO +cmp in2 out2 || test_failed $LINENO +rm -f in2 out2 inein.lz em.lz || framework_failure + headers='LZIp LZiP LZip LzIP LzIp LziP lZIP lZIp lZiP lzIP' -body='\001\014\000\203\377\373\377\377\300\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000$\000\000\000\000\000\000\000' -cat "${in_lz}" > int.lz || framework_failure +body='\001\014\000\000\101\376\367\377\377\340\000\200\000\215\357\002\322\001\000\000\000\000\000\000\000\045\000\000\000\000\000\000\000' +cp "${in_lz}" int.lz || framework_failure printf "LZIP${body}" >> int.lz || framework_failure -if "${LZIP}" -tq int.lz ; then +if "${LZIP}" -t int.lz ; then for header in ${headers} ; do printf "${header}${body}" > int.lz || framework_failure "${LZIP}" -lq int.lz # first member @@ -279,7 +308,7 @@ if "${LZIP}" -tq int.lz ; then [ $? = 2 ] || test_failed $LINENO ${header} "${LZIP}" -cdq --loose-trailing int.lz > /dev/null [ $? = 2 ] || test_failed $LINENO ${header} - cat "${in_lz}" > int.lz || framework_failure + cp "${in_lz}" int.lz || framework_failure printf "${header}${body}" >> int.lz || framework_failure "${LZIP}" -lq int.lz # trailing data [ $? = 2 ] || test_failed $LINENO ${header} @@ -289,7 +318,7 @@ if "${LZIP}" -tq int.lz ; then [ $? = 2 ] || test_failed $LINENO ${header} "${LZIP}" -cdq int.lz > /dev/null [ $? = 2 ] || test_failed $LINENO ${header} - "${LZIP}" -lq --loose-trailing int.lz || + "${LZIP}" -l --loose-trailing int.lz > /dev/null || test_failed $LINENO ${header} "${LZIP}" -t --loose-trailing int.lz || test_failed $LINENO ${header} @@ -307,10 +336,14 @@ if "${LZIP}" -tq int.lz ; then [ $? = 2 ] || test_failed $LINENO ${header} done else - printf "\nwarning: skipping header test: 'printf' does not work on your system." + printf "warning: skipping header test: 'printf' does not work on your system." fi rm -f int.lz || framework_failure +"${LZIP}" -l "${fnz_lz}" > /dev/null || test_failed $LINENO +"${LZIP}" -tq "${fnz_lz}" +[ $? = 2 ] || test_failed $LINENO + for i in fox_v2.lz fox_s11.lz fox_de20.lz \ fox_bcrc.lz fox_crc0.lz fox_das46.lz fox_mes81.lz ; do "${LZIP}" -tq "${testdir}"/$i @@ -322,13 +355,13 @@ for i in fox_bcrc.lz fox_crc0.lz fox_das46.lz fox_mes81.lz ; do [ $? = 2 ] || test_failed $LINENO $i cmp fox out || test_failed $LINENO $i done -rm -f fox out || framework_failure +rm -f fox || framework_failure cat "${in_lz}" "${in_lz}" > in2.lz || framework_failure cat "${in_lz}" "${in_lz}" "${in_lz}" > in3.lz || framework_failure -if dd if=in3.lz of=trunc.lz bs=14752 count=1 2> /dev/null && - [ -e trunc.lz ] && cmp in2.lz trunc.lz > /dev/null 2>&1 ; then - for i in 6 20 14734 14753 14754 14755 14756 14757 14758 ; do +if dd if=in3.lz of=trunc.lz bs=14682 count=1 2> /dev/null && + [ -e trunc.lz ] && cmp in2.lz trunc.lz ; then + for i in 6 20 14664 14683 14684 14685 14686 14687 14688 ; do dd if=in3.lz of=trunc.lz bs=$i count=1 2> /dev/null "${LZIP}" -lq trunc.lz [ $? = 2 ] || test_failed $LINENO $i @@ -342,11 +375,11 @@ if dd if=in3.lz of=trunc.lz bs=14752 count=1 2> /dev/null && [ $? = 2 ] || test_failed $LINENO $i done else - printf "\nwarning: skipping truncation test: 'dd' does not work on your system." + printf "warning: skipping truncation test: 'dd' does not work on your system." fi rm -f in2.lz in3.lz trunc.lz || framework_failure -cat "${in_lz}" > ingin.lz || framework_failure +cp "${in_lz}" ingin.lz || framework_failure printf "g" >> ingin.lz || framework_failure cat "${in_lz}" >> ingin.lz || framework_failure "${LZIP}" -lq ingin.lz @@ -355,17 +388,21 @@ cat "${in_lz}" >> ingin.lz || framework_failure [ $? = 2 ] || test_failed $LINENO "${LZIP}" -atq < ingin.lz [ $? = 2 ] || test_failed $LINENO -"${LZIP}" -acdq ingin.lz > /dev/null +"${LZIP}" -acdq ingin.lz > out [ $? = 2 ] || test_failed $LINENO -"${LZIP}" -adq < ingin.lz > /dev/null +cmp in out || test_failed $LINENO +"${LZIP}" -adq < ingin.lz > out [ $? = 2 ] || test_failed $LINENO +cmp in out || test_failed $LINENO "${LZIP}" -t ingin.lz || test_failed $LINENO "${LZIP}" -t < ingin.lz || test_failed $LINENO +"${LZIP}" -dk ingin.lz || test_failed $LINENO +cmp in ingin || test_failed $LINENO "${LZIP}" -cd ingin.lz > out || test_failed $LINENO cmp in out || test_failed $LINENO "${LZIP}" -d < ingin.lz > out || test_failed $LINENO cmp in out || test_failed $LINENO -rm -f out ingin.lz || framework_failure +rm -f out ingin ingin.lz || framework_failure echo if [ ${fail} = 0 ] ; then diff --git a/testsuite/em.lz b/testsuite/em.lz new file mode 100644 index 0000000000000000000000000000000000000000..ec607250eb85b946c420050d70e7514ca2b565f6 GIT binary patch literal 36 acmeZ?@(f_)VQBvU`~Uv~45&Z_$^`(o*#@it literal 0 HcmV?d00001 diff --git a/testsuite/fox6.lz b/testsuite/fox6.lz deleted file mode 100644 index 8401b9912049eb24a0ddb778211eba0ec894686c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 480 zcmeZ?@(f_)VbGG|Sj4QF_R+iI=pxqtde3fNQjl6}^5c>03rDkGci1v_+dL64QRjTU jIqmVA>95u)|NqywfFbKlz-wIw2nc}EKBMLhs_+803rDkGci1v_+dL64QRjTU vIqmVA>95u)|NqywfFbKlz-wIw2nc}EJ~S~em&)!-q_TNMR5mZ0YUTj|#dEtN diff --git a/testsuite/fox_nz.lz b/testsuite/fox_nz.lz new file mode 100644 index 0000000000000000000000000000000000000000..44a4b58b881d207627f6081d8ad04708c25400dc GIT binary patch literal 80 zcmeZ?@(f_)Nz{_!Sj4QF_R+iI=pxqtde3fNQjl6}^5c>03rDkGci1v_+dL64QRjTU cIqmVA>95u)|NqywfFbKlz-wIw2nc}E0AJQ0O8@`> literal 0 HcmV?d00001 diff --git a/testsuite/test.txt b/testsuite/test.txt index 9196a3a..423f0c0 100644 --- a/testsuite/test.txt +++ b/testsuite/test.txt @@ -1,8 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -339,8 +338,7 @@ Public License instead of this License. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/testsuite/test.txt.lz b/testsuite/test.txt.lz index 22cea6e4d2a58b4dcb006541dcdb8defabba0cbb..5dc169f7fb6e952f17e7a21ce3691eeaac2fb3aa 100644 GIT binary patch delta 7294 zcmV-^9D(D|IjuR6Zhys3yJ(c8peBW%f-elXuLG8+YcW?HC;m#{D%pU^WiRDBah9%H zoNKDm$X*ivN}frZPJV^dtsX$0%Z^SSxrOZEO@c$q7owY! z`W;W~c7VLKKJvfUJ)mGF=tySp{PxY$8`#(o1p?=vxoYM*hkx#@T($B}rE}wqiNbai zPkS|>8W_N@+GNU=i-?`g4gP^bKc=(O|BTS>tf}Y}sWaDOa9)@f3Z>pC@4M?MqsMyf z$l9a4FCJZ;`)tQ5Wbrs^K<$Gkx9@N*`^#x7IXK%ex=T%-ID=$T@CGUn*XOjK7%aI^ z{P$K|lLRk7DSwY<;3s2ge)hV`mKFEf_=bs$Oa|MsKB4EOEq5 zbi#35GkG6xskoc^ai7hojVayH{;Ap%0D7qkn{2{=+J9YtzU8rM;x_SA8&KAYW1k_h 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 I000000MqzJBme*a diff --git a/testsuite/test_em.txt.lz b/testsuite/test_em.txt.lz deleted file mode 100644 index 7e96250d9ec70bc7d8cb173f438c76be447e6b6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14024 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 Gt^OBPlQ$#) From 8906af078dfe96418de24afe144d339fe7b98324 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 17 Feb 2025 22:33:16 +0100 Subject: [PATCH 10/10] Adding upstream version 1.15. Signed-off-by: Daniel Baumann --- ChangeLog | 6 +++--- INSTALL | 5 +++-- README | 18 +++++++++++------- carg_parser.c | 2 +- carg_parser.h | 2 +- configure | 6 +++--- decoder.c | 6 +++--- decoder.h | 13 +++++-------- doc/lunzip.1 | 6 +++--- list.c | 4 ++-- lzip.h | 6 +++--- lzip_index.c | 4 ++-- lzip_index.h | 2 +- main.c | 10 +++++----- testsuite/check.sh | 2 +- 15 files changed, 47 insertions(+), 45 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6782d3f..cf5e714 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ -2024-11-17 Antonio Diaz Diaz +2025-01-07 Antonio Diaz Diaz - * Version 1.15-rc1 released. + * Version 1.15 released. * Remove options '--empty-error' and '--marking-error'. * main.c (Pp_free): New function. * decoder.c (LZd_decode_member): Remove support for Sync Flush marker. @@ -142,7 +142,7 @@ * Created from the decompression code of clzip 1.1. -Copyright (C) 2010-2024 Antonio Diaz Diaz. +Copyright (C) 2010-2025 Antonio Diaz Diaz. This file is a collection of facts, and thus it is not copyrightable, but just in case, you have unlimited permission to copy, distribute, and modify it. diff --git a/INSTALL b/INSTALL index 9fe40ef..ba261b4 100644 --- a/INSTALL +++ b/INSTALL @@ -3,7 +3,8 @@ Requirements You will need a C99 compiler. (gcc 3.3.6 or newer is recommended). I use gcc 6.1.0 and 3.3.6, but the code should compile with any standards compliant compiler. -Gcc is available at http://gcc.gnu.org. +Gcc is available at http://gcc.gnu.org +Lzip is available at http://www.nongnu.org/lzip/lzip.html The operating system must allow signal handlers read access to objects with static storage duration so that the cleanup handler for Control-C can delete @@ -74,7 +75,7 @@ After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2010-2024 Antonio Diaz Diaz. +Copyright (C) 2010-2025 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute, and modify it. diff --git a/README b/README index 36784cd..7a77db5 100644 --- a/README +++ b/README @@ -1,3 +1,5 @@ +See the file INSTALL for compilation and installation instructions. + Description Lunzip is a decompressor for the lzip format written in C. Its small size @@ -19,11 +21,10 @@ compressed format for Unix-like systems. The lzip file format is designed for data sharing and long-term archiving, taking into account both data integrity and decoder availability: - * The lzip format provides very safe integrity checking and some data - recovery means. The program lziprecover can repair bit flip errors - (one of the most common forms of data corruption) in lzip files, and - provides data recovery capabilities, including error-checked merging - of damaged copies of a file. + * The program lziprecover can repair bit flip errors (one of the most + common forms of data corruption) in lzip files, and provides data + recovery capabilities, including error-checked merging of damaged + copies of a file. * The lzip format is as simple as possible (but not simpler). The lzip manual provides the source code of a simple decompressor along with a @@ -50,7 +51,7 @@ compress the file. To activate it, specify the size of the output buffer with the option '--buffer-size' and lunzip will use the decompressed file as dictionary for distances beyond the buffer size. Of course, the larger the difference between the buffer size and the dictionary size, the -more accesses to disk are needed and the slower the decompression is. +more accesses to disc are needed and the slower the decompression is. This 'low memory' mode only works when decompressing to a regular file and is intended for systems without enough memory (RAM + swap) to keep the whole dictionary at once. It has been tested on a laptop with a 486 @@ -99,8 +100,11 @@ LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never have been compressed. Decompressed is used to refer to data which have undergone the process of decompression. +Lunzip uses Arg_parser for command-line argument parsing: +http://www.nongnu.org/arg-parser/arg_parser.html -Copyright (C) 2010-2024 Antonio Diaz Diaz. + +Copyright (C) 2010-2025 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute, and modify it. diff --git a/carg_parser.c b/carg_parser.c index 9400342..20b8a16 100644 --- a/carg_parser.c +++ b/carg_parser.c @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command-line argument parser. (C version) - Copyright (C) 2006-2024 Antonio Diaz Diaz. + Copyright (C) 2006-2025 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/carg_parser.h b/carg_parser.h index 294756c..28eabee 100644 --- a/carg_parser.h +++ b/carg_parser.h @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command-line argument parser. (C version) - Copyright (C) 2006-2024 Antonio Diaz Diaz. + Copyright (C) 2006-2025 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/configure b/configure index 4aea9d0..9a8aff2 100755 --- a/configure +++ b/configure @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2024 Antonio Diaz Diaz. +# Copyright (C) 2010-2025 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission # to copy, distribute, and modify it. pkgname=lunzip -pkgversion=1.15-rc1 +pkgversion=1.15 progname=lunzip srctrigger=doc/${progname}.1 @@ -171,7 +171,7 @@ echo "MAKEINFO = ${MAKEINFO}" rm -f Makefile cat > Makefile << EOF # Makefile for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2024 Antonio Diaz Diaz. +# Copyright (C) 2010-2025 Antonio Diaz Diaz. # This file was generated automatically by configure. Don't edit. # # This Makefile is free software: you have unlimited permission diff --git a/decoder.c b/decoder.c index fbd185e..7765e76 100644 --- a/decoder.c +++ b/decoder.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2024 Antonio Diaz Diaz. + Copyright (C) 2010-2025 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 @@ -102,7 +102,7 @@ void LZd_flush_data( LZ_decoder * const d ) CRC32_update_buf( &d->crc, d->buffer + d->stream_pos, size ); if( d->outfd >= 0 && writeblock( d->outfd, d->buffer + d->stream_pos, size ) != size ) - { show_error( write_error_msg, errno, false ); cleanup_and_fail( 1 ); } + { show_error( wr_err_msg, errno, false ); cleanup_and_fail( 1 ); } if( d->pos >= d->buffer_size ) { d->partial_data_pos += d->pos; d->pos = 0; if( d->partial_data_pos >= d->dictionary_size ) d->pos_wrapped = true; } @@ -234,7 +234,7 @@ int LZd_decode_member( LZ_decoder * const d, Pretty_print * const pp ) if( Rd_decode_bit( rdec, &bm_rep0[state] ) == 0 ) /* 3rd bit */ { if( Rd_decode_bit( rdec, &bm_len[state][pos_state] ) == 0 ) /* 4th bit */ - { state = St_set_short_rep( state ); + { state = St_set_shortrep( state ); LZd_put_byte( d, LZd_peek( d, rep0 ) ); continue; } } else diff --git a/decoder.h b/decoder.h index 853c04d..fe43e0a 100644 --- a/decoder.h +++ b/decoder.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2024 Antonio Diaz Diaz. + Copyright (C) 2010-2025 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 @@ -204,9 +204,8 @@ static inline unsigned Rd_decode_tree8( Range_decoder * const rdec, return symbol & 0xFF; } -static inline unsigned -Rd_decode_tree_reversed( Range_decoder * const rdec, - Bit_model bm[], const int num_bits ) +static inline unsigned Rd_decode_tree_reversed( Range_decoder * const rdec, + Bit_model bm[], const int num_bits ) { unsigned model = 1; unsigned symbol = 0; @@ -244,8 +243,7 @@ static inline unsigned Rd_decode_matched( Range_decoder * const rdec, } static inline unsigned Rd_decode_len( Range_decoder * const rdec, - Len_model * const lm, - const int pos_state ) + Len_model * const lm, const int pos_state ) { Bit_model * bm; unsigned mask, offset, symbol = 1; @@ -386,8 +384,7 @@ static inline void LZd_free( LZ_decoder * const d ) static inline unsigned LZd_crc( const LZ_decoder * const d ) { return d->crc ^ 0xFFFFFFFFU; } -static inline unsigned long long -LZd_data_position( const LZ_decoder * const d ) +static inline unsigned long long LZd_data_position( const LZ_decoder * const d ) { return d->partial_data_pos + d->pos; } int LZd_decode_member( LZ_decoder * const d, Pretty_print * const pp ); diff --git a/doc/lunzip.1 b/doc/lunzip.1 index 54c098e..be23de8 100644 --- a/doc/lunzip.1 +++ b/doc/lunzip.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. -.TH LUNZIP "1" "November 2024" "lunzip 1.15-rc1" "User Commands" +.TH LUNZIP "1" "January 2025" "lunzip 1.15" "User Commands" .SH NAME lunzip \- decompressor for the lzip format .SH SYNOPSIS @@ -28,7 +28,7 @@ compress the file. To activate it, specify the size of the output buffer with the option \fB\-\-buffer\-size\fR and lunzip will use the decompressed file as dictionary for distances beyond the buffer size. Of course, the larger the difference between the buffer size and the dictionary size, the -more accesses to disk are needed and the slower the decompression is. +more accesses to disc are needed and the slower the decompression is. This 'low memory' mode only works when decompressing to a regular file and is intended for systems without enough memory (RAM + swap) to keep the whole dictionary at once. @@ -101,7 +101,7 @@ Report bugs to lzip\-bug@nongnu.org .br Lunzip home page: http://www.nongnu.org/lzip/lunzip.html .SH COPYRIGHT -Copyright \(co 2024 Antonio Diaz Diaz. +Copyright \(co 2025 Antonio Diaz Diaz. License GPLv2+: GNU GPL version 2 or later .br This is free software: you are free to change and redistribute it. diff --git a/list.c b/list.c index 7ed74ff..503d4c5 100644 --- a/list.c +++ b/list.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2024 Antonio Diaz Diaz. + Copyright (C) 2010-2025 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 @@ -115,7 +115,7 @@ int list_files( const char * const filenames[], const int num_filenames, fflush( stdout ); } if( verbosity >= 0 && ( ferror( stdout ) || fclose( stdout ) != 0 ) ) - { show_file_error( "(stdout)", write_error_msg, errno ); + { show_file_error( "(stdout)", wr_err_msg, errno ); set_retval( &retval, 1 ); } return retval; } diff --git a/lzip.h b/lzip.h index 8f793a8..4f9a366 100644 --- a/lzip.h +++ b/lzip.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2024 Antonio Diaz Diaz. + Copyright (C) 2010-2025 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 @@ -36,7 +36,7 @@ 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 ) +static inline State St_set_shortrep( const State st ) { return ( st < 7 ) ? 9 : 11; } @@ -256,7 +256,7 @@ static const char * const empty_msg = "Empty member not allowed."; static const char * const mem_msg = "Not enough memory."; static const char * const nonzero_msg = "Nonzero first LZMA byte."; static const char * const trailing_msg = "Trailing data not allowed."; -static const char * const write_error_msg = "Write error"; +static const char * const wr_err_msg = "Write error"; /* defined in decoder.c */ int readblock( const int fd, uint8_t * const buf, const int size ); diff --git a/lzip_index.c b/lzip_index.c index 91a9ec6..ab58355 100644 --- a/lzip_index.c +++ b/lzip_index.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2024 Antonio Diaz Diaz. + Copyright (C) 2010-2025 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 @@ -208,7 +208,7 @@ bool Li_init( Lzip_index * const li, const int infd, ( !Li_read_header( li, infd, header, 0 ) || !Li_check_header( li, header ) ) ) return false; if( li->insize < min_member_size ) - { add_error( li, "Input file is too short." ); li->retval = 2; + { add_error( li, "Input file is truncated." ); li->retval = 2; return false; } if( li->insize > INT64_MAX ) { add_error( li, "Input file is too long (2^63 bytes or more)." ); diff --git a/lzip_index.h b/lzip_index.h index 3fb4f35..0b446dc 100644 --- a/lzip_index.h +++ b/lzip_index.h @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2024 Antonio Diaz Diaz. + Copyright (C) 2010-2025 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 diff --git a/main.c b/main.c index f46af3b..0fc8107 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ /* Lunzip - Decompressor for the lzip format - Copyright (C) 2010-2024 Antonio Diaz Diaz. + Copyright (C) 2010-2025 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 @@ -78,7 +78,7 @@ int verbosity = 0; static const char * const program_name = "lunzip"; -static const char * const program_year = "2024"; +static const char * const program_year = "2025"; static const char * invocation_name = "lunzip"; /* default value */ static const struct { const char * from; const char * to; } known_extensions[] = { @@ -117,7 +117,7 @@ static void show_help( void ) "with the option --buffer-size and lunzip will use the decompressed\n" "file as dictionary for distances beyond the buffer size. Of course, the\n" "larger the difference between the buffer size and the dictionary size, the\n" - "more accesses to disk are needed and the slower the decompression is.\n" + "more accesses to disc are needed and the slower the decompression is.\n" "This 'low memory' mode only works when decompressing to a regular file\n" "and is intended for systems without enough memory (RAM + swap) to keep\n" "the whole dictionary at once.\n" @@ -279,7 +279,7 @@ void show_header( const unsigned dictionary_size ) } -/* separate numbers of 6 or more digits in groups of 3 digits using '_' */ +/* separate numbers of 5 or more digits in groups of 3 digits using '_' */ static const char * format_num3( unsigned long long num ) { enum { buffers = 8, bufsize = 4 * sizeof num, n = 10 }; @@ -302,7 +302,7 @@ static const char * format_num3( unsigned long long num ) { num /= 1000; prefix = si_prefix[i]; } if( prefix ) *(--p) = prefix; } - const bool split = num >= 100000; + const bool split = num >= 10000; for( i = 0; ; ) { diff --git a/testsuite/check.sh b/testsuite/check.sh index 296e165..2396efb 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # check script for Lunzip - Decompressor for the lzip format -# Copyright (C) 2010-2024 Antonio Diaz Diaz. +# Copyright (C) 2010-2025 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission # to copy, distribute, and modify it.