Commit c8fa6581 authored by Andy Cedilnik's avatar Andy Cedilnik
Browse files

ENH: Move command line argument parsing code to kwsys

parent 3e03bed0
......@@ -78,6 +78,7 @@ IF(NOT KWSYS_NAMESPACE)
SET(KWSYS_USE_Process 1)
SET(KWSYS_USE_RegularExpression 1)
SET(KWSYS_USE_SystemTools 1)
SET(KWSYS_USE_CommandLineArguments 1)
ENDIF(NOT KWSYS_NAMESPACE)
#-----------------------------------------------------------------------------
......@@ -248,12 +249,20 @@ IF(KWSYS_USE_SystemTools)
ENDIF(KWSYS_USE_SystemTools)
# Add selected C++ classes.
FOREACH(c Directory RegularExpression SystemTools)
FOREACH(c Directory RegularExpression SystemTools CommandLineArguments)
IF(KWSYS_USE_${c})
SET(KWSYS_CLASSES ${KWSYS_CLASSES} ${c})
ENDIF(KWSYS_USE_${c})
ENDFOREACH(c)
IF(KWSYS_USE_CommandLineArguments)
IF(NOT ParaView_SOURCE_DIR)
IF(NOT KWSYS_STANDALONE)
MESSAGE("Command Line Arguments is experimental code for now")
ENDIF(NOT KWSYS_STANDALONE)
ENDIF(NOT ParaView_SOURCE_DIR)
ENDIF(KWSYS_USE_CommandLineArguments)
# Add selected C components.
FOREACH(c Process Base64)
IF(KWSYS_USE_${c})
......@@ -411,9 +420,11 @@ IF(KWSYS_STANDALONE)
ADD_EXECUTABLE(testIOS testIOS.cxx)
ADD_EXECUTABLE(testProcess testProcess.c)
ADD_EXECUTABLE(test1 test1.cxx)
ADD_EXECUTABLE(testCommandLineArguments testCommandLineArguments.cxx)
TARGET_LINK_LIBRARIES(testIOS ${KWSYS_NAMESPACE})
TARGET_LINK_LIBRARIES(testProcess ${KWSYS_NAMESPACE}_c)
TARGET_LINK_LIBRARIES(test1 ${KWSYS_NAMESPACE})
TARGET_LINK_LIBRARIES(testCommandLineArguments ${KWSYS_NAMESPACE})
IF(BUILD_TESTING)
ADD_TEST(testProcess-1 testProcess 1)
......
/*=========================================================================
Program: KWSys - Kitware System Library
Module: $RCSfile$
Copyright (c) Kitware, Inc., Insight Consortium. All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#include "kwsysPrivate.h"
#include KWSYS_HEADER(CommandLineArguments.hxx)
#include KWSYS_HEADER(Configure.hxx)
#include KWSYS_HEADER(stl/vector)
#include KWSYS_HEADER(stl/map)
#include KWSYS_HEADER(stl/set)
#include KWSYS_HEADER(ios/sstream)
#include <stdio.h>
namespace KWSYS_NAMESPACE
{
//----------------------------------------------------------------------------
//============================================================================
class CommandLineArgumentsString : public kwsys_stl::string
{
public:
typedef kwsys_stl::string StdString;
CommandLineArgumentsString(): StdString() {}
CommandLineArgumentsString(const value_type* s): StdString(s) {}
CommandLineArgumentsString(const value_type* s, size_type n): StdString(s, n) {}
CommandLineArgumentsString(const StdString& s, size_type pos=0, size_type n=npos):
StdString(s, pos, n) {}
};
class CommandLineArgumentsVectorOfStrings :
public kwsys_stl::vector<CommandLineArgumentsString> {};
class CommandLineArgumentsSetOfStrings :
public kwsys_stl::set<CommandLineArgumentsString> {};
class CommandLineArgumentsMapOfStrucs :
public kwsys_stl::map<CommandLineArgumentsString,
CommandLineArguments::CallbackStructure> {};
class CommandLineArgumentsInternal
{
public:
CommandLineArgumentsInternal()
{
this->UnknownArgumentCallback = 0;
this->ClientData = 0;
this->LastArgument = 0;
}
typedef CommandLineArgumentsVectorOfStrings VectorOfStrings;
typedef CommandLineArgumentsMapOfStrucs CallbacksMap;
typedef CommandLineArgumentsString String;
typedef CommandLineArgumentsSetOfStrings SetOfStrings;
VectorOfStrings Argv;
CallbacksMap Callbacks;
CommandLineArguments::ErrorCallbackType UnknownArgumentCallback;
void* ClientData;
VectorOfStrings::size_type LastArgument;
};
//============================================================================
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
CommandLineArguments::CommandLineArguments()
{
this->Internals = new CommandLineArguments::Internal;
this->Help = "";
this->LineLength = 80;
}
//----------------------------------------------------------------------------
CommandLineArguments::~CommandLineArguments()
{
delete this->Internals;
}
//----------------------------------------------------------------------------
void CommandLineArguments::Initialize(int argc, char* argv[])
{
int cc;
this->Initialize();
for ( cc = 1; cc < argc; cc ++ )
{
this->ProcessArgument(argv[cc]);
}
}
//----------------------------------------------------------------------------
void CommandLineArguments::Initialize()
{
this->Internals->Argv.clear();
this->Internals->LastArgument = 0;
}
//----------------------------------------------------------------------------
void CommandLineArguments::ProcessArgument(const char* arg)
{
this->Internals->Argv.push_back(arg);
}
//----------------------------------------------------------------------------
int CommandLineArguments::Parse()
{
CommandLineArguments::Internal::VectorOfStrings::size_type cc;
CommandLineArguments::Internal::VectorOfStrings matches;
for ( cc = 0; cc < this->Internals->Argv.size(); cc ++ )
{
matches.clear();
CommandLineArguments::Internal::String& arg = this->Internals->Argv[cc];
CommandLineArguments::Internal::CallbacksMap::iterator it;
// Does the argument match to any we know about?
for ( it = this->Internals->Callbacks.begin();
it != this->Internals->Callbacks.end();
it ++ )
{
const CommandLineArguments::Internal::String& parg = it->first;
CommandLineArguments::CallbackStructure *cs = &it->second;
if (cs->ArgumentType == CommandLineArguments::NO_ARGUMENT ||
cs->ArgumentType == CommandLineArguments::SPACE_ARGUMENT)
{
if ( arg == parg )
{
matches.push_back(parg);
}
}
else if ( arg.find( parg ) == 0 )
{
matches.push_back(parg);
}
}
if ( matches.size() > 0 )
{
// Ok, we found one or more arguments that match what user specified.
// Let's find the longest one.
CommandLineArguments::Internal::VectorOfStrings::size_type kk;
CommandLineArguments::Internal::VectorOfStrings::size_type maxidx = 0;
CommandLineArguments::Internal::String::size_type maxlen = 0;
for ( kk = 0; kk < matches.size(); kk ++ )
{
if ( matches[kk].size() > maxlen )
{
maxlen = matches[kk].size();
maxidx = kk;
}
}
// So, the longest one is probably the right one. Now see if it has any
// additional value
const char* value = 0;
CommandLineArguments::CallbackStructure *cs
= &this->Internals->Callbacks[matches[maxidx]];
const CommandLineArguments::Internal::String& sarg = matches[maxidx];
if ( cs->ArgumentType == NO_ARGUMENT )
{
// No value
}
else if ( cs->ArgumentType == SPACE_ARGUMENT )
{
if ( cc == this->Internals->Argv.size()-1 )
{
return 0;
}
// Value is the next argument
value = this->Internals->Argv[cc+1].c_str();
cc ++;
}
else if ( cs->ArgumentType == EQUAL_ARGUMENT )
{
if ( arg.size() == sarg.size() || *(arg.c_str() + sarg.size()) != '=' )
{
return 0;
}
// Value is everythng followed the '=' sign
value = arg.c_str() + sarg.size()+1;
}
else if ( cs->ArgumentType == CONCAT_ARGUMENT )
{
// Value is whatever follows the argument
value = arg.c_str() + sarg.size();
}
// Call the callback
if ( cs->Callback )
{
if ( !cs->Callback(sarg.c_str(), value, cs->CallData) )
{
return 0;
}
}
if ( cs->Variable )
{
kwsys_stl::string var = "1";
if ( value )
{
var = value;
}
if ( cs->VariableType == CommandLineArguments::INT_TYPE )
{
int* variable = static_cast<int*>(cs->Variable);
char* res = 0;
*variable = strtol(var.c_str(), &res, 10);
//if ( res && *res )
// {
// Can handle non-int
// }
}
else if ( cs->VariableType == CommandLineArguments::DOUBLE_TYPE )
{
double* variable = static_cast<double*>(cs->Variable);
char* res = 0;
*variable = strtod(var.c_str(), &res);
//if ( res && *res )
// {
// Can handle non-int
// }
}
else if ( cs->VariableType == CommandLineArguments::STRING_TYPE )
{
char** variable = static_cast<char**>(cs->Variable);
if ( *variable )
{
delete [] *variable;
*variable = 0;
}
*variable = new char[ strlen(var.c_str()) + 1 ];
strcpy(*variable, var.c_str());
}
else if ( cs->VariableType == CommandLineArguments::STL_STRING_TYPE )
{
kwsys_stl::string* variable = static_cast<kwsys_stl::string*>(cs->Variable);
*variable = var;
}
else if ( cs->VariableType == CommandLineArguments::BOOL_TYPE )
{
bool* variable = static_cast<bool*>(cs->Variable);
if ( var == "1" || var == "ON" || var == "TRUE" || var == "true" || var == "on" ||
var == "True" || var == "yes" || var == "Yes" || var == "YES" )
{
*variable = true;
}
else
{
*variable = false;
}
}
else
{
cerr << "Got unknown argument type: \"" << cs->VariableType << "\"" << endl;
return 0;
}
}
}
else
{
// Handle unknown arguments
if ( this->Internals->UnknownArgumentCallback )
{
if ( !this->Internals->UnknownArgumentCallback(arg.c_str(),
this->Internals->ClientData) )
{
return 0;
}
return 1;
}
else
{
cerr << "Got unknown argument: \"" << arg.c_str() << "\"" << endl;
return 0;
}
}
}
// We are done parsing, so remember what was the last argument
this->Internals->LastArgument = cc;
return 1;
}
//----------------------------------------------------------------------------
void CommandLineArguments::GetRemainingArguments(int* argc, char*** argv)
{
CommandLineArguments::Internal::VectorOfStrings::size_type size
= this->Internals->Argv.size() - this->Internals->LastArgument + 1;
CommandLineArguments::Internal::VectorOfStrings::size_type cc;
char** args = new char*[ size ];
args[0] = new char[ this->Internals->Argv[0].size() + 1 ];
strcpy(args[0], this->Internals->Argv[0].c_str());
int cnt = 1;
for ( cc = this->Internals->LastArgument;
cc < this->Internals->Argv.size(); cc ++ )
{
args[cnt] = new char[ this->Internals->Argv[cc].size() + 1];
strcpy(args[cnt], this->Internals->Argv[cc].c_str());
cnt ++;
}
*argc = cnt;
*argv = args;
}
//----------------------------------------------------------------------------
void CommandLineArguments::AddCallback(const char* argument, ArgumentTypeEnum type,
CallbackType callback, void* call_data, const char* help)
{
CommandLineArguments::CallbackStructure s;
s.Argument = argument;
s.ArgumentType = type;
s.Callback = callback;
s.CallData = call_data;
s.VariableType = CommandLineArguments::NO_VARIABLE_TYPE;
s.Variable = 0;
s.Help = help;
this->Internals->Callbacks[argument] = s;
this->GenerateHelp();
}
//----------------------------------------------------------------------------
void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
VariableTypeEnum vtype, void* variable, const char* help)
{
CommandLineArguments::CallbackStructure s;
s.Argument = argument;
s.ArgumentType = type;
s.Callback = 0;
s.CallData = 0;
s.VariableType = vtype;
s.Variable = variable;
s.Help = help;
this->Internals->Callbacks[argument] = s;
this->GenerateHelp();
}
//----------------------------------------------------------------------------
void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
int* variable, const char* help)
{
this->AddArgument(argument, type, CommandLineArguments::INT_TYPE, variable, help);
}
//----------------------------------------------------------------------------
void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
double* variable, const char* help)
{
this->AddArgument(argument, type, CommandLineArguments::DOUBLE_TYPE, variable, help);
}
//----------------------------------------------------------------------------
void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
char** variable, const char* help)
{
this->AddArgument(argument, type, CommandLineArguments::STRING_TYPE, variable, help);
}
//----------------------------------------------------------------------------
void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
kwsys_stl::string* variable, const char* help)
{
this->AddArgument(argument, type, CommandLineArguments::STL_STRING_TYPE, variable, help);
}
//----------------------------------------------------------------------------
void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type,
bool* variable, const char* help)
{
this->AddArgument(argument, type, CommandLineArguments::BOOL_TYPE, variable, help);
}
//----------------------------------------------------------------------------
void CommandLineArguments::AddBooleanArgument(const char* argument, bool*
variable, const char* help)
{
this->AddArgument(argument, CommandLineArguments::NO_ARGUMENT,
CommandLineArguments::BOOL_TYPE, variable, help);
}
//----------------------------------------------------------------------------
void CommandLineArguments::AddBooleanArgument(const char* argument, int*
variable, const char* help)
{
this->AddArgument(argument, CommandLineArguments::NO_ARGUMENT,
CommandLineArguments::INT_TYPE, variable, help);
}
//----------------------------------------------------------------------------
void CommandLineArguments::SetClientData(void* client_data)
{
this->Internals->ClientData = client_data;
}
//----------------------------------------------------------------------------
void CommandLineArguments::SetUnknownArgumentCallback(
CommandLineArguments::ErrorCallbackType callback)
{
this->Internals->UnknownArgumentCallback = callback;
}
//----------------------------------------------------------------------------
const char* CommandLineArguments::GetHelp(const char* arg)
{
CommandLineArguments::Internal::CallbacksMap::iterator it
= this->Internals->Callbacks.find(arg);
if ( it == this->Internals->Callbacks.end() )
{
return 0;
}
// Since several arguments may point to the same argument, find the one this
// one point to if this one is pointing to another argument.
CommandLineArguments::CallbackStructure *cs = &(it->second);
while ( 1 )
{
CommandLineArguments::Internal::CallbacksMap::iterator hit
= this->Internals->Callbacks.find(cs->Help);
if ( hit == this->Internals->Callbacks.end() )
{
return cs->Help;
}
cs = &(hit->second);
}
// Should never happened
return 0;
}
//----------------------------------------------------------------------------
void CommandLineArguments::GenerateHelp()
{
kwsys_ios::ostringstream str;
// Collapse all arguments into the map of vectors of all arguments that do
// the same thing.
CommandLineArguments::Internal::CallbacksMap::iterator it;
typedef kwsys_stl::map<CommandLineArguments::Internal::String,
CommandLineArguments::Internal::SetOfStrings > MapArgs;
MapArgs mp;
MapArgs::iterator mpit, smpit;
for ( it = this->Internals->Callbacks.begin();
it != this->Internals->Callbacks.end();
it ++ )
{
CommandLineArguments::CallbackStructure *cs = &(it->second);
mpit = mp.find(cs->Help);
if ( mpit != mp.end() )
{
mpit->second.insert(it->first);
mp[it->first].insert(it->first);
}
else
{
mp[it->first].insert(it->first);
}
}
for ( it = this->Internals->Callbacks.begin();
it != this->Internals->Callbacks.end();
it ++ )
{
CommandLineArguments::CallbackStructure *cs = &(it->second);
mpit = mp.find(cs->Help);
if ( mpit != mp.end() )
{
mpit->second.insert(it->first);
smpit = mp.find(it->first);
CommandLineArguments::Internal::SetOfStrings::iterator sit;
for ( sit = smpit->second.begin(); sit != smpit->second.end(); sit++ )
{
mpit->second.insert(*sit);
}
mp.erase(smpit);
}
else
{
mp[it->first].insert(it->first);
}
}
// Find the length of the longest string
CommandLineArguments::Internal::String::size_type maxlen = 0;
for ( mpit = mp.begin();
mpit != mp.end();
mpit ++ )
{
CommandLineArguments::Internal::SetOfStrings::iterator sit;
for ( sit = mpit->second.begin(); sit != mpit->second.end(); sit++ )
{
CommandLineArguments::Internal::String::size_type clen = sit->size();
switch ( this->Internals->Callbacks[*sit].ArgumentType )
{
case CommandLineArguments::NO_ARGUMENT: clen += 0; break;
case CommandLineArguments::CONCAT_ARGUMENT: clen += 6; break;
case CommandLineArguments::SPACE_ARGUMENT: clen += 7; break;
case CommandLineArguments::EQUAL_ARGUMENT: clen += 7; break;
}
if ( clen > maxlen )
{
maxlen = clen;
}
}
}
// Create format for that string
char format[80];
sprintf(format, "%%%ds", static_cast<unsigned int>(maxlen));
// Print help for each option
for ( mpit = mp.begin();
mpit != mp.end();
mpit ++ )
{
CommandLineArguments::Internal::SetOfStrings::iterator sit;
for ( sit = mpit->second.begin(); sit != mpit->second.end(); sit++ )
{
str << endl;
char argument[100];
sprintf(argument, sit->c_str());
switch ( this->Internals->Callbacks[*sit].ArgumentType )
{
case CommandLineArguments::NO_ARGUMENT: break;
case CommandLineArguments::CONCAT_ARGUMENT: strcat(argument, "option"); break;
case CommandLineArguments::SPACE_ARGUMENT: strcat(argument, " option"); break;
case CommandLineArguments::EQUAL_ARGUMENT: strcat(argument, "=option"); break;
}
char buffer[80];
sprintf(buffer, format, argument);
str << buffer;
}
str << "\t";
const char* ptr = this->Internals->Callbacks[mpit->first].Help;
int len = strlen(ptr);
int cnt = 0;
while ( len > 0)
{
// If argument with help is longer than line length, split it on previous
// space (or tab) and continue on the next line
CommandLineArguments::Internal::String::size_type cc;
for ( cc = 0; ptr[cc]; cc ++ )
{
if ( *ptr == ' ' || *ptr == '\t' )
{
ptr ++;
len --;
}
}
if ( cnt > 0 )
{
for ( cc = 0; cc < maxlen; cc ++ )
{
str << " ";