summaryrefslogtreecommitdiffstats
path: root/split.cc
diff options
context:
space:
mode:
Diffstat (limited to 'split.cc')
-rw-r--r--split.cc176
1 files changed, 176 insertions, 0 deletions
diff --git a/split.cc b/split.cc
new file mode 100644
index 0000000..d6538f8
--- /dev/null
+++ b/split.cc
@@ -0,0 +1,176 @@
+/* Lziprecover - Data recovery tool for lzipped files
+ Copyright (C) 2009, 2010, 2011 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/>.
+*/
+
+#define _FILE_OFFSET_BITS 64
+
+#include <cerrno>
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <string>
+#include <vector>
+#include <stdint.h>
+#include <sys/stat.h>
+
+#include "lzip.h"
+
+
+namespace {
+
+void first_filename( const std::string & input_filename,
+ const std::string & default_output_filename,
+ std::string & output_filename )
+ {
+ if( default_output_filename.size() )
+ output_filename = default_output_filename;
+ else
+ output_filename = input_filename;
+ int b = output_filename.size();
+ while( b > 0 && output_filename[b-1] != '/' ) --b;
+ output_filename.insert( b, "rec00001" );
+ }
+
+
+bool next_filename( std::string & output_filename )
+ {
+ int b = output_filename.size();
+ while( b > 0 && output_filename[b-1] != '/' ) --b;
+ for( int i = b + 7; i >= b + 3; --i ) // "rec00001"
+ {
+ if( output_filename[i] < '9' ) { ++output_filename[i]; return true; }
+ else output_filename[i] = '0';
+ }
+ return false;
+ }
+
+
+// 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()
+ {
+ 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,
+ 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,
+ 4,4,4,4,4,4,4,4,4,1,4,4,3,4,4,4,4,4,4,4,4,4,4,4,4,4,2,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,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,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,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,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,4,4,4,4,4 };
+
+ for( int i = pos; i <= pos + size - 4; i += table[buffer[i+3]] )
+ if( buffer[i] == 'L' && buffer[i+1] == 'Z' &&
+ buffer[i+2] == 'I' && buffer[i+3] == 'P' )
+ return i; // magic string found
+ return pos + size;
+ }
+
+
+int do_split_file( const std::string & input_filename, uint8_t * & base_buffer,
+ const std::string & default_output_filename, const bool force )
+ {
+ const int hsize = File_header::size;
+ const int tsize = File_trailer::size();
+ const int buffer_size = 65536;
+ const int base_buffer_size = tsize + buffer_size + hsize;
+ base_buffer = new uint8_t[base_buffer_size];
+ uint8_t * const buffer = base_buffer + tsize;
+
+ struct stat in_stats;
+ const int infd = open_instream( input_filename, &in_stats, true, true );
+ if( infd < 0 ) return 1;
+ int size = readblock( infd, buffer, buffer_size + hsize ) - hsize;
+ bool at_stream_end = ( size < buffer_size );
+ if( size != buffer_size && errno )
+ { show_error( "Read error", errno ); return 1; }
+ if( size <= tsize )
+ { show_error( "Input file is too short." ); return 2; }
+ if( !verify_header( *(File_header *)buffer ) ) return 2;
+
+ std::string output_filename;
+ first_filename( input_filename, default_output_filename, output_filename );
+ int outfd = open_outstream_rw( output_filename, force );
+ if( outfd < 0 ) { close( infd ); return 1; }
+
+ long long partial_member_size = 0;
+ while( true )
+ {
+ int pos = 0;
+ for( int newpos = 1; newpos <= size; ++newpos )
+ {
+ newpos = find_magic( buffer, newpos, size + 4 - newpos );
+ if( newpos <= size )
+ {
+ long long member_size = 0;
+ for( int i = 1; i <= 8; ++i )
+ { member_size <<= 8; member_size += base_buffer[tsize+newpos-i]; }
+ if( partial_member_size + newpos - pos == member_size )
+ { // header found
+ const int wr = writeblock( outfd, buffer + pos, newpos - pos );
+ if( wr != newpos - pos )
+ { show_error( "Write error", errno ); return 1; }
+ if( close( outfd ) != 0 )
+ { show_error( "Error closing output file", errno ); return 1; }
+ if( !next_filename( output_filename ) )
+ { show_error( "Too many members in file." ); close( infd ); return 1; }
+ outfd = open_outstream_rw( output_filename, force );
+ if( outfd < 0 ) { close( infd ); return 1; }
+ partial_member_size = 0;
+ pos = newpos;
+ }
+ }
+ }
+
+ if( at_stream_end )
+ {
+ const int wr = writeblock( outfd, buffer + pos, size + hsize - pos );
+ if( wr != size + hsize - pos )
+ { show_error( "Write error", errno ); return 1; }
+ break;
+ }
+ if( pos < buffer_size )
+ {
+ partial_member_size += buffer_size - pos;
+ const int wr = writeblock( outfd, buffer + pos, buffer_size - pos );
+ if( wr != buffer_size - pos )
+ { show_error( "Write error", errno ); return 1; }
+ }
+ std::memcpy( base_buffer, base_buffer + buffer_size, tsize + hsize );
+ size = readblock( infd, buffer + hsize, buffer_size );
+ at_stream_end = ( size < buffer_size );
+ if( size != buffer_size && errno )
+ { show_error( "Read error", errno ); return 1; }
+ }
+ close( infd );
+ if( close( outfd ) != 0 )
+ { show_error( "Error closing output file", errno ); return 1; }
+ return 0;
+ }
+
+} // end namespace
+
+
+int split_file( const std::string & input_filename,
+ const std::string & default_output_filename, const bool force )
+ {
+ uint8_t * base_buffer;
+ const int retval = do_split_file( input_filename, base_buffer,
+ default_output_filename, force );
+ delete[] base_buffer;
+ return retval;
+ }