fix includes
[awesomized/libmemcached] / src / bin / common / options.hpp
index a5d6bdf72f955347c213fb04f10c29c280fd3f8c..980ca38dc39ab8865a498002e98d6f334d4c2814 100644 (file)
 
 #pragma once
 
+#include <algorithm>
 #include <cstdint>
 #include <climits>
-#include <getopt.h>
+#include <functional>
 #include <iostream>
 #include <string>
 #include <vector>
 
-struct client_options {
-  static const int first_flag = '/';
-  static const int last_flag = 'z';
-  static const int flag_count = last_flag - first_flag + 1;
-
-  enum class flag : int {
-    _success = -1,
-    _failure = '?',
-
-    absolute = '/',
-    flush = '0',
-    add_cmd = 'A',
-    buffer = 'B',
-    concurrency = 'C',
-    flags = 'F',
-    hash = 'H',
-    load = 'L',
-    tcp_nodelay = 'N',
-    query = 'Q',
-    replace_cmd = 'R',
-    set_cmd = 'S',
-    udp = 'U',
-    version = 'V',
-    analyze = 'a',
-    binary = 'b',
-    debug = 'd',
-    expire = 'e',
-    file = 'f',
-    help = 'h',
-    non_block = 'n',
-    password = 'p',
-    quiet = 'q',
-    repeat = 'r',
-    servers = 's',
-    test = 't',
-    username = 'u',
-    verbose = 'v',
-    zero = 'z',
-  };
+#include "libmemcached/common.h"
 
-  struct flags {
-    int absolute;
-    int flush;
-    int add_cmd;
-    int buffer;
-    int flags;
-    int tcp_nodelay;
-    int replace_cmd;
-    int set_cmd;
-    int udp;
-    int version;
-    int analyze;
-    int binary;
-    int debug;
-    int help;
-    int non_block;
-    int quiet;
-    int verbose;
-    int zero;
-  } flags;
-
-  struct args {
-    unsigned long concurrency;
-    const char *hash;
-    unsigned long load;
-    const char *query;
-    const char *file;
-    unsigned long expire;
-    const char *password;
-    unsigned long repeat;
-    const char *servers;
-    const char *test;
-    const char *username;
-    const char * const *positional;
-  } args;
-
-  option avail[flag_count];
-  const char *help[flag_count];
-
-  std::string short_opts;
-  std::vector<struct option> long_opts;
-
-  const char *progname;
-  const char *progvers;
-  const char *progdesc;
-
-  client_options(const char *prg, const char *ver, const char *dsc, const std::vector<client_options::flag> &f)
-  : flags{}
-  , args{}
-  , avail{}
-  , help{}
-  , short_opts{}
-  , long_opts{}
-  , progname{prg}
-  , progvers{ver}
-  , progdesc{dsc}
-  {
+#ifdef HAVE_GETOPT_H
+#  include <getopt.h>
+#elif defined _MSC_VER
+#  include "win32/getopt.h"
+#endif
 
-# define opt_flag(s, a, h) opt_flag_ex(#s, s, a, h)
-# define opt_flag_ex(n, v, a, h) \
-  [static_cast<int>(flag::v)-first_flag] = {n, a, &flags.v, static_cast<int>(flag::v)}; \
-    help[static_cast<int>(flag::v)-first_flag] = h
-
-# define opt_data(s, a, h) opt_data_ex(#s, s, a, h)
-# define opt_data_ex(n, v, a, h) \
-  [static_cast<int>(flag::v)-first_flag] = {n, a, nullptr, static_cast<int>(flag::v)}; \
-    help[static_cast<int>(flag::v)-first_flag] = h
-
-    avail opt_flag(                 absolute,   no_argument,      "Use absolute path names.");
-    avail opt_flag(                 flush,      no_argument,      "Flush the server(s).");
-    avail opt_flag_ex("add",        add_cmd,    no_argument,      "Perform ADD operations.");
-    avail opt_flag(                 buffer,     no_argument,      "Buffer requests.");
-    avail opt_data(                 concurrency,required_argument,"Level of concurrency.");
-    avail opt_flag(                 flags,      no_argument,      "Print or set associated flags for key(s).");
-    avail opt_data(                 hash,       required_argument,"Select key hash.");
-    avail opt_data(                 load,       required_argument,"Initial load.");
-    avail opt_flag_ex("tcp-nodelay",tcp_nodelay,no_argument,      "Disable Nagle's algorithm.");
-    avail opt_data(                 query,      no_argument,      "Query arguments.");
-    avail opt_flag_ex("replace",    replace_cmd,no_argument,      "Perform REPLACE operations.");
-    avail opt_flag_ex("set",        set_cmd,    no_argument,      "Perform SET operations.");
-    avail opt_flag(                 udp,        no_argument,      "Use UDP.");
-    avail opt_flag(                 version,    no_argument,      "Print program version.");
-    avail opt_flag(                 analyze,    no_argument,      "Analyze the server's statistics.");
-    avail opt_flag(                 binary,     no_argument,      "Use the binary memcached protocol.");
-    avail opt_flag(                 debug,      no_argument,      "Print output useful only for debugging.");
-    avail opt_data(                 expire,     required_argument,"Set associated expiry time for key(s).");
-    avail opt_data(                 file,       required_argument,"File to save to or read from.");
-    avail opt_flag(                 help,       no_argument,      "Print this help.");
-    avail opt_flag_ex("non-block",  non_block,  no_argument,      "Use non blocking connections.");
-    avail opt_data(                 password,   required_argument,"SASL password.");
-    avail opt_flag(                 quiet,      no_argument,      "Print no output, not even errors.");
-    avail opt_data(                 repeat,     required_argument,"Repeat operation n times.");
-    avail opt_data(                 servers,    required_argument,"List of servers to connect to.");
-    avail opt_data(                 test,       required_argument,"Test to perform.");
-    avail opt_data(                 username,   required_argument,"SASL username.");
-    avail opt_flag(                 verbose,    no_argument,      "Print more informational output.");
-    avail opt_flag(                 zero,       no_argument,      "Zero.");
-
-    long_opts.reserve(f.size() + 1);
-    short_opts.reserve(f.size() * 3);
-
-    for (auto o : f) {
-      auto &opt = avail[static_cast<int>(o)-first_flag];
-
-      long_opts.push_back(opt);
-      short_opts.push_back(opt.val);
-
-      for (int i = 0; i < opt.has_arg; ++i) {
-        short_opts.push_back(':');
-      }
-    }
+class client_options {
+public:
 
-    long_opts.push_back(option{});
-  }
+  struct extended_option {
+    option opt;
+    std::string help;
+    std::function<bool(client_options &, extended_option &)> parse;
+    std::function<bool(const client_options &, const extended_option &, memcached_st *)> apply;
+    char *arg;
+    bool set;
+  };
 
-  void printVersion() const {
-    std::cout << progname << " v" << progvers
-              << " (libmemcached v" << LIBMEMCACHED_VERSION_STRING << ")"
-              << std::endl;
-  }
+  std::vector<extended_option> options;
+  std::vector<extended_option> defaults;
+
+  const char *prog_name;
+  const char *prog_vers;
+  const char *prog_desc;
+  const char *prog_argp;
+
+  client_options(const char *prg, const char *ver, const char *dsc, const char *arg = nullptr)
+  : options{}
+  , defaults{}
+  , prog_name{prg}
+  , prog_vers{ver}
+  , prog_desc{dsc}
+  , prog_argp{arg}
+  {
 
-  void printHelp(const char *positional_args, const char *extra_doc = nullptr) const {
-    printVersion();
-    std::cout << "\n\t" << progdesc << "\n\n";
-    std::cout << "Usage:\n\t" << progname << " -[";
-    for (auto &opt : long_opts) {
-      if (!opt.has_arg) {
-        std::cout << (char) opt.val;
+    def("help", 'h', no_argument, "Print this help.")
+        .apply = [](const client_options &opt, const extended_option &ext, memcached_st *) {
+      if (ext.set) {
+        opt.print_help();
+        exit(EXIT_SUCCESS);
       }
-    }
-    std::cout << "] [--";
-    for (auto &opt : long_opts) {
-      if (opt.has_arg) {
-        std::cout << (char) opt.val;
-        if ((&opt)+1 != &*long_opts.rbegin())
-        std::cout << '|';
+      return true;
+    };
+    def("version", 'V', no_argument, "Print program version.")
+        .apply = [](const client_options &opt, const extended_option &ext, memcached_st *) {
+      if (ext.set) {
+        opt.print_version();
+        exit(EXIT_SUCCESS);
       }
-    }
-    std::cout << " <arg>] ";
-
-    std::cout << positional_args << "\n\n";
-    std::cout << "Options:\n";
-    for (auto &opt : long_opts) {
-      if (opt.name) {
-        std::cout << "\t-" << (char) opt.val << "|--" << opt.name;
-        if (opt.has_arg) {
-          if (opt.has_arg == optional_argument) {
-            std::cout << "[";
+      return true;
+    };
+
+    def("verbose", 'v', no_argument, "Print more informational output.")
+        .parse = [](client_options &opt, extended_option &) {
+      opt.unset("quiet");
+      return true;
+    };
+    def("debug", 'd', no_argument, "Print output useful only for debugging.")
+        .parse = [](client_options &opt, extended_option &) {
+      opt.set("verbose");
+      opt.unset("quiet");
+      return true;
+    };
+    def("quiet", 'q', no_argument, "Print no output, not even errors.")
+        .parse = [](client_options &opt, extended_option &) {
+      opt.unset("verbose");
+      opt.unset("debug");
+      return true;
+    };
+
+    def("password", 'p', required_argument, "SASL password.");
+    def("username", 'u', required_argument, "SASL username.")
+        .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
+      if (auto username = ext.arg) {
+#if !LIBMEMCACHED_WITH_SASL_SUPPORT
+          if (!opt.isset("quiet")) {
+            std::cerr
+                << "SASL username was supplied, but binary was not built with SASL support.\n";
           }
-          std::cout << "=arg";
-          if (opt.has_arg == optional_argument) {
-            std::cout << "]";
+          return false;
+#else
+        if (memc) {
+          if (MEMCACHED_SUCCESS
+              != memcached_set_sasl_auth_data(memc, username, opt.argof("password"))) {
+            if (!opt.isset("quiet")) {
+              std::cerr << memcached_last_error_message(memc);
+            }
+            return false;
           }
         }
-        std::cout << "\n\t\t" << help[opt.val - first_flag];
-        std::cout << std::endl;
+#endif
       }
-    }
-
-    if (extra_doc) {
-      std::cout << extra_doc << std::endl;
-    }
-  }
-
-  void printLastError(memcached_st *memc) const {
-    if (!flags.quiet) {
-      std::cerr << memcached_last_error_message(memc);
-    }
-  }
-
-  memcached_hash_t getHash() const {
-    memcached_hash_t hash = MEMCACHED_HASH_DEFAULT;
-    if (args.hash && *args.hash) {
-      if (flags.verbose) {
-        std::cerr << "Checking for hash '" << args.hash << "'.\n";
+      return true;
+    };
+
+    def("binary", 'b', no_argument, "Use the binary memcached protocol.")
+        .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
+      if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, ext.set)) {
+        if(!opt.isset("quiet")) {
+          std::cerr << memcached_last_error_message(memc);
+        }
+        return false;
       }
-      for (int h = hash; h < MEMCACHED_HASH_MAX; ++h) {
-        auto hash_type = static_cast<memcached_hash_t>(h);
-        std::string hash_string{libmemcached_string_hash(hash_type)};
-        std::string hash_wanted{args.hash};
-
-        if (hash_wanted.length() == hash_string.length()) {
-          auto ci = std::equal(hash_string.begin(), hash_string.end(), hash_wanted.begin(), [](int a, int b) {
-            return std::tolower(a) == std::tolower(b);
-          });
-          if (ci) {
-            hash = hash_type;
-            break;
-          }
+      return true;
+    };
+    def("buffer", 'B', no_argument, "Buffer requests.")
+        .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
+      if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, ext.set)) {
+        if(!opt.isset("quiet")) {
+          std::cerr << memcached_last_error_message(memc);
         }
+        return false;
       }
-      if (hash == MEMCACHED_HASH_DEFAULT) {
-        if (!flags.quiet) {
-          std::cerr << "Could not find hash '" << args.hash << "'.\n";
+      return true;
+    };
+    def("non-blocking", 'n', no_argument, "Use non-blocking connections.")
+        .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
+      if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, ext.set)) {
+        if(!opt.isset("quiet")) {
+          std::cerr << memcached_last_error_message(memc);
         }
+        return false;
       }
-    }
-    return hash;
-  }
-
-  memcached_server_st *getServers() {
-    if (!args.servers) {
-      if (flags.verbose) {
-        std::cerr << "Checking environment for a server list in MEMCACHED_SERVERS.\n";
+      return true;
+    };
+    def("tcp-nodelay", 'N', no_argument, "Disable Nagle's algorithm.")
+        .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
+      if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, ext.set)) {
+        if(!opt.isset("quiet")) {
+          std::cerr << memcached_last_error_message(memc);
+        }
+        return false;
       }
-      args.servers = getenv("MEMCACHED_SERVERS");
-      if (!args.servers || !*args.servers) {
-        if (!flags.quiet) {
-          std::cerr << "No servers provided.\n";
+      return true;
+    };
+    def("servers", 's', required_argument, "List of servers to connect to.")
+        .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
+      auto servers = ext.arg;
+      if (!servers) {
+        if (opt.isset("verbose")) {
+          std::cerr << "Checking environment for a server list in MEMCACHED_SERVERS.\n";
+        }
+        servers = getenv("MEMCACHED_SERVERS");
+        if (!servers || !*servers) {
+          if (!opt.isset("quiet")) {
+            std::cerr << "No servers provided.\n";
+          }
+          return false;
         }
-        return nullptr;
       }
-    }
 
-    auto servers = memcached_servers_parse(args.servers);
-    if (!servers || !memcached_server_list_count(servers)) {
-      if (!flags.quiet) {
-        std::cerr << "Invalid server list provided: '" << args.servers << "'\n";
+      auto server_list = memcached_servers_parse(servers);
+      if (!server_list || !memcached_server_list_count(server_list)) {
+        if (!opt.isset("quiet")) {
+          std::cerr << "Invalid server list provided: '" << servers << "'\n";
+        }
+        if (server_list) {
+          memcached_server_list_free(server_list);
+        }
+        return false;
       }
-      if (servers) {
-        memcached_server_list_free(servers);
+
+      if (MEMCACHED_SUCCESS != memcached_server_push(memc, server_list)) {
+        if (!opt.isset("quiet")) {
+          std::cerr << memcached_last_error_message(memc);
+        }
+        memcached_server_list_free(server_list);
+        return false;
       }
-      return nullptr;
-    }
+      memcached_server_list_free(server_list);
+      return true;
+    };
+    def("hash", 'H', required_argument, "Key hashing method.")
+        .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
+      if (ext.set) {
+        std::string hash_wanted{ext.arg};
+        memcached_hash_t hash = MEMCACHED_HASH_DEFAULT;
+
+        std::transform(hash_wanted.begin(), hash_wanted.end(), hash_wanted.begin(), ::toupper);
+
+        if (opt.isset("verbose")) {
+          std::cerr << "Checking for hash '" << hash_wanted << "'.\n";
+        }
+        for (int h = MEMCACHED_HASH_DEFAULT; h < MEMCACHED_HASH_MAX; ++h) {
+          auto hash_type = static_cast<memcached_hash_t>(h);
+          std::string hash_string{libmemcached_string_hash(hash_type)};
+
+          if (hash_wanted.length() == hash_string.length()) {
+            auto ci = std::equal(hash_string.begin(), hash_string.end(), hash_wanted.begin(),
+                                 [](int a, int b) { return ::toupper(a) == b; });
+            if (ci) {
+              hash = hash_type;
+              break;
+            }
+          }
+        }
+        if (hash == MEMCACHED_HASH_DEFAULT) {
+          if (!opt.isset("quiet")) {
+            std::cerr << "Could not find hash '" << hash_wanted << "'.\n";
+          }
+        }
+        if (MEMCACHED_SUCCESS != memcached_behavior_set_key_hash(memc, hash)) {
+          if (!opt.isset("quiet")) {
+            std::cerr << memcached_last_error_message(memc);
+          }
+          return false;
+        }
+      }
+      return true;
+    };
+  }
 
-    return servers;
+  extended_option &def(option opt, std::string help) {
+    defaults.emplace_back(extended_option{opt, std::move(help), {}, {}, nullptr, false});
+    return defaults.back();
   }
 
-  bool setupServers(memcached_st *memc) {
-    auto servers = getServers();
-    if (!servers) {
-      return false;
-    }
-    if (MEMCACHED_SUCCESS != memcached_server_push(memc, servers)) {
-      printLastError(memc);
-      memcached_server_list_free(servers);
-      return false;
-    }
-    memcached_server_list_free(servers);
-    return true;
+  extended_option &def(const char *name, char flag, int has_arg, const char *help) {
+    return def(option{name, has_arg, nullptr, flag}, help);
   }
 
-  bool setupBehavior(memcached_st *memc) const {
-    if (MEMCACHED_SUCCESS != memcached_behavior_set_key_hash(memc, getHash())) {
-      goto failure;
-    }
-    if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, flags.binary)) {
-      goto failure;
-    }
-    if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, flags.buffer)) {
-      goto failure;
-    }
-    if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, flags.tcp_nodelay)) {
-      goto failure;
-    }
-    if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, flags.non_block)) {
-      goto failure;
-    }
-    if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_USE_UDP, flags.udp)) {
-      goto failure;
-    }
-    return true;
+  extended_option &add(extended_option ext) {
+    options.emplace_back(std::move(ext));
+    return options.back();
+  }
 
