summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--example.cpp73
-rw-r--r--getoptpp.hpp234
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;
-}
-
}