Coverage Report

Created: 2026-09-07 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/args/args.hxx
Line
Count
Source
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
27
/** \file args.hxx
28
 * \brief this single-header lets you use all of the args functionality
29
 *
30
 * The important stuff is done inside the args namespace
31
 */
32
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.6.0"
41
#define ARGS_VERSION_MAJOR 6
42
#define ARGS_VERSION_MINOR 6
43
#define ARGS_VERSION_PATCH 0
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
67
/** \namespace args
68
 * \brief contains all the functionality of the args library
69
 */
70
namespace args
71
{
72
    /** Getter to grab the value from the argument type.
73
     *
74
     * If the Get() function of the type returns a reference, so does this, and
75
     * the value will be modifiable.
76
     */
77
    template <typename Option>
78
    auto get(Option &option_) -> decltype(option_.Get())
79
    {
80
        return option_.Get();
81
    }
82
83
    /** (INTERNAL) Count UTF-8 glyphs
84
     *
85
     * This is not reliable, and will fail for combinatory glyphs, but it's
86
     * good enough here for now.
87
     *
88
     * \param string The string to count glyphs from
89
     * \return The UTF-8 glyphs in the string
90
     */
91
    inline std::string::size_type Glyphs(const std::string &string_)
92
0
    {
93
0
        std::string::size_type length = 0;
94
0
        for (const char c: string_)
95
0
        {
96
0
            if ((c & 0xc0) != 0x80)
97
0
            {
98
0
                ++length;
99
0
            }
100
0
        }
101
0
        return length;
102
0
    }
103
104
    /** Safe addition to prevent integer overflow.
105
     * Returns true if the addition is successful, false if it would overflow.
106
     */
107
    template<typename T>
108
    bool SafeAdd(T a, T b, T& out) noexcept
109
0
    {
110
0
        static_assert(std::is_integral<T>::value, "SafeAdd requires integral types.");
111
0
        if (std::is_unsigned<T>::value)
112
0
        {
113
0
            using U = typename std::make_unsigned<T>::type;
114
0
            const U ua = static_cast<U>(a);
115
0
            const U ub = static_cast<U>(b);
116
0
            const U maxv = std::numeric_limits<U>::max();
117
0
            if (ua > maxv - ub)
118
0
            {
119
0
                return false;
120
0
            }
121
0
            out = static_cast<T>(ua + ub);
122
0
            return true;
123
0
        }
124
0
        else
125
0
        {
126
0
#if defined(__clang__) || defined(__GNUC__)
127
0
            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
0
        }
142
0
    }
143
144
    /** Safe multiplication to prevent integer overflow.
145
     * Returns true if the multiplication is successful, false if it would overflow.
146
     */
147
    template<typename T>
148
    bool SafeMultiply(T a, T b, T& out) noexcept
149
0
    {
150
0
        static_assert(std::is_integral<T>::value, "SafeMultiply requires integral types.");
151
152
0
        if (a == 0 || b == 0)
153
0
        {
154
0
            out = 0;
155
0
            return true;
156
0
        }
157
158
0
        if (std::is_unsigned<T>::value)
159
0
        {
160
0
            using U = typename std::make_unsigned<T>::type;
161
0
            const U ua = static_cast<U>(a);
162
0
            const U ub = static_cast<U>(b);
163
0
            const U maxv = std::numeric_limits<U>::max();
164
0
            if (ub > maxv / ua)
165
0
            {
166
0
                return false;
167
0
            }
168
0
            out = static_cast<T>(ua * ub);
169
0
            return true;
170
0
        }
171
0
        else
172
0
        {
173
0
#if defined(__clang__) || defined(__GNUC__)
174
0
            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
0
        }
196
0
    }
197
198
    /** Safe subtraction to prevent integer underflow.
199
     * Returns true if the subtraction is successful, false if it would underflow.
200
     */
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
234
    /** Safe negation to prevent integer overflow.
235
     * Returns true if the negation is successful, false if it would overflow.
236
     */
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
265
    /** (INTERNAL) Wrap a vector of words into a vector of lines
266
     *
267
     * Empty words are skipped. Word "\n" forces wrapping.
268
     *
269
     * \param begin The begin iterator
270
     * \param end The end iterator
271
     * \param width The width of the body
272
     * \param firstlinewidth the width of the first line, defaults to the width of the body
273
     * \param firstlineindent the indent of the first line, defaults to 0
274
     * \return the vector of lines
275
     */
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
0
    {
283
0
        std::vector<std::string> output;
284
0
        std::string line(firstlineindent, ' ');
285
0
        bool empty = true;
286
0
287
0
        if (firstlinewidth == 0)
288
0
        {
289
0
            firstlinewidth = width;
290
0
        }
291
0
292
0
        auto currentwidth = firstlinewidth;
293
0
294
0
        for (auto it = begin; it != end; ++it)
295
0
        {
296
0
            if (it->empty())
297
0
            {
298
0
                continue;
299
0
            }
300
0
301
0
            if (*it == "\n")
302
0
            {
303
0
                if (!empty)
304
0
                {
305
0
                    output.push_back(line);
306
0
                    line.clear();
307
0
                    empty = true;
308
0
                    currentwidth = width;
309
0
                }
310
0
311
0
                continue;
312
0
            }
313
0
314
0
            auto itemsize = Glyphs(*it);
315
0
            
316
0
            // Refactored to prevent integer overflow
317
0
            bool needsWrap = false;
318
0
            if (itemsize >= currentwidth)
319
0
            {
320
0
                needsWrap = true;
321
0
            }
322
0
            else
323
0
            {
324
0
                size_t remainingWidth = (currentwidth > itemsize) ? (currentwidth - itemsize) : 0;
325
0
                size_t nextLength = 0;
326
0
                if (!SafeAdd<std::string::size_type>(line.length(), static_cast<std::string::size_type>(1), nextLength) || nextLength > remainingWidth)
327
0
                {
328
0
                    needsWrap = true;
329
0
                }
330
0
            }
331
0
            
332
0
            if (needsWrap)
333
0
            {
334
0
                if (!empty)
335
0
                {
336
0
                    output.push_back(line);
337
0
                    line.clear();
338
0
                    empty = true;
339
0
                    currentwidth = width;
340
0
                }
341
0
            }
342
0
343
0
            if (itemsize > 0)
344
0
            {
345
0
                if (!empty)
346
0
                {
347
0
                    line += ' ';
348
0
                }
349
0
350
0
                line += *it;
351
0
                empty = false;
352
0
            }
353
0
        }
354
0
355
0
        if (!empty)
356
0
        {
357
0
            output.push_back(line);
358
0
        }
359
0
360
0
        return output;
361
0
    }
Unexecuted instantiation: std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > args::Wrap<std::__1::istream_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char, std::__1::char_traits<char>, long> >(std::__1::istream_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char, std::__1::char_traits<char>, long>, std::__1::istream_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char, std::__1::char_traits<char>, long>, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > args::Wrap<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, unsigned long, unsigned long, unsigned long)
362
363
    namespace detail
364
    {
365
        template <typename T>
366
        std::string Join(const T& array, const std::string &delimiter)
367
0
        {
368
0
            std::string res;
369
            
370
            // Safely compute reservation size to avoid unbounded reallocations
371
0
            using size_type = std::string::size_type;
372
0
            size_type total = 0;
373
0
            size_type count = 0;
374
0
            const size_type delim_size = static_cast<size_type>(delimiter.size());
375
0
            bool can_reserve = true;
376
            
377
0
            for (const auto &element : array)
378
0
            {
379
0
                const size_type elem_size = static_cast<size_type>(element.size());
380
0
                if (!SafeAdd<size_type>(total, elem_size, total))
381
0
                {
382
0
                    can_reserve = false;
383
0
                    break;
384
0
                }
385
0
                ++count;
386
0
            }
387
            
388
0
            if (can_reserve && count > 1)
389
0
            {
390
0
                size_type delim_count = count - 1;
391
0
                size_type delim_total = 0;
392
0
                if (!SafeMultiply<size_type>(delim_count, delim_size, delim_total) ||
393
0
                    !SafeAdd<size_type>(total, delim_total, total))
394
0
                {
395
0
                    can_reserve = false;
396
0
                }
397
0
            }
398
            
399
0
            if (can_reserve && total > 0)
400
0
            {
401
0
                res.reserve(total);
402
0
            }
403
            
404
0
            bool first = true;
405
0
            for (const auto &element : array)
406
0
            {
407
0
                if (!first)
408
0
                {
409
0
                    res += delimiter;
410
0
                }
411
0
                res += element;
412
0
                first = false;
413
0
            }
414
            
415
0
            return res;
416
0
        }
417
    }
418
419
    /** (INTERNAL) Wrap a string into a vector of lines
420
     *
421
     * This is quick and hacky, but works well enough.  You can specify a
422
     * different width for the first line
423
     *
424
     * \param width The width of the body
425
     * \param firstlinewid the width of the first line, defaults to the width of the body
426
     * \return the vector of lines
427
     */
428
    inline std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0)
429
0
    {
430
0
        // Preserve existing line breaks
431
0
        const auto newlineloc = in.find('\n');
432
0
        if (newlineloc != in.npos)
433
0
        {
434
0
            auto first = Wrap(std::string(in, 0, newlineloc), width);
435
0
            auto second = Wrap(std::string(in, newlineloc + 1), width);
436
0
            first.insert(
437
0
                std::end(first),
438
0
                std::make_move_iterator(std::begin(second)),
439
0
                std::make_move_iterator(std::end(second)));
440
0
            return first;
441
0
        }
442
0
443
0
        std::istringstream stream(in);
444
0
        std::string::size_type indent = 0;
445
0
446
0
        for (auto c : in)
447
0
        {
448
0
            if (!std::isspace(static_cast<unsigned char>(c)))
449
0
            {
450
0
                break;
451
0
            }
452
0
            ++indent;
453
0
        }
454
0
455
0
        return Wrap(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>(),
456
0
                    width, firstlinewidth, indent);
457
0
    }
458
459
#ifdef ARGS_NOEXCEPT
460
    /// Error class, for when ARGS_NOEXCEPT is defined
461
    enum class Error
462
    {
463
        None,
464
        Usage,
465
        Parse,
466
        Validation,
467
        Required,
468
        Map,
469
        Extra,
470
        Help,
471
        Subparser,
472
        Completion,
473
    };
474
#else
475
    /** Base error class
476
     */
477
    class Error : public std::runtime_error
478
    {
479
        public:
480
958
            Error(const std::string &problem) : std::runtime_error(problem) {}
481
0
            virtual ~Error() {}
482
    };
483
484
    /** Errors that occur during usage
485
     */
486
    class UsageError : public Error
487
    {
488
        public:
489
0
            UsageError(const std::string &problem) : Error(problem) {}
490
0
            virtual ~UsageError() {}
491
    };
492
493
    /** Errors that occur during regular parsing
494
     */
495
    class ParseError : public Error
496
    {
497
        public:
498
937
            ParseError(const std::string &problem) : Error(problem) {}
499
0
            virtual ~ParseError() {}
500
    };
501
502
    /** Errors that are detected from group validation after parsing finishes
503
     */
504
    class ValidationError : public Error
505
    {
506
        public:
507
0
            ValidationError(const std::string &problem) : Error(problem) {}
508
0
            virtual ~ValidationError() {}
509
    };
510
511
    /** Errors that when a required flag is omitted
512
     */
513
    class RequiredError : public ValidationError
514
    {
515
        public:
516
0
            RequiredError(const std::string &problem) : ValidationError(problem) {}
517
0
            virtual ~RequiredError() {}
518
    };
519
520
    /** Errors in map lookups
521
     */
522
    class MapError : public ParseError
523
    {
524
        public:
525
0
            MapError(const std::string &problem) : ParseError(problem) {}
526
0
            virtual ~MapError() {}
527
    };
528
529
    /** Error that occurs when a singular flag is specified multiple times
530
     */
531
    class ExtraError : public ParseError
532
    {
533
        public:
534
0
            ExtraError(const std::string &problem) : ParseError(problem) {}
535
0
            virtual ~ExtraError() {}
536
    };
537
538
    /** An exception that indicates that the user has requested help
539
     */
540
    class Help : public Error
541
    {
542
        public:
543
21
            Help(const std::string &flag) : Error(flag) {}
544
0
            virtual ~Help() {}
545
    };
546
547
    /** (INTERNAL) An exception that emulates coroutine-like control flow for subparsers.
548
     */
549
    class SubparserError : public Error
550
    {
551
        public:
552
0
            SubparserError() : Error("") {}
553
0
            virtual ~SubparserError() {}
554
    };
555
556
    /** An exception that contains autocompletion reply
557
     */
558
    class Completion : public Error
559
    {
560
        public:
561
0
            Completion(const std::string &flag) : Error(flag) {}
562
0
            virtual ~Completion() {}
563
    };
564
#endif
565
566
    /** A simple unified option type for unified initializer lists for the Matcher class.
567
     */
568
    struct EitherFlag
569
    {
570
        const bool isShort;
571
        const char shortFlag;
572
        const std::string longFlag;
573
6.41k
        EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {}
574
2.94k
        EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {}
575
37.0k
        EitherFlag(const char flag) : isShort(true), shortFlag(flag), longFlag() {}
576
577
        /** Get just the long flags from an initializer list of EitherFlags
578
         */
579
        static std::unordered_set<std::string> GetLong(std::initializer_list<EitherFlag> flags)
580
2.94k
        {
581
2.94k
            std::unordered_set<std::string>  longFlags;
582
2.94k
            for (const EitherFlag &flag: flags)
583
5.89k
            {
584
5.89k
                if (!flag.isShort)
585
2.94k
                {
586
2.94k
                    longFlags.insert(flag.longFlag);
587
2.94k
                }
588
5.89k
            }
589
2.94k
            return longFlags;
590
2.94k
        }
591
592
        /** Get just the short flags from an initializer list of EitherFlags
593
         */
594
        static std::unordered_set<char> GetShort(std::initializer_list<EitherFlag> flags)
595
2.94k
        {
596
2.94k
            std::unordered_set<char>  shortFlags;
597
2.94k
            for (const EitherFlag &flag: flags)
598
5.89k
            {
599
5.89k
                if (flag.isShort)
600
2.94k
                {
601
2.94k
                    shortFlags.insert(flag.shortFlag);
602
2.94k
                }
603
5.89k
            }
604
2.94k
            return shortFlags;
605
2.94k
        }
606
607
        std::string str() const
608
0
        {
609
0
            return isShort ? std::string(1, shortFlag) : longFlag;
610
0
        }
611
612
        std::string str(const std::string &shortPrefix, const std::string &longPrefix) const
613
0
        {
614
0
            return isShort ? shortPrefix + std::string(1, shortFlag) : longPrefix + longFlag;
615
0
        }
616
    };
617
618
619
620
    /** A class of "matchers", specifying short and flags that can possibly be
621
     * matched.
622
     *
623
     * This is supposed to be constructed and then passed in, not used directly
624
     * from user code.
625
     */
626
    class Matcher
627
    {
628
        private:
629
            const std::unordered_set<char> shortFlags;
630
            const std::unordered_set<std::string> longFlags;
631
632
        public:
633
            /** Specify short and long flags separately as iterators
634
             *
635
             * ex: `args::Matcher(shortFlags.begin(), shortFlags.end(), longFlags.begin(), longFlags.end())`
636
             */
637
            template <typename ShortIt, typename LongIt>
638
            Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd) :
639
2.94k
                shortFlags(shortFlagsStart, shortFlagsEnd),
640
2.94k
                longFlags(longFlagsStart, longFlagsEnd)
641
2.94k
            {
642
2.94k
                if (shortFlags.empty() && longFlags.empty())
643
0
                {
644
0
#ifndef ARGS_NOEXCEPT
645
0
                    throw UsageError("empty Matcher");
646
0
#endif
647
0
                }
648
2.94k
            }
649
650
#ifdef ARGS_NOEXCEPT
651
            /// Only for ARGS_NOEXCEPT
652
            Error GetError() const noexcept
653
            {
654
                return shortFlags.empty() && longFlags.empty() ? Error::Usage : Error::None;
655
            }
656
#endif
657
658
            /** Specify short and long flags separately as iterables
659
             *
660
             * ex: `args::Matcher(shortFlags, longFlags)`
661
             */
662
            template <typename Short, typename Long>
663
            Matcher(Short &&shortIn, Long &&longIn) :
664
2.94k
                Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn))
665
2.94k
            {}
666
667
            /** Specify a mixed single initializer-list of both short and long flags
668
             *
669
             * This is the fancy one.  It takes a single initializer list of
670
             * any number of any mixed kinds of flags.  Chars are
671
             * automatically interpreted as short flags, and strings are
672
             * automatically interpreted as long flags:
673
             *
674
             *     args::Matcher{'a'}
675
             *     args::Matcher{"foo"}
676
             *     args::Matcher{'h', "help"}
677
             *     args::Matcher{"foo", 'f', 'F', "FoO"}
678
             */
679
            Matcher(std::initializer_list<EitherFlag> in) :
680
2.94k
                Matcher(EitherFlag::GetShort(in), EitherFlag::GetLong(in)) {}
681
682
2.94k
            Matcher(Matcher &&other) noexcept : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags))
683
2.94k
            {}
684
685
5.89k
            ~Matcher() {}
686
687
            /** (INTERNAL) Check if there is a match of a short flag
688
             */
689
            bool Match(const char flag) const
690
83.0k
            {
691
83.0k
                return shortFlags.find(flag) != shortFlags.end();
692
83.0k
            }
693
694
            /** (INTERNAL) Check if there is a match of a long flag
695
             */
696
            bool Match(const std::string &flag) const
697
1.44k
            {
698
1.44k
                return longFlags.find(flag) != longFlags.end();
699
1.44k
            }
700
701
            /** (INTERNAL) Check if there is a match of a flag
702
             */
703
            bool Match(const EitherFlag &flag) const
704
84.4k
            {
705
84.4k
                return flag.isShort ? Match(flag.shortFlag) : Match(flag.longFlag);
706
84.4k
            }
707
708
            /** (INTERNAL) Get all flag strings as a vector, with the prefixes embedded
709
             */
710
            std::vector<EitherFlag> GetFlagStrings() const
711
5.89k
            {
712
5.89k
                std::vector<EitherFlag> flagStrings;
713
5.89k
                flagStrings.reserve(shortFlags.size() + longFlags.size());
714
5.89k
                for (const char flag: shortFlags)
715
5.89k
                {
716
5.89k
                    flagStrings.emplace_back(flag);
717
5.89k
                }
718
5.89k
                for (const std::string &flag: longFlags)
719
5.89k
                {
720
5.89k
                    flagStrings.emplace_back(flag);
721
5.89k
                }
722
5.89k
                return flagStrings;
723
5.89k
            }
724
725
            /** (INTERNAL) Get long flag if it exists or any short flag
726
             */
727
            EitherFlag GetLongOrAny() const
728
0
            {
729
0
                if (!longFlags.empty())
730
0
                {
731
0
                    return *longFlags.begin();
732
0
                }
733
734
0
                if (!shortFlags.empty())
735
0
                {
736
0
                    return *shortFlags.begin();
737
0
                }
738
739
                // should be unreachable
740
0
                return ' ';
741
0
            }
742
743
            /** (INTERNAL) Get short flag if it exists or any long flag
744
             */
745
            EitherFlag GetShortOrAny() const
746
0
            {
747
0
                if (!shortFlags.empty())
748
0
                {
749
0
                    return *shortFlags.begin();
750
0
                }
751
752
0
                if (!longFlags.empty())
753
0
                {
754
0
                    return *longFlags.begin();
755
0
                }
756
757
                // should be unreachable
758
0
                return ' ';
759
0
            }
760
    };
761
762
    /** Attributes for flags.
763
     */
764
    enum class Options
765
    {
766
        /** Default options.
767
         */
768
        None = 0x0,
769
770
        /** Flag can't be passed multiple times.
771
         */
772
        Single = 0x01,
773
774
        /** Flag can't be omitted.
775
         */
776
        Required = 0x02,
777
778
        /** Flag is excluded from usage line.
779
         */
780
        HiddenFromUsage = 0x04,
781
782
        /** Flag is excluded from options help.
783
         */
784
        HiddenFromDescription = 0x08,
785
786
        /** Flag is global and can be used in any subcommand.
787
         */
788
        Global = 0x10,
789
790
        /** Flag stops a parser.
791
         */
792
        KickOut = 0x20,
793
794
        /** Flag is excluded from auto completion.
795
         */
796
        HiddenFromCompletion = 0x40,
797
798
        /** Flag is excluded from options help and usage line
799
         */
800
        Hidden = HiddenFromUsage | HiddenFromDescription | HiddenFromCompletion,
801
    };
802
803
    inline Options operator | (Options lhs, Options rhs)
804
0
    {
805
0
        return static_cast<Options>(static_cast<int>(lhs) | static_cast<int>(rhs));
806
0
    }
807
808
    inline Options operator & (Options lhs, Options rhs)
809
56.0k
    {
810
56.0k
        return static_cast<Options>(static_cast<int>(lhs) & static_cast<int>(rhs));
811
56.0k
    }
812
813
    class FlagBase;
814
    class PositionalBase;
815
    class Command;
816
    class ArgumentParser;
817
818
    /** A simple structure of parameters for easy user-modifyable help menus
819
     */
820
    struct HelpParams
