Merging upstream version 1.12~rc1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
411f37263d
commit
d5110769e8
29 changed files with 1120 additions and 662 deletions
157
zupdate.cc
157
zupdate.cc
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue