1 ////////////////////////////////////////////////////////////////////////////////
3 // Copyright 2012 - 2017, Paul Beckingham, Federico Hernandez.
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 // http://www.opensource.org/licenses/mit-license.php
25 ////////////////////////////////////////////////////////////////////////////////
33 ////////////////////////////////////////////////////////////////////////////////
34 void Args::addOption (const std::string& name, bool defaultValue)
36 _options[name] = defaultValue;
37 _optionCount[name] = 0;
40 ////////////////////////////////////////////////////////////////////////////////
41 void Args::addNamed (const std::string& name, const std::string& defaultValue)
43 _named[name] = defaultValue;
46 ////////////////////////////////////////////////////////////////////////////////
47 void Args::limitPositionals (int limit)
52 ////////////////////////////////////////////////////////////////////////////////
53 void Args::enableNegatives ()
58 ////////////////////////////////////////////////////////////////////////////////
59 void Args::scan (int argc, const char** argv)
61 for (int i = 1; i < argc; ++i)
63 // Is an option or named arg.
64 if (argv[i][0] == '-' && strlen (argv[i]) > 1)
66 auto name = ltrim (argv[i], "-");
68 std::string canonical;
69 if (canonicalizeOption (name, canonical))
71 bool negated = _negatives && name.find ("no") == 0;
72 _options[canonical] = ! negated;
73 _optionCount[canonical]++;
76 else if (canonicalizeNamed (name, canonical))
79 throw std::string ("Argument '" + canonical + "' has no value.");
82 _named[canonical] = argv[i];
86 throw std::string ("Unrecognized argument '" + name + "'.");
92 _positionals.push_back (argv[i]);
94 static_cast <int> (_positionals.size ()) > _limit)
95 throw std::string ("Too many positional arguments.");
100 ////////////////////////////////////////////////////////////////////////////////
101 bool Args::getOption (const std::string& name) const
103 if (_options.find (name) == _options.end ())
106 return _options.at (name);
109 ////////////////////////////////////////////////////////////////////////////////
110 int Args::getOptionCount (const std::string& name) const
112 if (_optionCount.find (name) == _optionCount.end ())
115 return _optionCount.at (name);
118 ////////////////////////////////////////////////////////////////////////////////
119 std::string Args::getNamed (const std::string& name) const
121 if (_named.find (name) == _named.end ())
124 return _named.at (name);
127 ////////////////////////////////////////////////////////////////////////////////
128 int Args::getPositionalCount () const
130 return static_cast <int> (_positionals.size ());
133 ////////////////////////////////////////////////////////////////////////////////
134 std::string Args::getPositional (int n) const
136 return _positionals.at (n);
139 ////////////////////////////////////////////////////////////////////////////////
140 // Assuming "abc" is a declared option, support the following canonicalization:
142 // abc --> abc (exact match always canonicalizes)
143 // ab --> abc (if unique)
144 // a --> abc (if unique)
145 // noabc --> abc (exact negation match always canonicalizes)
146 // noab --> abc (if unique)
147 // noa --> abc (if unique)
149 bool Args::canonicalizeOption (const std::string& partial, std::string& canonical) const
151 bool negated = _negatives && partial.find ("no") == 0;
153 // Look for exact positive or negative matches first, which should succeed
154 // regardless of any longer partial matches.
155 if (_options.find (partial) != _options.end ())
162 _options.find (partial.substr (2)) != _options.end ())
164 canonical = partial.substr (2);
168 // Iterate over all options, and look for partial matches. If there is only
169 // one, we have canonicalization.
170 std::vector <std::string> candidates;
171 for (const auto& option : _options)
173 if (option.first.find (partial) == 0 ||
174 (negated && option.first.find (partial, 2) == 2))
176 candidates.push_back (option.first);
180 if (candidates.size () == 1)
182 canonical = candidates[0];
189 ////////////////////////////////////////////////////////////////////////////////
190 // Assuming "abc" is a declared name, support the following canonicalization:
192 // abc --> abc (exact match always canonicalizes)
193 // ab --> abc (if unique)
194 // a --> abc (if unique)
196 bool Args::canonicalizeNamed (const std::string& partial, std::string& canonical) const
198 // Look for exact positive or negative matches first, which should succeed
199 // regardless of longer partial matches.
200 if (_named.find (partial) != _named.end ())
206 // Iterate over all options, and look for partial matches. If there is only
207 // one, we have canonicalization.
208 std::vector <std::string> candidates;
209 for (const auto& name : _named)
210 if (name.first.find (partial) == 0)
211 candidates.push_back (name.first);
213 if (candidates.size () == 1)
215 canonical = candidates[0];
222 ////////////////////////////////////////////////////////////////////////////////
223 std::string Args::dump () const
225 std::stringstream out;
228 for (const auto& arg : _options)
229 out << " " << arg.first << " = " << arg.second << " (" << _optionCount.at (arg.first) << ")\n";
232 for (const auto& arg : _named)
233 out << " " << arg.first << " = " << arg.second << '\n';
235 out << " Positionals\n"
236 << " limit = " << _limit << '\n';
237 for (const auto& arg : _positionals)
238 out << " " << arg << '\n';
240 out << " Negatives\n"
241 << " enabled = " << _negatives << '\n';
246 ////////////////////////////////////////////////////////////////////////////////