ALib C++ Framework
by
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
commandline.cpp
1//##################################################################################################
2// ALib C++ Framework
3//
4// Copyright 2013-2025 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6//##################################################################################################
7#include "alib_precompile.hpp"
8#if !defined(ALIB_C20_MODULES) || ((ALIB_C20_MODULES != 0) && (ALIB_C20_MODULES != 1))
9# error "Configuration MACRO ALIB_C20_MODULES has to be given to the compiler as either 0 or 1"
10#endif
11#if ALIB_C20_MODULES
12 module;
13#endif
14//========================================= Global Fragment ========================================
15#include "alib/alib.inl"
16//============================================== Module ============================================
17#if ALIB_C20_MODULES
18 module ALib.CLI;
19 import ALib.Characters.Functions;
20# if ALIB_STRINGS
21 import ALib.Strings;
22# endif
23#else
25# include "ALib.Strings.H"
26# include "ALib.CLI.H"
27#endif
28//========================================== Implementation ========================================
29namespace alib::cli {
30
31//##################################################################################################
32// CommandLine Constructor
33//##################################################################################################
34
35void CommandLine::Init( resources::ResourcePool* resourcePool, NCString resCategory ) {
36 ALIB_ASSERT_ERROR(Resources==nullptr, "CLI", "CommandLine::Init already called.")
37 Resources = resourcePool;
38 ResourceCategory= resCategory;
39
40 ArgStrings.reserve( size_t(ARG_C) );
41 ArgsLeft .reserve( size_t(ARG_C) );
42
43 #if !ALIB_CHARACTERS_WIDE
44 if( ARG_VN ) {
45 for ( int i= 1; i < ARG_C ; ++i ) {
46 ArgStrings.emplace_back( ARG_VN[i] );
47 ArgsLeft .emplace_back( i -1 );
48 }
49 } else {
50 // convert wide to narrow strings
51 NString1K converter;
53
54 for ( int i= 1; i < ARG_C ; ++i ) {
55 converter.Reset() << ARG_VW[i];
56 ArgStrings.emplace_back( String( allocator, converter) );
57 ArgsLeft .emplace_back( i - 1 );
58 } }
59 #else
60 #if ALIB_CHARACTERS_NATIVE_WCHAR // use original strings only if alib::wchar == wchar_t
61 if( ARG_VW ) {
62 for ( int i= 1; i < ARG_C ; ++i ) {
63 ArgStrings.emplace_back( ARG_VW[i] );
64 ArgsLeft .emplace_back( i - 1 );
65 } }
66 else
67 #endif
68 {
69 // convert narrow to wide strings (or "wrong" wide width to width)
70 String1K converter;
72
73 for ( int i= 1; i < ARG_C ; ++i ) {
74 converter.Reset() << ARG_VN[i];
75 ArgStrings.emplace_back( String(allocator, converter) );
76 ArgsLeft .emplace_back( i -1 );
77 } }
78 #endif
79}
80
81//##################################################################################################
82// Interface
83//##################################################################################################
84
86 // loop over all arg indices in ArgsLeft
87 integer argIdx= 0;
88 while( argIdx < integer(ArgsLeft.size()) ) {
89 // get arg number and string once
90 auto argNo= ArgsLeft[size_t(argIdx)];
91 String arg = GetArg(argNo);
92
93 SHORTCUT_JUMP:
94
95 // ignore non-option args
96 if( arg.CharAtStart() != '-' ) {
97 ++argIdx;
98 continue;
99 }
100
101 // create an option object and search decl with the actual argument
102 {
103 Option* option= allocator().New<Option>(this);
104
105 auto optionDeclIt= OptionDecls.begin();
106 try
107 {
108 while( optionDeclIt != OptionDecls.end() ) {
109 if( option->Read( **optionDeclIt, arg, argNo ) )
110 break;
111 ++optionDeclIt;
112 } }
113 catch ( Exception& e )
114 {
116 (*optionDeclIt)->HelpUsageLine() );
117 throw;
118 }
119
120 // found a declaration?
121 if( option->ConsumedArguments > 0 ) {
122 // shortcut to another option?
123 OptionDecl& decl= *option->Declaration;
125 arg= decl.ShortcutReplacementString();
126 goto SHORTCUT_JUMP;
127 }
128
129 // delete args and continue
130 ArgsLeft.erase( ArgsLeft.begin() + argIdx,
131 ArgsLeft.begin() + argIdx + option->ConsumedArguments );
132
133 // move local option into the monotonic memory add to the list for this option type.
134 Options.push_back( option );
135 continue;
136 }
137
138
139 // erase args that start with '-' and put them into field OptionsIgnored.
140 if( ArgsLeft.size() > 0 ) {
141 OptionArgsIgnored.push_back( GetArg(argNo) );
142 ArgsLeft.erase( ArgsLeft.begin() + argIdx );
143} } } }
144
146 for( auto optionIt= Options.rbegin() ; optionIt != Options.rend() ; optionIt ++ )
147 if( (*optionIt)->Declaration->Element() == element )
148 return *optionIt;
149 return nullptr;
150}
151
152
154 // loop over all arg indices in ArgsLeft
155 bool lastCommandFullyParsed= true;
156 while( lastCommandFullyParsed && ArgsLeft.size() > 0 ) {
157 // create a command object and search decl with actual argument
158 Command* command= allocator().New<Command>(this);
159 ALIB_ASSERT_ERROR( CommandDecls.size() > 0, "CLI", "No commands declared." )
160 for( auto* commandDecl : CommandDecls ) {
161 try
162 {
163 lastCommandFullyParsed= command->Read( *commandDecl );
164 }
165 catch ( Exception& e )
166 {
168 CLIUtil::GetCommandUsageFormat( *this, *commandDecl ),
169 commandDecl->HelpTextShort() );
170 throw;
171 }
172
173 if( command->ConsumedArguments > 0 ) {
174 CommandsParsed.push_back( command );
175 if( NextCommandIt == CommandsParsed.end() )
177 break;
178} } } }
179
181 if( NextCommandIt == CommandsParsed.end() )
183 if( NextCommandIt == CommandsParsed.end() ) {
184 // check for arguments left which got not recognized
185 if( ArgsLeft.size() > 0 )
187 ArgsLeft[0], PeekArg() );
188
189 // check for no command
190 if ( CommandsParsed.empty() )
192
193 return nullptr;
194 }
195
196 auto* result= *NextCommandIt;
198 return result;
199}
200
201
203 if( ArgsLeft.size() == 0)
204 return NULL_STRING;
205
206 String result= GetArg(ArgsLeft[0]);
207 ArgsLeft.erase( ArgsLeft.begin() );
208 return result;
209}
210
212 for( auto it= ArgsLeft.begin() ; it != ArgsLeft.end() ; ++it )
213 if( *it == argNo ) {
214 ArgsLeft.erase( it );
215 return;
216 }
217 ALIB_ERROR( "CLI", "Argument number {} already removed.", argNo )
218}
219
220
221
222} // namespace alib::cli
#define ALIB_CALLER_NULLED
Definition alib.inl:1105
#define ALIB_ERROR(domain,...)
Definition alib.inl:1140
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1144
static AString GetCommandUsageFormat(CommandLine &cmdLine, CommandDecl &commandDecl)
Definition cliutil.cpp:62
virtual String PeekArg()
void RemoveArg(integer argNo)
virtual void ReadOptions()
virtual void ReadNextCommands()
StdVectorMA< integer > ArgsLeft
Option * GetOption(Enum element)
ListMA< String, Recycling::Shared > OptionArgsIgnored
MonoAllocator allocator
StdVectorMA< String > ArgStrings
void Init(camp::Camp *resModule)
ListMA< CommandDecl * > CommandDecls
Commands defined.
ListMA< Command * > CommandsParsed
A list of commands actually parsed. Filled with method #"ReadNextCommands".
ListMA< Option * > Options
The options parsed in the order of their appearance.
NCString ResourceCategory
The resource category to fetch CLI resources within field #"Resources".
ListMA< Command * >::iterator NextCommandIt
The next command in #"CommandsParsed" to be processed. Used with method #"NextCommand".
ListMA< OptionDecl * > OptionDecls
Possible Options.
resources::ResourcePool * Resources
virtual String PopArg()
virtual Command * NextCommand()
virtual String GetArg(integer idx)
const String & ShortcutReplacementString()
Exception & Add(const lang::CallerInfo &ci, TEnum type, TArgs &&... args)
void DbgDisableBufferReplacementWarning()
Definition tastring.inl:241
TChar CharAtStart() const
Definition string.inl:421
constexpr bool IsNotEmpty() const
Definition string.inl:357
@ ParsingOptions
General option parse error. Adds option help text.
Definition clicamp.inl:68
@ ParsingCommand
General parameter parse error. Adds command help text.
Definition clicamp.inl:69
@ NoCommandGiven
Unknown command given.
Definition clicamp.inl:66
@ UnknownCommand
Unknown command given.
Definition clicamp.inl:67
constexpr String NULL_STRING
A nulled string of the default character type.
Definition string.inl:2254
strings::TCString< nchar > NCString
Type alias in namespace alib.
Definition cstring.inl:408
NLocalString< 1024 > NString1K
Type alias name for #"TLocalString;TLocalString<nchar,1024>".
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
const wchar_t ** ARG_VW
Definition mainargs.cpp:25
strings::TString< character > String
Type alias in namespace alib.
Definition string.inl:2172
LocalString< 1024 > String1K
Type alias name for #"TLocalString;TLocalString<character,1024>".
exceptions::Exception Exception
Type alias in namespace alib.
int ARG_C
Definition mainargs.cpp:23
const char ** ARG_VN
Definition mainargs.cpp:24
boxing::Enum Enum
Type alias in namespace alib.
Definition enum.inl:211
A command of a ALib CLI command-line.
bool Read(CommandDecl &decl)
OptionDecl * Declaration
The declaration struct.
bool Read(OptionDecl &decl, String &arg, const integer argNo)
Definition arguments.cpp:64
integer ConsumedArguments
Definition arguments.inl:35