args 6.4.16
A simple single-header C++11 STL-only argument parser library
Loading...
Searching...
No Matches
args.hxx
Go to the documentation of this file.
1/* A simple header-only C++ argument parser library.
2 *
3 * https://github.com/Taywee/args
4 *
5 * Copyright (c) 2016-2024 Taylor Richberger <taylor@axfive.net> and Pavel
6 * Belikov
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
33#ifndef ARGS_HXX
34#define ARGS_HXX
35#pragma push_macro("min")
36#pragma push_macro("max")
37#undef min
38#undef max
39
40#define ARGS_VERSION "6.4.16"
41#define ARGS_VERSION_MAJOR 6
42#define ARGS_VERSION_MINOR 4
43#define ARGS_VERSION_PATCH 16
44
45#include <algorithm>
46#include <iterator>
47#include <exception>
48#include <functional>
49#include <sstream>
50#include <string>
51#include <tuple>
52#include <vector>
53#include <unordered_map>
54#include <unordered_set>
55#include <type_traits>
56#include <cstddef>
57#include <cctype>
58#include <cerrno>
59#include <cstdlib>
60#include <limits>
61#include <iostream>
62
63#if defined(_MSC_VER) && _MSC_VER <= 1800
64#define noexcept
65#endif
66
70namespace args
71{
77 template <typename Option>
78 auto get(Option &option_) -> decltype(option_.Get())
79 {
80 return option_.Get();
81 }
82
91 inline std::string::size_type Glyphs(const std::string &string_)
92 {
93 std::string::size_type length = 0;
94 for (const char c: string_)
95 {
96 if ((c & 0xc0) != 0x80)
97 {
98 ++length;
99 }
100 }
101 return length;
102 }
103
107 template<typename T>
108 bool SafeAdd(T a, T b, T& out) noexcept
109 {
110 static_assert(std::is_integral<T>::value, "SafeAdd requires integral types.");
111 if (std::is_unsigned<T>::value)
112 {
113 using U = typename std::make_unsigned<T>::type;
114 const U ua = static_cast<U>(a);
115 const U ub = static_cast<U>(b);
116 const U maxv = std::numeric_limits<U>::max();
117 if (ua > maxv - ub)
118 {
119 return false;
120 }
121 out = static_cast<T>(ua + ub);
122 return true;
123 }
124 else
125 {
126#if defined(__clang__) || defined(__GNUC__)
127 return !__builtin_add_overflow(a, b, &out);
128#else
129 // Fallback bounds check
130 if (b > 0 && a > std::numeric_limits<T>::max() - b)
131 {
132 return false;
133 }
134 if (b < 0 && a < std::numeric_limits<T>::min() - b)
135 {
136 return false;
137 }
138 out = a + b;
139 return true;
140#endif
141 }
142 }
143
147 template<typename T>
148 bool SafeMultiply(T a, T b, T& out) noexcept
149 {
150 static_assert(std::is_integral<T>::value, "SafeMultiply requires integral types.");
151
152 if (a == 0 || b == 0)
153 {
154 out = 0;
155 return true;
156 }
157
158 if (std::is_unsigned<T>::value)
159 {
160 using U = typename std::make_unsigned<T>::type;
161 const U ua = static_cast<U>(a);
162 const U ub = static_cast<U>(b);
163 const U maxv = std::numeric_limits<U>::max();
164 if (ub > maxv / ua)
165 {
166 return false;
167 }
168 out = static_cast<T>(ua * ub);
169 return true;
170 }
171 else
172 {
173#if defined(__clang__) || defined(__GNUC__)
174 return !__builtin_mul_overflow(a, b, &out);
175#else
176 // Fallback bounds check
177 if (a == -1 && b == std::numeric_limits<T>::min())
178 {
179 return false;
180 }
181 if (b == -1 && a == std::numeric_limits<T>::min())
182 {
183 return false;
184 }
185 if ((a > 0 && b > 0 && a > std::numeric_limits<T>::max() / b) ||
186 (a > 0 && b < 0 && b < std::numeric_limits<T>::min() / a) ||
187 (a < 0 && b > 0 && a < std::numeric_limits<T>::min() / b) ||
188 (a < 0 && b < 0 && a < std::numeric_limits<T>::max() / b))
189 {
190 return false;
191 }
192 out = a * b;
193 return true;
194#endif
195 }
196 }
197
201 template<typename T>
202 bool SafeSub(T a, T b, T& out) noexcept
203 {
204 static_assert(std::is_integral<T>::value, "SafeSub requires integral types.");
205 if (std::is_unsigned<T>::value)
206 {
207 if (a < b)
208 {
209 return false;
210 }
211 out = a - b;
212 return true;
213 }
214 else
215 {
216#if defined(__clang__) || defined(__GNUC__)
217 return !__builtin_sub_overflow(a, b, &out);
218#else
219 // Fallback bounds check
220 if (b > 0 && a < std::numeric_limits<T>::min() + b)
221 {
222 return false;
223 }
224 if (b < 0 && a > std::numeric_limits<T>::max() + b)
225 {
226 return false;
227 }
228 out = a - b;
229 return true;
230#endif
231 }
232 }
233
237 // Unsigned overload
238 template<typename T>
239 typename std::enable_if<std::is_unsigned<T>::value, bool>::type
240 SafeNeg(T a, T& out) noexcept
241 {
242 static_assert(std::is_integral<T>::value, "SafeNeg requires integral types.");
243 if (a != 0)
244 {
245 return false;
246 }
247 out = 0;
248 return true;
249 }
250
251 // Signed overload
252 template<typename T>
253 typename std::enable_if<std::is_signed<T>::value, bool>::type
254 SafeNeg(T a, T& out) noexcept
255 {
256 static_assert(std::is_integral<T>::value, "SafeNeg requires integral types.");
257 if (a == std::numeric_limits<T>::min())
258 {
259 return false;
260 }
261 out = -a;
262 return true;
263 }
264
276 template <typename It>
277 inline std::vector<std::string> Wrap(It begin,
278 It end,
279 const std::string::size_type width,
280 std::string::size_type firstlinewidth = 0,
281 std::string::size_type firstlineindent = 0)
282 {
283 std::vector<std::string> output;
284 std::string line(firstlineindent, ' ');
285 bool empty = true;
286
287 if (firstlinewidth == 0)
288 {
289 firstlinewidth = width;
290 }
291
292 auto currentwidth = firstlinewidth;
293
294 for (auto it = begin; it != end; ++it)
295 {
296 if (it->empty())
297 {
298 continue;
299 }
300
301 if (*it == "\n")
302 {
303 if (!empty)
304 {
305 output.push_back(line);
306 line.clear();
307 empty = true;
308 currentwidth = width;
309 }
310
311 continue;
312 }
313
314 auto itemsize = Glyphs(*it);
315
316 // Refactored to prevent integer overflow
317 bool needsWrap = false;
318 if (itemsize >= currentwidth)
319 {
320 needsWrap = true;
321 }
322 else
323 {
324 size_t remainingWidth = (currentwidth > itemsize) ? (currentwidth - itemsize) : 0;
325 size_t nextLength = 0;
326 if (!SafeAdd<std::string::size_type>(line.length(), static_cast<std::string::size_type>(1), nextLength) || nextLength > remainingWidth)
327 {
328 needsWrap = true;
329 }
330 }
331
332 if (needsWrap)
333 {
334 if (!empty)
335 {
336 output.push_back(line);
337 line.clear();
338 empty = true;
339 currentwidth = width;
340 }
341 }
342
343 if (itemsize > 0)
344 {
345 if (!empty)
346 {
347 line += ' ';
348 }
349
350 line += *it;
351 empty = false;
352 }
353 }
354
355 if (!empty)
356 {
357 output.push_back(line);
358 }
359
360 return output;
361 }
362
363 namespace detail
364 {
365 template <typename T>
366 std::string Join(const T& array, const std::string &delimiter)
367 {
368 std::string res;
369
370 // Safely compute reservation size to avoid unbounded reallocations
371 using size_type = std::string::size_type;
372 size_type total = 0;
373 size_type count = 0;
374 const size_type delim_size = static_cast<size_type>(delimiter.size());
375 bool can_reserve = true;
376
377 for (const auto &element : array)
378 {
379 const size_type elem_size = static_cast<size_type>(element.size());
380 if (!SafeAdd<size_type>(total, elem_size, total))
381 {
382 can_reserve = false;
383 break;
384 }
385 ++count;
386 }
387
388 if (can_reserve && count > 1)
389 {
390 size_type delim_count = count - 1;
391 size_type delim_total = 0;
392 if (!SafeMultiply<size_type>(delim_count, delim_size, delim_total) ||
393 !SafeAdd<size_type>(total, delim_total, total))
394 {
395 can_reserve = false;
396 }
397 }
398
399 if (can_reserve && total > 0)
400 {
401 try
402 {
403 res.reserve(total);
404 }
405 catch (...) {
406 // Fall back to default allocation
407 }
408 }
409
410 bool first = true;
411 for (const auto &element : array)
412 {
413 if (!first)
414 {
415 res += delimiter;
416 }
417 res += element;
418 first = false;
419 }
420
421 return res;
422 }
423 }
424
434 inline std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0)
435 {
436 // Preserve existing line breaks
437 const auto newlineloc = in.find('\n');
438 if (newlineloc != in.npos)
439 {
440 auto first = Wrap(std::string(in, 0, newlineloc), width);
441 auto second = Wrap(std::string(in, newlineloc + 1), width);
442 first.insert(
443 std::end(first),
444 std::make_move_iterator(std::begin(second)),
445 std::make_move_iterator(std::end(second)));
446 return first;
447 }
448
449 std::istringstream stream(in);
450 std::string::size_type indent = 0;
451
452 for (auto c : in)
453 {
454 if (!std::isspace(static_cast<unsigned char>(c)))
455 {
456 break;
457 }
458 ++indent;
459 }
460
461 return Wrap(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>(),
462 width, firstlinewidth, indent);
463 }
464
465#ifdef ARGS_NOEXCEPT
467 enum class Error
468 {
469 None,
470 Usage,
471 Parse,
472 Validation,
473 Required,
474 Map,
475 Extra,
476 Help,
477 Subparser,
478 Completion,
479 };
480#else
483 class Error : public std::runtime_error
484 {
485 public:
486 Error(const std::string &problem) : std::runtime_error(problem) {}
487 virtual ~Error() {}
488 };
489
492 class UsageError : public Error
493 {
494 public:
495 UsageError(const std::string &problem) : Error(problem) {}
496 virtual ~UsageError() {}
497 };
498
501 class ParseError : public Error
502 {
503 public:
504 ParseError(const std::string &problem) : Error(problem) {}
505 virtual ~ParseError() {}
506 };
507
510 class ValidationError : public Error
511 {
512 public:
513 ValidationError(const std::string &problem) : Error(problem) {}
514 virtual ~ValidationError() {}
515 };
516
520 {
521 public:
522 RequiredError(const std::string &problem) : ValidationError(problem) {}
523 virtual ~RequiredError() {}
524 };
525
528 class MapError : public ParseError
529 {
530 public:
531 MapError(const std::string &problem) : ParseError(problem) {}
532 virtual ~MapError() {}
533 };
534
537 class ExtraError : public ParseError
538 {
539 public:
540 ExtraError(const std::string &problem) : ParseError(problem) {}
541 virtual ~ExtraError() {}
542 };
543
546 class Help : public Error
547 {
548 public:
549 Help(const std::string &flag) : Error(flag) {}
550 virtual ~Help() {}
551 };
552
555 class SubparserError : public Error
556 {
557 public:
558 SubparserError() : Error("") {}
559 virtual ~SubparserError() {}
560 };
561
564 class Completion : public Error
565 {
566 public:
567 Completion(const std::string &flag) : Error(flag) {}
568 virtual ~Completion() {}
569 };
570#endif
571
575 {
576 const bool isShort;
577 const char shortFlag;
578 const std::string longFlag;
579 EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {}
580 EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {}
581 EitherFlag(const char flag) : isShort(true), shortFlag(flag), longFlag() {}
582
585 static std::unordered_set<std::string> GetLong(std::initializer_list<EitherFlag> flags)
586 {
587 std::unordered_set<std::string> longFlags;
588 for (const EitherFlag &flag: flags)
589 {
590 if (!flag.isShort)
591 {
592 longFlags.insert(flag.longFlag);
593 }
594 }
595 return longFlags;
596 }
597
600 static std::unordered_set<char> GetShort(std::initializer_list<EitherFlag> flags)
601 {
602 std::unordered_set<char> shortFlags;
603 for (const EitherFlag &flag: flags)
604 {
605 if (flag.isShort)
606 {
607 shortFlags.insert(flag.shortFlag);
608 }
609 }
610 return shortFlags;
611 }
612
613 std::string str() const
614 {
615 return isShort ? std::string(1, shortFlag) : longFlag;
616 }
617
618 std::string str(const std::string &shortPrefix, const std::string &longPrefix) const
619 {
620 return isShort ? shortPrefix + std::string(1, shortFlag) : longPrefix + longFlag;
621 }
622 };
623
624
625
633 {
634 private:
635 const std::unordered_set<char> shortFlags;
636 const std::unordered_set<std::string> longFlags;
637
638 public:
643 template <typename ShortIt, typename LongIt>
644 Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd) :
645 shortFlags(shortFlagsStart, shortFlagsEnd),
646 longFlags(longFlagsStart, longFlagsEnd)
647 {
648 if (shortFlags.empty() && longFlags.empty())
649 {
650#ifndef ARGS_NOEXCEPT
651 throw UsageError("empty Matcher");
652#endif
653 }
654 }
655
656#ifdef ARGS_NOEXCEPT
658 Error GetError() const noexcept
659 {
660 return shortFlags.empty() && longFlags.empty() ? Error::Usage : Error::None;
661 }
662#endif
663
668 template <typename Short, typename Long>
669 Matcher(Short &&shortIn, Long &&longIn) :
670 Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn))
671 {}
672
685 Matcher(std::initializer_list<EitherFlag> in) :
686 Matcher(EitherFlag::GetShort(in), EitherFlag::GetLong(in)) {}
687
688 Matcher(Matcher &&other) noexcept : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags))
689 {}
690
691 ~Matcher() {}
692
695 bool Match(const char flag) const
696 {
697 return shortFlags.find(flag) != shortFlags.end();
698 }
699
702 bool Match(const std::string &flag) const
703 {
704 return longFlags.find(flag) != longFlags.end();
705 }
706
709 bool Match(const EitherFlag &flag) const
710 {
711 return flag.isShort ? Match(flag.shortFlag) : Match(flag.longFlag);
712 }
713
716 std::vector<EitherFlag> GetFlagStrings() const
717 {
718 std::vector<EitherFlag> flagStrings;
719 flagStrings.reserve(shortFlags.size() + longFlags.size());
720 for (const char flag: shortFlags)
721 {
722 flagStrings.emplace_back(flag);
723 }
724 for (const std::string &flag: longFlags)
725 {
726 flagStrings.emplace_back(flag);
727 }
728 return flagStrings;
729 }
730
734 {
735 if (!longFlags.empty())
736 {
737 return *longFlags.begin();
738 }
739
740 if (!shortFlags.empty())
741 {
742 return *shortFlags.begin();
743 }
744
745 // should be unreachable
746 return ' ';
747 }
748
752 {
753 if (!shortFlags.empty())
754 {
755 return *shortFlags.begin();
756 }
757
758 if (!longFlags.empty())
759 {
760 return *longFlags.begin();
761 }
762
763 // should be unreachable
764 return ' ';
765 }
766 };
767
770 enum class Options
771 {
774 None = 0x0,
775
778 Single = 0x01,
779
782 Required = 0x02,
783
786 HiddenFromUsage = 0x04,
787
791
794 Global = 0x10,
795
798 KickOut = 0x20,
799
803
807 };
808
809 inline Options operator | (Options lhs, Options rhs)
810 {
811 return static_cast<Options>(static_cast<int>(lhs) | static_cast<int>(rhs));
812 }
813
814 inline Options operator & (Options lhs, Options rhs)
815 {
816 return static_cast<Options>(static_cast<int>(lhs) & static_cast<int>(rhs));
817 }
818
819 class FlagBase;
820 class PositionalBase;
821 class Command;
822 class ArgumentParser;
823
827 {
830 unsigned int width = 80;
833 unsigned int progindent = 2;
836 unsigned int progtailindent = 4;
839 unsigned int descriptionindent = 4;
842 unsigned int flagindent = 6;
845 unsigned int helpindent = 40;
848 unsigned int eachgroupindent = 2;
849
852 unsigned int gutter = 1;
853
856 bool showTerminator = true;
857
861
865
868 std::string shortPrefix;
869
872 std::string longPrefix;
873
876 std::string shortSeparator;
877
880 std::string longSeparator;
881
884 std::string programName;
885
889
893
896 std::string proglineOptions = "{OPTIONS}";
897
900 std::string proglineCommand = "COMMAND";
901
904 std::string proglineValueOpen = " <";
905
908 std::string proglineValueClose = ">";
909
912 std::string proglineRequiredOpen = "";
913
916 std::string proglineRequiredClose = "";
917
920 std::string proglineNonrequiredOpen = "[";
921
924 std::string proglineNonrequiredClose = "]";
925
928 bool proglineShowFlags = false;
929
933
936 std::string usageString;
937
940 std::string optionsString = "OPTIONS:";
941
944 bool useValueNameOnce = false;
945
948 bool showValueName = true;
949
953
956 std::string valueOpen = "[";
957
960 std::string valueClose = "]";
961
964 bool addChoices = false;
965
968 std::string choiceString = "\nOne of: ";
969
972 bool addDefault = false;
973
976 std::string defaultString = "\nDefault: ";
977 };
978
983 struct Nargs
984 {
985 const size_t min;
986 const size_t max;
987
988 Nargs(size_t min_, size_t max_) : min{min_}, max{max_}
989 {
990#ifndef ARGS_NOEXCEPT
991 if (max < min)
992 {
993 throw UsageError("Nargs: max < min");
994 }
995#endif
996 }
997
998 Nargs(size_t num_) : min{num_}, max{num_}
999 {
1000 }
1001
1002 friend bool operator == (const Nargs &lhs, const Nargs &rhs)
1003 {
1004 return lhs.min == rhs.min && lhs.max == rhs.max;
1005 }
1006
1007 friend bool operator != (const Nargs &lhs, const Nargs &rhs)
1008 {
1009 return !(lhs == rhs);
1010 }
1011 };
1012
1015 class Base
1016 {
1017 private:
1018 Options options = {};
1019
1020 protected:
1021 bool matched = false;
1022 const std::string help;
1023#ifdef ARGS_NOEXCEPT
1025 mutable Error error = Error::None;
1026 mutable std::string errorMsg;
1027#endif
1028
1029 public:
1030 Base(const std::string &help_, Options options_ = {}) : options(options_), help(help_) {}
1031 virtual ~Base() {}
1032
1033 Options GetOptions() const noexcept
1034 {
1035 return options;
1036 }
1037
1038 bool IsRequired() const noexcept
1039 {
1040 return (GetOptions() & Options::Required) != Options::None;
1041 }
1042
1043 virtual bool Matched() const noexcept
1044 {
1045 return matched;
1046 }
1047
1048 virtual void Validate(const std::string &, const std::string &) const
1049 {
1050 }
1051
1052 operator bool() const noexcept
1053 {
1054 return Matched();
1055 }
1056
1057 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &, const unsigned indentLevel) const
1058 {
1059 std::tuple<std::string, std::string, unsigned> description;
1060 std::get<1>(description) = help;
1061 std::get<2>(description) = indentLevel;
1062 return { std::move(description) };
1063 }
1064
1065 virtual std::vector<Command*> GetCommands()
1066 {
1067 return {};
1068 }
1069
1070 virtual bool IsGroup() const
1071 {
1072 return false;
1073 }
1074
1075 virtual FlagBase *Match(const EitherFlag &)
1076 {
1077 return nullptr;
1078 }
1079
1080 virtual PositionalBase *GetNextPositional()
1081 {
1082 return nullptr;
1083 }
1084
1085 virtual std::vector<FlagBase*> GetAllFlags()
1086 {
1087 return {};
1088 }
1089
1090 virtual bool HasFlag() const
1091 {
1092 return false;
1093 }
1094
1095 virtual bool HasPositional() const
1096 {
1097 return false;
1098 }
1099
1100 virtual bool HasCommand() const
1101 {
1102 return false;
1103 }
1104
1105 virtual std::vector<std::string> GetProgramLine(const HelpParams &) const
1106 {
1107 return {};
1108 }
1109
1111 void KickOut(bool kickout_) noexcept
1112 {
1113 if (kickout_)
1114 {
1115 options = options | Options::KickOut;
1116 }
1117 else
1118 {
1119 options = static_cast<Options>(static_cast<int>(options) & ~static_cast<int>(Options::KickOut));
1120 }
1121 }
1122
1124 bool KickOut() const noexcept
1125 {
1126 return (options & Options::KickOut) != Options::None;
1127 }
1128
1129 virtual void Reset() noexcept
1130 {
1131 matched = false;
1132#ifdef ARGS_NOEXCEPT
1133 error = Error::None;
1134 errorMsg.clear();
1135#endif
1136 }
1137
1138#ifdef ARGS_NOEXCEPT
1140 virtual Error GetError() const
1141 {
1142 return error;
1143 }
1144
1146 virtual std::string GetErrorMsg() const
1147 {
1148 return errorMsg;
1149 }
1150#endif
1151 };
1152
1155 class NamedBase : public Base
1156 {
1157 protected:
1158 const std::string name;
1159 bool kickout = false;
1160 std::string defaultString;
1161 bool defaultStringManual = false;
1162 std::vector<std::string> choicesStrings;
1163 bool choicesStringManual = false;
1164
1165 virtual std::string GetDefaultString(const HelpParams&) const { return {}; }
1166
1167 virtual std::vector<std::string> GetChoicesStrings(const HelpParams&) const { return {}; }
1168
1169 virtual std::string GetNameString(const HelpParams&) const { return Name(); }
1170
1171 void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const
1172 {
1173 if (isManual && !manual.empty())
1174 {
1175 dest += str;
1176 dest += manual;
1177 }
1178 else if (!isManual && isGenerated && !generated.empty())
1179 {
1180 dest += str;
1181 dest += generated;
1182 }
1183 }
1184
1185 public:
1186 NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {}
1187 virtual ~NamedBase() {}
1188
1192 void HelpDefault(const std::string &str)
1193 {
1194 defaultStringManual = true;
1195 defaultString = str;
1196 }
1197
1200 std::string HelpDefault(const HelpParams &params) const
1201 {
1202 return defaultStringManual ? defaultString : GetDefaultString(params);
1203 }
1204
1208 void HelpChoices(const std::vector<std::string> &array)
1209 {
1210 choicesStringManual = true;
1211 choicesStrings = array;
1212 }
1213
1216 std::vector<std::string> HelpChoices(const HelpParams &params) const
1217 {
1218 return choicesStringManual ? choicesStrings : GetChoicesStrings(params);
1219 }
1220
1221 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned indentLevel) const override
1222 {
1223 std::tuple<std::string, std::string, unsigned> description;
1224 std::get<0>(description) = GetNameString(params);
1225 std::get<1>(description) = help;
1226 std::get<2>(description) = indentLevel;
1227
1228 AddDescriptionPostfix(std::get<1>(description), choicesStringManual, detail::Join(choicesStrings, ", "), params.addChoices, detail::Join(GetChoicesStrings(params), ", "), params.choiceString);
1229 AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString);
1230
1231 return { std::move(description) };
1232 }
1233
1234 virtual std::string Name() const
1235 {
1236 return name;
1237 }
1238 };
1239
1240 namespace detail
1241 {
1242 template<typename T>
1243 using vector = std::vector<T, std::allocator<T>>;
1244
1245 template<typename K, typename T>
1246 using unordered_map = std::unordered_map<K, T, std::hash<K>,
1247 std::equal_to<K>, std::allocator<std::pair<const K, T> > >;
1248
1249 template<typename S, typename T>
1251 {
1252 template<typename SS, typename TT>
1253 static auto test(int)
1254 -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
1255
1256 template<typename, typename>
1257 static auto test(...) -> std::false_type;
1258
1259 public:
1260 using type = decltype(test<S,T>(0));
1261 };
1262
1263 template <typename T>
1264 using IsConvertableToString = typename is_streamable<std::ostringstream, T>::type;
1265
1266 template <typename T>
1267 typename std::enable_if<IsConvertableToString<T>::value, std::string>::type
1268 ToString(const T &value)
1269 {
1270 std::ostringstream s;
1271 s << value;
1272 return s.str();
1273 }
1274
1275 template <typename T>
1276 typename std::enable_if<!IsConvertableToString<T>::value, std::string>::type
1277 ToString(const T &)
1278 {
1279 return {};
1280 }
1281
1282 template <typename T>
1283 std::vector<std::string> MapKeysToStrings(const T &map)
1284 {
1285 std::vector<std::string> res;
1286 using K = typename std::decay<decltype(std::begin(map)->first)>::type;
1287 if (IsConvertableToString<K>::value)
1288 {
1289 for (const auto &p : map)
1290 {
1291 res.push_back(detail::ToString(p.first));
1292 }
1293
1294 std::sort(res.begin(), res.end());
1295 }
1296 return res;
1297 }
1298 }
1299
1302 class FlagBase : public NamedBase
1303 {
1304 protected:
1305 const Matcher matcher;
1306
1307 virtual std::string GetNameString(const HelpParams &params) const override
1308 {
1309 const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name();
1310 std::string flags;
1311 const auto flagStrings = matcher.GetFlagStrings();
1312 const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce;
1313 for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it)
1314 {
1315 auto &flag = *it;
1316 if (it != flagStrings.begin())
1317 {
1318 flags += ", ";
1319 }
1320
1321 flags += flag.isShort ? params.shortPrefix : params.longPrefix;
1322 flags += flag.str();
1323
1324 if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end()))
1325 {
1326 flags += flag.isShort ? params.shortSeparator : params.longSeparator;
1327 flags += params.valueOpen + postfix + params.valueClose;
1328 }
1329 }
1330
1331 return flags;
1332 }
1333
1334 public:
1335 FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : NamedBase(name_, help_, extraError_ ? Options::Single : Options()), matcher(std::move(matcher_)) {}
1336
1337 FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {}
1338
1339 virtual ~FlagBase() {}
1340
1341 virtual FlagBase *Match(const EitherFlag &flag) override
1342 {
1343 if (matcher.Match(flag))
1344 {
1345 if ((GetOptions() & Options::Single) != Options::None && matched)
1346 {
1347 std::ostringstream problem;
1348 problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once";
1349#ifdef ARGS_NOEXCEPT
1350 error = Error::Extra;
1351 errorMsg = problem.str();
1352#else
1353 throw ExtraError(problem.str());
1354#endif
1355 }
1356 matched = true;
1357 return this;
1358 }
1359 return nullptr;
1360 }
1361
1362 virtual std::vector<FlagBase*> GetAllFlags() override
1363 {
1364 return { this };
1365 }
1366
1367 const Matcher &GetMatcher() const
1368 {
1369 return matcher;
1370 }
1371
1372 virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1373 {
1374 if (!Matched() && IsRequired())
1375 {
1376 std::ostringstream problem;
1377 problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required";
1378#ifdef ARGS_NOEXCEPT
1379 error = Error::Required;
1380 errorMsg = problem.str();
1381#else
1382 throw RequiredError(problem.str());
1383#endif
1384 }
1385 }
1386
1387 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1388 {
1389 if (!params.proglineShowFlags)
1390 {
1391 return {};
1392 }
1393
1394 const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name();
1395 const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny();
1396 std::string res = flag.str(params.shortPrefix, params.longPrefix);
1397 if (!postfix.empty())
1398 {
1399 res += params.proglineValueOpen + postfix + params.proglineValueClose;
1400 }
1401
1402 return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose
1403 : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose };
1404 }
1405
1406 virtual bool HasFlag() const override
1407 {
1408 return true;
1409 }
1410
1411#ifdef ARGS_NOEXCEPT
1413 virtual Error GetError() const override
1414 {
1415 const auto nargs = NumberOfArguments();
1416 if (nargs.min > nargs.max)
1417 {
1418 return Error::Usage;
1419 }
1420
1421 const auto matcherError = matcher.GetError();
1422 if (matcherError != Error::None)
1423 {
1424 return matcherError;
1425 }
1426
1427 return error;
1428 }
1429#endif
1430
1435 virtual Nargs NumberOfArguments() const noexcept = 0;
1436
1441 virtual void ParseValue(const std::vector<std::string> &value) = 0;
1442 };
1443
1447 {
1448 public:
1449 ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {}
1450 ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {}
1451 virtual ~ValueFlagBase() {}
1452
1453 virtual Nargs NumberOfArguments() const noexcept override
1454 {
1455 return 1;
1456 }
1457 };
1458
1460 {
1461 public:
1462 std::vector<std::string> reply;
1463 size_t cword = 0;
1464 std::string syntax;
1465
1466 template <typename GroupClass>
1467 CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden)
1468 {
1469 group_.AddCompletion(*this);
1470 }
1471
1472 virtual ~CompletionFlag() {}
1473
1474 virtual Nargs NumberOfArguments() const noexcept override
1475 {
1476 return 2;
1477 }
1478
1479 virtual void ParseValue(const std::vector<std::string> &value_) override
1480 {
1481 syntax = value_.at(0);
1482 const std::string &raw = value_.at(1);
1483 bool failed = false;
1484
1485 const auto firstNonSpace = std::find_if_not(raw.begin(), raw.end(), [](char c)
1486 {
1487 return std::isspace(static_cast<unsigned char>(c)) != 0;
1488 });
1489
1490 // Reject explicit signs: cword must be a plain non-negative
1491 // decimal index. istringstream would otherwise silently
1492 // accept "+1".
1493 if (firstNonSpace != raw.end() && (*firstNonSpace == '-' || *firstNonSpace == '+'))
1494 {
1495 failed = true;
1496 }
1497
1498 size_t parsed = 0;
1499 if (!failed)
1500 {
1501 std::istringstream ss(raw);
1502 // Use the C locale so that the cword index parses
1503 // consistently regardless of any std::locale::global call
1504 // elsewhere in the process. A locale with a non-empty
1505 // grouping facet would otherwise reject digit-only inputs
1506 // like "12" when grouping rules expect separators.
1507 ss.imbue(std::locale::classic());
1508 ss >> parsed;
1509 if (ss.fail())
1510 {
1511 failed = true;
1512 }
1513 else
1514 {
1515 char extra;
1516 if (ss >> extra)
1517 {
1518 failed = true;
1519 }
1520 else if (!ss.eof())
1521 {
1522 failed = true;
1523 }
1524 }
1525 }
1526
1527 if (failed)
1528 {
1529#ifdef ARGS_NOEXCEPT
1530 error = Error::Parse;
1531 errorMsg = "Argument 'completion' received invalid value type '" + raw + "'";
1532#else
1533 std::ostringstream problem;
1534 problem << "Argument 'completion' received invalid value type '" << raw << "'";
1535 throw ParseError(problem.str());
1536#endif
1537 return;
1538 }
1539
1540 cword = parsed;
1541 }
1542
1545 std::string Get() noexcept
1546 {
1547 return detail::Join(reply, "\n");
1548 }
1549
1550 virtual void Reset() noexcept override
1551 {
1552 ValueFlagBase::Reset();
1553 cword = 0;
1554 syntax.clear();
1555 reply.clear();
1556 }
1557 };
1558
1559
1563 {
1564 protected:
1565 bool ready;
1566
1567 public:
1568 PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {}
1569 virtual ~PositionalBase() {}
1570
1571 bool Ready()
1572 {
1573 return ready;
1574 }
1575
1576 virtual void ParseValue(const std::string &value_) = 0;
1577
1578 virtual void Reset() noexcept override
1579 {
1580 matched = false;
1581 ready = true;
1582#ifdef ARGS_NOEXCEPT
1583 error = Error::None;
1584 errorMsg.clear();
1585#endif
1586 }
1587
1588 virtual PositionalBase *GetNextPositional() override
1589 {
1590 return Ready() ? this : nullptr;
1591 }
1592
1593 virtual bool HasPositional() const override
1594 {
1595 return true;
1596 }
1597
1598 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1599 {
1600 return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose
1601 : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose };
1602 }
1603
1604 virtual void Validate(const std::string &, const std::string &) const override
1605 {
1606 if (IsRequired() && !Matched())
1607 {
1608 std::ostringstream problem;
1609 problem << "Option '" << Name() << "' is required";
1610#ifdef ARGS_NOEXCEPT
1611 error = Error::Required;
1612 errorMsg = problem.str();
1613#else
1614 throw RequiredError(problem.str());
1615#endif
1616 }
1617 }
1618 };
1619
1622 class Group : public Base
1623 {
1624 private:
1625 std::vector<Base*> children;
1626 std::function<bool(const Group &)> validator;
1627
1628 public:
1632 {
1633 static bool Xor(const Group &group)
1634 {
1635 return group.MatchedChildren() == 1;
1636 }
1637
1638 static bool AtLeastOne(const Group &group)
1639 {
1640 return group.MatchedChildren() >= 1;
1641 }
1642
1643 static bool AtMostOne(const Group &group)
1644 {
1645 return group.MatchedChildren() <= 1;
1646 }
1647
1648 static bool All(const Group &group)
1649 {
1650 return group.Children().size() == group.MatchedChildren();
1651 }
1652
1653 static bool AllOrNone(const Group &group)
1654 {
1655 return (All(group) || None(group));
1656 }
1657
1658 static bool AllChildGroups(const Group &group)
1659 {
1660 return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool {
1661 return child->IsGroup() && !child->Matched();
1662 });
1663 }
1664
1665 static bool DontCare(const Group &)
1666 {
1667 return true;
1668 }
1669
1670 static bool CareTooMuch(const Group &)
1671 {
1672 return false;
1673 }
1674
1675 static bool None(const Group &group)
1676 {
1677 return group.MatchedChildren() == 0;
1678 }
1679 };
1681 Group(const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) {}
1683 Group(Group &group_, const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_)
1684 {
1685 group_.Add(*this);
1686 }
1687 virtual ~Group() {}
1688
1691 void Add(Base &child)
1692 {
1693 children.emplace_back(&child);
1694 }
1695
1698 const std::vector<Base *> &Children() const
1699 {
1700 return children;
1701 }
1702
1708 virtual FlagBase *Match(const EitherFlag &flag) override
1709 {
1710 for (Base *child: Children())
1711 {
1712 if (FlagBase *match = child->Match(flag))
1713 {
1714 return match;
1715 }
1716 }
1717 return nullptr;
1718 }
1719
1720 virtual std::vector<FlagBase*> GetAllFlags() override
1721 {
1722 std::vector<FlagBase*> res;
1723 for (Base *child: Children())
1724 {
1725 auto childRes = child->GetAllFlags();
1726 res.insert(res.end(), childRes.begin(), childRes.end());
1727 }
1728 return res;
1729 }
1730
1731 virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1732 {
1733 for (Base *child: Children())
1734 {
1735 child->Validate(shortPrefix, longPrefix);
1736 }
1737 }
1738
1744 {
1745 for (Base *child: Children())
1746 {
1747 if (auto next = child->GetNextPositional())
1748 {
1749 return next;
1750 }
1751 }
1752 return nullptr;
1753 }
1754
1759 virtual bool HasFlag() const override
1760 {
1761 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); });
1762 }
1763
1768 virtual bool HasPositional() const override
1769 {
1770 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); });
1771 }
1772
1777 virtual bool HasCommand() const override
1778 {
1779 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); });
1780 }
1781
1784 std::vector<Base *>::size_type MatchedChildren() const
1785 {
1786 // Cast to avoid warnings from -Wsign-conversion
1787 return static_cast<std::vector<Base *>::size_type>(
1788 std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();}));
1789 }
1790
1793 virtual bool Matched() const noexcept override
1794 {
1795 return validator(*this);
1796 }
1797
1800 bool Get() const
1801 {
1802 return Matched();
1803 }
1804
1807 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
1808 {
1809 std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions;
1810
1811 // Push that group description on the back if not empty
1812 unsigned addindent = 0;
1813 if (!help.empty())
1814 {
1815 descriptions.emplace_back(help, "", indent);
1816 addindent = 1;
1817 }
1818
1819 for (Base *child: Children())
1820 {
1821 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
1822 {
1823 continue;
1824 }
1825
1826 auto groupDescriptions = child->GetDescription(params, indent + addindent);
1827 descriptions.insert(
1828 std::end(descriptions),
1829 std::make_move_iterator(std::begin(groupDescriptions)),
1830 std::make_move_iterator(std::end(groupDescriptions)));
1831 }
1832 return descriptions;
1833 }
1834
1837 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1838 {
1839 std::vector <std::string> names;
1840 for (Base *child: Children())
1841 {
1842 if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None)
1843 {
1844 continue;
1845 }
1846
1847 auto groupNames = child->GetProgramLine(params);
1848 names.insert(
1849 std::end(names),
1850 std::make_move_iterator(std::begin(groupNames)),
1851 std::make_move_iterator(std::end(groupNames)));
1852 }
1853 return names;
1854 }
1855
1856 virtual std::vector<Command*> GetCommands() override
1857 {
1858 std::vector<Command*> res;
1859 for (const auto &child : Children())
1860 {
1861 auto subparsers = child->GetCommands();
1862 res.insert(std::end(res), std::begin(subparsers), std::end(subparsers));
1863 }
1864 return res;
1865 }
1866
1867 virtual bool IsGroup() const override
1868 {
1869 return true;
1870 }
1871
1872 virtual void Reset() noexcept override
1873 {
1874 Base::Reset();
1875
1876 for (auto &child: Children())
1877 {
1878 child->Reset();
1879 }
1880#ifdef ARGS_NOEXCEPT
1881 error = Error::None;
1882 errorMsg.clear();
1883#endif
1884 }
1885
1886#ifdef ARGS_NOEXCEPT
1888 virtual Error GetError() const override
1889 {
1890 if (error != Error::None)
1891 {
1892 return error;
1893 }
1894
1895 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
1896 if (it == Children().end())
1897 {
1898 return Error::None;
1899 } else
1900 {
1901 return (*it)->GetError();
1902 }
1903 }
1904
1906 virtual std::string GetErrorMsg() const override
1907 {
1908 if (error != Error::None)
1909 {
1910 return errorMsg;
1911 }
1912
1913 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
1914 if (it == Children().end())
1915 {
1916 return "";
1917 } else
1918 {
1919 return (*it)->GetErrorMsg();
1920 }
1921 }
1922#endif
1923
1924 };
1925
1928 class GlobalOptions : public Group
1929 {
1930 public:
1931 GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global)
1932 {
1933 Add(options_);
1934 }
1935 };
1936
1954 class Subparser : public Group
1955 {
1956 private:
1957 std::vector<std::string> args;
1958 std::vector<std::string> kicked;
1959 ArgumentParser *parser = nullptr;
1960 const HelpParams &helpParams;
1961 const Command &command;
1962 bool isParsed = false;
1963
1964 public:
1965 Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_)
1966 : Group({}, Validators::AllChildGroups), args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_)
1967 {
1968 }
1969
1970 Subparser(const Command &command_, const HelpParams &helpParams_) : Group({}, Validators::AllChildGroups), helpParams(helpParams_), command(command_)
1971 {
1972 }
1973
1974 Subparser(const Subparser&) = delete;
1975 Subparser(Subparser&&) = delete;
1976 Subparser &operator = (const Subparser&) = delete;
1977 Subparser &operator = (Subparser&&) = delete;
1978
1979 const Command &GetCommand()
1980 {
1981 return command;
1982 }
1983
1986 bool IsParsed() const
1987 {
1988 return isParsed;
1989 }
1990
1993 void Parse();
1994
1999 const std::vector<std::string> &KickedOut() const noexcept
2000 {
2001 return kicked;
2002 }
2003 };
2004
2009 class Command : public Group
2010 {
2011 private:
2012 friend class Subparser;
2013
2014 std::string name;
2015 std::string help;
2016 std::string description;
2017 std::string epilog;
2018 std::string proglinePostfix;
2019
2020 std::function<void(Subparser&)> parserCoroutine;
2021 bool commandIsRequired = true;
2022 Command *selectedCommand = nullptr;
2023
2024 mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription;
2025 mutable std::vector<std::string> subparserProgramLine;
2026 mutable bool subparserHasFlag = false;
2027 mutable bool subparserHasPositional = false;
2028 mutable bool subparserHasCommand = false;
2029#ifdef ARGS_NOEXCEPT
2030 mutable Error subparserError = Error::None;
2031#endif
2032 mutable Subparser *subparser = nullptr;
2033
2034 protected:
2035
2036 class RaiiSubparser
2037 {
2038 public:
2039 RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_);
2040 RaiiSubparser(const Command &command_, const HelpParams &params_);
2041
2042 ~RaiiSubparser()
2043 {
2044 command.subparser = oldSubparser;
2045 }
2046
2047 Subparser &Parser()
2048 {
2049 return parser;
2050 }
2051
2052 private:
2053 const Command &command;
2054 Subparser parser;
2055 Subparser *oldSubparser;
2056 };
2057
2058 Command() = default;
2059
2060 std::function<void(Subparser&)> &GetCoroutine()
2061 {
2062 return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine;
2063 }
2064
2065 Command &SelectedCommand()
2066 {
2067 Command *res = this;
2068 while (res->selectedCommand != nullptr)
2069 {
2070 res = res->selectedCommand;
2071 }
2072
2073 return *res;
2074 }
2075
2076 const Command &SelectedCommand() const
2077 {
2078 const Command *res = this;
2079 while (res->selectedCommand != nullptr)
2080 {
2081 res = res->selectedCommand;
2082 }
2083
2084 return *res;
2085 }
2086
2087 void UpdateSubparserHelp(const HelpParams &params) const
2088 {
2089 if (parserCoroutine)
2090 {
2091 RaiiSubparser coro(*this, params);
2092#ifndef ARGS_NOEXCEPT
2093 try
2094 {
2095 parserCoroutine(coro.Parser());
2096 }
2097 catch (args::SubparserError&)
2098 {
2099 }
2100#else
2101 parserCoroutine(coro.Parser());
2102#endif
2103 }
2104 }
2105
2106 public:
2107 Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {})
2108 : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_))
2109 {
2110 base_.Add(*this);
2111 }
2112
2115 const std::string &ProglinePostfix() const
2116 { return proglinePostfix; }
2117
2120 void ProglinePostfix(const std::string &proglinePostfix_)
2121 { this->proglinePostfix = proglinePostfix_; }
2122
2125 const std::string &Description() const
2126 { return description; }
2130 void Description(const std::string &description_)
2131 { this->description = description_; }
2132
2135 const std::string &Epilog() const
2136 { return epilog; }
2137
2140 void Epilog(const std::string &epilog_)
2141 { this->epilog = epilog_; }
2142
2145 const std::string &Name() const
2146 { return name; }
2147
2150 const std::string &Help() const
2151 { return help; }
2152
2157 void RequireCommand(bool value)
2158 { commandIsRequired = value; }
2159
2160 virtual bool IsGroup() const override
2161 { return false; }
2162
2163 virtual bool Matched() const noexcept override
2164 { return Base::Matched(); }
2165
2166 operator bool() const noexcept
2167 { return Matched(); }
2168
2169 void Match() noexcept
2170 { matched = true; }
2171
2172 void SelectCommand(Command *c) noexcept
2173 {
2174 selectedCommand = c;
2175
2176 if (c != nullptr)
2177 {
2178 c->Match();
2179 }
2180 }
2181
2182 virtual FlagBase *Match(const EitherFlag &flag) override
2183 {
2184 if (selectedCommand != nullptr)
2185 {
2186 if (auto *res = selectedCommand->Match(flag))
2187 {
2188 return res;
2189 }
2190
2191 for (auto *child: Children())
2192 {
2193 if ((child->GetOptions() & Options::Global) != Options::None)
2194 {
2195 if (auto *res = child->Match(flag))
2196 {
2197 return res;
2198 }
2199 }
2200 }
2201
2202 return nullptr;
2203 }
2204
2205 if (subparser != nullptr)
2206 {
2207 return subparser->Match(flag);
2208 }
2209
2210 return Matched() ? Group::Match(flag) : nullptr;
2211 }
2212
2213 virtual std::vector<FlagBase*> GetAllFlags() override
2214 {
2215 std::vector<FlagBase*> res;
2216
2217 if (!Matched())
2218 {
2219 return res;
2220 }
2221
2222 for (auto *child: Children())
2223 {
2224 if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None)
2225 {
2226 auto childFlags = child->GetAllFlags();
2227 res.insert(res.end(), childFlags.begin(), childFlags.end());
2228 }
2229 }
2230
2231 if (selectedCommand != nullptr)
2232 {
2233 auto childFlags = selectedCommand->GetAllFlags();
2234 res.insert(res.end(), childFlags.begin(), childFlags.end());
2235 }
2236
2237 if (subparser != nullptr)
2238 {
2239 auto childFlags = subparser->GetAllFlags();
2240 res.insert(res.end(), childFlags.begin(), childFlags.end());
2241 }
2242
2243 return res;
2244 }
2245
2247 {
2248 if (selectedCommand != nullptr)
2249 {
2250 if (auto *res = selectedCommand->GetNextPositional())
2251 {
2252 return res;
2253 }
2254
2255 for (auto *child: Children())
2256 {
2257 if ((child->GetOptions() & Options::Global) != Options::None)
2258 {
2259 if (auto *res = child->GetNextPositional())
2260 {
2261 return res;
2262 }
2263 }
2264 }
2265
2266 return nullptr;
2267 }
2268
2269 if (subparser != nullptr)
2270 {
2271 return subparser->GetNextPositional();
2272 }
2273
2274 return Matched() ? Group::GetNextPositional() : nullptr;
2275 }
2276
2277 virtual bool HasFlag() const override
2278 {
2279 return subparserHasFlag || Group::HasFlag();
2280 }
2281
2282 virtual bool HasPositional() const override
2283 {
2284 return subparserHasPositional || Group::HasPositional();
2285 }
2286
2287 virtual bool HasCommand() const override
2288 {
2289 return true;
2290 }
2291
2292 std::vector<std::string> GetCommandProgramLine(const HelpParams &params) const
2293 {
2294 UpdateSubparserHelp(params);
2295
2296 std::vector<std::string> res;
2297
2298 if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags)
2299 {
2300 res.push_back(params.proglineOptions);
2301 }
2302
2303 auto group_res = Group::GetProgramLine(params);
2304 std::move(std::move(group_res).begin(), std::move(group_res).end(), std::back_inserter(res));
2305
2306 res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end());
2307
2308 if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand))
2309 {
2310 res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]");
2311 }
2312
2313 if (!Name().empty())
2314 {
2315 res.insert(res.begin(), Name());
2316 }
2317
2318 if (!ProglinePostfix().empty())
2319 {
2320 std::string line;
2321 for (auto c : ProglinePostfix())
2322 {
2323 if (std::isspace(static_cast<unsigned char>(c)))
2324 {
2325 if (!line.empty())
2326 {
2327 res.push_back(line);
2328 line.clear();
2329 }
2330
2331 if (c == '\n')
2332 {
2333 res.push_back("\n");
2334 }
2335 }
2336 else
2337 {
2338 line += c;
2339 }
2340 }
2341
2342 if (!line.empty())
2343 {
2344 res.push_back(line);
2345 }
2346 }
2347
2348 return res;
2349 }
2350
2351 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
2352 {
2353 if (!Matched())
2354 {
2355 return {};
2356 }
2357
2358 return GetCommandProgramLine(params);
2359 }
2360
2361 virtual std::vector<Command*> GetCommands() override
2362 {
2363 if (selectedCommand != nullptr)
2364 {
2365 return selectedCommand->GetCommands();
2366 }
2367
2368 if (Matched())
2369 {
2370 return Group::GetCommands();
2371 }
2372
2373 return { this };
2374 }
2375
2376 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
2377 {
2378 std::vector<std::tuple<std::string, std::string, unsigned>> descriptions;
2379 unsigned addindent = 0;
2380
2381 UpdateSubparserHelp(params);
2382
2383 if (!Matched())
2384 {
2385 if (params.showCommandFullHelp)
2386 {
2387 std::ostringstream s;
2388 bool empty = true;
2389 for (const auto &progline: GetCommandProgramLine(params))
2390 {
2391 if (!empty)
2392 {
2393 s << ' ';
2394 }
2395 else
2396 {
2397 empty = false;
2398 }
2399
2400 s << progline;
2401 }
2402
2403 descriptions.emplace_back(s.str(), "", indent);
2404 }
2405 else
2406 {
2407 descriptions.emplace_back(Name(), help, indent);
2408 }
2409
2410 if (!params.showCommandChildren && !params.showCommandFullHelp)
2411 {
2412 return descriptions;
2413 }
2414
2415 addindent = 1;
2416 }
2417
2418 if (params.showCommandFullHelp && !Matched())
2419 {
2420 descriptions.emplace_back("", "", indent + addindent);
2421 descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent);
2422 descriptions.emplace_back("", "", indent + addindent);
2423 }
2424
2425 for (Base *child: Children())
2426 {
2427 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
2428 {
2429 continue;
2430 }
2431
2432 auto groupDescriptions = child->GetDescription(params, indent + addindent);
2433 descriptions.insert(
2434 std::end(descriptions),
2435 std::make_move_iterator(std::begin(groupDescriptions)),
2436 std::make_move_iterator(std::end(groupDescriptions)));
2437 }
2438
2439 for (auto childDescription: subparserDescription)
2440 {
2441 std::get<2>(childDescription) += indent + addindent;
2442 descriptions.push_back(std::move(childDescription));
2443 }
2444
2445 if (params.showCommandFullHelp && !Matched())
2446 {
2447 descriptions.emplace_back("", "", indent + addindent);
2448 if (!Epilog().empty())
2449 {
2450 descriptions.emplace_back(Epilog(), "", indent + addindent);
2451 descriptions.emplace_back("", "", indent + addindent);
2452 }
2453 }
2454
2455 return descriptions;
2456 }
2457
2458 virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override
2459 {
2460 if (!Matched())
2461 {
2462 return;
2463 }
2464
2465 auto onValidationError = [&]
2466 {
2467 std::ostringstream problem;
2468 problem << "Group validation failed somewhere!";
2469#ifdef ARGS_NOEXCEPT
2470 error = Error::Validation;
2471 errorMsg = problem.str();
2472#else
2473 throw ValidationError(problem.str());
2474#endif
2475 };
2476
2477 for (Base *child: Children())
2478 {
2479 if (child->IsGroup() && !child->Matched())
2480 {
2481 onValidationError();
2482 }
2483
2484 child->Validate(shortprefix, longprefix);
2485 }
2486
2487 if (subparser != nullptr)
2488 {
2489 subparser->Validate(shortprefix, longprefix);
2490 if (!subparser->Matched())
2491 {
2492 onValidationError();
2493 }
2494 }
2495
2496 if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand))
2497 {
2498 std::ostringstream problem;
2499 problem << "Command is required";
2500#ifdef ARGS_NOEXCEPT
2501 error = Error::Validation;
2502 errorMsg = problem.str();
2503#else
2504 throw ValidationError(problem.str());
2505#endif
2506 }
2507 }
2508
2509 virtual void Reset() noexcept override
2510 {
2511 Group::Reset();
2512 selectedCommand = nullptr;
2513 subparserProgramLine.clear();
2514 subparserDescription.clear();
2515 subparserHasFlag = false;
2516 subparserHasPositional = false;
2517 subparserHasCommand = false;
2518#ifdef ARGS_NOEXCEPT
2519 subparserError = Error::None;
2520#endif
2521 }
2522
2523#ifdef ARGS_NOEXCEPT
2525 virtual Error GetError() const override
2526 {
2527 if (!Matched())
2528 {
2529 return Error::None;
2530 }
2531
2532 if (error != Error::None)
2533 {
2534 return error;
2535 }
2536
2537 if (subparserError != Error::None)
2538 {
2539 return subparserError;
2540 }
2541
2542 return Group::GetError();
2543 }
2544#endif
2545 };
2546
2550 {
2551 friend class Subparser;
2552
2553 private:
2554 std::string longprefix;
2555 std::string shortprefix;
2556
2557 std::string longseparator;
2558
2559 std::string terminator;
2560
2561 bool allowJoinedShortValue = true;
2562 bool allowJoinedLongValue = true;
2563 bool allowSeparateShortValue = true;
2564 bool allowSeparateLongValue = true;
2565
2566 bool readCompletion = false;
2567 CompletionFlag *completion = nullptr;
2568
2569 protected:
2570 enum class OptionType
2571 {
2572 LongFlag,
2573 ShortFlag,
2575 };
2576
2577 OptionType ParseOption(const std::string &s, bool allowEmpty = false)
2578 {
2579 if (s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length()))
2580 {
2581 return OptionType::LongFlag;
2582 }
2583
2584 if (s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length()))
2585 {
2586 return OptionType::ShortFlag;
2587 }
2588
2589 return OptionType::Positional;
2590 }
2591
2592 template <typename It>
2593 bool Complete(FlagBase &flag, It it, It end)
2594 {
2595 auto nextIt = it;
2596 if (!readCompletion || (++nextIt != end))
2597 {
2598 return false;
2599 }
2600
2601 const auto &chunk = *it;
2602 for (auto &choice : flag.HelpChoices(helpParams))
2603 {
2604 AddCompletionReply(chunk, choice);
2605 }
2606
2607#ifndef ARGS_NOEXCEPT
2608 throw Completion(completion->Get());
2609#else
2610 return true;
2611#endif
2612 }
2613
2623 template <typename It>
2624 std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end,
2625 const bool allowSeparate, const bool allowJoined,
2626 const bool hasJoined, const std::string &joinedArg,
2627 const bool canDiscardJoined, std::vector<std::string> &values)
2628 {
2629 values.clear();
2630
2631 Nargs nargs = flag.NumberOfArguments();
2632
2633 if (hasJoined && !allowJoined && nargs.min != 0)
2634 {
2635 return "Flag '" + arg + "' was passed a joined argument, but these are disallowed";
2636 }
2637
2638 if (hasJoined)
2639 {
2640 if (!canDiscardJoined || nargs.max != 0)
2641 {
2642 values.push_back(joinedArg);
2643 }
2644 } else if (!allowSeparate)
2645 {
2646 if (nargs.min != 0)
2647 {
2648 return "Flag '" + arg + "' was passed a separate argument, but these are disallowed";
2649 }
2650 } else
2651 {
2652 auto valueIt = it;
2653 ++valueIt;
2654
2655 while (valueIt != end &&
2656 values.size() < nargs.max &&
2657 (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional))
2658 {
2659 if (Complete(flag, valueIt, end))
2660 {
2661 // Park `it` on the completion position rather than
2662 // `end`. In ARGS_NOEXCEPT mode Complete returns
2663 // true (no throw), so the caller's for-loop will
2664 // run its ++it after we return; advancing an
2665 // already-end iterator is undefined behavior and
2666 // causes a subsequent out-of-bounds read of the
2667 // arg vector. Since Complete only fires when
2668 // ++nextIt == end, valueIt is the last element,
2669 // and ++(it=valueIt) safely lands on end.
2670 it = valueIt;
2671 return "";
2672 }
2673
2674 values.push_back(*valueIt);
2675 ++it;
2676 ++valueIt;
2677 }
2678 }
2679
2680 if (values.size() > nargs.max)
2681 {
2682 return "Passed an argument into a non-argument flag: " + arg;
2683 } else if (values.size() < nargs.min)
2684 {
2685 if (nargs.min == 1 && nargs.max == 1)
2686 {
2687 return "Flag '" + arg + "' requires an argument but received none";
2688 } else if (nargs.min == 1)
2689 {
2690 return "Flag '" + arg + "' requires at least one argument but received none";
2691 } else if (nargs.min != nargs.max)
2692 {
2693 return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) +
2694 " arguments but received " + std::to_string(values.size());
2695 } else
2696 {
2697 return "Flag '" + arg + "' requires " + std::to_string(nargs.min) +
2698 " arguments but received " + std::to_string(values.size());
2699 }
2700 }
2701
2702 return {};
2703 }
2704
2705 template <typename It>
2706 bool ParseLong(It &it, It end)
2707 {
2708 const auto &chunk = *it;
2709 const auto argchunk = chunk.substr(longprefix.size());
2710 // Try to separate it, in case of a separator:
2711 const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator);
2712 // If the separator is in the argument, separate it.
2713 const auto arg = (separator != argchunk.npos ?
2714 std::string(argchunk, 0, separator)
2715 : argchunk);
2716 const auto joined = (separator != argchunk.npos ?
2717 argchunk.substr(separator + longseparator.size())
2718 : std::string());
2719
2720 if (auto flag = Match(arg))
2721 {
2722#ifdef ARGS_NOEXCEPT
2723 // Match() may set the flag's error (e.g. Error::Extra when
2724 // Options::Single is violated). In non-noexcept mode that
2725 // path throws and parsing stops before the value is read;
2726 // in noexcept mode we must mirror that and skip the value
2727 // parsing so the previously-stored value is preserved.
2728 if (flag->GetError() != Error::None)
2729 {
2730 return false;
2731 }
2732#endif
2733 std::vector<std::string> values;
2734 const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue,
2735 separator != argchunk.npos, joined, false, values);
2736 if (!errorMessage.empty())
2737 {
2738#ifndef ARGS_NOEXCEPT
2739 throw ParseError(errorMessage);
2740#else
2741 error = Error::Parse;
2742 errorMsg = errorMessage;
2743 return false;
2744#endif
2745 }
2746
2747 if (!readCompletion)
2748 {
2749 flag->ParseValue(values);
2750#ifdef ARGS_NOEXCEPT
2751 // Non-noexcept ParseValue paths throw on Help, reader
2752 // failure, or Map miss, which halts parsing. Mirror
2753 // that here so a later parser-level error (e.g. an
2754 // unknown flag) cannot shadow the flag's error in
2755 // ArgumentParser::GetError().
2756 if (flag->GetError() != Error::None)
2757 {
2758 return false;
2759 }
2760#endif
2761 }
2762
2763 if (flag->KickOut())
2764 {
2765 ++it;
2766 return false;
2767 }
2768 } else
2769 {
2770 const std::string errorMessage("Flag could not be matched: " + arg);
2771#ifndef ARGS_NOEXCEPT
2772 throw ParseError(errorMessage);
2773#else
2774 error = Error::Parse;
2775 errorMsg = errorMessage;
2776 return false;
2777#endif
2778 }
2779
2780 return true;
2781 }
2782
2783 template <typename It>
2784 bool ParseShort(It &it, It end)
2785 {
2786 const auto &chunk = *it;
2787 const auto argchunk = chunk.substr(shortprefix.size());
2788 for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit)
2789 {
2790 const auto arg = *argit;
2791
2792 if (auto flag = Match(arg))
2793 {
2794#ifdef ARGS_NOEXCEPT
2795 // See ParseLong: if Match recorded an error
2796 // (e.g. Options::Single violation), bail before the
2797 // value is parsed so the prior value is preserved.
2798 if (flag->GetError() != Error::None)
2799 {
2800 return false;
2801 }
2802#endif
2803 const std::string value(argit + 1, std::end(argchunk));
2804 std::vector<std::string> values;
2805 const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end,
2806 allowSeparateShortValue, allowJoinedShortValue,
2807 !value.empty(), value, !value.empty(), values);
2808
2809 if (!errorMessage.empty())
2810 {
2811#ifndef ARGS_NOEXCEPT
2812 throw ParseError(errorMessage);
2813#else
2814 error = Error::Parse;
2815 errorMsg = errorMessage;
2816 return false;
2817#endif
2818 }
2819
2820 if (!readCompletion)
2821 {
2822 flag->ParseValue(values);
2823#ifdef ARGS_NOEXCEPT
2824 // See ParseLong: ensure a flag-level error from
2825 // ParseValue (Help, Parse, Map) halts parsing so
2826 // it cannot be shadowed by a later parser error.
2827 if (flag->GetError() != Error::None)
2828 {
2829 return false;
2830 }
2831#endif
2832 }
2833
2834 if (flag->KickOut())
2835 {
2836 ++it;
2837 return false;
2838 }
2839
2840 if (!values.empty())
2841 {
2842 break;
2843 }
2844 } else
2845 {
2846 const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'");
2847#ifndef ARGS_NOEXCEPT
2848 throw ParseError(errorMessage);
2849#else
2850 error = Error::Parse;
2851 errorMsg = errorMessage;
2852 return false;
2853#endif
2854 }
2855 }
2856
2857 return true;
2858 }
2859
2860 bool AddCompletionReply(const std::string &cur, const std::string &choice)
2861 {
2862 if (cur.empty() || choice.find(cur) == 0)
2863 {
2864 if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos)
2865 {
2866 completion->reply.push_back(choice.substr(choice.find(longseparator) + longseparator.size()));
2867 } else
2868 {
2869 completion->reply.push_back(choice);
2870 }
2871 return true;
2872 }
2873
2874 return false;
2875 }
2876
2877 template <typename It>
2878 bool Complete(It it, It end)
2879 {
2880 auto nextIt = it;
2881 if (!readCompletion || (++nextIt != end))
2882 {
2883 return false;
2884 }
2885
2886 const auto &chunk = *it;
2887 auto pos = GetNextPositional();
2888 std::vector<Command *> commands = GetCommands();
2889 const auto optionType = ParseOption(chunk, true);
2890
2891 if (!commands.empty() && (chunk.empty() || optionType == OptionType::Positional))
2892 {
2893 for (auto &cmd : commands)
2894 {
2895 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
2896 {
2897 AddCompletionReply(chunk, cmd->Name());
2898 }
2899 }
2900 } else
2901 {
2902 bool hasPositionalCompletion = true;
2903
2904 if (!commands.empty())
2905 {
2906 for (auto &cmd : commands)
2907 {
2908 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
2909 {
2910 AddCompletionReply(chunk, cmd->Name());
2911 }
2912 }
2913 } else if (pos)
2914 {
2915 if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None)
2916 {
2917 auto choices = pos->HelpChoices(helpParams);
2918 hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional;
2919 for (auto &choice : choices)
2920 {
2921 AddCompletionReply(chunk, choice);
2922 }
2923 }
2924 }
2925
2926 if (hasPositionalCompletion)
2927 {
2928 auto flags = GetAllFlags();
2929 for (auto flag : flags)
2930 {
2931 if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None)
2932 {
2933 continue;
2934 }
2935
2936 auto &matcher = flag->GetMatcher();
2937 if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix)))
2938 {
2939 for (auto &flagName : matcher.GetFlagStrings())
2940 {
2941 if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix)))
2942 {
2943 break;
2944 }
2945 }
2946 }
2947 }
2948
2949 if (optionType == OptionType::LongFlag && allowJoinedLongValue)
2950 {
2951 const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator);
2952 // Only attempt joined-value completion when the
2953 // separator lies at or past the long prefix, so
2954 // there is a (possibly empty) flag name between
2955 // them. With a custom longseparator that overlaps
2956 // the prefix (e.g. LongSeparator("-") under the
2957 // default "--" prefix), an attacker-controlled
2958 // completion word like "--x" puts the separator
2959 // inside the prefix, making `arg` shorter than
2960 // longprefix. arg.substr(longprefix.size()) would
2961 // then throw std::out_of_range, which escapes the
2962 // parser as a non-args exception (bypassing the
2963 // documented catch(args::Error) idiom) and is
2964 // thrown even under ARGS_NOEXCEPT.
2965 if (separator != chunk.npos && separator >= longprefix.size())
2966 {
2967 std::string arg(chunk, 0, separator);
2968 if (auto flag = this->Match(arg.substr(longprefix.size())))
2969 {
2970 for (auto &choice : flag->HelpChoices(helpParams))
2971 {
2972 AddCompletionReply(chunk, arg + longseparator + choice);
2973 }
2974 }
2975 }
2976 } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue)
2977 {
2978 if (chunk.size() > shortprefix.size() + 1)
2979 {
2980 auto arg = chunk.at(shortprefix.size());
2981 //TODO: support -abcVALUE where a and b take no value
2982 if (auto flag = this->Match(arg))
2983 {
2984 for (auto &choice : flag->HelpChoices(helpParams))
2985 {
2986 AddCompletionReply(chunk, shortprefix + arg + choice);
2987 }
2988 }
2989 }
2990 }
2991 }
2992 }
2993
2994#ifndef ARGS_NOEXCEPT
2995 throw Completion(completion->Get());
2996#else
2997 return true;
2998#endif
2999 }
3000
3001 template <typename It>
3002 It Parse(It begin, It end)
3003 {
3004 bool terminated = false;
3005 std::vector<Command *> commands = GetCommands();
3006
3007 // Check all arg chunks
3008 for (auto it = begin; it != end; ++it)
3009 {
3010 if (Complete(it, end))
3011 {
3012 return end;
3013 }
3014
3015 const auto &chunk = *it;
3016
3017 if (!terminated && chunk == terminator)
3018 {
3019 terminated = true;
3020 } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag)
3021 {
3022 if (!ParseLong(it, end))
3023 {
3024 return it;
3025 }
3026 } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag)
3027 {
3028 if (!ParseShort(it, end))
3029 {
3030 return it;
3031 }
3032 } else if (!terminated && !commands.empty())
3033 {
3034 auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; });
3035 if (itCommand == commands.end())
3036 {
3037 const std::string errorMessage("Unknown command: " + chunk);
3038#ifndef ARGS_NOEXCEPT
3039 throw ParseError(errorMessage);
3040#else
3041 error = Error::Parse;
3042 errorMsg = errorMessage;
3043 return it;
3044#endif
3045 }
3046
3047 SelectCommand(*itCommand);
3048
3049 if (const auto &coroutine = GetCoroutine())
3050 {
3051 ++it;
3052 RaiiSubparser coro(*this, std::vector<std::string>(it, end));
3053 coroutine(coro.Parser());
3054#ifdef ARGS_NOEXCEPT
3055 error = GetError();
3056 if (error != Error::None)
3057 {
3058 return end;
3059 }
3060
3061 if (!coro.Parser().IsParsed())
3062 {
3063 error = Error::Usage;
3064 return end;
3065 }
3066#else
3067 if (!coro.Parser().IsParsed())
3068 {
3069 throw UsageError("Subparser::Parse was not called");
3070 }
3071#endif
3072
3073 break;
3074 }
3075
3076 commands = GetCommands();
3077 } else
3078 {
3079 auto pos = GetNextPositional();
3080 if (pos)
3081 {
3082 pos->ParseValue(chunk);
3083#ifdef ARGS_NOEXCEPT
3084 if (pos->GetError() != Error::None)
3085 {
3086 return it;
3087 }
3088#endif
3089
3090 if (pos->KickOut())
3091 {
3092 return ++it;
3093 }
3094 } else
3095 {
3096 const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk);
3097#ifndef ARGS_NOEXCEPT
3098 throw ParseError(errorMessage);
3099#else
3100 error = Error::Parse;
3101 errorMsg = errorMessage;
3102 return it;
3103#endif
3104 }
3105 }
3106
3107 if (!readCompletion && completion != nullptr && completion->Matched())
3108 {
3109#ifdef ARGS_NOEXCEPT
3110 if (completion->GetError() != Error::None)
3111 {
3112 error = completion->GetError();
3113 if (errorMsg.empty())
3114 {
3115 errorMsg = completion->GetErrorMsg();
3116 }
3117 return it;
3118 }
3119
3120 error = Error::Completion;
3121#endif
3122 readCompletion = true;
3123 ++it;
3124 const auto argsLeft = static_cast<size_t>(std::distance(it, end));
3125 if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft)
3126 {
3127#ifndef ARGS_NOEXCEPT
3128 throw Completion("");
3129#else
3130 return end;
3131#endif
3132 }
3133
3134 ++it;
3135 std::vector<std::string> curArgs;
3136 curArgs.reserve(completion->cword);
3137 auto curIt = it;
3138 for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt)
3139 {
3140 curArgs.push_back(*curIt);
3141 }
3142
3143 if (completion->syntax == "bash")
3144 {
3145 // bash tokenizes --flag=value as --flag=value
3146 // Security fix: Use size_t arithmetic throughout to avoid conversion issues
3147 for (size_t idx = 0; idx < curArgs.size(); )
3148 {
3149 if (idx > 0 && curArgs[idx] == "=")
3150 {
3151 size_t prev_idx = idx - 1; // Safe since we checked idx > 0
3152 curArgs[prev_idx] += "=";
3153 size_t next_idx = 0;
3154 if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size())
3155 {
3156 curArgs[prev_idx] += curArgs[next_idx];
3157 // Erase the '=' token and the following value token.
3158 size_t erase_end = 0;
3159 if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end))
3160 {
3161 typedef std::vector<std::string>::difference_type diff_t;
3162 curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx),
3163 curArgs.begin() + static_cast<diff_t>(erase_end));
3164 }
3165 } else
3166 {
3167 // Safe erase of single '=' token at the end
3168 typedef std::vector<std::string>::difference_type diff_t;
3169 curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx));
3170 }
3171 // Do not increment idx - next element slides into current position
3172 } else
3173 {
3174 ++idx;
3175 }
3176 }
3177
3178 }
3179#ifndef ARGS_NOEXCEPT
3180 try
3181 {
3182 Parse(curArgs.begin(), curArgs.end());
3183 throw Completion("");
3184 }
3185 catch (Completion &)
3186 {
3187 throw;
3188 }
3189 catch (args::Error&)
3190 {
3191 throw Completion("");
3192 }
3193#else
3194 // Discard the nested Parse's return value: it points
3195 // into the local curArgs vector, which is destroyed
3196 // when this function returns, leaving the caller with
3197 // a dangling iterator that would be compared against
3198 // the outer `end` in ParseCLI. Return the outer
3199 // `end` instead so the iterator stays in the caller's
3200 // container.
3201 Parse(curArgs.begin(), curArgs.end());
3202 error = Error::Completion;
3203 errorMsg.clear();
3204 return end;
3205#endif
3206 }
3207 }
3208
3209 Validate(shortprefix, longprefix);
3210 return end;
3211 }
3212
3213 public:
3214 HelpParams helpParams;
3215
3216 ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string())
3217 {
3218 Description(description_);
3219 Epilog(epilog_);
3220 LongPrefix("--");
3221 ShortPrefix("-");
3222 LongSeparator("=");
3223 Terminator("--");
3224 SetArgumentSeparations(true, true, true, true);
3225 matched = true;
3226 }
3227
3228 void AddCompletion(CompletionFlag &completionFlag)
3229 {
3230 completion = &completionFlag;
3231 Add(completionFlag);
3232 }
3233
3236 const std::string &Prog() const
3237 { return helpParams.programName; }
3240 void Prog(const std::string &prog_)
3241 { this->helpParams.programName = prog_; }
3242
3245 const std::string &LongPrefix() const
3246 { return longprefix; }
3249 void LongPrefix(const std::string &longprefix_)
3250 {
3251 this->longprefix = longprefix_;
3252 this->helpParams.longPrefix = longprefix_;
3253 }
3254
3257 const std::string &ShortPrefix() const
3258 { return shortprefix; }
3261 void ShortPrefix(const std::string &shortprefix_)
3262 {
3263 this->shortprefix = shortprefix_;
3264 this->helpParams.shortPrefix = shortprefix_;
3265 }
3266
3269 const std::string &LongSeparator() const
3270 { return longseparator; }
3273 void LongSeparator(const std::string &longseparator_)
3274 {
3275 if (longseparator_.empty())
3276 {
3277 const std::string errorMessage("longseparator can not be set to empty");
3278#ifdef ARGS_NOEXCEPT
3279 error = Error::Usage;
3280 errorMsg = errorMessage;
3281#else
3282 throw UsageError(errorMessage);
3283#endif
3284 } else
3285 {
3286 this->longseparator = longseparator_;
3287 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3288 }
3289 }
3290
3293 const std::string &Terminator() const
3294 { return terminator; }
3297 void Terminator(const std::string &terminator_)
3298 { this->terminator = terminator_; }
3299
3305 bool &allowJoinedShortValue_,
3306 bool &allowJoinedLongValue_,
3307 bool &allowSeparateShortValue_,
3308 bool &allowSeparateLongValue_) const
3309 {
3310 allowJoinedShortValue_ = this->allowJoinedShortValue;
3311 allowJoinedLongValue_ = this->allowJoinedLongValue;
3312 allowSeparateShortValue_ = this->allowSeparateShortValue;
3313 allowSeparateLongValue_ = this->allowSeparateLongValue;
3314 }
3315
3324 const bool allowJoinedShortValue_,
3325 const bool allowJoinedLongValue_,
3326 const bool allowSeparateShortValue_,
3327 const bool allowSeparateLongValue_)
3328 {
3329 this->allowJoinedShortValue = allowJoinedShortValue_;
3330 this->allowJoinedLongValue = allowJoinedLongValue_;
3331 this->allowSeparateShortValue = allowSeparateShortValue_;
3332 this->allowSeparateLongValue = allowSeparateLongValue_;
3333
3334 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3335 this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " ";
3336 }
3337
3340 void Help(std::ostream &help_) const
3341 {
3342 auto &command = SelectedCommand();
3343 const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description();
3344 const auto desc_indent = helpParams.descriptionindent;
3345 const auto effective_desc_width = (helpParams.width > desc_indent) ? helpParams.width - desc_indent : 0;
3346 const auto description_text = Wrap(commandDescription, effective_desc_width);
3347 const auto epilog_text = Wrap(command.Epilog(), effective_desc_width);
3348
3349 const bool hasoptions = command.HasFlag();
3350 const bool hasarguments = command.HasPositional();
3351
3352 std::vector<std::string> prognameline;
3353 prognameline.push_back(helpParams.usageString);
3354 prognameline.push_back(Prog());
3355 auto commandProgLine = command.GetProgramLine(helpParams);
3356 prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end());
3357
3358 const auto prog_sum = helpParams.progindent + helpParams.progtailindent;
3359 const auto effective_prog_width = (helpParams.width > prog_sum) ? helpParams.width - prog_sum : 0;
3360 const auto effective_prog_first = (helpParams.width > helpParams.progindent) ? helpParams.width - helpParams.progindent : 0;
3361 const auto proglines = Wrap(prognameline.begin(), prognameline.end(),
3362 effective_prog_width,
3363 effective_prog_first);
3364 auto progit = std::begin(proglines);
3365 if (progit != std::end(proglines))
3366 {
3367 help_ << std::string(helpParams.progindent, ' ') << *progit << '\n';
3368 ++progit;
3369 }
3370 for (; progit != std::end(proglines); ++progit)
3371 {
3372 help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n';
3373 }
3374
3375 help_ << '\n';
3376
3377 if (!description_text.empty())
3378 {
3379 for (const auto &line: description_text)
3380 {
3381 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3382 }
3383 help_ << "\n";
3384 }
3385
3386 bool lastDescriptionIsNewline = false;
3387
3388 if (!helpParams.optionsString.empty())
3389 {
3390 help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n";
3391 }
3392
3393 for (const auto &desc: command.GetDescription(helpParams, 0))
3394 {
3395 lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty();
3396 const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent;
3397 const auto flag_sum = helpParams.flagindent + helpParams.helpindent + helpParams.gutter;
3398 const auto effective_flag_width = (helpParams.width > flag_sum) ? helpParams.width - flag_sum : 0;
3399 const auto flags = Wrap(std::get<0>(desc), effective_flag_width);
3400 const auto info_sum = helpParams.helpindent + groupindent;
3401 const auto effective_info_width = (helpParams.width > info_sum) ? helpParams.width - info_sum : 0;
3402 const auto info = Wrap(std::get<1>(desc), effective_info_width);
3403
3404 std::string::size_type flagssize = 0;
3405 for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit)
3406 {
3407 if (flagsit != std::begin(flags))
3408 {
3409 help_ << '\n';
3410 }
3411 help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit;
3412 flagssize = Glyphs(*flagsit);
3413 }
3414
3415 auto infoit = std::begin(info);
3416 // groupindent is on both sides of this inequality, and therefore can be removed
3417 if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription)
3418 {
3419 help_ << '\n';
3420 } else
3421 {
3422 // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here
3423 const auto indent_sum = helpParams.flagindent + flagssize;
3424 const auto effective_space = (helpParams.helpindent > indent_sum) ? helpParams.helpindent - indent_sum : 0;
3425 help_ << std::string(effective_space, ' ') << *infoit << '\n';
3426 ++infoit;
3427 }
3428 for (; infoit != std::end(info); ++infoit)
3429 {
3430 help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n';
3431 }
3432 }
3433 if (hasoptions && hasarguments && helpParams.showTerminator)
3434 {
3435 lastDescriptionIsNewline = false;
3436 const auto effective_term_width = (helpParams.width > helpParams.flagindent) ? helpParams.width - helpParams.flagindent : 0;
3437 for (const auto &item: Wrap(std::string("\"") + terminator + "\" can be used to terminate flag options and force all following arguments to be treated as positional options", effective_term_width))
3438 {
3439 help_ << std::string(helpParams.flagindent, ' ') << item << '\n';
3440 }
3441 }
3442
3443 if (!lastDescriptionIsNewline)
3444 {
3445 help_ << "\n";
3446 }
3447
3448 for (const auto &line: epilog_text)
3449 {
3450 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3451 }
3452 }
3453
3458 std::string Help() const
3459 {
3460 std::ostringstream help_;
3461 Help(help_);
3462 return help_.str();
3463 }
3464
3465 virtual void Reset() noexcept override
3466 {
3467 Command::Reset();
3468 matched = true;
3469 readCompletion = false;
3470 }
3471
3478 template <typename It>
3479 It ParseArgs(It begin, It end)
3480 {
3481 // Reset all Matched statuses and errors
3482 Reset();
3483#ifdef ARGS_NOEXCEPT
3484 error = GetError();
3485 if (error != Error::None)
3486 {
3487 return end;
3488 }
3489#endif
3490 return Parse(begin, end);
3491 }
3492
3498 template <typename T>
3499 auto ParseArgs(const T &args) -> decltype(std::begin(args))
3500 {
3501 return ParseArgs(std::begin(args), std::end(args));
3502 }
3503
3510 bool ParseCLI(const int argc, const char * const * argv)
3511 {
3512 if (argc > 0 && argv != nullptr && argv[0] != nullptr && Prog().empty())
3513 {
3514 Prog(argv[0]);
3515 }
3516
3517 std::vector<std::string> args;
3518 if (argc > 1 && argv != nullptr)
3519 {
3520 args.assign(argv + 1, argv + argc);
3521 }
3522
3523 return ParseArgs(args) == std::end(args);
3524 }
3525
3526 template <typename T>
3527 bool ParseCLI(const T &args)
3528 {
3529 return ParseArgs(args) == std::end(args);
3530 }
3531 };
3532
3533 inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_)
3534 : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser)
3535 {
3536 command.subparser = &parser;
3537 }
3538
3539 inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams &params_): command(command_), parser(command, params_), oldSubparser(command.subparser)
3540 {
3541 command.subparser = &parser;
3542 }
3543
3544 inline void Subparser::Parse()
3545 {
3546 isParsed = true;
3547 Reset();
3548 command.subparserDescription = GetDescription(helpParams, 0);
3549 command.subparserHasFlag = HasFlag();
3550 command.subparserHasPositional = HasPositional();
3551 command.subparserHasCommand = HasCommand();
3552 command.subparserProgramLine = GetProgramLine(helpParams);
3553 if (parser == nullptr)
3554 {
3555#ifndef ARGS_NOEXCEPT
3556 throw args::SubparserError();
3557#else
3558 error = Error::Subparser;
3559 return;
3560#endif
3561 }
3562
3563 auto it = parser->Parse(args.begin(), args.end());
3564 command.Validate(parser->ShortPrefix(), parser->LongPrefix());
3565 kicked.assign(it, args.end());
3566
3567#ifdef ARGS_NOEXCEPT
3568 command.subparserError = GetError();
3569#endif
3570 }
3571
3572 inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser)
3573 {
3574 parser.Help(os);
3575 return os;
3576 }
3577
3580 class Flag : public FlagBase
3581 {
3582 public:
3583 Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_)
3584 {
3585 group_.Add(*this);
3586 }
3587
3588 Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false): Flag(group_, name_, help_, std::move(matcher_), extraError_ ? Options::Single : Options::None)
3589 {
3590 }
3591
3592 virtual ~Flag() {}
3593
3596 bool Get() const
3597 {
3598 return Matched();
3599 }
3600
3601 virtual Nargs NumberOfArguments() const noexcept override
3602 {
3603 return 0;
3604 }
3605
3606 virtual void ParseValue(const std::vector<std::string>&) override
3607 {
3608 }
3609 };
3610
3615 class HelpFlag : public Flag
3616 {
3617 public:
3618 HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {}
3619
3620 virtual ~HelpFlag() {}
3621
3622 virtual void ParseValue(const std::vector<std::string> &)
3623 {
3624#ifdef ARGS_NOEXCEPT
3625 error = Error::Help;
3626 errorMsg = Name();
3627#else
3628 throw Help(Name());
3629#endif
3630 }
3631
3634 bool Get() const noexcept
3635 {
3636 return Matched();
3637 }
3638 };
3639
3642 class CounterFlag : public Flag
3643 {
3644 private:
3645 const int startcount;
3646 int count;
3647
3648 public:
3649 CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}):
3650 Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {}
3651
3652 virtual ~CounterFlag() {}
3653
3654 virtual FlagBase *Match(const EitherFlag &arg) override
3655 {
3656 auto me = FlagBase::Match(arg);
3657 if (me)
3658 {
3659#ifdef ARGS_NOEXCEPT
3660 // Suppress increment when FlagBase::Match recorded an
3661 // error on this same call (e.g. Options::Single violated).
3662 // In non-noexcept mode that path would have thrown before
3663 // reaching here and the count would not have advanced.
3664 if (GetError() != Error::None)
3665 {
3666 return me;
3667 }
3668#endif
3669 ++count;
3670 }
3671 return me;
3672 }
3673
3676 int &Get() noexcept
3677 {
3678 return count;
3679 }
3680
3681 int &operator *() noexcept {
3682 return count;
3683 }
3684
3685 const int &operator *() const noexcept {
3686 return count;
3687 }
3688
3689 virtual void Reset() noexcept override
3690 {
3691 FlagBase::Reset();
3692 count = startcount;
3693 }
3694 };
3695
3698 class ActionFlag : public FlagBase
3699 {
3700 private:
3701 std::function<void(const std::vector<std::string> &)> action;
3702 Nargs nargs;
3703
3704 public:
3705 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, std::function<void(const std::vector<std::string> &)> action_, Options options_ = {}):
3706 FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_)
3707 {
3708 group_.Add(*this);
3709 }
3710
3711 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}):
3712 FlagBase(name_, help_, std::move(matcher_), options_), nargs(1)
3713 {
3714 group_.Add(*this);
3715 action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); };
3716 }
3717
3718 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}):
3719 FlagBase(name_, help_, std::move(matcher_), options_), nargs(0)
3720 {
3721 group_.Add(*this);
3722 action = [action_](const std::vector<std::string> &) { return action_(); };
3723 }
3724
3725 virtual Nargs NumberOfArguments() const noexcept override
3726 { return nargs; }
3727
3728 virtual void ParseValue(const std::vector<std::string> &value) override
3729 { action(value); }
3730 };
3731
3739 {
3740 private:
3741 template <typename T>
3742 static typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, bool>::type
3743 HasUnsignedNegativeSign(const std::string &value)
3744 {
3745 const auto firstNonSpace = std::find_if_not(value.begin(), value.end(), [](char c)
3746 {
3747 return std::isspace(static_cast<unsigned char>(c)) != 0;
3748 });
3749
3750 return firstNonSpace != value.end() && *firstNonSpace == '-';
3751 }
3752
3753 template <typename T>
3754 static typename std::enable_if<!std::is_integral<T>::value || !std::is_unsigned<T>::value, bool>::type
3755 HasUnsignedNegativeSign(const std::string &)
3756 {
3757 return false;
3758 }
3759
3760 public:
3761 template <typename T>
3762 typename std::enable_if<
3763 std::is_integral<T>::value &&
3764 !std::is_same<T, bool>::value &&
3765 !std::is_same<T, char>::value &&
3766 !std::is_same<T, signed char>::value &&
3767 !std::is_same<T, unsigned char>::value,
3768 bool>::type
3769 ParseNumericValue(const std::string &value, T &destination)
3770 {
3771 if (HasUnsignedNegativeSign<T>(value))
3772 {
3773 return false;
3774 }
3775
3776 const char *begin = value.c_str();
3777 // The true end of the value, derived from its length rather than
3778 // from the first NUL. strtoull/strtoll treat the buffer as a C
3779 // string and stop at an embedded '\0', so checking `*end == '\0'`
3780 // for "no trailing data" is defeated by a value like "12\0junk":
3781 // end lands on the embedded NUL and the junk after it is silently
3782 // accepted. Comparing against `stop` validates the whole string
3783 // and matches the istringstream-based reader used for other types.
3784 const char *const stop = begin + value.size();
3785
3786 // C++11-compatible: use strtoull/strtoll. Hardening retained from
3787 // the original from_chars draft (errno save/restore, ERANGE check,
3788 // narrowing range check, trailing-whitespace tolerance). No
3789 // unconditional dependency on <charconv> / C++17.
3790 const int saved_errno = errno;
3791 errno = 0;
3792
3793 char *end = nullptr;
3794
3795 if (std::is_unsigned<T>::value)
3796 {
3797 const unsigned long long parsed = std::strtoull(begin, &end, 0);
3798 if (end == begin)
3799 {
3800 errno = saved_errno;
3801 return false;
3802 }
3803 while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
3804 {
3805 ++end;
3806 }
3807 if (end != stop || errno == ERANGE ||
3808 parsed > static_cast<unsigned long long>(std::numeric_limits<T>::max()))
3809 {
3810 errno = saved_errno;
3811 return false;
3812 }
3813
3814 destination = static_cast<T>(parsed);
3815 }
3816 else
3817 {
3818 const long long parsed = std::strtoll(begin, &end, 0);
3819 if (end == begin)
3820 {
3821 errno = saved_errno;
3822 return false;
3823 }
3824 while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
3825 {
3826 ++end;
3827 }
3828 if (end != stop || errno == ERANGE ||
3829 parsed < static_cast<long long>(std::numeric_limits<T>::min()) ||
3830 parsed > static_cast<long long>(std::numeric_limits<T>::max()))
3831 {
3832 errno = saved_errno;
3833 return false;
3834 }
3835
3836 destination = static_cast<T>(parsed);
3837 }
3838
3839 errno = saved_errno;
3840 return true;
3841 }
3842
3843 template <typename T>
3844 typename std::enable_if<
3845 !std::is_integral<T>::value ||
3846 std::is_same<T, bool>::value ||
3847 std::is_same<T, char>::value ||
3848 std::is_same<T, signed char>::value ||
3849 std::is_same<T, unsigned char>::value,
3850 bool>::type
3851 ParseNumericValue(const std::string &value, T &destination)
3852 {
3853 std::istringstream ss(value);
3854 // Pin parsing to the C locale so that the decimal separator and
3855 // thousands grouping behavior do not silently depend on whatever
3856 // std::locale::global was last set to elsewhere in the process.
3857 // Without this, e.g. "3.14" parses as 3 (with ".14" trailing) in
3858 // any locale whose numpunct facet treats ',' as the decimal point.
3859 ss.imbue(std::locale::classic());
3860 ss >> destination;
3861 if (ss.fail())
3862 {
3863 return false;
3864 }
3865
3866 // Check for trailing garbage by attempting to extract any remaining characters.
3867 // Do not use 'ss >> std::ws' followed by peek(), as std::ws can set failbit
3868 // on EOF, causing false rejection of valid input.
3869 char extra = '\0';
3870 ss >> std::ws >> extra;
3871 // If extraction succeeded, there's trailing garbage (return false).
3872 // If extraction failed due to EOF only (goodbit after ws extraction), it's valid (return true).
3873 // If extraction failed for other reasons, it's invalid (return false).
3874 if (ss.fail())
3875 {
3876 // Clear the failbit to check if EOF is the only issue
3877 ss.clear(ss.rdstate() & ~std::ios::failbit);
3878 return ss.eof();
3879 }
3880 // Extraction succeeded, meaning there's trailing garbage
3881 return false;
3882 }
3883
3884 template <typename T>
3885 typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type
3886 operator ()(const std::string &name, const std::string &value, T &destination)
3887 {
3888 const bool success = ParseNumericValue(value, destination);
3889 if (!success)
3890 {
3891#ifdef ARGS_NOEXCEPT
3892 (void)name;
3893 return false;
3894#else
3895 std::ostringstream problem;
3896 problem << "Argument '" << name << "' received invalid value type '" << value << "'";
3897 throw ParseError(problem.str());
3898#endif
3899 }
3900 return true;
3901 }
3902
3903 template <typename T>
3904 typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type
3905 operator()(const std::string &, const std::string &value, T &destination)
3906 {
3907 destination = value;
3908 return true;
3909 }
3910 };
3911
3917 template <
3918 typename T,
3919 typename Reader = ValueReader>
3921 {
3922 protected:
3923 T value;
3924 T defaultValue;
3925
3926 virtual std::string GetDefaultString(const HelpParams&) const override
3927 {
3928 return detail::ToString(defaultValue);
3929 }
3930
3931 private:
3932 Reader reader;
3933
3934 public:
3935
3936 ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), value(defaultValue_), defaultValue(defaultValue_)
3937 {
3938 group_.Add(*this);
3939 }
3940
3941 ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), const bool extraError_ = false): ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, extraError_ ? Options::Single : Options::None)
3942 {
3943 }
3944
3945 ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_)
3946 {
3947 }
3948
3949 virtual ~ValueFlag() {}
3950
3951 virtual void ParseValue(const std::vector<std::string> &values_) override
3952 {
3953 const std::string &value_ = values_.at(0);
3954
3955#ifdef ARGS_NOEXCEPT
3956 if (!reader(name, value_, this->value))
3957 {
3958 error = Error::Parse;
3959 }
3960#else
3961 reader(name, value_, this->value);
3962#endif
3963 }
3964
3965 virtual void Reset() noexcept override
3966 {
3967 ValueFlagBase::Reset();
3968 value = defaultValue;
3969 }
3970
3973 T &Get() noexcept
3974 {
3975 return value;
3976 }
3977
3980 T &operator *() noexcept
3981 {
3982 return value;
3983 }
3984
3987 const T &operator *() const noexcept
3988 {
3989 return value;
3990 }
3991
3994 T *operator ->() noexcept
3995 {
3996 return &value;
3997 }
3998
4001 const T *operator ->() const noexcept
4002 {
4003 return &value;
4004 }
4005
4008 const T &GetDefault() noexcept
4009 {
4010 return defaultValue;
4011 }
4012 };
4013
4019 template <
4020 typename T,
4021 typename Reader = ValueReader>
4022 class ImplicitValueFlag : public ValueFlag<T, Reader>
4023 {
4024 protected:
4025 T implicitValue;
4026
4027 public:
4028
4029 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {})
4030 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_)
4031 {
4032 }
4033
4034 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {})
4035 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_)
4036 {
4037 }
4038
4039 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_)
4040 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue()
4041 {
4042 }
4043
4044 virtual ~ImplicitValueFlag() {}
4045
4046 virtual Nargs NumberOfArguments() const noexcept override
4047 {
4048 return {0, 1};
4049 }
4050
4051 virtual void ParseValue(const std::vector<std::string> &value_) override
4052 {
4053 if (value_.empty())
4054 {
4055 this->value = implicitValue;
4056 } else
4057 {
4059 }
4060 }
4061 };
4062
4069 template <
4070 typename T,
4071 template <typename...> class List = detail::vector,
4072 typename Reader = ValueReader>
4074 {
4075 protected:
4076
4077 List<T> values;
4078 const List<T> defaultValues;
4079 Nargs nargs;
4080 Reader reader;
4081
4082 public:
4083
4084 typedef List<T> Container;
4085 typedef T value_type;
4086 typedef typename Container::allocator_type allocator_type;
4087 typedef typename Container::pointer pointer;
4088 typedef typename Container::const_pointer const_pointer;
4089 typedef T& reference;
4090 typedef const T& const_reference;
4091 typedef typename Container::size_type size_type;
4092 typedef typename Container::difference_type difference_type;
4093 typedef typename Container::iterator iterator;
4094 typedef typename Container::const_iterator const_iterator;
4095 typedef std::reverse_iterator<iterator> reverse_iterator;
4096 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4097
4098 NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {})
4099 : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_)
4100 {
4101 group_.Add(*this);
4102 }
4103
4104 virtual ~NargsValueFlag() {}
4105
4106 virtual Nargs NumberOfArguments() const noexcept override
4107 {
4108 return nargs;
4109 }
4110
4111 virtual void ParseValue(const std::vector<std::string> &values_) override
4112 {
4113 values.clear();
4114
4115 for (const std::string &value : values_)
4116 {
4117 T v {};
4118#ifdef ARGS_NOEXCEPT
4119 if (!reader(name, value, v))
4120 {
4121 error = Error::Parse;
4122 return;
4123 }
4124#else
4125 reader(name, value, v);
4126#endif
4127 values.insert(std::end(values), v);
4128 }
4129 }
4130
4131 List<T> &Get() noexcept
4132 {
4133 return values;
4134 }
4135
4138 List<T> &operator *() noexcept
4139 {
4140 return values;
4141 }
4142
4145 const List<T> &operator *() const noexcept
4146 {
4147 return values;
4148 }
4149
4152 List<T> *operator ->() noexcept
4153 {
4154 return &values;
4155 }
4156
4159 const List<T> *operator ->() const noexcept
4160 {
4161 return &values;
4162 }
4163
4164 iterator begin() noexcept
4165 {
4166 return values.begin();
4167 }
4168
4169 const_iterator begin() const noexcept
4170 {
4171 return values.begin();
4172 }
4173
4174 const_iterator cbegin() const noexcept
4175 {
4176 return values.cbegin();
4177 }
4178
4179 iterator end() noexcept
4180 {
4181 return values.end();
4182 }
4183
4184 const_iterator end() const noexcept
4185 {
4186 return values.end();
4187 }
4188
4189 const_iterator cend() const noexcept
4190 {
4191 return values.cend();
4192 }
4193
4194 virtual void Reset() noexcept override
4195 {
4196 FlagBase::Reset();
4197 values = defaultValues;
4198 }
4199
4200 virtual FlagBase *Match(const EitherFlag &arg) override
4201 {
4202 const bool wasMatched = Matched();
4203 auto me = FlagBase::Match(arg);
4204 if (me && !wasMatched)
4205 {
4206 values.clear();
4207 }
4208 return me;
4209 }
4210 };
4211
4218 template <
4219 typename T,
4220 template <typename...> class List = detail::vector,
4221 typename Reader = ValueReader>
4223 {
4224 private:
4225 using Container = List<T>;
4226 Container values;
4227 const Container defaultValues;
4228 Reader reader;
4229
4230 public:
4231
4232 typedef T value_type;
4233 typedef typename Container::allocator_type allocator_type;
4234 typedef typename Container::pointer pointer;
4235 typedef typename Container::const_pointer const_pointer;
4236 typedef T& reference;
4237 typedef const T& const_reference;
4238 typedef typename Container::size_type size_type;
4239 typedef typename Container::difference_type difference_type;
4240 typedef typename Container::iterator iterator;
4241 typedef typename Container::const_iterator const_iterator;
4242 typedef std::reverse_iterator<iterator> reverse_iterator;
4243 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4244
4245 ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}):
4246 ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_)
4247 {
4248 group_.Add(*this);
4249 }
4250
4251 virtual ~ValueFlagList() {}
4252
4253 virtual void ParseValue(const std::vector<std::string> &values_) override
4254 {
4255 const std::string &value_ = values_.at(0);
4256
4257 T v{};
4258#ifdef ARGS_NOEXCEPT
4259 if (!reader(name, value_, v))
4260 {
4261 error = Error::Parse;
4262 return;
4263 }
4264#else
4265 reader(name, value_, v);
4266#endif
4267 values.insert(std::end(values), v);
4268 }
4269
4272 Container &Get() noexcept
4273 {
4274 return values;
4275 }
4276
4279 Container &operator *() noexcept
4280 {
4281 return values;
4282 }
4283
4286 const Container &operator *() const noexcept
4287 {
4288 return values;
4289 }
4290
4293 Container *operator ->() noexcept
4294 {
4295 return &values;
4296 }
4297
4300 const Container *operator ->() const noexcept
4301 {
4302 return &values;
4303 }
4304
4305 virtual std::string Name() const override
4306 {
4307 return name + std::string("...");
4308 }
4309
4310 virtual void Reset() noexcept override
4311 {
4312 ValueFlagBase::Reset();
4313 values = defaultValues;
4314 }
4315
4316 virtual FlagBase *Match(const EitherFlag &arg) override
4317 {
4318 const bool wasMatched = Matched();
4319 auto me = FlagBase::Match(arg);
4320 if (me && !wasMatched)
4321 {
4322 values.clear();
4323 }
4324 return me;
4325 }
4326
4327 iterator begin() noexcept
4328 {
4329 return values.begin();
4330 }
4331
4332 const_iterator begin() const noexcept
4333 {
4334 return values.begin();
4335 }
4336
4337 const_iterator cbegin() const noexcept
4338 {
4339 return values.cbegin();
4340 }
4341
4342 iterator end() noexcept
4343 {
4344 return values.end();
4345 }
4346
4347 const_iterator end() const noexcept
4348 {
4349 return values.end();
4350 }
4351
4352 const_iterator cend() const noexcept
4353 {
4354 return values.cend();
4355 }
4356 };
4357
4365 template <
4366 typename K,
4367 typename T,
4368 typename Reader = ValueReader,
4369 template <typename...> class Map = detail::unordered_map>
4370 class MapFlag : public ValueFlagBase
4371 {
4372 private:
4373 const Map<K, T> map;
4374 T value;
4375 const T defaultValue;
4376 Reader reader;
4377
4378 protected:
4379 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4380 {
4381 return detail::MapKeysToStrings(map);
4382 }
4383
4384 public:
4385
4386 MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), value(defaultValue_), defaultValue(defaultValue_)
4387 {
4388 group_.Add(*this);
4389 }
4390
4391 MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const T &defaultValue_ = T(), const bool extraError_ = false): MapFlag(group_, name_, help_, std::move(matcher_), map_, defaultValue_, extraError_ ? Options::Single : Options::None)
4392 {
4393 }
4394
4395 MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, Options options_): MapFlag(group_, name_, help_, std::move(matcher_), map_, T(), options_)
4396 {
4397 }
4398
4399 virtual ~MapFlag() {}
4400
4401 virtual void ParseValue(const std::vector<std::string> &values_) override
4402 {
4403 const std::string &value_ = values_.at(0);
4404
4405 K key{};
4406#ifdef ARGS_NOEXCEPT
4407 if (!reader(name, value_, key))
4408 {
4409 error = Error::Parse;
4410 return;
4411 }
4412#else
4413 reader(name, value_, key);
4414#endif
4415 auto it = map.find(key);
4416 if (it == std::end(map))
4417 {
4418 std::ostringstream problem;
4419 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4420#ifdef ARGS_NOEXCEPT
4421 error = Error::Map;
4422 errorMsg = problem.str();
4423#else
4424 throw MapError(problem.str());
4425#endif
4426 } else
4427 {
4428 this->value = it->second;
4429 }
4430 }
4431
4434 T &Get() noexcept
4435 {
4436 return value;
4437 }
4438
4441 T &operator *() noexcept
4442 {
4443 return value;
4444 }
4445
4448 const T &operator *() const noexcept
4449 {
4450 return value;
4451 }
4452
4455 T *operator ->() noexcept
4456 {
4457 return &value;
4458 }
4459
4462 const T *operator ->() const noexcept
4463 {
4464 return &value;
4465 }
4466
4467 virtual void Reset() noexcept override
4468 {
4469 ValueFlagBase::Reset();
4470 value = defaultValue;
4471 }
4472 };
4473
4482 template <
4483 typename K,
4484 typename T,
4485 template <typename...> class List = detail::vector,
4486 typename Reader = ValueReader,
4487 template <typename...> class Map = detail::unordered_map>
4489 {
4490 private:
4491 using Container = List<T>;
4492 const Map<K, T> map;
4493 Container values;
4494 const Container defaultValues;
4495 Reader reader;
4496
4497 protected:
4498 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4499 {
4500 return detail::MapKeysToStrings(map);
4501 }
4502
4503 public:
4504 typedef T value_type;
4505 typedef typename Container::allocator_type allocator_type;
4506 typedef typename Container::pointer pointer;
4507 typedef typename Container::const_pointer const_pointer;
4508 typedef T& reference;
4509 typedef const T& const_reference;
4510 typedef typename Container::size_type size_type;
4511 typedef typename Container::difference_type difference_type;
4512 typedef typename Container::iterator iterator;
4513 typedef typename Container::const_iterator const_iterator;
4514 typedef std::reverse_iterator<iterator> reverse_iterator;
4515 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4516
4517 MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
4518 ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
4519 {
4520 group_.Add(*this);
4521 }
4522
4523 virtual ~MapFlagList() {}
4524
4525 virtual void ParseValue(const std::vector<std::string> &values_) override
4526 {
4527 const std::string &value_ = values_.at(0);
4528
4529 K key{};
4530#ifdef ARGS_NOEXCEPT
4531 if (!reader(name, value_, key))
4532 {
4533 error = Error::Parse;
4534 return;
4535 }
4536#else
4537 reader(name, value_, key);
4538#endif
4539 auto it = map.find(key);
4540 if (it == std::end(map))
4541 {
4542 std::ostringstream problem;
4543 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4544#ifdef ARGS_NOEXCEPT
4545 error = Error::Map;
4546 errorMsg = problem.str();
4547#else
4548 throw MapError(problem.str());
4549#endif
4550 } else
4551 {
4552 this->values.emplace_back(it->second);
4553 }
4554 }
4555
4558 Container &Get() noexcept
4559 {
4560 return values;
4561 }
4562
4565 Container &operator *() noexcept
4566 {
4567 return values;
4568 }
4569
4572 const Container &operator *() const noexcept
4573 {
4574 return values;
4575 }
4576
4579 Container *operator ->() noexcept
4580 {
4581 return &values;
4582 }
4583
4586 const Container *operator ->() const noexcept
4587 {
4588 return &values;
4589 }
4590
4591 virtual std::string Name() const override
4592 {
4593 return name + std::string("...");
4594 }
4595
4596 virtual void Reset() noexcept override
4597 {
4598 ValueFlagBase::Reset();
4599 values = defaultValues;
4600 }
4601
4602 virtual FlagBase *Match(const EitherFlag &arg) override
4603 {
4604 const bool wasMatched = Matched();
4605 auto me = FlagBase::Match(arg);
4606 if (me && !wasMatched)
4607 {
4608 values.clear();
4609 }
4610 return me;
4611 }
4612
4613 iterator begin() noexcept
4614 {
4615 return values.begin();
4616 }
4617
4618 const_iterator begin() const noexcept
4619 {
4620 return values.begin();
4621 }
4622
4623 const_iterator cbegin() const noexcept
4624 {
4625 return values.cbegin();
4626 }
4627
4628 iterator end() noexcept
4629 {
4630 return values.end();
4631 }
4632
4633 const_iterator end() const noexcept
4634 {
4635 return values.end();
4636 }
4637
4638 const_iterator cend() const noexcept
4639 {
4640 return values.cend();
4641 }
4642 };
4643
4649 template <
4650 typename T,
4651 typename Reader = ValueReader>
4653 {
4654 private:
4655 T value;
4656 const T defaultValue;
4657 Reader reader;
4658 public:
4659 Positional(Group &group_, const std::string &name_, const std::string &help_, const T &defaultValue_ = T(), Options options_ = {}): PositionalBase(name_, help_, options_), value(defaultValue_), defaultValue(defaultValue_)
4660 {
4661 group_.Add(*this);
4662 }
4663
4664 Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_)
4665 {
4666 }
4667
4668 virtual ~Positional() {}
4669
4670 virtual void ParseValue(const std::string &value_) override
4671 {
4672#ifdef ARGS_NOEXCEPT
4673 if (!reader(name, value_, this->value))
4674 {
4675 error = Error::Parse;
4676 return;
4677 }
4678#else
4679 reader(name, value_, this->value);
4680#endif
4681 ready = false;
4682 matched = true;
4683 }
4684
4687 T &Get() noexcept
4688 {
4689 return value;
4690 }
4691
4694 T &operator *() noexcept
4695 {
4696 return value;
4697 }
4698
4701 const T &operator *() const noexcept
4702 {
4703 return value;
4704 }
4705
4708 T *operator ->() noexcept
4709 {
4710 return &value;
4711 }
4712
4715 const T *operator ->() const noexcept
4716 {
4717 return &value;
4718 }
4719
4720 virtual void Reset() noexcept override
4721 {
4722 PositionalBase::Reset();
4723 value = defaultValue;
4724 }
4725 };
4726
4733 template <
4734 typename T,
4735 template <typename...> class List = detail::vector,
4736 typename Reader = ValueReader>
4738 {
4739 private:
4740 using Container = List<T>;
4741 Container values;
4742 const Container defaultValues;
4743 Reader reader;
4744
4745 public:
4746 typedef T value_type;
4747 typedef typename Container::allocator_type allocator_type;
4748 typedef typename Container::pointer pointer;
4749 typedef typename Container::const_pointer const_pointer;
4750 typedef T& reference;
4751 typedef const T& const_reference;
4752 typedef typename Container::size_type size_type;
4753 typedef typename Container::difference_type difference_type;
4754 typedef typename Container::iterator iterator;
4755 typedef typename Container::const_iterator const_iterator;
4756 typedef std::reverse_iterator<iterator> reverse_iterator;
4757 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4758
4759 PositionalList(Group &group_, const std::string &name_, const std::string &help_, const Container &defaultValues_ = Container(), Options options_ = {}): PositionalBase(name_, help_, options_), values(defaultValues_), defaultValues(defaultValues_)
4760 {
4761 group_.Add(*this);
4762 }
4763
4764 PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_)
4765 {
4766 }
4767
4768 virtual ~PositionalList() {}
4769
4770 virtual void ParseValue(const std::string &value_) override
4771 {
4772 T v{};
4773#ifdef ARGS_NOEXCEPT
4774 if (!reader(name, value_, v))
4775 {
4776 error = Error::Parse;
4777 return;
4778 }
4779#else
4780 reader(name, value_, v);
4781#endif
4782 values.insert(std::end(values), v);
4783 matched = true;
4784 }
4785
4786 virtual std::string Name() const override
4787 {
4788 return name + std::string("...");
4789 }
4790
4793 Container &Get() noexcept
4794 {
4795 return values;
4796 }
4797
4800 Container &operator *() noexcept
4801 {
4802 return values;
4803 }
4804
4807 const Container &operator *() const noexcept
4808 {
4809 return values;
4810 }
4811
4814 Container *operator ->() noexcept
4815 {
4816 return &values;
4817 }
4818
4821 const Container *operator ->() const noexcept
4822 {
4823 return &values;
4824 }
4825
4826 virtual void Reset() noexcept override
4827 {
4828 PositionalBase::Reset();
4829 values = defaultValues;
4830 }
4831
4832 virtual PositionalBase *GetNextPositional() override
4833 {
4834 const bool wasMatched = Matched();
4835 auto me = PositionalBase::GetNextPositional();
4836 if (me && !wasMatched)
4837 {
4838 values.clear();
4839 }
4840 return me;
4841 }
4842
4843 iterator begin() noexcept
4844 {
4845 return values.begin();
4846 }
4847
4848 const_iterator begin() const noexcept
4849 {
4850 return values.begin();
4851 }
4852
4853 const_iterator cbegin() const noexcept
4854 {
4855 return values.cbegin();
4856 }
4857
4858 iterator end() noexcept
4859 {
4860 return values.end();
4861 }
4862
4863 const_iterator end() const noexcept
4864 {
4865 return values.end();
4866 }
4867
4868 const_iterator cend() const noexcept
4869 {
4870 return values.cend();
4871 }
4872 };
4873
4881 template <
4882 typename K,
4883 typename T,
4884 typename Reader = ValueReader,
4885 template <typename...> class Map = detail::unordered_map>
4887 {
4888 private:
4889 const Map<K, T> map;
4890 T value;
4891 const T defaultValue;
4892 Reader reader;
4893
4894 protected:
4895 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4896 {
4897 return detail::MapKeysToStrings(map);
4898 }
4899
4900 public:
4901
4902 MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}):
4903 PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_)
4904 {
4905 group_.Add(*this);
4906 }
4907
4908 virtual ~MapPositional() {}
4909
4910 virtual void ParseValue(const std::string &value_) override
4911 {
4912 K key{};
4913#ifdef ARGS_NOEXCEPT
4914 if (!reader(name, value_, key))
4915 {
4916 error = Error::Parse;
4917 return;
4918 }
4919#else
4920 reader(name, value_, key);
4921#endif
4922 auto it = map.find(key);
4923 if (it == std::end(map))
4924 {
4925 std::ostringstream problem;
4926 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4927#ifdef ARGS_NOEXCEPT
4928 error = Error::Map;
4929 errorMsg = problem.str();
4930#else
4931 throw MapError(problem.str());
4932#endif
4933 } else
4934 {
4935 this->value = it->second;
4936 ready = false;
4937 matched = true;
4938 }
4939 }
4940
4943 T &Get() noexcept
4944 {
4945 return value;
4946 }
4947
4950 T &operator *() noexcept
4951 {
4952 return value;
4953 }
4954
4957 const T &operator *() const noexcept
4958 {
4959 return value;
4960 }
4961
4964 T *operator ->() noexcept
4965 {
4966 return &value;
4967 }
4968
4971 const T *operator ->() const noexcept
4972 {
4973 return &value;
4974 }
4975
4976 virtual void Reset() noexcept override
4977 {
4978 PositionalBase::Reset();
4979 value = defaultValue;
4980 }
4981 };
4982
4991 template <
4992 typename K,
4993 typename T,
4994 template <typename...> class List = detail::vector,
4995 typename Reader = ValueReader,
4996 template <typename...> class Map = detail::unordered_map>
4998 {
4999 private:
5000 using Container = List<T>;
5001
5002 const Map<K, T> map;
5003 Container values;
5004 const Container defaultValues;
5005 Reader reader;
5006
5007 protected:
5008 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
5009 {
5010 return detail::MapKeysToStrings(map);
5011 }
5012
5013 public:
5014 typedef T value_type;
5015 typedef typename Container::allocator_type allocator_type;
5016 typedef typename Container::pointer pointer;
5017 typedef typename Container::const_pointer const_pointer;
5018 typedef T& reference;
5019 typedef const T& const_reference;
5020 typedef typename Container::size_type size_type;
5021 typedef typename Container::difference_type difference_type;
5022 typedef typename Container::iterator iterator;
5023 typedef typename Container::const_iterator const_iterator;
5024 typedef std::reverse_iterator<iterator> reverse_iterator;
5025 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
5026
5027 MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
5028 PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
5029 {
5030 group_.Add(*this);
5031 }
5032
5033 virtual ~MapPositionalList() {}
5034
5035 virtual void ParseValue(const std::string &value_) override
5036 {
5037 K key{};
5038#ifdef ARGS_NOEXCEPT
5039 if (!reader(name, value_, key))
5040 {
5041 error = Error::Parse;
5042 return;
5043 }
5044#else
5045 reader(name, value_, key);
5046#endif
5047 auto it = map.find(key);
5048 if (it == std::end(map))
5049 {
5050 std::ostringstream problem;
5051 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
5052#ifdef ARGS_NOEXCEPT
5053 error = Error::Map;
5054 errorMsg = problem.str();
5055#else
5056 throw MapError(problem.str());
5057#endif
5058 } else
5059 {
5060 this->values.emplace_back(it->second);
5061 matched = true;
5062 }
5063 }
5064
5067 Container &Get() noexcept
5068 {
5069 return values;
5070 }
5071
5074 Container &operator *() noexcept
5075 {
5076 return values;
5077 }
5078
5081 const Container &operator *() const noexcept
5082 {
5083 return values;
5084 }
5085
5088 Container *operator ->() noexcept
5089 {
5090 return &values;
5091 }
5092
5095 const Container *operator ->() const noexcept
5096 {
5097 return &values;
5098 }
5099
5100 virtual std::string Name() const override
5101 {
5102 return name + std::string("...");
5103 }
5104
5105 virtual void Reset() noexcept override
5106 {
5107 PositionalBase::Reset();
5108 values = defaultValues;
5109 }
5110
5111 virtual PositionalBase *GetNextPositional() override
5112 {
5113 const bool wasMatched = Matched();
5114 auto me = PositionalBase::GetNextPositional();
5115 if (me && !wasMatched)
5116 {
5117 values.clear();
5118 }
5119 return me;
5120 }
5121
5122 iterator begin() noexcept
5123 {
5124 return values.begin();
5125 }
5126
5127 const_iterator begin() const noexcept
5128 {
5129 return values.begin();
5130 }
5131
5132 const_iterator cbegin() const noexcept
5133 {
5134 return values.cbegin();
5135 }
5136
5137 iterator end() noexcept
5138 {
5139 return values.end();
5140 }
5141
5142 const_iterator end() const noexcept
5143 {
5144 return values.end();
5145 }
5146
5147 const_iterator cend() const noexcept
5148 {
5149 return values.cend();
5150 }
5151 };
5152}
5153
5154#pragma pop_macro("min")
5155#pragma pop_macro("max")
5156#endif
A flag class that calls a function when it's matched.
Definition args.hxx:3699
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3725
virtual void ParseValue(const std::vector< std::string > &value) override
Parse values of this option.
Definition args.hxx:3728
The main user facing command line argument parser class.
Definition args.hxx:2550
const std::string & ShortPrefix() const
The prefix for short flags.
Definition args.hxx:3257
void SetArgumentSeparations(const bool allowJoinedShortValue_, const bool allowJoinedLongValue_, const bool allowSeparateShortValue_, const bool allowSeparateLongValue_)
Change allowed option separation.
Definition args.hxx:3323
void Prog(const std::string &prog_)
The program name for help generation.
Definition args.hxx:3240
It ParseArgs(It begin, It end)
Parse all arguments.
Definition args.hxx:3479
const std::string & Prog() const
The program name for help generation.
Definition args.hxx:3236
void LongPrefix(const std::string &longprefix_)
The prefix for long flags.
Definition args.hxx:3249
void LongSeparator(const std::string &longseparator_)
The separator for long flags.
Definition args.hxx:3273
void Help(std::ostream &help_) const
Pass the help menu into an ostream.
Definition args.hxx:3340
const std::string & LongPrefix() const
The prefix for long flags.
Definition args.hxx:3245
const std::string & LongSeparator() const
The separator for long flags.
Definition args.hxx:3269
bool ParseCLI(const int argc, const char *const *argv)
Convenience function to parse the CLI from argc and argv.
Definition args.hxx:3510
const std::string & Terminator() const
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3293
auto ParseArgs(const T &args) -> decltype(std::begin(args))
Parse all arguments.
Definition args.hxx:3499
void Terminator(const std::string &terminator_)
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3297
void GetArgumentSeparations(bool &allowJoinedShortValue_, bool &allowJoinedLongValue_, bool &allowSeparateShortValue_, bool &allowSeparateLongValue_) const
Get the current argument separation parameters.
Definition args.hxx:3304
std::string Help() const
Generate a help menu as a string.
Definition args.hxx:3458
void ShortPrefix(const std::string &shortprefix_)
The prefix for short flags.
Definition args.hxx:3261
std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end, const bool allowSeparate, const bool allowJoined, const bool hasJoined, const std::string &joinedArg, const bool canDiscardJoined, std::vector< std::string > &values)
(INTERNAL) Parse flag's values
Definition args.hxx:2624
Base class for all match types.
Definition args.hxx:1016
void KickOut(bool kickout_) noexcept
Sets a kick-out value for building subparsers.
Definition args.hxx:1111
bool KickOut() const noexcept
Gets the kick-out value for building subparsers.
Definition args.hxx:1124
Main class for building subparsers.
Definition args.hxx:2010
const std::string & Name() const
The name of command.
Definition args.hxx:2145
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:2246
const std::string & ProglinePostfix() const
The description that appears on the prog line after options.
Definition args.hxx:2115
void Description(const std::string &description_)
The description that appears above options.
Definition args.hxx:2130
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:2282
const std::string & Description() const
The description that appears above options.
Definition args.hxx:2125
const std::string & Help() const
The description of command.
Definition args.hxx:2150
void Epilog(const std::string &epilog_)
The description that appears below options.
Definition args.hxx:2140
void ProglinePostfix(const std::string &proglinePostfix_)
The description that appears on the prog line after options.
Definition args.hxx:2120
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:2182
virtual bool HasCommand() const override
Get whether this has any Command children.
Definition args.hxx:2287
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:2351
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:2277
virtual std::vector< std::tuple< std::string, std::string, unsigned > > GetDescription(const HelpParams &params, const unsigned int indent) const override
Get all the child descriptions for help generation.
Definition args.hxx:2376
const std::string & Epilog() const
The description that appears below options.
Definition args.hxx:2135
virtual bool Matched() const noexcept override
Whether or not this group matches validation.
Definition args.hxx:2163
void RequireCommand(bool value)
If value is true, parser will fail if no command was parsed.
Definition args.hxx:2157
Definition args.hxx:1460
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:1474
std::string Get() noexcept
Get the completion reply.
Definition args.hxx:1545
virtual void ParseValue(const std::vector< std::string > &value_) override
Parse values of this option.
Definition args.hxx:1479
An exception that contains autocompletion reply.
Definition args.hxx:565
A flag class that simply counts the number of times it's matched.
Definition args.hxx:3643
int & Get() noexcept
Get the count.
Definition args.hxx:3676
Base error class.
Definition args.hxx:484
Error that occurs when a singular flag is specified multiple times.
Definition args.hxx:538
Base class for all flag options.
Definition args.hxx:1303
virtual void ParseValue(const std::vector< std::string > &value)=0
Parse values of this option.
virtual Nargs NumberOfArguments() const noexcept=0
Defines how many values can be consumed by this option.
Boolean argument matcher.
Definition args.hxx:3581
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3601
virtual void ParseValue(const std::vector< std::string > &) override
Parse values of this option.
Definition args.hxx:3606
bool Get() const
Get whether this was matched.
Definition args.hxx:3596
Class for using global options in ArgumentParser.
Definition args.hxx:1929
Class for all kinds of validating groups, including ArgumentParser.
Definition args.hxx:1623
virtual bool HasCommand() const override
Get whether this has any Command children.
Definition args.hxx:1777
std::vector< Base * >::size_type MatchedChildren() const
Count the number of matched children this group has.
Definition args.hxx:1784
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:1768
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:1837
Group(Group &group_, const std::string &help_=std::string(), const std::function< bool(const Group &)> &validator_=Validators::DontCare, Options options_={})
If help is empty, this group will not be printed in help output.
Definition args.hxx:1683
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:1759
Group(const std::string &help_=std::string(), const std::function< bool(const Group &)> &validator_=Validators::DontCare, Options options_={})
If help is empty, this group will not be printed in help output.
Definition args.hxx:1681
virtual bool Matched() const noexcept override
Whether or not this group matches validation.
Definition args.hxx:1793
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:1708
bool Get() const
Get validation.
Definition args.hxx:1800
const std::vector< Base * > & Children() const
Get all this group's children.
Definition args.hxx:1698
void Add(Base &child)
Append a child to this Group.
Definition args.hxx:1691
virtual std::vector< std::tuple< std::string, std::string, unsigned > > GetDescription(const HelpParams &params, const unsigned int indent) const override
Get all the child descriptions for help generation.
Definition args.hxx:1807
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:1743
Help flag class.
Definition args.hxx:3616
bool Get() const noexcept
Get whether this was matched.
Definition args.hxx:3634
virtual void ParseValue(const std::vector< std::string > &)
Parse values of this option.
Definition args.hxx:3622
An exception that indicates that the user has requested help.
Definition args.hxx:547
An optional argument-accepting flag class.
Definition args.hxx:4023
virtual void ParseValue(const std::vector< std::string > &value_) override
Parse values of this option.
Definition args.hxx:4051
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:4046
Errors in map lookups.
Definition args.hxx:529
A mapping value flag list class.
Definition args.hxx:4489
Container * operator->() noexcept
Get the values.
Definition args.hxx:4579
Container & Get() noexcept
Get the value.
Definition args.hxx:4558
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4525
Container & operator*() noexcept
Get the value.
Definition args.hxx:4565
A mapping value flag class.
Definition args.hxx:4371
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4401
T & Get() noexcept
Get the value.
Definition args.hxx:4434
T * operator->() noexcept
Get the value.
Definition args.hxx:4455
T & operator*() noexcept
Get the value.
Definition args.hxx:4441
A positional argument mapping list class.
Definition args.hxx:4998
Container & operator*() noexcept
Get the value.
Definition args.hxx:5074
Container * operator->() noexcept
Get the values.
Definition args.hxx:5088
Container & Get() noexcept
Get the value.
Definition args.hxx:5067
A positional argument mapping class.
Definition args.hxx:4887
T * operator->() noexcept
Get the value.
Definition args.hxx:4964
T & Get() noexcept
Get the value.
Definition args.hxx:4943
T & operator*() noexcept
Get the value.
Definition args.hxx:4950
A class of "matchers", specifying short and flags that can possibly be matched.
Definition args.hxx:633
EitherFlag GetShortOrAny() const
(INTERNAL) Get short flag if it exists or any long flag
Definition args.hxx:751
std::vector< EitherFlag > GetFlagStrings() const
(INTERNAL) Get all flag strings as a vector, with the prefixes embedded
Definition args.hxx:716
Matcher(Short &&shortIn, Long &&longIn)
Specify short and long flags separately as iterables.
Definition args.hxx:669
Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd)
Specify short and long flags separately as iterators.
Definition args.hxx:644
bool Match(const std::string &flag) const
(INTERNAL) Check if there is a match of a long flag
Definition args.hxx:702
EitherFlag GetLongOrAny() const
(INTERNAL) Get long flag if it exists or any short flag
Definition args.hxx:733
bool Match(const char flag) const
(INTERNAL) Check if there is a match of a short flag
Definition args.hxx:695
bool Match(const EitherFlag &flag) const
(INTERNAL) Check if there is a match of a flag
Definition args.hxx:709
Matcher(std::initializer_list< EitherFlag > in)
Specify a mixed single initializer-list of both short and long flags.
Definition args.hxx:685
Base class for all match types that have a name.
Definition args.hxx:1156
void HelpDefault(const std::string &str)
Sets default value string that will be added to argument description.
Definition args.hxx:1192
void HelpChoices(const std::vector< std::string > &array)
Sets choices strings that will be added to argument description.
Definition args.hxx:1208
std::string HelpDefault(const HelpParams &params) const
Gets default value string that will be added to argument description.
Definition args.hxx:1200
std::vector< std::string > HelpChoices(const HelpParams &params) const
Gets choices strings that will be added to argument description.
Definition args.hxx:1216
A variadic arguments accepting flag class.
Definition args.hxx:4074
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:4106
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4111
List< T > * operator->() noexcept
Get the values.
Definition args.hxx:4152
List< T > & operator*() noexcept
Get the value.
Definition args.hxx:4138
Errors that occur during regular parsing.
Definition args.hxx:502
Base class for positional options.
Definition args.hxx:1563
A positional argument class that pushes the found values into a list.
Definition args.hxx:4738
Container & operator*() noexcept
Get the value.
Definition args.hxx:4800
Container & Get() noexcept
Get the values.
Definition args.hxx:4793
Container * operator->() noexcept
Get the values.
Definition args.hxx:4814
A positional argument class.
Definition args.hxx:4653
T & operator*() noexcept
Get the value.
Definition args.hxx:4694
T & Get() noexcept
Get the value.
Definition args.hxx:4687
T * operator->() noexcept
Get the value.
Definition args.hxx:4708
Errors that when a required flag is omitted.
Definition args.hxx:520
Utility class for building subparsers with coroutines/callbacks.
Definition args.hxx:1955
void Parse()
Continue parsing arguments for new command.
Definition args.hxx:3544
const std::vector< std::string > & KickedOut() const noexcept
Returns a vector of kicked out arguments.
Definition args.hxx:1999
bool IsParsed() const
(INTERNAL) Determines whether Parse was called or not.
Definition args.hxx:1986
Errors that occur during usage.
Definition args.hxx:493
Errors that are detected from group validation after parsing finishes.
Definition args.hxx:511
Base class for value-accepting flag options.
Definition args.hxx:1447
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:1453
An argument-accepting flag class that pushes the found values into a list.
Definition args.hxx:4223
Container * operator->() noexcept
Get the values.
Definition args.hxx:4293
Container & Get() noexcept
Get the values.
Definition args.hxx:4272
Container & operator*() noexcept
Get the value.
Definition args.hxx:4279
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4253
An argument-accepting flag class.
Definition args.hxx:3921
T & operator*() noexcept
Get the value.
Definition args.hxx:3980
T * operator->() noexcept
Get the value.
Definition args.hxx:3994
T & Get() noexcept
Get the value.
Definition args.hxx:3973
const T & GetDefault() noexcept
Get the default value.
Definition args.hxx:4008
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:3951
Definition args.hxx:1251
contains all the functionality of the args library
std::vector< std::string > Wrap(It begin, It end, const std::string::size_type width, std::string::size_type firstlinewidth=0, std::string::size_type firstlineindent=0)
(INTERNAL) Wrap a vector of words into a vector of lines
Definition args.hxx:277
bool SafeAdd(T a, T b, T &out) noexcept
Safe addition to prevent integer overflow.
Definition args.hxx:108
bool SafeMultiply(T a, T b, T &out) noexcept
Safe multiplication to prevent integer overflow.
Definition args.hxx:148
bool SafeSub(T a, T b, T &out) noexcept
Safe subtraction to prevent integer underflow.
Definition args.hxx:202
Options
Attributes for flags.
Definition args.hxx:771
@ HiddenFromCompletion
Flag is excluded from auto completion.
@ Global
Flag is global and can be used in any subcommand.
@ Single
Flag can't be passed multiple times.
@ None
Default options.
@ HiddenFromUsage
Flag is excluded from usage line.
@ Hidden
Flag is excluded from options help and usage line.
@ Required
Flag can't be omitted.
@ KickOut
Flag stops a parser.
@ HiddenFromDescription
Flag is excluded from options help.
std::enable_if< std::is_unsigned< T >::value, bool >::type SafeNeg(T a, T &out) noexcept
Safe negation to prevent integer overflow.
Definition args.hxx:240
auto get(Option &option_) -> decltype(option_.Get())
Getter to grab the value from the argument type.
Definition args.hxx:78
std::string::size_type Glyphs(const std::string &string_)
(INTERNAL) Count UTF-8 glyphs
Definition args.hxx:91
A simple unified option type for unified initializer lists for the Matcher class.
Definition args.hxx:575
static std::unordered_set< char > GetShort(std::initializer_list< EitherFlag > flags)
Get just the short flags from an initializer list of EitherFlags.
Definition args.hxx:600
static std::unordered_set< std::string > GetLong(std::initializer_list< EitherFlag > flags)
Get just the long flags from an initializer list of EitherFlags.
Definition args.hxx:585
Default validators.
Definition args.hxx:1632
A simple structure of parameters for easy user-modifyable help menus.
Definition args.hxx:827
std::string programName
The program name for help generation.
Definition args.hxx:884
bool showCommandFullHelp
Show command's descriptions and epilog.
Definition args.hxx:892
bool proglineShowFlags
Show flags in program line.
Definition args.hxx:928
std::string usageString
Program line prefix.
Definition args.hxx:936
unsigned int width
The width of the help menu.
Definition args.hxx:830
std::string proglineNonrequiredClose
The postfix for progline non-required argument.
Definition args.hxx:924
unsigned int helpindent
The indent of the flag descriptions.
Definition args.hxx:845
bool proglinePreferShortFlags
Use short flags in program lines when possible.
Definition args.hxx:932
std::string proglineCommand
The prefix for progline when command has any subcommands.
Definition args.hxx:900
std::string proglineOptions
The postfix for progline when showProglineOptions is true and command has any flags.
Definition args.hxx:896
std::string longSeparator
The separator for long flags.
Definition args.hxx:880
bool showValueName
Show value name.
Definition args.hxx:948
bool showTerminator
Show the terminator when both options and positional parameters are present.
Definition args.hxx:856
std::string proglineValueOpen
The prefix for progline value.
Definition args.hxx:904
std::string valueOpen
The prefix for option value.
Definition args.hxx:956
unsigned int progtailindent
The indent of the program trailing lines for long parameters.
Definition args.hxx:836
std::string proglineNonrequiredOpen
The prefix for progline non-required argument.
Definition args.hxx:920
unsigned int descriptionindent
The indent of the description and epilogs.
Definition args.hxx:839
bool showCommandChildren
Show command's flags.
Definition args.hxx:888
bool showProglineOptions
Show the {OPTIONS} on the prog line when this is true.
Definition args.hxx:860
std::string valueClose
The postfix for option value.
Definition args.hxx:960
std::string longPrefix
The prefix for long flags.
Definition args.hxx:872
std::string proglineRequiredClose
The postfix for progline required argument.
Definition args.hxx:916
unsigned int flagindent
The indent of the flags.
Definition args.hxx:842
std::string optionsString
String shown in help before flags descriptions.
Definition args.hxx:940
bool addNewlineBeforeDescription
Add newline before flag description.
Definition args.hxx:952
std::string shortPrefix
The prefix for short flags.
Definition args.hxx:868
bool addDefault
Add default values to argument description.
Definition args.hxx:972
unsigned int gutter
The minimum gutter between each flag and its help.
Definition args.hxx:852
bool showProglinePositionals
Show the positionals on the prog line when this is true.
Definition args.hxx:864
std::string shortSeparator
The separator for short flags.
Definition args.hxx:876
std::string proglineValueClose
The postfix for progline value.
Definition args.hxx:908
bool useValueNameOnce
Display value name after all the long and short flags.
Definition args.hxx:944
std::string defaultString
The prefix for default values.
Definition args.hxx:976
std::string proglineRequiredOpen
The prefix for progline required argument.
Definition args.hxx:912
unsigned int eachgroupindent
The additional indent each group adds.
Definition args.hxx:848
unsigned int progindent
The indent of the program line.
Definition args.hxx:833
bool addChoices
Add choices to argument description.
Definition args.hxx:964
std::string choiceString
The prefix for choices.
Definition args.hxx:968
A number of arguments which can be consumed by an option.
Definition args.hxx:984
A default Reader class for argument classes.
Definition args.hxx:3739