"Fossies" - the Fresh Open Source Software Archive

Member "zutils-1.10/zupdate.cc" (5 Jan 2021, 16464 Bytes) of package /linux/privat/zutils-1.10.tar.lz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "zupdate.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes reports: 1.10-rc1_vs_1.10 or 1.9_vs_1.10.

    1 /* Zupdate - recompress bzip2, gzip, xz files to lzip format
    2    Copyright (C) 2013-2021 Antonio Diaz Diaz.
    3 
    4    This program is free software: you can redistribute it and/or modify
    5    it under the terms of the GNU General Public License as published by
    6    the Free Software Foundation, either version 2 of the License, or
    7    (at your option) any later version.
    8 
    9    This program is distributed in the hope that it will be useful,
   10    but WITHOUT ANY WARRANTY; without even the implied warranty of
   11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12    GNU General Public License for more details.
   13 
   14    You should have received a copy of the GNU General Public License
   15    along with this program.  If not, see <http://www.gnu.org/licenses/>.
   16 */
   17 
   18 #define _FILE_OFFSET_BITS 64
   19 
   20 #include <cerrno>
   21 #include <climits>
   22 #include <csignal>
   23 #include <cstdio>
   24 #include <cstdlib>
   25 #include <cstring>
   26 #include <list>
   27 #include <string>
   28 #include <vector>
   29 #include <dirent.h>
   30 #include <fcntl.h>
   31 #include <stdint.h>
   32 #include <unistd.h>
   33 #include <utime.h>
   34 #include <sys/stat.h>
   35 #include <sys/wait.h>
   36 #if defined(__MSVCRT__) || defined(__OS2__)
   37 #include <io.h>
   38 #endif
   39 
   40 #include "arg_parser.h"
   41 #include "rc.h"
   42 
   43 #ifndef O_BINARY
   44 #define O_BINARY 0
   45 #endif
   46 
   47 
   48 namespace {
   49 
   50 #include "recursive.cc"
   51 
   52 void show_help()
   53   {
   54   std::printf( "zupdate recompresses files from bzip2, gzip, and xz formats to lzip\n"
   55                "format. Each original is compared with the new file and then deleted.\n"
   56                "Only regular files with standard file name extensions are recompressed,\n"
   57                "other files are ignored. Compressed files are decompressed and then\n"
   58                "recompressed on the fly; no temporary files are created. The lzip format\n"
   59                "is chosen as destination because it is the most appropriate for\n"
   60                "long-term data archiving.\n"
   61                "\nIf no files are specified, recursive searches examine the current\n"
   62                "working directory, and nonrecursive searches do nothing.\n"
   63                "\nIf the lzip compressed version of a file already exists, the file is\n"
   64                "skipped unless the option '--force' is given. In this case, if the\n"
   65                "comparison with the existing lzip version fails, an error is returned\n"
   66                "and the original file is not deleted. The operation of zupdate is meant\n"
   67                "to be safe and not cause any data loss. Therefore, existing lzip\n"
   68                "compressed files are never overwritten nor deleted.\n"
   69                "\nThe names of the original files must have one of the following extensions:\n"
   70                "'.bz2', '.gz', or '.xz', which are recompressed to '.lz';\n"
   71                "'.tbz', '.tbz2', '.tgz', or '.txz', which are recompressed to '.tlz'.\n"
   72                "\nUsage: zupdate [options] [files]\n"
   73                "\nExit status is 0 if all the compressed files were successfully recompressed\n"
   74                "(if needed), compared, and deleted (if requested). Non-zero otherwise.\n"
   75                "\nOptions:\n"
   76                "  -h, --help                   display this help and exit\n"
   77                "  -V, --version                output version information and exit\n"
   78                "  -f, --force                  don't skip a file even if the .lz exists\n"
   79                "  -k, --keep                   keep (don't delete) input files\n"
   80                "  -l, --lzip-verbose           pass one option -v to the lzip compressor\n"
   81                "  -M, --format=<list>          process only the formats in <list>\n"
   82                "  -N, --no-rcfile              don't read runtime configuration file\n"
   83                "  -q, --quiet                  suppress all messages\n"
   84                "  -r, --recursive              operate recursively on directories\n"
   85                "  -R, --dereference-recursive  recursively follow symbolic links\n"
   86                "  -v, --verbose                be verbose (a 2nd -v gives more)\n"
   87                "  -0 .. -9                     set compression level [default 9]\n"
   88                "      --bz2=<command>          set compressor and options for bzip2 format\n"
   89                "      --gz=<command>           set compressor and options for gzip format\n"
   90                "      --lz=<command>           set compressor and options for lzip format\n"
   91                "      --xz=<command>           set compressor and options for xz format\n" );
   92   show_help_addr();
   93   }
   94 
   95 
   96 int cant_execute( const std::string & command, const int status )
   97   {
   98   if( verbosity >= 0 )
   99     {
  100     if( WIFEXITED( status ) )
  101       std::fprintf( stderr, "%s: Error executing '%s'. Exit status = %d\n",
  102                     program_name, command.c_str(), WEXITSTATUS( status ) );
  103     else
  104       std::fprintf( stderr, "%s: Can't execute '%s'\n",
  105                     program_name, command.c_str() );
  106     }
  107   return 1;
  108   }
  109 
  110 
  111 // Set permissions, owner, and times.
  112 void set_permissions( const char * const rname, const struct stat & in_stats )
  113   {
  114   bool warning = false;
  115   const mode_t mode = in_stats.st_mode;
  116   // chown will in many cases return with EPERM, which can be safely ignored.
  117   if( chown( rname, in_stats.st_uid, in_stats.st_gid ) == 0 )
  118     { if( chmod( rname, mode ) != 0 ) warning = true; }
  119   else
  120     if( errno != EPERM ||
  121         chmod( rname, mode & ~( S_ISUID | S_ISGID | S_ISVTX ) ) != 0 )
  122       warning = true;
  123   struct utimbuf t;
  124   t.actime = in_stats.st_atime;
  125   t.modtime = in_stats.st_mtime;
  126   if( utime( rname, &t ) != 0 ) warning = true;
  127   if( warning && verbosity >= 2 )
  128     show_error( "Can't change output file attributes." );
  129   }
  130 
  131 
  132     // Returns 0 for success, -1 for file skipped, 1 for error.
  133 int zupdate_file( const std::string & name, const char * const lzip_name,
  134                   const std::vector< std::string > & lzip_args2,
  135                   const bool force, const bool keep_input_files,
  136                   const bool no_rcfile )
  137   {
  138   static int disable_xz = -1;               // tri-state bool
  139   int format_index = -1;
  140   std::string rname;                    // recompressed name
  141 
  142   const int eindex = extension_index( name );       // search extension
  143   if( eindex >= 0 )
  144     {
  145     format_index = extension_format( eindex );
  146     if( format_index == fmt_lz )
  147       {
  148       if( verbosity >= 2 )
  149         std::fprintf( stderr, "%s: Input file '%s' already has '%s' suffix.\n",
  150                       program_name, name.c_str(), extension_from( eindex ) );
  151       return 0;                 // ignore this file
  152       }
  153     rname.assign( name, 0, name.size() - std::strlen( extension_from( eindex ) ) );
  154     rname += ( std::strcmp( extension_to( eindex ), ".tar" ) == 0 ) ?
  155              ".tlz" : ".lz";            // keep combined extension
  156     }
  157   const char * const compressor_name = get_compressor_name( format_index );
  158   if( !compressor_name )
  159     {
  160     if( verbosity >= 2 )
  161       std::fprintf( stderr, "%s: Unknown extension in file name '%s' -- ignored.\n",
  162                     program_name, name.c_str() );
  163     return 0;                       // ignore this file
  164     }
  165 
  166   struct stat in_stats;
  167   if( stat( name.c_str(), &in_stats ) != 0 )        // check input file
  168     {
  169     if( verbosity >= 0 )
  170       std::fprintf( stderr, "%s: Can't stat input file '%s': %s\n",
  171                     program_name, name.c_str(), std::strerror( errno ) );
  172     return 1;
  173     }
  174   if( !S_ISREG( in_stats.st_mode ) )
  175     {
  176     if( verbosity >= 0 )
  177       std::fprintf( stderr, "%s: Input file '%s' is not a regular file.\n",
  178                     program_name, name.c_str() );
  179     return 1;
  180     }
  181 
  182   struct stat st;               // not used
  183   const std::string rname2( rname + ".lz" );    // produced by lzip < 1.20
  184   const bool lz_exists = ( stat( rname.c_str(), &st ) == 0 );
  185   // don't modify an existing 'rname.lz'
  186   const bool lz_lz_exists = ( stat( rname2.c_str(), &st ) == 0 );
  187   if( lz_exists && !force )
  188     {
  189     if( verbosity >= 0 )
  190       std::fprintf( stderr, "%s: Output file '%s' already exists, skipping.\n",
  191                     program_name, rname.c_str() );
  192     return -1;
  193     }
  194 
  195   if( format_index == fmt_xz )
  196     {
  197     if( disable_xz < 0 )
  198       {
  199       std::string command( compressor_name ); command += " -V > /dev/null 2>&1";
  200       disable_xz = ( std::system( command.c_str() ) != 0 );
  201       }
  202     if( disable_xz ) return 0;      // ignore this file if no xz installed
  203     }
  204 
  205   if( !lz_exists )                  // recompress
  206     {
  207     if( verbosity >= 1 )
  208       std::fprintf( stderr, "Recompressing file '%s'\n", name.c_str() );
  209     int fda[2];         // pipe between decompressor and compressor
  210     if( pipe( fda ) < 0 )
  211       { show_error( "Can't create pipe", errno ); return 1; }
  212 
  213     const pid_t pid = fork();
  214     if( pid == 0 )              // child1 (decompressor)
  215       {
  216       if( dup2( fda[1], STDOUT_FILENO ) >= 0 &&
  217           close( fda[0] ) == 0 && close( fda[1] ) == 0 )
  218         {
  219         const std::vector< std::string > & compressor_args =
  220           get_compressor_args( format_index );
  221         const int size = compressor_args.size();
  222         const char ** const argv = new const char *[size+5];
  223         argv[0] = compressor_name;
  224         for( int i = 0; i < size; ++i ) argv[i+1] = compressor_args[i].c_str();
  225         argv[size+1] = "-cd";
  226         argv[size+2] = "--";
  227         argv[size+3] = name.c_str();
  228         argv[size+4] = 0;
  229         execvp( argv[0], (char **)argv );
  230         }
  231       show_exec_error( compressor_name );
  232       _exit( 1 );
  233       }
  234     if( pid < 0 )               // parent
  235       { show_fork_error( compressor_name ); return 1; }
  236 
  237     const pid_t pid2 = fork();
  238     if( pid2 == 0 )             // child2 (lzip compressor)
  239       {
  240       if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
  241           close( fda[0] ) == 0 && close( fda[1] ) == 0 )
  242         {
  243         const std::vector< std::string > & lzip_args =
  244           get_compressor_args( fmt_lz );
  245         const int size = lzip_args.size();
  246         const int size2 = lzip_args2.size();
  247         const char ** const argv = new const char *[size+size2+5];
  248         argv[0] = lzip_name;
  249         argv[1] = "-9";
  250         for( int i = 0; i < size; ++i ) argv[i+2] = lzip_args[i].c_str();
  251         for( int i = 0; i < size2; ++i ) argv[i+size+2] = lzip_args2[i].c_str();
  252         argv[size+size2+2] = "-o";
  253         argv[size+size2+3] = rname.c_str();
  254         argv[size+size2+4] = 0;
  255         execvp( argv[0], (char **)argv );
  256         }
  257       show_exec_error( lzip_name );
  258       _exit( 1 );
  259       }
  260     if( pid2 < 0 )              // parent
  261       { show_fork_error( lzip_name ); return 1; }
  262 
  263     close( fda[0] ); close( fda[1] );
  264     int retval = wait_for_child( pid, compressor_name );
  265     int retval2 = wait_for_child( pid2, lzip_name );
  266     if( retval || retval2 )
  267       { if( !lz_lz_exists ) std::remove( rname2.c_str() );  // lzip < 1.20
  268         std::remove( rname.c_str() ); return 1; }
  269     if( stat( rname.c_str(), &st ) != 0 &&
  270         ( lz_lz_exists || stat( rname2.c_str(), &st ) != 0 ||
  271           std::rename( rname2.c_str(), rname.c_str() ) != 0 ) )
  272       { show_file_error( rname.c_str(), "Error renaming output file", errno );
  273         return 1; }                     // lzip < 1.11
  274     set_permissions( rname.c_str(), in_stats );
  275     }
  276 
  277   {
  278   if( lz_exists && verbosity >= 1 )
  279     std::fprintf( stderr, "Comparing file '%s'\n", name.c_str() );
  280   std::string zcmp_command( invocation_name );
  281   unsigned i =  zcmp_command.size();
  282   while( i > 0 && zcmp_command[i-1] != '/' ) --i;
  283   zcmp_command.resize( i ); zcmp_command.insert( zcmp_command.begin(), '\'' );
  284   zcmp_command += "zcmp' ";             // '[dir/]zcmp'
  285   if( no_rcfile ) zcmp_command += "-N ";
  286   if( verbosity < 0 ) zcmp_command += "-q ";
  287   zcmp_command += '\''; zcmp_command += name;
  288   zcmp_command += "' '"; zcmp_command += rname; zcmp_command += '\'';
  289   int status = std::system( zcmp_command.c_str() );
  290   if( status != 0 )
  291     { if( !lz_exists ) std::remove( rname.c_str() );
  292       return cant_execute( zcmp_command, status ); }
  293   }
  294 
  295   if( !keep_input_files && std::remove( name.c_str() ) != 0 && errno != ENOENT )
  296     {
  297     if( verbosity >= 0 )
  298       std::fprintf( stderr, "%s: Can't delete input file '%s': %s\n",
  299                     program_name, name.c_str(), std::strerror( errno ) );
  300     return 1;
  301     }
  302   return 0;
  303   }
  304 
  305 } // end namespace
  306 
  307 
  308 int main( const int argc, const char * const argv[] )
  309   {
  310   enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt };
  311   int recursive = 0;                // 1 = '-r', 2 = '-R'
  312   std::list< std::string > filenames;
  313   std::vector< std::string > lzip_args2;    // args to lzip, maybe empty
  314   bool force = false;
  315   bool keep_input_files = false;
  316   bool no_rcfile = false;
  317   program_name = "zupdate";
  318   invocation_name = ( argc > 0 ) ? argv[0] : program_name;
  319 
  320   const Arg_parser::Option options[] =
  321     {
  322     { '0',  0,                      Arg_parser::no  },
  323     { '1',  0,                      Arg_parser::no  },
  324     { '2',  0,                      Arg_parser::no  },
  325     { '3',  0,                      Arg_parser::no  },
  326     { '4',  0,                      Arg_parser::no  },
  327     { '5',  0,                      Arg_parser::no  },
  328     { '6',  0,                      Arg_parser::no  },
  329     { '7',  0,                      Arg_parser::no  },
  330     { '8',  0,                      Arg_parser::no  },
  331     { '9',  0,                      Arg_parser::no  },
  332     { 'f', "force",                 Arg_parser::no  },
  333     { 'h', "help",                  Arg_parser::no  },
  334     { 'k', "keep",                  Arg_parser::no  },
  335     { 'l', "lzip-verbose",          Arg_parser::no  },
  336     { 'M', "format",                Arg_parser::yes },
  337     { 'N', "no-rcfile",             Arg_parser::no  },
  338     { 'q', "quiet",                 Arg_parser::no  },
  339     { 'r', "recursive",             Arg_parser::no  },
  340     { 'R', "dereference-recursive", Arg_parser::no  },
  341     { 'v', "verbose",               Arg_parser::no  },
  342     { 'V', "version",               Arg_parser::no  },
  343     { bz2_opt,    "bz2",            Arg_parser::yes },
  344     { gz_opt,     "gz",             Arg_parser::yes },
  345     { lz_opt,     "lz",             Arg_parser::yes },
  346     { xz_opt,     "xz",             Arg_parser::yes },
  347     {  0 ,  0,                      Arg_parser::no  } };
  348 
  349   const Arg_parser parser( argc, argv, options );
  350   if( parser.error().size() )               // bad option
  351     { show_error( parser.error().c_str(), 0, true ); return 1; }
  352 
  353   maybe_process_config_file( parser );
  354 
  355   int argind = 0;
  356   for( ; argind < parser.arguments(); ++argind )
  357     {
  358     const int code = parser.code( argind );
  359     if( !code ) break;                  // no more options
  360     const std::string & arg = parser.argument( argind );
  361     switch( code )
  362       {
  363       case '0': case '1': case '2': case '3': case '4':
  364       case '5': case '6': case '7': case '8': case '9':
  365                 lzip_args2.push_back( "-" ); lzip_args2.back() += code; break;
  366       case 'f': force = true; break;
  367       case 'h': show_help(); return 0;
  368       case 'k': keep_input_files = true; break;
  369       case 'l': lzip_args2.push_back( "-v" ); break;
  370       case 'M': parse_format_list( arg ); break;
  371       case 'N': no_rcfile = true; break;
  372       case 'q': verbosity = -1; lzip_args2.push_back( "-q" ); break;
  373       case 'r': recursive = 1; break;
  374       case 'R': recursive = 2; break;
  375       case 'v': if( verbosity < 4 ) ++verbosity; break;
  376       case 'V': show_version(); return 0;
  377       case bz2_opt: parse_compressor( arg, fmt_bz2, 1 ); break;
  378       case gz_opt: parse_compressor( arg, fmt_gz, 1 ); break;
  379       case lz_opt: parse_compressor( arg, fmt_lz, 1 ); break;
  380       case xz_opt: parse_compressor( arg, fmt_xz, 1 ); break;
  381       default : internal_error( "uncaught option." );
  382       }
  383     } // end process options
  384 
  385 #if defined(__MSVCRT__) || defined(__OS2__)
  386   setmode( STDIN_FILENO, O_BINARY );
  387   setmode( STDOUT_FILENO, O_BINARY );
  388 #endif
  389 
  390   const char * const lzip_name = get_compressor_name( fmt_lz );
  391   if( !lzip_name )
  392     { show_error( "Missing name of compressor for lzip format." ); return 1; }
  393 
  394   for( ; argind < parser.arguments(); ++argind )
  395     filenames.push_back( parser.argument( argind ) );
  396 
  397   if( filenames.empty() && recursive ) filenames.push_back( "." );
  398 
  399   std::string input_filename;
  400   int retval = 0;
  401   bool error = false;
  402   while( next_filename( filenames, input_filename, error, recursive, true ) )
  403     {
  404     int tmp = zupdate_file( input_filename, lzip_name, lzip_args2, force,
  405                             keep_input_files, no_rcfile );
  406     if( tmp < 0 ) error = true;
  407     if( tmp > retval ) retval = tmp;
  408     if( tmp > 0 ) break;
  409     }
  410   if( error && retval == 0 ) retval = 1;
  411   return retval;
  412   }