bin: consolidate clients
authorMichael Wallner <mike@php.net>
Mon, 16 Nov 2020 22:46:40 +0000 (23:46 +0100)
committerMichael Wallner <mike@php.net>
Mon, 16 Nov 2020 22:46:40 +0000 (23:46 +0100)
src/bin/common/CMakeLists.txt
src/bin/common/options.cpp [new file with mode: 0644]
src/bin/common/options.hpp
src/bin/memcat.cc
src/bin/memcp.cc
src/bin/memdump.cc
src/bin/memerror.cc
src/bin/memexist.cc

index b068045f67343f1ec18174c81edfda8091c0e40e..7a65bedd0dd70e38bb46bfa75e2577d230381c0f 100644 (file)
@@ -1,4 +1,4 @@
-add_library(libclient_common STATIC utilities.cc generator.cc)
+add_library(libclient_common STATIC utilities.cc generator.cc options.cpp)
 add_library(client_common ALIAS libclient_common)
 target_link_libraries(libclient_common PUBLIC libmemcached)
 target_include_directories(libclient_common PUBLIC
diff --git a/src/bin/common/options.cpp b/src/bin/common/options.cpp
new file mode 100644 (file)
index 0000000..4583e7b
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+    +--------------------------------------------------------------------+
+    | libmemcached - C/C++ Client Library for memcached                  |
+    +--------------------------------------------------------------------+
+    | Redistribution and use in source and binary forms, with or without |
+    | modification, are permitted under the terms of the BSD license.    |
+    | You should have received a copy of the license in a bundled file   |
+    | named LICENSE; in case you did not receive a copy you can review   |
+    | the terms online at: https://opensource.org/licenses/BSD-3-Clause  |
+    +--------------------------------------------------------------------+
+    | Copyright (c) 2006-2014 Brian Aker   https://datadifferential.com/ |
+    | Copyright (c) 2020 Michael Wallner   <mike@php.net>                |
+    +--------------------------------------------------------------------+
+*/
+
+#include "options.hpp"
+#include <array>
+
+option client_options::null_opt{};
+const client_options::extended_option client_options::null_ext_opt{
+    client_options::null_opt,
+    {}, {}, {},nullptr, false
+};
+
+void client_options::print_version() const {
+  std::cout << prog_name << " v" << prog_vers << " (libmemcached v" << LIBMEMCACHED_VERSION_STRING << ")"
+            << std::endl;
+}
+
+void client_options::print_help() const {
+  print_version();
+  std::cout << "\n\t" << prog_desc << "\n\n";
+  std::cout << "Usage:\n\t" << prog_name << " -[";
+  for (const auto &opt : options) {
+    if (!opt.opt.has_arg && opt.opt.val != '-') {
+      std::cout << (char) opt.opt.val;
+    }
+  }
+  std::cout << "] [-";
+  for (const auto &ext : options) {
+    if (ext.opt.has_arg) {
+      std::cout << (char) ext.opt.val;
+      if ((&ext) != &*options.rbegin())
+        std::cout << '|';
+    }
+  }
+  std::cout << " <arg>] ";
+
+  if (prog_argp) {
+    std::cout << prog_argp;
+  }
+  std::cout << "\n\nOptions:\n";
+  for (const auto &ext : options) {
+    if (ext.opt.val == '-' || !(ext.opt.val || ext.opt.name)) {
+      continue;
+    }
+    std::cout << "\t";
+    if (ext.opt.val) {
+      std::cout << "-" << (char) ext.opt.val;
+      if (ext.opt.name) {
+        std::cout << "|";
+      }
+    } else {
+      std::cout << "   ";
+    }
+    if (ext.opt.name) {
+      std::cout << "--" << ext.opt.name << " ";
+    } else {
+      std::cout << " ";
+    }
+    if (ext.opt.has_arg) {
+      if (ext.opt.has_arg == optional_argument) {
+        std::cout << "[";
+      } else {
+        std::cout << "<";
+      }
+      std::cout << "arg";
+      if (ext.opt.has_arg == optional_argument) {
+        std::cout << "]";
+      } else {
+        std::cout << ">";
+      }
+    }
+    std::cout << "\n\t\t" << ext.help << "\n";
+  }
+
+  const auto &servers = get("servers");
+  if (&servers != &null_ext_opt) {
+    std::cout << "\nEnvironment:\n";
+    std::cout << "\tMEMCACHED_SERVERS=\n";
+    std::cout << "\t\tList of servers to use if `-s|--servers` was not provided.\n";
+  }
+  std::cout << std::endl;
+}
+
+bool client_options::parse(int argc, char **argv, char ***argp) {
+  /* extern */ optind = 1;
+  auto &debug = get("debug");
+  std::string short_opts{};
+  std::vector<option> long_opts{};
+
+  short_opts.reserve(options.size() * 3);
+  long_opts.reserve(options.size() + 1);
+
+  for (const auto &ext : options) {
+    if (ext.opt.val) {
+      short_opts.push_back(ext.opt.val);
+      for (int i = 0; i < ext.opt.has_arg; ++i) {
+        short_opts.push_back(':');
+      }
+    }
+    if (ext.opt.name) {
+      long_opts.push_back(ext.opt);
+    }
+  }
+  long_opts.push_back({});
+
+  while (true) {
+    auto opt = getopt_long(argc, argv, short_opts.c_str(), long_opts.data(), nullptr);
+
+    if (debug.set && opt > 0) {
+      std::cerr << "Processing option '" << (char) opt << "' (" << opt << ")\n";
+    }
+    if (opt == '?') {
+      return false;
+    }
+    if (opt == -1) {
+      if (argp) {
+        *argp = &argv[optind];
+      }
+      return true;
+    }
+
+    auto &ext_opt = get(opt);
+
+    ext_opt.set = true;
+    ext_opt.arg = optarg;
+
+    if (ext_opt.parse) {
+      if (!ext_opt.parse(*this, ext_opt)) {
+        return false;
+      }
+    }
+  }
+}
+
+bool client_options::apply(memcached_st *memc) {
+#ifdef _WIN32
+  WSADATA wsaData;
+  if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
+    std::cerr << "Socket Initialization Error.\n";
+    return false;
+  }
+#endif // _WIN32
+
+  for (auto &opt : options) {
+    if (opt.apply) {
+      if (!opt.apply(*this, opt, memc)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
index a5d6bdf72f955347c213fb04f10c29c280fd3f8c..27ba5433d6ac5b1d7a36a7643c09dd7e1debac2e 100644 (file)
 
 #pragma once
 
+#include <algorithm>
 #include <cstdint>
 #include <climits>
+#include <functional>
 #include <getopt.h>
 #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',
-  };
-
-  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}
-  {
+#include "libmemcached/common.h"
 
-# 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;
+    const 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";
+            return false;
           }
-          std::cout << "=arg";
-          if (opt.has_arg == optional_argument) {
-            std::cout << "]";
+        }
+        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;
       }
-    }
-
-    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);
   }
 
