diff options
Diffstat (limited to 'main.cc')
-rw-r--r-- | main.cc | 445 |
1 files changed, 51 insertions, 394 deletions
@@ -25,20 +25,17 @@ #define _FILE_OFFSET_BITS 64 #include <algorithm> -#include <cassert> #include <cerrno> #include <climits> #include <csignal> -#include <cstdarg> -#include <cstddef> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <vector> #include <fcntl.h> -#include <stdint.h> #include <pthread.h> +#include <stdint.h> #include <unistd.h> #include <utime.h> #include <sys/stat.h> @@ -49,7 +46,6 @@ #endif #include "arg_parser.h" -#include "main.h" #include "plzip.h" #ifndef LLONG_MAX @@ -72,6 +68,12 @@ const char * const Program_name = "Plzip"; const char * const program_name = "plzip"; const char * const program_year = "2010"; +#ifdef O_BINARY +const int o_binary = O_BINARY; +#else +const int o_binary = 0; +#endif + struct { const char * from; const char * to; } const known_extensions[] = { { ".lz", "" }, { ".tlz", ".tar" }, @@ -88,6 +90,8 @@ enum Mode { m_compress = 0, m_decompress, m_test }; std::string output_filename; int outhandle = -1; bool delete_output_on_interrupt = false; +pthread_t main_thread; +pid_t main_thread_pid; class Pretty_print { @@ -132,6 +136,7 @@ void show_help() throw() std::printf( " -h, --help display this help and exit\n" ); std::printf( " -V, --version output version information and exit\n" ); // std::printf( " -b, --member-size=<n> set member size limit in bytes\n" ); + std::printf( " -B, --data-size=<n> set input data block size in bytes\n" ); std::printf( " -c, --stdout send output to standard output\n" ); std::printf( " -d, --decompress decompress\n" ); std::printf( " -f, --force overwrite existing output files\n" ); @@ -267,7 +272,7 @@ int open_instream( const std::string & name, struct stat * in_statsp, } else { - inhandle = open( name.c_str(), O_RDONLY ); + inhandle = open( name.c_str(), O_RDONLY | o_binary ); if( inhandle < 0 ) { if( verbosity >= 0 ) @@ -324,9 +329,11 @@ void set_d_outname( const std::string & name, const int i ) throw() bool open_outstream( const bool force ) throw() { if( force ) - outhandle = open( output_filename.c_str(), O_CREAT | O_TRUNC | O_WRONLY, + outhandle = open( output_filename.c_str(), + O_CREAT | O_TRUNC | O_WRONLY | o_binary, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); - else outhandle = open( output_filename.c_str(), O_CREAT | O_EXCL | O_WRONLY, + else outhandle = open( output_filename.c_str(), + O_CREAT | O_EXCL | O_WRONLY | o_binary, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); if( outhandle < 0 ) { @@ -412,14 +419,13 @@ int do_decompress( LZ_Decoder * const decoder, const int inhandle, const int in_buffer_size = 65536, out_buffer_size = 8 * in_buffer_size; uint8_t in_buffer[in_buffer_size], out_buffer[out_buffer_size]; - if( verbosity >= 1 ) pp(); while( true ) { int in_size = std::min( LZ_decompress_write_size( decoder ), in_buffer_size ); if( in_size > 0 ) { const int max_in_size = in_size; - in_size = readblock( inhandle, (char *)in_buffer, max_in_size ); + in_size = readblock( inhandle, in_buffer, max_in_size ); if( in_size != max_in_size && errno ) { pp(); show_error( "read error", errno ); return 1; } if( in_size == 0 ) LZ_decompress_finish( decoder ); @@ -458,7 +464,7 @@ int do_decompress( LZ_Decoder * const decoder, const int inhandle, } else if( out_size > 0 && outhandle >= 0 ) { - const int wr = writeblock( outhandle, (char *)out_buffer, out_size ); + const int wr = writeblock( outhandle, out_buffer, out_size ); if( wr != out_size ) { pp(); show_error( "write error", errno ); return 1; } } @@ -490,9 +496,12 @@ int decompress( const int inhandle, const Pretty_print & pp, } -extern "C" void signal_handler( int ) throw() +extern "C" void signal_handler( int sig ) throw() { - show_error( "Control-C or similar caught, quitting." ); + if( !pthread_equal( pthread_self(), main_thread ) ) + kill( main_thread_pid, sig ); + if( sig != SIGUSR1 ) + show_error( "Control-C or similar caught, quitting." ); cleanup_and_fail( 1 ); } @@ -502,6 +511,7 @@ void set_signals() throw() signal( SIGHUP, signal_handler ); signal( SIGINT, signal_handler ); signal( SIGTERM, signal_handler ); + signal( SIGUSR1, signal_handler ); } } // end namespace @@ -551,372 +561,16 @@ void internal_error( const char * msg ) } -/* Private stuff needed by fatal(). */ -static pthread_t main_thread; - -static pid_t pid; - - -/* Public utility variables and functions. */ - -/* - This can be called from any thread, main thread or sub-threads alike, since - they all call common helper functions that call fatal() in case of an error. -*/ -void fatal() -{ - if( pthread_equal(pthread_self(), main_thread) ) - cleanup_and_fail( 1 ); - else - { - if( 0 == kill(pid, SIGUSR1) ) - pthread_exit(0); - } - _exit( 1 ); -} - - -void -fail(const char *fmt, int err, ...) -{ - va_list args; - - /* Locking stderr should also protect strerror(). */ - flockfile(stderr); - (void)fprintf(stderr, "%s: ", program_name); - - va_start(args, err); - (void)vfprintf(stderr, fmt, args); - va_end(args); - - (void)fprintf(stderr, ": %s\n", strerror(err)); - funlockfile(stderr); - /* Stream stderr is never fully buffered originally. */ - fatal(); -} - - -void -xinit(Cond *cond) -{ - pthread_mutexattr_t attr; - - int ret = pthread_mutexattr_init(&attr); - if( ret != 0 ) { - fail("pthread_mutexattr_init()", ret); - } - - ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); - if( ret != 0 ) { - fail("pthread_mutexattr_settype()", ret); - } - - ret = pthread_mutex_init(&cond->lock, &attr); - if( ret != 0 ) { - fail("pthread_mutex_init()", ret); - } - - ret = pthread_mutexattr_destroy(&attr); - if( ret != 0 ) { - fail("pthread_mutexattr_destroy()", ret); - } - - ret = pthread_cond_init(&cond->cond, 0); - if( ret != 0 ) { - fail("pthread_cond_init()", ret); - } - - cond->ccount = 0; - cond->wcount = 0; -} - - -void -xdestroy(Cond *cond) -{ - int ret = pthread_cond_destroy(&cond->cond); - if( ret != 0 ) { - fail("pthread_cond_destroy()", ret); - } - - ret = pthread_mutex_destroy(&cond->lock); - if( ret != 0 ) { - fail("pthread_mutex_destroy()", ret); - } -} - - -void -xlock(Cond *cond) -{ - int ret = pthread_mutex_lock(&cond->lock); - if( ret != 0 ) { - fail("pthread_mutex_lock()", ret); - } -} - - -void -xlock_pred(Cond *cond) -{ - xlock(cond); - ++cond->ccount; -} - - -void -xunlock(Cond *cond) -{ - int ret = pthread_mutex_unlock(&cond->lock); - if( ret != 0 ) { - fail("pthread_mutex_unlock()", ret); - } -} - - -void -xwait(Cond *cond) -{ - ++cond->wcount; - int ret = pthread_cond_wait(&cond->cond, &cond->lock); - if( ret != 0 ) { - fail("pthread_cond_wait()", ret); - } - ++cond->ccount; -} - - -void -xsignal(Cond *cond) -{ - int ret = pthread_cond_signal(&cond->cond); - if( ret != 0 ) { - fail("pthread_cond_signal()", ret); - } -} - - -void -xbroadcast(Cond *cond) -{ - int ret = pthread_cond_broadcast(&cond->cond); - if( ret != 0 ) { - fail("pthread_cond_broadcast()", ret); - } -} - - -void -xcreate(pthread_t *thread, void *(*routine)(void *), void *arg) -{ - int ret = pthread_create(thread, 0, routine, arg); - if( ret != 0 ) { - fail("pthread_create()", ret); - } -} - - -void -xjoin(pthread_t thread) -{ - int ret = pthread_join(thread, 0); - if( ret != 0 ) { - fail("pthread_join()", ret); - } -} - - -void -xraise(int sig) -{ - if( -1 == kill(pid, sig) ) { - fail("kill()", errno); - } -} - - -/* Private stuff part 2. */ - - -static void -xsigemptyset(sigset_t *set) -{ - if( -1 == sigemptyset(set) ) { - fail("sigemptyset()", errno); - } -} - - -static void -xsigaddset(sigset_t *set, int signo) -{ - if( -1 == sigaddset(set, signo) ) { - fail("sigaddset()", errno); - } -} - - -static void -xsigmask(int how, const sigset_t *set, sigset_t *oset) -{ - int ret = pthread_sigmask(how, set, oset); - if( ret != 0 ) { - fail("pthread_sigmask()", ret); - } -} - - -static void -xsigaction(int sig, void (*handler)(int)) -{ - struct sigaction act; - - act.sa_handler = handler; - xsigemptyset(&act.sa_mask); - act.sa_flags = 0; - - if( -1 == sigaction(sig, &act, 0) ) { - fail("sigaction()", errno); - } -} - - -enum Caught_sig { CS_INT = 1, CS_TERM, CS_USR1, CS_USR2 }; - -static volatile sig_atomic_t caught_sig; - - -extern "C" void sighandler( int sig ) - { - /* sig_atomic_t is nowhere required to be able to hold signal values. */ - switch( sig ) - { - case SIGINT : caught_sig = CS_INT; break; - case SIGTERM: caught_sig = CS_TERM; break; - case SIGUSR1: caught_sig = CS_USR1; break; - case SIGUSR2: caught_sig = CS_USR2; break; - default: internal_error( "caught signal not in set" ); - } - } - - -static void compress( const lzma_options & encoder_options, const int num_workers, - int debug_level, int num_slots, int infd, int outfd, - const Pretty_print & pp, const sigset_t *unblocked ) - { - /* - We could wait for signals with either sigwait() or sigsuspend(). SUSv2 - states about sigwait() that its effect on signal actions is unspecified. - SUSv3 still claims the same. - - The SUSv2 description of sigsuspend() talks about both the thread and the - whole process being suspended until a signal arrives, although thread - suspension seems much more likely from the wording. They note that they - filed a clarification request for this. SUSv3 cleans this up and chooses - thread suspension which was more logical anyway. - - I favor sigsuspend() because I need to re-raise SIGTERM and SIGINT, and - unspecified action behavior with sigwait() seems messy. - - 13-OCT-2009 lacos - */ - - if( verbosity >= 1 ) pp(); - - Muxer_arg muxer_arg; - muxer_arg.dictionary_size = encoder_options.dictionary_size; - muxer_arg.match_len_limit = encoder_options.match_len_limit; - muxer_arg.num_workers = num_workers; - muxer_arg.num_slots = num_slots; - muxer_arg.debug_level = debug_level; - muxer_arg.infd = infd; - muxer_arg.outfd = outfd; - - pthread_t muxer_thread; - xcreate(&muxer_thread, muxer, &muxer_arg); - - /* Unblock signals, wait for them, then block them again. */ - { - int ret = sigsuspend(unblocked); - assert(-1 == ret && EINTR == errno); - } - - switch( caught_sig ) { - case CS_INT: - case CS_TERM: // FIXME remove output file - { - int sig; - sigset_t mask; - - sig = (CS_INT == caught_sig) ? SIGINT : SIGTERM; - /* - We might have inherited a SIG_IGN from the parent, but that would - make no sense here. 24-OCT-2009 lacos - */ - xsigaction(sig, SIG_DFL); - xraise(sig); - - xsigemptyset(&mask); - xsigaddset(&mask, sig); - xsigmask(SIG_UNBLOCK, &mask, 0); - } - /* - We shouldn't reach this point, but if we do for some reason, fall - through. - */ - - case CS_USR1: - /* Error from a non-main thread via fatal(). */ - fatal(); - - case CS_USR2: - /* Muxer thread joined other sub-threads and finished successfully. */ - break; - - default: - assert(0); - } - - xjoin(muxer_thread); - } - - -static void -sigs_mod(int block_n_catch, sigset_t *oset) - { - void (*handler)(int); - - if( block_n_catch ) { - sigset_t mask; - - xsigemptyset(&mask); - xsigaddset(&mask, SIGINT); - xsigaddset(&mask, SIGTERM); - xsigaddset(&mask, SIGUSR1); - xsigaddset(&mask, SIGUSR2); - xsigmask(SIG_BLOCK, &mask, oset); - - handler = sighandler; - } - else { - handler = SIG_DFL; - } - - xsigaction(SIGINT, handler); - xsigaction(SIGTERM, handler); - xsigaction(SIGUSR1, handler); - xsigaction(SIGUSR2, handler); - - if( !block_n_catch ) { - xsigmask(SIG_SETMASK, oset, 0); - } - } +// This can be called from any thread, main thread or sub-threads alike, since +// they all call common helper functions that call fatal() in case of an error. +// +void fatal() { signal_handler( SIGUSR1 ); } // Returns the number of bytes really read. // If (returned value < size) and (errno == 0), means EOF was reached. // -int readblock( const int fd, char * buf, const int size ) throw() +int readblock( const int fd, uint8_t * buf, const int size ) throw() { int rest = size; errno = 0; @@ -935,7 +589,7 @@ int readblock( const int fd, char * buf, const int size ) throw() // Returns the number of bytes really written. // If (returned value < size), it is always an error. // -int writeblock( const int fd, const char * buf, const int size ) throw() +int writeblock( const int fd, const uint8_t * buf, const int size ) throw() { int rest = size; errno = 0; @@ -966,6 +620,7 @@ int main( const int argc, const char * argv[] ) { 1 << 24, 163 }, // -8 { 1 << 25, 273 } }; // -9 lzma_options encoder_options = option_mapping[5]; // default = "-6" + int data_size = 0; int debug_level = 0; int inhandle = -1; int num_workers = 0; // Start this many worker threads @@ -977,22 +632,18 @@ int main( const int argc, const char * argv[] ) std::string default_output_filename; std::vector< std::string > filenames; invocation_name = argv[0]; + main_thread = pthread_self(); + main_thread_pid = getpid(); if( LZ_version()[0] != LZ_version_string[0] ) internal_error( "bad library version" ); - main_thread = pthread_self(); - pid = getpid(); - - xsigaction(SIGPIPE, SIG_IGN); - xsigaction(SIGXFSZ, SIG_IGN); - const int slots_per_worker = 2; long max_workers = sysconf( _SC_THREAD_THREADS_MAX ); if( max_workers < 1 || max_workers > INT_MAX / slots_per_worker ) max_workers = INT_MAX / slots_per_worker; - if( max_workers > INT_MAX / (int)sizeof( pthread_t ) ) - max_workers = INT_MAX / sizeof( pthread_t ); + if( max_workers > INT_MAX / (int)sizeof (pthread_t) ) + max_workers = INT_MAX / sizeof (pthread_t); const Arg_parser::Option options[] = { @@ -1006,6 +657,7 @@ int main( const int argc, const char * argv[] ) { '8', 0, Arg_parser::no }, { '9', "best", Arg_parser::no }, { 'b', "member-size", Arg_parser::yes }, + { 'B', "data-size", Arg_parser::yes }, { 'c', "stdout", Arg_parser::no }, { 'd', "decompress", Arg_parser::no }, { 'D', "debug", Arg_parser::yes }, @@ -1040,6 +692,8 @@ int main( const int argc, const char * argv[] ) case '7': case '8': case '9': encoder_options = option_mapping[code-'1']; break; case 'b': break; + case 'B': data_size = getnum( arg, 0, 100000, + 2 * LZ_max_dictionary_size() ); break; case 'c': to_stdout = true; break; case 'd': program_mode = m_decompress; break; case 'D': debug_level = getnum( arg, 0, 0, 3 ); @@ -1048,8 +702,8 @@ int main( const int argc, const char * argv[] ) case 'h': show_help(); return 0; case 'k': keep_input_files = true; break; case 'm': encoder_options.match_len_limit = - getnum( arg, 0, LZ_min_match_len_limit(), - LZ_max_match_len_limit() ); break; + getnum( arg, 0, LZ_min_match_len_limit(), + LZ_max_match_len_limit() ); break; case 'o': default_output_filename = arg; break; case 'n': num_workers = getnum( arg, 0, 1, max_workers ); break; case 'q': verbosity = -1; break; @@ -1063,13 +717,16 @@ int main( const int argc, const char * argv[] ) } } + if( data_size <= 0 ) + data_size = 2 * std::max( 65536, encoder_options.dictionary_size ); + if( num_workers <= 0 ) { long num_online = sysconf( _SC_NPROCESSORS_ONLN ); - if( num_online <= 0 ) num_online = 2; + if( num_online <= 0 ) num_online = 1; num_workers = std::min( num_online, max_workers ); } - const int num_slots = num_workers * slots_per_worker; + const int num_slots = std::max( 1, ( num_workers * slots_per_worker ) - 1 ); bool filenames_given = false; for( ; argind < parser.arguments(); ++argind ) @@ -1079,7 +736,9 @@ int main( const int argc, const char * argv[] ) } if( filenames.empty() ) filenames.push_back("-"); - if( filenames_given && program_mode != m_compress ) set_signals(); + if( !to_stdout && program_mode != m_test && + ( filenames_given || default_output_filename.size() ) ) + set_signals(); Pretty_print pp( filenames ); if( program_mode == m_test ) @@ -1144,14 +803,12 @@ int main( const int argc, const char * argv[] ) delete_output_on_interrupt = true; const struct stat * const in_statsp = input_filename.size() ? &in_stats : 0; pp.set_name( input_filename ); + if( verbosity >= 1 ) pp(); int tmp = 0; if( program_mode == m_compress ) - { - sigset_t unblocked; - sigs_mod(1, &unblocked); - compress( encoder_options, num_workers, debug_level, num_slots, inhandle, outhandle, pp, &unblocked ); - sigs_mod(0, &unblocked); - } + tmp = compress( data_size, encoder_options.dictionary_size, + encoder_options.match_len_limit, num_workers, + num_slots, inhandle, outhandle, debug_level ); else tmp = decompress( inhandle, pp, program_mode == m_test ); if( tmp > retval ) retval = tmp; |