summaryrefslogtreecommitdiffstats
path: root/main.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--main.cc157
1 files changed, 132 insertions, 25 deletions
diff --git a/main.cc b/main.cc
index 167e8c6..192f32b 100644
--- a/main.cc
+++ b/main.cc
@@ -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() )