123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- //==============================================================================
- //
- // Arguments - the argument parser class
- //
- // Copyright (C) 2018 Dick van Oudheusden
- //
- // This library is free software; you can redistribute it and/or
- // modify it under the terms of the GNU Lesser General Public
- // License as published by the Free Software Foundation; either
- // version 3 of the License, or (at your option) any later version.
- //
- // This library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- // Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public
- // License along with this library; if not, write to the Free
- // Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- //
- //==============================================================================
- #include <iostream>
- #include <sstream>
- #include <cstring>
- #include "Arguments.h"
- namespace arg
- {
- // Switch argument implementation
- Argument::Argument(Arguments &arguments,
- bool optional,
- char shortOption,
- const std::string &longOption,
- const std::string &help) :
- _optional(optional),
- _withValue(false),
- _active(false),
- _shortOption(shortOption),
- _longOption(longOption),
- _valueInfo(""),
- _value(""),
- _helpInfo(help)
- {
- arguments.add(*this);
- }
- // Value argument implementation
- Argument::Argument(Arguments &arguments,
- bool optional,
- char shortOption,
- const std::string &longOption,
- const std::string &valueInfo,
- const std::string &helpInfo,
- const std::string &initial) :
- _optional(optional),
- _withValue(true),
- _active(false),
- _shortOption(shortOption),
- _longOption(longOption),
- _valueInfo(valueInfo),
- _helpInfo(helpInfo),
- _value(initial)
- {
- arguments.add(*this);
- }
- Argument::~Argument()
- {
- }
- int Argument::optionWidth() const
- {
- return _longOption.length() + (_valueInfo.empty() ? 0 : _valueInfo.length() + 1);
- }
- void Argument::print(int column) const
- {
- Argument::print(_shortOption, _longOption, _valueInfo, _helpInfo, column);
- }
- void Argument::print(char shortOption, const std::string &longOption, const std::string &valueInfo, const std::string &helpInfo, int column)
- {
- std::stringstream str;
- str << " ";
- if (shortOption != '\0')
- {
- str << '-' << shortOption;
- }
- else
- {
- str << " ";
- }
- if ((shortOption != '\0') && (!longOption.empty()))
- {
- str << ", ";
- }
- else
- {
- str << " ";
- }
- if (!longOption.empty())
- {
- str << "--" << longOption;
- if (!valueInfo.empty())
- {
- str << "=" << valueInfo;
- }
- }
- int extra = column - str.str().size();
- str << std::string(extra, ' ') << helpInfo;
- std::cout << str.str() << std::endl;
- }
- // Arguments implementation
- Arguments::Arguments(const std::string &usage, const std::string &version, const std::string &help) :
- _usage(usage),
- _version(version),
- _help(help)
- {
- }
- Arguments::~Arguments()
- {
- }
- void Arguments::add(Argument &argument)
- {
- _arguments.push_back(&argument);
- }
- bool Arguments::parse(int argc, char *argv[], std::vector<std::string> &unprocessed)
- {
- // Reset in-use
- for (auto argument = _arguments.begin(); argument != _arguments.end(); ++argument)
- {
- (*argument)->active(false);
- }
- // Parse the arguments
- int arg = 1;
- while (arg < argc)
- {
- size_t arglen = strlen(argv[arg]);
- if ((arglen >= 2) && (argv[arg][0] == '-') && (argv[arg][1] == '-'))
- {
- if (arglen == 2) break; // special: --, stop parsing
- if (!parseLongOption(argc, argv, arg)) return false;
- }
- else if ((arglen >= 1) && (argv[arg][0] == '-'))
- {
- if (!parseShortOption(argc, argv, arg)) return false;
- }
- else
- {
- unprocessed.push_back(argv[arg]);
- }
- arg++;
- }
- // Save the arguments after --
- for (arg++; arg < argc; arg++)
- {
- unprocessed.push_back(argv[arg]);
- }
- // Check the required arguments
- for (auto argument = _arguments.begin(); argument != _arguments.end(); ++argument)
- {
- if (!checkRequiredArgument(*argument)) return false;
- }
- return true;
- }
- bool Arguments::parseShortOption(int argc, char *argv[], int &arg)
- {
- if (argv[arg][1] == '\0')
- {
- std::cerr << "Invalid argument: " << argv[arg] << std::endl;
- return false;
- }
- if (argv[arg][1] == '?')
- {
- printHelp();
- return false;
- }
- size_t i = 1;
- while (argv[arg][i] != '\0')
- {
- auto argument = _arguments.begin();
- for (; argument != _arguments.end(); ++argument)
- {
- char shortOption = (*argument)->shortOption();
- if ((shortOption != '\0') && (shortOption == argv[arg][i]))
- {
- (*argument)->active(true);
- if ((*argument)->withValue())
- {
- if (argv[arg][i+1] != '\0')
- {
- (*argument)->value(&argv[arg][i+1]);
- return true;
- }
- else if ((arg+1) < argc)
- {
- (*argument)->value(argv[++arg]);
- return true;
- }
- else
- {
- std::cerr << "Missing value for argument: -" << argv[arg][i] << std::endl;
- return false;
- }
- }
- break;
- }
- }
- if (argument == _arguments.end())
- {
- std::cerr << "Unknown argument: -" << argv[arg][i] << std::endl;
- return false;
- }
- i++;
- }
- return true;
- }
- bool Arguments::parseLongOption(int argc, char *argv[], int arg)
- {
- std::string option(argv[arg] + 2); // skip --
- std::string value;
- if (option == "help")
- {
- printHelp();
- return false;
- }
- if (option == "version")
- {
- std::cout << _version << std::endl;
- return false;
- }
- // Split option and value
- std::size_t assign = option.find_first_of("=:");
- if (assign != std::string::npos)
- {
- value = option.substr(assign+1);
- option = option.substr(0, assign);
- }
- auto argument = _arguments.begin();
- for (; argument != _arguments.end(); ++argument)
- {
- if (option == (*argument)->longOption())
- {
- (*argument)->active(true);
- if ((*argument)->withValue())
- {
- if (!value.empty())
- {
- (*argument)->value(value);
- }
- else
- {
- std::cerr << "Missing value for argument: " << argv[arg] << std::endl;
- return false;
- }
- }
- break;
- }
- }
- if (argument == _arguments.end())
- {
- std::cerr << "Unknown argument: " << argv[arg] << std::endl;
- return false;
- }
- return true;
- }
- bool Arguments::checkRequiredArgument(Argument *argument)
- {
- if ((argument->optional()) || (argument->active())) return true;
- std::cerr << "Missing argument: ";
- if (argument->shortOption() != '\0')
- {
- std::cerr << '-' << argument->shortOption();
- }
- else
- {
- std::cerr << "--" << argument->longOption();
- }
- std::cerr << std::endl;
- return false;
- }
- void Arguments::printHelp() const
- {
- std::cout << "Usage: " << _usage << std::endl;
- int column = 7; // "help" + 3 spaces
- for (auto argument = _arguments.begin(); argument != _arguments.end(); ++argument)
- {
- column = std::max(column, (*argument)->optionWidth());
- }
- column += 12; // short option, six spaces and --
- Argument::print('?', "help", "", "display this help and exit", column);
- Argument::print('\0', "version", "", "display version and exit", column);
- for (auto argument = _arguments.begin(); argument != _arguments.end(); ++argument)
- {
- (*argument)->print(column);
- }
- std::cout << std::endl << _help << std::endl;
- }
- }
|