Commit 29bbac83 authored by Matthias Vallentin's avatar Matthias Vallentin

Make message::extract_opts more flexible

This function message::extract_opts no longer prints help text and errors
to the console. These remain available in the result struct as members,
however.

Moreover, this commit also fixes a bug when users specified their own -h
or -? option, but not --help. In this case extract_opts would override
it with the default help arg. This no longer happens.

Another extension concerns handling of values for single options: it is
now possible to omit the space between a value and the single option,
e.g., -v2 is equivalent to -v 2.
parent c9822a54
...@@ -35,11 +35,16 @@ int main(int argc, char** argv) { ...@@ -35,11 +35,16 @@ int main(int argc, char** argv) {
{"name,n", "set chat name", name}, {"name,n", "set chat name", name},
{"group,g", "join chat group", group_id} {"group,g", "join chat group", group_id}
}); });
if (!res.error.empty()) {
cerr << res.error << endl;
return 1;
}
if (!res.remainder.empty()) { if (!res.remainder.empty()) {
std::cerr << res.helptext << std::endl; std::cerr << res.helptext << std::endl;
return 1; return 1;
} }
if (res.opts.count("help") > 0) { if (res.opts.count("help") > 0) {
cout << res.helptext << endl;
return 0; return 0;
} }
group gptr; group gptr;
......
...@@ -241,8 +241,12 @@ int main(int argc, char** argv) { ...@@ -241,8 +241,12 @@ int main(int argc, char** argv) {
{"server,s", "run in server mode"}, {"server,s", "run in server mode"},
{"client,c", "run in client mode"} {"client,c", "run in client mode"}
}); });
if (!res.error.empty()) {
cerr << res.error << endl;
return 1;
}
if (res.opts.count("help") > 0) { if (res.opts.count("help") > 0) {
// help text has already been printed cout << res.helptext << endl;
return 0; return 0;
} }
if (!res.remainder.empty()) { if (!res.remainder.empty()) {
......
...@@ -71,11 +71,16 @@ int main(int argc, char** argv) { ...@@ -71,11 +71,16 @@ int main(int argc, char** argv) {
{"name,n", "set name", name}, {"name,n", "set name", name},
{"group,g", "join group", group_id} {"group,g", "join group", group_id}
}); });
if (!res.error.empty()) {
cerr << res.error << endl;
return 1;
}
if (!res.remainder.empty()) { if (!res.remainder.empty()) {
std::cout << res.helptext << std::endl; std::cout << res.helptext << std::endl;
return 1; return 1;
} }
if (res.opts.count("help") > 0) { if (res.opts.count("help") > 0) {
cout << res.helptext << endl;
return 0; return 0;
} }
while (name.empty()) { while (name.empty()) {
......
...@@ -273,9 +273,13 @@ class message { ...@@ -273,9 +273,13 @@ class message {
* {"host,H", "set host (default: localhost)", host}, * {"host,H", "set host (default: localhost)", host},
* {"verbose,v", "enable verbose mode"} * {"verbose,v", "enable verbose mode"}
* }); * });
* if (!res.error.empty()) {
* cerr << res.error << endl;
* return 1;
* }
* if (res.opts.count("help") > 0) { * if (res.opts.count("help") > 0) {
* // CLI arguments contained "-h", "--help", or "-?" (builtin); * // CLI arguments contained "-h", "--help", or "-?" (builtin);
* // note: the help text has already been printed to stdout * cout << res.helptext << endl;
* return 0; * return 0;
* } * }
* if (!res.remainder.empty()) { * if (!res.remainder.empty()) {
...@@ -418,6 +422,11 @@ struct message::cli_res { ...@@ -418,6 +422,11 @@ struct message::cli_res {
* Stores the automatically generated help text. * Stores the automatically generated help text.
*/ */
std::string helptext; std::string helptext;
/**
* Stores errors during option parsing.
*/
std::string error;
}; };
/** /**
......
...@@ -145,9 +145,18 @@ message message::extract(message_handler handler) const { ...@@ -145,9 +145,18 @@ message message::extract(message_handler handler) const {
message::cli_res message::extract_opts(std::vector<cli_arg> xs, message::cli_res message::extract_opts(std::vector<cli_arg> xs,
help_factory f) const { help_factory f) const {
// add default help item if not specified by user // add default help item if user did not specify any help option
auto pred = [](const cli_arg& arg) { auto pred = [](const cli_arg& arg) {
return arg.name == "help" || arg.name.compare(0, 5, "help,") == 0; std::vector<std::string> s;
split(s, arg.name, is_any_of(","), token_compress_on);
if (s.empty()) {
throw std::invalid_argument("invalid option name: " + arg.name);
}
auto has_short_help = [](const std::string& opt) {
return opt.find_first_of("h?") != std::string::npos;
};
return s[0] == "help"
|| std::find_if(s.begin() + 1, s.end(), has_short_help) != s.end();
}; };
if (std::none_of(xs.begin(), xs.end(), pred)) { if (std::none_of(xs.begin(), xs.end(), pred)) {
xs.push_back(cli_arg{"help,h,?", "print this text"}); xs.push_back(cli_arg{"help,h,?", "print this text"});
...@@ -198,15 +207,25 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs, ...@@ -198,15 +207,25 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs,
opts.insert(ptr->name.substr(0, separator)); opts.insert(ptr->name.substr(0, separator));
} }
}; };
std::string error;
auto res = extract({ auto res = extract({
[&](const std::string& arg) -> optional<skip_message_t> { [&](const std::string& arg) -> optional<skip_message_t> {
if (arg.empty() || arg.front() != '-') { if (arg.empty() || arg.front() != '-') {
return skip_message(); return skip_message();
} }
auto i = shorts.find(arg); auto i = shorts.find(arg.substr(0, 2));
if (i != shorts.end()) { if (i != shorts.end()) {
if (i->second->fun) { if (i->second->fun) {
// this short opt expects two arguments // this short opt expects two arguments
if (arg.size() > 2) {
// this short opt comes with a value (no space), e.g., -x2
if (!i->second->fun(arg.substr(2))) {
error = "invalid value for option " + i->second->name + ": " + arg;
return none;
}
insert_opt_name(i->second);
return none;
}
return skip_message(); return skip_message();
} }
insert_opt_name(i->second); insert_opt_name(i->second);
...@@ -217,12 +236,11 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs, ...@@ -217,12 +236,11 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs,
if (j != longs.end()) { if (j != longs.end()) {
if (j->second->fun) { if (j->second->fun) {
if (eq_pos == std::string::npos) { if (eq_pos == std::string::npos) {
std::cerr << "missing argument to " << arg << std::endl; error = "missing argument to " + arg;
return none; return none;
} }
if (!j->second->fun(arg.substr(eq_pos + 1))) { if (!j->second->fun(arg.substr(eq_pos + 1))) {
std::cerr << "invalid value for option " error = "invalid value for option " + j->second->name + ": " + arg;
<< j->second->name << ": " << arg << std::endl;
return none; return none;
} }
insert_opt_name(j->second); insert_opt_name(j->second);
...@@ -231,7 +249,7 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs, ...@@ -231,7 +249,7 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs,
insert_opt_name(j->second); insert_opt_name(j->second);
return none; return none;
} }
std::cerr << "unknown command line option: " << arg << std::endl; error = "unknown command line option: " + arg;
return none; return none;
}, },
[&](const std::string& arg1, [&](const std::string& arg1,
...@@ -248,14 +266,13 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs, ...@@ -248,14 +266,13 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs,
return skip_message(); return skip_message();
} }
if (!i->second->fun(arg2)) { if (!i->second->fun(arg2)) {
std::cerr << "invalid value for option " error = "invalid value for option " + i->second->name + ": " + arg2;
<< i->second->name << ": " << arg2 << std::endl;
return none; return none;
} }
insert_opt_name(i->second); insert_opt_name(i->second);
return none; return none;
} }
std::cerr << "unknown command line option: " << arg1 << std::endl; error = "unknown command line option: " + arg1;
return none; return none;
} }
}); });
...@@ -277,10 +294,7 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs, ...@@ -277,10 +294,7 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs,
} }
helptext = oss.str(); helptext = oss.str();
} }
if (opts.count("help") == 1) { return {res, std::move(opts), std::move(helptext), std::move(error)};
std::cout << helptext << std::endl;
}
return {res, std::move(opts), std::move(helptext)};
} }
message::cli_arg::cli_arg(std::string nstr, std::string tstr) message::cli_arg::cli_arg(std::string nstr, std::string tstr)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment