powerd++  0.4.4
Options.hpp
Go to the documentation of this file.
1 
127 #ifndef _POWERDXX_NIH_OPTIONS_HPP_
128 #define _POWERDXX_NIH_OPTIONS_HPP_
129 
130 #include "utility.hpp" /* utility::literals */
131 
132 #include <cstddef> /* size_t */
133 #include <type_traits> /* std::true_type, std::void_t */
134 #include <cassert> /* assert() */
135 
140 namespace nih {
141 
159 template <class OptionT, class = void>
160 struct enum_has_members : std::false_type {};
161 
162 template <class OptionT>
163 struct enum_has_members<OptionT, std::void_t<decltype(OptionT::OPT_UNKNOWN),
164  decltype(OptionT::OPT_NOOPT),
165  decltype(OptionT::OPT_DASH),
166  decltype(OptionT::OPT_LDASH),
167  decltype(OptionT::OPT_DONE)>> : std::true_type {};
169 
182 template <class OptionT>
183 struct Parameter {
187  OptionT option;
188 
194  char sparam;
195 
201  char const * lparam;
202 
208  char const * args;
209 
213  char const * usage;
214 };
215 
226 template <class OptionT>
227 size_t argCount(Parameter<OptionT> const & def) {
228  if (!def.args || !def.args[0]) { return 0; }
229  size_t argc = 1;
230  for (char const * pch = def.args; *pch; ++pch) {
231  argc += (*pch == ',' ? 1 : 0);
232  }
233  return argc;
234 }
235 
248 template <class OptionT, size_t DefCount>
249 class Options {
250  static_assert(enum_has_members<OptionT>::value,
251  "The enum must have the members OPT_UNKNOWN, OPT_NOOPT, OPT_DASH, OPT_LDASH and OPT_DONE");
252  private:
256  int const argc;
257 
261  char const * const * const argv;
262 
266  char const * const usageStr;
267 
271  Parameter<OptionT> const (& defs)[DefCount];
272 
277  OptionT::OPT_UNKNOWN, 0, nullptr, nullptr, nullptr
278  };
279 
284  OptionT::OPT_NOOPT, 0, nullptr, nullptr, nullptr
285  };
286 
291  OptionT::OPT_DASH, 0, nullptr, nullptr, nullptr
292  };
293 
298  OptionT::OPT_LDASH, 0, nullptr, nullptr, nullptr
299  };
300 
305  int argi;
306 
310  char const * argp;
311 
316 
327  int mutable showi;
328 
337  static char const * removePath(char const * const file) {
338  auto result = file;
339  for (auto ptr = file; *ptr; ++ptr) {
340  if (*ptr == '/' || *ptr == '\\') {
341  result = ptr + 1;
342  }
343  }
344  return result;
345  }
346 
357  static bool match(char const * const lstr, char const * const rstr) {
358  size_t i = 0;
359  for (; lstr[i] && rstr[i]; ++i) {
360  if (lstr[i] != rstr[i]) { return false; }
361  }
362  return lstr[i] == rstr[i];
363  }
364 
375  static bool bmatch(char const * const str,
376  char const * const prefix) {
377  size_t i = 0;
378  for (; str[i] && prefix[i]; ++i) {
379  if (str[i] != prefix[i]) { return false; }
380  }
381  return !prefix[i];
382  }
383 
392  Parameter<OptionT> const & get(char const ch) {
393  for (auto const & def : this->defs) {
394  if (def.sparam == ch) {
395  return def;
396  }
397  }
398  return this->opt_unknown;
399  }
400 
409  Parameter<OptionT> const & get(char const * const str) {
410  for (auto const & def : this->defs) {
411  if (match(str, def.lparam)) {
412  return def;
413  }
414  }
415  return this->opt_unknown;
416  }
417 
418  public:
429  Options(int const argc, char const * const * const argv,
430  char const * const usage,
431  Parameter<OptionT> const (& defs)[DefCount]) :
433  argi{0}, argp{nullptr}, current{nullptr}, showi{0} {}
434 
446  /* reset what to show() */
447  this->showi = 0;
448 
449  /*
450  * point argi and argp to the appropriate places
451  */
452  if (this->current) {
453  /* this is not the first call */
454  if (this->argp && this->argp[0] && this->argp[1]) {
455  /* argp is set and does not point to the end
456  * of an argument */
457  if (argCount(*this->current) == 0) {
458  /* proceed to the next short option */
459  ++this->argp;
460  } else {
461  /* the chained characters were an
462  * option argument */
463  this->argp = nullptr;
464  this->argi += argCount(*this->current);
465  }
466  } else {
467  /* point forward for the option stand alone
468  * case */
469  this->argp = nullptr;
470  this->argi += argCount(*this->current) + 1;
471  }
472  } else {
473  /* no current state, start with the first argument */
474  this->argi = 1;
475  this->argp = nullptr;
476  }
477 
478  /*
479  * match the current option
480  */
481  /* ran out of options */
482  if (this->argi >= this->argc) {
483  /* reset state */
484  this->current = nullptr;
485  return *this;
486  }
487  /* continue short option chain */
488  if (this->argp) {
489  this->current = &get(this->argp[0]);
490  return *this;
491  }
492  /* long option */
493  if (bmatch(this->argv[this->argi], "--")) {
494  if (!this->argv[this->argi][2]) {
495  this->current = &this->opt_ldash;
496  return *this;
497  }
498  this->current = &get(this->argv[this->argi] + 2);
499  return *this;
500  }
501  /* short option */
502  if (this->argv[this->argi][0] == '-') {
503  if (!this->argv[this->argi][1]) {
504  this->current = &this->opt_dash;
505  return *this;
506  }
507  this->argp = this->argv[this->argi] + 1;
508  this->current = &get(this->argp[0]);
509  return *this;
510  }
511  /* not an option */
512  this->current = &this->opt_noopt;
513  return *this;
514  }
515 
534  operator OptionT() const {
535  return this->current
536  ? this->current->option
537  : OptionT::OPT_DONE;
538  }
539 
553  char const * operator [](int const i) const {
554  /* show() the latest requested argument */
555  this->showi = i;
556 
557  /* argument is in the same string as option */
558  if (this->argp && this->argp[0] && this->argp[1] && i > 0) {
559  if (this->argi + i - 1 >= this->argc) {
560  return "";
561  }
562  if (i == 1) {
563  return this->argp + 1;
564  }
565  return this->argv[this->argi + i - 1];
566  }
567  /* read in front of arguments */
568  if (this->argi + i < 0) {
569  return "";
570  }
571  /* argument is in the string following the option */
572  if (this->argi + i < this->argc) {
573  return this->argv[this->argi + i];
574  }
575  /* read beyond end of arguments */
576  return "";
577  }
578 
586  std::string usage() const {
587  using namespace utility::literals;
588  auto result = "usage: %s %s\n\n"_fmt
589  (removePath(this->argv[0]), this->usageStr);
590 
591  /* get column sizes */
592  size_t lparam_max = 0; /* length of the longest option */
593  size_t args_max = 0; /* length of the longest argument */
594  for (auto const & def : this->defs) {
595  if (def.lparam) {
596  auto const len = std::string{def.lparam}.size();
597  lparam_max =
598  lparam_max >= len ? lparam_max : len;
599  }
600  auto const len = std::string{def.args}.size();
601  args_max = args_max >= len ? args_max : len;
602  }
603  /* print columns */
604  for (auto const & def : this->defs) {
605  /* get option */
606  auto const sparam = def.sparam ? def.sparam : ' ';
607  auto const sdash = def.sparam ? '-' : ' ';
608  auto const lparam = def.lparam ? def.lparam : "";
609  auto const ldash = lparam[0] ? '-' : ' ';
610  auto const sep = def.sparam && lparam[0] ? ',' : ' ';
611  result += " %c%c%c %c%c%-*s "_fmt
612  (sdash, sparam, sep,
613  ldash, ldash, lparam_max, lparam);
614  /* get arguments */
615  auto len = args_max;
616  for (auto it = def.args; it && *it; ++it, --len) {
617  result += *it == ',' ? ' ' : *it;
618  }
619  result += "%-*s %s\n"_fmt(len, "", def.usage);
620  }
621  return result;
622  }
623 
639  utility::Underlined show(int const i, int const n = 1) const {
640  using utility::highlight;
641  using std::string;
642 
643  /* select a whole argument */
644  char const * const select = (*this)[i];
645  /* if the current option (i == 0) is requested, pick
646  * up the pointer to the current short option character */
647  char const * const argp = i == 0 ? this->argp : nullptr;
648  string cmd; /* command and arguments string */
649  ptrdiff_t offset = -1; /* the underline byte-offset */
650  ptrdiff_t length = 1; /* the underline byte-length */
651  int remaining = 0; /* #args left to underline */
652  /* build cmd string */
653  for (int p = 0; p < this->argc; ++p) {
654  /* build each argument character wise */
655  for (auto it = this->argv[p]; *it; ++it) {
656  /* underline short option */
657  if (argp && it == argp) {
658  remaining = n > 0 ? n : -1;
659  it[1] && --remaining;
660  offset = cmd.size();
661  }
662  /* underline long option / argument */
663  if (!argp && it == select) {
664  remaining = n > 0 ? n : -1;
665  offset = cmd.size();
666  }
667  /* regular character */
668  cmd += *it;
669  }
670  /* update the underline length */
671  remaining && (length = cmd.size() - offset);
672  remaining > 0 && --remaining;
673  /* space separate arguments */
674  cmd += ' ';
675  }
676  /* the selected argument must be behind the last argument,
677  * e.g. because the last parameter is missing an argument
678  * or the state is OPT_DONE */
679  offset >= 0 || i < 0 || (offset = cmd.size());
680  return highlight(cmd, offset, length);
681  }
682 
690  std::string show() const {
691  return show(this->showi);
692  }
693 
703  int offset() const {
704  return this->argi;
705  }
706 };
707 
708 } /* namespace nih */
709 
710 #endif /* _POWERDXX_NIH_OPTIONS_HPP_ */
utility::literals
Contains literal operators.
Definition: utility.hpp:147
nih::Options::argc
const int argc
The number of command line arguments.
Definition: Options.hpp:251
nih::Options::usage
std::string usage() const
Returns a string for usage output, created from the option definitions.
Definition: Options.hpp:586
nih::argCount
size_t argCount(Parameter< OptionT > const &def)
Retrieves the count of arguments in an option definition.
Definition: Options.hpp:227
nih::Options::operator[]
const char * operator[](int const i) const
Retrieve arguments to the current option.
Definition: Options.hpp:553
nih::Options::usageStr
const char *const usageStr
A string literal for the usage() output.
Definition: Options.hpp:266
nih::Options::opt_dash
const Parameter< OptionT > opt_dash
The option definition to use for a single dash.
Definition: Options.hpp:290
nih::Options::operator()
Options & operator()()
Updates the internal state by parsing the next option.
Definition: Options.hpp:445
utility::highlight
Underlined highlight(std::string const &str, ptrdiff_t const offs, ptrdiff_t const len=1)
Underline the given number of characters.
Definition: utility.cpp:12
nih::Parameter
Container for an option definition.
Definition: Options.hpp:183
nih::Parameter::args
const char * args
A comma separated list of arguments.
Definition: Options.hpp:208
nih::Options::showi
int showi
The argument index to show if no argument is supplied to show().
Definition: Options.hpp:327
nih::Options::argv
const char *const *const argv
The command line arguments.
Definition: Options.hpp:261
nih::Options::offset
int offset() const
Returns the argument offset of the current parameter/argument.
Definition: Options.hpp:703
nih::Options::get
const Parameter< OptionT > & get(char const ch)
Finds the short option matching the given character.
Definition: Options.hpp:392
nih::Parameter::sparam
char sparam
The short version of this parameter.
Definition: Options.hpp:194
nih::Options::defs
const Parameter< OptionT >(& defs)[DefCount]
A reference to the option definitions.
Definition: Options.hpp:271
nih::Options::argi
int argi
The index of the command line argument containing the current option.
Definition: Options.hpp:305
nih::Options::current
const Parameter< OptionT > * current
Points to the current option definition.
Definition: Options.hpp:315
nih::Options::argp
const char * argp
Points to the current short option character.
Definition: Options.hpp:310
nih::Options::removePath
static const char * removePath(char const *const file)
Returns a pointer to the file name portion of the given string.
Definition: Options.hpp:337
nih::Options::opt_noopt
const Parameter< OptionT > opt_noopt
The option definition to use for non-options.
Definition: Options.hpp:283
utility::Underlined
A line of text and an underlining line.
Definition: utility.hpp:415
nih::Options
An instance of this class offers operators to retrieve command line options and arguments.
Definition: Options.hpp:249
nih::Options::opt_ldash
const Parameter< OptionT > opt_ldash
The option definition to use for a single double-dash.
Definition: Options.hpp:297
nih::Parameter::lparam
const char * lparam
The long version of this parameter.
Definition: Options.hpp:201
nih::Parameter::option
OptionT option
The enum value to return for this option.
Definition: Options.hpp:187
nih::enum_has_members
Tests whether the given enum provides all the required definitions.
Definition: Options.hpp:160
nih::Options::match
static bool match(char const *const lstr, char const *const rstr)
Returns true if the given strings match.
Definition: Options.hpp:357
nih::Options::show
std::string show() const
Highlight the last recently accessed argument.
Definition: Options.hpp:690
nih::Options::show
utility::Underlined show(int const i, int const n=1) const
Provide a string containing the entire command line, with the indexed argument highlighted.
Definition: Options.hpp:639
nih::Parameter::usage
const char * usage
A usage string.
Definition: Options.hpp:213
nih::Options::Options
Options(int const argc, char const *const *const argv, char const *const usage, Parameter< OptionT > const (&defs)[DefCount])
Construct an options functor.
Definition: Options.hpp:429
nih
Not invented here namespace, for code that substitutes already commonly available functionality.
Definition: Options.hpp:140
utility.hpp
Implements generally useful functions.
nih::Options::bmatch
static bool bmatch(char const *const str, char const *const prefix)
Returns true if the given string starts with the given prefix.
Definition: Options.hpp:375
nih::Options::get
const Parameter< OptionT > & get(char const *const str)
Finds the long option matching the given string.
Definition: Options.hpp:409
nih::Options::opt_unknown
const Parameter< OptionT > opt_unknown
The option definition to use for unknown options.
Definition: Options.hpp:276