821
    {
822
        /** The width of the help menu
823
         */
824
        unsigned int width = 80;
825
        /** The indent of the program line
826
         */
827
        unsigned int progindent = 2;
828
        /** The indent of the program trailing lines for long parameters
829
         */
830
        unsigned int progtailindent = 4;
831
        /** The indent of the description and epilogs
832
         */
833
        unsigned int descriptionindent = 4;
834
        /** The indent of the flags
835
         */
836
        unsigned int flagindent = 6;
837
        /** The indent of the flag descriptions
838
         */
839
        unsigned int helpindent = 40;
840
        /** The additional indent each group adds
841
         */
842
        unsigned int eachgroupindent = 2;
843
844
        /** The minimum gutter between each flag and its help
845
         */
846
        unsigned int gutter = 1;
847
848
        /** Show the terminator when both options and positional parameters are present
849
         */
850
        bool showTerminator = true;
851
852
        /** Show the {OPTIONS} on the prog line when this is true
853
         */
854
        bool showProglineOptions = true;
855
856
        /** Show the positionals on the prog line when this is true
857
         */
858
        bool showProglinePositionals = true;
859
860
        /** The prefix for short flags
861
         */
862
        std::string shortPrefix;
863
864
        /** The prefix for long flags
865
         */
866
        std::string longPrefix;
867
868
        /** The separator for short flags
869
         */
870
        std::string shortSeparator;
871
872
        /** The separator for long flags
873
         */
874
        std::string longSeparator;
875
876
        /** The program name for help generation
877
         */
878
        std::string programName;
879
880
        /** Show command's flags
881
         */
882
        bool showCommandChildren = false;
883
884
        /** Show command's descriptions and epilog
885
         */
886
        bool showCommandFullHelp = false;
887
888
        /** The postfix for progline when showProglineOptions is true and command has any flags
889
         */
890
        std::string proglineOptions = "{OPTIONS}";
891
892
        /** The prefix for progline when command has any subcommands
893
         */
894
        std::string proglineCommand = "COMMAND";
895
896
        /** The prefix for progline value
897
         */
898
        std::string proglineValueOpen = " <";
899
900
        /** The postfix for progline value
901
         */
902
        std::string proglineValueClose = ">";
903
904
        /** The prefix for progline required argument
905
         */
906
        std::string proglineRequiredOpen = "";
907
908
        /** The postfix for progline required argument
909
         */
910
        std::string proglineRequiredClose = "";
911
912
        /** The prefix for progline non-required argument
913
         */
914
        std::string proglineNonrequiredOpen = "[";
915
916
        /** The postfix for progline non-required argument
917
         */
918
        std::string proglineNonrequiredClose = "]";
919
920
        /** Show flags in program line
921
         */
922
        bool proglineShowFlags = false;
923
924
        /** Use short flags in program lines when possible
925
         */
926
        bool proglinePreferShortFlags = false;
927
928
        /** Program line prefix
929
         */
930
        std::string usageString;
931
932
        /** String shown in help before flags descriptions
933
         */
934
        std::string optionsString = "OPTIONS:";
935
936
        /** Display value name after all the long and short flags
937
         */
938
        bool useValueNameOnce = false;
939
940
        /** Show value name
941
         */
942
        bool showValueName = true;
943
944
        /** Add newline before flag description
945
         */
946
        bool addNewlineBeforeDescription = false;
947
948
        /** The prefix for option value
949
         */
950
        std::string valueOpen = "[";
951
952
        /** The postfix for option value
953
         */
954
        std::string valueClose = "]";
955
956
        /** Add choices to argument description
957
         */
958
        bool addChoices = false;
959
960
        /** The prefix for choices
961
         */
962
        std::string choiceString = "\nOne of: ";
963
964
        /** Add default values to argument description
965
         */
966
        bool addDefault = false;
967
968
        /** The prefix for default values
969
         */
970
        std::string defaultString = "\nDefault: ";
971
    };
972
973
    /** A number of arguments which can be consumed by an option.
974
     *
975
     * Represents a closed interval [min, max].
976
     */
977
    struct Nargs
978
    {
979
        const size_t min;
980
        const size_t max;
981
982
        Nargs(size_t min_, size_t max_) : min{min_}, max{max_}
983
0
        {
984
0
#ifndef ARGS_NOEXCEPT
985
0
            if (max < min)
986
0
            {
987
0
                throw UsageError("Nargs: max < min");
988
0
            }
989
0
#endif
990
0
        }
991
992
28.0k
        Nargs(size_t num_) : min{num_}, max{num_}
993
28.0k
        {
994
28.0k
        }
995
996
        friend bool operator == (const Nargs &lhs, const Nargs &rhs)
997
0
        {
998
0
            return lhs.min == rhs.min && lhs.max == rhs.max;
999
0
        }
1000
1001
        friend bool operator != (const Nargs &lhs, const Nargs &rhs)
1002
0
        {
1003
0
            return !(lhs == rhs);
1004
0
        }
1005
    };
1006
1007
    /** Base class for all match types
1008
     */
1009
    class Base
1010
    {
1011
        private:
1012
            Options options = {};
1013
1014
        protected:
1015
            bool matched = false;
1016
            const std::string help;
1017
#ifdef ARGS_NOEXCEPT
1018
            /// Only for ARGS_NOEXCEPT
1019
            mutable Error error = Error::None;
1020
            mutable std::string errorMsg;
1021
#endif
1022
1023
        public:
1024
3.93k
            Base(const std::string &help_, Options options_ = {}) : options(options_), help(help_) {}
1025
3.93k
            virtual ~Base() {}
1026
1027
            Options GetOptions() const noexcept
1028
28.0k
            {
1029
28.0k
                return options;
1030
28.0k
            }
1031
1032
            bool IsRequired() const noexcept
1033
37
            {
1034
37
                return (GetOptions() & Options::Required) != Options::None;
1035
37
            }
1036
1037
            virtual bool Matched() const noexcept
1038
30.0k
            {
1039
30.0k
                return matched;
1040
30.0k
            }
1041
1042
            virtual void Validate(const std::string &, const std::string &) const
1043
0
            {
1044
0
            }
1045
1046
            operator bool() const noexcept
1047
0
            {
1048
0
                return Matched();
1049
0
            }
1050
1051
            virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &, const unsigned indentLevel) const
1052
0
            {
1053
0
                std::tuple<std::string, std::string, unsigned> description;
1054
0
                std::get<1>(description) = help;
1055
0
                std::get<2>(description) = indentLevel;
1056
0
                return { std::move(description) };
1057
0
            }
1058
1059
            virtual std::vector<Command*> GetCommands()
1060
2.94k
            {
1061
2.94k
                return {};
1062
2.94k
            }
1063
1064
            virtual bool IsGroup() const
1065
75
            {
1066
75
                return false;
1067
75
            }
1068
1069
            virtual bool IsFlag() const
1070
0
            {
1071
0
                return false;
1072
0
            }
1073
1074
            virtual FlagBase *Match(const EitherFlag &)
1075
0
            {
1076
0
                return nullptr;
1077
0
            }
1078
1079
            virtual PositionalBase *GetNextPositional()
1080
774
            {
1081
774
                return nullptr;
1082
774
            }
1083
1084
            virtual std::vector<FlagBase*> GetAllFlags()
1085
0
            {
1086
0
                return {};
1087
0
            }
1088
1089
            virtual bool HasFlag() const
1090
0
            {
1091
0
                return false;
1092
0
            }
1093
1094
            virtual bool HasPositional() const
1095
0
            {
1096
0
                return false;
1097
0
            }
1098
1099
            virtual bool HasCommand() const
1100
75
            {
1101
75
                return false;
1102
75
            }
1103
1104
            virtual std::vector<std::string> GetProgramLine(const HelpParams &) const
1105
0
            {
1106
0
                return {};
1107
0
            }
1108
1109
            /// Sets a kick-out value for building subparsers
1110
            void KickOut(bool kickout_) noexcept
1111
0
            {
1112
0
                if (kickout_)
1113
0
                {
1114
0
                    options = options | Options::KickOut;
1115
0
                }
1116
0
                else
1117
0
                {
1118
0
                    options = static_cast<Options>(static_cast<int>(options) & ~static_cast<int>(Options::KickOut));
1119
0
                }
1120
0
            }
1121
1122
            /// Gets the kick-out value for building subparsers
1123
            bool KickOut() const noexcept
1124
27.9k
            {
1125
27.9k
                return (options & Options::KickOut) != Options::None;
1126
27.9k
            }
1127
1128
            virtual void Reset() noexcept
1129
3.93k
            {
1130
3.93k
                matched = false;
1131
#ifdef ARGS_NOEXCEPT
1132
                error = Error::None;
1133
                errorMsg.clear();
1134
#endif
1135
3.93k
            }
1136
1137
#ifdef ARGS_NOEXCEPT
1138
            /// Only for ARGS_NOEXCEPT
1139
            virtual Error GetError() const
1140
            {
1141
                return error;
1142
            }
1143
1144
            /// Only for ARGS_NOEXCEPT
1145
            virtual std::string GetErrorMsg() const
1146
            {
1147
                return errorMsg;
1148
            }
1149
#endif
1150
    };
1151
1152
    /** Base class for all match types that have a name
1153
     */
1154
    class NamedBase : public Base
1155
    {
1156
        protected:
1157
            const std::string name;
1158
            bool kickout = false;
1159
            std::string defaultString;
1160
            bool defaultStringManual = false;
1161
            std::vector<std::string> choicesStrings;
1162
            bool choicesStringManual = false;
1163
1164
0
            virtual std::string GetDefaultString(const HelpParams&) const { return {}; }
1165
1166
0
            virtual std::vector<std::string> GetChoicesStrings(const HelpParams&) const { return {}; }
1167
1168
0
            virtual std::string GetNameString(const HelpParams&) const { return Name(); }
1169
1170
            void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const
1171
0
            {
1172
0
                if (isManual && !manual.empty())
1173
0
                {
1174
0
                    dest += str;
1175
0
                    dest += manual;
1176
0
                }
1177
0
                else if (!isManual && isGenerated && !generated.empty())
1178
0
                {
1179
0
                    dest += str;
1180
0
                    dest += generated;
1181
0
                }
1182
0
            }
1183
1184
        public:
1185
2.94k
            NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {}
1186
2.94k
            virtual ~NamedBase() {}
1187
1188
            /** Sets default value string that will be added to argument description.
1189
             *  Use empty string to disable it for this argument.
1190
             */
1191
            void HelpDefault(const std::string &str)
1192
0
            {
1193
0
                defaultStringManual = true;
1194
0
                defaultString = str;
1195
0
            }
1196
1197
            /** Gets default value string that will be added to argument description.
1198
             */
1199
            std::string HelpDefault(const HelpParams &params) const
1200
0
            {
1201
0
                return defaultStringManual ? defaultString : GetDefaultString(params);
1202
0
            }
1203
1204
            /** Sets choices strings that will be added to argument description.
1205
             *  Use empty vector to disable it for this argument.
1206
             */
1207
            void HelpChoices(const std::vector<std::string> &array)
1208
0
            {
1209
0
                choicesStringManual = true;
1210
0
                choicesStrings = array;
1211
0
            }
1212
1213
            /** Gets choices strings that will be added to argument description.
1214
             */
1215
            std::vector<std::string> HelpChoices(const HelpParams &params) const
1216
0
            {
1217
0
                return choicesStringManual ? choicesStrings : GetChoicesStrings(params);
1218
0
            }
1219
1220
            virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned indentLevel) const override
1221
0
            {
1222
0
                std::tuple<std::string, std::string, unsigned> description;
1223
0
                std::get<0>(description) = GetNameString(params);
1224
0
                std::get<1>(description) = help;
1225
0
                std::get<2>(description) = indentLevel;
1226
1227
0
                AddDescriptionPostfix(std::get<1>(description), choicesStringManual, detail::Join(choicesStrings, ", "), params.addChoices, detail::Join(GetChoicesStrings(params), ", "), params.choiceString);
1228
0
                AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString);
1229
1230
0
                return { std::move(description) };
1231
0
            }
1232
1233
            virtual std::string Name() const
1234
21
            {
1235
21
                return name;
1236
21
            }
1237
    };
1238
1239
    namespace detail
1240
    {
1241
        template<typename T>
1242
        using vector = std::vector<T, std::allocator<T>>;
1243
        
1244
        template<typename K, typename T>
1245
        using unordered_map = std::unordered_map<K, T, std::hash<K>, 
1246
            std::equal_to<K>, std::allocator<std::pair<const K, T> > >;
1247
1248
        template<typename S, typename T>
1249
        class is_streamable
1250
        {
1251
            template<typename SS, typename TT>
1252
            static auto test(int)
1253
            -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
1254
1255
            template<typename, typename>
1256
            static auto test(...) -> std::false_type;
1257
1258
        public:
1259
            using type = decltype(test<S,T>(0));
1260
        };
1261
1262
        template <typename T>
1263
        using IsConvertableToString = typename is_streamable<std::ostringstream, T>::type;
1264
1265
        template <typename T>
1266
        typename std::enable_if<IsConvertableToString<T>::value, std::string>::type
1267
        ToString(const T &value)
1268
        {
1269
            std::ostringstream s;
1270
            s << value;
1271
            return s.str();
1272
        }
1273
1274
        template <typename T>
1275
        typename std::enable_if<!IsConvertableToString<T>::value, std::string>::type
1276
        ToString(const T &)
1277
        {
1278
            return {};
1279
        }
1280
1281
        template <typename T>
1282
        std::vector<std::string> MapKeysToStrings(const T &map)
1283
        {
1284
            std::vector<std::string> res;
1285
            using K = typename std::decay<decltype(std::begin(map)->first)>::type;
1286
            if (IsConvertableToString<K>::value)
1287
            {
1288
                for (const auto &p : map)
1289
                {
1290
                    res.push_back(detail::ToString(p.first));
1291
                }
1292
1293
                std::sort(res.begin(), res.end());
1294
            }
1295
            return res;
1296
        }
1297
    }
1298
1299
    /** Base class for all flag options
1300
     */
1301
    class FlagBase : public NamedBase
1302
    {
1303
        protected:
1304
            const Matcher matcher;
1305
1306
            virtual std::string GetNameString(const HelpParams &params) const override
1307
0
            {
1308
0
                const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name();
1309
0
                std::string flags;
1310
0
                const auto flagStrings = matcher.GetFlagStrings();
1311
0
                const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce;
1312
0
                for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it)
1313
0
                {
1314
0
                    auto &flag = *it;
1315
0
                    if (it != flagStrings.begin())
1316
0
                    {
1317
0
                        flags += ", ";
1318
0
                    }
1319
1320
0
                    flags += flag.isShort ? params.shortPrefix : params.longPrefix;
1321
0
                    flags += flag.str();
1322
1323
0
                    if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end()))
1324
0
                    {
1325
0
                        flags += flag.isShort ? params.shortSeparator : params.longSeparator;
1326
0
                        flags += params.valueOpen + postfix + params.valueClose;
1327
0
                    }
1328
0
                }
1329
1330
0
                return flags;
1331
0
            }
1332
1333
        public:
1334
0
            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_)) {}
1335
1336
2.94k
            FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {}
1337
1338
2.94k
            virtual ~FlagBase() {}
1339
1340
            virtual bool IsFlag() const override
1341
2.94k
            {
1342
2.94k
                return true;
1343
2.94k
            }
1344
1345
            virtual FlagBase *Match(const EitherFlag &flag) override
1346
84.4k
            {
1347
84.4k
                if (matcher.Match(flag))
1348
28.0k
                {
1349
28.0k
                    if ((GetOptions() & Options::Single) != Options::None && matched)
1350
0
                    {
1351
0
                        std::ostringstream problem;
1352
0
                        problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once";
1353
#ifdef ARGS_NOEXCEPT
1354
                        error = Error::Extra;
1355
                        errorMsg = problem.str();
1356
#else
1357
0
                        throw ExtraError(problem.str());
1358
0
#endif
1359
0
                    }
1360
28.0k
                    matched = true;
1361
28.0k
                    return this;
1362
28.0k
                }
1363
56.4k
                return nullptr;
1364
84.4k
            }
1365
1366
            virtual std::vector<FlagBase*> GetAllFlags() override
1367
0
            {
1368
0
                return { this };
1369
0
            }
1370
1371
            const Matcher &GetMatcher() const
1372
5.89k
            {
1373
5.89k
                return matcher;
1374
5.89k
            }
1375
1376
            virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1377
75
            {
1378
75
                if (!Matched() && IsRequired())
1379
0
                {
1380
0
                        std::ostringstream problem;
1381
0
                        problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required";
1382
#ifdef ARGS_NOEXCEPT
1383
                        error = Error::Required;
1384
                        errorMsg = problem.str();
1385
#else
1386
0
                        throw RequiredError(problem.str());
1387
0
#endif
1388
0
                }
1389
75
            }
1390
1391
            virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1392
0
            {
1393
0
                if (!params.proglineShowFlags)
1394
0
                {
1395
0
                    return {};
1396
0
                }
1397
1398
0
                const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name();
1399
0
                const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny();
1400
0
                std::string res = flag.str(params.shortPrefix, params.longPrefix);
1401
0
                if (!postfix.empty())
1402
0
                {
1403
0
                    res += params.proglineValueOpen + postfix + params.proglineValueClose;
1404
0
                }
1405
1406
0
                return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose
1407
0
                                      : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose };
1408
0
            }
1409
1410
            virtual bool HasFlag() const override
1411
0
            {
1412
0
                return true;
1413
0
            }
1414
1415
#ifdef ARGS_NOEXCEPT
1416
            /// Only for ARGS_NOEXCEPT
1417
            bool usageError = false;
1418
            void SetUsageError()
1419
            {
1420
                usageError = true;
1421
            }
1422
            void ClearUsageError()
1423
            {
1424
                usageError = false;
1425
            }
1426
            virtual Error GetError() const override
1427
            {
1428
                if(usageError)
1429
                {
1430
                    return Error::Usage;
1431
                }
1432
                const auto nargs = NumberOfArguments();
1433
                if (nargs.min > nargs.max)
1434
                {
1435
                    return Error::Usage;
1436
                }
1437
1438
                const auto matcherError = matcher.GetError();
1439
                if (matcherError != Error::None)
1440
                {
1441
                    return matcherError;
1442
                }
1443
1444
                return error;
1445
            }
1446
#endif
1447
1448
            /** Defines how many values can be consumed by this option.
1449
             *
1450
             * \return closed interval [min, max]
1451
             */
1452
            virtual Nargs NumberOfArguments() const noexcept = 0;
1453
1454
            /** Parse values of this option.
1455
             *
1456
             * \param value Vector of values. It's size must be in NumberOfArguments() interval.
1457
             */
1458
            virtual void ParseValue(const std::vector<std::string> &value) = 0;
1459
    };
1460
1461
    /** Base class for value-accepting flag options
1462
     */
1463
    class ValueFlagBase : public FlagBase
1464
    {
1465
        public:
1466
0
            ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {}
1467
0
            ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {}
1468
0
            virtual ~ValueFlagBase() {}
1469
1470
            virtual Nargs NumberOfArguments() const noexcept override
1471
0
            {
1472
0
                return 1;
1473
0
            }
1474
    };
1475
1476
    class CompletionFlag : public ValueFlagBase
1477
    {
1478
        public:
1479
            std::vector<std::string> reply;
1480
            size_t cword = 0;
1481
            std::string syntax;
1482
1483
            template <typename GroupClass>
1484
            CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden)
1485
            {
1486
                group_.AddCompletion(*this);
1487
            }
1488
1489
0
            virtual ~CompletionFlag() {}
1490
1491
            virtual Nargs NumberOfArguments() const noexcept override
1492
0
            {
1493
0
                return 2;
1494
0
            }
1495
1496
            virtual void ParseValue(const std::vector<std::string> &value_) override
1497
0
            {
1498
0
                syntax = value_.at(0);
1499
0
                const std::string &raw = value_.at(1);
1500
0
                bool failed = false;
1501
0
1502
0
                const auto firstNonSpace = std::find_if_not(raw.begin(), raw.end(), [](char c)
1503
0
                {
1504
0
                    return std::isspace(static_cast<unsigned char>(c)) != 0;
1505
0
                });
1506
0
1507
0
                // Reject explicit signs: cword must be a plain non-negative
1508
0
                // decimal index. istringstream would otherwise silently
1509
0
                // accept "+1".
1510
0
                if (firstNonSpace != raw.end() && (*firstNonSpace == '-' || *firstNonSpace == '+'))
1511
0
                {
1512
0
                    failed = true;
1513
0
                }
1514
0
1515
0
                size_t parsed = 0;
1516
0
                if (!failed)
1517
0
                {
1518
0
                    std::istringstream ss(raw);
1519
0
                    // Use the C locale so that the cword index parses
1520
0
                    // consistently regardless of any std::locale::global call
1521
0
                    // elsewhere in the process. A locale with a non-empty
1522
0
                    // grouping facet would otherwise reject digit-only inputs
1523
0
                    // like "12" when grouping rules expect separators.
1524
0
                    ss.imbue(std::locale::classic());
1525
0
                    ss >> parsed;
1526
0
                    if (ss.fail())
1527
0
                    {
1528
0
                        failed = true;
1529
0
                    }
1530
0
                    else
1531
0
                    {
1532
0
                        char extra;
1533
0
                        if (ss >> extra)
1534
0
                        {
1535
0
                            failed = true;
1536
0
                        }
1537
0
                        else if (!ss.eof())
1538
0
                        {
1539
0
                            failed = true;
1540
0
                        }
1541
0
                    }
1542
0
                }
1543
0
1544
0
                if (failed)
1545
0
                {
1546
0
#ifdef ARGS_NOEXCEPT
1547
0
                    error = Error::Parse;
1548
0
                    errorMsg = "Argument 'completion' received invalid value type '" + raw + "'";
1549
0
#else
1550
0
                    std::ostringstream problem;
1551
0
                    problem << "Argument 'completion' received invalid value type '" << raw << "'";
1552
0
                    throw ParseError(problem.str());
1553
0
#endif
1554
0
                    return;
1555
0
                }
1556
0
1557
0
                cword = parsed;
1558
0
            }
1559
1560
            /** Get the completion reply
1561
             */
1562
            std::string Get() noexcept
1563
0
            {
1564
0
                return detail::Join(reply, "\n");
1565
0
            }
1566
1567
            virtual void Reset() noexcept override
1568
0
            {
1569
0
                ValueFlagBase::Reset();
1570
0
                cword = 0;
1571
0
                syntax.clear();
1572
0
                reply.clear();
1573
0
            }
1574
    };
1575
1576
1577
    /** Base class for positional options
1578
     */
1579
    class PositionalBase : public NamedBase
1580
    {
1581
        protected:
1582
            bool ready;
1583
1584
        public:
1585
0
            PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {}
1586
0
            virtual ~PositionalBase() {}
1587
1588
            bool Ready()
1589
0
            {
1590
0
                return ready;
1591
0
            }
1592
1593
            virtual void ParseValue(const std::string &value_) = 0;
1594
1595
            virtual void Reset() noexcept override
1596
0
            {
1597
0
                matched = false;
1598
0
                ready = true;
1599
0
#ifdef ARGS_NOEXCEPT
1600
0
                error = Error::None;
1601
0
                errorMsg.clear();
1602
0
#endif
1603
0
            }
1604
1605
            virtual PositionalBase *GetNextPositional() override
1606
0
            {
1607
0
                return Ready() ? this : nullptr;
1608
0
            }
1609
1610
            virtual bool HasPositional() const override
1611
0
            {
1612
0
                return true;
1613
0
            }
1614
1615
            virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1616
0
            {
1617
0
                return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose
1618
0
                                      : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose };
1619
0
            }
1620
1621
            virtual void Validate(const std::string &, const std::string &) const override
1622
0
            {
1623
0
                if (IsRequired() && !Matched())
1624
0
                {
1625
0
                    std::ostringstream problem;
1626
0
                    problem << "Option '" << Name() << "' is required";
1627
0
#ifdef ARGS_NOEXCEPT
1628
0
                    error = Error::Required;
1629
0
                    errorMsg = problem.str();
1630
0
#else
1631
0
                    throw RequiredError(problem.str());
1632
0
#endif
1633
0
                }
1634
0
            }
1635
    };
1636
1637
    /** Class for all kinds of validating groups, including ArgumentParser
1638
     */
1639
    class Group : public Base
