1
0
Fork 0

Adding upstream version 0.3.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-24 03:25:20 +01:00
parent e748b84dff
commit e1ed3a8d42
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
11 changed files with 455 additions and 814 deletions

445
main.cc
View file

@ -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;