summaryrefslogtreecommitdiffstats
path: root/common_decode.cc
diff options
context:
space:
mode:
Diffstat (limited to 'common_decode.cc')
-rw-r--r--common_decode.cc90
1 files changed, 51 insertions, 39 deletions
diff --git a/common_decode.cc b/common_decode.cc
index 835687f..a0ff89d 100644
--- a/common_decode.cc
+++ b/common_decode.cc
@@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
- Copyright (C) 2013-2022 Antonio Diaz Diaz.
+ Copyright (C) 2013-2024 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
@@ -19,12 +19,13 @@
#include <cerrno>
#include <cstdio>
-#include <cstdlib>
#include <ctime>
+#include <unistd.h>
#include <sys/stat.h>
#include "tarlz.h"
#include "arg_parser.h"
+#include "decode.h"
namespace {
@@ -125,7 +126,7 @@ bool format_member_name( const Extended & extended, const Tar_header header,
const time_t mtime = extended.mtime().sec();
struct tm t;
if( !localtime_r( &mtime, &t ) ) // if local time fails
- { time_t z = 0; if( !gmtime_r( &z, &t ) ) // use the UTC epoch
+ { time_t z = 0; if( !gmtime_r( &z, &t ) ) // use UTC, the epoch
{ t.tm_year = 70; t.tm_mon = t.tm_hour = t.tm_min = 0; t.tm_mday = 1; } }
const Typeflag typeflag = (Typeflag)header[typeflag_o];
const bool islink = ( typeflag == tf_link || typeflag == tf_symlink );
@@ -184,54 +185,65 @@ bool show_member_name( const Extended & extended, const Tar_header header,
bool check_skip_filename( const Cl_options & cl_opts,
std::vector< char > & name_pending,
- const char * const filename )
+ const char * const filename, const int chdir_fd )
{
+ static int c_idx = -1; // parser index of last -C executed
if( Exclude::excluded( filename ) ) return true; // skip excluded files
- bool skip = cl_opts.num_files > 0; // if no files specified, skip nothing
- if( skip ) // else skip all but the files (or trees) specified
- for( int i = 0; i < cl_opts.parser.arguments(); ++i )
- if( nonempty_arg( cl_opts.parser, i ) )
+ if( cl_opts.num_files <= 0 ) return false; // no files specified, no skip
+ bool skip = true; // else skip all but the files (or trees) specified
+ bool chdir_pending = false;
+
+ for( int i = 0; i < cl_opts.parser.arguments(); ++i )
+ {
+ if( cl_opts.parser.code( i ) == 'C' ) { chdir_pending = true; continue; }
+ if( !nonempty_arg( cl_opts.parser, i ) ) continue; // skip opts, empty names
+ std::string removed_prefix;
+ const char * const name = remove_leading_dotslash(
+ cl_opts.parser.argument( i ).c_str(), &removed_prefix );
+ if( compare_prefix_dir( name, filename ) ||
+ compare_tslash( name, filename ) )
+ {
+ print_removed_prefix( removed_prefix );
+ skip = false; name_pending[i] = false;
+ if( chdir_pending && chdir_fd >= 0 )
{
- std::string removed_prefix;
- const char * const name = remove_leading_dotslash(
- cl_opts.parser.argument( i ).c_str(), &removed_prefix );
- if( compare_prefix_dir( name, filename ) ||
- compare_tslash( name, filename ) )
- { print_removed_prefix( removed_prefix );
- skip = false; name_pending[i] = false; break; }
+ if( c_idx > i )
+ { if( fchdir( chdir_fd ) != 0 )
+ { show_error( "Error changing to initial working directory", errno );
+ throw Chdir_error(); } c_idx = -1; }
+ for( int j = c_idx + 1; j < i; ++j )
+ {
+ if( cl_opts.parser.code( j ) != 'C' ) continue;
+ const char * const dir = cl_opts.parser.argument( j ).c_str();
+ if( chdir( dir ) != 0 )
+ { show_file_error( dir, chdir_msg, errno ); throw Chdir_error(); }
+ c_idx = j;
+ }
}
+ break;
+ }
+ }
return skip;
}
-mode_t get_umask()
+bool make_dirs( const std::string & name )
{
- static mode_t mask = 0; // read once, cache the result
- static bool first_call = true;
- if( first_call ) { first_call = false; mask = umask( 0 ); umask( mask );
- mask &= S_IRWXU | S_IRWXG | S_IRWXO; }
- return mask;
- }
-
-
-bool make_path( const std::string & name )
- {
- const mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
- unsigned end = name.size(); // first slash before last component
-
- while( end > 0 && name[end-1] == '/' ) --end; // remove trailing slashes
- while( end > 0 && name[end-1] != '/' ) --end; // remove last component
- while( end > 0 && name[end-1] == '/' ) --end; // remove more slashes
+ int i = name.size();
+ while( i > 0 && name[i-1] == '/' ) --i; // remove trailing slashes
+ while( i > 0 && name[i-1] != '/' ) --i; // remove last component
+ while( i > 0 && name[i-1] == '/' ) --i; // remove more slashes
+ const int dirsize = i; // first slash before last component
- unsigned index = 0;
- while( index < end )
+ for( i = 0; i < dirsize; ) // if dirsize == 0, dirname is '/' or empty
{
- while( index < end && name[index] == '/' ) ++index;
- unsigned first = index;
- while( index < end && name[index] != '/' ) ++index;
- if( first < index )
+ while( i < dirsize && name[i] == '/' ) ++i;
+ const int first = i;
+ while( i < dirsize && name[i] != '/' ) ++i;
+ if( first < i )
{
- const std::string partial( name, 0, index );
+ const std::string partial( name, 0, i );
+ const mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
struct stat st;
if( lstat( partial.c_str(), &st ) == 0 )
{ if( !S_ISDIR( st.st_mode ) ) { errno = ENOTDIR; return false; } }