1
0
Fork 0

Merging upstream version 1.12~rc1.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-24 06:02:28 +01:00
parent 411f37263d
commit d5110769e8
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
29 changed files with 1120 additions and 662 deletions

148
zcmp.cc
View file

@ -40,7 +40,7 @@
#include "zutils.h"
#ifndef LLONG_MAX
#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL
#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL
#endif
@ -70,27 +70,32 @@ void show_help()
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
" -b, --print-bytes print differing bytes\n"
" -H, --hexadecimal print hexadecimal values instead of octal\n"
" -i, --ignore-initial=<n>[:<n2>] ignore differences in the first <n> bytes\n"
" -l, --list list position, value of all differing bytes\n"
" -M, --format=<list> process only the formats in <list>\n"
" -n, --bytes=<n> compare at most <n> bytes\n"
" -N, --no-rcfile don't read runtime configuration file\n"
" -O, --force-format=[<f1>][,<f2>] force the formats given (bz2,gz,lz,xz,zst)\n"
" -q, --quiet suppress all messages\n"
" -s, --silent (same as --quiet)\n"
" -v, --verbose verbose mode (same as --list)\n"
" -O, --force-format=[<f1>][,<f2>] force one or both input formats\n"
" -q, --quiet, --silent suppress diagnostics written to stderr\n"
" -s, --script suppress messages about file differences\n"
" -v, --verbose verbose mode (opposite of --quiet)\n"
" --bz2=<command> set compressor and options for bzip2 format\n"
" --gz=<command> set compressor and options for gzip format\n"
" --lz=<command> set compressor and options for lzip format\n"
" --xz=<command> set compressor and options for xz format\n"
" --zst=<command> set compressor and options for zstd format\n"
"\nNumbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n"
"Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" );
"\nValid formats for options '-M' and '-O' are 'bz2', 'gz', 'lz', 'xz', 'zst',\n"
"and 'un' for uncompressed.\n"
"\nByte counts given as arguments to options may be expressed in decimal,\n"
"hexadecimal, or octal (using the same syntax as integer constants in C++),\n"
"and may be followed by a multiplier: k = kB = 10^3 = 1000,\n"
"Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc.\n" );
show_help_addr();
}
// separate large numbers >= 100_000 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";
@ -103,20 +108,20 @@ const char * format_num3( long long num )
char * p = buf + bufsize - 1; // fill the buffer backwards
*p = 0; // terminator
const bool negative = num < 0;
if( negative ) num = -num;
char prefix = 0; // try binary first, then si
for( int i = 0; i < 8 && num >= 1024 && num % 1024 == 0; ++i )
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 >= 1000 && num % 1000 == 0; ++i )
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;
const bool split = num >= 10000 || num <= -10000;
for( int i = 0; ; )
{
*(--p) = num % 10 + '0'; num /= 10; if( num == 0 ) break;
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) = '-';
@ -133,12 +138,8 @@ long long getnum( const char * const arg, const char * const option_name,
errno = 0;
long long result = strtoll( arg, &tail, 0 );
if( tail == arg )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Bad or missing numerical argument in "
"option '%s'.\n", program_name, option_name );
std::exit( 2 );
}
{ show_option_error( arg, "Bad or missing numerical argument in",
option_name ); std::exit( 2 ); }
if( result < 0 ) errno = ERANGE;
if( !errno && tail[0] && std::isalpha( tail[0] ) )
@ -163,12 +164,8 @@ long long getnum( const char * const arg, const char * const option_name,
case 'B': if( factor == 1000 && !bsuf ) exponent = 0; break;
}
if( exponent < 0 )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Bad multiplier in numerical argument of "
"option '%s'.\n", program_name, option_name );
std::exit( 2 );
}
{ show_option_error( arg, "Bad multiplier in numerical argument of",
option_name ); std::exit( 2 ); }
for( int i = 0; i < exponent; ++i )
{
if( ulimit / factor >= result ) result *= factor;
@ -179,8 +176,8 @@ long long getnum( const char * const arg, const char * const option_name,
if( errno )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Numerical argument out of limits [%s,%s] "
"in option '%s'.\n", program_name, format_num3( llimit ),
std::fprintf( stderr, "%s: '%s': Value out of limits [%s,%s] in "
"option '%s'.\n", program_name, arg, format_num3( llimit ),
format_num3( ulimit ), option_name );
std::exit( 2 );
}
@ -197,13 +194,7 @@ void parse_ignore_initial( const char * const arg, const char * const pn,
if( *tail == ':' || *tail == ',' )
ignore_initial[1] = getnum( ++tail, pn );
else if( *tail == 0 ) ignore_initial[1] = ignore_initial[0];
else
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Bad separator in argument of option '%s'.\n",
program_name, pn );
std::exit( 2 );
}
else { show_option_error( arg, "Missing colon in", pn ); std::exit( 2 ); }
}
@ -227,8 +218,8 @@ bool skip_ignore_initial( const long long ignore_initial, const int infd )
}
// Put into buf the unsigned char c, making unprintable bytes
// visible by quoting like cat -t does.
/* Put into buf the unsigned char c, making unprintable bytes visible by
quoting like cat -t does. */
void sprintc( char * const buf, unsigned char c )
{
int i = 0;
@ -251,7 +242,7 @@ int block_compare( const uint8_t * const buffer0,
const uint8_t * p0 = buffer0;
const uint8_t * p1 = buffer1;
if( verbosity == 0 )
if( line_numberp )
{
int nl_count = 0;
while( *p0 == *p1 )
@ -265,7 +256,8 @@ int block_compare( const uint8_t * const buffer0,
int cmp( const long long max_size, const int infd[2],
const std::string filenames[2], bool finished[2],
const bool print_bytes )
const bool hexadecimal, const bool list, const bool print_bytes,
const bool scripted )
{
const int buffer_size = 4096;
unsigned long long byte_number = 1;
@ -277,7 +269,7 @@ int cmp( const long long max_size, const int infd[2],
uint8_t * const buffer1 = buffer0 + buffer_size + 1;
uint8_t * buffer[2];
buffer[0] = buffer0; buffer[1] = buffer1;
int different = 0;
int retval = 0;
while( rest > 0 )
{
@ -289,7 +281,7 @@ int cmp( const long long max_size, const int infd[2],
rd[i] = readblock( infd[i], buffer[i], size );
if( rd[i] != size && errno )
{ show_file_error( filenames[i].c_str(), "Read error", errno );
return 2; }
retval = 2; goto done; }
}
for( int i = 0; i < 2; ++i )
if( rd[i] < size ) finished[i] = true;
@ -298,13 +290,14 @@ int cmp( const long long max_size, const int infd[2],
buffer0[min_rd] = 0; // sentinels for the block compare
buffer1[min_rd] = 1;
int first_diff = block_compare( buffer0, buffer1, &line_number );
int first_diff = block_compare( buffer0, buffer1, list ? 0 : &line_number );
byte_number += first_diff;
if( first_diff < min_rd )
{
if( verbosity < 0 ) return 1; // return status only
if( verbosity == 0 ) // show first difference
retval = 1; // difference found
if( scripted ) break; // status only
if( !list ) // show first difference
{
if( !print_bytes )
std::printf( "%s %s differ: byte %llu, line %llu\n",
@ -316,16 +309,17 @@ int cmp( const long long max_size, const int infd[2],
const unsigned char c1 = buffer1[first_diff];
char buf0[5], buf1[5];
sprintc( buf0, c0 ); sprintc( buf1, c1 );
std::printf( "%s %s differ: byte %llu, line %llu is %3o %s %3o %s\n",
std::printf( hexadecimal ?
"%s %s differ: byte %llu, line %llu is %02X %s %02X %s\n" :
"%s %s differ: byte %llu, line %llu is %3o %s %3o %s\n",
filenames[0].c_str(), filenames[1].c_str(),
byte_number, line_number, c0, buf0, c1, buf1 );
}
std::fflush( stdout );
return 1;
break;
}
else // verbosity > 0 ; show all differences
else // list ; show all differences
{
different = 1;
for( ; first_diff < min_rd; ++byte_number, ++first_diff )
{
const unsigned char c0 = buffer0[first_diff];
@ -333,12 +327,14 @@ int cmp( const long long max_size, const int infd[2],
if( c0 != c1 )
{
if( !print_bytes )
std::printf( "%llu %3o %3o\n", byte_number, c0, c1 );
std::printf( hexadecimal ? "%llu %02X %02X\n" : "%llu %3o %3o\n",
byte_number, c0, c1 );
else
{
char buf0[5], buf1[5];
sprintc( buf0, c0 ); sprintc( buf1, c1 );
std::printf( "%llu %3o %-4s %3o %s\n",
std::printf( hexadecimal ? "%llu %02X %-4s %02X %s\n" :
"%llu %3o %-4s %3o %s\n",
byte_number, c0, buf0, c1, buf1 );
}
}
@ -350,15 +346,18 @@ int cmp( const long long max_size, const int infd[2],
if( rd[0] != rd[1] )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: EOF on %s\n",
program_name, filenames[rd[1]<rd[0]].c_str() );
return 1;
std::fprintf( stderr, list ?
"%s: EOF on %s after byte %llu\n" :
"%s: EOF on %s after byte %llu, in line %llu\n",
program_name, filenames[rd[1]<rd[0]].c_str(),
byte_number - 1, line_number );
retval = 1; break;
}
if( min_rd != buffer_size ) break;
}
done:
delete[] buffer0;
return different;
return retval;
}
} // end namespace
@ -370,8 +369,11 @@ int main( const int argc, const char * const argv[] )
// number of initial bytes ignored for each file
long long ignore_initial[2] = { 0, 0 };
long long max_size = -1; // < 0 means unlimited size
int format_types[2] = { -1, -1 };
bool print_bytes = false;
int format_types[2] = { -1, -1 }; // < 0 means undefined
bool hexadecimal = false;
bool list = false; // list position, value of all differing bytes
bool print_bytes = false; // print differing bytes
bool scripted = false; // suppress messages about file differences
program_name = "zcmp";
invocation_name = ( argc > 0 ) ? argv[0] : program_name;
@ -379,6 +381,7 @@ int main( const int argc, const char * const argv[] )
{
{ 'b', "print-bytes", Arg_parser::no },
{ 'h', "help", Arg_parser::no },
{ 'H', "hexadecimal", Arg_parser::no },
{ 'i', "ignore-initial", Arg_parser::yes },
{ 'l', "list", Arg_parser::no },
{ 'M', "format", Arg_parser::yes },
@ -386,7 +389,8 @@ int main( const int argc, const char * const argv[] )
{ 'N', "no-rcfile", Arg_parser::no },
{ 'O', "force-format", Arg_parser::yes },
{ 'q', "quiet", Arg_parser::no },
{ 's', "silent", Arg_parser::no },
{ 'q', "silent", Arg_parser::no },
{ 's', "script", Arg_parser::no },
{ 'v', "verbose", Arg_parser::no },
{ 'V', "version", Arg_parser::no },
{ bz2_opt, "bz2", Arg_parser::yes },
@ -414,21 +418,22 @@ int main( const int argc, const char * const argv[] )
{
case 'b': print_bytes = true; break;
case 'h': show_help(); return 0;
case 'H': hexadecimal = true; break;
case 'i': parse_ignore_initial( arg, pn, ignore_initial ); break;
case 'l': verbosity = 1; break;
case 'l': list = true; break;
case 'M': parse_format_list( sarg, pn ); break;
case 'n': max_size = getnum( arg, pn ); break;
case 'N': break;
case 'O': parse_format_types2( sarg, pn, format_types ); break;
case 'q':
case 's': verbosity = -1; break;
case 'v': verbosity = 1; break;
case 'q': verbosity = -1; break;
case 's': scripted = true; break;
case 'v': if( verbosity < 4 ) ++verbosity; break;
case 'V': show_version(); return 0;
case bz2_opt: parse_compressor( sarg, fmt_bz2 ); break;
case gz_opt: parse_compressor( sarg, fmt_gz ); break;
case lz_opt: parse_compressor( sarg, fmt_lz ); break;
case xz_opt: parse_compressor( sarg, fmt_xz ); break;
case zst_opt: parse_compressor( sarg, fmt_zst ); break;
case bz2_opt: parse_compressor( sarg, pn, fmt_bz2 ); break;
case gz_opt: parse_compressor( sarg, pn, fmt_gz ); break;
case lz_opt: parse_compressor( sarg, pn, fmt_lz ); break;
case xz_opt: parse_compressor( sarg, pn, fmt_xz ); break;
case zst_opt: parse_compressor( sarg, pn, fmt_zst ); break;
default : internal_error( "uncaught option." );
}
} // end process options
@ -438,12 +443,10 @@ int main( const int argc, const char * const argv[] )
setmode( STDOUT_FILENO, O_BINARY );
#endif
if( argind >= parser.arguments() )
{ show_error( "No files given.", 0, true ); return 2; }
if( parser.arguments() - argind > 2 )
{ show_error( "Too many files.", 0, true ); return 2; }
const int files = parser.arguments() - argind;
if( files < 1 ) { show_error( "No files given.", 0, true ); return 2; }
if( files > 2 ) { show_error( "Too many files.", 0, true ); return 2; }
std::string filenames[2]; // file names of the two input files
filenames[0] = parser.argument( argind );
if( files == 2 ) filenames[1] = parser.argument( argind + 1 );
@ -498,7 +501,8 @@ int main( const int argc, const char * const argv[] )
}
bool finished[2] = { false, false };
int retval = cmp( max_size, infd, filenames, finished, print_bytes );
int retval = cmp( max_size, infd, filenames, finished, hexadecimal, list,
print_bytes, scripted );
for( int i = 0; i < 2; ++i )
if( !good_status( children[i], finished[i] ) ) retval = 2;