229 lines
6.4 KiB
C++
229 lines
6.4 KiB
C++
/* Zutils - Utilities dealing with compressed files
|
|
Copyright (C) 2009, 2010, 2011, 2012, 2013 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 3 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define _FILE_OFFSET_BITS 64
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <stdint.h>
|
|
|
|
#include "arg_parser.h"
|
|
#include "zutils.h"
|
|
#include "rc.h"
|
|
|
|
|
|
namespace {
|
|
|
|
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 i = 0; i < num_formats; ++i )
|
|
if( name == format_names[i] ) { format_index = i; 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.size() == 0 ) 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 )
|
|
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];
|
|
}
|