diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2018-02-13 06:55:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2018-02-13 06:56:31 +0000 |
commit | c47aa7dfb21de77e630d5738d73a0f38204bfe0b (patch) | |
tree | 73bf32ded6d79f9d4876203cb922233e44f757c3 /main.c | |
parent | Releasing debian version 1.9-4. (diff) | |
download | clzip-c47aa7dfb21de77e630d5738d73a0f38204bfe0b.tar.xz clzip-c47aa7dfb21de77e630d5738d73a0f38204bfe0b.zip |
Merging upstream version 1.10.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 340 |
1 files changed, 208 insertions, 132 deletions
@@ -1,5 +1,5 @@ /* Clzip - LZMA lossless data compressor - Copyright (C) 2010-2017 Antonio Diaz Diaz. + Copyright (C) 2010-2018 Antonio Diaz Diaz. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -71,7 +71,7 @@ int verbosity = 0; const char * const Program_name = "Clzip"; const char * const program_name = "clzip"; -const char * const program_year = "2017"; +const char * const program_year = "2018"; const char * invocation_name = 0; const struct { const char * from; const char * to; } known_extensions[] = { @@ -111,12 +111,13 @@ static void show_help( void ) " -o, --output=<file> if reading standard input, write to <file>\n" " -q, --quiet suppress all messages\n" " -s, --dictionary-size=<bytes> set dictionary size limit in bytes [8 MiB]\n" - " -S, --volume-size=<bytes> set volume size limit in bytes\n" + " -S, --volume-size=<bytes> set volume size limit in bytes, implies -k\n" " -t, --test test compressed file integrity\n" " -v, --verbose be verbose (a 2nd -v gives more)\n" " -0 .. -9 set compression level [default 6]\n" " --fast alias for -0\n" " --best alias for -9\n" + " --loose-trailing allow trailing data seeming corrupt header\n" "If no file names are given, or if a file is '-', clzip compresses or\n" "decompresses from standard input to standard output.\n" "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" @@ -146,6 +147,35 @@ static void show_version( void ) } +/* assure at least a minimum size for buffer 'buf' */ +void * resize_buffer( void * buf, const unsigned min_size ) + { + if( buf ) buf = realloc( buf, min_size ); + else buf = malloc( min_size ); + if( !buf ) + { + show_error( "Not enough memory.", 0, false ); + cleanup_and_fail( 1 ); + } + return buf; + } + + +void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ) + { + if( verbosity >= 0 ) + { + if( pp->first_post ) + { + pp->first_post = false; + fputs( pp->padded_name, stderr ); + if( !msg ) fflush( stderr ); + } + if( msg ) fprintf( stderr, "%s\n", msg ); + } + } + + const char * bad_version( const unsigned version ) { static char buf[80]; @@ -163,10 +193,10 @@ const char * format_ds( const unsigned dictionary_size ) { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; const char * p = ""; const char * np = " "; - unsigned num = dictionary_size, i; + unsigned num = dictionary_size; bool exact = ( num % factor == 0 ); - for( i = 0; i < 8 && ( num > 9999 || ( exact && num >= factor ) ); ++i ) + int i; for( i = 0; i < 8 && ( num > 9999 || ( exact && num >= factor ) ); ++i ) { num /= factor; if( num % factor != 0 ) exact = false; p = prefix[i]; np = ""; } snprintf( buf, bufsize, "%s%4u %sB", np, num, p ); @@ -174,10 +204,9 @@ const char * format_ds( const unsigned dictionary_size ) } -static void show_header( const unsigned dictionary_size ) +void show_header( const unsigned dictionary_size ) { - if( verbosity >= 3 ) - fprintf( stderr, "dictionary %s. ", format_ds( dictionary_size ) ); + fprintf( stderr, "dictionary %s, ", format_ds( dictionary_size ) ); } @@ -271,6 +300,43 @@ static int extension_index( const char * const name ) } +static void set_c_outname( const char * const name, const bool force_ext, + const bool multifile ) + { + output_filename = resize_buffer( output_filename, strlen( name ) + 5 + + strlen( known_extensions[0].from ) + 1 ); + strcpy( output_filename, name ); + if( multifile ) strcat( output_filename, "00001" ); + if( force_ext || multifile || extension_index( output_filename ) < 0 ) + strcat( output_filename, known_extensions[0].from ); + } + + +static void set_d_outname( const char * const name, const int eindex ) + { + const unsigned name_len = strlen( name ); + if( eindex >= 0 ) + { + const char * const from = known_extensions[eindex].from; + const unsigned from_len = strlen( from ); + if( name_len > from_len ) + { + output_filename = resize_buffer( output_filename, name_len + + strlen( known_extensions[eindex].to ) + 1 ); + strcpy( output_filename, name ); + strcpy( output_filename + name_len - from_len, known_extensions[eindex].to ); + return; + } + } + output_filename = resize_buffer( output_filename, name_len + 4 + 1 ); + strcpy( output_filename, name ); + strcat( output_filename, ".out" ); + if( verbosity >= 1 ) + fprintf( stderr, "%s: Can't guess original name for '%s' -- using '%s'\n", + program_name, name, output_filename ); + } + + int open_instream( const char * const name, struct stat * const in_statsp, const bool no_ofile, const bool reg_only ) { @@ -315,55 +381,6 @@ static int open_instream2( const char * const name, struct stat * const in_stats } -/* assure at least a minimum size for buffer 'buf' */ -void * resize_buffer( void * buf, const unsigned min_size ) - { - if( buf ) buf = realloc( buf, min_size ); - else buf = malloc( min_size ); - if( !buf ) - { - show_error( "Not enough memory.", 0, false ); - cleanup_and_fail( 1 ); - } - return buf; - } - - -static void set_c_outname( const char * const name, const bool multifile ) - { - output_filename = resize_buffer( output_filename, strlen( name ) + 5 + - strlen( known_extensions[0].from ) + 1 ); - strcpy( output_filename, name ); - if( multifile ) strcat( output_filename, "00001" ); - strcat( output_filename, known_extensions[0].from ); - } - - -static void set_d_outname( const char * const name, const int eindex ) - { - const unsigned name_len = strlen( name ); - if( eindex >= 0 ) - { - const char * const from = known_extensions[eindex].from; - const unsigned from_len = strlen( from ); - if( name_len > from_len ) - { - output_filename = resize_buffer( output_filename, name_len + - strlen( known_extensions[eindex].to ) + 1 ); - strcpy( output_filename, name ); - strcpy( output_filename + name_len - from_len, known_extensions[eindex].to ); - return; - } - } - output_filename = resize_buffer( output_filename, name_len + 4 + 1 ); - strcpy( output_filename, name ); - strcat( output_filename, ".out" ); - if( verbosity >= 1 ) - fprintf( stderr, "%s: Can't guess original name for '%s' -- using '%s'\n", - program_name, name, output_filename ); - } - - static bool open_outstream( const bool force, const bool from_stdin ) { const mode_t usr_rw = S_IRUSR | S_IWUSR; @@ -479,14 +496,13 @@ struct Poly_encoder }; -static int compress( const unsigned long long member_size, +static int compress( const unsigned long long cfile_size, + const unsigned long long member_size, const unsigned long long volume_size, const int infd, const struct Lzma_options * const encoder_options, struct Pretty_print * const pp, const struct stat * const in_statsp, const bool zero ) { - const unsigned long long cfile_size = - (in_statsp && S_ISREG( in_statsp->st_mode )) ? in_statsp->st_size / 100 : 0; unsigned long long in_size = 0, out_size = 0, partial_volume_size = 0; int retval = 0; struct Poly_encoder encoder = { 0, 0, 0 }; /* polymorphic encoder */ @@ -524,7 +540,7 @@ static int compress( const unsigned long long member_size, { const unsigned long long size = ( volume_size > 0 ) ? min( member_size, volume_size - partial_volume_size ) : member_size; - show_progress( in_size, &encoder.eb->mb, pp, cfile_size ); /* init */ + show_cprogress( cfile_size, in_size, &encoder.eb->mb, pp ); /* init */ if( ( zero && !FLZe_encode_member( encoder.fe, size ) ) || ( !zero && !LZe_encode_member( encoder.e, size ) ) ) { Pp_show_msg( pp, "Encoder error." ); retval = 1; break; } @@ -554,11 +570,11 @@ static int compress( const unsigned long long member_size, if( in_size == 0 || out_size == 0 ) fputs( " no data compressed.\n", stderr ); else - fprintf( stderr, "%6.3f:1, %6.3f bits/byte, " - "%5.2f%% saved, %llu in, %llu out.\n", + fprintf( stderr, "%6.3f:1, %5.2f%% ratio, %5.2f%% saved, " + "%llu in, %llu out.\n", (double)in_size / out_size, - ( 8.0 * out_size ) / in_size, - 100.0 * ( 1.0 - ( (double)out_size / in_size ) ), + ( 100.0 * out_size ) / in_size, + 100.0 - ( ( 100.0 * out_size ) / in_size ), in_size, out_size ); } LZeb_free( encoder.eb ); @@ -577,9 +593,9 @@ static unsigned char xdigit( const unsigned value ) static bool show_trailing_data( const uint8_t * const data, const int size, struct Pretty_print * const pp, const bool all, - const bool ignore_trailing ) + const int ignore_trailing ) /* -1 = show */ { - if( verbosity >= 4 || !ignore_trailing ) + if( verbosity >= 4 || ignore_trailing <= 0 ) { int i; char buf[80]; @@ -597,14 +613,15 @@ static bool show_trailing_data( const uint8_t * const data, const int size, if( len < sizeof buf ) buf[len++] = '\''; if( len < sizeof buf ) buf[len] = 0; else buf[sizeof buf - 1] = 0; Pp_show_msg( pp, buf ); - if( !ignore_trailing ) show_file_error( pp->name, trailing_msg, 0 ); + if( ignore_trailing == 0 ) show_file_error( pp->name, trailing_msg, 0 ); } - return ignore_trailing; + return ( ignore_trailing > 0 ); } -static int decompress( const int infd, struct Pretty_print * const pp, - const bool ignore_trailing, const bool testing ) +static int decompress( const unsigned long long cfile_size, const int infd, + struct Pretty_print * const pp, const bool ignore_trailing, + const bool loose_trailing, const bool testing ) { unsigned long long partial_file_pos = 0; struct Range_decoder rdec; @@ -626,8 +643,12 @@ static int decompress( const int infd, struct Pretty_print * const pp, size = Rd_read_data( &rdec, header, Fh_size ); if( Rd_finished( &rdec ) ) /* End Of File */ { - if( first_member || Fh_verify_prefix( header, size ) ) - { Pp_show_msg( pp, "File ends unexpectedly at member header." ); + if( first_member ) + { show_file_error( pp->name, "File ends unexpectedly at member header.", 0 ); + retval = 2; } + else if( Fh_verify_prefix( header, size ) ) + { Pp_show_msg( pp, "Truncated header in multimember file." ); + show_trailing_data( header, size, pp, true, -1 ); retval = 2; } else if( size > 0 && !show_trailing_data( header, size, pp, true, ignore_trailing ) ) @@ -638,24 +659,27 @@ static int decompress( const int infd, struct Pretty_print * const pp, { if( first_member ) { show_file_error( pp->name, bad_magic_msg, 0 ); retval = 2; } + else if( !loose_trailing && Fh_verify_corrupt( header ) ) + { Pp_show_msg( pp, corrupt_mm_msg ); + show_trailing_data( header, size, pp, false, -1 ); + retval = 2; } else if( !show_trailing_data( header, size, pp, false, ignore_trailing ) ) retval = 2; break; } if( !Fh_verify_version( header ) ) - { - Pp_show_msg( pp, bad_version( Fh_version( header ) ) ); - retval = 2; break; - } + { Pp_show_msg( pp, bad_version( Fh_version( header ) ) ); + retval = 2; break; } dictionary_size = Fh_get_dictionary_size( header ); if( !isvalid_ds( dictionary_size ) ) { Pp_show_msg( pp, bad_dict_msg ); retval = 2; break; } if( verbosity >= 2 || ( verbosity == 1 && first_member ) ) - { Pp_show_msg( pp, 0 ); show_header( dictionary_size ); } + Pp_show_msg( pp, 0 ); if( !LZd_init( &decoder, &rdec, dictionary_size, outfd ) ) { Pp_show_msg( pp, "Not enough memory." ); retval = 1; break; } + show_dprogress( cfile_size, partial_file_pos, &rdec, pp ); /* init */ result = LZd_decode_member( &decoder, pp ); partial_file_pos += Rd_member_position( &rdec ); LZd_free( &decoder ); @@ -728,25 +752,61 @@ void internal_error( const char * const msg ) } -void show_progress( const unsigned long long partial_size, - const struct Matchfinder_base * const m, - struct Pretty_print * const p, - const unsigned long long cfile_size ) +void show_cprogress( const unsigned long long cfile_size, + const unsigned long long partial_size, + const struct Matchfinder_base * const m, + struct Pretty_print * const p ) { static unsigned long long csize = 0; /* file_size / 100 */ static unsigned long long psize = 0; static const struct Matchfinder_base * mb = 0; static struct Pretty_print * pp = 0; + static bool enabled = true; - if( verbosity < 2 ) return; - if( m ) /* initialize static vars */ - { csize = cfile_size; psize = partial_size; mb = m; pp = p; } + if( !enabled ) return; + if( p ) /* initialize static vars */ + { + if( verbosity < 2 || !isatty( STDERR_FILENO ) ) { enabled = false; return; } + csize = cfile_size; psize = partial_size; mb = m; pp = p; + } if( mb && pp ) { const unsigned long long pos = psize + Mb_data_position( mb ); if( csize > 0 ) - fprintf( stderr, "%4llu%%", pos / csize ); - fprintf( stderr, " %.1f MB\r", pos / 1000000.0 ); + fprintf( stderr, "%4llu%% %.1f MB\r", pos / csize, pos / 1000000.0 ); + else + fprintf( stderr, " %.1f MB\r", pos / 1000000.0 ); + Pp_reset( pp ); Pp_show_msg( pp, 0 ); /* restore cursor position */ + } + } + + +void show_dprogress( const unsigned long long cfile_size, + const unsigned long long partial_size, + const struct Range_decoder * const d, + struct Pretty_print * const p ) + { + static unsigned long long csize = 0; /* file_size / 100 */ + static unsigned long long psize = 0; + static const struct Range_decoder * rdec = 0; + static struct Pretty_print * pp = 0; + static int counter = 0; + static bool enabled = true; + + if( !enabled ) return; + if( p ) /* initialize static vars */ + { + if( verbosity < 2 || !isatty( STDERR_FILENO ) ) { enabled = false; return; } + csize = cfile_size; psize = partial_size; rdec = d; pp = p; counter = 0; + } + if( rdec && pp && --counter <= 0 ) + { + const unsigned long long pos = psize + Rd_member_position( rdec ); + counter = 7; /* update display every 114688 bytes */ + if( csize > 0 ) + fprintf( stderr, "%4llu%% %.1f MB\r", pos / csize, pos / 1000000.0 ); + else + fprintf( stderr, " %.1f MB\r", pos / 1000000.0 ); Pp_reset( pp ); Pp_show_msg( pp, 0 ); /* restore cursor position */ } } @@ -758,7 +818,7 @@ int main( const int argc, const char * const argv[] ) to the corresponding LZMA compression modes. */ const struct Lzma_options option_mapping[] = { - { 1 << 16, 16 }, /* -0 entry values not used */ + { 1 << 16, 16 }, /* -0 */ { 1 << 20, 5 }, /* -1 */ { 3 << 19, 6 }, /* -2 */ { 1 << 21, 8 }, /* -3 */ @@ -776,52 +836,55 @@ int main( const int argc, const char * const argv[] ) const char * default_output_filename = ""; const char ** filenames = 0; int num_filenames = 0; - int infd = -1; enum Mode program_mode = m_compress; int argind = 0; + int failed_tests = 0; int retval = 0; int i; bool filenames_given = false; bool force = false; bool ignore_trailing = true; bool keep_input_files = false; + bool loose_trailing = false; bool recompress = false; bool stdin_used = false; bool to_stdout = false; bool zero = false; struct Pretty_print pp; + enum { opt_lt = 256 }; const struct ap_Option options[] = { - { '0', "fast", ap_no }, - { '1', 0, ap_no }, - { '2', 0, ap_no }, - { '3', 0, ap_no }, - { '4', 0, ap_no }, - { '5', 0, ap_no }, - { '6', 0, ap_no }, - { '7', 0, ap_no }, - { '8', 0, ap_no }, - { '9', "best", ap_no }, - { 'a', "trailing-error", ap_no }, - { 'b', "member-size", ap_yes }, - { 'c', "stdout", ap_no }, - { 'd', "decompress", ap_no }, - { 'f', "force", ap_no }, - { 'F', "recompress", ap_no }, - { 'h', "help", ap_no }, - { 'k', "keep", ap_no }, - { 'l', "list", ap_no }, - { 'm', "match-length", ap_yes }, - { 'n', "threads", ap_yes }, - { 'o', "output", ap_yes }, - { 'q', "quiet", ap_no }, - { 's', "dictionary-size", ap_yes }, - { 'S', "volume-size", ap_yes }, - { 't', "test", ap_no }, - { 'v', "verbose", ap_no }, - { 'V', "version", ap_no }, - { 0 , 0, ap_no } }; + { '0', "fast", ap_no }, + { '1', 0, ap_no }, + { '2', 0, ap_no }, + { '3', 0, ap_no }, + { '4', 0, ap_no }, + { '5', 0, ap_no }, + { '6', 0, ap_no }, + { '7', 0, ap_no }, + { '8', 0, ap_no }, + { '9', "best", ap_no }, + { 'a', "trailing-error", ap_no }, + { 'b', "member-size", ap_yes }, + { 'c', "stdout", ap_no }, + { 'd', "decompress", ap_no }, + { 'f', "force", ap_no }, + { 'F', "recompress", ap_no }, + { 'h', "help", ap_no }, + { 'k', "keep", ap_no }, + { 'l', "list", ap_no }, + { 'm', "match-length", ap_yes }, + { 'n', "threads", ap_yes }, + { 'o', "output", ap_yes }, + { 'q', "quiet", ap_no }, + { 's', "dictionary-size", ap_yes }, + { 'S', "volume-size", ap_yes }, + { 't', "test", ap_no }, + { 'v', "verbose", ap_no }, + { 'V', "version", ap_no }, + { opt_lt, "loose-trailing", ap_no }, + { 0 , 0, ap_no } }; struct Arg_parser parser; @@ -865,6 +928,7 @@ int main( const int argc, const char * const argv[] ) case 't': set_mode( &program_mode, m_test ); break; case 'v': if( verbosity < 4 ) ++verbosity; break; case 'V': show_version(); return 0; + case opt_lt: loose_trailing = true; break; default : internal_error( "uncaught option." ); } } /* end process options */ @@ -885,7 +949,7 @@ int main( const int argc, const char * const argv[] ) } if( program_mode == m_list ) - return list_files( filenames, num_filenames, ignore_trailing ); + return list_files( filenames, num_filenames, ignore_trailing, loose_trailing ); if( program_mode == m_test ) outfd = -1; @@ -899,12 +963,14 @@ int main( const int argc, const char * const argv[] ) ( filenames_given || default_output_filename[0] ) ) set_signals(); - Pp_init( &pp, filenames, num_filenames, verbosity ); + Pp_init( &pp, filenames, num_filenames ); output_filename = resize_buffer( output_filename, 1 ); for( i = 0; i < num_filenames; ++i ) { + unsigned long long cfile_size; const char * input_filename = ""; + int infd; int tmp; struct stat in_stats; const struct stat * in_statsp; @@ -921,7 +987,7 @@ int main( const int argc, const char * const argv[] ) else { if( program_mode == m_compress ) - set_c_outname( default_output_filename, volume_size > 0 ); + set_c_outname( default_output_filename, false, volume_size > 0 ); else { output_filename = resize_buffer( output_filename, @@ -931,7 +997,7 @@ int main( const int argc, const char * const argv[] ) if( !open_outstream( force, true ) ) { if( retval < 1 ) retval = 1; - close( infd ); infd = -1; + close( infd ); continue; } } @@ -949,12 +1015,12 @@ int main( const int argc, const char * const argv[] ) else { if( program_mode == m_compress ) - set_c_outname( input_filename, volume_size > 0 ); + set_c_outname( input_filename, true, volume_size > 0 ); else set_d_outname( input_filename, eindex ); if( !open_outstream( force, false ) ) { if( retval < 1 ) retval = 1; - close( infd ); infd = -1; + close( infd ); continue; } } @@ -965,33 +1031,43 @@ int main( const int argc, const char * const argv[] ) if( !check_tty( pp.name, infd, program_mode ) ) { if( retval < 1 ) retval = 1; - if( program_mode == m_test ) { close( infd ); infd = -1; continue; } + if( program_mode == m_test ) { close( infd ); continue; } cleanup_and_fail( retval ); } in_statsp = input_filename[0] ? &in_stats : 0; + cfile_size = ( in_statsp && S_ISREG( in_statsp->st_mode ) ) ? + ( in_statsp->st_size + 99 ) / 100 : 0; if( program_mode == m_compress ) - tmp = compress( member_size, volume_size, infd, &encoder_options, &pp, - in_statsp, zero ); + tmp = compress( cfile_size, member_size, volume_size, infd, + &encoder_options, &pp, in_statsp, zero ); else - tmp = decompress( infd, &pp, ignore_trailing, program_mode == m_test ); + tmp = decompress( cfile_size, infd, &pp, ignore_trailing, + loose_trailing, program_mode == m_test ); if( tmp > retval ) retval = tmp; - if( tmp && program_mode != m_test ) cleanup_and_fail( retval ); + if( tmp ) + { if( program_mode != m_test ) cleanup_and_fail( retval ); + else ++failed_tests; } if( delete_output_on_interrupt ) close_and_set_permissions( in_statsp ); if( input_filename[0] ) { - close( infd ); infd = -1; - if( !keep_input_files && !to_stdout && program_mode != m_test ) + close( infd ); + if( !keep_input_files && !to_stdout && program_mode != m_test && + ( program_mode != m_compress || volume_size == 0 ) ) remove( input_filename ); } } if( outfd >= 0 && close( outfd ) != 0 ) { - show_error( "Can't close stdout", errno, false ); + show_error( "Error closing stdout", errno, false ); if( retval < 1 ) retval = 1; } + if( failed_tests > 0 && verbosity >= 1 && num_filenames > 1 ) + fprintf( stderr, "%s: warning: %d %s failed the test.\n", + program_name, failed_tests, + ( failed_tests == 1 ) ? "file" : "files" ); free( output_filename ); free( filenames ); ap_free( &parser ); |