1640
    {
1641
        private:
1642
            Group* parent;
1643
            std::vector<Base*> children;
1644
            std::function<bool(const Group &)> validator;
1645
1646
        public:
1647
            /** Default validators
1648
             */
1649
            struct Validators
1650
            {
1651
                static bool Xor(const Group &group)
1652
0
                {
1653
0
                    return group.MatchedChildren() == 1;
1654
0
                }
1655
1656
                static bool AtLeastOne(const Group &group)
1657
0
                {
1658
0
                    return group.MatchedChildren() >= 1;
1659
0
                }
1660
1661
                static bool AtMostOne(const Group &group)
1662
0
                {
1663
0
                    return group.MatchedChildren() <= 1;
1664
0
                }
1665
1666
                static bool All(const Group &group)
1667
0
                {
1668
0
                    return group.Children().size() == group.MatchedChildren();
1669
0
                }
1670
1671
                static bool AllOrNone(const Group &group)
1672
0
                {
1673
0
                    return (All(group) || None(group));
1674
0
                }
1675
1676
                static bool AllChildGroups(const Group &group)
1677
0
                {
1678
0
                    return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool {
1679
0
                            return child->IsGroup() && !child->Matched();
1680
0
                            });
1681
0
                }
1682
1683
                static bool DontCare(const Group &)
1684
0
                {
1685
0
                    return true;
1686
0
                }
1687
1688
                static bool CareTooMuch(const Group &)
1689
0
                {
1690
0
                    return false;
1691
0
                }
1692
1693
                static bool None(const Group &group)
1694
0
                {
1695
0
                    return group.MatchedChildren() == 0;
1696
0
                }
1697
            };
1698
            /// If help is empty, this group will not be printed in help output
1699
983
            Group(const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_)
1700
983
            {
1701
983
                parent = nullptr;
1702
983
            }
1703
            /// If help is empty, this group will not be printed in help output
1704
            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_)
1705
0
            {
1706
0
                group_.Add(*this);
1707
0
                parent = &group_;
1708
0
            }
1709
983
            virtual ~Group() {}
1710
1711
            /** Append a child to this Group.
1712
             */
1713
            void Add(Base &child)
1714
2.94k
            {
1715
2.94k
                children.emplace_back(&child);
1716
1717
2.94k
                if(child.IsFlag()) {
1718
2.94k
#ifndef ARGS_NOEXCEPT
1719
                    // Detection runs from the child's own constructor, so a
1720
                    // duplicate throws before that constructor completes and the
1721
                    // child's storage is released while the stack unwinds. Undo
1722
                    // the registration first, or a caller that catches the error
1723
                    // leaves this group holding a pointer to a dead object.
1724
2.94k
                    try
1725
2.94k
                    {
1726
2.94k
                        SignalDetectDuplicates();
1727
2.94k
                    }
1728
2.94k
                    catch (...)
1729
2.94k
                    {
1730
0
                        children.pop_back();
1731
0
                        throw;
1732
0
                    }
1733
#else
1734
                    SignalDetectDuplicates();
1735
#endif
1736
2.94k
                }
1737
2.94k
            }
1738
1739
            /** Get all this group's children
1740
             */
1741
            const std::vector<Base *> &Children() const
1742
33.9k
            {
1743
33.9k
                return children;
1744
33.9k
            }
1745
1746
            /** Return the first FlagBase that matches flag, or nullptr
1747
             *
1748
             * \param flag The flag with prefixes stripped
1749
             * \return the first matching FlagBase pointer, or nullptr if there is no match
1750
             */
1751
            virtual FlagBase *Match(const EitherFlag &flag) override
1752
28.6k
            {
1753
28.6k
                for (Base *child: Children())
1754
84.4k
                {
1755
84.4k
                    if (FlagBase *match = child->Match(flag))
1756
28.0k
                    {
1757
28.0k
                        return match;
1758
28.0k
                    }
1759
84.4k
                }
1760
662
                return nullptr;
1761
28.6k
            }
1762
1763
            virtual std::vector<FlagBase*> GetAllFlags() override
1764
0
            {
1765
0
                std::vector<FlagBase*> res;
1766
0
                for (Base *child: Children())
1767
0
                {
1768
0
                    auto childRes = child->GetAllFlags();
1769
0
                    res.insert(res.end(), childRes.begin(), childRes.end());
1770
0
                }
1771
0
                return res;
1772
0
            }
1773
1774
            virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1775
0
            {
1776
0
                for (Base *child: Children())
1777
0
                {
1778
0
                    child->Validate(shortPrefix, longPrefix);
1779
0
                }
1780
0
            }
1781
1782
            /** Get the next ready positional, or nullptr if there is none
1783
             *
1784
             * \return the first ready PositionalBase pointer, or nullptr if there is no match
1785
             */
1786
            virtual PositionalBase *GetNextPositional() override
1787
258
            {
1788
258
                for (Base *child: Children())
1789
774
                {
1790
774
                    if (auto next = child->GetNextPositional())
1791
0
                    {
1792
0
                        return next;
1793
0
                    }
1794
774
                }
1795
258
                return nullptr;
1796
258
            }
1797
1798
            /** Get whether this has any FlagBase children
1799
             *
1800
             * \return Whether or not there are any FlagBase children
1801
             */
1802
            virtual bool HasFlag() const override
1803
0
            {
1804
0
                return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); });
1805
0
            }
1806
1807
            /** Get whether this has any PositionalBase children
1808
             *
1809
             * \return Whether or not there are any PositionalBase children
1810
             */
1811
            virtual bool HasPositional() const override
1812
0
            {
1813
0
                return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); });
1814
0
            }
1815
1816
            /** Get whether this has any Command children
1817
             *
1818
             * \return Whether or not there are any Command children
1819
             */
1820
            virtual bool HasCommand() const override
1821
25
            {
1822
75
                return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); });
1823
25
            }
1824
1825
            /** Count the number of matched children this group has
1826
             */
1827
            std::vector<Base *>::size_type MatchedChildren() const
1828
0
            {
1829
0
                // Cast to avoid warnings from -Wsign-conversion
1830
0
                return static_cast<std::vector<Base *>::size_type>(
1831
0
                        std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();}));
1832
0
            }
1833
1834
            /** Get the list of children which were matched
1835
             */
1836
            std::vector<Base *> GetMatchedChildren() const
1837
0
            {
1838
0
                // Could be replaced by C++ 20 filter, or a custom iterator.
1839
0
                std::vector<Base*> matched_children;
1840
0
                std::copy_if(children.begin(), children.end(), std::back_inserter(matched_children), [](Base* b){
1841
0
                    return b->Matched();
1842
0
                });
1843
0
                return matched_children;
1844
0
            }
1845
1846
            /** Gets the children which are a certain type.
1847
              * \tparam ChildType The type of child to select. 
1848
              * \param matching Return only children of the type which matched (default false).
1849
              * \return Vector of children meeting the criteria.
1850
             */
1851
             template <typename ChildType>
1852
             std::vector<ChildType *> GetFilteredChildren(bool matching = false) const
1853
             {
1854
                std::vector<ChildType *> filtered_children;
1855
                for(Base *child : children) {
1856
                    if(!matching || child->Matched())
1857
                    {
1858
                        ChildType* cast_result = dynamic_cast<ChildType*>(child);
1859
                        if(cast_result != nullptr)
1860
                        {
1861
                            filtered_children.push_back(cast_result);
1862
                        }
1863
1864
                    }
1865
                }
1866
                return filtered_children;
1867
             }
1868
1869
            /** Whether or not this group matches validation
1870
             */
1871
            virtual bool Matched() const noexcept override
1872
0
            {
1873
0
                return validator(*this);
1874
0
            }
1875
1876
            /** Get validation
1877
             */
1878
            bool Get() const
1879
0
            {
1880
0
                return Matched();
1881
0
            }
1882
1883
            /** Get all the child descriptions for help generation
1884
             */
1885
            virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
1886
0
            {
1887
0
                std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions;
1888
1889
                // Push that group description on the back if not empty
1890
0
                unsigned addindent = 0;
1891
0
                if (!help.empty())
1892
0
                {
1893
0
                    descriptions.emplace_back(help, "", indent);
1894
0
                    addindent = 1;
1895
0
                }
1896
1897
0
                for (Base *child: Children())
1898
0
                {
1899
0
                    if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
1900
0
                    {
1901
0
                        continue;
1902
0
                    }
1903
1904
0
                    auto groupDescriptions = child->GetDescription(params, indent + addindent);
1905
0
                    descriptions.insert(
1906
0
                        std::end(descriptions),
1907
0
                        std::make_move_iterator(std::begin(groupDescriptions)),
1908
0
                        std::make_move_iterator(std::end(groupDescriptions)));
1909
0
                }
1910
0
                return descriptions;
1911
0
            }
1912
1913
            /** Get the names of positional parameters
1914
             */
1915
            virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1916
0
            {
1917
0
                std::vector <std::string> names;
1918
0
                for (Base *child: Children())
1919
0
                {
1920
0
                    if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None)
1921
0
                    {
1922
0
                        continue;
1923
0
                    }
1924
1925
0
                    auto groupNames = child->GetProgramLine(params);
1926
0
                    names.insert(
1927
0
                        std::end(names),
1928
0
                        std::make_move_iterator(std::begin(groupNames)),
1929
0
                        std::make_move_iterator(std::end(groupNames)));
1930
0
                }
1931
0
                return names;
1932
0
            }
1933
1934
            virtual std::vector<Command*> GetCommands() override
1935
983
            {
1936
983
                std::vector<Command*> res;
1937
983
                for (const auto &child : Children())
1938
2.94k
                {
1939
2.94k
                    auto subparsers = child->GetCommands();
1940
2.94k
                    res.insert(std::end(res), std::begin(subparsers), std::end(subparsers));
1941
2.94k
                }
1942
983
                return res;
1943
983
            }
1944
1945
            virtual bool IsGroup() const override
1946
0
            {
1947
0
                return true;
1948
0
            }
1949
1950
            virtual void Reset() noexcept override
1951
983
            {
1952
983
                Base::Reset();
1953
1954
983
                for (auto &child: Children())
1955
2.94k
                {
1956
2.94k
                    child->Reset();
1957
2.94k
                }
1958
#ifdef ARGS_NOEXCEPT
1959
                error = Error::None;
1960
                errorMsg.clear();
1961
#endif
1962
983
            }
1963
1964
            /** Sends a signal to the root of the tree to begin checking for
1965
              * duplicates. If this is the root, begins checking.
1966
              */
1967
            void SignalDetectDuplicates()
1968
2.94k
            {
1969
2.94k
                if(parent != nullptr) parent->SignalDetectDuplicates();
1970
2.94k
                else DetectDuplicateFlags();
1971
2.94k
            }
1972
1973
            /** Detect duplicate flags.
1974
              * In a noexcept context, sets an error on the duplicate flag.
1975
              * In a normal context, throws a ParseError.
1976
              */
1977
            void DetectDuplicateFlags()
1978
2.94k
            {
1979
2.94k
                std::unordered_set<char> usedShortFlags;
1980
2.94k
                std::unordered_set<std::string> usedLongFlags;
1981
2.94k
                DetectDuplicateFlags(usedShortFlags, usedLongFlags);
1982
2.94k
            }
1983
        
1984
            /** Used by parameterless DetectDuplicateFlags.
1985
              */
1986
            void DetectDuplicateFlags(std::unordered_set<char> &usedShortFlags, std::unordered_set<std::string> &usedLongFlags)
1987
2.94k
            {
1988
2.94k
                for (Base *child: Children())
1989
5.89k
                {
1990
5.89k
                    if(auto flag = dynamic_cast<FlagBase*>(child))
1991
5.89k
                    {
1992
                        // Check for duplicate flags, setting a usage error on the
1993
                        // flag if a duplicate is detected.
1994
5.89k
                        for(EitherFlag flagString: flag->GetMatcher().GetFlagStrings())
1995
11.7k
                        {
1996
11.7k
                            if(flagString.isShort)
1997
5.89k
                            {
1998
5.89k
                                if(usedShortFlags.count(flagString.shortFlag))
1999
0
                                {
2000
#ifdef ARGS_NOEXCEPT
2001
                                    flag->SetUsageError();
2002
#else
2003
0
                                    throw ParseError("duplicate short flag detected");
2004
0
#endif
2005
0
                                }
2006
5.89k
                                else
2007
5.89k
                                {
2008
5.89k
                                    usedShortFlags.insert(flagString.shortFlag);
2009
5.89k
                                }
2010
5.89k
                            }
2011
5.89k
                            else
2012
5.89k
                            {
2013
5.89k
                                if(usedLongFlags.count(flagString.longFlag))
2014
0
                                {
2015
#ifdef ARGS_NOEXCEPT
2016
                                    flag->SetUsageError();
2017
#else
2018
0
                                    throw ParseError("duplicate long flag detected");
2019
0
#endif
2020
0
                                }
2021
5.89k
                                else
2022
5.89k
                                {
2023
5.89k
                                    usedLongFlags.insert(flagString.longFlag);
2024
5.89k
                                }
2025
5.89k
                            }
2026
11.7k
                        }
2027
5.89k
                    }
2028
0
                    else if(auto group = dynamic_cast<Group*>(child))
2029
0
                    {
2030
                        // A command opens its own flag namespace and runs its
2031
                        // own duplicate detection as a separate root, so a flag
2032
                        // reused either side of a command boundary is not a
2033
                        // genuine duplicate. Only descend into plain groups
2034
                        // here; IsGroup() is false for a Command.
2035
0
                        if(group->IsGroup())
2036
0
                        {
2037
0
                            group->DetectDuplicateFlags(usedShortFlags, usedLongFlags);
2038
0
                        }
2039
0
                    }
2040
5.89k
                }
2041
2.94k
            }
2042
2043
#ifdef ARGS_NOEXCEPT
2044
            /// Only for ARGS_NOEXCEPT
2045
            virtual Error GetError() const override
2046
            {
2047
                if (error != Error::None)
2048
                {
2049
                    return error;
2050
                }
2051
2052
                auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
2053
                if (it == Children().end())
2054
                {
2055
                    return Error::None;
2056
                } else
2057
                {
2058
                    return (*it)->GetError();
2059
                }
2060
            }
2061
2062
            /// Only for ARGS_NOEXCEPT
2063
            virtual std::string GetErrorMsg() const override
2064
            {
2065
                if (error != Error::None)
2066
                {
2067
                    return errorMsg;
2068
                }
2069
2070
                auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
2071
                if (it == Children().end())
2072
                {
2073
                    return "";
2074
                } else
2075
                {
2076
                    return (*it)->GetErrorMsg();
2077
                }
2078
            }
2079
#endif
2080
2081
    };
2082
2083
    /** Class for using global options in ArgumentParser.
2084
     */
2085
    class GlobalOptions : public Group
2086
    {
2087
        public:
2088
            GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global)
2089
0
            {
2090
0
                Add(options_);
2091
0
            }
2092
    };
2093
2094
    /** Utility class for building subparsers with coroutines/callbacks.
2095
     *
2096
     * Brief example:
2097
     * \code
2098
     * Command command(argumentParser, "command", "my command", [](args::Subparser &s)
2099
     * {
2100
     *      // your command flags/positionals
2101
     *      s.Parse(); //required
2102
     *      //your command code
2103
     * });
2104
     * \endcode
2105
     *
2106
     * For ARGS_NOEXCEPT mode don't forget to check `s.GetError()` after `s.Parse()`
2107
     * and return if it isn't equals to args::Error::None.
2108
     *
2109
     * \sa Command
2110
     */
2111
    class Subparser : public Group
2112
    {
2113
        private:
2114
            std::vector<std::string> args;
2115
            std::vector<std::string> kicked;
2116
            ArgumentParser *parser = nullptr;
2117
            const HelpParams &helpParams;
2118
            const Command &command;
2119
            bool isParsed = false;
2120
2121
        public:
2122
            Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_)
2123
0
                : Group({}, Validators::AllChildGroups), args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_)
2124
0
            {
2125
0
            }
2126
2127
0
            Subparser(const Command &command_, const HelpParams &helpParams_) : Group({}, Validators::AllChildGroups), helpParams(helpParams_), command(command_)
2128
0
            {
2129
0
            }
2130
2131
            Subparser(const Subparser&) = delete;
2132
            Subparser(Subparser&&) = delete;
2133
            Subparser &operator = (const Subparser&) = delete;
2134
            Subparser &operator = (Subparser&&) = delete;
2135
2136
            const Command &GetCommand()
2137
0
            {
2138
0
                return command;
2139
0
            }
2140
2141
            /** (INTERNAL) Determines whether Parse was called or not.
2142
             */
2143
            bool IsParsed() const
2144
0
            {
2145
0
                return isParsed;
2146
0
            }
2147
2148
            /** Continue parsing arguments for new command.
2149
             */
2150
            void Parse();
2151
2152
            /** Returns a vector of kicked out arguments.
2153
             *
2154
             * \sa Base::KickOut
2155
             */
2156
            const std::vector<std::string> &KickedOut() const noexcept
2157
0
            {
2158
0
                return kicked;
2159
0
            }
2160
    };
2161
2162
    /** Main class for building subparsers.
2163
     *
2164
     * /sa Subparser
2165
     */
2166
    class Command : public Group
2167
    {
2168
        private:
2169
            friend class Subparser;
2170
2171
            std::string name;
2172
            std::string help;
2173
            std::string description;
2174
            std::string epilog;
2175
            std::string proglinePostfix;
2176
2177
            std::function<void(Subparser&)> parserCoroutine;
2178
            bool commandIsRequired = true;
2179
            Command *selectedCommand = nullptr;
2180
2181
            mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription;
2182
            mutable std::vector<std::string> subparserProgramLine;
2183
            mutable bool subparserHasFlag = false;
2184
            mutable bool subparserHasPositional = false;
2185
            mutable bool subparserHasCommand = false;
2186
#ifdef ARGS_NOEXCEPT
2187
            mutable Error subparserError = Error::None;
2188
#endif
2189
            mutable Subparser *subparser = nullptr;
2190
2191
        protected:
2192
2193
            class RaiiSubparser
2194
            {
2195
                public:
2196
                    RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_);
2197
                    RaiiSubparser(const Command &command_, const HelpParams &params_);
2198
2199
                    ~RaiiSubparser()
2200
0
                    {
2201
0
                        command.subparser = oldSubparser;
2202
0
                    }
2203
2204
                    Subparser &Parser()
2205
0
                    {
2206
0
                        return parser;
2207
0
                    }
2208
2209
                private:
2210
                    const Command &command;
2211
                    Subparser parser;
2212
                    Subparser *oldSubparser;
2213
            };
2214
2215
983
            Command() = default;
2216
2217
            std::function<void(Subparser&)> &GetCoroutine()
2218
0
            {
2219
0
                return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine;
2220
0
            }
2221
2222
            Command &SelectedCommand()
2223
0
            {
2224
0
                Command *res = this;
2225
0
                while (res->selectedCommand != nullptr)
2226
0
                {
2227
0
                    res = res->selectedCommand;
2228
0
                }
2229
2230
0
                return *res;
2231
0
            }
2232
2233
            const Command &SelectedCommand() const
2234
0
            {
2235
0
                const Command *res = this;
2236
0
                while (res->selectedCommand != nullptr)
2237
0
                {
2238
0
                    res = res->selectedCommand;
2239
0
                }
2240
0
2241
0
                return *res;
2242
0
            }
2243
2244
            void UpdateSubparserHelp(const HelpParams &params) const
2245
0
            {
2246
0
                if (parserCoroutine)
2247
0
                {
2248
0
                    RaiiSubparser coro(*this, params);
2249
0
#ifndef ARGS_NOEXCEPT
2250
0
                    try
2251
0
                    {
2252
0
                        parserCoroutine(coro.Parser());
2253
0
                    }
2254
0
                    catch (args::SubparserError&)
2255
0
                    {
2256
0
                    }
2257
#else
2258
                    parserCoroutine(coro.Parser());
2259
#endif
2260
0
                }
2261
0
            }
2262
2263
        public:
2264
            Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {})
2265
                : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_))
2266
0
            {
2267
0
                base_.Add(*this);
2268
0
            }
2269
2270
            /** The description that appears on the prog line after options
2271
             */
2272
            const std::string &ProglinePostfix() const
2273
0
            { return proglinePostfix; }
2274
2275
            /** The description that appears on the prog line after options
2276
             */
2277
            void ProglinePostfix(const std::string &proglinePostfix_)
2278
0
            { this->proglinePostfix = proglinePostfix_; }
2279
2280
            /** The description that appears above options
2281
             */
2282
            const std::string &Description() const
2283
0
            { return description; }
2284
            /** The description that appears above options
2285
             */
2286
2287
            void Description(const std::string &description_)
2288
983
            { this->description = description_; }
2289
2290
            /** The description that appears below options
2291
             */
2292
            const std::string &Epilog() const
2293
0
            { return epilog; }
2294
2295
            /** The description that appears below options
2296
             */
2297
            void Epilog(const std::string &epilog_)
2298
983
            { this->epilog = epilog_; }
2299
2300
            /** The name of command
2301
             */
2302
            const std::string &Name() const
2303
0
            { return name; }
2304
2305
            /** The description of command
2306
             */
2307
            const std::string &Help() const
2308
0
            { return help; }
2309
2310
            /** If value is true, parser will fail if no command was parsed.
2311
             *
2312
             * Default: true.
2313
             */
2314
            void RequireCommand(bool value)
2315
0
            { commandIsRequired = value; }
2316
2317
            virtual bool IsGroup() const override
2318
0
            { return false; }
2319
2320
            virtual bool Matched() const noexcept override
2321
29.9k
            { return Base::Matched(); }
2322
2323
            operator bool() const noexcept
2324
0
            { return Matched(); }
2325
2326
            void Match() noexcept
2327
0
            { matched = true; }
2328
2329
            void SelectCommand(Command *c) noexcept
2330
0
            {
2331
0
                selectedCommand = c;
2332
2333
0
                if (c != nullptr)
2334
0
                {
2335
0
                    c->Match();
2336
0
                }
2337
0
            }
2338
2339
            virtual FlagBase *Match(const EitherFlag &flag) override
2340
28.6k
            {
2341
28.6k
                if (selectedCommand != nullptr)
2342
0
                {
2343
0
                    if (auto *res = selectedCommand->Match(flag))
2344
0
                    {
2345
0
                        return res;
2346
0
                    }
2347
2348
0
                    for (auto *child: Children())
2349
0
                    {
2350
0
                        if ((child->GetOptions() & Options::Global) != Options::None)
2351
0
                        {
2352
0
                            if (auto *res = child->Match(flag))
2353
0
                            {
2354
0
                                return res;
2355
0
                            }
2356
0
                        }
2357
0
                    }
2358
2359
0
                    return nullptr;
2360
0
                }
2361
2362
28.6k
                if (subparser != nullptr)
2363
0
                {
2364
0
                    return subparser->Match(flag);
2365
0
                }
2366
2367
28.6k
                return Matched() ? Group::Match(flag) : nullptr;
2368
28.6k
            }
