diff options
-rw-r--r-- | ChangeLog | 51 | ||||
-rw-r--r-- | INSTALL | 26 | ||||
-rw-r--r-- | Makefile.in | 10 | ||||
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | README | 17 | ||||
-rw-r--r-- | arg_parser.cc | 15 | ||||
-rw-r--r-- | arg_parser.h | 27 | ||||
-rwxr-xr-x | configure | 60 | ||||
-rw-r--r-- | decoder.cc | 32 | ||||
-rw-r--r-- | decoder.h | 45 | ||||
-rw-r--r-- | doc/lziprecover.1 | 6 | ||||
-rw-r--r-- | doc/lziprecover.info | 45 | ||||
-rw-r--r-- | doc/lziprecover.texinfo | 36 | ||||
-rw-r--r-- | lzip.h | 102 | ||||
-rw-r--r-- | main.cc | 92 | ||||
-rw-r--r-- | merge.cc | 13 | ||||
-rw-r--r-- | range_dec.cc | 36 | ||||
-rw-r--r-- | repair.cc | 2 | ||||
-rw-r--r-- | split.cc | 4 | ||||
-rwxr-xr-x | testsuite/check.sh | 26 | ||||
-rw-r--r-- | testsuite/unzcrash.cc | 353 |
21 files changed, 722 insertions, 284 deletions
@@ -1,49 +1,49 @@ -2011-11-20 Antonio Diaz Diaz <ant_diaz@teleline.es> +2012-02-24 Antonio Diaz Diaz <ant_diaz@teleline.es> - * Version 1.13-rc2 released. - * Added new option `-D, --range-decompress' which extracts a - range of bytes decompressing only the members containing the - desired data. - * Added new option `-l, --list' which prints correct total file - sizes and ratios even for multimember files. - * New file range_dec.cc. - * testsuite/check.sh: Do not use `ln'. `ln' doesn't work on OS/2. - -2011-11-12 Antonio Diaz Diaz <ant_diaz@teleline.es> - - * Version 1.13-rc1 released. + * Version 1.13 released. * Lziprecover is now distributed in its own package. Until - version 1.12 it was included in the lzip package. + version 1.12 it was included in the lzip package. Previous + entries in this file are taken from there. * lziprecover.cc: Renamed to main.cc. + * New files merge.cc, repair.cc, split.cc, and range_dec.cc. * main.cc: Added decompressor options (-c, -d, -k, -t) so that a external decompressor is not needed for recovery nor for "make check". - * New files merge.cc, repair.cc and split.cc. + * Added new option '-D, --range-decompress' which extracts a + range of bytes decompressing only the members containing the + desired data. + * Added new option '-l, --list' which prints correct total file + sizes and ratios even for multi-member files. * merge.cc repair.cc: Remove output file if recovery fails. + * Changed quote characters in messages as advised by GNU Standards. * split.cc: Use Boyer-Moore algorithm to search for headers. - * testsuite/check.sh: Use `ln' instead of `cat' for input files. + * configure: 'datadir' renamed to 'datarootdir'. 2011-04-30 Antonio Diaz Diaz <ant_diaz@teleline.es> * Version 1.12 released. - * lziprecover.cc: If `-v' is not specified show errors only. + * lziprecover.cc: If '-v' is not specified show errors only. + * testsuite/unzcrash.cc: Use Arg_parser. + * testsuite/unzcrash.cc: Added new option '-b, --bits'. + * testsuite/unzcrash.cc: Added new option '-p, --position'. + * testsuite/unzcrash.cc: Added new option '-s, --size'. 2010-09-16 Antonio Diaz Diaz <ant_diaz@teleline.es> * Version 1.11 released. - * lziprecover.cc: Added new option `-m, --merge' which tries to + * lziprecover.cc: Added new option '-m, --merge' which tries to produce a correct file merging the good parts of two or more damaged copies. - * lziprecover.cc: Added new option `-R, --repair' for repairing + * lziprecover.cc: Added new option '-R, --repair' for repairing a 1-byte error in single-member files. * decoder.cc (decode_member): Detect file errors earlier to improve efficiency of lziprecover's new repair capability. This change also prevents (harmless) access to uninitialized memory when decompressing a corrupt file. - * lziprecover.cc: Added new option `-f, --force'. - * lziprecover.cc: Added new option `-o, --output'. - * lziprecover.cc: Added new option `-s, --split' to select the - until now only operation of splitting multimember files. + * lziprecover.cc: Added new option '-f, --force'. + * lziprecover.cc: Added new option '-o, --output'. + * lziprecover.cc: Added new option '-s, --split' to select the + until now only operation of splitting multi-member files. * lziprecover.cc: If no operation is specified, warn the user and do nothing. @@ -56,10 +56,11 @@ 2009-01-24 Antonio Diaz Diaz <ant_diaz@teleline.es> * Version 1.4 released. - * Added `lziprecover', a member recoverer program. + * Added 'lziprecover', a member recoverer program. + * testsuite/unzcrash.cc: Test all 1-byte errors. -Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. +Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz. This file is a collection of facts, and thus it is not copyrightable, but just in case, you have unlimited permission to copy, distribute and @@ -18,7 +18,7 @@ This creates the directory ./lziprecover[version] containing the source from the main archive. 2. Change to lziprecover directory and run configure. - (Try `configure --help' for usage instructions). + (Try 'configure --help' for usage instructions). cd lziprecover[version] ./configure @@ -27,31 +27,31 @@ from the main archive. make -4. Optionally, type `make check' to run the tests that come with +4. Optionally, type 'make check' to run the tests that come with lziprecover. -5. Type `make install' to install the programs and any data files and +5. Type 'make install' to install the programs and any data files and documentation. Another way ----------- You can also compile lziprecover into a separate directory. To do this, -you must use a version of `make' that supports the `VPATH' variable, -such as GNU `make'. `cd' to the directory where you want the object -files and executables to go and run the `configure' script. `configure' -automatically checks for the source code in `.', in `..' and in the -directory that `configure' is in. - -`configure' recognizes the option `--srcdir=DIR' to control where to -look for the sources. Usually `configure' can determine that directory +you must use a version of 'make' that supports the 'VPATH' variable, +such as GNU 'make'. 'cd' to the directory where you want the object +files and executables to go and run the 'configure' script. 'configure' +automatically checks for the source code in '.', in '..' and in the +directory that 'configure' is in. + +'configure' recognizes the option '--srcdir=DIR' to control where to +look for the sources. Usually 'configure' can determine that directory automatically. -After running `configure', you can run `make' and `make install' as +After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. +Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute and modify it. diff --git a/Makefile.in b/Makefile.in index cc8d1c9..08df8ab 100644 --- a/Makefile.in +++ b/Makefile.in @@ -7,6 +7,7 @@ INSTALL_DIR = $(INSTALL) -d -m 755 SHELL = /bin/sh objs = arg_parser.o decoder.o merge.o range_dec.o repair.o split.o main.o +unzobjs = arg_parser.o unzcrash.o .PHONY : all install install-info install-man install-strip \ @@ -21,9 +22,15 @@ $(progname) : $(objs) $(progname)_profiled : $(objs) $(CXX) $(LDFLAGS) -pg -o $@ $(objs) +unzcrash : $(unzobjs) + $(CXX) $(LDFLAGS) -o $@ $(unzobjs) + main.o : main.cc $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< +unzcrash.o : testsuite/unzcrash.cc + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< + %.o : %.cc $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< @@ -35,6 +42,7 @@ merge.o : lzip.h decoder.h range_dec.o : lzip.h decoder.h repair.o : lzip.h split.o : lzip.h +unzcrash.o : arg_parser.h Makefile doc : info man @@ -101,6 +109,7 @@ dist : doc $(DISTNAME)/testsuite/test921-1921.txt \ $(DISTNAME)/testsuite/test_bad[1-5].lz \ $(DISTNAME)/testsuite/test_v[01].lz \ + $(DISTNAME)/testsuite/unzcrash.cc \ $(DISTNAME)/*.h \ $(DISTNAME)/*.cc rm -f $(DISTNAME) @@ -108,6 +117,7 @@ dist : doc clean : -rm -f $(progname) $(progname)_profiled $(objs) + -rm -f unzcrash unzcrash.o distclean : clean -rm -f Makefile config.status *.tar *.tar.lz @@ -12,6 +12,12 @@ decompressing only the members containing the desired data, has been added. The new option "-l, --list" which prints correct total file sizes and -ratios even for multimember files, has been added. +ratios even for multi-member files, has been added. "--merge" and "--repair" now remove the output file if recovery fails. + +Quote characters in messages have been changed as advised by GNU Coding +Standards. + +Configure option "--datadir" has been renamed to "--datarootdir" to +follow GNU Standards. @@ -11,6 +11,13 @@ the compressors in the lzip family; lzip, plzip, minilzip/lzlib, clzip and pdlzip. This recovery capability contributes to make the lzip format one of the best options for long-term data archiving. +Lziprecover is able to efficiently extract a range of bytes from a +multi-member file, because it only decompresses the members containing +the desired data. + +Lziprecover can print correct total file sizes and ratios even for +multi-member files. + When recovering data, lziprecover takes as arguments the names of the damaged files and writes zero or more recovered files depending on the operation selected and whether the recovery succeeded or not. The @@ -32,3 +39,13 @@ copies. If the cause of file corruption is damaged media, the combination GNU ddrescue + lziprecover is the best option for recovering data from multiple damaged copies. + + +Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz. + +This file is free documentation: you have unlimited permission to copy, +distribute and modify it. + +The file Makefile.in is a data file used by configure to produce the +Makefile. It has the same copyright owner and permissions that configure +itself. diff --git a/arg_parser.cc b/arg_parser.cc index bc4b4a3..b3fd48d 100644 --- a/arg_parser.cc +++ b/arg_parser.cc @@ -1,5 +1,6 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C++ version) - Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Antonio Diaz Diaz. + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Antonio Diaz Diaz. This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -55,30 +56,30 @@ bool Arg_parser::parse_long_option( const char * const opt, const char * const a if( ambig && !exact ) { - error_ = "option `"; error_ += opt; error_ += "' is ambiguous"; + error_ = "option '"; error_ += opt; error_ += "' is ambiguous"; return false; } if( index < 0 ) // nothing found { - error_ = "unrecognized option `"; error_ += opt; error_ += '\''; + error_ = "unrecognized option '"; error_ += opt; error_ += '\''; return false; } ++argind; data.push_back( Record( options[index].code ) ); - if( opt[len+2] ) // `--<long_option>=<argument>' syntax + if( opt[len+2] ) // '--<long_option>=<argument>' syntax { if( options[index].has_arg == no ) { - error_ = "option `--"; error_ += options[index].name; + error_ = "option '--"; error_ += options[index].name; error_ += "' doesn't allow an argument"; return false; } if( options[index].has_arg == yes && !opt[len+3] ) { - error_ = "option `--"; error_ += options[index].name; + error_ = "option '--"; error_ += options[index].name; error_ += "' requires an argument"; return false; } @@ -90,7 +91,7 @@ bool Arg_parser::parse_long_option( const char * const opt, const char * const a { if( !arg || !arg[0] ) { - error_ = "option `--"; error_ += options[index].name; + error_ = "option '--"; error_ += options[index].name; error_ += "' requires an argument"; return false; } diff --git a/arg_parser.h b/arg_parser.h index d1e5c02..4fbd1af 100644 --- a/arg_parser.h +++ b/arg_parser.h @@ -1,5 +1,6 @@ /* Arg_parser - POSIX/GNU command line argument parser. (C++ version) - Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Antonio Diaz Diaz. + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Antonio Diaz Diaz. This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,12 +26,12 @@ Public License. */ -/* Arg_parser reads the arguments in `argv' and creates a number of +/* Arg_parser reads the arguments in 'argv' and creates a number of option codes, option arguments and non-option arguments. - In case of error, `error' returns a non-empty error message. + In case of error, 'error' returns a non-empty error message. - `options' is an array of `struct Option' terminated by an element + 'options' is an array of 'struct Option' terminated by an element containing a code which is zero. A null name means a short-only option. A code value outside the unsigned char range means a long-only option. @@ -39,13 +40,13 @@ were specified before all the non-option arguments for the purposes of parsing, even if the user of your program intermixed option and non-option arguments. If you want the arguments in the exact order - the user typed them, call `Arg_parser' with `in_order' = true. + the user typed them, call 'Arg_parser' with 'in_order' = true. - The argument `--' terminates all options; any following arguments are + The argument '--' terminates all options; any following arguments are treated as non-option arguments, even if they begin with a hyphen. - The syntax for optional option arguments is `-<short_option><argument>' - (without whitespace), or `--<long_option>=<argument>'. + The syntax for optional option arguments is '-<short_option><argument>' + (without whitespace), or '--<long_option>=<argument>'. */ class Arg_parser @@ -65,7 +66,7 @@ private: { int code; std::string argument; - Record( const int c = 0 ) : code( c ) {} + explicit Record( const int c = 0 ) : code( c ) {} }; std::string error_; @@ -84,20 +85,20 @@ public: Arg_parser( const char * const opt, const char * const arg, const Option options[] ); - const std::string & error() const throw() { return error_; } + const std::string & error() const { return error_; } // The number of arguments parsed (may be different from argc) - int arguments() const throw() { return data.size(); } + int arguments() const { return data.size(); } // If code( i ) is 0, argument( i ) is a non-option. // Else argument( i ) is the option's argument (or empty). - int code( const int i ) const throw() + int code( const int i ) const { if( i >= 0 && i < arguments() ) return data[i].code; else return 0; } - const std::string & argument( const int i ) const throw() + const std::string & argument( const int i ) const { if( i >= 0 && i < arguments() ) return data[i].argument; else return error_; @@ -1,6 +1,6 @@ #! /bin/sh # configure script for Lziprecover - Data recovery tool for lzipped files -# Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. +# Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission # to copy, distribute and modify it. @@ -8,7 +8,7 @@ args= no_create= pkgname=lziprecover -pkgversion=1.13-rc2 +pkgversion=1.13 progname=lziprecover srctrigger=lzip.h @@ -19,10 +19,9 @@ srcdir= prefix=/usr/local exec_prefix='$(prefix)' bindir='$(exec_prefix)/bin' -datadir='$(prefix)/share' -infodir='$(datadir)/info' -mandir='$(datadir)/man' -sysconfdir='$(prefix)/etc' +datarootdir='$(prefix)/share' +infodir='$(datarootdir)/info' +mandir='$(datarootdir)/man' CXX= CPPFLAGS= CXXFLAGS='-Wall -W -O2' @@ -40,12 +39,12 @@ while [ -n "$1" ] ; do # Split out the argument for options that take them case ${option} in - *=*) optarg=`echo ${option} | sed -e 's,^[^=]*=,,'` ;; + *=*) optarg=`echo ${option} | sed -e 's,^[^=]*=,,;s,/$,,'` ;; esac # Process the options case ${option} in - --help | --he* | -h) + --help | -h) echo "Usage: configure [options]" echo echo "Options: [defaults in brackets]" @@ -55,42 +54,31 @@ while [ -n "$1" ] ; do echo " --prefix=DIR install into DIR [${prefix}]" echo " --exec-prefix=DIR base directory for arch-dependent files [${exec_prefix}]" echo " --bindir=DIR user executables directory [${bindir}]" - echo " --datadir=DIR base directory for doc and data [${datadir}]" + echo " --datarootdir=DIR base directory for doc and data [${datarootdir}]" echo " --infodir=DIR info files directory [${infodir}]" echo " --mandir=DIR man pages directory [${mandir}]" - echo " --sysconfdir=DIR read-only single-machine data directory [${sysconfdir}]" echo " CXX=COMPILER C++ compiler to use [g++]" echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]" echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]" echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]" echo exit 0 ;; - --version | --ve* | -V) + --version | -V) echo "Configure script for ${pkgname} version ${pkgversion}" exit 0 ;; - --srcdir* | --sr*) - srcdir=`echo ${optarg} | sed -e 's,/$,,'` ;; - --prefix* | --pr*) - prefix=`echo ${optarg} | sed -e 's,/$,,'` ;; - --exec-prefix* | --ex*) - exec_prefix=`echo ${optarg} | sed -e 's,/$,,'` ;; - --bindir* | --bi*) - bindir=`echo ${optarg} | sed -e 's,/$,,'` ;; - --datadir* | --da*) - datadir=`echo ${optarg} | sed -e 's,/$,,'` ;; - --infodir* | --inf*) - infodir=`echo ${optarg} | sed -e 's,/$,,'` ;; - --mandir* | --ma*) - mandir=`echo ${optarg} | sed -e 's,/$,,'` ;; - --sysconfdir* | --sy*) - sysconfdir=`echo ${optarg} | sed -e 's,/$,,'` ;; - --no-create | --no-c*) - no_create=yes ;; - - CXX=*) CXX=${optarg} ;; + --srcdir=*) srcdir=${optarg} ;; + --prefix=*) prefix=${optarg} ;; + --exec-prefix=*) exec_prefix=${optarg} ;; + --bindir=*) bindir=${optarg} ;; + --datarootdir=*) datarootdir=${optarg} ;; + --infodir=*) infodir=${optarg} ;; + --mandir=*) mandir=${optarg} ;; + --no-create) no_create=yes ;; + + CXX=*) CXX=${optarg} ;; CPPFLAGS=*) CPPFLAGS=${optarg} ;; CXXFLAGS=*) CXXFLAGS=${optarg} ;; - LDFLAGS=*) LDFLAGS=${optarg} ;; + LDFLAGS=*) LDFLAGS=${optarg} ;; --* | *=* | *-*-*) ;; *) @@ -154,10 +142,9 @@ echo "VPATH = ${srcdir}" echo "prefix = ${prefix}" echo "exec_prefix = ${exec_prefix}" echo "bindir = ${bindir}" -echo "datadir = ${datadir}" +echo "datarootdir = ${datarootdir}" echo "infodir = ${infodir}" echo "mandir = ${mandir}" -echo "sysconfdir = ${sysconfdir}" echo "CXX = ${CXX}" echo "CPPFLAGS = ${CPPFLAGS}" echo "CXXFLAGS = ${CXXFLAGS}" @@ -165,7 +152,7 @@ echo "LDFLAGS = ${LDFLAGS}" rm -f Makefile cat > Makefile << EOF # Makefile for Lziprecover - Data recovery tool for lzipped files -# Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. +# Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz. # This file was generated automatically by configure. Do not edit. # # This Makefile is free software: you have unlimited permission @@ -178,10 +165,9 @@ VPATH = ${srcdir} prefix = ${prefix} exec_prefix = ${exec_prefix} bindir = ${bindir} -datadir = ${datadir} +datarootdir = ${datarootdir} infodir = ${infodir} mandir = ${mandir} -sysconfdir = ${sysconfdir} CXX = ${CXX} CPPFLAGS = ${CPPFLAGS} CXXFLAGS = ${CXXFLAGS} @@ -1,5 +1,5 @@ /* Lziprecover - Data recovery tool for lzipped files - Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. + Copyright (C) 2009, 2010, 2011, 2012 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 @@ -34,7 +34,7 @@ const CRC32 crc32; -void Pretty_print::operator()( const char * const msg ) const throw() +void Pretty_print::operator()( const char * const msg ) const { if( verbosity_ >= 0 ) { @@ -54,7 +54,7 @@ void Pretty_print::operator()( const char * const msg ) const throw() // Returns the number of bytes really read. // If (returned value < size) and (errno == 0), means EOF was reached. // -int readblock( const int fd, uint8_t * const buf, const int size ) throw() +int readblock( const int fd, uint8_t * const buf, const int size ) { int rest = size; errno = 0; @@ -73,7 +73,7 @@ int readblock( const int fd, uint8_t * const buf, const int size ) throw() // Returns the number of bytes really written. // If (returned value < size), it is always an error. // -int writeblock( const int fd, const uint8_t * const buf, const int size ) throw() +int writeblock( const int fd, const uint8_t * const buf, const int size ) { int rest = size; errno = 0; @@ -82,7 +82,7 @@ int writeblock( const int fd, const uint8_t * const buf, const int size ) throw( errno = 0; const int n = write( fd, buf + size - rest, rest ); if( n > 0 ) rest -= n; - else if( errno && errno != EINTR && errno != EAGAIN ) break; + else if( n < 0 && errno != EINTR && errno != EAGAIN ) break; } return ( rest > 0 ) ? size - rest : size; } @@ -129,21 +129,17 @@ bool LZ_decoder::verify_trailer( const Pretty_print & pp ) const const long long member_size = range_decoder.member_position() + trailer_size; bool error = false; - for( int i = 0; i < trailer_size && !error; ++i ) + const int size = range_decoder.read( trailer.data, trailer_size ); + if( size < trailer_size ) { - if( !range_decoder.finished() ) - trailer.data[i] = range_decoder.get_byte(); - else + error = true; + if( pp.verbosity() >= 0 ) { - error = true; - if( pp.verbosity() >= 0 ) - { - pp(); - std::fprintf( stderr, "Trailer truncated at trailer position %d;" - " some checks may fail.\n", i ); - } - for( ; i < trailer_size; ++i ) trailer.data[i] = 0; + pp(); + std::fprintf( stderr, "Trailer truncated at trailer position %d;" + " some checks may fail.\n", size ); } + for( int i = size; i < trailer_size; ++i ) trailer.data[i] = 0; } if( member_version == 0 ) trailer.member_size( member_size ); if( !range_decoder.code_is_zero() ) @@ -297,7 +293,7 @@ int LZ_decoder::decode_member( const Pretty_print & pp ) if( pp.verbosity() >= 0 ) { pp(); - std::fprintf( stderr, "Unsupported marker code `%d'.\n", len ); + std::fprintf( stderr, "Unsupported marker code '%d'.\n", len ); } return 4; } @@ -1,5 +1,5 @@ /* Lziprecover - Data recovery tool for lzipped files - Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. + Copyright (C) 2009, 2010, 2011, 2012 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 @@ -29,8 +29,11 @@ class Range_decoder bool read_block(); + Range_decoder( const Range_decoder & ); // declared as private + void operator=( const Range_decoder & ); // declared as private + public: - Range_decoder( const int ifd ) + explicit Range_decoder( const int ifd ) : partial_member_pos( 0 ), buffer( new uint8_t[buffer_size] ), @@ -43,12 +46,10 @@ public: ~Range_decoder() { delete[] buffer; } - bool code_is_zero() const throw() { return ( code == 0 ); } + bool code_is_zero() const { return ( code == 0 ); } bool finished() { return pos >= stream_pos && !read_block(); } - long long member_position() const throw() - { return partial_member_pos + pos; } - void reset_member_position() throw() - { partial_member_pos = -pos; } + long long member_position() const { return partial_member_pos + pos; } + void reset_member_position() { partial_member_pos = -pos; } uint8_t get_byte() { @@ -56,6 +57,19 @@ public: return buffer[pos++]; } + int read( uint8_t * const outbuf, const int size ) + { + int rest = size; + while( rest > 0 && !finished() ) + { + const int rd = std::min( rest, stream_pos - pos ); + std::memcpy( outbuf + size - rest, buffer + pos, rd ); + pos += rd; + rest -= rd; + } + return ( rest > 0 ) ? size - rest : size; + } + void load() { code = 0; @@ -176,7 +190,7 @@ class Literal_decoder { Bit_model bm_literal[1<<literal_context_bits][0x300]; - int lstate( const int prev_byte ) const throw() + int lstate( const uint8_t prev_byte ) const { return ( prev_byte >> ( 8 - literal_context_bits ) ); } public: @@ -205,18 +219,17 @@ class LZ_decoder const int member_version; Range_decoder & range_decoder; - long long stream_position() const throw() - { return partial_data_pos + stream_pos; } + long long stream_position() const { return partial_data_pos + stream_pos; } void flush_data(); bool verify_trailer( const Pretty_print & pp ) const; - uint8_t get_prev_byte() const throw() + uint8_t get_prev_byte() const { const int i = ( ( pos > 0 ) ? pos : buffer_size ) - 1; return buffer[i]; } - uint8_t get_byte( const int distance ) const throw() + uint8_t get_byte( const int distance ) const { int i = pos - distance - 1; if( i < 0 ) i += buffer_size; @@ -246,6 +259,9 @@ class LZ_decoder } } + LZ_decoder( const LZ_decoder & ); // declared as private + void operator=( const LZ_decoder & ); // declared as private + public: LZ_decoder( const File_header & header, Range_decoder & rdec, const int ofd, const long long oskip = 0, const long long oend = LLONG_MAX ) @@ -266,10 +282,9 @@ public: ~LZ_decoder() { delete[] buffer; } - uint32_t crc() const throw() { return crc_ ^ 0xFFFFFFFFU; } + uint32_t crc() const { return crc_ ^ 0xFFFFFFFFU; } - long long data_position() const throw() - { return partial_data_pos + pos; } + long long data_position() const { return partial_data_pos + pos; } int decode_member( const Pretty_print & pp ); }; diff --git a/doc/lziprecover.1 b/doc/lziprecover.1 index f8e6148..862cbe0 100644 --- a/doc/lziprecover.1 +++ b/doc/lziprecover.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. -.TH LZIPRECOVER "1" "November 2011" "Lziprecover 1.13-rc2" "User Commands" +.TH LZIPRECOVER "1" "February 2012" "Lziprecover 1.13" "User Commands" .SH NAME Lziprecover \- recovers data from damaged lzip files .SH SYNOPSIS @@ -46,7 +46,7 @@ suppress all messages try to repair a small error in file .TP \fB\-s\fR, \fB\-\-split\fR -split multimember file in single\-member files +split multi\-member file in single\-member files .TP \fB\-t\fR, \fB\-\-test\fR test compressed file integrity @@ -61,7 +61,7 @@ Report bugs to lzip\-bug@nongnu.org .br Lziprecover home page: http://www.nongnu.org/lzip/lziprecover.html .SH COPYRIGHT -Copyright \(co 2011 Antonio Diaz Diaz. +Copyright \(co 2012 Antonio Diaz Diaz. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> .br This is free software: you are free to change and redistribute it. diff --git a/doc/lziprecover.info b/doc/lziprecover.info index 621f5cf..73830cf 100644 --- a/doc/lziprecover.info +++ b/doc/lziprecover.info @@ -12,7 +12,7 @@ File: lziprecover.info, Node: Top, Next: Introduction, Up: (dir) Lziprecover Manual ****************** -This manual is for Lziprecover (version 1.13-rc2, 20 November 2011). +This manual is for Lziprecover (version 1.13, 24 February 2012). * Menu: @@ -24,7 +24,7 @@ This manual is for Lziprecover (version 1.13-rc2, 20 November 2011). * Concept Index:: Index of concepts - Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. + Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz. This manual is free documentation: you have unlimited permission to copy, distribute and modify it. @@ -46,6 +46,13 @@ the compressors in the lzip family; lzip, plzip, minilzip/lzlib, clzip and pdlzip. This recovery capability contributes to make the lzip format one of the best options for long-term data archiving. + Lziprecover is able to efficiently extract a range of bytes from a +multi-member file, because it only decompresses the members containing +the desired data. + + Lziprecover can print correct total file sizes and ratios even for +multi-member files. + When recovering data, lziprecover takes as arguments the names of the damaged files and writes zero or more recovered files depending on the operation selected and whether the recovery succeeded or not. The @@ -111,7 +118,7 @@ The format for running lziprecover is: `--output' option is used. In order to guarantee the correctness of the data produced, all members containing any part of the desired data are decompressed and their integrity is verified. - This operation is more efficient in multimember files because it + This operation is more efficient in multi-member files because it only decompresses the members containing the desired data. `-f' @@ -125,7 +132,7 @@ The format for running lziprecover is: `-l' `--list' Print total file sizes and ratios. The values produced are correct - even for multimember files. + even for multi-member files. `-m' `--merge' @@ -266,7 +273,7 @@ additional information before, between, or after them. `Member size (8 bytes)' Total size of the member, including header and trailer. This - facilitates safe recovery of undamaged members from multimember + facilitates safe recovery of undamaged members from multi-member files. @@ -291,13 +298,13 @@ show status. Example 3: Decompress `file.lz' partially until 10KiB of decompressed data are produced. - lziprecover -cd file.lz | dd bs=1024 count=10 + lziprecover -D 10KiB file.lz Example 4: Decompress `file.lz' partially from decompressed byte 10000 to decompressed byte 15000 (5000 bytes are produced). - lziprecover -cd file.lz | dd bs=1000 skip=10 count=5 + lziprecover -D 10000-15000 file.lz Example 5: Repair a one-byte corruption in the single-member file @@ -317,8 +324,9 @@ the integrity of the resulting files. lziprecover -tv rec*file.lz -Example 7: Recover a compressed backup from two copies on CD-ROM (see -the GNU ddrescue manual for details about ddrescue) +Example 7: Recover a compressed backup from two copies on CD-ROM with +error-checked merging of copies (*Note GNU ddrescue manual: +(ddrescue)Top, for details about ddrescue). ddrescue -b2048 /dev/cdrom cdimage1 logfile1 mount -t iso9660 -o loop,ro cdimage1 /mnt/cdimage @@ -391,12 +399,17 @@ Concept Index Tag Table: Node: Top231 -Node: Introduction898 -Node: Invoking Lziprecover2684 -Node: File Format7727 -Node: Examples9733 -Ref: ddrescue-example10986 -Node: Problems12765 -Node: Concept Index13315 +Node: Introduction900 +Node: Invoking Lziprecover2937 +Node: File Format7982 +Node: Examples9989 +Ref: ddrescue-example11207 +Node: Problems13038 +Node: Concept Index13588 End Tag Table + + +Local Variables: +coding: iso-8859-15 +End: diff --git a/doc/lziprecover.texinfo b/doc/lziprecover.texinfo index 9086787..22eea8a 100644 --- a/doc/lziprecover.texinfo +++ b/doc/lziprecover.texinfo @@ -1,12 +1,13 @@ \input texinfo @c -*-texinfo-*- @c %**start of header @setfilename lziprecover.info +@documentencoding ISO-8859-15 @settitle Lziprecover Manual @finalout @c %**end of header -@set UPDATED 20 November 2011 -@set VERSION 1.13-rc2 +@set UPDATED 24 February 2012 +@set VERSION 1.13 @dircategory Data Compression @direntry @@ -43,7 +44,7 @@ This manual is for Lziprecover (version @value{VERSION}, @value{UPDATED}). @end menu @sp 1 -Copyright @copyright{} 2009, 2010, 2011 Antonio Diaz Diaz. +Copyright @copyright{} 2009, 2010, 2011, 2012 Antonio Diaz Diaz. This manual is free documentation: you have unlimited permission to copy, distribute and modify it. @@ -64,6 +65,13 @@ the compressors in the lzip family; lzip, plzip, minilzip/lzlib, clzip and pdlzip. This recovery capability contributes to make the lzip format one of the best options for long-term data archiving. +Lziprecover is able to efficiently extract a range of bytes from a +multi-member file, because it only decompresses the members containing +the desired data. + +Lziprecover can print correct total file sizes and ratios even for +multi-member files. + When recovering data, lziprecover takes as arguments the names of the damaged files and writes zero or more recovered files depending on the operation selected and whether the recovery succeeded or not. The @@ -133,7 +141,7 @@ produced bytes are sent to standard output unless the @samp{--output} option is used. In order to guarantee the correctness of the data produced, all members containing any part of the desired data are decompressed and their integrity is verified. This operation is more -efficient in multimember files because it only decompresses the members +efficient in multi-member files because it only decompresses the members containing the desired data. @item -f @@ -147,7 +155,7 @@ Keep (don't delete) input files during decompression. @item -l @itemx --list Print total file sizes and ratios. The values produced are correct even -for multimember files. +for multi-member files. @item -m @itemx --merge @@ -295,7 +303,7 @@ Size of the uncompressed original data. @item Member size (8 bytes) Total size of the member, including header and trailer. This facilitates -safe recovery of undamaged members from multimember files. +safe recovery of undamaged members from multi-member files. @end table @@ -327,7 +335,7 @@ Example 3: Decompress @samp{file.lz} partially until 10KiB of decompressed data are produced. @example -lziprecover -cd file.lz | dd bs=1024 count=10 +lziprecover -D 10KiB file.lz @end example @sp 1 @@ -336,7 +344,7 @@ Example 4: Decompress @samp{file.lz} partially from decompressed byte 10000 to decompressed byte 15000 (5000 bytes are produced). @example -lziprecover -cd file.lz | dd bs=1000 skip=10 count=5 +lziprecover -D 10000-15000 file.lz @end example @sp 1 @@ -365,8 +373,16 @@ lziprecover -tv rec*file.lz @sp 1 @anchor{ddrescue-example} @noindent -Example 7: Recover a compressed backup from two copies on CD-ROM (see -the GNU ddrescue manual for details about ddrescue) +Example 7: Recover a compressed backup from two copies on CD-ROM with +error-checked merging of copies +@ifnothtml +(@xref{Top,GNU ddrescue manual,,ddrescue}, +@end ifnothtml +@ifhtml +(See the +@uref{http://www.gnu.org/software/ddrescue/manual/ddrescue_manual.html,,ddrescue manual} +@end ifhtml +for details about ddrescue). @example ddrescue -b2048 /dev/cdrom cdimage1 logfile1 @@ -1,5 +1,5 @@ /* Lziprecover - Data recovery tool for lzipped files - Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. + Copyright (C) 2009, 2010, 2011, 2012 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 @@ -21,32 +21,32 @@ class State public: enum { states = 12 }; - State() throw() : st( 0 ) {} - unsigned char operator()() const throw() { return st; } - bool is_char() const throw() { return st < 7; } + State() : st( 0 ) {} + unsigned char operator()() const { return st; } + bool is_char() const { return st < 7; } - void set_char() throw() + void set_char() { static const unsigned char next[states] = { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5 }; st = next[st]; } - void set_match() throw() + void set_match() { static const unsigned char next[states] = { 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10 }; st = next[st]; } - void set_rep() throw() + void set_rep() { static const unsigned char next[states] = { 8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11 }; st = next[st]; } - void set_short_rep() throw() + void set_short_rep() { static const unsigned char next[states] = { 9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11 }; @@ -87,7 +87,7 @@ enum { max_dis_states = 4 }; -inline int get_dis_state( int len ) throw() +inline int get_dis_state( int len ) { len -= min_match_len; if( len >= max_dis_states ) len = max_dis_states - 1; @@ -102,7 +102,7 @@ enum { bit_model_move_bits = 5, struct Bit_model { unsigned int probability; - Bit_model() throw() : probability( bit_model_total / 2 ) {} + Bit_model() : probability( bit_model_total / 2 ) {} }; @@ -145,10 +145,10 @@ public: first_post = true; } - void reset() const throw() { if( name_.size() ) first_post = true; } - const char * name() const throw() { return name_.c_str(); } - int verbosity() const throw() { return verbosity_; } - void operator()( const char * const msg = 0 ) const throw(); + void reset() const { if( name_.size() ) first_post = true; } + const char * name() const { return name_.c_str(); } + int verbosity() const { return verbosity_; } + void operator()( const char * const msg = 0 ) const; }; @@ -168,10 +168,10 @@ public: } } - uint32_t operator[]( const uint8_t byte ) const throw() { return data[byte]; } - void update( uint32_t & crc, const uint8_t byte ) const throw() + uint32_t operator[]( const uint8_t byte ) const { return data[byte]; } + void update( uint32_t & crc, const uint8_t byte ) const { crc = data[(crc^byte)&0xFF] ^ ( crc >> 8 ); } - void update( uint32_t & crc, const uint8_t * const buffer, const int size ) const throw() + void update( uint32_t & crc, const uint8_t * const buffer, const int size ) const { for( int i = 0; i < size; ++i ) crc = data[(crc^buffer[i])&0xFF] ^ ( crc >> 8 ); @@ -181,14 +181,15 @@ public: extern const CRC32 crc32; -inline int real_bits( const int value ) throw() +inline int real_bits( const unsigned int value ) { - int bits = 0; - for( int i = 1, mask = 1; mask > 0; ++i, mask <<= 1 ) - if( value & mask ) bits = i; + int bits = 0, i = 1; + unsigned int mask = 1; + for( ; mask > 0; ++i, mask <<= 1 ) if( value & mask ) bits = i; return bits; } + const uint8_t magic_string[4] = { 'L', 'Z', 'I', 'P' }; struct File_header @@ -198,16 +199,14 @@ struct File_header // 5 coded_dict_size enum { size = 6 }; - void set_magic() throw() - { std::memcpy( data, magic_string, 4 ); data[4] = 1; } - - bool verify_magic() const throw() + void set_magic() { std::memcpy( data, magic_string, 4 ); data[4] = 1; } + bool verify_magic() const { return ( std::memcmp( data, magic_string, 4 ) == 0 ); } - uint8_t version() const throw() { return data[4]; } - bool verify_version() const throw() { return ( data[4] <= 1 ); } + uint8_t version() const { return data[4]; } + bool verify_version() const { return ( data[4] <= 1 ); } - int dictionary_size() const throw() + int dictionary_size() const { int sz = ( 1 << ( data[5] & 0x1F ) ); if( sz > min_dictionary_size && sz <= max_dictionary_size ) @@ -215,7 +214,7 @@ struct File_header return sz; } - bool dictionary_size( const int sz ) throw() + bool dictionary_size( const int sz ) { if( sz >= min_dictionary_size && sz <= max_dictionary_size ) { @@ -244,36 +243,36 @@ struct File_trailer static int size( const int version = 1 ) { return ( ( version >= 1 ) ? 20 : 12 ); } - uint32_t data_crc() const throw() + uint32_t data_crc() const { uint32_t tmp = 0; for( int i = 3; i >= 0; --i ) { tmp <<= 8; tmp += data[i]; } return tmp; } - void data_crc( uint32_t crc ) throw() + void data_crc( uint32_t crc ) { for( int i = 0; i <= 3; ++i ) { data[i] = (uint8_t)crc; crc >>= 8; } } - long long data_size() const throw() + long long data_size() const { long long tmp = 0; for( int i = 11; i >= 4; --i ) { tmp <<= 8; tmp += data[i]; } return tmp; } - void data_size( long long sz ) throw() + void data_size( long long sz ) { for( int i = 4; i <= 11; ++i ) { data[i] = (uint8_t)sz; sz >>= 8; } } - long long member_size() const throw() + long long member_size() const { long long tmp = 0; for( int i = 19; i >= 12; --i ) { tmp <<= 8; tmp += data[i]; } return tmp; } - void member_size( long long sz ) throw() + void member_size( long long sz ) { for( int i = 12; i <= 19; ++i ) { data[i] = (uint8_t)sz; sz >>= 8; } } @@ -283,7 +282,7 @@ struct File_trailer struct Error { const char * const msg; - Error( const char * const s ) throw() : msg( s ) {} + explicit Error( const char * const s ) : msg( s ) {} }; @@ -303,41 +302,40 @@ class Block long long pos_, size_; // pos + size <= LLONG_MAX public: - Block( const long long p, const long long s ) throw() - : pos_( p ), size_( s ) {} + Block( const long long p, const long long s ) : pos_( p ), size_( s ) {} - long long pos() const throw() { return pos_; } - long long size() const throw() { return size_; } - long long end() const throw() { return pos_ + size_; } + long long pos() const { return pos_; } + long long size() const { return size_; } + long long end() const { return pos_ + size_; } - void pos( const long long p ) throw() { pos_ = p; } - void size( const long long s ) throw() { size_ = s; } + void pos( const long long p ) { pos_ = p; } + void size( const long long s ) { size_ = s; } - bool overlaps( const Block & b ) const throw() + bool overlaps( const Block & b ) const { return ( pos_ < b.end() && b.pos_ < end() ); } - void shift( Block & b ) throw() { ++size_; ++b.pos_; --b.size_; } + void shift( Block & b ) { ++size_; ++b.pos_; --b.size_; } }; // defined in decoder.cc -int readblock( const int fd, uint8_t * const buf, const int size ) throw(); -int writeblock( const int fd, const uint8_t * const buf, const int size ) throw(); +int readblock( const int fd, uint8_t * const buf, const int size ); +int writeblock( const int fd, const uint8_t * const buf, const int size ); // defined in main.cc extern int verbosity; const char * format_num( long long num, long long limit = LLONG_MAX, - const int set_prefix = 0 ) throw(); + const int set_prefix = 0 ); int open_instream( const std::string & name, struct stat * const in_statsp, - const bool to_stdout, const bool reg_only = false ) throw(); + const bool to_stdout, const bool reg_only = false ); int open_outstream_rw( const std::string & output_filename, - const bool force ) throw(); + const bool force ); void show_error( const char * const msg, const int errcode = 0, - const bool help = false ) throw(); + const bool help = false ); void internal_error( const char * const msg ); // defined in merge.cc void cleanup_and_fail( const std::string & output_filename, - const int outfd, const int retval ) throw(); + const int outfd, const int retval ); bool copy_file( const int infd, const int outfd, const long long size = LLONG_MAX ); bool try_decompress( const int fd, const long long file_size, @@ -1,5 +1,5 @@ /* Lziprecover - Data recovery tool for lzipped files - Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. + Copyright (C) 2009, 2010, 2011, 2012 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 @@ -38,6 +38,10 @@ #include <utime.h> #include <sys/stat.h> #if defined(__MSVCRT__) +#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 @@ -57,7 +61,7 @@ namespace { const char * const Program_name = "Lziprecover"; const char * const program_name = "lziprecover"; -const char * const program_year = "2011"; +const char * const program_year = "2012"; const char * invocation_name = 0; #ifdef O_BINARY @@ -82,7 +86,7 @@ mode_t outfd_mode = usr_rw; bool delete_output_on_interrupt = false; -void show_help() throw() +void show_help() { std::printf( "%s - Data recovery tool and decompressor for lzipped files.\n", Program_name ); std::printf( "\nUsage: %s [options] [files]\n", invocation_name ); @@ -101,7 +105,7 @@ void show_help() throw() " -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" + " -s, --split split multi-member 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" @@ -112,7 +116,7 @@ void show_help() throw() } -void show_version() throw() +void show_version() { std::printf( "%s %s\n", Program_name, PROGVERSION ); std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year ); @@ -122,7 +126,7 @@ void show_version() throw() } -void one_file( const int argind, const int arguments ) throw() +void one_file( const int argind, const int arguments ) { if( argind + 1 != arguments ) { @@ -132,7 +136,7 @@ void one_file( const int argind, const int arguments ) throw() } -void set_mode( Mode & program_mode, const Mode new_mode ) throw() +void set_mode( Mode & program_mode, const Mode new_mode ) { if( program_mode != m_none && program_mode != new_mode ) { @@ -143,7 +147,7 @@ void set_mode( Mode & program_mode, const Mode new_mode ) throw() } -int extension_index( const std::string & name ) throw() +int extension_index( const std::string & name ) { for( int i = 0; known_extensions[i].from; ++i ) { @@ -156,7 +160,7 @@ int extension_index( const std::string & name ) throw() } -void set_d_outname( const std::string & name, const int i ) throw() +void set_d_outname( const std::string & name, const int i ) { if( i >= 0 ) { @@ -170,12 +174,12 @@ void set_d_outname( const std::string & name, const int i ) throw() } output_filename = name; output_filename += ".out"; if( verbosity >= 1 ) - 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() ); } -bool open_outstream( const bool force ) throw() +bool open_outstream( const bool force ) { int flags = O_CREAT | O_WRONLY | o_binary; if( force ) flags |= O_TRUNC; else flags |= O_EXCL; @@ -184,17 +188,17 @@ bool open_outstream( const bool force ) throw() if( outfd < 0 && verbosity >= 0 ) { if( errno == EEXIST ) - std::fprintf( stderr, "%s: Output file `%s' already exists, skipping.\n", + 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", + std::fprintf( stderr, "%s: Can't create output file '%s': %s.\n", program_name, output_filename.c_str(), std::strerror( errno ) ); } return ( outfd >= 0 ); } -bool check_tty( const int infd ) throw() +bool check_tty( const int infd ) { if( isatty( infd ) ) { @@ -205,13 +209,13 @@ bool check_tty( const int infd ) throw() } -void cleanup_and_fail( const int retval ) throw() +void cleanup_and_fail( const int retval ) { if( delete_output_on_interrupt ) { delete_output_on_interrupt = false; if( verbosity >= 0 ) - std::fprintf( stderr, "%s: Deleting output file `%s', if it exists.\n", + std::fprintf( stderr, "%s: Deleting output file '%s', if it exists.\n", program_name, output_filename.c_str() ); if( outfd >= 0 ) { close( outfd ); outfd = -1; } if( std::remove( output_filename.c_str() ) != 0 && errno != ENOENT ) @@ -247,7 +251,7 @@ void close_and_set_permissions( const struct stat * const in_statsp ) } -std::string insert_fixed( std::string name ) throw() +std::string insert_fixed( std::string name ) { if( name.size() > 4 && name.compare( name.size() - 4, 4, ".tlz" ) == 0 ) name.insert( name.size() - 4, "_fixed" ); @@ -258,7 +262,7 @@ std::string insert_fixed( std::string name ) throw() } -unsigned char xdigit( const int value ) throw() +unsigned char xdigit( const int value ) { if( value >= 0 && value <= 9 ) return '0' + value; if( value >= 10 && value <= 15 ) return 'A' + value - 10; @@ -267,7 +271,7 @@ unsigned char xdigit( const int value ) throw() void show_trailing_garbage( const uint8_t * const data, const int size, - const Pretty_print & pp, const bool all ) throw() + const Pretty_print & pp, const bool all ) { std::string garbage_msg; if( !all ) garbage_msg = "first bytes of "; @@ -277,11 +281,11 @@ void show_trailing_garbage( const uint8_t * const data, const int size, if( !std::isprint( data[i] ) ) { text = false; break; } if( text ) { - garbage_msg += '`'; + garbage_msg += '\''; garbage_msg.append( (const char *)data, size ); garbage_msg += '\''; } - else + else { for( int i = 0; i < size; ++i ) { @@ -304,10 +308,8 @@ int decompress( const int infd, const Pretty_print & pp, const bool testing ) for( bool first_member = true; ; first_member = false, pp.reset() ) { File_header header; - int size; rdec.reset_member_position(); - for( size = 0; size < File_header::size && !rdec.finished(); ++size ) - header.data[size] = rdec.get_byte(); + const int size = rdec.read( header.data, File_header::size ); if( rdec.finished() ) // End Of File { if( first_member ) @@ -344,8 +346,8 @@ int decompress( const int infd, const Pretty_print & pp, const bool testing ) header.version(), format_num( header.dictionary_size(), 9999, -1 ) ); } - LZ_decoder decoder( header, rdec, outfd ); + LZ_decoder decoder( header, rdec, outfd ); const int result = decoder.decode_member( pp ); partial_file_pos += rdec.member_position(); if( result != 0 ) @@ -380,14 +382,14 @@ int decompress( const int infd, const Pretty_print & pp, const bool testing ) } -extern "C" void signal_handler( int ) throw() +extern "C" void signal_handler( int ) { show_error( "Control-C or similar caught, quitting." ); cleanup_and_fail( 1 ); } -void set_signals() throw() +void set_signals() { std::signal( SIGHUP, signal_handler ); std::signal( SIGINT, signal_handler ); @@ -401,7 +403,7 @@ int verbosity = 0; const char * format_num( long long num, long long limit, - const int set_prefix ) throw() + const int set_prefix ) { const char * const si_prefix[8] = { "k", "M", "G", "T", "P", "E", "Z", "Y" }; @@ -425,13 +427,13 @@ const char * format_num( long long num, long long limit, int open_instream( const std::string & name, struct stat * const in_statsp, - const bool to_stdout, const bool reg_only ) throw() + const bool to_stdout, const bool reg_only ) { int infd = open( name.c_str(), O_RDONLY | o_binary ); if( infd < 0 ) { if( verbosity >= 0 ) - std::fprintf( stderr, "%s: Can't open input file `%s': %s.\n", + std::fprintf( stderr, "%s: Can't open input file '%s': %s.\n", program_name, name.c_str(), std::strerror( errno ) ); } else @@ -444,10 +446,10 @@ int open_instream( const std::string & name, struct stat * const in_statsp, if( i != 0 || ( !S_ISREG( mode ) && ( !to_stdout || !can_read ) ) ) { 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(), ( can_read && !to_stdout ) ? - " and `--stdout' was not specified" : "" ); + " and '--stdout' was not specified" : "" ); close( infd ); infd = -1; } @@ -457,7 +459,7 @@ int open_instream( const std::string & name, struct stat * const in_statsp, int open_outstream_rw( const std::string & output_filename, - const bool force ) throw() + const bool force ) { int flags = O_CREAT | O_RDWR | o_binary; if( force ) flags |= O_TRUNC; else flags |= O_EXCL; @@ -466,18 +468,18 @@ int open_outstream_rw( const std::string & output_filename, if( outfd < 0 && verbosity >= 0 ) { if( errno == EEXIST ) - std::fprintf( stderr, "%s: Output file `%s' already exists." - " Use `--force' to overwrite it.\n", + std::fprintf( stderr, "%s: Output file '%s' already exists." + " Use '--force' to overwrite it.\n", program_name, output_filename.c_str() ); else - std::fprintf( stderr, "%s: Can't create output file `%s': %s.\n", + std::fprintf( stderr, "%s: Can't create output file '%s': %s.\n", program_name, output_filename.c_str(), std::strerror( errno ) ); } return outfd; } -void show_error( const char * const msg, const int errcode, const bool help ) throw() +void show_error( const char * const msg, const int errcode, const bool help ) { if( verbosity >= 0 ) { @@ -489,7 +491,7 @@ void show_error( const char * const msg, const int errcode, const bool help ) th std::fprintf( stderr, "\n" ); } if( help && invocation_name && invocation_name[0] ) - std::fprintf( stderr, "Try `%s --help' for more information.\n", + std::fprintf( stderr, "Try '%s --help' for more information.\n", invocation_name ); } } @@ -567,6 +569,11 @@ int main( const int argc, const char * const argv[] ) } } // end process options +#if defined(__MSVCRT__) || defined(__OS2__) + _fsetmode( stdin, "b" ); + _fsetmode( stdout, "b" ); +#endif + if( program_mode == m_none ) { show_error( "You must specify the operation to be performed.", 0, true ); @@ -607,6 +614,11 @@ int main( const int argc, const char * const argv[] ) case m_test: break; } + if( program_mode == m_test ) + outfd = -1; + else if( program_mode != m_decompress ) + internal_error( "invalid decompressor operation" ); + bool filenames_given = false; for( ; argind < parser.arguments(); ++argind ) { @@ -620,10 +632,6 @@ int main( const int argc, const char * const argv[] ) set_signals(); Pretty_print pp( filenames, verbosity ); - if( program_mode == m_test ) - outfd = -1; - else if( program_mode != m_decompress ) - internal_error( "invalid decompressor operation" ); int retval = 0; for( unsigned int i = 0; i < filenames.size(); ++i ) @@ -1,5 +1,5 @@ /* Lziprecover - Data recovery tool for lzipped files - Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. + Copyright (C) 2009, 2010, 2011, 2012 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 @@ -102,7 +102,7 @@ bool copy_and_diff_file( const std::vector< int > & infd_vector, } -int ipow( const unsigned int base, const unsigned int exponent ) throw() +int ipow( const unsigned int base, const unsigned int exponent ) { int result = 1; for( unsigned int i = 0; i < exponent; ++i ) @@ -142,13 +142,14 @@ int open_input_files( const std::vector< std::string > & filenames, if( tmp < 0 ) { if( verbosity >= 0 ) - std::fprintf( stderr, "File `%s' is not seekable.\n", filenames[i].c_str() ); + std::fprintf( stderr, "File '%s' is not seekable.\n", filenames[i].c_str() ); return 1; } if( i == 0 ) { isize = tmp; - if( isize < min_member_size ) { show_error( "Input file is too short." ); return 2; } + if( isize < min_member_size ) + { show_error( "Input file is too short." ); return 2; } } else if( isize != tmp ) { show_error( "Sizes of input files are different." ); return 1; } @@ -165,7 +166,7 @@ int open_input_files( const std::vector< std::string > & filenames, if( try_decompress( infd_vector[i], isize ) ) { if( verbosity >= 1 ) - std::printf( "File `%s' has no errors. Recovery is not needed.\n", + std::printf( "File '%s' has no errors. Recovery is not needed.\n", filenames[i].c_str() ); return 0; } @@ -179,7 +180,7 @@ int open_input_files( const std::vector< std::string > & filenames, void cleanup_and_fail( const std::string & output_filename, - const int outfd, const int retval ) throw() + const int outfd, const int retval ) { if( outfd >= 0 ) close( outfd ); if( std::remove( output_filename.c_str() ) != 0 && errno != ENOENT ) diff --git a/range_dec.cc b/range_dec.cc index 650d81a..d8e171a 100644 --- a/range_dec.cc +++ b/range_dec.cc @@ -1,5 +1,5 @@ /* Lziprecover - Data recovery tool for lzipped files - Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. + Copyright (C) 2009, 2010, 2011, 2012 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 @@ -44,15 +44,15 @@ public: const long long mp, const long long ms ) : dblock_( dp, ds ), mblock_( mp, ms ) {} - const Block & dblock() const throw() { return dblock_; } - Block & dblock() throw() { return dblock_; } - const Block & mblock() const throw() { return mblock_; } - Block & mblock() throw() { return mblock_; } + const Block & dblock() const { return dblock_; } + Block & dblock() { return dblock_; } + const Block & mblock() const { return mblock_; } + Block & mblock() { return mblock_; } }; int seek_read( const int fd, uint8_t * const buf, const int size, - const long long pos ) throw() + const long long pos ) { if( lseek( fd, pos, SEEK_SET ) == pos ) return readblock( fd, buf, size ); @@ -95,30 +95,28 @@ public: member_vector[i+1].dblock().pos( member_vector[i].dblock().end() ); } - long long data_end() const throw() + long long data_end() const { if( member_vector.size() ) return member_vector.back().dblock().end(); else return 0; } - const Member & member( const int i ) const throw() - { return member_vector[i]; } - const Block & dblock( const int i ) const throw() + const Member & member( const int i ) const { return member_vector[i]; } + const Block & dblock( const int i ) const { return member_vector[i].dblock(); } - const Block & mblock( const int i ) const throw() + const Block & mblock( const int i ) const { return member_vector[i].mblock(); } - int members() const throw() { return (int)member_vector.size(); } + int members() const { return (int)member_vector.size(); } }; // Returns the number of chars read, or 0 if error. // -int parse_long_long( const char * const ptr, long long & value ) throw() +int parse_long_long( const char * const ptr, long long & value ) { char * tail; - int c = 0; errno = 0; value = strtoll( ptr, &tail, 0 ); if( tail == ptr || errno ) return 0; - c = tail - ptr; + int c = tail - ptr; if( ptr[c] ) { @@ -133,8 +131,8 @@ int parse_long_long( const char * const ptr, long long & value ) throw() case 'T': exponent = 4; break; case 'G': exponent = 3; break; case 'M': exponent = 2; break; - case 'K': if( factor == 1024 ) exponent = 1; break; - case 'k': if( factor == 1000 ) exponent = 1; 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 ) { @@ -154,7 +152,7 @@ int parse_long_long( const char * const ptr, long long & value ) throw() // Recognized formats: <begin> <begin>-<end> <begin>,<size> // -void parse_range( const char * const ptr, Block & range ) throw() +void parse_range( const char * const ptr, Block & range ) { long long value = 0; int c = parse_long_long( ptr, value ); // pos @@ -176,7 +174,7 @@ void parse_range( const char * const ptr, Block & range ) throw() } -bool safe_seek( const int fd, const long long pos ) throw() +bool safe_seek( const int fd, const long long pos ) { if( lseek( fd, pos, SEEK_SET ) == pos ) return true; show_error( "Seek error", errno ); return false; @@ -1,5 +1,5 @@ /* Lziprecover - Data recovery tool for lzipped files - Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. + Copyright (C) 2009, 2010, 2011, 2012 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 @@ -1,5 +1,5 @@ /* Lziprecover - Data recovery tool for lzipped files - Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. + Copyright (C) 2009, 2010, 2011, 2012 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 @@ -62,7 +62,7 @@ bool next_filename( std::string & output_filename ) // Search forward from 'pos' for "LZIP" (Boyer-Moore algorithm) // Return pos of found string or 'pos+size' if not found. // -int find_magic( const uint8_t * const buffer, const int pos, const int size ) throw() +int find_magic( const uint8_t * const buffer, const int pos, const int size ) { const unsigned char table[256] = { 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, diff --git a/testsuite/check.sh b/testsuite/check.sh index 5b8244c..92dcfdd 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # check script for Lziprecover - Data recovery tool for lzipped files -# Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. +# Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission # to copy, distribute and modify it. @@ -9,11 +9,12 @@ LC_ALL=C export LC_ALL objdir=`pwd` testdir=`cd "$1" ; pwd` +LZIP="${objdir}"/lziprecover LZIPRECOVER="${objdir}"/lziprecover framework_failure() { echo "failure in testing framework" ; exit 1 ; } -if [ ! -x "${LZIPRECOVER}" ] ; then - echo "${LZIPRECOVER}: cannot execute" +if [ ! -x "${LZIP}" ] ; then + echo "${LZIP}: cannot execute" exit 1 fi @@ -40,7 +41,19 @@ fail=0 printf "testing lziprecover-%s..." "$2" -"${LZIPRECOVER}" -D 921-1921 -o copy ${in_lz} || fail=1 +"${LZIP}" -t "${testdir}"/test_v0.lz || fail=1 +printf . +"${LZIP}" -cd "${testdir}"/test_v0.lz > copy || fail=1 +cmp ${in} copy || fail=1 +printf . + +"${LZIP}" -t "${testdir}"/test_v1.lz || fail=1 +printf . +"${LZIP}" -cd "${testdir}"/test_v1.lz > copy || fail=1 +cmp ${in} copy || fail=1 +printf . + +"${LZIPRECOVER}" -D 921-1921 -fo copy ${in_lz} || fail=1 cmp ${inD} copy || fail=1 printf . "${LZIPRECOVER}" -D 921,1000 ${in_lz} > copy || fail=1 @@ -102,6 +115,11 @@ for i in 1 2 3 ; do printf . done +cat ${in_lz} > anyothername || framework_failure +"${LZIP}" -d anyothername || fail=1 +cmp ${in} anyothername.out || fail=1 +printf . + echo if [ ${fail} = 0 ] ; then echo "tests completed successfully." diff --git a/testsuite/unzcrash.cc b/testsuite/unzcrash.cc new file mode 100644 index 0000000..4c49035 --- /dev/null +++ b/testsuite/unzcrash.cc @@ -0,0 +1,353 @@ +/* Unzcrash - A test program written to test robustness to + decompression of corrupted data. + Inspired by unzcrash.c from Julian Seward's bzip2. + Copyright (C) 2008, 2009, 2010, 2011, 2012 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <cerrno> +#include <climits> +#include <csignal> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <string> +#include <vector> +#include <stdint.h> +#include <unistd.h> + +#include "../arg_parser.h" + +#if CHAR_BIT != 8 +#error "Environments where CHAR_BIT != 8 are not supported." +#endif + +#ifndef LLONG_MAX +#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL +#endif +#ifndef LLONG_MIN +#define LLONG_MIN (-LLONG_MAX - 1LL) +#endif +#ifndef ULLONG_MAX +#define ULLONG_MAX 0xFFFFFFFFFFFFFFFFULL +#endif + + +namespace { + +const char * const Program_name = "Unzcrash"; +const char * const program_name = "unzcrash"; +const char * const program_year = "2012"; +const char * invocation_name = 0; + +int verbosity = 0; + + +void show_help() + { + std::printf( "%s - A test program written to test robustness to\n", Program_name ); + std::printf( "decompression of corrupted data.\n" + "\nUsage: %s [options] \"lzip -tv\" filename.lz\n", invocation_name ); + std::printf( "\nThis program reads the specified file and then repeatedly decompresses\n" + "it, increasing 256 times each byte of the compressed data, so as to test\n" + "all possible one-byte errors. This should not cause any invalid memory\n" + "accesses. If it does, please, report it as a bug.\n" + "\nOptions:\n" + " -h, --help display this help and exit\n" + " -V, --version output version information and exit\n" + " -b, --bits=<n>[,<n>]... test <n>-bit errors instead of full byte\n" + " -p, --position=<bytes> first byte position to test\n" + " -q, --quiet suppress all messages\n" + " -s, --size=<bytes> number of byte positions to test\n" + " -v, --verbose be verbose (a 2nd -v gives more)\n" + "\nReport bugs to lzip-bug@nongnu.org\n" + "Lzip home page: http://www.nongnu.org/lzip/lzip.html\n" ); + } + + +void show_version() + { + std::printf( "%s %s\n", Program_name, PROGVERSION ); + std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year ); + std::printf( "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n" ); + } + + +void show_error( const char * const msg, const int errcode = 0, + const bool help = false ) + { + if( verbosity >= 0 ) + { + if( msg && msg[0] ) + { + std::fprintf( stderr, "%s: %s", program_name, msg ); + if( errcode > 0 ) + std::fprintf( stderr, ": %s", std::strerror( errcode ) ); + std::fprintf( stderr, "\n" ); + } + 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 ) + { + if( verbosity >= 0 ) + std::fprintf( stderr, "%s: internal error: %s.\n", program_name, msg ); + std::exit( 3 ); + } + + +long long getnum( const char * const ptr, + const long long llimit = LLONG_MIN + 1, + const long long ulimit = LLONG_MAX ) + { + errno = 0; + char *tail; + long long result = strtoll( ptr, &tail, 0 ); + if( tail == ptr ) + { + show_error( "Bad or missing numerical argument.", 0, true ); + std::exit( 1 ); + } + + if( !errno && tail[0] ) + { + int factor = ( tail[1] == 'i' ) ? 1024 : 1000; + int exponent = 0; + bool bad_multiplier = false; + switch( tail[0] ) + { + case ' ': break; + 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 bad_multiplier = true; + break; + case 'k': if( factor == 1000 ) exponent = 1; else bad_multiplier = true; + break; + default : bad_multiplier = true; + } + if( bad_multiplier ) + { + show_error( "Bad multiplier in numerical argument.", 0, true ); + std::exit( 1 ); + } + for( int i = 0; i < exponent; ++i ) + { + if( LLONG_MAX / factor >= llabs( result ) ) result *= factor; + else { errno = ERANGE; break; } + } + } + if( !errno && ( result < llimit || result > ulimit ) ) errno = ERANGE; + if( errno ) + { + show_error( "Numerical argument out of limits." ); + std::exit( 1 ); + } + return result; + } + + +class Bitset8 // 8 value bitset (1..8) + { + bool data[8]; + static bool valid_digit( const unsigned char ch ) + { return ( ch >= '1' && ch <= '8' ); } + +public: + Bitset8() { for( int i = 0; i < 8; ++i ) data[i] = true; } + + bool includes( const int i ) const + { return ( i >= 1 && i <= 8 && data[i-1] ); } + + // Recognized formats: 1 1,2,3 1-4 1,3-5,8 + bool parse( const char * p ) + { + for( int i = 0; i < 8; ++i ) data[i] = false; + while( true ) + { + const unsigned char ch1 = *p++; + if( !valid_digit( ch1 ) ) break; + if( *p != '-' ) data[ch1-'1'] = true; + else + { + ++p; + if( !valid_digit( *p ) || ch1 > *p ) break; + for( int c = ch1; c <= *p; ++c ) data[c-'1'] = true; + ++p; + } + if( *p == 0 ) return true; + if( *p == ',' ) ++p; else break; + } + show_error( "Invalid value or range." ); + return false; + } + + // number of n-bit errors per byte (n=0..8): 1 8 28 56 70 56 28 8 1 + void print() const + { + std::fflush( stderr ); + int c = 0; + for( int i = 0; i < 8; ++i ) if( data[i] ) ++c; + if( c == 8 ) std::printf( "Testing full byte.\n" ); + else if( c == 0 ) std::printf( "Nothing to test.\n" ); + else + { + std::printf( "Testing " ); + for( int i = 0; i < 8; ++i ) + if( data[i] ) + { + std::printf( "%d", i + 1 ); + if( --c ) std::printf( "," ); + } + std::printf( " bit errors.\n" ); + } + std::fflush( stdout ); + } + }; + + +int differing_bits( const uint8_t byte1, const uint8_t byte2 ) + { + int count = 0; + uint8_t dif = byte1 ^ byte2; + while( dif ) + { count += ( dif & 1 ); dif >>= 1; } + return count; + } + +} // end namespace + + +int main( const int argc, const char * const argv[] ) + { + enum { buffer_size = 3 << 20 }; + Bitset8 bits; // if Bitset8::parse not called test full byte + int pos = 0; + int max_size = buffer_size; + invocation_name = argv[0]; + + const Arg_parser::Option options[] = + { + { 'h', "help", Arg_parser::no }, + { 'b', "bits", Arg_parser::yes }, + { 'p', "position", Arg_parser::yes }, + { 'q', "quiet", Arg_parser::no }, + { 's', "size", Arg_parser::yes }, + { '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 + { show_error( parser.error().c_str(), 0, true ); return 1; } + + int argind = 0; + for( ; argind < parser.arguments(); ++argind ) + { + const int code = parser.code( argind ); + if( !code ) break; // no more options + const char * const arg = parser.argument( argind ).c_str(); + switch( code ) + { + case 'h': show_help(); return 0; + case 'b': if( !bits.parse( arg ) ) return 1; break; + case 'p': pos = getnum( arg, 0, buffer_size - 1 ); break; + case 'q': verbosity = -1; break; + case 's': max_size = getnum( arg, 1, buffer_size ); break; + case 'v': if( verbosity < 4 ) ++verbosity; break; + case 'V': show_version(); return 0; + default : internal_error( "uncaught option" ); + } + } // end process options + + if( argind + 2 != parser.arguments() ) + { + if( verbosity >= 0 ) + std::fprintf( stderr, "Usage: %s \"lzip -tv\" filename.lz\n", + invocation_name ); + return 1; + } + + FILE *f = std::fopen( parser.argument( argind + 1 ).c_str(), "rb" ); + if( !f ) + { + if( verbosity >= 0 ) + std::fprintf( stderr, "Can't open file '%s' for reading\n", + parser.argument( argind + 1 ).c_str() ); + return 1; + } + + uint8_t * const buffer = new uint8_t[buffer_size]; + const int size = std::fread( buffer, 1, buffer_size, f ); + if( size >= buffer_size ) + { + if( verbosity >= 0 ) + std::fprintf( stderr, "input file '%s' is too big.\n", + parser.argument( argind + 1 ).c_str() ); + return 1; + } + std::fclose( f ); + + f = popen( parser.argument( argind ).c_str(), "w" ); + if( !f ) + { show_error( "Can't open pipe", errno ); return 1; } + const int wr = std::fwrite( buffer, 1, size, f ); + if( wr != size || pclose( f ) != 0 ) + { + if( verbosity >= 0 ) + std::fprintf( stderr, "Could not run '%s' : %s.\n", + parser.argument( argind ).c_str(), std::strerror( errno ) ); + return 1; + } + + std::signal( SIGPIPE, SIG_IGN ); + if( verbosity >= 1 ) bits.print(); + + const int end = ( ( pos + max_size < size ) ? pos + max_size : size ); + for( int i = pos; i < end; ++i ) + { + if( verbosity >= 0 ) + std::fprintf( stderr, "byte %d\n", i ); + const uint8_t byte = buffer[i]; + for( int j = 0; j < 255; ++j ) + { + ++buffer[i]; + if( bits.includes( differing_bits( byte, buffer[i] ) ) ) + { + f = popen( parser.argument( argind ).c_str(), "w" ); + if( !f ) + { show_error( "Can't open pipe", errno ); return 1; } + std::fwrite( buffer, 1, size, f ); + if( pclose( f ) == 0 && verbosity >= 0 ) + std::fprintf( stderr, "0x%02X (0x%02X+0x%02X) passed the test\n", + buffer[i], byte, j + 1 ); + } + } + buffer[i] = byte; + } + + delete[] buffer; + return 0; + } |