Manticore  Version 2.0alpha
Physics of Molecular Clouds
CommandLine.cc
Go to the documentation of this file.
1 
8 #include <mutils/CommandLine.h>
9 
10 namespace mutils {
11 
25 bool CommandLine::parseLong(const char *name, const char *next)
26 {
27  const char *fn = "CommandLine::parseLong";
28  const std::vector<Option> &opts = *validOpts_;
29  MU_EXCEPTION_IF(*name == '=', fn, "Invalid option '%s'", name);
30 
31  bool used = false;
32  std::string opt, arg = nullStr; // C string cannot produce this value.
33  const char *attach = strchr(name, '=');
34  if (attach == nullptr) {
35  opt.assign(name);
36  } else {
37  // Attached value.
38  opt.assign(name, attach-name);
39  }
40 
41  // Check if option is valid.
42  auto o = std::find_if(opts.begin(), opts.end(),
43  [&opt](const Option &x) { return !opt.compare(x.longName); });
44  MU_EXCEPTION_IF(o == opts.end(), fn, "Unknown option '%s'", opt);
45 
46  if (! o->valueName.empty()) {
47  if (attach == nullptr) {
48  MU_EXCEPTION_IF(!next, fn, "Option '%s' requires a value", opt);
49 
50  // Trailing value.
51  arg.assign(next);
52  used = true;
53  } else {
54  // Attached value.
55  arg.assign(attach+1);
56  }
57  } else {
58  MU_EXCEPTION_IF(attach, fn, "Option '%s' does not take a value", opt);
59  }
60 
61  // Insert option.
62  auto p = longOpts_.upper_bound(opt);
63  longOpts_.insert(p, LongMap::value_type(opt, arg));
64 
65  return used;
66 }
67 
68 
83 bool CommandLine::parseShort(const char *name, const char *next)
84 {
85  const char *fn = "CommandLine::parseShort";
86  const std::vector<Option> &opts = *validOpts_;
87 
88  // Allowing attached values precludes -= as an option.
89  MU_EXCEPTION_IF(*name == '=', fn, "Invalid option '%s'", name);
90 
91  // Add new option(s).
92  bool used = false;
93  for (; *name != 0; name++) {
94  char opt = *name;
95  std::string arg = nullStr; // C string cannot produce this value.
96 
97  // Check if option is valid.
98  auto o = std::find_if(opts.begin(), opts.end(),
99  [opt](const Option &x) { return x.shortName == opt; });
100  MU_EXCEPTION_IF(o == opts.end(), fn, "Unknown option '%c'", opt);
101 
102  // Last option may have a value.
103  if (! o->valueName.empty()) {
104  if (name[1] == '=') {
105  // Attached value.
106  arg.assign(name+2);
107  } else {
108  // Option must be the last of this group and needs a value.
109  MU_EXCEPTION_IF(name[1] != '\0' || next == nullptr,
110  fn, "Option '%c' requires a value", opt);
111 
112  // Trailing value.
113  arg.assign(next);
114  used = true;
115  }
116 
117  // Insert option and return.
118  auto p = shortOpts_.upper_bound(opt);
119  shortOpts_.insert(p, ShortMap::value_type(opt, arg));
120  break;
121  } else {
122  MU_EXCEPTION_IF(name[1] == '=',
123  fn, "Option '%c' does not take a value", opt);
124 
125  // Insert option and continue.
126  auto p = shortOpts_.upper_bound(opt);
127  shortOpts_.insert(p, ShortMap::value_type(opt, arg));
128  }
129  }
130 
131  return used;
132 }
133 
151 void CommandLine::init(int argc, char *argv[], const std::vector<Option> *opts)
152 {
153  // Clean slate.
154  driver_ = (argc > 0 ? argv[0] : "");
155  validOpts_ = opts;
156  shortOpts_.clear();
157  longOpts_.clear();
158  args_.clear();
159 
160  // Parse input into maps of short and long options, plus trailing arguments.
161  int i = 1;
162  for (; i < argc; i++) {
163  char *arg = argv[i], *next = (i+1 < argc ? argv[i+1] : nullptr);
164 
165  // End of options?
166  if (!strcmp(arg, "--") || !strcmp(arg, "-") || arg[0] != '-') {
167  i += !strcmp(arg, "--");
168  break;
169  }
170 
171  // New option(s).
172  i += (arg[1] != '-' ? parseShort(arg+1, next) : parseLong(arg+2, next));
173  }
174 
175  // Store trailing arguments.
176  for (; i < argc; i++) { args_.push_back(argv[i]); }
177 }
178 
197 unsigned CommandLine::getOption(const Option &option, Arguments *values,
198  unsigned nMax) const
199 {
200  const char *const fn = "CommandLine::getOption";
201  std::string names;
202  unsigned n = 0;
203 
204  if (values != nullptr) {
205  // Fill in the values.
206  values->clear();
207 
208  if (option.shortName != '\0') {
209  auto s = shortOpts_.equal_range(option.shortName);
210  for (auto x = s.first; x != s.second; x++, n++) {
211  values->push_back(x->second);
212  }
213  names += '-';
214  names += option.shortName;
215  }
216  if (! option.longName.empty()) {
217  auto l = longOpts_.equal_range(option.longName);
218  for (auto x = l.first; x != l.second; x++, n++) {
219  values->push_back(x->second);
220  }
221  if (! names.empty()) { names += '/'; }
222  names += "--";
223  names += option.longName;
224  }
225  } else {
226  n = (option.shortName != '\0' ? shortOpts_.count(option.shortName) : 0) +
227  (!option.longName.empty() ? longOpts_.count(option.longName) : 0);
228  }
229  MU_EXCEPTION_IF(n > nMax, fn,
230  "Option %s can appear at most %lu time%s (%lu present)",
231  names, nMax, nMax ==1 ? "" : "s", n);
232 
233  return n;
234 }
235 
249 unsigned CommandLine::getOption(char shortName, Arguments *values,
250  unsigned nMax) const
251 {
252  // Find option specification.
253  for (auto &opt: *validOpts_) {
254  if (opt.shortName == shortName) { return getOption(opt, values, nMax); }
255  }
256  return 0;
257 }
258 
272 unsigned CommandLine::getOption(const std::string &longName,
273  Arguments *values, unsigned nMax) const
274 {
275  // Find option specification.
276  for (auto &opt: *validOpts_) {
277  if (opt.longName == longName) { return getOption(opt, values, nMax); }
278  }
279  return 0;
280 }
281 
290 size_t CommandLine::popOption(const Option &option)
291 {
292  size_t rtn = 0;
293  if (option.shortName != '\0') { rtn += shortOpts_.erase(option.shortName); }
294  if (!option.longName.empty()) { rtn += longOpts_.erase(option.longName); }
295  return rtn;
296 }
297 
306 size_t CommandLine::popOption(char shortName)
307 {
308  // Find option specification.
309  for (auto &opt: *validOpts_) {
310  if (opt.shortName == shortName) { return popOption(opt); }
311  }
312  return 0;
313 }
314 
323 size_t CommandLine::popOption(const std::string &longName)
324 {
325  // Find option specification.
326  for (auto &opt: *validOpts_) {
327  if (opt.longName == longName) { return popOption(opt); }
328  }
329  return 0;
330 }
331 
332 
341 long CommandLine::getInteger(char shortName) const
342 {
343  const char *const fn = "CommandLine::getInteger";
344  Arguments vals;
345  MU_EXCEPTION_IF(getOption(shortName, &vals, 1) == 0, fn,
346  "Option '%c' not found", shortName);
347  return strtol(vals[0].c_str(), nullptr, 10);
348 }
349 
359 long CommandLine::getInteger(char shortName, long defValue) const
360 {
361  Arguments vals;
362  return (getOption(shortName, &vals, 1) ?
363  strtol(vals[0].c_str(), nullptr, 10) : defValue);
364 }
365 
366 
376 double CommandLine::getDouble(char shortName, double defValue) const
377 {
378  Arguments vals;
379  return (getOption(shortName, &vals, 1) ?
380  strtod(vals[0].c_str(), nullptr) : defValue);
381 }
382 
392 std::string CommandLine::getString(char shortName,
393  const std::string &defValue) const
394 {
395  Arguments vals;
396  return (getOption(shortName, &vals, 1) ? vals[0] : defValue);
397 }
398 
399 
411 void CommandLine::usage(FILE *f, const std::vector<Option> &opts,
412  const char *args, bool quiet, bool basename) const
413 {
414  size_t base = (basename ? driver_.find_last_of('/') : npos);
415  fprintf(f, "Usage: %s [OPTIONS] %s\n\nOptions:\n",
416  driver_.c_str()+(base != npos ? base+1 : 0U), args);
417 
418  // Output formatted option summary.
419  for (auto &opt: opts) {
420  bool hasShort = ( opt.shortName != '\0'),
421  hasLong = (!opt.longName.empty()),
422  hasValue = (!opt.valueName.empty());
423  fprintf(f, "%s %s%c%s%s%s%s%s%s",
424  (quiet ? "" : "\n"),
425  (hasShort ? "-" : ""), opt.shortName,
426  (hasShort && hasLong ? ", " : ""), (hasLong ? "--" : ""),
427  (hasLong ? opt.longName.c_str() : ""),
428  (hasValue ? (hasLong ? "=" : " ") : ""),
429  (hasValue ? opt.valueName.c_str() : ""),
430  (hasLong || hasValue ? "\n" : ""));
431 
432  if (!quiet) {
433  // Print each line of the help text on an indented line.
434  int indent = (hasLong || hasValue ? 8 : 4);
435  const char *line = opt.helpText.c_str();
436  while (line != nullptr) {
437  const char *next = strchr(line, '\n');
438  if (next == nullptr) {
439  fprintf(f, "%*s%s\n", indent, "", line);
440  line = next;
441  } else {
442  fprintf(f, "%*s%.*s\n", indent, "", (int )(next-line), line);
443  line = next+1;
444  }
445  indent = 8;
446  }
447  if (Log::level >= Log::DEBUG && opt.keyName != nullStr) {
448  fprintf(f, "%*s(File option: *SECTION = %s::%s, keyword %s)\n",
449  indent, "", opt.scopeName.c_str(), opt.sectName.c_str(),
450  opt.keyword().c_str());
451  }
452  }
453  }
454 }
455 
456 } // namespace mutils
unsigned getOption(const Option &option, Arguments *values=nullptr, unsigned nMax=-1) const
Retrieves all copies of a command line option.
Definition: CommandLine.cc:197
Command line option specification.
Definition: CommandLine.h:69
std::string longName
Long option name.
Definition: CommandLine.h:71
std::string getString(char shortName, const std::string &defValue) const
Retrieves unique string option.
Definition: CommandLine.cc:392
std::vector< std::string > Arguments
Command line arguments type.
Definition: CommandLine.h:51
#define MU_EXCEPTION_IF(cond, src, msg,...)
Conditionally throws mutils::Exception.
Definition: Exception.h:101
size_t popOption(const Option &option)
Removes and destroys all copies of a command line option.
Definition: CommandLine.cc:290
Declares the CommandLine class.
const std::string nullStr(1, 0)
const std::vector< Option > * validOpts_
Recognized command line options.
Definition: CommandLine.h:190
bool parseLong(const char *name, const char *next)
Processes a long option (possibly with a value).
Definition: CommandLine.cc:25
static int level
Message logging level for output to stream.
Definition: Log.h:430
bool parseShort(const char *name, const char *next)
Processes one or more short options (the last possibly with a value).
Definition: CommandLine.cc:83
MathUtils package.
Definition: CommandLine.cc:10
std::string::size_type npos
Definition: statics.cc:24
static constexpr int DEBUG
Verbose information.
Definition: Log.h:350
std::string driver_
Invoked driver program name (argv[0] value).
Definition: CommandLine.h:199
void init(int argc, char *argv[], const std::vector< Option > *opts)
Initializes new command line input.
Definition: CommandLine.cc:151
LongMap longOpts_
Long option map.
Definition: CommandLine.h:196
void usage(FILE *f, const std::vector< Option > &opts, const char *args="", bool quiet=false, bool basename=true) const
Summarizes command line usage (including options).
Definition: CommandLine.cc:411
long getInteger(char shortName) const
Retrieves unique decimal integer option.
Definition: CommandLine.cc:341
ShortMap shortOpts_
Short option map.
Definition: CommandLine.h:193
char shortName
Short option name.
Definition: CommandLine.h:70
double getDouble(char shortName, double defValue) const
Retrieves unique floating point option.
Definition: CommandLine.cc:376
Arguments args_
Trailing command line arguments.
Definition: CommandLine.h:202