2369
2370
            virtual std::vector<FlagBase*> GetAllFlags() override
2371
0
            {
2372
0
                std::vector<FlagBase*> res;
2373
2374
0
                if (!Matched())
2375
0
                {
2376
0
                    return res;
2377
0
                }
2378
2379
0
                for (auto *child: Children())
2380
0
                {
2381
0
                    if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None)
2382
0
                    {
2383
0
                        auto childFlags = child->GetAllFlags();
2384
0
                        res.insert(res.end(), childFlags.begin(), childFlags.end());
2385
0
                    }
2386
0
                }
2387
2388
0
                if (selectedCommand != nullptr)
2389
0
                {
2390
0
                    auto childFlags = selectedCommand->GetAllFlags();
2391
0
                    res.insert(res.end(), childFlags.begin(), childFlags.end());
2392
0
                }
2393
2394
0
                if (subparser != nullptr)
2395
0
                {
2396
0
                    auto childFlags = subparser->GetAllFlags();
2397
0
                    res.insert(res.end(), childFlags.begin(), childFlags.end());
2398
0
                }
2399
2400
0
                return res;
2401
0
            }
2402
2403
            virtual PositionalBase *GetNextPositional() override
2404
258
            {
2405
258
                if (selectedCommand != nullptr)
2406
0
                {
2407
0
                    if (auto *res = selectedCommand->GetNextPositional())
2408
0
                    {
2409
0
                        return res;
2410
0
                    }
2411
2412
0
                    for (auto *child: Children())
2413
0
                    {
2414
0
                        if ((child->GetOptions() & Options::Global) != Options::None)
2415
0
                        {
2416
0
                            if (auto *res = child->GetNextPositional())
2417
0
                            {
2418
0
                                return res;
2419
0
                            }
2420
0
                        }
2421
0
                    }
2422
2423
0
                    return nullptr;
2424
0
                }
2425
2426
258
                if (subparser != nullptr)
2427
0
                {
2428
0
                    return subparser->GetNextPositional();
2429
0
                }
2430
2431
258
                return Matched() ? Group::GetNextPositional() : nullptr;
2432
258
            }
2433
2434
            virtual bool HasFlag() const override
2435
0
            {
2436
0
                return subparserHasFlag || Group::HasFlag();
2437
0
            }
2438
2439
            virtual bool HasPositional() const override
2440
0
            {
2441
0
                return subparserHasPositional || Group::HasPositional();
2442
0
            }
2443
2444
            virtual bool HasCommand() const override
2445
0
            {
2446
0
                return true;
2447
0
            }
2448
2449
            std::vector<std::string> GetCommandProgramLine(const HelpParams &params) const
2450
0
            {
2451
0
                UpdateSubparserHelp(params);
2452
2453
0
                std::vector<std::string> res;
2454
2455
0
                if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags)
2456
0
                {
2457
0
                    res.push_back(params.proglineOptions);
2458
0
                }
2459
2460
0
                auto group_res = Group::GetProgramLine(params);
2461
0
                std::move(std::move(group_res).begin(), std::move(group_res).end(), std::back_inserter(res));
2462
2463
0
                res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end());
2464
2465
0
                if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand))
2466
0
                {
2467
0
                    res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]");
2468
0
                }
2469
2470
0
                if (!Name().empty())
2471
0
                {
2472
0
                    res.insert(res.begin(), Name());
2473
0
                }
2474
2475
0
                if (!ProglinePostfix().empty())
2476
0
                {
2477
0
                    std::string line;
2478
0
                    for (auto c : ProglinePostfix())
2479
0
                    {
2480
0
                        if (std::isspace(static_cast<unsigned char>(c)))
2481
0
                        {
2482
0
                            if (!line.empty())
2483
0
                            {
2484
0
                                res.push_back(line);
2485
0
                                line.clear();
2486
0
                            }
2487
2488
0
                            if (c == '\n')
2489
0
                            {
2490
0
                                res.push_back("\n");
2491
0
                            }
2492
0
                        }
2493
0
                        else
2494
0
                        {
2495
0
                            line += c;
2496
0
                        }
2497
0
                    }
2498
2499
0
                    if (!line.empty())
2500
0
                    {
2501
0
                        res.push_back(line);
2502
0
                    }
2503
0
                }
2504
2505
0
                return res;
2506
0
            }
2507
2508
            virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
2509
0
            {
2510
0
                if (!Matched())
2511
0
                {
2512
0
                    return {};
2513
0
                }
2514
2515
0
                return GetCommandProgramLine(params);
2516
0
            }
2517
2518
            virtual std::vector<Command*> GetCommands() override
2519
983
            {
2520
983
                if (selectedCommand != nullptr)
2521
0
                {
2522
0
                    return selectedCommand->GetCommands();
2523
0
                }
2524
2525
983
                if (Matched())
2526
983
                {
2527
983
                    return Group::GetCommands();
2528
983
                }
2529
2530
0
                return { this };
2531
983
            }
2532
2533
            virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
2534
0
            {
2535
0
                std::vector<std::tuple<std::string, std::string, unsigned>> descriptions;
2536
0
                unsigned addindent = 0;
2537
2538
0
                UpdateSubparserHelp(params);
2539
2540
0
                if (!Matched())
2541
0
                {
2542
0
                    if (params.showCommandFullHelp)
2543
0
                    {
2544
0
                        std::ostringstream s;
2545
0
                        bool empty = true;
2546
0
                        for (const auto &progline: GetCommandProgramLine(params))
2547
0
                        {
2548
0
                            if (!empty)
2549
0
                            {
2550
0
                                s << ' ';
2551
0
                            }
2552
0
                            else
2553
0
                            {
2554
0
                                empty = false;
2555
0
                            }
2556
2557
0
                            s << progline;
2558
0
                        }
2559
2560
0
                        descriptions.emplace_back(s.str(), "", indent);
2561
0
                    }
2562
0
                    else
2563
0
                    {
2564
0
                        descriptions.emplace_back(Name(), help, indent);
2565
0
                    }
2566
2567
0
                    if (!params.showCommandChildren && !params.showCommandFullHelp)
2568
0
                    {
2569
0
                        return descriptions;
2570
0
                    }
2571
2572
0
                    addindent = 1;
2573
0
                }
2574
2575
0
                if (params.showCommandFullHelp && !Matched())
2576
0
                {
2577
0
                    descriptions.emplace_back("", "", indent + addindent);
2578
0
                    descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent);
2579
0
                    descriptions.emplace_back("", "", indent + addindent);
2580
0
                }
2581
2582
0
                for (Base *child: Children())
2583
0
                {
2584
0
                    if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
2585
0
                    {
2586
0
                        continue;
2587
0
                    }
2588
2589
0
                    auto groupDescriptions = child->GetDescription(params, indent + addindent);
2590
0
                    descriptions.insert(
2591
0
                                        std::end(descriptions),
2592
0
                                        std::make_move_iterator(std::begin(groupDescriptions)),
2593
0
                                        std::make_move_iterator(std::end(groupDescriptions)));
2594
0
                }
2595
2596
0
                for (auto childDescription: subparserDescription)
2597
0
                {
2598
0
                    std::get<2>(childDescription) += indent + addindent;
2599
0
                    descriptions.push_back(std::move(childDescription));
2600
0
                }
2601
2602
0
                if (params.showCommandFullHelp && !Matched())
2603
0
                {
2604
0
                    descriptions.emplace_back("", "", indent + addindent);
2605
0
                    if (!Epilog().empty())
2606
0
                    {
2607
0
                        descriptions.emplace_back(Epilog(), "", indent + addindent);
2608
0
                        descriptions.emplace_back("", "", indent + addindent);
2609
0
                    }
2610
0
                }
2611
2612
0
                return descriptions;
2613
0
            }
2614
2615
            virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override
2616
25
            {
2617
25
                if (!Matched())
2618
0
                {
2619
0
                    return;
2620
0
                }
2621
2622
25
                auto onValidationError = [&]
2623
25
                {
2624
0
                    std::ostringstream problem;
2625
0
                    problem << "Group validation failed somewhere!";
2626
#ifdef ARGS_NOEXCEPT
2627
                    error = Error::Validation;
2628
                    errorMsg = problem.str();
2629
#else
2630
0
                    throw ValidationError(problem.str());
2631
0
#endif
2632
0
                };
2633
2634
25
                for (Base *child: Children())
2635
75
                {
2636
75
                    if (child->IsGroup() && !child->Matched())
2637
0
                    {
2638
0
                        onValidationError();
2639
0
                    }
2640
2641
75
                    child->Validate(shortprefix, longprefix);
2642
75
                }
2643
2644
25
                if (subparser != nullptr)
2645
0
                {
2646
0
                    subparser->Validate(shortprefix, longprefix);
2647
0
                    if (!subparser->Matched())
2648
0
                    {
2649
0
                        onValidationError();
2650
0
                    }
2651
0
                }
2652
2653
25
                if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand))
2654
0
                {
2655
0
                    std::ostringstream problem;
2656
0
                    problem << "Command is required";
2657
#ifdef ARGS_NOEXCEPT
2658
                    error = Error::Validation;
2659
                    errorMsg = problem.str();
2660
#else
2661
0
                    throw ValidationError(problem.str());
2662
0
#endif
2663
0
                }
2664
25
            }
2665
2666
            virtual void Reset() noexcept override
2667
983
            {
2668
983
                Group::Reset();
2669
983
                selectedCommand = nullptr;
2670
983
                subparserProgramLine.clear();
2671
983
                subparserDescription.clear();
2672
983
                subparserHasFlag = false;
2673
983
                subparserHasPositional = false;
2674
983
                subparserHasCommand = false;
2675
#ifdef ARGS_NOEXCEPT
2676
                subparserError = Error::None;
2677
#endif
2678
983
            }
2679
2680
#ifdef ARGS_NOEXCEPT
2681
            /// Only for ARGS_NOEXCEPT
2682
            virtual Error GetError() const override
2683
            {
2684
                if (!Matched())
2685
                {
2686
                    return Error::None;
2687
                }
2688
2689
                if (error != Error::None)
2690
                {
2691
                    return error;
2692
                }
2693
2694
                if (subparserError != Error::None)
2695
                {
2696
                    return subparserError;
2697
                }
2698
2699
                return Group::GetError();
2700
            }
2701
#endif
2702
    };
2703
2704
    /** The main user facing command line argument parser class
2705
     */
2706
    class ArgumentParser : public Command
2707
    {
2708
        friend class Subparser;
2709
2710
        private:
2711
            std::string longprefix;
2712
            std::string shortprefix;
2713
2714
            std::string longseparator;
2715
2716
            std::string terminator;
2717
2718
            bool allowJoinedShortValue = true;
2719
            bool allowJoinedLongValue = true;
2720
            bool allowSeparateShortValue = true;
2721
            bool allowSeparateLongValue = true;
2722
2723
            bool readCompletion = false;
2724
            CompletionFlag *completion = nullptr;
2725
2726
        protected:
2727
            enum class OptionType
2728
            {
2729
                LongFlag,
2730
                ShortFlag,
2731
                Positional
2732
            };
2733
2734
            OptionType ParseOption(const std::string &s, bool allowEmpty = false)
2735
2.26k
            {
2736
2.26k
                const bool matchesLong = s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length());
2737
2.26k
                const bool matchesShort = s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length());
2738
2739
                // A chunk can start with both prefixes when one is a prefix of
2740
                // the other, or when the long prefix is empty (every string
2741
                // starts with it). Resolve to the longer, more specific prefix:
2742
                // this keeps the default "--"/"-" preference for long flags
2743
                // while letting a short flag be recognised under an empty long
2744
                // prefix instead of being swallowed as a nameless long flag.
2745
2.26k
                if (matchesLong && matchesShort)
2746
514
                {
2747
514
                    return longprefix.length() >= shortprefix.length() ? OptionType::LongFlag : OptionType::ShortFlag;
2748
514
                }
2749
2750
1.75k
                if (matchesLong)
2751
0
                {
2752
0
                    return OptionType::LongFlag;
2753
0
                }
2754
2755
1.75k
                if (matchesShort)
2756
1.24k
                {
2757
1.24k
                    return OptionType::ShortFlag;
2758
1.24k
                }
2759
2760
512
                return OptionType::Positional;
2761
1.75k
            }
2762
2763
            template <typename It>
2764
            bool Complete(FlagBase &flag, It it, It end)
2765
0
            {
2766
0
                auto nextIt = it;
2767
0
                if (!readCompletion || (++nextIt != end))
2768
0
                {
2769
0
                    return false;
2770
0
                }
2771
2772
0
                const auto &chunk = *it;
2773
0
                for (auto &choice : flag.HelpChoices(helpParams))
2774
0
                {
2775
0
                    AddCompletionReply(chunk, choice);
2776
0
                }
2777
2778
0
#ifndef ARGS_NOEXCEPT
2779
0
                throw Completion(completion->Get());
2780
#else
2781
                return true;
2782
#endif
2783
0
            }
Unexecuted instantiation: bool args::ArgumentParser::Complete<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(args::FlagBase&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>)
Unexecuted instantiation: bool args::ArgumentParser::Complete<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(args::FlagBase&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>)
2784
2785
            /** (INTERNAL) Parse flag's values
2786
             *
2787
             * \param arg The string to display in error message as a flag name
2788
             * \param[in, out] it The iterator to first value. It will point to the last value
2789
             * \param end The end iterator
2790
             * \param joinedArg Joined value (e.g. bar in --foo=bar)
2791
             * \param canDiscardJoined If true joined value can be parsed as flag not as a value (as in -abcd)
2792
             * \param[out] values The vector to store parsed arg's values
2793
             */
2794
            template <typename It>
2795
            std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end,
2796
                                        const bool allowSeparate, const bool allowJoined,
2797
                                        const bool hasJoined, const std::string &joinedArg,
2798
                                        const bool canDiscardJoined, std::vector<std::string> &values)
2799
28.0k
            {
2800
28.0k
                values.clear();
2801
2802
28.0k
                Nargs nargs = flag.NumberOfArguments();
2803
2804
28.0k
                if (hasJoined && !allowJoined && (nargs.min != 0 || !canDiscardJoined))
2805
0
                {
2806
0
                    return "Flag '" + arg + "' was passed a joined argument, but these are disallowed";
2807
0
                }
2808
2809
28.0k
                if (hasJoined)
2810
27.5k
                {
2811
27.5k
                    if (!canDiscardJoined || (allowJoined && nargs.max != 0))
2812
17
                    {
2813
17
                        values.push_back(joinedArg);
2814
17
                    }
2815
27.5k
                } else if (!allowSeparate)
2816
0
                {
2817
0
                    if (nargs.min != 0)
2818
0
                    {
2819
0
                        return "Flag '" + arg + "' was passed a separate argument, but these are disallowed";
2820
0
                    }
2821
0
                }
2822
2823
                // Only gather separate values when they are allowed. A joined
2824
                // value that was discarded rather than taken (short chunks such
2825
                // as -nf when joined short values are off) means this flag isn't
2826
                // taking an argument here, so the rest of the chunk is flags.
2827
28.0k
                if (allowSeparate && (!hasJoined || !values.empty()))
2828
454
                {
2829
454
                    auto valueIt = it;
2830
454
                    ++valueIt;
2831
2832
454
                    while (valueIt != end &&
2833
423
                           *valueIt != terminator &&
2834
422
                           values.size() < nargs.max &&
2835
0
                           (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional))
2836
0
                    {
2837
0
                        if (Complete(flag, valueIt, end))
2838
0
                        {
2839
                            // Park `it` on the completion position rather than
2840
                            // `end`. In ARGS_NOEXCEPT mode Complete returns
2841
                            // true (no throw), so the caller's for-loop will
2842
                            // run its ++it after we return; advancing an
2843
                            // already-end iterator is undefined behavior and
2844
                            // causes a subsequent out-of-bounds read of the
2845
                            // arg vector. Since Complete only fires when
2846
                            // ++nextIt == end, valueIt is the last element,
2847
                            // and ++(it=valueIt) safely lands on end.
2848
0
                            it = valueIt;
2849
0
                            return "";
2850
0
                        }
2851
2852
0
                        values.push_back(*valueIt);
2853
0
                        ++it;
2854
0
                        ++valueIt;
2855
0
                    }
2856
454
                }
2857
2858
28.0k
                if (values.size() > nargs.max)
2859
17
                {
2860
17
                    return "Passed an argument into a non-argument flag: " + arg;
2861
27.9k
                } else if (values.size() < nargs.min)
2862
0
                {
2863
0
                    if (nargs.min == 1 && nargs.max == 1)
2864
0
                    {
2865
0
                        return "Flag '" + arg + "' requires an argument but received none";
2866
0
                    } else if (nargs.min == 1)
2867
0
                    {
2868
0
                        return "Flag '" + arg + "' requires at least one argument but received none";
2869
0
                    } else if (nargs.min != nargs.max)
2870
0
                    {
2871
0
                        return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) +
2872
0
                               " arguments but received " + std::to_string(values.size());
2873
0
                    } else
2874
0
                    {
2875
0
                        return "Flag '" + arg + "' requires " + std::to_string(nargs.min) +
2876
0
                               " arguments but received " + std::to_string(values.size());
2877
0
                    }
2878
0
                }
2879
2880
27.9k
                return {};
2881
28.0k
            }
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > args::ArgumentParser::ParseArgsValues<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(args::FlagBase&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, bool, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&)
Line
Count
Source
2799
28.0k
            {
2800
28.0k
                values.clear();
2801
2802
28.0k
                Nargs nargs = flag.NumberOfArguments();
2803
2804
28.0k
                if (hasJoined && !allowJoined && (nargs.min != 0 || !canDiscardJoined))
2805
0
                {
2806
0
                    return "Flag '" + arg + "' was passed a joined argument, but these are disallowed";
2807
0
                }
2808
2809
28.0k
                if (hasJoined)
2810
27.5k
                {
2811
27.5k
                    if (!canDiscardJoined || (allowJoined && nargs.max != 0))
2812
17
                    {
2813
17
                        values.push_back(joinedArg);
2814
17
                    }
2815
27.5k
                } else if (!allowSeparate)
2816
0
                {
2817
0
                    if (nargs.min != 0)
2818
0
                    {
2819
0
                        return "Flag '" + arg + "' was passed a separate argument, but these are disallowed";
2820
0
                    }
2821
0
                }
2822
2823
                // Only gather separate values when they are allowed. A joined
2824
                // value that was discarded rather than taken (short chunks such
2825
                // as -nf when joined short values are off) means this flag isn't
2826
                // taking an argument here, so the rest of the chunk is flags.
2827
28.0k
                if (allowSeparate && (!hasJoined || !values.empty()))
2828
454
                {
2829
454
                    auto valueIt = it;
2830
454
                    ++valueIt;
2831
2832
454
                    while (valueIt != end &&
2833
423
                           *valueIt != terminator &&
2834
422
                           values.size() < nargs.max &&
2835
0
                           (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional))
2836
0
                    {
2837
0
                        if (Complete(flag, valueIt, end))
2838
0
                        {
2839
                            // Park `it` on the completion position rather than
2840
                            // `end`. In ARGS_NOEXCEPT mode Complete returns
2841
                            // true (no throw), so the caller's for-loop will
2842
                            // run its ++it after we return; advancing an
2843
                            // already-end iterator is undefined behavior and
2844
                            // causes a subsequent out-of-bounds read of the
2845
                            // arg vector. Since Complete only fires when
2846
                            // ++nextIt == end, valueIt is the last element,
2847
                            // and ++(it=valueIt) safely lands on end.
2848
0
                            it = valueIt;
2849
0
                            return "";
2850
0
                        }
2851
2852
0
                        values.push_back(*valueIt);
2853
0
                        ++it;
2854
0
                        ++valueIt;
2855
0
                    }
2856
454
                }
2857
2858
28.0k
                if (values.size() > nargs.max)
2859
17
                {
2860
17
                    return "Passed an argument into a non-argument flag: " + arg;
2861
27.9k
                } else if (values.size() < nargs.min)
2862
0
                {
2863
0
                    if (nargs.min == 1 && nargs.max == 1)
2864
0
                    {
2865
0
                        return "Flag '" + arg + "' requires an argument but received none";
2866
0
                    } else if (nargs.min == 1)
2867
0
                    {
2868
0
                        return "Flag '" + arg + "' requires at least one argument but received none";
2869
0
                    } else if (nargs.min != nargs.max)
2870
0
                    {
2871
0
                        return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) +
2872
0
                               " arguments but received " + std::to_string(values.size());
2873
0
                    } else
2874
0
                    {
2875
0
                        return "Flag '" + arg + "' requires " + std::to_string(nargs.min) +
2876
0
                               " arguments but received " + std::to_string(values.size());
2877
0
                    }
2878
0
                }
2879
2880
27.9k
                return {};
2881
28.0k
            }
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > args::ArgumentParser::ParseArgsValues<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(args::FlagBase&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, bool, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&)
2882
2883
            template <typename It>
2884
            bool ParseLong(It &it, It end)
2885
514
            {
2886
514
                const auto &chunk = *it;
2887
514
                const auto argchunk = chunk.substr(longprefix.size());
2888
                // Try to separate it, in case of a separator:
2889
514
                const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator);
2890
                // If the separator is in the argument, separate it.
2891
514
                const auto arg = (separator != argchunk.npos ?
2892
241
                    std::string(argchunk, 0, separator)
2893
514
                    : argchunk);
2894
514
                const auto joined = (separator != argchunk.npos ?
2895
241
                    argchunk.substr(separator + longseparator.size())
2896
514
                    : std::string());
2897
2898
514
                if (auto flag = Match(arg))
2899
95
                {
2900
#ifdef ARGS_NOEXCEPT
2901
                    // Match() may set the flag's error (e.g. Error::Extra when
2902
                    // Options::Single is violated). In non-noexcept mode that
2903
                    // path throws and parsing stops before the value is read;
2904
                    // in noexcept mode we must mirror that and skip the value
2905
                    // parsing so the previously-stored value is preserved.
2906
                    if (flag->GetError() != Error::None)
2907
                    {
2908
                        return false;
2909
                    }
2910
#endif
2911
95
                    std::vector<std::string> values;
2912
95
                    const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue,
2913
95
                                                                     separator != argchunk.npos, joined, false, values);
2914
95
                    if (!errorMessage.empty())
2915
17
                    {
2916
17
#ifndef ARGS_NOEXCEPT
2917
17
                        throw ParseError(errorMessage);
2918
#else
2919
                        error = Error::Parse;
2920
                        errorMsg = errorMessage;
2921
                        return false;
2922
#endif
2923
17
                    }
2924
2925
78
                    if (!readCompletion)
