Coverage Report

Created: 2026-11-13 06:30

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