diff options
-rw-r--r-- | COPYING | 3 | ||||
-rw-r--r-- | ChangeLog | 30 | ||||
-rw-r--r-- | Makefile.in | 11 | ||||
-rw-r--r-- | NEWS | 27 | ||||
-rw-r--r-- | README | 27 | ||||
-rw-r--r-- | carg_parser.c | 60 | ||||
-rw-r--r-- | carg_parser.h | 43 | ||||
-rwxr-xr-x | configure | 4 | ||||
-rw-r--r-- | decoder.c | 63 | ||||
-rw-r--r-- | decoder.h | 94 | ||||
-rw-r--r-- | doc/lunzip.1 | 31 | ||||
-rw-r--r-- | list.c | 21 | ||||
-rw-r--r-- | lzip.h | 34 | ||||
-rw-r--r-- | lzip_index.c | 78 | ||||
-rw-r--r-- | lzip_index.h | 64 | ||||
-rw-r--r-- | main.c | 183 | ||||
-rwxr-xr-x | testsuite/check.sh | 159 | ||||
-rw-r--r-- | testsuite/em.lz | bin | 0 -> 36 bytes | |||
-rw-r--r-- | testsuite/fox6.lz | bin | 480 -> 0 bytes | |||
-rw-r--r-- | testsuite/fox6_mark.lz | bin | 480 -> 0 bytes | |||
-rw-r--r-- | testsuite/fox_nz.lz | bin | 0 -> 80 bytes | |||
-rw-r--r-- | testsuite/test.txt | 6 | ||||
-rw-r--r-- | testsuite/test.txt.lz | bin | 7376 -> 7341 bytes | |||
-rw-r--r-- | testsuite/test_em.txt.lz | bin | 14024 -> 0 bytes |
24 files changed, 472 insertions, 466 deletions
@@ -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. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -1,3 +1,13 @@ +2024-11-17 Antonio Diaz Diaz <antonio@gnu.org> + + * 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 <antonio@gnu.org> * Version 1.14 released. @@ -43,13 +53,13 @@ 2018-02-05 Antonio Diaz Diaz <antonio@gnu.org> * 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 <antonio@gnu.org> * 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 <antonio@gnu.org> @@ -93,7 +101,7 @@ 2014-04-11 Antonio Diaz Diaz <antonio@gnu.org> * 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 <antonio@gnu.org> @@ -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 <ant_diaz@teleline.es> 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 @@ -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. @@ -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; } - - -int ap_arguments( const struct Arg_parser * const ap ) - { return ap->data_size; } +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_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 '-<short_option><argument>' - (without whitespace), or '--<long_option>=<argument>'. + The syntax of options with an optional argument is + '-<short_option><argument>' (without whitespace), or + '--<long_option>=<argument>'. + + The syntax of options with an empty argument is '-<short_option> ""', + '--<long_option> ""', or '--<long_option>=""'. */ #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 } @@ -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 @@ -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<<literal_context_bits][0x300]; Bit_model bm_match[states][pos_states]; Bit_model bm_rep[states]; @@ -196,8 +191,8 @@ int LZd_decode_member( struct LZ_decoder * const d, Bit_model bm_dis_slot[len_states][1<<dis_slot_bits]; Bit_model bm_dis[modeled_distances-end_dis_model+1]; Bit_model bm_align[dis_align_size]; - struct Len_model match_len_model; - struct Len_model rep_len_model; + Len_model match_len_model; + Len_model rep_len_model; unsigned rep0 = 0; /* rep[0-3] latest four distances */ unsigned rep1 = 0; /* used for efficient coding of */ unsigned rep2 = 0; /* repeated distances */ @@ -218,7 +213,7 @@ int LZd_decode_member( struct LZ_decoder * const d, Lm_init( &match_len_model ); Lm_init( &rep_len_model ); - if( !Rd_load( rdec, cl_opts->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 ) ) ) @@ -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 @@ -17,6 +17,7 @@ #define _FILE_OFFSET_BITS 64 +#include <errno.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> @@ -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; } @@ -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; } @@ -26,7 +26,7 @@ #include <ctype.h> #include <errno.h> #include <fcntl.h> -#include <limits.h> /* SSIZE_MAX */ +#include <limits.h> /* CHAR_BIT, SSIZE_MAX */ #include <signal.h> #include <stdbool.h> #include <stdint.h> /* SIZE_MAX */ @@ -39,8 +39,10 @@ #if defined __MSVCRT__ || defined __OS2__ || defined __DJGPP__ #include <io.h> #if defined __MSVCRT__ +#include <direct.h> #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=<file> write to <file>, keep input files\n" - " -q, --quiet suppress all messages\n" - " -t, --test test compressed file integrity\n" - " -u, --buffer-size=<bytes> 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=<file> write to <file>, keep input files\n" + " -q, --quiet suppress all messages\n" + " -t, --test test compressed file integrity\n" + " -u, --buffer-size=<bytes> 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 - -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 +"${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 -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 Binary files differnew file mode 100644 index 0000000..ec60725 --- /dev/null +++ b/testsuite/em.lz diff --git a/testsuite/fox6.lz b/testsuite/fox6.lz Binary files differdeleted file mode 100644 index 8401b99..0000000 --- a/testsuite/fox6.lz +++ /dev/null diff --git a/testsuite/fox6_mark.lz b/testsuite/fox6_mark.lz Binary files differdeleted file mode 100644 index 32b2ac0..0000000 --- a/testsuite/fox6_mark.lz +++ /dev/null diff --git a/testsuite/fox_nz.lz b/testsuite/fox_nz.lz Binary files differnew file mode 100644 index 0000000..44a4b58 --- /dev/null +++ b/testsuite/fox_nz.lz 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. <http://fsf.org/> 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. <http://fsf.org/>
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 Binary files differindex 22cea6e..5dc169f 100644 --- a/testsuite/test.txt.lz +++ b/testsuite/test.txt.lz diff --git a/testsuite/test_em.txt.lz b/testsuite/test_em.txt.lz Binary files differdeleted file mode 100644 index 7e96250..0000000 --- a/testsuite/test_em.txt.lz +++ /dev/null |