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

View file

@ -73,11 +73,12 @@ void show_help()
"\nExit status is 0 if all the compressed files were successfully recompressed\n"
"(if needed), compared, and deleted (if requested). 1 if a non-fatal error\n"
"occurred (file not found or not regular, or has invalid format, or can't be\n"
"deleted). 2 if a fatal error occurred (compressor can't be run, or\n"
"comparison fails).\n"
"deleted). 2 if a fatal error occurred (invalid command line options,\n"
"compressor can't be run, or comparison fails).\n"
"\nOptions:\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
" -d, --destdir=<dir> write recompressed files into <dir>\n"
" -e, --expand-extensions expand combined extensions; tgz -> tar.lz\n"
" -f, --force don't skip a file even if the .lz exists\n"
" -i, --ignore-errors ignore non-fatal errors\n"
@ -94,11 +95,60 @@ void show_help()
" --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" );
" --zst=<command> set compressor and options for zstd format\n"
"\nValid formats for option '-M' are 'bz2', 'gz', 'lz', 'xz', and 'zst'.\n" );
show_help_addr();
}
void extract_srcdir_name( const std::string & name, std::string & srcdir )
{
if( name.empty() || name == "." ) return; // leave srcdir empty
if( name[name.size()-1] == '/' ) // remove last slash
{ srcdir.assign( name, 0, name.size() - 1 ); return; }
struct stat st;
if( stat( name.c_str(), &st ) == 0 && S_ISDIR( st.st_mode ) )
{ srcdir = name; return; }
unsigned size = 0; // size of srcdir without last slash nor basename
for( unsigned i = name.size(); i > 0; --i )
if( name[i-1] == '/' ) { size = i - 1; break; }
if( size > 0 ) srcdir.assign( name, 0, size );
}
bool make_dirs( const std::string & name )
{
static std::string cached_dirname;
unsigned dirsize = name.size(); // size of dirname without last slash
for( unsigned i = name.size(); i > 0; --i )
if( name[i-1] == '/' ) { dirsize = i - 1; break; }
if( dirsize >= name.size() ) return true; // no dirname
if( dirsize == 0 ) return true; // dirname is '/'
if( cached_dirname.size() == dirsize &&
cached_dirname.compare( 0, dirsize, name ) == 0 ) return true;
for( unsigned i = 0; i < dirsize; )
{
while( i < dirsize && name[i] == '/' ) ++i;
const unsigned first = i;
while( i < dirsize && name[i] != '/' ) ++i;
if( first < i )
{
std::string partial( name, 0, i );
struct stat st;
if( stat( partial.c_str(), &st ) == 0 )
{ if( !S_ISDIR( st.st_mode ) ) return false; }
else if( mkdir( partial.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH |
S_IXOTH ) != 0 && errno != EEXIST ) return false;
}
}
cached_dirname.assign( name, 0, dirsize );
return true;
}
void cant_execute( const std::string & command, const int status )
{
if( verbosity >= 0 )
@ -137,13 +187,14 @@ void set_permissions( const char * const rname, const struct stat & in_stats )
// Return value: 0 = success, -1 = file skipped, 1 = error, 2 = fatal error.
int zupdate_file( const std::string & name, const char * const lzip_name,
const std::vector< std::string > & lzip_args2,
const std::string & srcdir, const std::string & destdir,
const bool expand, const bool force,
const bool keep_input_files, const bool no_rcfile )
{
// bzip2, gzip, and lzip are the primary formats. xz and zstd are optional.
static int disable_xz = -1; // tri-state bool
static int disable_zst = -1; // tri-state bool
int format_index = -1;
int format_index = -1; // undefined
std::string rname; // recompressed name
const int eindex = extension_index( name ); // search extension
@ -157,7 +208,18 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
program_name, name.c_str(), extension_from( eindex ) );
return 0; // ignore this file
}
rname.assign( name, 0, name.size() - std::strlen( extension_from( eindex ) ) );
if( destdir.size() )
{
if( srcdir.size() && name.compare( 0, srcdir.size(), srcdir ) != 0 )
internal_error( "srcdir mismatch." );
rname = destdir;
if( rname[rname.size()-1] != '/' && name[srcdir.size()] != '/' )
rname += '/';
rname.append( name, srcdir.size(), name.size() - srcdir.size() -
std::strlen( extension_from( eindex ) ) );
}
else
rname.assign( name, 0, name.size() - std::strlen( extension_from( eindex ) ) );
rname += ( std::strcmp( extension_to( eindex ), ".tar" ) == 0 ) ?
( expand ? ".tar.lz" : ".tlz" ) : ".lz";
}
@ -172,19 +234,11 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
struct stat in_stats;
if( stat( name.c_str(), &in_stats ) != 0 ) // check input file
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't stat input file '%s': %s\n",
program_name, name.c_str(), std::strerror( errno ) );
return 1;
}
{ show_file_error( name.c_str(), "Can't stat input file", errno );
return 1; }
if( !S_ISREG( in_stats.st_mode ) )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Input file '%s' is not a regular file.\n",
program_name, name.c_str() );
return 1;
}
{ show_file_error( name.c_str(), "Input file is not a regular file." );
return 1; }
struct stat st; // not used
const std::string rname2( rname + ".lz" ); // produced by lzip < 1.20
@ -206,8 +260,8 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
std::string command( compressor_name ); command += " -V > /dev/null 2>&1";
disable_xz = ( std::system( command.c_str() ) != 0 );
if( disable_xz && verbosity >= 2 )
std::fprintf( stderr, "%s: '%s' not found. Ignoring xz files.\n",
program_name, compressor_name );
show_file_error( compressor_name,
"Xz decompressor not found. Ignoring xz files." );
}
if( disable_xz ) return 0; // ignore this file if no xz installed
}
@ -218,8 +272,8 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
std::string command( compressor_name ); command += " -V > /dev/null 2>&1";
disable_zst = ( std::system( command.c_str() ) != 0 );
if( disable_zst && verbosity >= 2 )
std::fprintf( stderr, "%s: '%s' not found. Ignoring zstd files.\n",
program_name, compressor_name );
show_file_error( compressor_name,
"Zstd decompressor not found. Ignoring zstd files." );
}
if( disable_zst ) return 0; // ignore this file if no zstd installed
}
@ -228,6 +282,9 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
{
if( verbosity >= 1 )
std::fprintf( stderr, "Recompressing file '%s'\n", name.c_str() );
if( destdir.size() && !make_dirs( rname ) )
{ show_file_error( rname.c_str(), "Error creating intermediate directory." );
return 2; }
int fda[2]; // pipe between decompressor and compressor
if( pipe( fda ) < 0 )
{ show_error( "Can't create pipe", errno ); return 2; }
@ -264,8 +321,8 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
{
const std::vector< std::string > & lzip_args =
get_compressor_args( fmt_lz );
const int size = lzip_args.size();
const int size2 = lzip_args2.size();
const int size = lzip_args.size(); // from .conf or --lz
const int size2 = lzip_args2.size(); // from command line
const char ** const argv = new const char *[size+size2+5];
argv[0] = lzip_name;
argv[1] = "-9";
@ -299,13 +356,14 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
{
if( lz_exists && verbosity >= 1 )
std::fprintf( stderr, "Comparing file '%s'\n", name.c_str() );
// Quote names in zcmp_command to allow file/dir names with spaces.
std::string zcmp_command( invocation_name );
unsigned i = zcmp_command.size();
while( i > 0 && zcmp_command[i-1] != '/' ) --i; // strip "zupdate"
zcmp_command.resize( i ); zcmp_command.insert( zcmp_command.begin(), '\'' );
zcmp_command += "zcmp' "; // '[dir/]zcmp'
if( no_rcfile ) zcmp_command += "-N ";
if( verbosity < 0 ) zcmp_command += "-q ";
if( verbosity < 0 ) zcmp_command += "-q -s ";
zcmp_command += '\''; zcmp_command += name;
zcmp_command += "' '"; zcmp_command += rname; zcmp_command += '\'';
int status = std::system( zcmp_command.c_str() );
@ -315,12 +373,8 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
}
if( !keep_input_files && std::remove( name.c_str() ) != 0 && errno != ENOENT )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't delete input file '%s': %s\n",
program_name, name.c_str(), std::strerror( errno ) );
return 1;
}
{ show_file_error( name.c_str(), "Can't delete input file", errno );
return 1; }
return 0;
}
@ -331,7 +385,7 @@ int main( const int argc, const char * const argv[] )
{
enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt, zst_opt };
int recursive = 0; // 1 = '-r', 2 = '-R'
std::list< std::string > filenames;
std::string destdir; // write recompressed files here
std::vector< std::string > lzip_args2; // args to lzip, maybe empty
bool expand = false;
bool force = false;
@ -353,6 +407,7 @@ int main( const int argc, const char * const argv[] )
{ '7', 0, Arg_parser::no },
{ '8', 0, Arg_parser::no },
{ '9', 0, Arg_parser::no },
{ 'd', "destdir", Arg_parser::yes },
{ 'e', "expand-extensions", Arg_parser::no },
{ 'f', "force", Arg_parser::no },
{ 'h', "help", Arg_parser::no },
@ -391,6 +446,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':
lzip_args2.push_back( "-" ); lzip_args2.back() += code; break;
case 'd': destdir = arg; break;
case 'e': expand = true; break;
case 'f': force = true; break;
case 'h': show_help(); return 0;
@ -404,11 +460,11 @@ int main( const int argc, const char * const argv[] )
case 'R': recursive = 2; break;
case 'v': if( verbosity < 4 ) ++verbosity; break;
case 'V': show_version(); return 0;
case bz2_opt: parse_compressor( arg, fmt_bz2, 1 ); break;
case gz_opt: parse_compressor( arg, fmt_gz, 1 ); break;
case lz_opt: parse_compressor( arg, fmt_lz, 1 ); break;
case xz_opt: parse_compressor( arg, fmt_xz, 1 ); break;
case zst_opt: parse_compressor( arg, fmt_zst, 1 ); break;
case bz2_opt: parse_compressor( arg, pn, fmt_bz2, 1 ); break;
case gz_opt: parse_compressor( arg, pn, fmt_gz, 1 ); break;
case lz_opt: parse_compressor( arg, pn, fmt_lz, 1 ); break;
case xz_opt: parse_compressor( arg, pn, fmt_xz, 1 ); break;
case zst_opt: parse_compressor( arg, pn, fmt_zst, 1 ); break;
default : internal_error( "uncaught option." );
}
} // end process options
@ -422,22 +478,31 @@ int main( const int argc, const char * const argv[] )
if( !lzip_name )
{ show_error( "Missing name of compressor for lzip format." ); return 2; }
for( ; argind < parser.arguments(); ++argind )
filenames.push_back( parser.argument( argind ) );
if( filenames.empty() && recursive ) filenames.push_back( "." );
std::list< std::string > filenames;
if( argind < parser.arguments() )
filenames.push_back( parser.argument( argind++ ) ); // first argument
else if( recursive ) filenames.push_back( "." );
else return 0; // nothing to do
std::string input_filename;
int retval = 0;
bool error = false;
while( next_filename( filenames, input_filename, error, recursive, true ) )
while( true )
{
int tmp = zupdate_file( input_filename, lzip_name, lzip_args2, expand,
force, keep_input_files, no_rcfile );
if( tmp < 0 ) error = true;
if( tmp > retval ) retval = tmp;
if( tmp >= 2 || ( tmp == 1 && !ignore_errors ) ) break;
std::string srcdir; // dirname to be replaced by destdir
if( destdir.size() ) extract_srcdir_name( filenames.front(), srcdir );
while( next_filename( filenames, input_filename, error, recursive, true ) )
{
int tmp = zupdate_file( input_filename, lzip_name, lzip_args2, srcdir,
destdir, expand, force, keep_input_files, no_rcfile );
if( tmp < 0 ) error = true; // file skipped
if( tmp > retval ) retval = tmp;
if( tmp >= 2 || ( tmp == 1 && !ignore_errors ) ) goto out;
}
if( argind >= parser.arguments() ) break;
filenames.push_back( parser.argument( argind++ ) );
}
out:
if( error && retval == 0 ) retval = 1;
return retval;
}