summaryrefslogtreecommitdiffstats
path: root/client/cmd_line_parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/cmd_line_parser.cpp')
-rw-r--r--client/cmd_line_parser.cpp524
1 files changed, 524 insertions, 0 deletions
diff --git a/client/cmd_line_parser.cpp b/client/cmd_line_parser.cpp
new file mode 100644
index 00000000..65ae404a
--- /dev/null
+++ b/client/cmd_line_parser.cpp
@@ -0,0 +1,524 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "common.h"
+
+#include <getopt.h>
+#include <iostream>
+
+#include "cmd_line_parser.h"
+#include "utils.h"
+#include "debug.h"
+
+#define DISABLE_ABBREVIATE
+
+
+CmdLineParser::Option::Option(int in_id, const std::string& in_name, char in_short_name,
+ OptionType in_type, const std::string& in_help,
+ const std::string& in_arg_name)
+ : id (in_id)
+ , name (in_name)
+ , arg_name (in_arg_name)
+ , type (in_type)
+ , short_name (in_short_name)
+ , help (in_help)
+ , optional (true)
+ , is_set (false)
+ , seperator (0)
+{
+}
+
+CmdLineParser::CmdLineParser(std::string description, bool allow_positional_args)
+ : _description (description)
+ , _short_options ("+")
+ , _argc (0)
+ , _argv (NULL)
+ , _multi_args (NULL)
+ , _multi_next (NULL)
+ , _multi_seperator (0)
+ , _positional_args (allow_positional_args)
+ , _done (false)
+{
+}
+
+CmdLineParser::~CmdLineParser()
+{
+ Options::iterator iter = _options.begin();
+ for (; iter != _options.end(); ++iter) {
+ delete *iter;
+ }
+ delete[] _multi_args;
+}
+
+void CmdLineParser::add_private(int id, const std::string& name, char short_name,
+ OptionType type, const std::string& help,
+ const std::string& arg_name)
+{
+ if (_argv) {
+ THROW("unexpected");
+ }
+
+ if (find(id)) {
+ THROW("exist");
+ }
+
+ if (name.size() == 0) {
+ THROW("invalid name");
+ }
+
+ if (find(name)) {
+ THROW("name exist");
+ }
+
+ if (short_name != 0) {
+ if (!isalnum(short_name) || short_name == 'W') {
+ THROW("invalid short name");
+ }
+
+ if (find(short_name)) {
+ THROW("short name exist");
+ }
+ }
+
+ if (help.size() == 0) {
+ THROW("invalid help string");
+ }
+
+ if (help.find_first_of('\t') != std::string::npos) {
+ THROW("tab is not allow in help string");
+ }
+
+ _options.push_back(new Option(id, name, short_name, type, help, arg_name));
+}
+
+void CmdLineParser::add(int id, const std::string& name, const std::string& help, char short_name)
+{
+ if (id < OPTION_FIRST_AVILABLE) {
+ THROW("invalid id");
+ }
+ add_private(id, name, short_name, NO_ARGUMENT, help, "");
+}
+
+void CmdLineParser::add(int id, const std::string& name, const std::string& help,
+ const std::string& arg_name, bool reqired_arg, char short_name)
+{
+ if (id < OPTION_FIRST_AVILABLE) {
+ THROW("invalid id");
+ }
+
+ if (arg_name.size() == 0) {
+ THROW("invalid arg name");
+ }
+
+ add_private(id, name, short_name, reqired_arg ? REQUIRED_ARGUMENT : OPTIONAL_ARGUMENT, help,
+ arg_name);
+}
+
+void CmdLineParser::set_multi(int id, char seperator)
+{
+ if (_argv) {
+ THROW("unexpected");
+ }
+
+ if (!ispunct(seperator)) {
+ THROW("invalid seperator");
+ }
+
+ Option* opt = find(id);
+
+ if (!opt) {
+ THROW("not found");
+ }
+
+ if (opt->type == NO_ARGUMENT) {
+ THROW("can't set multi for option without argument");
+ }
+
+ opt->seperator = seperator;
+}
+
+void CmdLineParser::set_reqired(int id)
+{
+ if (_argv) {
+ THROW("unexpected");
+ }
+
+ Option* opt = find(id);
+
+ if (!opt) {
+ THROW("not found");
+ }
+
+ opt->optional = false;
+}
+
+CmdLineParser::Option* CmdLineParser::find(int id)
+{
+ Options::iterator iter = _options.begin();
+ for (; iter != _options.end(); ++iter) {
+ if ((*iter)->id == id) {
+ return *iter;
+ }
+ }
+ return NULL;
+}
+
+bool CmdLineParser::is_set(int id)
+{
+ Option *opt = find(id);
+
+ if (!opt) {
+ THROW("not found");
+ }
+ return opt->is_set;
+}
+
+CmdLineParser::Option* CmdLineParser::find(const std::string& name)
+{
+ Options::iterator iter = _options.begin();
+ for (; iter != _options.end(); ++iter) {
+ if ((*iter)->name == name) {
+ return *iter;
+ }
+ }
+ return NULL;
+}
+
+CmdLineParser::Option* CmdLineParser::find(char short_name)
+{
+ if (short_name == 0) {
+ return NULL;
+ }
+
+ Options::iterator iter = _options.begin();
+ for (; iter != _options.end(); ++iter) {
+ if ((*iter)->short_name == short_name) {
+ return *iter;
+ }
+ }
+ return NULL;
+}
+
+CmdLineParser::Option* CmdLineParser::find_missing_opt()
+{
+ Options::iterator iter = _options.begin();
+ for (; iter != _options.end(); ++iter) {
+ CmdLineParser::Option* opt = *iter;
+ if (!opt->optional && !opt->is_set) {
+ return opt;
+ }
+ }
+ return NULL;
+}
+
+void CmdLineParser::build()
+{
+ Options::iterator iter = _options.begin();
+ _long_options.resize(_options.size() + 1);
+ for (int i = 0; iter != _options.end(); ++iter, i++) {
+ CmdLineParser::Option* opt = *iter;
+ struct option& long_option = _long_options[i];
+ long_option.name = opt->name.c_str();
+ switch (opt->type) {
+ case NO_ARGUMENT:
+ long_option.has_arg = no_argument;
+ break;
+ case OPTIONAL_ARGUMENT:
+ long_option.has_arg = optional_argument;
+ break;
+ case REQUIRED_ARGUMENT:
+ long_option.has_arg = required_argument;
+ break;
+ }
+ long_option.flag = &long_option.val;
+ long_option.val = opt->id;
+ if (opt->short_name != 0) {
+ _short_options += opt->short_name;
+ switch (opt->type) {
+ case OPTIONAL_ARGUMENT:
+ _short_options += "::";
+ break;
+ case REQUIRED_ARGUMENT:
+ _short_options += ":";
+ break;
+ case NO_ARGUMENT:
+ break;
+ }
+ }
+ }
+ struct option& long_option = _long_options[_long_options.size() - 1];
+ long_option.flag = 0;
+ long_option.has_arg = 0;
+ long_option.name = NULL;
+ long_option.val = 0;
+}
+
+void CmdLineParser::begin(int argc, char** argv)
+{
+ if (_argv) {
+ THROW("unexpected");
+ }
+
+ if (!argv || argc < 1) {
+ THROW("invalid args");
+ }
+
+ add_private(CmdLineParser::OPTION_HELP, "help", 0, NO_ARGUMENT, "show command help", "");
+ opterr = 0;
+ _argv = argv;
+ _argc = argc;
+ build();
+}
+
+char* CmdLineParser::start_multi(char *optarg, char seperator)
+{
+ if (!optarg) {
+ return NULL;
+ }
+ _multi_args = new char[strlen(optarg) + 1];
+ _multi_seperator = seperator;
+ strcpy(_multi_args, optarg);
+ if ((_multi_next = strchr(_multi_args, _multi_seperator))) {
+ *(_multi_next++) = 0;
+ }
+ return _multi_args;
+}
+
+char* CmdLineParser::next_multi()
+{
+ if (!_multi_next) {
+ _multi_seperator = 0;
+ delete[] _multi_args;
+ _multi_args = NULL;
+ return NULL;
+ }
+ char* ret = _multi_next;
+ if ((_multi_next = strchr(_multi_next, _multi_seperator))) {
+ *(_multi_next++) = 0;
+ }
+
+ return ret;
+}
+
+int CmdLineParser::get_option(char** val)
+{
+ CmdLineParser::Option* opt_obj;
+
+ if (!_argv) {
+ THROW("unexpected");
+ }
+
+ if (_multi_args) {
+ THROW("in multi args mode");
+ }
+
+ if (_done) {
+ THROW("is done");
+ }
+
+ int long_index;
+
+ int opt = getopt_long(_argc, _argv, _short_options.c_str(), &_long_options[0], &long_index);
+
+ switch (opt) {
+ case 0: {
+ if (!(opt_obj = find(_long_options[long_index].val))) {
+ THROW("long option no found");
+ }
+
+#ifdef DISABLE_ABBREVIATE
+ int name_pos = (opt_obj->type == REQUIRED_ARGUMENT) ? optind - 2 : optind - 1;
+ std::string cmd_name(_argv[name_pos] + 2);
+ if (cmd_name.find(opt_obj->name) != 0) {
+ std::cout << _argv[0] << ": invalid option '--" << cmd_name << "'\n";
+ return OPTION_ERROR;
+ }
+#endif
+
+ if (opt_obj->seperator) {
+ *val = start_multi(optarg, opt_obj->seperator);
+ } else {
+ *val = optarg;
+ }
+ opt_obj->is_set = true;
+ return opt_obj->id;
+ }
+ case -1: {
+ *val = NULL;
+ if (!_positional_args && optind != _argc) {
+ std::cout << _argv[0] << ": unexpected positional arguments\n";
+ return OPTION_ERROR;
+ }
+ if ((opt_obj = find_missing_opt())) {
+ std::cout << _argv[0] << ": option --" << opt_obj->name << " is required\n";
+ return OPTION_ERROR;
+ }
+ _done = true;
+ return OPTION_DONE;
+ }
+ case '?':
+ if (optopt >= 255) {
+ opt_obj = find(optopt);
+ ASSERT(opt_obj);
+
+#ifdef DISABLE_ABBREVIATE
+ std::string cmd_name(_argv[optind - 1] + 2);
+ if (cmd_name.find(opt_obj->name) != 0) {
+ std::cout << _argv[0] << ": invalid option '--" << cmd_name << "'\n";
+ return OPTION_ERROR;
+ }
+#endif
+ std::cout << _argv[0] << ": option --" << opt_obj->name << " requires an argument\n";
+ } else if (optopt == 0) {
+ std::cout << _argv[0] << ": invalid option '" << _argv[optind - 1] << "'\n";
+ } else if ((opt_obj = find((char)optopt))) {
+ std::cout << _argv[0] << ": option '-" << opt_obj->short_name <<
+ "' requires an argument\n";
+ } else {
+ std::cout << _argv[0] << ": invalid option '-" << char(optopt) << "'\n";
+ }
+ return OPTION_ERROR;
+ default:
+ if (opt > 255 || !(opt_obj = find((char)opt))) {
+ *val = NULL;
+ return OPTION_ERROR;
+ }
+ if (opt_obj->seperator) {
+ *val = start_multi(optarg, opt_obj->seperator);
+ } else {
+ *val = optarg;
+ }
+ opt_obj->is_set = true;
+ return opt_obj->id;
+ }
+}
+
+char* CmdLineParser::next_argument()
+{
+ if (!_argv) {
+ THROW("unexpected");
+ }
+
+ if (_multi_args) {
+ return next_multi();
+ }
+
+ if (!_done) {
+ THROW("not done");
+ }
+
+ if (optind == _argc) {
+ return NULL;
+ }
+ return _argv[optind++];
+}
+
+#ifdef WIN32
+char* basename(char *str)
+{
+ char *base;
+ if ((base = strrchr(str, '\\'))) {
+ return base;
+ }
+
+ if ((base = strrchr(str, ':'))) {
+ return base;
+ }
+ return str;
+}
+
+#endif
+
+void CmdLineParser::show_help()
+{
+ static const int HELP_START_POS = 30;
+ static const int HELP_WIDTH = 80 - HELP_START_POS;
+
+ std::cout << basename(_argv[0]) << " - " << _description.c_str() << "\n\noptions:\n\n";
+
+ Options::iterator iter = _options.begin();
+ for (; iter != _options.end(); ++iter) {
+ CmdLineParser::Option* opt = *iter;
+ std::ostringstream os;
+
+ if (opt->short_name) {
+ os << " -" << opt->short_name << ", ";
+ } else {
+ os << " ";
+ }
+
+ os << "--" << opt->name;
+
+ if (opt->type == OPTIONAL_ARGUMENT) {
+ os << "[=";
+ } else if (opt->type == REQUIRED_ARGUMENT) {
+ os << " <";
+ }
+
+ if (opt->type == OPTIONAL_ARGUMENT || opt->type == REQUIRED_ARGUMENT) {
+ if (opt->seperator) {
+ os << opt->arg_name << opt->seperator << opt->arg_name << "...";
+ } else {
+ os << opt->arg_name;
+ }
+ }
+
+ if (opt->type == OPTIONAL_ARGUMENT) {
+ os << "]";
+ } else if (opt->type == REQUIRED_ARGUMENT) {
+ os << ">";
+ }
+
+ int skip = HELP_START_POS - os.str().size();
+ if (skip < 2) {
+ os << "\n ";
+ } else {
+ while (skip--) {
+ os << " ";
+ }
+ }
+
+ int line_count = 0;
+ std::istringstream is(opt->help);
+ std::string line;
+ std::getline(is, line);
+ do {
+ if (line_count++) {
+ os << " ";
+ }
+ if (line.size() > HELP_WIDTH) {
+ int now = HELP_WIDTH;
+ std::string sub;
+ sub.append(line, 0, now);
+ int last_space;
+ if ((last_space = sub.find_last_of(' ')) != std::string::npos) {
+ now = last_space;
+ sub.resize(now++);
+ }
+ os << sub << "\n";
+ line = line.substr(now, line.size() - now);
+ } else {
+ os << line << "\n";
+ line.clear();
+ }
+ } while (line.size() || std::getline(is, line));
+
+ std::cout << os.str();
+ }
+ std::cout << "\n";
+}
+