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