From c8fa6581428dd1899298034f93720882ac95488c Mon Sep 17 00:00:00 2001 From: Andy Cedilnik Date: Mon, 13 Sep 2004 16:15:02 -0400 Subject: [PATCH] ENH: Move command line argument parsing code to kwsys --- Source/kwsys/CMakeLists.txt | 13 +- Source/kwsys/CommandLineArguments.cxx | 591 ++++++++++++++++++++++ Source/kwsys/CommandLineArguments.hxx.in | 173 +++++++ Source/kwsys/testCommandLineArguments.cxx | 110 ++++ 4 files changed, 886 insertions(+), 1 deletion(-) create mode 100644 Source/kwsys/CommandLineArguments.cxx create mode 100644 Source/kwsys/CommandLineArguments.hxx.in create mode 100644 Source/kwsys/testCommandLineArguments.cxx diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt index fa1bb7dbc..f4edfc9b0 100644 --- a/Source/kwsys/CMakeLists.txt +++ b/Source/kwsys/CMakeLists.txt @@ -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) diff --git a/Source/kwsys/CommandLineArguments.cxx b/Source/kwsys/CommandLineArguments.cxx new file mode 100644 index 000000000..054956f6c --- /dev/null +++ b/Source/kwsys/CommandLineArguments.cxx @@ -0,0 +1,591 @@ +/*========================================================================= + + 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 + +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 {}; +class CommandLineArgumentsSetOfStrings : + public kwsys_stl::set {}; +class CommandLineArgumentsMapOfStrucs : + public kwsys_stl::map {}; + +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(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(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(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(cs->Variable); + *variable = var; + } + else if ( cs->VariableType == CommandLineArguments::BOOL_TYPE ) + { + bool* variable = static_cast(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 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(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 << " "; + } + str << "\t"; + } + CommandLineArguments::Internal::String::size_type skip = len; + if ( skip > this->LineLength - maxlen ) + { + skip = this->LineLength - maxlen; + for ( cc = skip-1; cc > 0; cc -- ) + { + if ( ptr[cc] == ' ' || ptr[cc] == '\t' ) + { + break; + } + } + if ( cc != 0 ) + { + skip = cc; + } + } + str.write(ptr, skip); + str << endl; + ptr += skip; + len -= skip; + cnt ++; + } + } + this->Help = str.str(); +} + +} // namespace KWSYS_NAMESPACE diff --git a/Source/kwsys/CommandLineArguments.hxx.in b/Source/kwsys/CommandLineArguments.hxx.in new file mode 100644 index 000000000..f4893b544 --- /dev/null +++ b/Source/kwsys/CommandLineArguments.hxx.in @@ -0,0 +1,173 @@ +/*========================================================================= + + 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. + +=========================================================================*/ +#ifndef @KWSYS_NAMESPACE@_CommandLineArguments_hxx +#define @KWSYS_NAMESPACE@_CommandLineArguments_hxx + +#include <@KWSYS_NAMESPACE@/Configure.h> +#include <@KWSYS_NAMESPACE@/Configure.hxx> + +#include <@KWSYS_NAMESPACE@/stl/string> + +/* Define this macro temporarily to keep the code readable. */ +#if !defined (KWSYS_NAMESPACE) && !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# define kwsys_stl @KWSYS_NAMESPACE@_stl +#endif + +namespace @KWSYS_NAMESPACE@ +{ + +class CommandLineArgumentsInternal; + +/** \class CommandLineArguments + * \brief Command line arguments processing code. + * + * Find specified arguments with optional options and execute specified methods + * or set given variables. + */ + +class @KWSYS_NAMESPACE@_EXPORT CommandLineArguments +{ +public: + CommandLineArguments(); + ~CommandLineArguments(); + + /** + * Various argument types. + */ + enum ArgumentTypeEnum { + NO_ARGUMENT, // The option takes no argument --foo + CONCAT_ARGUMENT,// The option takes argument after no space --foobar + SPACE_ARGUMENT, // The option takes argument after space --foo bar + EQUAL_ARGUMENT // The option takes argument after equal --foo=bar + }; + + /** + * Various string types. + */ + enum VariableTypeEnum { + NO_VARIABLE_TYPE = 0, // The variable is not specified + INT_TYPE, // The variable is integer (int) + BOOL_TYPE, // The vairable is boolean (bool) + DOUBLE_TYPE, // The variable is float (double) + STRING_TYPE, // The variable is string (char*) + STL_STRING_TYPE // The variable is string (char*) + }; + + /** + * Prototypes for callbacks for callback interface. + */ + typedef int(*CallbackType)(const char* argument, const char* value, + void* call_data); + typedef int(*ErrorCallbackType)(const char* argument, void* client_data); + + struct CallbackStructure + { + const char* Argument; + int ArgumentType; + CallbackType Callback; + void* CallData; + void* Variable; + int VariableType; + const char* Help; + }; + + /** + * Initialize internal data structures. This should be called before parsing. + */ + void Initialize(int argc, char* argv[]); + + /** + * Initialize internal data structure and pass arguments one by one. This is + * convinience method for use from scripting languages where argc and argv + * are not available. + */ + void Initialize(); + void ProcessArgument(const char* arg); + + /** + * This method will parse arguments and call apropriate methods. + */ + int Parse(); + + /** + * This method will add a callback for a specific argument. The arguments to + * it are argument, argument type, callback method, and call data. The + * argument help specifies the help string used with this option. The + * callback and call_data can be skipped. + */ + void AddCallback(const char* argument, ArgumentTypeEnum type, CallbackType callback, + void* call_data, const char* help); + + /** + * Add handler for argument which is going to set the variable to the + * specified value. + */ + void AddArgument(const char* argument, ArgumentTypeEnum type, VariableTypeEnum vtype, void* variable, const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, bool* variable, const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, int* variable, const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, double* variable, const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, char** variable, const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, kwsys_stl::string* variable, const char* help); + void AddBooleanArgument(const char* argument, bool* variable, const char* help); + void AddBooleanArgument(const char* argument, int* variable, const char* help); + + /** + * Set the callbacks for error handling. + */ + void SetClientData(void* client_data); + void SetUnknownArgumentCallback(ErrorCallbackType callback); + + /** + * Get remaining arguments. It allocates space for argv, so you have to call + * delete[] on it. + */ + void GetRemainingArguments(int* argc, char*** argv); + + /** + * Return string containing help. If the argument is specified, only return + * help for that argument. + */ + const char* GetHelp() { return this->Help.c_str(); } + const char* GetHelp(const char* arg); + + /** + * Get / Set the help line length. Default length is 80. + */ + void SetLineLength(); + unsigned int GetLineLength(); + +protected: + + void GenerateHelp(); + + typedef CommandLineArgumentsInternal Internal; + Internal* Internals; + kwsys_stl::string Help; + + unsigned int LineLength; +}; + +} // namespace @KWSYS_NAMESPACE@ + +/* Undefine temporary macro. */ +#if !defined (KWSYS_NAMESPACE) && !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# undef kwsys_stl +#endif + +#endif + + + + + diff --git a/Source/kwsys/testCommandLineArguments.cxx b/Source/kwsys/testCommandLineArguments.cxx new file mode 100644 index 000000000..3ac099aab --- /dev/null +++ b/Source/kwsys/testCommandLineArguments.cxx @@ -0,0 +1,110 @@ +/*========================================================================= + + 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 + +void* random_ptr = (void*)0x123; + +int argument(const char* arg, const char* value, void* call_data) +{ + cout << "Got argument: \"" << arg << "\" value: \"" << (value?value:"(null)") << "\"" << endl; + if ( call_data != random_ptr ) + { + cerr << "Problem processing call_data" << endl; + return 0; + } + return 1; +} + +int unknown_argument(const char* argument, void* call_data) +{ + cout << "Got unknown argument: \"" << argument << "\"" << endl; + if ( call_data != random_ptr ) + { + cerr << "Problem processing call_data" << endl; + return 0; + } + return 1; +} + +int main(int argc, char* argv[]) +{ + // Example run: ./testCommandLineArguments --some-int-variable 4 + // --another-bool-variable --some-bool-variable=yes + // --some-stl-string-variable=foobar --set-bool-arg1 --set-bool-arg2 + // --some-string-variable=hello + + int res = 0; + kwsys::CommandLineArguments arg; + arg.Initialize(argc, argv); + + // For error handling + arg.SetClientData(random_ptr); + arg.SetUnknownArgumentCallback(unknown_argument); + + int some_int_variable = 10; + double some_double_variable = 10.10; + char* some_string_variable = 0; + kwsys_stl::string some_stl_string_variable = ""; + bool some_bool_variable = false; + bool some_bool_variable1 = false; + bool bool_arg1 = false; + int bool_arg2 = 0; + + typedef kwsys::CommandLineArguments argT; + + arg.AddArgument("--some-int-variable", argT::SPACE_ARGUMENT, &some_int_variable, "Set some random int variable"); + arg.AddArgument("--some-double-variable", argT::CONCAT_ARGUMENT, &some_double_variable, "Set some random double variable"); + arg.AddArgument("--some-string-variable", argT::EQUAL_ARGUMENT, &some_string_variable, "Set some random string variable"); + arg.AddArgument("--some-stl-string-variable", argT::EQUAL_ARGUMENT, &some_stl_string_variable, "Set some random stl string variable"); + arg.AddArgument("--some-bool-variable", argT::EQUAL_ARGUMENT, &some_bool_variable, "Set some random bool variable"); + arg.AddArgument("--another-bool-variable", argT::NO_ARGUMENT, &some_bool_variable1, "Set some random bool variable 1"); + arg.AddBooleanArgument("--set-bool-arg1", &bool_arg1, "Test AddBooleanArgument 1"); + arg.AddBooleanArgument("--set-bool-arg2", &bool_arg2, "Test AddBooleanArgument 2"); + + arg.AddCallback("-A", argT::NO_ARGUMENT, argument, random_ptr, "Some option -A. This option has a multiline comment. It should demonstrate how the code splits lines."); + arg.AddCallback("-B", argT::SPACE_ARGUMENT, argument, random_ptr, "Option -B takes argument with space"); + arg.AddCallback("-C", argT::EQUAL_ARGUMENT, argument, random_ptr, "Option -C takes argument after ="); + arg.AddCallback("-D", argT::CONCAT_ARGUMENT, argument, random_ptr, "This option takes concatinated argument"); + arg.AddCallback("--long1", argT::NO_ARGUMENT, argument, random_ptr, "-A"); + arg.AddCallback("--long2", argT::SPACE_ARGUMENT, argument, random_ptr, "-B"); + arg.AddCallback("--long3", argT::EQUAL_ARGUMENT, argument, random_ptr, "Same as -C but a bit different"); + arg.AddCallback("--long4", argT::CONCAT_ARGUMENT, argument, random_ptr, "-C"); + + if ( !arg.Parse() ) + { + cerr << "Problem parsing arguments" << endl; + res = 1; + } + cout << "Help: " << arg.GetHelp() << endl; + + cout << "Some int variable was set to: " << some_int_variable << endl; + cout << "Some double variable was set to: " << some_double_variable << endl; + if ( some_string_variable ) + { + cout << "Some string variable was set to: " << some_string_variable << endl; + delete [] some_string_variable; + } + else + { + cerr << "Problem setting string variable" << endl; + res = 1; + } + cout << "Some STL String variable was set to: " << some_stl_string_variable.c_str() << endl; + cout << "Some bool variable was set to: " << some_bool_variable << endl; + cout << "Some bool variable was set to: " << some_bool_variable1 << endl; + cout << "bool_arg1 variable was set to: " << bool_arg1 << endl; + cout << "bool_arg2 variable was set to: " << bool_arg2 << endl; + cout << endl; + return res; +}