comparison modules/options/options.cpp @ 618:1ae8106369e5

Options: initial reimport, closes #705
author David Demelier <markand@malikania.fr>
date Tue, 26 Sep 2017 09:50:02 +0200
parents
children
comparison
equal deleted inserted replaced
617:266f32919d0a 618:1ae8106369e5
1 /*
2 * options.cpp -- parse Unix command line options
3 *
4 * Copyright (c) 2015-2017 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 #include <cassert>
20
21 #include "options.hpp"
22
23 namespace option {
24
25 namespace {
26
27 using iterator = std::vector<std::string>::iterator;
28 using args = std::vector<std::string>;
29
30 inline bool is_option(const std::string& arg) noexcept
31 {
32 return arg.size() >= 2 && arg[0] == '-';
33 }
34
35 inline bool is_long_option(const std::string& arg) noexcept
36 {
37 assert(is_option(arg));
38
39 return arg.size() >= 3 && arg[1] == '-';
40 }
41
42 inline bool is_short_simple(const std::string& arg) noexcept
43 {
44 assert(is_option(arg) && !is_long_option(arg));
45
46 return arg.size() == 2;
47 }
48
49 void parse_long_option(result& result, args& args, iterator& it, iterator& end, const options& definition)
50 {
51 auto arg = *it++;
52 auto opt = definition.find(arg);
53
54 if (opt == definition.end())
55 throw invalid_option(arg);
56
57 // Need argument?
58 if (opt->second) {
59 if (it == end || is_option(*it))
60 throw missing_value(arg);
61
62 result.insert(std::make_pair(arg, *it++));
63 it = args.erase(args.begin(), it);
64 end = args.end();
65 } else {
66 result.insert(std::make_pair(arg, ""));
67 it = args.erase(args.begin());
68 end = args.end();
69 }
70 }
71
72 void parse_short_option_simple(result& result, args& args, iterator& it, iterator &end, const options& definition)
73 {
74 /*
75 * Here two cases:
76 *
77 * -v (no option)
78 * -c value
79 */
80 auto arg = *it++;
81 auto opt = definition.find(arg);
82
83 if (opt == definition.end())
84 throw invalid_option(arg);
85
86 // Need argument?
87 if (opt->second) {
88 if (it == end || is_option(*it))
89 throw missing_value(arg);
90
91 result.insert(std::make_pair(arg, *it++));
92 it = args.erase(args.begin(), it);
93 end = args.end();
94 } else {
95 result.insert(std::make_pair(arg, ""));
96 it = args.erase(args.begin());
97 end = args.end();
98 }
99 }
100
101 void parse_short_option_compressed(result& result, args& args, iterator& it, iterator &end, const options& definition)
102 {
103 /*
104 * Here multiple scenarios:
105 *
106 * 1. -abc (-a -b -c if all are simple boolean arguments)
107 * 2. -vc foo.conf (-v -c foo.conf if -c is argument dependant)
108 * 3. -vcfoo.conf (-v -c foo.conf also)
109 */
110 auto value = it->substr(1);
111 auto len = value.length();
112 int toremove = 1;
113
114 for (std::size_t i = 0; i < len; ++i) {
115 auto arg = std::string{'-'} + value[i];
116 auto opt = definition.find(arg);
117
118 if (opt == definition.end())
119 throw invalid_option(arg);
120
121 if (opt->second) {
122 if (i == (len - 1)) {
123 // End of string, get the next argument (see 2.).
124 if (++it == end || is_option(*it))
125 throw missing_value(arg);
126
127 result.insert(std::make_pair(arg, *it));
128 toremove += 1;
129 } else {
130 result.insert(std::make_pair(arg, value.substr(i + 1)));
131 i = len;
132 }
133 } else
134 result.insert(std::make_pair(arg, ""));
135 }
136
137 it = args.erase(args.begin(), args.begin() + toremove);
138 end = args.end();
139 }
140
141 void parse_short_option(result& result, args& args, iterator& it, iterator &end, const options& definition)
142 {
143 if (is_short_simple(*it))
144 parse_short_option_simple(result, args, it, end, definition);
145 else
146 parse_short_option_compressed(result, args, it, end, definition);
147 }
148
149 } // !namespace
150
151 result read(std::vector<std::string>& args, const options& definition)
152 {
153 result result;
154
155 auto it = args.begin();
156 auto end = args.end();
157
158 while (it != end) {
159 if (!is_option(*it))
160 break;
161
162 if (is_long_option(*it))
163 parse_long_option(result, args, it, end, definition);
164 else
165 parse_short_option(result, args, it, end, definition);
166 }
167
168 return result;
169 }
170
171 result read(int& argc, char**& argv, const options& definition)
172 {
173 std::vector<std::string> args;
174
175 for (int i = 0; i < argc; ++i)
176 args.push_back(argv[i]);
177
178 auto before = args.size();
179 auto result = read(args, definition);
180
181 argc -= before - args.size();
182 argv += before - args.size();
183
184 return result;
185 }
186
187 } // !option