mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
clOptions.cpp
Go to the documentation of this file.
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 
35 static const char * falseStr = "false";
36 static const char * trueStr = "true";
37 static const char * blankStr = "";
38 
39 static option::ArgStatus Arg_Required( const option::Option& option,
40  bool msg
41  )
42 {
43  static_cast<void>(msg);
44 
45  if (option.arg != 0)
46  return option::ARG_OK;
47 
48  return option::ARG_ILLEGAL;
49 }
50 
51 
53 {
54  if(options) delete[] options;
55  if(buffer) delete[] buffer;
56 }
57 
59 {
60  if(options) delete[] options;
61  options = 0;
62 
63  if(buffer) delete[] buffer;
64  buffer = 0;
65 
66  map.clear();
67  typeMap.clear();
68  descriptions.clear();
69 }
70 
71 void clOptions::add( const std::string & optName,
72  const char * const shortOpt,
73  const char * const longOpt,
74  int argT
75  )
76 {
77  mapIterator it;
78  it = map.find(optName);
79 
80 
81  if(it == map.end())
82  {
83  if(argT == argType::Optional)
84  {
85  descriptions.push_back({nOpts, argT, shortOpt, longOpt, option::Arg::Optional, ""});
86  }
87  else if(argT == argType::Required)
88  {
89  descriptions.push_back({nOpts, argT, shortOpt, longOpt, Arg_Required, ""});
90  }
91  else
92  {
93  descriptions.push_back({nOpts, argT, shortOpt, longOpt, option::Arg::None, ""});
94  }
95 
96  map.insert({optName, nOpts});
97  typeMap.insert({optName, argT});
98 
99  ++nOpts;
100  return;
101  }
102 }
103 
104 void clOptions::parse( int argc,
105  char **argv,
106  std::vector<std::string> * nonOptions
107  )
108 {
109  argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present
110 
111  //If not already done, we push the unknown catcher and the termination descriptor.
112  if(descriptions.back().index != 0)
113  {
114  descriptions.push_back({nOpts, 0, "", "", option::Arg::None, ""}); //This is inserted to catch unknown options
115  descriptions.push_back({0,0,0,0,0,0});
116  }
117 
118  //Now allocate.
119  option::Stats stats(descriptions.data(), argc, argv);
120  options = new option::Option[stats.options_max];
121  buffer = new option::Option[stats.buffer_max];
122 
123  option::Parser parse(false, descriptions.data(), argc, argv, options, buffer);
124 
125  if(nonOptions)
126  {
127  nonOptions->resize(parse.nonOptionsCount());
128 
129  for(int i=0;i<parse.nonOptionsCount(); ++i)
130  {
131  (*nonOptions)[i] = parse.nonOption(i);
132  }
133  }
134 }
135 
136 const char * clOptions::operator[]( const std::string & key )
137 {
138  mapIterator it = map.find(key);
139 
140  if(it == map.end())
141  {
142  std::cerr << "oh no\n";
143  return 0;
144  }
145 
146  int typeDesc = typeMap[key];
147 
148  //If this option is not pressent, either return the opposite T/F condition or blank
149  if(options[it->second].type() == argType::None)
150  {
151  if(typeDesc == argType::False) return trueStr;
152  if(typeDesc == argType::True) return falseStr;
153  return blankStr;
154  }
155 
156  if(typeDesc == argType::False || typeDesc == argType::True)
157  {
158  if(options[it->second].last()->type() == argType::False) return falseStr;
159  else return trueStr;
160  }
161 
162  if(options[it->second].arg == 0) return blankStr;
163 
164  return options[it->second].last()->arg;
165 }
166 
167 int clOptions::count( const std::string & key )
168 {
169  mapIterator it = map.find(key);
170 
171  if(it == map.end()) return -1;
172 
173  return options[it->second].count();
174 }
175 
176 void clOptions::getAll( std::vector<std::string> & args,
177  const std::string & key
178  )
179 {
180  mapIterator it = map.find(key);
181 
182 
183  if(it == map.end())
184  {
185  std::cerr << "oh no\n";
186  return;
187  }
188 
189  int typeDesc = typeMap[key];
190 
191  //If this option is not present, either return the opposite T/F condition or blank
192  if(options[it->second].type() == argType::None)
193  {
194 
195  if(typeDesc == argType::False)
196  {
197  args.resize(1);
198  args[0]= trueStr;
199  return;
200  }
201 
202  if(typeDesc == argType::True)
203  {
204  args.resize(1);
205  args[0]= falseStr;
206  return;
207  }
208 
209  args.clear();
210 
211  return;
212  }
213 
214  if(typeDesc == argType::False || typeDesc == argType::True)
215  {
216  args.resize(1);
217 
218  if(options[it->second].last()->type() == argType::False) args[0] = falseStr;
219  else args[0] = trueStr;
220  return;
221  }
222 
223  if(options[it->second].arg == 0)
224  {
225  args.clear();
226  return;
227  }
228 
229  int N = options[it->second].count();
230 
231  args.resize(N);
232 
233  int i=0;
234 
235  for (option::Option* opt = options[it->second]; opt != NULL && i < N; opt = opt->next())
236  {
237  args[i] = opt->arg;
238  ++i;
239  }
240 }
241 
242 bool clOptions::optSet( const std::string & key )
243 {
244  mapIterator it = map.find(key);
245 
246  if(it == map.end()) return false; //Not found --> e.g. if neither command line short nor long option set.
247 
248  if( options[it->second].type() != argType::None) return true;
249  return false;
250 };
251 
253 {
254  //The dummy description to catch unknown options is inserted at position nOpts.
255  return options[nOpts].count();
256 }
257 
258 int clOptions::unknown( std::vector<std::string> & unk )
259 {
260  unk.clear();
261 
262  if(numUnknown() == 0) return 0;
263 
264  for (option::Option* opt = options[nOpts]; opt != NULL; opt = opt->next())
265  {
266  unk.push_back( opt->name );
267  }
268 
269  return 0;
270 }
271 
272 } //namespace app
273 } //namespace mx
274 
275 
276 
A command line parser.
The mxlib c++ namespace.
Definition: mxError.hpp:107
void parse(int argc, char **argv, std::vector< std::string > *nonOptions=0)
Parse the command line.
Definition: clOptions.cpp:104
const char * operator[](const std::string &key)
Get the value of the option, if present.
Definition: clOptions.cpp:136
void clear()
Clear the memory held by this object.
Definition: clOptions.cpp:58
int numUnknown()
Get the number of unknown options found by the parser.
Definition: clOptions.cpp:252
void add(const std::string &optName, const char *const shortOpt, const char *const longOpt, int argT)
Add a command line option target.
Definition: clOptions.cpp:71
int count(const std::string &key)
Get the number of times the option was set on the command line.
Definition: clOptions.cpp:167
unsigned int nOpts
The number of options added.
Definition: clOptions.hpp:70
void getAll(std::vector< std::string > &args, const std::string &key)
Fill a vector of strings with the arguments supplied for key, last to first.
Definition: clOptions.cpp:176
~clOptions()
D'tor. Deletes the options and buffer pointers.
Definition: clOptions.cpp:52
int unknown(std::vector< std::string > &unk)
Get a vector of the unknown options found by the parser.
Definition: clOptions.cpp:258
bool optSet(const std::string &key)
Test whether this option was set.
Definition: clOptions.cpp:242