-  bool apply(memcached_st *memc) {
-    if (!setupBehavior(memc)) {
-      return false;
-    }
-    if (!setupServers(memc)) {
-      return false;
-    }
-    if (!setupSASL(memc)) {
-      return false;
+  extended_option &get(const std::string &name) {
+    // UB if not found
+    return *std::find_if(options.begin(), options.end(), [&name](extended_option &ext) {
+      return ext.opt.name && ext.opt.name == name;
+    });
+  }
+  extended_option &get(int c) {
+    // UB if not found
+    return *std::find_if(options.begin(), options.end(), [c](extended_option &ext) {
+      return ext.opt.val == c || (c == 1 && ext.opt.val == '-');
+    });
+  }
+
+  const extended_option &get(const std::string &name) const {
+    for (const auto &ext_opt : options) {
+      if (ext_opt.opt.name && ext_opt.opt.name == name) {
+        return ext_opt;
+      }
     }
-#if defined(_WIN32)
-    WSADATA wsaData;
-    if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
-      std::cerr << "Socket Initialization Error.\n";
-      return false;
+    return null_ext_opt;
+  }
+  const extended_option &get(int c) const {
+    for (const auto &ext_opt : options) {
+      if (ext_opt.opt.val == c) {
+        return ext_opt;
+      } else if (c == 1 && ext_opt.opt.val == '-') {
+        // GNU argv extension
+        return ext_opt;
+      }
     }
-#endif // #if defined(_WIN32)
+    return null_ext_opt;
+  }
 
-    return true;
+  bool isset(const std::string &name) const {
+    return get(name).set;
+  }
+  bool isset(int c) const {
+    return get(c).set;
   }
 
-  bool parse(int argc, char *argv[]) {
-    optind = 1;
+  void unset(const std::string &name) {
+    auto &opt = get(name);
+    opt.set = false;
+    opt.arg = nullptr;
+  }
+  void unset(int c) {
+    auto &opt = get(c);
+    opt.set = false;
+    opt.arg = nullptr;
+  }
 
-    while (true) {
-      auto opt = getopt_long(argc, argv, short_opts.c_str(), long_opts.data(), nullptr);
+  void set(const std::string &name, bool set_ = true, const char *optarg_ = nullptr) {
+    auto &opt = get(name);
+    opt.set = set_;
+    opt.arg = optarg_;
+  }
+  void set(int c, bool set_ = true, const char *optarg_ = nullptr) {
+    auto &opt = get(c);
+    opt.set = set_;
+    opt.arg = optarg_;
+  }
 
-      if (flags.debug && opt != -1 && opt != '?') {
-        std::cerr << "Processing option '" << (char) opt << "'\n";
-      }
+  const char *argof(const std::string &name) const {
+    return get(name).arg;
+  }
+  const char *argof(int c) const {
+    return get(c).arg;
+  }
 
-      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 extended_option &operator[](const std::string &name) const {
+    return get(name);
+  }
+  const extended_option &operator[](int c) const {
+    return get(c);
   }
+
+  void print_version() const;
+  void print_help() const;
+
+  bool parse(int argc, char *argv[], char ***argp = nullptr);
+  bool apply(memcached_st *memc);
+
+private:
+  static option null_opt;
+  static const extended_option null_ext_opt;
 };
index 00519b520bd9f1193d86a49c7c4b6bdfe1a6c78c..56b5a46c9e9ee76bdc889beeb4a3a559d7b14cb4 100644 (file)
@@ -19,7 +19,6 @@
 #define PROGRAM_DESCRIPTION "Cat a set of key values to stdout."
 #define PROGRAM_VERSION     "1.1"
 
-#include "libmemcached/common.h"
 #include "common/options.hpp"
 
 #include <iostream>
@@ -32,13 +31,13 @@ memcached_return_t memcat(const client_options &opt, memcached_st *memc, const c
   auto val = memcached_get(memc, key, strlen(key), &len, &flags, &rc);
 
   if (MEMCACHED_SUCCESS == rc) {
-    if (opt.flags.verbose) {
+    if (opt.isset("verbose")) {
       ref << "key: " << key << "\n";
     }
-    if (opt.flags.flags) {
+    if (opt.isset("flags")) {
       ref << "flags: " << flags << "\n";
     }
-    if (opt.flags.verbose) {
+    if (opt.isset("verbose")) {
       ref << "value: ";
     }
     ref.write(val, len);
@@ -52,41 +51,27 @@ memcached_return_t memcat(const client_options &opt, memcached_st *memc, const c
 }
 
 int main(int argc, char *argv[]) {
+  char **argp = nullptr;
   memcached_st memc;
-  client_options opt{PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_DESCRIPTION, {
-      client_options::flag::help,
-      client_options::flag::version,
-      client_options::flag::verbose,
-      client_options::flag::debug,
-      client_options::flag::quiet,
-      client_options::flag::servers,
-      client_options::flag::binary,
-      client_options::flag::username,
-      client_options::flag::password,
-      client_options::flag::hash,
-      client_options::flag::flags,
-      client_options::flag::file,
-  }};
-
-  if (!opt.parse(argc, argv)) {
-    exit(EXIT_FAILURE);
-  }
-  if (opt.flags.help) {
-    opt.printHelp("key [ key ... ]");
-    exit(EXIT_SUCCESS);
+  client_options opt{PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_DESCRIPTION, "key [ key ... ]"};
+
+  for (const auto &def : opt.defaults) {
+    opt.add(def);
   }
-  if (opt.flags.version) {
-    opt.printVersion();
-    exit(EXIT_SUCCESS);
+  opt.add("flags", 'F', no_argument, "Display key flags, too.");
+  opt.add("file", 'f', required_argument, "Output to file instead of standard output.");
+
+  if (!opt.parse(argc, argv, &argp)) {
+    exit(EXIT_FAILURE);
   }
 
-  if (opt.flags.quiet && !opt.args.file) {
+  if (opt.isset("quiet") && !opt.isset("file")) {
     std::cerr << "--quiet operation was requested, but --file was not set.\n";
     exit(EXIT_FAILURE);
   }
 
   if (!memcached_create(&memc)) {
-    if (!opt.flags.quiet) {
+    if (!opt.isset("quiet")) {
       std::cerr << "Failed to initialize memcached client.\n";
     }
     exit(EXIT_FAILURE);
@@ -97,22 +82,23 @@ int main(int argc, char *argv[]) {
     exit(EXIT_FAILURE);
   }
 
-  if (!*opt.args.positional) {
-    if (!opt.flags.quiet) {
+  if (!*argp) {
+    if (!opt.isset("quiet")) {
       std::cerr << "No key(s) provided.\n";
-      memcached_free(&memc);
-      exit(EXIT_FAILURE);
     }
+    memcached_free(&memc);
+    exit(EXIT_FAILURE);
   }
 
   auto exit_code = EXIT_SUCCESS;
-  for (auto arg = opt.args.positional; *arg; ++arg) {
+  for (auto arg = argp; *arg; ++arg) {
     auto key = *arg;
     if (*key) {
       memcached_return_t rc;
-      if (opt.args.file && *opt.args.file) {
-        std::ofstream file{opt.args.file, std::ios::binary};
-        rc = memcat(opt, &memc, key, file);
+      auto file = opt.argof("file");
+      if (file && *file) {
+        std::ofstream stream{file, std::ios::binary};
+        rc = memcat(opt, &memc, key, stream);
       } else {
         rc = memcat(opt, &memc, key, std::cout);
       }
@@ -120,12 +106,12 @@ int main(int argc, char *argv[]) {
         exit_code = EXIT_FAILURE;
 
         if (MEMCACHED_NOTFOUND == rc) {
-          if (opt.flags.verbose) {
+          if (opt.isset("verbose")) {
             std::cerr << "not found: " << key << "\n";
           }
           // continue;
         } else {
-          if (!opt.flags.quiet) {
+          if (!opt.isset("quiet")) {
             std::cerr << memcached_last_error_message(&memc) << "\n";
           }
           break;
index 632cea3025dc9e20b5d2b44de8481760606d756a..e149ef316df876d531b4019596f6a53abd04cedd 100644 (file)
 
 #include "mem_config.h"
 
-#include <cerrno>
-#include <climits>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <fcntl.h>
-#include <getopt.h>
-#include <iostream>
-#ifdef HAVE_STRINGS_H
-#  include <strings.h>
-#endif
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "libmemcached-1.0/memcached.h"
-
-#include "client_options.h"
-#include "utilities.h"
-
 #define PROGRAM_NAME        "memcp"
 #define PROGRAM_DESCRIPTION "Copy a set of files to a memcached cluster."
+#define PROGRAM_VERSION     "1.1"
+
+#include "common/options.hpp"
 
-/* Prototypes */
-static void options_parse(int argc, char *argv[]);
+#include <climits>
+#include <cstdlib>
+#include <fstream>
+#include <sstream>
 
-static bool opt_binary = false;
-static bool opt_udp = false;
-static bool opt_buffer = false;
-static int opt_verbose = 0;
-static char *opt_servers = NULL;
-static char *opt_hash = NULL;
-static int opt_method = OPT_SET;
-static uint32_t opt_flags = 0;
-static time_t opt_expires = 0;
-static char *opt_username;
-static char *opt_passwd;
+struct memcp_file {
+  enum key {
+    basename,
+    relative,
+    absolute
+  } type;
+  enum op {
+    SET,
+    ADD,
+    REPLACE
+  } mode;
+  const char *path;
+  uint32_t flags;
+  time_t expire;
+};
 
-static long strtol_wrapper(const char *nptr, int base, bool *error) {
-  long val;
-  char *endptr;
+static void add_file(std::vector<memcp_file> &files, const client_options &opt, const char *file) {
+  memcp_file::key type = memcp_file::basename;
+  memcp_file::op mode = memcp_file::SET;
+  uint32_t flags = 0;
+  time_t expire = 0;
 
-  errno = 0; /* To distinguish success/failure after call */
-  val = strtol(nptr, &endptr, base);
+  if (opt.isset("absolute")) {
+    type = memcp_file::absolute;
+  } else if (opt.isset("relative")) {
+    type = memcp_file::relative;
+  }
 
-  /* Check for various possible errors */
+  if (opt.isset("replace")) {
+    mode = memcp_file::REPLACE;
+  } else if (opt.isset("add")) {
+    mode = memcp_file::ADD;
+  }
 
-  if ((errno == ERANGE and (val == LONG_MAX or val == LONG_MIN)) or (errno && val == 0)) {
-    *error = true;
-    return 0;
+  if (auto flags_str = opt.argof("flags")) {
+    flags = std::stoul(flags_str);
+  }
+  if (auto expire_str = opt.argof("expire")) {
+    expire = std::stoul(expire_str);
   }
 
-  if (endptr == nptr) {
-    *error = true;
-    return 0;
+  if (opt.isset("debug")) {
+    auto mode_str = mode == memcp_file::REPLACE ? "REPLACE" : mode == memcp_file::ADD ? "ADD" : "SET";
+    std::cerr << "Scheduling " << mode_str << " '" << file << "' (expire=" << expire << ", flags=" << flags << ").\n";
   }
 
-  *error = false;
-  return val;
+  files.emplace_back(memcp_file{type, mode, file, flags, expire});
 }
 
 int main(int argc, char *argv[]) {
-  options_parse(argc, argv);
+  memcached_st memc;
+  std::vector<memcp_file> files{};
+  client_options opt{PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_DESCRIPTION, "file [file ...]"};
+
+  opt.add(nullptr, '-', no_argument, "GNU argv extension")
+      .parse = [&files](client_options &opt_, client_options::extended_option &ext) {
+    add_file(files, opt_, ext.arg);
+    return true;
+  };
 
-  if (optind >= argc) {
-    fprintf(stderr, "Expected argument after options\n");
-    exit(EXIT_FAILURE);
+  for (const auto &def : opt.defaults) {
+    opt.add(def);
   }
 
-  initialize_sockets();
-
-  memcached_st *memc = memcached_create(NULL);
+  opt.add("set", 'S', no_argument, "Perform SET operations.")
+      .parse = [](client_options &opt_, client_options::extended_option &) {
+    opt_.unset("add");
+    opt_.unset("replace");
+    return true;
+  };
+  opt.add("add", 'A', no_argument, "Perform ADD operations.")
+      .parse = [](client_options &opt_, client_options::extended_option &) {
+    opt_.unset("set");
+    opt_.unset("replace");
+    return true;
+  };
+  opt.add("replace", 'R', no_argument, "Perform REPLACE operations.")
+      .parse = [](client_options &opt_, client_options::extended_option &) {
+    opt_.unset("set");
+    opt_.unset("add");
+    return true;
+  };
 
-  if (opt_udp) {
-    if (opt_verbose) {
-      std::cout << "Enabling UDP" << std::endl;
+  opt.add("udp", 'U', no_argument, "Use UDP.")
+      .apply = [](const client_options &opt, const client_options::extended_option &ext, memcached_st *memc) {
+    if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_USE_UDP, ext.set)) {
+      if (!opt.isset("quiet")) {
+        std::cerr << memcached_last_error_message(memc) << "\n";
+      }
+      return false;
     }
+    return true;
+  };
+  opt.add("flags", 'F', required_argument, "Set key flags, too.");
+  opt.add("expire", 'e', required_argument, "Set expire time, too.");
 
-    if (memcached_failed(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_USE_UDP, opt_udp))) {
-      memcached_free(memc);
-      std::cerr << "Could not enable UDP protocol." << std::endl;
-      return EXIT_FAILURE;
-    }
-  }
+  opt.add("basename", '.', no_argument, "Use basename of path as key (default).");
+  opt.add("relative", '+', no_argument, "Use relative path (as passed), instead of basename only.");
+  opt.add("absolute", '/', no_argument, "Use absolute path (real path), instead of basename only.");
 
-  if (opt_buffer) {
-    if (opt_verbose) {
-      std::cout << "Enabling MEMCACHED_BEHAVIOR_BUFFER_REQUESTS" << std::endl;
-    }
+  // defaults
+  opt.set("set");
+  opt.set("basename");
 
-    if (memcached_failed(
-            memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, opt_buffer))) {
-      memcached_free(memc);
-      std::cerr << "Could not enable MEMCACHED_BEHAVIOR_BUFFER_REQUESTS." << std::endl;
-      return EXIT_FAILURE;
-    }
+  char **argp = nullptr;
+  if (!opt.parse(argc, argv, &argp)) {
+    exit(EXIT_FAILURE);
   }
 
-  process_hash_option(memc, opt_hash);
-
-  if (opt_servers == NULL) {
-    char *temp;
-
-    if ((temp = getenv("MEMCACHED_SERVERS"))) {
-      opt_servers = strdup(temp);
-    }
-#if 0
-    else if (argc >= 1 and argv[--argc])
-    {
-      opt_servers= strdup(argv[argc]);
-    }
-#endif
-
-    if (opt_servers == NULL) {
-      std::cerr << "No Servers provided" << std::endl;
-      exit(EXIT_FAILURE);
+  if (!memcached_create(&memc)) {
+    if (!opt.isset("quiet")) {
+      std::cerr << "Failed to initialize memcached client.\n";
     }
+    exit(EXIT_FAILURE);
   }
 
-  memcached_server_st *servers = memcached_servers_parse(opt_servers);
-  if (servers == NULL or memcached_server_list_count(servers) == 0) {
-    std::cerr << "Invalid server list provided:" << opt_servers << std::endl;
-    return EXIT_FAILURE;
-  }
-
-  memcached_server_push(memc, servers);
-  memcached_server_list_free(servers);
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, opt_binary);
-  if (opt_username and LIBMEMCACHED_WITH_SASL_SUPPORT == 0) {
-    memcached_free(memc);
-    std::cerr << "--username was supplied, but binary was not built with SASL support."
-              << std::endl;
-    return EXIT_FAILURE;
+  if (!opt.apply(&memc)) {
+    exit(EXIT_FAILURE);
   }
 
-  if (opt_username) {
-    memcached_return_t ret;
-    if (memcached_failed(ret = memcached_set_sasl_auth_data(memc, opt_username, opt_passwd))) {
-      std::cerr << memcached_last_error_message(memc) << std::endl;
-      memcached_free(memc);
-      return EXIT_FAILURE;
+  if (files.empty()) {
+    if (!*argp) {
+      if (!opt.isset("quiet")) {
+        std::cerr << "No file(s) provided.\n";
+      }
+      memcached_free(&memc);
+      exit(EXIT_FAILURE);
+    }
+    for (auto arg = argp; *arg; ++arg) {
+      add_file(files, opt, *arg);
     }
   }
 
-  int exit_code = EXIT_SUCCESS;
-  while (optind < argc) {
-    int fd = open(argv[optind], O_RDONLY);
-    if (fd < 0) {
-      std::cerr << "memcp " << argv[optind] << " " << strerror(errno) << std::endl;
-      optind++;
-      exit_code = EXIT_FAILURE;
-      continue;
-    }
+  auto exit_code = EXIT_SUCCESS;
+  for (const auto &file : files) {
+    auto filename = file.path;
+    std::ifstream filestream{filename, std::ios::in|std::ios::binary};
 
-    struct stat sbuf;
-    if (fstat(fd, &sbuf) == -1) {
-      std::cerr << "memcp " << argv[optind] << " " << strerror(errno) << std::endl;
-      optind++;
+    if (!filestream) {
+      if (!opt.isset("quiet")) {
+        std::cerr << "Could not open file '" << filename << "'.\n";
+      }
       exit_code = EXIT_FAILURE;
-      continue;
-    }
-
-    char *ptr = rindex(argv[optind], '/');
-    if (ptr) {
-      ptr++;
+      // continue;
     } else {
-      ptr = argv[optind];
-    }
-
-    if (opt_verbose) {
-      static const char *opstr[] = {"set", "add", "replace"};
-      printf("op: %s\nsource file: %s\nlength: %lu\n"
-             "key: %s\nflags: %x\nexpires: %lu\n",
-             opstr[opt_method - OPT_SET], argv[optind], (unsigned long) sbuf.st_size, ptr,
-             opt_flags, (unsigned long) opt_expires);
-    }
-
-    // The file may be empty
-    char *file_buffer_ptr = NULL;
-    if (sbuf.st_size > 0) {
-      if ((file_buffer_ptr = (char *) malloc(sizeof(char) * (size_t) sbuf.st_size)) == NULL) {
-        std::cerr << "Error allocating file buffer(" << strerror(errno) << ")" << std::endl;
-        close(fd);
-        exit(EXIT_FAILURE);
+      const char *path;
+      char rpath[PATH_MAX+1];
+
+      if (file.type == memcp_file::relative) {
+        path = filename;
+      } else if (file.type == memcp_file::absolute) {
+        path = realpath(filename, rpath);
+        if (!path) {
+          if (!opt.isset("quiet")) {
+            perror(filename);
+          }
+          exit_code = EXIT_FAILURE;
+          continue;
+        }
+      } else {
+        path = basename(filename);
       }
 
-      ssize_t read_length;
-      if ((read_length = ::read(fd, file_buffer_ptr, (size_t) sbuf.st_size)) == -1) {
-        std::cerr << "Error while reading file " << file_buffer_ptr << " (" << strerror(errno)
-                  << ")" << std::endl;
-        close(fd);
-        free(file_buffer_ptr);
-        exit(EXIT_FAILURE);
+      std::ostringstream data{};
+      data << filestream.rdbuf();
+
+      memcached_return_t rc;
+      const char *mode;
+      if (file.mode == memcp_file::REPLACE) {
+        mode = "replace";
+        rc = memcached_replace(&memc, path, strlen(path), data.str().c_str(), data.str().length(),
+                               file.expire, file.flags);
+      } else if (file.mode == memcp_file::ADD) {
+        mode = "add";
+        rc = memcached_add(&memc, path, strlen(path), data.str().c_str(), data.str().length(),
+                           file.expire, file.flags);
+      } else {
+        mode = "set";
+        rc = memcached_set(&memc, path, strlen(path), data.str().c_str(), data.str().length(),
+                           file.expire, file.flags);
       }
 
-      if (read_length != sbuf.st_size) {
-        std::cerr << "Failure while reading file. Read length was not equal to stat() length"
-                  << std::endl;
-        close(fd);
-        free(file_buffer_ptr);
-        exit(EXIT_FAILURE);
-      }
-    }
-
-    memcached_return_t rc;
-    if (opt_method == OPT_ADD) {
-      rc = memcached_add(memc, ptr, strlen(ptr), file_buffer_ptr, (size_t) sbuf.st_size,
-                         opt_expires, opt_flags);
-    } else if (opt_method == OPT_REPLACE) {
-      rc = memcached_replace(memc, ptr, strlen(ptr), file_buffer_ptr, (size_t) sbuf.st_size,
-                             opt_expires, opt_flags);
-    } else {
-      rc = memcached_set(memc, ptr, strlen(ptr), file_buffer_ptr, (size_t) sbuf.st_size,
-                         opt_expires, opt_flags);
-    }
-
-    if (memcached_failed(rc)) {
-      std::cerr << "Error occurred during memcached_set(): " << memcached_last_error_message(memc)
-                << std::endl;
-      exit_code = EXIT_FAILURE;
-    }
-
-    ::free(file_buffer_ptr);
-    ::close(fd);
-    optind++;
-  }
-
-  if (opt_verbose) {
-    std::cout << "Calling memcached_free()" << std::endl;
-  }
-
-  memcached_free(memc);
-
-  if (opt_servers) {
-    free(opt_servers);
-  }
-
-  if (opt_hash) {
-    free(opt_hash);
-  }
-
-  return exit_code;
-}
-
-static void options_parse(int argc, char *argv[]) {
-  memcached_programs_help_st help_options[] = {
-      {0},
-  };
-
-  static struct option long_options[] = {
-      {(OPTIONSTRING) "version", no_argument, NULL, OPT_VERSION},
-      {(OPTIONSTRING) "help", no_argument, NULL, OPT_HELP},
-      {(OPTIONSTRING) "quiet", no_argument, NULL, OPT_QUIET},
-      {(OPTIONSTRING) "udp", no_argument, NULL, OPT_UDP},
-      {(OPTIONSTRING) "buffer", no_argument, NULL, OPT_BUFFER},
-      {(OPTIONSTRING) "verbose", no_argument, &opt_verbose, OPT_VERBOSE},
-      {(OPTIONSTRING) "debug", no_argument, &opt_verbose, OPT_DEBUG},
-      {(OPTIONSTRING) "servers", required_argument, NULL, OPT_SERVERS},
-      {(OPTIONSTRING) "flag", required_argument, NULL, OPT_FLAG},
-      {(OPTIONSTRING) "expire", required_argument, NULL, OPT_EXPIRE},
-      {(OPTIONSTRING) "set", no_argument, NULL, OPT_SET},
-      {(OPTIONSTRING) "add", no_argument, NULL, OPT_ADD},
-      {(OPTIONSTRING) "replace", no_argument, NULL, OPT_REPLACE},
-      {(OPTIONSTRING) "hash", required_argument, NULL, OPT_HASH},
-      {(OPTIONSTRING) "binary", no_argument, NULL, OPT_BINARY},
-      {(OPTIONSTRING) "username", required_argument, NULL, OPT_USERNAME},
-      {(OPTIONSTRING) "password", required_argument, NULL, OPT_PASSWD},
-      {0, 0, 0, 0},
-  };
+      if (MEMCACHED_SUCCESS != rc) {
+        exit_code = EXIT_FAILURE;
 
-  bool opt_version = false;
-  bool opt_help = false;
-  int option_index = 0;
-
-  while (1) {
-    int option_rv = getopt_long(argc, argv, "Vhvds:", long_options, &option_index);
-
-    if (option_rv == -1)
-      break;
-
-    switch (option_rv) {
-    case 0:
-      break;
-
-    case OPT_BINARY:
-      opt_binary = true;
-      break;
-
-    case OPT_VERBOSE: /* --verbose or -v */
-      opt_verbose = OPT_VERBOSE;
-      break;
-
-    case OPT_DEBUG: /* --debug or -d */
-      opt_verbose = OPT_DEBUG;
-      break;
-
-    case OPT_VERSION: /* --version or -V */
-      opt_version = true;
-      break;
-
-    case OPT_HELP: /* --help or -h */
-      opt_help = true;
-      break;
-
-    case OPT_SERVERS: /* --servers or -s */
-      opt_servers = strdup(optarg);
-      break;
-
-    case OPT_FLAG: /* --flag */
-    {
-      bool strtol_error;
-      opt_flags = (uint32_t) strtol_wrapper(optarg, 16, &strtol_error);
-      if (strtol_error == true) {
-        fprintf(stderr, "Bad value passed via --flag\n");
-        exit(1);
+        auto error = memcached_last_error(&memc)
+            ? memcached_last_error_message(&memc)
+            : memcached_strerror(&memc, rc);
+        std::cerr << "Error occurred during memcached_" << mode <<"('" << path << "'): " << error << "\n";
+        break;
       }
-    } break;
 
-    case OPT_EXPIRE: /* --expire */
-    {
-      bool strtol_error;
-      opt_expires = (time_t) strtol_wrapper(optarg, 10, &strtol_error);
-      if (strtol_error == true) {
-        fprintf(stderr, "Bad value passed via --expire\n");
-        exit(1);
+      if (opt.isset("verbose")) {
+        std::cerr << path << "\n";
       }
-    } break;
-
-    case OPT_SET:
-      opt_method = OPT_SET;
-      break;
-
-    case OPT_REPLACE:
-      opt_method = OPT_REPLACE;
-      break;
-
-    case OPT_ADD:
-      opt_method = OPT_ADD;
-      break;
-
-    case OPT_HASH:
-      opt_hash = strdup(optarg);
-      break;
-
-    case OPT_USERNAME:
-      opt_username = optarg;
-      break;
-
-    case OPT_PASSWD:
-      opt_passwd = optarg;
-      break;
-
-    case OPT_QUIET:
-      close_stdio();
-      break;
-
-    case OPT_UDP:
-      opt_udp = true;
-      break;
-
-    case OPT_BUFFER:
-      opt_buffer = true;
-      break;
-
-    case '?':
-      /* getopt_long already printed an error message. */
-      exit(1);
-    default:
-      abort();
     }
   }
 
-  if (opt_version) {
-    version_command(PROGRAM_NAME);
-    exit(EXIT_SUCCESS);
-  }
-
-  if (opt_help) {
-    help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options);
-    exit(EXIT_SUCCESS);
-  }
+  memcached_free(&memc);
+  exit(exit_code);
 }
index 6d31780c0c3b06dadb5401513b5dacb2b9c28cd3..8c5690d5d3f27af36b8294c7ba26e1ff445d4d0d 100644 (file)
     +--------------------------------------------------------------------+
 */
 
+#include <fstream>
 #include "mem_config.h"
 
-#include <cerrno>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <fcntl.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <iostream>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "libmemcached-1.0/memcached.h"
-
-#include "client_options.h"
-#include "utilities.h"
-
 #define PROGRAM_NAME        "memdump"
 #define PROGRAM_DESCRIPTION "Dump all values from one or many servers."
+#define PROGRAM_VERSION     "1.1"
 
-/* Prototypes */
-static void options_parse(int argc, char *argv[]);
-
-static bool opt_binary = 0;
-static int opt_verbose = 0;
-static char *opt_servers = NULL;
-static char *opt_hash = NULL;
-static char *opt_username;
-static char *opt_passwd;
-
-/* Print the keys and counter how many were found */
-static memcached_return_t key_printer(const memcached_st *, const char *key, size_t key_length,
-                                      void *) {
-  std::cout.write(key, key_length);
-  std::cout << std::endl;
+#include "common/options.hpp"
 
+static memcached_return_t print(const memcached_st *, const char *k, size_t l, void *ctx) {
+  auto out = static_cast<std::ostream *>(ctx);
+  out->write(k, l);
+  out->put('\n');
   return MEMCACHED_SUCCESS;
 }
 
 int main(int argc, char *argv[]) {
-  memcached_dump_fn callbacks[1];
-
-  callbacks[0] = &key_printer;
-
-  options_parse(argc, argv);
+  client_options opt{PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_DESCRIPTION};
 
-  if (opt_servers == NULL) {
-    char *temp;
-
-    if ((temp = getenv("MEMCACHED_SERVERS"))) {
-      opt_servers = strdup(temp);
-    } else if (argc > 1 and argv[--argc]) {
-      opt_servers = strdup(argv[argc]);
-    }
-
-    if (opt_servers == NULL) {
-      std::cerr << "No Servers provided" << std::endl;
-      exit(EXIT_FAILURE);
+  for (const auto &def : opt.defaults) {
+    switch (def.opt.val) {
+    case 'H': // no need for --hash
+    case 'b': // binary proto not available
+      break;
+    default:
+      opt.add(def);
     }
   }
 
-  memcached_server_st *servers = memcached_servers_parse(opt_servers);
-  if (servers == NULL or memcached_server_list_count(servers) == 0) {
-    std::cerr << "Invalid server list provided:" << opt_servers << std::endl;
-    return EXIT_FAILURE;
-  }
+  opt.add("file", 'f', required_argument, "Output to file instead of standard output.");
 
-  memcached_st *memc = memcached_create(NULL);
-  if (memc == NULL) {
-    std::cerr << "Could not allocate a memcached_st structure.\n" << std::endl;
-    return EXIT_FAILURE;
+  if (!opt.parse(argc, argv)) {
+    exit(EXIT_FAILURE);
   }
-  process_hash_option(memc, opt_hash);
-
-  memcached_server_push(memc, servers);
-  memcached_server_list_free(servers);
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, (uint64_t) opt_binary);
 
-  if (opt_username and LIBMEMCACHED_WITH_SASL_SUPPORT == 0) {
-    memcached_free(memc);
-    std::cerr << "--username was supplied, but binary was not built with SASL support."
-              << std::endl;
-    return EXIT_FAILURE;
+  if (opt.isset("quiet") && !opt.isset("file")) {
+    std::cerr << "--quiet operation was requested, but --file was not set.\n";
+    exit(EXIT_FAILURE);
   }
 
-  if (opt_username) {
-    memcached_return_t ret;
-    if (memcached_failed(ret = memcached_set_sasl_auth_data(memc, opt_username, opt_passwd))) {
-      std::cerr << memcached_last_error_message(memc) << std::endl;
-      memcached_free(memc);
-      return EXIT_FAILURE;
+  memcached_st memc;
+  if (!memcached_create(&memc)) {
+    if (!opt.isset("quiet")) {
+      std::cerr << "Failed to initialize memcached client.\n";
     }
+    exit(EXIT_FAILURE);
   }
 
-  memcached_return_t rc = memcached_dump(memc, callbacks, NULL, 1);
-
-  int exit_code = EXIT_SUCCESS;
-  if (memcached_failed(rc)) {
-    if (opt_verbose) {
-      std::cerr << "Failed to dump keys: " << memcached_last_error_message(memc) << std::endl;
-    }
-    exit_code = EXIT_FAILURE;
+  if (!opt.apply(&memc)) {
+    exit(EXIT_FAILURE);
   }
 
-  memcached_free(memc);
+  memcached_dump_fn cb[1] = {&print};
+  std::ostream *outstream = &std::cout;
+  std::ofstream outfile{};
 
-  if (opt_servers) {
-    free(opt_servers);
-  }
-  if (opt_hash) {
-    free(opt_hash);
+  if (auto filename = opt.argof("file")) {
+    if (opt.isset("debug")) {
+      std::cerr << "Opening " << filename << " for output.\n";
+    }
+    outfile.open(filename, std::ios::binary | std::ios::out);
+    if (!outfile.is_open()) {
+      if (!opt.isset("quiet")) {
+        std::cerr << "Failed to open " << filename << " for writing.\n";
+      }
+      memcached_free(&memc);
+      exit(EXIT_FAILURE);
+    }
+    outstream = &outfile;
   }
 
-  return exit_code;
-}
-
-static void options_parse(int argc, char *argv[]) {
-  static struct option long_options[] = {
-      {(OPTIONSTRING) "version", no_argument, NULL, OPT_VERSION},
-      {(OPTIONSTRING) "help", no_argument, NULL, OPT_HELP},
-      {(OPTIONSTRING) "quiet", no_argument, NULL, OPT_QUIET},
-      {(OPTIONSTRING) "verbose", no_argument, &opt_verbose, OPT_VERBOSE},
-      {(OPTIONSTRING) "debug", no_argument, &opt_verbose, OPT_DEBUG},
-      {(OPTIONSTRING) "servers", required_argument, NULL, OPT_SERVERS},
-      {(OPTIONSTRING) "hash", required_argument, NULL, OPT_HASH},
-      {(OPTIONSTRING) "binary", no_argument, NULL, OPT_BINARY},
-      {(OPTIONSTRING) "username", required_argument, NULL, OPT_USERNAME},
-      {(OPTIONSTRING) "password", required_argument, NULL, OPT_PASSWD},
-      {0, 0, 0, 0}};
-
-  int option_index = 0;
-  bool opt_version = false;
-  bool opt_help = false;
-  while (1) {
-    int option_rv = getopt_long(argc, argv, "Vhvds:", long_options, &option_index);
-
-    if (option_rv == -1)
-      break;
+  auto rc = memcached_dump(&memc, cb, outstream, 1);
 
-    switch (option_rv) {
-    case 0:
-      break;
-
-    case OPT_BINARY:
-      opt_binary = true;
-      break;
-
-    case OPT_VERBOSE: /* --verbose or -v */
-      opt_verbose = OPT_VERBOSE;
-      break;
-
-    case OPT_DEBUG: /* --debug or -d */
-      opt_verbose = OPT_DEBUG;
-      break;
-
-    case OPT_VERSION: /* --version or -V */
-      opt_verbose = true;
-      break;
-
-    case OPT_HELP: /* --help or -h */
-      opt_help = true;
-      break;
-
-    case OPT_SERVERS: /* --servers or -s */
-      opt_servers = strdup(optarg);
-      break;
-
-    case OPT_HASH:
-      opt_hash = strdup(optarg);
-      break;
-
-    case OPT_USERNAME:
-      opt_username = optarg;
-      break;
-
-    case OPT_PASSWD:
-      opt_passwd = optarg;
-      break;
-
-    case OPT_QUIET:
-      close_stdio();
-      break;
-
-    case '?':
-      /* getopt_long already printed an error message. */
-      exit(1);
-    default:
-      abort();
+  if (outfile) {
+    if (opt.isset("debug")) {
+      std::cerr << "Flushing " << opt.argof("file") << ".\n";
     }
+    outfile.flush();
   }
 
-  if (opt_version) {
-    version_command(PROGRAM_NAME);
-    exit(EXIT_SUCCESS);
+  if (MEMCACHED_SUCCESS != rc) {
+    if (!opt.isset("quiet")) {
+      std::cerr << "Failed to dump keys:" << memcached_last_error_message(&memc) << "\n";
+    }
+    memcached_free(&memc);
+    exit(EXIT_FAILURE);
   }
 
-  if (opt_help) {
-    help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, NULL);
-    exit(EXIT_SUCCESS);
-  }
+  memcached_free(&memc);
+  exit(EXIT_SUCCESS);
 }
index 14a70c29b0ae506132506a93e29a022569702fde..208e013dd6a0d3b915865fcbea8af7abc25b1f04 100644 (file)
 
 #include "mem_config.h"
 
-#include <cerrno>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <climits>
-
-#include <getopt.h>
-#include <iostream>
-#include <unistd.h>
-
-#include "libmemcached-1.0/memcached.h"
-
-#include "utilities.h"
-
 #define PROGRAM_NAME        "memerror"
-#define PROGRAM_DESCRIPTION "Translate a memcached errror code into a string."
+#define PROGRAM_DESCRIPTION "Translate a memcached error code into a string."
+#define PROGRAM_VERSION     "1.1"
 
-/* Prototypes */
-void options_parse(int argc, char *argv[]);
+#include "common/options.hpp"
 
 int main(int argc, char *argv[]) {
-  options_parse(argc, argv);
-
-  if (argc < 2) {
-    return EXIT_FAILURE;
-  }
-
-  while (optind < argc) {
-    errno = 0;
-    char *nptr;
-    unsigned long value = strtoul(argv[optind], &nptr, 10);
-
-    if ((errno) or (nptr == argv[optind] and value == 0)
-        or (value == ULONG_MAX and errno == ERANGE) or (value == 0 and errno == EINVAL))
-    {
-      std::cerr << "strtoul() was unable to parse given value" << std::endl;
-      return EXIT_FAILURE;
-    }
-
-    if (value < MEMCACHED_MAXIMUM_RETURN) {
-      std::cout << memcached_strerror(NULL, (memcached_return_t) value) << std::endl;
-    } else {
-      std::cerr << memcached_strerror(NULL, MEMCACHED_MAXIMUM_RETURN) << std::endl;
-      return EXIT_FAILURE;
-    }
-
-    optind++;
-  }
-
-  return EXIT_SUCCESS;
-}
-
-void options_parse(int argc, char *argv[]) {
-  static struct option long_options[] = {
-      {(OPTIONSTRING) "version", no_argument, NULL, OPT_VERSION},
-      {(OPTIONSTRING) "help", no_argument, NULL, OPT_HELP},
-      {0, 0, 0, 0},
-  };
-
-  bool opt_version = false;
-  bool opt_help = false;
-  int option_index = 0;
-  while (1) {
-    int option_rv = getopt_long(argc, argv, "Vhvds:", long_options, &option_index);
-    if (option_rv == -1) {
+  client_options opt{PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_DESCRIPTION, "code [code ...]"};
+
+  for (const auto &def : opt.defaults) {
+    switch (def.opt.val) {
+    case 'h': // --help
+    case 'V': // --version
+    case 'v': // --verbose
+    case 'd': // --debug
+      opt.add(def);
+      break;
+    default:
       break;
     }
+  }
 
-    switch (option_rv) {
-    case 0:
-      break;
+  char **argp = nullptr;
+  if (!opt.parse(argc, argv, &argp)) {
+    exit(EXIT_FAILURE);
+  }
 
-    case OPT_VERSION: /* --version or -V */
-      opt_version = true;
-      break;
+  opt.apply(nullptr);
 
-    case OPT_HELP: /* --help or -h */
-      opt_help = true;
-      break;
+  if (!*argp) {
+    std::cerr << "No error codes provided.\n";
+    exit(EXIT_FAILURE);
+  }
 
-    case '?':
-      /* getopt_long already printed an error message. */
-      exit(EXIT_FAILURE);
+  for (auto arg = argp; *arg; ++arg) {
+    auto code = std::stoul(*arg);
+    auto rc = static_cast<memcached_return_t>(code);
 
-    default:
-      exit(EXIT_FAILURE);
+    if (opt.isset("verbose")) {
+      std::cout << "code: " << code << "\n";
+      std::cout << "name: " << memcached_strerror(nullptr, rc) << "\n";
+    } else {
+      std::cout << memcached_strerror(nullptr, rc) << "\n";
     }
   }
 
-  if (opt_version) {
-    version_command(PROGRAM_NAME);
-    exit(EXIT_SUCCESS);
-  }
-
-  if (opt_help) {
-    help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, NULL);
-    exit(EXIT_SUCCESS);
-  }
+  exit(EXIT_SUCCESS);
 }
index 8fb69d4755322b69a5b200759b1ba07c2bf0157b..b3b7a1ec9a1fcbbb77d482f842480cbca06ad421 100644 (file)
 
 #include "mem_config.h"
 
-#include <cstdio>
-#include <cstring>
-#include <getopt.h>
-#include <iostream>
-#include <unistd.h>
-
-#include "libmemcached-1.0/memcached.h"
-#include "client_options.h"
-#include "utilities.h"
-
-static int opt_binary = 0;
-static int opt_verbose = 0;
-static char *opt_servers = NULL;
-static char *opt_hash = NULL;
-static char *opt_username;
-static char *opt_passwd;
-
 #define PROGRAM_NAME        "memexist"
-#define PROGRAM_DESCRIPTION "Check for the existance of a key within a cluster."
+#define PROGRAM_DESCRIPTION "Check for the existence of a key within a memcached cluster."
+#define PROGRAM_VERSION     "1.1"
 
-/* Prototypes */
-static void options_parse(int argc, char *argv[]);
+#include "common/options.hpp"
 
 int main(int argc, char *argv[]) {
-  options_parse(argc, argv);
-  initialize_sockets();
-
-  if (opt_servers == NULL) {
-    char *temp;
-
-    if ((temp = getenv("MEMCACHED_SERVERS"))) {
-      opt_servers = strdup(temp);
-    }
+  client_options opt{PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_DESCRIPTION, "key [key ...]"};
 
-    if (opt_servers == NULL) {
-      std::cerr << "No Servers provided" << std::endl;
-      exit(EXIT_FAILURE);
-    }
+  for (const auto &def : opt.defaults) {
+    opt.add(def);
   }
 
-  memcached_server_st *servers = memcached_servers_parse(opt_servers);
-  if (servers == NULL or memcached_server_list_count(servers) == 0) {
-    std::cerr << "Invalid server list provided:" << opt_servers << std::endl;
-    return EXIT_FAILURE;
+  char **argp = nullptr;
+  if (!opt.parse(argc, argv, &argp)) {
+    exit(EXIT_FAILURE);
   }
 
-  memcached_st *memc = memcached_create(NULL);
-  process_hash_option(memc, opt_hash);
-
-  memcached_server_push(memc, servers);
-  memcached_server_list_free(servers);
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, (uint64_t) opt_binary);
+  memcached_st memc;
+  if (!memcached_create(&memc)) {
+    if (!opt.isset("quiet")) {
+      std::cerr << "Failed to initialize memcached client.\n";
+    }
+    exit(EXIT_FAILURE);
+  }
 
-  if (opt_username and LIBMEMCACHED_WITH_SASL_SUPPORT == 0) {
-    memcached_free(memc);
-    std::cerr << "--username was supplied, but binary was not built with SASL support."
-              << std::endl;
-    return EXIT_FAILURE;
+  if (!opt.apply(&memc)) {
+    memcached_free(&memc);
+    exit(EXIT_FAILURE);
   }
 
-  if (opt_username) {
-    memcached_return_t ret;
-    if (memcached_failed(ret = memcached_set_sasl_auth_data(memc, opt_username, opt_passwd))) {
-      std::cerr << memcached_last_error_message(memc) << std::endl;
-      memcached_free(memc);
-      return EXIT_FAILURE;
+  if (!*argp) {
+    if (!opt.isset("quiet")) {
+      std::cerr << "No key(s) provided.\n";
     }
+    memcached_free(&memc);
+    exit(EXIT_FAILURE);
   }
 
-  int return_code = EXIT_SUCCESS;
-
-  while (optind < argc) {
-    memcached_return_t rc = memcached_exist(memc, argv[optind], strlen(argv[optind]));
+  auto exit_code = EXIT_SUCCESS;
+  for (auto arg = argp; *arg; ++arg) {
+    auto rc = memcached_exist(&memc, *arg, strlen(*arg));
 
-    if (rc == MEMCACHED_NOTFOUND) {
-      if (opt_verbose) {
-        std::cout << "Could not find key \"" << argv[optind] << "\"" << std::endl;
+    if (MEMCACHED_SUCCESS == rc) {
+      if (opt.isset("verbose")) {
+        std::cerr << "Found key '" << *arg << "'.\n";
       }
-
-      return_code = EXIT_FAILURE;
-    } else if (memcached_failed(rc)) {
-      if (opt_verbose) {
-        std::cerr << "Fatal error for key \"" << argv[optind]
-                  << "\" :" << memcached_last_error_message(memc) << std::endl;
-      }
-
-      return_code = EXIT_FAILURE;
-    } else // success
-    {
-      if (opt_verbose) {
-        std::cout << "Found key " << argv[optind] << std::endl;
+    } else {
+      exit_code = EXIT_FAILURE;
+      if (opt.isset("verbose")) {
+        if (rc == MEMCACHED_NOTFOUND) {
+          std::cerr << "Could not find key '" << *arg << "'.\n";
+        } else {
+          std::cerr << "Fatal error for key '" << *arg << "': "
+                    << memcached_last_error_message(&memc) << "\n";
+        }
       }
     }
-
-    optind++;
-  }
-
-  memcached_free(memc);
-
-  if (opt_servers) {
-    free(opt_servers);
   }
 
-  if (opt_hash) {
-    free(opt_hash);
-  }
-
-  return return_code;
-}
-
-static void options_parse(int argc, char *argv[]) {
-  memcached_programs_help_st help_options[] = {
-      {0},
-  };
-
-  static struct option long_options[] = {
-      {(OPTIONSTRING) "version", no_argument, NULL, OPT_VERSION},
-      {(OPTIONSTRING) "help", no_argument, NULL, OPT_HELP},
-      {(OPTIONSTRING) "quiet", no_argument, NULL, OPT_QUIET},
-      {(OPTIONSTRING) "verbose", no_argument, &opt_verbose, OPT_VERBOSE},
-      {(OPTIONSTRING) "debug", no_argument, &opt_verbose, OPT_DEBUG},
-      {(OPTIONSTRING) "servers", required_argument, NULL, OPT_SERVERS},
-      {(OPTIONSTRING) "hash", required_argument, NULL, OPT_HASH},
-      {(OPTIONSTRING) "binary", no_argument, NULL, OPT_BINARY},
-      {(OPTIONSTRING) "username", required_argument, NULL, OPT_USERNAME},
-      {(OPTIONSTRING) "password", required_argument, NULL, OPT_PASSWD},
-      {0, 0, 0, 0},
-  };
-
-  bool opt_version = false;
-  bool opt_help = false;
-  int option_index = 0;
-
-  while (1) {
-    int option_rv = getopt_long(argc, argv, "Vhvds:", long_options, &option_index);
-    if (option_rv == -1) {
-      break;
-    }
-
-    switch (option_rv) {
-    case 0:
-      break;
-
-    case OPT_BINARY:
-      opt_binary = 1;
-      break;
-
-    case OPT_VERBOSE: /* --verbose or -v */
-      opt_verbose = OPT_VERBOSE;
-      break;
-
-    case OPT_DEBUG: /* --debug or -d */
-      opt_verbose = OPT_DEBUG;
-      break;
-
-    case OPT_VERSION: /* --version or -V */
-      opt_version = true;
-      break;
-
-    case OPT_HELP: /* --help or -h */
-      opt_help = true;
-      break;
-
-    case OPT_SERVERS: /* --servers or -s */
-      opt_servers = strdup(optarg);
-      break;
-
-    case OPT_HASH:
-      opt_hash = strdup(optarg);
-      break;
-
-    case OPT_USERNAME:
-      opt_username = optarg;
-      break;
-
-    case OPT_PASSWD:
-      opt_passwd = optarg;
-      break;
-
-    case OPT_QUIET:
-      close_stdio();
-      break;
-
-    case '?':
-      /* getopt_long already printed an error message. */
-      exit(EXIT_SUCCESS);
-
-    default:
-      abort();
-    }
-  }
-
-  if (opt_version) {
-    version_command(PROGRAM_NAME);
-    exit(EXIT_SUCCESS);
-  }
-
-  if (opt_help) {
-    help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options);
-    exit(EXIT_SUCCESS);
-  }
+  memcached_free(&memc);
+  exit(exit_code);
 }