irccd  3.0.3
options.hpp
1 /*
2  * options.hpp -- C++ similar interface to getopt(3)
3  *
4  * Copyright (c) 2019 David Demelier <markand@malikania.fr>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #ifndef IRCCD_OPTIONS_HPP
20 #define IRCCD_OPTIONS_HPP
21 
27 #include <initializer_list>
28 #include <stdexcept>
29 #include <string>
30 #include <string_view>
31 #include <tuple>
32 #include <unordered_map>
33 #include <vector>
34 
38 namespace irccd::options {
39 
43 using pack = std::tuple<
44  std::vector<std::string>,
45  std::unordered_multimap<char, std::string>
46 >;
47 
82 template <typename InputIt>
83 inline auto parse(InputIt&& it, InputIt&& end, std::string_view fmt) -> pack
84 {
85  pack result;
86 
87  for (; it != end; ++it) {
88  const std::string_view token(*it);
89 
90  /*
91  * Special token that stops parsing options, all next tokens
92  * will be considered as positional arguments.
93  */
94  if (token == "--") {
95  for (++it; it != end; ++it)
96  std::get<0>(result).push_back(std::string(*it));
97  break;
98  }
99 
100  // Is this a positional argument?
101  if (token.compare(0U, 1U, "-") != 0) {
102  // Stop parsing in case of '!' in format string.
103  if (fmt.find('!') != std::string_view::npos)
104  break;
105 
106  std::get<0>(result).push_back(std::string(token));
107  continue;
108  }
109 
110  const auto sub = token.substr(1);
111 
112  for (std::size_t i = 0U; i < sub.size(); ++i) {
113  const auto idx = fmt.find(sub[i]);
114 
115  if (idx == std::string_view::npos)
116  throw std::runtime_error("invalid option");
117 
118  // This is a boolean value.
119  if (fmt.compare(idx + 1U, 1U, ":") != 0) {
120  std::get<1>(result).emplace(sub[i], "");
121  continue;
122  }
123 
124  /*
125  * The value is adjacent to the option (e.g.
126  * -csuper.conf).
127  */
128  if (i + 1U < sub.size()) {
129  std::get<1>(result).emplace(sub[i], std::string(sub.substr(i + 1)));
130  break;
131  }
132 
133  // Option is the next token (e.g. -c super.conf).
134  if (++it == end || std::string_view(*it).compare(0U, 1U, "-") == 0)
135  throw std::runtime_error("option require a value");
136 
137  std::get<1>(result).emplace(sub[i], std::string(*it));
138  }
139  }
140 
141  return result;
142 }
143 
152 template <typename String>
153 inline auto parse(std::initializer_list<String> args, std::string_view fmt) -> pack
154 {
155  auto begin = args.begin();
156  auto end = args.end();
157 
158  return parse(begin, end, fmt);
159 }
160 
169 inline auto parse(int argc, char** argv, std::string_view fmt) -> pack
170 {
171  std::vector<std::string_view> args(argc);
172 
173  for (int i = 0; i < argc; ++i)
174  args[i] = argv[i];
175 
176  auto begin = args.begin();
177  auto end = args.end();
178 
179  return parse(begin, end, fmt);
180 }
181 
182 
183 } // !irccd::options
184 
185 #endif // !IRCCD_OPTIONS_HPP
irccd::options
C++ similar interface to getopt(3).
Definition: options.hpp:38
irccd::options::pack
std::tuple< std::vector< std::string >, std::unordered_multimap< char, std::string > > pack
Definition: options.hpp:46
irccd::options::parse
auto parse(InputIt &&it, InputIt &&end, std::string_view fmt) -> pack
Definition: options.hpp:83