2926
78
                    {
2927
78
                        flag->ParseValue(values);
2928
#ifdef ARGS_NOEXCEPT
2929
                        // Non-noexcept ParseValue paths throw on Help, reader
2930
                        // failure, or Map miss, which halts parsing. Mirror
2931
                        // that here so a later parser-level error (e.g. an
2932
                        // unknown flag) cannot shadow the flag's error in
2933
                        // ArgumentParser::GetError().
2934
                        if (flag->GetError() != Error::None)
2935
                        {
2936
                            return false;
2937
                        }
2938
#endif
2939
78
                    }
2940
2941
78
                    if (flag->KickOut())
2942
0
                    {
2943
0
                        ++it;
2944
0
                        return false;
2945
0
                    }
2946
78
                } else
2947
419
                {
2948
419
                    const std::string errorMessage("Flag could not be matched: " + arg);
2949
419
#ifndef ARGS_NOEXCEPT
2950
419
                    throw ParseError(errorMessage);
2951
#else
2952
                    error = Error::Parse;
2953
                    errorMsg = errorMessage;
2954
                    return false;
2955
#endif
2956
419
                }
2957
2958
78
                return true;
2959
514
            }
bool args::ArgumentParser::ParseLong<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>)
Line
Count
Source
2885
514
            {
2886
514
                const auto &chunk = *it;
2887
514
                const auto argchunk = chunk.substr(longprefix.size());
2888
                // Try to separate it, in case of a separator:
2889
514
                const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator);
2890
                // If the separator is in the argument, separate it.
2891
514
                const auto arg = (separator != argchunk.npos ?
2892
241
                    std::string(argchunk, 0, separator)
2893
514
                    : argchunk);
2894
514
                const auto joined = (separator != argchunk.npos ?
2895
241
                    argchunk.substr(separator + longseparator.size())
2896
514
                    : std::string());
2897
2898
514
                if (auto flag = Match(arg))
2899
95
                {
2900
#ifdef ARGS_NOEXCEPT
2901
                    // Match() may set the flag's error (e.g. Error::Extra when
2902
                    // Options::Single is violated). In non-noexcept mode that
2903
                    // path throws and parsing stops before the value is read;
2904
                    // in noexcept mode we must mirror that and skip the value
2905
                    // parsing so the previously-stored value is preserved.
2906
                    if (flag->GetError() != Error::None)
2907
                    {
2908
                        return false;
2909
                    }
2910
#endif
2911
95
                    std::vector<std::string> values;
2912
95
                    const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue,
2913
95
                                                                     separator != argchunk.npos, joined, false, values);
2914
95
                    if (!errorMessage.empty())
2915
17
                    {
2916
17
#ifndef ARGS_NOEXCEPT
2917
17
                        throw ParseError(errorMessage);
2918
#else
2919
                        error = Error::Parse;
2920
                        errorMsg = errorMessage;
2921
                        return false;
2922
#endif
2923
17
                    }
2924
2925
78
                    if (!readCompletion)
2926
78
                    {
2927
78
                        flag->ParseValue(values);
2928
#ifdef ARGS_NOEXCEPT
2929
                        // Non-noexcept ParseValue paths throw on Help, reader
2930
                        // failure, or Map miss, which halts parsing. Mirror
2931
                        // that here so a later parser-level error (e.g. an
2932
                        // unknown flag) cannot shadow the flag's error in
2933
                        // ArgumentParser::GetError().
2934
                        if (flag->GetError() != Error::None)
2935
                        {
2936
                            return false;
2937
                        }
2938
#endif
2939
78
                    }
2940
2941
78
                    if (flag->KickOut())
2942
0
                    {
2943
0
                        ++it;
2944
0
                        return false;
2945
0
                    }
2946
78
                } else
2947
419
                {
2948
419
                    const std::string errorMessage("Flag could not be matched: " + arg);
2949
419
#ifndef ARGS_NOEXCEPT
2950
419
                    throw ParseError(errorMessage);
2951
#else
2952
                    error = Error::Parse;
2953
                    errorMsg = errorMessage;
2954
                    return false;
2955
#endif
2956
419
                }
2957
2958
78
                return true;
2959
514
            }
Unexecuted instantiation: bool args::ArgumentParser::ParseLong<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>)
2960
2961
            template <typename It>
2962
            bool ParseShort(It &it, It end)
2963
621
            {
2964
621
                const auto &chunk = *it;
2965
621
                const auto argchunk = chunk.substr(shortprefix.size());
2966
28.5k
                for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit)
2967
28.1k
                {
2968
28.1k
                    const auto arg = *argit;
2969
2970
28.1k
                    if (auto flag = Match(arg))
2971
27.9k
                    {
2972
#ifdef ARGS_NOEXCEPT
2973
                        // See ParseLong: if Match recorded an error
2974
                        // (e.g. Options::Single violation), bail before the
2975
                        // value is parsed so the prior value is preserved.
2976
                        if (flag->GetError() != Error::None)
2977
                        {
2978
                            return false;
2979
                        }
2980
#endif
2981
27.9k
                        const std::string value(argit + 1, std::end(argchunk));
2982
27.9k
                        std::vector<std::string> values;
2983
27.9k
                        const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end,
2984
27.9k
                                                                         allowSeparateShortValue, allowJoinedShortValue,
2985
27.9k
                                                                         !value.empty(), value, !value.empty(), values);
2986
2987
27.9k
                        if (!errorMessage.empty())
2988
0
                        {
2989
0
#ifndef ARGS_NOEXCEPT
2990
0
                            throw ParseError(errorMessage);
2991
#else
2992
                            error = Error::Parse;
2993
                            errorMsg = errorMessage;
2994
                            return false;
2995
#endif
2996
0
                        }
2997
2998
27.9k
                        if (!readCompletion)
2999
27.9k
                        {
3000
27.9k
                            flag->ParseValue(values);
3001
#ifdef ARGS_NOEXCEPT
3002
                            // See ParseLong: ensure a flag-level error from
3003
                            // ParseValue (Help, Parse, Map) halts parsing so
3004
                            // it cannot be shadowed by a later parser error.
3005
                            if (flag->GetError() != Error::None)
3006
                            {
3007
                                return false;
3008
                            }
3009
#endif
3010
27.9k
                        }
3011
3012
27.9k
                        if (flag->KickOut())
3013
0
                        {
3014
0
                            ++it;
3015
0
                            return false;
3016
0
                        }
3017
3018
27.9k
                        if (!values.empty())
3019
0
                        {
3020
0
                            break;
3021
0
                        }
3022
27.9k
                    } else
3023
243
                    {
3024
243
                        const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'");
3025
243
#ifndef ARGS_NOEXCEPT
3026
243
                        throw ParseError(errorMessage);
3027
#else
3028
                        error = Error::Parse;
3029
                        errorMsg = errorMessage;
3030
                        return false;
3031
#endif
3032
243
                    }
3033
28.1k
                }
3034
3035
378
                return true;
3036
621
            }
bool args::ArgumentParser::ParseShort<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>)
Line
Count
Source
2963
621
            {
2964
621
                const auto &chunk = *it;
2965
621
                const auto argchunk = chunk.substr(shortprefix.size());
2966
28.5k
                for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit)
2967
28.1k
                {
2968
28.1k
                    const auto arg = *argit;
2969
2970
28.1k
                    if (auto flag = Match(arg))
2971
27.9k
                    {
2972
#ifdef ARGS_NOEXCEPT
2973
                        // See ParseLong: if Match recorded an error
2974
                        // (e.g. Options::Single violation), bail before the
2975
                        // value is parsed so the prior value is preserved.
2976
                        if (flag->GetError() != Error::None)
2977
                        {
2978
                            return false;
2979
                        }
2980
#endif
2981
27.9k
                        const std::string value(argit + 1, std::end(argchunk));
2982
27.9k
                        std::vector<std::string> values;
2983
27.9k
                        const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end,
2984
27.9k
                                                                         allowSeparateShortValue, allowJoinedShortValue,
2985
27.9k
                                                                         !value.empty(), value, !value.empty(), values);
2986
2987
27.9k
                        if (!errorMessage.empty())
2988
0
                        {
2989
0
#ifndef ARGS_NOEXCEPT
2990
0
                            throw ParseError(errorMessage);
2991
#else
2992
                            error = Error::Parse;
2993
                            errorMsg = errorMessage;
2994
                            return false;
2995
#endif
2996
0
                        }
2997
2998
27.9k
                        if (!readCompletion)
2999
27.9k
                        {
3000
27.9k
                            flag->ParseValue(values);
3001
#ifdef ARGS_NOEXCEPT
3002
                            // See ParseLong: ensure a flag-level error from
3003
                            // ParseValue (Help, Parse, Map) halts parsing so
3004
                            // it cannot be shadowed by a later parser error.
3005
                            if (flag->GetError() != Error::None)
3006
                            {
3007
                                return false;
3008
                            }
3009
#endif
3010
27.9k
                        }
3011
3012
27.9k
                        if (flag->KickOut())
3013
0
                        {
3014
0
                            ++it;
3015
0
                            return false;
3016
0
                        }
3017
3018
27.9k
                        if (!values.empty())
3019
0
                        {
3020
0
                            break;
3021
0
                        }
3022
27.9k
                    } else
3023
243
                    {
3024
243
                        const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'");
3025
243
#ifndef ARGS_NOEXCEPT
3026
243
                        throw ParseError(errorMessage);
3027
#else
3028
                        error = Error::Parse;
3029
                        errorMsg = errorMessage;
3030
                        return false;
3031
#endif
3032
243
                    }
3033
28.1k
                }
3034
3035
378
                return true;
3036
621
            }
Unexecuted instantiation: bool args::ArgumentParser::ParseShort<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>)
3037
3038
            bool AddCompletionReply(const std::string &cur, const std::string &choice)
3039
0
            {
3040
0
                if (cur.empty() || choice.find(cur) == 0)
3041
0
                {
3042
0
                    if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos)
3043
0
                    {
3044
0
                        completion->reply.push_back(choice.substr(choice.find(longseparator) + longseparator.size()));
3045
0
                    } else
3046
0
                    {
3047
0
                        completion->reply.push_back(choice);
3048
0
                    }
3049
0
                    return true;
3050
0
                }
3051
3052
0
                return false;
3053
0
            }
3054
3055
            template <typename It>
3056
            bool Complete(It it, It end, bool terminated)
3057
1.39k
            {
3058
1.39k
                auto nextIt = it;
3059
1.39k
                if (!readCompletion || (++nextIt != end))
3060
1.39k
                {
3061
1.39k
                    return false;
3062
1.39k
                }
3063
3064
0
                const auto &chunk = *it;
3065
0
                auto pos = GetNextPositional();
3066
0
                std::vector<Command *> commands = GetCommands();
3067
0
                const auto optionType = ParseOption(chunk, true);
3068
3069
                // Once the terminator has been seen the parser treats every
3070
                // following chunk as positional, so only positional choices are
3071
                // valid completions here. Suggesting flags or commands past the
3072
                // terminator offers candidates the parser would then reject.
3073
0
                if (!terminated && !commands.empty() && (chunk.empty() || optionType == OptionType::Positional))
3074
0
                {
3075
0
                    for (auto &cmd : commands)
3076
0
                    {
3077
0
                        if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3078
0
                        {
3079
0
                            AddCompletionReply(chunk, cmd->Name());
3080
0
                        }
3081
0
                    }
3082
0
                } else
3083
0
                {
3084
0
                    bool hasPositionalCompletion = true;
3085
3086
0
                    if (!terminated && !commands.empty())
3087
0
                    {
3088
0
                        for (auto &cmd : commands)
3089
0
                        {
3090
0
                            if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3091
0
                            {
3092
0
                                AddCompletionReply(chunk, cmd->Name());
3093
0
                            }
3094
0
                        }
3095
0
                    } else if (pos)
3096
0
                    {
3097
0
                        if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3098
0
                        {
3099
0
                            auto choices = pos->HelpChoices(helpParams);
3100
0
                            hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional;
3101
0
                            for (auto &choice : choices)
3102
0
                            {
3103
0
                                AddCompletionReply(chunk, choice);
3104
0
                            }
3105
0
                        }
3106
0
                    }
3107
3108
0
                    if (!terminated && hasPositionalCompletion)
3109
0
                    {
3110
0
                        auto flags = GetAllFlags();
3111
0
                        for (auto flag : flags)
3112
0
                        {
3113
0
                            if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None)
3114
0
                            {
3115
0
                                continue;
3116
0
                            }
3117
3118
0
                            auto &matcher = flag->GetMatcher();
3119
0
                            if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix)))
3120
0
                            {
3121
0
                                for (auto &flagName : matcher.GetFlagStrings())
3122
0
                                {
3123
0
                                    if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix)))
3124
0
                                    {
3125
0
                                        break;
3126
0
                                    }
3127
0
                                }
3128
0
                            }
3129
0
                        }
3130
3131
0
                        if (optionType == OptionType::LongFlag && allowJoinedLongValue)
3132
0
                        {
3133
0
                            const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator);
3134
                            // Only attempt joined-value completion when the
3135
                            // separator lies at or past the long prefix, so
3136
                            // there is a (possibly empty) flag name between
3137
                            // them. With a custom longseparator that overlaps
3138
                            // the prefix (e.g. LongSeparator("-") under the
3139
                            // default "--" prefix), an attacker-controlled
3140
                            // completion word like "--x" puts the separator
3141
                            // inside the prefix, making `arg` shorter than
3142
                            // longprefix. arg.substr(longprefix.size()) would
3143
                            // then throw std::out_of_range, which escapes the
3144
                            // parser as a non-args exception (bypassing the
3145
                            // documented catch(args::Error) idiom) and is
3146
                            // thrown even under ARGS_NOEXCEPT.
3147
0
                            if (separator != chunk.npos && separator >= longprefix.size())
3148
0
                            {
3149
0
                                std::string arg(chunk, 0, separator);
3150
0
                                if (auto flag = this->Match(arg.substr(longprefix.size())))
3151
0
                                {
3152
0
                                    for (auto &choice : flag->HelpChoices(helpParams))
3153
0
                                    {
3154
0
                                        AddCompletionReply(chunk, arg + longseparator + choice);
3155
0
                                    }
3156
0
                                }
3157
0
                            }
3158
0
                        } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue)
3159
0
                        {
3160
0
                            if (chunk.size() > shortprefix.size() + 1)
3161
0
                            {
3162
0
                                auto arg = chunk.at(shortprefix.size());
3163
                                //TODO: support -abcVALUE where a and b take no value
3164
0
                                if (auto flag = this->Match(arg))
3165
0
                                {
3166
0
                                    for (auto &choice : flag->HelpChoices(helpParams))
3167
0
                                    {
3168
0
                                        AddCompletionReply(chunk, shortprefix + arg + choice);
3169
0
                                    }
3170
0
                                }
3171
0
                            }
3172
0
                        }
3173
0
                    }
3174
0
                }
3175
3176
0
#ifndef ARGS_NOEXCEPT
3177
0
                throw Completion(completion->Get());
3178
#else
3179
                return true;
3180
#endif
3181
1.39k
            }
bool args::ArgumentParser::Complete<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, bool)
Line
Count
Source
3057
1.39k
            {
3058
1.39k
                auto nextIt = it;
3059
1.39k
                if (!readCompletion || (++nextIt != end))
3060
1.39k
                {
3061
1.39k
                    return false;
3062
1.39k
                }
3063
3064
0
                const auto &chunk = *it;
3065
0
                auto pos = GetNextPositional();
3066
0
                std::vector<Command *> commands = GetCommands();
3067
0
                const auto optionType = ParseOption(chunk, true);
3068
3069
                // Once the terminator has been seen the parser treats every
3070
                // following chunk as positional, so only positional choices are
3071
                // valid completions here. Suggesting flags or commands past the
3072
                // terminator offers candidates the parser would then reject.
3073
0
                if (!terminated && !commands.empty() && (chunk.empty() || optionType == OptionType::Positional))
3074
0
                {
3075
0
                    for (auto &cmd : commands)
3076
0
                    {
3077
0
                        if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3078
0
                        {
3079
0
                            AddCompletionReply(chunk, cmd->Name());
3080
0
                        }
3081
0
                    }
3082
0
                } else
3083
0
                {
3084
0
                    bool hasPositionalCompletion = true;
3085
3086
0
                    if (!terminated && !commands.empty())
3087
0
                    {
3088
0
                        for (auto &cmd : commands)
3089
0
                        {
3090
0
                            if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3091
0
                            {
3092
0
                                AddCompletionReply(chunk, cmd->Name());
3093
0
                            }
3094
0
                        }
3095
0
                    } else if (pos)
3096
0
                    {
3097
0
                        if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3098
0
                        {
3099
0
                            auto choices = pos->HelpChoices(helpParams);
3100
0
                            hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional;
3101
0
                            for (auto &choice : choices)
3102
0
                            {
3103
0
                                AddCompletionReply(chunk, choice);
3104
0
                            }
3105
0
                        }
3106
0
                    }
3107
3108
0
                    if (!terminated && hasPositionalCompletion)
3109
0
                    {
3110
0
                        auto flags = GetAllFlags();
3111
0
                        for (auto flag : flags)
3112
0
                        {
3113
0
                            if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None)
3114
0
                            {
3115
0
                                continue;
3116
0
                            }
3117
3118
0
                            auto &matcher = flag->GetMatcher();
3119
0
                            if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix)))
3120
0
                            {
3121
0
                                for (auto &flagName : matcher.GetFlagStrings())
3122
0
                                {
3123
0
                                    if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix)))
3124
0
                                    {
3125
0
                                        break;
3126
0
                                    }
3127
0
                                }
3128
0
                            }
3129
0
                        }
3130
3131
0
                        if (optionType == OptionType::LongFlag && allowJoinedLongValue)
3132
0
                        {
3133
0
                            const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator);
3134
                            // Only attempt joined-value completion when the
3135
                            // separator lies at or past the long prefix, so
3136
                            // there is a (possibly empty) flag name between
3137
                            // them. With a custom longseparator that overlaps
3138
                            // the prefix (e.g. LongSeparator("-") under the
3139
                            // default "--" prefix), an attacker-controlled
3140
                            // completion word like "--x" puts the separator
3141
                            // inside the prefix, making `arg` shorter than
3142
                            // longprefix. arg.substr(longprefix.size()) would
3143
                            // then throw std::out_of_range, which escapes the
3144
                            // parser as a non-args exception (bypassing the
3145
                            // documented catch(args::Error) idiom) and is
3146
                            // thrown even under ARGS_NOEXCEPT.
3147
0
                            if (separator != chunk.npos && separator >= longprefix.size())
3148
0
                            {
3149
0
                                std::string arg(chunk, 0, separator);
3150
0
                                if (auto flag = this->Match(arg.substr(longprefix.size())))
3151
0
                                {
3152
0
                                    for (auto &choice : flag->HelpChoices(helpParams))
3153
0
                                    {
3154
0
                                        AddCompletionReply(chunk, arg + longseparator + choice);
3155
0
                                    }
3156
0
                                }
3157
0
                            }
3158
0
                        } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue)
3159
0
                        {
3160
0
                            if (chunk.size() > shortprefix.size() + 1)
3161
0
                            {
3162
0
                                auto arg = chunk.at(shortprefix.size());
3163
                                //TODO: support -abcVALUE where a and b take no value
3164
0
                                if (auto flag = this->Match(arg))
3165
0
                                {
3166
0
                                    for (auto &choice : flag->HelpChoices(helpParams))
3167
0
                                    {
3168
0
                                        AddCompletionReply(chunk, shortprefix + arg + choice);
3169
0
                                    }
3170
0
                                }
3171
0
                            }
3172
0
                        }
3173
0
                    }
3174
0
                }
3175
3176
0
#ifndef ARGS_NOEXCEPT
3177
0
                throw Completion(completion->Get());
3178
#else
3179
                return true;
3180
#endif
3181
1.39k
            }
Unexecuted instantiation: bool args::ArgumentParser::Complete<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, bool)
3182
3183
            template <typename It>
3184
            It Parse(It begin, It end)
3185
983
            {
3186
983
                bool terminated = false;
3187
983
                std::vector<Command *> commands = GetCommands();
3188
3189
                // Check all arg chunks
3190
2.12k
                for (auto it = begin; it != end; ++it)
3191
1.39k
                {
3192
1.39k
                    if (Complete(it, end, terminated))
3193
0
                    {
3194
0
                        return end;
3195
0
                    }
3196
3197
1.39k
                    const auto &chunk = *it;
3198
3199
1.39k
                    if (!terminated && chunk == terminator)
3200
3
                    {
3201
3
                        terminated = true;
3202
1.39k
                    } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag)
3203
514
                    {
3204
514
                        if (!ParseLong(it, end))
3205
0
                        {
3206
0
                            return it;
3207
0
                        }
3208
879
                    } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag)
3209
621
                    {
3210
621
                        if (!ParseShort(it, end))
3211
0
                        {
3212
0
                            return it;
3213
0
                        }
3214
621
                    } else if (!terminated && !commands.empty())
3215
0
                    {
3216
0
                        auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; });
3217
0
                        if (itCommand == commands.end())
3218
0
                        {
3219
0
                            const std::string errorMessage("Unknown command: " + chunk);
3220
0
#ifndef ARGS_NOEXCEPT
3221
0
                            throw ParseError(errorMessage);
3222
#else
3223
                            error = Error::Parse;
3224
                            errorMsg = errorMessage;
3225
                            return it;
3226
#endif
3227
0
                        }
3228
3229
0
                        SelectCommand(*itCommand);
3230
3231
0
                        if (const auto &coroutine = GetCoroutine())
3232
0
                        {
3233
0
                            ++it;
3234
0
                            RaiiSubparser coro(*this, std::vector<std::string>(it, end));
3235
0
                            coroutine(coro.Parser());
3236
#ifdef ARGS_NOEXCEPT
3237
                            error = GetError();
3238
                            if (error != Error::None)
3239
                            {
3240
                                return end;
3241
                            }
3242
3243
                            if (!coro.Parser().IsParsed())
3244
                            {
3245
                                error = Error::Usage;
3246
                                return end;
3247
                            }
3248
#else
3249
0
                            if (!coro.Parser().IsParsed())
3250
0
                            {
3251
0
                                throw UsageError("Subparser::Parse was not called");
3252
0
                            }
3253
0
#endif
3254
3255
0
                            break;
3256
0
                        }
3257
3258
0
                        commands = GetCommands();
3259
0
                    } else
3260
258
                    {
3261
258
                        auto pos = GetNextPositional();
3262
258
                        if (pos)
3263
0
                        {
3264
0
                            pos->ParseValue(chunk);
3265
#ifdef ARGS_NOEXCEPT
3266
                            if (pos->GetError() != Error::None)
3267
                            {
3268
                                return it;
3269
                            }
3270
#endif
3271
3272
0
                            if (pos->KickOut())
3273
0
                            {
3274
0
                                return ++it;
3275
0
                            }
3276
0
                        } else
3277
258
                        {
3278
258
                            const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk);
3279
258
#ifndef ARGS_NOEXCEPT
3280
258
                            throw ParseError(errorMessage);
3281
#else
3282
                            error = Error::Parse;
3283
                            errorMsg = errorMessage;
3284
                            return it;
3285
#endif
3286
258
                        }