-  failure:
-    printLastError(memc);
-    return false;
+  extended_option &add(option opt, std::string help) {
+    options.emplace_back(extended_option{opt, std::move(help), nullptr, nullptr, nullptr, false});
+    return options.back();
   }
 
-  bool setupSASL(memcached_st *memc) const {
-    if (args.username) {
-      if (!LIBMEMCACHED_WITH_SASL_SUPPORT) {
-        if (!flags.quiet) {
-          std::cerr << "SASL username was supplied, but binary was not built with SASL support.\n";
-          return false;
-        }
-      }
-      if (MEMCACHED_SUCCESS != memcached_set_sasl_auth_data(memc, args.username, args.password)) {
-        printLastError(memc);
-        return false;
-      }
-    }
-    return true;
+  extended_option &add(const char *name, char flag, int has_arg, const char *help) {
+    return add(option{name, has_arg, nullptr, flag}, help);
+  }
+
+  extended_option &get(const std::string &name) {
+    // UB if not found
+    return *find(name);
+  }
+  extended_option &get(int c) {
+    // UB if not found
+    return *find(c);
+  }
+
+  const extended_option &get(const std::string &name) const {
+    // UB if not found
+    return *find(name);
+  }
+  const extended_option &get(int c) const {
+    // UB if not found
+    return *find(c);
+  }
+
+  bool has(const std::string &name) const {
+    auto found = find(name);
+    return found != options.cend();
+  }
+  bool has(int c) const {
+    auto found = find(c);
+    return found != options.cend();
+  }
+
+  bool isset(const std::string &name) const {
+    return has(name) && get(name).set;
+  }
+  bool isset(int c) const {
+    return has(c) && get(c).set;
+  }
+
+  void unset(const std::string &name) {
+    set(name, false);
+  }
+  void unset(int c) {
+    set(c, false);
   }
 
-  bool apply(memcached_st *memc) {
-    if (!setupBehavior(memc)) {
-      return false;
+  void set(const std::string &name, bool set_ = true, char *optarg_ = nullptr) {
+    if (has(name)) {
+      auto &opt = get(name);
+      opt.set = set_;
+      opt.arg = optarg_;
     }
-    if (!setupServers(memc)) {
-      return false;
+  }
+  void set(int c, bool set_ = true, char *optarg_ = nullptr) {
+    if (has(c)) {
+      auto &opt = get(c);
+      opt.set = set_;
+      opt.arg = optarg_;
     }
-    if (!setupSASL(memc)) {
-      return false;
+  }
+
+  char *argof(const std::string &name) const {
+    if (has(name)) {
+      return get(name).arg;
     }
-#if defined(_WIN32)
-    WSADATA wsaData;
-    if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
-      std::cerr << "Socket Initialization Error.\n";
-      return false;
+    return nullptr;
+  }
+  char *argof(int c) const {
+    if (has(c)) {
+      return get(c).arg;
     }
-#endif // #if defined(_WIN32)
+    return nullptr;
+  }
 
-    return true;
+  const extended_option &operator[](const std::string &name) const {
+    return get(name);
+  }
+  const extended_option &operator[](int c) const {
+    return get(c);
   }
 
-  bool parse(int argc, char *argv[]) {
-    optind = 1;
+  void print_version() const;
+  void print_help() const;
 
-    while (true) {
-      auto opt = getopt_long(argc, argv, short_opts.c_str(), long_opts.data(), nullptr);
+  bool parse(int argc, char *argv[], char ***argp = nullptr);
+  bool apply(memcached_st *memc);
 
-      if (flags.debug && opt != -1 && opt != '?') {
-        std::cerr << "Processing option '" << (char) opt << "'\n";
-      }
+private:
+  using iterator = std::vector<extended_option>::iterator;
+  using const_iterator = std::vector<extended_option>::const_iterator;
+  using predicate = std::function<bool(const extended_option &ext)>;
 
-      switch (static_cast<flag>(opt)) {
-      case flag::_success:
-        args.positional = &argv[optind];
-        return true;
-      case flag::_failure:
-        return false;
-      case flag::absolute:
-        flags.absolute = true;
-        break;
-      case flag::flush:
-        flags.flush = true;
-        break;
-      case flag::add_cmd:
-        flags.add_cmd = true;
-        flags.replace_cmd = false;
-        flags.set_cmd = false;
-        break;
-      case flag::buffer:
-        flags.buffer = true;
-        break;
-      case flag::concurrency:
-        args.concurrency = std::stoul(optarg);
-        break;
-      case flag::flags:
-        flags.flags = true;
-        break;
-      case flag::hash:
-        args.hash = optarg;
-        break;
-      case flag::load:
-        args.load = std::stoul(optarg);
-        break;
-      case flag::tcp_nodelay:
-        flags.tcp_nodelay = true;
-        break;
-      case flag::query:
-        args.query = optarg;
-        break;
-      case flag::replace_cmd:
-        flags.add_cmd = false;
-        flags.replace_cmd = true;
-        flags.set_cmd = false;
-        break;
-      case flag::set_cmd:
-        flags.add_cmd = false;
-        flags.replace_cmd = false;
-        flags.set_cmd = true;
-        break;
-      case flag::udp:
-        flags.udp = true;
-        break;
-      case flag::version:
-        flags.version = true;
-        break;
-      case flag::analyze:
-        flags.analyze = true;
-        break;
-      case flag::binary:
-        flags.binary = true;
-        break;
-      case flag::debug:
-        flags.debug = true;
-        flags.quiet = false;
-        flags.verbose = true;
-        break;
-      case flag::expire:
-        args.expire = std::stoul(optarg);
-        break;
-      case flag::file:
-        args.file = optarg;
-        break;
-      case flag::help:
-        flags.help = true;
-        break;
-      case flag::non_block:
-        flags.non_block = true;
-        break;
-      case flag::password:
-        args.password = optarg;
-        break;
-      case flag::quiet:
-        flags.debug = false;
-        flags.quiet = true;
-        flags.verbose = false;
-        break;
-      case flag::repeat:
-        args.repeat = std::stoul(optarg);
-        break;
-      case flag::servers:
-        args.servers = optarg;
-        break;
-      case flag::test:
-        args.test = optarg;
-        break;
-      case flag::username:
-        args.username = optarg;
-        break;
-      case flag::verbose:
-        flags.quiet = false;
-        flags.verbose = true;
-        break;
-      case flag::zero:
-        flags.zero = true;
-        break;
-      }
-    }
+  const_iterator find(const predicate &pred) const {
+    return std::find_if(options.cbegin(), options.cend(), pred);
+  }
+  const_iterator find(const std::string &name) const {
+    return find([&name](const extended_option &ext) {
+      return ext.opt.name && ext.opt.name == name;
+    });
+  }
+  const_iterator find(int c) const {
+    return find([c](const extended_option &ext) {
+      return ext.opt.val == c || (c == 1 && ext.opt.val == '-');
+    });
+  }
+
+  iterator find(const predicate &pred) {
+    return std::find_if(options.begin(), options.end(), pred);
+  }
+  iterator find(const std::string &name) {
+    return find([&name](const extended_option &ext) {
+      return ext.opt.name && ext.opt.name == name;
+    });
+  }
+  iterator find(int c) {
+    return find([c](const extended_option &ext) {
+      return ext.opt.val == c || (c == 1 && ext.opt.val == '-');
+    });
   }
 };