diff options
-rw-r--r-- | example.cpp | 73 | ||||
-rw-r--r-- | getoptpp.hpp | 234 |
2 files changed, 200 insertions, 107 deletions
diff --git a/example.cpp b/example.cpp index 73988aa..bd94585 100644 --- a/example.cpp +++ b/example.cpp @@ -6,30 +6,55 @@ int main(int argc, char* argv[]) { int verbose_flag; dg::Options opt; - opt.add("verbose", no_argument, &verbose_flag, 1, [&]() { - std::cout << "verbose: " << verbose_flag << "\n"; - }); - opt.add("brief", no_argument, &verbose_flag, 0, [&]() { - std::cout << "brief: " << verbose_flag << "\n"; - }); - opt.add("add", no_argument, 'a', [&]() { - std::cout << "add\n"; - }); - opt.add("append", no_argument, 'b', [&]() { - std::cout << "append\n"; - }); - opt.add("delete", required_argument, 'd', [&]() { - std::cout << "delete: " << optarg << "\n"; - }); - opt.add("create", required_argument, 'c', [&]() { - std::cout << "create: " << optarg << "\n"; - }); - opt.add("file", required_argument, 'f', [&]() { - std::cout << "file: " << optarg << "\n"; - }); - opt.add("help", no_argument, '?', [&]() { - std::cout << "usage stuff\n"; - }); + opt.add("verbose", no_argument, &verbose_flag, 1, + "Be verbose.", + [&]() { + std::cout << "verbose: " << verbose_flag << "\n"; + return 0; + }); + opt.add("brief", no_argument, &verbose_flag, 0, + "Be brief.", + [&]() { + std::cout << "brief: " << verbose_flag << "\n"; + return 0; + }); + opt.add("add", no_argument, 'a', + "Add an entry.", + [&]() { + std::cout << "add\n"; + return 0; + }); + opt.add("append", no_argument, 'b', + "Append an entry.", + [&]() { + std::cout << "append\n"; + return 0; + }); + opt.add("delete", optional_argument, 'd', + "Delete an entry.", + [&]() { + std::cout << "delete: " << optarg << "\n"; + return 0; + }); + opt.add("create", required_argument, 'c', + "Create an entry.", + [&]() { + std::cout << "create: " << optarg << "\n"; + return 0; + }); + opt.add("file", required_argument, 'f', + "Some file argument.", + [&]() { + std::cout << "file: " << optarg << "\n"; + return 0; + }); + opt.add("help", no_argument, 'h', + "Print this help text.", + [&]() { + std::cout << "usage stuff\n"; + opt.help(); + return 0; + }); opt.process(argc, argv); for(auto const &arg : opt.arguments()) 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; -} - } |