3287
258
                    }
3288
3289
1.13k
                    if (!readCompletion && completion != nullptr && completion->Matched())
3290
0
                    {
3291
#ifdef ARGS_NOEXCEPT
3292
                        if (completion->GetError() != Error::None)
3293
                        {
3294
                            error = completion->GetError();
3295
                            if (errorMsg.empty())
3296
                            {
3297
                                errorMsg = completion->GetErrorMsg();
3298
                            }
3299
                            return it;
3300
                        }
3301
3302
                        error = Error::Completion;
3303
#endif
3304
0
                        readCompletion = true;
3305
0
                        ++it;
3306
0
                        const auto argsLeft = static_cast<size_t>(std::distance(it, end));
3307
0
                        if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft)
3308
0
                        {
3309
0
#ifndef ARGS_NOEXCEPT
3310
0
                            throw Completion("");
3311
#else
3312
                            return end;
3313
#endif
3314
0
                        }
3315
3316
0
                        ++it;
3317
0
                        std::vector<std::string> curArgs;
3318
0
                        curArgs.reserve(completion->cword);
3319
0
                        auto curIt = it;
3320
0
                        for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt)
3321
0
                        {
3322
0
                            curArgs.push_back(*curIt);
3323
0
                        }
3324
3325
0
                        if (completion->syntax == "bash")
3326
0
                        {
3327
                            // bash tokenizes --flag=value as --flag=value
3328
                            // Security fix: Use size_t arithmetic throughout to avoid conversion issues
3329
0
                            for (size_t idx = 0; idx < curArgs.size(); )
3330
0
                            {
3331
0
                                if (idx > 0 && curArgs[idx] == "=")
3332
0
                                {
3333
0
                                    size_t prev_idx = idx - 1;  // Safe since we checked idx > 0
3334
0
                                    curArgs[prev_idx] += "=";
3335
0
                                    size_t next_idx = 0;
3336
0
                                    if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size())
3337
0
                                    {
3338
0
                                        curArgs[prev_idx] += curArgs[next_idx];
3339
                                        // Erase the '=' token and the following value token.
3340
0
                                        size_t erase_end = 0;
3341
0
                                        if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end))
3342
0
                                        {
3343
0
                                            typedef std::vector<std::string>::difference_type diff_t;
3344
0
                                            curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx),
3345
0
                                                         curArgs.begin() + static_cast<diff_t>(erase_end));
3346
0
                                        }
3347
0
                                    } else
3348
0
                                    {
3349
                                        // Safe erase of single '=' token at the end
3350
0
                                        typedef std::vector<std::string>::difference_type diff_t;
3351
0
                                        curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx));
3352
0
                                    }
3353
                                    // Do not increment idx - next element slides into current position
3354
0
                                } else
3355
0
                                {
3356
0
                                    ++idx;
3357
0
                                }
3358
0
                            }
3359
3360
0
                        }
3361
0
#ifndef ARGS_NOEXCEPT
3362
0
                        try
3363
0
                        {
3364
0
                            Parse(curArgs.begin(), curArgs.end());
3365
0
                            throw Completion("");
3366
0
                        }
3367
0
                        catch (Completion &)
3368
0
                        {
3369
0
                            throw;
3370
0
                        }
3371
0
                        catch (args::Error&)
3372
0
                        {
3373
0
                            throw Completion("");
3374
0
                        }
3375
#else
3376
                        // Discard the nested Parse's return value: it points
3377
                        // into the local curArgs vector, which is destroyed
3378
                        // when this function returns, leaving the caller with
3379
                        // a dangling iterator that would be compared against
3380
                        // the outer `end` in ParseCLI. Return the outer
3381
                        // `end` instead so the iterator stays in the caller's
3382
                        // container.
3383
                        Parse(curArgs.begin(), curArgs.end());
3384
                        error = Error::Completion;
3385
                        errorMsg.clear();
3386
                        return end;
3387
#endif
3388
0
                    }
3389
1.13k
                }
3390
3391
725
                Validate(shortprefix, longprefix);
3392
725
                return end;
3393
983
            }
std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> args::ArgumentParser::Parse<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>)
Line
Count
Source
3185
983
            {
3186
983
                bool terminated = false;
3187
983
                std::vector<Command *> commands = GetCommands();
3188
3189
                // Check all arg chunks
3190
2.12k
                for (auto it = begin; it != end; ++it)
3191
1.39k
                {
3192
1.39k
                    if (Complete(it, end, terminated))
3193
0
                    {
3194
0
                        return end;
3195
0
                    }
3196
3197
1.39k
                    const auto &chunk = *it;
3198
3199
1.39k
                    if (!terminated && chunk == terminator)
3200
3
                    {
3201
3
                        terminated = true;
3202
1.39k
                    } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag)
3203
514
                    {
3204
514
                        if (!ParseLong(it, end))
3205
0
                        {
3206
0
                            return it;
3207
0
                        }
3208
879
                    } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag)
3209
621
                    {
3210
621
                        if (!ParseShort(it, end))
3211
0
                        {
3212
0
                            return it;
3213
0
                        }
3214
621
                    } else if (!terminated && !commands.empty())
3215
0
                    {
3216
0
                        auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; });
3217
0
                        if (itCommand == commands.end())
3218
0
                        {
3219
0
                            const std::string errorMessage("Unknown command: " + chunk);
3220
0
#ifndef ARGS_NOEXCEPT
3221
0
                            throw ParseError(errorMessage);
3222
#else
3223
                            error = Error::Parse;
3224
                            errorMsg = errorMessage;
3225
                            return it;
3226
#endif
3227
0
                        }
3228
3229
0
                        SelectCommand(*itCommand);
3230
3231
0
                        if (const auto &coroutine = GetCoroutine())
3232
0
                        {
3233
0
                            ++it;
3234
0
                            RaiiSubparser coro(*this, std::vector<std::string>(it, end));
3235
0
                            coroutine(coro.Parser());
3236
#ifdef ARGS_NOEXCEPT
3237
                            error = GetError();
3238
                            if (error != Error::None)
3239
                            {
3240
                                return end;
3241
                            }
3242
3243
                            if (!coro.Parser().IsParsed())
3244
                            {
3245
                                error = Error::Usage;
3246
                                return end;
3247
                            }
3248
#else
3249
0
                            if (!coro.Parser().IsParsed())
3250
0
                            {
3251
0
                                throw UsageError("Subparser::Parse was not called");
3252
0
                            }
3253
0
#endif
3254
3255
0
                            break;
3256
0
                        }
3257
3258
0
                        commands = GetCommands();
3259
0
                    } else
3260
258
                    {
3261
258
                        auto pos = GetNextPositional();
3262
258
                        if (pos)
3263
0
                        {
3264
0
                            pos->ParseValue(chunk);
3265
#ifdef ARGS_NOEXCEPT
3266
                            if (pos->GetError() != Error::None)
3267
                            {
3268
                                return it;
3269
                            }
3270
#endif
3271
3272
0
                            if (pos->KickOut())
3273
0
                            {
3274
0
                                return ++it;
3275
0
                            }
3276
0
                        } else
3277
258
                        {
3278
258
                            const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk);
3279
258
#ifndef ARGS_NOEXCEPT
3280
258
                            throw ParseError(errorMessage);
3281
#else
3282
                            error = Error::Parse;
3283
                            errorMsg = errorMessage;
3284
                            return it;
3285
#endif
3286
258
                        }
3287
258
                    }
3288
3289
1.13k
                    if (!readCompletion && completion != nullptr && completion->Matched())
3290
0
                    {
3291
#ifdef ARGS_NOEXCEPT
3292
                        if (completion->GetError() != Error::None)
3293
                        {
3294
                            error = completion->GetError();
3295
                            if (errorMsg.empty())
3296
                            {
3297
                                errorMsg = completion->GetErrorMsg();
3298
                            }
3299
                            return it;
3300
                        }
3301
3302
                        error = Error::Completion;
3303
#endif
3304
0
                        readCompletion = true;
3305
0
                        ++it;
3306
0
                        const auto argsLeft = static_cast<size_t>(std::distance(it, end));
3307
0
                        if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft)
3308
0
                        {
3309
0
#ifndef ARGS_NOEXCEPT
3310
0
                            throw Completion("");
3311
#else
3312
                            return end;
3313
#endif
3314
0
                        }
3315
3316
0
                        ++it;
3317
0
                        std::vector<std::string> curArgs;
3318
0
                        curArgs.reserve(completion->cword);
3319
0
                        auto curIt = it;
3320
0
                        for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt)
3321
0
                        {
3322
0
                            curArgs.push_back(*curIt);
3323
0
                        }
3324
3325
0
                        if (completion->syntax == "bash")
3326
0
                        {
3327
                            // bash tokenizes --flag=value as --flag=value
3328
                            // Security fix: Use size_t arithmetic throughout to avoid conversion issues
3329
0
                            for (size_t idx = 0; idx < curArgs.size(); )
3330
0
                            {
3331
0
                                if (idx > 0 && curArgs[idx] == "=")
3332
0
                                {
3333
0
                                    size_t prev_idx = idx - 1;  // Safe since we checked idx > 0
3334
0
                                    curArgs[prev_idx] += "=";
3335
0
                                    size_t next_idx = 0;
3336
0
                                    if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size())
3337
0
                                    {
3338
0
                                        curArgs[prev_idx] += curArgs[next_idx];
3339
                                        // Erase the '=' token and the following value token.
3340
0
                                        size_t erase_end = 0;
3341
0
                                        if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end))
3342
0
                                        {
3343
0
                                            typedef std::vector<std::string>::difference_type diff_t;
3344
0
                                            curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx),
3345
0
                                                         curArgs.begin() + static_cast<diff_t>(erase_end));
3346
0
                                        }
3347
0
                                    } else
3348
0
                                    {
3349
                                        // Safe erase of single '=' token at the end
3350
0
                                        typedef std::vector<std::string>::difference_type diff_t;
3351
0
                                        curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx));
3352
0
                                    }
3353
                                    // Do not increment idx - next element slides into current position
3354
0
                                } else
3355
0
                                {
3356
0
                                    ++idx;
3357
0
                                }
3358
0
                            }
3359
3360
0
                        }
3361
0
#ifndef ARGS_NOEXCEPT
3362
0
                        try
3363
0
                        {
3364
0
                            Parse(curArgs.begin(), curArgs.end());
3365
0
                            throw Completion("");
3366
0
                        }
3367
0
                        catch (Completion &)
3368
0
                        {
3369
0
                            throw;
3370
0
                        }
3371
0
                        catch (args::Error&)
3372
0
                        {
3373
0
                            throw Completion("");
3374
0
                        }
3375
#else
3376
                        // Discard the nested Parse's return value: it points
3377
                        // into the local curArgs vector, which is destroyed
3378
                        // when this function returns, leaving the caller with
3379
                        // a dangling iterator that would be compared against
3380
                        // the outer `end` in ParseCLI. Return the outer
3381
                        // `end` instead so the iterator stays in the caller's
3382
                        // container.
3383
                        Parse(curArgs.begin(), curArgs.end());
3384
                        error = Error::Completion;
3385
                        errorMsg.clear();
3386
                        return end;
3387
#endif
3388
0
                    }
3389
1.13k
                }
3390
3391
725
                Validate(shortprefix, longprefix);
3392
725
                return end;
3393
983
            }
Unexecuted instantiation: std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> args::ArgumentParser::Parse<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>)
3394
3395
        public:
3396
            HelpParams helpParams;
3397
3398
            ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string())
3399
983
            {
3400
983
                Description(description_);
3401
983
                Epilog(epilog_);
3402
983
                LongPrefix("--");
3403
983
                ShortPrefix("-");
3404
983
                LongSeparator("=");
3405
983
                Terminator("--");
3406
983
                SetArgumentSeparations(true, true, true, true);
3407
983
                matched = true;
3408
983
            }
3409
3410
            void AddCompletion(CompletionFlag &completionFlag)
3411
0
            {
3412
0
                // Only take the pointer once registration has succeeded: Add()
3413
0
                // throws on a duplicate flag, and the flag it was handed is
3414
0
                // gone by the time that error reaches the caller.
3415
0
                Add(completionFlag);
3416
0
                completion = &completionFlag;
3417
0
            }
3418
3419
            /** The program name for help generation
3420
             */
3421
            const std::string &Prog() const
3422
0
            { return helpParams.programName; }
3423
            /** The program name for help generation
3424
             */
3425
            void Prog(const std::string &prog_)
3426
0
            { this->helpParams.programName = prog_; }
3427
3428
            /** The prefix for long flags
3429
             */
3430
            const std::string &LongPrefix() const
3431
0
            { return longprefix; }
3432
            /** The prefix for long flags
3433
             */
3434
            void LongPrefix(const std::string &longprefix_)
3435
983
            {
3436
983
                this->longprefix = longprefix_;
3437
983
                this->helpParams.longPrefix = longprefix_;
3438
983
            }
3439
3440
            /** The prefix for short flags
3441
             */
3442
            const std::string &ShortPrefix() const
3443
0
            { return shortprefix; }
3444
            /** The prefix for short flags
3445
             */
3446
            void ShortPrefix(const std::string &shortprefix_)
3447
983
            {
3448
983
                this->shortprefix = shortprefix_;
3449
983
                this->helpParams.shortPrefix = shortprefix_;
3450
983
            }
3451
3452
            /** The separator for long flags
3453
             */
3454
            const std::string &LongSeparator() const
3455
0
            { return longseparator; }
3456
            /** The separator for long flags
3457
             */
3458
            void LongSeparator(const std::string &longseparator_)
3459
983
            {
3460
983
                if (longseparator_.empty())
3461
0
                {
3462
0
                    const std::string errorMessage("longseparator can not be set to empty");
3463
#ifdef ARGS_NOEXCEPT
3464
                    error = Error::Usage;
3465
                    errorMsg = errorMessage;
3466
#else
3467
0
                    throw UsageError(errorMessage);
3468
0
#endif
3469
0
                } else
3470
983
                {
3471
983
                    this->longseparator = longseparator_;
3472
983
                    this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3473
983
                }
3474
983
            }
3475
3476
            /** The terminator that forcibly separates flags from positionals
3477
             */
3478
            const std::string &Terminator() const
3479
0
            { return terminator; }
3480
            /** The terminator that forcibly separates flags from positionals
3481
             */
3482
            void Terminator(const std::string &terminator_)
3483
983
            { this->terminator = terminator_; }
3484
3485
            /** Get the current argument separation parameters.
3486
             *
3487
             * See SetArgumentSeparations for details on what each one means.
3488
             */
3489
            void GetArgumentSeparations(
3490
                bool &allowJoinedShortValue_,
3491
                bool &allowJoinedLongValue_,
3492
                bool &allowSeparateShortValue_,
3493
                bool &allowSeparateLongValue_) const
3494
0
            {
3495
0
                allowJoinedShortValue_ = this->allowJoinedShortValue;
3496
0
                allowJoinedLongValue_ = this->allowJoinedLongValue;
3497
0
                allowSeparateShortValue_ = this->allowSeparateShortValue;
3498
0
                allowSeparateLongValue_ = this->allowSeparateLongValue;
3499
0
            }
3500
3501
            /** Change allowed option separation.
3502
             *
3503
             * \param allowJoinedShortValue_ Allow a short flag that accepts an argument to be passed its argument immediately next to it (ie. in the same argv field)
3504
             * \param allowJoinedLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by the longseparator (ie. in the same argv field)
3505
             * \param allowSeparateShortValue_ Allow a short flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field)
3506
             * \param allowSeparateLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field)
3507
             */
3508
            void SetArgumentSeparations(
3509
                const bool allowJoinedShortValue_,
3510
                const bool allowJoinedLongValue_,
3511
                const bool allowSeparateShortValue_,
3512
                const bool allowSeparateLongValue_)
3513
983
            {
3514
983
                this->allowJoinedShortValue = allowJoinedShortValue_;
3515
983
                this->allowJoinedLongValue = allowJoinedLongValue_;
3516
983
                this->allowSeparateShortValue = allowSeparateShortValue_;
3517
983
                this->allowSeparateLongValue = allowSeparateLongValue_;
3518
3519
983
                this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3520
983
                this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " ";
3521
983
            }
3522
3523
            /** Pass the help menu into an ostream
3524
             */
3525
            void Help(std::ostream &help_) const
3526
0
            {
3527
0
                auto &command = SelectedCommand();
3528
0
                const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description();
3529
0
                const auto desc_indent = helpParams.descriptionindent;
3530
0
                const auto effective_desc_width = (helpParams.width > desc_indent) ? helpParams.width - desc_indent : 0;
3531
0
                const auto description_text = Wrap(commandDescription, effective_desc_width);
3532
0
                const auto epilog_text = Wrap(command.Epilog(), effective_desc_width);
3533
0
3534
0
                const bool hasoptions = command.HasFlag();
3535
0
                const bool hasarguments = command.HasPositional();
3536
0
3537
0
                std::vector<std::string> prognameline;
3538
0
                prognameline.push_back(helpParams.usageString);
3539
0
                prognameline.push_back(Prog());
3540
0
                auto commandProgLine = command.GetProgramLine(helpParams);
3541
0
                prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end());
3542
0
3543
0
                const auto prog_sum = helpParams.progindent + helpParams.progtailindent;
3544
0
                const auto effective_prog_width = (helpParams.width > prog_sum) ? helpParams.width - prog_sum : 0;
3545
0
                const auto effective_prog_first = (helpParams.width > helpParams.progindent) ? helpParams.width - helpParams.progindent : 0;
3546
0
                const auto proglines = Wrap(prognameline.begin(), prognameline.end(),
3547
0
                                            effective_prog_width,
3548
0
                                            effective_prog_first);
3549
0
                auto progit = std::begin(proglines);
3550
0
                if (progit != std::end(proglines))
3551
0
                {
3552
0
                    help_ << std::string(helpParams.progindent, ' ') << *progit << '\n';
3553
0
                    ++progit;
3554
0
                }
3555
0
                for (; progit != std::end(proglines); ++progit)
3556
0
                {
3557
0
                    help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n';
3558
0
                }
3559
0
3560
0
                help_ << '\n';
3561
0
3562
0
                if (!description_text.empty())
3563
0
                {
3564
0
                    for (const auto &line: description_text)
3565
0
                    {
3566
0
                        help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3567
0
                    }
3568
0
                    help_ << "\n";
3569
0
                }
3570
0
3571
0
                bool lastDescriptionIsNewline = false;
3572
0
3573
0
                if (!helpParams.optionsString.empty())
3574
0
                {
3575
0
                    help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n";
3576
0
                }
3577
0
3578
0
                for (const auto &desc: command.GetDescription(helpParams, 0))
3579
0
                {
3580
0
                    lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty();
3581
0
                    const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent;
3582
0
                    const auto flag_sum = helpParams.flagindent + helpParams.helpindent + helpParams.gutter;
3583
0
                    const auto effective_flag_width = (helpParams.width > flag_sum) ? helpParams.width - flag_sum : 0;
3584
0
                    const auto flags = Wrap(std::get<0>(desc), effective_flag_width);
3585
0
                    const auto info_sum = helpParams.helpindent + groupindent;
3586
0
                    const auto effective_info_width = (helpParams.width > info_sum) ? helpParams.width - info_sum : 0;
3587
0
                    const auto info = Wrap(std::get<1>(desc), effective_info_width);
3588
0
3589
0
                    std::string::size_type flagssize = 0;
3590
0
                    for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit)
3591
0
                    {
3592
0
                        if (flagsit != std::begin(flags))
3593
0
                        {
3594
0
                            help_ << '\n';
3595
0
                        }
3596
0
                        help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit;
3597
0
                        flagssize = Glyphs(*flagsit);
3598
0
                    }
3599
0
3600
0
                    auto infoit = std::begin(info);
3601
0
                    // groupindent is on both sides of this inequality, and therefore can be removed
3602
0
                    if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription)
3603
0
                    {
3604
0
                        help_ << '\n';
3605
0
                    } else
3606
0
                    {
3607
0
                        // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here
3608
0
                        const auto indent_sum = helpParams.flagindent + flagssize;
3609
0
                        const auto effective_space = (helpParams.helpindent > indent_sum) ? helpParams.helpindent - indent_sum : 0;
3610
0
                        help_ << std::string(effective_space, ' ') << *infoit << '\n';
3611
0
                        ++infoit;
3612
0
                    }
3613
0
                    for (; infoit != std::end(info); ++infoit)
3614
0
                    {
3615
0
                        help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n';
3616
0
                    }
3617
0
                }
3618
0
                if (hasoptions && hasarguments && helpParams.showTerminator)
3619
0
                {
3620
0
                    lastDescriptionIsNewline = false;
3621
0
                    const auto effective_term_width = (helpParams.width > helpParams.flagindent) ? helpParams.width - helpParams.flagindent : 0;
3622
0
                    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))
3623
0
                    {
3624
0
                        help_ << std::string(helpParams.flagindent, ' ') << item << '\n';
3625
0
                    }
3626
0
                }
3627
0
3628
0
                if (!lastDescriptionIsNewline)
3629
0
                {
3630
0
                    help_ << "\n";
3631
0
                }
3632
0
3633
0
                for (const auto &line: epilog_text)
3634
0
                {
3635
0
                    help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3636
0
                }
3637
0
            }
3638
3639
            /** Generate a help menu as a string.
3640
             *
3641
             * \return the help text as a single string
3642
             */
3643
            std::string Help() const
3644
0
            {
3645
0
                std::ostringstream help_;
3646
0
                Help(help_);
3647
0
                return help_.str();
3648
0
            }
3649
3650
            virtual void Reset() noexcept override
3651
983
            {
3652
983
                Command::Reset();
3653
983
                matched = true;
3654
983
                readCompletion = false;
3655
983
            }
3656
3657
            /** Parse all arguments.
3658
             *
3659
             * \param begin an iterator to the beginning of the argument list
3660
             * \param end an iterator to the past-the-end element of the argument list
3661
             * \return the iterator after the last parsed value.  Only useful for kick-out
3662
             */
3663
            template <typename It>
3664
            It ParseArgs(It begin, It end)
3665
983
            {
3666
                // Reset all Matched statuses and errors
3667
983
                Reset();
3668
#ifdef ARGS_NOEXCEPT
3669
                error = GetError();
3670
                if (error != Error::None)
3671
                {
3672
                    return end;
3673
                }
3674
#endif
3675
983
                return Parse(begin, end);
3676
983
            }
std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> args::ArgumentParser::ParseArgs<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>)
Line
Count
Source
3665
983
            {
3666
                // Reset all Matched statuses and errors
3667
983
                Reset();
3668
#ifdef ARGS_NOEXCEPT
3669
                error = GetError();
3670
                if (error != Error::None)
3671
                {
3672
                    return end;
3673
                }
3674
#endif
3675
983
                return Parse(begin, end);
3676
983
            }
