Merging upstream version 1.17~rc1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
97763bf5de
commit
3574ba518d
27 changed files with 624 additions and 240 deletions
155
main.cc
155
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 );
|
||||
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 );
|
||||
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() )
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue