summaryrefslogtreecommitdiffstats
path: root/main.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--main.cc143
1 files changed, 85 insertions, 58 deletions
diff --git a/main.cc b/main.cc
index 2abbc0e..dec1ec5 100644
--- a/main.cc
+++ b/main.cc
@@ -71,8 +71,8 @@ struct { const char * from; const char * to; } const known_extensions[] = {
{ ".tlz", ".tar" },
{ 0, 0 } };
-enum Mode { m_none, m_decompress, m_generate, m_merge, m_recover, m_repair,
- m_split, m_test, m_update };
+enum Mode { m_none, m_decompress, m_generate, m_list, m_merge, m_range,
+ m_recover, m_repair, m_split, m_test, m_update };
std::string output_filename;
int outfd = -1;
@@ -87,22 +87,26 @@ void show_help() throw()
std::printf( "%s - Data recovery tool and decompressor for lzipped files.\n", Program_name );
std::printf( "\nUsage: %s [options] [files]\n", invocation_name );
std::printf( "\nOptions:\n"
- " -h, --help display this help and exit\n"
- " -V, --version output version information and exit\n"
- " -c, --stdout send decompressed output to standard output\n"
- " -d, --decompress decompress\n"
- " -f, --force overwrite existing output files\n"
-// " -g, --generate-recover-file generate a recover file\n"
- " -k, --keep keep (don't delete) input files\n"
- " -m, --merge correct errors in file using several copies\n"
- " -o, --output=<file> place the output into <file>\n"
- " -q, --quiet suppress all messages\n"
-// " -r, --recover correct errors in file using a recover file\n"
- " -R, --repair try to repair a small error in file\n"
- " -s, --split split a multimember file in single-member files\n"
- " -t, --test test compressed file integrity\n"
-// " -u, --update convert file from version 0 to version 1\n"
- " -v, --verbose be verbose (a 2nd -v gives more)\n"
+ " -h, --help display this help and exit\n"
+ " -V, --version output version information and exit\n"
+ " -c, --stdout send decompressed output to standard output\n"
+ " -d, --decompress decompress\n"
+ " -D, --range-decompress=<range> decompress only a range of bytes (N-M)\n"
+ " -f, --force overwrite existing output files\n"
+// " -g, --generate-recover-file generate a recover file\n"
+ " -k, --keep keep (don't delete) input files\n"
+ " -l, --list print total file sizes and ratios\n"
+ " -m, --merge correct errors in file using several copies\n"
+ " -o, --output=<file> place the output into <file>\n"
+ " -q, --quiet suppress all messages\n"
+// " -r, --recover correct errors in file using a recover file\n"
+ " -R, --repair try to repair a small error in file\n"
+ " -s, --split split multimember file in single-member files\n"
+ " -t, --test test compressed file integrity\n"
+// " -u, --update convert file from version 0 to version 1\n"
+ " -v, --verbose be verbose (a 2nd -v gives more)\n"
+ "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n"
+ "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n"
"\nReport bugs to lzip-bug@nongnu.org\n"
"Lziprecover home page: http://www.nongnu.org/lzip/lziprecover.html\n" );
}
@@ -118,20 +122,13 @@ void show_version() throw()
}
-const char * format_num( long long num ) throw()
+void one_file( const int argind, const int arguments ) throw()
{
- const char * const prefix[8] =
- { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" };
- enum { buf_size = 16, factor = 1024 };
- static char buf[buf_size];
- const char *p = "";
- bool exact = ( num % factor == 0 );
-
- for( int i = 0; i < 8 && ( llabs( num ) > 9999 ||
- ( exact && llabs( num ) >= factor ) ); ++i )
- { num /= factor; if( num % factor != 0 ) exact = false; p = prefix[i]; }
- snprintf( buf, buf_size, "%lld %s", num, p );
- return buf;
+ if( argind + 1 != arguments )
+ {
+ show_error( "You must specify exactly 1 file.", 0, true );
+ std::exit( 1 );
+ }
}
@@ -345,7 +342,7 @@ int decompress( const int infd, const Pretty_print & pp, const bool testing )
if( verbosity >= 2 )
std::fprintf( stderr, "version %d, dictionary size %7sB. ",
header.version(),
- format_num( header.dictionary_size() ) );
+ format_num( header.dictionary_size(), 9999, -1 ) );
}
LZ_decoder decoder( header, rdec, outfd );
@@ -403,6 +400,30 @@ void set_signals() throw()
int verbosity = 0;
+const char * format_num( long long num, long long limit,
+ const int set_prefix ) throw()
+ {
+ const char * const si_prefix[8] =
+ { "k", "M", "G", "T", "P", "E", "Z", "Y" };
+ const char * const binary_prefix[8] =
+ { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" };
+ static bool si = true;
+ static char buf[32];
+
+ if( set_prefix ) si = ( set_prefix > 0 );
+ const int factor = ( si ? 1000 : 1024 );
+ const char * const * prefix = ( si ? si_prefix : binary_prefix );
+ const char * p = "";
+ bool exact = ( num % factor == 0 );
+
+ for( int i = 0; i < 8 && ( llabs( num ) > limit ||
+ ( exact && llabs( num ) >= factor ) ); ++i )
+ { num /= factor; if( num % factor != 0 ) exact = false; p = prefix[i]; }
+ snprintf( buf, sizeof buf, "%lld %s", num, p );
+ return buf;
+ }
+
+
int open_instream( const std::string & name, struct stat * const in_statsp,
const bool to_stdout, const bool reg_only ) throw()
{
@@ -491,25 +512,28 @@ int main( const int argc, const char * const argv[] )
bool to_stdout = false;
std::string input_filename;
std::string default_output_filename;
+ std::string range_string;
std::vector< std::string > filenames;
invocation_name = argv[0];
const Arg_parser::Option options[] =
{
- { 'c', "stdout", Arg_parser::no },
- { 'd', "decompress", Arg_parser::no },
- { 'f', "force", Arg_parser::no },
- { 'h', "help", Arg_parser::no },
- { 'k', "keep", Arg_parser::no },
- { 'm', "merge", Arg_parser::no },
- { 'o', "output", Arg_parser::yes },
- { 'q', "quiet", Arg_parser::no },
- { 'R', "repair", Arg_parser::no },
- { 's', "split", Arg_parser::no },
- { 't', "test", Arg_parser::no },
- { 'v', "verbose", Arg_parser::no },
- { 'V', "version", Arg_parser::no },
- { 0 , 0, Arg_parser::no } };
+ { 'c', "stdout", Arg_parser::no },
+ { 'd', "decompress", Arg_parser::no },
+ { 'D', "range-decompress", Arg_parser::yes },
+ { 'f', "force", Arg_parser::no },
+ { 'h', "help", Arg_parser::no },
+ { 'k', "keep", Arg_parser::no },
+ { 'l', "list", Arg_parser::no },
+ { 'm', "merge", Arg_parser::no },
+ { 'o', "output", Arg_parser::yes },
+ { 'q', "quiet", Arg_parser::no },
+ { 'R', "repair", Arg_parser::no },
+ { 's', "split", Arg_parser::no },
+ { 't', "test", Arg_parser::no },
+ { 'v', "verbose", Arg_parser::no },
+ { 'V', "version", Arg_parser::no },
+ { 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
@@ -520,15 +544,19 @@ 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 ).c_str();
switch( code )
{
case 'c': to_stdout = true; break;
case 'd': set_mode( program_mode, m_decompress ); break;
+ case 'D': set_mode( program_mode, m_range );
+ range_string = arg; break;
case 'f': force = true; break;
case 'h': show_help(); return 0;
case 'k': keep_input_files = true; break;
+ case 'l': set_mode( program_mode, m_list ); break;
case 'm': set_mode( program_mode, m_merge ); break;
- case 'o': default_output_filename = parser.argument( argind ); break;
+ case 'o': default_output_filename = arg; break;
case 'q': verbosity = -1; break;
case 'R': set_mode( program_mode, m_repair ); break;
case 's': set_mode( program_mode, m_split ); break;
@@ -552,9 +580,10 @@ int main( const int argc, const char * const argv[] )
case m_update:
case m_none: internal_error( "invalid operation" ); break;
case m_decompress: break;
+ case m_list:
+ one_file( argind, parser.arguments() );
+ return list_file( parser.argument( argind ) );
case m_merge:
- {
- std::vector< std::string > filenames;
for( ; argind < parser.arguments(); ++argind )
filenames.push_back( parser.argument( argind ) );
if( filenames.size() < 2 )
@@ -562,21 +591,19 @@ int main( const int argc, const char * const argv[] )
if( !default_output_filename.size() )
default_output_filename = insert_fixed( filenames[0] );
return merge_files( filenames, default_output_filename, force );
- } break;
+ case m_range:
+ one_file( argind, parser.arguments() );
+ return range_decompress( parser.argument( argind ),
+ default_output_filename, range_string,
+ to_stdout, force );
case m_repair:
- {
- if( argind + 1 != parser.arguments() )
- { show_error( "You must specify exactly 1 file.", 0, true ); return 1; }
+ one_file( argind, parser.arguments() );
if( !default_output_filename.size() )
default_output_filename = insert_fixed( parser.argument( argind ) );
return repair_file( parser.argument( argind ), default_output_filename, force );
- } break;
case m_split:
- {
- if( argind + 1 != parser.arguments() )
- { show_error( "You must specify exactly 1 file.", 0, true ); return 1; }
+ one_file( argind, parser.arguments() );
return split_file( parser.argument( argind ), default_output_filename, force );
- } break;
case m_test: break;
}