diff options
Diffstat (limited to '')
-rw-r--r-- | common_decode.cc | 200 |
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; + } |