/* Arg_parser - A POSIX/GNU command line argument parser.
Copyright (C) 2006, 2007, 2008 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 .
*/
#include
#include
#include
#include "arg_parser.h"
bool Arg_parser::parse_long_option( const char * const opt, const char * const arg,
const Option options[], int & argind ) throw()
{
unsigned int len;
int index = -1;
bool exact = false, ambig = false;
for( len = 0; opt[len+2] && opt[len+2] != '='; ++len ) ;
// Test all long options for either exact match or abbreviated matches.
for( int i = 0; options[i].code != 0; ++i )
if( options[i].name && !std::strncmp( options[i].name, &opt[2], len ) )
{
if( std::strlen( options[i].name ) == len ) // Exact match found
{ index = i; exact = true; break; }
else if( index < 0 ) index = i; // First nonexact match found
else if( options[index].code != options[i].code ||
options[index].has_arg != options[i].has_arg )
ambig = true; // Second or later nonexact match found
}
if( ambig && !exact )
{
_error = "option `"; _error += opt; _error += "' is ambiguous";
return false;
}
if( index < 0 ) // nothing found
{
_error = "unrecognized option `"; _error += opt; _error += '\'';
return false;
}
++argind;
data.push_back( Record( options[index].code ) );
if( opt[len+2] ) // `--=' syntax
{
if( options[index].has_arg == no )
{
_error = "option `--"; _error += options[index].name;
_error += "' doesn't allow an argument";
return false;
}
if( options[index].has_arg == yes && !opt[len+3] )
{
_error = "option `--"; _error += options[index].name;
_error += "' requires an argument";
return false;
}
data.back().argument = &opt[len+3];
return true;
}
if( options[index].has_arg == yes )
{
if( !arg )
{
_error = "option `--"; _error += options[index].name;
_error += "' requires an argument";
return false;
}
++argind; data.back().argument = arg;
return true;
}
return true;
}
bool Arg_parser::parse_short_option( const char * const opt, const char * const arg,
const Option options[], int & argind ) throw()
{
int cind = 1; // character index in opt
while( cind > 0 )
{
int index = -1;
const unsigned char code = opt[cind];
if( code != 0 )
for( int i = 0; options[i].code; ++i )
if( code == options[i].code )
{ index = i; break; }
if( index < 0 )
{
_error = "invalid option -- "; _error += code;
return false;
}
data.push_back( Record( code ) );
if( opt[++cind] == 0 ) { ++argind; cind = 0; } // opt finished
if( options[index].has_arg != no && cind > 0 && opt[cind] )
{
data.back().argument = &opt[cind]; ++argind; cind = 0;
}
else if( options[index].has_arg == yes )
{
if( !arg || !arg[0] )
{
_error = "option requires an argument -- "; _error += code;
return false;
}
data.back().argument = arg; ++argind; cind = 0;
}
}
return true;
}
Arg_parser::Arg_parser( const int argc, const char * const argv[],
const Option options[], const bool in_order ) throw()
{
if( argc < 2 || !argv || !options ) return;
std::vector< std::string > non_options; // skipped non-options
int argind = 1; // index in argv
while( argind < argc )
{
const unsigned char ch1 = argv[argind][0];
const unsigned char ch2 = ( ch1 ? argv[argind][1] : 0 );
if( ch1 == '-' && ch2 ) // we found an option
{
const char * const opt = argv[argind];
const char * const arg = (argind + 1 < argc) ? argv[argind+1] : 0;
if( ch2 == '-' )
{
if( !argv[argind][2] ) { ++argind; break; } // we found "--"
else if( !parse_long_option( opt, arg, options, argind ) ) break;
}
else if( !parse_short_option( opt, arg, options, argind ) ) break;
}
else
{
if( !in_order ) non_options.push_back( argv[argind++] );
else { data.push_back( Record() ); data.back().argument = argv[argind++]; }
}
}
if( _error.size() ) data.clear();
else
{
for( unsigned int i = 0; i < non_options.size(); ++i )
{ data.push_back( Record() ); data.back().argument.swap( non_options[i] ); }
while( argind < argc )
{ data.push_back( Record() ); data.back().argument = argv[argind++]; }
}
}
Arg_parser::Arg_parser( const char * const opt, const char * const arg,
const Option options[] ) throw()
{
if( !opt || !opt[0] || !options ) return;
if( opt[0] == '-' && opt[1] ) // we found an option
{
int argind = 1; // dummy
if( opt[1] == '-' )
{ if( opt[2] ) parse_long_option( opt, arg, options, argind ); }
else
parse_short_option( opt, arg, options, argind );
if( _error.size() ) data.clear();
}
else { data.push_back( Record() ); data.back().argument = opt; }
}