diff options
Diffstat (limited to 'libbuild2/test/script')
-rw-r--r-- | libbuild2/test/script/builtin-options.cxx | 3091 | ||||
-rw-r--r-- | libbuild2/test/script/builtin-options.hxx | 680 | ||||
-rw-r--r-- | libbuild2/test/script/builtin-options.ixx | 138 | ||||
-rw-r--r-- | libbuild2/test/script/builtin.cli | 79 | ||||
-rw-r--r-- | libbuild2/test/script/builtin.cxx | 1924 | ||||
-rw-r--r-- | libbuild2/test/script/builtin.hxx | 74 | ||||
-rw-r--r-- | libbuild2/test/script/runner.cxx | 250 |
7 files changed, 242 insertions, 5994 deletions
diff --git a/libbuild2/test/script/builtin-options.cxx b/libbuild2/test/script/builtin-options.cxx index 9a7968e..6b6afe0 100644 --- a/libbuild2/test/script/builtin-options.cxx +++ b/libbuild2/test/script/builtin-options.cxx @@ -366,2274 +366,6 @@ namespace build2 { namespace script { - // cleanup_options - // - - cleanup_options:: - cleanup_options () - : no_cleanup_ () - { - } - - typedef - std::map<std::string, void (*) (cleanup_options&, ::build2::test::script::cli::scanner&)> - _cli_cleanup_options_map; - - static _cli_cleanup_options_map _cli_cleanup_options_map_; - - struct _cli_cleanup_options_map_init - { - _cli_cleanup_options_map_init () - { - _cli_cleanup_options_map_["--no-cleanup"] = - &::build2::test::script::cli::thunk< cleanup_options, bool, &cleanup_options::no_cleanup_ >; - } - }; - - static _cli_cleanup_options_map_init _cli_cleanup_options_map_init_; - - bool cleanup_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_cleanup_options_map::const_iterator i (_cli_cleanup_options_map_.find (o)); - - if (i != _cli_cleanup_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - return false; - } - - // cat_options - // - - cat_options:: - cat_options () - { - } - - cat_options:: - cat_options (int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - } - - cat_options:: - cat_options (int start, - int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - } - - cat_options:: - cat_options (int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - cat_options:: - cat_options (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - cat_options:: - cat_options (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - { - _parse (s, opt, arg); - } - - typedef - std::map<std::string, void (*) (cat_options&, ::build2::test::script::cli::scanner&)> - _cli_cat_options_map; - - static _cli_cat_options_map _cli_cat_options_map_; - - struct _cli_cat_options_map_init - { - _cli_cat_options_map_init () - { - } - }; - - static _cli_cat_options_map_init _cli_cat_options_map_init_; - - bool cat_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_cat_options_map::const_iterator i (_cli_cat_options_map_.find (o)); - - if (i != _cli_cat_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - return false; - } - - bool cat_options:: - _parse (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt_mode, - ::build2::test::script::cli::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::test::script::cli::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - s.skip (); - r = true; - continue; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::test::script::cli::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::test::script::cli::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - 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::test::script::cli::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; - } - } - } - - switch (opt_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_argument (o); - } - } - - break; - } - - return r; - } - - // cp_options - // - - cp_options:: - cp_options () - : recursive_ (), - preserve_ () - { - } - - cp_options:: - cp_options (int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : recursive_ (), - preserve_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - } - - cp_options:: - cp_options (int start, - int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : recursive_ (), - preserve_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - } - - cp_options:: - cp_options (int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : recursive_ (), - preserve_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - cp_options:: - cp_options (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : recursive_ (), - preserve_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - cp_options:: - cp_options (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : recursive_ (), - preserve_ () - { - _parse (s, opt, arg); - } - - typedef - std::map<std::string, void (*) (cp_options&, ::build2::test::script::cli::scanner&)> - _cli_cp_options_map; - - static _cli_cp_options_map _cli_cp_options_map_; - - struct _cli_cp_options_map_init - { - _cli_cp_options_map_init () - { - _cli_cp_options_map_["--recursive"] = - &::build2::test::script::cli::thunk< cp_options, bool, &cp_options::recursive_ >; - _cli_cp_options_map_["-R"] = - &::build2::test::script::cli::thunk< cp_options, bool, &cp_options::recursive_ >; - _cli_cp_options_map_["-r"] = - &::build2::test::script::cli::thunk< cp_options, bool, &cp_options::recursive_ >; - _cli_cp_options_map_["--preserve"] = - &::build2::test::script::cli::thunk< cp_options, bool, &cp_options::preserve_ >; - _cli_cp_options_map_["-p"] = - &::build2::test::script::cli::thunk< cp_options, bool, &cp_options::preserve_ >; - } - }; - - static _cli_cp_options_map_init _cli_cp_options_map_init_; - - bool cp_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_cp_options_map::const_iterator i (_cli_cp_options_map_.find (o)); - - if (i != _cli_cp_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - // cleanup_options base - // - if (::build2::test::script::cleanup_options::_parse (o, s)) - return true; - - return false; - } - - bool cp_options:: - _parse (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt_mode, - ::build2::test::script::cli::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::test::script::cli::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - s.skip (); - r = true; - continue; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::test::script::cli::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::test::script::cli::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - 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::test::script::cli::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; - } - } - } - - switch (opt_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_argument (o); - } - } - - break; - } - - return r; - } - - // ln_options - // - - ln_options:: - ln_options () - : symbolic_ () - { - } - - ln_options:: - ln_options (int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : symbolic_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - } - - ln_options:: - ln_options (int start, - int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : symbolic_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - } - - ln_options:: - ln_options (int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : symbolic_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - ln_options:: - ln_options (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : symbolic_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - ln_options:: - ln_options (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : symbolic_ () - { - _parse (s, opt, arg); - } - - typedef - std::map<std::string, void (*) (ln_options&, ::build2::test::script::cli::scanner&)> - _cli_ln_options_map; - - static _cli_ln_options_map _cli_ln_options_map_; - - struct _cli_ln_options_map_init - { - _cli_ln_options_map_init () - { - _cli_ln_options_map_["--symbolic"] = - &::build2::test::script::cli::thunk< ln_options, bool, &ln_options::symbolic_ >; - _cli_ln_options_map_["-s"] = - &::build2::test::script::cli::thunk< ln_options, bool, &ln_options::symbolic_ >; - } - }; - - static _cli_ln_options_map_init _cli_ln_options_map_init_; - - bool ln_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_ln_options_map::const_iterator i (_cli_ln_options_map_.find (o)); - - if (i != _cli_ln_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - // cleanup_options base - // - if (::build2::test::script::cleanup_options::_parse (o, s)) - return true; - - return false; - } - - bool ln_options:: - _parse (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt_mode, - ::build2::test::script::cli::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::test::script::cli::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - s.skip (); - r = true; - continue; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::test::script::cli::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::test::script::cli::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - 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::test::script::cli::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; - } - } - } - - switch (opt_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_argument (o); - } - } - - break; - } - - return r; - } - - // mkdir_options - // - - mkdir_options:: - mkdir_options () - : parents_ () - { - } - - mkdir_options:: - mkdir_options (int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : parents_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - } - - mkdir_options:: - mkdir_options (int start, - int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : parents_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - } - - mkdir_options:: - mkdir_options (int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : parents_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - mkdir_options:: - mkdir_options (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : parents_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - mkdir_options:: - mkdir_options (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : parents_ () - { - _parse (s, opt, arg); - } - - typedef - std::map<std::string, void (*) (mkdir_options&, ::build2::test::script::cli::scanner&)> - _cli_mkdir_options_map; - - static _cli_mkdir_options_map _cli_mkdir_options_map_; - - struct _cli_mkdir_options_map_init - { - _cli_mkdir_options_map_init () - { - _cli_mkdir_options_map_["--parents"] = - &::build2::test::script::cli::thunk< mkdir_options, bool, &mkdir_options::parents_ >; - _cli_mkdir_options_map_["-p"] = - &::build2::test::script::cli::thunk< mkdir_options, bool, &mkdir_options::parents_ >; - } - }; - - static _cli_mkdir_options_map_init _cli_mkdir_options_map_init_; - - bool mkdir_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_mkdir_options_map::const_iterator i (_cli_mkdir_options_map_.find (o)); - - if (i != _cli_mkdir_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - // cleanup_options base - // - if (::build2::test::script::cleanup_options::_parse (o, s)) - return true; - - return false; - } - - bool mkdir_options:: - _parse (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt_mode, - ::build2::test::script::cli::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::test::script::cli::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - s.skip (); - r = true; - continue; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::test::script::cli::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::test::script::cli::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - 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::test::script::cli::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; - } - } - } - - switch (opt_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_argument (o); - } - } - - break; - } - - return r; - } - - // mv_options - // - - mv_options:: - mv_options () - : force_ () - { - } - - mv_options:: - mv_options (int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : force_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - } - - mv_options:: - mv_options (int start, - int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : force_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - } - - mv_options:: - mv_options (int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : force_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - mv_options:: - mv_options (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : force_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - mv_options:: - mv_options (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : force_ () - { - _parse (s, opt, arg); - } - - typedef - std::map<std::string, void (*) (mv_options&, ::build2::test::script::cli::scanner&)> - _cli_mv_options_map; - - static _cli_mv_options_map _cli_mv_options_map_; - - struct _cli_mv_options_map_init - { - _cli_mv_options_map_init () - { - _cli_mv_options_map_["--force"] = - &::build2::test::script::cli::thunk< mv_options, bool, &mv_options::force_ >; - _cli_mv_options_map_["-f"] = - &::build2::test::script::cli::thunk< mv_options, bool, &mv_options::force_ >; - } - }; - - static _cli_mv_options_map_init _cli_mv_options_map_init_; - - bool mv_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_mv_options_map::const_iterator i (_cli_mv_options_map_.find (o)); - - if (i != _cli_mv_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - // cleanup_options base - // - if (::build2::test::script::cleanup_options::_parse (o, s)) - return true; - - return false; - } - - bool mv_options:: - _parse (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt_mode, - ::build2::test::script::cli::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::test::script::cli::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - s.skip (); - r = true; - continue; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::test::script::cli::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::test::script::cli::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - 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::test::script::cli::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; - } - } - } - - switch (opt_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_argument (o); - } - } - - break; - } - - return r; - } - - // rm_options - // - - rm_options:: - rm_options () - : recursive_ (), - force_ () - { - } - - rm_options:: - rm_options (int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : recursive_ (), - force_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - } - - rm_options:: - rm_options (int start, - int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : recursive_ (), - force_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - } - - rm_options:: - rm_options (int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : recursive_ (), - force_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - rm_options:: - rm_options (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : recursive_ (), - force_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - rm_options:: - rm_options (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : recursive_ (), - force_ () - { - _parse (s, opt, arg); - } - - typedef - std::map<std::string, void (*) (rm_options&, ::build2::test::script::cli::scanner&)> - _cli_rm_options_map; - - static _cli_rm_options_map _cli_rm_options_map_; - - struct _cli_rm_options_map_init - { - _cli_rm_options_map_init () - { - _cli_rm_options_map_["--recursive"] = - &::build2::test::script::cli::thunk< rm_options, bool, &rm_options::recursive_ >; - _cli_rm_options_map_["-r"] = - &::build2::test::script::cli::thunk< rm_options, bool, &rm_options::recursive_ >; - _cli_rm_options_map_["--force"] = - &::build2::test::script::cli::thunk< rm_options, bool, &rm_options::force_ >; - _cli_rm_options_map_["-f"] = - &::build2::test::script::cli::thunk< rm_options, bool, &rm_options::force_ >; - } - }; - - static _cli_rm_options_map_init _cli_rm_options_map_init_; - - bool rm_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_rm_options_map::const_iterator i (_cli_rm_options_map_.find (o)); - - if (i != _cli_rm_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - return false; - } - - bool rm_options:: - _parse (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt_mode, - ::build2::test::script::cli::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::test::script::cli::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - s.skip (); - r = true; - continue; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::test::script::cli::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::test::script::cli::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - 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::test::script::cli::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; - } - } - } - - switch (opt_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_argument (o); - } - } - - break; - } - - return r; - } - - // rmdir_options - // - - rmdir_options:: - rmdir_options () - : force_ () - { - } - - rmdir_options:: - rmdir_options (int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : force_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - } - - rmdir_options:: - rmdir_options (int start, - int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : force_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - } - - rmdir_options:: - rmdir_options (int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : force_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - rmdir_options:: - rmdir_options (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : force_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - rmdir_options:: - rmdir_options (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : force_ () - { - _parse (s, opt, arg); - } - - typedef - std::map<std::string, void (*) (rmdir_options&, ::build2::test::script::cli::scanner&)> - _cli_rmdir_options_map; - - static _cli_rmdir_options_map _cli_rmdir_options_map_; - - struct _cli_rmdir_options_map_init - { - _cli_rmdir_options_map_init () - { - _cli_rmdir_options_map_["--force"] = - &::build2::test::script::cli::thunk< rmdir_options, bool, &rmdir_options::force_ >; - _cli_rmdir_options_map_["-f"] = - &::build2::test::script::cli::thunk< rmdir_options, bool, &rmdir_options::force_ >; - } - }; - - static _cli_rmdir_options_map_init _cli_rmdir_options_map_init_; - - bool rmdir_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_rmdir_options_map::const_iterator i (_cli_rmdir_options_map_.find (o)); - - if (i != _cli_rmdir_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - return false; - } - - bool rmdir_options:: - _parse (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt_mode, - ::build2::test::script::cli::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::test::script::cli::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - s.skip (); - r = true; - continue; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::test::script::cli::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::test::script::cli::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - 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::test::script::cli::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; - } - } - } - - switch (opt_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_argument (o); - } - } - - break; - } - - return r; - } - - // sed_options - // - - sed_options:: - sed_options () - : quiet_ (), - in_place_ (), - expression_ (), - expression_specified_ (false) - { - } - - sed_options:: - sed_options (int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : quiet_ (), - in_place_ (), - expression_ (), - expression_specified_ (false) - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - } - - sed_options:: - sed_options (int start, - int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : quiet_ (), - in_place_ (), - expression_ (), - expression_specified_ (false) - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - } - - sed_options:: - sed_options (int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : quiet_ (), - in_place_ (), - expression_ (), - expression_specified_ (false) - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - sed_options:: - sed_options (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : quiet_ (), - in_place_ (), - expression_ (), - expression_specified_ (false) - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - sed_options:: - sed_options (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : quiet_ (), - in_place_ (), - expression_ (), - expression_specified_ (false) - { - _parse (s, opt, arg); - } - - typedef - std::map<std::string, void (*) (sed_options&, ::build2::test::script::cli::scanner&)> - _cli_sed_options_map; - - static _cli_sed_options_map _cli_sed_options_map_; - - struct _cli_sed_options_map_init - { - _cli_sed_options_map_init () - { - _cli_sed_options_map_["--quiet"] = - &::build2::test::script::cli::thunk< sed_options, bool, &sed_options::quiet_ >; - _cli_sed_options_map_["-n"] = - &::build2::test::script::cli::thunk< sed_options, bool, &sed_options::quiet_ >; - _cli_sed_options_map_["--in-place"] = - &::build2::test::script::cli::thunk< sed_options, bool, &sed_options::in_place_ >; - _cli_sed_options_map_["-i"] = - &::build2::test::script::cli::thunk< sed_options, bool, &sed_options::in_place_ >; - _cli_sed_options_map_["--expression"] = - &::build2::test::script::cli::thunk< sed_options, strings, &sed_options::expression_, - &sed_options::expression_specified_ >; - _cli_sed_options_map_["-e"] = - &::build2::test::script::cli::thunk< sed_options, strings, &sed_options::expression_, - &sed_options::expression_specified_ >; - } - }; - - static _cli_sed_options_map_init _cli_sed_options_map_init_; - - bool sed_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_sed_options_map::const_iterator i (_cli_sed_options_map_.find (o)); - - if (i != _cli_sed_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - return false; - } - - bool sed_options:: - _parse (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt_mode, - ::build2::test::script::cli::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::test::script::cli::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - s.skip (); - r = true; - continue; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::test::script::cli::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::test::script::cli::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - 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::test::script::cli::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; - } - } - } - - switch (opt_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_argument (o); - } - } - - break; - } - - return r; - } - // set_options // @@ -2924,829 +656,6 @@ namespace build2 return r; } - - // sleep_options - // - - sleep_options:: - sleep_options () - { - } - - sleep_options:: - sleep_options (int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - } - - sleep_options:: - sleep_options (int start, - int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - } - - sleep_options:: - sleep_options (int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - sleep_options:: - sleep_options (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - sleep_options:: - sleep_options (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - { - _parse (s, opt, arg); - } - - typedef - std::map<std::string, void (*) (sleep_options&, ::build2::test::script::cli::scanner&)> - _cli_sleep_options_map; - - static _cli_sleep_options_map _cli_sleep_options_map_; - - struct _cli_sleep_options_map_init - { - _cli_sleep_options_map_init () - { - } - }; - - static _cli_sleep_options_map_init _cli_sleep_options_map_init_; - - bool sleep_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_sleep_options_map::const_iterator i (_cli_sleep_options_map_.find (o)); - - if (i != _cli_sleep_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - return false; - } - - bool sleep_options:: - _parse (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt_mode, - ::build2::test::script::cli::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::test::script::cli::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - s.skip (); - r = true; - continue; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::test::script::cli::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::test::script::cli::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - 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::test::script::cli::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; - } - } - } - - switch (opt_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_argument (o); - } - } - - break; - } - - return r; - } - - // test_options - // - - test_options:: - test_options () - : file_ (), - directory_ () - { - } - - test_options:: - test_options (int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : file_ (), - directory_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - } - - test_options:: - test_options (int start, - int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : file_ (), - directory_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - } - - test_options:: - test_options (int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : file_ (), - directory_ () - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - test_options:: - test_options (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : file_ (), - directory_ () - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - test_options:: - test_options (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : file_ (), - directory_ () - { - _parse (s, opt, arg); - } - - typedef - std::map<std::string, void (*) (test_options&, ::build2::test::script::cli::scanner&)> - _cli_test_options_map; - - static _cli_test_options_map _cli_test_options_map_; - - struct _cli_test_options_map_init - { - _cli_test_options_map_init () - { - _cli_test_options_map_["--file"] = - &::build2::test::script::cli::thunk< test_options, bool, &test_options::file_ >; - _cli_test_options_map_["-f"] = - &::build2::test::script::cli::thunk< test_options, bool, &test_options::file_ >; - _cli_test_options_map_["--directory"] = - &::build2::test::script::cli::thunk< test_options, bool, &test_options::directory_ >; - _cli_test_options_map_["-d"] = - &::build2::test::script::cli::thunk< test_options, bool, &test_options::directory_ >; - } - }; - - static _cli_test_options_map_init _cli_test_options_map_init_; - - bool test_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_test_options_map::const_iterator i (_cli_test_options_map_.find (o)); - - if (i != _cli_test_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - return false; - } - - bool test_options:: - _parse (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt_mode, - ::build2::test::script::cli::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::test::script::cli::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - s.skip (); - r = true; - continue; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::test::script::cli::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::test::script::cli::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - 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::test::script::cli::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; - } - } - } - - switch (opt_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_argument (o); - } - } - - break; - } - - return r; - } - - // touch_options - // - - touch_options:: - touch_options () - : after_ (), - after_specified_ (false) - { - } - - touch_options:: - touch_options (int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : after_ (), - after_specified_ (false) - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - } - - touch_options:: - touch_options (int start, - int& argc, - char** argv, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : after_ (), - after_specified_ (false) - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - } - - touch_options:: - touch_options (int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : after_ (), - after_specified_ (false) - { - ::build2::test::script::cli::argv_scanner s (argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - touch_options:: - touch_options (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : after_ (), - after_specified_ (false) - { - ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase); - _parse (s, opt, arg); - end = s.end (); - } - - touch_options:: - touch_options (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt, - ::build2::test::script::cli::unknown_mode arg) - : after_ (), - after_specified_ (false) - { - _parse (s, opt, arg); - } - - typedef - std::map<std::string, void (*) (touch_options&, ::build2::test::script::cli::scanner&)> - _cli_touch_options_map; - - static _cli_touch_options_map _cli_touch_options_map_; - - struct _cli_touch_options_map_init - { - _cli_touch_options_map_init () - { - _cli_touch_options_map_["--after"] = - &::build2::test::script::cli::thunk< touch_options, string, &touch_options::after_, - &touch_options::after_specified_ >; - } - }; - - static _cli_touch_options_map_init _cli_touch_options_map_init_; - - bool touch_options:: - _parse (const char* o, ::build2::test::script::cli::scanner& s) - { - _cli_touch_options_map::const_iterator i (_cli_touch_options_map_.find (o)); - - if (i != _cli_touch_options_map_.end ()) - { - (*(i->second)) (*this, s); - return true; - } - - // cleanup_options base - // - if (::build2::test::script::cleanup_options::_parse (o, s)) - return true; - - return false; - } - - bool touch_options:: - _parse (::build2::test::script::cli::scanner& s, - ::build2::test::script::cli::unknown_mode opt_mode, - ::build2::test::script::cli::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::test::script::cli::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - s.skip (); - r = true; - continue; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::test::script::cli::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::test::script::cli::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - 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::test::script::cli::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; - } - } - } - - switch (opt_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::test::script::cli::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::test::script::cli::unknown_mode::stop: - { - break; - } - case ::build2::test::script::cli::unknown_mode::fail: - { - throw ::build2::test::script::cli::unknown_argument (o); - } - } - - break; - } - - return r; - } } } } diff --git a/libbuild2/test/script/builtin-options.hxx b/libbuild2/test/script/builtin-options.hxx index 7ff0bac..44e129a 100644 --- a/libbuild2/test/script/builtin-options.hxx +++ b/libbuild2/test/script/builtin-options.hxx @@ -260,514 +260,12 @@ namespace build2 } } -#include <libbuild2/types.hxx> - namespace build2 { namespace test { namespace script { - class cleanup_options - { - public: - // Option accessors. - // - const bool& - no_cleanup () const; - - // Implementation details. - // - protected: - cleanup_options (); - - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - public: - bool no_cleanup_; - }; - - class cat_options - { - public: - cat_options (); - - cat_options (int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - cat_options (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - cat_options (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - cat_options (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - cat_options (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - // Option accessors. - // - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - private: - bool - _parse (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option, - ::build2::test::script::cli::unknown_mode argument); - - public: - }; - - class cp_options: public ::build2::test::script::cleanup_options - { - public: - cp_options (); - - cp_options (int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - cp_options (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - cp_options (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - cp_options (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - cp_options (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - // Option accessors. - // - const bool& - recursive () const; - - const bool& - preserve () const; - - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - private: - bool - _parse (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option, - ::build2::test::script::cli::unknown_mode argument); - - public: - bool recursive_; - bool preserve_; - }; - - class ln_options: public ::build2::test::script::cleanup_options - { - public: - ln_options (); - - ln_options (int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - ln_options (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - ln_options (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - ln_options (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - ln_options (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - // Option accessors. - // - const bool& - symbolic () const; - - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - private: - bool - _parse (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option, - ::build2::test::script::cli::unknown_mode argument); - - public: - bool symbolic_; - }; - - class mkdir_options: public ::build2::test::script::cleanup_options - { - public: - mkdir_options (); - - mkdir_options (int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - mkdir_options (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - mkdir_options (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - mkdir_options (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - mkdir_options (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - // Option accessors. - // - const bool& - parents () const; - - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - private: - bool - _parse (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option, - ::build2::test::script::cli::unknown_mode argument); - - public: - bool parents_; - }; - - class mv_options: public ::build2::test::script::cleanup_options - { - public: - mv_options (); - - mv_options (int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - mv_options (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - mv_options (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - mv_options (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - mv_options (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - // Option accessors. - // - const bool& - force () const; - - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - private: - bool - _parse (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option, - ::build2::test::script::cli::unknown_mode argument); - - public: - bool force_; - }; - - class rm_options - { - public: - rm_options (); - - rm_options (int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - rm_options (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - rm_options (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - rm_options (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - rm_options (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - // Option accessors. - // - const bool& - recursive () const; - - const bool& - force () const; - - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - private: - bool - _parse (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option, - ::build2::test::script::cli::unknown_mode argument); - - public: - bool recursive_; - bool force_; - }; - - class rmdir_options - { - public: - rmdir_options (); - - rmdir_options (int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - rmdir_options (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - rmdir_options (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - rmdir_options (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - rmdir_options (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - // Option accessors. - // - const bool& - force () const; - - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - private: - bool - _parse (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option, - ::build2::test::script::cli::unknown_mode argument); - - public: - bool force_; - }; - - class sed_options - { - public: - sed_options (); - - sed_options (int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - sed_options (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - sed_options (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - sed_options (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - sed_options (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - // Option accessors. - // - const bool& - quiet () const; - - const bool& - in_place () const; - - const strings& - expression () const; - - bool - expression_specified () const; - - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - private: - bool - _parse (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option, - ::build2::test::script::cli::unknown_mode argument); - - public: - bool quiet_; - bool in_place_; - strings expression_; - bool expression_specified_; - }; - class set_options { public: @@ -833,184 +331,6 @@ namespace build2 bool newline_; bool whitespace_; }; - - class sleep_options - { - public: - sleep_options (); - - sleep_options (int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - sleep_options (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - sleep_options (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - sleep_options (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - sleep_options (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - // Option accessors. - // - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - private: - bool - _parse (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option, - ::build2::test::script::cli::unknown_mode argument); - - public: - }; - - class test_options - { - public: - test_options (); - - test_options (int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - test_options (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - test_options (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - test_options (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - test_options (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - // Option accessors. - // - const bool& - file () const; - - const bool& - directory () const; - - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - private: - bool - _parse (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option, - ::build2::test::script::cli::unknown_mode argument); - - public: - bool file_; - bool directory_; - }; - - class touch_options: public ::build2::test::script::cleanup_options - { - public: - touch_options (); - - touch_options (int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - touch_options (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - touch_options (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - touch_options (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - touch_options (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option = ::build2::test::script::cli::unknown_mode::fail, - ::build2::test::script::cli::unknown_mode argument = ::build2::test::script::cli::unknown_mode::stop); - - // Option accessors. - // - const string& - after () const; - - bool - after_specified () const; - - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::test::script::cli::scanner&); - - private: - bool - _parse (::build2::test::script::cli::scanner&, - ::build2::test::script::cli::unknown_mode option, - ::build2::test::script::cli::unknown_mode argument); - - public: - string after_; - bool after_specified_; - }; } } } diff --git a/libbuild2/test/script/builtin-options.ixx b/libbuild2/test/script/builtin-options.ixx index 55fd6d2..bdb95b4 100644 --- a/libbuild2/test/script/builtin-options.ixx +++ b/libbuild2/test/script/builtin-options.ixx @@ -158,111 +158,6 @@ namespace build2 { namespace script { - // cleanup_options - // - - inline const bool& cleanup_options:: - no_cleanup () const - { - return this->no_cleanup_; - } - - // cat_options - // - - // cp_options - // - - inline const bool& cp_options:: - recursive () const - { - return this->recursive_; - } - - inline const bool& cp_options:: - preserve () const - { - return this->preserve_; - } - - // ln_options - // - - inline const bool& ln_options:: - symbolic () const - { - return this->symbolic_; - } - - // mkdir_options - // - - inline const bool& mkdir_options:: - parents () const - { - return this->parents_; - } - - // mv_options - // - - inline const bool& mv_options:: - force () const - { - return this->force_; - } - - // rm_options - // - - inline const bool& rm_options:: - recursive () const - { - return this->recursive_; - } - - inline const bool& rm_options:: - force () const - { - return this->force_; - } - - // rmdir_options - // - - inline const bool& rmdir_options:: - force () const - { - return this->force_; - } - - // sed_options - // - - inline const bool& sed_options:: - quiet () const - { - return this->quiet_; - } - - inline const bool& sed_options:: - in_place () const - { - return this->in_place_; - } - - inline const strings& sed_options:: - expression () const - { - return this->expression_; - } - - inline bool sed_options:: - expression_specified () const - { - return this->expression_specified_; - } - // set_options // @@ -283,39 +178,6 @@ namespace build2 { return this->whitespace_; } - - // sleep_options - // - - // test_options - // - - inline const bool& test_options:: - file () const - { - return this->file_; - } - - inline const bool& test_options:: - directory () const - { - return this->directory_; - } - - // touch_options - // - - inline const string& touch_options:: - after () const - { - return this->after_; - } - - inline bool touch_options:: - after_specified () const - { - return this->after_specified_; - } } } } diff --git a/libbuild2/test/script/builtin.cli b/libbuild2/test/script/builtin.cli index 8b8de73..790e6f1 100644 --- a/libbuild2/test/script/builtin.cli +++ b/libbuild2/test/script/builtin.cli @@ -2,102 +2,25 @@ // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -include <libbuild2/types.hxx>; - // Note that options in this file are undocumented because we generate neither // the usage printing code nor man pages. Instead, they are documented in the // Testscript Language Manual's builtin descriptions. // -// Also note that the string type is used for the path options because their -// parsing depends on the testscript scope working directory (see parse_path() -// for details) and passing this information to the CLI custom parser would -// not be easy. -// namespace build2 { namespace test { namespace script { - // Common option base classes. - // - - class cleanup_options = 0 - { - bool --no-cleanup; - }; - - // Builtin options. + // Pseudo-builtin options. // - class cat_options - { - // No options so far. - // - }; - - class cp_options: cleanup_options - { - bool --recursive|-R|-r; - bool --preserve|-p; - }; - - class ln_options: cleanup_options - { - bool --symbolic|-s; - }; - - class mkdir_options: cleanup_options - { - bool --parents|-p; - }; - - class mv_options: cleanup_options - { - bool --force|-f; - }; - - class rm_options - { - bool --recursive|-r; - bool --force|-f; - }; - - class rmdir_options - { - bool --force|-f; - }; - - class sed_options - { - bool --quiet|-n; - bool --in-place|-i; - strings --expression|-e; - }; - class set_options { bool --exact|-e; bool --newline|-n; bool --whitespace|-w; }; - - class sleep_options - { - // No options so far. - // - }; - - class test_options - { - bool --file|-f; - bool --directory|-d; - }; - - class touch_options: cleanup_options - { - string --after; // Path (see above). - }; } } } diff --git a/libbuild2/test/script/builtin.cxx b/libbuild2/test/script/builtin.cxx deleted file mode 100644 index 06b0cec..0000000 --- a/libbuild2/test/script/builtin.cxx +++ /dev/null @@ -1,1924 +0,0 @@ -// file : libbuild2/test/script/builtin.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include <libbuild2/test/script/builtin.hxx> - -#include <chrono> -#include <locale> -#include <ostream> -#include <sstream> -#include <cstdlib> // strtoull() - -#include <libbutl/regex.mxx> -#include <libbutl/path-io.mxx> // use default operator<< implementation -#include <libbutl/fdstream.mxx> // fdopen_mode, fdstream_mode -#include <libbutl/filesystem.mxx> - -#include <libbuild2/context.hxx> // sched - -#include <libbuild2/test/script/script.hxx> -#include <libbuild2/test/script/builtin-options.hxx> - -// Strictly speaking a builtin which reads/writes from/to standard streams -// must be asynchronous so that the caller can communicate with it through -// pipes without being blocked on I/O operations. However, as an optimization, -// we allow builtins that only print diagnostics to STDERR to be synchronous -// assuming that their output will always fit the pipe buffer. Synchronous -// builtins must not read from STDIN and write to STDOUT. Later we may relax -// this rule to allow a "short" output for such builtins. -// -using namespace std; -using namespace butl; - -namespace build2 -{ - namespace test - { - namespace script - { - using builtin_impl = uint8_t (scope&, - const strings& args, - auto_fd in, auto_fd out, auto_fd err); - - // Operation failed, diagnostics has already been issued. - // - struct failed {}; - - // Accumulate an error message, print it atomically in dtor to the - // provided stream and throw failed afterwards if requested. Prefixes - // the message with the builtin name. - // - // Move constructible-only, not assignable (based to diag_record). - // - class error_record - { - public: - template <typename T> - friend const error_record& - operator<< (const error_record& r, const T& x) - { - r.ss_ << x; - return r; - } - - error_record (ostream& o, bool fail, const char* name) - : os_ (o), fail_ (fail), empty_ (false) - { - ss_ << name << ": "; - } - - // Older versions of libstdc++ don't have the ostringstream move - // support. Luckily, GCC doesn't seem to be actually needing move due - // to copy/move elision. - // -#ifdef __GLIBCXX__ - error_record (error_record&&); -#else - error_record (error_record&& r) - : os_ (r.os_), - ss_ (move (r.ss_)), - fail_ (r.fail_), - empty_ (r.empty_) - { - r.empty_ = true; - } -#endif - - ~error_record () noexcept (false) - { - if (!empty_) - { - // The output stream can be in a bad state (for example as a - // result of unsuccessful attempt to report a previous error), so - // we check it. - // - if (os_.good ()) - { - ss_.put ('\n'); - os_ << ss_.str (); - os_.flush (); - } - - if (fail_) - throw failed (); - } - } - - private: - ostream& os_; - mutable ostringstream ss_; - - bool fail_; - bool empty_; - }; - - // Parse and normalize a path. Also, unless it is already absolute, make - // the path absolute using the specified directory. Throw invalid_path - // if the path is empty, and on parsing and normalization failures. - // - static path - parse_path (string s, const dir_path& d) - { - path p (move (s)); - - if (p.empty ()) - throw invalid_path (""); - - if (p.relative ()) - p = d / move (p); - - p.normalize (); - return p; - } - - // Builtin commands functions. - // - - // cat <file>... - // - // Note that POSIX doesn't specify if after I/O operation failure the - // command should proceed with the rest of the arguments. The current - // implementation exits immediatelly in such a case. - // - // @@ Shouldn't we check that we don't print a nonempty regular file to - // itself, as that would merely exhaust the output device? POSIX - // allows (but not requires) such a check and some implementations do - // this. That would require to fstat() file descriptors and complicate - // the code a bit. Was able to reproduce on a big file (should be - // bigger than the stream buffer size) with the test - // 'cat file >+file'. - // - // Note: must be executed asynchronously. - // - static uint8_t - cat (scope& sp, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (1); - ofdstream cerr (move (err)); - - auto error = [&cerr] (bool fail = true) - { - return error_record (cerr, fail, "cat"); - }; - - try - { - ifdstream cin (move (in), fdstream_mode::binary); - ofdstream cout (move (out), fdstream_mode::binary); - - // Parse arguments. - // - cli::vector_scanner scan (args); - cat_options ops (scan); // Makes sure no options passed. - - // Print files. - // - // Copy input stream to STDOUT. - // - auto copy = [&cout] (istream& is) - { - if (is.peek () != ifdstream::traits_type::eof ()) - cout << is.rdbuf (); - - is.clear (istream::eofbit); // Sets eofbit. - }; - - // Path of a file being printed to STDOUT. An empty path represents - // STDIN. Used in diagnostics. - // - path p; - - try - { - // Print STDIN. - // - if (!scan.more ()) - copy (cin); - - // Print files. - // - while (scan.more ()) - { - string f (scan.next ()); - - if (f == "-") - { - if (!cin.eof ()) - { - p.clear (); - copy (cin); - } - - continue; - } - - p = parse_path (move (f), sp.wd_path); - - ifdstream is (p, fdopen_mode::binary); - copy (is); - is.close (); - } - } - catch (const io_error& e) - { - error_record d (error ()); - d << "unable to print "; - - if (p.empty ()) - d << "stdin"; - else - d << "'" << p << "'"; - - d << ": " << e; - } - - cin.close (); - cout.close (); - r = 0; - } - catch (const invalid_path& e) - { - error (false) << "invalid path '" << e.path << "'"; - } - // Can be thrown while creating/closing cin, cout or writing to cerr. - // - catch (const io_error& e) - { - error (false) << e; - } - catch (const failed&) - { - // Diagnostics has already been issued. - } - catch (const cli::exception& e) - { - error (false) << e; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 1; - } - - // Make a copy of a file at the specified path, preserving permissions, - // and registering a cleanup for a newly created file. The file paths - // must be absolute. Fail if an exception is thrown by the underlying - // copy operation. - // - static void - cpfile (scope& sp, - const path& from, const path& to, - bool overwrite, - bool attrs, - bool cleanup, - const function<error_record()>& fail) - { - try - { - bool exists (file_exists (to)); - - cpflags f ( - overwrite - ? cpflags::overwrite_permissions | cpflags::overwrite_content - : cpflags::none); - - if (attrs) - f |= cpflags::overwrite_permissions | cpflags::copy_timestamps; - - cpfile (from, to, f); - - if (!exists && cleanup) - sp.clean ({cleanup_type::always, to}, true /* implicit */); - } - catch (const system_error& e) - { - fail () << "unable to copy file '" << from << "' to '" << to - << "': " << e; - } - } - - // Make a copy of a directory at the specified path, registering a - // cleanup for the created directory. The directory paths must be - // absolute. Fail if the destination directory already exists or - // an exception is thrown by the underlying copy operation. - // - static void - cpdir (scope& sp, - const dir_path& from, const dir_path& to, - bool attrs, - bool cleanup, - const function<error_record()>& fail) - { - try - { - if (try_mkdir (to) == mkdir_status::already_exists) - throw_generic_error (EEXIST); - - if (cleanup) - sp.clean ({cleanup_type::always, to}, true /* implicit */); - - for (const auto& de: dir_iterator (from, - false /* ignore_dangling */)) - { - path f (from / de.path ()); - path t (to / de.path ()); - - if (de.type () == entry_type::directory) - cpdir (sp, - path_cast<dir_path> (move (f)), - path_cast<dir_path> (move (t)), - attrs, - cleanup, - fail); - else - cpfile (sp, f, t, false /* overwrite */, attrs, cleanup, fail); - } - - // Note that it is essential to copy timestamps and permissions after - // the directory content is copied. - // - if (attrs) - { - path_permissions (to, path_permissions (from)); - dir_time (to, dir_time (from)); - } - } - catch (const system_error& e) - { - fail () << "unable to copy directory '" << from << "' to '" << to - << "': " << e; - } - } - - // cp [-p|--preserve] [--no-cleanup] <src-file> <dst-file> - // cp [-p|--preserve] [--no-cleanup] -R|-r|--recursive <src-dir> <dst-dir> - // cp [-p|--preserve] [--no-cleanup] <src-file>... <dst-dir>/ - // cp [-p|--preserve] [--no-cleanup] -R|-r|--recursive <src-path>... <dst-dir>/ - // - // Note: can be executed synchronously. - // - static uint8_t - cp (scope& sp, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (1); - ofdstream cerr (move (err)); - - auto error = [&cerr] (bool fail = true) - { - return error_record (cerr, fail, "cp"); - }; - - try - { - in.close (); - out.close (); - - // Parse arguments. - // - cli::vector_scanner scan (args); - cp_options ops (scan); - - // Copy files or directories. - // - if (!scan.more ()) - error () << "missing arguments"; - - // Note that the arguments semantics depends on the last argument, - // so we read out and cache them. - // - small_vector<string, 2> args; - while (scan.more ()) - args.push_back (scan.next ()); - - const dir_path& wd (sp.wd_path); - - auto i (args.begin ()); - auto j (args.rbegin ()); - path dst (parse_path (move (*j++), wd)); - auto e (j.base ()); - - if (i == e) - error () << "missing source path"; - - auto fail = [&error] () {return error (true);}; - - bool cleanup (!ops.no_cleanup ()); - - // If destination is not a directory path (no trailing separator) - // then make a copy of the filesystem entry at the specified path - // (the only source path is allowed in such a case). Otherwise copy - // the source filesystem entries into the destination directory. - // - if (!dst.to_directory ()) - { - path src (parse_path (move (*i++), wd)); - - // If there are multiple sources but no trailing separator for the - // destination, then, most likelly, it is missing. - // - if (i != e) - error () << "multiple source paths without trailing separator " - << "for destination directory"; - - if (!ops.recursive ()) - // Synopsis 1: make a file copy at the specified path. - // - cpfile (sp, - src, - dst, - true /* overwrite */, - ops.preserve (), - cleanup, - fail); - else - // Synopsis 2: make a directory copy at the specified path. - // - cpdir (sp, - path_cast<dir_path> (src), path_cast<dir_path> (dst), - ops.preserve (), - cleanup, - fail); - } - else - { - for (; i != e; ++i) - { - path src (parse_path (move (*i), wd)); - - if (ops.recursive () && dir_exists (src)) - // Synopsis 4: copy a filesystem entry into the specified - // directory. Note that we handle only source directories here. - // Source files are handled below. - // - cpdir (sp, - path_cast<dir_path> (src), - path_cast<dir_path> (dst / src.leaf ()), - ops.preserve (), - cleanup, - fail); - else - // Synopsis 3: copy a file into the specified directory. Also, - // here we cover synopsis 4 for the source path being a file. - // - cpfile (sp, - src, - dst / src.leaf (), - true /* overwrite */, - ops.preserve (), - cleanup, - fail); - } - } - - r = 0; - } - catch (const invalid_path& e) - { - error (false) << "invalid path '" << e.path << "'"; - } - // Can be thrown while closing in, out or writing to cerr. - // - catch (const io_error& e) - { - error (false) << e; - } - catch (const failed&) - { - // Diagnostics has already been issued. - } - catch (const cli::exception& e) - { - error (false) << e; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 1; - } - - // echo <string>... - // - // Note: must be executed asynchronously. - // - static uint8_t - echo (scope&, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (1); - ofdstream cerr (move (err)); - - try - { - in.close (); - ofdstream cout (move (out)); - - for (auto b (args.begin ()), i (b), e (args.end ()); i != e; ++i) - cout << (i != b ? " " : "") << *i; - - cout << '\n'; - cout.close (); - r = 0; - } - catch (const std::exception& e) - { - cerr << "echo: " << e << endl; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 1; - } - - // false - // - // Failure to close the file descriptors is silently ignored. - // - // Note: can be executed synchronously. - // - static builtin - false_ (scope&, uint8_t& r, const strings&, auto_fd, auto_fd, auto_fd) - { - return builtin (r = 1); - } - - // true - // - // Failure to close the file descriptors is silently ignored. - // - // Note: can be executed synchronously. - // - static builtin - true_ (scope&, uint8_t& r, const strings&, auto_fd, auto_fd, auto_fd) - { - return builtin (r = 0); - } - - // Create a symlink to a file or directory at the specified path. The - // paths must be absolute. Fall back to creating a hardlink, if symlink - // creation is not supported for the link path. If hardlink creation is - // not supported either, then fall back to copies. If requested, created - // filesystem entries are registered for cleanup. Fail if the target - // filesystem entry doesn't exist or an exception is thrown by the - // underlying filesystem operation (specifically for an already existing - // filesystem entry at the link path). - // - // Note that supporting optional removal of an existing filesystem entry - // at the link path (the -f option) tends to get hairy. As soon as an - // existing and the resulting filesystem entries could be of different - // types, we would end up with canceling an old cleanup and registering - // the new one. Also removing non-empty directories doesn't look very - // natural, but would be required if we want the behavior on POSIX and - // Windows to be consistent. - // - static void - mksymlink (scope& sp, - const path& target, const path& link, - bool cleanup, - const function<error_record()>& fail) - { - // Determine the target type, fail if the target doesn't exist. - // - bool dir (false); - - try - { - pair<bool, entry_stat> pe (path_entry (target)); - - if (!pe.first) - fail () << "unable to create symlink to '" << target << "': " - << "no such file or directory"; - - dir = pe.second.type == entry_type::directory; - } - catch (const system_error& e) - { - fail () << "unable to stat '" << target << "': " << e; - } - - // First we try to create a symlink. If that fails (e.g., "Windows - // happens"), then we resort to hard links. If that doesn't work out - // either (e.g., not on the same filesystem), then we fall back to - // copies. So things are going to get a bit nested. - // - // Note: similar to mkanylink() but with support for directories. - // - try - { - mksymlink (target, link, dir); - - if (cleanup) - sp.clean ({cleanup_type::always, link}, true /* implicit */); - } - catch (const system_error& e) - { - // Note that we are not guaranteed (here and below) that the - // system_error exception is of the generic category. - // - int c (e.code ().value ()); - if (!(e.code ().category () == generic_category () && - (c == ENOSYS || // Not implemented. - c == EPERM))) // Not supported by the filesystem(s). - fail () << "unable to create symlink '" << link << "' to '" - << target << "': " << e; - - try - { - mkhardlink (target, link, dir); - - if (cleanup) - sp.clean ({cleanup_type::always, link}, true /* implicit */); - } - catch (const system_error& e) - { - c = e.code ().value (); - if (!(e.code ().category () == generic_category () && - (c == ENOSYS || // Not implemented. - c == EPERM || // Not supported by the filesystem(s). - c == EXDEV))) // On different filesystems. - fail () << "unable to create hardlink '" << link << "' to '" - << target << "': " << e; - - if (dir) - cpdir (sp, - path_cast<dir_path> (target), path_cast<dir_path> (link), - false, - cleanup, - fail); - else - cpfile (sp, - target, - link, - false /* overwrite */, - true /* attrs */, - cleanup, - fail); - } - } - } - - // ln [--no-cleanup] -s|--symbolic <target-path> <link-path> - // ln [--no-cleanup] -s|--symbolic <target-path>... <link-dir>/ - // - // Note: can be executed synchronously. - // - static uint8_t - ln (scope& sp, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (1); - ofdstream cerr (move (err)); - - auto error = [&cerr] (bool fail = true) - { - return error_record (cerr, fail, "ln"); - }; - - try - { - in.close (); - out.close (); - - // Parse arguments. - // - cli::vector_scanner scan (args); - ln_options ops (scan); - - if (!ops.symbolic ()) - error () << "missing -s|--symbolic option"; - - // Create file or directory symlinks. - // - if (!scan.more ()) - error () << "missing arguments"; - - // Note that the arguments semantics depends on the last argument, - // so we read out and cache them. - // - small_vector<string, 2> args; - while (scan.more ()) - args.push_back (scan.next ()); - - const dir_path& wd (sp.wd_path); - - auto i (args.begin ()); - auto j (args.rbegin ()); - path link (parse_path (move (*j++), wd)); - auto e (j.base ()); - - if (i == e) - error () << "missing target path"; - - auto fail = [&error] () {return error (true);}; - - bool cleanup (!ops.no_cleanup ()); - - // If link is not a directory path (no trailing separator), then - // create a symlink to the target path at the specified link path - // (the only target path is allowed in such a case). Otherwise create - // links to the target paths inside the specified directory. - // - if (!link.to_directory ()) - { - path target (parse_path (move (*i++), wd)); - - // If there are multiple targets but no trailing separator for the - // link, then, most likelly, it is missing. - // - if (i != e) - error () << "multiple target paths with non-directory link path"; - - // Synopsis 1: create a target path symlink at the specified path. - // - mksymlink (sp, target, link, cleanup, fail); - } - else - { - for (; i != e; ++i) - { - path target (parse_path (move (*i), wd)); - - // Synopsis 2: create a target path symlink in the specified - // directory. - // - mksymlink (sp, target, link / target.leaf (), cleanup, fail); - } - } - - r = 0; - } - catch (const invalid_path& e) - { - error (false) << "invalid path '" << e.path << "'"; - } - // Can be thrown while closing in, out or writing to cerr. - // - catch (const io_error& e) - { - error (false) << e; - } - catch (const failed&) - { - // Diagnostics has already been issued. - } - catch (const cli::exception& e) - { - error (false) << e; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 1; - } - - // Create a directory if not exist and its parent directories if - // necessary. Throw system_error on failure. Register created - // directories for cleanup. The directory path must be absolute. - // - static void - mkdir_p (scope& sp, const dir_path& p, bool cleanup) - { - if (!dir_exists (p)) - { - if (!p.root ()) - mkdir_p (sp, p.directory (), cleanup); - - try_mkdir (p); // Returns success or throws. - - if (cleanup) - sp.clean ({cleanup_type::always, p}, true /* implicit */); - } - } - - // mkdir [--no-cleanup] [-p|--parents] <dir>... - // - // Note that POSIX doesn't specify if after a directory creation failure - // the command should proceed with the rest of the arguments. The current - // implementation exits immediatelly in such a case. - // - // Note: can be executed synchronously. - // - static uint8_t - mkdir (scope& sp, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (1); - ofdstream cerr (move (err)); - - auto error = [&cerr] (bool fail = true) - { - return error_record (cerr, fail, "mkdir"); - }; - - try - { - in.close (); - out.close (); - - // Parse arguments. - // - cli::vector_scanner scan (args); - mkdir_options ops (scan); - - // Create directories. - // - if (!scan.more ()) - error () << "missing directory"; - - bool cleanup (!ops.no_cleanup ()); - - while (scan.more ()) - { - dir_path p (path_cast<dir_path> (parse_path (scan.next (), - sp.wd_path))); - - try - { - if (ops.parents ()) - mkdir_p (sp, p, cleanup); - else if (try_mkdir (p) == mkdir_status::success) - { - if (cleanup) - sp.clean ({cleanup_type::always, p}, true /* implicit */); - } - else // == mkdir_status::already_exists - throw_generic_error (EEXIST); - } - catch (const system_error& e) - { - error () << "unable to create directory '" << p << "': " << e; - } - } - - r = 0; - } - catch (const invalid_path& e) - { - error (false) << "invalid path '" << e.path << "'"; - } - // Can be thrown while closing in, out or writing to cerr. - // - catch (const io_error& e) - { - error (false) << e; - } - catch (const failed&) - { - // Diagnostics has already been issued. - } - catch (const cli::exception& e) - { - error (false) << e; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 1; - } - - // mv [--no-cleanup] [-f|--force] <src-path> <dst-path> - // mv [--no-cleanup] [-f|--force] <src-path>... <dst-dir>/ - // - // Note: can be executed synchronously. - // - static uint8_t - mv (scope& sp, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (1); - ofdstream cerr (move (err)); - - auto error = [&cerr] (bool fail = true) - { - return error_record (cerr, fail, "mv"); - }; - - try - { - in.close (); - out.close (); - - // Parse arguments. - // - cli::vector_scanner scan (args); - mv_options ops (scan); - - // Move filesystem entries. - // - if (!scan.more ()) - error () << "missing arguments"; - - // Note that the arguments semantics depends on the last argument, - // so we read out and cache them. - // - small_vector<string, 2> args; - while (scan.more ()) - args.push_back (scan.next ()); - - const dir_path& wd (sp.wd_path); - - auto i (args.begin ()); - auto j (args.rbegin ()); - path dst (parse_path (move (*j++), wd)); - auto e (j.base ()); - - if (i == e) - error () << "missing source path"; - - auto mv = [ops, &wd, &sp, &error] (const path& from, const path& to) - { - const dir_path& rwd (sp.root.wd_path); - - if (!from.sub (rwd) && !ops.force ()) - error () << "'" << from << "' is out of working directory '" - << rwd << "'"; - - try - { - auto check_wd = [&wd, &error] (const path& p) - { - if (wd.sub (path_cast<dir_path> (p))) - error () << "'" << p << "' contains test working directory '" - << wd << "'"; - }; - - check_wd (from); - check_wd (to); - - bool exists (butl::entry_exists (to)); - - // Fail if the source and destination paths are the same. - // - // Note that for mventry() function (that is based on the POSIX - // rename() function) this is a noop. - // - if (exists && to == from) - error () << "unable to move entity '" << from << "' to itself"; - - // Rename/move the filesystem entry, replacing an existing one. - // - mventry (from, - to, - cpflags::overwrite_permissions | - cpflags::overwrite_content); - - // Unless suppressed, adjust the cleanups that are sub-paths of - // the source path. - // - if (!ops.no_cleanup ()) - { - // "Move" the matching cleanup if the destination path doesn't - // exist and is a sub-path of the working directory. Otherwise - // just remove it. - // - // Note that it's not enough to just change the cleanup paths. - // We also need to make sure that these cleanups happen before - // the destination directory (or any of its parents) cleanup, - // that is potentially registered. To achieve that we can just - // relocate these cleanup entries to the end of the list, - // preserving their mutual order. Remember that cleanups in - // the list are executed in the reversed order. - // - bool mv_cleanups (!exists && to.sub (rwd)); - cleanups cs; - - // Remove the source path sub-path cleanups from the list, - // adjusting/caching them if required (see above). - // - for (auto i (sp.cleanups.begin ()); i != sp.cleanups.end (); ) - { - cleanup& c (*i); - path& p (c.path); - - if (p.sub (from)) - { - if (mv_cleanups) - { - // Note that we need to preserve the cleanup path - // trailing separator which indicates the removal - // method. Also note that leaf(), in particular, does - // that. - // - p = p != from - ? to / p.leaf (path_cast<dir_path> (from)) - : p.to_directory () - ? path_cast<dir_path> (to) - : to; - - cs.push_back (move (c)); - } - - i = sp.cleanups.erase (i); - } - else - ++i; - } - - // Re-insert the adjusted cleanups at the end of the list. - // - sp.cleanups.insert (sp.cleanups.end (), - make_move_iterator (cs.begin ()), - make_move_iterator (cs.end ())); - } - } - catch (const system_error& e) - { - error () << "unable to move entity '" << from << "' to '" << to - << "': " << e; - } - }; - - // If destination is not a directory path (no trailing separator) - // then move the filesystem entry to the specified path (the only - // source path is allowed in such a case). Otherwise move the source - // filesystem entries into the destination directory. - // - if (!dst.to_directory ()) - { - path src (parse_path (move (*i++), wd)); - - // If there are multiple sources but no trailing separator for the - // destination, then, most likelly, it is missing. - // - if (i != e) - error () << "multiple source paths without trailing separator " - << "for destination directory"; - - // Synopsis 1: move an entity to the specified path. - // - mv (src, dst); - } - else - { - // Synopsis 2: move entities into the specified directory. - // - for (; i != e; ++i) - { - path src (parse_path (move (*i), wd)); - mv (src, dst / src.leaf ()); - } - } - - r = 0; - } - catch (const invalid_path& e) - { - error (false) << "invalid path '" << e.path << "'"; - } - // Can be thrown while closing in, out or writing to cerr. - // - catch (const io_error& e) - { - error (false) << e; - } - catch (const failed&) - { - // Diagnostics has already been issued. - } - catch (const cli::exception& e) - { - error (false) << e; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 1; - } - - // rm [-r|--recursive] [-f|--force] <path>... - // - // The implementation deviates from POSIX in a number of ways. It doesn't - // interact with a user and fails immediatelly if unable to process an - // argument. It doesn't check for dots containment in the path, and - // doesn't consider files and directory permissions in any way just - // trying to remove a filesystem entry. Always fails if empty path is - // specified. - // - // Note: can be executed synchronously. - // - static uint8_t - rm (scope& sp, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (1); - ofdstream cerr (move (err)); - - auto error = [&cerr] (bool fail = true) - { - return error_record (cerr, fail, "rm"); - }; - - try - { - in.close (); - out.close (); - - // Parse arguments. - // - cli::vector_scanner scan (args); - rm_options ops (scan); - - // Remove entries. - // - if (!scan.more () && !ops.force ()) - error () << "missing file"; - - const dir_path& wd (sp.wd_path); - const dir_path& rwd (sp.root.wd_path); - - while (scan.more ()) - { - path p (parse_path (scan.next (), wd)); - - if (!p.sub (rwd) && !ops.force ()) - error () << "'" << p << "' is out of working directory '" << rwd - << "'"; - - try - { - dir_path d (path_cast<dir_path> (p)); - - if (dir_exists (d)) - { - if (!ops.recursive ()) - error () << "'" << p << "' is a directory"; - - if (wd.sub (d)) - error () << "'" << p << "' contains test working directory '" - << wd << "'"; - - // The call can result in rmdir_status::not_exist. That's not - // very likelly but there is also nothing bad about it. - // - try_rmdir_r (d); - } - else if (try_rmfile (p) == rmfile_status::not_exist && - !ops.force ()) - throw_generic_error (ENOENT); - } - catch (const system_error& e) - { - error () << "unable to remove '" << p << "': " << e; - } - } - - r = 0; - } - catch (const invalid_path& e) - { - error (false) << "invalid path '" << e.path << "'"; - } - // Can be thrown while closing in, out or writing to cerr. - // - catch (const io_error& e) - { - error (false) << e; - } - catch (const failed&) - { - // Diagnostics has already been issued. - } - catch (const cli::exception& e) - { - error (false) << e; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 1; - } - - // rmdir [-f|--force] <path>... - // - // Note: can be executed synchronously. - // - static uint8_t - rmdir (scope& sp, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (1); - ofdstream cerr (move (err)); - - auto error = [&cerr] (bool fail = true) - { - return error_record (cerr, fail, "rmdir"); - }; - - try - { - in.close (); - out.close (); - - // Parse arguments. - // - cli::vector_scanner scan (args); - rmdir_options ops (scan); - - // Remove directories. - // - if (!scan.more () && !ops.force ()) - error () << "missing directory"; - - const dir_path& wd (sp.wd_path); - const dir_path& rwd (sp.root.wd_path); - - while (scan.more ()) - { - dir_path p (path_cast<dir_path> (parse_path (scan.next (), wd))); - - if (wd.sub (p)) - error () << "'" << p << "' contains test working directory '" - << wd << "'"; - - if (!p.sub (rwd) && !ops.force ()) - error () << "'" << p << "' is out of working directory '" - << rwd << "'"; - - try - { - rmdir_status s (try_rmdir (p)); - - if (s == rmdir_status::not_empty) - throw_generic_error (ENOTEMPTY); - else if (s == rmdir_status::not_exist && !ops.force ()) - throw_generic_error (ENOENT); - } - catch (const system_error& e) - { - error () << "unable to remove '" << p << "': " << e; - } - } - - r = 0; - } - catch (const invalid_path& e) - { - error (false) << "invalid path '" << e.path << "'"; - } - // Can be thrown while closing in, out or writing to cerr. - // - catch (const io_error& e) - { - error (false) << e; - } - catch (const failed&) - { - // Diagnostics has already been issued. - } - catch (const cli::exception& e) - { - error (false) << e; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 1; - } - - // sed [-n|--quiet] [-i|--in-place] -e|--expression <script> [<file>] - // - // Note: must be executed asynchronously. - // - static uint8_t - sed (scope& sp, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (1); - ofdstream cerr (move (err)); - - auto error = [&cerr] (bool fail = true) - { - return error_record (cerr, fail, "sed"); - }; - - try - { - // Automatically remove a temporary file (used for in place editing) - // on failure. - // - auto_rmfile rm; - - // Do not throw when failbit is set (getline() failed to extract any - // character). - // - ifdstream cin (move (in), ifdstream::badbit); - ofdstream cout (move (out)); - - // Parse arguments. - // - cli::vector_scanner scan (args); - sed_options ops (scan); - - if (ops.expression ().empty ()) - error () << "missing script"; - - // Only a single script is supported. - // - if (ops.expression ().size () != 1) - error () << "multiple scripts"; - - struct - { - string regex; - string replacement; - bool icase = false; - bool global = false; - bool print = false; - } subst; - - { - const string& v (ops.expression ()[0]); - if (v.empty ()) - error () << "empty script"; - - if (v[0] != 's') - error () << "only 's' command supported"; - - // Parse the substitute command. - // - if (v.size () < 2) - error () << "no delimiter for 's' command"; - - char delim (v[1]); - if (delim == '\\' || delim == '\n') - error () << "invalid delimiter for 's' command"; - - size_t p (v.find (delim, 2)); - if (p == string::npos) - error () << "unterminated 's' command regex"; - - subst.regex.assign (v, 2, p - 2); - - // Empty regex matches nothing, so not of much use. - // - if (subst.regex.empty ()) - error () << "empty regex in 's' command"; - - size_t b (p + 1); - p = v.find (delim, b); - if (p == string::npos) - error () << "unterminated 's' command replacement"; - - subst.replacement.assign (v, b, p - b); - - // Parse the substitute command flags. - // - char c; - for (++p; (c = v[p]) != '\0'; ++p) - { - switch (c) - { - case 'i': subst.icase = true; break; - case 'g': subst.global = true; break; - case 'p': subst.print = true; break; - default: - { - error () << "invalid 's' command flag '" << c << "'"; - } - } - } - } - - // Path of a file to edit. An empty path represents stdin. - // - path p; - if (scan.more ()) - { - string f (scan.next ()); - - if (f != "-") - p = parse_path (move (f), sp.wd_path); - } - - if (scan.more ()) - error () << "unexpected argument '" << scan.next () << "'"; - - // Edit file. - // - // If we edit file in place make sure that the file path is specified - // and obtain a temporary file path. We will be writing to the - // temporary file (rather than to stdout) and will move it to the - // original file path afterwards. - // - path tp; - if (ops.in_place ()) - { - if (p.empty ()) - error () << "-i|--in-place option specified while reading from " - << "stdin"; - - try - { - tp = path::temp_path ("build2-sed"); - - cout.close (); // Flush and close. - - cout.open ( - fdopen (tp, - fdopen_mode::out | fdopen_mode::truncate | - fdopen_mode::create, - path_permissions (p))); - } - catch (const io_error& e) - { - error_record d (error ()); - d << "unable to open '" << tp << "': " << e; - } - catch (const system_error& e) - { - error_record d (error ()); - d << "unable to obtain temporary file: " << e; - } - - rm = auto_rmfile (tp); - } - - // Note that ECMAScript is implied if no grammar flag is specified. - // - regex re (subst.regex, - subst.icase ? regex::icase : regex::ECMAScript); - - // Edit a file or STDIN. - // - try - { - // Open a file if specified. - // - if (!p.empty ()) - { - cin.close (); // Flush and close. - cin.open (p); - } - - // Read until failbit is set (throw on badbit). - // - string s; - while (getline (cin, s)) - { - auto r (regex_replace_search ( - s, - re, - subst.replacement, - subst.global - ? regex_constants::format_default - : regex_constants::format_first_only)); - - // Add newline regardless whether the source line is newline- - // terminated or not (in accordance with POSIX). - // - if (!ops.quiet () || (r.second && subst.print)) - cout << r.first << '\n'; - } - - cin.close (); - cout.close (); - - if (ops.in_place ()) - { - mvfile ( - tp, p, - cpflags::overwrite_content | cpflags::overwrite_permissions); - - rm.cancel (); - } - - r = 0; - } - catch (const io_error& e) - { - error_record d (error ()); - d << "unable to edit "; - - if (p.empty ()) - d << "stdin"; - else - d << "'" << p << "'"; - - d << ": " << e; - } - } - catch (const regex_error& e) - { - // Print regex_error description if meaningful (no space). - // - error (false) << "invalid regex" << e; - } - catch (const invalid_path& e) - { - error (false) << "invalid path '" << e.path << "'"; - } - // Can be thrown while creating cin, cout or writing to cerr. - // - catch (const io_error& e) - { - error (false) << e; - } - catch (const system_error& e) - { - error (false) << e; - } - catch (const failed&) - { - // Diagnostics has already been issued. - } - catch (const cli::exception& e) - { - error (false) << e; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 1; - } - - // sleep <seconds> - // - // Note: can be executed synchronously. - // - static uint8_t - sleep (scope& s, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (1); - ofdstream cerr (move (err)); - - auto error = [&cerr] (bool fail = true) - { - return error_record (cerr, fail, "sleep"); - }; - - try - { - in.close (); - out.close (); - - // Parse arguments. - // - cli::vector_scanner scan (args); - sleep_options ops (scan); // Makes sure no options passed. - - if (!scan.more ()) - error () << "missing time interval"; - - uint64_t n; - - for (;;) // Breakout loop. - { - string a (scan.next ()); - - // Note: strtoull() allows these. - // - if (!a.empty () && a[0] != '-' && a[0] != '+') - { - char* e (nullptr); - n = strtoull (a.c_str (), &e, 10); // Can't throw. - - if (errno != ERANGE && e == a.c_str () + a.size ()) - break; - } - - error () << "invalid time interval '" << a << "'"; - } - - if (scan.more ()) - error () << "unexpected argument '" << scan.next () << "'"; - - // Sleep. - // - // If/when required we could probably support the precise sleep mode - // (e.g., via an option). - // - s.root.test_target.ctx.sched.sleep (chrono::seconds (n)); - - r = 0; - } - // Can be thrown while closing in, out or writing to cerr. - // - catch (const io_error& e) - { - error (false) << e; - } - catch (const failed&) - { - // Diagnostics has already been issued. - } - catch (const cli::exception& e) - { - error (false) << e; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 1; - } - - // test (-f|--file)|(-d|--directory) <path> - // - // Note: can be executed synchronously. - // - static uint8_t - test (scope& sp, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (2); - ofdstream cerr (move (err)); - - auto error = [&cerr] (bool fail = true) - { - return error_record (cerr, fail, "test"); - }; - - try - { - in.close (); - out.close (); - - // Parse arguments. - // - cli::vector_scanner scan (args); - test_options ops (scan); // Makes sure no options passed. - - if (!ops.file () && !ops.directory ()) - error () << "either -f|--file or -d|--directory must be specified"; - - if (ops.file () && ops.directory ()) - error () << "both -f|--file and -d|--directory specified"; - - if (!scan.more ()) - error () << "missing path"; - - path p (parse_path (scan.next (), sp.wd_path)); - - if (scan.more ()) - error () << "unexpected argument '" << scan.next () << "'"; - - try - { - r = (ops.file () ? file_exists (p) : dir_exists (p)) ? 0 : 1; - } - catch (const system_error& e) - { - error () << "cannot test '" << p << "': " << e; - } - } - catch (const invalid_path& e) - { - error (false) << "invalid path '" << e.path << "'"; - } - // Can be thrown while closing in, out or writing to cerr. - // - catch (const io_error& e) - { - error (false) << e; - } - catch (const failed&) - { - // Diagnostics has already been issued. - } - catch (const cli::exception& e) - { - error (false) << e; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 2; - } - - // touch [--no-cleanup] [--after <ref-file>] <file>... - // - // Note that POSIX doesn't specify the behavior for touching an entry - // other than file. - // - // Also note that POSIX doesn't specify if after a file touch failure the - // command should proceed with the rest of the arguments. The current - // implementation exits immediatelly in such a case. - // - // Note: can be executed synchronously. - // - static uint8_t - touch (scope& sp, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) noexcept - try - { - uint8_t r (1); - ofdstream cerr (move (err)); - - auto error = [&cerr] (bool fail = true) - { - return error_record (cerr, fail, "touch"); - }; - - try - { - in.close (); - out.close (); - - // Parse arguments. - // - cli::vector_scanner scan (args); - touch_options ops (scan); - - auto mtime = [&error] (const path& p) -> timestamp - { - try - { - timestamp t (file_mtime (p)); - - if (t == timestamp_nonexistent) - throw_generic_error (ENOENT); - - return t; - } - catch (const system_error& e) - { - error () << "cannot obtain file '" << p - << "' modification time: " << e; - } - assert (false); // Can't be here. - return timestamp (); - }; - - optional<timestamp> after; - if (ops.after_specified ()) - after = mtime (parse_path (ops.after (), sp.wd_path)); - - if (!scan.more ()) - error () << "missing file"; - - // Create files. - // - while (scan.more ()) - { - path p (parse_path (scan.next (), sp.wd_path)); - - try - { - // Note that we don't register (implicit) cleanup for an - // existing path. - // - if (touch_file (p) && !ops.no_cleanup ()) - sp.clean ({cleanup_type::always, p}, true /* implicit */); - - if (after) - { - while (mtime (p) <= *after) - touch_file (p, false /* create */); - } - } - catch (const system_error& e) - { - error () << "cannot create/update '" << p << "': " << e; - } - } - - r = 0; - } - catch (const invalid_path& e) - { - error (false) << "invalid path '" << e.path << "'"; - } - // Can be thrown while closing in, out or writing to cerr. - // - catch (const io_error& e) - { - error (false) << e; - } - catch (const failed&) - { - // Diagnostics has already been issued. - } - catch (const cli::exception& e) - { - error (false) << e; - } - - cerr.close (); - return r; - } - catch (const std::exception&) - { - return 1; - } - - // Run builtin implementation asynchronously. - // - static builtin - async_impl (builtin_impl* fn, - scope& sp, - uint8_t& r, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) - { - return builtin ( - r, - thread ([fn, &sp, &r, &args, - in = move (in), - out = move (out), - err = move (err)] () mutable noexcept - { - r = fn (sp, args, move (in), move (out), move (err)); - })); - } - - template <builtin_impl fn> - static builtin - async_impl (scope& sp, - uint8_t& r, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) - { - return async_impl (fn, sp, r, args, move (in), move (out), move (err)); - } - - // Run builtin implementation synchronously. - // - template <builtin_impl fn> - static builtin - sync_impl (scope& sp, - uint8_t& r, - const strings& args, - auto_fd in, auto_fd out, auto_fd err) - { - r = fn (sp, args, move (in), move (out), move (err)); - return builtin (r, thread ()); - } - - const builtin_map builtins - { - {"cat", &async_impl<&cat>}, - {"cp", &sync_impl<&cp>}, - {"echo", &async_impl<&echo>}, - {"false", &false_}, - {"ln", &sync_impl<&ln>}, - {"mkdir", &sync_impl<&mkdir>}, - {"mv", &sync_impl<&mv>}, - {"rm", &sync_impl<&rm>}, - {"rmdir", &sync_impl<&rmdir>}, - {"sed", &async_impl<&sed>}, - {"sleep", &sync_impl<&sleep>}, - {"test", &sync_impl<&test>}, - {"touch", &sync_impl<&touch>}, - {"true", &true_} - }; - } - } -} diff --git a/libbuild2/test/script/builtin.hxx b/libbuild2/test/script/builtin.hxx deleted file mode 100644 index b340335..0000000 --- a/libbuild2/test/script/builtin.hxx +++ /dev/null @@ -1,74 +0,0 @@ -// file : libbuild2/test/script/builtin.hxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef LIBBUILD2_TEST_SCRIPT_BUILTIN_HXX -#define LIBBUILD2_TEST_SCRIPT_BUILTIN_HXX - -#include <map> - -#include <libbuild2/types.hxx> -#include <libbuild2/utility.hxx> - -namespace build2 -{ - namespace test - { - namespace script - { - class scope; - - // A process/thread-like object representing a running builtin. - // - // For now, instead of allocating the result storage dynamically, we - // expect it to be provided by the caller. - // - class builtin - { - public: - uint8_t - wait () {if (t_.joinable ()) t_.join (); return r_;} - - ~builtin () {wait ();} - - public: - builtin (uint8_t& r, thread&& t = thread ()): r_ (r), t_ (move (t)) {} - - builtin (builtin&&) = default; - - private: - uint8_t& r_; - thread t_; - }; - - // Start builtin command. Throw system_error on failure. - // - // Note that unlike argc/argv, our args don't include the program name. - // - using builtin_func = builtin (scope&, - uint8_t& result, - const strings& args, - auto_fd in, auto_fd out, auto_fd err); - - class builtin_map: public std::map<string, builtin_func*> - { - public: - using base = std::map<string, builtin_func*>; - using base::base; - - // Return NULL if not a builtin. - // - builtin_func* - find (const string& n) const - { - auto i (base::find (n)); - return i != end () ? i->second : nullptr; - } - }; - - extern const builtin_map builtins; - } - } -} - -#endif // LIBBUILD2_TEST_SCRIPT_BUILTIN_HXX diff --git a/libbuild2/test/script/runner.cxx b/libbuild2/test/script/runner.cxx index 53f6741..630d4c2 100644 --- a/libbuild2/test/script/runner.cxx +++ b/libbuild2/test/script/runner.cxx @@ -4,10 +4,10 @@ #include <libbuild2/test/script/runner.hxx> -#include <set> #include <ios> // streamsize #include <libbutl/regex.mxx> +#include <libbutl/builtin.mxx> #include <libbutl/fdstream.mxx> // fdopen_mode, fdnull(), fddup() #include <libbuild2/variable.hxx> @@ -18,7 +18,6 @@ #include <libbuild2/test/script/regex.hxx> #include <libbuild2/test/script/parser.hxx> -#include <libbuild2/test/script/builtin.hxx> #include <libbuild2/test/script/builtin-options.hxx> using namespace std; @@ -928,7 +927,8 @@ namespace build2 ? rmdir_buildignore ( ctx, d, - sp.root.target_scope.root_scope ()->root_extra->buildignore_file, + sp.root.target_scope.root_scope ()->root_extra-> + buildignore_file, v) : rmdir (ctx, d, v))); @@ -1145,6 +1145,21 @@ namespace build2 } } + // Sorted array of builtins that support filesystem entries cleanup. + // + static const char* cleanup_builtins[] = { + "cp", "ln", "mkdir", "mv", "touch"}; + + static inline bool + cleanup_builtin (const string& name) + { + return binary_search ( + cleanup_builtins, + cleanup_builtins + + sizeof (cleanup_builtins) / sizeof (*cleanup_builtins), + name); + } + static bool run_pipe (scope& sp, command_pipe::const_iterator bc, @@ -1205,6 +1220,8 @@ namespace build2 command_pipe::const_iterator nc (bc + 1); bool last (nc == ec); + const string& program (c.program.string ()); + // Prior to opening file descriptors for command input/output // redirects let's check if the command is the exit builtin. Being a // builtin syntactically it differs from the regular ones in a number @@ -1217,7 +1234,7 @@ namespace build2 // specify any redirects or exit code check sounds like a right thing // to do. // - if (c.program.string () == "exit") + if (program == "exit") { // In case the builtin is erroneously pipelined from the other // command, we will close stdin gracefully (reading out the stream @@ -1240,7 +1257,7 @@ namespace build2 if (err.type != redirect_type::none) fail (ll) << "exit builtin stderr cannot be redirected"; - // We can't make sure that there is not exit code check. Let's, at + // We can't make sure that there is no exit code check. Let's, at // least, check that non-zero code is not expected. // if (eq != (c.exit.code == 0)) @@ -1391,7 +1408,7 @@ namespace build2 // that. Checking that the user didn't specify any meaningless // redirects or exit code check sounds as a right thing to do. // - if (c.program.string () == "set") + if (program == "set") { if (!last) fail (ll) << "set builtin must be the last pipe command"; @@ -1583,7 +1600,7 @@ namespace build2 assert (ofd.out.get () != -1 && efd.get () != -1); optional<process_exit> exit; - builtin_func* bf (builtins.find (c.program.string ())); + builtin_function* bf (builtins.find (program)); bool success; @@ -1605,11 +1622,226 @@ namespace build2 if (verb >= 2) print_process (process_args ()); + // Some of the testscript builtins (cp, mkdir, etc) extend libbutl + // builtins (via callbacks) registering/moving cleanups for the + // filesystem entries they create/move, unless explicitly requested + // not to do so via the --no-cleanup option. + // + // Let's "wrap up" the cleanup-related flags into the single object + // to rely on "small function object" optimization. + // + struct cleanup + { + // Whether the cleanups are enabled for the builtin. Can be set to + // false by the parse_option callback if --no-cleanup is + // encountered. + // + bool enabled = true; + + // Whether to register cleanup for a filesystem entry being + // created/updated depending on its existence. Calculated by the + // create pre-hook and used by the subsequent post-hook. + // + bool add; + + // Whether to move existing cleanups for the filesystem entry + // being moved, rather than to erase them. Calculated by the move + // pre-hook and used by the subsequent post-hook. + // + bool move; + }; + + // nullopt if the builtin doesn't support cleanups. + // + optional<cleanup> cln; + + if (cleanup_builtin (program)) + cln = cleanup (); + + builtin_callbacks bcs { + + // create + // + // Unless cleanups are suppressed, test that the filesystem entry + // doesn't exist (pre-hook) and, if that's the case, register the + // cleanup for the newly created filesystem entry (post-hook). + // + [&sp, &cln] (const path& p, bool pre) + { + // Cleanups must be supported by a filesystem entry-creating + // builtin. + // + assert (cln); + + if (cln->enabled) + { + if (pre) + cln->add = !butl::entry_exists (p); + else if (cln->add) + sp.clean ({cleanup_type::always, p}, true /* implicit */); + } + }, + + // move + // + // Validate the source and destination paths (pre-hook) and, + // unless suppressed, adjust the cleanups that are sub-paths of + // the source path (post-hook). + // + [&sp, &cln] + (const path& from, const path& to, bool force, bool pre) + { + // Cleanups must be supported by a filesystem entry-moving + // builtin. + // + assert (cln); + + if (pre) + { + const dir_path& wd (sp.wd_path); + const dir_path& rwd (sp.root.wd_path); + + auto fail = [] (const string& d) {throw runtime_error (d);}; + + if (!from.sub (rwd) && !force) + fail ("'" + from.representation () + + "' is out of working directory '" + rwd.string () + + "'"); + + auto check_wd = [&wd, fail] (const path& p) + { + if (wd.sub (path_cast<dir_path> (p))) + fail ("'" + p.string () + + "' contains test working directory '" + + wd.string () + "'"); + }; + + check_wd (from); + check_wd (to); + + // Unless cleanups are disabled, "move" the matching cleanups + // if the destination path doesn't exist and it is a sub-path + // of the working directory and just remove them otherwise. + // + if (cln->enabled) + cln->move = !butl::entry_exists (to) && to.sub (rwd); + } + else if (cln->enabled) + { + // Move or remove the matching cleanups (see above). + // + // Note that it's not enough to just change the cleanup paths. + // We also need to make sure that these cleanups happen before + // the destination directory (or any of its parents) cleanup, + // that is potentially registered. To achieve that we can just + // relocate these cleanup entries to the end of the list, + // preserving their mutual order. Remember that cleanups in + // the list are executed in the reversed order. + // + cleanups cs; + + // Remove the source path sub-path cleanups from the list, + // adjusting/caching them if required (see above). + // + for (auto i (sp.cleanups.begin ()); i != sp.cleanups.end (); ) + { + build2::test::script::cleanup& c (*i); + path& p (c.path); + + if (p.sub (from)) + { + if (cln->move) + { + // Note that we need to preserve the cleanup path + // trailing separator which indicates the removal + // method. Also note that leaf(), in particular, does + // that. + // + p = p != from + ? to / p.leaf (path_cast<dir_path> (from)) + : p.to_directory () + ? path_cast<dir_path> (to) + : to; + + cs.push_back (move (c)); + } + + i = sp.cleanups.erase (i); + } + else + ++i; + } + + // Re-insert the adjusted cleanups at the end of the list. + // + sp.cleanups.insert (sp.cleanups.end (), + make_move_iterator (cs.begin ()), + make_move_iterator (cs.end ())); + + } + }, + + // remove + // + // Validate the filesystem entry path (pre-hook). + // + [&sp] (const path& p, bool force, bool pre) + { + if (pre) + { + const dir_path& wd (sp.wd_path); + const dir_path& rwd (sp.root.wd_path); + + auto fail = [] (const string& d) {throw runtime_error (d);}; + + if (!p.sub (rwd) && !force) + fail ("'" + p.representation () + + "' is out of working directory '" + rwd.string () + + "'"); + + if (wd.sub (path_cast<dir_path> (p))) + fail ("'" + p.string () + + "' contains test working directory '" + wd.string () + + "'"); + } + }, + + // parse_option + // + [&cln] (const strings& args, size_t i) + { + // Parse --no-cleanup, if it is supported by the builtin. + // + if (cln && args[i] == "--no-cleanup") + { + cln->enabled = false; + return 1; + } + + return 0; + }, + + // sleep + // + // Deactivate the thread before going to sleep. + // + [&sp] (const duration& d) + { + // If/when required we could probably support the precise sleep + // mode (e.g., via an option). + // + sp.root.test_target.ctx.sched.sleep (d); + } + }; + try { uint8_t r; // Storage. - builtin b ( - bf (sp, r, c.arguments, move (ifd), move (ofd.out), move (efd))); + builtin b (bf (r, + c.arguments, + move (ifd), move (ofd.out), move (efd), + sp.wd_path, + bcs)); success = run_pipe (sp, nc, |