ewmscp  ..
Options.cpp
Go to the documentation of this file.
1 /*
2  Option parser C++(11) library for parsing command line options
3  Copyright (C) 2016 Juergen Hannappel
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program 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 General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "Options.h"
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #ifdef IS_NONBROKEN_SYSTEM
22 #include <termios.h>
23 #include <sys/ioctl.h>
24 #endif
25 #include <string.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <ctime>
29 #include <iterator>
30 #include <algorithm>
31 #include <list>
32 #include <system_error>
33 
36 
37 namespace options {
38  namespace internal {
39  // ugly trick here: we give a reference to the newly construcetd item to the constructor
40  // we have only one constructor that needs the reference as parameter
41  const sourceFile sourceFile::gUnsetSource("unset", sourceFile::gUnsetSource);
42  const sourceFile sourceFile::gCmdLine("commandLine", sourceFile::gUnsetSource);
43 
44  optionError::optionError(const base* aOffendingOption, const std::string& aWhat):
45  std::runtime_error(aWhat),
46  offendingOption(*aOffendingOption) {}
47  optionError::optionError(const base* aOffendingOption, const char* aWhat):
48  std::runtime_error(aWhat),
49  offendingOption(*aOffendingOption) {}
50  const base& optionError::fGetOption() const {
51  return offendingOption;
52  }
53  } // end of namespace internal
54 
55 
57  std::ostream& operator<< (std::ostream &aStream, const internal::sourceItem& aItem) {
58  aStream << aItem.fGetFile()->fGetName() << ":" << aItem.fGetLineNumber();
59  return aStream;
60  }
61 
62  namespace internal { // for clarity we put these options into the
63  // internal namespace, although they are already hidden by the static
65  static single<bool> gOptionDebugOptions('\0', "debugOptions", "give debug output to option parsing");
67  static single<bool> gOptionNoCfgFiles('\0', "noCfgFiles", "do not read the default config files, must be FIRST option");
68 
69 
70  positional_base::positional_base(int aOrderingNumber,
71  base* aAsBase) {
72  if (aAsBase->fIsContainer()) {
73  for (const auto& it : fGetPositonalArgs()) {
74  if (it.second->fIsContainer()) {
75  throw std::logic_error("only one container type option allowed");
76  }
77  }
78  }
79  auto result = fGetPositonalArgs().emplace(aOrderingNumber, aAsBase);
80  if (result.second == false) {
81  throw std::logic_error("non-unique numbered positional arg");
82  }
83  };
84 
85 
86  } // end of namespace internal
87 
88  parser* parser::gParser = nullptr;
89 
90  parser::parser(const std::string& aDescription, const std::string& aTrailer, const std::vector<std::string>& aSearchPaths):
91  lDescription(aDescription),
92  lTrailer(aTrailer),
93  lSearchPaths(aSearchPaths),
94  lParsingIsDone(false) {
95  if (gParser != nullptr) {
96  std::cerr << "there may be only one parser" << std::endl;
97  fComplainAndLeave(false);
98  }
99  gParser = this;
100  lErrorStream = &std::cerr;
101  lMessageStream = &std::cout;
102  lHelpReturnValue = 0;
105  }
107  gParser = nullptr;
108  }
109 
110  bool parser::fIsParsingDone() const {
111  return lParsingIsDone;
112  }
113 
114 
115  void parser::fSetMessageStream(std::ostream *aStream) {
116  lMessageStream = aStream;
117  }
118  void parser::fSetErrorStream(std::ostream *aStream) {
119  lErrorStream = aStream;
120  }
121  std::ostream& parser::fGetErrorStream() const {
122  return *lErrorStream;
123  }
124  void parser::fSetHelpReturnValue(int aValue) {
125  lHelpReturnValue = aValue;
126  }
127  void parser::fSetAssignmentChars(char aPrimary, char aSecondary) {
128  lPrimaryAssignment = aPrimary;
129  lSecondaryAssignment = aSecondary;
130  }
131  void parser::fSetExecutableName(const char *aName) {
132  lExecutableName = aName;
133  }
134 
135  void parser::fRequire(const base* aOption) {
136  lRequiredOptions.insert(aOption);
137  }
138  void parser::fRequire(std::vector<const base*> aOptions) {
139  lRequiredOptions.insert(aOptions.cbegin(), aOptions.cend());
140  }
141 
142 
143 
145  return gParser;
146  }
147 
149 
154  for (auto f : lSearchPaths) {
155  auto tildePosition = f.find_first_of('~');
156  if (tildePosition != std::string::npos) {
157  f.replace(tildePosition, 1, getenv("HOME"));
158  }
159  f += lProgName;
161  }
162  }
163 
164  const std::vector<std::string>& parser::fParse(int argc, char *argv[]) {
165  return fParse(argc, const_cast<const char**>(argv));
166  }
167 
168  const std::vector<std::string>& parser::fParse(int argc, const char *argv[]) {
169  if (lParsingIsDone) {
170  throw std::logic_error("parsing may be done only once");
171  }
172  lParsingIsDone = true; // we set this early, as of now now new options may be created
173  {
174  #ifdef IS_NONBROKEN_SYSTEM
175  auto buf = strdup(argv[0]);
176  lProgName = basename(buf);
177  free(buf);
178  #else
179  lProgName = argv[0];
180  #endif
181  }
182  bool firstOptionNotSeen = true;
183  try {
184  for (int i = 1; i < argc; i++) {
185  if (firstOptionNotSeen && !(argv[i][0] == '-' && argv[i][1] == '-' && internal::gOptionNoCfgFiles.lLongName.compare(argv[i] + 2) == 0)) {
187  }
188  if (argv[i][0] == '-' && argv[i][1] != '-') {
189  auto length = strlen(argv[i]);
190  for (unsigned int j = 1; j < length; j++) {
191  auto it = base::fGetShortOptionMap().find(argv[i][j]);
192  if (it == base::fGetShortOptionMap().end()) {
193  throw std::runtime_error(internal::conCat("unknown short option '", argv[i][j], "'"));
194  } else {
195  auto opt = it->second;
196  if (opt->lNargs > 0 && strlen(argv[i]) > 2) {
197  throw internal::optionError(opt, internal::conCat("run-together short options '", argv[i], "'may not use parameters"));
198  }
199  opt->fHandleOption(argc, argv, &i);
200  }
201  }
202  } else if (argv[i][0] == '-' && argv[i][1] == '-' && argv[i][2] != '-') {
203  auto length = strlen(argv[i]);
204  if (length == 2) { // end of options
205  for (i++; i < argc; i++) {
207  lUnusedOptions.push_back(argv[i]);
208  } else {
209  lStuffAfterMinusMinus.push_back(argv[i]);
210  }
211  }
212  break;
213  } else {
214  if (nullptr == strchr(argv[i], lPrimaryAssignment)) {
215  auto it = base::fGetOptionMap().find(argv[i] + 2);
216  if (it == base::fGetOptionMap().end()) {
217  throw std::runtime_error(internal::conCat("unknown long option '", argv[i], "'"));
218  }
219  auto opt = it->second;
220  opt->fHandleOption(argc, argv, &i);
221  } else {
222  auto buf = strdup(argv[i]);
223  auto equalsAt = strchr(buf, lPrimaryAssignment);
224  *equalsAt = '\0';
225  auto it = base::fGetOptionMap().find(buf + 2);
226  if (it == base::fGetOptionMap().end()) {
227  throw std::runtime_error(internal::conCat("unknown long option '", argv[i], "'"));
228  }
229  auto opt = it->second;
230  std::stringstream sbuf(equalsAt + 1);
232  opt->fCheckRange();
233  free(buf);
234  }
235  }
236  } else {
237  lUnusedOptions.push_back(argv[i]);
238  }
239  firstOptionNotSeen = false;
240  }
241  if (firstOptionNotSeen) { // read cfg files if no options set at all
243  }
244 
246  std::list<base*> positionalArgs;
248  positionalArgs.push_back(it.second);
249  }
250  while (!lUnusedOptions.empty()) {
251  auto opt = positionalArgs.front();
252  if (opt->fIsContainer()) { // process first options from the back
253  for (;;) {
254  auto opt2 = positionalArgs.back();
255  if (opt2 == opt) {
256  break;
257  }
258  positionalArgs.pop_back();
259  auto& arg = lUnusedOptions.back();
260  std::stringstream sbuf(arg);
261  opt2->fSetMe(sbuf, internal::sourceItem(&internal::sourceFile::gCmdLine, 0));
262  opt2->fCheckRange();
263  lUnusedOptions.pop_back();
264  if (lUnusedOptions.empty()) {
265  break;
266  }
267  }
268  }
269  if (lUnusedOptions.empty()) {
270  break;
271  }
272  auto& arg = lUnusedOptions.front();
273  std::stringstream sbuf(arg);
275  opt->fCheckRange();
276  lUnusedOptions.erase(lUnusedOptions.begin());
277  if (! opt->fIsContainer()) {
278  positionalArgs.pop_front();
279  if (positionalArgs.empty()) {
280  break;
281  }
282  }
283  }
284  }
285  } catch (const internal::rangeError& e) {
286  fGetErrorStream() << e.what() << " in option '" << e.fGetOption().fGetLongName() << "' value '" << e.fGetBadValue() << "'\n";
289  } catch (const internal::conversionError& e) {
290  fGetErrorStream() << e.what() << " in option '" << e.fGetOption().fGetLongName()
291  << "' cannot convert '" << e.fGetArgument() << "' to type '" << e.fGetType().name() << "'\n";
293  } catch (const internal::optionError& e) {
294  fGetErrorStream() << e.what() << " in option '" << e.fGetOption().fGetLongName() << "'\n";
296  } catch (std::exception& e) {
297  fGetErrorStream() << e.what() << "\n";
299  }
300 
301 
303  for (auto & it : base::fGetOptionMap()) {
304  auto opt = it.second;
305  fGetErrorStream() << "option " << opt->lLongName << " has value '";
306  opt->fWriteValue(fGetErrorStream());
307  fGetErrorStream() << "' ";
308  if (opt->lSource.fIsUnset()) {
309  fGetErrorStream() << " (default) ";
310  } else {
311  fGetErrorStream() << " from " << opt->lSource;
312  }
313  fGetErrorStream() << std::endl;
314  }
315  for (auto & unusedOption : lUnusedOptions) {
316  fGetErrorStream() << "unused option '" << unusedOption << "'" << std::endl;
317  }
318  }
320  return lUnusedOptions;
321  }
322 
324  std::set<const base*> optionsThatWereSet;
325  for (auto & it : base::fGetOptionMap()) {
326  auto opt = it.second;
327  if (opt->fIsSet()) {
328  optionsThatWereSet.insert(opt);
329  }
330  }
331  if (! lRequiredOptions.empty()) {
332  std::vector<const base*> missing;
333  std::set_difference(lRequiredOptions.cbegin(), lRequiredOptions.cend(),
334  optionsThatWereSet.cbegin(), optionsThatWereSet.cend(),
335  std::inserter(missing, missing.begin()));
336  if (! missing.empty()) {
337  fGetErrorStream() << "The following options are required but were not given:";
338  for (auto opt : missing) {
339  fGetErrorStream() << " " << opt->fGetLongName();
340  }
341  fGetErrorStream() << "\n";
343  }
344  }
345  for (auto opt : optionsThatWereSet) {
346  for (auto forbidden : opt->lForbiddenOptions) {
347  if (optionsThatWereSet.count(forbidden) != 0) {
348  fGetErrorStream() << "The option " << opt->fGetLongName() << " forbids the use of " << forbidden->fGetLongName() << " but it is given.\n";
350  }
351  }
352  for (auto required : opt->lRequiredOptions) {
353  if (optionsThatWereSet.count(required) == 0) {
354  fGetErrorStream() << "The option " << opt->fGetLongName() << " requires the use of " << required->fGetLongName() << " but it is not given.\n";
356  }
357  }
358  }
359  }
360 
362 
365  void parser::fComplainAndLeave(bool aWithHelp) {
366  if (aWithHelp) {
367  fGetErrorStream() << "\nFor complete usage: " << fGetProgName() << " --help\n";
368  }
369  exit(1);
370  }
371 
374  }
375 
376  void parser::fPrintEscapedString(std::ostream & aStream, const std::string& aString) {
377  bool delimit = aString.find_first_of(" \t,") != std::string::npos;
378  if (delimit) {
379  aStream << '\'';
380  }
381  for (auto c : aString) {
382  switch (c) {
383  case '\a':
384  aStream << "\\a";
385  break;
386  case '\b':
387  aStream << "\\b";
388  break;
389  case '\f':
390  aStream << "\\f";
391  break;
392  case '\n':
393  aStream << "\\n";
394  break;
395  case '\r':
396  aStream << "\\r";
397  break;
398  case '\t':
399  aStream << "\\t";
400  break;
401  case ' ':
402  aStream << "\\ ";
403  break;
404  case '\v':
405  aStream << "\\v";
406  break;
407  case '\\':
408  aStream << "\\\\";
409  break;
410  case '\'':
411  aStream << "\\\'";
412  break;
413  case '"':
414  aStream << "\\\"";
415  break;
416  default:
417  if (c >= ' ' && c < 127) {
418  aStream << c;
419  } else {
420  aStream << '\\' << std::oct << std::setw(3) << std::setfill('0') << static_cast<unsigned int>(c) << std::setfill(' ') << std::dec;
421  }
422  }
423  }
424  if (delimit) {
425  aStream << '\'';
426  }
427  }
428 
429  void parser::fReCaptureEscapedString(std::string& aDest, const std::string& aSource) {
430  std::stringstream buffer(aSource);
431  buffer >> aDest;
432  }
433  namespace escapedIO {
434 
435  std::istream& operator>> (std::istream &aStream, const char*& aCstring) {
436  auto buffer = new std::string;
437  aStream >> *buffer;
438  if (aStream.fail()) {
439  delete buffer;
440  } else { // leak a string...
441  aCstring = buffer->c_str();
442  }
443  return aStream;
444  }
445 
446 
447  std::ostream& operator<<(std::ostream& aStream, const std::string& aString) {
448  parser::fPrintEscapedString(aStream, aString);
449  return aStream;
450  }
451  std::istream& operator>>(std::istream& aStream, std::string& aString) {
452  std::istream::sentry s(aStream);
453  if (s) {
454  aString.clear();
455  auto delimiter = aStream.peek();
456  if (delimiter == '"' || delimiter == '\'') { // string is enclosed in a pair of delimiters
457  aStream.get();
458  } else {
459  delimiter = '\0';
460  }
461  while (! aStream.eof()) {
462  auto c = aStream.peek();
463  if (c == '\n' || aStream.eof()) {
464  break;
465  }
466  aStream.get();
467  if (c == '\\') {
468  c = aStream.get();
469  switch (c) {
470  case 'a':
471  aString.push_back('\a');
472  break;
473  case 'b':
474  aString.push_back('\b');
475  break;
476  case 'f':
477  aString.push_back('\f');
478  break;
479  case 'n':
480  aString.push_back('\n');
481  break;
482  case 'r':
483  aString.push_back('\r');
484  break;
485  case 't':
486  aString.push_back('\t');
487  break;
488  case 'v':
489  aString.push_back('\v');
490  break;
491  default:
492  if (c >= '0' && c <= '7') {
493  char ch = 0;
494  for (int i = 0; i < 3 && c >= '0' && c <= '7'; i++) {
495  ch = (ch << 3) | ((c - '0') & 0x7);
496  c = aStream.get();
497  }
498  aString.push_back(ch);
499  } else {
500  aString.push_back(c);
501  }
502  break;
503  }
504  } else if (c == delimiter) {
505  break;
506  } else {
507  aString.push_back(c);
508  }
509  }
510  }
511  auto eof = aStream.eof();
512  aStream.clear();
513  if (eof) {
514  aStream.setstate(std::ios_base::eofbit);
515  };
516  return aStream;
517  }
518  } // end of namespace escapedIO
519 
521 
529  base::base(char aShortName, const std::string& aLongName, const std::string& aExplanation, short aNargs) :
530  lShortName(aShortName),
531  lLongName(aLongName),
532  lExplanation(aExplanation),
533  lNargs(aNargs) {
534 
535  {
536  auto p = parser::fGetInstance();
537  if (p != nullptr) {
538  if (p->fIsParsingDone()) {
539  throw std::logic_error(internal::conCat(lLongName, " construction after parsing is done"));
540  }
541  }
542  }
543 
544  if (fGetOptionMap().emplace(lLongName, this).second == false) {
545  throw std::logic_error(internal::conCat(lLongName, " already registered"));
546  }
547  if (lShortName != '\0') {
548  if (fGetShortOptionMap().emplace(lShortName, this).second == false) {
549  throw std::logic_error(internal::conCat(lLongName, ": short name '", lShortName, " already registered"));
550  }
551  }
552  lPreserveWorthyStuff = nullptr;
553  }
555  fGetOptionMap().clear();
556  fGetShortOptionMap().clear();
557  delete lPreserveWorthyStuff;
558  }
559 
561  void base::fSetSource(const internal::sourceItem& aSource) {
562  lSource = aSource;
563  }
564 
565 
566  void base::fHandleOption(int argc, const char *argv[], int *i) {
567  if (*i + lNargs >= argc) {
568  throw internal::optionError(this, internal::conCat(lNargs, " args needed, but only ", argc - *i - 1 , " remain."));
569  }
570  if (lNargs == 0) {
572  } else if (lNargs == 1) {
573  std::stringstream sbuf(argv[*i + 1]);
575  *i += lNargs;
576  }
577  fCheckRange();
578  }
579 
580  void base::fWriteCfgLines(std::ostream & aStream, const char *aPrefix) const {
581  aStream << aPrefix << lLongName << "=";
582  auto asOriginalStringKeeper = dynamic_cast<const originalStringKeeper*>(this);
583  if (asOriginalStringKeeper) {
584  asOriginalStringKeeper->fWriteOriginalString(aStream);
585  } else {
586  fWriteValue(aStream);
587  }
588  aStream << "\n";
589  }
590 
591  void base::fSetPreserveWorthyStuff(std::vector<std::string>* aStuff) {
592  lPreserveWorthyStuff = aStuff;
593  }
594 
596  void base::fHide() {
597  lHidden = true;
598  }
599 
600  bool base::fIsHidden() const {
601  return lHidden;
602  }
603 
605  void base::fDisable() {
606  fHide(); // needed to hide forbidden options
607  fGetOptionMap().erase(lLongName);
609  }
610 
611  void base::fRequire(const base* aOtherOption) {
612  lRequiredOptions.push_back(aOtherOption);
613  }
614  void base::fRequire(std::vector<const base*> aOtherOptions) {
615  lRequiredOptions.insert(lRequiredOptions.end(), aOtherOptions.cbegin(), aOtherOptions.cend());
616  }
617  void base::fForbid(const base* aOtherOption) {
618  lForbiddenOptions.push_back(aOtherOption);
619  }
620  void base::fForbid(std::vector<const base*> aOtherOptions) {
621  lForbiddenOptions.insert(lForbiddenOptions.end(), aOtherOptions.cbegin(), aOtherOptions.cend());
622  }
623 
624  namespace internal {
626  template <typename T> class supressed : public single<T> {
627  public:
628  template <class ... Types> supressed (Types ... args) :
629  single<T>(args...) {
630  };
631  void fWriteCfgLines(std::ostream& aStream, const char */*aPrefix*/) const override {
632  single<T>::fWriteCfgLines(aStream, "# ");
633  }
634  };
635 
637  class NoCfgFileRecursion: public supressed<bool> {
638  public:
640  supressed('\0', "noCfgFileRecursion", "do not read config files recursively, must be set before use") {
641  };
642  };
644  } // end namespace internal
645 
646  void parser::fPrintOptionHelp(std::ostream& aMessageStream, const base& aOption, std::size_t aMaxName, std::size_t aMaxExplain, size_t lineLenght) const {
647  std::size_t fullNameLength = 8 + aMaxName;
648  std::size_t descLength = std::min(lineLenght - 20 - fullNameLength, aMaxExplain);
649  if (aOption.lShortName != '\0') {
650  aMessageStream << " -" << aOption.lShortName << ", ";
651  } else {
652  aMessageStream << " ";
653  }
654  auto explanation = aOption.lExplanation;
655  bool firstLine = true;
656  do {
657  if (firstLine) {
658  aMessageStream << "--" << std::setw(aMaxName) << std::left << aOption.lLongName
659  << " ";
660  } else {
661  aMessageStream << " " << std::setw(aMaxName) << " " << " ";
662  }
663  if (explanation.length() > descLength) {
664  // Need to linebreak.
665  bool keepBrokenChar = false;
666  bool hardBreak = false;
667  auto breakPos = explanation.find_last_of(" -", descLength - 1);
668  if (breakPos == std::string::npos) {
669  // No space, use hard break. In this case, keep the broken character.
670  // -2 since we add a "-" separator for readability.
671  breakPos = descLength - 2;
672  hardBreak = true;
673  keepBrokenChar = true;
674  } else {
675  // We also want to keep the broken character in case it's not a whitespace.
676  if (explanation[breakPos] != ' ') {
677  breakPos++;
678  keepBrokenChar = true;
679  }
680  }
681  aMessageStream << std::setw(descLength) << std::left << explanation.substr(0, breakPos) + (hardBreak ? "-" : "");
682  explanation = explanation.substr(breakPos + (keepBrokenChar ? 0 : 1));
683  } else {
684  aMessageStream << std::setw(descLength) << std::left << explanation;
685  // Trigger delayed break.
686  explanation.clear();
687  }
688  if (firstLine) {
689  aMessageStream << " default: ";
690  aOption.fWriteValue(aMessageStream);
691  firstLine = false;
692  }
693  aMessageStream << "\n";
694  } while (explanation.length() > 0);
695  if (std::count_if(aOption.lRequiredOptions.cbegin(), aOption.lRequiredOptions.cend(),
696  [](const base * opt) {
697  return ! opt->fIsHidden();
698  })) {
699  aMessageStream << " " << std::setw(aMaxName) << " " << " Requires the following other options:\n";
700  for (auto opt : aOption.lRequiredOptions) {
701  if (opt->fIsHidden()) {
702  continue;
703  }
704  aMessageStream << " " << std::setw(aMaxName) << " " << " " << opt->fGetLongName() << "\n";
705  }
706  }
707  if (std::count_if(aOption.lForbiddenOptions.cbegin(), aOption.lForbiddenOptions.cend(),
708  [](const base * opt) {
709  return ! opt->fIsHidden();
710  })) {
711  aMessageStream << " " << std::setw(aMaxName) << " " << " Forbids the following other options:\n";
712  for (auto opt : aOption.lForbiddenOptions) {
713  if (opt->fIsHidden()) {
714  continue;
715  }
716  aMessageStream << " " << std::setw(aMaxName) << " " << " " << opt->fGetLongName() << "\n";
717  }
718  }
719  if (lRequiredOptions.count(&aOption)) {
720  aMessageStream << " " << std::setw(aMaxName) << " " << " this option is required\n";
721  }
722  }
723 
724  void parser::fHelp() {
725  *lMessageStream << lProgName << ": " << lDescription << "\n";
726 
728  *lMessageStream << "Usage: " << lProgName << " [option]...";
729  for (const auto& it : internal::positional_base::fGetPositonalArgs()) {
730  *lMessageStream << " [" << it.second->fGetLongName() << "]";
731  if (it.second->fIsContainer()) {
732  *lMessageStream << "...";
733  }
734  }
735  *lMessageStream << "\n";
736  }
737 
738  size_t maxName = 0;
739  size_t maxExplain = 0;
740  size_t lineLenght = 132;
741  #ifdef IS_NONBROKEN_SYSTEM
742  {
743  struct winsize window;
744  if (ioctl(0, TIOCGWINSZ, &window) == 0) {
745  lineLenght = window.ws_col;
746  }
747  }
748  #endif
749  for (auto & it : base::fGetOptionMap()) {
750  const auto opt = it.second;
751  maxName = std::max(opt->lLongName.length(), maxName);
752  maxExplain = std::max(opt->lExplanation.length(), maxExplain);
753  }
754  for (auto & it : base::fGetOptionMap()) {
755  const auto opt = it.second;
756  if (! opt->fIsHidden()) {
757  fPrintOptionHelp(*lMessageStream, *opt, maxName, maxExplain, lineLenght);
758  }
759  }
760  if (!lSearchPaths.empty()) {
761  *lMessageStream << "Looking for config files in ";
762  for (const auto & searchPath : lSearchPaths) {
763  *lMessageStream << searchPath << " ";
764  }
765  *lMessageStream << "\n";
766  }
767 
769  *lMessageStream << std::endl;
770  }
771  void parser::fWriteCfgFile(const std::string& aFileName) {
772  std::ofstream cfgFile(aFileName, std::ofstream::out | std::ofstream::trunc);
773  if (lExecutableName.empty()) {
774  #ifdef IS_NONBROKEN_SYSTEM
775  char buf[128];
776  auto result = readlink("/proc/self/exe", buf, sizeof(buf));
777  if (result > 0 && result < 128 - 2 - 14) {
778  cfgFile << "#!" << buf << " --readCfgFile\n";
779  }
780  #endif
781  } else {
782  cfgFile << "#!" << lExecutableName << " --readCfgFile\n";
783  }
784  {
785  cfgFile << "# written ";
786  std::time_t t = std::time(nullptr);
787  char mbstr[100];
788  if (std::strftime(mbstr, sizeof(mbstr), "%Y-%m-%d %H:%M:%S", std::localtime(&t))) {
789  cfgFile << mbstr;
790  }
791  cfgFile << " by " << getenv("USER") << " using " << lProgName << "\n";
792  cfgFile << "# only comments started by ## will be preserved on a re-write!\n";
793  cfgFile << "# to rewrite this file for a different executable location, use:\n";
794  cfgFile << "# " << lProgName << " --noCfgFileRecursion --readCfgFile " << aFileName << " --writeCfgFile " << aFileName << "\n";
795  cfgFile << "# Assuming " << lProgName << " is in your PATH\n";
796  }
797  for (auto & it : base::fGetOptionMap()) {
798  const auto opt = it.second;
799  if (opt->lPreserveWorthyStuff != nullptr) {
800  for (const auto& line : * (opt->lPreserveWorthyStuff)) {
801  cfgFile << "\n" << line;
802  }
803  }
804  cfgFile << "\n# " << opt->lExplanation << "\n";
805  auto prefix = "";
806  if (opt->lSource.fIsUnset()) {
807  prefix = "# ";
808  }
809  opt->fWriteCfgLines(cfgFile, prefix);
810  opt->fWriteRange(cfgFile);
811  if (!opt->lSource.fIsUnset()) {
812  cfgFile << "# set from " << opt->lSource << "\n";
813  }
814  }
815  cfgFile << "#\n";
816  cfgFile.close();
817  }
818 
819  void parser::fReadCfgFile(const std::string& aFileName, const internal::sourceItem& aSource, bool aMayBeAbsent) {
820  std::ifstream cfgFile(aFileName);
821  if (!cfgFile.good() && !(aMayBeAbsent && errno == ENOENT)) {
822  throw std::system_error(errno, std::system_category(),
823  internal::conCat("can't acccess config file '", aFileName , "'."));
824  }
825  auto sourceF = new internal::sourceFile(aFileName, *(aSource.fGetFile()));
826  int lineNumber = 0;
827  std::vector<std::string>* preserveWorthyStuff = nullptr;
828  bool hideNextOption = false;
829  bool disableNextOption = false;
830  try {
831  while (cfgFile.good()) {
832  std::string line;
833  std::getline(cfgFile, line);
834  lineNumber++;
835  internal::sourceItem source(sourceF, lineNumber);
836  if (line.length() == 0) {
837  hideNextOption = false;
838  disableNextOption = false;
839  continue;
840  } else if (line[0] == '#') {
841  if (line[1] == '#') {
842  if (preserveWorthyStuff == nullptr) {
843  preserveWorthyStuff = new std::vector<std::string>;
844  }
845  preserveWorthyStuff->push_back(line);
846  if (line == "## hide") {
847  hideNextOption = true;
848  } else if (line == "## disable" && ! internal::gNoCfgFileRecursion) {
849  disableNextOption = true;
850  }
851  } else if (preserveWorthyStuff != nullptr) {
852  auto equalsAt = line.find_first_of('=');
853  if (equalsAt != std::string::npos) {
854  auto optionName = line.substr(2, equalsAt - 2);
855  auto it = base::fGetOptionMap().find(optionName);
856  if (it != base::fGetOptionMap().end()) {
857  auto option = it->second;
858  option->fSetPreserveWorthyStuff(preserveWorthyStuff);
859  preserveWorthyStuff = nullptr;
860  if (hideNextOption) {
861  option->fHide();
862  }
863  if (disableNextOption) {
864  option->fDisable();
865  }
866  }
867  }
868  }
869  continue;
870  }
871  auto equalsAt = line.find_first_of('=');
872  if (equalsAt == std::string::npos || equalsAt < 1 || equalsAt == line.length()) {
873  std::string buf;
874  fReCaptureEscapedString(buf, line);
875  lUnusedOptions.push_back(buf);
876  continue;
877  }
878  auto optionName = line.substr(0, equalsAt);
879  auto it = base::fGetOptionMap().find(optionName);
880  if (it == base::fGetOptionMap().end()) {
881  throw std::runtime_error(internal::conCat("unknown option '", optionName, "'"));
882  }
883  auto option = it->second;
884  if (hideNextOption) {
885  option->fHide();
886  }
887  if (disableNextOption) {
888  option->fDisable();
889  }
890  {
891  std::stringstream sbuf(line.substr(equalsAt + 1));
892  option->fSetMe(sbuf, source);
893  if (preserveWorthyStuff != nullptr) {
894  option->fSetPreserveWorthyStuff(preserveWorthyStuff);
895  preserveWorthyStuff = nullptr;
896  }
897  option->fCheckRange();
898  }
899  }
900  } catch (const std::exception& e) {
901  fGetErrorStream() << aFileName << ":" << lineNumber << ": error: " << e.what() << "\n";
902  throw;
903  }
904 
905  cfgFile.close();
906  }
907 
908 
914 
915 
916  void single<bool>::fWriteValue(std::ostream & aStream) const {
917  aStream << std::boolalpha << lValue;
918  }
920  lValue = ! lDefault;
921  fSetSource(aSource);
922  }
923  void single<bool>::fSetMe(std::istream& aStream, const internal::sourceItem& aSource) {
924  aStream >> std::boolalpha >> lValue;
925  fSetSource(aSource);
926  }
927  void single<bool>::fAddDefaultFromStream(std::istream& aStream) {
928  aStream >> std::boolalpha >> lValue;
929  lDefault = lValue;
930  }
931 
932  namespace internal { // put clases and options for internal use
933  // into internal namespace
934 
936  class OptionHelp : public single<bool> {
937  public:
939  single('h', "help", "give this help") {
940  }
941  void fSetMeNoarg(const sourceItem& /*aSource*/) override {
943  exit(parser::fGetInstance()->fGetHelpReturnValue());
944  }
945  void fSetMe(std::istream& /*aArg*/, const sourceItem& aSource) override {
946  fSetMeNoarg(aSource);
947  }
948  };
950 
951 
952 
953 
955  class OptionWriteCfgFile : public supressed<std::string> {
956  public:
958  supressed('\0', "writeCfgFile", "write a config file", "") {
959  }
960  void fSetMe(std::istream& aStream, const sourceItem&/* aSource */) override {
961  aStream >> *this;
963  exit(parser::fGetInstance()->fGetHelpReturnValue());
964  }
965  };
967 
969  template <bool mayBeMissing> class OptionReadCfgFile : public single<std::string> {
970  public:
971  OptionReadCfgFile(const char *name, const char *description):
972  single('\0', name, description, "") {
973  }
974  void fSetMe(std::istream& aStream, const sourceItem& aSource) override {
975  single<std::string>::fSetMe(aStream, aSource);
976  if (gNoCfgFileRecursion && aSource.fGetFile() != &sourceFile::gCmdLine) {
977  return;
978  }
979  parser::fGetInstance()->fReadCfgFile(*this, aSource, mayBeMissing);
980  };
981  void fWriteCfgLines(std::ostream& aStream, const char */*aPrefix*/) const override {
983  };
984  };
985  static OptionReadCfgFile<false> gReadCfgFile("readCfgFile", "read a config file");
986  static OptionReadCfgFile<true> gReadCfgFileIfThere("readCfgFileIfThere", "read a config file if it's there");
987  } // end of name internal
988 
989  void originalStringKeeper::fWriteOriginalString(std::ostream& aStream) const {
990  aStream << lOriginalString;
991  }
992 
993 } // end of namespace options
options::parser::fHelp
void fHelp()
print help, normally automatically called by the –help option or in case of problems.
Definition: Options.cpp:724
options::parser::lMinusMinusJustEndsOptions
bool lMinusMinusJustEndsOptions
Definition: Options.h:374
options::parser::fPrintEscapedString
static void fPrintEscapedString(std::ostream &aStream, const std::string &aString)
Definition: Options.cpp:376
options::internal::NoCfgFileRecursion
standard option to suppress parsing of config files within config files
Definition: Options.cpp:637
options::base::fSetPreserveWorthyStuff
void fSetPreserveWorthyStuff(std::vector< std::string > *aStuff)
Definition: Options.cpp:591
options::base::fSetMeNoarg
virtual void fSetMeNoarg(const internal::sourceItem &)
Definition: Options.h:218
options::parser::lMessageStream
std::ostream * lMessageStream
Definition: Options.h:375
options::internal::OptionReadCfgFile::fWriteCfgLines
void fWriteCfgLines(std::ostream &aStream, const char *) const override
Definition: Options.cpp:981
options::parser
class that contains the parser, i.e. does that option handling
Definition: Options.h:363
options::single
generic option class with any type that can be used with std::istream and std::ostream
Definition: Options.h:533
options::parser::fWriteCfgFile
void fWriteCfgFile(const std::string &aFileName)
Definition: Options.cpp:771
options::parser::lUnusedOptions
std::vector< std::string > lUnusedOptions
Definition: Options.h:369
options::parser::fIsParsingDone
bool fIsParsingDone() const
Definition: Options.cpp:110
options::internal::rangeError::fGetBadValue
const std::string & fGetBadValue() const
Definition: Options.h:295
options::parser::lExecutableName
std::string lExecutableName
Definition: Options.h:378
options::internal::OptionHelp::fSetMeNoarg
void fSetMeNoarg(const sourceItem &) override
Definition: Options.cpp:941
options::internal::rangeError
Definition: Options.h:288
options::internal::conversionError
Definition: Options.h:310
options::base::fGetLongName
const std::string & fGetLongName() const
returns long name of option, usually only for internal use.
Definition: Options.h:273
options::originalStringKeeper
interface class that is used for options where the original string rather than the streamed value is ...
Definition: Options.h:864
options::parser::fParse
const std::vector< std::string > & fParse(int argc, const char *argv[])
parse the options on the command line
Definition: Options.cpp:168
options::internal::OptionHelp::OptionHelp
OptionHelp()
Definition: Options.cpp:938
options::internal::sourceItem::fGetFile
decltype(lFile) fGetFile() const
Definition: Options.h:64
options::parser::lStuffAfterMinusMinus
std::vector< std::string > lStuffAfterMinusMinus
Definition: Options.h:370
options::internal::OptionWriteCfgFile
special derived class used to write out config files
Definition: Options.cpp:955
options::parser::lHelpReturnValue
int lHelpReturnValue
Definition: Options.h:379
options::parser::lSearchPaths
const std::vector< std::string > lSearchPaths
Definition: Options.h:368
options::parser::gParser
static parser * gParser
Definition: Options.h:365
options::originalStringKeeper::lOriginalString
std::string lOriginalString
Definition: Options.h:866
options::parser::lPrimaryAssignment
char lPrimaryAssignment
Definition: Options.h:380
options::internal::OptionHelp::fSetMe
void fSetMe(std::istream &, const sourceItem &aSource) override
function to set the value from a string, remembering the source
Definition: Options.cpp:945
options::base::lLongName
const std::string lLongName
Definition: Options.h:206
options::internal::supressed
special class for options which never have a value setting in cfg files
Definition: Options.cpp:626
options::internal::gWriteCfgFile
static OptionWriteCfgFile gWriteCfgFile
Definition: Options.cpp:966
options::internal::positional_base::positional_base
positional_base(int aOrderingNumber, base *aAsBase)
Definition: Options.cpp:70
options::parser::fReadConfigFiles
void fReadConfigFiles()
read config files if present
Definition: Options.cpp:153
options::internal::NoCfgFileRecursion::NoCfgFileRecursion
NoCfgFileRecursion()
Definition: Options.cpp:639
options::internal::OptionReadCfgFile::OptionReadCfgFile
OptionReadCfgFile(const char *name, const char *description)
Definition: Options.cpp:971
options::parser::fPrintOptionHelp
void fPrintOptionHelp(std::ostream &aMessageStream, const base &aOption, std::size_t aMaxName, std::size_t aMaxExplain, size_t lineLenght) const
Definition: Options.cpp:646
options::base::lPreserveWorthyStuff
std::vector< std::string > * lPreserveWorthyStuff
Definition: Options.h:211
options::base
base class for options
Definition: Options.h:193
options::parser::lProgName
std::string lProgName
Definition: Options.h:377
options::internal::conversionError::fGetArgument
const std::string & fGetArgument() const
Definition: Options.h:320
options::base::lSource
internal::sourceItem lSource
Definition: Options.h:208
options
Definition: Options.h:33
Options.h
options::base::~base
virtual ~base()
Definition: Options.cpp:554
options::parser::fSetErrorStream
void fSetErrorStream(std::ostream *aStream)
Definition: Options.cpp:118
options::base::fWriteCfgLines
virtual void fWriteCfgLines(std::ostream &aStream, const char *aPrefix) const
Definition: Options.cpp:580
options::single::fSetMe
void fSetMe(std::istream &aStream, const internal::sourceItem &aSource) override
function to set the value from a string, remembering the source
Definition: Options.h:578
options::base::fForbid
virtual void fForbid(const base *aOtherOption)
forbid aOtherOption when this option is set
Definition: Options.cpp:617
options::base::fDisable
void fDisable()
disable option by removing it from the maps
Definition: Options.cpp:605
options::internal::gReadCfgFileIfThere
static OptionReadCfgFile< true > gReadCfgFileIfThere("readCfgFileIfThere", "read a config file if it's there")
options::base::fHide
void fHide()
hide option, will be respected by help
Definition: Options.cpp:596
options::parser::fReadCfgFile
void fReadCfgFile(const std::string &aFileName, const options::internal::sourceItem &aSource, bool aMayBeAbsent=false)
Definition: Options.cpp:819
options::parser::lDescription
const std::string lDescription
Definition: Options.h:366
options::parser::lSecondaryAssignment
char lSecondaryAssignment
Definition: Options.h:381
options::escapedIO::operator>>
std::istream & operator>>(std::istream &aStream, const char *&aCstring)
Definition: Options.cpp:435
options::base::lRequiredOptions
std::vector< const base * > lRequiredOptions
Definition: Options.h:213
options::internal::gOptionNoCfgFiles
static single< bool > gOptionNoCfgFiles('\0', "noCfgFiles", "do not read the default config files, must be FIRST option")
standard option to suppress parsing of config files
options::internal::gOptionDebugOptions
static single< bool > gOptionDebugOptions('\0', "debugOptions", "give debug output to option parsing")
standard option for producing debug output about the options
options::base::fRequire
virtual void fRequire(const base *aOtherOption)
require aOtherOption when this option is set
Definition: Options.cpp:611
options::base::fWriteRange
virtual void fWriteRange(std::ostream &) const
Definition: Options.h:226
options::base::base
base(char aShortName, const std::string &aLongName, const std::string &aExplanation, short aNargs)
construct an object of type base
Definition: Options.cpp:529
options::internal::gHelp
static OptionHelp gHelp
Definition: Options.cpp:949
options::internal::conversionError::fGetType
const std::type_info & fGetType() const
Definition: Options.h:323
options::internal::gReadCfgFile
static OptionReadCfgFile< false > gReadCfgFile("readCfgFile", "read a config file")
options::internal::gNoCfgFileRecursion
static NoCfgFileRecursion gNoCfgFileRecursion
Definition: Options.cpp:643
options::internal::sourceFile::gUnsetSource
static const sourceFile gUnsetSource
Definition: Options.h:41
options::parser::fReCaptureEscapedString
static void fReCaptureEscapedString(std::string &aDest, const std::string &aSource)
Definition: Options.cpp:429
options::escapedIO::operator<<
std::ostream & operator<<(std::ostream &aStream, const std::string &aString)
Definition: Options.cpp:447
options::internal::OptionHelp
special derived class used to give help
Definition: Options.cpp:936
options::parser::fSetAssignmentChars
void fSetAssignmentChars(char aPrimary='=', char aSecondary=':')
Definition: Options.cpp:127
options::internal::OptionReadCfgFile
special derived class used to read in config files
Definition: Options.cpp:969
options::internal::OptionReadCfgFile::fSetMe
void fSetMe(std::istream &aStream, const sourceItem &aSource) override
function to set the value from a string, remembering the source
Definition: Options.cpp:974
options::single::fWriteValue
void fWriteValue(std::ostream &aStream) const override
write textual representation of value to a std::ostream
Definition: Options.h:569
options::base::lExplanation
const std::string lExplanation
Definition: Options.h:207
options::parser::lErrorStream
std::ostream * lErrorStream
Definition: Options.h:376
options::originalStringKeeper::fWriteOriginalString
void fWriteOriginalString(std::ostream &aStream) const
Definition: Options.cpp:989
options::internal::sourceFile::gCmdLine
static const sourceFile gCmdLine
Definition: Options.h:42
options::base::fWriteValue
virtual void fWriteValue(std::ostream &aStream) const =0
write textual representation of value to a std::ostream
options::internal::optionError
Definition: Options.h:279
options::parser::fGetErrorStream
std::ostream & fGetErrorStream() const
Definition: Options.cpp:121
options::base::fSetMe
virtual void fSetMe(std::istream &aStream, const internal::sourceItem &aSource)=0
function to set the value from a string, remembering the source
options::parser::fSetHelpReturnValue
void fSetHelpReturnValue(int aValue)
Definition: Options.cpp:124
options::operator<<
std::ostream & operator<<(std::ostream &aStream, const internal::sourceItem &aItem)
operator to write sourceItems to a stream
Definition: Options.cpp:57
options::base::fIsContainer
virtual bool fIsContainer() const
Definition: Options.h:267
options::internal::supressed::fWriteCfgLines
void fWriteCfgLines(std::ostream &aStream, const char *) const override
Definition: Options.cpp:631
options::parser::~parser
~parser()
Definition: Options.cpp:106
options::parser::fGetProgName
const std::string & fGetProgName() const
Definition: Options.h:436
options::base::lShortName
char lShortName
Definition: Options.h:203
options::parser::fSetMinusMinusStartsExtraList
void fSetMinusMinusStartsExtraList()
switch on use of – to separate a trailer on the command line that is not to be parsed
Definition: Options.cpp:372
options::internal::conCat
std::string conCat(const Args &... args)
Definition: Options.h:338
options::internal::optionError::offendingOption
const base & offendingOption
Definition: Options.h:281
options::internal::positional_base::fGetPositonalArgs
static std::map< int, base * > & fGetPositonalArgs()
Definition: Options.h:349
options::base::fGetOptionMap
static std::map< std::string, base * > & fGetOptionMap()
Definition: Options.h:196
options::parser::fCheckConsistency
void fCheckConsistency()
Definition: Options.cpp:323
options::internal::sourceItem
class to remember from which line (or item) of a file/line an option was set from
Definition: Options.h:53
options::parser::lTrailer
const std::string lTrailer
Definition: Options.h:367
options::internal::sourceItem::fGetLineNumber
decltype(lLineNumber) fGetLineNumber() const
Definition: Options.h:67
options::parser::fGetInstance
static parser * fGetInstance()
get the only allwed instance of the option parser.
Definition: Options.cpp:144
options::parser::lParsingIsDone
bool lParsingIsDone
Definition: Options.h:383
options::internal::optionError::fGetOption
const base & fGetOption() const
Definition: Options.cpp:50
options::internal::OptionWriteCfgFile::OptionWriteCfgFile
OptionWriteCfgFile()
Definition: Options.cpp:957
options::base::fGetShortOptionMap
static std::map< char, base * > & fGetShortOptionMap()
Definition: Options.h:200
options::internal::optionError::optionError
optionError(const base *aOffendingOption, const std::string &aWhat)
Definition: Options.cpp:44
options::parser::fComplainAndLeave
virtual void fComplainAndLeave(bool aWithHelp=true)
print help (if required) and exit.
Definition: Options.cpp:365
options::parser::lRequiredOptions
std::set< const base * > lRequiredOptions
Definition: Options.h:372
options::parser::parser
parser(const std::string &aDescription="", const std::string &aTrailer="", const std::vector< std::string > &aSearchPaths={"/etc/", "~/.", "~/.config/", "./."})
Definition: Options.cpp:90
f
int f(int a, int line)
Definition: cctest.cpp:4
options::base::fCheckRange
virtual void fCheckRange() const =0
options::single::fAddDefaultFromStream
void fAddDefaultFromStream(std::istream &aStream) override
special for use in the shellScriptOptionParser
Definition: Options.h:564
options::base::lNargs
short lNargs
Definition: Options.h:209
options::parser::fSetMessageStream
void fSetMessageStream(std::ostream *aStream)
Definition: Options.cpp:115
options::base::fIsHidden
bool fIsHidden() const
Definition: Options.cpp:600
options::base::fSetSource
virtual void fSetSource(const internal::sourceItem &aSource)
remember the source that provided the value, e.g. commandline or a config file
Definition: Options.cpp:561
options::internal::sourceFile
class to remember the file (or cmd line) an option was set from
Definition: Options.h:36
options::internal::OptionWriteCfgFile::fSetMe
void fSetMe(std::istream &aStream, const sourceItem &) override
function to set the value from a string, remembering the source
Definition: Options.cpp:960
options::parser::fSetExecutableName
void fSetExecutableName(const char *aName)
Definition: Options.cpp:131
options::internal::supressed::supressed
supressed(Types ... args)
Definition: Options.cpp:628
options::base::lForbiddenOptions
std::vector< const base * > lForbiddenOptions
Definition: Options.h:214
options::base::lHidden
bool lHidden
Definition: Options.h:210
options::base::fHandleOption
virtual void fHandleOption(int argc, const char *argv[], int *i)
Definition: Options.cpp:566
options::parser::fRequire
virtual void fRequire(const base *aOtherOption)
Definition: Options.cpp:135