20 #include <sys/types.h> 
   21 #ifdef IS_NONBROKEN_SYSTEM 
   23 #include <sys/ioctl.h> 
   32 #include <system_error> 
   45             std::runtime_error(aWhat),
 
   46             offendingOption(*aOffendingOption) {}
 
   48             std::runtime_error(aWhat),
 
   49             offendingOption(*aOffendingOption) {}
 
   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");
 
   74                     if (it.second->fIsContainer()) {
 
   75                         throw std::logic_error(
"only one container type option allowed");
 
   80             if (result.second == 
false) {
 
   81                 throw std::logic_error(
"non-unique numbered positional arg");
 
   90     parser::parser(
const std::string& aDescription, 
const std::string& aTrailer, 
const std::vector<std::string>& aSearchPaths):
 
   91         lDescription(aDescription),
 
   93         lSearchPaths(aSearchPaths),
 
   94         lParsingIsDone(false) {
 
   96             std::cerr << 
"there may be only one parser" << std::endl;
 
  155             auto tildePosition = 
f.find_first_of(
'~');
 
  156             if (tildePosition != std::string::npos) {
 
  157                 f.replace(tildePosition, 1, getenv(
"HOME"));
 
  165         return fParse(argc, 
const_cast<const char**
>(argv));
 
  170             throw std::logic_error(
"parsing may be done only once");
 
  174             #ifdef IS_NONBROKEN_SYSTEM 
  175             auto buf = strdup(argv[0]);
 
  182         bool firstOptionNotSeen = 
true;
 
  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)) {
 
  188                 if (argv[i][0] == 
'-' && argv[i][1] != 
'-') {
 
  189                     auto length = strlen(argv[i]);
 
  190                     for (
unsigned int j = 1; j < length; j++) {
 
  193                             throw std::runtime_error(
internal::conCat(
"unknown short option '", argv[i][j], 
"'"));
 
  195                             auto opt = it->second;
 
  196                             if (opt->lNargs > 0 && strlen(argv[i]) > 2) {
 
  199                             opt->fHandleOption(argc, argv, &i);
 
  202                 } 
else if (argv[i][0] == 
'-' && argv[i][1] == 
'-' && argv[i][2] != 
'-') {
 
  203                     auto length = strlen(argv[i]);
 
  205                         for (i++; i < argc; i++) {
 
  217                                 throw std::runtime_error(
internal::conCat(
"unknown long option '", argv[i], 
"'"));
 
  219                             auto opt = it->second;
 
  220                             opt->fHandleOption(argc, argv, &i);
 
  222                             auto buf = strdup(argv[i]);
 
  227                                 throw std::runtime_error(
internal::conCat(
"unknown long option '", argv[i], 
"'"));
 
  229                             auto opt = it->second;
 
  230                             std::stringstream sbuf(equalsAt + 1);
 
  239                 firstOptionNotSeen = 
false;
 
  241             if (firstOptionNotSeen) { 
 
  246                 std::list<base*> positionalArgs;
 
  248                     positionalArgs.push_back(it.second);
 
  251                     auto opt = positionalArgs.front();
 
  252                     if (opt->fIsContainer()) { 
 
  254                             auto opt2 = positionalArgs.back();
 
  258                             positionalArgs.pop_back();
 
  260                             std::stringstream sbuf(arg);
 
  273                     std::stringstream sbuf(arg);
 
  277                     if (! opt->fIsContainer()) {
 
  278                         positionalArgs.pop_front();
 
  279                         if (positionalArgs.empty()) {
 
  296         } 
catch (std::exception& e) {
 
  304                 auto opt = it.second;
 
  308                 if (opt->lSource.fIsUnset()) {
 
  316                 fGetErrorStream() << 
"unused option '" << unusedOption << 
"'" << std::endl;
 
  324         std::set<const base*> optionsThatWereSet;
 
  326             auto opt = it.second;
 
  328                 optionsThatWereSet.insert(opt);
 
  332             std::vector<const base*> missing;
 
  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) {
 
  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";
 
  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";
 
  377         bool delimit = aString.find_first_of(
" \t,") != std::string::npos;
 
  381         for (
auto c : aString) {
 
  417                     if (c >= 
' ' && c < 127) {
 
  420                         aStream << '\\' << std::oct << std::setw(3) << std::setfill('0') << static_cast<unsigned int>(c) << std::setfill(
' ') << std::dec;
 
  430         std::stringstream buffer(aSource);
 
  433     namespace escapedIO {
 
  435         std::istream& 
operator>> (std::istream &aStream, 
const char*& aCstring) {
 
  436             auto buffer = 
new std::string;
 
  438             if (aStream.fail()) {
 
  441                 aCstring = buffer->c_str();
 
  447         std::ostream& 
operator<<(std::ostream& aStream, 
const std::string& aString) {
 
  451         std::istream& 
operator>>(std::istream& aStream, std::string& aString) {
 
  452             std::istream::sentry s(aStream);
 
  455                 auto delimiter = aStream.peek();
 
  456                 if (delimiter == 
'"' || delimiter == 
'\'') { 
 
  461                 while (! aStream.eof()) {
 
  462                     auto c = aStream.peek();
 
  463                     if (c == 
'\n' || aStream.eof()) {
 
  471                                 aString.push_back(
'\a');
 
  474                                 aString.push_back(
'\b');
 
  477                                 aString.push_back(
'\f');
 
  480                                 aString.push_back(
'\n');
 
  483                                 aString.push_back(
'\r');
 
  486                                 aString.push_back(
'\t');
 
  489                                 aString.push_back(
'\v');
 
  492                                 if (c >= 
'0' && c <= 
'7') {
 
  494                                     for (
int i = 0; i < 3 && c >= 
'0' && c <= 
'7'; i++) {
 
  495                                         ch = (ch << 3) | ((c - 
'0') & 0x7);
 
  498                                     aString.push_back(ch);
 
  500                                     aString.push_back(c);
 
  504                     } 
else if (c == delimiter) {
 
  507                         aString.push_back(c);
 
  511             auto eof = aStream.eof();
 
  514                 aStream.setstate(std::ios_base::eofbit);
 
  529     base::base(
char aShortName, 
const std::string&  aLongName, 
const std::string&  aExplanation, 
short aNargs) :
 
  530         lShortName(aShortName),
 
  531         lLongName(aLongName),
 
  532         lExplanation(aExplanation),
 
  538                 if (p->fIsParsingDone()) {
 
  567         if (*i + 
lNargs >= argc) {
 
  573             std::stringstream sbuf(argv[*i + 1]);
 
  583         if (asOriginalStringKeeper) {
 
  640                 supressed(
'\0', 
"noCfgFileRecursion", 
"do not read config files recursively, must be set before use") {
 
  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);
 
  650             aMessageStream << 
"  -" << aOption.
lShortName << 
", ";
 
  652             aMessageStream << 
"      ";
 
  655         bool firstLine = 
true;
 
  658                 aMessageStream << 
"--" << std::setw(aMaxName) << std::left << aOption.
lLongName 
  661                 aMessageStream << 
"        " << std::setw(aMaxName) << 
" " << 
"  ";
 
  663             if (explanation.length() > descLength) {
 
  665                 bool keepBrokenChar = 
false;
 
  666                 bool hardBreak = 
false;
 
  667                 auto breakPos = explanation.find_last_of(
" -", descLength - 1);
 
  668                 if (breakPos == std::string::npos) {
 
  671                     breakPos = descLength - 2;
 
  673                     keepBrokenChar = 
true;
 
  676                     if (explanation[breakPos] != 
' ') {
 
  678                         keepBrokenChar = 
true;
 
  681                 aMessageStream << std::setw(descLength) << std::left << explanation.substr(0, breakPos) + (hardBreak ? 
"-" : 
"");
 
  682                 explanation = explanation.substr(breakPos + (keepBrokenChar ? 0 : 1));
 
  684                 aMessageStream << std::setw(descLength) << std::left << explanation;
 
  689                 aMessageStream << 
" default: ";
 
  693             aMessageStream << 
"\n";
 
  694         } 
while (explanation.length() > 0);
 
  696         [](
const base * opt) {
 
  697         return ! opt->fIsHidden();
 
  699             aMessageStream << 
"      " << std::setw(aMaxName) << 
" " << 
" Requires the following other options:\n";
 
  704                 aMessageStream << 
"      " << std::setw(aMaxName) << 
" " << 
"  " << opt->
fGetLongName() << 
"\n";
 
  708         [](
const base * opt) {
 
  709         return ! opt->fIsHidden();
 
  711             aMessageStream << 
"      " << std::setw(aMaxName) << 
" " << 
" Forbids the following other options:\n";
 
  716                 aMessageStream << 
"      " << std::setw(aMaxName) << 
" " << 
"  " << opt->
fGetLongName() << 
"\n";
 
  720             aMessageStream << 
"      " << std::setw(aMaxName) << 
" " << 
"   this option is required\n";
 
  731                 if (it.second->fIsContainer()) {
 
  739         size_t maxExplain = 0;
 
  740         size_t lineLenght = 132;
 
  741         #ifdef IS_NONBROKEN_SYSTEM 
  743             struct winsize window;
 
  744             if (ioctl(0, TIOCGWINSZ, &window) == 0) {
 
  745                 lineLenght = window.ws_col;
 
  750             const auto opt = it.second;
 
  751             maxName = std::max(opt->lLongName.length(), maxName);
 
  752             maxExplain = std::max(opt->lExplanation.length(), maxExplain);
 
  755             const auto opt = it.second;
 
  756             if (! opt->fIsHidden()) {
 
  772         std::ofstream cfgFile(aFileName, std::ofstream::out | std::ofstream::trunc);
 
  774             #ifdef IS_NONBROKEN_SYSTEM 
  776             auto result = readlink(
"/proc/self/exe", buf, 
sizeof(buf));
 
  777             if (result > 0 && result < 128 - 2 - 14) {
 
  778                 cfgFile << 
"#!" << buf << 
" --readCfgFile\n";
 
  785             cfgFile << 
"# written ";
 
  786             std::time_t t = std::time(
nullptr);
 
  788             if (std::strftime(mbstr, 
sizeof(mbstr), 
"%Y-%m-%d %H:%M:%S", std::localtime(&t))) {
 
  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";
 
  798             const auto opt = it.second;
 
  799             if (opt->lPreserveWorthyStuff != 
nullptr) {
 
  800                 for (
const auto& line : * (opt->lPreserveWorthyStuff)) {
 
  801                     cfgFile << 
"\n" << line;
 
  804             cfgFile << 
"\n# " << opt->lExplanation << 
"\n";
 
  806             if (opt->lSource.fIsUnset()) {
 
  809             opt->fWriteCfgLines(cfgFile, prefix);
 
  810             opt->fWriteRange(cfgFile);
 
  811             if (!opt->lSource.fIsUnset()) {
 
  812                 cfgFile << 
"# set from " << opt->lSource << 
"\n";
 
  820         std::ifstream cfgFile(aFileName);
 
  821         if (!cfgFile.good() && !(aMayBeAbsent && errno == ENOENT)) {
 
  822             throw std::system_error(errno, std::system_category(),
 
  827         std::vector<std::string>* preserveWorthyStuff = 
nullptr;
 
  828         bool hideNextOption = 
false;
 
  829         bool disableNextOption = 
false;
 
  831             while (cfgFile.good()) {
 
  833                 std::getline(cfgFile, line);
 
  836                 if (line.length() == 0) {
 
  837                     hideNextOption = 
false;
 
  838                     disableNextOption = 
false;
 
  840                 } 
else if (line[0] == 
'#') {
 
  841                     if (line[1] == 
'#') {
 
  842                         if (preserveWorthyStuff == 
nullptr) {
 
  843                             preserveWorthyStuff = 
new std::vector<std::string>;
 
  845                         preserveWorthyStuff->push_back(line);
 
  846                         if (line == 
"## hide") {
 
  847                             hideNextOption = 
true;
 
  849                             disableNextOption = 
true;
 
  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);
 
  857                                 auto option = it->second;
 
  858                                 option->fSetPreserveWorthyStuff(preserveWorthyStuff);
 
  859                                 preserveWorthyStuff = 
nullptr;
 
  860                                 if (hideNextOption) {
 
  863                                 if (disableNextOption) {
 
  871                 auto equalsAt = line.find_first_of(
'=');
 
  872                 if (equalsAt == std::string::npos || equalsAt < 1 || equalsAt == line.length()) {
 
  878                 auto optionName = line.substr(0, equalsAt);
 
  881                     throw std::runtime_error(
internal::conCat(
"unknown option '", optionName, 
"'"));
 
  883                 auto option = it->second;
 
  884                 if (hideNextOption) {
 
  887                 if (disableNextOption) {
 
  891                     std::stringstream sbuf(line.substr(equalsAt + 1));
 
  892                     option->fSetMe(sbuf, source);
 
  893                     if (preserveWorthyStuff != 
nullptr) {
 
  894                         option->fSetPreserveWorthyStuff(preserveWorthyStuff);
 
  895                         preserveWorthyStuff = 
nullptr;
 
  897                     option->fCheckRange();
 
  900         } 
catch (
const std::exception& e) {
 
  901             fGetErrorStream() << aFileName << 
":" << lineNumber << 
": error: " << e.what() << 
"\n";
 
  917         aStream << std::boolalpha << lValue;
 
  924         aStream >> std::boolalpha >> lValue;
 
  928         aStream >> std::boolalpha >> lValue;
 
  939                 single(
'h', 
"help", 
"give this help") {
 
  958                 supressed(
'\0', 
"writeCfgFile", 
"write a config file", 
"") {
 
  972                 single(
'\0', name, description, 
"") {
 
  985         static OptionReadCfgFile<false> 
gReadCfgFile(
"readCfgFile", 
"read a config file");
 
  986         static OptionReadCfgFile<true> 
gReadCfgFileIfThere(
"readCfgFileIfThere", 
"read a config file if it's there");