summaryrefslogtreecommitdiffstats
path: root/common_decode.cc
diff options
context:
space:
mode:
Diffstat (limited to 'common_decode.cc')
-rw-r--r--common_decode.cc200
1 files changed, 200 insertions, 0 deletions
diff --git a/common_decode.cc b/common_decode.cc
new file mode 100644
index 0000000..791d6e5
--- /dev/null
+++ b/common_decode.cc
@@ -0,0 +1,200 @@
+/* Tarlz - Archiver with multimember lzip compression
+ Copyright (C) 2013-2019 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 2 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 <climits>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <string>
+#include <vector>
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/stat.h>
+
+#include "arg_parser.h"
+#include "tarlz.h"
+
+
+namespace {
+
+enum { mode_string_size = 10,
+ group_string_size = 1 + uname_l + 1 + gname_l + 1 }; // 67
+
+void format_mode_string( const Tar_header header, char buf[mode_string_size] )
+ {
+ const Typeflag typeflag = (Typeflag)header[typeflag_o];
+
+ std::memcpy( buf, "----------", mode_string_size );
+ switch( typeflag )
+ {
+ case tf_regular: break;
+ case tf_link: buf[0] = 'h'; break;
+ case tf_symlink: buf[0] = 'l'; break;
+ case tf_chardev: buf[0] = 'c'; break;
+ case tf_blockdev: buf[0] = 'b'; break;
+ case tf_directory: buf[0] = 'd'; break;
+ case tf_fifo: buf[0] = 'p'; break;
+ case tf_hiperf: buf[0] = 'C'; break;
+ default: buf[0] = '?';
+ }
+ const mode_t mode = parse_octal( header + mode_o, mode_l ); // 12 bits
+ const bool setuid = mode & S_ISUID;
+ const bool setgid = mode & S_ISGID;
+ const bool sticky = mode & S_ISVTX;
+ if( mode & S_IRUSR ) buf[1] = 'r';
+ if( mode & S_IWUSR ) buf[2] = 'w';
+ if( mode & S_IXUSR ) buf[3] = setuid ? 's' : 'x';
+ else if( setuid ) buf[3] = 'S';
+ if( mode & S_IRGRP ) buf[4] = 'r';
+ if( mode & S_IWGRP ) buf[5] = 'w';
+ if( mode & S_IXGRP ) buf[6] = setgid ? 's' : 'x';
+ else if( setgid ) buf[6] = 'S';
+ if( mode & S_IROTH ) buf[7] = 'r';
+ if( mode & S_IWOTH ) buf[8] = 'w';
+ if( mode & S_IXOTH ) buf[9] = sticky ? 't' : 'x';
+ else if( sticky ) buf[9] = 'T';
+ }
+
+
+int format_user_group_string( const Tar_header header,
+ char buf[group_string_size] )
+ {
+ int len;
+ if( header[uname_o] && header[gname_o] )
+ len = snprintf( buf, group_string_size,
+ " %.32s/%.32s", header + uname_o, header + gname_o );
+ else
+ {
+ const unsigned uid = parse_octal( header + uid_o, uid_l );
+ const unsigned gid = parse_octal( header + gid_o, gid_l );
+ len = snprintf( buf, group_string_size, " %u/%u", uid, gid );
+ }
+ return len;
+ }
+
+
+// return true if dir is a parent directory of name
+bool compare_prefix_dir( const char * const dir, const char * const name )
+ {
+ int len = 0;
+ while( dir[len] && dir[len] == name[len] ) ++len;
+ return ( !dir[len] && len > 0 && ( dir[len-1] == '/' || name[len] == '/' ) );
+ }
+
+
+// compare two file names ignoring trailing slashes
+bool compare_tslash( const char * const name1, const char * const name2 )
+ {
+ const char * p = name1;
+ const char * q = name2;
+ while( *p && *p == *q ) { ++p; ++q; }
+ while( *p == '/' ) ++p;
+ while( *q == '/' ) ++q;
+ return ( !*p && !*q );
+ }
+
+} // end namespace
+
+
+bool block_is_zero( const uint8_t * const buf, const int size )
+ {
+ for( int i = 0; i < size; ++i ) if( buf[i] != 0 ) return false;
+ return true;
+ }
+
+
+bool format_member_name( const Extended & extended, const Tar_header header,
+ Resizable_buffer & rbuf, const bool long_format )
+ {
+ if( long_format )
+ {
+ format_mode_string( header, rbuf() );
+ const int group_string_len =
+ format_user_group_string( header, rbuf() + mode_string_size );
+ int offset = mode_string_size + group_string_len;
+ const time_t mtime = parse_octal( header + mtime_o, mtime_l ); // 33 bits
+ struct tm tms;
+ const struct tm * tm = localtime_r( &mtime, &tms );
+ if( !tm )
+ { time_t z = 0; tm = localtime_r( &z, &tms ); if( !tm ) tm = &tms; }
+ const Typeflag typeflag = (Typeflag)header[typeflag_o];
+ const bool islink = ( typeflag == tf_link || typeflag == tf_symlink );
+ const char * const link_string = !islink ? "" :
+ ( ( typeflag == tf_link ) ? " link to " : " -> " );
+ if( typeflag == tf_chardev || typeflag == tf_blockdev )
+ offset += snprintf( rbuf() + offset, rbuf.size() - offset, " %5u,%u",
+ (unsigned)parse_octal( header + devmajor_o, devmajor_l ),
+ (unsigned)parse_octal( header + devminor_o, devminor_l ) );
+ else
+ offset += snprintf( rbuf() + offset, rbuf.size() - offset, " %9llu",
+ extended.file_size() );
+ for( int i = 0; i < 2; ++i )
+ {
+ const int len = snprintf( rbuf() + offset, rbuf.size() - offset,
+ " %4d-%02u-%02u %02u:%02u %s%s%s\n",
+ 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;
+ if( !rbuf.resize( len + offset + 1 ) ) return false;
+ }
+ }
+ else
+ {
+ if( rbuf.size() < extended.path().size() + 2 &&
+ !rbuf.resize( extended.path().size() + 2 ) ) return false;
+ snprintf( rbuf(), rbuf.size(), "%s\n", extended.path().c_str() );
+ }
+ return true;
+ }
+
+
+bool show_member_name( const Extended & extended, const Tar_header header,
+ const int vlevel, Resizable_buffer & rbuf )
+ {
+ if( verbosity >= vlevel )
+ {
+ if( !format_member_name( extended, header, rbuf, verbosity > vlevel ) )
+ { show_error( mem_msg ); return false; }
+ std::fputs( rbuf(), stdout );
+ std::fflush( stdout );
+ }
+ return true;
+ }
+
+
+bool check_skip_filename( const Arg_parser & parser,
+ std::vector< char > & name_pending,
+ const char * const filename, const int filenames )
+ {
+ if( Exclude::excluded( filename ) ) return true; // skip excluded files
+ bool skip = filenames > 0;
+ if( skip )
+ for( int i = 0; i < parser.arguments(); ++i )
+ if( !parser.code( i ) && parser.argument( i ).size() )
+ {
+ const char * const name =
+ 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; }
+ }
+ return skip;
+ }