Line data Source code
1 : /** \file clOptions.cpp
2 : * \author Jared R. Males
3 : * \brief Implementatino of a command line parser
4 : *
5 : * \ingroup mxApp_files
6 : */
7 :
8 : //***********************************************************************//
9 : // Copyright 2021 Jared R. Males (jaredmales@gmail.com)
10 : //
11 : // This file is part of mxlib.
12 : //
13 : // mxlib is free software: you can redistribute it and/or modify
14 : // it under the terms of the GNU General Public License as published by
15 : // the Free Software Foundation, either version 3 of the License, or
16 : // (at your option) any later version.
17 : //
18 : // mxlib is distributed in the hope that it will be useful,
19 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 : // GNU General Public License for more details.
22 : //
23 : // You should have received a copy of the GNU General Public License
24 : // along with mxlib. If not, see <http://www.gnu.org/licenses/>.
25 : //***********************************************************************//
26 :
27 : #include "app/clOptions.hpp"
28 :
29 : namespace mx
30 : {
31 : namespace app
32 : {
33 :
34 : static const char *falseStr = "false";
35 : static const char *trueStr = "true";
36 : static const char *blankStr = "";
37 :
38 54 : static option::ArgStatus Arg_Required( const option::Option &option, bool msg )
39 : {
40 : static_cast<void>( msg );
41 :
42 54 : if( option.arg != 0 )
43 54 : return option::ARG_OK;
44 :
45 0 : return option::ARG_ILLEGAL;
46 : }
47 :
48 24 : clOptions::~clOptions()
49 : {
50 24 : if( options )
51 24 : delete[] options;
52 24 : if( buffer )
53 24 : delete[] buffer;
54 24 : }
55 :
56 0 : void clOptions::clear()
57 : {
58 0 : if( options )
59 0 : delete[] options;
60 0 : options = 0;
61 :
62 0 : if( buffer )
63 0 : delete[] buffer;
64 0 : buffer = 0;
65 :
66 0 : map.clear();
67 0 : typeMap.clear();
68 0 : descriptions.clear();
69 0 : }
70 :
71 98 : void clOptions::add( const std::string &optName, const char *const shortOpt, const char *const longOpt, int argT )
72 : {
73 98 : mapIterator it;
74 98 : it = map.find( optName );
75 :
76 98 : if( it == map.end() )
77 : {
78 98 : if( argT == argType::Optional )
79 : {
80 0 : descriptions.push_back( { nOpts, argT, shortOpt, longOpt, option::Arg::Optional, "" } );
81 : }
82 98 : else if( argT == argType::Required )
83 : {
84 78 : descriptions.push_back( { nOpts, argT, shortOpt, longOpt, Arg_Required, "" } );
85 : }
86 : else
87 : {
88 20 : descriptions.push_back( { nOpts, argT, shortOpt, longOpt, option::Arg::None, "" } );
89 : }
90 :
91 98 : map.insert( { optName, nOpts } );
92 98 : typeMap.insert( { optName, argT } );
93 :
94 98 : ++nOpts;
95 98 : return;
96 : }
97 : }
98 :
99 24 : void clOptions::parse( int argc, char **argv, std::vector<std::string> *nonOptions )
100 : {
101 24 : argc -= ( argc > 0 );
102 24 : argv += ( argc > 0 ); // skip program name argv[0] if present
103 :
104 : // If not already done, we push the unknown catcher and the termination descriptor.
105 24 : if( descriptions.size() == 0 || descriptions.back().shortopt != 0 )
106 : {
107 : // This is inserted to catch unknown options
108 24 : descriptions.push_back( { nOpts, 0, "", "", option::Arg::None, "" } );
109 24 : descriptions.push_back( { 0, 0, 0, 0, 0, 0 } );
110 : }
111 :
112 : // Now allocate.
113 24 : option::Stats stats( descriptions.data(), argc, argv );
114 170 : options = new option::Option[stats.options_max];
115 80 : buffer = new option::Option[stats.buffer_max];
116 :
117 24 : option::Parser parse( false, descriptions.data(), argc, argv, options, buffer );
118 :
119 24 : if( nonOptions )
120 : {
121 24 : nonOptions->resize( parse.nonOptionsCount() );
122 :
123 24 : for( int i = 0; i < parse.nonOptionsCount(); ++i )
124 : {
125 0 : ( *nonOptions )[i] = parse.nonOption( i );
126 : }
127 : }
128 24 : }
129 :
130 0 : const char *clOptions::operator[]( const std::string &key )
131 : {
132 0 : mapIterator it = map.find( key );
133 :
134 0 : if( it == map.end() )
135 : {
136 0 : std::cerr << "oh no\n";
137 0 : return 0;
138 : }
139 :
140 0 : int typeDesc = typeMap[key];
141 :
142 : // If this option is not pressent, either return the opposite T/F condition or blank
143 0 : if( options[it->second].type() == argType::None )
144 : {
145 0 : if( typeDesc == argType::False )
146 0 : return trueStr;
147 0 : if( typeDesc == argType::True )
148 0 : return falseStr;
149 0 : return blankStr;
150 : }
151 :
152 0 : if( typeDesc == argType::False || typeDesc == argType::True )
153 : {
154 0 : if( options[it->second].last()->type() == argType::False )
155 0 : return falseStr;
156 : else
157 0 : return trueStr;
158 : }
159 :
160 0 : if( options[it->second].arg == 0 )
161 0 : return blankStr;
162 :
163 0 : return options[it->second].last()->arg;
164 : }
165 :
166 19 : int clOptions::count( const std::string &key )
167 : {
168 19 : mapIterator it = map.find( key );
169 :
170 19 : if( it == map.end() )
171 0 : return -1;
172 :
173 19 : return options[it->second].count();
174 : }
175 :
176 19 : void clOptions::getAll( std::vector<std::string> &args, const std::string &key )
177 : {
178 19 : mapIterator it = map.find( key );
179 :
180 19 : if( it == map.end() )
181 : {
182 0 : std::cerr << "oh no\n";
183 3 : return;
184 : }
185 :
186 19 : int typeDesc = typeMap[key];
187 :
188 : // If this option is not present, either return the opposite T/F condition or blank
189 19 : if( options[it->second].type() == argType::None )
190 : {
191 :
192 0 : if( typeDesc == argType::False )
193 : {
194 0 : args.resize( 1 );
195 0 : args[0] = trueStr;
196 0 : return;
197 : }
198 :
199 0 : if( typeDesc == argType::True )
200 : {
201 0 : args.resize( 1 );
202 0 : args[0] = falseStr;
203 0 : return;
204 : }
205 :
206 0 : args.clear();
207 :
208 0 : return;
209 : }
210 :
211 19 : if( typeDesc == argType::False || typeDesc == argType::True )
212 : {
213 3 : args.resize( 1 );
214 :
215 3 : if( options[it->second].last()->type() == argType::False )
216 0 : args[0] = falseStr;
217 : else
218 3 : args[0] = trueStr;
219 3 : return;
220 : }
221 :
222 16 : if( options[it->second].arg == 0 )
223 : {
224 0 : args.clear();
225 0 : return;
226 : }
227 :
228 16 : int N = options[it->second].count();
229 :
230 16 : args.resize( N );
231 :
232 16 : int i = 0;
233 :
234 32 : for( option::Option *opt = options[it->second]; opt != NULL && i < N; opt = opt->next() )
235 : {
236 16 : args[i] = opt->arg;
237 16 : ++i;
238 : }
239 : }
240 :
241 71 : bool clOptions::optSet( const std::string &key )
242 : {
243 71 : mapIterator it = map.find( key );
244 :
245 71 : if( it == map.end() )
246 0 : return false; // Not found --> e.g. if neither command line short nor long option set.
247 :
248 71 : if( options[it->second].type() != argType::None )
249 19 : return true;
250 52 : return false;
251 : };
252 :
253 26 : int clOptions::numUnknown()
254 : {
255 : // The dummy description to catch unknown options is inserted at position nOpts.
256 26 : return options[nOpts].count();
257 : }
258 :
259 2 : int clOptions::unknown( std::vector<std::string> &unk )
260 : {
261 2 : unk.clear();
262 :
263 2 : if( numUnknown() == 0 )
264 0 : return 0;
265 :
266 4 : for( option::Option *opt = options[nOpts]; opt != NULL; opt = opt->next() )
267 : {
268 4 : unk.push_back( opt->name );
269 : }
270 :
271 2 : return 0;
272 : }
273 :
274 : } // namespace app
275 : } // namespace mx
|