From 030c9ed901dc38b7f6cafa6250435d67f19cfe8b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 13 Apr 2018 11:32:28 +0200 Subject: Regenerate option parser (adds support for combined flags and values) --- build2/b-options.cxx | 194 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 155 insertions(+), 39 deletions(-) diff --git a/build2/b-options.cxx b/build2/b-options.cxx index 231299f..3cfb708 100644 --- a/build2/b-options.cxx +++ b/build2/b-options.cxx @@ -224,24 +224,45 @@ namespace build2 // See if the next argument is the file option. // const char* a (base::peek ()); - const option_info* oi; + const option_info* oi = 0; + const char* ov = 0; - if (!skip_ && (oi = find (a))) + if (!skip_) { - base::next (); + if ((oi = find (a)) != 0) + { + base::next (); + + if (!base::more ()) + throw missing_value (a); - if (!base::more ()) - throw missing_value (oi->option); + ov = base::next (); + } + else if (std::strncmp (a, "-", 1) == 0) + { + if ((ov = std::strchr (a, '=')) != 0) + { + std::string o (a, 0, ov - a); + if ((oi = find (o.c_str ())) != 0) + { + base::next (); + ++ov; + } + } + } + } + if (oi != 0) + { if (oi->search_func != 0) { - std::string f (oi->search_func (base::next (), oi->arg)); + std::string f (oi->search_func (ov, oi->arg)); if (!f.empty ()) load (f); } else - load (base::next ()); + load (ov); if (!args_.empty ()) return true; @@ -936,6 +957,10 @@ namespace build2 ::build2::cl::unknown_mode opt_mode, ::build2::cl::unknown_mode arg_mode) { + // Can't skip combined flags (--no-combined-flags). + // + assert (opt_mode != ::build2::cl::unknown_mode::skip); + bool r = false; bool opt = true; @@ -948,52 +973,143 @@ namespace build2 opt = false; } - if (opt && _parse (o, s)) - r = true; - else if (opt && std::strncmp (o, "-", 1) == 0 && o[1] != '\0') + if (opt) { - switch (opt_mode) + if (_parse (o, s)) { - case ::build2::cl::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::cl::unknown_mode::stop: - { - break; - } - case ::build2::cl::unknown_mode::fail: - { - throw ::build2::cl::unknown_option (o); - } + r = true; + continue; } - break; - } - else - { - switch (arg_mode) + if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') { - case ::build2::cl::unknown_mode::skip: + // Handle combined option values. + // + std::string co; + if (const char* v = std::strchr (o, '=')) { - s.skip (); - r = true; - continue; + co.assign (o, 0, v - o); + ++v; + + int ac (2); + char* av[] = + { + const_cast (co.c_str ()), + const_cast (v) + }; + + ::build2::cl::argv_scanner ns (0, ac, av); + + if (_parse (co.c_str (), ns)) + { + // Parsed the option but not its value? + // + if (ns.end () != 2) + throw ::build2::cl::invalid_value (co, v); + + s.next (); + r = true; + continue; + } + else + { + // Set the unknown option and fall through. + // + o = co.c_str (); + } } - case ::build2::cl::unknown_mode::stop: + + // Handle combined flags. + // + char cf[3]; { - break; + const char* p = o + 1; + for (; *p != '\0'; ++p) + { + if (!((*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z') || + (*p >= '0' && *p <= '9'))) + break; + } + + if (*p == '\0') + { + for (p = o + 1; *p != '\0'; ++p) + { + std::strcpy (cf, "-"); + cf[1] = *p; + cf[2] = '\0'; + + int ac (1); + char* av[] = + { + cf + }; + + ::build2::cl::argv_scanner ns (0, ac, av); + + if (!_parse (cf, ns)) + break; + } + + if (*p == '\0') + { + // All handled. + // + s.next (); + r = true; + continue; + } + else + { + // Set the unknown option and fall through. + // + o = cf; + } + } } - case ::build2::cl::unknown_mode::fail: + + switch (opt_mode) { - throw ::build2::cl::unknown_argument (o); + case ::build2::cl::unknown_mode::skip: + { + s.skip (); + r = true; + continue; + } + case ::build2::cl::unknown_mode::stop: + { + break; + } + case ::build2::cl::unknown_mode::fail: + { + throw ::build2::cl::unknown_option (o); + } } + + break; } + } - break; + switch (arg_mode) + { + case ::build2::cl::unknown_mode::skip: + { + s.skip (); + r = true; + continue; + } + case ::build2::cl::unknown_mode::stop: + { + break; + } + case ::build2::cl::unknown_mode::fail: + { + throw ::build2::cl::unknown_argument (o); + } } + + break; } return r; -- cgit v1.1