args-parser 6.3.3
Loading...
Searching...
No Matches
cmd_line.hpp
Go to the documentation of this file.
1
31#ifndef ARGS__CMD_LINE_HPP__INCLUDED
32#define ARGS__CMD_LINE_HPP__INCLUDED
33
34// Args include.
35#include "api.hpp"
36#include "utils.hpp"
37#include "context.hpp"
38#include "exceptions.hpp"
39#include "command.hpp"
40#include "help.hpp"
41
42// C++ include.
43#include <vector>
44#include <algorithm>
45#include <memory>
46
47
48namespace Args {
49
50namespace details {
51
52//
53// makeContext
54//
55
57static inline ContextInternal
58#ifdef ARGS_WSTRING_BUILD
59 makeContext( int argc, const Char * const * argv )
60#else
61 makeContext( int argc, const char * const * argv )
62#endif
63{
64 ContextInternal context;
65
66 // We skip first argv because of it's executable name.
67 for( int i = 1; i < argc; ++i )
68 context.push_back( argv[ i ] );
69
70 return context;
71} // makeContext
72
73
74//
75// formatCorrectNamesString
76//
77
79static inline String
80formatCorrectNamesString( const StringList & names )
81{
82 if( !names.empty() )
83 {
84 String res;
85
86 bool first = true;
87
88 for( const auto & name : details::asConst( names ) )
89 {
90 if( !first )
91 res.append( SL( " or " ) );
92
93 res.append( name );
94
95 first = false;
96 }
97
98 return res;
99 }
100 else
101 return String();
102}
103
104} /* namespace details */
105
106
107//
108// CmdLine
109//
110
111template< typename PARENT, typename SELF, typename ARGPTR >
113
114using ArgPtrToAPI = std::unique_ptr< ArgIface, details::Deleter< ArgIface > >;
115
121class CmdLine final
122 : public CmdLineAPI< CmdLine, CmdLine, ArgPtrToAPI >
123{
124public:
128 using Arguments = std::vector< ArgPtr >;
129
133 Empty = 0,
138 }; // enum CmdLineOpt
139
141 using CmdLineOpts = int;
142
144 explicit CmdLine( CmdLineOpts opt = Empty )
145 : details::API< CmdLine, CmdLine, ArgPtr, true > ( *this, *this )
146 , m_command( nullptr )
147 , m_currCommand( nullptr )
148 , m_opt( opt )
149 {
150 }
151
152#ifdef ARGS_WSTRING_BUILD
154 CmdLine( int argc, const Char * const * argv,
155 CmdLineOpts opt = Empty );
156#else
158 CmdLine( int argc, const char * const * argv,
159 CmdLineOpts opt = Empty );
160#endif
161
162 virtual ~CmdLine()
163 {
164 }
165
168 {
169 return m_opt;
170 }
171
173 void addArg( ArgIface * arg );
174
176 void addArg( ArgIface & arg );
177
179 void addArg( ArgPtr arg )
180 {
181 if( std::find( m_args.begin(), m_args.end(), arg ) ==
182 m_args.end() )
183 {
184 arg->setCmdLine( this );
185
186 m_args.push_back( std::move( arg ) );
187 }
188 else
189 throw BaseException( String( SL( "Argument \"" ) ) +
190 arg->name() + SL( "\" already in the command line parser." ) );
191 }
192
194 void parse();
195
196#ifdef ARGS_WSTRING_BUILD
198 void parse( int argc, const Char * const * argv )
199#else
201 void parse( int argc, const char * const * argv )
202#endif
203 {
204 m_context = std::move( details::makeContext( argc, argv ) );
205
206 parse();
207 }
208
210 const StringList & positional() const
211 {
212 return m_positional;
213 }
214
217 {
218 return m_positionalDescription;
219 }
220
223 {
224 m_positionalDescription = d;
225 }
226
229 ArgIface * findArgument( const String & name )
230 {
231 auto it = std::find_if( m_args.begin(),
232 m_args.end(), [ &name ] ( const auto & arg ) -> bool
233 { return ( arg->findArgument( name ) != nullptr ); } );
234
235 if( it != m_args.end() )
236 {
237 if( (*it)->type() == ArgType::Command )
238 return it->get();
239 else
240 return (*it)->findArgument( name );
241 }
242 else if( !m_prevCommand.empty() )
243 {
244 for( const auto & c : m_prevCommand )
245 {
246 ArgIface * tmp = c->findChild( name );
247
248 if( tmp )
249 return tmp;
250 }
251 }
252
253 return nullptr;
254 }
255
258 const ArgIface * findArgument( const String & name ) const
259 {
260 auto it = std::find_if( m_args.cbegin(),
261 m_args.cend(), [ &name ] ( const auto & arg ) -> bool
262 { return ( arg->findArgument( name ) != nullptr ); } );
263
264 if( it != m_args.end() )
265 {
266 if( (*it)->type() == ArgType::Command )
267 return it->get();
268 else
269 return (*it)->findArgument( name );
270 }
271 else if( !m_prevCommand.empty() )
272 {
273 for( const auto & c : m_prevCommand )
274 {
275 ArgIface * tmp = c->findChild( name );
276
277 if( tmp )
278 return tmp;
279 }
280 }
281
282 return nullptr;
283 }
284
286 const Arguments & arguments() const;
287
291 const String & name,
293 StringList & possibleNames ) const
294 {
295 bool ret = false;
296
297 std::for_each( arguments().cbegin(), arguments().cend(),
298 [ & ] ( const auto & arg )
299 {
300 if( arg->type() == ArgType::Command )
301 {
302 if( arg.get() == m_command )
303 {
304 if( arg->isMisspelledName( name, possibleNames ) )
305 ret = true;
306 }
307 else if( static_cast< Command* > ( arg.get() )->
308 isMisspelledCommand( name, possibleNames ) )
309 ret = true;
310 }
311 else if( arg->isMisspelledName( name, possibleNames ) )
312 ret = true;
313 } );
314
315 return ret;
316 }
317
321 bool throwExceptionOnPrint = true,
323 const String & appExe = String(),
325 const String & appDesc = String(),
327 String::size_type length = 79,
329 const String & posDesc = String() )
330 {
331 auto help = std::unique_ptr< Help, details::Deleter< ArgIface > >
332 ( new Help( throwExceptionOnPrint ),
334
335 if( !appExe.empty() )
336 help->setExecutable( appExe );
337
338 if( !appDesc.empty() )
339 help->setAppDescription( appDesc );
340
341 if( !posDesc.empty() )
342 setPositionalDescription( posDesc );
343
344 help->setLineLength( length );
345
346 ArgPtr arg = std::move( help );
347
348 addArg( std::move( arg ) );
349
350 return *this;
351 }
352
353 template< typename T >
354 void addHelp( T throwExceptionOnPrint,
355 const String & appExe = String(),
356 const String & appDesc = String(),
357 String::size_type length = 79 ) = delete;
358
363 const String & name ) const
364 {
365 const auto * arg = findArgument( name );
366
367 if( arg )
368 {
369 switch( arg->type() )
370 {
371 case ArgType::Command :
372 return ( static_cast< const Command* > ( arg )->value() );
373
374 case ArgType::Arg :
375 return ( static_cast< const Arg* > ( arg )->value() );
376
377 case ArgType::MultiArg :
378 return ( static_cast< const MultiArg* > ( arg )->value() );
379
380 default :
381 return String();
382 }
383 }
384 else
385 return String();
386 }
387
392 const String & name ) const
393 {
394 const auto * arg = findArgument( name );
395
396 if( arg )
397 {
398 switch( arg->type() )
399 {
400 case ArgType::Command :
401 return ( static_cast< const Command* > ( arg )->values() );
402
403 case ArgType::Arg :
404 return StringList(
405 { ( static_cast< const Arg* > ( arg )->value() ) } );
406
407 case ArgType::MultiArg :
408 return ( static_cast< const MultiArg* > ( arg )->values() );
409
410 default :
411 return StringList();
412 }
413 }
414 else
415 return StringList();
416 }
417
424 const String & name ) const
425 {
426 const auto * arg = findArgument( name );
427
428 if( arg )
429 return arg->isDefined();
430 else
431 return false;
432 }
433
435 void clear()
436 {
437 std::for_each( arguments().begin(), arguments().end(),
438 [] ( const auto & a ) { a->clear(); } );
439
440 m_command = nullptr;
441 m_currCommand = nullptr;
442 m_prevCommand.clear();
443 }
444
445private:
447 void checkCorrectnessBeforeParsing() const;
449 void checkCorrectnessAfterParsing() const;
450
452 void printInfoAboutUnknownArgument( const String & word )
453 {
454 StringList correctNames;
455
456 if( isMisspelledName( word, correctNames ) )
457 {
458 const String names = details::formatCorrectNamesString(
459 correctNames );
460
461 throw BaseException( String( SL( "Unknown argument \"" ) ) +
462 word + SL( "\".\n\nProbably you mean \"" ) + names +
463 SL( "\"." ) );
464 }
465 else
466 throw BaseException( String( SL( "Unknown argument \"" ) ) +
467 word + SL( "\"." ) );
468 }
469
471 void savePositionalArguments( const String & word, bool splitted, bool valuePrepended )
472 {
473 auto tmp = word;
474
475 if( splitted )
476 {
477 tmp.append( 1, '=' );
478
479 if( valuePrepended )
480 tmp.append( *m_context.next() );
481 }
482
483 if( tmp != String( 2, '-' ) )
484 m_positional.push_back( tmp );
485
486 while( !m_context.atEnd() )
487 {
488 auto it = m_context.next();
489
490 m_positional.push_back( *it );
491 }
492 }
493
494private:
495 DISABLE_COPY( CmdLine )
496
497 // Context.
498 Context m_context;
500 Arguments m_args;
502 Command * m_command;
504 Command * m_currCommand;
506 std::vector< Command * > m_prevCommand;
508 CmdLineOpts m_opt;
510 StringList m_positional;
512 String m_positionalDescription;
513}; // class CmdLine
514
515
516//
517// CmdLine
518//
519
520inline
521#ifdef ARGS_WSTRING_BUILD
522 CmdLine::CmdLine( int argc, const Char * const * argv, CmdLineOpts opt )
523#else
524 CmdLine::CmdLine( int argc, const char * const * argv, CmdLineOpts opt )
525#endif
527 , m_context( details::makeContext( argc, argv ) )
528 , m_command( nullptr )
529 , m_currCommand( nullptr )
530 , m_opt( opt )
531{
532}
533
534inline void
535CmdLine::addArg( ArgIface * arg )
536{
537 if( arg )
538 {
539 if( std::find( m_args.begin(), m_args.end(),
540 ArgPtr( arg, details::Deleter< ArgIface > ( false ) ) ) ==
541 m_args.end() )
542 {
543 arg->setCmdLine( this );
544
545 m_args.push_back( ArgPtr( arg,
546 details::Deleter< ArgIface > ( false ) ) );
547 }
548 else
549 throw BaseException( String( SL( "Argument \"" ) ) +
550 arg->name() + SL( "\" already in the command line parser." ) );
551 }
552 else
553 throw BaseException( String( SL( "Attempt to add nullptr to the "
554 "command line as argument." ) ) );
555}
556
557inline void
558CmdLine::addArg( ArgIface & arg )
559{
560 addArg( &arg );
561}
562
563inline void
564CmdLine::parse()
565{
566 clear();
567
568 checkCorrectnessBeforeParsing();
569
570 while( !m_context.atEnd() )
571 {
572 String word = *m_context.next();
573
574 const String::size_type eqIt = word.find( '=' );
575
576 bool splitted = false;
577 bool valuePrepended = false;
578
579 if( eqIt != String::npos )
580 {
581 splitted = true;
582
583 const String value = word.substr( eqIt + 1 );
584
585 if( !value.empty() )
586 {
587 valuePrepended = true;
588 m_context.prepend( value );
589 }
590
591 word = word.substr( 0, eqIt );
592 }
593
594 if( details::isArgument( word ) )
595 {
596 auto * arg = findArgument( word );
597
598 if( arg )
599 arg->process( m_context );
600 else if( m_opt & HandlePositionalArguments )
601 savePositionalArguments( word, splitted, valuePrepended );
602 else
603 printInfoAboutUnknownArgument( word );
604 }
605 else if( details::isFlag( word ) )
606 {
607 std::vector< ArgIface* > tmpArgs;
608 bool failed = false;
609
610 for( String::size_type i = 1, length = word.length(); i < length; ++i )
611 {
612 const String flag = String( SL( "-" ) ) +
613
614#ifdef ARGS_QSTRING_BUILD
615 String( word[ i ] );
616#else
617 String( 1, word[ i ] );
618#endif
619
620 auto * arg = findArgument( flag );
621
622 if( !arg )
623 {
624 failed = true;
625
626 if( !( m_opt & HandlePositionalArguments ) )
627 throw BaseException( String( SL( "Unknown argument \"" ) ) +
628 flag + SL( "\"." ) );
629 else
630 break;
631 }
632 else
633 tmpArgs.push_back( arg );
634
635 if( i < length - 1 && arg->isWithValue() )
636 {
637 failed = true;
638
639 if( !( m_opt & HandlePositionalArguments ) )
640 throw BaseException( String( SL( "Only last argument in "
641 "flags combo can be with value. Flags combo is \"" ) ) +
642 word + SL( "\"." ) );
643 else
644 break;
645 }
646 else
647 arg->process( m_context );
648 }
649
650 if( failed && ( m_opt & HandlePositionalArguments ) )
651 {
652 for( auto * a : tmpArgs )
653 a->clear();
654
655 savePositionalArguments( word, splitted, valuePrepended );
656 }
657 }
658 // Command?
659 else
660 {
661 auto * tmp = findArgument( word );
662
663 auto check = [this, &tmp, &word] ()
664 {
665 const auto & args = m_args;
666
667 const auto it = std::find_if( args.cbegin(),
668 args.cend(), [&tmp] ( const auto & arg ) -> bool
669 { return ( arg.get() == tmp ); } );
670
671 if( m_command && it != args.cend() && !m_command->findChild( word ) )
672 throw BaseException( String( SL(
673 "Only one command from one level can be specified. "
674 "But you defined \"" ) ) +
675 word + SL( "\" and \"" ) + m_command->name() + SL( "\"." ) );
676 };
677
678 if( tmp )
679 {
680 if( tmp->type() == ArgType::Command )
681 {
682 bool stopProcessing = false;
683
684 try {
685 if( !m_prevCommand.empty() )
686 {
687 for( const auto & prev : m_prevCommand )
688 {
689 const auto & args = prev->children();
690
691 const auto it = std::find_if( args.cbegin(),
692 args.cend(), [&tmp] ( const auto & arg ) -> bool
693 { return ( arg.get() == tmp ); } );
694
695 if( it != args.cend() && !m_currCommand->findChild( word ) )
696 throw BaseException( String( SL(
697 "Only one command from one level can be specified. "
698 "But you defined \"" ) ) +
699 word + SL( "\" and \"" ) + prev->name() + SL( "\"." ) );
700 }
701
702 check();
703 }
704 else
705 check();
706 }
707 catch( const BaseException & )
708 {
709 if( m_opt & HandlePositionalArguments )
710 {
711 savePositionalArguments( word, splitted, valuePrepended );
712
713 stopProcessing = true;
714 }
715 else
716 throw;
717 }
718
719 if( stopProcessing )
720 continue;
721
722 if( !m_command )
723 {
724 m_command = static_cast< Command* > ( tmp );
725
726 m_currCommand = m_command;
727
728 m_prevCommand.push_back( m_command );
729
730 m_command->process( m_context );
731 }
732 else
733 {
734 auto * cmd = static_cast< Command* > ( tmp );
735
736 m_currCommand->setCurrentSubCommand( cmd );
737
738 m_currCommand = cmd;
739
740 m_prevCommand.push_back( m_currCommand );
741
742 m_currCommand->process( m_context );
743 }
744 }
745 else if( m_opt & HandlePositionalArguments )
746 savePositionalArguments( word, splitted, valuePrepended );
747 else
748 printInfoAboutUnknownArgument( word );
749 }
750 else if( m_opt & HandlePositionalArguments )
751 savePositionalArguments( word, splitted, valuePrepended );
752 else
753 printInfoAboutUnknownArgument( word );
754 }
755 }
756
757 checkCorrectnessAfterParsing();
758}
759
760inline const CmdLine::Arguments &
761CmdLine::arguments() const
762{
763 return m_args;
764}
765
766inline void
767CmdLine::checkCorrectnessBeforeParsing() const
768{
769 StringList flags;
770 StringList names;
771
772 std::vector< ArgIface* > cmds;
773
774 std::for_each( m_args.cbegin(), m_args.cend(),
775 [ &cmds, &flags, &names ] ( const auto & arg )
776 {
777 if( arg->type() == ArgType::Command )
778 cmds.push_back( arg.get() );
779 else
780 arg->checkCorrectnessBeforeParsing( flags, names );
781 }
782 );
783
784 std::for_each( cmds.cbegin(), cmds.cend(),
785 [ &flags, &names ] ( ArgIface * arg )
786 { arg->checkCorrectnessBeforeParsing( flags, names ); }
787 );
788}
789
790inline void
791CmdLine::checkCorrectnessAfterParsing() const
792{
793 std::for_each( m_args.begin(), m_args.end(),
794 [] ( const auto & arg )
795 { arg->checkCorrectnessAfterParsing(); }
796 );
797
798 if( m_opt & CommandIsRequired && !m_command )
799 throw BaseException( SL( "Not specified command." ) );
800}
801
802} /* namespace Args */
803
804#include "help_printer.hpp"
805
806namespace Args {
807
808inline
809Help::Help( bool throwExceptionOnPrint )
810 : Arg( SL( 'h' ), SL( "help" ), true, false )
811 , m_printer( new HelpPrinter )
812 , m_throwExceptionOnPrint( throwExceptionOnPrint )
813{
814 setDescription( SL( "Print this help." ) );
815 setLongDescription( SL( "Print this help." ) );
816}
817
818} /* namespace Args */
819
820#endif // ARGS__CMD_LINE_HPP__INCLUDED
Argument with one value that can be present only once in the command line.
Definition arg.hpp:58
void setLongDescription(const String &desc)
Set long description.
Definition arg.hpp:505
void setDescription(const String &desc)
Set description.
Definition arg.hpp:490
Interface for arguments.
Definition arg_iface.hpp:51
virtual String name() const =0
virtual void setCmdLine(CmdLine *cmdLine)
Set command line parser.
Base exception of the library.
CmdLine is class that holds all rguments and parse command line arguments in the correspondence with ...
Definition cmd_line.hpp:123
void parse(int argc, const char *const *argv)
Parse arguments.
Definition cmd_line.hpp:201
const StringList & positional() const
Definition cmd_line.hpp:210
CmdLine & addHelp(bool throwExceptionOnPrint=true, const String &appExe=String(), const String &appDesc=String(), String::size_type length=79, const String &posDesc=String())
Add help.
Definition cmd_line.hpp:319
bool isMisspelledName(const String &name, StringList &possibleNames) const
Definition cmd_line.hpp:289
void addArg(ArgIface *arg)
Add argument.
Definition cmd_line.hpp:535
bool isDefined(const String &name) const
Definition cmd_line.hpp:421
String value(const String &name) const
Definition cmd_line.hpp:360
CmdLine(CmdLineOpts opt=Empty)
Construct empty CmdLine.
Definition cmd_line.hpp:144
void parse()
Parse arguments.
Definition cmd_line.hpp:564
void clear()
Clear state of the arguments.
Definition cmd_line.hpp:435
const ArgIface * findArgument(const String &name) const
Definition cmd_line.hpp:258
CmdLineOpt
Command line options.
Definition cmd_line.hpp:131
@ Empty
No special options.
Definition cmd_line.hpp:133
@ HandlePositionalArguments
Handle positional arguments.
Definition cmd_line.hpp:137
@ CommandIsRequired
Command should be defined.
Definition cmd_line.hpp:135
void addHelp(T throwExceptionOnPrint, const String &appExe=String(), const String &appDesc=String(), String::size_type length=79)=delete
StringList values(const String &name) const
Definition cmd_line.hpp:389
int CmdLineOpts
Storage of command line options.
Definition cmd_line.hpp:141
void setPositionalDescription(const String &d)
Set positional string description for the help.
Definition cmd_line.hpp:222
ArgPtrToAPI ArgPtr
Smart pointer to the argument.
Definition cmd_line.hpp:126
const String & positionalDescription() const
Definition cmd_line.hpp:216
ArgIface * findArgument(const String &name)
Definition cmd_line.hpp:229
void addArg(ArgPtr arg)
Add argument.
Definition cmd_line.hpp:179
const Arguments & arguments() const
Definition cmd_line.hpp:761
std::vector< ArgPtr > Arguments
List of child arguments.
Definition cmd_line.hpp:128
CmdLineOpts parserOptions() const
Definition cmd_line.hpp:167
virtual ~CmdLine()
Definition cmd_line.hpp:162
Command in the command line interface.
Definition command.hpp:53
void process(Context &ctx) override
Process argument's staff, for example take values from context.
Definition command.hpp:329
void setCurrentSubCommand(Command *sub)
Set current subcommand.
Definition command.hpp:404
Help argument.
Definition help.hpp:76
Help(bool throwExceptionOnPrint=true)
Definition cmd_line.hpp:809
HelpPrinter is a class that prints help.
MultiArg is a class that presents argument in the command line taht can be presented more than once o...
Definition multi_arg.hpp:59
virtual const StringList & values() const
constexpr std::add_const< T >::type & asConst(T &t) noexcept
Adds const to non-const objects.
Definition utils.hpp:72
Definition api.hpp:42
std::string String
String type.
Definition types.hpp:324
std::vector< String > StringList
List of strings.
Definition types.hpp:346
std::unique_ptr< ArgIface, details::Deleter< ArgIface > > ArgPtrToAPI
Definition cmd_line.hpp:114
@ Arg
Argument.
@ MultiArg
Multi argument.
@ Command
Command.
StringList ContextInternal
What Context actually is.
Definition context.hpp:49
String::value_type Char
Char type.
Definition types.hpp:327
#define SL(str)
Definition types.hpp:338
#define DISABLE_COPY(Class)
Macro for disabling copy.
Definition utils.hpp:50