diff options
Diffstat (limited to 'getoptpp.hpp')
-rw-r--r-- | getoptpp.hpp | 234 |
1 files changed, 151 insertions, 83 deletions
diff --git a/getoptpp.hpp b/getoptpp.hpp index 0eafe3f..92f8e82 100644 --- a/getoptpp.hpp +++ b/getoptpp.hpp @@ -3,113 +3,181 @@ #include <cassert> #include <functional> #include <vector> +#include <deque> #include <unordered_map> #include <string> +#include <cstring> #include <getopt.h> namespace dg { -using Handle = std::function<void()>; +using Handle = std::function<int()>; class Options { public: - Options(); - /// @param name name of the option /// @param has_arg kind of arguments that are used (no_argument, required_argument, optional_argument) /// @param val identifies the option (see getopt documentation) + /// @param help_text help text for this option /// @param handle lambda that is invoked when the option occures - void add(std::string const & name, int has_arg, int val, Handle handle); + void add(std::string const & name, int has_arg, int val, std::string const & help_text, Handle handle) { + add(name, has_arg, nullptr, val, help_text, handle); + } /// @param name name of the option /// @param has_arg kind of arguments that are used (no_argument, required_argument, optional_argument) /// @param flag pointer that is set after the option occured /// @param val value for the flag to be set + /// @param help_text help text for this option /// @param handle lambda that is invoked when the option occures - void add(std::string const & name, int has_arg, int* flag, int val, Handle handle); + void add(std::string const & name, int has_arg, int* flag, int val, std::string const & help_text, Handle handle) { + names.push_back(name); + // create a new option from the args + options.push_back({names.back().data(), has_arg, flag, val}); + help_texts.push_back(help_text); + + int index = val; // let val be the option's unique identifier + if (flag != nullptr) { + // flag is not null, so the val is not used as identifier + // so pick another one + index = num_flags++; + } + // store the handle + handles[index] = handle; + } + + int process(int argc, char* argv[]) { + int ret = 0; + std::string shortopts; + for (auto const & option: options) { + if (option.flag != nullptr) { + continue; + } + shortopts += static_cast<char>(option.val); + + switch (option.has_arg) { + case no_argument: + break; + case required_argument: + shortopts += ":"; + break; + case optional_argument: + shortopts += "::"; + break; + } + } + + // add termination option + options.push_back({0, 0, 0, 0}); + + // handle arguments + while (true) { + int index = 0; + int key = getopt_long(argc, argv, shortopts.data(), options.data(), &index); + + if (key == -1) { + break; + } else if (key == '?') { + return 1; + } else if (key == ':') { + return 1; + } else if (key == 0) { + // call flag's handle + ret = handles.at(index)(); + } else { + // call option's handle + ret = handles.at(key)(); + } + + if(ret) { + return ret; + } + } + + for (int i = optind; i < argc; ++i) { + args.push_back(argv[i]); + } + + // remove terminating option + options.pop_back(); + assert(options.size() == handles.size()); + assert(options.size() == help_texts.size()); + return 0; + } - bool process(int argc, char* argv[]); const std::vector<std::string> arguments() const { return args; } + void help() { + std::size_t width = 26; + std::size_t column_width = 80; + + int index = 0; + for(auto const & help_text : help_texts) { + if(index >= (int)options.size()) { + break; + } + auto const & opt = options.at(index); + std::string args; + switch(opt.has_arg) { + case required_argument: + args = "<x>"; + break; + case optional_argument: + args = "[x]"; + break; + case no_argument: + default: + break; + } + + std::string option_str; + if(opt.val >= '!' && opt.val <= '~') + { + option_str = " -" + std::string(1, opt.val) + ", --" + opt.name + " " + args; + } + else + { + option_str = " --" + std::string(opt.name) + " " + args; + } + + std::string padding; + if(option_str.size() < width) + { + padding.append(width - option_str.size(), ' '); + } + else + { + padding = "\n"; + padding.append(width, ' '); + } + + std::cout << option_str << padding; + + auto i = width; + for(auto c : help_text) + { + if((c == '\n') || (i > column_width && (c == ' ' || c == '\t'))) + { + std::string padding(width, ' '); + std::cout << '\n' << padding; + i = width; + continue; + } + std::cout << c; + ++i; + } + std::cout << '\n'; + ++index; + } + } + private: - std::size_t num_flags; - std::vector<option> options; + std::size_t num_flags{}; + std::vector<option> options{}; + std::deque<std::string> names{}; + std::vector<std::string> help_texts{}; std::vector<std::string> args; - std::unordered_map<int, Handle> handles; + std::unordered_map<int, Handle> handles{}; }; -Options::Options() - : num_flags{} - , options{} - , handles{} { -} - -void Options::add(std::string const & name, int has_arg, int val, Handle handle) { - add(name, has_arg, nullptr, val, handle); -} - -void Options::add(std::string const & name, int has_arg, int* flag, int val, Handle handle) { - int index = val; // let val be the option's unique identifier - options.push_back({name.data(), has_arg, flag, val}); - if (flag != nullptr) { - // flag is not null, so the val is not used as identifier - // so pick another one - index = num_flags++; - } - // store the handle - handles[index] = handle; -} - -bool Options::process(int argc, char* argv[]) { - std::string shortopts; - for (auto const & option: options) { - if (option.flag != nullptr) { - continue; - } - shortopts += static_cast<char>(option.val); - - switch (option.has_arg) { - case no_argument: - break; - case required_argument: - shortopts += ":"; - break; - case optional_argument: - shortopts += "::"; - break; - } - } - - // add termination option - options.push_back({0, 0, 0, 0}); - - // handle arguments - while (true) { - int index = 0; - int key = getopt_long(argc, argv, shortopts.data(), options.data(), &index); - - if (key == -1) { - break; - } else if (key == '?') { - return false; - } else if (key == ':') { - return false; - } else if (key == 0) { - // call flag's handle - handles.at(index)(); - } else { - // call option's handle - handles.at(key)(); - } - } - for (int i = optind; i < argc; ++i) { - args.push_back(argv[i]); - } - - // remove terminating option - options.pop_back(); - assert(options.size() == handles.size()); - return true; -} - } |