diff options
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 183 |
1 files changed, 93 insertions, 90 deletions
@@ -26,7 +26,7 @@ #include <ctype.h> #include <errno.h> #include <fcntl.h> -#include <limits.h> /* SSIZE_MAX */ +#include <limits.h> /* CHAR_BIT, SSIZE_MAX */ #include <signal.h> #include <stdbool.h> #include <stdint.h> /* SIZE_MAX */ @@ -39,8 +39,10 @@ #if defined __MSVCRT__ || defined __OS2__ || defined __DJGPP__ #include <io.h> #if defined __MSVCRT__ +#include <direct.h> #define fchmod(x,y) 0 #define fchown(x,y,z) 0 +#define mkdir(name,mode) _mkdir(name) #define SIGHUP SIGTERM #define S_ISSOCK(x) 0 #ifndef S_IRGRP @@ -84,7 +86,7 @@ static const struct { const char * from; const char * to; } known_extensions[] = { ".tlz", ".tar" }, { 0, 0 } }; -enum Mode { m_compress, m_decompress, m_list, m_test }; +typedef enum Mode { m_compress, m_decompress, m_list, m_test } Mode; /* Variables used in signal handler context. They are not declared volatile because the handler never returns. */ @@ -97,19 +99,18 @@ static void show_help( void ) { printf( "Lunzip is a decompressor for the lzip format written in C. Its small size\n" "makes it well suited for embedded devices or software installers that need\n" - "to decompress files but don't need compression capabilities. Lunzip is\n" - "compatible with lzip 1.4 or newer.\n" + "to decompress files but don't need compression capabilities.\n" "\nLzip is a lossless data compressor with a user interface similar to the one\n" - "of gzip or bzip2. Lzip uses a simplified form of the 'Lempel-Ziv-Markov\n" - "chain-Algorithm' (LZMA) stream format to maximize interoperability. The\n" - "maximum dictionary size is 512 MiB so that any lzip file can be decompressed\n" - "on 32-bit machines. Lzip provides accurate and robust 3-factor integrity\n" - "checking. Lzip can compress about as fast as gzip (lzip -0) or compress most\n" - "files more than bzip2 (lzip -9). Decompression speed is intermediate between\n" - "gzip and bzip2. Lzip is better than gzip and bzip2 from a data recovery\n" - "perspective. Lzip has been designed, written, and tested with great care to\n" - "replace gzip and bzip2 as the standard general-purpose compressed format for\n" - "Unix-like systems.\n" + "of gzip or bzip2. Lzip uses a simplified form of LZMA (Lempel-Ziv-Markov\n" + "chain-Algorithm) designed to achieve complete interoperability between\n" + "implementations. The maximum dictionary size is 512 MiB so that any lzip\n" + "file can be decompressed on 32-bit machines. Lzip provides accurate and\n" + "robust 3-factor integrity checking. 'lzip -0' compresses about as fast as\n" + "gzip, while 'lzip -9' compresses most files more than bzip2. Decompression\n" + "speed is intermediate between gzip and bzip2. Lzip provides better data\n" + "recovery capabilities than gzip and bzip2. Lzip has been designed, written,\n" + "and tested with great care to replace gzip and bzip2 as general-purpose\n" + "compressed format for Unix-like systems.\n" "\nLunzip provides a 'low memory' mode able to decompress any file using as\n" "little memory as 50 kB, irrespective of the dictionary size used to\n" "compress the file. To activate it, specify the size of the output buffer\n" @@ -122,22 +123,20 @@ static void show_help( void ) "the whole dictionary at once.\n" "\nUsage: %s [options] [files]\n", invocation_name ); printf( "\nOptions:\n" - " -h, --help display this help and exit\n" - " -V, --version output version information and exit\n" - " -a, --trailing-error exit with error status if trailing data\n" - " -c, --stdout write to standard output, keep input files\n" - " -d, --decompress decompress (this is the default)\n" - " -f, --force overwrite existing output files\n" - " -k, --keep keep (don't delete) input files\n" - " -l, --list print (un)compressed file sizes\n" - " -o, --output=<file> write to <file>, keep input files\n" - " -q, --quiet suppress all messages\n" - " -t, --test test compressed file integrity\n" - " -u, --buffer-size=<bytes> set output buffer size in bytes\n" - " -v, --verbose be verbose (a 2nd -v gives more)\n" - " --empty-error exit with error status if empty member in file\n" - " --marking-error exit with error status if 1st LZMA byte not 0\n" - " --loose-trailing allow trailing data seeming corrupt header\n" + " -h, --help display this help and exit\n" + " -V, --version output version information and exit\n" + " -a, --trailing-error exit with error status if trailing data\n" + " -c, --stdout write to standard output, keep input files\n" + " -d, --decompress decompress (this is the default)\n" + " -f, --force overwrite existing output files\n" + " -k, --keep keep (don't delete) input files\n" + " -l, --list print (un)compressed file sizes\n" + " -o, --output=<file> write to <file>, keep input files\n" + " -q, --quiet suppress all messages\n" + " -t, --test test compressed file integrity\n" + " -u, --buffer-size=<bytes> set output buffer size in bytes\n" + " -v, --verbose be verbose (a 2nd -v gives more)\n" + " --loose-trailing allow trailing data seeming corrupt header\n" "\nIf no file names are given, or if a file is '-', lunzip decompresses\n" "from standard input to standard output.\n" "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" @@ -189,7 +188,7 @@ struct Pretty_print bool first_post; }; -static void Pp_init( struct Pretty_print * const pp, +static void Pp_init( Pretty_print * const pp, const char * const filenames[], const int num_filenames ) { pp->name = 0; @@ -210,8 +209,10 @@ static void Pp_init( struct Pretty_print * const pp, if( pp->longest_name == 0 ) pp->longest_name = stdin_name_len; } -static void Pp_set_name( struct Pretty_print * const pp, - const char * const filename ) +void Pp_free( Pretty_print * const pp ) + { if( pp->padded_name ) { free( pp->padded_name ); pp->padded_name = 0; } } + +static void Pp_set_name( Pretty_print * const pp, const char * const filename ) { unsigned name_len, padded_name_len, i = 0; @@ -229,10 +230,10 @@ static void Pp_set_name( struct Pretty_print * const pp, pp->first_post = true; } -static void Pp_reset( struct Pretty_print * const pp ) +static void Pp_reset( Pretty_print * const pp ) { if( pp->name && pp->name[0] ) pp->first_post = true; } -void Pp_show_msg( struct Pretty_print * const pp, const char * const msg ) +void Pp_show_msg( Pretty_print * const pp, const char * const msg ) { if( verbosity < 0 ) return; if( pp->first_post ) @@ -262,7 +263,7 @@ const char * format_ds( const unsigned dictionary_size ) const char * p = ""; const char * np = " "; unsigned num = dictionary_size; - bool exact = ( num % factor == 0 ); + bool exact = num % factor == 0; int i; for( i = 0; i < n && ( num > 9999 || ( exact && num >= factor ) ); ++i ) { num /= factor; if( num % factor != 0 ) exact = false; @@ -278,7 +279,7 @@ void show_header( const unsigned dictionary_size ) } -/* separate numbers of 5 or more digits in groups of 3 digits using '_' */ +/* separate numbers of 6 or more digits in groups of 3 digits using '_' */ static const char * format_num3( unsigned long long num ) { enum { buffers = 8, bufsize = 4 * sizeof num, n = 10 }; @@ -290,7 +291,7 @@ static const char * format_num3( unsigned long long num ) char * const buf = buffer[current++]; current %= buffers; char * p = buf + bufsize - 1; /* fill the buffer backwards */ *p = 0; /* terminator */ - if( num > 1024 ) + if( num > 9999 ) { char prefix = 0; /* try binary first, then si */ for( i = 0; i < n && num != 0 && num % 1024 == 0; ++i ) @@ -301,7 +302,7 @@ static const char * format_num3( unsigned long long num ) { num /= 1000; prefix = si_prefix[i]; } if( prefix ) *(--p) = prefix; } - const bool split = num >= 10000; + const bool split = num >= 100000; for( i = 0; ; ) { @@ -336,7 +337,7 @@ static unsigned long getnum( const char * const arg, if( !errno && tail[0] ) { - const unsigned factor = ( tail[1] == 'i' ) ? 1024 : 1000; + const unsigned factor = (tail[1] == 'i') ? 1024 : 1000; int exponent = 0; /* 0 = bad multiplier */ int i; switch( tail[0] ) @@ -386,7 +387,7 @@ static int get_dict_size( const char * const arg, const char * const option_name } -static void set_mode( enum Mode * const program_modep, const enum Mode new_mode ) +static void set_mode( Mode * const program_modep, const Mode new_mode ) { if( *program_modep != m_compress && *program_modep != new_mode ) { @@ -448,9 +449,9 @@ int open_instream( const char * const name, struct stat * const in_statsp, { const int i = fstat( infd, in_statsp ); const mode_t mode = in_statsp->st_mode; - const bool can_read = ( i == 0 && !reg_only && - ( S_ISBLK( mode ) || S_ISCHR( mode ) || - S_ISFIFO( mode ) || S_ISSOCK( mode ) ) ); + const bool can_read = i == 0 && !reg_only && + ( S_ISBLK( mode ) || S_ISCHR( mode ) || + S_ISFIFO( mode ) || S_ISSOCK( mode ) ); if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || one_to_one ) ) ) { if( verbosity >= 0 ) @@ -479,7 +480,7 @@ static bool make_dirs( const char * const name ) while( i < dirsize && name[i] != '/' ) ++i; if( first < i ) { - char partial[i+1]; memcpy( partial, name, i ); partial[i] = 0; + char partial[i+1]; memcpy( partial, name, i ); partial[i] = 0; /* vla */ const mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; struct stat st; if( stat( partial, &st ) == 0 ) @@ -550,7 +551,7 @@ static void signal_handler( int sig ) static bool check_tty_in( const char * const input_filename, const int infd, - const enum Mode program_mode, int * const retval ) + const Mode program_mode, int * const retval ) { if( isatty( infd ) ) /* for example /dev/tty */ { show_file_error( input_filename, @@ -604,7 +605,7 @@ static unsigned char xdigit( const unsigned value ) /* hex digit for 'value' */ static bool show_trailing_data( const uint8_t * const data, const int size, - struct Pretty_print * const pp, const bool all, + Pretty_print * const pp, const bool all, const int ignore_trailing ) /* -1 = show */ { if( verbosity >= 4 || ignore_trailing <= 0 ) @@ -632,16 +633,17 @@ static bool show_trailing_data( const uint8_t * const data, const int size, static int decompress( const unsigned long long cfile_size, const int infd, - const struct Cl_options * const cl_opts, - struct Pretty_print * const pp, - const unsigned buffer_size, const bool testing ) + const Cl_options * const cl_opts, Pretty_print * const pp, + const unsigned buffer_size, + const bool from_stdin, const bool testing ) { unsigned long long partial_file_pos = 0; - struct Range_decoder rdec; + Range_decoder rdec; int retval = 0; bool first_member; if( !Rd_init( &rdec, infd ) ) { show_error( mem_msg, 0, false ); cleanup_and_fail( 1 ); } + bool empty = false, multi = false; for( first_member = true; ; first_member = false ) { @@ -681,14 +683,14 @@ static int decompress( const unsigned long long cfile_size, const int infd, if( verbosity >= 2 || ( verbosity == 1 && first_member ) ) Pp_show_msg( pp, 0 ); - struct LZ_decoder decoder; + LZ_decoder decoder; if( !LZd_init( &decoder, &rdec, buffer_size, dictionary_size, outfd ) ) { Pp_show_msg( pp, "Not enough memory. Try a smaller output buffer size." ); retval = 1; break; } show_dprogress( cfile_size, partial_file_pos, &rdec, pp ); /* init */ - const int result = LZd_decode_member( &decoder, cl_opts, pp ); + const int result = LZd_decode_member( &decoder, pp ); partial_file_pos += Rd_member_position( &rdec ); LZd_free( &decoder ); if( result != 0 ) @@ -700,16 +702,19 @@ static int decompress( const unsigned long long cfile_size, const int infd, "File ends unexpectedly" : "Decoder error", partial_file_pos ); } - else if( result == 5 ) Pp_show_msg( pp, empty_msg ); - else if( result == 6 ) Pp_show_msg( pp, marking_msg ); + else if( result == 5 ) Pp_show_msg( pp, nonzero_msg ); retval = 2; break; } + if( !from_stdin ) { multi = !first_member; + if( LZd_data_position( &decoder ) == 0 ) empty = true; } if( verbosity >= 2 ) { fputs( testing ? "ok\n" : "done\n", stderr ); Pp_reset( pp ); } } Rd_free( &rdec ); if( verbosity == 1 && retval == 0 ) fputs( testing ? "ok\n" : "done\n", stderr ); + if( empty && multi && retval == 0 ) + { show_file_error( pp->name, empty_msg, 0 ); retval = 2; } return retval; } @@ -747,13 +752,13 @@ static void internal_error( const char * const msg ) void show_dprogress( const unsigned long long cfile_size, const unsigned long long partial_size, - const struct Range_decoder * const d, - struct Pretty_print * const p ) + const Range_decoder * const d, + Pretty_print * const p ) { static unsigned long long csize = 0; /* file_size / 100 */ static unsigned long long psize = 0; - static const struct Range_decoder * rdec = 0; - static struct Pretty_print * pp = 0; + static const Range_decoder * rdec = 0; + static Pretty_print * pp = 0; static int counter = 0; static bool enabled = true; @@ -780,41 +785,38 @@ int main( const int argc, const char * const argv[] ) { const char * default_output_filename = ""; unsigned buffer_size = max_dictionary_size; - enum Mode program_mode = m_compress; - int i; - struct Cl_options cl_opts; /* command-line options */ + Mode program_mode = m_compress; + Cl_options cl_opts; /* command-line options */ Cl_options_init( &cl_opts ); bool force = false; bool keep_input_files = false; bool to_stdout = false; if( argc > 0 ) invocation_name = argv[0]; - enum { opt_eer = 256, opt_lt, opt_mer }; - const struct ap_Option options[] = + enum { opt_lt = 256 }; + const ap_Option options[] = { - { 'a', "trailing-error", ap_no }, - { 'c', "stdout", ap_no }, - { 'd', "decompress", ap_no }, - { 'f', "force", ap_no }, - { 'h', "help", ap_no }, - { 'k', "keep", ap_no }, - { 'l', "list", ap_no }, - { 'n', "threads", ap_yes }, - { 'o', "output", ap_yes }, - { 'q', "quiet", ap_no }, - { 't', "test", ap_no }, - { 'u', "buffer-size", ap_yes }, - { 'v', "verbose", ap_no }, - { 'V', "version", ap_no }, - { opt_eer, "empty-error", ap_no }, - { opt_lt, "loose-trailing", ap_no }, - { opt_mer, "marking-error", ap_no }, - { 0, 0, ap_no } }; + { 'a', "trailing-error", ap_no }, + { 'c', "stdout", ap_no }, + { 'd', "decompress", ap_no }, + { 'f', "force", ap_no }, + { 'h', "help", ap_no }, + { 'k', "keep", ap_no }, + { 'l', "list", ap_no }, + { 'n', "threads", ap_yes }, + { 'o', "output", ap_yes }, + { 'q', "quiet", ap_no }, + { 't', "test", ap_no }, + { 'u', "buffer-size", ap_yes }, + { 'v', "verbose", ap_no }, + { 'V', "version", ap_no }, + { opt_lt, "loose-trailing", ap_no }, + { 0, 0, ap_no } }; CRC32_init(); /* static because valgrind complains and memory management in C sucks */ - static struct Arg_parser parser; + static Arg_parser parser; if( !ap_init( &parser, argc, argv, options, 0 ) ) { show_error( mem_msg, 0, false ); return 1; } if( ap_error( &parser ) ) /* bad option */ @@ -836,7 +838,7 @@ int main( const int argc, const char * const argv[] ) case 'h': show_help(); return 0; case 'k': keep_input_files = true; break; case 'l': set_mode( &program_mode, m_list ); break; - case 'n': break; + case 'n': break; /* ignored */ case 'o': if( strcmp( arg, "-" ) == 0 ) to_stdout = true; else { default_output_filename = arg; } break; case 'q': verbosity = -1; break; @@ -844,9 +846,7 @@ int main( const int argc, const char * const argv[] ) case 'u': buffer_size = get_dict_size( arg, pn ); break; case 'v': if( verbosity < 4 ) ++verbosity; break; case 'V': show_version(); return 0; - case opt_eer: cl_opts.ignore_empty = false; break; - case opt_lt: cl_opts.loose_trailing = true; break; - case opt_mer: cl_opts.ignore_marking = false; break; + case opt_lt: cl_opts.loose_trailing = true; break; default: internal_error( "uncaught option." ); } } /* end process options */ @@ -861,6 +861,7 @@ int main( const int argc, const char * const argv[] ) filenames = resize_buffer( filenames, num_filenames * sizeof filenames[0] ); filenames[0] = "-"; + int i; bool filenames_given = false; for( i = 0; argind + i < ap_arguments( &parser ); ++i ) { @@ -900,7 +901,7 @@ int main( const int argc, const char * const argv[] ) if( !to_stdout && program_mode != m_test && ( filenames_given || to_file ) ) set_signals( signal_handler ); - static struct Pretty_print pp; + static Pretty_print pp; Pp_init( &pp, filenames, num_filenames ); int failed_tests = 0; @@ -912,9 +913,10 @@ int main( const int argc, const char * const argv[] ) { const char * input_filename = ""; int infd; + const bool from_stdin = strcmp( filenames[i], "-" ) == 0; Pp_set_name( &pp, filenames[i] ); - if( strcmp( filenames[i], "-" ) == 0 ) + if( from_stdin ) { if( stdin_used ) continue; else stdin_used = true; infd = STDIN_FILENO; @@ -963,7 +965,7 @@ int main( const int argc, const char * const argv[] ) ( input_filename[0] && S_ISREG( in_stats.st_mode ) ) ? ( in_stats.st_size + 99 ) / 100 : 0; int tmp = decompress( cfile_size, infd, &cl_opts, &pp, buffer_size, - program_mode == m_test ); + from_stdin, program_mode == m_test ); if( close( infd ) != 0 ) { show_file_error( pp.name, "Error closing input file", errno ); set_retval( &tmp, 1 ); } @@ -990,6 +992,7 @@ int main( const int argc, const char * const argv[] ) program_name, failed_tests, ( failed_tests == 1 ) ? "file" : "files" ); free( output_filename ); + Pp_free( &pp ); free( filenames ); ap_free( &parser ); return retval; |