🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2611 lines
56 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. /*
  2. Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in
  10. all copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  17. THE SOFTWARE.
  18. */
  19. #ifndef CXXOPTS_HPP_INCLUDED
  20. #define CXXOPTS_HPP_INCLUDED
  21. #include <cctype>
  22. #include <cstring>
  23. #include <exception>
  24. #include <iostream>
  25. #include <limits>
  26. #include <list>
  27. #include <map>
  28. #include <memory>
  29. #include <sstream>
  30. #include <string>
  31. #include <unordered_map>
  32. #include <unordered_set>
  33. #include <utility>
  34. #include <vector>
  35. #include <algorithm>
  36. #if defined(__GNUC__) && !defined(__clang__)
  37. # if (__GNUC__ * 10 + __GNUC_MINOR__) < 49
  38. # define CXXOPTS_NO_REGEX true
  39. # endif
  40. #endif
  41. #ifndef CXXOPTS_NO_REGEX
  42. # include <regex>
  43. #endif // CXXOPTS_NO_REGEX
  44. // Nonstandard before C++17, which is coincidentally what we also need for <optional>
  45. #ifdef __has_include
  46. # if __has_include(<optional>)
  47. # include <optional>
  48. # ifdef __cpp_lib_optional
  49. # define CXXOPTS_HAS_OPTIONAL
  50. # endif
  51. # endif
  52. #endif
  53. #if __cplusplus >= 201603L
  54. #define CXXOPTS_NODISCARD [[nodiscard]]
  55. #else
  56. #define CXXOPTS_NODISCARD
  57. #endif
  58. #ifndef CXXOPTS_VECTOR_DELIMITER
  59. #define CXXOPTS_VECTOR_DELIMITER ','
  60. #endif
  61. #define CXXOPTS__VERSION_MAJOR 3
  62. #define CXXOPTS__VERSION_MINOR 0
  63. #define CXXOPTS__VERSION_PATCH 0
  64. #if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
  65. #define CXXOPTS_NULL_DEREF_IGNORE
  66. #endif
  67. namespace cxxopts
  68. {
  69. static constexpr struct {
  70. uint8_t major, minor, patch;
  71. } version = {
  72. CXXOPTS__VERSION_MAJOR,
  73. CXXOPTS__VERSION_MINOR,
  74. CXXOPTS__VERSION_PATCH
  75. };
  76. } // namespace cxxopts
  77. //when we ask cxxopts to use Unicode, help strings are processed using ICU,
  78. //which results in the correct lengths being computed for strings when they
  79. //are formatted for the help output
  80. //it is necessary to make sure that <unicode/unistr.h> can be found by the
  81. //compiler, and that icu-uc is linked in to the binary.
  82. #ifdef CXXOPTS_USE_UNICODE
  83. #include <unicode/unistr.h>
  84. namespace cxxopts
  85. {
  86. using String = icu::UnicodeString;
  87. inline
  88. String
  89. toLocalString(std::string s)
  90. {
  91. return icu::UnicodeString::fromUTF8(std::move(s));
  92. }
  93. #if defined(__GNUC__)
  94. // GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it:
  95. // warning: base class 'class std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual destructor
  96. #pragma GCC diagnostic push
  97. #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
  98. #pragma GCC diagnostic ignored "-Weffc++"
  99. // This will be ignored under other compilers like LLVM clang.
  100. #endif
  101. class UnicodeStringIterator : public
  102. std::iterator<std::forward_iterator_tag, int32_t>
  103. {
  104. public:
  105. UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos)
  106. : s(string)
  107. , i(pos)
  108. {
  109. }
  110. value_type
  111. operator*() const
  112. {
  113. return s->char32At(i);
  114. }
  115. bool
  116. operator==(const UnicodeStringIterator& rhs) const
  117. {
  118. return s == rhs.s && i == rhs.i;
  119. }
  120. bool
  121. operator!=(const UnicodeStringIterator& rhs) const
  122. {
  123. return !(*this == rhs);
  124. }
  125. UnicodeStringIterator&
  126. operator++()
  127. {
  128. ++i;
  129. return *this;
  130. }
  131. UnicodeStringIterator
  132. operator+(int32_t v)
  133. {
  134. return UnicodeStringIterator(s, i + v);
  135. }
  136. private:
  137. const icu::UnicodeString* s;
  138. int32_t i;
  139. };
  140. #if defined(__GNUC__)
  141. #pragma GCC diagnostic pop
  142. #endif
  143. inline
  144. String&
  145. stringAppend(String&s, String a)
  146. {
  147. return s.append(std::move(a));
  148. }
  149. inline
  150. String&
  151. stringAppend(String& s, size_t n, UChar32 c)
  152. {
  153. for (size_t i = 0; i != n; ++i)
  154. {
  155. s.append(c);
  156. }
  157. return s;
  158. }
  159. template <typename Iterator>
  160. String&
  161. stringAppend(String& s, Iterator begin, Iterator end)
  162. {
  163. while (begin != end)
  164. {
  165. s.append(*begin);
  166. ++begin;
  167. }
  168. return s;
  169. }
  170. inline
  171. size_t
  172. stringLength(const String& s)
  173. {
  174. return s.length();
  175. }
  176. inline
  177. std::string
  178. toUTF8String(const String& s)
  179. {
  180. std::string result;
  181. s.toUTF8String(result);
  182. return result;
  183. }
  184. inline
  185. bool
  186. empty(const String& s)
  187. {
  188. return s.isEmpty();
  189. }
  190. }
  191. namespace std
  192. {
  193. inline
  194. cxxopts::UnicodeStringIterator
  195. begin(const icu::UnicodeString& s)
  196. {
  197. return cxxopts::UnicodeStringIterator(&s, 0);
  198. }
  199. inline
  200. cxxopts::UnicodeStringIterator
  201. end(const icu::UnicodeString& s)
  202. {
  203. return cxxopts::UnicodeStringIterator(&s, s.length());
  204. }
  205. }
  206. //ifdef CXXOPTS_USE_UNICODE
  207. #else
  208. namespace cxxopts
  209. {
  210. using String = std::string;
  211. template <typename T>
  212. T
  213. toLocalString(T&& t)
  214. {
  215. return std::forward<T>(t);
  216. }
  217. inline
  218. size_t
  219. stringLength(const String& s)
  220. {
  221. return s.length();
  222. }
  223. inline
  224. String&
  225. stringAppend(String&s, const String& a)
  226. {
  227. return s.append(a);
  228. }
  229. inline
  230. String&
  231. stringAppend(String& s, size_t n, char c)
  232. {
  233. return s.append(n, c);
  234. }
  235. template <typename Iterator>
  236. String&
  237. stringAppend(String& s, Iterator begin, Iterator end)
  238. {
  239. return s.append(begin, end);
  240. }
  241. template <typename T>
  242. std::string
  243. toUTF8String(T&& t)
  244. {
  245. return std::forward<T>(t);
  246. }
  247. inline
  248. bool
  249. empty(const std::string& s)
  250. {
  251. return s.empty();
  252. }
  253. } // namespace cxxopts
  254. //ifdef CXXOPTS_USE_UNICODE
  255. #endif
  256. namespace cxxopts
  257. {
  258. namespace
  259. {
  260. #ifdef _WIN32
  261. const std::string LQUOTE("\'");
  262. const std::string RQUOTE("\'");
  263. #else
  264. const std::string LQUOTE("");
  265. const std::string RQUOTE("");
  266. #endif
  267. } // namespace
  268. #if defined(__GNUC__)
  269. // GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it:
  270. // warning: base class 'class std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual destructor
  271. #pragma GCC diagnostic push
  272. #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
  273. #pragma GCC diagnostic ignored "-Weffc++"
  274. // This will be ignored under other compilers like LLVM clang.
  275. #endif
  276. class Value : public std::enable_shared_from_this<Value>
  277. {
  278. public:
  279. virtual ~Value() = default;
  280. virtual
  281. std::shared_ptr<Value>
  282. clone() const = 0;
  283. virtual void
  284. parse(const std::string& text) const = 0;
  285. virtual void
  286. parse() const = 0;
  287. virtual bool
  288. has_default() const = 0;
  289. virtual bool
  290. is_container() const = 0;
  291. virtual bool
  292. has_implicit() const = 0;
  293. virtual std::string
  294. get_default_value() const = 0;
  295. virtual std::string
  296. get_implicit_value() const = 0;
  297. virtual std::shared_ptr<Value>
  298. default_value(const std::string& value) = 0;
  299. virtual std::shared_ptr<Value>
  300. implicit_value(const std::string& value) = 0;
  301. virtual std::shared_ptr<Value>
  302. no_implicit_value() = 0;
  303. virtual bool
  304. is_boolean() const = 0;
  305. };
  306. #if defined(__GNUC__)
  307. #pragma GCC diagnostic pop
  308. #endif
  309. class OptionException : public std::exception
  310. {
  311. public:
  312. explicit OptionException(std::string message)
  313. : m_message(std::move(message))
  314. {
  315. }
  316. CXXOPTS_NODISCARD
  317. const char*
  318. what() const noexcept override
  319. {
  320. return m_message.c_str();
  321. }
  322. private:
  323. std::string m_message;
  324. };
  325. class OptionSpecException : public OptionException
  326. {
  327. public:
  328. explicit OptionSpecException(const std::string& message)
  329. : OptionException(message)
  330. {
  331. }
  332. };
  333. class OptionParseException : public OptionException
  334. {
  335. public:
  336. explicit OptionParseException(const std::string& message)
  337. : OptionException(message)
  338. {
  339. }
  340. };
  341. class option_exists_error : public OptionSpecException
  342. {
  343. public:
  344. explicit option_exists_error(const std::string& option)
  345. : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists")
  346. {
  347. }
  348. };
  349. class invalid_option_format_error : public OptionSpecException
  350. {
  351. public:
  352. explicit invalid_option_format_error(const std::string& format)
  353. : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE)
  354. {
  355. }
  356. };
  357. class option_syntax_exception : public OptionParseException {
  358. public:
  359. explicit option_syntax_exception(const std::string& text)
  360. : OptionParseException("Argument " + LQUOTE + text + RQUOTE +
  361. " starts with a - but has incorrect syntax")
  362. {
  363. }
  364. };
  365. class option_not_exists_exception : public OptionParseException
  366. {
  367. public:
  368. explicit option_not_exists_exception(const std::string& option)
  369. : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist")
  370. {
  371. }
  372. };
  373. class missing_argument_exception : public OptionParseException
  374. {
  375. public:
  376. explicit missing_argument_exception(const std::string& option)
  377. : OptionParseException(
  378. "Option " + LQUOTE + option + RQUOTE + " is missing an argument"
  379. )
  380. {
  381. }
  382. };
  383. class option_requires_argument_exception : public OptionParseException
  384. {
  385. public:
  386. explicit option_requires_argument_exception(const std::string& option)
  387. : OptionParseException(
  388. "Option " + LQUOTE + option + RQUOTE + " requires an argument"
  389. )
  390. {
  391. }
  392. };
  393. class option_not_has_argument_exception : public OptionParseException
  394. {
  395. public:
  396. option_not_has_argument_exception
  397. (
  398. const std::string& option,
  399. const std::string& arg
  400. )
  401. : OptionParseException(
  402. "Option " + LQUOTE + option + RQUOTE +
  403. " does not take an argument, but argument " +
  404. LQUOTE + arg + RQUOTE + " given"
  405. )
  406. {
  407. }
  408. };
  409. class option_not_present_exception : public OptionParseException
  410. {
  411. public:
  412. explicit option_not_present_exception(const std::string& option)
  413. : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present")
  414. {
  415. }
  416. };
  417. class option_has_no_value_exception : public OptionException
  418. {
  419. public:
  420. explicit option_has_no_value_exception(const std::string& option)
  421. : OptionException(
  422. !option.empty() ?
  423. ("Option " + LQUOTE + option + RQUOTE + " has no value") :
  424. "Option has no value")
  425. {
  426. }
  427. };
  428. class argument_incorrect_type : public OptionParseException
  429. {
  430. public:
  431. explicit argument_incorrect_type
  432. (
  433. const std::string& arg
  434. )
  435. : OptionParseException(
  436. "Argument " + LQUOTE + arg + RQUOTE + " failed to parse"
  437. )
  438. {
  439. }
  440. };
  441. class option_required_exception : public OptionParseException
  442. {
  443. public:
  444. explicit option_required_exception(const std::string& option)
  445. : OptionParseException(
  446. "Option " + LQUOTE + option + RQUOTE + " is required but not present"
  447. )
  448. {
  449. }
  450. };
  451. template <typename T>
  452. void throw_or_mimic(const std::string& text)
  453. {
  454. static_assert(std::is_base_of<std::exception, T>::value,
  455. "throw_or_mimic only works on std::exception and "
  456. "deriving classes");
  457. #ifndef CXXOPTS_NO_EXCEPTIONS
  458. // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw
  459. throw T{text};
  460. #else
  461. // Otherwise manually instantiate the exception, print what() to stderr,
  462. // and exit
  463. T exception{text};
  464. std::cerr << exception.what() << std::endl;
  465. std::exit(EXIT_FAILURE);
  466. #endif
  467. }
  468. namespace values
  469. {
  470. namespace parser_tool
  471. {
  472. struct IntegerDesc
  473. {
  474. std::string negative = "";
  475. std::string base = "";
  476. std::string value = "";
  477. };
  478. struct ArguDesc {
  479. std::string arg_name = "";
  480. bool grouping = false;
  481. bool set_value = false;
  482. std::string value = "";
  483. };
  484. #ifdef CXXOPTS_NO_REGEX
  485. inline IntegerDesc SplitInteger(const std::string &text)
  486. {
  487. if (text.empty())
  488. {
  489. throw_or_mimic<argument_incorrect_type>(text);
  490. }
  491. IntegerDesc desc;
  492. const char *pdata = text.c_str();
  493. if (*pdata == '-')
  494. {
  495. pdata += 1;
  496. desc.negative = "-";
  497. }
  498. if (strncmp(pdata, "0x", 2) == 0)
  499. {
  500. pdata += 2;
  501. desc.base = "0x";
  502. }
  503. if (*pdata != '\0')
  504. {
  505. desc.value = std::string(pdata);
  506. }
  507. else
  508. {
  509. throw_or_mimic<argument_incorrect_type>(text);
  510. }
  511. return desc;
  512. }
  513. inline bool IsTrueText(const std::string &text)
  514. {
  515. const char *pdata = text.c_str();
  516. if (*pdata == 't' || *pdata == 'T')
  517. {
  518. pdata += 1;
  519. if (strncmp(pdata, "rue\0", 4) == 0)
  520. {
  521. return true;
  522. }
  523. }
  524. else if (strncmp(pdata, "1\0", 2) == 0)
  525. {
  526. return true;
  527. }
  528. return false;
  529. }
  530. inline bool IsFalseText(const std::string &text)
  531. {
  532. const char *pdata = text.c_str();
  533. if (*pdata == 'f' || *pdata == 'F')
  534. {
  535. pdata += 1;
  536. if (strncmp(pdata, "alse\0", 5) == 0)
  537. {
  538. return true;
  539. }
  540. }
  541. else if (strncmp(pdata, "0\0", 2) == 0)
  542. {
  543. return true;
  544. }
  545. return false;
  546. }
  547. inline std::pair<std::string, std::string> SplitSwitchDef(const std::string &text)
  548. {
  549. std::string short_sw, long_sw;
  550. const char *pdata = text.c_str();
  551. if (isalnum(*pdata) && *(pdata + 1) == ',') {
  552. short_sw = std::string(1, *pdata);
  553. pdata += 2;
  554. }
  555. while (*pdata == ' ') { pdata += 1; }
  556. if (isalnum(*pdata)) {
  557. const char *store = pdata;
  558. pdata += 1;
  559. while (isalnum(*pdata) || *pdata == '-' || *pdata == '_') {
  560. pdata += 1;
  561. }
  562. if (*pdata == '\0') {
  563. long_sw = std::string(store, pdata - store);
  564. } else {
  565. throw_or_mimic<invalid_option_format_error>(text);
  566. }
  567. }
  568. return std::pair<std::string, std::string>(short_sw, long_sw);
  569. }
  570. inline ArguDesc ParseArgument(const char *arg, bool &matched)
  571. {
  572. ArguDesc argu_desc;
  573. const char *pdata = arg;
  574. matched = false;
  575. if (strncmp(pdata, "--", 2) == 0)
  576. {
  577. pdata += 2;
  578. if (isalnum(*pdata))
  579. {
  580. argu_desc.arg_name.push_back(*pdata);
  581. pdata += 1;
  582. while (isalnum(*pdata) || *pdata == '-' || *pdata == '_')
  583. {
  584. argu_desc.arg_name.push_back(*pdata);
  585. pdata += 1;
  586. }
  587. if (argu_desc.arg_name.length() > 1)
  588. {
  589. if (*pdata == '=')
  590. {
  591. argu_desc.set_value = true;
  592. pdata += 1;
  593. if (*pdata != '\0')
  594. {
  595. argu_desc.value = std::string(pdata);
  596. }
  597. matched = true;
  598. }
  599. else if (*pdata == '\0')
  600. {
  601. matched = true;
  602. }
  603. }
  604. }
  605. }
  606. else if (strncmp(pdata, "-", 1) == 0)
  607. {
  608. pdata += 1;
  609. argu_desc.grouping = true;
  610. while (isalnum(*pdata))
  611. {
  612. argu_desc.arg_name.push_back(*pdata);
  613. pdata += 1;
  614. }
  615. matched = !argu_desc.arg_name.empty() && *pdata == '\0';
  616. }
  617. return argu_desc;
  618. }
  619. #else // CXXOPTS_NO_REGEX
  620. namespace
  621. {
  622. std::basic_regex<char> integer_pattern
  623. ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
  624. std::basic_regex<char> truthy_pattern
  625. ("(t|T)(rue)?|1");
  626. std::basic_regex<char> falsy_pattern
  627. ("(f|F)(alse)?|0");
  628. std::basic_regex<char> option_matcher
  629. ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
  630. std::basic_regex<char> option_specifier
  631. ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
  632. } // namespace
  633. inline IntegerDesc SplitInteger(const std::string &text)
  634. {
  635. std::smatch match;
  636. std::regex_match(text, match, integer_pattern);
  637. if (match.length() == 0)
  638. {
  639. throw_or_mimic<argument_incorrect_type>(text);
  640. }
  641. IntegerDesc desc;
  642. desc.negative = match[1];
  643. desc.base = match[2];
  644. desc.value = match[3];
  645. if (match.length(4) > 0)
  646. {
  647. desc.base = match[5];
  648. desc.value = "0";
  649. return desc;
  650. }
  651. return desc;
  652. }
  653. inline bool IsTrueText(const std::string &text)
  654. {
  655. std::smatch result;
  656. std::regex_match(text, result, truthy_pattern);
  657. return !result.empty();
  658. }
  659. inline bool IsFalseText(const std::string &text)
  660. {
  661. std::smatch result;
  662. std::regex_match(text, result, falsy_pattern);
  663. return !result.empty();
  664. }
  665. inline std::pair<std::string, std::string> SplitSwitchDef(const std::string &text)
  666. {
  667. std::match_results<const char*> result;
  668. std::regex_match(text.c_str(), result, option_specifier);
  669. if (result.empty())
  670. {
  671. throw_or_mimic<invalid_option_format_error>(text);
  672. }
  673. const std::string& short_sw = result[2];
  674. const std::string& long_sw = result[3];
  675. return std::pair<std::string, std::string>(short_sw, long_sw);
  676. }
  677. inline ArguDesc ParseArgument(const char *arg, bool &matched)
  678. {
  679. std::match_results<const char*> result;
  680. std::regex_match(arg, result, option_matcher);
  681. matched = !result.empty();
  682. ArguDesc argu_desc;
  683. if (matched) {
  684. argu_desc.arg_name = result[1].str();
  685. argu_desc.set_value = result[2].length() > 0;
  686. argu_desc.value = result[3].str();
  687. if (result[4].length() > 0)
  688. {
  689. argu_desc.grouping = true;
  690. argu_desc.arg_name = result[4].str();
  691. }
  692. }
  693. return argu_desc;
  694. }
  695. #endif // CXXOPTS_NO_REGEX
  696. #undef CXXOPTS_NO_REGEX
  697. }
  698. namespace detail
  699. {
  700. template <typename T, bool B>
  701. struct SignedCheck;
  702. template <typename T>
  703. struct SignedCheck<T, true>
  704. {
  705. template <typename U>
  706. void
  707. operator()(bool negative, U u, const std::string& text)
  708. {
  709. if (negative)
  710. {
  711. if (u > static_cast<U>((std::numeric_limits<T>::min)()))
  712. {
  713. throw_or_mimic<argument_incorrect_type>(text);
  714. }
  715. }
  716. else
  717. {
  718. if (u > static_cast<U>((std::numeric_limits<T>::max)()))
  719. {
  720. throw_or_mimic<argument_incorrect_type>(text);
  721. }
  722. }
  723. }
  724. };
  725. template <typename T>
  726. struct SignedCheck<T, false>
  727. {
  728. template <typename U>
  729. void
  730. operator()(bool, U, const std::string&) const {}
  731. };
  732. template <typename T, typename U>
  733. void
  734. check_signed_range(bool negative, U value, const std::string& text)
  735. {
  736. SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
  737. }
  738. } // namespace detail
  739. template <typename R, typename T>
  740. void
  741. checked_negate(R& r, T&& t, const std::string&, std::true_type)
  742. {
  743. // if we got to here, then `t` is a positive number that fits into
  744. // `R`. So to avoid MSVC C4146, we first cast it to `R`.
  745. // See https://github.com/jarro2783/cxxopts/issues/62 for more details.
  746. r = static_cast<R>(-static_cast<R>(t-1)-1);
  747. }
  748. template <typename R, typename T>
  749. void
  750. checked_negate(R&, T&&, const std::string& text, std::false_type)
  751. {
  752. throw_or_mimic<argument_incorrect_type>(text);
  753. }
  754. template <typename T>
  755. void
  756. integer_parser(const std::string& text, T& value)
  757. {
  758. parser_tool::IntegerDesc int_desc = parser_tool::SplitInteger(text);
  759. using US = typename std::make_unsigned<T>::type;
  760. constexpr bool is_signed = std::numeric_limits<T>::is_signed;
  761. const bool negative = int_desc.negative.length() > 0;
  762. const uint8_t base = int_desc.base.length() > 0 ? 16 : 10;
  763. const std::string & value_match = int_desc.value;
  764. US result = 0;
  765. for (char ch : value_match)
  766. {
  767. US digit = 0;
  768. if (ch >= '0' && ch <= '9')
  769. {
  770. digit = static_cast<US>(ch - '0');
  771. }
  772. else if (base == 16 && ch >= 'a' && ch <= 'f')
  773. {
  774. digit = static_cast<US>(ch - 'a' + 10);
  775. }
  776. else if (base == 16 && ch >= 'A' && ch <= 'F')
  777. {
  778. digit = static_cast<US>(ch - 'A' + 10);
  779. }
  780. else
  781. {
  782. throw_or_mimic<argument_incorrect_type>(text);
  783. }
  784. const US next = static_cast<US>(result * base + digit);
  785. if (result > next)
  786. {
  787. throw_or_mimic<argument_incorrect_type>(text);
  788. }
  789. result = next;
  790. }
  791. detail::check_signed_range<T>(negative, result, text);
  792. if (negative)
  793. {
  794. checked_negate<T>(value, result, text, std::integral_constant<bool, is_signed>());
  795. }
  796. else
  797. {
  798. value = static_cast<T>(result);
  799. }
  800. }
  801. template <typename T>
  802. void stringstream_parser(const std::string& text, T& value)
  803. {
  804. std::stringstream in(text);
  805. in >> value;
  806. if (!in) {
  807. throw_or_mimic<argument_incorrect_type>(text);
  808. }
  809. }
  810. template <typename T,
  811. typename std::enable_if<std::is_integral<T>::value>::type* = nullptr
  812. >
  813. void parse_value(const std::string& text, T& value)
  814. {
  815. integer_parser(text, value);
  816. }
  817. inline
  818. void
  819. parse_value(const std::string& text, bool& value)
  820. {
  821. if (parser_tool::IsTrueText(text))
  822. {
  823. value = true;
  824. return;
  825. }
  826. if (parser_tool::IsFalseText(text))
  827. {
  828. value = false;
  829. return;
  830. }
  831. throw_or_mimic<argument_incorrect_type>(text);
  832. }
  833. inline
  834. void
  835. parse_value(const std::string& text, std::string& value)
  836. {
  837. value = text;
  838. }
  839. // The fallback parser. It uses the stringstream parser to parse all types
  840. // that have not been overloaded explicitly. It has to be placed in the
  841. // source code before all other more specialized templates.
  842. template <typename T,
  843. typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr
  844. >
  845. void
  846. parse_value(const std::string& text, T& value) {
  847. stringstream_parser(text, value);
  848. }
  849. template <typename T>
  850. void
  851. parse_value(const std::string& text, std::vector<T>& value)
  852. {
  853. if (text.empty()) {
  854. T v;
  855. parse_value(text, v);
  856. value.emplace_back(std::move(v));
  857. return;
  858. }
  859. std::stringstream in(text);
  860. std::string token;
  861. while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
  862. T v;
  863. parse_value(token, v);
  864. value.emplace_back(std::move(v));
  865. }
  866. }
  867. #ifdef CXXOPTS_HAS_OPTIONAL
  868. template <typename T>
  869. void
  870. parse_value(const std::string& text, std::optional<T>& value)
  871. {
  872. T result;
  873. parse_value(text, result);
  874. value = std::move(result);
  875. }
  876. #endif
  877. inline
  878. void parse_value(const std::string& text, char& c)
  879. {
  880. if (text.length() != 1)
  881. {
  882. throw_or_mimic<argument_incorrect_type>(text);
  883. }
  884. c = text[0];
  885. }
  886. template <typename T>
  887. struct type_is_container
  888. {
  889. static constexpr bool value = false;
  890. };
  891. template <typename T>
  892. struct type_is_container<std::vector<T>>
  893. {
  894. static constexpr bool value = true;
  895. };
  896. template <typename T>
  897. class abstract_value : public Value
  898. {
  899. using Self = abstract_value<T>;
  900. public:
  901. abstract_value()
  902. : m_result(std::make_shared<T>())
  903. , m_store(m_result.get())
  904. {
  905. }
  906. explicit abstract_value(T* t)
  907. : m_store(t)
  908. {
  909. }
  910. ~abstract_value() override = default;
  911. abstract_value& operator=(const abstract_value&) = default;
  912. abstract_value(const abstract_value& rhs)
  913. {
  914. if (rhs.m_result)
  915. {
  916. m_result = std::make_shared<T>();
  917. m_store = m_result.get();
  918. }
  919. else
  920. {
  921. m_store = rhs.m_store;
  922. }
  923. m_default = rhs.m_default;
  924. m_implicit = rhs.m_implicit;
  925. m_default_value = rhs.m_default_value;
  926. m_implicit_value = rhs.m_implicit_value;
  927. }
  928. void
  929. parse(const std::string& text) const override
  930. {
  931. parse_value(text, *m_store);
  932. }
  933. bool
  934. is_container() const override
  935. {
  936. return type_is_container<T>::value;
  937. }
  938. void
  939. parse() const override
  940. {
  941. parse_value(m_default_value, *m_store);
  942. }
  943. bool
  944. has_default() const override
  945. {
  946. return m_default;
  947. }
  948. bool
  949. has_implicit() const override
  950. {
  951. return m_implicit;
  952. }
  953. std::shared_ptr<Value>
  954. default_value(const std::string& value) override
  955. {
  956. m_default = true;
  957. m_default_value = value;
  958. return shared_from_this();
  959. }
  960. std::shared_ptr<Value>
  961. implicit_value(const std::string& value) override
  962. {
  963. m_implicit = true;
  964. m_implicit_value = value;
  965. return shared_from_this();
  966. }
  967. std::shared_ptr<Value>
  968. no_implicit_value() override
  969. {
  970. m_implicit = false;
  971. return shared_from_this();
  972. }
  973. std::string
  974. get_default_value() const override
  975. {
  976. return m_default_value;
  977. }
  978. std::string
  979. get_implicit_value() const override
  980. {
  981. return m_implicit_value;
  982. }
  983. bool
  984. is_boolean() const override
  985. {
  986. return std::is_same<T, bool>::value;
  987. }
  988. const T&
  989. get() const
  990. {
  991. if (m_store == nullptr)
  992. {
  993. return *m_result;
  994. }
  995. return *m_store;
  996. }
  997. protected:
  998. std::shared_ptr<T> m_result{};
  999. T* m_store{};
  1000. bool m_default = false;
  1001. bool m_implicit = false;
  1002. std::string m_default_value{};
  1003. std::string m_implicit_value{};
  1004. };
  1005. template <typename T>
  1006. class standard_value : public abstract_value<T>
  1007. {
  1008. public:
  1009. using abstract_value<T>::abstract_value;
  1010. CXXOPTS_NODISCARD
  1011. std::shared_ptr<Value>
  1012. clone() const override
  1013. {
  1014. return std::make_shared<standard_value<T>>(*this);
  1015. }
  1016. };
  1017. template <>
  1018. class standard_value<bool> : public abstract_value<bool>
  1019. {
  1020. public:
  1021. ~standard_value() override = default;
  1022. standard_value()
  1023. {
  1024. set_default_and_implicit();
  1025. }
  1026. explicit standard_value(bool* b)
  1027. : abstract_value(b)
  1028. {
  1029. set_default_and_implicit();
  1030. }
  1031. std::shared_ptr<Value>
  1032. clone() const override
  1033. {
  1034. return std::make_shared<standard_value<bool>>(*this);
  1035. }
  1036. private:
  1037. void
  1038. set_default_and_implicit()
  1039. {
  1040. m_default = true;
  1041. m_default_value = "false";
  1042. m_implicit = true;
  1043. m_implicit_value = "true";
  1044. }
  1045. };
  1046. } // namespace values
  1047. template <typename T>
  1048. std::shared_ptr<Value>
  1049. value()
  1050. {
  1051. return std::make_shared<values::standard_value<T>>();
  1052. }
  1053. template <typename T>
  1054. std::shared_ptr<Value>
  1055. value(T& t)
  1056. {
  1057. return std::make_shared<values::standard_value<T>>(&t);
  1058. }
  1059. class OptionAdder;
  1060. class OptionDetails
  1061. {
  1062. public:
  1063. OptionDetails
  1064. (
  1065. std::string short_,
  1066. std::string long_,
  1067. String desc,
  1068. std::shared_ptr<const Value> val
  1069. )
  1070. : m_short(std::move(short_))
  1071. , m_long(std::move(long_))
  1072. , m_desc(std::move(desc))
  1073. , m_value(std::move(val))
  1074. , m_count(0)
  1075. {
  1076. m_hash = std::hash<std::string>{}(m_long + m_short);
  1077. }
  1078. OptionDetails(const OptionDetails& rhs)
  1079. : m_desc(rhs.m_desc)
  1080. , m_value(rhs.m_value->clone())
  1081. , m_count(rhs.m_count)
  1082. {
  1083. }
  1084. OptionDetails(OptionDetails&& rhs) = default;
  1085. CXXOPTS_NODISCARD
  1086. const String&
  1087. description() const
  1088. {
  1089. return m_desc;
  1090. }
  1091. CXXOPTS_NODISCARD
  1092. const Value&
  1093. value() const {
  1094. return *m_value;
  1095. }
  1096. CXXOPTS_NODISCARD
  1097. std::shared_ptr<Value>
  1098. make_storage() const
  1099. {
  1100. return m_value->clone();
  1101. }
  1102. CXXOPTS_NODISCARD
  1103. const std::string&
  1104. short_name() const
  1105. {
  1106. return m_short;
  1107. }
  1108. CXXOPTS_NODISCARD
  1109. const std::string&
  1110. long_name() const
  1111. {
  1112. return m_long;
  1113. }
  1114. size_t
  1115. hash() const
  1116. {
  1117. return m_hash;
  1118. }
  1119. private:
  1120. std::string m_short{};
  1121. std::string m_long{};
  1122. String m_desc{};
  1123. std::shared_ptr<const Value> m_value{};
  1124. int m_count;
  1125. size_t m_hash{};
  1126. };
  1127. struct HelpOptionDetails
  1128. {
  1129. std::string s;
  1130. std::string l;
  1131. String desc;
  1132. bool has_default;
  1133. std::string default_value;
  1134. bool has_implicit;
  1135. std::string implicit_value;
  1136. std::string arg_help;
  1137. bool is_container;
  1138. bool is_boolean;
  1139. };
  1140. struct HelpGroupDetails
  1141. {
  1142. std::string name{};
  1143. std::string description{};
  1144. std::vector<HelpOptionDetails> options{};
  1145. };
  1146. class OptionValue
  1147. {
  1148. public:
  1149. void
  1150. parse
  1151. (
  1152. const std::shared_ptr<const OptionDetails>& details,
  1153. const std::string& text
  1154. )
  1155. {
  1156. ensure_value(details);
  1157. ++m_count;
  1158. m_value->parse(text);
  1159. m_long_name = &details->long_name();
  1160. }
  1161. void
  1162. parse_default(const std::shared_ptr<const OptionDetails>& details)
  1163. {
  1164. ensure_value(details);
  1165. m_default = true;
  1166. m_long_name = &details->long_name();
  1167. m_value->parse();
  1168. }
  1169. void
  1170. parse_no_value(const std::shared_ptr<const OptionDetails>& details)
  1171. {
  1172. m_long_name = &details->long_name();
  1173. }
  1174. #if defined(CXXOPTS_NULL_DEREF_IGNORE)
  1175. #pragma GCC diagnostic push
  1176. #pragma GCC diagnostic ignored "-Wnull-dereference"
  1177. #endif
  1178. CXXOPTS_NODISCARD
  1179. size_t
  1180. count() const noexcept
  1181. {
  1182. return m_count;
  1183. }
  1184. #if defined(CXXOPTS_NULL_DEREF_IGNORE)
  1185. #pragma GCC diagnostic pop
  1186. #endif
  1187. // TODO: maybe default options should count towards the number of arguments
  1188. CXXOPTS_NODISCARD
  1189. bool
  1190. has_default() const noexcept
  1191. {
  1192. return m_default;
  1193. }
  1194. template <typename T>
  1195. const T&
  1196. as() const
  1197. {
  1198. if (m_value == nullptr) {
  1199. throw_or_mimic<option_has_no_value_exception>(
  1200. m_long_name == nullptr ? "" : *m_long_name);
  1201. }
  1202. #ifdef CXXOPTS_NO_RTTI
  1203. return static_cast<const values::standard_value<T>&>(*m_value).get();
  1204. #else
  1205. return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
  1206. #endif
  1207. }
  1208. private:
  1209. void
  1210. ensure_value(const std::shared_ptr<const OptionDetails>& details)
  1211. {
  1212. if (m_value == nullptr)
  1213. {
  1214. m_value = details->make_storage();
  1215. }
  1216. }
  1217. const std::string* m_long_name = nullptr;
  1218. // Holding this pointer is safe, since OptionValue's only exist in key-value pairs,
  1219. // where the key has the string we point to.
  1220. std::shared_ptr<Value> m_value{};
  1221. size_t m_count = 0;
  1222. bool m_default = false;
  1223. };
  1224. class KeyValue
  1225. {
  1226. public:
  1227. KeyValue(std::string key_, std::string value_)
  1228. : m_key(std::move(key_))
  1229. , m_value(std::move(value_))
  1230. {
  1231. }
  1232. CXXOPTS_NODISCARD
  1233. const std::string&
  1234. key() const
  1235. {
  1236. return m_key;
  1237. }
  1238. CXXOPTS_NODISCARD
  1239. const std::string&
  1240. value() const
  1241. {
  1242. return m_value;
  1243. }
  1244. template <typename T>
  1245. T
  1246. as() const
  1247. {
  1248. T result;
  1249. values::parse_value(m_value, result);
  1250. return result;
  1251. }
  1252. private:
  1253. std::string m_key;
  1254. std::string m_value;
  1255. };
  1256. using ParsedHashMap = std::unordered_map<size_t, OptionValue>;
  1257. using NameHashMap = std::unordered_map<std::string, size_t>;
  1258. class ParseResult
  1259. {
  1260. public:
  1261. ParseResult() = default;
  1262. ParseResult(const ParseResult&) = default;
  1263. ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector<KeyValue> sequential, std::vector<std::string>&& unmatched_args)
  1264. : m_keys(std::move(keys))
  1265. , m_values(std::move(values))
  1266. , m_sequential(std::move(sequential))
  1267. , m_unmatched(std::move(unmatched_args))
  1268. {
  1269. }
  1270. ParseResult& operator=(ParseResult&&) = default;
  1271. ParseResult& operator=(const ParseResult&) = default;
  1272. size_t
  1273. count(const std::string& o) const
  1274. {
  1275. auto iter = m_keys.find(o);
  1276. if (iter == m_keys.end())
  1277. {
  1278. return 0;
  1279. }
  1280. auto viter = m_values.find(iter->second);
  1281. if (viter == m_values.end())
  1282. {
  1283. return 0;
  1284. }
  1285. return viter->second.count();
  1286. }
  1287. const OptionValue&
  1288. operator[](const std::string& option) const
  1289. {
  1290. auto iter = m_keys.find(option);
  1291. if (iter == m_keys.end())
  1292. {
  1293. throw_or_mimic<option_not_present_exception>(option);
  1294. }
  1295. auto viter = m_values.find(iter->second);
  1296. if (viter == m_values.end())
  1297. {
  1298. throw_or_mimic<option_not_present_exception>(option);
  1299. }
  1300. return viter->second;
  1301. }
  1302. const std::vector<KeyValue>&
  1303. arguments() const
  1304. {
  1305. return m_sequential;
  1306. }
  1307. const std::vector<std::string>&
  1308. unmatched() const
  1309. {
  1310. return m_unmatched;
  1311. }
  1312. private:
  1313. NameHashMap m_keys{};
  1314. ParsedHashMap m_values{};
  1315. std::vector<KeyValue> m_sequential{};
  1316. std::vector<std::string> m_unmatched{};
  1317. };
  1318. struct Option
  1319. {
  1320. Option
  1321. (
  1322. std::string opts,
  1323. std::string desc,
  1324. std::shared_ptr<const Value> value = ::cxxopts::value<bool>(),
  1325. std::string arg_help = ""
  1326. )
  1327. : opts_(std::move(opts))
  1328. , desc_(std::move(desc))
  1329. , value_(std::move(value))
  1330. , arg_help_(std::move(arg_help))
  1331. {
  1332. }
  1333. std::string opts_;
  1334. std::string desc_;
  1335. std::shared_ptr<const Value> value_;
  1336. std::string arg_help_;
  1337. };
  1338. using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
  1339. using PositionalList = std::vector<std::string>;
  1340. using PositionalListIterator = PositionalList::const_iterator;
  1341. class OptionParser
  1342. {
  1343. public:
  1344. OptionParser(const OptionMap& options, const PositionalList& positional, bool allow_unrecognised)
  1345. : m_options(options)
  1346. , m_positional(positional)
  1347. , m_allow_unrecognised(allow_unrecognised)
  1348. {
  1349. }
  1350. ParseResult
  1351. parse(int argc, const char* const* argv);
  1352. bool
  1353. consume_positional(const std::string& a, PositionalListIterator& next);
  1354. void
  1355. checked_parse_arg
  1356. (
  1357. int argc,
  1358. const char* const* argv,
  1359. int& current,
  1360. const std::shared_ptr<OptionDetails>& value,
  1361. const std::string& name
  1362. );
  1363. void
  1364. add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg);
  1365. void
  1366. parse_option
  1367. (
  1368. const std::shared_ptr<OptionDetails>& value,
  1369. const std::string& name,
  1370. const std::string& arg = ""
  1371. );
  1372. void
  1373. parse_default(const std::shared_ptr<OptionDetails>& details);
  1374. void
  1375. parse_no_value(const std::shared_ptr<OptionDetails>& details);
  1376. private:
  1377. void finalise_aliases();
  1378. const OptionMap& m_options;
  1379. const PositionalList& m_positional;
  1380. std::vector<KeyValue> m_sequential{};
  1381. bool m_allow_unrecognised;
  1382. ParsedHashMap m_parsed{};
  1383. NameHashMap m_keys{};
  1384. };
  1385. class Options
  1386. {
  1387. public:
  1388. explicit Options(std::string program, std::string help_string = "")
  1389. : m_program(std::move(program))
  1390. , m_help_string(toLocalString(std::move(help_string)))
  1391. , m_custom_help("[OPTION...]")
  1392. , m_positional_help("positional parameters")
  1393. , m_show_positional(false)
  1394. , m_allow_unrecognised(false)
  1395. , m_width(76)
  1396. , m_tab_expansion(false)
  1397. , m_options(std::make_shared<OptionMap>())
  1398. {
  1399. }
  1400. Options&
  1401. positional_help(std::string help_text)
  1402. {
  1403. m_positional_help = std::move(help_text);
  1404. return *this;
  1405. }
  1406. Options&
  1407. custom_help(std::string help_text)
  1408. {
  1409. m_custom_help = std::move(help_text);
  1410. return *this;
  1411. }
  1412. Options&
  1413. show_positional_help()
  1414. {
  1415. m_show_positional = true;
  1416. return *this;
  1417. }
  1418. Options&
  1419. allow_unrecognised_options()
  1420. {
  1421. m_allow_unrecognised = true;
  1422. return *this;
  1423. }
  1424. Options&
  1425. set_width(size_t width)
  1426. {
  1427. m_width = width;
  1428. return *this;
  1429. }
  1430. Options&
  1431. set_tab_expansion(bool expansion=true)
  1432. {
  1433. m_tab_expansion = expansion;
  1434. return *this;
  1435. }
  1436. ParseResult
  1437. parse(int argc, const char* const* argv);
  1438. OptionAdder
  1439. add_options(std::string group = "");
  1440. void
  1441. add_options
  1442. (
  1443. const std::string& group,
  1444. std::initializer_list<Option> options
  1445. );
  1446. void
  1447. add_option
  1448. (
  1449. const std::string& group,
  1450. const Option& option
  1451. );
  1452. void
  1453. add_option
  1454. (
  1455. const std::string& group,
  1456. const std::string& s,
  1457. const std::string& l,
  1458. std::string desc,
  1459. const std::shared_ptr<const Value>& value,
  1460. std::string arg_help
  1461. );
  1462. //parse positional arguments into the given option
  1463. void
  1464. parse_positional(std::string option);
  1465. void
  1466. parse_positional(std::vector<std::string> options);
  1467. void
  1468. parse_positional(std::initializer_list<std::string> options);
  1469. template <typename Iterator>
  1470. void
  1471. parse_positional(Iterator begin, Iterator end) {
  1472. parse_positional(std::vector<std::string>{begin, end});
  1473. }
  1474. std::string
  1475. help(const std::vector<std::string>& groups = {}) const;
  1476. std::vector<std::string>
  1477. groups() const;
  1478. const HelpGroupDetails&
  1479. group_help(const std::string& group) const;
  1480. private:
  1481. void
  1482. add_one_option
  1483. (
  1484. const std::string& option,
  1485. const std::shared_ptr<OptionDetails>& details
  1486. );
  1487. String
  1488. help_one_group(const std::string& group) const;
  1489. void
  1490. generate_group_help
  1491. (
  1492. String& result,
  1493. const std::vector<std::string>& groups
  1494. ) const;
  1495. void
  1496. generate_all_groups_help(String& result) const;
  1497. std::string m_program{};
  1498. String m_help_string{};
  1499. std::string m_custom_help{};
  1500. std::string m_positional_help{};
  1501. bool m_show_positional;
  1502. bool m_allow_unrecognised;
  1503. size_t m_width;
  1504. bool m_tab_expansion;
  1505. std::shared_ptr<OptionMap> m_options;
  1506. std::vector<std::string> m_positional{};
  1507. std::unordered_set<std::string> m_positional_set{};
  1508. //mapping from groups to help options
  1509. std::map<std::string, HelpGroupDetails> m_help{};
  1510. std::list<OptionDetails> m_option_list{};
  1511. std::unordered_map<std::string, decltype(m_option_list)::iterator> m_option_map{};
  1512. };
  1513. class OptionAdder
  1514. {
  1515. public:
  1516. OptionAdder(Options& options, std::string group)
  1517. : m_options(options), m_group(std::move(group))
  1518. {
  1519. }
  1520. OptionAdder&
  1521. operator()
  1522. (
  1523. const std::string& opts,
  1524. const std::string& desc,
  1525. const std::shared_ptr<const Value>& value
  1526. = ::cxxopts::value<bool>(),
  1527. std::string arg_help = ""
  1528. );
  1529. private:
  1530. Options& m_options;
  1531. std::string m_group;
  1532. };
  1533. namespace
  1534. {
  1535. constexpr size_t OPTION_LONGEST = 30;
  1536. constexpr size_t OPTION_DESC_GAP = 2;
  1537. String
  1538. format_option
  1539. (
  1540. const HelpOptionDetails& o
  1541. )
  1542. {
  1543. const auto& s = o.s;
  1544. const auto& l = o.l;
  1545. String result = " ";
  1546. if (!s.empty())
  1547. {
  1548. result += "-" + toLocalString(s);
  1549. if (!l.empty())
  1550. {
  1551. result += ",";
  1552. }
  1553. }
  1554. else
  1555. {
  1556. result += " ";
  1557. }
  1558. if (!l.empty())
  1559. {
  1560. result += " --" + toLocalString(l);
  1561. }
  1562. auto arg = !o.arg_help.empty() ? toLocalString(o.arg_help) : "arg";
  1563. if (!o.is_boolean)
  1564. {
  1565. if (o.has_implicit)
  1566. {
  1567. result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
  1568. }
  1569. else
  1570. {
  1571. result += " " + arg;
  1572. }
  1573. }
  1574. return result;
  1575. }
  1576. String
  1577. format_description
  1578. (
  1579. const HelpOptionDetails& o,
  1580. size_t start,
  1581. size_t allowed,
  1582. bool tab_expansion
  1583. )
  1584. {
  1585. auto desc = o.desc;
  1586. if (o.has_default && (!o.is_boolean || o.default_value != "false"))
  1587. {
  1588. if(!o.default_value.empty())
  1589. {
  1590. desc += toLocalString(" (default: " + o.default_value + ")");
  1591. }
  1592. else
  1593. {
  1594. desc += toLocalString(" (default: \"\")");
  1595. }
  1596. }
  1597. String result;
  1598. if (tab_expansion)
  1599. {
  1600. String desc2;
  1601. auto size = size_t{ 0 };
  1602. for (auto c = std::begin(desc); c != std::end(desc); ++c)
  1603. {
  1604. if (*c == '\n')
  1605. {
  1606. desc2 += *c;
  1607. size = 0;
  1608. }
  1609. else if (*c == '\t')
  1610. {
  1611. auto skip = 8 - size % 8;
  1612. stringAppend(desc2, skip, ' ');
  1613. size += skip;
  1614. }
  1615. else
  1616. {
  1617. desc2 += *c;
  1618. ++size;
  1619. }
  1620. }
  1621. desc = desc2;
  1622. }
  1623. desc += " ";
  1624. auto current = std::begin(desc);
  1625. auto previous = current;
  1626. auto startLine = current;
  1627. auto lastSpace = current;
  1628. auto size = size_t{};
  1629. bool appendNewLine;
  1630. bool onlyWhiteSpace = true;
  1631. while (current != std::end(desc))
  1632. {
  1633. appendNewLine = false;
  1634. if (std::isblank(*previous))
  1635. {
  1636. lastSpace = current;
  1637. }
  1638. if (!std::isblank(*current))
  1639. {
  1640. onlyWhiteSpace = false;
  1641. }
  1642. while (*current == '\n')
  1643. {
  1644. previous = current;
  1645. ++current;
  1646. appendNewLine = true;
  1647. }
  1648. if (!appendNewLine && size >= allowed)
  1649. {
  1650. if (lastSpace != startLine)
  1651. {
  1652. current = lastSpace;
  1653. previous = current;
  1654. }
  1655. appendNewLine = true;
  1656. }
  1657. if (appendNewLine)
  1658. {
  1659. stringAppend(result, startLine, current);
  1660. startLine = current;
  1661. lastSpace = current;
  1662. if (*previous != '\n')
  1663. {
  1664. stringAppend(result, "\n");
  1665. }
  1666. stringAppend(result, start, ' ');
  1667. if (*previous != '\n')
  1668. {
  1669. stringAppend(result, lastSpace, current);
  1670. }
  1671. onlyWhiteSpace = true;
  1672. size = 0;
  1673. }
  1674. previous = current;
  1675. ++current;
  1676. ++size;
  1677. }
  1678. //append whatever is left but ignore whitespace
  1679. if (!onlyWhiteSpace)
  1680. {
  1681. stringAppend(result, startLine, previous);
  1682. }
  1683. return result;
  1684. }
  1685. } // namespace
  1686. inline
  1687. void
  1688. Options::add_options
  1689. (
  1690. const std::string &group,
  1691. std::initializer_list<Option> options
  1692. )
  1693. {
  1694. OptionAdder option_adder(*this, group);
  1695. for (const auto &option: options)
  1696. {
  1697. option_adder(option.opts_, option.desc_, option.value_, option.arg_help_);
  1698. }
  1699. }
  1700. inline
  1701. OptionAdder
  1702. Options::add_options(std::string group)
  1703. {
  1704. return OptionAdder(*this, std::move(group));
  1705. }
  1706. inline
  1707. OptionAdder&
  1708. OptionAdder::operator()
  1709. (
  1710. const std::string& opts,
  1711. const std::string& desc,
  1712. const std::shared_ptr<const Value>& value,
  1713. std::string arg_help
  1714. )
  1715. {
  1716. std::string short_sw, long_sw;
  1717. std::tie(short_sw, long_sw) = values::parser_tool::SplitSwitchDef(opts);
  1718. if (!short_sw.length() && !long_sw.length())
  1719. {
  1720. throw_or_mimic<invalid_option_format_error>(opts);
  1721. }
  1722. else if (long_sw.length() == 1 && short_sw.length())
  1723. {
  1724. throw_or_mimic<invalid_option_format_error>(opts);
  1725. }
  1726. auto option_names = []
  1727. (
  1728. const std::string &short_,
  1729. const std::string &long_
  1730. )
  1731. {
  1732. if (long_.length() == 1)
  1733. {
  1734. return std::make_tuple(long_, short_);
  1735. }
  1736. return std::make_tuple(short_, long_);
  1737. }(short_sw, long_sw);
  1738. m_options.add_option
  1739. (
  1740. m_group,
  1741. std::get<0>(option_names),
  1742. std::get<1>(option_names),
  1743. desc,
  1744. value,
  1745. std::move(arg_help)
  1746. );
  1747. return *this;
  1748. }
  1749. inline
  1750. void
  1751. OptionParser::parse_default(const std::shared_ptr<OptionDetails>& details)
  1752. {
  1753. // TODO: remove the duplicate code here
  1754. auto& store = m_parsed[details->hash()];
  1755. store.parse_default(details);
  1756. }
  1757. inline
  1758. void
  1759. OptionParser::parse_no_value(const std::shared_ptr<OptionDetails>& details)
  1760. {
  1761. auto& store = m_parsed[details->hash()];
  1762. store.parse_no_value(details);
  1763. }
  1764. inline
  1765. void
  1766. OptionParser::parse_option
  1767. (
  1768. const std::shared_ptr<OptionDetails>& value,
  1769. const std::string& /*name*/,
  1770. const std::string& arg
  1771. )
  1772. {
  1773. auto hash = value->hash();
  1774. auto& result = m_parsed[hash];
  1775. result.parse(value, arg);
  1776. m_sequential.emplace_back(value->long_name(), arg);
  1777. }
  1778. inline
  1779. void
  1780. OptionParser::checked_parse_arg
  1781. (
  1782. int argc,
  1783. const char* const* argv,
  1784. int& current,
  1785. const std::shared_ptr<OptionDetails>& value,
  1786. const std::string& name
  1787. )
  1788. {
  1789. if (current + 1 >= argc)
  1790. {
  1791. if (value->value().has_implicit())
  1792. {
  1793. parse_option(value, name, value->value().get_implicit_value());
  1794. }
  1795. else
  1796. {
  1797. throw_or_mimic<missing_argument_exception>(name);
  1798. }
  1799. }
  1800. else
  1801. {
  1802. if (value->value().has_implicit())
  1803. {
  1804. parse_option(value, name, value->value().get_implicit_value());
  1805. }
  1806. else
  1807. {
  1808. parse_option(value, name, argv[current + 1]);
  1809. ++current;
  1810. }
  1811. }
  1812. }
  1813. inline
  1814. void
  1815. OptionParser::add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg)
  1816. {
  1817. parse_option(iter->second, option, arg);
  1818. }
  1819. inline
  1820. bool
  1821. OptionParser::consume_positional(const std::string& a, PositionalListIterator& next)
  1822. {
  1823. while (next != m_positional.end())
  1824. {
  1825. auto iter = m_options.find(*next);
  1826. if (iter != m_options.end())
  1827. {
  1828. if (!iter->second->value().is_container())
  1829. {
  1830. auto& result = m_parsed[iter->second->hash()];
  1831. if (result.count() == 0)
  1832. {
  1833. add_to_option(iter, *next, a);
  1834. ++next;
  1835. return true;
  1836. }
  1837. ++next;
  1838. continue;
  1839. }
  1840. add_to_option(iter, *next, a);
  1841. return true;
  1842. }
  1843. throw_or_mimic<option_not_exists_exception>(*next);
  1844. }
  1845. return false;
  1846. }
  1847. inline
  1848. void
  1849. Options::parse_positional(std::string option)
  1850. {
  1851. parse_positional(std::vector<std::string>{std::move(option)});
  1852. }
  1853. inline
  1854. void
  1855. Options::parse_positional(std::vector<std::string> options)
  1856. {
  1857. m_positional = std::move(options);
  1858. m_positional_set.insert(m_positional.begin(), m_positional.end());
  1859. }
  1860. inline
  1861. void
  1862. Options::parse_positional(std::initializer_list<std::string> options)
  1863. {
  1864. parse_positional(std::vector<std::string>(options));
  1865. }
  1866. inline
  1867. ParseResult
  1868. Options::parse(int argc, const char* const* argv)
  1869. {
  1870. OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
  1871. return parser.parse(argc, argv);
  1872. }
  1873. inline ParseResult
  1874. OptionParser::parse(int argc, const char* const* argv)
  1875. {
  1876. int current = 1;
  1877. bool consume_remaining = false;
  1878. auto next_positional = m_positional.begin();
  1879. std::vector<std::string> unmatched;
  1880. while (current != argc)
  1881. {
  1882. if (strcmp(argv[current], "--") == 0)
  1883. {
  1884. consume_remaining = true;
  1885. ++current;
  1886. break;
  1887. }
  1888. bool matched = false;
  1889. values::parser_tool::ArguDesc argu_desc =
  1890. values::parser_tool::ParseArgument(argv[current], matched);
  1891. if (!matched)
  1892. {
  1893. //not a flag
  1894. // but if it starts with a `-`, then it's an error
  1895. if (argv[current][0] == '-' && argv[current][1] != '\0') {
  1896. if (!m_allow_unrecognised) {
  1897. throw_or_mimic<option_syntax_exception>(argv[current]);
  1898. }
  1899. }
  1900. //if true is returned here then it was consumed, otherwise it is
  1901. //ignored
  1902. if (consume_positional(argv[current], next_positional))
  1903. {
  1904. }
  1905. else
  1906. {
  1907. unmatched.emplace_back(argv[current]);
  1908. }
  1909. //if we return from here then it was parsed successfully, so continue
  1910. }
  1911. else
  1912. {
  1913. //short or long option?
  1914. if (argu_desc.grouping)
  1915. {
  1916. const std::string& s = argu_desc.arg_name;
  1917. for (std::size_t i = 0; i != s.size(); ++i)
  1918. {
  1919. std::string name(1, s[i]);
  1920. auto iter = m_options.find(name);
  1921. if (iter == m_options.end())
  1922. {
  1923. if (m_allow_unrecognised)
  1924. {
  1925. unmatched.push_back(std::string("-") + s[i]);
  1926. continue;
  1927. }
  1928. //error
  1929. throw_or_mimic<option_not_exists_exception>(name);
  1930. }
  1931. auto value = iter->second;
  1932. if (i + 1 == s.size())
  1933. {
  1934. //it must be the last argument
  1935. checked_parse_arg(argc, argv, current, value, name);
  1936. }
  1937. else if (value->value().has_implicit())
  1938. {
  1939. parse_option(value, name, value->value().get_implicit_value());
  1940. }
  1941. else if (i + 1 < s.size())
  1942. {
  1943. std::string arg_value = s.substr(i + 1);
  1944. parse_option(value, name, arg_value);
  1945. break;
  1946. }
  1947. else
  1948. {
  1949. //error
  1950. throw_or_mimic<option_requires_argument_exception>(name);
  1951. }
  1952. }
  1953. }
  1954. else if (argu_desc.arg_name.length() != 0)
  1955. {
  1956. const std::string& name = argu_desc.arg_name;
  1957. auto iter = m_options.find(name);
  1958. if (iter == m_options.end())
  1959. {
  1960. if (m_allow_unrecognised)
  1961. {
  1962. // keep unrecognised options in argument list, skip to next argument
  1963. unmatched.emplace_back(argv[current]);
  1964. ++current;
  1965. continue;
  1966. }
  1967. //error
  1968. throw_or_mimic<option_not_exists_exception>(name);
  1969. }
  1970. auto opt = iter->second;
  1971. //equals provided for long option?
  1972. if (argu_desc.set_value)
  1973. {
  1974. //parse the option given
  1975. parse_option(opt, name, argu_desc.value);
  1976. }
  1977. else
  1978. {
  1979. //parse the next argument
  1980. checked_parse_arg(argc, argv, current, opt, name);
  1981. }
  1982. }
  1983. }
  1984. ++current;
  1985. }
  1986. for (auto& opt : m_options)
  1987. {
  1988. auto& detail = opt.second;
  1989. const auto& value = detail->value();
  1990. auto& store = m_parsed[detail->hash()];
  1991. if (value.has_default()) {
  1992. if (!store.count() && !store.has_default()) {
  1993. parse_default(detail);
  1994. }
  1995. }
  1996. else {
  1997. parse_no_value(detail);
  1998. }
  1999. }
  2000. if (consume_remaining)
  2001. {
  2002. while (current < argc)
  2003. {
  2004. if (!consume_positional(argv[current], next_positional)) {
  2005. break;
  2006. }
  2007. ++current;
  2008. }
  2009. //adjust argv for any that couldn't be swallowed
  2010. while (current != argc) {
  2011. unmatched.emplace_back(argv[current]);
  2012. ++current;
  2013. }
  2014. }
  2015. finalise_aliases();
  2016. ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(unmatched));
  2017. return parsed;
  2018. }
  2019. inline
  2020. void
  2021. OptionParser::finalise_aliases()
  2022. {
  2023. for (auto& option: m_options)
  2024. {
  2025. auto& detail = *option.second;
  2026. auto hash = detail.hash();
  2027. m_keys[detail.short_name()] = hash;
  2028. m_keys[detail.long_name()] = hash;
  2029. m_parsed.emplace(hash, OptionValue());
  2030. }
  2031. }
  2032. inline
  2033. void
  2034. Options::add_option
  2035. (
  2036. const std::string& group,
  2037. const Option& option
  2038. )
  2039. {
  2040. add_options(group, {option});
  2041. }
  2042. inline
  2043. void
  2044. Options::add_option
  2045. (
  2046. const std::string& group,
  2047. const std::string& s,
  2048. const std::string& l,
  2049. std::string desc,
  2050. const std::shared_ptr<const Value>& value,
  2051. std::string arg_help
  2052. )
  2053. {
  2054. auto stringDesc = toLocalString(std::move(desc));
  2055. auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
  2056. if (!s.empty())
  2057. {
  2058. add_one_option(s, option);
  2059. }
  2060. if (!l.empty())
  2061. {
  2062. add_one_option(l, option);
  2063. }
  2064. m_option_list.push_front(*option.get());
  2065. auto iter = m_option_list.begin();
  2066. m_option_map[s] = iter;
  2067. m_option_map[l] = iter;
  2068. //add the help details
  2069. auto& options = m_help[group];
  2070. options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
  2071. value->has_default(), value->get_default_value(),
  2072. value->has_implicit(), value->get_implicit_value(),
  2073. std::move(arg_help),
  2074. value->is_container(),
  2075. value->is_boolean()});
  2076. }
  2077. inline
  2078. void
  2079. Options::add_one_option
  2080. (
  2081. const std::string& option,
  2082. const std::shared_ptr<OptionDetails>& details
  2083. )
  2084. {
  2085. auto in = m_options->emplace(option, details);
  2086. if (!in.second)
  2087. {
  2088. throw_or_mimic<option_exists_error>(option);
  2089. }
  2090. }
  2091. inline
  2092. String
  2093. Options::help_one_group(const std::string& g) const
  2094. {
  2095. using OptionHelp = std::vector<std::pair<String, String>>;
  2096. auto group = m_help.find(g);
  2097. if (group == m_help.end())
  2098. {
  2099. return "";
  2100. }
  2101. OptionHelp format;
  2102. size_t longest = 0;
  2103. String result;
  2104. if (!g.empty())
  2105. {
  2106. result += toLocalString(" " + g + " options:\n");
  2107. }
  2108. for (const auto& o : group->second.options)
  2109. {
  2110. if (m_positional_set.find(o.l) != m_positional_set.end() &&
  2111. !m_show_positional)
  2112. {
  2113. continue;
  2114. }
  2115. auto s = format_option(o);
  2116. longest = (std::max)(longest, stringLength(s));
  2117. format.push_back(std::make_pair(s, String()));
  2118. }
  2119. longest = (std::min)(longest, OPTION_LONGEST);
  2120. //widest allowed description -- min 10 chars for helptext/line
  2121. size_t allowed = 10;
  2122. if (m_width > allowed + longest + OPTION_DESC_GAP)
  2123. {
  2124. allowed = m_width - longest - OPTION_DESC_GAP;
  2125. }
  2126. auto fiter = format.begin();
  2127. for (const auto& o : group->second.options)
  2128. {
  2129. if (m_positional_set.find(o.l) != m_positional_set.end() &&
  2130. !m_show_positional)
  2131. {
  2132. continue;
  2133. }
  2134. auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion);
  2135. result += fiter->first;
  2136. if (stringLength(fiter->first) > longest)
  2137. {
  2138. result += '\n';
  2139. result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
  2140. }
  2141. else
  2142. {
  2143. result += toLocalString(std::string(longest + OPTION_DESC_GAP -
  2144. stringLength(fiter->first),
  2145. ' '));
  2146. }
  2147. result += d;
  2148. result += '\n';
  2149. ++fiter;
  2150. }
  2151. return result;
  2152. }
  2153. inline
  2154. void
  2155. Options::generate_group_help
  2156. (
  2157. String& result,
  2158. const std::vector<std::string>& print_groups
  2159. ) const
  2160. {
  2161. for (size_t i = 0; i != print_groups.size(); ++i)
  2162. {
  2163. const String& group_help_text = help_one_group(print_groups[i]);
  2164. if (empty(group_help_text))
  2165. {
  2166. continue;
  2167. }
  2168. result += group_help_text;
  2169. if (i < print_groups.size() - 1)
  2170. {
  2171. result += '\n';
  2172. }
  2173. }
  2174. }
  2175. inline
  2176. void
  2177. Options::generate_all_groups_help(String& result) const
  2178. {
  2179. std::vector<std::string> all_groups;
  2180. std::transform(
  2181. m_help.begin(),
  2182. m_help.end(),
  2183. std::back_inserter(all_groups),
  2184. [] (const std::map<std::string, HelpGroupDetails>::value_type& group)
  2185. {
  2186. return group.first;
  2187. }
  2188. );
  2189. generate_group_help(result, all_groups);
  2190. }
  2191. inline
  2192. std::string
  2193. Options::help(const std::vector<std::string>& help_groups) const
  2194. {
  2195. String result = m_help_string + "\nUsage:\n " +
  2196. toLocalString(m_program) + " " + toLocalString(m_custom_help);
  2197. if (!m_positional.empty() && !m_positional_help.empty()) {
  2198. result += " " + toLocalString(m_positional_help);
  2199. }
  2200. result += "\n\n";
  2201. if (help_groups.empty())
  2202. {
  2203. generate_all_groups_help(result);
  2204. }
  2205. else
  2206. {
  2207. generate_group_help(result, help_groups);
  2208. }
  2209. return toUTF8String(result);
  2210. }
  2211. inline
  2212. std::vector<std::string>
  2213. Options::groups() const
  2214. {
  2215. std::vector<std::string> g;
  2216. std::transform(
  2217. m_help.begin(),
  2218. m_help.end(),
  2219. std::back_inserter(g),
  2220. [] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
  2221. {
  2222. return pair.first;
  2223. }
  2224. );
  2225. return g;
  2226. }
  2227. inline
  2228. const HelpGroupDetails&
  2229. Options::group_help(const std::string& group) const
  2230. {
  2231. return m_help.at(group);
  2232. }
  2233. } // namespace cxxopts
  2234. #endif //CXXOPTS_HPP_INCLUDED