diff options
Diffstat (limited to 'main.cc')
-rw-r--r-- | main.cc | 107 |
1 files changed, 52 insertions, 55 deletions
@@ -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 @@ -16,7 +16,7 @@ */ /* Exit status: 0 for a normal exit, 1 for environmental problems - (file not found, files differ, invalid command line options, I/O errors, + (file not found, files differ, invalid command-line options, I/O errors, etc), 2 to indicate a corrupt or invalid input file, 3 for an internal consistency error (e.g., bug) which caused tarlz to panic. */ @@ -27,10 +27,9 @@ #include <cerrno> #include <cstdarg> #include <cstdio> -#include <cstdlib> #include <ctime> #include <fcntl.h> -#include <pthread.h> +#include <pthread.h> // for pthread_t #include <stdint.h> // for lzlib.h #include <unistd.h> #include <sys/stat.h> @@ -57,7 +56,7 @@ const char * const program_name = "tarlz"; namespace { -const char * const program_year = "2022"; +const char * const program_year = "2024"; const char * invocation_name = program_name; // default value @@ -66,12 +65,12 @@ void show_help( const long num_online ) std::printf( "Tarlz is a massively parallel (multi-threaded) combined implementation of\n" "the tar archiver and the lzip compressor. Tarlz uses the compression library\n" "lzlib.\n" - "\nTarlz creates, lists, and extracts archives in a simplified and safer\n" - "variant of the POSIX pax format compressed in lzip format, keeping the\n" - "alignment between tar members and lzip members. The resulting multimember\n" - "tar.lz archive is fully backward compatible with standard tar tools like GNU\n" - "tar, which treat it like any other tar.lz archive. Tarlz can append files to\n" - "the end of such compressed archives.\n" + "\nTarlz creates tar archives using a simplified and safer variant of the POSIX\n" + "pax format compressed in lzip format, keeping the alignment between tar\n" + "members and lzip members. The resulting multimember tar.lz archive is\n" + "backward compatible with standard tar tools like GNU tar, which treat it\n" + "like any other tar.lz archive. Tarlz can append files to the end of such\n" + "compressed archives.\n" "\nKeeping the alignment between tar members and lzip members has two\n" "advantages. It adds an indexed lzip layer on top of the tar archive, making\n" "it possible to decode the archive safely in parallel. It also minimizes the\n" @@ -101,7 +100,7 @@ void show_help( const long num_online ) " -f, --file=<archive> use archive file <archive>\n" " -h, --dereference follow symlinks; archive the files they point to\n" " -n, --threads=<n> set number of (de)compression threads [%ld]\n" - " -o, --output=<file> compress to <file>\n" + " -o, --output=<file> compress to <file> ('-' for stdout)\n" " -p, --preserve-permissions don't subtract the umask on extraction\n" " -q, --quiet suppress all messages\n" " -v, --verbose verbosely list files processed\n" @@ -117,6 +116,7 @@ void show_help( const long num_online ) " --group=<group> use <group> name/ID for files added to archive\n" " --exclude=<pattern> exclude files matching a shell pattern\n" " --ignore-ids ignore differences in owner and group IDs\n" + " --ignore-metadata compare only file size and file content\n" " --ignore-overflow ignore mtime overflow differences on 32-bit\n" " --keep-damaged don't delete partially extracted files\n" " --missing-crc exit with error status if missing extended CRC\n" @@ -132,7 +132,7 @@ void show_help( const long num_online ) std::printf( "\nIf no archive is specified, tarlz tries to read it from standard input or\n" "write it to standard output.\n" "\nExit status: 0 for a normal exit, 1 for environmental problems\n" - "(file not found, files differ, invalid command line options, I/O errors,\n" + "(file not found, files differ, invalid command-line options, I/O errors,\n" "etc), 2 to indicate a corrupt or invalid input file, 3 for an internal\n" "consistency error (e.g., bug) which caused tarlz to panic.\n" "\nReport bugs to lzip-bug@nongnu.org\n" @@ -209,12 +209,12 @@ int check_lib() } -// separate numbers of 6 or more digits in groups of 3 digits using '_' +// separate numbers of 5 or more digits in groups of 3 digits using '_' const char * format_num3( long long num ) { - const char * const si_prefix = "kMGTPEZY"; - const char * const binary_prefix = "KMGTPEZY"; - enum { buffers = 8, bufsize = 4 * sizeof num }; + enum { buffers = 8, bufsize = 4 * sizeof num, n = 10 }; + const char * const si_prefix = "kMGTPEZYRQ"; + const char * const binary_prefix = "KMGTPEZYRQ"; static char buffer[buffers][bufsize]; // circle of static buffers for printf static int current = 0; @@ -222,20 +222,23 @@ const char * format_num3( long long num ) char * p = buf + bufsize - 1; // fill the buffer backwards *p = 0; // terminator const bool negative = num < 0; - char prefix = 0; // try binary first, then si - for( int i = 0; i < 8 && num != 0 && ( num / 1024 ) * 1024 == num; ++i ) - { num /= 1024; prefix = binary_prefix[i]; } - if( prefix ) *(--p) = 'i'; - else - for( int i = 0; i < 8 && num != 0 && ( num / 1000 ) * 1000 == num; ++i ) - { num /= 1000; prefix = si_prefix[i]; } - if( prefix ) *(--p) = prefix; - const bool split = num >= 100000 || num <= -100000; + if( num > 1024 || num < -1024 ) + { + char prefix = 0; // try binary first, then si + for( int i = 0; i < n && num != 0 && num % 1024 == 0; ++i ) + { num /= 1024; prefix = binary_prefix[i]; } + if( prefix ) *(--p) = 'i'; + else + for( int i = 0; i < n && num != 0 && num % 1000 == 0; ++i ) + { num /= 1000; prefix = si_prefix[i]; } + if( prefix ) *(--p) = prefix; + } + const bool split = num >= 10000 || num <= -10000; for( int i = 0; ; ) { - long long onum = num; num /= 10; - *(--p) = llabs( onum - 10 * num ) + '0'; if( num == 0 ) break; + const long long onum = num; num /= 10; + *(--p) = llabs( onum - ( 10 * num ) ) + '0'; if( num == 0 ) break; if( split && ++i >= 3 ) { i = 0; *(--p) = '_'; } } if( negative ) *(--p) = '-'; @@ -252,6 +255,7 @@ void show_option_error( const char * const arg, const char * const msg, } +// Recognized formats: <num>k, <num>Ki, <num>[MGTPEZYRQ][i] long long getnum( const char * const arg, const char * const option_name, const long long llimit = LLONG_MIN, const long long ulimit = LLONG_MAX ) @@ -269,6 +273,8 @@ long long getnum( const char * const arg, const char * const option_name, int exponent = 0; // 0 = bad multiplier switch( tail[0] ) { + case 'Q': exponent = 10; break; + case 'R': exponent = 9; break; case 'Y': exponent = 8; break; case 'Z': exponent = 7; break; case 'E': exponent = 6; break; @@ -327,7 +333,7 @@ void set_mode( Program_mode & program_mode, const Program_mode new_mode ) // parse time as 'long long' even if time_t is 32-bit long long parse_mtime( const char * arg, const char * const pn ) { - if( *arg == '@' ) return getnum( arg + 1, pn ); + if( *arg == '@' ) return getnum( arg + 1, pn ); // seconds since the epoch else if( *arg == '.' || *arg == '/' ) { struct stat st; @@ -389,6 +395,10 @@ int hstat( const char * const filename, struct stat * const st, { return dereference ? stat( filename, st ) : lstat( filename, st ); } +bool nonempty_arg( const Arg_parser & parser, const int i ) + { return ( parser.code( i ) == 0 && !parser.argument( i ).empty() ); } + + int open_instream( const std::string & name ) { const int infd = open( name.c_str(), O_RDONLY | O_BINARY ); @@ -422,20 +432,6 @@ int open_outstream( const std::string & name, const bool create, } -/* This can be called from any thread, main thread or sub-threads alike, - since they all call common helper functions that call exit_fail_mt() - in case of an error. -*/ -void exit_fail_mt( const int retval ) - { - // calling 'exit' more than once results in undefined behavior - static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - - pthread_mutex_lock( &mutex ); // ignore errors to avoid loop - std::exit( retval ); - } - - void show_error( const char * const msg, const int errcode, const bool help ) { if( verbosity < 0 ) return; @@ -531,8 +527,8 @@ int main( const int argc, const char * const argv[] ) if( argc > 0 ) invocation_name = argv[0]; enum { opt_ano = 256, opt_aso, opt_bso, opt_chk, opt_crc, opt_dbg, opt_del, - opt_dso, opt_exc, opt_grp, opt_hlp, opt_id, opt_kd, opt_mti, opt_nso, - opt_ofl, opt_out, opt_own, opt_per, opt_sol, opt_un, opt_wn }; + opt_dso, opt_exc, opt_grp, opt_hlp, opt_iid, opt_imd, opt_kd, opt_mti, + opt_nso, opt_ofl, opt_out, opt_own, opt_per, opt_sol, opt_un, opt_wn }; const Arg_parser::Option options[] = { { '0', 0, Arg_parser::no }, @@ -573,7 +569,8 @@ int main( const int argc, const char * const argv[] ) { opt_exc, "exclude", Arg_parser::yes }, { opt_grp, "group", Arg_parser::yes }, { opt_hlp, "help", Arg_parser::no }, - { opt_id, "ignore-ids", Arg_parser::no }, + { opt_iid, "ignore-ids", Arg_parser::no }, + { opt_imd, "ignore-metadata", Arg_parser::no }, { opt_kd, "keep-damaged", Arg_parser::no }, { opt_crc, "missing-crc", Arg_parser::no }, { opt_mti, "mtime", Arg_parser::yes }, @@ -617,7 +614,7 @@ int main( const int argc, const char * const argv[] ) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - cl_opts.level = code - '0'; break; + cl_opts.set_level( code - '0' ); break; case 'A': set_mode( cl_opts.program_mode, m_concatenate ); break; case 'B': cl_opts.data_size = getnum( arg, pn, min_data_size, max_data_size ); break; @@ -649,7 +646,8 @@ int main( const int argc, const char * const argv[] ) case opt_exc: Exclude::add_pattern( sarg ); break; case opt_grp: cl_opts.gid = parse_group( arg, pn ); break; case opt_hlp: show_help( num_online ); return 0; - case opt_id: cl_opts.ignore_ids = true; break; + case opt_iid: cl_opts.ignore_ids = true; break; + case opt_imd: cl_opts.ignore_metadata = true; break; case opt_kd: cl_opts.keep_damaged = true; break; case opt_mti: cl_opts.mtime = parse_mtime( arg, pn ); cl_opts.mtime_set = true; break; @@ -659,9 +657,9 @@ int main( const int argc, const char * const argv[] ) case opt_own: cl_opts.uid = parse_owner( arg, pn ); break; case opt_per: cl_opts.permissive = true; break; case opt_sol: cl_opts.solidity = solid; break; - case opt_un: cl_opts.level = -1; break; + case opt_un: cl_opts.set_level( -1 ); break; case opt_wn: cl_opts.warn_newer = true; break; - default : internal_error( "uncaught option" ); + default: internal_error( "uncaught option." ); } } // end process options @@ -679,8 +677,7 @@ int main( const int argc, const char * const argv[] ) program_name, f_pn, z_pn ); return 1; } - if( cl_opts.program_mode == m_compress && - ( cl_opts.level < 0 || cl_opts.level > 9 ) ) + if( cl_opts.program_mode == m_compress && cl_opts.uncompressed() ) { if( verbosity >= 0 ) std::fprintf( stderr, "%s: Option '--uncompressed' can't be used with '%s'.\n", @@ -700,7 +697,7 @@ int main( const int argc, const char * const argv[] ) setmode( STDOUT_FILENO, O_BINARY ); #endif - if( cl_opts.data_size <= 0 && cl_opts.level >= 0 && cl_opts.level <= 9 ) + if( cl_opts.data_size <= 0 && !cl_opts.uncompressed() ) { if( cl_opts.level == 0 ) cl_opts.data_size = 1 << 20; else cl_opts.data_size = 2 * option_mapping[cl_opts.level].dictionary_size; @@ -715,9 +712,9 @@ int main( const int argc, const char * const argv[] ) case m_create: return encode( cl_opts ); case m_compress: return compress( cl_opts ); case m_concatenate: return concatenate( cl_opts ); - case m_delete: return delete_members( cl_opts ); + case m_delete: tzset(); return delete_members( cl_opts ); case m_diff: case m_extract: - case m_list: return decode( cl_opts ); + case m_list: tzset(); return decode( cl_opts ); } } |