Unexecuted instantiation: std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> args::ArgumentParser::ParseArgs<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>)
3677
3678
            /** Parse all arguments.
3679
             *
3680
             * \param args an iterable of the arguments
3681
             * \return the iterator after the last parsed value.  Only useful for kick-out
3682
             */
3683
            template <typename T>
3684
            auto ParseArgs(const T &args) -> decltype(std::begin(args))
3685
0
            {
3686
0
                return ParseArgs(std::begin(args), std::end(args));
3687
0
            }
3688
3689
            /** Convenience function to parse the CLI from argc and argv
3690
             *
3691
             * Just assigns the program name and vectorizes arguments for passing into ParseArgs()
3692
             *
3693
             * \return whether or not all arguments were parsed.  This works for detecting kick-out, but is generally useless as it can't do anything with it.
3694
             */
3695
            bool ParseCLI(const int argc, const char * const * argv)
3696
0
            {
3697
0
                if (argc > 0 && argv != nullptr && argv[0] != nullptr && Prog().empty())
3698
0
                {
3699
0
                    Prog(argv[0]);
3700
0
                }
3701
0
3702
0
                std::vector<std::string> args;
3703
0
                if (argc > 1 && argv != nullptr)
3704
0
                {
3705
0
                    args.assign(argv + 1, argv + argc);
3706
0
                }
3707
0
3708
0
                return ParseArgs(args) == std::end(args);
3709
0
            }
3710
            
3711
            template <typename T>
3712
            bool ParseCLI(const T &args)
3713
            {
3714
                return ParseArgs(args) == std::end(args);
3715
            }
3716
    };
3717
3718
    inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_)
3719
0
        : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser)
3720
0
    {
3721
0
        command.subparser = &parser;
3722
0
    }
3723
3724
0
    inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams &params_): command(command_), parser(command, params_), oldSubparser(command.subparser)
3725
0
    {
3726
0
        command.subparser = &parser;
3727
0
    }
3728
3729
    inline void Subparser::Parse()
3730
0
    {
3731
0
        isParsed = true;
3732
0
        Reset();
3733
0
        command.subparserDescription = GetDescription(helpParams, 0);
3734
0
        command.subparserHasFlag = HasFlag();
3735
0
        command.subparserHasPositional = HasPositional();
3736
0
        command.subparserHasCommand = HasCommand();
3737
0
        command.subparserProgramLine = GetProgramLine(helpParams);
3738
0
        if (parser == nullptr)
3739
0
        {
3740
0
#ifndef ARGS_NOEXCEPT
3741
0
            throw args::SubparserError();
3742
0
#else
3743
0
            error = Error::Subparser;
3744
0
            return;
3745
0
#endif
3746
0
        }
3747
0
3748
0
        auto it = parser->Parse(args.begin(), args.end());
3749
0
        command.Validate(parser->ShortPrefix(), parser->LongPrefix());
3750
0
        kicked.assign(it, args.end());
3751
0
3752
0
#ifdef ARGS_NOEXCEPT
3753
0
        command.subparserError = GetError();
3754
0
#endif
3755
0
    }
3756
3757
    inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser)
3758
0
    {
3759
0
        parser.Help(os);
3760
0
        return os;
3761
0
    }
3762
3763
    /** Boolean argument matcher
3764
     */
3765
    class Flag : public FlagBase
3766
    {
3767
        public:
3768
2.94k
            Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_)
3769
2.94k
            {
3770
2.94k
                group_.Add(*this);
3771
2.94k
            }
3772
3773
1.96k
            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)
3774
1.96k
            {
3775
1.96k
            }
3776
3777
0
            virtual ~Flag() {}
3778
3779
            /** Get whether this was matched
3780
             */
3781
            bool Get() const
3782
0
            {
3783
0
                return Matched();
3784
0
            }
3785
3786
            virtual Nargs NumberOfArguments() const noexcept override
3787
28.0k
            {
3788
28.0k
                return 0;
3789
28.0k
            }
3790
3791
            virtual void ParseValue(const std::vector<std::string>&) override
3792
27.9k
            {
3793
27.9k
            }
3794
    };
3795
3796
    /** Help flag class
3797
     *
3798
     * Works like a regular flag, but throws an instance of Help when it is matched
3799
     */
3800
    class HelpFlag : public Flag
3801
    {
3802
        public:
3803
983
            HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {}
3804
3805
0
            virtual ~HelpFlag() {}
3806
3807
            virtual void ParseValue(const std::vector<std::string> &)
3808
21
            {
3809
#ifdef ARGS_NOEXCEPT
3810
                    error = Error::Help;
3811
                    errorMsg = Name();
3812
#else
3813
21
                    throw Help(Name());
3814
21
#endif
3815
21
            }
3816
3817
            /** Get whether this was matched
3818
             */
3819
            bool Get() const noexcept
3820
0
            {
3821
0
                return Matched();
3822
0
            }
3823
    };
3824
3825
    /** A flag class that simply counts the number of times it's matched
3826
     */
3827
    class CounterFlag : public Flag
3828
    {
3829
        private:
3830
            const int startcount;
3831
            int count;
3832
3833
        public:
3834
            CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}):
3835
0
                Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {}
3836
3837
0
            virtual ~CounterFlag() {}
3838
3839
            virtual FlagBase *Match(const EitherFlag &arg) override
3840
0
            {
3841
0
                auto me = FlagBase::Match(arg);
3842
0
                if (me)
3843
0
                {
3844
0
#ifdef ARGS_NOEXCEPT
3845
0
                    // Suppress increment when FlagBase::Match recorded an
3846
0
                    // error on this same call (e.g. Options::Single violated).
3847
0
                    // In non-noexcept mode that path would have thrown before
3848
0
                    // reaching here and the count would not have advanced.
3849
0
                    if (GetError() != Error::None)
3850
0
                    {
3851
0
                        return me;
3852
0
                    }
3853
0
#endif
3854
0
                    ++count;
3855
0
                }
3856
0
                return me;
3857
0
            }
3858
3859
            /** Get the count
3860
             */
3861
            int &Get() noexcept
3862
0
            {
3863
0
                return count;
3864
0
            }
3865
3866
0
            int &operator *() noexcept {
3867
0
                return count;
3868
0
            }
3869
            
3870
0
            const int &operator *() const noexcept {
3871
0
                return count;
3872
0
            }
3873
3874
            virtual void Reset() noexcept override
3875
0
            {
3876
0
                FlagBase::Reset();
3877
0
                count = startcount;
3878
0
            }
3879
    };
3880
3881
    /** A flag class that calls a function when it's matched
3882
     */
3883
    class ActionFlag : public FlagBase
3884
    {
3885
        private:
3886
            std::function<void(const std::vector<std::string> &)> action;
3887
            Nargs nargs;
3888
3889
        public:
3890
            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_ = {}):
3891
                FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_)
3892
0
            {
3893
0
                group_.Add(*this);
3894
0
            }
3895
3896
            ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}):
3897
                FlagBase(name_, help_, std::move(matcher_), options_), nargs(1)
3898
0
            {
3899
0
                group_.Add(*this);
3900
0
                action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); };
3901
0
            }
3902
3903
            ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}):
3904
                FlagBase(name_, help_, std::move(matcher_), options_), nargs(0)
3905
0
            {
3906
0
                group_.Add(*this);
3907
0
                action = [action_](const std::vector<std::string> &) { return action_(); };
3908
0
            }
3909
3910
            virtual Nargs NumberOfArguments() const noexcept override
3911
0
            { return nargs; }
3912
3913
            virtual void ParseValue(const std::vector<std::string> &value) override
3914
0
            { action(value); }
3915
    };
3916
3917
    /** A default Reader class for argument classes
3918
     *
3919
     * If destination type is assignable to std::string it uses an assignment to std::string.
3920
     * Otherwise ValueReader simply uses a std::istringstream to read into the destination type, and
3921
     * raises a ParseError if there are any characters left.
3922
     */
3923
    struct ValueReader
3924
    {
3925
      private:
3926
        template <typename T>
3927
        static typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, bool>::type
3928
        HasUnsignedNegativeSign(const std::string &value)
3929
        {
3930
            const auto firstNonSpace = std::find_if_not(value.begin(), value.end(), [](char c)
3931
            {
3932
                return std::isspace(static_cast<unsigned char>(c)) != 0;
3933
            });
3934
3935
            return firstNonSpace != value.end() && *firstNonSpace == '-';
3936
        }
3937
3938
        template <typename T>
3939
        static typename std::enable_if<!std::is_integral<T>::value || !std::is_unsigned<T>::value, bool>::type
3940
        HasUnsignedNegativeSign(const std::string &)
3941
        {
3942
            return false;
3943
        }
3944
3945
      public:
3946
        template <typename T>
3947
        typename std::enable_if<
3948
            std::is_integral<T>::value &&
3949
            !std::is_same<T, bool>::value &&
3950
            !std::is_same<T, char>::value &&
3951
            !std::is_same<T, signed char>::value &&
3952
            !std::is_same<T, unsigned char>::value,
3953
            bool>::type
3954
        ParseNumericValue(const std::string &value, T &destination)
3955
        {
3956
            if (HasUnsignedNegativeSign<T>(value))
3957
            {
3958
                return false;
3959
            }
3960
3961
            const char *begin = value.c_str();
3962
            // The true end of the value, derived from its length rather than
3963
            // from the first NUL. strtoull/strtoll treat the buffer as a C
3964
            // string and stop at an embedded '\0', so checking `*end == '\0'`
3965
            // for "no trailing data" is defeated by a value like "12\0junk":
3966
            // end lands on the embedded NUL and the junk after it is silently
3967
            // accepted. Comparing against `stop` validates the whole string
3968
            // and matches the istringstream-based reader used for other types.
3969
            const char *const stop = begin + value.size();
3970
3971
            // C++11-compatible: use strtoull/strtoll. Hardening retained from
3972
            // the original from_chars draft (errno save/restore, ERANGE check,
3973
            // narrowing range check, trailing-whitespace tolerance). No
3974
            // unconditional dependency on <charconv> / C++17.
3975
            const int saved_errno = errno;
3976
            errno = 0;
3977
3978
            char *end = nullptr;
3979
3980
            if (std::is_unsigned<T>::value)
3981
            {
3982
                const unsigned long long parsed = std::strtoull(begin, &end, 0);
3983
                if (end == begin)
3984
                {
3985
                    errno = saved_errno;
3986
                    return false;
3987
                }
3988
                while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
3989
                {
3990
                    ++end;
3991
                }
3992
                if (end != stop || errno == ERANGE ||
3993
                    parsed > static_cast<unsigned long long>(std::numeric_limits<T>::max()))
3994
                {
3995
                    errno = saved_errno;
3996
                    return false;
3997
                }
3998
3999
                destination = static_cast<T>(parsed);
4000
            }
4001
            else
4002
            {
4003
                const long long parsed = std::strtoll(begin, &end, 0);
4004
                if (end == begin)
4005
                {
4006
                    errno = saved_errno;
4007
                    return false;
4008
                }
4009
                while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
4010
                {
4011
                    ++end;
4012
                }
4013
                if (end != stop || errno == ERANGE ||
4014
                    parsed < static_cast<long long>(std::numeric_limits<T>::min()) ||
4015
                    parsed > static_cast<long long>(std::numeric_limits<T>::max()))
4016
                {
4017
                    errno = saved_errno;
4018
                    return false;
4019
                }
4020
4021
                destination = static_cast<T>(parsed);
4022
            }
4023
4024
            errno = saved_errno;
4025
            return true;
4026
        }
4027
4028
        template <typename T>
4029
        typename std::enable_if<
4030
            !std::is_integral<T>::value ||
4031
            std::is_same<T, bool>::value ||
4032
            std::is_same<T, char>::value ||
4033
            std::is_same<T, signed char>::value ||
4034
            std::is_same<T, unsigned char>::value,
4035
            bool>::type
4036
        ParseNumericValue(const std::string &value, T &destination)
4037
        {
4038
            std::istringstream ss(value);
4039
            // Pin parsing to the C locale so that the decimal separator and
4040
            // thousands grouping behavior do not silently depend on whatever
4041
            // std::locale::global was last set to elsewhere in the process.
4042
            // Without this, e.g. "3.14" parses as 3 (with ".14" trailing) in
4043
            // any locale whose numpunct facet treats ',' as the decimal point.
4044
            ss.imbue(std::locale::classic());
4045
            ss >> destination;
4046
            if (ss.fail())
4047
            {
4048
                return false;
4049
            }
4050
4051
            // Check for trailing garbage by attempting to extract any remaining characters.
4052
            // Do not use 'ss >> std::ws' followed by peek(), as std::ws can set failbit
4053
            // on EOF, causing false rejection of valid input.
4054
            char extra = '\0';
4055
            ss >> std::ws >> extra;
4056
            // If extraction succeeded, there's trailing garbage (return false).
4057
            // If extraction failed due to EOF only (goodbit after ws extraction), it's valid (return true).
4058
            // If extraction failed for other reasons, it's invalid (return false).
4059
            if (ss.fail())
4060
            {
4061
                // Clear the failbit to check if EOF is the only issue
4062
                ss.clear(ss.rdstate() & ~std::ios::failbit);
4063
                return ss.eof();
4064
            }
4065
            // Extraction succeeded, meaning there's trailing garbage
4066
            return false;
4067
        }
4068
4069
        template <typename T>
4070
        typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type
4071
        operator ()(const std::string &name, const std::string &value, T &destination)
4072
        {
4073
            const bool success = ParseNumericValue(value, destination);
4074
            if (!success)
4075
            {
4076
#ifdef ARGS_NOEXCEPT
4077
                (void)name;
4078
                return false;
4079
#else
4080
                std::ostringstream problem;
4081
                problem << "Argument '" << name << "' received invalid value type '" << value << "'";
4082
                throw ParseError(problem.str());
4083
#endif
4084
            }
4085
            return true;
4086
        }
4087
4088
        template <typename T>
4089
        typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type
4090
        operator()(const std::string &, const std::string &value, T &destination)
4091
        {
4092
            destination = value;
4093
            return true;
4094
        }
4095
    };
4096
4097
    /** An argument-accepting flag class
4098
     * 
4099
     * \tparam T the type to extract the argument as
4100
     * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
4101
     */
4102
    template <
4103
        typename T,
4104
        typename Reader = ValueReader>
4105
    class ValueFlag : public ValueFlagBase
4106
    {
4107
        protected:
4108
            T value;
4109
            T defaultValue;
4110
4111
            virtual std::string GetDefaultString(const HelpParams&) const override
4112
            {
4113
                return detail::ToString(defaultValue);
4114
            }
4115
4116
        private:
4117
            Reader reader;
4118
4119
        public:
4120
4121
            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_)
4122
            {
4123
                group_.Add(*this);
4124
            }
4125
4126
            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)
4127
            {
4128
            }
4129
4130
            ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_)
4131
            {
4132
            }
4133
4134
            virtual ~ValueFlag() {}
4135
4136
            virtual void ParseValue(const std::vector<std::string> &values_) override
4137
            {
4138
                const std::string &value_ = values_.at(0);
4139
4140
#ifdef ARGS_NOEXCEPT
4141
                if (!reader(name, value_, this->value))
4142
                {
4143
                    error = Error::Parse;
4144
                }
4145
#else
4146
                reader(name, value_, this->value);
4147
#endif
4148
            }
4149
4150
            virtual void Reset() noexcept override
4151
            {
4152
                ValueFlagBase::Reset();
4153
                value = defaultValue;
4154
            }
4155
4156
            /** Get the value
4157
             */
4158
            T &Get() noexcept
4159
            {
4160
                return value;
4161
            }
4162
4163
            /** Get the value
4164
             */
4165
            T &operator *() noexcept
4166
            {
4167
                return value;
4168
            }
4169
4170
            /** Get the value
4171
             */
4172
            const T &operator *() const noexcept
4173
            {
4174
                return value;
4175
            }
4176
4177
            /** Get the value
4178
             */
4179
            T *operator ->() noexcept
4180
            {
4181
                return &value;
4182
            }
4183
4184
            /** Get the value
4185
             */
4186
            const T *operator ->() const noexcept
4187
            {
4188
                return &value;
4189
            }
4190
4191
            /** Get the default value
4192
             */
4193
            const T &GetDefault() noexcept
4194
            {
4195
                return defaultValue;
4196
            }
4197
    };
4198
4199
    /** An optional argument-accepting flag class
4200
     *
4201
     * \tparam T the type to extract the argument as
4202
     * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
4203
     */
4204
    template <
4205
        typename T,
4206
        typename Reader = ValueReader>
4207
    class ImplicitValueFlag : public ValueFlag<T, Reader>
4208
    {
4209
        protected:
4210
            T implicitValue;
4211
4212
        public:
4213
4214
            ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {})
4215
                : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_)
4216
            {
4217
            }
4218
4219
            ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {})
4220
                : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_)
4221
            {
4222
            }
4223
4224
            ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_)
4225
                : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue()
4226
            {
4227
            }
4228
4229
            virtual ~ImplicitValueFlag() {}
4230
4231
            virtual Nargs NumberOfArguments() const noexcept override
4232
            {
4233
                return {0, 1};
4234
            }
4235
4236
            virtual void ParseValue(const std::vector<std::string> &value_) override
4237
            {
4238
                if (value_.empty())
4239
                {
4240
                    this->value = implicitValue;
4241
                } else
4242
                {
4243
                    ValueFlag<T, Reader>::ParseValue(value_);
4244
                }
4245
            }
4246
    };
4247
4248
    /** A boolean flag containing a retrievable constant.
4249
     * 
4250
     * \tparam T the type of the constant
4251
     */
4252
    template <typename T>
4253
    class ConstantFlag : public Flag
4254
    {
4255
        T value;
4256
4257
        public:
4258
4259
        ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_, const T& value_):
4260
        Flag(group_, name_, help_, std::move(matcher_), options_),
4261
        value(value_)
4262
        {}
4263
4264
        ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T& value_, bool extraError_ = false):
4265
        Flag(group_, name_, help_, std::move(matcher_), extraError_),
4266
        value(value_)
4267
        {}
4268
4269
        T operator * () const noexcept
4270
        {
4271
            return value;
4272
        }
4273
4274
        T Get() const noexcept
4275
        {
4276
            return value;
4277
        }
4278
4279
        const T *operator -> () const noexcept
4280
        {
4281
            return &value;
4282
        }
4283
    };
4284
4285
    /** A variadic arguments accepting flag class
4286
     *
4287
     * \tparam T the type to extract the argument as
4288
     * \tparam List the list type that houses the values
4289
     * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
4290
     */
4291
    template <
4292
        typename T,
4293
        template <typename...> class List = detail::vector,
4294
        typename Reader = ValueReader>
4295
    class NargsValueFlag : public FlagBase
4296
    {
4297
        protected:
4298
4299
            List<T> values;
4300
            const List<T> defaultValues;
4301
            Nargs nargs;
4302
            Reader reader;
4303
4304
        public:
4305
4306
            typedef List<T> Container;
4307
            typedef T value_type;
4308
            typedef typename Container::allocator_type allocator_type;
4309
            typedef typename Container::pointer pointer;
4310
            typedef typename Container::const_pointer const_pointer;
4311
            typedef T& reference;
4312
            typedef const T& const_reference;
4313
            typedef typename Container::size_type size_type;
4314
            typedef typename Container::difference_type difference_type;
4315
            typedef typename Container::iterator iterator;
4316
            typedef typename Container::const_iterator const_iterator;
4317
            typedef std::reverse_iterator<iterator> reverse_iterator;
4318
            typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4319
4320
            NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {})
4321
                : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_)
4322
            {
4323
                group_.Add(*this);
4324
            }
4325
4326
            virtual ~NargsValueFlag() {}
4327
4328
            virtual Nargs NumberOfArguments() const noexcept override
4329
            {
4330
                return nargs;
4331
            }
4332
4333
            virtual void ParseValue(const std::vector<std::string> &values_) override
4334
            {
4335
                values.clear();
4336
4337
                for (const std::string &value : values_)
4338
                {
4339
                    T v {};
4340
#ifdef ARGS_NOEXCEPT
4341
                    if (!reader(name, value, v))
4342
                    {
4343
                        error = Error::Parse;
4344
                        return;
4345
                    }
4346
#else
4347
                    reader(name, value, v);
4348
#endif
4349
                    values.insert(std::end(values), v);
4350
                }
4351
            }
4352
4353
            List<T> &Get() noexcept
4354
            {
4355
                return values;
4356
            }
4357
4358
            /** Get the value
4359
             */
4360
            List<T> &operator *() noexcept
4361
            {
4362
                return values;
4363
            }
4364
4365
            /** Get the values
4366
             */
4367
            const List<T> &operator *() const noexcept
4368
            {
4369
                return values;
4370
            }
4371
4372
            /** Get the values
4373
             */
4374
            List<T> *operator ->() noexcept
4375
            {
4376
                return &values;
4377
            }
4378
4379
            /** Get the values
4380
             */
4381
            const List<T> *operator ->() const noexcept
4382
            {
4383
                return &values;
4384
            }
4385
4386
            iterator begin() noexcept
4387
            {
4388
                return values.begin();
4389
            }
4390
4391
            const_iterator begin() const noexcept
4392
            {
4393
                return values.begin();
4394
            }
4395
4396
            const_iterator cbegin() const noexcept
4397
            {
4398
                return values.cbegin();
4399
            }
4400
4401
            iterator end() noexcept
4402
            {
4403
                return values.end();
4404
            }
4405
4406
            const_iterator end() const noexcept 
4407
            {
4408
                return values.end();
4409
            }
4410
4411
            const_iterator cend() const noexcept
4412
            {
4413
                return values.cend();
4414
            }
4415
4416
            virtual void Reset() noexcept override
4417
            {
4418
                FlagBase::Reset();
4419
                values = defaultValues;
4420
            }
4421
4422
            virtual FlagBase *Match(const EitherFlag &arg) override
4423
            {
4424
                const bool wasMatched = Matched();
4425
                auto me = FlagBase::Match(arg);
4426
                if (me && !wasMatched)
4427
                {
4428
                    values.clear();
4429
                }
4430
                return me;
4431
            }
4432
    };
4433
4434
    /** An argument-accepting flag class that pushes the found values into a list
4435
     * 
4436
     * \tparam T the type to extract the argument as
4437
     * \tparam List the list type that houses the values
4438
     * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
4439
     */
4440
    template <
4441
        typename T,
4442
        template <typename...> class List = detail::vector,
4443
        typename Reader = ValueReader>
4444
    class ValueFlagList : public ValueFlagBase
