Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
CommandLine.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_CommandLine_h_
5#define _Stroika_Foundation_Execution_CommandLine_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include "Stroika/Foundation/Characters/SDKChar.h"
11#include "Stroika/Foundation/Common/Common.h"
12#include "Stroika/Foundation/Containers/Sequence.h"
13#include "Stroika/Foundation/Execution/Exceptions.h"
14
15/*
16 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
17 */
18
20
21 using Characters::String;
22 using Containers::Iterable;
23 using Containers::Sequence;
24
25 /**
26 * \todo Perhaps refactor slightly, so easy to tell one kind of issue from another.
27 */
29 public:
31 InvalidCommandLineArgument (const String& message);
32 InvalidCommandLineArgument (const String& message, const String& argument);
33
34 public:
35 String fMessage;
36 String fArgument;
37 };
38
39 /**
40 * Take in a 'command line' specification (typically from 'main', but also used as arguments to ProcessRunner),
41 * and define 'Option' objects and lookup if given arguments
42 * are 'present' in the commandline (and grab associated arguments).
43 *
44 * Supports repeated option elements. Supports -o and --output-file formats
45 * Supports -o ARG and -o=ARG formats
46 *
47 * inspired partly by https://man7.org/linux/man-pages/man3/getopt.3.html
48 *
49 * \par Example Usage (in application main)
50 * \code
51 * uint16_t portNumber = 8080;
52 *
53 * const CommandLine::Option kPortO_{
54 * .fLongName = "port"sv, .fSupportsArgument = true, .fHelpOptionText = "specify webserver listen port (default {})"_f(portNumber)};
55 * const CommandLine::Option kQuitAfterO_{
56 * .fLongName = "quit-after"sv, .fSupportsArgument = true, .fHelpOptionText = "automatically quit after <argument> seconds"sv};
57 * const Sequence<CommandLine::Option> kAllOptions_{StandardCommandLineOptions::kHelp, kPortO_, kQuitAfterO_};
58 *
59 * try {
60 * cmdLine.Validate (kAllOptions_);
61 * }
62 * catch (const InvalidCommandLineArgument&) {
63 * cerr << Characters::ToString (current_exception ()).AsNarrowSDKString () << endl;
64 * cerr << cmdLine.GenerateUsage (kAllOptions_).AsNarrowSDKString () << endl;
65 * return EXIT_FAILURE;
66 * }
67 * if (cmdLine.Has (StandardCommandLineOptions::kHelp)) {
68 * cerr << cmdLine.GenerateUsage (kAllOptions_).AsNarrowSDKString () << endl;
69 * return EXIT_SUCCESS;
70 * }
71 * \endcode
72 *
73 * \par Example Usage (args to ProcessRunner)
74 * \code
75 * ProcessRunner p{CommandLine{WrapInShell::eBash, "echo $USER"}};
76 * \endcode
77 *
78 * TODO:
79 * o \todo find some way to better handle std::filesystem::path arguments (handle quoting, normalizing paths so they work better cross platform if needed/helpful (e.g. /cygrdrive/c/???)
80 *
81 */
83 public:
84 /**
85 * Used as optional CTOR argument, to create a CommandLine with
86 * bash -c "actual string arg"
87 * or
88 * cmd /C "actual string arg"
89 */
90 enum class WrapInShell {
91#if qStroika_Foundation_Common_Platform_Windows
92 eWindowsCMD,
93#endif
94 eBash,
95 };
96
97 public:
98 /**
99 * Unlike most other Stroika APIs, plain 'char' here for char*, is interpreted as being in the SDK code page
100 * (current locale - like SDKChar if narrow).
101 *
102 * CommandLine{STRING} parsing the string into individual components the way a generic shell would (space separation)
103 * and respecting quote characters).
104 *
105 * CommandLine{WrapInShell, STRING} really doesn't parse the string (except to add quotes as needed), but creates
106 * a CommandLine that will allow the argument shell to parse the command (e.g. bash -c "arguments").
107 *
108 * CommandLine{Sequence<String>} just captures that sequence of arguments and does not processing/parsing.
109 *
110 * CommandLine{argc,argv} are meant for being called from main, and also do no processing (besides treating the
111 * char* strings as SDKChar and mapping them from the OS codepage to UNICODE).
112 */
113 CommandLine () = delete;
114 CommandLine (const CommandLine&) = default;
115 CommandLine (const String& cmdLine);
116 CommandLine (WrapInShell wrapInShell, const String& cmdLine);
117 CommandLine (const Sequence<String>& cmdLine);
118 CommandLine (int argc, char* argv[]);
119 CommandLine (int argc, const char* argv[]);
120 CommandLine (int argc, wchar_t* argv[]);
121 CommandLine (int argc, const wchar_t* argv[]);
122
123 public:
124 /**
125 * \par Example Usage
126 * \code
127 * const CommandLine::Option kDashO = CommandLine::Option{.fSingleCharName = 'o', .fSupportsArgument = true };
128 * \endcode
129 *
130 * \note fSingleCharName is optional, and fLongName is also optional. Meaning its totally legal to supply no short name and no long-name (in which case its required to support fSupportsArgument)
131 *
132 * \pre fSingleCharName or fLongName or fSupportsArgument
133 * \pre not fRepeatable or fSupportsArgument
134 *
135 * \todo figure out if I can make this a literal type, so can be defined constexpr when not using fLongName (or maybe even with using stringview)
136 */
137 struct Option {
138 optional<char> fSingleCharName; // for -s
139 optional<String> fLongName; // for --long use
140
141 /**
142 * refers to long-name only - being case sensitive; defaults to eCaseInsensitive (appropriate for long names, less appropriate default for short - one letter - options).
143 */
144 Characters::CompareOptions fLongNameCaseSensitive{Characters::eCaseInsensitive};
145
146 /**
147 * Look for argument after option.
148 *
149 * if true, and long-form option, look for -OPT=XXX and copy out XXX as the argument
150 * if true, and either form option given, if no =, look for next argi, and if there, use that as argument.
151 */
152 bool fSupportsArgument{false};
153
154 /**
155 * Typically, an option that takes an argument, that argument is required. But rarely - you might want an option that takes
156 * an argument that is optional.
157 */
159
160 /**
161 * If you can have the same option repeated multiple times. The only point of this would be for
162 * things to gather multiple arguments. Note that this can be used with no fSingleCharName and no fLongName, meaning it captures
163 * un-dash-decorated arguments.
164 */
165 bool fRepeatable{false};
166
167 /**
168 * If true, then Get (o) (and GetArgument (i)) will throw if this option isn't found in the commandline; though Has () will not throw.
169 */
170 bool fRequired{false};
171
172 /**
173 * If provided, its the name used in generating help, for the argument to this option.
174 */
175 optional<String> fHelpArgName;
176
177 /**
178 * If provided, its the name used in generating help, for this option.
179 */
180 optional<String> fHelpOptionText;
181
182 bool operator== (const Option&) const = default;
183#if qCompilerAndStdLib_explicitly_defaulted_threeway_warning_Buggy
184 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdefaulted-function-deleted\"")
185#endif
186 auto operator<=> (const Option&) const = default;
187#if qCompilerAndStdLib_explicitly_defaulted_threeway_warning_Buggy
188 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdefaulted-function-deleted\"")
189#endif
190
191 nonvirtual String GetArgumentDescription (bool includeArg = false) const;
192
193 nonvirtual String ToString () const;
194 };
195
196 public:
197 /**
198 * Throw InvalidCommandLineArgument if arguments not fit with options.
199 * This checks for unrecognized arguments.
200 *
201 * \par Example Usage
202 * \code
203 * const initializer_list<Execution::CommandLine::Option> kAllOptions_{StandardCommandLineOptions::kHelp, ...others...};
204 * cmdLine.Validate (kAllOptions_); // throws InvalidCommandLineArgument if bad args so catch/report usage
205 * \endcode
206 */
207 nonvirtual void Validate (Iterable<Option> options) const;
208
209 public:
210 /**
211 */
212 nonvirtual String GenerateUsage (const Iterable<Option>& options) const;
213 static String GenerateUsage (const String& exeName, const Iterable<Option>& options);
214
215 public:
216 /*
217 * return 'argv[0]'
218 *
219 */
220 nonvirtual String GetAppName (bool onlyBaseName = true) const;
221
222 public:
223 /*
224 * return get<bool> true iff arg is present in command line.
225 * Either way, get<Sequence<String>>> returns same as GetArguments ();
226 *
227 * \see often simpler GetArgument ()
228 */
229 nonvirtual tuple<bool, Sequence<String>> Get (const Option& o) const;
230
231 public:
232 /*
233 * Return true iff arguments (in this object) have that option set. Note this will not throw just because option is required and missing (but may if ill formed).
234 */
235 nonvirtual bool Has (const Option& o) const;
236
237 public:
238 /**
239 * \pre o.fSupportsArgument
240 *
241 * \par Example Usage
242 * \code
243 * constexpr CommandLine::Option kOutFileOption_ = CommandLine::Option{.fSingleCharName = 'o', .fSupportsArgument = true };
244 * CommandLine cmdLine {argc, argv};
245 * String file2Use = cmdLine.GetArgument (kOutFileOption_).value_or ("default-file-name.xml");
246 * \endcode
247 */
248 nonvirtual optional<String> GetArgument (const Option& o) const;
249
250 public:
251 /**
252 * overload with no arguments /0 - returns all commandline arguments.
253 * \pre o.fSupportsArgument
254 */
255 nonvirtual Sequence<String> GetArguments () const;
256 nonvirtual Sequence<String> GetArguments (const Option& o) const;
257
258 public:
259 /**
260 * Strategy/rules used when converting between a list of arguments to a single string.
261 */
263 eWindowsCMD,
264 eBash
265 };
266
267 public:
268 /**
269 */
270 nonvirtual optional<StringShellQuoting> GetStringShellQuoting () const;
271 nonvirtual void SetStringShellQuoting (const optional<StringShellQuoting>& s);
272
273 public:
274 /**
275 * \par Example Usage (use default StringShellQuoting)
276 * \code
277 * String cmdLineText = cmdLine.As<String> ();
278 * \endcode
279 *
280 * \par Example Usage (use no StringShellQuoting)
281 * \code
282 * String cmdLineText = cmdLine.As<String> (nullopt);
283 * \endcode
284 *
285 * \par Example Usage (use bash style quoting)
286 * \code
287 * String cmdLineText = cmdLine.As<String> (StringShellQuoting::eBash);
288 * \endcode
289 */
290 template <Common::IAnyOf<String> T, typename... ARGS>
291 nonvirtual T As (ARGS... args) const;
292
293 public:
294 /**
295 *
296 */
297 nonvirtual String ToString () const;
298
299 private:
300 /*
301 * This may throw, but NOT for not finding option o, just for finding o, but ill-formed.
302 * Returns nullopt if Option 'o' not found at this point in sequence, or the result if it is found.
303 */
304 static optional<pair<bool, optional<String>>> ParseOneArg_ (const Option& o, Traversal::Iterator<String>* argi);
305
306 private:
307 optional<StringShellQuoting> fShellStyleQuoting_;
308 Sequence<String> fArgs_;
309 };
310 template <>
311 String CommandLine::As<String> () const;
312 template <>
313 String CommandLine::As<String> (optional<CommandLine::StringShellQuoting> shellStyle) const;
314
315 namespace StandardCommandLineOptions {
316 static inline const CommandLine::Option kHelp{.fSingleCharName = 'h', .fLongName = "help"sv, .fHelpOptionText = "Print out this help."sv};
317 static inline const CommandLine::Option kVersion{.fSingleCharName = 'v', .fLongName = "version"sv, .fHelpOptionText = "Print this application's version."sv};
318 }
319
320}
321
322/*
323 ********************************************************************************
324 ***************************** Implementation Details ***************************
325 ********************************************************************************
326 */
327#include "CommandLine.inl"
328
329#endif /*_Stroika_Foundation_Execution_CommandLine_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Definition Sequence.h:187
nonvirtual Sequence< String > GetArguments() const
nonvirtual T As(ARGS... args) const
nonvirtual optional< String > GetArgument(const Option &o) const
nonvirtual void Validate(Iterable< Option > options) const
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
An Iterator<T> is a copyable object which allows traversing the contents of some container....
Definition Iterator.h:225