"Fossies" - the Fresh Open Source Software Archive

Member "zutils-1.10/rc.cc" (5 Jan 2021, 11785 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 "rc.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 /* Zutils - Utilities dealing with compressed files
    2    Copyright (C) 2009-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 <cstdio>
   22 #include <cstdlib>
   23 #include <cstring>
   24 #include <string>
   25 #include <vector>
   26 #include <unistd.h>
   27 #include <sys/wait.h>
   28 
   29 #include "arg_parser.h"
   30 #include "rc.h"
   31 
   32 
   33 const char * invocation_name = 0;
   34 const char * program_name = 0;
   35 int verbosity = 0;
   36 
   37 namespace {
   38 
   39 const char * const config_file_name = "zutilsrc";
   40 const char * const     program_year = "2021";
   41 
   42 std::string compressor_names[num_formats] =
   43   { "bzip2", "gzip", "lzip", "xz" };        // default compressor names
   44 
   45 // args to compressors read from rc or from options --[bglx]z, maybe empty
   46 std::vector< std::string > compressor_args[num_formats];
   47 
   48 // vector of enabled formats plus [num_formats] for uncompressed.
   49 // empty means all enabled.
   50 std::vector< bool > enabled_formats;
   51 
   52 const struct { const char * from; const char * to; int format_index; }
   53   known_extensions[] = {
   54   { ".bz2",  "",     fmt_bz2 },
   55   { ".tbz",  ".tar", fmt_bz2 },
   56   { ".tbz2", ".tar", fmt_bz2 },
   57   { ".gz",   "",     fmt_gz },
   58   { ".tgz",  ".tar", fmt_gz },
   59   { ".lz",   "",     fmt_lz },
   60   { ".tlz",  ".tar", fmt_lz },
   61   { ".xz",   "",     fmt_xz },
   62   { ".txz",  ".tar", fmt_xz },
   63   { 0,       0,      -1 } };
   64 
   65 
   66 int my_fgetc( FILE * const f )
   67   {
   68   int ch;
   69   bool comment = false;
   70 
   71   do {
   72     ch = std::fgetc( f );
   73     if( ch == '#' ) comment = true;
   74     else if( ch == '\n' || ch == EOF ) comment = false;
   75     else if( ch == '\\' && comment )
   76       {
   77       const int c = std::fgetc( f );
   78       if( c == '\n' ) { std::ungetc( c, f ); comment = false; }
   79       }
   80     }
   81   while( comment );
   82   return ch;
   83   }
   84 
   85 
   86 // Returns the parity of escapes (backslashes) at the end of a string.
   87 bool trailing_escape( const std::string & s )
   88   {
   89   unsigned len = s.size();
   90   bool odd_escape = false;
   91   while( len > 0 && s[--len] == '\\' ) odd_escape = !odd_escape;
   92   return odd_escape;
   93   }
   94 
   95 
   96 /* Read a line discarding comments, leading whitespace, and blank lines.
   97    Escaped newlines are discarded.
   98    Returns the empty string if at EOF.
   99 */
  100 const std::string & my_fgets( FILE * const f, int & linenum )
  101   {
  102   static std::string s;
  103   bool strip = true;            // strip leading whitespace
  104   s.clear();
  105 
  106   while( true )
  107     {
  108     int ch = my_fgetc( f );
  109     if( strip )
  110       {
  111       strip = false;
  112       while( std::isspace( ch ) )
  113         { if( ch == '\n' ) { ++linenum; } ch = my_fgetc( f ); }
  114       }
  115     if( ch == EOF ) { if( s.size() ) { ++linenum; } break; }
  116     else if( ch == '\n' )
  117       {
  118       ++linenum; strip = true;
  119       if( trailing_escape( s ) ) s.erase( s.size() - 1 );
  120       else if( s.size() ) break;
  121       }
  122     else s += ch;
  123     }
  124   return s;
  125   }
  126 
  127 
  128 bool parse_compressor_command( const std::string & s, int i,
  129                                const int format_index )
  130   {
  131   const int len = s.size();
  132   while( i < len && std::isspace( s[i] ) ) ++i;     // strip spaces
  133   int l = i;
  134   while( i < len && !std::isspace( s[i] ) ) ++i;
  135   if( l >= i || s[l] == '-' ) return false;
  136   compressor_names[format_index].assign( s, l, i - l );
  137 
  138   compressor_args[format_index].clear();
  139   while( i < len )
  140     {
  141     while( i < len && std::isspace( s[i] ) ) ++i;   // strip spaces
  142     l = i;
  143     while( i < len && !std::isspace( s[i] ) ) ++i;
  144     if( l < i )
  145       compressor_args[format_index].push_back( std::string( s, l, i - l ) );
  146     }
  147   return true;
  148   }
  149 
  150 
  151 bool parse_rc_line( const std::string & line,
  152                     const char * const filename, const int linenum )
  153   {
  154   const int len = line.size();
  155   int i = 0;
  156   while( i < len && std::isspace( line[i] ) ) ++i;  // strip spaces
  157   int l = i;
  158   while( i < len && line[i] != '=' && !std::isspace( line[i] ) ) ++i;
  159   if( l >= i )
  160     { if( verbosity >= 0 )
  161         std::fprintf( stderr, "%s %d: missing format name.\n", filename, linenum );
  162       return false; }
  163   const std::string name( line, l, i - l );
  164   int format_index = -1;
  165   for( int j = 0; j < num_formats; ++j )
  166     if( name == format_names[j] ) { format_index = j; break; }
  167   if( format_index < 0 )
  168     { if( verbosity >= 0 )
  169         std::fprintf( stderr, "%s %d: bad format name '%s'\n",
  170                       filename, linenum, name.c_str() );
  171       return false; }
  172 
  173   while( i < len && std::isspace( line[i] ) ) ++i;  // strip spaces
  174   if( i <= 0 || i >= len || line[i] != '=' )
  175     { if( verbosity >= 0 )
  176         std::fprintf( stderr, "%s %d: missing '='\n", filename, linenum );
  177       return false; }
  178   ++i;                          // skip the '='
  179   if( !parse_compressor_command( line, i, format_index ) )
  180     {
  181     if( verbosity >= 0 )
  182       std::fprintf( stderr, "%s %d: missing compressor name.\n", filename, linenum );
  183     return false;
  184     }
  185   return true;
  186   }
  187 
  188 
  189     // Returns 0 for success, 1 for file not found, 2 for syntax error.
  190 int process_rcfile( const std::string & name )
  191   {
  192   FILE * const f = std::fopen( name.c_str(), "r" );
  193   if( !f ) return 1;
  194 
  195   int linenum = 0;
  196   int retval = 0;
  197 
  198   while( true )
  199     {
  200     const std::string & line = my_fgets( f, linenum );
  201     if( line.empty() ) break;               // EOF
  202     if( !parse_rc_line( line, name.c_str(), linenum ) )
  203       { retval = 2; break; }
  204     }
  205   std::fclose( f );
  206   return retval;
  207   }
  208 
  209 } // end namespace
  210 
  211 
  212 bool enabled_format( const int format_index )
  213   {
  214   if( enabled_formats.size() <= num_formats ) return true;  // all enabled
  215   if( format_index < 0 ) return enabled_formats[num_formats];   // uncompressed
  216   return enabled_formats[format_index];
  217   }
  218 
  219 
  220 void parse_format_list( const std::string & arg )
  221   {
  222   const std::string un( "uncompressed" );
  223   bool error = arg.empty();
  224   enabled_formats.assign( num_formats + 1, false );
  225 
  226   for( unsigned l = 0, r; l < arg.size(); l = r + 1 )
  227     {
  228     r = std::min( arg.find( ',', l ), arg.size() );
  229     if( l >= r ) { error = true; break; }       // empty format
  230     int format_index = num_formats;
  231     const std::string s( arg, l, r - l );
  232     for( int i = 0; i < num_formats; ++i )
  233       if( s == format_names[i] )
  234         { format_index = i; break; }
  235     if( format_index == num_formats && un.find( s ) != 0 )
  236       { error = true; break; }
  237     enabled_formats[format_index] = true;
  238     }
  239   if( error )
  240     { show_error( "Bad argument for option '--format'." ); std::exit( 1 ); }
  241   }
  242 
  243 
  244 int parse_format_type( const std::string & arg )
  245   {
  246   for( int i = 0; i < num_formats; ++i )
  247     if( arg == format_names[i] )
  248       return i;
  249   show_error( "Bad argument for option '--force-format'." );
  250   std::exit( 1 );
  251   }
  252 
  253 
  254 int extension_index( const std::string & name )
  255   {
  256   for( int eindex = 0; known_extensions[eindex].from; ++eindex )
  257     {
  258     const std::string ext( known_extensions[eindex].from );
  259     if( name.size() > ext.size() &&
  260         name.compare( name.size() - ext.size(), ext.size(), ext ) == 0 )
  261       return eindex;
  262     }
  263   return -1;
  264   }
  265 
  266 int extension_format( const int eindex )
  267   { return ( eindex >= 0 ) ? known_extensions[eindex].format_index : -1; }
  268 
  269 const char * extension_from( const int eindex )
  270   { return known_extensions[eindex].from; }
  271 
  272 const char * extension_to( const int eindex )
  273   { return known_extensions[eindex].to; }
  274 
  275 
  276 void maybe_process_config_file( const Arg_parser & parser )
  277   {
  278   for( int i = 0; i < parser.arguments(); ++i )
  279     if( parser.code( i ) == 'N' ) return;
  280   std::string name;
  281   const char * p = std::getenv( "HOME" ); if( p ) name = p;
  282   if( name.size() )
  283     {
  284     name += "/."; name += config_file_name;
  285     const int retval = process_rcfile( name );
  286     if( retval == 0 ) return;
  287     if( retval == 2 ) std::exit( 2 );
  288     }
  289   name = SYSCONFDIR; name += '/'; name += config_file_name;
  290   const int retval = process_rcfile( name );
  291   if( retval == 2 ) std::exit( 2 );
  292   }
  293 
  294 
  295 void parse_compressor( const std::string & arg, const int format_index,
  296                        const int eretval )
  297   {
  298   if( !parse_compressor_command( arg, 0, format_index ) )
  299     { show_error( "Missing compressor name." ); std::exit( eretval ); }
  300   }
  301 
  302 
  303 const char * get_compressor_name( const int format_index )
  304   {
  305   if( format_index >= 0 && format_index < num_formats &&
  306       compressor_names[format_index].size() )
  307     return compressor_names[format_index].c_str();
  308   return 0;
  309   }
  310 
  311 
  312 const std::vector< std::string > & get_compressor_args( const int format_index )
  313   {
  314   return compressor_args[format_index];
  315   }
  316 
  317 
  318 void show_help_addr()
  319   {
  320   std::printf( "\nReport bugs to zutils-bug@nongnu.org\n"
  321                "Zutils home page: http://www.nongnu.org/zutils/zutils.html\n" );
  322   }
  323 
  324 
  325 void show_version()
  326   {
  327   std::printf( "%s (zutils) %s\n", program_name, PROGVERSION );
  328   std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year );
  329   std::printf( "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
  330                "This is free software: you are free to change and redistribute it.\n"
  331                "There is NO WARRANTY, to the extent permitted by law.\n" );
  332   }
  333 
  334 
  335 void show_error( const char * const msg, const int errcode, const bool help )
  336   {
  337   if( verbosity < 0 ) return;
  338   if( msg && msg[0] )
  339     std::fprintf( stderr, "%s: %s%s%s\n", program_name, msg,
  340                   ( errcode > 0 ) ? ": " : "",
  341                   ( errcode > 0 ) ? std::strerror( errcode ) : "" );
  342   if( help )
  343     std::fprintf( stderr, "Try '%s --help' for more information.\n",
  344                   invocation_name );
  345   }
  346 
  347 
  348 void show_file_error( const char * const filename, const char * const msg,
  349                       const int errcode )
  350   {
  351   if( verbosity >= 0 )
  352     std::fprintf( stderr, "%s: %s: %s%s%s\n", program_name, filename, msg,
  353                   ( errcode > 0 ) ? ": " : "",
  354                   ( errcode > 0 ) ? std::strerror( errcode ) : "" );
  355   }
  356 
  357 
  358 void internal_error( const char * const msg )
  359   {
  360   if( verbosity >= 0 )
  361     std::fprintf( stderr, "%s: internal error: %s\n", program_name, msg );
  362   std::exit( 3 );
  363   }
  364 
  365 
  366 void show_close_error( const char * const prog_name )
  367   {
  368   if( verbosity >= 0 )
  369     std::fprintf( stderr, "%s: Error closing output of %s: %s\n",
  370                   program_name, prog_name, std::strerror( errno ) );
  371   }
  372 
  373 
  374 void show_exec_error( const char * const prog_name )
  375   {
  376   if( verbosity >= 0 )
  377     std::fprintf( stderr, "%s: Can't exec '%s': %s\n",
  378                   program_name, prog_name, std::strerror( errno ) );
  379   }
  380 
  381 
  382 void show_fork_error( const char * const prog_name )
  383   {
  384   if( verbosity >= 0 )
  385     std::fprintf( stderr, "%s: Can't fork '%s': %s\n",
  386                   program_name, prog_name, std::strerror( errno ) );
  387   }
  388 
  389 
  390 int wait_for_child( const pid_t pid, const char * const name,
  391                     const int eretval, const bool isgzxz )
  392   {
  393   int status;
  394   while( waitpid( pid, &status, 0 ) == -1 )
  395     {
  396     if( errno != EINTR )
  397       {
  398       if( verbosity >= 0 )
  399         std::fprintf( stderr, "%s: Error waiting termination of '%s': %s\n",
  400                       program_name, name, std::strerror( errno ) );
  401       _exit( eretval );
  402       }
  403     }
  404   if( WIFEXITED( status ) )
  405     {
  406     const int tmp = WEXITSTATUS( status );
  407     if( isgzxz && eretval == 1 && tmp == 1 ) return 2;      // for ztest
  408     return tmp;
  409     }
  410   return eretval;
  411   }