/*========================================================================= 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(String.hxx) #include KWSYS_HEADER(stl/vector) #include KWSYS_HEADER(stl/map) #include KWSYS_HEADER(stl/set) #include KWSYS_HEADER(ios/sstream) #include KWSYS_HEADER(ios/iostream) // Work-around CMake dependency scanning limitation. This must // duplicate the above list of headers. #if 0 # include "CommandLineArguments.hxx.in" # include "Configure.hxx.in" # include "kwsys_stl.hxx.in" # include "kwsys_ios_sstream.h.in" # include "kwsys_ios_iostream.h.in" #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef _MSC_VER # pragma warning (disable: 4786) #endif #if defined(__sgi) && !defined(__GNUC__) # pragma set woff 1375 /* base class destructor not virtual */ #endif #if 0 # define CommandLineArguments_DEBUG(x) \ kwsys_ios::cout << __LINE__ << " CLA: " << x << kwsys_ios::endl #else # define CommandLineArguments_DEBUG(x) #endif namespace KWSYS_NAMESPACE { //---------------------------------------------------------------------------- //============================================================================ struct CommandLineArgumentsCallbackStructure { const char* Argument; int ArgumentType; CommandLineArguments::CallbackType Callback; void* CallData; void* Variable; int VariableType; const char* Help; }; class CommandLineArgumentsVectorOfStrings : public kwsys_stl::vector<kwsys::String> {}; class CommandLineArgumentsSetOfStrings : public kwsys_stl::set<kwsys::String> {}; class CommandLineArgumentsMapOfStrucs : public kwsys_stl::map<kwsys::String, CommandLineArgumentsCallbackStructure> {}; class CommandLineArgumentsInternal { public: CommandLineArgumentsInternal() { this->UnknownArgumentCallback = 0; this->ClientData = 0; this->LastArgument = 0; } typedef CommandLineArgumentsVectorOfStrings VectorOfStrings; typedef CommandLineArgumentsMapOfStrucs CallbacksMap; typedef kwsys::String String; typedef CommandLineArgumentsSetOfStrings SetOfStrings; VectorOfStrings Argv; String Argv0; CallbacksMap Callbacks; CommandLineArguments::ErrorCallbackType UnknownArgumentCallback; void* ClientData; VectorOfStrings::size_type LastArgument; VectorOfStrings UnusedArguments; }; //============================================================================ //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- CommandLineArguments::CommandLineArguments() { this->Internals = new CommandLineArguments::Internal; this->Help = ""; this->LineLength = 80; this->StoreUnusedArgumentsFlag = false; } //---------------------------------------------------------------------------- CommandLineArguments::~CommandLineArguments() { delete this->Internals; } //---------------------------------------------------------------------------- void CommandLineArguments::Initialize(int argc, const char* const argv[]) { int cc; this->Initialize(); this->Internals->Argv0 = argv[0]; for ( cc = 1; cc < argc; cc ++ ) { this->ProcessArgument(argv[cc]); } } //---------------------------------------------------------------------------- void CommandLineArguments::Initialize(int argc, char* argv[]) { this->Initialize(argc, static_cast<const char* const*>(argv)); } //---------------------------------------------------------------------------- void CommandLineArguments::Initialize() { this->Internals->Argv.clear(); this->Internals->LastArgument = 0; } //---------------------------------------------------------------------------- void CommandLineArguments::ProcessArgument(const char* arg) { this->Internals->Argv.push_back(arg); } //---------------------------------------------------------------------------- bool CommandLineArguments::GetMatchedArguments( kwsys_stl::vector<kwsys_stl::string>* matches, const kwsys_stl::string& arg) { matches->clear(); 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; CommandLineArgumentsCallbackStructure *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); } } return matches->size() > 0; } //---------------------------------------------------------------------------- int CommandLineArguments::Parse() { kwsys_stl::vector<kwsys_stl::string>::size_type cc; kwsys_stl::vector<kwsys_stl::string> matches; if ( this->StoreUnusedArgumentsFlag ) { this->Internals->UnusedArguments.clear(); } for ( cc = 0; cc < this->Internals->Argv.size(); cc ++ ) { const kwsys_stl::string& arg = this->Internals->Argv[cc]; CommandLineArguments_DEBUG("Process argument: " << arg); this->Internals->LastArgument = cc; if ( this->GetMatchedArguments(&matches, arg) ) { // 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 CommandLineArgumentsCallbackStructure *cs = &this->Internals->Callbacks[matches[maxidx]]; const kwsys_stl::string& sarg = matches[maxidx]; if ( cs->Argument != sarg ) { abort(); } switch ( cs->ArgumentType ) { case NO_ARGUMENT: // No value if ( !this->PopulateVariable(cs, 0) ) { return 0; } break; case SPACE_ARGUMENT: if ( cc == this->Internals->Argv.size()-1 ) { this->Internals->LastArgument --; return 0; } CommandLineArguments_DEBUG("This is a space argument: " << arg << " value: " << this->Internals->Argv[cc+1].c_str()); // Value is the next argument if ( !this->PopulateVariable(cs, this->Internals->Argv[cc+1].c_str()) ) { return 0; } cc ++; break; case EQUAL_ARGUMENT: if ( arg.size() == sarg.size() || *(arg.c_str() + sarg.size()) != '=' ) { this->Internals->LastArgument --; return 0; } // Value is everythng followed the '=' sign if ( !this->PopulateVariable(cs, arg.c_str() + sarg.size() + 1) ) { return 0; } break; case CONCAT_ARGUMENT: // Value is whatever follows the argument if ( !this->PopulateVariable(cs, arg.c_str() + sarg.size()) ) { return 0; } break; case MULTI_ARGUMENT: // Suck in all the rest of the arguments CommandLineArguments_DEBUG("This is a multi argument: " << arg); for (cc++; cc < this->Internals->Argv.size(); ++ cc ) { const kwsys_stl::string& marg = this->Internals->Argv[cc]; CommandLineArguments_DEBUG(" check multi argument value: " << marg); if ( this->GetMatchedArguments(&matches, marg) ) { CommandLineArguments_DEBUG("End of multi argument " << arg << " with value: " << marg); break; } CommandLineArguments_DEBUG(" populate multi argument value: " << marg); if ( !this->PopulateVariable(cs, marg.c_str()) ) { return 0; } } if ( cc != this->Internals->Argv.size() ) { CommandLineArguments_DEBUG("Again End of multi argument " << arg); cc--; continue; } break; default: kwsys_ios::cerr << "Got unknown argument type: \"" << cs->ArgumentType << "\"" << kwsys_ios::endl; this->Internals->LastArgument --; return 0; } } else { // Handle unknown arguments if ( this->Internals->UnknownArgumentCallback ) { if ( !this->Internals->UnknownArgumentCallback(arg.c_str(), this->Internals->ClientData) ) { this->Internals->LastArgument --; return 0; } return 1; } else if ( this->StoreUnusedArgumentsFlag ) { CommandLineArguments_DEBUG("Store unused argument " << arg); this->Internals->UnusedArguments.push_back(arg.c_str()); } else { kwsys_ios::cerr << "Got unknown argument: \"" << arg.c_str() << "\"" << kwsys_ios::endl; this->Internals->LastArgument --; return 0; } } } 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; // Copy Argv0 as the first argument char** args = new char*[ size ]; args[0] = new char[ this->Internals->Argv0.size() + 1 ]; strcpy(args[0], this->Internals->Argv0.c_str()); int cnt = 1; // Copy everything after the LastArgument, since that was not parsed. for ( cc = this->Internals->LastArgument+1; 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::GetUnusedArguments(int* argc, char*** argv) { CommandLineArguments::Internal::VectorOfStrings::size_type size = this->Internals->UnusedArguments.size() + 1; CommandLineArguments::Internal::VectorOfStrings::size_type cc; // Copy Argv0 as the first argument char** args = new char*[ size ]; args[0] = new char[ this->Internals->Argv0.size() + 1 ]; strcpy(args[0], this->Internals->Argv0.c_str()); int cnt = 1; // Copy everything after the LastArgument, since that was not parsed. for ( cc = 0; cc < this->Internals->UnusedArguments.size(); cc ++ ) { kwsys::String &str = this->Internals->UnusedArguments[cc]; args[cnt] = new char[ str.size() + 1]; strcpy(args[cnt], str.c_str()); cnt ++; } *argc = cnt; *argv = args; } //---------------------------------------------------------------------------- void CommandLineArguments::DeleteRemainingArguments(int argc, char*** argv) { int cc; for ( cc = 0; cc < argc; ++ cc ) { delete [] (*argv)[cc]; } delete [] *argv; } //---------------------------------------------------------------------------- void CommandLineArguments::AddCallback(const char* argument, ArgumentTypeEnum type, CallbackType callback, void* call_data, const char* help) { CommandLineArgumentsCallbackStructure 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) { CommandLineArgumentsCallbackStructure 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(); } //---------------------------------------------------------------------------- #define CommandLineArgumentsAddArgumentMacro(type, ctype) \ void CommandLineArguments::AddArgument(const char* argument, ArgumentTypeEnum type, \ ctype* variable, const char* help) \ { \ this->AddArgument(argument, type, CommandLineArguments::type##_TYPE, variable, help); \ } CommandLineArgumentsAddArgumentMacro(BOOL, bool) CommandLineArgumentsAddArgumentMacro(INT, int) CommandLineArgumentsAddArgumentMacro(DOUBLE, double) CommandLineArgumentsAddArgumentMacro(STRING, char*) CommandLineArgumentsAddArgumentMacro(STL_STRING, kwsys_stl::string) CommandLineArgumentsAddArgumentMacro(VECTOR_BOOL, kwsys_stl::vector<bool>) CommandLineArgumentsAddArgumentMacro(VECTOR_INT, kwsys_stl::vector<int>) CommandLineArgumentsAddArgumentMacro(VECTOR_DOUBLE, kwsys_stl::vector<double>) CommandLineArgumentsAddArgumentMacro(VECTOR_STRING, kwsys_stl::vector<char*>) CommandLineArgumentsAddArgumentMacro(VECTOR_STL_STRING, kwsys_stl::vector<kwsys_stl::string>) //---------------------------------------------------------------------------- #define CommandLineArgumentsAddBooleanArgumentMacro(type, ctype) \ void CommandLineArguments::AddBooleanArgument(const char* argument, \ ctype* variable, const char* help) \ { \ this->AddArgument(argument, CommandLineArguments::NO_ARGUMENT, \ CommandLineArguments::type##_TYPE, variable, help); \ } CommandLineArgumentsAddBooleanArgumentMacro(BOOL, bool) CommandLineArgumentsAddBooleanArgumentMacro(INT, int) CommandLineArgumentsAddBooleanArgumentMacro(DOUBLE, double) CommandLineArgumentsAddBooleanArgumentMacro(STRING, char*) CommandLineArgumentsAddBooleanArgumentMacro(STL_STRING, kwsys_stl::string) //---------------------------------------------------------------------------- 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. CommandLineArgumentsCallbackStructure *cs = &(it->second); for(;;) { CommandLineArguments::Internal::CallbacksMap::iterator hit = this->Internals->Callbacks.find(cs->Help); if ( hit == this->Internals->Callbacks.end() ) { break; } cs = &(hit->second); } return cs->Help; } //---------------------------------------------------------------------------- void CommandLineArguments::SetLineLength(unsigned int ll) { if ( ll < 9 || ll > 1000 ) { return; } this->LineLength = ll; this->GenerateHelp(); } //---------------------------------------------------------------------------- const char* CommandLineArguments::GetArgv0() { return this->Internals->Argv0.c_str(); } //---------------------------------------------------------------------------- unsigned int CommandLineArguments::GetLastArgument() { return static_cast<unsigned int>(this->Internals->LastArgument + 1); } //---------------------------------------------------------------------------- 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 ++ ) { CommandLineArgumentsCallbackStructure *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 ++ ) { CommandLineArgumentsCallbackStructure *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 += 3; break; case CommandLineArguments::SPACE_ARGUMENT: clen += 4; break; case CommandLineArguments::EQUAL_ARGUMENT: clen += 4; break; } if ( clen > maxlen ) { maxlen = clen; } } } // Create format for that string char format[80]; sprintf(format, " %%-%ds ", static_cast<unsigned int>(maxlen)); maxlen += 4; // For the space before and after the option // 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 << kwsys_ios::endl; char argument[100]; sprintf(argument, "%s", sit->c_str()); switch ( this->Internals->Callbacks[*sit].ArgumentType ) { case CommandLineArguments::NO_ARGUMENT: break; case CommandLineArguments::CONCAT_ARGUMENT: strcat(argument, "opt"); break; case CommandLineArguments::SPACE_ARGUMENT: strcat(argument, " opt"); break; case CommandLineArguments::EQUAL_ARGUMENT: strcat(argument, "=opt"); break; case CommandLineArguments::MULTI_ARGUMENT: strcat(argument, " opt opt ..."); break; } char buffer[80]; sprintf(buffer, format, argument); str << buffer; } const char* ptr = this->Internals->Callbacks[mpit->first].Help; size_t 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 << " "; } } 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 << kwsys_ios::endl; ptr += skip; len -= skip; cnt ++; } } /* // This can help debugging help string str << endl; unsigned int cc; for ( cc = 0; cc < this->LineLength; cc ++ ) { str << cc % 10; } str << endl; */ this->Help = str.str(); } //---------------------------------------------------------------------------- void CommandLineArguments::PopulateVariable( bool* variable, const kwsys_stl::string& value) { if ( value == "1" || value == "ON" || value == "on" || value == "On" || value == "TRUE" || value == "true" || value == "True" || value == "yes" || value == "Yes" || value == "YES" ) { *variable = true; } else { *variable = false; } } //---------------------------------------------------------------------------- void CommandLineArguments::PopulateVariable( int* variable, const kwsys_stl::string& value) { char* res = 0; *variable = strtol(value.c_str(), &res, 10); //if ( res && *res ) // { // Can handle non-int // } } //---------------------------------------------------------------------------- void CommandLineArguments::PopulateVariable( double* variable, const kwsys_stl::string& value) { char* res = 0; *variable = strtod(value.c_str(), &res); //if ( res && *res ) // { // Can handle non-double // } } //---------------------------------------------------------------------------- void CommandLineArguments::PopulateVariable( char** variable, const kwsys_stl::string& value) { if ( *variable ) { delete [] *variable; *variable = 0; } *variable = new char[ value.size() + 1 ]; strcpy(*variable, value.c_str()); } //---------------------------------------------------------------------------- void CommandLineArguments::PopulateVariable( kwsys_stl::string* variable, const kwsys_stl::string& value) { *variable = value; } //---------------------------------------------------------------------------- void CommandLineArguments::PopulateVariable( kwsys_stl::vector<bool>* variable, const kwsys_stl::string& value) { bool val = false; if ( value == "1" || value == "ON" || value == "on" || value == "On" || value == "TRUE" || value == "true" || value == "True" || value == "yes" || value == "Yes" || value == "YES" ) { val = true; } variable->push_back(val); } //---------------------------------------------------------------------------- void CommandLineArguments::PopulateVariable( kwsys_stl::vector<int>* variable, const kwsys_stl::string& value) { char* res = 0; variable->push_back(strtol(value.c_str(), &res, 10)); //if ( res && *res ) // { // Can handle non-int // } } //---------------------------------------------------------------------------- void CommandLineArguments::PopulateVariable( kwsys_stl::vector<double>* variable, const kwsys_stl::string& value) { char* res = 0; variable->push_back(strtod(value.c_str(), &res)); //if ( res && *res ) // { // Can handle non-int // } } //---------------------------------------------------------------------------- void CommandLineArguments::PopulateVariable( kwsys_stl::vector<char*>* variable, const kwsys_stl::string& value) { char* var = new char[ value.size() + 1 ]; strcpy(var, value.c_str()); variable->push_back(var); } //---------------------------------------------------------------------------- void CommandLineArguments::PopulateVariable( kwsys_stl::vector<kwsys_stl::string>* variable, const kwsys_stl::string& value) { variable->push_back(value); } //---------------------------------------------------------------------------- bool CommandLineArguments::PopulateVariable(CommandLineArgumentsCallbackStructure* cs, const char* value) { // Call the callback if ( cs->Callback ) { if ( !cs->Callback(cs->Argument, value, cs->CallData) ) { this->Internals->LastArgument --; return 0; } } CommandLineArguments_DEBUG("Set argument: " << cs->Argument << " to " << value); if ( cs->Variable ) { kwsys_stl::string var = "1"; if ( value ) { var = value; } switch ( cs->VariableType ) { case CommandLineArguments::INT_TYPE: this->PopulateVariable(static_cast<int*>(cs->Variable), var); break; case CommandLineArguments::DOUBLE_TYPE: this->PopulateVariable(static_cast<double*>(cs->Variable), var); break; case CommandLineArguments::STRING_TYPE: this->PopulateVariable(static_cast<char**>(cs->Variable), var); break; case CommandLineArguments::STL_STRING_TYPE: this->PopulateVariable(static_cast<kwsys_stl::string*>(cs->Variable), var); break; case CommandLineArguments::BOOL_TYPE: this->PopulateVariable(static_cast<bool*>(cs->Variable), var); break; case CommandLineArguments::VECTOR_BOOL_TYPE: this->PopulateVariable(static_cast<kwsys_stl::vector<bool>*>(cs->Variable), var); break; case CommandLineArguments::VECTOR_INT_TYPE: this->PopulateVariable(static_cast<kwsys_stl::vector<int>*>(cs->Variable), var); break; case CommandLineArguments::VECTOR_DOUBLE_TYPE: this->PopulateVariable(static_cast<kwsys_stl::vector<double>*>(cs->Variable), var); break; case CommandLineArguments::VECTOR_STRING_TYPE: this->PopulateVariable(static_cast<kwsys_stl::vector<char*>*>(cs->Variable), var); break; case CommandLineArguments::VECTOR_STL_STRING_TYPE: this->PopulateVariable(static_cast<kwsys_stl::vector<kwsys_stl::string>*>(cs->Variable), var); break; default: kwsys_ios::cerr << "Got unknown variable type: \"" << cs->VariableType << "\"" << kwsys_ios::endl; this->Internals->LastArgument --; return 0; } } return 1; } } // namespace KWSYS_NAMESPACE