webrtc-streamer 0.8.13 → 0.8.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2925 @@
1
+ /*
2
+
3
+ Copyright (c) 2014-2022 Jarryd Beck
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
23
+ */
24
+
25
+ // vim: ts=2:sw=2:expandtab
26
+
27
+ #ifndef CXXOPTS_HPP_INCLUDED
28
+ #define CXXOPTS_HPP_INCLUDED
29
+
30
+ #include <cstdint>
31
+ #include <cstdlib>
32
+ #include <cstring>
33
+ #include <exception>
34
+ #include <limits>
35
+ #include <initializer_list>
36
+ #include <map>
37
+ #include <memory>
38
+ #include <sstream>
39
+ #include <string>
40
+ #include <unordered_map>
41
+ #include <unordered_set>
42
+ #include <utility>
43
+ #include <vector>
44
+ #include <algorithm>
45
+ #include <locale>
46
+
47
+ #ifdef CXXOPTS_NO_EXCEPTIONS
48
+ #include <iostream>
49
+ #endif
50
+
51
+ #if defined(__GNUC__) && !defined(__clang__)
52
+ # if (__GNUC__ * 10 + __GNUC_MINOR__) < 49
53
+ # define CXXOPTS_NO_REGEX true
54
+ # endif
55
+ #endif
56
+ #if defined(_MSC_VER) && !defined(__clang__)
57
+ #define CXXOPTS_LINKONCE_CONST __declspec(selectany) extern
58
+ #define CXXOPTS_LINKONCE __declspec(selectany) extern
59
+ #else
60
+ #define CXXOPTS_LINKONCE_CONST
61
+ #define CXXOPTS_LINKONCE
62
+ #endif
63
+
64
+ #ifndef CXXOPTS_NO_REGEX
65
+ # include <regex>
66
+ #endif // CXXOPTS_NO_REGEX
67
+
68
+ // Nonstandard before C++17, which is coincidentally what we also need for <optional>
69
+ #ifdef __has_include
70
+ # if __has_include(<optional>)
71
+ # include <optional>
72
+ # ifdef __cpp_lib_optional
73
+ # define CXXOPTS_HAS_OPTIONAL
74
+ # endif
75
+ # endif
76
+ # if __has_include(<filesystem>)
77
+ # include <filesystem>
78
+ # ifdef __cpp_lib_filesystem
79
+ # define CXXOPTS_HAS_FILESYSTEM
80
+ # endif
81
+ # endif
82
+ #endif
83
+
84
+ #define CXXOPTS_FALLTHROUGH
85
+ #ifdef __has_cpp_attribute
86
+ #if __has_cpp_attribute(fallthrough)
87
+ #undef CXXOPTS_FALLTHROUGH
88
+ #define CXXOPTS_FALLTHROUGH [[fallthrough]]
89
+ #endif
90
+ #endif
91
+
92
+ #if __cplusplus >= 201603L
93
+ #define CXXOPTS_NODISCARD [[nodiscard]]
94
+ #else
95
+ #define CXXOPTS_NODISCARD
96
+ #endif
97
+
98
+ #ifndef CXXOPTS_VECTOR_DELIMITER
99
+ #define CXXOPTS_VECTOR_DELIMITER ','
100
+ #endif
101
+
102
+ #define CXXOPTS__VERSION_MAJOR 3
103
+ #define CXXOPTS__VERSION_MINOR 3
104
+ #define CXXOPTS__VERSION_PATCH 1
105
+
106
+ #if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
107
+ #define CXXOPTS_NULL_DEREF_IGNORE
108
+ #endif
109
+
110
+ #if defined(__GNUC__)
111
+ #define DO_PRAGMA(x) _Pragma(#x)
112
+ #define CXXOPTS_DIAGNOSTIC_PUSH DO_PRAGMA(GCC diagnostic push)
113
+ #define CXXOPTS_DIAGNOSTIC_POP DO_PRAGMA(GCC diagnostic pop)
114
+ #define CXXOPTS_IGNORE_WARNING(x) DO_PRAGMA(GCC diagnostic ignored x)
115
+ #else
116
+ // define other compilers here if needed
117
+ #define CXXOPTS_DIAGNOSTIC_PUSH
118
+ #define CXXOPTS_DIAGNOSTIC_POP
119
+ #define CXXOPTS_IGNORE_WARNING(x)
120
+ #endif
121
+
122
+ #ifdef CXXOPTS_NO_RTTI
123
+ #define CXXOPTS_RTTI_CAST static_cast
124
+ #else
125
+ #define CXXOPTS_RTTI_CAST dynamic_cast
126
+ #endif
127
+
128
+ namespace cxxopts {
129
+ static constexpr struct {
130
+ uint8_t major, minor, patch;
131
+ } version = {
132
+ CXXOPTS__VERSION_MAJOR,
133
+ CXXOPTS__VERSION_MINOR,
134
+ CXXOPTS__VERSION_PATCH
135
+ };
136
+ } // namespace cxxopts
137
+
138
+ //when we ask cxxopts to use Unicode, help strings are processed using ICU,
139
+ //which results in the correct lengths being computed for strings when they
140
+ //are formatted for the help output
141
+ //it is necessary to make sure that <unicode/unistr.h> can be found by the
142
+ //compiler, and that icu-uc is linked in to the binary.
143
+
144
+ #ifdef CXXOPTS_USE_UNICODE
145
+ #include <unicode/unistr.h>
146
+
147
+ namespace cxxopts {
148
+
149
+ using String = icu::UnicodeString;
150
+
151
+ inline
152
+ String
153
+ toLocalString(std::string s)
154
+ {
155
+ return icu::UnicodeString::fromUTF8(std::move(s));
156
+ }
157
+
158
+ // GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it:
159
+ // warning: base class 'class std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual destructor
160
+ CXXOPTS_DIAGNOSTIC_PUSH
161
+ CXXOPTS_IGNORE_WARNING("-Wnon-virtual-dtor")
162
+ // This will be ignored under other compilers like LLVM clang.
163
+ class UnicodeStringIterator
164
+ {
165
+ public:
166
+
167
+ using iterator_category = std::forward_iterator_tag;
168
+ using value_type = int32_t;
169
+ using difference_type = std::ptrdiff_t;
170
+ using pointer = value_type*;
171
+ using reference = value_type&;
172
+
173
+ UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos)
174
+ : s(string)
175
+ , i(pos)
176
+ {
177
+ }
178
+
179
+ value_type
180
+ operator*() const
181
+ {
182
+ return s->char32At(i);
183
+ }
184
+
185
+ bool
186
+ operator==(const UnicodeStringIterator& rhs) const
187
+ {
188
+ return s == rhs.s && i == rhs.i;
189
+ }
190
+
191
+ bool
192
+ operator!=(const UnicodeStringIterator& rhs) const
193
+ {
194
+ return !(*this == rhs);
195
+ }
196
+
197
+ UnicodeStringIterator&
198
+ operator++()
199
+ {
200
+ ++i;
201
+ return *this;
202
+ }
203
+
204
+ UnicodeStringIterator
205
+ operator+(int32_t v)
206
+ {
207
+ return UnicodeStringIterator(s, i + v);
208
+ }
209
+
210
+ private:
211
+ const icu::UnicodeString* s;
212
+ int32_t i;
213
+ };
214
+ CXXOPTS_DIAGNOSTIC_POP
215
+
216
+ inline
217
+ String&
218
+ stringAppend(String&s, String a)
219
+ {
220
+ return s.append(std::move(a));
221
+ }
222
+
223
+ inline
224
+ String&
225
+ stringAppend(String& s, std::size_t n, UChar32 c)
226
+ {
227
+ for (std::size_t i = 0; i != n; ++i)
228
+ {
229
+ s.append(c);
230
+ }
231
+
232
+ return s;
233
+ }
234
+
235
+ template <typename Iterator>
236
+ String&
237
+ stringAppend(String& s, Iterator begin, Iterator end)
238
+ {
239
+ while (begin != end)
240
+ {
241
+ s.append(*begin);
242
+ ++begin;
243
+ }
244
+
245
+ return s;
246
+ }
247
+
248
+ inline
249
+ size_t
250
+ stringLength(const String& s)
251
+ {
252
+ return static_cast<size_t>(s.length());
253
+ }
254
+
255
+ inline
256
+ std::string
257
+ toUTF8String(const String& s)
258
+ {
259
+ std::string result;
260
+ s.toUTF8String(result);
261
+
262
+ return result;
263
+ }
264
+
265
+ inline
266
+ bool
267
+ empty(const String& s)
268
+ {
269
+ return s.isEmpty();
270
+ }
271
+
272
+ } // namespace cxxopts
273
+
274
+ namespace std {
275
+
276
+ inline
277
+ cxxopts::UnicodeStringIterator
278
+ begin(const icu::UnicodeString& s)
279
+ {
280
+ return cxxopts::UnicodeStringIterator(&s, 0);
281
+ }
282
+
283
+ inline
284
+ cxxopts::UnicodeStringIterator
285
+ end(const icu::UnicodeString& s)
286
+ {
287
+ return cxxopts::UnicodeStringIterator(&s, s.length());
288
+ }
289
+
290
+ } // namespace std
291
+
292
+ //ifdef CXXOPTS_USE_UNICODE
293
+ #else
294
+
295
+ namespace cxxopts {
296
+
297
+ using String = std::string;
298
+
299
+ template <typename T>
300
+ T
301
+ toLocalString(T&& t)
302
+ {
303
+ return std::forward<T>(t);
304
+ }
305
+
306
+ inline
307
+ std::size_t
308
+ stringLength(const String& s)
309
+ {
310
+ return s.length();
311
+ }
312
+
313
+ inline
314
+ String&
315
+ stringAppend(String&s, const String& a)
316
+ {
317
+ return s.append(a);
318
+ }
319
+
320
+ inline
321
+ String&
322
+ stringAppend(String& s, std::size_t n, char c)
323
+ {
324
+ return s.append(n, c);
325
+ }
326
+
327
+ template <typename Iterator>
328
+ String&
329
+ stringAppend(String& s, Iterator begin, Iterator end)
330
+ {
331
+ return s.append(begin, end);
332
+ }
333
+
334
+ template <typename T>
335
+ std::string
336
+ toUTF8String(T&& t)
337
+ {
338
+ return std::forward<T>(t);
339
+ }
340
+
341
+ inline
342
+ bool
343
+ empty(const std::string& s)
344
+ {
345
+ return s.empty();
346
+ }
347
+
348
+ } // namespace cxxopts
349
+
350
+ //ifdef CXXOPTS_USE_UNICODE
351
+ #endif
352
+
353
+ namespace cxxopts {
354
+
355
+ namespace {
356
+ CXXOPTS_LINKONCE_CONST std::string LQUOTE("\'");
357
+ CXXOPTS_LINKONCE_CONST std::string RQUOTE("\'");
358
+ } // namespace
359
+
360
+ // GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we
361
+ // want to silence it: warning: base class 'class
362
+ // std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual
363
+ // destructor This will be ignored under other compilers like LLVM clang.
364
+ CXXOPTS_DIAGNOSTIC_PUSH
365
+ CXXOPTS_IGNORE_WARNING("-Wnon-virtual-dtor")
366
+
367
+ // some older versions of GCC warn under this warning
368
+ CXXOPTS_IGNORE_WARNING("-Weffc++")
369
+ class Value : public std::enable_shared_from_this<Value>
370
+ {
371
+ public:
372
+
373
+ virtual ~Value() = default;
374
+
375
+ virtual
376
+ std::shared_ptr<Value>
377
+ clone() const = 0;
378
+
379
+ virtual void
380
+ add(const std::string& text) const = 0;
381
+
382
+ virtual void
383
+ parse(const std::string& text) const = 0;
384
+
385
+ virtual void
386
+ parse() const = 0;
387
+
388
+ virtual bool
389
+ has_default() const = 0;
390
+
391
+ virtual bool
392
+ is_container() const = 0;
393
+
394
+ virtual bool
395
+ has_implicit() const = 0;
396
+
397
+ virtual std::string
398
+ get_default_value() const = 0;
399
+
400
+ virtual std::string
401
+ get_implicit_value() const = 0;
402
+
403
+ virtual std::shared_ptr<Value>
404
+ default_value(const std::string& value) = 0;
405
+
406
+ virtual std::shared_ptr<Value>
407
+ implicit_value(const std::string& value) = 0;
408
+
409
+ virtual std::shared_ptr<Value>
410
+ no_implicit_value() = 0;
411
+
412
+ virtual bool
413
+ is_boolean() const = 0;
414
+ };
415
+
416
+ CXXOPTS_DIAGNOSTIC_POP
417
+
418
+ namespace exceptions {
419
+
420
+ class exception : public std::exception
421
+ {
422
+ public:
423
+ explicit exception(std::string message)
424
+ : m_message(std::move(message))
425
+ {
426
+ }
427
+
428
+ CXXOPTS_NODISCARD
429
+ const char*
430
+ what() const noexcept override
431
+ {
432
+ return m_message.c_str();
433
+ }
434
+
435
+ private:
436
+ std::string m_message;
437
+ };
438
+
439
+ class specification : public exception
440
+ {
441
+ public:
442
+
443
+ explicit specification(const std::string& message)
444
+ : exception(message)
445
+ {
446
+ }
447
+ };
448
+
449
+ class parsing : public exception
450
+ {
451
+ public:
452
+ explicit parsing(const std::string& message)
453
+ : exception(message)
454
+ {
455
+ }
456
+ };
457
+
458
+ class option_already_exists : public specification
459
+ {
460
+ public:
461
+ explicit option_already_exists(const std::string& option)
462
+ : specification("Option " + LQUOTE + option + RQUOTE + " already exists")
463
+ {
464
+ }
465
+ };
466
+
467
+ class invalid_option_format : public specification
468
+ {
469
+ public:
470
+ explicit invalid_option_format(const std::string& format)
471
+ : specification("Invalid option format " + LQUOTE + format + RQUOTE)
472
+ {
473
+ }
474
+ };
475
+
476
+ class invalid_option_syntax : public parsing {
477
+ public:
478
+ explicit invalid_option_syntax(const std::string& text)
479
+ : parsing("Argument " + LQUOTE + text + RQUOTE +
480
+ " starts with a - but has incorrect syntax")
481
+ {
482
+ }
483
+ };
484
+
485
+ class no_such_option : public parsing
486
+ {
487
+ public:
488
+ explicit no_such_option(const std::string& option)
489
+ : parsing("Option " + LQUOTE + option + RQUOTE + " does not exist")
490
+ {
491
+ }
492
+ };
493
+
494
+ class missing_argument : public parsing
495
+ {
496
+ public:
497
+ explicit missing_argument(const std::string& option)
498
+ : parsing(
499
+ "Option " + LQUOTE + option + RQUOTE + " is missing an argument"
500
+ )
501
+ {
502
+ }
503
+ };
504
+
505
+ class option_requires_argument : public parsing
506
+ {
507
+ public:
508
+ explicit option_requires_argument(const std::string& option)
509
+ : parsing(
510
+ "Option " + LQUOTE + option + RQUOTE + " requires an argument"
511
+ )
512
+ {
513
+ }
514
+ };
515
+
516
+ class gratuitous_argument_for_option : public parsing
517
+ {
518
+ public:
519
+ gratuitous_argument_for_option
520
+ (
521
+ const std::string& option,
522
+ const std::string& arg
523
+ )
524
+ : parsing(
525
+ "Option " + LQUOTE + option + RQUOTE +
526
+ " does not take an argument, but argument " +
527
+ LQUOTE + arg + RQUOTE + " given"
528
+ )
529
+ {
530
+ }
531
+ };
532
+
533
+ class requested_option_not_present : public parsing
534
+ {
535
+ public:
536
+ explicit requested_option_not_present(const std::string& option)
537
+ : parsing("Option " + LQUOTE + option + RQUOTE + " not present")
538
+ {
539
+ }
540
+ };
541
+
542
+ class option_has_no_value : public exception
543
+ {
544
+ public:
545
+ explicit option_has_no_value(const std::string& option)
546
+ : exception(
547
+ !option.empty() ?
548
+ ("Option " + LQUOTE + option + RQUOTE + " has no value") :
549
+ "Option has no value")
550
+ {
551
+ }
552
+ };
553
+
554
+ class incorrect_argument_type : public parsing
555
+ {
556
+ public:
557
+ explicit incorrect_argument_type
558
+ (
559
+ const std::string& arg
560
+ )
561
+ : parsing(
562
+ "Argument " + LQUOTE + arg + RQUOTE + " failed to parse"
563
+ )
564
+ {
565
+ }
566
+ };
567
+
568
+ } // namespace exceptions
569
+
570
+
571
+ template <typename T>
572
+ void throw_or_mimic(const std::string& text)
573
+ {
574
+ static_assert(std::is_base_of<std::exception, T>::value,
575
+ "throw_or_mimic only works on std::exception and "
576
+ "deriving classes");
577
+
578
+ #ifndef CXXOPTS_NO_EXCEPTIONS
579
+ // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw
580
+ throw T{text};
581
+ #else
582
+ // Otherwise manually instantiate the exception, print what() to stderr,
583
+ // and exit
584
+ T exception{text};
585
+ std::cerr << exception.what() << std::endl;
586
+ std::exit(EXIT_FAILURE);
587
+ #endif
588
+ }
589
+
590
+ using OptionNames = std::vector<std::string>;
591
+
592
+ namespace values {
593
+
594
+ namespace parser_tool {
595
+
596
+ struct IntegerDesc
597
+ {
598
+ std::string negative = "";
599
+ std::string base = "";
600
+ std::string value = "";
601
+ };
602
+ struct ArguDesc {
603
+ std::string arg_name = "";
604
+ bool grouping = false;
605
+ bool set_value = false;
606
+ std::string value = "";
607
+ };
608
+
609
+ #ifdef CXXOPTS_NO_REGEX
610
+ inline IntegerDesc SplitInteger(const std::string &text)
611
+ {
612
+ if (text.empty())
613
+ {
614
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
615
+ }
616
+ IntegerDesc desc;
617
+ const char *pdata = text.c_str();
618
+ if (*pdata == '-')
619
+ {
620
+ pdata += 1;
621
+ desc.negative = "-";
622
+ }
623
+ if (strncmp(pdata, "0x", 2) == 0)
624
+ {
625
+ pdata += 2;
626
+ desc.base = "0x";
627
+ }
628
+ if (*pdata != '\0')
629
+ {
630
+ desc.value = std::string(pdata);
631
+ }
632
+ else
633
+ {
634
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
635
+ }
636
+ return desc;
637
+ }
638
+
639
+ inline bool IsTrueText(const std::string &text)
640
+ {
641
+ const char *pdata = text.c_str();
642
+ if (*pdata == 't' || *pdata == 'T')
643
+ {
644
+ pdata += 1;
645
+ if (strncmp(pdata, "rue\0", 4) == 0)
646
+ {
647
+ return true;
648
+ }
649
+ }
650
+ else if (strncmp(pdata, "1\0", 2) == 0)
651
+ {
652
+ return true;
653
+ }
654
+ return false;
655
+ }
656
+
657
+ inline bool IsFalseText(const std::string &text)
658
+ {
659
+ const char *pdata = text.c_str();
660
+ if (*pdata == 'f' || *pdata == 'F')
661
+ {
662
+ pdata += 1;
663
+ if (strncmp(pdata, "alse\0", 5) == 0)
664
+ {
665
+ return true;
666
+ }
667
+ }
668
+ else if (strncmp(pdata, "0\0", 2) == 0)
669
+ {
670
+ return true;
671
+ }
672
+ return false;
673
+ }
674
+
675
+ inline OptionNames split_option_names(const std::string &text)
676
+ {
677
+ OptionNames split_names;
678
+
679
+ std::string::size_type token_start_pos = 0;
680
+ auto length = text.length();
681
+
682
+ if (length == 0)
683
+ {
684
+ throw_or_mimic<exceptions::invalid_option_format>(text);
685
+ }
686
+
687
+ while (token_start_pos < length) {
688
+ const auto &npos = std::string::npos;
689
+ auto next_non_space_pos = text.find_first_not_of(' ', token_start_pos);
690
+ if (next_non_space_pos == npos) {
691
+ throw_or_mimic<exceptions::invalid_option_format>(text);
692
+ }
693
+ token_start_pos = next_non_space_pos;
694
+ auto next_delimiter_pos = text.find(',', token_start_pos);
695
+ if (next_delimiter_pos == token_start_pos) {
696
+ throw_or_mimic<exceptions::invalid_option_format>(text);
697
+ }
698
+ if (next_delimiter_pos == npos) {
699
+ next_delimiter_pos = length;
700
+ }
701
+ auto token_length = next_delimiter_pos - token_start_pos;
702
+ // validate the token itself matches the regex /([:alnum:][-_[:alnum:]]*/
703
+ {
704
+ const char* option_name_valid_chars =
705
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
706
+ "abcdefghijklmnopqrstuvwxyz"
707
+ "0123456789"
708
+ "_-.?";
709
+
710
+ if (!std::isalnum(text[token_start_pos], std::locale::classic()) ||
711
+ text.find_first_not_of(option_name_valid_chars, token_start_pos) < next_delimiter_pos) {
712
+ throw_or_mimic<exceptions::invalid_option_format>(text);
713
+ }
714
+ }
715
+ split_names.emplace_back(text.substr(token_start_pos, token_length));
716
+ token_start_pos = next_delimiter_pos + 1;
717
+ }
718
+ return split_names;
719
+ }
720
+
721
+ inline ArguDesc ParseArgument(const char *arg, bool &matched)
722
+ {
723
+ ArguDesc argu_desc;
724
+ const char *pdata = arg;
725
+ matched = false;
726
+ if (strncmp(pdata, "--", 2) == 0)
727
+ {
728
+ pdata += 2;
729
+ if (isalnum(*pdata, std::locale::classic()))
730
+ {
731
+ argu_desc.arg_name.push_back(*pdata);
732
+ pdata += 1;
733
+ while (isalnum(*pdata, std::locale::classic()) || *pdata == '-' || *pdata == '_')
734
+ {
735
+ argu_desc.arg_name.push_back(*pdata);
736
+ pdata += 1;
737
+ }
738
+ if (argu_desc.arg_name.length() > 1)
739
+ {
740
+ if (*pdata == '=')
741
+ {
742
+ argu_desc.set_value = true;
743
+ pdata += 1;
744
+ if (*pdata != '\0')
745
+ {
746
+ argu_desc.value = std::string(pdata);
747
+ }
748
+ matched = true;
749
+ }
750
+ else if (*pdata == '\0')
751
+ {
752
+ matched = true;
753
+ }
754
+ }
755
+ }
756
+ }
757
+ else if (strncmp(pdata, "-", 1) == 0)
758
+ {
759
+ pdata += 1;
760
+ argu_desc.grouping = true;
761
+ while (isalnum(*pdata, std::locale::classic()))
762
+ {
763
+ argu_desc.arg_name.push_back(*pdata);
764
+ pdata += 1;
765
+ }
766
+ matched = !argu_desc.arg_name.empty() && *pdata == '\0';
767
+ }
768
+ return argu_desc;
769
+ }
770
+
771
+ #else // CXXOPTS_NO_REGEX
772
+
773
+ namespace {
774
+ CXXOPTS_LINKONCE
775
+ const char* const integer_pattern =
776
+ "(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)";
777
+ CXXOPTS_LINKONCE
778
+ const char* const truthy_pattern =
779
+ "(t|T)(rue)?|1";
780
+ CXXOPTS_LINKONCE
781
+ const char* const falsy_pattern =
782
+ "(f|F)(alse)?|0";
783
+ CXXOPTS_LINKONCE
784
+ const char* const option_pattern =
785
+ "--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)";
786
+ CXXOPTS_LINKONCE
787
+ const char* const option_specifier_pattern =
788
+ "([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*";
789
+ CXXOPTS_LINKONCE
790
+ const char* const option_specifier_separator_pattern = ", *";
791
+
792
+ } // namespace
793
+
794
+ inline IntegerDesc SplitInteger(const std::string &text)
795
+ {
796
+ static const std::basic_regex<char> integer_matcher(integer_pattern);
797
+
798
+ std::smatch match;
799
+ std::regex_match(text, match, integer_matcher);
800
+
801
+ if (match.length() == 0)
802
+ {
803
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
804
+ }
805
+
806
+ IntegerDesc desc;
807
+ desc.negative = match[1];
808
+ desc.base = match[2];
809
+ desc.value = match[3];
810
+
811
+ if (match.length(4) > 0)
812
+ {
813
+ desc.base = match[5];
814
+ desc.value = "0";
815
+ return desc;
816
+ }
817
+
818
+ return desc;
819
+ }
820
+
821
+ inline bool IsTrueText(const std::string &text)
822
+ {
823
+ static const std::basic_regex<char> truthy_matcher(truthy_pattern);
824
+ std::smatch result;
825
+ std::regex_match(text, result, truthy_matcher);
826
+ return !result.empty();
827
+ }
828
+
829
+ inline bool IsFalseText(const std::string &text)
830
+ {
831
+ static const std::basic_regex<char> falsy_matcher(falsy_pattern);
832
+ std::smatch result;
833
+ std::regex_match(text, result, falsy_matcher);
834
+ return !result.empty();
835
+ }
836
+
837
+ // Gets the option names specified via a single, comma-separated string,
838
+ // and returns the separate, space-discarded, non-empty names
839
+ // (without considering which or how many are single-character)
840
+ inline OptionNames split_option_names(const std::string &text)
841
+ {
842
+ static const std::basic_regex<char> option_specifier_matcher(option_specifier_pattern);
843
+ if (!std::regex_match(text.c_str(), option_specifier_matcher))
844
+ {
845
+ throw_or_mimic<exceptions::invalid_option_format>(text);
846
+ }
847
+
848
+ OptionNames split_names;
849
+
850
+ static const std::basic_regex<char> option_specifier_separator_matcher(option_specifier_separator_pattern);
851
+ constexpr int use_non_matches { -1 };
852
+ auto token_iterator = std::sregex_token_iterator(
853
+ text.begin(), text.end(), option_specifier_separator_matcher, use_non_matches);
854
+ std::copy(token_iterator, std::sregex_token_iterator(), std::back_inserter(split_names));
855
+ return split_names;
856
+ }
857
+
858
+ inline ArguDesc ParseArgument(const char *arg, bool &matched)
859
+ {
860
+ static const std::basic_regex<char> option_matcher(option_pattern);
861
+ std::match_results<const char*> result;
862
+ std::regex_match(arg, result, option_matcher);
863
+ matched = !result.empty();
864
+
865
+ ArguDesc argu_desc;
866
+ if (matched) {
867
+ argu_desc.arg_name = result[1].str();
868
+ argu_desc.set_value = result[2].length() > 0;
869
+ argu_desc.value = result[3].str();
870
+ if (result[4].length() > 0)
871
+ {
872
+ argu_desc.grouping = true;
873
+ argu_desc.arg_name = result[4].str();
874
+ }
875
+ }
876
+
877
+ return argu_desc;
878
+ }
879
+
880
+ #endif // CXXOPTS_NO_REGEX
881
+ #undef CXXOPTS_NO_REGEX
882
+ } // namespace parser_tool
883
+
884
+ namespace detail {
885
+
886
+ template <typename T, bool B>
887
+ struct SignedCheck;
888
+
889
+ template <typename T>
890
+ struct SignedCheck<T, true>
891
+ {
892
+ template <typename U>
893
+ void
894
+ operator()(bool negative, U u, const std::string& text)
895
+ {
896
+ if (negative)
897
+ {
898
+ if (u > static_cast<U>((std::numeric_limits<T>::min)()))
899
+ {
900
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
901
+ }
902
+ }
903
+ else
904
+ {
905
+ if (u > static_cast<U>((std::numeric_limits<T>::max)()))
906
+ {
907
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
908
+ }
909
+ }
910
+ }
911
+ };
912
+
913
+ template <typename T>
914
+ struct SignedCheck<T, false>
915
+ {
916
+ template <typename U>
917
+ void
918
+ operator()(bool, U, const std::string&) const {}
919
+ };
920
+
921
+ template <typename T, typename U>
922
+ void
923
+ check_signed_range(bool negative, U value, const std::string& text)
924
+ {
925
+ SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
926
+ }
927
+
928
+ } // namespace detail
929
+
930
+ template <typename R, typename T>
931
+ void
932
+ checked_negate(R& r, T&& t, const std::string&, std::true_type)
933
+ {
934
+ // if we got to here, then `t` is a positive number that fits into
935
+ // `R`. So to avoid MSVC C4146, we first cast it to `R`.
936
+ // See https://github.com/jarro2783/cxxopts/issues/62 for more details.
937
+ r = static_cast<R>(-static_cast<R>(t-1)-1);
938
+ }
939
+
940
+ template <typename R, typename T>
941
+ void
942
+ checked_negate(R&, T&&, const std::string& text, std::false_type)
943
+ {
944
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
945
+ }
946
+
947
+ template <typename T>
948
+ void
949
+ integer_parser(const std::string& text, T& value)
950
+ {
951
+ parser_tool::IntegerDesc int_desc = parser_tool::SplitInteger(text);
952
+
953
+ using US = typename std::make_unsigned<T>::type;
954
+ constexpr bool is_signed = std::numeric_limits<T>::is_signed;
955
+
956
+ const bool negative = int_desc.negative.length() > 0;
957
+ const uint8_t base = int_desc.base.length() > 0 ? 16 : 10;
958
+ const std::string & value_match = int_desc.value;
959
+
960
+ US result = 0;
961
+
962
+ for (char ch : value_match)
963
+ {
964
+ US digit = 0;
965
+
966
+ if (ch >= '0' && ch <= '9')
967
+ {
968
+ digit = static_cast<US>(ch - '0');
969
+ }
970
+ else if (base == 16 && ch >= 'a' && ch <= 'f')
971
+ {
972
+ digit = static_cast<US>(ch - 'a' + 10);
973
+ }
974
+ else if (base == 16 && ch >= 'A' && ch <= 'F')
975
+ {
976
+ digit = static_cast<US>(ch - 'A' + 10);
977
+ }
978
+ else
979
+ {
980
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
981
+ }
982
+
983
+ US limit = 0;
984
+ if (negative)
985
+ {
986
+ limit = static_cast<US>(std::abs(static_cast<intmax_t>((std::numeric_limits<T>::min)())));
987
+ }
988
+ else
989
+ {
990
+ limit = (std::numeric_limits<T>::max)();
991
+ }
992
+
993
+ if (base != 0 && result > limit / base)
994
+ {
995
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
996
+ }
997
+ if (result * base > limit - digit)
998
+ {
999
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
1000
+ }
1001
+
1002
+ result = static_cast<US>(result * base + digit);
1003
+ }
1004
+
1005
+ detail::check_signed_range<T>(negative, result, text);
1006
+
1007
+ if (negative)
1008
+ {
1009
+ checked_negate<T>(value, result, text, std::integral_constant<bool, is_signed>());
1010
+ }
1011
+ else
1012
+ {
1013
+ value = static_cast<T>(result);
1014
+ }
1015
+ }
1016
+
1017
+ template <typename T>
1018
+ void stringstream_parser(const std::string& text, T& value)
1019
+ {
1020
+ std::stringstream in(text);
1021
+ in >> value;
1022
+ if (!in) {
1023
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
1024
+ }
1025
+ }
1026
+
1027
+ template <typename T,
1028
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr
1029
+ >
1030
+ void parse_value(const std::string& text, T& value)
1031
+ {
1032
+ integer_parser(text, value);
1033
+ }
1034
+
1035
+ inline
1036
+ void
1037
+ parse_value(const std::string& text, bool& value)
1038
+ {
1039
+ if (parser_tool::IsTrueText(text))
1040
+ {
1041
+ value = true;
1042
+ return;
1043
+ }
1044
+
1045
+ if (parser_tool::IsFalseText(text))
1046
+ {
1047
+ value = false;
1048
+ return;
1049
+ }
1050
+
1051
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
1052
+ }
1053
+
1054
+ inline
1055
+ void
1056
+ parse_value(const std::string& text, std::string& value)
1057
+ {
1058
+ value = text;
1059
+ }
1060
+
1061
+ // The fallback parser. It uses the stringstream parser to parse all types
1062
+ // that have not been overloaded explicitly. It has to be placed in the
1063
+ // source code before all other more specialized templates.
1064
+ template <typename T,
1065
+ typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr
1066
+ >
1067
+ void
1068
+ parse_value(const std::string& text, T& value) {
1069
+ stringstream_parser(text, value);
1070
+ }
1071
+
1072
+ #ifdef CXXOPTS_HAS_OPTIONAL
1073
+ template <typename T>
1074
+ void
1075
+ parse_value(const std::string& text, std::optional<T>& value)
1076
+ {
1077
+ T result;
1078
+ parse_value(text, result);
1079
+ value = std::move(result);
1080
+ }
1081
+ #endif
1082
+
1083
+ #ifdef CXXOPTS_HAS_FILESYSTEM
1084
+ inline
1085
+ void
1086
+ parse_value(const std::string& text, std::filesystem::path& value)
1087
+ {
1088
+ value.assign(text);
1089
+ }
1090
+ #endif
1091
+
1092
+ inline
1093
+ void parse_value(const std::string& text, char& c)
1094
+ {
1095
+ if (text.length() != 1)
1096
+ {
1097
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
1098
+ }
1099
+
1100
+ c = text[0];
1101
+ }
1102
+
1103
+ template<typename T> void add_value(const std::string& text, std::vector<T>& value);
1104
+
1105
+ template <typename T>
1106
+ void
1107
+ parse_value(const std::string& text, std::vector<T>& value)
1108
+ {
1109
+ if (text.empty()) {
1110
+ return;
1111
+ }
1112
+ std::stringstream in(text);
1113
+ std::string token;
1114
+ while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
1115
+ add_value(token, value);
1116
+ }
1117
+ }
1118
+
1119
+ template <typename T>
1120
+ void
1121
+ add_value(const std::string& text, T& value)
1122
+ {
1123
+ parse_value(text, value);
1124
+ }
1125
+
1126
+ template <typename T>
1127
+ void
1128
+ add_value(const std::string& text, std::vector<T>& value)
1129
+ {
1130
+ T v;
1131
+ add_value(text, v);
1132
+ value.emplace_back(std::move(v));
1133
+ }
1134
+
1135
+ template <typename T>
1136
+ struct type_is_container
1137
+ {
1138
+ static constexpr bool value = false;
1139
+ };
1140
+
1141
+ template <typename T>
1142
+ struct type_is_container<std::vector<T>>
1143
+ {
1144
+ static constexpr bool value = true;
1145
+ };
1146
+
1147
+ template <typename T>
1148
+ class abstract_value : public Value
1149
+ {
1150
+ using Self = abstract_value<T>;
1151
+
1152
+ public:
1153
+ abstract_value()
1154
+ : m_result(std::make_shared<T>())
1155
+ , m_store(m_result.get())
1156
+ {
1157
+ }
1158
+
1159
+ explicit abstract_value(T* t)
1160
+ : m_store(t)
1161
+ {
1162
+ }
1163
+
1164
+ ~abstract_value() override = default;
1165
+
1166
+ abstract_value& operator=(const abstract_value&) = default;
1167
+
1168
+ abstract_value(const abstract_value& rhs)
1169
+ {
1170
+ if (rhs.m_result)
1171
+ {
1172
+ m_result = std::make_shared<T>();
1173
+ m_store = m_result.get();
1174
+ }
1175
+ else
1176
+ {
1177
+ m_store = rhs.m_store;
1178
+ }
1179
+
1180
+ m_default = rhs.m_default;
1181
+ m_implicit = rhs.m_implicit;
1182
+ m_default_value = rhs.m_default_value;
1183
+ m_implicit_value = rhs.m_implicit_value;
1184
+ }
1185
+
1186
+ void
1187
+ add(const std::string& text) const override
1188
+ {
1189
+ add_value(text, *m_store);
1190
+ }
1191
+
1192
+ void
1193
+ parse(const std::string& text) const override
1194
+ {
1195
+ parse_value(text, *m_store);
1196
+ }
1197
+
1198
+ bool
1199
+ is_container() const override
1200
+ {
1201
+ return type_is_container<T>::value;
1202
+ }
1203
+
1204
+ void
1205
+ parse() const override
1206
+ {
1207
+ parse_value(m_default_value, *m_store);
1208
+ }
1209
+
1210
+ bool
1211
+ has_default() const override
1212
+ {
1213
+ return m_default;
1214
+ }
1215
+
1216
+ bool
1217
+ has_implicit() const override
1218
+ {
1219
+ return m_implicit;
1220
+ }
1221
+
1222
+ std::shared_ptr<Value>
1223
+ default_value(const std::string& value) override
1224
+ {
1225
+ m_default = true;
1226
+ m_default_value = value;
1227
+ return shared_from_this();
1228
+ }
1229
+
1230
+ std::shared_ptr<Value>
1231
+ implicit_value(const std::string& value) override
1232
+ {
1233
+ m_implicit = true;
1234
+ m_implicit_value = value;
1235
+ return shared_from_this();
1236
+ }
1237
+
1238
+ std::shared_ptr<Value>
1239
+ no_implicit_value() override
1240
+ {
1241
+ m_implicit = false;
1242
+ return shared_from_this();
1243
+ }
1244
+
1245
+ std::string
1246
+ get_default_value() const override
1247
+ {
1248
+ return m_default_value;
1249
+ }
1250
+
1251
+ std::string
1252
+ get_implicit_value() const override
1253
+ {
1254
+ return m_implicit_value;
1255
+ }
1256
+
1257
+ bool
1258
+ is_boolean() const override
1259
+ {
1260
+ return std::is_same<T, bool>::value;
1261
+ }
1262
+
1263
+ const T&
1264
+ get() const
1265
+ {
1266
+ if (m_store == nullptr)
1267
+ {
1268
+ return *m_result;
1269
+ }
1270
+ return *m_store;
1271
+ }
1272
+
1273
+ protected:
1274
+ std::shared_ptr<T> m_result{};
1275
+ T* m_store{};
1276
+
1277
+ bool m_default = false;
1278
+ bool m_implicit = false;
1279
+
1280
+ std::string m_default_value{};
1281
+ std::string m_implicit_value{};
1282
+ };
1283
+
1284
+ template <typename T>
1285
+ class standard_value : public abstract_value<T>
1286
+ {
1287
+ public:
1288
+ using abstract_value<T>::abstract_value;
1289
+
1290
+ CXXOPTS_NODISCARD
1291
+ std::shared_ptr<Value>
1292
+ clone() const override
1293
+ {
1294
+ return std::make_shared<standard_value<T>>(*this);
1295
+ }
1296
+ };
1297
+
1298
+ template <>
1299
+ class standard_value<bool> : public abstract_value<bool>
1300
+ {
1301
+ public:
1302
+ standard_value()
1303
+ {
1304
+ set_default_and_implicit();
1305
+ }
1306
+
1307
+ explicit standard_value(bool* b)
1308
+ : abstract_value(b)
1309
+ {
1310
+ m_implicit = true;
1311
+ m_implicit_value = "true";
1312
+ }
1313
+
1314
+ std::shared_ptr<Value>
1315
+ clone() const override
1316
+ {
1317
+ return std::make_shared<standard_value<bool>>(*this);
1318
+ }
1319
+
1320
+ private:
1321
+
1322
+ void
1323
+ set_default_and_implicit()
1324
+ {
1325
+ m_default = true;
1326
+ m_default_value = "false";
1327
+ m_implicit = true;
1328
+ m_implicit_value = "true";
1329
+ }
1330
+ };
1331
+
1332
+ } // namespace values
1333
+
1334
+ template <typename T>
1335
+ std::shared_ptr<Value>
1336
+ value()
1337
+ {
1338
+ return std::make_shared<values::standard_value<T>>();
1339
+ }
1340
+
1341
+ template <typename T>
1342
+ std::shared_ptr<Value>
1343
+ value(T& t)
1344
+ {
1345
+ return std::make_shared<values::standard_value<T>>(&t);
1346
+ }
1347
+
1348
+ class OptionAdder;
1349
+
1350
+ CXXOPTS_NODISCARD
1351
+ inline
1352
+ const std::string&
1353
+ first_or_empty(const OptionNames& long_names)
1354
+ {
1355
+ static const std::string empty{""};
1356
+ return long_names.empty() ? empty : long_names.front();
1357
+ }
1358
+
1359
+ class OptionDetails
1360
+ {
1361
+ public:
1362
+ OptionDetails
1363
+ (
1364
+ std::string short_,
1365
+ OptionNames long_,
1366
+ String desc,
1367
+ std::shared_ptr<const Value> val
1368
+ )
1369
+ : m_short(std::move(short_))
1370
+ , m_long(std::move(long_))
1371
+ , m_desc(std::move(desc))
1372
+ , m_value(std::move(val))
1373
+ , m_count(0)
1374
+ {
1375
+ m_hash = std::hash<std::string>{}(first_long_name() + m_short);
1376
+ }
1377
+
1378
+ OptionDetails(const OptionDetails& rhs)
1379
+ : m_desc(rhs.m_desc)
1380
+ , m_value(rhs.m_value->clone())
1381
+ , m_count(rhs.m_count)
1382
+ {
1383
+ }
1384
+
1385
+ OptionDetails(OptionDetails&& rhs) = default;
1386
+
1387
+ CXXOPTS_NODISCARD
1388
+ const String&
1389
+ description() const
1390
+ {
1391
+ return m_desc;
1392
+ }
1393
+
1394
+ CXXOPTS_NODISCARD
1395
+ const Value&
1396
+ value() const {
1397
+ return *m_value;
1398
+ }
1399
+
1400
+ CXXOPTS_NODISCARD
1401
+ std::shared_ptr<Value>
1402
+ make_storage() const
1403
+ {
1404
+ return m_value->clone();
1405
+ }
1406
+
1407
+ CXXOPTS_NODISCARD
1408
+ const std::string&
1409
+ short_name() const
1410
+ {
1411
+ return m_short;
1412
+ }
1413
+
1414
+ CXXOPTS_NODISCARD
1415
+ const std::string&
1416
+ first_long_name() const
1417
+ {
1418
+ return first_or_empty(m_long);
1419
+ }
1420
+
1421
+ CXXOPTS_NODISCARD
1422
+ const std::string&
1423
+ essential_name() const
1424
+ {
1425
+ return m_long.empty() ? m_short : m_long.front();
1426
+ }
1427
+
1428
+ CXXOPTS_NODISCARD
1429
+ const OptionNames &
1430
+ long_names() const
1431
+ {
1432
+ return m_long;
1433
+ }
1434
+
1435
+ std::size_t
1436
+ hash() const
1437
+ {
1438
+ return m_hash;
1439
+ }
1440
+
1441
+ private:
1442
+ std::string m_short{};
1443
+ OptionNames m_long{};
1444
+ String m_desc{};
1445
+ std::shared_ptr<const Value> m_value{};
1446
+ int m_count;
1447
+
1448
+ std::size_t m_hash{};
1449
+ };
1450
+
1451
+ struct HelpOptionDetails
1452
+ {
1453
+ std::string s;
1454
+ OptionNames l;
1455
+ String desc;
1456
+ bool has_default;
1457
+ std::string default_value;
1458
+ bool has_implicit;
1459
+ std::string implicit_value;
1460
+ std::string arg_help;
1461
+ bool is_container;
1462
+ bool is_boolean;
1463
+ };
1464
+
1465
+ struct HelpGroupDetails
1466
+ {
1467
+ std::string name{};
1468
+ std::string description{};
1469
+ std::vector<HelpOptionDetails> options{};
1470
+ };
1471
+
1472
+ class OptionValue
1473
+ {
1474
+ public:
1475
+ void
1476
+ add
1477
+ (
1478
+ const std::shared_ptr<const OptionDetails>& details,
1479
+ const std::string& text
1480
+ )
1481
+ {
1482
+ ensure_value(details);
1483
+ ++m_count;
1484
+ m_value->add(text);
1485
+ m_long_names = &details->long_names();
1486
+ }
1487
+
1488
+ void
1489
+ parse
1490
+ (
1491
+ const std::shared_ptr<const OptionDetails>& details,
1492
+ const std::string& text
1493
+ )
1494
+ {
1495
+ ensure_value(details);
1496
+ ++m_count;
1497
+ m_value->parse(text);
1498
+ m_long_names = &details->long_names();
1499
+ }
1500
+
1501
+ void
1502
+ parse_default(const std::shared_ptr<const OptionDetails>& details)
1503
+ {
1504
+ ensure_value(details);
1505
+ m_default = true;
1506
+ m_long_names = &details->long_names();
1507
+ m_value->parse();
1508
+ }
1509
+
1510
+ void
1511
+ parse_no_value(const std::shared_ptr<const OptionDetails>& details)
1512
+ {
1513
+ m_long_names = &details->long_names();
1514
+ }
1515
+
1516
+ #if defined(CXXOPTS_NULL_DEREF_IGNORE)
1517
+ CXXOPTS_DIAGNOSTIC_PUSH
1518
+ CXXOPTS_IGNORE_WARNING("-Wnull-dereference")
1519
+ #endif
1520
+
1521
+ CXXOPTS_NODISCARD
1522
+ std::size_t
1523
+ count() const noexcept
1524
+ {
1525
+ return m_count;
1526
+ }
1527
+
1528
+ #if defined(CXXOPTS_NULL_DEREF_IGNORE)
1529
+ CXXOPTS_DIAGNOSTIC_POP
1530
+ #endif
1531
+
1532
+ // TODO: maybe default options should count towards the number of arguments
1533
+ CXXOPTS_NODISCARD
1534
+ bool
1535
+ has_default() const noexcept
1536
+ {
1537
+ return m_default;
1538
+ }
1539
+
1540
+ template <typename T>
1541
+ const T&
1542
+ as() const
1543
+ {
1544
+ if (m_value == nullptr) {
1545
+ throw_or_mimic<exceptions::option_has_no_value>(
1546
+ m_long_names == nullptr ? "" : first_or_empty(*m_long_names));
1547
+ }
1548
+
1549
+ return CXXOPTS_RTTI_CAST<const values::standard_value<T>&>(*m_value).get();
1550
+ }
1551
+
1552
+ #ifdef CXXOPTS_HAS_OPTIONAL
1553
+ template <typename T>
1554
+ std::optional<T>
1555
+ as_optional() const
1556
+ {
1557
+ if (m_value == nullptr) {
1558
+ return std::nullopt;
1559
+ }
1560
+ return as<T>();
1561
+ }
1562
+ #endif
1563
+
1564
+ private:
1565
+ void
1566
+ ensure_value(const std::shared_ptr<const OptionDetails>& details)
1567
+ {
1568
+ if (m_value == nullptr)
1569
+ {
1570
+ m_value = details->make_storage();
1571
+ }
1572
+ }
1573
+
1574
+
1575
+ const OptionNames * m_long_names = nullptr;
1576
+ // Holding this pointer is safe, since OptionValue's only exist in key-value pairs,
1577
+ // where the key has the string we point to.
1578
+ std::shared_ptr<Value> m_value{};
1579
+ std::size_t m_count = 0;
1580
+ bool m_default = false;
1581
+ };
1582
+
1583
+ class KeyValue
1584
+ {
1585
+ public:
1586
+ KeyValue(std::string key_, std::string value_) noexcept
1587
+ : m_key(std::move(key_))
1588
+ , m_value(std::move(value_))
1589
+ {
1590
+ }
1591
+
1592
+ CXXOPTS_NODISCARD
1593
+ const std::string&
1594
+ key() const
1595
+ {
1596
+ return m_key;
1597
+ }
1598
+
1599
+ CXXOPTS_NODISCARD
1600
+ const std::string&
1601
+ value() const
1602
+ {
1603
+ return m_value;
1604
+ }
1605
+
1606
+ template <typename T>
1607
+ T
1608
+ as() const
1609
+ {
1610
+ T result;
1611
+ values::parse_value(m_value, result);
1612
+ return result;
1613
+ }
1614
+
1615
+ private:
1616
+ std::string m_key;
1617
+ std::string m_value;
1618
+ };
1619
+
1620
+ using ParsedHashMap = std::unordered_map<std::size_t, OptionValue>;
1621
+ using NameHashMap = std::unordered_map<std::string, std::size_t>;
1622
+
1623
+ class ParseResult
1624
+ {
1625
+ public:
1626
+ class Iterator
1627
+ {
1628
+ public:
1629
+ using iterator_category = std::forward_iterator_tag;
1630
+ using value_type = KeyValue;
1631
+ using difference_type = void;
1632
+ using pointer = const KeyValue*;
1633
+ using reference = const KeyValue&;
1634
+
1635
+ Iterator() = default;
1636
+ Iterator(const Iterator&) = default;
1637
+
1638
+ // GCC complains about m_iter not being initialised in the member
1639
+ // initializer list
1640
+ CXXOPTS_DIAGNOSTIC_PUSH
1641
+ CXXOPTS_IGNORE_WARNING("-Weffc++")
1642
+ Iterator(const ParseResult *pr, bool end=false)
1643
+ : m_pr(pr)
1644
+ {
1645
+ if (end)
1646
+ {
1647
+ m_sequential = false;
1648
+ m_iter = m_pr->m_defaults.end();
1649
+ }
1650
+ else
1651
+ {
1652
+ m_sequential = true;
1653
+ m_iter = m_pr->m_sequential.begin();
1654
+
1655
+ if (m_iter == m_pr->m_sequential.end())
1656
+ {
1657
+ m_sequential = false;
1658
+ m_iter = m_pr->m_defaults.begin();
1659
+ }
1660
+ }
1661
+ }
1662
+ CXXOPTS_DIAGNOSTIC_POP
1663
+
1664
+ Iterator& operator++()
1665
+ {
1666
+ ++m_iter;
1667
+ if(m_sequential && m_iter == m_pr->m_sequential.end())
1668
+ {
1669
+ m_sequential = false;
1670
+ m_iter = m_pr->m_defaults.begin();
1671
+ return *this;
1672
+ }
1673
+ return *this;
1674
+ }
1675
+
1676
+ Iterator operator++(int)
1677
+ {
1678
+ Iterator retval = *this;
1679
+ ++(*this);
1680
+ return retval;
1681
+ }
1682
+
1683
+ bool operator==(const Iterator& other) const
1684
+ {
1685
+ return (m_sequential == other.m_sequential) && (m_iter == other.m_iter);
1686
+ }
1687
+
1688
+ bool operator!=(const Iterator& other) const
1689
+ {
1690
+ return !(*this == other);
1691
+ }
1692
+
1693
+ const KeyValue& operator*()
1694
+ {
1695
+ return *m_iter;
1696
+ }
1697
+
1698
+ const KeyValue* operator->()
1699
+ {
1700
+ return m_iter.operator->();
1701
+ }
1702
+
1703
+ private:
1704
+ const ParseResult* m_pr;
1705
+ std::vector<KeyValue>::const_iterator m_iter;
1706
+ bool m_sequential = true;
1707
+ };
1708
+
1709
+ ParseResult() = default;
1710
+ ParseResult(const ParseResult&) = default;
1711
+
1712
+ ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector<KeyValue> sequential,
1713
+ std::vector<KeyValue> default_opts, std::vector<std::string>&& unmatched_args)
1714
+ : m_keys(std::move(keys))
1715
+ , m_values(std::move(values))
1716
+ , m_sequential(std::move(sequential))
1717
+ , m_defaults(std::move(default_opts))
1718
+ , m_unmatched(std::move(unmatched_args))
1719
+ {
1720
+ }
1721
+
1722
+ ParseResult& operator=(ParseResult&&) = default;
1723
+ ParseResult& operator=(const ParseResult&) = default;
1724
+
1725
+ Iterator
1726
+ begin() const
1727
+ {
1728
+ return Iterator(this);
1729
+ }
1730
+
1731
+ Iterator
1732
+ end() const
1733
+ {
1734
+ return Iterator(this, true);
1735
+ }
1736
+
1737
+ std::size_t
1738
+ count(const std::string& o) const
1739
+ {
1740
+ auto iter = m_keys.find(o);
1741
+ if (iter == m_keys.end())
1742
+ {
1743
+ return 0;
1744
+ }
1745
+
1746
+ auto viter = m_values.find(iter->second);
1747
+
1748
+ if (viter == m_values.end())
1749
+ {
1750
+ return 0;
1751
+ }
1752
+
1753
+ return viter->second.count();
1754
+ }
1755
+
1756
+ bool
1757
+ contains(const std::string& o) const
1758
+ {
1759
+ return static_cast<bool>(count(o));
1760
+ }
1761
+
1762
+ const OptionValue&
1763
+ operator[](const std::string& option) const
1764
+ {
1765
+ auto iter = m_keys.find(option);
1766
+
1767
+ if (iter == m_keys.end())
1768
+ {
1769
+ throw_or_mimic<exceptions::requested_option_not_present>(option);
1770
+ }
1771
+
1772
+ auto viter = m_values.find(iter->second);
1773
+
1774
+ if (viter == m_values.end())
1775
+ {
1776
+ throw_or_mimic<exceptions::requested_option_not_present>(option);
1777
+ }
1778
+
1779
+ return viter->second;
1780
+ }
1781
+
1782
+ #ifdef CXXOPTS_HAS_OPTIONAL
1783
+ template <typename T>
1784
+ std::optional<T>
1785
+ as_optional(const std::string& option) const
1786
+ {
1787
+ auto iter = m_keys.find(option);
1788
+ if (iter != m_keys.end())
1789
+ {
1790
+ auto viter = m_values.find(iter->second);
1791
+ if (viter != m_values.end())
1792
+ {
1793
+ return viter->second.as_optional<T>();
1794
+ }
1795
+ }
1796
+ return std::nullopt;
1797
+ }
1798
+ #endif
1799
+
1800
+ const std::vector<KeyValue>&
1801
+ arguments() const
1802
+ {
1803
+ return m_sequential;
1804
+ }
1805
+
1806
+ const std::vector<std::string>&
1807
+ unmatched() const
1808
+ {
1809
+ return m_unmatched;
1810
+ }
1811
+
1812
+ const std::vector<KeyValue>&
1813
+ defaults() const
1814
+ {
1815
+ return m_defaults;
1816
+ }
1817
+
1818
+ const std::string
1819
+ arguments_string() const
1820
+ {
1821
+ std::string result;
1822
+ for(const auto& kv: m_sequential)
1823
+ {
1824
+ result += kv.key() + " = " + kv.value() + "\n";
1825
+ }
1826
+ for(const auto& kv: m_defaults)
1827
+ {
1828
+ result += kv.key() + " = " + kv.value() + " " + "(default)" + "\n";
1829
+ }
1830
+ return result;
1831
+ }
1832
+
1833
+ private:
1834
+ NameHashMap m_keys{};
1835
+ ParsedHashMap m_values{};
1836
+ std::vector<KeyValue> m_sequential{};
1837
+ std::vector<KeyValue> m_defaults{};
1838
+ std::vector<std::string> m_unmatched{};
1839
+ };
1840
+
1841
+ struct Option
1842
+ {
1843
+ Option
1844
+ (
1845
+ std::string opts,
1846
+ std::string desc,
1847
+ std::shared_ptr<const Value> value = ::cxxopts::value<bool>(),
1848
+ std::string arg_help = ""
1849
+ )
1850
+ : opts_(std::move(opts))
1851
+ , desc_(std::move(desc))
1852
+ , value_(std::move(value))
1853
+ , arg_help_(std::move(arg_help))
1854
+ {
1855
+ }
1856
+
1857
+ std::string opts_;
1858
+ std::string desc_;
1859
+ std::shared_ptr<const Value> value_;
1860
+ std::string arg_help_;
1861
+ };
1862
+
1863
+ using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
1864
+ using PositionalList = std::vector<std::string>;
1865
+ using PositionalListIterator = PositionalList::const_iterator;
1866
+
1867
+ class OptionParser
1868
+ {
1869
+ public:
1870
+ OptionParser(const OptionMap& options, const PositionalList& positional, bool allow_unrecognised)
1871
+ : m_options(options)
1872
+ , m_positional(positional)
1873
+ , m_allow_unrecognised(allow_unrecognised)
1874
+ {
1875
+ }
1876
+
1877
+ ParseResult
1878
+ parse(int argc, const char* const* argv);
1879
+
1880
+ bool
1881
+ consume_positional(const std::string& a, PositionalListIterator& next);
1882
+
1883
+ void
1884
+ checked_parse_arg
1885
+ (
1886
+ int argc,
1887
+ const char* const* argv,
1888
+ int& current,
1889
+ const std::shared_ptr<OptionDetails>& value,
1890
+ const std::string& name
1891
+ );
1892
+
1893
+ void
1894
+ add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg);
1895
+
1896
+ void
1897
+ parse_option
1898
+ (
1899
+ const std::shared_ptr<OptionDetails>& value,
1900
+ const std::string& name,
1901
+ const std::string& arg = ""
1902
+ );
1903
+
1904
+ void
1905
+ parse_default(const std::shared_ptr<OptionDetails>& details);
1906
+
1907
+ void
1908
+ parse_no_value(const std::shared_ptr<OptionDetails>& details);
1909
+
1910
+ private:
1911
+
1912
+ void finalise_aliases();
1913
+
1914
+ const OptionMap& m_options;
1915
+ const PositionalList& m_positional;
1916
+
1917
+ std::vector<KeyValue> m_sequential{};
1918
+ std::vector<KeyValue> m_defaults{};
1919
+ bool m_allow_unrecognised;
1920
+
1921
+ ParsedHashMap m_parsed{};
1922
+ NameHashMap m_keys{};
1923
+ };
1924
+
1925
+ class Options
1926
+ {
1927
+ public:
1928
+
1929
+ explicit Options(std::string program_name, std::string help_string = "")
1930
+ : m_program(std::move(program_name))
1931
+ , m_help_string(toLocalString(std::move(help_string)))
1932
+ , m_custom_help("[OPTION...]")
1933
+ , m_positional_help("positional parameters")
1934
+ , m_show_positional(false)
1935
+ , m_allow_unrecognised(false)
1936
+ , m_width(76)
1937
+ , m_tab_expansion(false)
1938
+ , m_options(std::make_shared<OptionMap>())
1939
+ {
1940
+ }
1941
+
1942
+ Options&
1943
+ positional_help(std::string help_text)
1944
+ {
1945
+ m_positional_help = std::move(help_text);
1946
+ return *this;
1947
+ }
1948
+
1949
+ Options&
1950
+ custom_help(std::string help_text)
1951
+ {
1952
+ m_custom_help = std::move(help_text);
1953
+ return *this;
1954
+ }
1955
+
1956
+ Options&
1957
+ show_positional_help()
1958
+ {
1959
+ m_show_positional = true;
1960
+ return *this;
1961
+ }
1962
+
1963
+ Options&
1964
+ allow_unrecognised_options()
1965
+ {
1966
+ m_allow_unrecognised = true;
1967
+ return *this;
1968
+ }
1969
+
1970
+ Options&
1971
+ set_width(std::size_t width)
1972
+ {
1973
+ m_width = width;
1974
+ return *this;
1975
+ }
1976
+
1977
+ Options&
1978
+ set_tab_expansion(bool expansion=true)
1979
+ {
1980
+ m_tab_expansion = expansion;
1981
+ return *this;
1982
+ }
1983
+
1984
+ ParseResult
1985
+ parse(int argc, const char* const* argv);
1986
+
1987
+ OptionAdder
1988
+ add_options(std::string group = "");
1989
+
1990
+ void
1991
+ add_options
1992
+ (
1993
+ const std::string& group,
1994
+ std::initializer_list<Option> options
1995
+ );
1996
+
1997
+ void
1998
+ add_option
1999
+ (
2000
+ const std::string& group,
2001
+ const Option& option
2002
+ );
2003
+
2004
+ void
2005
+ add_option
2006
+ (
2007
+ const std::string& group,
2008
+ const std::string& s,
2009
+ const OptionNames& l,
2010
+ std::string desc,
2011
+ const std::shared_ptr<const Value>& value,
2012
+ std::string arg_help
2013
+ );
2014
+
2015
+ void
2016
+ add_option
2017
+ (
2018
+ const std::string& group,
2019
+ const std::string& short_name,
2020
+ const std::string& single_long_name,
2021
+ std::string desc,
2022
+ const std::shared_ptr<const Value>& value,
2023
+ std::string arg_help
2024
+ )
2025
+ {
2026
+ OptionNames long_names;
2027
+ long_names.emplace_back(single_long_name);
2028
+ add_option(group, short_name, long_names, desc, value, arg_help);
2029
+ }
2030
+
2031
+ //parse positional arguments into the given option
2032
+ void
2033
+ parse_positional(std::string option);
2034
+
2035
+ void
2036
+ parse_positional(std::vector<std::string> options);
2037
+
2038
+ void
2039
+ parse_positional(std::initializer_list<std::string> options);
2040
+
2041
+ template <typename Iterator>
2042
+ void
2043
+ parse_positional(Iterator begin, Iterator end) {
2044
+ parse_positional(std::vector<std::string>{begin, end});
2045
+ }
2046
+
2047
+ std::string
2048
+ help(const std::vector<std::string>& groups = {}, bool print_usage=true) const;
2049
+
2050
+ std::vector<std::string>
2051
+ groups() const;
2052
+
2053
+ const HelpGroupDetails&
2054
+ group_help(const std::string& group) const;
2055
+
2056
+ const std::string& program() const
2057
+ {
2058
+ return m_program;
2059
+ }
2060
+
2061
+ private:
2062
+
2063
+ void
2064
+ add_one_option
2065
+ (
2066
+ const std::string& option,
2067
+ const std::shared_ptr<OptionDetails>& details
2068
+ );
2069
+
2070
+ String
2071
+ help_one_group(const std::string& group) const;
2072
+
2073
+ void
2074
+ generate_group_help
2075
+ (
2076
+ String& result,
2077
+ const std::vector<std::string>& groups
2078
+ ) const;
2079
+
2080
+ void
2081
+ generate_all_groups_help(String& result) const;
2082
+
2083
+ std::string m_program{};
2084
+ String m_help_string{};
2085
+ std::string m_custom_help{};
2086
+ std::string m_positional_help{};
2087
+ bool m_show_positional;
2088
+ bool m_allow_unrecognised;
2089
+ std::size_t m_width;
2090
+ bool m_tab_expansion;
2091
+
2092
+ std::shared_ptr<OptionMap> m_options;
2093
+ std::vector<std::string> m_positional{};
2094
+ std::unordered_set<std::string> m_positional_set{};
2095
+
2096
+ //mapping from groups to help options
2097
+ std::vector<std::string> m_group{};
2098
+ std::map<std::string, HelpGroupDetails> m_help{};
2099
+ };
2100
+
2101
+ class OptionAdder
2102
+ {
2103
+ public:
2104
+
2105
+ OptionAdder(Options& options, std::string group)
2106
+ : m_options(options), m_group(std::move(group))
2107
+ {
2108
+ }
2109
+
2110
+ OptionAdder&
2111
+ operator()
2112
+ (
2113
+ const std::string& opts,
2114
+ const std::string& desc,
2115
+ const std::shared_ptr<const Value>& value
2116
+ = ::cxxopts::value<bool>(),
2117
+ std::string arg_help = ""
2118
+ );
2119
+
2120
+ private:
2121
+ Options& m_options;
2122
+ std::string m_group;
2123
+ };
2124
+
2125
+ namespace {
2126
+ constexpr std::size_t OPTION_LONGEST = 30;
2127
+ constexpr std::size_t OPTION_DESC_GAP = 2;
2128
+
2129
+ String
2130
+ format_option
2131
+ (
2132
+ const HelpOptionDetails& o
2133
+ )
2134
+ {
2135
+ const auto& s = o.s;
2136
+ const auto& l = first_or_empty(o.l);
2137
+
2138
+ String result = " ";
2139
+
2140
+ if (!s.empty())
2141
+ {
2142
+ result += "-" + toLocalString(s);
2143
+ if (!l.empty())
2144
+ {
2145
+ result += ",";
2146
+ }
2147
+ }
2148
+ else
2149
+ {
2150
+ result += " ";
2151
+ }
2152
+
2153
+ if (!l.empty())
2154
+ {
2155
+ result += " --" + toLocalString(l);
2156
+ }
2157
+
2158
+ auto arg = !o.arg_help.empty() ? toLocalString(o.arg_help) : "arg";
2159
+
2160
+ if (!o.is_boolean)
2161
+ {
2162
+ if (o.has_implicit)
2163
+ {
2164
+ result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
2165
+ }
2166
+ else
2167
+ {
2168
+ result += " " + arg;
2169
+ }
2170
+ }
2171
+
2172
+ return result;
2173
+ }
2174
+
2175
+ String
2176
+ format_description
2177
+ (
2178
+ const HelpOptionDetails& o,
2179
+ std::size_t start,
2180
+ std::size_t allowed,
2181
+ bool tab_expansion
2182
+ )
2183
+ {
2184
+ auto desc = o.desc;
2185
+
2186
+ if (o.has_default && (!o.is_boolean || o.default_value != "false"))
2187
+ {
2188
+ if(!o.default_value.empty())
2189
+ {
2190
+ desc += toLocalString(" (default: " + o.default_value + ")");
2191
+ }
2192
+ else
2193
+ {
2194
+ desc += toLocalString(" (default: \"\")");
2195
+ }
2196
+ }
2197
+
2198
+ String result;
2199
+
2200
+ if (tab_expansion)
2201
+ {
2202
+ String desc2;
2203
+ auto size = std::size_t{ 0 };
2204
+ for (auto c = std::begin(desc); c != std::end(desc); ++c)
2205
+ {
2206
+ if (*c == '\n')
2207
+ {
2208
+ desc2 += *c;
2209
+ size = 0;
2210
+ }
2211
+ else if (*c == '\t')
2212
+ {
2213
+ auto skip = 8 - size % 8;
2214
+ stringAppend(desc2, skip, ' ');
2215
+ size += skip;
2216
+ }
2217
+ else
2218
+ {
2219
+ desc2 += *c;
2220
+ ++size;
2221
+ }
2222
+ }
2223
+ desc = desc2;
2224
+ }
2225
+
2226
+ desc += " ";
2227
+
2228
+ auto current = std::begin(desc);
2229
+ auto previous = current;
2230
+ auto startLine = current;
2231
+ auto lastSpace = current;
2232
+
2233
+ auto size = std::size_t{};
2234
+
2235
+ bool appendNewLine;
2236
+ bool onlyWhiteSpace = true;
2237
+
2238
+ while (current != std::end(desc))
2239
+ {
2240
+ appendNewLine = false;
2241
+ if (*previous == ' ' || *previous == '\t')
2242
+ {
2243
+ lastSpace = current;
2244
+ }
2245
+ if (*current != ' ' && *current != '\t')
2246
+ {
2247
+ onlyWhiteSpace = false;
2248
+ }
2249
+
2250
+ while (*current == '\n')
2251
+ {
2252
+ previous = current;
2253
+ ++current;
2254
+ appendNewLine = true;
2255
+ }
2256
+
2257
+ if (!appendNewLine && size >= allowed)
2258
+ {
2259
+ if (lastSpace != startLine)
2260
+ {
2261
+ current = lastSpace;
2262
+ previous = current;
2263
+ }
2264
+ appendNewLine = true;
2265
+ }
2266
+
2267
+ if (appendNewLine)
2268
+ {
2269
+ stringAppend(result, startLine, current);
2270
+ startLine = current;
2271
+ lastSpace = current;
2272
+
2273
+ if (*previous != '\n')
2274
+ {
2275
+ stringAppend(result, "\n");
2276
+ }
2277
+
2278
+ stringAppend(result, start, ' ');
2279
+
2280
+ if (*previous != '\n')
2281
+ {
2282
+ stringAppend(result, lastSpace, current);
2283
+ }
2284
+
2285
+ onlyWhiteSpace = true;
2286
+ size = 0;
2287
+ }
2288
+
2289
+ previous = current;
2290
+ ++current;
2291
+ ++size;
2292
+ }
2293
+
2294
+ //append whatever is left but ignore whitespace
2295
+ if (!onlyWhiteSpace)
2296
+ {
2297
+ stringAppend(result, startLine, previous);
2298
+ }
2299
+
2300
+ return result;
2301
+ }
2302
+
2303
+ } // namespace
2304
+
2305
+ inline
2306
+ void
2307
+ Options::add_options
2308
+ (
2309
+ const std::string &group,
2310
+ std::initializer_list<Option> options
2311
+ )
2312
+ {
2313
+ OptionAdder option_adder(*this, group);
2314
+ for (const auto &option: options)
2315
+ {
2316
+ option_adder(option.opts_, option.desc_, option.value_, option.arg_help_);
2317
+ }
2318
+ }
2319
+
2320
+ inline
2321
+ OptionAdder
2322
+ Options::add_options(std::string group)
2323
+ {
2324
+ return OptionAdder(*this, std::move(group));
2325
+ }
2326
+
2327
+ inline
2328
+ OptionAdder&
2329
+ OptionAdder::operator()
2330
+ (
2331
+ const std::string& opts,
2332
+ const std::string& desc,
2333
+ const std::shared_ptr<const Value>& value,
2334
+ std::string arg_help
2335
+ )
2336
+ {
2337
+ OptionNames option_names = values::parser_tool::split_option_names(opts);
2338
+ // Note: All names will be non-empty; but we must separate the short
2339
+ // (length-1) and longer names
2340
+ std::string short_name {""};
2341
+ auto first_short_name_iter =
2342
+ std::partition(option_names.begin(), option_names.end(),
2343
+ [&](const std::string& name) { return name.length() > 1; }
2344
+ );
2345
+ auto num_length_1_names = (option_names.end() - first_short_name_iter);
2346
+ switch(num_length_1_names) {
2347
+ case 1:
2348
+ short_name = *first_short_name_iter;
2349
+ option_names.erase(first_short_name_iter);
2350
+ CXXOPTS_FALLTHROUGH;
2351
+ case 0:
2352
+ break;
2353
+ default:
2354
+ throw_or_mimic<exceptions::invalid_option_format>(opts);
2355
+ };
2356
+
2357
+ m_options.add_option
2358
+ (
2359
+ m_group,
2360
+ short_name,
2361
+ option_names,
2362
+ desc,
2363
+ value,
2364
+ std::move(arg_help)
2365
+ );
2366
+
2367
+ return *this;
2368
+ }
2369
+
2370
+ inline
2371
+ void
2372
+ OptionParser::parse_default(const std::shared_ptr<OptionDetails>& details)
2373
+ {
2374
+ // TODO: remove the duplicate code here
2375
+ auto& store = m_parsed[details->hash()];
2376
+ store.parse_default(details);
2377
+ m_defaults.emplace_back(details->essential_name(), details->value().get_default_value());
2378
+ }
2379
+
2380
+ inline
2381
+ void
2382
+ OptionParser::parse_no_value(const std::shared_ptr<OptionDetails>& details)
2383
+ {
2384
+ auto& store = m_parsed[details->hash()];
2385
+ store.parse_no_value(details);
2386
+ }
2387
+
2388
+ inline
2389
+ void
2390
+ OptionParser::parse_option
2391
+ (
2392
+ const std::shared_ptr<OptionDetails>& value,
2393
+ const std::string& /*name*/,
2394
+ const std::string& arg
2395
+ )
2396
+ {
2397
+ auto hash = value->hash();
2398
+ auto& result = m_parsed[hash];
2399
+ result.parse(value, arg);
2400
+
2401
+ m_sequential.emplace_back(value->essential_name(), arg);
2402
+ }
2403
+
2404
+ inline
2405
+ void
2406
+ OptionParser::checked_parse_arg
2407
+ (
2408
+ int argc,
2409
+ const char* const* argv,
2410
+ int& current,
2411
+ const std::shared_ptr<OptionDetails>& value,
2412
+ const std::string& name
2413
+ )
2414
+ {
2415
+ if (current + 1 >= argc)
2416
+ {
2417
+ if (value->value().has_implicit())
2418
+ {
2419
+ parse_option(value, name, value->value().get_implicit_value());
2420
+ }
2421
+ else
2422
+ {
2423
+ throw_or_mimic<exceptions::missing_argument>(name);
2424
+ }
2425
+ }
2426
+ else
2427
+ {
2428
+ if (value->value().has_implicit())
2429
+ {
2430
+ parse_option(value, name, value->value().get_implicit_value());
2431
+ }
2432
+ else
2433
+ {
2434
+ parse_option(value, name, argv[current + 1]);
2435
+ ++current;
2436
+ }
2437
+ }
2438
+ }
2439
+
2440
+ inline
2441
+ void
2442
+ OptionParser::add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg)
2443
+ {
2444
+ auto hash = value->hash();
2445
+ auto& result = m_parsed[hash];
2446
+ result.add(value, arg);
2447
+
2448
+ m_sequential.emplace_back(value->essential_name(), arg);
2449
+ }
2450
+
2451
+ inline
2452
+ bool
2453
+ OptionParser::consume_positional(const std::string& a, PositionalListIterator& next)
2454
+ {
2455
+ while (next != m_positional.end())
2456
+ {
2457
+ auto iter = m_options.find(*next);
2458
+ if (iter != m_options.end())
2459
+ {
2460
+ if (!iter->second->value().is_container())
2461
+ {
2462
+ auto& result = m_parsed[iter->second->hash()];
2463
+ if (result.count() == 0)
2464
+ {
2465
+ add_to_option(iter->second, a);
2466
+ ++next;
2467
+ return true;
2468
+ }
2469
+ ++next;
2470
+ continue;
2471
+ }
2472
+ add_to_option(iter->second, a);
2473
+ return true;
2474
+ }
2475
+ throw_or_mimic<exceptions::no_such_option>(*next);
2476
+ }
2477
+
2478
+ return false;
2479
+ }
2480
+
2481
+ inline
2482
+ void
2483
+ Options::parse_positional(std::string option)
2484
+ {
2485
+ parse_positional(std::vector<std::string>{std::move(option)});
2486
+ }
2487
+
2488
+ inline
2489
+ void
2490
+ Options::parse_positional(std::vector<std::string> options)
2491
+ {
2492
+ m_positional = std::move(options);
2493
+
2494
+ m_positional_set.insert(m_positional.begin(), m_positional.end());
2495
+ }
2496
+
2497
+ inline
2498
+ void
2499
+ Options::parse_positional(std::initializer_list<std::string> options)
2500
+ {
2501
+ parse_positional(std::vector<std::string>(options));
2502
+ }
2503
+
2504
+ inline
2505
+ ParseResult
2506
+ Options::parse(int argc, const char* const* argv)
2507
+ {
2508
+ OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
2509
+
2510
+ return parser.parse(argc, argv);
2511
+ }
2512
+
2513
+ inline ParseResult
2514
+ OptionParser::parse(int argc, const char* const* argv)
2515
+ {
2516
+ int current = 1;
2517
+ bool consume_remaining = false;
2518
+ auto next_positional = m_positional.begin();
2519
+
2520
+ std::vector<std::string> unmatched;
2521
+
2522
+ while (current < argc)
2523
+ {
2524
+ if (strcmp(argv[current], "--") == 0)
2525
+ {
2526
+ consume_remaining = true;
2527
+ ++current;
2528
+ break;
2529
+ }
2530
+ bool matched = false;
2531
+ values::parser_tool::ArguDesc argu_desc =
2532
+ values::parser_tool::ParseArgument(argv[current], matched);
2533
+
2534
+ if (!matched)
2535
+ {
2536
+ //not a flag
2537
+
2538
+ // but if it starts with a `-`, then it's an error
2539
+ if (argv[current][0] == '-' && argv[current][1] != '\0') {
2540
+ if (!m_allow_unrecognised) {
2541
+ throw_or_mimic<exceptions::invalid_option_syntax>(argv[current]);
2542
+ }
2543
+ }
2544
+
2545
+ //if true is returned here then it was consumed, otherwise it is
2546
+ //ignored
2547
+ if (consume_positional(argv[current], next_positional))
2548
+ {
2549
+ }
2550
+ else
2551
+ {
2552
+ unmatched.emplace_back(argv[current]);
2553
+ }
2554
+ //if we return from here then it was parsed successfully, so continue
2555
+ }
2556
+ else
2557
+ {
2558
+ //short or long option?
2559
+ if (argu_desc.grouping)
2560
+ {
2561
+ const std::string& s = argu_desc.arg_name;
2562
+
2563
+ for (std::size_t i = 0; i != s.size(); ++i)
2564
+ {
2565
+ std::string name(1, s[i]);
2566
+ auto iter = m_options.find(name);
2567
+
2568
+ if (iter == m_options.end())
2569
+ {
2570
+ if (m_allow_unrecognised)
2571
+ {
2572
+ unmatched.push_back(std::string("-") + s[i]);
2573
+ continue;
2574
+ }
2575
+ //error
2576
+ throw_or_mimic<exceptions::no_such_option>(name);
2577
+ }
2578
+
2579
+ auto value = iter->second;
2580
+
2581
+ if (i + 1 == s.size())
2582
+ {
2583
+ //it must be the last argument
2584
+ checked_parse_arg(argc, argv, current, value, name);
2585
+ }
2586
+ else if (value->value().has_implicit())
2587
+ {
2588
+ parse_option(value, name, value->value().get_implicit_value());
2589
+ }
2590
+ else if (i + 1 < s.size())
2591
+ {
2592
+ std::string arg_value = s.substr(i + 1);
2593
+ parse_option(value, name, arg_value);
2594
+ break;
2595
+ }
2596
+ else
2597
+ {
2598
+ //error
2599
+ throw_or_mimic<exceptions::option_requires_argument>(name);
2600
+ }
2601
+ }
2602
+ }
2603
+ else if (argu_desc.arg_name.length() != 0)
2604
+ {
2605
+ const std::string& name = argu_desc.arg_name;
2606
+
2607
+ auto iter = m_options.find(name);
2608
+
2609
+ if (iter == m_options.end())
2610
+ {
2611
+ if (m_allow_unrecognised)
2612
+ {
2613
+ // keep unrecognised options in argument list, skip to next argument
2614
+ unmatched.emplace_back(argv[current]);
2615
+ ++current;
2616
+ continue;
2617
+ }
2618
+ //error
2619
+ throw_or_mimic<exceptions::no_such_option>(name);
2620
+ }
2621
+
2622
+ auto opt = iter->second;
2623
+
2624
+ //equals provided for long option?
2625
+ if (argu_desc.set_value)
2626
+ {
2627
+ //parse the option given
2628
+
2629
+ parse_option(opt, name, argu_desc.value);
2630
+ }
2631
+ else
2632
+ {
2633
+ //parse the next argument
2634
+ checked_parse_arg(argc, argv, current, opt, name);
2635
+ }
2636
+ }
2637
+
2638
+ }
2639
+
2640
+ ++current;
2641
+ }
2642
+
2643
+ for (auto& opt : m_options)
2644
+ {
2645
+ auto& detail = opt.second;
2646
+ const auto& value = detail->value();
2647
+
2648
+ auto& store = m_parsed[detail->hash()];
2649
+
2650
+ if (value.has_default()) {
2651
+ if (!store.count() && !store.has_default()) {
2652
+ parse_default(detail);
2653
+ }
2654
+ }
2655
+ else {
2656
+ parse_no_value(detail);
2657
+ }
2658
+ }
2659
+
2660
+ if (consume_remaining)
2661
+ {
2662
+ while (current < argc)
2663
+ {
2664
+ if (!consume_positional(argv[current], next_positional)) {
2665
+ break;
2666
+ }
2667
+ ++current;
2668
+ }
2669
+
2670
+ //adjust argv for any that couldn't be swallowed
2671
+ while (current != argc) {
2672
+ unmatched.emplace_back(argv[current]);
2673
+ ++current;
2674
+ }
2675
+ }
2676
+
2677
+ finalise_aliases();
2678
+
2679
+ ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(m_defaults), std::move(unmatched));
2680
+ return parsed;
2681
+ }
2682
+
2683
+ inline
2684
+ void
2685
+ OptionParser::finalise_aliases()
2686
+ {
2687
+ for (auto& option: m_options)
2688
+ {
2689
+ auto& detail = *option.second;
2690
+ auto hash = detail.hash();
2691
+ m_keys[detail.short_name()] = hash;
2692
+ for(const auto& long_name : detail.long_names()) {
2693
+ m_keys[long_name] = hash;
2694
+ }
2695
+
2696
+ m_parsed.emplace(hash, OptionValue());
2697
+ }
2698
+ }
2699
+
2700
+ inline
2701
+ void
2702
+ Options::add_option
2703
+ (
2704
+ const std::string& group,
2705
+ const Option& option
2706
+ )
2707
+ {
2708
+ add_options(group, {option});
2709
+ }
2710
+
2711
+ inline
2712
+ void
2713
+ Options::add_option
2714
+ (
2715
+ const std::string& group,
2716
+ const std::string& s,
2717
+ const OptionNames& l,
2718
+ std::string desc,
2719
+ const std::shared_ptr<const Value>& value,
2720
+ std::string arg_help
2721
+ )
2722
+ {
2723
+ auto stringDesc = toLocalString(std::move(desc));
2724
+ auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
2725
+
2726
+ if (!s.empty())
2727
+ {
2728
+ add_one_option(s, option);
2729
+ }
2730
+
2731
+ for(const auto& long_name : l) {
2732
+ add_one_option(long_name, option);
2733
+ }
2734
+
2735
+ //add the help details
2736
+
2737
+ if (m_help.find(group) == m_help.end())
2738
+ {
2739
+ m_group.push_back(group);
2740
+ }
2741
+
2742
+ auto& options = m_help[group];
2743
+
2744
+ options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
2745
+ value->has_default(), value->get_default_value(),
2746
+ value->has_implicit(), value->get_implicit_value(),
2747
+ std::move(arg_help),
2748
+ value->is_container(),
2749
+ value->is_boolean()});
2750
+ }
2751
+
2752
+ inline
2753
+ void
2754
+ Options::add_one_option
2755
+ (
2756
+ const std::string& option,
2757
+ const std::shared_ptr<OptionDetails>& details
2758
+ )
2759
+ {
2760
+ auto in = m_options->emplace(option, details);
2761
+
2762
+ if (!in.second)
2763
+ {
2764
+ throw_or_mimic<exceptions::option_already_exists>(option);
2765
+ }
2766
+ }
2767
+
2768
+ inline
2769
+ String
2770
+ Options::help_one_group(const std::string& g) const
2771
+ {
2772
+ using OptionHelp = std::vector<std::pair<String, String>>;
2773
+
2774
+ String result;
2775
+
2776
+ auto group = m_help.find(g);
2777
+ if (group == m_help.end())
2778
+ {
2779
+ return result;
2780
+ }
2781
+
2782
+ OptionHelp format;
2783
+
2784
+ std::size_t longest = 0;
2785
+
2786
+ if (!g.empty())
2787
+ {
2788
+ result += toLocalString(" " + g + " options:\n");
2789
+ }
2790
+
2791
+ for (const auto& o : group->second.options)
2792
+ {
2793
+ if (o.l.size() &&
2794
+ m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2795
+ !m_show_positional)
2796
+ {
2797
+ continue;
2798
+ }
2799
+
2800
+ auto s = format_option(o);
2801
+ longest = (std::max)(longest, stringLength(s));
2802
+ format.push_back(std::make_pair(s, String()));
2803
+ }
2804
+ longest = (std::min)(longest, OPTION_LONGEST);
2805
+
2806
+ //widest allowed description -- min 10 chars for helptext/line
2807
+ std::size_t allowed = 10;
2808
+ if (m_width > allowed + longest + OPTION_DESC_GAP)
2809
+ {
2810
+ allowed = m_width - longest - OPTION_DESC_GAP;
2811
+ }
2812
+
2813
+ auto fiter = format.begin();
2814
+ for (const auto& o : group->second.options)
2815
+ {
2816
+ if (o.l.size() &&
2817
+ m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2818
+ !m_show_positional)
2819
+ {
2820
+ continue;
2821
+ }
2822
+
2823
+ auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion);
2824
+
2825
+ result += fiter->first;
2826
+ if (stringLength(fiter->first) > longest)
2827
+ {
2828
+ result += '\n';
2829
+ result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
2830
+ }
2831
+ else
2832
+ {
2833
+ result += toLocalString(std::string(longest + OPTION_DESC_GAP -
2834
+ stringLength(fiter->first),
2835
+ ' '));
2836
+ }
2837
+ result += d;
2838
+ result += '\n';
2839
+
2840
+ ++fiter;
2841
+ }
2842
+
2843
+ return result;
2844
+ }
2845
+
2846
+ inline
2847
+ void
2848
+ Options::generate_group_help
2849
+ (
2850
+ String& result,
2851
+ const std::vector<std::string>& print_groups
2852
+ ) const
2853
+ {
2854
+ for (std::size_t i = 0; i != print_groups.size(); ++i)
2855
+ {
2856
+ const String& group_help_text = help_one_group(print_groups[i]);
2857
+ if (empty(group_help_text))
2858
+ {
2859
+ continue;
2860
+ }
2861
+ result += group_help_text;
2862
+ if (i < print_groups.size() - 1)
2863
+ {
2864
+ result += '\n';
2865
+ }
2866
+ }
2867
+ }
2868
+
2869
+ inline
2870
+ void
2871
+ Options::generate_all_groups_help(String& result) const
2872
+ {
2873
+ generate_group_help(result, m_group);
2874
+ }
2875
+
2876
+ inline
2877
+ std::string
2878
+ Options::help(const std::vector<std::string>& help_groups, bool print_usage) const
2879
+ {
2880
+ String result = m_help_string;
2881
+ if(print_usage)
2882
+ {
2883
+ result+= "\nUsage:\n " + toLocalString(m_program);
2884
+ }
2885
+
2886
+ if (!m_custom_help.empty())
2887
+ {
2888
+ result += " " + toLocalString(m_custom_help);
2889
+ }
2890
+
2891
+ if (!m_positional.empty() && !m_positional_help.empty()) {
2892
+ result += " " + toLocalString(m_positional_help);
2893
+ }
2894
+
2895
+ result += "\n\n";
2896
+
2897
+ if (help_groups.empty())
2898
+ {
2899
+ generate_all_groups_help(result);
2900
+ }
2901
+ else
2902
+ {
2903
+ generate_group_help(result, help_groups);
2904
+ }
2905
+
2906
+ return toUTF8String(result);
2907
+ }
2908
+
2909
+ inline
2910
+ std::vector<std::string>
2911
+ Options::groups() const
2912
+ {
2913
+ return m_group;
2914
+ }
2915
+
2916
+ inline
2917
+ const HelpGroupDetails&
2918
+ Options::group_help(const std::string& group) const
2919
+ {
2920
+ return m_help.at(group);
2921
+ }
2922
+
2923
+ } // namespace cxxopts
2924
+
2925
+ #endif //CXXOPTS_HPP_INCLUDED