diff options
Diffstat (limited to 'main.cc')
-rw-r--r-- | main.cc | 256 |
1 files changed, 166 insertions, 90 deletions
@@ -1,4 +1,4 @@ -/* Lzip - A data compressor based on the LZMA algorithm +/* Lzip - Data compressor based on the LZMA algorithm Copyright (C) 2008, 2009, 2010 Antonio Diaz Diaz. This program is free software: you can redistribute it and/or modify @@ -37,11 +37,26 @@ #include <unistd.h> #include <utime.h> #include <sys/stat.h> +#if defined(__MSVCRT__) +#include <io.h> +#define fchmod(x,y) 0 +#define fchown(x,y,z) 0 +#define SIGHUP SIGTERM +#define S_ISSOCK(x) 0 +#define S_IRGRP 0 +#define S_IWGRP 0 +#define S_IROTH 0 +#define S_IWOTH 0 +#endif +#if defined(__OS2__) +#include <io.h> +#endif #include "arg_parser.h" #include "lzip.h" #include "decoder.h" #include "encoder.h" +#include "fast_encoder.h" #if CHAR_BIT != 8 #error "Environments where CHAR_BIT != 8 are not supported." @@ -82,17 +97,18 @@ struct Lzma_options int match_len_limit; // 5..273 }; -enum Mode { m_compress = 0, m_decompress, m_test }; +enum Mode { m_compress, m_decompress, m_test }; std::string output_filename; int outfd = -1; +int verbosity = 0; mode_t outfd_mode = S_IRUSR | S_IWUSR; bool delete_output_on_interrupt = false; void show_help() throw() { - std::printf( "%s - A data compressor based on the LZMA algorithm.\n", Program_name ); + std::printf( "%s - Data compressor based on the LZMA algorithm.\n", Program_name ); std::printf( "\nUsage: %s [options] [files]\n", invocation_name ); std::printf( "\nOptions:\n" ); std::printf( " -h, --help display this help and exit\n" ); @@ -102,15 +118,15 @@ void show_help() throw() std::printf( " -d, --decompress decompress\n" ); std::printf( " -f, --force overwrite existing output files\n" ); std::printf( " -k, --keep keep (don't delete) input files\n" ); - std::printf( " -m, --match-length=<n> set match length limit in bytes [80]\n" ); + std::printf( " -m, --match-length=<n> set match length limit in bytes [36]\n" ); std::printf( " -o, --output=<file> if reading stdin, place the output into <file>\n" ); std::printf( " -q, --quiet suppress all messages\n" ); std::printf( " -s, --dictionary-size=<n> set dictionary size limit in bytes [8MiB]\n" ); std::printf( " -S, --volume-size=<n> set volume size limit in bytes\n" ); std::printf( " -t, --test test compressed file integrity\n" ); std::printf( " -v, --verbose be verbose (a 2nd -v gives more)\n" ); - std::printf( " -1 .. -9 set compression level [default 6]\n" ); - std::printf( " --fast alias for -1\n" ); + std::printf( " -0 .. -9 set compression level [default 6]\n" ); + std::printf( " --fast alias for -0\n" ); std::printf( " --best alias for -9\n" ); std::printf( "If no file names are given, %s compresses or decompresses\n", program_name ); std::printf( "from standard input to standard output.\n" ); @@ -131,26 +147,19 @@ void show_version() throw() } -const char * format_num( long long num, long long limit = 9999, - const int set_prefix = 0 ) throw() +const char * format_num( long long num ) throw() { - const char * const si_prefix[8] = - { "k", "M", "G", "T", "P", "E", "Z", "Y" }; - const char * const binary_prefix[8] = + const char * const prefix[8] = { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; - static bool si = false; - static char buf[16]; - - if( set_prefix ) si = ( set_prefix > 0 ); - const int factor = ( si ) ? 1000 : 1024; - const char * const *prefix = ( si ) ? si_prefix : binary_prefix; + enum { buf_size = 16 }; + static char buf[buf_size]; const char *p = ""; - limit = std::max( 999LL, std::min( 999999LL, limit ) ); + const int factor = 1024; - for( int i = 0; i < 8 && ( llabs( num ) > limit || + for( int i = 0; i < 8 && ( llabs( num ) > 9999 || ( llabs( num ) >= factor && num % factor == 0 ) ); ++i ) { num /= factor; p = prefix[i]; } - snprintf( buf, sizeof buf, "%lld %s", num, p ); + snprintf( buf, buf_size, "%lld %s", num, p ); return buf; } @@ -164,7 +173,7 @@ long long getnum( const char * const ptr, const int bs = 0, long long result = strtoll( ptr, &tail, 0 ); if( tail == ptr ) { - show_error( "bad or missing numerical argument", 0, true ); + show_error( "Bad or missing numerical argument.", 0, true ); std::exit( 1 ); } @@ -194,7 +203,7 @@ long long getnum( const char * const ptr, const int bs = 0, } if( bad_multiplier ) { - show_error( "bad multiplier in numerical argument", 0, true ); + show_error( "Bad multiplier in numerical argument.", 0, true ); std::exit( 1 ); } for( int i = 0; i < exponent; ++i ) @@ -206,7 +215,7 @@ long long getnum( const char * const ptr, const int bs = 0, if( !errno && ( result < llimit || result > ulimit ) ) errno = ERANGE; if( errno ) { - show_error( "numerical argument out of limits" ); + show_error( "Numerical argument out of limits." ); std::exit( 1 ); } return result; @@ -245,7 +254,7 @@ int open_instream( const std::string & name, struct stat * const in_statsp, if( program_mode == m_compress && !force && eindex >= 0 ) { if( verbosity >= 0 ) - std::fprintf( stderr, "%s: input file `%s' already has `%s' suffix.\n", + std::fprintf( stderr, "%s: Input file `%s' already has `%s' suffix.\n", program_name, name.c_str(), known_extensions[eindex].from ); } @@ -267,7 +276,7 @@ int open_instream( const std::string & name, struct stat * const in_statsp, S_ISBLK( mode ) || S_ISCHR( mode ) ) ) ) ) { if( verbosity >= 0 ) - std::fprintf( stderr, "%s: input file `%s' is not a regular file%s.\n", + std::fprintf( stderr, "%s: Input file `%s' is not a regular file%s.\n", program_name, name.c_str(), to_stdout ? "" : " and `--stdout' was not specified" ); close( infd ); @@ -301,7 +310,7 @@ void set_d_outname( const std::string & name, const int i ) throw() } output_filename = name; output_filename += ".out"; if( verbosity >= 0 ) - std::fprintf( stderr, "%s: can't guess original name for `%s' -- using `%s'.\n", + std::fprintf( stderr, "%s: Can't guess original name for `%s' -- using `%s'.\n", program_name, name.c_str(), output_filename.c_str() ); } @@ -312,18 +321,14 @@ bool open_outstream( const bool force ) throw() if( force ) flags |= O_TRUNC; else flags |= O_EXCL; outfd = open( output_filename.c_str(), flags, outfd_mode ); - if( outfd < 0 ) + if( outfd < 0 && verbosity >= 0 ) { - if( errno == EEXIST ) outfd = -2; else outfd = -1; - if( verbosity >= 0 ) - { - if( outfd == -2 ) - std::fprintf( stderr, "%s: Output file %s already exists, skipping.\n", - program_name, output_filename.c_str() ); - else - std::fprintf( stderr, "%s: Can't create output file `%s': %s.\n", - program_name, output_filename.c_str(), std::strerror( errno ) ); - } + if( errno == EEXIST ) + std::fprintf( stderr, "%s: Output file `%s' already exists, skipping.\n", + program_name, output_filename.c_str() ); + else + std::fprintf( stderr, "%s: Can't create output file `%s': %s.\n", + program_name, output_filename.c_str(), std::strerror( errno ) ); } return ( outfd >= 0 ); } @@ -331,7 +336,7 @@ bool open_outstream( const bool force ) throw() bool check_tty( const int infd, const Mode program_mode ) throw() { - if( program_mode == m_compress && isatty( outfd ) ) + if( program_mode == m_compress && outfd >= 0 && isatty( outfd ) ) { show_error( "I won't write compressed data to a terminal.", 0, true ); return false; @@ -368,8 +373,9 @@ void close_and_set_permissions( const struct stat * const in_statsp ) bool error = false; if( in_statsp ) { - if( fchmod( outfd, in_statsp->st_mode ) != 0 ) error = true; - else (void)fchown( outfd, in_statsp->st_uid, in_statsp->st_gid ); + if( fchmod( outfd, in_statsp->st_mode ) != 0 || + ( fchown( outfd, in_statsp->st_uid, in_statsp->st_gid ) != 0 && + errno != EPERM ) ) error = true; // fchown will in many cases return with EPERM, which can be safely ignored. } if( close( outfd ) == 0 ) outfd = -1; @@ -385,7 +391,7 @@ void close_and_set_permissions( const struct stat * const in_statsp ) } if( error ) { - show_error( "I can't change output file attributes." ); + show_error( "Can't change output file attributes." ); cleanup_and_fail( 1 ); } } @@ -429,7 +435,7 @@ int compress( const long long member_size, const long long volume_size, const long long size = std::min( member_size, volume_size - partial_volume_size ); if( !encoder.encode_member( size ) ) - { pp(); show_error( "read error", errno ); retval = 1; break; } + { pp( "Encoder error" ); retval = 1; break; } in_size += matchfinder.data_position(); out_size += encoder.member_position(); if( matchfinder.finished() ) break; @@ -441,20 +447,18 @@ int compress( const long long member_size, const long long volume_size, { close_and_set_permissions( in_statsp ); if( !next_filename() ) - { pp(); show_error( "too many volume files" ); retval = 1; break; } + { pp( "Too many volume files" ); retval = 1; break; } if( !open_outstream( true ) ) { retval = 1; break; } delete_output_on_interrupt = true; } } - if( !matchfinder.reset() ) - { pp(); show_error( "can't reset matchfinder", errno ); - retval = 1; break; } + matchfinder.reset(); } if( retval == 0 && verbosity >= 1 ) { if( in_size <= 0 || out_size <= 0 ) - std::fprintf( stderr, "no data compressed.\n" ); + std::fprintf( stderr, "No data compressed.\n" ); else std::fprintf( stderr, "%6.3f:1, %6.3f bits/byte, " "%5.2f%% saved, %lld in, %lld out.\n", @@ -466,10 +470,73 @@ int compress( const long long member_size, const long long volume_size, } catch( std::bad_alloc ) { - pp( "not enough memory. Try a smaller dictionary size" ); + pp( "Not enough memory. Try a smaller dictionary size" ); retval = 1; } - catch( Error e ) { pp(); show_error( e.s, errno ); retval = 1; } + catch( Error e ) { pp(); show_error( e.msg, errno ); retval = 1; } + return retval; + } + + +int fcompress( const long long member_size, const long long volume_size, + const int infd, + const Pretty_print & pp, const struct stat * const in_statsp ) + { + if( verbosity >= 1 ) pp(); + File_header header; + header.set_magic(); + int retval = 0; + + try { + Fmatchfinder fmatchfinder( infd ); + header.dictionary_size( fmatchfinder.dictionary_size() ); + + long long in_size = 0, out_size = 0, partial_volume_size = 0; + while( true ) // encode one member per iteration + { + FLZ_encoder encoder( fmatchfinder, header, outfd ); + const long long size = + std::min( member_size, volume_size - partial_volume_size ); + if( !encoder.encode_member( size ) ) + { pp( "Encoder error" ); retval = 1; break; } + in_size += fmatchfinder.data_position(); + out_size += encoder.member_position(); + if( fmatchfinder.finished() ) break; + partial_volume_size += encoder.member_position(); + if( partial_volume_size >= volume_size - min_dictionary_size ) + { + partial_volume_size = 0; + if( delete_output_on_interrupt ) + { + close_and_set_permissions( in_statsp ); + if( !next_filename() ) + { pp( "Too many volume files" ); retval = 1; break; } + if( !open_outstream( true ) ) { retval = 1; break; } + delete_output_on_interrupt = true; + } + } + fmatchfinder.reset(); + } + + if( retval == 0 && verbosity >= 1 ) + { + if( in_size <= 0 || out_size <= 0 ) + std::fprintf( stderr, "No data compressed.\n" ); + else + std::fprintf( stderr, "%6.3f:1, %6.3f bits/byte, " + "%5.2f%% saved, %lld in, %lld out.\n", + (double)in_size / out_size, + ( 8.0 * out_size ) / in_size, + 100.0 * ( 1.0 - ( (double)out_size / in_size ) ), + in_size, out_size ); + } + } + catch( std::bad_alloc ) + { + pp( "Not enough memory. Try a smaller dictionary size" ); + retval = 1; + } + catch( Error e ) { pp(); show_error( e.msg, errno ); retval = 1; } return retval; } @@ -489,26 +556,26 @@ int decompress( const int infd, const Pretty_print & pp, const bool testing ) header.data[i] = rdec.get_byte(); if( rdec.finished() ) // End Of File { - if( first_member ) { pp( "error reading member header" ); retval = 1; } + if( first_member ) { pp( "Error reading member header" ); retval = 1; } break; } if( !header.verify_magic() ) { if( !first_member ) break; // trailing garbage - pp( "bad magic number (file not in lzip format)" ); + pp( "Bad magic number (file not in lzip format)" ); retval = 2; break; } if( !header.verify_version() ) { if( verbosity >= 0 ) { pp(); - std::fprintf( stderr, "version %d member format not supported, newer %s needed.\n", - header.version(), program_name ); } + std::fprintf( stderr, "Version %d member format not supported.\n", + header.version() ); } retval = 2; break; } if( header.dictionary_size() < min_dictionary_size || header.dictionary_size() > max_dictionary_size ) - { pp( "invalid dictionary size in member header" ); retval = 2; break; } + { pp( "Invalid dictionary size in member header" ); retval = 2; break; } if( verbosity >= 1 ) { @@ -528,10 +595,10 @@ int decompress( const int infd, const Pretty_print & pp, const bool testing ) { pp(); if( result == 2 ) - std::fprintf( stderr, "file ends unexpectedly at pos %lld\n", + std::fprintf( stderr, "File ends unexpectedly at pos %lld\n", partial_file_pos ); else - std::fprintf( stderr, "decoder error at pos %lld\n", + std::fprintf( stderr, "Decoder error at pos %lld\n", partial_file_pos ); } retval = 2; break; @@ -543,10 +610,10 @@ int decompress( const int infd, const Pretty_print & pp, const bool testing ) } catch( std::bad_alloc ) { - pp( "not enough memory. Find a machine with more memory" ); + pp( "Not enough memory. Find a machine with more memory" ); retval = 1; } - catch( Error e ) { pp(); show_error( e.s, errno ); retval = 1; } + catch( Error e ) { pp(); show_error( e.msg, errno ); retval = 1; } return retval; } @@ -568,12 +635,9 @@ void set_signals() throw() } // end namespace -int verbosity = 0; - - void Pretty_print::operator()( const char * const msg ) const throw() { - if( verbosity >= 0 ) + if( verbosity_ >= 0 ) { if( first_post ) { @@ -592,22 +656,23 @@ void show_error( const char * const msg, const int errcode, const bool help ) th { if( verbosity >= 0 ) { - if( msg && msg[0] != 0 ) + if( msg && msg[0] ) { std::fprintf( stderr, "%s: %s", program_name, msg ); - if( errcode > 0 ) std::fprintf( stderr, ": %s", std::strerror( errcode ) ); + if( errcode > 0 ) + std::fprintf( stderr, ": %s", std::strerror( errcode ) ); std::fprintf( stderr, "\n" ); } - if( help && invocation_name && invocation_name[0] != 0 ) - std::fprintf( stderr, "Try `%s --help' for more information.\n", invocation_name ); + if( help && invocation_name && invocation_name[0] ) + std::fprintf( stderr, "Try `%s --help' for more information.\n", + invocation_name ); } } void internal_error( const char * const msg ) { - std::string s( "internal error: " ); s += msg; - show_error( s.c_str() ); + std::fprintf( stderr, "%s: internal error: %s.\n", program_name, msg ); std::exit( 3 ); } @@ -655,15 +720,15 @@ int main( const int argc, const char * const argv[] ) // to the corresponding LZMA compression modes. const Lzma_options option_mapping[] = { - { 1 << 16, 5 }, // -0 - { 1 << 20, 10 }, // -1 - { 3 << 19, 12 }, // -2 - { 1 << 21, 17 }, // -3 - { 3 << 20, 26 }, // -4 - { 1 << 22, 44 }, // -5 - { 1 << 23, 80 }, // -6 - { 1 << 24, 108 }, // -7 - { 3 << 23, 163 }, // -8 + { 1 << 16, 16 }, // -0 entry values not used + { 1 << 20, 5 }, // -1 + { 3 << 19, 6 }, // -2 + { 1 << 21, 8 }, // -3 + { 3 << 20, 12 }, // -4 + { 1 << 22, 20 }, // -5 + { 1 << 23, 36 }, // -6 + { 1 << 24, 68 }, // -7 + { 3 << 23, 132 }, // -8 { 1 << 25, 273 } }; // -9 Lzma_options encoder_options = option_mapping[6]; // default = "-6" long long member_size = LLONG_MAX; @@ -673,16 +738,16 @@ int main( const int argc, const char * const argv[] ) bool force = false; bool keep_input_files = false; bool to_stdout = false; + bool zero = false; std::string input_filename; std::string default_output_filename; std::vector< std::string > filenames; invocation_name = argv[0]; -// std::setvbuf( stderr, 0, _IONBF, 0 ); const Arg_parser::Option options[] = { - { '0', 0, Arg_parser::no }, - { '1', "fast", Arg_parser::no }, + { '0', "fast", Arg_parser::no }, + { '1', 0, Arg_parser::no }, { '2', 0, Arg_parser::no }, { '3', 0, Arg_parser::no }, { '4', 0, Arg_parser::no }, @@ -720,9 +785,11 @@ int main( const int argc, const char * const argv[] ) const char * const arg = parser.argument( argind ).c_str(); switch( code ) { - case '0': case '1': case '2': case '3': case '4': + case '0': zero = true; break; + case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - encoder_options = option_mapping[code-'0']; break; + encoder_options = option_mapping[code-'0']; + zero = false; break; case 'b': member_size = getnum( arg, 0, 100000, LLONG_MAX / 2 ); break; case 'c': to_stdout = true; break; case 'd': program_mode = m_decompress; break; @@ -731,11 +798,12 @@ int main( const int argc, const char * const argv[] ) case 'h': show_help(); return 0; case 'k': keep_input_files = true; break; case 'm': encoder_options.match_len_limit = - getnum( arg, 0, min_match_len_limit, max_match_len ); break; + getnum( arg, 0, min_match_len_limit, max_match_len ); + zero = false; break; case 'o': default_output_filename = arg; break; case 'q': verbosity = -1; break; case 's': encoder_options.dictionary_size = get_dict_size( arg ); - break; + zero = false; break; case 'S': volume_size = getnum( arg, 0, 100000, LLONG_MAX / 2 ); break; case 't': program_mode = m_test; break; case 'v': if( verbosity < 4 ) ++verbosity; break; @@ -744,6 +812,11 @@ int main( const int argc, const char * const argv[] ) } } +#if defined(__MSVCRT__) || defined(__OS2__) + _setmode( STDIN_FILENO, O_BINARY ); + _setmode( STDOUT_FILENO, O_BINARY ); +#endif + bool filenames_given = false; for( ; argind < parser.arguments(); ++argind ) { @@ -756,7 +829,7 @@ int main( const int argc, const char * const argv[] ) ( filenames_given || default_output_filename.size() ) ) set_signals(); - Pretty_print pp( filenames ); + Pretty_print pp( filenames, verbosity ); if( program_mode == m_test ) outfd = -1; else if( program_mode == m_compress ) @@ -828,8 +901,13 @@ int main( const int argc, const char * const argv[] ) pp.set_name( input_filename ); int tmp = 0; if( program_mode == m_compress ) - tmp = compress( member_size, volume_size, encoder_options, infd, - pp, in_statsp ); + { + if( zero ) + tmp = fcompress( member_size, volume_size, infd, pp, in_statsp ); + else + tmp = compress( member_size, volume_size, encoder_options, infd, + pp, in_statsp ); + } else tmp = decompress( infd, pp, program_mode == m_test ); if( tmp > retval ) retval = tmp; @@ -846,9 +924,7 @@ int main( const int argc, const char * const argv[] ) } if( outfd >= 0 && close( outfd ) != 0 ) { - if( verbosity >= 0 ) - std::fprintf( stderr, "%s: Can't close stdout: %s.\n", - program_name, std::strerror( errno ) ); + show_error( "Can't close stdout", errno ); if( retval < 1 ) retval = 1; } return retval; |