diff options
-rw-r--r-- | ChangeLog | 11 | ||||
-rw-r--r-- | INSTALL | 2 | ||||
-rw-r--r-- | Makefile.in | 14 | ||||
-rw-r--r-- | NEWS | 17 | ||||
-rw-r--r-- | README | 12 | ||||
-rw-r--r-- | arg_parser.cc | 16 | ||||
-rw-r--r-- | arg_parser.h | 5 | ||||
-rw-r--r-- | compress.cc | 2 | ||||
-rwxr-xr-x | configure | 21 | ||||
-rw-r--r-- | dec_stdout.cc | 8 | ||||
-rw-r--r-- | dec_stream.cc | 45 | ||||
-rw-r--r-- | decompress.cc | 8 | ||||
-rw-r--r-- | doc/plzip.1 | 9 | ||||
-rw-r--r-- | doc/plzip.info | 93 | ||||
-rw-r--r-- | doc/plzip.texi | 64 | ||||
-rw-r--r-- | file_index.cc | 130 | ||||
-rw-r--r-- | file_index.h | 25 | ||||
-rw-r--r-- | list.cc | 121 | ||||
-rw-r--r-- | lzip.h | 33 | ||||
-rw-r--r-- | main.cc | 226 | ||||
-rwxr-xr-x | testsuite/check.sh | 282 | ||||
-rw-r--r-- | testsuite/test.txt.lz | bin | 7376 -> 7376 bytes |
22 files changed, 746 insertions, 398 deletions
@@ -1,3 +1,12 @@ +2017-04-12 Antonio Diaz Diaz <antonio@gnu.org> + + * Version 1.6 released. + * The option '-l, --list' has been ported from lziprecover. + * Don't allow mixing different operations (-d, -l or -t). + * main.cc: Continue testing if any input file is a terminal. + * file_index.cc: Improve detection of bad dict and trailing data. + * lzip.h: Unified messages for bad magic, trailing data, etc. + 2016-05-14 Antonio Diaz Diaz <antonio@gnu.org> * Version 1.5 released. @@ -139,7 +148,7 @@ until something better appears on the net. -Copyright (C) 2009-2016 Antonio Diaz Diaz. +Copyright (C) 2009-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 @@ -65,7 +65,7 @@ After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2009-2016 Antonio Diaz Diaz. +Copyright (C) 2009-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 8f07828..a9ead0a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -8,14 +8,15 @@ LIBS = -llz -lpthread SHELL = /bin/sh CAN_RUN_INSTALLINFO = $(SHELL) -c "install-info --version" > /dev/null 2>&1 -objs = arg_parser.o file_index.o compress.o dec_stdout.o dec_stream.o \ - decompress.o main.o +objs = arg_parser.o file_index.o list.o compress.o dec_stdout.o \ + dec_stream.o decompress.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) @@ -36,6 +37,7 @@ dec_stdout.o : lzip.h file_index.h dec_stream.o : lzip.h decompress.o : lzip.h file_index.h file_index.o : lzip.h file_index.h +list.o : lzip.h file_index.h main.o : arg_parser.h lzip.h @@ -120,11 +122,11 @@ dist : doc $(DISTNAME)/doc/$(progname).1 \ $(DISTNAME)/doc/$(pkgname).info \ $(DISTNAME)/doc/$(pkgname).texi \ + $(DISTNAME)/*.h \ + $(DISTNAME)/*.cc \ $(DISTNAME)/testsuite/check.sh \ $(DISTNAME)/testsuite/test.txt \ - $(DISTNAME)/testsuite/test.txt.lz \ - $(DISTNAME)/*.h \ - $(DISTNAME)/*.cc + $(DISTNAME)/testsuite/test.txt.lz rm -f $(DISTNAME) lzip -v -9 $(DISTNAME).tar @@ -1,14 +1,9 @@ -Changes in version 1.5: +Changes in version 1.6: -The option "-a, --trailing-error", which makes plzip 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. -When decompressing, the file specified with the '--output' option is now -deleted if the input is a terminal. +It is now an error to specify two or more different operations in the +command line (--decompress, --list or --test). -The new chapters "Trailing data" and "Examples" have been added to the -manual. - -A harmless check failure on Windows, caused by the failed comparison of -a message in text mode, has been fixed. +In test mode, plzip now continues checking the rest of the files if any +input file is a terminal. @@ -35,11 +35,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. @@ -88,7 +88,7 @@ corresponding uncompressed files. Integrity testing of concatenated compressed files is also supported. -Copyright (C) 2009-2016 Antonio Diaz Diaz. +Copyright (C) 2009-2017 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute and modify it. diff --git a/arg_parser.cc b/arg_parser.cc index 82972ad..cc7d1e2 100644 --- a/arg_parser.cc +++ b/arg_parser.cc @@ -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 @@ -42,7 +42,7 @@ bool Arg_parser::parse_long_option( const char * const opt, const char * const a 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 = true; // Second or later nonexact match found + ambig = true; // Second or later nonexact match found } if( ambig && !exact ) @@ -142,7 +142,7 @@ Arg_parser::Arg_parser( const int argc, const char * const argv[], { if( argc < 2 || !argv || !options ) return; - std::vector< std::string > non_options; // skipped non-options + std::vector< const char * > non_options; // skipped non-options int argind = 1; // index in argv while( argind < argc ) @@ -163,17 +163,17 @@ Arg_parser::Arg_parser( const int argc, const char * const argv[], } else { - if( !in_order ) non_options.push_back( argv[argind++] ); - else { data.push_back( Record() ); data.back().argument = argv[argind++]; } + if( in_order ) data.push_back( Record( argv[argind++] ) ); + else non_options.push_back( argv[argind++] ); } } if( error_.size() ) data.clear(); else { for( unsigned i = 0; i < non_options.size(); ++i ) - { data.push_back( Record() ); data.back().argument.swap( non_options[i] ); } + data.push_back( Record( non_options[i] ) ); while( argind < argc ) - { data.push_back( Record() ); data.back().argument = argv[argind++]; } + data.push_back( Record( argv[argind++] ) ); } } @@ -192,5 +192,5 @@ Arg_parser::Arg_parser( const char * const opt, const char * const arg, parse_short_option( opt, arg, options, argind ); if( error_.size() ) data.clear(); } - else { data.push_back( Record() ); data.back().argument = opt; } + else data.push_back( Record( opt ) ); } diff --git a/arg_parser.h b/arg_parser.h index f45b9ac..95b0320 100644 --- a/arg_parser.h +++ b/arg_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 @@ -57,7 +57,8 @@ private: { int code; std::string argument; - explicit Record( const int c = 0 ) : code( c ) {} + explicit Record( const int c ) : code( c ) {} + explicit Record( const char * const arg ) : code( 0 ), argument( arg ) {} }; std::string error_; diff --git a/compress.cc b/compress.cc index 56d5314..5bcd999 100644 --- a/compress.cc +++ b/compress.cc @@ -1,6 +1,6 @@ /* Plzip - Parallel compressor compatible with lzip Copyright (C) 2009 Laszlo Ersek. - Copyright (C) 2009-2016 Antonio Diaz Diaz. + Copyright (C) 2009-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 @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Plzip - Parallel compressor compatible with lzip -# Copyright (C) 2009-2016 Antonio Diaz Diaz. +# Copyright (C) 2009-2017 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission # to copy, distribute and modify it. pkgname=plzip -pkgversion=1.5 +pkgversion=1.6 progname=plzip srctrigger=doc/${pkgname}.texi @@ -26,11 +26,11 @@ CXXFLAGS='-Wall -W -O2' LDFLAGS= # checking whether we are using GNU C++. -if /bin/sh -c "${CXX} --version" > /dev/null 2>&1 ; then true -else +/bin/sh -c "${CXX} --version" > /dev/null 2>&1 || + { CXX=c++ - CXXFLAGS='-W -O2' -fi + CXXFLAGS=-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 "To assign makefile variables (e.g., CXX, CXXFLAGS...), specify them as" + echo "arguments to configure in the form VAR=VALUE." echo - echo "Options: [defaults in brackets]" + 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 Plzip - Parallel compressor compatible with lzip -# Copyright (C) 2009-2016 Antonio Diaz Diaz. +# Copyright (C) 2009-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/dec_stdout.cc b/dec_stdout.cc index 620075e..a1a9d61 100644 --- a/dec_stdout.cc +++ b/dec_stdout.cc @@ -1,6 +1,6 @@ /* Plzip - Parallel compressor compatible with lzip Copyright (C) 2009 Laszlo Ersek. - Copyright (C) 2009-2016 Antonio Diaz Diaz. + Copyright (C) 2009-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 @@ -298,15 +298,15 @@ int dec_stdout( const int num_workers, const int infd, const int outfd, delete[] worker_threads; delete[] worker_args; - const unsigned long long in_size = file_index.file_end(); - const unsigned long long out_size = file_index.data_end(); + const unsigned long long in_size = file_index.cdata_size(); + const unsigned long long out_size = file_index.udata_size(); if( verbosity >= 2 && out_size > 0 && in_size > 0 ) std::fprintf( stderr, "%6.3f:1, %6.3f bits/byte, %5.2f%% saved. ", (double)out_size / in_size, ( 8.0 * in_size ) / out_size, 100.0 * ( 1.0 - ( (double)in_size / out_size ) ) ); if( verbosity >= 4 ) - std::fprintf( stderr, "decompressed size %9llu, size %9llu. ", + std::fprintf( stderr, "decompressed %9llu, compressed %9llu. ", out_size, in_size ); if( verbosity >= 1 ) std::fputs( "done\n", stderr ); diff --git a/dec_stream.cc b/dec_stream.cc index e074ee2..5ec1ff7 100644 --- a/dec_stream.cc +++ b/dec_stream.cc @@ -1,6 +1,6 @@ /* Plzip - Parallel compressor compatible with lzip Copyright (C) 2009 Laszlo Ersek. - Copyright (C) 2009-2016 Antonio Diaz Diaz. + Copyright (C) 2009-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 @@ -267,16 +267,13 @@ extern "C" void * dsplitter_s( void * arg ) { pp( "Input file is too short." ); cleanup_and_fail( 2 ); } const File_header & header = *(File_header *)buffer; if( !header.verify_magic() ) - { pp( "Bad magic number (file not in lzip format)." ); cleanup_and_fail( 2 ); } + { pp( bad_magic_msg ); cleanup_and_fail( 2 ); } if( !header.verify_version() ) - { - if( verbosity >= 0 ) - { pp(); - std::fprintf( stderr, "Version %d member format not supported.\n", - header.version() ); } - cleanup_and_fail( 2 ); - } - show_header( header.dictionary_size() ); + { pp( bad_version( header.version() ) ); cleanup_and_fail( 2 ); } + const unsigned dictionary_size = header.dictionary_size(); + if( !isvalid_ds( dictionary_size ) ) + { pp( bad_dict_msg ); cleanup_and_fail( 2 ); } + show_header( dictionary_size ); unsigned long long partial_member_size = 0; while( true ) @@ -293,13 +290,10 @@ extern "C" void * dsplitter_s( void * arg ) { // header found const File_header & header = *(File_header *)(buffer + newpos); if( !header.verify_version() ) - { - if( verbosity >= 0 ) - { pp(); - std::fprintf( stderr, "Version %d member format not supported.\n", - header.version() ); } - cleanup_and_fail( 2 ); - } + { pp( bad_version( header.version() ) ); cleanup_and_fail( 2 ); } + const unsigned dictionary_size = header.dictionary_size(); + if( !isvalid_ds( dictionary_size ) ) + { pp( bad_dict_msg ); cleanup_and_fail( 2 ); } uint8_t * const data = new( std::nothrow ) uint8_t[newpos - pos]; if( !data ) { pp( "Not enough memory." ); cleanup_and_fail(); } std::memcpy( data, buffer + pos, newpos - pos ); @@ -367,7 +361,7 @@ extern "C" void * dworker_s( void * arg ) { pp( "Not enough memory." ); cleanup_and_fail(); } unsigned long long partial_out_size = 0; int new_pos = 0; - bool trailing_garbage_found = false; + bool trailing_data_found = false; while( true ) { @@ -376,7 +370,7 @@ extern "C" void * dworker_s( void * arg ) if( !ipacket->data ) LZ_decompress_finish( decoder ); int written = 0; - while( !trailing_garbage_found ) + while( !trailing_data_found ) { if( LZ_decompress_write_size( decoder ) > 0 && written < ipacket->size ) { @@ -387,7 +381,7 @@ extern "C" void * dworker_s( void * arg ) if( written > ipacket->size ) internal_error( "ipacket size exceeded in worker." ); } - while( !trailing_garbage_found ) // read and pack decompressed data + while( !trailing_data_found ) // read and pack decompressed data { const int rd = LZ_decompress_read( decoder, new_data + new_pos, max_packet_size - new_pos ); @@ -395,9 +389,9 @@ extern "C" void * dworker_s( void * arg ) { if( LZ_decompress_errno( decoder ) == LZ_header_error ) { - trailing_garbage_found = true; + trailing_data_found = true; if( !ignore_trailing ) - { pp( "Trailing data not allowed." ); cleanup_and_fail( 2 ); } + { pp( trailing_msg ); cleanup_and_fail( 2 ); } } else cleanup_and_fail( decompress_read_error( decoder, pp, worker_id ) ); @@ -405,7 +399,7 @@ extern "C" void * dworker_s( void * arg ) else new_pos += rd; if( new_pos > max_packet_size ) internal_error( "opacket size exceeded in worker." ); - if( new_pos == max_packet_size || trailing_garbage_found || + if( new_pos == max_packet_size || trailing_data_found || LZ_decompress_finished( decoder ) == 1 ) { if( !testing && new_pos > 0 ) // make data packet @@ -417,8 +411,7 @@ extern "C" void * dworker_s( void * arg ) } partial_out_size += new_pos; new_pos = 0; - if( trailing_garbage_found || - LZ_decompress_finished( decoder ) == 1 ) + if( trailing_data_found || LZ_decompress_finished( decoder ) == 1 ) { if( !testing ) // end of member token courier.collect_packet( new Packet, worker_id ); @@ -525,7 +518,7 @@ int dec_stream( const int num_workers, const int infd, const int outfd, ( 8.0 * in_size ) / out_size, 100.0 * ( 1.0 - ( (double)in_size / out_size ) ) ); if( verbosity >= 4 ) - std::fprintf( stderr, "decompressed size %9llu, size %9llu. ", + std::fprintf( stderr, "decompressed %9llu, compressed %9llu. ", out_size, in_size ); if( verbosity >= 1 ) std::fputs( (outfd < 0) ? "ok\n" : "done\n", stderr ); diff --git a/decompress.cc b/decompress.cc index 3511921..f580bca 100644 --- a/decompress.cc +++ b/decompress.cc @@ -1,6 +1,6 @@ /* Plzip - Parallel compressor compatible with lzip Copyright (C) 2009 Laszlo Ersek. - Copyright (C) 2009-2016 Antonio Diaz Diaz. + Copyright (C) 2009-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 @@ -266,15 +266,15 @@ int decompress( int num_workers, const int infd, const int outfd, delete[] worker_threads; delete[] worker_args; - const unsigned long long in_size = file_index.file_end(); - const unsigned long long out_size = file_index.data_end(); + const unsigned long long in_size = file_index.cdata_size(); + const unsigned long long out_size = file_index.udata_size(); if( verbosity >= 2 && out_size > 0 && in_size > 0 ) std::fprintf( stderr, "%6.3f:1, %6.3f bits/byte, %5.2f%% saved. ", (double)out_size / in_size, ( 8.0 * in_size ) / out_size, 100.0 * ( 1.0 - ( (double)in_size / out_size ) ) ); if( verbosity >= 4 ) - std::fprintf( stderr, "decompressed size %9llu, size %9llu. ", + std::fprintf( stderr, "decompressed %9llu, compressed %9llu. ", out_size, in_size ); if( verbosity >= 1 ) std::fputs( (outfd < 0) ? "ok\n" : "done\n", stderr ); diff --git a/doc/plzip.1 b/doc/plzip.1 index 5a61596..5c47edd 100644 --- a/doc/plzip.1 +++ b/doc/plzip.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. -.TH PLZIP "1" "May 2016" "plzip 1.5" "User Commands" +.TH PLZIP "1" "April 2017" "plzip 1.6" "User Commands" .SH NAME plzip \- reduces the size of files .SH SYNOPSIS @@ -36,6 +36,9 @@ force re\-compression of compressed 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\-m\fR, \fB\-\-match\-length=\fR<bytes> set match length limit in bytes [36] .TP @@ -89,8 +92,8 @@ Plzip home page: http://www.nongnu.org/lzip/plzip.html .SH COPYRIGHT Copyright \(co 2009 Laszlo Ersek. .br -Copyright \(co 2016 Antonio Diaz Diaz. -Using lzlib 1.8 +Copyright \(co 2017 Antonio Diaz Diaz. +Using lzlib 1.9 License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html> .br This is free software: you are free to change and redistribute it. diff --git a/doc/plzip.info b/doc/plzip.info index a814b3f..cf53f13 100644 --- a/doc/plzip.info +++ b/doc/plzip.info @@ -11,7 +11,7 @@ File: plzip.info, Node: Top, Next: Introduction, Up: (dir) Plzip Manual ************ -This manual is for Plzip (version 1.5, 14 May 2016). +This manual is for Plzip (version 1.6, 12 April 2017). * Menu: @@ -27,7 +27,7 @@ This manual is for Plzip (version 1.5, 14 May 2016). * Concept index:: Index of concepts - Copyright (C) 2009-2016 Antonio Diaz Diaz. + Copyright (C) 2009-2017 Antonio Diaz Diaz. This manual is free documentation: you have unlimited permission to copy, distribute and modify it. @@ -65,11 +65,11 @@ availability: (lziprecover)Data safety. * 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. @@ -117,6 +117,10 @@ two or more compressed files. The result is the concatenation of the corresponding uncompressed files. Integrity testing of concatenated compressed files is also supported. + 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. + File: plzip.info, Node: Invoking plzip, Next: Program design, Prev: Introduction, Up: Top @@ -185,6 +189,21 @@ command line. Keep (don't delete) input files during compression or decompression. +'-l' +'--list' + Print the uncompressed size, compressed size and percentage saved + of the specified file(s). Trailing data are ignored. The values + produced are correct even for multimember files. If more than one + file is given, a final line containing the cumulative sizes is + printed. With '-v', the dictionary size, the number of members in + the file, and the amount of trailing data (if any) are also + printed. With '-vv', the positions and sizes of each member in + multimember files are also printed. '-lq' can be used to verify + quickly (without decompressing) the structural integrity of the + specified files. (Use '--test' to verify the data integrity). + '-alq' additionally verifies that none of the specified files + contain trailing data. + '-m BYTES' '--match-length=BYTES' Set the match length limit in bytes. After a match this long is @@ -236,8 +255,10 @@ command line. Check integrity of the specified file(s), but don't decompress them. This really performs a trial decompression and throws away the result. Use it together with '-v' to see information about - the file(s). If a file fails the test, plzip may be unable to - check the rest of the files. + the file(s). If a file does not exist, can't be opened, or is a + terminal, plzip continues checking the rest of the files. If a + file fails the test, plzip may be unable to check the rest of the + files. '-v' '--verbose' @@ -420,9 +441,6 @@ following: * For decompression of a regular (seekable) file to another regular file, or for testing of a regular file; the dictionary size. - (Note that regular files with more than 1024 bytes of trailing - data are treated as non-seekable). - * For testing of a non-seekable file or of standard input; the dictionary size plus up to 5 MiB. @@ -474,16 +492,21 @@ File: plzip.info, Node: Trailing data, Next: Examples, Prev: Minimum file siz 7 Extra data appended to the file ********************************* -Sometimes extra data is found appended to a lzip file after the last +Sometimes extra data are found appended to a lzip file after the last member. Such trailing data may be: * Padding added to make the file size a multiple of some block size, - for example when writing to a tape. - - * Garbage added by some not totally successful copy operation. + for example when writing to a tape. It is safe to append any + amount of padding zero bytes to a lzip file. * Useful data added by the user; a cryptographically secure hash, a - description of file contents, etc. + description of file contents, etc. It is safe to append any amount + of text to a lzip file as long as the text does not begin with the + string "LZIP", and does not contain any zero bytes (null + characters). Nonzero bytes and zero bytes can't be safely mixed in + trailing data. + + * Garbage added by some not totally successful copy operation. * Malicious data added to the file in order to make its total size and hash value (for a chosen hash) coincide with those of another @@ -496,8 +519,12 @@ member. Such trailing data may be: the corruption of the integrity information itself. Therefore it can be considered to be below the noise level. + Trailing data are in no way part of the lzip file format, but tools +reading lzip files are expected to behave as correctly and usefully as +possible in the presence of trailing data. + Trailing data can be safely ignored in most cases. In some cases, -like that of user-added data, it is expected to be ignored. In those +like that of user-added data, they are expected to be ignored. In those cases where a file containing trailing data must be rejected, the option '--trailing-error' can be used. *Note --trailing-error::. @@ -545,8 +572,8 @@ Example 5: Compress a whole device in /dev/sdc and send the output to plzip -c /dev/sdc > file.lz -Example 6: The right way of concatenating compressed files. *Note -Trailing data::. +Example 6: The right way of concatenating the decompressed output of two +or more compressed files. *Note Trailing data::. Don't do this cat file1.lz file2.lz file3.lz | plzip -d @@ -607,19 +634,19 @@ Concept index Tag Table: Node: Top221 -Node: Introduction1101 -Node: Invoking plzip5078 -Ref: --trailing-error5647 -Ref: --data-size5890 -Node: Program design11683 -Node: File format13270 -Node: Memory requirements15702 -Node: Minimum file sizes16811 -Node: Trailing data18737 -Node: Examples20121 -Ref: concat-example21286 -Node: Problems21823 -Node: Concept index22349 +Node: Introduction1103 +Node: Invoking plzip5274 +Ref: --trailing-error5843 +Ref: --data-size6086 +Node: Program design12796 +Node: File format14383 +Node: Memory requirements16815 +Node: Minimum file sizes17815 +Node: Trailing data19741 +Node: Examples21648 +Ref: concat-example22813 +Node: Problems23388 +Node: Concept index23914 End Tag Table diff --git a/doc/plzip.texi b/doc/plzip.texi index c459cde..5f32f6e 100644 --- a/doc/plzip.texi +++ b/doc/plzip.texi @@ -6,8 +6,8 @@ @finalout @c %**end of header -@set UPDATED 14 May 2016 -@set VERSION 1.5 +@set UPDATED 12 April 2017 +@set VERSION 1.6 @dircategory Data Compression @direntry @@ -48,7 +48,7 @@ This manual is for Plzip (version @value{VERSION}, @value{UPDATED}). @end menu @sp 1 -Copyright @copyright{} 2009-2016 Antonio Diaz Diaz. +Copyright @copyright{} 2009-2017 Antonio Diaz Diaz. This manual is free documentation: you have unlimited permission to copy, distribute and modify it. @@ -90,10 +90,10 @@ including error-checked merging of damaged copies of a file. @item 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 +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. @item @@ -146,6 +146,10 @@ or more compressed files. The result is the concatenation of the corresponding uncompressed files. Integrity testing of concatenated compressed files is also supported. +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. + @node Invoking plzip @chapter Invoking plzip @@ -220,6 +224,20 @@ Force re-compression of files whose name already has the @samp{.lz} or @itemx --keep Keep (don't delete) input files during compression or decompression. +@item -l +@itemx --list +Print the uncompressed size, compressed size and percentage saved of the +specified file(s). Trailing data are ignored. The values produced are +correct even for multimember files. If more than one file is given, a +final line containing the cumulative sizes is printed. With @samp{-v}, +the dictionary size, the number of members in the file, and the amount +of trailing data (if any) are also printed. With @samp{-vv}, the +positions and sizes of each member in multimember files are also +printed. @samp{-lq} can be used to verify quickly (without +decompressing) the structural integrity of the specified files. (Use +@samp{--test} to verify the data integrity). @samp{-alq} additionally +verifies that none of the specified files contain trailing data. + @item -m @var{bytes} @itemx --match-length=@var{bytes} Set the match length limit in bytes. After a match this long is found, @@ -267,8 +285,9 @@ is affected at compression time by the choice of dictionary size limit. Check integrity of the specified file(s), but don't decompress them. This really performs a trial decompression and throws away the result. Use it together with @samp{-v} to see information about the file(s). If -a file fails the test, plzip may be unable to check the rest of the -files. +a file does not exist, can't be opened, or is a terminal, plzip +continues checking the rest of the files. If a file fails the test, +plzip may be unable to check the rest of the files. @item -v @itemx --verbose @@ -469,9 +488,6 @@ times the data size. Default is 136 MiB. For decompression of a regular (seekable) file to another regular file, or for testing of a regular file; the dictionary size. -(Note that regular files with more than 1024 bytes of trailing data are -treated as non-seekable). - @item For testing of a non-seekable file or of standard input; the dictionary size plus up to 5 MiB. @@ -526,20 +542,24 @@ data size for each level: @chapter Extra data appended to the file @cindex trailing data -Sometimes extra data is found appended to a lzip file after the last +Sometimes extra data are found appended to a lzip file after the last member. Such trailing data may be: @itemize @bullet @item Padding added to make the file size a multiple of some block size, for -example when writing to a tape. +example when writing to a tape. It is safe to append any amount of +padding zero bytes to a lzip file. @item -Garbage added by some not totally successful copy operation. +Useful data added by the user; a cryptographically secure hash, a +description of file contents, etc. It is safe to append any amount of +text to a lzip file as long as the text does not begin with the string +"LZIP", and does not contain any zero bytes (null characters). Nonzero +bytes and zero bytes can't be safely mixed in trailing data. @item -Useful data added by the user; a cryptographically secure hash, a -description of file contents, etc. +Garbage added by some not totally successful copy operation. @item Malicious data added to the file in order to make its total size and @@ -554,8 +574,12 @@ integrity information itself. Therefore it can be considered to be below the noise level. @end itemize +Trailing data are in no way part of the lzip file format, but tools +reading lzip files are expected to behave as correctly and usefully as +possible in the presence of trailing data. + Trailing data can be safely ignored in most cases. In some cases, like -that of user-added data, it is expected to be ignored. In those cases +that of user-added data, they are expected to be ignored. In those cases where a file containing trailing data must be rejected, the option @samp{--trailing-error} can be used. @xref{--trailing-error}. @@ -620,8 +644,8 @@ plzip -c /dev/sdc > file.lz @sp 1 @anchor{concat-example} @noindent -Example 6: The right way of concatenating compressed files. -@xref{Trailing data}. +Example 6: The right way of concatenating the decompressed output of two +or more compressed files. @xref{Trailing data}. @example Don't do this diff --git a/file_index.cc b/file_index.cc index 0e48833..581b516 100644 --- a/file_index.cc +++ b/file_index.cc @@ -1,5 +1,5 @@ /* Plzip - Parallel compressor compatible with lzip - Copyright (C) 2009-2016 Antonio Diaz Diaz. + Copyright (C) 2009-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 @@ -15,6 +15,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#define _FILE_OFFSET_BITS 64 + #include <algorithm> #include <cerrno> #include <cstdio> @@ -29,6 +31,8 @@ #include "file_index.h" +namespace { + int seek_read( const int fd, uint8_t * const buf, const int size, const long long pos ) { @@ -37,6 +41,8 @@ int seek_read( const int fd, uint8_t * const buf, const int size, return 0; } +} // end namespace + void File_index::set_errno_error( const char * const msg ) { @@ -44,20 +50,75 @@ void File_index::set_errno_error( const char * const msg ) retval_ = 1; } -void File_index::set_num_error( const char * const msg1, unsigned long long num, - const char * const msg2 ) +void File_index::set_num_error( const char * const msg, unsigned long long num ) { char buf[80]; - snprintf( buf, sizeof buf, "%s%llu%s", msg1, num, msg2 ); + snprintf( buf, sizeof buf, "%s%llu", msg, num ); error_ = buf; - retval_ = member_vector.empty() ? 1 : 2; // maybe trailing data + retval_ = 2; } -File_index::File_index( const int infd, const bool ignore_garbage ) - : retval_( 0 ) +// If successful, push last member and set pos to member header. +bool File_index::skip_trailing_data( const int fd, long long & pos ) + { + enum { block_size = 16384, + buffer_size = block_size + File_trailer::size - 1 + File_header::size }; + uint8_t buffer[buffer_size]; + if( pos < min_member_size ) return false; + int bsize = pos % block_size; // total bytes in buffer + if( bsize <= buffer_size - block_size ) bsize += 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 ) + { + if( seek_read( fd, buffer, rd_size, ipos ) != rd_size ) + { set_errno_error( "Error seeking member trailer: " ); return false; } + const uint8_t max_msb = ( ipos + search_size ) >> 56; + for( int i = search_size; i >= File_trailer::size; --i ) + if( buffer[i-1] <= max_msb ) // most significant byte of member_size + { + File_trailer & trailer = + *(File_trailer *)( buffer + i - File_trailer::size ); + const unsigned long long member_size = trailer.member_size(); + if( member_size == 0 ) + { while( i > File_trailer::size && buffer[i-9] == 0 ) --i; continue; } + if( member_size < min_member_size || member_size > ipos + i ) + continue; + File_header header; + if( seek_read( fd, header.data, File_header::size, + ipos + i - member_size ) != File_header::size ) + { set_errno_error( "Error reading member header: " ); return false; } + const unsigned dictionary_size = header.dictionary_size(); + if( !header.verify_magic() || !header.verify_version() || + !isvalid_ds( dictionary_size ) ) continue; + if( (*(File_header *)( buffer + i )).verify_prefix( bsize - i ) ) + { + error_ = "Last member in input file is truncated or corrupt."; + retval_ = 2; return false; + } + pos = ipos + i - member_size; + member_vector.push_back( Member( 0, trailer.data_size(), pos, + member_size, dictionary_size ) ); + return true; + } + if( ipos <= 0 ) + { set_num_error( "Member size in trailer is corrupt at pos ", pos - 8 ); + return false; } + bsize = buffer_size; + search_size = bsize - File_header::size; + rd_size = block_size; + ipos -= rd_size; + std::memcpy( buffer + rd_size, buffer, buffer_size - rd_size ); + } + } + + +File_index::File_index( const int infd, const bool ignore_trailing ) + : isize( lseek( infd, 0, SEEK_END ) ), retval_( 0 ) { - const long long isize = lseek( infd, 0, SEEK_END ); if( isize < 0 ) { set_errno_error( "Input file is not seekable: " ); return; } if( isize < min_member_size ) @@ -70,55 +131,46 @@ File_index::File_index( const int infd, const bool ignore_garbage ) if( seek_read( infd, header.data, File_header::size, 0 ) != File_header::size ) { set_errno_error( "Error reading member header: " ); return; } if( !header.verify_magic() ) - { error_ = "Bad magic number (file not in lzip format)."; - retval_ = 2; return; } + { error_ = bad_magic_msg; retval_ = 2; return; } if( !header.verify_version() ) - { set_num_error( "Version ", header.version(), - " member format not supported." ); retval_ = 2; return; } + { error_ = bad_version( header.version() ); retval_ = 2; return; } + if( !isvalid_ds( header.dictionary_size() ) ) + { error_ = bad_dict_msg; retval_ = 2; return; } - long long pos = isize; // always points to a header or to EOF - const long long max_garbage = 1024; + long long pos = isize; // always points to a header or to EOF while( pos >= min_member_size ) { File_trailer trailer; if( seek_read( infd, trailer.data, File_trailer::size, pos - File_trailer::size ) != File_trailer::size ) { set_errno_error( "Error reading member trailer: " ); break; } - const long long member_size = trailer.member_size(); - if( member_size < min_member_size || member_size > pos ) + const unsigned long long member_size = trailer.member_size(); + if( member_size < min_member_size || member_size > (unsigned long long)pos ) { - if( member_vector.empty() && isize - pos < max_garbage ) - { - if( ignore_garbage ) { --pos; continue; } // maybe trailing data - error_ = "Trailing data not allowed."; retval_ = 2; return; - } - set_num_error( "Member size in trailer is corrupt at pos ", pos - 8 ); + if( !member_vector.empty() ) + set_num_error( "Member size in trailer is corrupt at pos ", pos - 8 ); + else if( skip_trailing_data( infd, pos ) ) + { if( ignore_trailing ) continue; + error_ = trailing_msg; retval_ = 2; return; } break; } if( seek_read( infd, header.data, File_header::size, pos - member_size ) != File_header::size ) { set_errno_error( "Error reading member header: " ); break; } - if( !header.verify_magic() || !header.verify_version() ) - { - if( member_vector.empty() && isize - pos < max_garbage ) - { - if( ignore_garbage ) { --pos; continue; } // maybe trailing data - error_ = "Trailing data not allowed."; retval_ = 2; return; - } - set_num_error( "Bad header at pos ", pos - member_size ); - break; - } const unsigned dictionary_size = header.dictionary_size(); - if( member_vector.empty() && isize - pos > File_header::size && - seek_read( infd, header.data, File_header::size, pos ) == File_header::size && - header.verify_magic() && header.verify_version() ) + if( !header.verify_magic() || !header.verify_version() || + !isvalid_ds( dictionary_size ) ) { - error_ = "Last member in input file is truncated or corrupt."; - retval_ = 2; break; + if( !member_vector.empty() ) + set_num_error( "Bad header at pos ", pos - member_size ); + else if( skip_trailing_data( infd, pos ) ) + { if( ignore_trailing ) continue; + error_ = trailing_msg; retval_ = 2; return; } + break; } pos -= member_size; - member_vector.push_back( Member( 0, trailer.data_size(), - pos, member_size, dictionary_size ) ); + member_vector.push_back( Member( 0, trailer.data_size(), pos, + member_size, dictionary_size ) ); } if( pos != 0 || member_vector.empty() ) { diff --git a/file_index.h b/file_index.h index 8345796..5b9813e 100644 --- a/file_index.h +++ b/file_index.h @@ -1,5 +1,5 @@ /* Plzip - Parallel compressor compatible with lzip - Copyright (C) 2009-2016 Antonio Diaz Diaz. + Copyright (C) 2009-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 @@ -50,26 +50,31 @@ class File_index std::vector< Member > member_vector; std::string error_; + const long long isize; int retval_; void set_errno_error( const char * const msg ); - void set_num_error( const char * const msg1, unsigned long long num, - const char * const msg2 = "" ); + void set_num_error( const char * const msg, unsigned long long num ); + bool skip_trailing_data( const int fd, long long & pos ); public: - File_index( const int infd, const bool ignore_garbage ); + File_index( const int infd, const bool ignore_trailing ); long members() const { return member_vector.size(); } const std::string & error() const { return error_; } int retval() const { return retval_; } - long long data_end() const - { if( member_vector.size() ) return member_vector.back().dblock.end(); - else return 0; } + long long udata_size() const + { if( member_vector.empty() ) return 0; + return member_vector.back().dblock.end(); } - long long file_end() const - { if( member_vector.size() ) return member_vector.back().mblock.end(); - else return 0; } + long long cdata_size() const + { if( member_vector.empty() ) return 0; + return member_vector.back().mblock.end(); } + + // total size including trailing data (if any) + long long file_size() const + { if( isize >= 0 ) return isize; else return 0; } const Block & dblock( const long i ) const { return member_vector[i].dblock; } @@ -0,0 +1,121 @@ +/* Plzip - Parallel compressor compatible with lzip + Copyright (C) 2009-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 <http://www.gnu.org/licenses/>. +*/ + +#define _FILE_OFFSET_BITS 64 + +#include <cstdio> +#include <cstring> +#include <string> +#include <vector> +#include <stdint.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "lzip.h" +#include "file_index.h" + + +namespace { + +void list_line( const unsigned long long uncomp_size, + const unsigned long long comp_size, + const char * const input_filename ) + { + if( uncomp_size > 0 ) + std::printf( "%15llu %15llu %6.2f%% %s\n", uncomp_size, comp_size, + 100.0 * ( 1.0 - ( (double)comp_size / uncomp_size ) ), + input_filename ); + else + std::printf( "%15llu %15llu -INF%% %s\n", uncomp_size, comp_size, + input_filename ); + } + +} // end namespace + + +int list_files( const std::vector< std::string > & filenames, + const bool ignore_trailing ) + { + unsigned long long total_comp = 0, total_uncomp = 0; + int files = 0, retval = 0; + bool first_post = true; + bool stdin_used = false; + for( unsigned i = 0; i < filenames.size(); ++i ) + { + const bool from_stdin = ( filenames[i] == "-" ); + if( from_stdin ) { if( stdin_used ) continue; else stdin_used = true; } + const char * const input_filename = + from_stdin ? "(stdin)" : filenames[i].c_str(); + struct stat in_stats; // not used + const int infd = from_stdin ? STDIN_FILENO : + open_instream( input_filename, &in_stats, true, true ); + if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } + + const File_index file_index( infd, ignore_trailing ); + close( infd ); + if( file_index.retval() != 0 ) + { + show_file_error( input_filename, file_index.error().c_str() ); + if( retval < file_index.retval() ) retval = file_index.retval(); + continue; + } + if( verbosity >= 0 ) + { + const unsigned long long udata_size = file_index.udata_size(); + const unsigned long long cdata_size = file_index.cdata_size(); + total_comp += cdata_size; total_uncomp += udata_size; ++files; + if( first_post ) + { + first_post = false; + if( verbosity >= 1 ) std::fputs( " dict memb trail ", stdout ); + std::fputs( " uncompressed compressed saved name\n", stdout ); + } + if( verbosity >= 1 ) + { + unsigned dictionary_size = 0; + for( long i = 0; i < file_index.members(); ++i ) + dictionary_size = + std::max( dictionary_size, file_index.dictionary_size( i ) ); + const long long trailing_size = file_index.file_size() - cdata_size; + std::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 ) + { + std::fputs( " member data_pos data_size member_pos member_size\n", stdout ); + for( long i = 0; i < file_index.members(); ++i ) + { + const Block & db = file_index.dblock( i ); + const Block & mb = file_index.mblock( i ); + std::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 + } + std::fflush( stdout ); + } + } + if( verbosity >= 0 && files > 1 ) + { + if( verbosity >= 1 ) std::fputs( " ", stdout ); + list_line( total_uncomp, total_comp, "(totals)" ); + std::fflush( stdout ); + } + return retval; + } @@ -1,5 +1,5 @@ /* Plzip - Parallel compressor compatible with lzip - Copyright (C) 2009-2016 Antonio Diaz Diaz. + Copyright (C) 2009-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 @@ -88,6 +88,12 @@ struct File_header void set_magic() { std::memcpy( data, magic_string, 4 ); data[4] = 1; } bool verify_magic() const { return ( std::memcmp( data, magic_string, 4 ) == 0 ); } + bool verify_prefix( const int size ) const // detect truncated header + { + for( int i = 0; i < size && i < 4; ++i ) + if( data[i] != magic_string[i] ) return false; + return ( size > 0 ); + } uint8_t version() const { return data[4]; } bool verify_version() const { return ( data[4] == 1 ); } @@ -108,7 +114,7 @@ struct File_header { const unsigned base_size = 1 << data[5]; const unsigned fraction = base_size / 16; - for( int i = 7; i >= 1; --i ) + for( unsigned i = 7; i >= 1; --i ) if( base_size - ( i * fraction ) >= sz ) { data[5] |= ( i << 5 ); break; } } @@ -157,6 +163,10 @@ struct File_trailer }; +const char * const bad_magic_msg = "Bad magic number (file not in lzip format)."; +const char * const bad_dict_msg = "Invalid dictionary size in member header."; +const char * const trailing_msg = "Trailing data not allowed."; + // defined in compress.cc int readblock( const int fd, uint8_t * const buf, const int size ); int writeblock( const int fd, const uint8_t * const buf, const int size ); @@ -185,25 +195,34 @@ int dec_stdout( const int num_workers, const int infd, const int outfd, // defined in dec_stream.cc int dec_stream( const int num_workers, const int infd, const int outfd, const Pretty_print & pp, const int debug_level, - const bool ignore_garbage ); + const bool ignore_trailing ); // defined in decompress.cc int preadblock( const int fd, uint8_t * const buf, const int size, const long long pos ); -int pwriteblock( const int fd, const uint8_t * const buf, const int size, - const long long pos ); int decompress_read_error( struct LZ_Decoder * const decoder, const Pretty_print & pp, const int worker_id ); int decompress( int num_workers, const int infd, const int outfd, const Pretty_print & pp, const int debug_level, - const bool ignore_garbage, const bool infd_isreg ); + const bool ignore_trailing, const bool infd_isreg ); + +// defined in list.cc +int list_files( const std::vector< std::string > & filenames, + const bool ignore_trailing ); // defined in main.cc extern int verbosity; -void cleanup_and_fail( const int retval = 1 ); // terminate the program +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 = false ); +void cleanup_and_fail( const int retval = 1 ); // terminate the program void show_error( const char * const msg, const int errcode = 0, const bool help = false ); +void show_file_error( const char * const filename, const char * const msg, + const int errcode = 0 ); void internal_error( const char * const msg ); void show_progress( const int packet_size, const Pretty_print * const p = 0, @@ -1,6 +1,6 @@ /* Plzip - Parallel compressor compatible with lzip Copyright (C) 2009 Laszlo Ersek. - Copyright (C) 2009-2016 Antonio Diaz Diaz. + Copyright (C) 2009-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 @@ -73,10 +73,10 @@ namespace { const char * const Program_name = "Plzip"; const char * const program_name = "plzip"; -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 } }; @@ -87,7 +87,7 @@ struct Lzma_options int match_len_limit; // 5 .. 273 }; -enum Mode { m_compress, m_decompress, m_test }; +enum Mode { m_compress, m_decompress, m_list, m_test }; std::string output_filename; int outfd = -1; @@ -108,6 +108,7 @@ void show_help( const long num_online ) " -f, --force overwrite existing output files\n" " -F, --recompress force re-compression of compressed files\n" " -k, --keep keep (don't delete) input files\n" + " -l, --list print (un)compressed file sizes\n" " -m, --match-length=<bytes> set match length limit in bytes [36]\n" " -n, --threads=<n> set number of (de)compression threads [%ld]\n" " -o, --output=<file> if reading standard input, write to <file>\n" @@ -120,7 +121,7 @@ void show_help( const long num_online ) " --best alias for -9\n", num_online ); if( verbosity >= 1 ) { - std::printf( " -D, --debug=<level> (0-1) print debug statistics to stderr\n" ); + std::printf( " --debug=<level> (0-1) print debug statistics to stderr\n" ); } std::printf( "If no file names are given, or if a file is '-', plzip compresses or\n" "decompresses from standard input to standard output.\n" @@ -154,23 +155,38 @@ void show_version() } // end namespace +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; + bool exact = ( num % factor == 0 ); + + for( int 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; + } + + 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; - bool exact = ( num % factor == 0 ); - - for( int i = 0; i < 8 && ( num > 9999 || ( exact && num >= factor ) ); ++i ) - { num /= factor; if( num % factor != 0 ) exact = false; - p = prefix[i]; np = ""; } - std::fprintf( stderr, "dictionary size %s%4u %sB. ", np, num, p ); - } + std::fprintf( stderr, "dictionary %s. ", format_ds( dictionary_size ) ); } namespace { @@ -190,7 +206,7 @@ unsigned long 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 switch( tail[0] ) { @@ -228,7 +244,7 @@ unsigned long long getnum( const char * const ptr, int get_dict_size( const char * const arg ) { char * tail; - const int bits = std::strtol( arg, &tail, 0 ); + const long bits = std::strtol( arg, &tail, 0 ); if( bits >= LZ_min_dictionary_bits() && bits <= LZ_max_dictionary_bits() && *tail == 0 ) return ( 1 << bits ); @@ -239,62 +255,75 @@ int get_dict_size( const char * const arg ) } +void set_mode( Mode & program_mode, const Mode new_mode ) + { + if( program_mode != m_compress && program_mode != new_mode ) + { + show_error( "Only one operation can be specified.", 0, true ); + std::exit( 1 ); + } + program_mode = new_mode; + } + + int extension_index( const std::string & name ) { - for( int i = 0; known_extensions[i].from; ++i ) + for( int eindex = 0; known_extensions[eindex].from; ++eindex ) { - const std::string ext( known_extensions[i].from ); + const std::string ext( known_extensions[eindex].from ); if( name.size() > ext.size() && name.compare( name.size() - ext.size(), ext.size(), ext ) == 0 ) - return i; + return eindex; } return -1; } +} // end namespace int open_instream( const char * const name, struct stat * const in_statsp, - const Mode program_mode, const int eindex, - const bool recompress, const bool to_stdout ) + const bool no_ofile, const bool reg_only ) { - int infd = -1; - if( program_mode == m_compress && !recompress && eindex >= 0 ) - { - if( verbosity >= 0 ) - std::fprintf( stderr, "%s: Input file '%s' already has '%s' suffix.\n", - program_name, name, known_extensions[eindex].from ); - } + int infd = open( name, O_RDONLY | O_BINARY ); + if( infd < 0 ) + show_file_error( name, "Can't open input file", errno ); else { - infd = open( name, O_RDONLY | O_BINARY ); - if( infd < 0 ) + 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 ) ) ); + if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || !no_ofile ) ) ) { if( verbosity >= 0 ) - std::fprintf( stderr, "%s: Can't open input file '%s': %s\n", - program_name, name, std::strerror( errno ) ); - } - else - { - const int i = fstat( infd, in_statsp ); - const mode_t mode = in_statsp->st_mode; - const bool can_read = ( i == 0 && - ( S_ISBLK( mode ) || S_ISCHR( mode ) || - S_ISFIFO( mode ) || S_ISSOCK( mode ) ) ); - const bool no_ofile = ( to_stdout || program_mode == m_test ); - if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || !no_ofile ) ) ) - { - if( verbosity >= 0 ) - std::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" : "" ); - close( infd ); - infd = -1; - } + std::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" : "" ); + close( infd ); + infd = -1; } } return infd; } +namespace { + +int open_instream2( const char * const name, struct stat * const in_statsp, + const Mode program_mode, const int eindex, + const bool recompress, const bool to_stdout ) + { + if( program_mode == m_compress && !recompress && eindex >= 0 ) + { + if( verbosity >= 0 ) + std::fprintf( stderr, "%s: Input file '%s' already has '%s' suffix.\n", + program_name, name, known_extensions[eindex].from ); + return -1; + } + const bool no_ofile = ( to_stdout || program_mode == m_test ); + return open_instream( name, in_statsp, no_ofile, false ); + } + void set_c_outname( const std::string & name ) { @@ -303,15 +332,15 @@ void set_c_outname( const std::string & name ) } -void set_d_outname( const std::string & name, const int i ) +void set_d_outname( const std::string & name, const int eindex ) { - if( i >= 0 ) + if( eindex >= 0 ) { - const std::string from( known_extensions[i].from ); + const std::string from( known_extensions[eindex].from ); if( name.size() > from.size() ) { output_filename.assign( name, 0, name.size() - from.size() ); - output_filename += known_extensions[i].to; + output_filename += known_extensions[eindex].to; return; } } @@ -345,7 +374,8 @@ bool open_outstream( const bool force, const bool from_stdin ) } -bool check_tty( const int infd, const Mode program_mode ) +bool check_tty( const char * const input_filename, const int infd, + const Mode program_mode ) { if( program_mode == m_compress && isatty( outfd ) ) { @@ -355,7 +385,8 @@ bool check_tty( const int infd, const Mode program_mode ) if( ( program_mode == m_decompress || program_mode == m_test ) && isatty( infd ) ) { - show_error( "I won't read compressed data from a terminal.", 0, true ); + show_file_error( input_filename, + "I won't read compressed data from a terminal." ); return false; } return true; @@ -454,6 +485,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; + std::fprintf( stderr, "%s: %s: %s", program_name, filename, msg ); + if( errcode > 0 ) std::fprintf( stderr, ": %s", std::strerror( errcode ) ); + std::fputc( '\n', stderr ); + } + + void internal_error( const char * const msg ) { if( verbosity >= 0 ) @@ -504,7 +545,6 @@ int main( const int argc, const char * const argv[] ) { 3 << 23, 132 }, // -8 { 1 << 25, 273 } }; // -9 Lzma_options encoder_options = option_mapping[6]; // default = "-6" - std::string input_filename; std::string default_output_filename; std::vector< std::string > filenames; int data_size = 0; @@ -528,6 +568,7 @@ int main( const int argc, const char * const argv[] ) if( max_workers < 1 || max_workers > INT_MAX / (int)sizeof (pthread_t) ) max_workers = INT_MAX / sizeof (pthread_t); + enum Optcode { opt_dbg = 256 }; const Arg_parser::Option options[] = { { '0', "fast", Arg_parser::no }, @@ -545,11 +586,11 @@ int main( const int argc, const char * const argv[] ) { 'B', "data-size", Arg_parser::yes }, { 'c', "stdout", Arg_parser::no }, { 'd', "decompress", Arg_parser::no }, - { 'D', "debug", Arg_parser::yes }, { 'f', "force", Arg_parser::no }, { 'F', "recompress", Arg_parser::no }, { 'h', "help", Arg_parser::no }, { 'k', "keep", Arg_parser::no }, + { 'l', "list", Arg_parser::no }, { 'm', "match-length", Arg_parser::yes }, { 'n', "threads", Arg_parser::yes }, { 'o', "output", Arg_parser::yes }, @@ -559,7 +600,8 @@ int main( const int argc, const char * const argv[] ) { 't', "test", Arg_parser::no }, { 'v', "verbose", Arg_parser::no }, { 'V', "version", Arg_parser::no }, - { 0 , 0, Arg_parser::no } }; + { opt_dbg, "debug", Arg_parser::yes }, + { 0 , 0, Arg_parser::no } }; const Arg_parser parser( argc, argv, options ); if( parser.error().size() ) // bad option @@ -570,8 +612,8 @@ int main( const int argc, const char * const argv[] ) { const int code = parser.code( argind ); if( !code ) break; // no more options - const std::string & arg = parser.argument( argind ); - const char * const ptr = arg.c_str(); + const std::string & sarg = parser.argument( argind ); + const char * const arg = sarg.c_str(); switch( code ) { case '0': case '1': case '2': case '3': case '4': @@ -579,27 +621,28 @@ int main( const int argc, const char * const argv[] ) encoder_options = option_mapping[code-'0']; break; case 'a': ignore_trailing = false; break; case 'b': break; - case 'B': data_size = getnum( ptr, 2 * LZ_min_dictionary_size(), + case 'B': data_size = getnum( arg, 2 * LZ_min_dictionary_size(), 2 * LZ_max_dictionary_size() ); break; case 'c': to_stdout = true; break; - case 'd': program_mode = m_decompress; break; - case 'D': debug_level = getnum( ptr, 0, 3 ); break; + case 'd': set_mode( program_mode, m_decompress ); break; case 'f': force = true; break; case 'F': recompress = true; break; case 'h': show_help( num_online ); return 0; case 'k': keep_input_files = true; break; + case 'l': set_mode( program_mode, m_list ); break; case 'm': encoder_options.match_len_limit = - getnum( ptr, LZ_min_match_len_limit(), + getnum( arg, LZ_min_match_len_limit(), LZ_max_match_len_limit() ); break; - case 'n': num_workers = getnum( ptr, 1, max_workers ); break; - case 'o': default_output_filename = arg; break; + case 'n': num_workers = getnum( arg, 1, max_workers ); break; + case 'o': default_output_filename = sarg; break; case 'q': verbosity = -1; break; - case 's': encoder_options.dictionary_size = get_dict_size( ptr ); + case 's': encoder_options.dictionary_size = get_dict_size( arg ); break; case 'S': break; - case 't': program_mode = m_test; break; + case 't': set_mode( program_mode, m_test ); break; case 'v': if( verbosity < 4 ) ++verbosity; break; case 'V': show_version(); return 0; + case opt_dbg: debug_level = getnum( arg, 0, 3 ); break; default : internal_error( "uncaught option." ); } } // end process options @@ -609,6 +652,17 @@ int main( const int argc, const char * const argv[] ) setmode( STDOUT_FILENO, O_BINARY ); #endif + bool filenames_given = false; + for( ; argind < parser.arguments(); ++argind ) + { + filenames.push_back( parser.argument( argind ) ); + if( filenames.back() != "-" ) filenames_given = true; + } + if( filenames.empty() ) filenames.push_back("-"); + + if( program_mode == m_list ) + return list_files( filenames, ignore_trailing ); + if( program_mode == m_test ) outfd = -1; @@ -626,14 +680,6 @@ int main( const int argc, const char * const argv[] ) if( num_workers <= 0 ) num_workers = std::min( num_online, max_workers ); - bool filenames_given = false; - for( ; argind < parser.arguments(); ++argind ) - { - filenames.push_back( parser.argument( argind ) ); - if( filenames.back() != "-" ) filenames_given = true; - } - - if( filenames.empty() ) filenames.push_back("-"); if( !to_stdout && program_mode != m_test && ( filenames_given || default_output_filename.size() ) ) set_signals(); @@ -644,13 +690,13 @@ int main( const int argc, const char * const argv[] ) bool stdin_used = false; for( unsigned i = 0; i < filenames.size(); ++i ) { + std::string input_filename; struct stat in_stats; output_filename.clear(); if( filenames[i].empty() || filenames[i] == "-" ) { if( stdin_used ) continue; else stdin_used = true; - input_filename.clear(); infd = STDIN_FILENO; if( program_mode != m_test ) { @@ -672,10 +718,9 @@ int main( const int argc, const char * const argv[] ) } else { - input_filename = filenames[i]; - const int eindex = extension_index( input_filename ); - infd = open_instream( input_filename.c_str(), &in_stats, program_mode, - eindex, recompress, to_stdout ); + const int eindex = extension_index( input_filename = filenames[i] ); + infd = open_instream2( input_filename.c_str(), &in_stats, program_mode, + eindex, recompress, to_stdout ); if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } if( program_mode != m_test ) { @@ -695,15 +740,16 @@ int main( const int argc, const char * const argv[] ) } } - if( !check_tty( infd, program_mode ) ) + pp.set_name( input_filename ); + if( !check_tty( pp.name(), infd, program_mode ) ) { if( retval < 1 ) retval = 1; + if( program_mode == m_test ) { close( infd ); infd = -1; continue; } cleanup_and_fail( retval ); } const struct stat * const in_statsp = input_filename.size() ? &in_stats : 0; const bool infd_isreg = in_statsp && S_ISREG( in_statsp->st_mode ); - pp.set_name( input_filename ); if( verbosity >= 1 ) pp(); int tmp; if( program_mode == m_compress ) diff --git a/testsuite/check.sh b/testsuite/check.sh index 51f9e41..4421fc4 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # check script for Plzip - Parallel compressor compatible with lzip -# Copyright (C) 2009-2016 Antonio Diaz Diaz. +# Copyright (C) 2009-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,185 +31,233 @@ 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 plzip-%s..." "$2" "${LZIP}" -fkqm4 in -if [ $? = 1 ] && [ ! -e in.lz ] ; then printf . ; else printf - ; fail=1 ; fi +{ [ $? = 1 ] && [ ! -e in.lz ] ; } || test_failed $LINENO "${LZIP}" -fkqm274 in -if [ $? = 1 ] && [ ! -e in.lz ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -fkqs-1 in -if [ $? = 1 ] && [ ! -e in.lz ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -fkqs0 in -if [ $? = 1 ] && [ ! -e in.lz ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -fkqs4095 in -if [ $? = 1 ] && [ ! -e in.lz ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -fkqs513MiB in -if [ $? = 1 ] && [ ! -e in.lz ] ; then printf . ; else printf - ; fail=1 ; fi +{ [ $? = 1 ] && [ ! -e in.lz ] ; } || test_failed $LINENO +for i in bad_size -1 0 4095 513MiB 1G 1T 1P 1E 1Z 1Y 10KB ; do + "${LZIP}" -fkqs $i in + { [ $? = 1 ] && [ ! -e in.lz ] ; } || test_failed $LINENO $i +done +"${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 -"${LZIP}" < in > anyothername || fail=1 -"${LZIP}" -d -o copy - anyothername - < "${in_lz}" -if [ $? = 0 ] && cmp in copy && cmp in anyothername.out ; then - printf . ; else printf - ; fail=1 ; fi +"${LZIP}" < in > anyothername || test_failed $LINENO +"${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 -"${LZIP}" -o copy2 < in2 || fail=1 -"${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 + +"${LZIP}" --output=copy2 < in2 || test_failed $LINENO +"${LZIP}" -lq copy2.lz || test_failed $LINENO +"${LZIP}" -t copy2.lz || test_failed $LINENO +"${LZIP}" -cd copy2.lz > copy2 || test_failed $LINENO +cmp in2 copy2 || test_failed $LINENO -printf "garbage" >> 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 printf "\ntesting compression..." -"${LZIP}" -cfq "${in_lz}" > out # /dev/null is a tty on OS/2 -if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cF "${in_lz}" > out || fail=1 -"${LZIP}" -cd out | "${LZIP}" -d > copy || fail=1 -cmp in copy || fail=1 -printf . +"${LZIP}" -cf "${in_lz}" > out 2> /dev/null # /dev/null is a tty on OS/2 +[ $? = 1 ] || test_failed $LINENO +"${LZIP}" -cFvvm36 "${in_lz}" > out 2> /dev/null || test_failed $LINENO +"${LZIP}" -cd out | "${LZIP}" -d > copy || test_failed $LINENO +cmp in copy || test_failed $LINENO for i in s4Ki 0 1 2 3 4 5 6 7 8 9 ; do - "${LZIP}" -k -$i in || fail=1 - mv -f in.lz copy.lz || fail=1 - printf "garbage" >> copy.lz || fail=1 - "${LZIP}" -df copy.lz || fail=1 - cmp in copy || fail=1 + "${LZIP}" -k -$i in || test_failed $LINENO $i + mv -f in.lz copy.lz || test_failed $LINENO $i + printf "garbage" >> copy.lz || framework_failure + "${LZIP}" -df copy.lz || test_failed $LINENO $i + cmp in copy || test_failed $LINENO $i done -printf . for i in s4Ki 0 1 2 3 4 5 6 7 8 9 ; do - "${LZIP}" -c -$i in > out || fail=1 - printf "g" >> out || fail=1 - "${LZIP}" -cd out > copy || fail=1 - cmp in copy || fail=1 + "${LZIP}" -c -$i in > out || test_failed $LINENO $i + printf "g" >> out || framework_failure + "${LZIP}" -cd out > copy || test_failed $LINENO $i + cmp in copy || test_failed $LINENO $i done -printf . for i in s4Ki 0 1 2 3 4 5 6 7 8 9 ; do - "${LZIP}" -$i < in > out || fail=1 - "${LZIP}" -d < out > copy || fail=1 - cmp in copy || fail=1 + "${LZIP}" -$i < in > out || test_failed $LINENO $i + "${LZIP}" -d < out > copy || test_failed $LINENO $i + cmp in copy || test_failed $LINENO $i done -printf . for i in s4Ki 0 1 2 3 4 5 6 7 8 9 ; do - "${LZIP}" -f -$i -o out < in || fail=1 - "${LZIP}" -df -o copy < out.lz || fail=1 - cmp in copy || fail=1 + "${LZIP}" -f -$i -o out < in || test_failed $LINENO $i + "${LZIP}" -df -o copy < out.lz || test_failed $LINENO $i + cmp in copy || test_failed $LINENO $i done -printf . cat in in in in > in4 || framework_failure for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do - "${LZIP}" -c -s4Ki -B8Ki -n$i in4 > out4.lz || fail=1 - printf "g" >> out4.lz || fail=1 - "${LZIP}" -cd -n$i out4.lz > copy4 || fail=1 - cmp in4 copy4 || fail=1 - "${LZIP}" -d -n$i out4.lz || fail=1 - cmp in4 out4 || fail=1 + "${LZIP}" -c -s4Ki -B8Ki -n$i in4 > out4.lz || test_failed $LINENO $i + printf "g" >> out4.lz || framework_failure + "${LZIP}" -cd -n$i out4.lz > copy4 || test_failed $LINENO $i + cmp in4 copy4 || test_failed $LINENO $i + "${LZIP}" -d -n$i out4.lz || test_failed $LINENO $i + cmp in4 out4 || test_failed $LINENO $i rm -f out4 done -printf . for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do - "${LZIP}" -s4Ki -B8Ki -n$i < in4 > out4 || fail=1 - printf "g" >> out4 || fail=1 - "${LZIP}" -d -n$i < out4 > copy4 || fail=1 - cmp in4 copy4 || fail=1 + "${LZIP}" -s4Ki -B8Ki -n$i < in4 > out4 || test_failed $LINENO $i + printf "g" >> out4 || framework_failure + "${LZIP}" -d -n$i < out4 > copy4 || test_failed $LINENO $i + cmp in4 copy4 || test_failed $LINENO $i done -printf . + +cat in in in in in in in in | "${LZIP}" -1s4Ki | "${LZIP}" -t || + test_failed $LINENO + +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}" -tq ingin.lz -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +[ $? = 2 ] || test_failed $LINENO "${LZIP}" -cdq ingin.lz > out -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -t < ingin.lz || fail=1 -"${LZIP}" -d < ingin.lz > copy || fail=1 -cmp in copy || fail=1 -printf . - -dd if="${in_lz}" bs=1024 count=6 > trunc.lz 2> /dev/null || framework_failure -"${LZIP}" -tq trunc.lz -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -tq < trunc.lz -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -cdq trunc.lz > out -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi -"${LZIP}" -dq < trunc.lz > out -if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi +[ $? = 2 ] || 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 diff --git a/testsuite/test.txt.lz b/testsuite/test.txt.lz Binary files differindex 41d2e39..22cea6e 100644 --- a/testsuite/test.txt.lz +++ b/testsuite/test.txt.lz |