CommandOptionParser.cpp
Go to the documentation of this file.
1 //==============================================================================
2 //
3 // This file is part of GNSSTk, the ARL:UT GNSS Toolkit.
4 //
5 // The GNSSTk is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published
7 // by the Free Software Foundation; either version 3.0 of the License, or
8 // any later version.
9 //
10 // The GNSSTk is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with GNSSTk; if not, write to the Free Software Foundation,
17 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 // This software was developed by Applied Research Laboratories at the
20 // University of Texas at Austin.
21 // Copyright 2004-2022, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24 
25 //==============================================================================
26 //
27 // This software was developed by Applied Research Laboratories at the
28 // University of Texas at Austin, under contract to an agency or agencies
29 // within the U.S. Department of Defense. The U.S. Government retains all
30 // rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 // Pursuant to DoD Directive 523024
33 //
34 // DISTRIBUTION STATEMENT A: This software has been approved for public
35 // release, distribution is unlimited.
36 //
37 //==============================================================================
38 
44 #include <cstring>
45 #include "CommandOptionParser.hpp"
46 
47 #include "StringUtils.hpp"
48 
49 using namespace std;
50 using namespace gnsstk::StringUtils;
51 
52 #ifdef _MSC_VER
53  #if ( _MSC_VER < 1600 )
54  #define min(VAL1, VAL2) _cpp_min(VAL1, VAL2)
55  #define max(VAL1, VAL2) _cpp_max(VAL1, VAL2)
56  #endif
57 #endif
58 
59 namespace gnsstk
60 {
61 
62  CommandOptionParser::CommandOptionParser(
63  const std::string& description,
64  const CommandOptionVec& optList)
65  : hasRequiredArguments(false),
66  hasOptionalArguments(false),
67  text(description)
68 
69  {
70  CommandOptionVec::const_iterator optIter = optList.begin();
71  for ( ; optIter != optList.end() ; ++optIter)
72  {
73  addOption( *(*optIter) );
74  }
75  }
76 
77 
78  // validate a new option and add it to optionVec
81  {
82  CommandOptionVec::const_iterator optIter = optionVec.begin();
83  for ( ; optIter != optionVec.end() ; ++optIter)
84  {
85  if ( (co.shortOpt != 0)
86  && ((*optIter)->shortOpt == co.shortOpt) )
87  {
88  std::string msg("Short option already in use: ");
89  msg.append(1, co.shortOpt);
90  gnsstk::InvalidParameter exc(msg);
91  GNSSTK_THROW(exc);
92  }
93  if ( (co.longOpt.size() != 0)
94  && ((*optIter)->longOpt.compare(co.longOpt) == 0) )
95  {
96  std::string msg("Long option already in use: ");
97  msg.append(co.longOpt);
98  gnsstk::InvalidParameter exc(msg);
99  GNSSTK_THROW(exc);
100  }
101  if ( ((*optIter)->optType == CommandOption::trailingType)
103  {
104  gnsstk::InvalidParameter exc("Multiple trailing options are disallowed");
105  GNSSTK_THROW(exc);
106  }
107  }
108  optionVec.push_back( &co );
109  return *this;
110  }
111 
112 
113  // parses the command line input
114  void
116  char* argv[])
117  {
118  // this maps the index of optionVec to the command line options
119  CommandOptionMap com;
120 
121  // keep track of the order of command options
122  unsigned int order = 0;
123 
124  // get the program name. If there's a / in it, take the part
125  // after the last / as the program name
126  progName = string(argv[0]);
127  string::size_type slashPos = progName.rfind('/');
128  if (slashPos != string::npos)
129  progName = progName.substr(slashPos + 1);
130 #ifdef WIN32
131  // remove file extension of executable under windows for
132  // consistency with other platforms
133  string::size_type p = progName.find(".exe");
134  if (p != string::npos)
135  {
136  progName.erase(p,4);
137  }
138 #endif
139 
140  string shortOptString;
141  struct option* optArray = NULL;
142  unsigned long optArraySize = 0;
143 
144  CommandOption *trailing = NULL;
145 
146  // build the getopt and getopt_long inputs
147  CommandOptionVec::size_type index;
148  for(index = 0; index < optionVec.size(); index++)
149  {
150  // Make sure the parser is set in every option
151  optionVec[index]->parser = this;
152  // add short options
153  switch (optionVec[index]->optType)
154  {
156  if (trailing)
157  errorStrings.push_back("More than one trailing argument"
158  " object used (programming error");
159  else
160  trailing = optionVec[index];
161  break;
163  if (optionVec[index]->shortOpt != 0)
164  {
165  shortOptString += optionVec[index]->toGetoptShortOption();
166  com[string(1,optionVec[index]->shortOpt)] = optionVec[index];
167  }
168 
169  // add long options
170  if (!optionVec[index]->longOpt.empty())
171  {
172  resizeOptionArray(optArray, optArraySize);
173  optArray[optArraySize - 1] =
174  optionVec[index]->toGetoptLongOption();
175  com[optionVec[index]->longOpt] = optionVec[index];
176  }
177 
178  // keep track of whether or not there are required or
179  // optional arguments
180  if (optionVec[index]->required)
181  hasRequiredArguments = true;
182  else
183  hasOptionalArguments = true;
184  break;
185  default:
186  // do nothing
187  break;
188  }
189  }
190 
191  // add the getopt_long terminator value
192  resizeOptionArray(optArray, optArraySize);
193  struct option lastOption = {0,0,0,0};
194  optArray[optArraySize - 1] = lastOption;
195 
196  // use '+' to make getopt not mangle the inputs (if i remember right)
197  shortOptString.insert((string::size_type)0, (string::size_type)1, '+');
198 
199  int cha;
200  int optionIndex;
201 
202  // ensure parsing begins at the first option
203  optind = 1;
204 
205  // disable internal error messages
206  opterr = 0;
207 
208  while (optind < argc)
209  {
210  if ((cha = getopt_long(argc, argv, shortOptString.c_str(),
211  optArray, &optionIndex)) == -1)
212  {
213  if (!trailing)
214  errorStrings.push_back("Excess arguments");
215  break;
216  }
217 
218  order++;
219 
220  // Solaris uses '?' for all getopt errors. Linux uses '?'
221  // for unknown options and ':' for options that require
222  // arguments but don't have them. That's why the error
223  // message is "option error" cause we can't differentiate
224  // what the REAL error is...
225  if ((cha == '?') || (cha == ':'))
226  {
227  // get the argument that had the error and write an
228  // error string
229  string errorArg;
230  // for a character option error
231  if (optopt != 0)
232  errorArg = string(1, (char)optopt);
233  // for a getopt_long error
234  else
235  errorArg = argv[optind - 1];
236  errorStrings.push_back(string("Option error: " + errorArg));
237  }
238  // otherwise this is probably a found option
239  else
240  {
241  string thisOption;
242 
243  // determine whether it found the short or long version
244  if (cha != 0)
245  thisOption = string(1,(char)cha);
246  else
247  thisOption = string(optArray[optionIndex].name);
248 
249  // try to find the option in our option map
250  map<string, CommandOption*>::iterator itr = com.find(thisOption);
251 
252  if (itr != com.end())
253  {
254  CommandOption* pickedOption = (*itr).second;
255  // if there is an value for this option...
256  if (optarg)
257  {
258  if (pickedOption->optFlag == CommandOption::noArgument)
259  {
260  errorStrings.push_back(string("Option ") +
261  thisOption +
262  string(" has an argument but it shouldn't."));
263  }
264  // add this argument to the picked option and
265  // increment the count
266  else if (pickedOption->optFlag == CommandOption::hasArgument)
267  {
268  pickedOption->value.push_back(string(optarg));
269  pickedOption->count++;
270  pickedOption->order.push_back(order);
271  }
272 
273  }
274  // no value for option...
275  else
276  {
277  if (pickedOption->optFlag == CommandOption::hasArgument)
278  {
279  errorStrings.push_back(string("Option ") +
280  thisOption +
281  string(" has no argument when it should."));
282  }
283  // increment the picked option's count
284  else if (pickedOption->optFlag == CommandOption::noArgument)
285  {
286  pickedOption->count++;
287  pickedOption->order.push_back(order);
288  }
289  }
290  // special handling for help-like options
291  CommandOptionHelp *helpOpt =
292  dynamic_cast<CommandOptionHelp*>(pickedOption);
293  if (helpOpt)
294  {
295  helpOptions.push_back(helpOpt);
296  }
297  } // itr != end()
298  else
299  {
300  errorStrings.push_back("Unknown option error");
301  }
302  } // else cha ==
303  } // getopt_long
304 
305  // check for remaining arguments
306  if (optind < argc)
307  {
308  if (trailing)
309  {
310  int i;
311  for(i = optind; i < argc; i++)
312  {
313  order++;
314 
315  trailing->value.push_back(string(argv[i]));
316  trailing->count++;
317  trailing->order.push_back(order);
318  }
319  }
320  // the case where trailing==null is handled above
321  }
322 
323  for(index = 0; index < optionVec.size(); index++)
324  {
325  string retVal = optionVec[index]->checkArguments();
326  if (!retVal.empty())
327  errorStrings.push_back(retVal);
328 
329  // check max count
330  if (optionVec[index]->maxCount != 0)
331  {
332  if (optionVec[index]->count > optionVec[index]->maxCount)
333  {
334  string errstr("Option ");
335  errstr += optionVec[index]->getOptionString();
336  errstr += string(" appeared more times than allowed.");
337  errorStrings.push_back(errstr);
338  }
339  }
340  }
341 
342  delete [] optArray;
343  }
344 
345 
346  ostream& CommandOptionParser::dumpErrors(ostream& out)
347  {
348  vector<string>::size_type index;
349  for(index = 0; index < errorStrings.size(); index++)
350  out << errorStrings[index] << endl;
351  return out;
352  }
353 
354  std::ostream& CommandOptionParser::printHelp(std::ostream& out,
355  bool doPretty, bool firstOnly)
356  {
357  std::vector<CommandOptionHelp*>::size_type index;
358  if (!helpOptions.empty())
359  {
360  if (firstOnly)
361  {
362  helpOptions[0]->printHelp(out, doPretty);
363  }
364  else
365  {
366  for(index = 0; index < helpOptions.size(); index++)
367  helpOptions[index]->printHelp(out, doPretty);
368  }
369  }
370  return out;
371  }
372 
373  // prints the required arguments first (if any) then the optional
374  // ones (if any)
375  ostream& CommandOptionParser::displayUsage(ostream& out, bool doPretty)
376  {
377  CommandOptionVec::size_type index;
378  CommandOption *trailing = NULL;
379 
380  char *colch = getenv("COLUMNS");
381  int columns = 80;
382  unsigned maxlen = 0;
383  if (colch)
384  {
385  string colStr(colch);
386  columns = asInt(colStr);
387  }
388 
389  // find the trailing argument if any, and max option string length
390  for (index = 0; index < optionVec.size(); index++)
391  {
392  if (optionVec[index]->optType == CommandOption::trailingType)
393  trailing = optionVec[index];
394  else if (optionVec[index]->optType == CommandOption::stdType)
395  maxlen = std::max(maxlen,
396  unsigned(optionVec[index]->getFullOptionString().length()));
397  }
398 
399  out << "Usage: " << progName;
401  out << " [OPTION] ...";
402  if (trailing)
403  out << " " << trailing->description;
404  out << endl
405  << (doPretty ? prettyPrint(text,"\n","","",columns) : text);
406 // << endl
407 // << endl
408 // << "Command options:" << endl;
409 
410  for(int required = 1; required >= 0; required--)
411  {
412  if (required==1 && hasRequiredArguments)
413  out << endl << "Required arguments:" << endl;
414  else if (required==0 && hasOptionalArguments)
415  out << endl << "Optional arguments:" << endl;
416 
417  for(index = 0; index < optionVec.size(); index++)
418  {
419  if ((optionVec[index]->required == (required==1)) &&
420  (optionVec[index]->optType == CommandOption::stdType))
421  {
422  string optstr(optionVec[index]->getFullOptionString());
423  string desc(optionVec[index]->description);
424  string indent(maxlen, ' ');
425 
426  if(doPretty) {
427  leftJustify(optstr, maxlen);
428  prettyPrint(desc, "\n", indent, optstr, columns);
429  }
430  out << desc;
431  if(!doPretty) out << endl;
432  }
433  }
434  }
435 
436  return out;
437  }
438 
439 
441  {
442  CommandOptionVec::size_type index;
443  CommandOption *trailing = nullptr;
444 
449  // SYNOPSIS section
450  // print the help options one at a time first
451  for (index = 0; index < optionVec.size(); index++)
452  {
453  // Find our trailing type first so we can add it on the end
454  // of the non-help synopses.
455  if (optionVec[index]->optType == CommandOption::trailingType)
456  trailing = optionVec[index];
457  if (dynamic_cast<CommandOptionHelp*>(optionVec[index]) != nullptr)
458  {
459  out << " * <b>" << progName << "</b> ";
460  if (optionVec[index]->shortOpt)
461  {
462  out << " <b>-" << optionVec[index]->shortOpt;
463  }
464  else if (!optionVec[index]->longOpt.empty())
465  out << " <b>\\--" << optionVec[index]->longOpt;
466  out << "</b>";
467  if (optionVec[index]->optFlag == CommandOption::hasArgument)
468  {
469  out << "&nbsp;\\argarg{" << optionVec[index]->getArgString()
470  << "}";
471  }
472  out << " <br/>" << endl;
473  }
474  }
475  out << " * <b>" << progName << "</b>";
476  for (int required = 1; required >= 0; required--)
477  {
478  for (index = 0; index < optionVec.size(); index++)
479  {
480  if (dynamic_cast<CommandOptionHelp*>(optionVec[index]) == nullptr)
481  {
482  if ((optionVec[index]->required == (required==1)) &&
483  (optionVec[index]->optType == CommandOption::stdType))
484  {
485  out << " <b>";
486  if (required == 0)
487  out << "[";
488  if (optionVec[index]->shortOpt)
489  {
490  out << "-" << optionVec[index]->shortOpt;
491  }
492  else if (!optionVec[index]->longOpt.empty())
493  out << "\\--" << optionVec[index]->longOpt;
494  out << "</b>";
495  if (optionVec[index]->optFlag == CommandOption::hasArgument)
496  {
497  out << "&nbsp;\\argarg{"
498  << optionVec[index]->getArgString() << "}";
499  }
500  if (required == 0)
501  out << "<b>]</b>";
502  }
503  }
504  }
505  }
506  if (trailing)
507  {
508  out << " ";
509  if (!trailing->required)
510  out << "<b>[</b>";
511  out << "\\argarg{" << trailing->getArgString() << "}";
512  if (!trailing->required)
513  out << "<b>]</b>";
515  if (trailing->maxCount != 1)
516  out << " <b>[</b>...<b>]</b>";
517  }
518 
519  // DESCRIPTION section
520  out << endl << endl << " * \\dictionary" << endl;
521 
522  for (int required = 1; required >= 0; required--)
523  {
524  for(index = 0; index < optionVec.size(); index++)
525  {
526  if ((optionVec[index]->required == (required==1)) &&
527  (optionVec[index]->optType == CommandOption::stdType))
528  {
529  out << " * \\dicterm{";
530  if (optionVec[index]->shortOpt)
531  {
532  out << "-" << optionVec[index]->shortOpt;
533  if (!optionVec[index]->longOpt.empty())
534  out << ", ";
535  }
536  if (!optionVec[index]->longOpt.empty())
537  out << "\\--" << optionVec[index]->longOpt;
538  if (optionVec[index]->optFlag == CommandOption::hasArgument)
539  {
540  out << "=\\argarg{" << optionVec[index]->getArgString()
541  << "}";
542  }
543  out << "}" << endl
544  << " * \\dicdef{" << strip(optionVec[index]->description)
545  << "}" << endl;
546  }
547  }
548  }
549  out << " * \\enddictionary" << endl;
550 
551  return out;
552  }
553 
554 
555  // resizes the array for getopt_long
557  unsigned long& oldSize)
558  {
559  struct option* newArray = new struct option[1 + oldSize];
560  std::memcpy(newArray, oldArray, oldSize * sizeof(struct option));
561  delete [] oldArray;
562  oldArray = newArray;
563  newArray = NULL;
564  oldSize += 1;
565  }
566 
567 } // end namespace gnsstk
opterr
int opterr
Definition: getopt.c:153
gnsstk::CommandOptionParser::addOption
CommandOptionParser & addOption(gnsstk::CommandOption &co)
Definition: CommandOptionParser.cpp:80
getenv
char * getenv()
gnsstk::StringUtils::asInt
long asInt(const std::string &s)
Definition: StringUtils.hpp:713
gnsstk::CommandOption::stdType
@ stdType
The argument of this option can be any type.
Definition: CommandOption.hpp:135
gnsstk::CommandOption::longOpt
std::string longOpt
The string for the long option (for example, "--foo").
Definition: CommandOption.hpp:246
gnsstk::CommandOption::required
bool required
Whether or not this is a required command line option.
Definition: CommandOption.hpp:252
gnsstk::CommandOption::description
std::string description
The description for the help text.
Definition: CommandOption.hpp:248
gnsstk::CommandOptionParser::displayUsageDoxygen
std::ostream & displayUsageDoxygen(std::ostream &out)
Definition: CommandOptionParser.cpp:440
StringUtils.hpp
gnsstk::CommandOption::maxCount
unsigned long maxCount
Definition: CommandOption.hpp:258
gnsstk::max
T max(const SparseMatrix< T > &SM)
Maximum element - return 0 if empty.
Definition: SparseMatrix.hpp:881
gnsstk::CommandOption::getArgString
virtual std::string getArgString() const
Returns a string with the argument format.
Definition: CommandOption.hpp:176
gnsstk::CommandOptionParser::dumpErrors
std::ostream & dumpErrors(std::ostream &out)
Writes the errors to out.
Definition: CommandOptionParser.cpp:346
gnsstk::CommandOptionParser::progName
std::string progName
the name of this program
Definition: CommandOptionParser.hpp:165
CommandOptionParser.hpp
NULL
#define NULL
Definition: getopt1.c:64
gnsstk
For Sinex::InputHistory.
Definition: BasicFramework.cpp:50
optopt
int optopt
Definition: getopt.c:159
gnsstk::CommandOption::shortOpt
char shortOpt
The character for the short option (for example, '-f').
Definition: CommandOption.hpp:244
gnsstk::CommandOptionParser::helpOptions
std::vector< CommandOptionHelp * > helpOptions
Definition: CommandOptionParser.hpp:170
gnsstk::CommandOptionParser::optionVec
CommandOptionVec optionVec
The vector of CommandOptions for the parser.
Definition: CommandOptionParser.hpp:150
gnsstk::CommandOptionVec
std::vector< CommandOption * > CommandOptionVec
Definition: CommandOption.hpp:66
gnsstk::CommandOptionParser::hasRequiredArguments
bool hasRequiredArguments
The vector of unprocessed command line arguments.
Definition: CommandOptionParser.hpp:157
gnsstk::CommandOption::count
unsigned long count
Definition: CommandOption.hpp:255
gnsstk::CommandOption::optFlag
CommandOptionFlag optFlag
Flag for determining whether this option has an argument or not.
Definition: CommandOption.hpp:239
gnsstk::CommandOption::value
std::vector< std::string > value
Any arguments passed with this option get put in here.
Definition: CommandOption.hpp:250
gnsstk::CommandOptionParser::hasOptionalArguments
bool hasOptionalArguments
whether or not this command line has optional options
Definition: CommandOptionParser.hpp:159
gnsstk::CommandOption
Definition: CommandOption.hpp:115
gnsstk::CommandOption::order
std::vector< unsigned long > order
The order in which this option was encountered on the command line.
Definition: CommandOption.hpp:260
gnsstk::CommandOption::optType
CommandOptionType optType
Definition: CommandOption.hpp:242
gnsstk::CommandOption::hasArgument
@ hasArgument
option requires an argument
Definition: CommandOption.hpp:126
gnsstk::CommandOption::noArgument
@ noArgument
option requires no arguments
Definition: CommandOption.hpp:125
gnsstk::CommandOptionParser::resizeOptionArray
void resizeOptionArray(struct option *&oldArray, unsigned long &oldSize)
changes the size of the option array for getopt_long.
Definition: CommandOptionParser.cpp:556
gnsstk::StringUtils
Definition: IonexStoreStrategy.cpp:44
option
Definition: getopt.h:94
gnsstk::CommandOption::trailingType
@ trailingType
Special case, no option, just the remaining args.
Definition: CommandOption.hpp:134
gnsstk::CommandOptionParser::parseOptions
void parseOptions(int argc, char *argv[])
Parses the command line.
Definition: CommandOptionParser.cpp:115
optind
int optind
Definition: getopt.c:133
std
Definition: Angle.hpp:142
option::name
char * name
Definition: getopt.h:99
gnsstk::CommandOptionParser
Definition: CommandOptionParser.hpp:86
gnsstk::CommandOptionHelp
Definition: CommandOption.hpp:735
gnsstk::StringUtils::strip
std::string & strip(std::string &s, const std::string &aString, std::string::size_type num=std::string::npos)
Definition: StringUtils.hpp:1482
gnsstk::StringUtils::leftJustify
std::string & leftJustify(std::string &s, const std::string::size_type length, const char pad=' ')
Definition: StringUtils.hpp:1582
GNSSTK_THROW
#define GNSSTK_THROW(exc)
Definition: Exception.hpp:366
gnsstk::CommandOptionParser::displayUsage
std::ostream & displayUsage(std::ostream &out, bool doPretty=true)
Definition: CommandOptionParser.cpp:375
gnsstk::CommandOptionParser::CommandOptionMap
std::map< std::string, gnsstk::CommandOption * > CommandOptionMap
Definition: CommandOptionParser.hpp:91
gnsstk::CommandOptionParser::text
std::string text
the description of this program
Definition: CommandOptionParser.hpp:162
optarg
char * optarg
Definition: getopt.c:118
gnsstk::CommandOptionParser::errorStrings
std::vector< std::string > errorStrings
The vector of error strings for displaying to the user.
Definition: CommandOptionParser.hpp:152
getopt_long
int getopt_long()
gnsstk::StringUtils::prettyPrint
std::string & prettyPrint(std::string &aStr, const std::string &lineDelim="\n", const std::string &indent="", const std::string &firstIndent=" ", const std::string::size_type len=80, const char wordDelim=' ')
Definition: StringUtils.hpp:2774
gnsstk::CommandOptionParser::printHelp
std::ostream & printHelp(std::ostream &out, bool doPretty=true, bool firstOnly=true)
Definition: CommandOptionParser.cpp:354


gnsstk
Author(s):
autogenerated on Wed Oct 25 2023 02:40:38