]> git.armaanb.net Git - gen-shell.git/blob - src/libshared/src/Args.cpp
added install instructions
[gen-shell.git] / src / libshared / src / Args.cpp
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2012 - 2017, Paul Beckingham, Federico Hernandez.
4 //
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:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
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
21 // SOFTWARE.
22 //
23 // http://www.opensource.org/licenses/mit-license.php
24 //
25 ////////////////////////////////////////////////////////////////////////////////
26
27 #include <cmake.h>
28 #include <Args.h>
29 #include <shared.h>
30 #include <sstream>
31 #include <string.h>
32
33 ////////////////////////////////////////////////////////////////////////////////
34 void Args::addOption (const std::string& name, bool defaultValue)
35 {
36   _options[name] = defaultValue;
37   _optionCount[name] = 0;
38 }
39
40 ////////////////////////////////////////////////////////////////////////////////
41 void Args::addNamed (const std::string& name, const std::string& defaultValue)
42 {
43   _named[name] = defaultValue;
44 }
45
46 ////////////////////////////////////////////////////////////////////////////////
47 void Args::limitPositionals (int limit)
48 {
49   _limit = limit;
50 }
51
52 ////////////////////////////////////////////////////////////////////////////////
53 void Args::enableNegatives ()
54 {
55   _negatives = true;
56 }
57
58 ////////////////////////////////////////////////////////////////////////////////
59 void Args::scan (int argc, const char** argv)
60 {
61   for (int i = 1; i < argc; ++i)
62   {
63     // Is an option or named arg.
64     if (argv[i][0] == '-' && strlen (argv[i]) > 1)
65     {
66       auto name = ltrim (argv[i], "-");
67
68       std::string canonical;
69       if (canonicalizeOption (name, canonical))
70       {
71         bool negated = _negatives && name.find ("no") == 0;
72         _options[canonical] = ! negated;
73         _optionCount[canonical]++;
74       }
75
76       else if (canonicalizeNamed (name, canonical))
77       {
78         if (i >= argc)
79           throw std::string ("Argument '" + canonical + "' has no value.");
80
81         ++i;
82         _named[canonical] = argv[i];
83       }
84
85       else
86         throw std::string ("Unrecognized argument '" + name + "'.");
87     }
88
89     // Or a positional.
90     else
91     {
92       _positionals.push_back (argv[i]);
93       if (_limit != -1 &&
94           static_cast <int> (_positionals.size ()) > _limit)
95         throw std::string ("Too many positional arguments.");
96     }
97   }
98 }
99
100 ////////////////////////////////////////////////////////////////////////////////
101 bool Args::getOption (const std::string& name) const
102 {
103   if (_options.find (name) == _options.end ())
104     return false;
105
106   return _options.at (name);
107 }
108
109 ////////////////////////////////////////////////////////////////////////////////
110 int Args::getOptionCount (const std::string& name) const
111 {
112   if (_optionCount.find (name) == _optionCount.end ())
113     return false;
114
115   return _optionCount.at (name);
116 }
117
118 ////////////////////////////////////////////////////////////////////////////////
119 std::string Args::getNamed (const std::string& name) const
120 {
121   if (_named.find (name) == _named.end ())
122     return "";
123
124   return _named.at (name);
125 }
126
127 ////////////////////////////////////////////////////////////////////////////////
128 int Args::getPositionalCount () const
129 {
130   return static_cast <int> (_positionals.size ());
131 }
132
133 ////////////////////////////////////////////////////////////////////////////////
134 std::string Args::getPositional (int n) const
135 {
136   return _positionals.at (n);
137 }
138
139 ////////////////////////////////////////////////////////////////////////////////
140 // Assuming "abc" is a declared option, support the following canonicalization:
141 //
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)
148 //
149 bool Args::canonicalizeOption (const std::string& partial, std::string& canonical) const
150 {
151   bool negated = _negatives && partial.find ("no") == 0;
152
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 ())
156   {
157     canonical = partial;
158     return true;
159   }
160
161   if (negated &&
162       _options.find (partial.substr (2)) != _options.end ())
163   {
164     canonical = partial.substr (2);
165     return true;
166   }
167
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)
172   {
173     if (option.first.find (partial) == 0 ||
174         (negated && option.first.find (partial, 2) == 2))
175     {
176       candidates.push_back (option.first);
177     }
178   }
179
180   if (candidates.size () == 1)
181   {
182     canonical = candidates[0];
183     return true;
184   }
185
186   return false;
187 }
188
189 ////////////////////////////////////////////////////////////////////////////////
190 // Assuming "abc" is a declared name, support the following canonicalization:
191 //
192 //   abc --> abc (exact match always canonicalizes)
193 //    ab --> abc (if unique)
194 //     a --> abc (if unique)
195 //
196 bool Args::canonicalizeNamed (const std::string& partial, std::string& canonical) const
197 {
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 ())
201   {
202     canonical = partial;
203     return true;
204   }
205
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);
212
213   if (candidates.size () == 1)
214   {
215     canonical = candidates[0];
216     return true;
217   }
218
219   return false;
220 }
221
222 ////////////////////////////////////////////////////////////////////////////////
223 std::string Args::dump () const
224 {
225   std::stringstream out;
226   out << "Args\n"
227       << "  Options\n";
228   for (const auto& arg : _options)
229     out << "    " << arg.first << " = " << arg.second << " (" << _optionCount.at (arg.first) << ")\n";
230
231   out << "  Named\n";
232   for (const auto& arg : _named)
233     out << "    " << arg.first << " = " << arg.second << '\n';
234
235   out << "  Positionals\n"
236       << "    limit = " << _limit << '\n';
237   for (const auto& arg : _positionals)
238     out << "    " << arg << '\n';
239
240   out << "  Negatives\n"
241       << "    enabled = " << _negatives << '\n';
242
243   return out.str ();
244 }
245
246 ////////////////////////////////////////////////////////////////////////////////