summaryrefslogtreecommitdiffstats
path: root/extract.cc
diff options
context:
space:
mode:
Diffstat (limited to 'extract.cc')
-rw-r--r--extract.cc75
1 files changed, 27 insertions, 48 deletions
diff --git a/extract.cc b/extract.cc
index f85cf67..63f58a5 100644
--- a/extract.cc
+++ b/extract.cc
@@ -44,7 +44,6 @@
namespace {
Resizable_buffer grbuf( initial_line_length );
-int gretval = 0;
bool has_lz_ext; // global var for archive_read
void skip_warn( const bool reset = false ) // avoid duplicate warnings
@@ -118,16 +117,14 @@ int archive_read( const int infd, uint8_t * const buf, const int size,
if( !islz && !istar && !iseof ) // corrupt or invalid format
{
show_error( "This does not look like a POSIX tar archive." );
- if( has_lz_ext ) islz = true;
- if( verbosity >= 2 && !islz && rd == size )
- std::fprintf( stderr, "ustar chksum = %07o\n", ustar_chksum( buf ) );
+ if( has_lz_ext && rd >= min_member_size ) islz = true;
if( !islz ) return 1;
}
if( !islz ) // uncompressed
{ if( rd == size ) return 0; fatal = true; return 2; }
decoder = LZ_decompress_open(); // compressed
if( !decoder || LZ_decompress_errno( decoder ) != LZ_ok )
- { show_error( "Not enough memory." );
+ { show_error( mem_msg );
LZ_decompress_close( decoder ); fatal = true; return 2; }
if( LZ_decompress_write( decoder, buf, rd ) != rd )
internal_error( "library error (LZ_decompress_write)." );
@@ -154,7 +151,7 @@ int archive_read( const int infd, uint8_t * const buf, const int size,
{
if( LZ_decompress_sync_to_member( decoder ) < 0 )
internal_error( "library error (LZ_decompress_sync_to_member)." );
- skip_warn(); gretval = 2; return 1;
+ skip_warn(); set_error_status( 2 ); return 1;
}
if( rd == 0 && LZ_decompress_finished( decoder ) == 1 )
{ LZ_decompress_close( decoder );
@@ -271,8 +268,8 @@ void format_member_name( const Extended & extended, const Tar_header header,
extended.file_size(), 1900 + tm->tm_year, 1 + tm->tm_mon,
tm->tm_mday, tm->tm_hour, tm->tm_min, extended.path().c_str(),
link_string, !islink ? "" : extended.linkpath().c_str() );
- if( (int)rbuf.size() > len + offset ) break;
- else rbuf.resize( len + offset + 1 );
+ if( (int)rbuf.size() > len + offset || !rbuf.resize( len + offset + 1 ) )
+ break;
}
}
else
@@ -458,25 +455,6 @@ int extract_member( const int infd, const Extended & extended,
} // end namespace
-// Removes any amount of leading "./" and '/' strings.
-const char * remove_leading_slash( const char * const filename )
- {
- static bool first_post = true;
- const char * p = filename;
-
- while( *p == '/' || ( *p == '.' && p[1] == '/' ) ) ++p;
- if( p != filename && first_post )
- {
- first_post = false;
- std::string msg( "Removing leading '" );
- msg.append( filename, p - filename );
- msg += "' from member names.";
- show_error( msg.c_str() );
- }
- if( *p == 0 ) p = ".";
- return p;
- }
-
// return true if dir is a parent directory of name
bool compare_prefix_dir( const char * const dir, const char * const name )
@@ -587,19 +565,21 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
{ show_file_error( dir, "Error changing working directory", errno );
return 1; }
}
- if( !code ) name_pending[i] = true;
+ if( !code && parser.argument( i ).size() ) name_pending[i] = true;
}
- if( listing && num_workers > 0 ) // multi-threaded --list
+ // multi-threaded --list is faster even with 1 thread and 1 file in archive
+ if( listing && num_workers > 0 )
{
- const Lzip_index lzip_index( infd, true, false );
+ const Lzip_index lzip_index( infd, true, false ); // only regular files
const long members = lzip_index.members();
- if( lzip_index.retval() == 0 && ( members >= 3 ||
- ( members >= 2 && lzip_index.dblock( members - 1 ).size() > 1024 ) ) )
- { //show_file_error( archive_name.c_str(), "Is compressed seekable" );
- return list_lz( parser, name_pending, lzip_index, filenames,
- debug_level, infd, std::min( (long)num_workers, members ),
- missing_crc, permissive ); }
+ if( lzip_index.retval() == 0 && members >= 2 ) // one file + eof
+ {
+ // show_file_error( archive_name.c_str(), "Is compressed seekable" );
+ return list_lz( parser, name_pending, lzip_index, filenames, debug_level,
+ infd, std::min( (long)num_workers, members ),
+ missing_crc, permissive );
+ }
lseek( infd, 0, SEEK_SET );
}
@@ -619,7 +599,9 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
if( ret != 0 || !verify_ustar_chksum( header ) )
{
if( ret == 0 && block_is_zero( header, header_size ) ) break; // EOF
- skip_warn(); gretval = 2; continue;
+ if( verbosity >= 2 )
+ std::fprintf( stderr, "ustar chksum = %07o\n", ustar_chksum( header ) );
+ skip_warn(); set_error_status( 2 ); continue;
}
skip_warn( true ); // reset warning
@@ -632,7 +614,7 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
Extended dummy; // global headers are parsed and ignored
if( !parse_records( infd, dummy, header, true ) )
{ show_error( "Error in global extended records. Skipping to next header." );
- gretval = 2; }
+ set_error_status( 2 ); }
continue;
}
if( typeflag == tf_extended )
@@ -642,7 +624,7 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
/*" Use --permissive.", 0, true*/ ); return 2; }
if( !parse_records( infd, extended, header, permissive ) )
{ show_error( "Error in extended records. Skipping to next header." );
- extended.reset(); gretval = 2; }
+ extended.reset(); set_error_status( 2 ); }
else if( !extended.crc_present() && missing_crc )
{ show_error( "Missing CRC in extended records.", 0, true ); return 2; }
prev_extended = true;
@@ -674,17 +656,17 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
{ stored_name[len] = header[name_o+i]; ++len; }
while( len > 0 && stored_name[len-1] == '/' ) --len; // trailing '/'
stored_name[len] = 0;
- extended.path( remove_leading_slash( stored_name ) );
+ extended.path( remove_leading_dotslash( stored_name ) );
}
const char * const filename = extended.path().c_str();
bool skip = filenames > 0;
if( skip )
for( int i = 0; i < parser.arguments(); ++i )
- if( parser.code( i ) == 0 )
+ if( !parser.code( i ) && parser.argument( i ).size() )
{
const char * const name =
- remove_leading_slash( parser.argument( i ).c_str() );
+ remove_leading_dotslash( parser.argument( i ).c_str() );
if( compare_prefix_dir( name, filename ) ||
compare_tslash( name, filename ) )
{ skip = false; name_pending[i] = false; break; }
@@ -705,13 +687,10 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
}
for( int i = 0; i < parser.arguments(); ++i )
- if( parser.code( i ) == 0 && name_pending[i] )
+ if( !parser.code( i ) && parser.argument( i ).size() && name_pending[i] )
{
show_file_error( parser.argument( i ).c_str(), "Not found in archive." );
- if( gretval < 1 ) gretval = 1;
+ set_error_status( 1 );
}
- if( !retval && gretval )
- { show_error( "Exiting with failure status due to previous errors." );
- retval = gretval; }
- return retval;
+ return final_exit_status( retval );
}