diff options
Diffstat (limited to 'main.cc')
-rw-r--r-- | main.cc | 157 |
1 files changed, 132 insertions, 25 deletions
@@ -1,5 +1,5 @@ /* Lziprecover - Data recovery tool for the lzip format - Copyright (C) 2009-2014 Antonio Diaz Diaz. + Copyright (C) 2009-2015 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 @@ -55,6 +55,7 @@ #include "arg_parser.h" #include "lzip.h" #include "decoder.h" +#include "block.h" #ifndef O_BINARY #define O_BINARY 0 @@ -69,7 +70,7 @@ namespace { const char * const Program_name = "Lziprecover"; const char * const program_name = "lziprecover"; -const char * const program_year = "2014"; +const char * const program_year = "2015"; const char * invocation_name = 0; struct { const char * from; const char * to; } const known_extensions[] = { @@ -77,8 +78,8 @@ struct { const char * from; const char * to; } const known_extensions[] = { { ".tlz", ".tar" }, { 0, 0 } }; -enum Mode { m_none, m_decompress, m_list, m_merge, m_range, m_repair, - m_split, m_test }; +enum Mode { m_none, m_debug_delay, m_debug_repair, m_decompress, m_list, + m_merge, m_range_dec, m_repair, m_split, m_test }; std::string output_filename; int outfd = -1; @@ -92,7 +93,7 @@ bool delete_output_on_interrupt = false; void show_help() { std::printf( "%s - Data recovery tool and decompressor for the lzip format.\n", Program_name ); - std::printf( "Lziprecover can repair perfectly most files with small errors (up to one\n" + std::printf( "\nLziprecover can repair perfectly most files with small errors (up to one\n" "single-byte error per member), without the need of any extra redundance\n" "at all. Losing an entire archive just because of a corrupt byte near the\n" "beginning is a thing of the past.\n" @@ -116,8 +117,13 @@ void show_help() " -R, --repair try to repair a small error in file\n" " -s, --split split multi-member file in single-member files\n" " -t, --test test compressed file integrity\n" - " -v, --verbose be verbose (a 2nd -v gives more)\n" - "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" + " -v, --verbose be verbose (a 2nd -v gives more)\n" ); + if( verbosity >= 1 ) + { + std::printf( " -y, --debug-delay=<range> find max error detection delay in <range>\n" + " -z, --debug-repair=<pos>,<val> test repair one-byte error at <pos>\n" ); + } + std::printf( "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" "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n" "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n" @@ -141,22 +147,109 @@ void show_version() void show_header( const File_header & header ) { - const char * const prefix[8] = - { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; - enum { factor = 1024 }; - const char * p = ""; - const char * np = " "; - unsigned num = header.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 ); + 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 = header.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 ); + } } namespace { +// Returns the number of chars read, or 0 if error. +// +int parse_long_long( const char * const ptr, long long & value ) + { + char * tail; + errno = 0; + value = strtoll( ptr, &tail, 0 ); + if( tail == ptr || errno || value < 0 ) return 0; + int c = tail - ptr; + + if( ptr[c] ) + { + const int factor = ( ptr[c+1] == 'i' ) ? 1024 : 1000; + int exponent = 0; + switch( ptr[c] ) + { + case 'Y': exponent = 8; break; + case 'Z': exponent = 7; break; + case 'E': exponent = 6; break; + case 'P': exponent = 5; break; + case 'T': exponent = 4; break; + case 'G': exponent = 3; break; + case 'M': exponent = 2; break; + case 'K': if( factor == 1024 ) exponent = 1; else return 0; break; + case 'k': if( factor == 1000 ) exponent = 1; else return 0; break; + } + if( exponent > 0 ) + { + ++c; + if( ptr[c] == 'i' ) { ++c; if( value ) format_num( 0, 0, -1 ); } + if( ptr[c] == 'B' ) ++c; + for( int i = 0; i < exponent; ++i ) + { + if( INT64_MAX / factor >= value ) value *= factor; + else return 0; + } + } + } + return c; + } + + +// Recognized formats: <begin> <begin>-<end> <begin>,<size> +// +void parse_range( const char * const ptr, Block & range ) + { + long long value = 0; + int c = parse_long_long( ptr, value ); // pos + if( c && value >= 0 && value < INT64_MAX && + ( ptr[c] == 0 || ptr[c] == ',' || ptr[c] == '-' ) ) + { + range.pos( value ); + if( ptr[c] == 0 ) { range.size( INT64_MAX - value ); return; } + const bool issize = ( ptr[c] == ',' ); + c = parse_long_long( ptr + c + 1, value ); // size + if( c && value > 0 && ( issize || value > range.pos() ) ) + { + if( !issize ) value -= range.pos(); + if( INT64_MAX - range.pos() >= value ) { range.size( value ); return; } + } + } + show_error( "Bad decompression range.", 0, true ); + std::exit( 1 ); + } + + +// Recognized format: <pos>,<value> +// +void parse_pos_value( const char * const ptr, long long & pos, uint8_t & value ) + { + long long val = 0; + int c = parse_long_long( ptr, val ); // pos + if( c && val >= 0 && val < INT64_MAX && ptr[c] == ',' ) + { + pos = val; + c = parse_long_long( ptr + c + 1, val ); // value + if( c && val >= 0 && val < 256 ) + { value = val; return; } + } + show_error( "Bad file position or byte value.", 0, true ); + std::exit( 1 ); + } + + void one_file( const int files ) { if( files != 1 ) @@ -400,7 +493,7 @@ int decompress( const int infd, const Pretty_print & pp, const bool testing ) { pp( "Invalid dictionary size in member header." ); retval = 2; break; } if( verbosity >= 2 || ( verbosity == 1 && first_member ) ) - { pp(); if( verbosity >= 3 ) show_header( header ); } + { pp(); show_header( header ); } LZ_decoder decoder( header, rdec, outfd ); const int result = decoder.decode_member( pp ); @@ -504,12 +597,14 @@ void internal_error( const char * const msg ) int main( const int argc, const char * const argv[] ) { + Block range( 0, 0 ); + long long bad_pos = 0; std::string input_filename; std::string default_output_filename; - std::string range_string; std::vector< std::string > filenames; int infd = -1; Mode program_mode = m_none; + uint8_t bad_value = 0; bool force = false; bool ignore = false; bool keep_input_files = false; @@ -535,6 +630,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 }, + { 'y', "debug-delay", Arg_parser::yes }, + { 'z', "debug-repair", Arg_parser::yes }, { 0 , 0, Arg_parser::no } }; const Arg_parser parser( argc, argv, options ); @@ -551,8 +648,8 @@ int main( const int argc, const char * const argv[] ) { 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 'D': set_mode( program_mode, m_range_dec ); + parse_range( arg.c_str(), range ); break; case 'f': force = true; break; case 'h': show_help(); return 0; case 'i': ignore = true; break; @@ -567,6 +664,10 @@ 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 'y': set_mode( program_mode, m_debug_delay ); + parse_range( arg.c_str(), range ); break; + case 'z': set_mode( program_mode, m_debug_repair ); + parse_pos_value( arg.c_str(), bad_pos, bad_value ); break; default : internal_error( "uncaught option." ); } } // end process options @@ -593,6 +694,12 @@ int main( const int argc, const char * const argv[] ) switch( program_mode ) { case m_none: internal_error( "invalid operation." ); break; + case m_debug_delay: + one_file( filenames.size() ); + return debug_delay( filenames[0], range, verbosity ); + case m_debug_repair: + one_file( filenames.size() ); + return debug_repair( filenames[0], bad_pos, verbosity, bad_value ); case m_decompress: break; case m_list: if( filenames.size() < 1 ) @@ -604,10 +711,10 @@ int main( const int argc, const char * const argv[] ) if( default_output_filename.empty() ) default_output_filename = insert_fixed( filenames[0] ); return merge_files( filenames, default_output_filename, verbosity, force ); - case m_range: + case m_range_dec: one_file( filenames.size() ); return range_decompress( filenames[0], default_output_filename, - range_string, verbosity, force, ignore, to_stdout ); + range, verbosity, force, ignore, to_stdout ); case m_repair: one_file( filenames.size() ); if( default_output_filename.empty() ) |