4445
    {
4446
        private:
4447
            using Container = List<T>;
4448
            Container values;
4449
            const Container defaultValues;
4450
            Reader reader;
4451
4452
        public:
4453
4454
            typedef T value_type;
4455
            typedef typename Container::allocator_type allocator_type;
4456
            typedef typename Container::pointer pointer;
4457
            typedef typename Container::const_pointer const_pointer;
4458
            typedef T& reference;
4459
            typedef const T& const_reference;
4460
            typedef typename Container::size_type size_type;
4461
            typedef typename Container::difference_type difference_type;
4462
            typedef typename Container::iterator iterator;
4463
            typedef typename Container::const_iterator const_iterator;
4464
            typedef std::reverse_iterator<iterator> reverse_iterator;
4465
            typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4466
4467
            ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}):
4468
                ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_)
4469
            {
4470
                group_.Add(*this);
4471
            }
4472
4473
            virtual ~ValueFlagList() {}
4474
4475
            virtual void ParseValue(const std::vector<std::string> &values_) override
4476
            {
4477
                const std::string &value_ = values_.at(0);
4478
4479
                T v{};
4480
#ifdef ARGS_NOEXCEPT
4481
                if (!reader(name, value_, v))
4482
                {
4483
                    error = Error::Parse;
4484
                    return;
4485
                }
4486
#else
4487
                reader(name, value_, v);
4488
#endif
4489
                values.insert(std::end(values), v);
4490
            }
4491
4492
            /** Get the values
4493
             */
4494
            Container &Get() noexcept
4495
            {
4496
                return values;
4497
            }
4498
4499
            /** Get the value
4500
             */
4501
            Container &operator *() noexcept
4502
            {
4503
                return values;
4504
            }
4505
4506
            /** Get the values
4507
             */
4508
            const Container &operator *() const noexcept
4509
            {
4510
                return values;
4511
            }
4512
4513
            /** Get the values
4514
             */
4515
            Container *operator ->() noexcept
4516
            {
4517
                return &values;
4518
            }
4519
4520
            /** Get the values
4521
             */
4522
            const Container *operator ->() const noexcept
4523
            {
4524
                return &values;
4525
            }
4526
4527
            virtual std::string Name() const override
4528
            {
4529
                return name + std::string("...");
4530
            }
4531
4532
            virtual void Reset() noexcept override
4533
            {
4534
                ValueFlagBase::Reset();
4535
                values = defaultValues;
4536
            }
4537
4538
            virtual FlagBase *Match(const EitherFlag &arg) override
4539
            {
4540
                const bool wasMatched = Matched();
4541
                auto me = FlagBase::Match(arg);
4542
                if (me && !wasMatched)
4543
                {
4544
                    values.clear();
4545
                }
4546
                return me;
4547
            }
4548
4549
            iterator begin() noexcept
4550
            {
4551
                return values.begin();
4552
            }
4553
4554
            const_iterator begin() const noexcept
4555
            {
4556
                return values.begin();
4557
            }
4558
4559
            const_iterator cbegin() const noexcept
4560
            {
4561
                return values.cbegin();
4562
            }
4563
4564
            iterator end() noexcept
4565
            {
4566
                return values.end();
4567
            }
4568
4569
            const_iterator end() const noexcept 
4570
            {
4571
                return values.end();
4572
            }
4573
4574
            const_iterator cend() const noexcept
4575
            {
4576
                return values.cend();
4577
            }
4578
    };
4579
4580
    /** A mapping value flag class
4581
     * 
4582
     * \tparam K the type to extract the argument as
4583
     * \tparam T the type to store the result as
4584
     * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
4585
     * \tparam Map The Map type.  Should operate like std::map or std::unordered_map
4586
     */
4587
    template <
4588
        typename K,
4589
        typename T,
4590
        typename Reader = ValueReader,
4591
        template <typename...> class Map = detail::unordered_map>
4592
    class MapFlag : public ValueFlagBase
4593
    {
4594
        private:
4595
            const Map<K, T> map;
4596
            T value;
4597
            const T defaultValue;
4598
            Reader reader;
4599
4600
        protected:
4601
            virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4602
            {
4603
                return detail::MapKeysToStrings(map);
4604
            }
4605
4606
        public:
4607
4608
            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_)
4609
            {
4610
                group_.Add(*this);
4611
            }
4612
4613
            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)
4614
            {
4615
            }
4616
4617
            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_)
4618
            {
4619
            }
4620
4621
            virtual ~MapFlag() {}
4622
4623
            virtual void ParseValue(const std::vector<std::string> &values_) override
4624
            {
4625
                const std::string &value_ = values_.at(0);
4626
4627
                K key{};
4628
#ifdef ARGS_NOEXCEPT
4629
                if (!reader(name, value_, key))
4630
                {
4631
                    error = Error::Parse;
4632
                    return;
4633
                }
4634
#else
4635
                reader(name, value_, key);
4636
#endif
4637
                auto it = map.find(key);
4638
                if (it == std::end(map))
4639
                {
4640
                    std::ostringstream problem;
4641
                    problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4642
#ifdef ARGS_NOEXCEPT
4643
                    error = Error::Map;
4644
                    errorMsg = problem.str();
4645
#else
4646
                    throw MapError(problem.str());
4647
#endif
4648
                } else
4649
                {
4650
                    this->value = it->second;
4651
                }
4652
            }
4653
4654
            /** Get the value
4655
             */
4656
            T &Get() noexcept
4657
            {
4658
                return value;
4659
            }
4660
4661
            /** Get the value
4662
             */
4663
            T &operator *() noexcept
4664
            {
4665
                return value;
4666
            }
4667
4668
            /** Get the value
4669
             */
4670
            const T &operator *() const noexcept
4671
            {
4672
                return value;
4673
            }
4674
4675
            /** Get the value
4676
             */
4677
            T *operator ->() noexcept
4678
            {
4679
                return &value;
4680
            }
4681
4682
            /** Get the value
4683
             */
4684
            const T *operator ->() const noexcept
4685
            {
4686
                return &value;
4687
            }
4688
4689
            virtual void Reset() noexcept override
4690
            {
4691
                ValueFlagBase::Reset();
4692
                value = defaultValue;
4693
            }
4694
    };
4695
4696
    /** A mapping value flag list class
4697
     * 
4698
     * \tparam K the type to extract the argument as
4699
     * \tparam T the type to store the result as
4700
     * \tparam List the list type that houses the values
4701
     * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
4702
     * \tparam Map The Map type.  Should operate like std::map or std::unordered_map
4703
     */
4704
    template <
4705
        typename K,
4706
        typename T,
4707
        template <typename...> class List = detail::vector,
4708
        typename Reader = ValueReader,
4709
        template <typename...> class Map = detail::unordered_map>
4710
    class MapFlagList : public ValueFlagBase
4711
    {
4712
        private:
4713
            using Container = List<T>;
4714
            const Map<K, T> map;
4715
            Container values;
4716
            const Container defaultValues;
4717
            Reader reader;
4718
4719
        protected:
4720
            virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4721
            {
4722
                return detail::MapKeysToStrings(map);
4723
            }
4724
4725
        public:
4726
            typedef T value_type;
4727
            typedef typename Container::allocator_type allocator_type;
4728
            typedef typename Container::pointer pointer;
4729
            typedef typename Container::const_pointer const_pointer;
4730
            typedef T& reference;
4731
            typedef const T& const_reference;
4732
            typedef typename Container::size_type size_type;
4733
            typedef typename Container::difference_type difference_type;
4734
            typedef typename Container::iterator iterator;
4735
            typedef typename Container::const_iterator const_iterator;
4736
            typedef std::reverse_iterator<iterator> reverse_iterator;
4737
            typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4738
4739
            MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
4740
                ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
4741
            {
4742
                group_.Add(*this);
4743
            }
4744
4745
            virtual ~MapFlagList() {}
4746
4747
            virtual void ParseValue(const std::vector<std::string> &values_) override
4748
            {
4749
                const std::string &value_ = values_.at(0);
4750
4751
                K key{};
4752
#ifdef ARGS_NOEXCEPT
4753
                if (!reader(name, value_, key))
4754
                {
4755
                    error = Error::Parse;
4756
                    return;
4757
                }
4758
#else
4759
                reader(name, value_, key);
4760
#endif
4761
                auto it = map.find(key);
4762
                if (it == std::end(map))
4763
                {
4764
                    std::ostringstream problem;
4765
                    problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4766
#ifdef ARGS_NOEXCEPT
4767
                    error = Error::Map;
4768
                    errorMsg = problem.str();
4769
#else
4770
                    throw MapError(problem.str());
4771
#endif
4772
                } else
4773
                {
4774
                    this->values.emplace_back(it->second);
4775
                }
4776
            }
4777
4778
            /** Get the value
4779
             */
4780
            Container &Get() noexcept
4781
            {
4782
                return values;
4783
            }
4784
4785
            /** Get the value
4786
             */
4787
            Container &operator *() noexcept
4788
            {
4789
                return values;
4790
            }
4791
4792
            /** Get the values
4793
             */
4794
            const Container &operator *() const noexcept
4795
            {
4796
                return values;
4797
            }
4798
4799
            /** Get the values
4800
             */
4801
            Container *operator ->() noexcept
4802
            {
4803
                return &values;
4804
            }
4805
4806
            /** Get the values
4807
             */
4808
            const Container *operator ->() const noexcept
4809
            {
4810
                return &values;
4811
            }
4812
4813
            virtual std::string Name() const override
4814
            {
4815
                return name + std::string("...");
4816
            }
4817
4818
            virtual void Reset() noexcept override
4819
            {
4820
                ValueFlagBase::Reset();
4821
                values = defaultValues;
4822
            }
4823
4824
            virtual FlagBase *Match(const EitherFlag &arg) override
4825
            {
4826
                const bool wasMatched = Matched();
4827
                auto me = FlagBase::Match(arg);
4828
                if (me && !wasMatched)
4829
                {
4830
                    values.clear();
4831
                }
4832
                return me;
4833
            }
4834
4835
            iterator begin() noexcept
4836
            {
4837
                return values.begin();
4838
            }
4839
4840
            const_iterator begin() const noexcept
4841
            {
4842
                return values.begin();
4843
            }
4844
4845
            const_iterator cbegin() const noexcept
4846
            {
4847
                return values.cbegin();
4848
            }
4849
4850
            iterator end() noexcept
4851
            {
4852
                return values.end();
4853
            }
4854
4855
            const_iterator end() const noexcept 
4856
            {
4857
                return values.end();
4858
            }
4859
4860
            const_iterator cend() const noexcept
4861
            {
4862
                return values.cend();
4863
            }
4864
    };
4865
4866
    /** A positional argument class
4867
     *
4868
     * \tparam T the type to extract the argument as
4869
     * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
4870
     */
4871
    template <
4872
        typename T,
4873
        typename Reader = ValueReader>
4874
    class Positional : public PositionalBase
4875
    {
4876
        private:
4877
            T value;
4878
            const T defaultValue;
4879
            Reader reader;
4880
        public:
4881
            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_)
4882
            {
4883
                group_.Add(*this);
4884
            }
4885
4886
            Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_)
4887
            {
4888
            }
4889
4890
            virtual ~Positional() {}
4891
4892
            virtual void ParseValue(const std::string &value_) override
4893
            {
4894
#ifdef ARGS_NOEXCEPT
4895
                if (!reader(name, value_, this->value))
4896
                {
4897
                    error = Error::Parse;
4898
                    return;
4899
                }
4900
#else
4901
                reader(name, value_, this->value);
4902
#endif
4903
                ready = false;
4904
                matched = true;
4905
            }
4906
4907
            /** Get the value
4908
             */
4909
            T &Get() noexcept
4910
            {
4911
                return value;
4912
            }
4913
4914
            /** Get the value
4915
             */
4916
            T &operator *() noexcept
4917
            {
4918
                return value;
4919
            }
4920
4921
            /** Get the value
4922
             */
4923
            const T &operator *() const noexcept
4924
            {
4925
                return value;
4926
            }
4927
4928
            /** Get the value
4929
             */
4930
            T *operator ->() noexcept
4931
            {
4932
                return &value;
4933
            }
4934
4935
            /** Get the value
4936
             */
4937
            const T *operator ->() const noexcept
4938
            {
4939
                return &value;
4940
            }
4941
4942
            virtual void Reset() noexcept override
4943
            {
4944
                PositionalBase::Reset();
4945
                value = defaultValue;
4946
            }
4947
    };
4948
4949
    /** A positional argument class that pushes the found values into a list
4950
     * 
4951
     * \tparam T the type to extract the argument as
4952
     * \tparam List the list type that houses the values
4953
     * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
4954
     */
4955
    template <
4956
        typename T,
4957
        template <typename...> class List = detail::vector,
4958
        typename Reader = ValueReader>
4959
    class PositionalList : public PositionalBase
4960
    {
4961
        private:
4962
            using Container = List<T>;
4963
            Container values;
4964
            const Container defaultValues;
4965
            Reader reader;
4966
4967
        public:
4968
            typedef T value_type;
4969
            typedef typename Container::allocator_type allocator_type;
4970
            typedef typename Container::pointer pointer;
4971
            typedef typename Container::const_pointer const_pointer;
4972
            typedef T& reference;
4973
            typedef const T& const_reference;
4974
            typedef typename Container::size_type size_type;
4975
            typedef typename Container::difference_type difference_type;
4976
            typedef typename Container::iterator iterator;
4977
            typedef typename Container::const_iterator const_iterator;
4978
            typedef std::reverse_iterator<iterator> reverse_iterator;
4979
            typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4980
4981
            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_)
4982
            {
4983
                group_.Add(*this);
4984
            }
4985
4986
            PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_)
4987
            {
4988
            }
4989
4990
            virtual ~PositionalList() {}
4991
4992
            virtual void ParseValue(const std::string &value_) override
4993
            {
4994
                T v{};
4995
#ifdef ARGS_NOEXCEPT
4996
                if (!reader(name, value_, v))
4997
                {
4998
                    error = Error::Parse;
4999
                    return;
5000
                }
5001
#else
5002
                reader(name, value_, v);
5003
#endif
5004
                values.insert(std::end(values), v);
5005
                matched = true;
5006
            }
5007
5008
            virtual std::string Name() const override
5009
            {
5010
                return name + std::string("...");
5011
            }
5012
5013
            /** Get the values
5014
             */
5015
            Container &Get() noexcept
5016
            {
5017
                return values;
5018
            }
5019
5020
            /** Get the value
5021
             */
5022
            Container &operator *() noexcept
5023
            {
5024
                return values;
5025
            }
5026
5027
            /** Get the values
5028
             */
5029
            const Container &operator *() const noexcept
5030
            {
5031
                return values;
5032
            }
5033
5034
            /** Get the values
5035
             */
5036
            Container *operator ->() noexcept
5037
            {
5038
                return &values;
5039
            }
5040
5041
            /** Get the values
5042
             */
5043
            const Container *operator ->() const noexcept
5044
            {
5045
                return &values;
5046
            }
5047
5048
            virtual void Reset() noexcept override
5049
            {
5050
                PositionalBase::Reset();
5051
                values = defaultValues;
5052
            }
5053
5054
            virtual PositionalBase *GetNextPositional() override
5055
            {
5056
                const bool wasMatched = Matched();
5057
                auto me = PositionalBase::GetNextPositional();
5058
                if (me && !wasMatched)
5059
                {
5060
                    values.clear();
5061
                }
5062
                return me;
5063
            }
5064
5065
            iterator begin() noexcept
5066
            {
5067
                return values.begin();
5068
            }
5069
5070
            const_iterator begin() const noexcept
5071
            {
5072
                return values.begin();
5073
            }
5074
5075
            const_iterator cbegin() const noexcept
5076
            {
5077
                return values.cbegin();
5078
            }
5079
5080
            iterator end() noexcept
5081
            {
5082
                return values.end();
5083
            }
5084
5085
            const_iterator end() const noexcept 
5086
            {
5087
                return values.end();
5088
            }
5089
5090
            const_iterator cend() const noexcept
5091
            {
5092
                return values.cend();
5093
            }
5094
    };
5095
5096
    /** A positional argument mapping class
5097
     * 
5098
     * \tparam K the type to extract the argument as
5099
     * \tparam T the type to store the result as
5100
     * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
5101
     * \tparam Map The Map type.  Should operate like std::map or std::unordered_map
5102
     */
5103
    template <
5104
        typename K,
5105
        typename T,
5106
        typename Reader = ValueReader,
5107
        template <typename...> class Map = detail::unordered_map>
5108
    class MapPositional : public PositionalBase
5109
    {
5110
        private:
5111
            const Map<K, T> map;
5112
            T value;
5113
            const T defaultValue;
5114
            Reader reader;
5115
5116
        protected:
5117
            virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
5118
            {
5119
                return detail::MapKeysToStrings(map);
5120
            }
5121
5122
        public:
5123
5124
            MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}):
5125
                PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_)
5126
            {
5127
                group_.Add(*this);
5128
            }
5129
5130
            virtual ~MapPositional() {}
5131
5132
            virtual void ParseValue(const std::string &value_) override
5133
            {
5134
                K key{};
5135
#ifdef ARGS_NOEXCEPT
5136
                if (!reader(name, value_, key))
5137
                {
5138
                    error = Error::Parse;
5139
                    return;
5140
                }
5141
#else
5142
                reader(name, value_, key);
5143
#endif
5144
                auto it = map.find(key);
5145
                if (it == std::end(map))
5146
                {
5147
                    std::ostringstream problem;
5148
                    problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
5149
#ifdef ARGS_NOEXCEPT
5150
                    error = Error::Map;
5151
                    errorMsg = problem.str();
5152
#else
5153
                    throw MapError(problem.str());
5154
#endif
5155
                } else
5156
                {
5157
                    this->value = it->second;
5158
                    ready = false;
5159
                    matched = true;
5160
                }
5161
            }
5162
5163
            /** Get the value
5164
             */
5165
            T &Get() noexcept
5166
            {
5167
                return value;
5168
            }
5169
5170
            /** Get the value
5171
             */
5172
            T &operator *() noexcept
5173
            {
5174
                return value;
5175
            }
5176
5177
            /** Get the value
5178
             */
5179
            const T &operator *() const noexcept
5180
            {
5181
                return value;
5182
            }
5183
5184
            /** Get the value
5185
             */
5186
            T *operator ->() noexcept
5187
            {
5188
                return &value;
5189
            }
5190
5191
            /** Get the value
5192
             */
5193
            const T *operator ->() const noexcept
5194
            {
5195
                return &value;
5196
            }
5197
5198
            virtual void Reset() noexcept override
5199
            {
5200
                PositionalBase::Reset();
5201
                value = defaultValue;
5202
            }
5203
    };
5204
5205
    /** A positional argument mapping list class
5206
     * 
5207
     * \tparam K the type to extract the argument as
5208
     * \tparam T the type to store the result as
5209
     * \tparam List the list type that houses the values
5210
     * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined)
5211
     * \tparam Map The Map type.  Should operate like std::map or std::unordered_map
5212
     */
5213
    template <
5214
        typename K,
5215
        typename T,
5216
        template <typename...> class List = detail::vector,
5217
        typename Reader = ValueReader,
5218
        template <typename...> class Map = detail::unordered_map>
5219
    class MapPositionalList : public PositionalBase
5220
    {
5221
        private:
5222
            using Container = List<T>;
5223
5224
            const Map<K, T> map;
5225
            Container values;
5226
            const Container defaultValues;
5227
            Reader reader;
5228
5229
        protected:
5230
            virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
5231
            {
5232
                return detail::MapKeysToStrings(map);
5233
            }
5234
5235
        public:
5236
            typedef T value_type;
5237
            typedef typename Container::allocator_type allocator_type;
5238
            typedef typename Container::pointer pointer;
5239
            typedef typename Container::const_pointer const_pointer;
5240
            typedef T& reference;
5241
            typedef const T& const_reference;
5242
            typedef typename Container::size_type size_type;
5243
            typedef typename Container::difference_type difference_type;
5244
            typedef typename Container::iterator iterator;
5245
            typedef typename Container::const_iterator const_iterator;
5246
            typedef std::reverse_iterator<iterator> reverse_iterator;
5247
            typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
5248
5249
            MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
5250
                PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
5251
            {
5252
                group_.Add(*this);
5253
            }
5254
5255
            virtual ~MapPositionalList() {}
5256
5257
            virtual void ParseValue(const std::string &value_) override
5258
            {
5259
                K key{};
5260
#ifdef ARGS_NOEXCEPT
5261
                if (!reader(name, value_, key))
5262
                {
5263
                    error = Error::Parse;
5264
                    return;
5265
                }
5266
#else
5267
                reader(name, value_, key);
5268
#endif
5269
                auto it = map.find(key);
5270
                if (it == std::end(map))
5271
                {
5272
                    std::ostringstream problem;
5273
                    problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
5274
#ifdef ARGS_NOEXCEPT
5275
                    error = Error::Map;
5276
                    errorMsg = problem.str();
5277
#else
5278
                    throw MapError(problem.str());
5279
#endif
5280
                } else
5281
                {
5282
                    this->values.emplace_back(it->second);
5283
                    matched = true;
5284
                }
5285
            }
5286
5287
            /** Get the value
5288
             */
5289
            Container &Get() noexcept
5290
            {
5291
                return values;
5292
            }
5293
5294
            /** Get the value
5295
             */
5296
            Container &operator *() noexcept
5297
            {
5298
                return values;
5299
            }
5300
5301
            /** Get the values
5302
             */
5303
            const Container &operator *() const noexcept
5304
            {
5305
                return values;
5306
            }
5307
5308
            /** Get the values
5309
             */
5310
            Container *operator ->() noexcept
5311
            {
5312
                return &values;
5313
            }
5314
5315
            /** Get the values
5316
             */
5317
            const Container *operator ->() const noexcept
5318
            {
5319
                return &values;
5320
            }
5321
5322
            virtual std::string Name() const override
5323
            {
5324
                return name + std::string("...");
5325
            }
5326
5327
            virtual void Reset() noexcept override
5328
            {
5329
                PositionalBase::Reset();
5330
                values = defaultValues;
5331
            }
5332
5333
            virtual PositionalBase *GetNextPositional() override
5334
            {
5335
                const bool wasMatched = Matched();
5336
                auto me = PositionalBase::GetNextPositional();
5337
                if (me && !wasMatched)
5338
                {
5339
                    values.clear();
5340
                }
5341
                return me;
5342
            }
5343
5344
            iterator begin() noexcept
5345
            {
5346
                return values.begin();
5347
            }
5348
5349
            const_iterator begin() const noexcept
5350
            {
5351
                return values.begin();
5352
            }
5353
5354
            const_iterator cbegin() const noexcept
5355
            {
5356
                return values.cbegin();
5357
            }
5358
5359
            iterator end() noexcept
5360
            {
5361
                return values.end();
5362
            }
5363
5364
            const_iterator end() const noexcept 
5365
            {
5366
                return values.end();
5367
            }
5368
5369
            const_iterator cend() const noexcept
5370
            {
5371
                return values.cend();
5372
            }
5373
    };
5374
}
5375
5376
#pragma pop_macro("min")
5377
#pragma pop_macro("max")
5378
#endif