blob: 3924b4ac4a15c3997450cb5623dfc0699749380e [file] [log] [blame]
// Copyright (C) 2001-2010 Gianni Mariani
//
// 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction. Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License. This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// Common C++. If you copy code from other releases into a copy of GNU
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way. To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//
//
// cmdoptns.cpp
//
#include <cc++/config.h>
#include <cc++/string.h>
#include <cc++/thread.h>
#include <cc++/exception.h>
#include <cc++/export.h>
#include <cc++/cmdoptns.h>
#ifndef HAVE_GETOPT_LONG
// fix problem with vc++ library
#undef __argc
#undef __argv
#include "getopt.h"
#else
#include <getopt.h>
#endif
#ifndef WIN32
#include <unistd.h>
#endif
#include <cstdlib>
#include <iostream>
#include <fstream>
using std::fstream;
#ifdef CCXX_NAMESPACES
namespace ost {
#endif
//
// In most cases, users will use this default option list.
//
CommandOption * defaultCommandOptionList = 0;
CommandOption::CommandOption(
const char * inOptionName,
const char * inOptionLetter,
const char * inDescription,
OptionType inOptionType,
bool inRequired,
CommandOption ** ppNext
)
: optionName( inOptionName ),
optionLetter( inOptionLetter ),
description( inDescription ),
optionType( inOptionType ),
required( inRequired ),
next( * ppNext ) {
* ppNext = this;
}
CommandOptionWithArg::CommandOptionWithArg(
const char * inOptionName,
const char * inOptionLetter,
const char * inDescription,
OptionType inOptionType,
bool inRequired,
CommandOption ** ppNext
)
: CommandOption(
inOptionName,
inOptionLetter,
inDescription,
inOptionType,
inRequired,
ppNext
), values( 0 ), numValue( 0 )
{
}
CommandOptionArg::CommandOptionArg(
const char * inOptionName,
const char * inOptionLetter,
const char * inDescription,
bool inRequired,
CommandOption ** ppNext
)
: CommandOptionWithArg(
inOptionName,
inOptionLetter,
inDescription,
hasArg,
inRequired,
ppNext
)
{
}
CommandOptionRest::CommandOptionRest(
const char * inOptionName,
const char * inOptionLetter,
const char * inDescription,
bool inRequired,
CommandOption ** ppNext
)
: CommandOptionWithArg(
inOptionName,
inOptionLetter,
inDescription,
trailing,
inRequired,
ppNext
)
{
}
CommandOptionCollect::CommandOptionCollect(
const char * inOptionName,
const char * inOptionLetter,
const char * inDescription,
bool inRequired,
CommandOption ** ppNext
)
: CommandOptionWithArg(
inOptionName,
inOptionLetter,
inDescription,
collect,
inRequired,
ppNext
)
{
}
CommandOptionNoArg::CommandOptionNoArg(
const char * inOptionName,
const char * inOptionLetter,
const char * inDescription,
bool inRequired,
CommandOption ** ppNext
)
: CommandOption(
inOptionName,
inOptionLetter,
inDescription,
noArg,
inRequired,
ppNext
), numSet( 0 )
{
}
// ======== CommandOption =============================================
// PURPOSE:
// CommandOption dummy methods ..
//
void CommandOption::parseDone( CommandOptionParse * cop )
{
}
void CommandOption::foundOption( CommandOptionParse * cop, const char * value)
{
}
void CommandOption::foundOption( CommandOptionParse * cop, const char ** value, int num )
{
}
void CommandOption::performTask( CommandOptionParse * cop )
{
}
bool CommandOption::hasValue()
{
return true;
}
CommandOption::~CommandOption()
{
}
CommandOptionWithArg::~CommandOptionWithArg()
{
if ( values ) {
free( values );
values = 0;
}
}
CommandOptionParse::~CommandOptionParse(void)
{
}
// ======== CommandOptionArg ==========================================
// PURPOSE:
// Methods for CommandOptionArg
//
bool CommandOptionWithArg::hasValue()
{
return numValue > 0;
}
CommandOptionArg::~CommandOptionArg()
{
}
//
//
static void my_alloc( char *** vals, int num, int incr )
{
int num_alloc = 0;
if ( * vals ) {
num_alloc = num | 3;
}
if ( ( incr + num ) > num_alloc ) {
int newsiz = ( incr + num ) | 3;
* vals = ( char ** ) realloc( * vals, sizeof( ** vals ) * newsiz );
}
}
void CommandOptionWithArg::foundOption( CommandOptionParse * cop, const char * value )
{
if ( value ) {
my_alloc( ( char *** ) & values, numValue ? numValue + 1 : 0, 1 );
values[ numValue ++ ] = value;
values[ numValue ] = 0;
}
}
void CommandOptionWithArg::foundOption( CommandOptionParse * cop, const char ** value, int num )
{
my_alloc( ( char *** ) & values, numValue ? numValue + 1 : 0, num + 1 );
int j = 0;
for ( int i = numValue; j < num; i ++, j ++ ) {
values[ i ] = value[ j ];
}
numValue += num;
values[ numValue ] = 0;
}
void CommandOptionNoArg::foundOption( CommandOptionParse * cop, const char * value)
{
numSet ++;
}
// ======== CommandOptionParse ========================================
// PURPOSE:
// methods for CommandOptionParse
//
class CommandOptionParse_impl : public CommandOptionParse {
public:
const char * comment;
int num_options;
struct option * long_options;
CommandOption ** opt_list;
CommandOption ** co_list;
char * optstring;
int argc;
char ** argv;
bool has_err;
char * fail_arg;
bool usage_string_set;
bool required_errors_set;
String error_msgs;
CommandOption * fail_option;
CommandOption * trailing;
String usage_string;
virtual ~CommandOptionParse_impl() {
delete[] opt_list;
delete[] co_list;
delete[] optstring;
delete[] long_options;
}
CommandOptionParse_impl(
int in_argc,
char ** in_argv,
const char * in_comment,
CommandOption * options
) :
comment( in_comment ),
argc( in_argc ),
argv( in_argv ),
has_err( false ),
fail_arg( 0 ),
usage_string_set( false ),
required_errors_set( false ),
error_msgs( "" ),
fail_option( 0 ),
trailing(0) {
// First need to count all options.
CommandOption * to = options;
int ocnt = 0;
int ccnt = 0;
int flag;
while ( to ) {
if ( to->optionName ) ocnt ++;
ccnt ++;
to = to->next;
}
num_options = ccnt;
#ifdef __KCC
co_list = new (CommandOption **)[ocnt];
opt_list = new (CommandOption **)[ccnt];
#else
// fix compiling bug in vc++
typedef CommandOption* PCommandOption;
co_list = new PCommandOption[ocnt];
opt_list = new PCommandOption[ccnt];
#endif
long_options = new option[ccnt+1];
optstring = new char[ 2*ccnt+2 ];
// initialize the last option count
long_options[ ocnt ].name = 0;
long_options[ ocnt ].has_arg = 0;
long_options[ ocnt ].flag = 0;
long_options[ ocnt ].val = 0;
char *tos = optstring;
*(tos++) = '+';
to = options;
while ( to ) {
if ( to->optionType == CommandOption::trailing ) {
if ( ! trailing ) {
trailing = to;
}
} else if ( to->optionType == CommandOption::collect ) {
trailing = to;
}
opt_list[ -- ccnt ] = to;
if ( to->optionName ) {
-- ocnt;
co_list[ ocnt ] = to;
long_options[ ocnt ].name = to->optionName;
long_options[ ocnt ].has_arg = to->optionType == CommandOption::hasArg;
long_options[ ocnt ].flag = & flag;
long_options[ ocnt ].val = ocnt;
}
if ( to->optionLetter && to->optionLetter[ 0 ] ) {
* tos ++ = to->optionLetter[ 0 ];
if ( to->optionType == CommandOption::hasArg ) {
* tos ++ = ':';
}
}
to = to->next;
}
* tos = 0;
int c;
int optionIndex;
opterr = 0; // tell getopt_long not to print any errors
flag = -1;
while ( optind < argc ) {
if (
(
c = getopt_long(
argc, argv, optstring, long_options, &optionIndex
)
) == -1
) {
if ( ! trailing ) {
break;
} else if ( trailing->optionType == CommandOption::trailing ) {
break;
} else {
optarg = argv[ optind ];
optind ++;
to = trailing;
}
} else if ( flag != -1 ) {
to = co_list[ flag ];
flag = -1;
} else if ( c == '?' ) {
if ( optind < 2 ) {
fail_arg = argv[ optind ];
} else {
fail_arg = argv[ optind - 1 ];
}
has_err = true;
return;
} else {
// need to search through the options.
for ( int i = 0; i < num_options; i ++ ) {
to = opt_list[ i ];
if ( ! to->optionLetter ) continue;
if ( c == to->optionLetter[ 0 ] ) {
break;
}
}
// assert( to );
}
// do we terminate here ?
if ( to->optionType == CommandOption::trailing ) {
break;
}
if ( c != ':' ) {
to->foundOption( this, optarg );
} else {
has_err = true;
fail_option = to;
break;
}
}
if ( optind < argc ) {
if ( trailing ) {
trailing->foundOption(
this,
( const char ** ) ( argv + optind ),
argc - optind
);
} else {
has_err = true;
fail_arg = argv[ optind ];
}
}
// Now check to see that all required args made it !
for ( int i = 0; i < num_options; i ++ ) {
CommandOption * toq = opt_list[ i ];
// Tell this parameter that it's done now.
toq->parseDone( this );
if ( toq->required && ! toq->hasValue() ) {
has_err = true;
break;
}
}
}
bool argsHaveError();
virtual const char * printUsage();
virtual const char * printErrors();
void makePrintErrors() {
if ( required_errors_set ) return;
required_errors_set = true;
if ( fail_arg ) {
error_msgs = error_msgs + "Unknown/malformed option '" + fail_arg + "' \n";
} else if ( fail_option ) {
String name;
bool name_msg;
if ( fail_option->optionName ) {
name_msg = true;
name = fail_option->optionName;
} else if ( fail_option->optionLetter ) {
name_msg = true;
name = fail_option->optionLetter;
} else if ( fail_option == trailing ) {
name_msg = false;
} else {
name = "--option with no name--";
name_msg = true;
}
if ( name_msg ) {
error_msgs = error_msgs + "Option '" + name + "' requires value\n";
}
} else if ( has_err ) {
// loop thru all required args
for ( int i = 0; i < num_options; i ++ ) {
CommandOption * to = opt_list[ i ];
if ( to->required && ! to->hasValue() ) {
error_msgs = error_msgs + "Value required for option '";
if ( to->optionName ) {
error_msgs = error_msgs + "--" + to->optionName;
} else if ( to->optionLetter && to->optionLetter[ 0 ] ) {
error_msgs = error_msgs + '-' + to->optionLetter[ 0 ];
} else {
error_msgs = error_msgs + to->description;
}
error_msgs = error_msgs + "' is missing\n";
}
}
}
}
void makePrintUsage() {
if ( usage_string_set ) return;
String str( "" );
String str_argv0 = argv[ 0 ];
str = str + (char *)"Usage : ";
String::size_type slashpos = str_argv0.rfind('/');
if ( slashpos > str_argv0.length() ) {
slashpos = 0;
} else {
slashpos ++;
}
str.append( str_argv0, slashpos, str_argv0.length() - slashpos );
str = str + ' ' + comment + '\n';
for ( int i = 0; i < num_options; i ++ ) {
CommandOption * to = opt_list[ i ];
char * begin = (char *)"\t";
char * obegin = (char *)"\t";
to = opt_list[ i ];
if ( to->optionLetter && to->optionLetter[ 0 ] ) {
str = str + begin + '-' + to->optionLetter[ 0 ];
begin = (char *)", ";
obegin = (char *)" - ";
}
if ( to->optionName ) {
str = str + begin + "--" + to->optionName;
begin = (char *)", ";
obegin = (char *)" - ";
}
if ( to->optionType == CommandOption::hasArg ) {
str = str + begin + " <value>";
} else if ( to->optionType == CommandOption::trailing ) {
str = str + begin + " <rest of command...>";
} else if ( to->optionType == CommandOption::collect ) {
str = str + begin + " <...>";
}
str = str + obegin + to->description + "\n";
}
usage_string = str;
}
virtual void registerError( const char * errMsg ) {
error_msgs = error_msgs + errMsg + '\n';
has_err = true;
}
virtual void performTask() {
for ( int i = 0; i < num_options; i ++ ) {
CommandOption * to = opt_list[ i ];
// Each parameter has this invoked
to->performTask( this );
}
}
};
CommandOptionParse * makeCommandOptionParse(
int argc,
char ** argv,
const char * comment,
CommandOption * options
) {
return new CommandOptionParse_impl( argc, argv, comment, options );
}
bool CommandOptionParse_impl::argsHaveError()
{
return has_err;
}
const char * CommandOptionParse_impl::printUsage()
{
makePrintUsage();
return usage_string.c_str();
}
const char * CommandOptionParse_impl::printErrors()
{
makePrintErrors();
return error_msgs.c_str();
}
#ifdef CCXX_NAMESPACES
}
#endif
/** EMACS **
* Local variables:
* mode: c++
* c-basic-offset: 4
* End:
*/