comparison C++/modules/OptionParser/OptionParser.cpp @ 334:0b576ee64d45

* Create brand new hierarchy * Rename DynLib to Dynlib * Remove some warnings
author David Demelier <markand@malikania.fr>
date Sun, 08 Mar 2015 14:26:33 +0100
parents C++/OptionParser.cpp@99e83685d4da
children 3a1380b4428c
comparison
equal deleted inserted replaced
333:412ca7a5e1ea 334:0b576ee64d45
1 /*
2 * OptionParser.cpp -- command line option parser
3 *
4 * Copyright (c) 2013, 2014 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 <algorithm>
20
21 #include "OptionParser.h"
22
23 bool OptionParser::isShort(const std::string &arg) const
24 {
25 return arg.size() >= 2 && arg[0] == '-' && arg[1] != '-';
26 }
27
28 bool OptionParser::isLong(const std::string &arg) const
29 {
30 return arg.size() >= 3 && arg[0] == '-' && arg[1] == '-' && arg[2] != '-';
31 }
32
33 bool OptionParser::isOption(const std::string &arg) const
34 {
35 return isShort(arg) || isLong(arg);
36 }
37
38 std::string OptionParser::key(const std::string &arg) const
39 {
40 if (isShort(arg))
41 return arg.substr(1, 1);
42
43 return arg.substr(2);
44 }
45
46 bool OptionParser::isShortCompacted(const std::string &arg) const
47 {
48 return arg.size() >= 3;
49 }
50
51 bool OptionParser::isDefined(const std::string &arg) const
52 {
53 auto n = key(arg);
54 auto it = std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) -> bool {
55 return o.key() == n || o.full() == n;
56 });
57
58 return it != m_options.end();
59 }
60
61 const Option &OptionParser::get(const std::string &arg) const
62 {
63 std::string n = key(arg);
64
65 return *std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) -> bool {
66 return o.key() == n || o.full() == n;
67 });
68 }
69
70 bool OptionParser::isToggle(const std::string &arg) const
71 {
72 return (get(arg).flags() & Option::NoArg);
73 }
74
75 void OptionParser::readShort(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const
76 {
77 /*
78 * There are many options when passing a short option:
79 *
80 * 1. -cmyconfig Takes on argument but parsed as unique,
81 * 2. -c myconfig Takes on argument but parsed as two strings
82 * 3. -abc If a is not a toggle option, its argument is `bc'
83 * 4. -abc If a is a toggle option and b, c are toggle, they are added
84 */
85
86 std::string v = it->substr(2);
87 std::string k = key(*it);
88 const Option &option = get(std::string("-") + k);
89
90 if (isToggle(*it)) {
91 // 3. and optionally 4.
92 pack.push_back(OptionValue(option, ""));
93 pack.m_argsParsed += 1;
94
95 if (isShortCompacted(*it)) {
96 for (char c : v) {
97 if (!isDefined("-" + std::string(1, c))) {
98 pack.m_error = "-" + std::string(1, c) + " is not a valid option";
99 break;
100 }
101
102 const Option &sub = get("-" + std::string(1, c));
103
104 pack.push_back(OptionValue(sub, ""));
105 }
106 }
107
108 ++ it;
109 } else {
110 // 1.
111 if (isShortCompacted(*it++)) {
112 pack.push_back(OptionValue(option, v));
113 pack.m_argsParsed += 1;
114 } else {
115 // 2.
116 if (it == end) {
117 pack.m_error = option.key() + " requires an option";
118 } else {
119 pack.push_back(OptionValue(option, *it++));
120 pack.m_argsParsed += 2;
121 }
122 }
123 }
124 }
125
126 void OptionParser::readFull(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const
127 {
128 /*
129 * Long options can't be compacted, there are only two possibilities:
130 *
131 * 1. --fullscreen No argument
132 * 2. --config foo One argument
133 */
134 const Option &option = get(*it);
135
136 if (!isToggle(*it)) {
137 // 2.
138 if (++it == end) {
139 pack.m_error = "--" + option.full() + " requires an option";
140 } else {
141 pack.push_back(OptionValue(option, *it++));
142 pack.m_argsParsed += 2;
143 }
144 } else {
145 pack.push_back(OptionValue(option, ""));
146 pack.m_argsParsed ++;
147
148 ++ it;
149 }
150 }
151
152 OptionParser::OptionParser(std::initializer_list<Option> options)
153 : m_options(options.begin(), options.end())
154 {
155 }
156
157 OptionParser::OptionParser(std::vector<Option> options)
158 : m_options(std::move(options))
159 {
160 }
161
162 OptionPack OptionParser::parse(Args::const_iterator it, Args::const_iterator end, int flags) const
163 {
164 OptionPack pack;
165
166 while (it != end) {
167 if (!isOption(*it)) {
168 if (flags & Unstrict) {
169 pack.m_argsParsed ++;
170 it ++;
171 continue;
172 } else {
173 pack.m_error = *it + " is not an option";
174 return pack;
175 }
176 }
177
178 if (!isDefined(*it)) {
179 pack.m_error = "Invalid option";
180 return pack;
181 }
182
183 if (isShort(*it)) {
184 readShort(pack, it, end);
185 } else {
186 readFull(pack, it, end);
187 }
188
189 // Read failure
190 if (pack.m_error != "No error") {
191 return pack;
192 }
193 }
194
195 return pack;
196 }
197
198 OptionPack OptionParser::parse(int argc, char **argv, int flags) const
199 {
200 std::vector<std::string> args;
201
202 for (int i = 0; i < argc; ++i)
203 args.push_back(argv[i]);
204
205 return parse(args, flags);
206 }
207
208 OptionPack OptionParser::parse(const std::vector<std::string> &args, int flags) const
209 {
210 return parse(args.begin(), args.end(), flags);
211 }