2015-09-06 20:19:09 +02:00
# include "./argumentparser.h"
2016-12-23 09:55:12 +01:00
# include "./argumentparserprivate.h"
2015-09-06 20:19:09 +02:00
# include "./commandlineutils.h"
# include "./failure.h"
2015-05-08 23:20:47 +02:00
2015-09-06 20:19:09 +02:00
# include "../conversion/stringconversion.h"
2017-01-27 18:51:54 +01:00
# include "../conversion/stringbuilder.h"
2016-07-03 22:36:48 +02:00
# include "../io/path.h"
2016-07-31 23:20:31 +02:00
# include "../io/ansiescapecodes.h"
2015-04-22 18:36:40 +02:00
# include <algorithm>
# include <iostream>
2016-06-12 01:56:57 +02:00
# include <string>
2015-04-22 18:36:40 +02:00
# include <sstream>
2016-06-12 01:56:57 +02:00
# include <cstring>
2016-07-17 01:26:34 +02:00
# include <cstdlib>
2015-04-22 18:36:40 +02:00
using namespace std ;
using namespace std : : placeholders ;
2016-06-12 01:56:57 +02:00
using namespace ConversionUtilities ;
2016-07-03 22:36:48 +02:00
using namespace IoUtilities ;
2015-04-22 18:36:40 +02:00
/*!
2016-06-12 01:56:57 +02:00
* \ namespace ApplicationUtilities
* \ brief Contains currently only ArgumentParser and related classes .
*/
2015-04-22 18:36:40 +02:00
namespace ApplicationUtilities {
2016-12-23 09:55:12 +01:00
/*!
* \ brief The ArgumentDenotationType enum specifies the type of a given argument denotation .
*/
enum ArgumentDenotationType : unsigned char {
Value = 0 , /**< parameter value */
Abbreviation = 1 , /**< argument abbreviation */
FullName = 2 /**< full argument name */
} ;
/*!
* \ brief The ArgReader struct internally encapsulates the process of reading command line arguments .
* \ remarks
* - For meaning of parameter see documentation of corresponding member variables .
* - Results are stored in specified \ a args and assigned sub arguments .
*/
ArgumentReader : : ArgumentReader ( ArgumentParser & parser , const char * const * argv , const char * const * end , bool completionMode ) :
parser ( parser ) ,
args ( parser . m_mainArgs ) ,
index ( 0 ) ,
argv ( argv ) ,
end ( end ) ,
lastArg ( nullptr ) ,
argDenotation ( nullptr ) ,
completionMode ( completionMode )
{ }
2016-12-23 22:41:06 +01:00
/*!
* \ brief Resets the ArgumentReader to continue reading new \ a argv .
*/
2016-12-23 09:55:12 +01:00
ArgumentReader & ArgumentReader : : reset ( const char * const * argv , const char * const * end )
{
this - > argv = argv ;
this - > end = end ;
index = 0 ;
lastArg = nullptr ;
argDenotation = nullptr ;
return * this ;
}
/*!
* \ brief Reads the commands line arguments specified when constructing the object .
2016-12-23 22:41:06 +01:00
* \ remarks Reads on main - argument - level .
2016-12-23 09:55:12 +01:00
*/
void ArgumentReader : : read ( )
{
read ( args ) ;
}
/*!
* \ brief Reads the commands line arguments specified when constructing the object .
2016-12-23 22:41:06 +01:00
* \ remarks Reads on custom argument - level specified via \ a args .
2016-12-23 09:55:12 +01:00
*/
void ArgumentReader : : read ( ArgumentVector & args )
{
// method is called recursively for sub args to the last argument (which is nullptr in the initial call) is the current parent argument
Argument * const parentArg = lastArg ;
// determine the current path
const vector < Argument * > & parentPath = parentArg ? parentArg - > path ( parentArg - > occurrences ( ) - 1 ) : vector < Argument * > ( ) ;
Argument * lastArgInLevel = nullptr ;
vector < const char * > * values = nullptr ;
// iterate through all argument denotations; loop might exit earlier when an denotation is unknown
while ( argv ! = end ) {
if ( values & & lastArgInLevel - > requiredValueCount ( ) ! = static_cast < size_t > ( - 1 ) & & values - > size ( ) < lastArgInLevel - > requiredValueCount ( ) ) {
// there are still values to read
values - > emplace_back ( argDenotation ? argDenotation : * argv ) ;
+ + index , + + argv , argDenotation = nullptr ;
} else {
// determine how denotation must be processed
bool abbreviationFound = false ;
if ( argDenotation ) {
// continue reading childs for abbreviation denotation already detected
abbreviationFound = false ;
argDenotationType = Abbreviation ;
} else {
// determine denotation type
argDenotation = * argv ;
if ( ! * argDenotation & & ( ! lastArgInLevel | | values - > size ( ) > = lastArgInLevel - > requiredValueCount ( ) ) ) {
// skip empty arguments
+ + index , + + argv , argDenotation = nullptr ;
continue ;
}
abbreviationFound = false ;
argDenotationType = Value ;
* argDenotation = = ' - ' & & ( + + argDenotation , + + argDenotationType )
& & * argDenotation = = ' - ' & & ( + + argDenotation , + + argDenotationType ) ;
}
// try to find matching Argument instance
Argument * matchingArg = nullptr ;
size_t argDenotationLength ;
if ( argDenotationType ! = Value ) {
const char * const equationPos = strchr ( argDenotation , ' = ' ) ;
for ( argDenotationLength = equationPos ? static_cast < size_t > ( equationPos - argDenotation ) : strlen ( argDenotation ) ; argDenotationLength ; matchingArg = nullptr ) {
// search for arguments by abbreviation or name depending on the previously determined denotation type
if ( argDenotationType = = Abbreviation ) {
for ( Argument * arg : args ) {
if ( arg - > abbreviation ( ) & & arg - > abbreviation ( ) = = * argDenotation ) {
matchingArg = arg ;
abbreviationFound = true ;
break ;
}
}
} else {
for ( Argument * arg : args ) {
if ( arg - > name ( ) & & ! strncmp ( arg - > name ( ) , argDenotation , argDenotationLength ) & & * ( arg - > name ( ) + argDenotationLength ) = = ' \0 ' ) {
matchingArg = arg ;
break ;
}
}
}
if ( matchingArg ) {
// an argument matched the specified denotation so add an occurrence
matchingArg - > m_occurrences . emplace_back ( index , parentPath , parentArg ) ;
// prepare reading parameter values
values = & matchingArg - > m_occurrences . back ( ) . values ;
if ( equationPos ) {
values - > push_back ( equationPos + 1 ) ;
}
// read sub arguments
2016-12-23 22:41:06 +01:00
+ + index , + + parser . m_actualArgc , lastArg = lastArgInLevel = matchingArg , lastArgDenotation = argv ;
2016-12-23 09:55:12 +01:00
if ( argDenotationType ! = Abbreviation | | ( + + argDenotation ! = equationPos ) ) {
if ( argDenotationType ! = Abbreviation | | ! * argDenotation ) {
// no further abbreviations follow -> read sub args for next argv
+ + argv , argDenotation = nullptr ;
read ( lastArg - > m_subArgs ) ;
argDenotation = nullptr ;
} else {
// further abbreviations follow -> don't increment argv, keep processing outstanding chars of argDenotation
read ( lastArg - > m_subArgs ) ;
}
break ;
} // else: another abbreviated argument follows (and it is not present in the sub args)
} else {
break ;
}
}
}
if ( ! matchingArg ) {
// unknown argument might be a sibling of the parent element
if ( argDenotationType ! = Value ) {
for ( auto parentArgument = parentPath . crbegin ( ) , pathEnd = parentPath . crend ( ) ; ; + + parentArgument ) {
for ( Argument * sibling : ( parentArgument ! = pathEnd ? ( * parentArgument ) - > subArguments ( ) : parser . m_mainArgs ) ) {
if ( sibling - > occurrences ( ) < sibling - > maxOccurrences ( ) ) {
if ( ( argDenotationType = = Abbreviation & & ( sibling - > abbreviation ( ) & & sibling - > abbreviation ( ) = = * argDenotation ) )
| | ( sibling - > name ( ) & & ! strncmp ( sibling - > name ( ) , argDenotation , argDenotationLength ) ) ) {
return ;
}
}
}
if ( parentArgument = = pathEnd ) {
break ;
}
} ;
}
// unknown argument might just be a parameter value of the last argument
if ( lastArgInLevel & & values - > size ( ) < lastArgInLevel - > requiredValueCount ( ) ) {
values - > emplace_back ( abbreviationFound ? argDenotation : * argv ) ;
+ + index , + + argv , argDenotation = nullptr ;
continue ;
}
// first value might denote "operation"
if ( ! index ) {
for ( Argument * arg : args ) {
if ( arg - > denotesOperation ( ) & & arg - > name ( ) & & ! strcmp ( arg - > name ( ) , * argv ) ) {
( matchingArg = arg ) - > m_occurrences . emplace_back ( index , parentPath , parentArg ) ;
2016-12-23 22:41:06 +01:00
lastArgDenotation = argv ;
2016-12-23 09:55:12 +01:00
+ + index , + + argv ;
break ;
}
}
}
// use the first default argument which is not already present if there is still no match
if ( ! matchingArg & & ( ! completionMode | | ( argv + 1 ! = end ) ) ) {
const bool uncombinableMainArgPresent = parentArg ? false : parser . isUncombinableMainArgPresent ( ) ;
for ( Argument * arg : args ) {
if ( arg - > isImplicit ( ) & & ! arg - > isPresent ( ) & & ! arg - > wouldConflictWithArgument ( ) & & ( ! uncombinableMainArgPresent | | ! arg - > isMainArgument ( ) ) ) {
( matchingArg = arg ) - > m_occurrences . emplace_back ( index , parentPath , parentArg ) ;
break ;
}
}
}
if ( matchingArg ) {
// an argument matched the specified denotation
if ( lastArgInLevel = = matchingArg ) {
break ; // break required? -> TODO: add test for this condition
}
// prepare reading parameter values
values = & matchingArg - > m_occurrences . back ( ) . values ;
// read sub arguments
+ + parser . m_actualArgc , lastArg = lastArgInLevel = matchingArg , argDenotation = nullptr ;
read ( lastArg - > m_subArgs ) ;
argDenotation = nullptr ;
continue ;
}
// argument denotation is unknown -> handle error
if ( parentArg ) {
// continue with parent level
return ;
}
if ( completionMode ) {
// ignore unknown denotation
+ + index , + + argv , argDenotation = nullptr ;
} else {
switch ( parser . m_unknownArgBehavior ) {
case UnknownArgumentBehavior : : Warn :
cerr < < " The specified argument \" " < < * argv < < " \" is unknown and will be ignored. " < < endl ;
FALLTHROUGH ;
case UnknownArgumentBehavior : : Ignore :
// ignore unknown denotation
+ + index , + + argv , argDenotation = nullptr ;
break ;
case UnknownArgumentBehavior : : Fail :
2017-02-06 18:23:59 +01:00
throw Failure ( " The specified argument \" " s % * argv + " \" is unknown and will be ignored. " s ) ;
2016-12-23 09:55:12 +01:00
}
}
} // if(!matchingArg)
} // no values to read
} // while(argv != end)
}
2016-10-30 00:30:54 +02:00
/// \brief Specifies the name of the application (used by ArgumentParser::printHelp()).
2015-08-25 19:12:05 +02:00
const char * applicationName = nullptr ;
2016-10-30 00:30:54 +02:00
/// \brief Specifies the author of the application (used by ArgumentParser::printHelp()).
2015-08-25 19:12:05 +02:00
const char * applicationAuthor = nullptr ;
2016-10-30 00:30:54 +02:00
/// \brief Specifies the version of the application (used by ArgumentParser::printHelp()).
2015-08-25 19:12:05 +02:00
const char * applicationVersion = nullptr ;
2016-10-30 00:30:54 +02:00
/// \brief Specifies the URL to the application website (used by ArgumentParser::printHelp()).
2015-08-25 19:12:05 +02:00
const char * applicationUrl = nullptr ;
2016-10-30 00:37:28 +02:00
/*!
* \ brief Specifies a function quit the application .
* \ remarks Currently only used after printing Bash completion . Default is std : : exit ( ) .
*/
void ( * exitFunction ) ( int ) = & exit ;
2016-10-30 00:30:54 +02:00
/// \cond
2016-05-25 01:24:17 +02:00
inline bool notEmpty ( const char * str )
{
return str & & * str ;
}
2016-06-12 01:56:57 +02:00
/// \endcond
2015-04-22 18:36:40 +02:00
/*!
* \ class ApplicationUtilities : : Argument
* \ brief The Argument class is a wrapper for command line argument information .
*
* Instaces of the Argument class are used as definition when parsing command line
* arguments . Arguments can be assigned to an ArgumentParser using
* ArgumentParser : : setMainArguments ( ) and to another Argument instance using
* Argument : : setSecondaryArguments ( ) .
*/
/*!
2016-02-27 01:19:16 +01:00
* \ brief Constructs an Argument with the given \ a name , \ a abbreviation and \ a description .
2015-04-22 18:36:40 +02:00
*
* The \ a name and the abbreviation mustn ' t contain any whitespaces .
* The \ a name mustn ' t be empty . The \ a abbreviation and the \ a description might be empty .
*/
2016-06-12 01:56:57 +02:00
Argument : : Argument ( const char * name , char abbreviation , const char * description , const char * example ) :
2016-05-25 01:24:17 +02:00
m_name ( name ) ,
m_abbreviation ( abbreviation ) ,
2016-07-17 01:26:34 +02:00
m_environmentVar ( nullptr ) ,
2016-05-25 01:24:17 +02:00
m_description ( description ) ,
m_example ( example ) ,
2016-06-12 01:56:57 +02:00
m_minOccurrences ( 0 ) ,
m_maxOccurrences ( 1 ) ,
2015-04-22 18:36:40 +02:00
m_combinable ( false ) ,
2015-05-08 23:20:47 +02:00
m_denotesOperation ( false ) ,
2015-04-22 18:36:40 +02:00
m_requiredValueCount ( 0 ) ,
2016-06-14 00:43:32 +02:00
m_implicit ( false ) ,
2016-07-03 22:36:48 +02:00
m_isMainArg ( false ) ,
m_valueCompletionBehavior ( ValueCompletionBehavior : : PreDefinedValues | ValueCompletionBehavior : : Files | ValueCompletionBehavior : : Directories | ValueCompletionBehavior : : FileSystemIfNoPreDefinedValues ) ,
m_preDefinedCompletionValues ( nullptr )
2016-05-25 01:24:17 +02:00
{ }
2015-04-22 18:36:40 +02:00
/*!
2016-02-27 01:19:16 +01:00
* \ brief Destroys the Argument .
2015-04-22 18:36:40 +02:00
*/
Argument : : ~ Argument ( )
{ }
/*!
2016-07-31 23:20:31 +02:00
* \ brief Returns the first parameter value of the first occurrence of the argument .
2016-07-17 01:26:34 +02:00
* \ remarks
* - If the argument is not present and the an environment variable has been set
* using setEnvironmentVariable ( ) the value of the specified variable will be returned .
* - Returns nullptr if no value is available though .
*/
const char * Argument : : firstValue ( ) const
{
2016-07-31 23:20:31 +02:00
if ( ! m_occurrences . empty ( ) & & ! m_occurrences . front ( ) . values . empty ( ) ) {
return m_occurrences . front ( ) . values . front ( ) ;
2016-07-17 01:26:34 +02:00
} else if ( m_environmentVar ) {
return getenv ( m_environmentVar ) ;
} else {
return nullptr ;
}
}
/*!
* \ brief Writes the name , the abbreviation and other information about the Argument to the give ostream .
2015-04-22 18:36:40 +02:00
*/
2016-10-02 21:53:58 +02:00
void Argument : : printInfo ( ostream & os , unsigned char indentation ) const
2015-04-22 18:36:40 +02:00
{
2016-10-02 21:53:58 +02:00
os < < Indentation ( indentation ) ;
2016-07-31 23:20:31 +02:00
EscapeCodes : : setStyle ( os , EscapeCodes : : TextAttribute : : Bold ) ;
2016-05-25 01:24:17 +02:00
if ( notEmpty ( name ( ) ) ) {
2016-06-12 01:56:57 +02:00
os < < ' - ' < < ' - ' < < name ( ) ;
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
if ( notEmpty ( name ( ) ) & & abbreviation ( ) ) {
os < < ' , ' < < ' ' ;
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
if ( abbreviation ( ) ) {
os < < ' - ' < < abbreviation ( ) ;
2015-04-22 18:36:40 +02:00
}
2016-07-31 23:20:31 +02:00
EscapeCodes : : setStyle ( os ) ;
2016-07-17 01:26:34 +02:00
if ( requiredValueCount ( ) ) {
2016-06-12 01:56:57 +02:00
unsigned int valueNamesPrint = 0 ;
2015-04-22 18:36:40 +02:00
for ( auto i = valueNames ( ) . cbegin ( ) , end = valueNames ( ) . cend ( ) ; i ! = end & & valueNamesPrint < requiredValueCount ( ) ; + + i ) {
2016-06-12 01:56:57 +02:00
os < < ' ' < < ' [ ' < < * i < < ' ] ' ;
2015-04-22 18:36:40 +02:00
+ + valueNamesPrint ;
}
2016-06-12 01:56:57 +02:00
if ( requiredValueCount ( ) = = static_cast < size_t > ( - 1 ) ) {
os < < " ... " ;
} else {
for ( ; valueNamesPrint < requiredValueCount ( ) ; + + valueNamesPrint ) {
os < < " [value " < < ( valueNamesPrint + 1 ) < < ' ] ' ;
}
2015-04-22 18:36:40 +02:00
}
}
2016-10-02 21:53:58 +02:00
indentation + = 2 ;
2016-05-25 01:24:17 +02:00
if ( notEmpty ( description ( ) ) ) {
2016-10-02 21:53:58 +02:00
os < < ' \n ' < < Indentation ( indentation ) < < description ( ) ;
2015-04-22 18:36:40 +02:00
}
if ( isRequired ( ) ) {
2016-10-02 21:53:58 +02:00
os < < ' \n ' < < Indentation ( indentation ) < < " particularities: mandatory " ;
if ( ! isMainArgument ( ) ) {
os < < " if parent argument is present " ;
}
}
if ( environmentVariable ( ) ) {
os < < ' \n ' < < Indentation ( indentation ) < < " default environment variable: " < < environmentVariable ( ) ;
2015-04-22 18:36:40 +02:00
}
2016-05-25 01:24:17 +02:00
if ( notEmpty ( example ( ) ) ) {
2016-10-02 21:53:58 +02:00
os < < ' \n ' < < Indentation ( indentation ) < < " \n usage: " < < example ( ) ;
2015-10-06 22:27:16 +02:00
}
2016-10-02 21:53:58 +02:00
os < < ' \n ' ;
2016-06-11 19:41:40 +02:00
for ( const auto * arg : subArguments ( ) ) {
2016-10-02 21:53:58 +02:00
arg - > printInfo ( os , indentation ) ;
2015-04-22 18:36:40 +02:00
}
}
/*!
2016-02-27 01:19:16 +01:00
* \ brief This function return the first present and uncombinable argument of the given list of arguments .
*
2015-04-22 18:36:40 +02:00
* The Argument \ a except will be ignored .
*/
Argument * firstPresentUncombinableArg ( const ArgumentVector & args , const Argument * except )
{
for ( Argument * arg : args ) {
if ( arg ! = except & & arg - > isPresent ( ) & & ! arg - > isCombinable ( ) ) {
return arg ;
}
}
return nullptr ;
}
/*!
2016-02-27 01:19:16 +01:00
* \ brief Sets the secondary arguments for this arguments .
*
* The given arguments will be considered as secondary arguments of this argument by the argument parser .
* This means that the parser will complain if these arguments are given , but not this argument .
* If secondary arguments are labeled as mandatory their parent is also mandatory .
2015-04-22 18:36:40 +02:00
*
* The Argument does not take ownership . Do not destroy the given arguments as long as they are
* used as secondary arguments .
*
* \ sa secondaryArguments ( )
2015-07-27 23:19:10 +02:00
* \ sa addSecondaryArgument ( )
2015-04-22 18:36:40 +02:00
* \ sa hasSecondaryArguments ( )
*/
2016-06-11 19:41:40 +02:00
void Argument : : setSubArguments ( const ArgumentInitializerList & secondaryArguments )
2015-04-22 18:36:40 +02:00
{
// remove this argument from the parents list of the previous secondary arguments
2016-06-11 19:41:40 +02:00
for ( Argument * arg : m_subArgs ) {
2015-04-22 18:36:40 +02:00
arg - > m_parents . erase ( remove ( arg - > m_parents . begin ( ) , arg - > m_parents . end ( ) , this ) , arg - > m_parents . end ( ) ) ;
}
// assign secondary arguments
2016-06-11 19:41:40 +02:00
m_subArgs . assign ( secondaryArguments ) ;
2015-04-22 18:36:40 +02:00
// add this argument to the parents list of the assigned secondary arguments
// and set the parser
2016-06-11 19:41:40 +02:00
for ( Argument * arg : m_subArgs ) {
2015-04-22 18:36:40 +02:00
if ( find ( arg - > m_parents . cbegin ( ) , arg - > m_parents . cend ( ) , this ) = = arg - > m_parents . cend ( ) ) {
arg - > m_parents . push_back ( this ) ;
}
}
}
2015-07-27 23:19:10 +02:00
/*!
2016-02-27 01:19:16 +01:00
* \ brief Adds \ a arg as a secondary argument for this argument .
2015-07-27 23:19:10 +02:00
*
* \ sa secondaryArguments ( )
* \ sa setSecondaryArguments ( )
* \ sa hasSecondaryArguments ( )
*/
2016-06-11 19:41:40 +02:00
void Argument : : addSubArgument ( Argument * arg )
2015-07-27 23:19:10 +02:00
{
2016-06-11 19:41:40 +02:00
if ( find ( m_subArgs . cbegin ( ) , m_subArgs . cend ( ) , arg ) = = m_subArgs . cend ( ) ) {
m_subArgs . push_back ( arg ) ;
2015-07-27 23:19:10 +02:00
if ( find ( arg - > m_parents . cbegin ( ) , arg - > m_parents . cend ( ) , this ) = = arg - > m_parents . cend ( ) ) {
arg - > m_parents . push_back ( this ) ;
}
}
}
2015-04-22 18:36:40 +02:00
/*!
2016-06-12 01:56:57 +02:00
* \ brief Returns whether at least one parent argument is present .
* \ remarks Returns always true for main arguments .
2015-04-22 18:36:40 +02:00
*/
bool Argument : : isParentPresent ( ) const
{
2016-06-12 01:56:57 +02:00
if ( isMainArgument ( ) ) {
return true ;
}
for ( const Argument * parent : m_parents ) {
2015-04-22 18:36:40 +02:00
if ( parent - > isPresent ( ) ) {
return true ;
}
}
return false ;
}
/*!
2016-02-27 01:19:16 +01:00
* \ brief Checks if this arguments conflicts with other arguments .
*
* If the argument is in conflict with an other argument this argument will be returned .
* Otherwise nullptr will be returned .
2016-11-15 21:44:23 +01:00
*
* \ remarks Conflicts with main arguments aren ' t considered by this method !
2015-04-22 18:36:40 +02:00
*/
Argument * Argument : : conflictsWithArgument ( ) const
{
2016-11-15 21:44:23 +01:00
return isPresent ( ) ? wouldConflictWithArgument ( ) : nullptr ;
}
/*!
* \ brief Checks if this argument would conflict with other arguments if it was present .
*
* If the argument is in conflict with an other argument this argument will be returned .
* Otherwise nullptr will be returned .
*
* \ remarks Conflicts with main arguments aren ' t considered by this method !
*/
Argument * Argument : : wouldConflictWithArgument ( ) const
{
if ( ! isCombinable ( ) ) {
2015-04-22 18:36:40 +02:00
for ( Argument * parent : m_parents ) {
2016-06-11 19:41:40 +02:00
for ( Argument * sibling : parent - > subArguments ( ) ) {
2015-04-22 18:36:40 +02:00
if ( sibling ! = this & & sibling - > isPresent ( ) & & ! sibling - > isCombinable ( ) ) {
return sibling ;
}
}
}
}
return nullptr ;
}
2016-11-26 00:11:48 +01:00
/*!
* \ brief Resets this argument and all sub arguments recursively .
* \ sa Argument : : reset ( )
*/
void Argument : : resetRecursively ( )
{
for ( Argument * arg : m_subArgs ) {
arg - > resetRecursively ( ) ;
}
reset ( ) ;
}
2015-04-22 18:36:40 +02:00
/*!
* \ class ApplicationUtilities : : ArgumentParser
* \ brief The ArgumentParser class provides a means for handling command line arguments .
*
* To setup the parser create instances of ApplicationUtilities : : Argument to define a
* set of known arguments and assign these to the parser using setMainArguments ( ) .
*
* To invoke parsing call parseArgs ( ) . The parser will verify the previously
* assigned definitions ( and might throw std : : invalid_argument ) and then parse the
* given command line arguments according the definitions ( and might throw
* ApplicationUtilities : : Failure ) .
*/
/*!
2016-02-27 01:19:16 +01:00
* \ brief Constructs a new ArgumentParser .
2015-04-22 18:36:40 +02:00
*/
ArgumentParser : : ArgumentParser ( ) :
m_actualArgc ( 0 ) ,
2016-06-14 00:43:32 +02:00
m_executable ( nullptr ) ,
2016-07-03 22:36:48 +02:00
m_unknownArgBehavior ( UnknownArgumentBehavior : : Fail ) ,
2016-06-14 00:43:32 +02:00
m_defaultArg ( nullptr )
2015-04-22 18:36:40 +02:00
{ }
/*!
2016-02-27 01:19:16 +01:00
* \ brief Sets the main arguments for the parser . The parser will use these argument definitions
* to when parsing the command line arguments and when printing help information .
* \ remarks
2016-06-14 00:43:32 +02:00
* - The parser does not take ownership . Do not destroy the arguments as long as they are used as
* main arguments .
2016-08-05 01:43:46 +02:00
* - Sets the first specified argument as default argument if none has been assigned yet and the
* first argument does not require any values or has no mandatory sub arguments .
2015-04-22 18:36:40 +02:00
*/
void ArgumentParser : : setMainArguments ( const ArgumentInitializerList & mainArguments )
{
2016-06-14 00:43:32 +02:00
if ( mainArguments . size ( ) ) {
for ( Argument * arg : mainArguments ) {
arg - > m_isMainArg = true ;
}
m_mainArgs . assign ( mainArguments ) ;
if ( ! m_defaultArg ) {
if ( ! ( * mainArguments . begin ( ) ) - > requiredValueCount ( ) ) {
bool subArgsRequired = false ;
2016-08-05 01:43:46 +02:00
for ( const Argument * subArg : ( * mainArguments . begin ( ) ) - > subArguments ( ) ) {
2016-06-14 00:43:32 +02:00
if ( subArg - > isRequired ( ) ) {
subArgsRequired = true ;
break ;
}
}
if ( ! subArgsRequired ) {
m_defaultArg = * mainArguments . begin ( ) ;
}
}
}
} else {
m_mainArgs . clear ( ) ;
2015-04-22 18:36:40 +02:00
}
}
/*!
2016-02-27 01:19:16 +01:00
* \ brief Adds the specified \ a argument to the main argument .
* \ remarks
* The parser does not take ownership . Do not destroy the argument as long as it is used as
* main argument .
*/
void ArgumentParser : : addMainArgument ( Argument * argument )
{
argument - > m_isMainArg = true ;
m_mainArgs . push_back ( argument ) ;
}
/*!
2016-07-17 01:26:34 +02:00
* \ brief Prints help text for all assigned arguments .
2015-04-22 18:36:40 +02:00
*/
void ArgumentParser : : printHelp ( ostream & os ) const
{
2016-07-31 23:20:31 +02:00
EscapeCodes : : setStyle ( os , EscapeCodes : : TextAttribute : : Bold ) ;
2015-08-25 19:12:05 +02:00
if ( applicationName & & * applicationName ) {
os < < applicationName ;
if ( applicationVersion & & * applicationVersion ) {
os < < ' , ' < < ' ' ;
}
}
if ( applicationVersion & & * applicationVersion ) {
os < < " version " < < applicationVersion ;
}
if ( ( applicationName & & * applicationName ) | | ( applicationVersion & & * applicationVersion ) ) {
os < < ' \n ' < < ' \n ' ;
}
2016-07-31 23:20:31 +02:00
EscapeCodes : : setStyle ( os ) ;
2015-08-25 19:12:05 +02:00
if ( ! m_mainArgs . empty ( ) ) {
2016-07-31 23:20:31 +02:00
os < < " Available arguments: " ;
for ( const Argument * arg : m_mainArgs ) {
os < < ' \n ' ;
2015-08-25 19:12:05 +02:00
arg - > printInfo ( os ) ;
}
2015-04-22 18:36:40 +02:00
}
2015-08-25 19:12:05 +02:00
if ( applicationUrl & & * applicationUrl ) {
os < < " \n Project website: " < < applicationUrl < < endl ;
2015-04-22 18:36:40 +02:00
}
}
2016-06-12 01:56:57 +02:00
/*!
* \ brief Parses the specified command line arguments .
* \ remarks
* - The results are stored in the Argument instances assigned as main arguments and sub arguments .
* - Calls the assigned callbacks if no constraints are violated .
2016-10-02 21:53:58 +02:00
* \ throws Throws Failure if the specified arguments are invalid or violate the constraints defined
2016-06-12 01:56:57 +02:00
* by the Argument instances .
2016-10-02 21:53:58 +02:00
* \ sa readArgs ( )
2016-06-12 01:56:57 +02:00
*/
2016-06-17 17:55:09 +02:00
void ArgumentParser : : parseArgs ( int argc , const char * const * argv )
2016-06-12 01:56:57 +02:00
{
2016-10-02 21:53:58 +02:00
readArgs ( argc , argv ) ;
if ( argc ) {
checkConstraints ( m_mainArgs ) ;
invokeCallbacks ( m_mainArgs ) ;
2016-07-03 22:36:48 +02:00
}
2016-10-02 21:53:58 +02:00
}
/*!
* \ brief Parses the specified command line arguments .
* \ remarks
* - The results are stored in the Argument instances assigned as main arguments and sub arguments .
* - In contrast to parseArgs ( ) this method does not check whether constraints are violated and it
* does not call any callbacks .
* \ throws Throws Failure if the specified arguments are invalid .
* \ sa readArgs ( )
*/
void ArgumentParser : : readArgs ( int argc , const char * const * argv )
{
2016-11-15 22:02:40 +01:00
IF_DEBUG_BUILD ( verifyArgs ( m_mainArgs , std : : vector < char > ( ) , std : : vector < const char * > ( ) ) ; )
2016-10-02 21:53:58 +02:00
m_actualArgc = 0 ;
2016-06-14 00:43:32 +02:00
if ( argc ) {
2016-07-03 22:36:48 +02:00
// the first argument is the executable name
2016-06-14 00:43:32 +02:00
m_executable = * argv ;
2016-07-03 22:36:48 +02:00
// check for further arguments
2016-06-14 00:43:32 +02:00
if ( - - argc ) {
2016-07-03 22:36:48 +02:00
// if the first argument (after executable name) is "--bash-completion-for" bash completion for the following arguments is requested
bool completionMode = ! strcmp ( * + + argv , " --bash-completion-for " ) ;
unsigned int currentWordIndex ;
if ( completionMode ) {
// the first argument after "--bash-completion-for" is the index of the current word
try {
currentWordIndex = ( - - argc ? stringToNumber < unsigned int , string > ( * ( + + argv ) ) : 0 ) ;
+ + argv , - - argc ;
} catch ( const ConversionException & ) {
2016-10-02 21:53:58 +02:00
currentWordIndex = static_cast < unsigned int > ( argc - 1 ) ;
2016-07-03 22:36:48 +02:00
}
}
// read specified arguments
2016-12-23 09:55:12 +01:00
ArgumentReader reader ( * this , argv , argv + ( completionMode ? min ( static_cast < unsigned int > ( argc ) , currentWordIndex + 1 ) : static_cast < unsigned int > ( argc ) ) , completionMode ) ;
2016-07-03 22:36:48 +02:00
try {
2016-12-23 09:55:12 +01:00
reader . read ( ) ;
2016-07-03 22:36:48 +02:00
} catch ( const Failure & ) {
if ( ! completionMode ) {
throw ;
}
}
if ( completionMode ) {
2016-12-23 22:41:06 +01:00
printBashCompletion ( argc , argv , currentWordIndex , reader ) ;
2016-10-30 00:37:28 +02:00
exitFunction ( 0 ) ; // prevent the applicaton to continue with the regular execution
2016-07-03 22:36:48 +02:00
}
2016-06-14 00:43:32 +02:00
} else {
2016-07-03 22:36:48 +02:00
// no arguments specified -> flag default argument as present if one is assigned
2016-06-14 00:43:32 +02:00
if ( m_defaultArg ) {
2016-07-31 23:20:31 +02:00
m_defaultArg - > m_occurrences . emplace_back ( 0 ) ;
2016-06-14 00:43:32 +02:00
}
}
2016-06-12 01:56:57 +02:00
} else {
2016-06-14 00:43:32 +02:00
m_executable = nullptr ;
2016-06-12 01:56:57 +02:00
}
}
2016-11-26 00:11:48 +01:00
/*!
* \ brief Resets all Argument instances assigned as mainArguments ( ) and sub arguments .
* \ sa Argument : : reset ( )
*/
void ArgumentParser : : resetArgs ( )
{
for ( Argument * arg : m_mainArgs ) {
arg - > resetRecursively ( ) ;
}
2016-12-23 22:41:06 +01:00
m_actualArgc = 0 ;
2016-11-26 00:11:48 +01:00
}
2016-11-15 21:44:23 +01:00
/*!
* \ brief Checks whether at least one uncombinable main argument is present .
*/
bool ArgumentParser : : isUncombinableMainArgPresent ( ) const
{
for ( const Argument * arg : m_mainArgs ) {
if ( ! arg - > isCombinable ( ) & & arg - > isPresent ( ) ) {
return true ;
}
}
return false ;
}
2016-05-25 01:24:17 +02:00
# ifdef DEBUG_BUILD
2015-04-22 18:36:40 +02:00
/*!
2016-06-12 01:56:57 +02:00
* \ brief Verifies the specified \ a argument definitions .
2015-04-22 18:36:40 +02:00
*
2016-06-12 01:56:57 +02:00
* Asserts that
* - The same argument has not been added twice to the same parent .
* - Only one argument within a parent is default or implicit .
* - Only main arguments denote operations .
* - Argument abbreviations are unique within one parent .
* - Argument names are unique within one parent .
2015-04-22 18:36:40 +02:00
*
2016-06-12 01:56:57 +02:00
* \ remarks
* - Verifies the sub arguments , too .
* - For debugging purposes only ; hence only available in debug builds .
2015-04-22 18:36:40 +02:00
*/
2016-11-15 22:02:40 +01:00
void ApplicationUtilities : : ArgumentParser : : verifyArgs ( const ArgumentVector & args , vector < char > abbreviations , vector < const char * > names )
2015-04-22 18:36:40 +02:00
{
2016-06-12 01:56:57 +02:00
vector < const Argument * > verifiedArgs ;
verifiedArgs . reserve ( args . size ( ) ) ;
2016-11-15 22:02:40 +01:00
abbreviations . reserve ( abbreviations . size ( ) + args . size ( ) ) ;
names . reserve ( names . size ( ) + args . size ( ) ) ;
2016-06-14 00:43:32 +02:00
bool hasImplicit = false ;
2016-06-12 01:56:57 +02:00
for ( const Argument * arg : args ) {
assert ( find ( verifiedArgs . cbegin ( ) , verifiedArgs . cend ( ) , arg ) = = verifiedArgs . cend ( ) ) ;
verifiedArgs . push_back ( arg ) ;
assert ( arg - > isMainArgument ( ) | | ! arg - > denotesOperation ( ) ) ;
2016-06-14 00:43:32 +02:00
assert ( ! arg - > isImplicit ( ) | | ! hasImplicit ) ;
hasImplicit | = arg - > isImplicit ( ) ;
2016-06-12 01:56:57 +02:00
assert ( ! arg - > abbreviation ( ) | | find ( abbreviations . cbegin ( ) , abbreviations . cend ( ) , arg - > abbreviation ( ) ) = = abbreviations . cend ( ) ) ;
abbreviations . push_back ( arg - > abbreviation ( ) ) ;
2016-11-15 22:02:40 +01:00
assert ( ! arg - > name ( ) | | find_if ( names . cbegin ( ) , names . cend ( ) , [ arg ] ( const char * name ) { return ! strcmp ( arg - > name ( ) , name ) ; } ) = = names . cend ( ) ) ;
2016-06-12 01:56:57 +02:00
assert ( arg - > requiredValueCount ( ) = = 0 | | arg - > subArguments ( ) . size ( ) = = 0 ) ;
names . emplace_back ( arg - > name ( ) ) ;
2016-11-15 22:02:40 +01:00
}
for ( const Argument * arg : args ) {
verifyArgs ( arg - > subArguments ( ) , abbreviations , names ) ;
2016-06-12 01:56:57 +02:00
}
2015-04-22 18:36:40 +02:00
}
2016-05-25 01:24:17 +02:00
# endif
2015-04-22 18:36:40 +02:00
2016-07-03 22:36:48 +02:00
/*!
* \ brief Returns whether \ a arg1 should be listed before \ a arg2 when
* printing completion .
*
* Arguments are sorted by name ( ascending order ) . However , all arguments
* denoting an operation are listed before all other arguments .
*/
bool compareArgs ( const Argument * arg1 , const Argument * arg2 )
{
if ( arg1 - > denotesOperation ( ) & & ! arg2 - > denotesOperation ( ) ) {
return true ;
} else if ( ! arg1 - > denotesOperation ( ) & & arg2 - > denotesOperation ( ) ) {
return false ;
} else {
return strcmp ( arg1 - > name ( ) , arg2 - > name ( ) ) < 0 ;
}
}
/*!
* \ brief Inserts the specified \ a siblings in the \ a target list .
* \ remarks Only inserts siblings which could still occur at least once more .
*/
void insertSiblings ( const ArgumentVector & siblings , list < const Argument * > & target )
{
bool onlyCombinable = false ;
for ( const Argument * sibling : siblings ) {
if ( sibling - > isPresent ( ) & & ! sibling - > isCombinable ( ) ) {
onlyCombinable = true ;
break ;
}
}
for ( const Argument * sibling : siblings ) {
if ( ( ! onlyCombinable | | sibling - > isCombinable ( ) ) & & sibling - > occurrences ( ) < sibling - > maxOccurrences ( ) ) {
target . push_back ( sibling ) ;
}
}
}
/*!
* \ brief Prints the bash completion for the specified arguments and the specified \ a lastPath .
* \ remarks Arguments must have been parsed before with readSpecifiedArgs ( ) . When calling this method , completionMode must
* be set to true .
*/
2016-12-23 22:41:06 +01:00
void ArgumentParser : : printBashCompletion ( int argc , const char * const * argv , unsigned int currentWordIndex , const ArgumentReader & reader )
2016-07-03 22:36:48 +02:00
{
// variables to store relevant completions (arguments, pre-defined values, files/dirs)
list < const Argument * > relevantArgs , relevantPreDefinedValues ;
bool completeFiles = false , completeDirs = false , noWhitespace = false ;
// get the last argument the argument parser was able to detect successfully
2016-12-23 22:41:06 +01:00
const Argument * const lastDetectedArg = reader . lastArg ;
2016-07-03 22:36:48 +02:00
size_t lastDetectedArgIndex ;
vector < Argument * > lastDetectedArgPath ;
if ( lastDetectedArg ) {
2016-12-23 22:41:06 +01:00
lastDetectedArgIndex = reader . lastArgDenotation - argv ;
2016-07-03 22:36:48 +02:00
lastDetectedArgPath = lastDetectedArg - > path ( lastDetectedArg - > occurrences ( ) - 1 ) ;
}
2016-10-22 19:32:16 +02:00
// determine last arg, omitting trailing empty args
2016-07-03 22:36:48 +02:00
const char * const * lastSpecifiedArg ;
unsigned int lastSpecifiedArgIndex ;
if ( argc ) {
lastSpecifiedArgIndex = static_cast < unsigned int > ( argc ) - 1 ;
lastSpecifiedArg = argv + lastSpecifiedArgIndex ;
for ( ; lastSpecifiedArg > = argv & & * * lastSpecifiedArg = = ' \0 ' ; - - lastSpecifiedArg , - - lastSpecifiedArgIndex ) ;
}
2016-10-22 19:32:16 +02:00
// determine arguments relevant for completion
bool nextArgumentOrValue ;
2016-07-03 22:36:48 +02:00
if ( lastDetectedArg & & lastDetectedArg - > isPresent ( ) ) {
2016-10-22 19:32:16 +02:00
if ( ( nextArgumentOrValue = ( currentWordIndex > lastDetectedArgIndex ) ) ) {
2016-07-03 22:36:48 +02:00
// parameter values of the last arg are possible completions
auto currentValueCount = lastDetectedArg - > values ( lastDetectedArg - > occurrences ( ) - 1 ) . size ( ) ;
if ( currentValueCount ) {
currentValueCount - = ( currentWordIndex - lastDetectedArgIndex ) ;
}
if ( lastDetectedArg - > requiredValueCount ( ) = = static_cast < size_t > ( - 1 ) | | ( currentValueCount < lastDetectedArg - > requiredValueCount ( ) ) ) {
if ( lastDetectedArg - > valueCompletionBehaviour ( ) & ValueCompletionBehavior : : PreDefinedValues ) {
relevantPreDefinedValues . push_back ( lastDetectedArg ) ;
}
if ( ! ( lastDetectedArg - > valueCompletionBehaviour ( ) & ValueCompletionBehavior : : FileSystemIfNoPreDefinedValues ) | | ! lastDetectedArg - > preDefinedCompletionValues ( ) ) {
completeFiles = completeFiles | | lastDetectedArg - > valueCompletionBehaviour ( ) & ValueCompletionBehavior : : Files ;
completeDirs = completeDirs | | lastDetectedArg - > valueCompletionBehaviour ( ) & ValueCompletionBehavior : : Directories ;
}
}
if ( lastDetectedArg - > requiredValueCount ( ) = = static_cast < size_t > ( - 1 ) | | lastDetectedArg - > values ( lastDetectedArg - > occurrences ( ) - 1 ) . size ( ) > = lastDetectedArg - > requiredValueCount ( ) ) {
// sub arguments of the last arg are possible completions
for ( const Argument * subArg : lastDetectedArg - > subArguments ( ) ) {
if ( subArg - > occurrences ( ) < subArg - > maxOccurrences ( ) ) {
relevantArgs . push_back ( subArg ) ;
}
}
// siblings of parents are possible completions as well
for ( auto parentArgument = lastDetectedArgPath . crbegin ( ) , end = lastDetectedArgPath . crend ( ) ; ; + + parentArgument ) {
insertSiblings ( parentArgument ! = end ? ( * parentArgument ) - > subArguments ( ) : m_mainArgs , relevantArgs ) ;
if ( parentArgument = = end ) {
break ;
}
}
}
} else {
// since the argument could be detected (hopefully unambiguously?) just return it for "final completion"
relevantArgs . push_back ( lastDetectedArg ) ;
}
} else {
2016-10-22 19:32:16 +02:00
// no arguments detected -> just use main arguments for completion
2016-07-03 22:36:48 +02:00
nextArgumentOrValue = true ;
insertSiblings ( m_mainArgs , relevantArgs ) ;
}
// read the "opening" (started but not finished argument denotation)
const char * opening = nullptr ;
2016-07-31 23:20:31 +02:00
string compoundOpening ;
size_t openingLen , compoundOpeningStartLen = 0 ;
2016-07-03 22:36:48 +02:00
unsigned char openingDenotationType = Value ;
if ( argc & & nextArgumentOrValue ) {
2017-03-20 23:00:23 +01:00
if ( currentWordIndex < static_cast < unsigned int > ( argc ) ) {
2016-07-31 23:20:31 +02:00
opening = argv [ currentWordIndex ] ;
2016-11-26 00:14:45 +01:00
// For some reason completions for eg. "set --values disk=1 tag=a" are splitted so the
2016-07-31 23:20:31 +02:00
// equation sign is an own argument ("set --values disk = 1 tag = a").
// This is not how values are treated by the argument parser. Hence the opening
// must be joined again. In this case only the part after the equation sign needs to be
// provided for completion so compoundOpeningStartLen is set to number of characters to skip.
size_t minCurrentWordIndex = ( lastDetectedArg ? lastDetectedArgIndex : 0 ) ;
if ( currentWordIndex > minCurrentWordIndex & & ! strcmp ( opening , " = " ) ) {
compoundOpening . reserve ( compoundOpeningStartLen = strlen ( argv [ - - currentWordIndex ] ) + 1 ) ;
compoundOpening = argv [ currentWordIndex ] ;
compoundOpening + = ' = ' ;
} else if ( currentWordIndex > ( minCurrentWordIndex + 1 ) & & ! strcmp ( argv [ currentWordIndex - 1 ] , " = " ) ) {
compoundOpening . reserve ( ( compoundOpeningStartLen = strlen ( argv [ currentWordIndex - = 2 ] ) + 1 ) + strlen ( opening ) ) ;
compoundOpening = argv [ currentWordIndex ] ;
compoundOpening + = ' = ' ;
compoundOpening + = opening ;
}
if ( ! compoundOpening . empty ( ) ) {
opening = compoundOpening . data ( ) ;
}
} else {
opening = * lastSpecifiedArg ;
}
2016-07-03 22:36:48 +02:00
* opening = = ' - ' & & ( + + opening , + + openingDenotationType )
& & * opening = = ' - ' & & ( + + opening , + + openingDenotationType ) ;
openingLen = strlen ( opening ) ;
}
relevantArgs . sort ( compareArgs ) ;
// print "COMPREPLY" bash array
cout < < " COMPREPLY=( " ;
// -> completions for parameter values
for ( const Argument * arg : relevantPreDefinedValues ) {
if ( arg - > preDefinedCompletionValues ( ) ) {
bool appendEquationSign = arg - > valueCompletionBehaviour ( ) & ValueCompletionBehavior : : AppendEquationSign ;
if ( argc & & currentWordIndex < = lastSpecifiedArgIndex & & opening ) {
if ( openingDenotationType = = Value ) {
2016-07-31 23:20:31 +02:00
bool wordStart = true , ok = false , equationSignAlreadyPresent = false ;
2016-10-22 19:32:16 +02:00
size_t wordIndex = 0 ;
2016-07-03 22:36:48 +02:00
for ( const char * i = arg - > preDefinedCompletionValues ( ) , * end = opening + openingLen ; * i ; ) {
if ( wordStart ) {
const char * i1 = i , * i2 = opening ;
for ( ; * i1 & & i2 ! = end & & * i1 = = * i2 ; + + i1 , + + i2 ) ;
2016-10-22 19:32:16 +02:00
if ( ( ok = ( i2 = = end ) ) ) {
cout < < ' \' ' ;
}
2016-07-03 22:36:48 +02:00
wordStart = false ;
2016-07-31 23:20:31 +02:00
wordIndex = 0 ;
} else if ( ( wordStart = ( * i = = ' ' ) | | ( * i = = ' \n ' ) ) ) {
equationSignAlreadyPresent = false ;
2016-10-22 19:32:16 +02:00
if ( ok ) {
cout < < ' \' ' < < ' ' ;
}
+ + i ;
continue ;
2016-07-31 23:20:31 +02:00
} else if ( * i = = ' = ' ) {
equationSignAlreadyPresent = true ;
2016-07-03 22:36:48 +02:00
}
if ( ok ) {
2016-07-31 23:20:31 +02:00
if ( ! compoundOpeningStartLen | | wordIndex > = compoundOpeningStartLen ) {
2016-10-22 19:32:16 +02:00
if ( * i = = ' \' ' ) {
cout < < " ' \" ' \" ' " ;
2016-11-26 00:14:45 +01:00
} else {
cout < < * i ;
2016-10-22 19:32:16 +02:00
}
2016-07-31 23:20:31 +02:00
}
+ + i , + + wordIndex ;
2016-10-22 19:32:16 +02:00
switch ( * i ) {
case ' ' : case ' \n ' : case ' \0 ' :
if ( appendEquationSign & & ! equationSignAlreadyPresent ) {
2016-07-03 22:36:48 +02:00
cout < < ' = ' ;
noWhitespace = true ;
2016-07-31 23:20:31 +02:00
equationSignAlreadyPresent = false ;
2016-07-03 22:36:48 +02:00
}
2016-11-26 00:14:45 +01:00
if ( * i = = ' \0 ' ) {
cout < < ' \' ' ;
}
2016-07-03 22:36:48 +02:00
}
} else {
+ + i ;
}
}
cout < < ' ' ;
}
2016-10-22 19:32:16 +02:00
} else if ( const char * i = arg - > preDefinedCompletionValues ( ) ) {
2016-07-31 23:20:31 +02:00
bool equationSignAlreadyPresent = false ;
2016-10-22 19:32:16 +02:00
cout < < ' \' ' ;
while ( * i ) {
if ( * i = = ' \' ' ) {
cout < < " ' \" ' \" ' " ;
} else {
cout < < * i ;
}
2016-07-03 22:36:48 +02:00
switch ( * ( + + i ) ) {
2016-07-31 23:20:31 +02:00
case ' = ' :
equationSignAlreadyPresent = true ;
break ;
2016-07-03 22:36:48 +02:00
case ' ' : case ' \n ' : case ' \0 ' :
2016-10-22 19:32:16 +02:00
if ( appendEquationSign & & ! equationSignAlreadyPresent ) {
2016-07-31 23:20:31 +02:00
cout < < ' = ' ;
equationSignAlreadyPresent = false ;
}
2016-10-22 19:32:16 +02:00
if ( * i ! = ' \0 ' ) {
cout < < ' \' ' ;
if ( * ( + + i ) ) {
cout < < ' ' < < ' \' ' ;
}
}
2016-07-03 22:36:48 +02:00
}
}
2016-10-22 19:32:16 +02:00
cout < < ' \' ' < < ' ' ;
2016-07-03 22:36:48 +02:00
}
}
}
// -> completions for further arguments
for ( const Argument * arg : relevantArgs ) {
if ( argc & & currentWordIndex < = lastSpecifiedArgIndex & & opening ) {
switch ( openingDenotationType ) {
case Value :
if ( ! arg - > denotesOperation ( ) | | strncmp ( arg - > name ( ) , opening , openingLen ) ) {
continue ;
}
break ;
case Abbreviation :
break ;
case FullName :
if ( strncmp ( arg - > name ( ) , opening , openingLen ) ) {
continue ;
}
}
}
2016-12-23 22:41:06 +01:00
if ( opening & & openingDenotationType = = Abbreviation & & ! nextArgumentOrValue ) {
2016-10-22 19:32:16 +02:00
cout < < ' \' ' < < ' - ' < < opening < < arg - > abbreviation ( ) < < ' \' ' < < ' ' ;
2016-12-23 22:41:06 +01:00
} else if ( lastDetectedArg & & reader . argDenotationType = = Abbreviation & & ! nextArgumentOrValue ) {
if ( reader . argv = = reader . end ) {
cout < < ' \' ' < < * ( reader . argv - 1 ) < < ' \' ' < < ' ' ;
}
2016-07-03 22:36:48 +02:00
} else if ( arg - > denotesOperation ( ) & & ( ! actualArgumentCount ( ) | | ( currentWordIndex = = 0 & & ( ! lastDetectedArg | | ( lastDetectedArg - > isPresent ( ) & & lastDetectedArgIndex = = 0 ) ) ) ) ) {
2016-10-22 19:32:16 +02:00
cout < < ' \' ' < < arg - > name ( ) < < ' \' ' < < ' ' ;
2016-07-03 22:36:48 +02:00
} else {
2016-10-22 19:32:16 +02:00
cout < < ' \' ' < < ' - ' < < ' - ' < < arg - > name ( ) < < ' \' ' < < ' ' ;
2016-07-03 22:36:48 +02:00
}
}
// -> completions for files and dirs
// -> if there's already an "opening", determine the dir part and the file part
string actualDir , actualFile ;
bool haveFileOrDirCompletions = false ;
if ( argc & & currentWordIndex = = lastSpecifiedArgIndex & & opening ) {
2016-10-22 19:32:16 +02:00
// the "opening" might contain escaped characters which need to be unescaped first (let's hope this covers all possible escapings)
2016-07-03 22:36:48 +02:00
string unescapedOpening ( opening ) ;
findAndReplace < string > ( unescapedOpening , " \\ " , " " ) ;
findAndReplace < string > ( unescapedOpening , " \\ , " , " , " ) ;
findAndReplace < string > ( unescapedOpening , " \\ [ " , " [ " ) ;
findAndReplace < string > ( unescapedOpening , " \\ ] " , " ] " ) ;
findAndReplace < string > ( unescapedOpening , " \\ ! " , " ! " ) ;
findAndReplace < string > ( unescapedOpening , " \\ # " , " # " ) ;
findAndReplace < string > ( unescapedOpening , " \\ $ " , " $ " ) ;
2016-10-22 19:32:16 +02:00
findAndReplace < string > ( unescapedOpening , " \\ ' " , " ' " ) ;
findAndReplace < string > ( unescapedOpening , " \\ \" " , " \" " ) ;
findAndReplace < string > ( unescapedOpening , " \\ \\ " , " \\ " ) ;
2016-07-03 22:36:48 +02:00
// determine the "directory" part
string dir = directory ( unescapedOpening ) ;
if ( dir . empty ( ) ) {
actualDir = " . " ;
} else {
if ( dir [ 0 ] = = ' \" ' | | dir [ 0 ] = = ' \' ' ) {
dir . erase ( 0 , 1 ) ;
}
if ( dir . size ( ) > 1 & & ( dir [ dir . size ( ) - 2 ] = = ' \" ' | | dir [ dir . size ( ) - 2 ] = = ' \' ' ) ) {
dir . erase ( dir . size ( ) - 2 , 1 ) ;
}
actualDir = move ( dir ) ;
}
// determine the "file" part
string file = fileName ( unescapedOpening ) ;
if ( file [ 0 ] = = ' \" ' | | file [ 0 ] = = ' \' ' ) {
file . erase ( 0 , 1 ) ;
}
if ( file . size ( ) > 1 & & ( file [ dir . size ( ) - 2 ] = = ' \" ' | | dir [ file . size ( ) - 2 ] = = ' \' ' ) ) {
file . erase ( file . size ( ) - 2 , 1 ) ;
}
actualFile = move ( file ) ;
}
2016-10-22 19:32:16 +02:00
// -> completion for files and dirs
DirectoryEntryType entryTypes = DirectoryEntryType : : None ;
2016-07-03 22:36:48 +02:00
if ( completeFiles ) {
2016-10-22 19:32:16 +02:00
entryTypes | = DirectoryEntryType : : File ;
2016-07-03 22:36:48 +02:00
}
if ( completeDirs ) {
2016-10-22 19:32:16 +02:00
entryTypes | = DirectoryEntryType : : Directory ;
}
if ( entryTypes ! = DirectoryEntryType : : None ) {
const string replace ( " ' " ) , with ( " ' \" ' \" ' " ) ;
2016-07-03 22:36:48 +02:00
if ( argc & & currentWordIndex < = lastSpecifiedArgIndex & & opening ) {
2016-10-22 19:32:16 +02:00
list < string > entries = directoryEntries ( actualDir . c_str ( ) , entryTypes ) ;
findAndReplace ( actualDir , replace , with ) ;
for ( string & dirEntry : entries ) {
2016-07-03 22:36:48 +02:00
if ( startsWith ( dirEntry , actualFile ) ) {
cout < < ' \' ' ;
if ( actualDir ! = " . " ) {
cout < < actualDir ;
}
2016-10-22 19:32:16 +02:00
findAndReplace ( dirEntry , replace , with ) ;
2016-07-03 22:36:48 +02:00
cout < < dirEntry < < ' \' ' < < ' ' ;
haveFileOrDirCompletions = true ;
}
}
} else {
2016-10-22 19:32:16 +02:00
for ( string & dirEntry : directoryEntries ( " . " , entryTypes ) ) {
findAndReplace ( dirEntry , replace , with ) ;
cout < < ' \' ' < < dirEntry < < ' \' ' < < ' ' ;
2016-07-03 22:36:48 +02:00
haveFileOrDirCompletions = true ;
}
}
}
cout < < ' ) ' ;
// ensure file or dir completions are formatted appropriately
if ( haveFileOrDirCompletions ) {
cout < < " ; compopt -o filenames " ;
}
// ensure trailing whitespace is ommitted
if ( noWhitespace ) {
cout < < " ; compopt -o nospace " ;
}
cout < < endl ;
}
2016-06-12 01:56:57 +02:00
/*!
* \ brief Checks the constrains of the specified \ a args .
* \ remarks Checks the contraints of sub arguments , too .
*/
void ArgumentParser : : checkConstraints ( const ArgumentVector & args )
{
for ( const Argument * arg : args ) {
const auto occurrences = arg - > occurrences ( ) ;
if ( arg - > isParentPresent ( ) & & occurrences > arg - > maxOccurrences ( ) ) {
2017-02-06 18:23:59 +01:00
throw Failure ( " The argument \" " s % arg - > name ( ) % " \" mustn't be specified more than " s % arg - > maxOccurrences ( ) + ( arg - > maxOccurrences ( ) = = 1 ? " time. " s : " times. " s ) ) ;
2015-07-27 23:19:10 +02:00
}
2016-06-12 01:56:57 +02:00
if ( arg - > isParentPresent ( ) & & occurrences < arg - > minOccurrences ( ) ) {
2017-02-06 18:23:59 +01:00
throw Failure ( " The argument \" " s % arg - > name ( ) % " \" must be specified at least " s % arg - > minOccurrences ( ) + ( arg - > minOccurrences ( ) = = 1 ? " time. " s : " times. " s ) ) ;
2016-06-12 01:56:57 +02:00
}
Argument * conflictingArgument = nullptr ;
if ( arg - > isMainArgument ( ) ) {
if ( ! arg - > isCombinable ( ) & & arg - > isPresent ( ) ) {
conflictingArgument = firstPresentUncombinableArg ( m_mainArgs , arg ) ;
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
} else {
conflictingArgument = arg - > conflictsWithArgument ( ) ;
}
if ( conflictingArgument ) {
2017-02-06 18:23:59 +01:00
throw Failure ( " The argument \" " s % conflictingArgument - > name ( ) % " \" can not be combined with \" " s + arg - > name ( ) + " \" . " s ) ;
2016-06-12 01:56:57 +02:00
}
for ( size_t i = 0 ; i ! = occurrences ; + + i ) {
2016-06-14 00:43:32 +02:00
if ( ! arg - > allRequiredValuesPresent ( i ) ) {
2015-04-22 18:36:40 +02:00
stringstream ss ( stringstream : : in | stringstream : : out ) ;
2016-06-12 01:56:57 +02:00
ss < < " Not all parameter for argument \" " < < arg - > name ( ) < < " \" " ;
if ( i ) {
ss < < " ( " < < ( i + 1 ) < < " occurrence) " ;
}
ss < < " provided. You have to provide the following parameter: " ;
size_t valueNamesPrint = 0 ;
2015-04-22 18:36:40 +02:00
for ( const auto & name : arg - > m_valueNames ) {
2016-06-14 00:43:32 +02:00
ss < < ' ' < < name , + + valueNamesPrint ;
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
if ( arg - > m_requiredValueCount ! = static_cast < size_t > ( - 1 ) ) {
while ( valueNamesPrint < arg - > m_requiredValueCount ) {
ss < < " \n value " < < ( + + valueNamesPrint ) ;
}
2015-04-22 18:36:40 +02:00
}
throw Failure ( ss . str ( ) ) ;
}
}
2016-06-12 01:56:57 +02:00
// check contraints of sub arguments recursively
checkConstraints ( arg - > m_subArgs ) ;
}
}
/*!
* \ brief Invokes the callbacks for the specified \ a args .
* \ remarks
* - Checks the callbacks for sub arguments , too .
2016-07-31 23:20:31 +02:00
* - Invokes the assigned callback methods for each occurrence of
2016-06-12 01:56:57 +02:00
* the argument .
*/
void ArgumentParser : : invokeCallbacks ( const ArgumentVector & args )
{
for ( const Argument * arg : args ) {
2016-07-31 23:20:31 +02:00
// invoke the callback for each occurrence of the argument
2015-04-22 18:36:40 +02:00
if ( arg - > m_callbackFunction ) {
2016-07-31 23:20:31 +02:00
for ( const auto & occurrence : arg - > m_occurrences ) {
arg - > m_callbackFunction ( occurrence ) ;
2015-04-22 18:36:40 +02:00
}
}
2016-06-12 01:56:57 +02:00
// invoke the callbacks for sub arguments recursively
invokeCallbacks ( arg - > m_subArgs ) ;
}
2015-04-22 18:36:40 +02:00
}
/*!
2016-06-10 22:59:22 +02:00
* \ class HelpArgument
2015-04-22 18:36:40 +02:00
* \ brief The HelpArgument class prints help information for an argument parser
* when present ( - - help , - h ) .
*/
/*!
* \ brief Constructs a new help argument for the specified parser .
*/
HelpArgument : : HelpArgument ( ArgumentParser & parser ) :
2016-06-12 01:56:57 +02:00
Argument ( " help " , ' h ' , " shows this information " )
2015-04-22 18:36:40 +02:00
{
2016-07-31 23:20:31 +02:00
setCallback ( [ & parser ] ( const ArgumentOccurrence & ) {
2015-09-01 20:05:38 +02:00
CMD_UTILS_START_CONSOLE ;
parser . printHelp ( cout ) ;
} ) ;
2015-04-22 18:36:40 +02:00
}
2016-10-30 00:30:54 +02:00
/*!
* \ class OperationArgument
* \ brief The OperationArgument class is an Argument where denotesOperation ( ) is true by default .
*/
/*!
* \ class ConfigValueArgument
* \ brief The ConfigValueArgument class is an Argument where setCombinable ( ) is true by default .
* \ sa ConfigValueArgument : : ConfigValueArgument ( )
*/
2015-04-22 18:36:40 +02:00
}