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