From 215ee293ae705943e28ab1509f50d03c85dd26a0 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 5 Aug 2021 13:02:54 +0200 Subject: [PATCH] CLI parsing: allow giving explicit values for bool options, improved error reporting: It is now possible to use e.g. --ensure-on-bed=0 for bools (meaning the same as --no-ensure-on-bed). Using --no- prefix on non-boolean is an error (--no-ensure-on-bed=1) Providing a value for --no- prefixed bool is an error (--no-loglevel 5) --- src/libslic3r/Config.cpp | 45 +++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index b9f9b266d..cbc434b39 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -871,6 +871,7 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option token.erase(equals_pos); } } + // Look for the cli -> option mapping. const auto it = opts.find(token); if (it == opts.end()) { @@ -879,15 +880,46 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option } const t_config_option_key opt_key = it->second; const ConfigOptionDef &optdef = this->def()->options.at(opt_key); + // If the option type expects a value and it was not already provided, // look for it in the next token. - if (optdef.type != coBool && optdef.type != coBools && value.empty()) { - if (i == (argc-1)) { - boost::nowide::cerr << "No value supplied for --" << token.c_str() << std::endl; + if (value.empty()) { + if (optdef.type != coBool && optdef.type != coBools) { + if (i == (argc-1)) { + boost::nowide::cerr << "No value supplied for --" << token.c_str() << std::endl; + return false; + } + value = argv[++ i]; + } else { + // This is a bool or bools. The value is optional, but may still be there. + // Check if the next token can be deserialized into ConfigOptionBool. + // If it is in fact bools, it will be rejected later anyway. + if (i != argc-1) { // There is still a token to read. + ConfigOptionBool cobool; + if (cobool.deserialize(argv[i+1])) + value = argv[++i]; + } + } + + } + + if (no) { + if (optdef.type != coBool && optdef.type != coBools) { + boost::nowide::cerr << "Only boolean config options can be negated with --no- prefix." << std::endl; + return false; + } + else if (! value.empty()) { + boost::nowide::cerr << "Boolean options negated by the --no- prefix cannot have a value." << std::endl; return false; } - value = argv[++ i]; } + if (optdef.type == coBools && ! value.empty()) { + boost::nowide::cerr << "Vector boolean options cannot have a value. Fill them in by " + "repeating them and negate by --no- prefix." << std::endl; + return false; + } + + // Store the option value. const bool existing = this->has(opt_key); if (keys != nullptr && ! existing) { @@ -911,7 +943,10 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option // unescaped by the calling shell. opt_vector->deserialize(value, true); } else if (opt_base->type() == coBool) { - static_cast(opt_base)->value = !no; + if (value.empty()) + static_cast(opt_base)->value = !no; + else + opt_base->deserialize(value); } else if (opt_base->type() == coString) { // Do not unescape single string values, the unescaping is left to the calling shell. static_cast(opt_base)->value = value;