/* Zutils - Utilities dealing with compressed files
Copyright (C) 2009-2014 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#define _FILE_OFFSET_BITS 64
#include
#include
#include
#include
#include
#include
#include
#include
#include "arg_parser.h"
#include "rc.h"
const char * invocation_name = 0;
const char * program_name = 0;
int verbosity = 0;
namespace {
const char * const config_file_name = "zutilsrc";
const char * const program_year = "2014";
std::string compressor_names[num_formats] =
{ "bzip2", "gzip", "lzip", "xz" }; // default compressor names
// args to compressors, maybe empty
std::vector< std::string > compressor_args[num_formats];
int my_fgetc( FILE * const f )
{
int ch;
bool comment = false;
do {
ch = std::fgetc( f );
if( ch == '#' ) comment = true;
else if( ch == '\n' || ch == EOF ) comment = false;
else if( ch == '\\' && comment )
{
const int c = std::fgetc( f );
if( c == '\n' ) { std::ungetc( c, f ); comment = false; }
}
}
while( comment );
return ch;
}
// Returns the parity of escapes (backslashes) at the end of a string.
bool trailing_escape( const std::string & s )
{
unsigned len = s.size();
bool odd_escape = false;
while( len > 0 && s[--len] == '\\' ) odd_escape = !odd_escape;
return odd_escape;
}
// Read a line discarding comments, leading whitespace and blank lines.
// Escaped newlines are discarded.
// Returns the empty string if at EOF.
//
const std::string & my_fgets( FILE * const f, int & linenum )
{
static std::string s;
bool strip = true; // strip leading whitespace
s.clear();
while( true )
{
int ch = my_fgetc( f );
if( strip )
{
strip = false;
while( std::isspace( ch ) )
{ if( ch == '\n' ) { ++linenum; } ch = my_fgetc( f ); }
}
if( ch == EOF ) { if( s.size() > 0 ) { ++linenum; } break; }
else if( ch == '\n' )
{
++linenum; strip = true;
if( trailing_escape( s ) ) s.erase( s.size() - 1 );
else if( s.size() > 0 ) break;
}
else s += ch;
}
return s;
}
bool parse_rc_line( const std::string & line,
const char * const filename, const int linenum )
{
const int len = line.size();
int i = 0;
while( i < len && std::isspace( line[i] ) ) ++i; // strip spaces
int l = i;
while( i < len && line[i] != '=' && !std::isspace( line[i] ) ) ++i;
if( l >= i )
{ if( verbosity >= 0 )
std::fprintf( stderr, "%s %d: missing format name.\n", filename, linenum );
return false; }
const std::string name( line, l, i - l );
int format_index = -1;
for( int j = 0; j < num_formats; ++j )
if( name == format_names[j] ) { format_index = j; break; }
if( format_index < 0 )
{ if( verbosity >= 0 )
std::fprintf( stderr, "%s %d: bad format name '%s'\n",
filename, linenum, name.c_str() );
return false; }
while( i < len && std::isspace( line[i] ) ) ++i; // strip spaces
if( i <= 0 || i >= len || line[i] != '=' )
{ if( verbosity >= 0 )
std::fprintf( stderr, "%s %d: missing '='.\n", filename, linenum );
return false; }
++i; // skip the '='
while( i < len && std::isspace( line[i] ) ) ++i; // strip spaces
l = i;
while( i < len && !std::isspace( line[i] ) ) ++i;
if( l >= i )
{ if( verbosity >= 0 )
std::fprintf( stderr, "%s %d: missing compressor name.\n", filename, linenum );
return false; }
compressor_names[format_index].assign( line, l, i - l );
compressor_args[format_index].clear();
while( i < len )
{
while( i < len && std::isspace( line[i] ) ) ++i; // strip spaces
l = i;
while( i < len && !std::isspace( line[i] ) ) ++i;
if( l < i )
compressor_args[format_index].push_back( std::string( line, l, i - l ) );
}
return true;
}
// Returns 0 for success, 1 for file not found, 2 for syntax error.
int process_rcfile( const std::string & name )
{
FILE * const f = std::fopen( name.c_str(), "r" );
if( !f ) return 1;
int linenum = 0;
int retval = 0;
while( true )
{
const std::string & line = my_fgets( f, linenum );
if( line.empty() ) break; // EOF
if( !parse_rc_line( line, name.c_str(), linenum ) )
{ retval = 2; break; }
}
std::fclose( f );
return retval;
}
} // end namespace
void maybe_process_config_file( const Arg_parser & parser )
{
for( int i = 0; i < parser.arguments(); ++i )
if( parser.code( i ) == 'N' ) return;
std::string name;
const char * p = std::getenv( "HOME" ); if( p ) name = p;
if( name.size() )
{
name += "/."; name += config_file_name;
const int retval = process_rcfile( name );
if( retval == 0 ) return;
if( retval == 2 ) std::exit( 2 );
}
name = SYSCONFDIR; name += '/'; name += config_file_name;
const int retval = process_rcfile( name );
if( retval == 2 ) std::exit( 2 );
}
void parse_compressor( const std::string & arg, const int format_index,
const int eretval )
{
const int len = arg.size();
int i = 0;
while( i < len && std::isspace( arg[i] ) ) ++i; // strip spaces
int l = i;
while( i < len && !std::isspace( arg[i] ) ) ++i;
if( l >= i )
{ show_error( "Missing compressor name." ); std::exit( eretval ); }
compressor_names[format_index].assign( arg, l, i - l );
compressor_args[format_index].clear();
while( i < len )
{
while( i < len && std::isspace( arg[i] ) ) ++i; // strip spaces
l = i;
while( i < len && !std::isspace( arg[i] ) ) ++i;
if( l < i )
compressor_args[format_index].push_back( std::string( arg, l, i - l ) );
}
}
const char * get_compressor_name( const int format_index )
{
if( format_index >= 0 && format_index < num_formats &&
compressor_names[format_index].size() &&
compressor_names[format_index][0] != '-' )
return compressor_names[format_index].c_str();
return 0;
}
const std::vector< std::string > & get_compressor_args( const int format_index )
{
return compressor_args[format_index];
}
void show_help_addr()
{
std::printf( "\nReport bugs to zutils-bug@nongnu.org\n"
"Zutils home page: http://www.nongnu.org/zutils/zutils.html\n" );
}
void show_version()
{
std::printf( "%s (zutils) %s\n", program_name, PROGVERSION );
std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year );
std::printf( "License GPLv2+: GNU GPL version 2 or later \n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n" );
}
void show_error( const char * const msg, const int errcode, const bool help )
{
if( verbosity >= 0 )
{
if( msg && msg[0] )
{
std::fprintf( stderr, "%s: %s", program_name, msg );
if( errcode > 0 )
std::fprintf( stderr, ": %s.", std::strerror( errcode ) );
std::fprintf( stderr, "\n" );
}
if( help )
std::fprintf( stderr, "Try '%s --help' for more information.\n",
invocation_name );
}
}
void show_error2( const char * const msg, const char * const name )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: %s '%s': %s.\n",
program_name, msg, name, std::strerror( errno ) );
}
void internal_error( const char * const msg )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: internal error: %s\n", program_name, msg );
std::exit( 3 );
}
void show_close_error( const char * const prog_name )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't close output of %s: %s.\n",
program_name, prog_name, std::strerror( errno ) );
}
void show_exec_error( const char * const prog_name )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't exec '%s': %s.\n",
program_name, prog_name, std::strerror( errno ) );
}
void show_fork_error( const char * const prog_name )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't fork '%s': %s.\n",
program_name, prog_name, std::strerror( errno ) );
}
int wait_for_child( const pid_t pid, const char * const name,
const int eretval, const bool isgzxz )
{
int status;
while( waitpid( pid, &status, 0 ) == -1 )
{
if( errno != EINTR )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Error waiting termination of '%s': %s.\n",
program_name, name, std::strerror( errno ) );
_exit( eretval );
}
}
if( WIFEXITED( status ) )
{
const int tmp = WEXITSTATUS( status );
if( isgzxz && eretval == 1 && tmp == 1 ) return 2; // for ztest
return tmp;
}
return eretval;
}