X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;f=src%2Fbin%2Fcommon%2Foptions.hpp;h=980ca38dc39ab8865a498002e98d6f334d4c2814;hb=4b584c02c679edd005cd2e542d2ff1d9dcb312b9;hp=a5d6bdf72f955347c213fb04f10c29c280fd3f8c;hpb=9b0e362f4bf6efa125dbb1a4a70c3de7298c2ca8;p=awesomized%2Flibmemcached diff --git a/src/bin/common/options.hpp b/src/bin/common/options.hpp index a5d6bdf7..980ca38d 100644 --- a/src/bin/common/options.hpp +++ b/src/bin/common/options.hpp @@ -15,473 +15,363 @@ #pragma once +#include #include #include -#include +#include #include #include #include -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 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 &f) - : flags{} - , args{} - , avail{} - , help{} - , short_opts{} - , long_opts{} - , progname{prg} - , progvers{ver} - , progdesc{dsc} - { +#ifdef HAVE_GETOPT_H +# include +#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(flag::v)-first_flag] = {n, a, &flags.v, static_cast(flag::v)}; \ - help[static_cast(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(flag::v)-first_flag] = {n, a, nullptr, static_cast(flag::v)}; \ - help[static_cast(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(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 parse; + std::function apply; + char *arg; + bool set; + }; - void printVersion() const { - std::cout << progname << " v" << progvers - << " (libmemcached v" << LIBMEMCACHED_VERSION_STRING << ")" - << std::endl; - } + std::vector options; + std::vector 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 << " ] "; - - 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(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(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::iterator; + using const_iterator = std::vector::const_iterator; + using predicate = std::function; - switch (static_cast(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 == '-'); + }); } };