mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-26 14:41:25 +00:00
525 lines
13 KiB
C++
525 lines
13 KiB
C++
/*
|
|
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";
|
|
}
|
|
|