diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2019-09-10 23:23:43 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2019-09-27 18:04:30 +0300 |
commit | dbed808c7d534069f76e63a1a68a85f30d2be81c (patch) | |
tree | 3161d9c9617f2fccf37bd278f0c9bf45fad2e20e | |
parent | 6e84c0f9c5e4d7d98d2a352eec6bc19de0d75d28 (diff) |
Move testscript builtins to libbutl
21 files changed, 443 insertions, 7525 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, diff --git a/tests/test/script/builtin/buildfile b/tests/test/script/builtin/buildfile index ca594ff..f5b719d 100644 --- a/tests/test/script/builtin/buildfile +++ b/tests/test/script/builtin/buildfile @@ -2,4 +2,4 @@ # copyright : Copyright (c) 2014-2019 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file -./: testscript{*} file{cp-dir/cp-file} $b +./: testscript{*} $b diff --git a/tests/test/script/builtin/cat.testscript b/tests/test/script/builtin/cat.testscript deleted file mode 100644 index b23efcf..0000000 --- a/tests/test/script/builtin/cat.testscript +++ /dev/null @@ -1,90 +0,0 @@ -# file : tests/test/script/builtin/cat.testscript -# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file - -.include ../common.testscript - -: unknown-option -: -$c <<EOI && $b -cat -u 2>"cat: unknown option '-u'" == 1 -EOI - -: in -: -$c <<EOI && $b -cat <<EOF >>EOO -foo -bar -EOF -foo -bar -EOO -EOI - -: dash -: -$c <<EOI && $b -cat - <<EOF >>EOO -foo -bar -EOF -foo -bar -EOO -EOI - -: file -: -$c <<EOI && $b -cat <<EOF >=out; -foo -bar -EOF -cat out >>EOO -foo -bar -EOO -EOI - -: in-repeat -: -$c <<EOI && $b -cat - <<EOF >>EOO -foo -bar -EOF -foo -bar -EOO -EOI - -: non-existent -: -$c <<EOI && $b -cat in 2>>/~%EOE% != 0 -%cat: unable to print '.+/test/cat/non-existent/test/1/in': .+% -EOE -EOI - -: empty-path -: -: Cat an empty path. -: -$c <<EOI && $b -cat '' 2>"cat: invalid path ''" == 1 -EOI - -: big -: -: Cat a big file (about 100K) to test that the builtin is asynchronous. -: -{ - $c <<EOI && $b - s="--------------------------------" - s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" - s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" - s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" - cat <"$s" | cat >"$s" - EOI -} diff --git a/tests/test/script/builtin/cp-dir/cp-file b/tests/test/script/builtin/cp-dir/cp-file deleted file mode 100644 index e69de29..0000000 --- a/tests/test/script/builtin/cp-dir/cp-file +++ /dev/null diff --git a/tests/test/script/builtin/cp.testscript b/tests/test/script/builtin/cp.testscript index 60497b1..802cfee 100644 --- a/tests/test/script/builtin/cp.testscript +++ b/tests/test/script/builtin/cp.testscript @@ -4,109 +4,29 @@ .include ../common.testscript -: unknown-option -: -$c <<EOI && $b -cp -u 2>"cp: unknown option '-u'" == 1 -EOI - -: args -: -{ - : none - : - $c <'cp 2>"cp: missing arguments" == 1' && $b - - : no-source - : - $c <'cp -R a 2>"cp: missing source path" == 1' && $b - - : no-trailing-sep - : - $c <<EOI && $b - cp a b c 2>"cp: multiple source paths without trailing separator for destination directory" == 1 - EOI - - : empty - : - { - : dest - : - $c <<EOI && $b - cp '' 2>"cp: invalid path ''" == 1 - EOI - - : src1 - : - $c <<EOI && $b - cp '' a 2>"cp: invalid path ''" == 1 - EOI - - : src2 - : - $c <<EOI && $b - cp '' a b/ 2>"cp: invalid path ''" == 1 - EOI - } -} - : file : : Test synopsis 1: make a file copy at the specified path. : { - : existing + : cleanup : { - : to-non-existing - : - $c <<EOI && $b - touch a; - cp a b && test -f b - EOI - - : to-existing + : enabled : $c <<EOI && $b - touch a b; - cp a b - EOI + touch a; + cp a b + EOI - : to-dir + : disabled : $c <<EOI && $b - touch a; - mkdir b; - cp a b 2>>/~%EOE% != 0 - %cp: unable to copy file '.+/a' to '.+/b': .+% - EOE - EOI - } - - : non-existing - : - { - $c <<EOI && $b - cp a b 2>>/~%EOE% != 0 - %cp: unable to copy file '.+/a' to '.+/b': .+% - EOE - EOI - } - - : non-file - : - { - $c <<EOI && $b - mkdir a; - cp a b 2>>/~%EOE% != 0 - %cp: unable to copy file '.+/a' to '.+/b': .+% - EOE - EOI - } + touch a; + cp --no-cleanup a b; + rm b + EOI - : cleanup - : - { : existing : : Test that copy over an existing file does not register cleanup. If it @@ -114,12 +34,12 @@ EOI : and so the cleanup registered by the outer touch would fail. : $c <<EOI && $b - +touch b - { - touch a; - cp a ../b - } - EOI + +touch b + { + touch a; + cp a ../b + } + EOI } } @@ -128,66 +48,22 @@ EOI : Test synopsis 2: make a directory copy at the specified path. : { - : existing + : cleanup : - { - : to-non-existing - : - $c <<EOI && $b - mkdir a; - cp -r a b && test -d b - EOI - - : to-existing - : - $c <<EOI && $b - mkdir a b; - cp -R a b 2>>/~%EOE% != 0 - %cp: unable to copy directory '.+/a' to '.+/b': .+% - EOE - EOI - - : to-file - : - $c <<EOI && $b + $c <<EOI && $b mkdir a; - touch b; - cp -r a b 2>>/~%EOE% != 0 - %cp: unable to copy directory '.+/a' to '.+/b': .+% - EOE - EOI - - : recursively - : - { - $c <<EOI && $b - mkdir -p a/b/c; - touch a/x a/b/y; - cp -r a d && test -d d/b/c && test -f d/x && test -f d/b/y - EOI - } - } - - : non-existing - : - { - $c <<EOI && $b - cp -r a b 2>>/~%EOE% != 0 - %cp: unable to copy directory '.+/a' to '.+/b': .+% - EOE + touch a/b; + cp -r a b EOI - } - : non-dir + : no-cleanup : - { - $c <<EOI && $b - touch a; - cp -r a b 2>>/~%EOE% != 0 - %cp: unable to copy directory '.+/a' to '.+/b': .+% - EOE + $c <<EOI && $b + mkdir a; + touch a/b; + cp --no-cleanup -r a b; + rm -r b EOI - } } : files @@ -195,83 +71,22 @@ EOI : Test synopsis 3: copy files into the specified directory. : { - : existing + : cleanup : - { - : into-dir - : - { - : over-non-existing - : - $c <<EOI && $b - mkdir b; - touch a; - cp a b/ && test -f b/a - EOI - - : over-dir - : - $c <<EOI && $b - mkdir -p b/a; - touch a; - cp a b/ 2>>/~%EOE% != 0 - %cp: unable to copy file '.+/a' to '.+/b/a': .+% - EOE - EOI - - : multiple - : - $c <<EOI && $b + $c <<EOI && $b touch a b; mkdir c; - cp a b c/ && test -f c/a && test -f c/b - EOI - } - - : into-non-existing-dir - : - { - $c <<EOI && $b - touch a; - cp a b/ 2>>/~%EOE% != 0 - %cp: unable to copy file '.+/a' to '.+/b/a': .+% - EOE + cp a b c/ EOI - } - : into-non-dir - : - { - $c <<EOI && $b - touch a b; - cp a b/ 2>>/~%EOE% != 0 - %cp: unable to copy file '.+/a' to '.+/b/a': .+% - EOE - EOI - } - } - - : non-existing - : - { - $c <<EOI && $b - mkdir b; - cp a b/ 2>>/~%EOE% != 0 - %cp: unable to copy file '.+/a' to '.+/b/a': .+% - EOE - EOI - } - - : non-file + : no-cleanup : - { $c <<EOI && $b - mkdir a b; - cp a b/ 2>>/~%EOE% != 0 - %cp: unable to copy file '.+/a' to '.+/b/a': .+% - EOE - EOI - } + touch a b; + mkdir c; + cp --no-cleanup a b c/; + rm c/a c/b + EOI } : filesystem-entries @@ -279,112 +94,20 @@ EOI : Test synopsis 4: copy filesystem entries into the specified directory. : { - : file + : cleanup : - { - $c <<EOI && $b - mkdir b; - touch a; - cp -R a b/ && test -f b/a + $c <<EOI && $b + mkdir a b; + touch c a/c; + cp -R a c b/ EOI - } - : dir + : no-cleanup : - { - : over-non-existing - : - $c <<EOI && $b + $c <<EOI && $b mkdir a b; - touch a/c; - cp -R a b/ && test -f b/a/c - EOI - - : over-existing - : - $c <<EOI && $b - mkdir -p a b/a; - cp -R a b/ 2>>/~%EOE% != 0 - %cp: unable to copy directory '.+/a' to '.+/b/a': .+% - EOE + touch c a/c; + cp --no-cleanup -R a c b/; + rm -r b/a/ b/c EOI - } -} - -: attrs -: -if ($cxx.target.class != 'windows') -{ - fs = 's/.+ (\S+\s+\S+\s+\S+)\s+cp-file/\1/p' - ds = 's/.+ (\S+\s+\S+\s+\S+)\s+cp-dir/\1/p' - - : copy - : - { - : file - : - { - $c <<"EOI" && $b - ls -l $src_base/cp-dir | sed -n -e '$fs' | \ - set t; - - cp -p $src_base/cp-dir/cp-file ./; - ls -l | sed -n -e '$fs' >"\$t" - EOI - } - - : dir - : - { - $c <<"EOI" && $b - ls -l $src_base | sed -n -e '$ds' | \ - set t; - - cp -pr $src_base/cp-dir ./; - ls -l | sed -n -e '$ds' >"\$t" - EOI - } - } - - : no-copy - : - { - : file - : - { - $c <<"EOI" && $b 2>>~%EOE% != 0 - ls -l $src_base/cp-dir | sed -n -e '$fs' | \ - set t; - - cp $src_base/cp-dir/cp-file ./; - ls -l | sed -n -e '$fs' >"\$t" - EOI - %.+ error: sed stdout doesn't match expected% - %.+ - EOE - } - - : dir - : - : Note that the `ls -l` command by default displays the filesystem entry - : modification time with the minute resolution and the build2 dist - : meta-operation (involved when we fetch from the git repository - : location) doesn't preserve the directory modification time. That's why - : we also pass --full-time and enable the test for only platforms where ls - : supports this option. - : - if ($cxx.target.class == 'linux') - { - $c <<"EOI" && $b 2>>~%EOE% != 0 - ls -l --full-time $src_base | sed -n -e '$ds' | \ - set t; - - cp -r $src_base/cp-dir ./; - ls -l --full-time | sed -n -e '$ds' >"\$t" - EOI - %.+ error: sed stdout doesn't match expected% - %.+ - EOE - } - } } diff --git a/tests/test/script/builtin/echo.testscript b/tests/test/script/builtin/echo.testscript deleted file mode 100644 index 3227e89..0000000 --- a/tests/test/script/builtin/echo.testscript +++ /dev/null @@ -1,27 +0,0 @@ -# file : tests/test/script/builtin/echo.testscript -# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file - -.include ../common.testscript - -: string -: -$c <'echo foo >foo' && $b - -: strings -: -$c <'echo foo bar >"foo bar"' && $b - -: big -: -: Echo a big string (about 100K) to test that the builtin is asynchronous. -: -{ - $c <<EOI && $b - s="--------------------------------" - s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" - s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" - s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" - echo "$s" | cat >"$s" - EOI -} diff --git a/tests/test/script/builtin/ln.testscript b/tests/test/script/builtin/ln.testscript index 072c197..456126a 100644 --- a/tests/test/script/builtin/ln.testscript +++ b/tests/test/script/builtin/ln.testscript @@ -4,119 +4,25 @@ .include ../common.testscript -: unknown-option -: -$c <<EOI && $b -ln -u 2>"ln: unknown option '-u'" == 1 -EOI - -: args -: -{ - : -s-option - : - $c <'ln 2>"ln: missing -s|--symbolic option" == 1' && $b - - : none - : - $c <'ln -s 2>"ln: missing arguments" == 1' && $b - - : no-target - : - $c <'ln -s a 2>"ln: missing target path" == 1' && $b - - : no-trailing-sep - : - $c <<EOI && $b - ln -s a b c 2>"ln: multiple target paths with non-directory link path" == 1 - EOI - - : empty - : - { - : link - : - $c <<EOI && $b - ln -s '' 2>"ln: invalid path ''" == 1 - EOI - - : target1 - : - $c <<EOI && $b - ln -s '' a 2>"ln: invalid path ''" == 1 - EOI - - : target2 - : - $c <<EOI && $b - ln -s '' a b/ 2>"ln: invalid path ''" == 1 - EOI - } -} - : file : : Test creating a file symlink. : { - : non-existing-link-path + : cleanup : $c <<EOI && $b - touch a; - ln -s a b && test -f b - EOI - - : existing-link - : - { - : file - : - $c <<EOI && $b - touch a b; - ln -s a b 2>>/~%EOE% != 0 - %( - %ln: unable to create .+link '.+/b' to '.+/a': .+%| - %ln: unable to copy file '.+/a' to '.+/b': .+% - %) - EOE - EOI - - : dir - : - $c <<EOI && $b touch a; - mkdir b; - ln -s a b 2>>/~%EOE% != 0 - %( - %ln: unable to create .+link '.+/b' to '.+/a': .+%| - %ln: unable to copy file '.+/a' to '.+/b': .+% - %) - EOE - EOI - } - - : non-existing - { - : target - : - $c <<EOI && $b - ln -s a b 2>>/~%EOE% != 0 - %ln: unable to create symlink to '.+/a': no such file or directory% - EOE + ln -s a b EOI - : link-dir - : - $c <<EOI && $b + : no-cleanup + : + $c <<EOI && $b touch a; - ln -s a b/c 2>>/~%EOE% != 0 - %( - %ln: unable to create .+link '.+/b/c' to '.+/a': .+%| - %ln: unable to copy file '.+/a' to '.+/b/c': .+% - %) - EOE + ln -s --no-cleanup a b; + rm b EOI - } } : dir @@ -124,57 +30,22 @@ EOI : Test creating a directory symlink. : { - : non-existing-link-path + : cleanup : $c <<EOI && $b - mkdir a; - touch a/b; - ln -s a c && test -f c/b - EOI - - : existing-link - : - { - : dir - : - $c <<EOI && $b - mkdir a b; - ln -s a b 2>>/~%EOE% != 0 - %( - %ln: unable to create .+link '.+/b' to '.+/a': .+%| - %ln: unable to copy directory '.+/a' to '.+/b': .+% - %) - EOE - EOI - - : file - : - $c <<EOI && $b mkdir a; - touch b; - ln -s a b 2>>/~%EOE% != 0 - %( - %ln: unable to create .+link '.+/b' to '.+/a': .+%| - %ln: unable to copy directory '.+/a' to '.+/b': .+% - %) - EOE + touch a/b; + ln -s a c EOI - } - : non-existing - { - : link-dir - : - $c <<EOI && $b + : no-cleanup + : + $c <<EOI && $b mkdir a; - ln -s a b/c 2>>/~%EOE% != 0 - %( - %ln: unable to create .+link '.+/b/c' to '.+/a': .+%| - %ln: unable to copy directory '.+/a' to '.+/b/c': .+% - %) - EOE + touch a/b; + ln -s --no-cleanup a c; + rm -r c EOI - } } : multiple-targets @@ -182,9 +53,20 @@ EOI : Test creating links for multiple targets in the specified directory. : { + : cleanup + : $c <<EOI && $b - touch a; - mkdir b c; - ln -s a b c/ && test -f c/a && test -d c/b - EOI + mkdir b c; + touch a b/c; + ln -s a b c/ + EOI + + : no-cleanup + : + $c <<EOI && $b + mkdir b c; + touch a b/c; + ln -s --no-cleanup a b c/; + rm -r c/a c/b + EOI } diff --git a/tests/test/script/builtin/mkdir.testscript b/tests/test/script/builtin/mkdir.testscript index d1a3b4a..00fc5cf 100644 --- a/tests/test/script/builtin/mkdir.testscript +++ b/tests/test/script/builtin/mkdir.testscript @@ -4,69 +4,38 @@ .include ../common.testscript -: unknown-option -: -$c <<EOI && $b -mkdir -u 2>"mkdir: unknown option '-u'" == 1 -EOI - -: dirs -: -$c <<EOI && $b -mkdir a b; -touch a/a b/b -EOI +: dir +: +{ + : cleanup + : + $c <<EOI && $b + mkdir a b + EOI + + : no-cleanup + : + : Also test that `--` is handled properly. + : + $c <<EOI && $b + mkdir --no-cleanup -- --no-cleanup; + rmdir -- --no-cleanup + EOI +} : parent : -$c <<EOI && $b -mkdir -p a/b; -touch a/a a/b/b -EOI - -: exists -: -$c <'mkdir -p a a a/b a/b' && $b - -: double-dash -: -: Make sure '-p' directory is created. -: -$c <<EOI && $b -mkdir -p -- -p; -touch -- -p/a -EOI - -: no-args -: -: Test passing no arguments. -: -$c <'mkdir 2>"mkdir: missing directory" == 1' && $b - -: empty-path -: -: Test creation of empty directory path. -: -$c <<EOI && $b -mkdir '' 2>"mkdir: invalid path ''" == 1 -EOI - -: already-exists -: -: Test creation of an existing directory. -: -$c <<EOI && $b -mkdir a a 2>>/~%EOE% == 1 -%mkdir: unable to create directory '.+/test/mkdir/already-exists/test/1/a': .+% -EOE -EOI - -: not-exists -: -: Test creation of a directory with non-existent parent. -: -$c <<EOI && $b -mkdir a/b 2>>/~%EOE% == 1 -%mkdir: unable to create directory '.+/test/mkdir/not-exists/test/1/a/b': .+% -EOE -EOI +{ + : cleanup + : + $c <<EOI && $b + mkdir -p a/b + EOI + + : no-cleanup + : + $c <<EOI && $b + mkdir -p --no-cleanup a/b; + rm -r a + EOI +} diff --git a/tests/test/script/builtin/mv.testscript b/tests/test/script/builtin/mv.testscript index eb4860e..d703faf 100644 --- a/tests/test/script/builtin/mv.testscript +++ b/tests/test/script/builtin/mv.testscript @@ -4,52 +4,6 @@ .include ../common.testscript -: unknown-option -: -$c <<EOI && $b -mv -u 2>"mv: unknown option '-u'" == 1 -EOI - -: args -: -{ - : none - : - $c <'mv 2>"mv: missing arguments" == 1' && $b - - : no-source - : - $c <'mv a 2>"mv: missing source path" == 1' && $b - - : no-trailing-sep - : - $c <<EOI && $b - mv a b c 2>"mv: multiple source paths without trailing separator for destination directory" == 1 - EOI - - : empty - : - { - : dest - : - $c <<EOI && $b - mv '' 2>"mv: invalid path ''" == 1 - EOI - - : src1 - : - $c <<EOI && $b - mv '' a 2>"mv: invalid path ''" == 1 - EOI - - : src2 - : - $c <<EOI && $b - mv '' a b/ 2>"mv: invalid path ''" == 1 - EOI - } -} - : synopsis-1 : : Move an entity to the specified path. @@ -58,43 +12,6 @@ EOI : file : { - : existing - : - { - : to-non-existing - : - $c <<EOI && $b - touch a; - mv a b && test -f b && test -f a == 1 - EOI - - : to-existing - : - $c <<EOI && $b - touch a b; - mv a b && test -f b && test -f a == 1 - EOI - - : to-self - : - $c <<EOI && $b - touch a; - mv a a 2>>/~%EOE% != 0 - %mv: unable to move entity '.+/a' to itself% - EOE - EOI - - : to-dir - : - $c <<EOI && $b - touch a; - mkdir b; - mv a b 2>>/~%EOE% != 0 - %mv: unable to move entity '.+/a' to '.+/b': .+% - EOE - EOI - } - : outside-scope : : Need to use a path that unlikely exists (not to remove something useful). @@ -105,10 +22,10 @@ EOI : Moving path outside the testscript working directory fails. : $c <<EOI && $b - mv ../../a/b/c ./c 2>>/~%EOE% == 1 - %mv: '.+/fail/a/b/c' is out of working directory '.+/fail/test'% - EOE - EOI + mv ../../a/b/c ./c 2>>/~%EOE% == 1 + %mv: '.+/fail/a/b/c' is out of working directory '.+/fail/test'% + EOE + EOI : force : @@ -117,15 +34,30 @@ EOI : exist. : $c <<EOI && $b - mv -f ../../a/b/c ./c 2>>/~%EOE% == 1 - %mv: unable to move entity '.+/force/a/b/c' to '.+/c': .+% - EOE - EOI + mv -f ../../a/b/c ./c 2>>/~%EOE% == 1 + %mv: unable to move entity '.+/force/a/b/c' to '.+/c': .+% + EOE + EOI } : cleanup : { + : enabled + : + $c <<EOI && $b + touch a; + mv a b + EOI + + : disabled + : + $c <<EOI && $b + touch a; + mv --no-cleanup a b &!a; + rm b + EOI + : existing : : Test that moving over an existing file does not move the cleanup. If @@ -135,91 +67,60 @@ EOI : fail. : $c <<EOI && $b - +touch b - { - touch a; - mv a ../b - } - EOI + +touch b + { + touch a; + mv a ../b + } + EOI } } : dir : { - : existing + : working-dir : { - : to-non-existing - : - : Note the here we also test that b path is cleaned up as a directory. - : - $c <<EOI && $b - mkdir a; - mv a b && test -d b && test -d a == 1 - EOI - - : to-non-empty + : src : $c <<EOI && $b - mkdir a b; - touch b/c; - mv a b 2>>/~%EOE% != 0 - %mv: unable to move entity '.+/a' to '.+/b': .+% - EOE - EOI + mv $~ b 2>"mv: '$~' contains test working directory '$~'" != 0 + EOI - : to-non-dir + : dst : $c <<EOI && $b - mkdir a; - touch b; - mv a b 2>>/~%EOE% != 0 - %mv: unable to move entity '.+/a' to '.+/b': .+% - EOE - EOI + mkdir a; + mv a "$~" 2>"mv: '$~' contains test working directory '$~'" != 0 + EOI } - : working-dir + : cleanup : { - : src + : enabled : - { - $c <<EOI && $b - mv $~ b 2>"mv: '([string] $~)' contains test working directory '$~'" != 0 + $c <<EOI && $b + mkdir a; + mv a b EOI - } - : dst + : disabled : - { - $c <<EOI && $b + $c <<EOI && $b mkdir a; - mv a "$~" 2>"mv: '$~' contains test working directory '$~'" != 0 + mv --no-cleanup a b &!a/; + rm -r b EOI - } - } - - : overlap - : - $c <<EOI && $b - mkdir a; - mv a a/b 2>>/~%EOE% != 0 - %mv: unable to move entity '.+/a' to '.+/a/b': .+% - EOE - EOI - : cleanup - : - { : sub-entry : - { + $c <<EOI && $b mkdir a; touch a/b; mv a c - } + EOI : reorder : @@ -227,21 +128,11 @@ EOI : it, get removed before b/ after being renamed to b/c. : $c <<EOI && $b - mkdir a b; - mv a b/c - EOI + mkdir a b; + mv a b/c + EOI } } - - : non-existing - : - { - $c <<EOI && $b - mv a b 2>>/~%EOE% != 0 - %mv: unable to move entity '.+/a' to '.+/b': .+% - EOE - EOI - } } : synopsis-2 @@ -249,10 +140,20 @@ EOI : Move entities into the specified directory. : { + : cleanup + : $c <<EOI && $b - mkdir a c; - touch a/b b; - mv a b c/; - test -d c/a && test -f c/a/b && test -f c/b - EOI + mkdir a c; + touch a/b b; + mv a b c/ + EOI + + : no-cleanup + : + $c <<EOI && $b + mkdir a c; + touch a/b b; + mv --no-cleanup a b c/ &!a/ &!a/b &!b; + rm -r c/a/ c/b + EOI } diff --git a/tests/test/script/builtin/rm.testscript b/tests/test/script/builtin/rm.testscript index 46ed566..7160c73 100644 --- a/tests/test/script/builtin/rm.testscript +++ b/tests/test/script/builtin/rm.testscript @@ -4,102 +4,21 @@ .include ../common.testscript -: unknown-option -: -$c <<EOI && $b -rm -u 2>"rm: unknown option '-u'" == 1 -EOI - -: no-args -: -{ - : fail - : - : Removing with no arguments fails. - : - $c <'rm 2>"rm: missing file" == 1' && $b - - : force - : - : Removing with no arguments succeeds with -f option. - : - $c <'rm -f' && $b -} - -: file -: -{ - : exists - : - : Removing existing file succeeds. - : - $c <<EOI && $b - touch a &!a; - rm a - EOI - - : not-exists - : - { - : fail - : - : Removing non-existing file fails. - : - $c <<EOI && $b - rm a 2>>/~%EOE% == 1 - %rm: unable to remove '.+/file/not-exists/fail/test/1/a': .+% - EOE - EOI - - : force - : - : Removing non-existing file succeeds with -f option. - : - $c <'rm -f a' && $b - } -} - : dir : { - : default - : - : Removing directory fails by default. - : - $c <<EOI && $b - mkdir a; - rm a 2>"rm: '$normalize([path] $~/a)' is a directory" == 1 - EOI - - : recursive - : - : Removing directory succeeds with -r option. - : - $c <<EOI && $b - mkdir -p a/b &!a &!a/b; - rm -r a - EOI - : scope : : Removing scope directory fails. : $c <<EOI && $b - rm -r ./ 2>"rm: '([string] $~)' contains test working directory '$~'" == 1 - EOI + rm -r ./ 2>"rm: '$~' contains test working directory '$~'" == 1 + EOI } : path : { - : empty - : - : Removing an empty path fails. - : - $c <<EOI && $b - rm '' 2>"rm: invalid path ''" == 1 - EOI - : outside-scope : : Need to use a path that unlikely exists (not to remove something useful). @@ -110,10 +29,10 @@ EOI : Removing path outside the testscript working directory fails. : $c <<EOI && $b - rm ../../a/b/c 2>>/~%EOE% == 1 - %rm: '.+/path/outside-scope/fail/a/b/c' is out of working directory '.+/path/outside-scope/fail/test'% - EOE - EOI + rm ../../a/b/c 2>>/~%EOE% == 1 + %rm: '.+/path/outside-scope/fail/a/b/c' is out of working directory '.+/path/outside-scope/fail/test'% + EOE + EOI : force : diff --git a/tests/test/script/builtin/rmdir.testscript b/tests/test/script/builtin/rmdir.testscript index 9f0c945..897b5f4 100644 --- a/tests/test/script/builtin/rmdir.testscript +++ b/tests/test/script/builtin/rmdir.testscript @@ -4,39 +4,9 @@ .include ../common.testscript -: unknown-option -: -$c <<EOI && $b -rmdir -u 2>"rmdir: unknown option '-u'" == 1 -EOI - -: no-args -: -{ - : fail - : - : Removing with no arguments fails. - : - $c <'rmdir 2>"rmdir: missing directory" == 1' && $b - - : force - : - : Removing with no arguments succeeds with -f option. - : - $c <'rmdir -f' && $b -} - : dir : { - : empty-path - : - : Removing an empty path fails. - : - $c <<EOI && $b - rmdir '' 2>"rmdir: invalid path ''" == 1 - EOI - : test-scope : : Removing scope directory fails. @@ -56,7 +26,7 @@ EOI : $c <<EOI && $b rmdir ../../a/b/c 2>>/~%EOE% == 1 - %rmdir: '.+/dir/outside-scope/fail/a/b/c' is out of working directory '.+/dir/outside-scope/fail/test'% + %rmdir: '.+/dir/outside-scope/fail/a/b/c/' is out of working directory '.+/dir/outside-scope/fail/test'% EOE EOI @@ -67,54 +37,4 @@ EOI : $c <'rmdir -f ../../a/b/c' && $b } - - : exists - : - : Removing existing directory succeeds. - : - $c <<EOI && $b - mkdir a &!a; - rmdir a - EOI - - : not-exists - : - { - : fail - : Removing non-existing directory fails. - : - $c <<EOI && $b - rmdir a 2>>/~%EOE% == 1 - %rmdir: unable to remove '.+/dir/not-exists/fail/test/1/a': .+% - EOE - EOI - - : force - : - : Removing non-existing directory succeeds with -f option. - : - $c <'rmdir -f a' && $b - } - - : not-empty - : - : Removing non-empty directory fails. - : - $c <<EOI && $b - mkdir -p a/b; - rmdir a 2>>/~%EOE% == 1 - %rmdir: unable to remove '.+/dir/not-empty/test/1/a': .+% - EOE - EOI - - : not-dir - : - : Removing not a directory path fails. - : - $c <<EOI && $b - touch a; - rmdir a 2>>/~%EOE% == 1 - %rmdir: unable to remove '.+/dir/not-dir/test/1/a': .+% - EOE - EOI } diff --git a/tests/test/script/builtin/sed.testscript b/tests/test/script/builtin/sed.testscript deleted file mode 100644 index 4a79fe3..0000000 --- a/tests/test/script/builtin/sed.testscript +++ /dev/null @@ -1,355 +0,0 @@ -# file : tests/test/script/builtin/sed.testscript -# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file - -.include ../common.testscript - -: unknown-option -: -$c <<EOI && $b -sed -u 2>"sed: unknown option '-u'" == 1 -EOI - -: arg -: -{ - : auto-prn - : - { - $c <"sed -n -e 's/fox/bar/' <'foo' " && $b : on - $c <"sed -e 's/fox/bar/' <'foo' >'foo'" && $b : off - } - - : script - : - { - : missed - : - $c <'sed' && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: missing script - info: test id: 1 - EOE - - : missed-val - : - $c <'sed -e' && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: missing value for option '-e' - info: test id: 1 - EOE - - : empty - : - $c <"sed -e ''" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: empty script - info: test id: 1 - EOE - - : multiple - : - $c <"sed -e 's/a//' -e 's/a//'" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: multiple scripts - info: test id: 1 - EOE - - : invalid - : - $c <"sed -e 'z'" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: only 's' command supported - info: test id: 1 - EOE - } - - : file - : - { - : exist - : - $c <<EOI && $b - cat <'foo' >=f; - sed -e 's/foo/bar/' f >'bar' - EOI - - : none - : - $c <<EOI && $b - sed -e 's/foo/bar/' <'foo' >'bar' - EOI - - : dash - : - $c <<EOI && $b - sed -e 's/foo/bar/' - <'foo' >'bar' - EOI - - : not-exist - : - $c <"sed -e 's/foo/bar/' f" && $b 2>>/~%EOE% != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - %sed: unable to edit '.+/1/f': .+% - info: test id: 1 - EOE - - : empty - : - $c <"sed -e 's/foo/bar/' ''" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: invalid path '' - info: test id: 1 - EOE - } - - : unexpected - : - $c <"sed -e 's/a//' a b" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: unexpected argument 'b' - info: test id: 1 - EOE -} - -: command -: -{ - : subst - : - { - : parsing - : - { - : delim - : - { - : none - : - $c <"sed -e 's'" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: no delimiter for 's' command - info: test id: 1 - EOE - - : invalid - : - $c <"sed -e 's\\'" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: invalid delimiter for 's' command - info: test id: 1 - EOE - } - - : regex - : - { - : unterminated - : - $c <"sed -e 's/foo'" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: unterminated 's' command regex - info: test id: 1 - EOE - - : empty - : - $c <"sed -e 's///'" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: empty regex in 's' command - info: test id: 1 - EOE - - : invalid - : - : Note that old versions of libc++ (for example 1.1) do not detect some - : regex errors. For example '*' is parsed successfully. - : - $c <"sed -e 's/foo[/bar/'" && $b 2>>/~%EOE% != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - %sed: invalid regex.*% - info: test id: 1 - EOE - } - - : unterminated-replacement - : - $c <"sed -e 's/foo/bar'" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: unterminated 's' command replacement - info: test id: 1 - EOE - - : invalid-flags - : - $c <"sed -e 's/foo/bar/a'" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: invalid 's' command flag 'a' - info: test id: 1 - EOE - } - - : exec - : - { - : flags - : - { - : global - : - { - $c <"sed -e 's/o/a/g' <'foo' >'faa'" && $b : on - $c <"sed -e 's/o/a/' <'foo' >'fao'" && $b : off - } - - : icase - : - { - $c <"sed -e 's/O/a/i' <'foo' >'fao'" && $b : on - $c <"sed -e 's/O/a/' <'foo' >'foo'" && $b : off - } - - : print - : - { - $c <"sed -n -e 's/o/a/p' <'foo' >'fao'" && $b : on-match - $c <"sed -n -e 's/o/a/' <'foo' " && $b : off-match - $c <"sed -n -e 's/u/a/p' <'foo' " && $b : on-no-match - } - } - - : search - { - : anchor - : - { - $c <"sed -n -e 's/^o/a/gp' <'oof' >'aof'" && $b : begin - $c <"sed -n -e 's/o\$/a/gp' <'foo' >'foa'" && $b : end - } - - : match - : Match corner cases - : - { - $c <"sed -n -e 's/a/b/p' <'a' >'b' " && $b : full - $c <"sed -n -e 's/a/b/p' <'ac' >'bc' " && $b : left - $c <"sed -n -e 's/a/b/p' <'ca' >'cb' " && $b : right - $c <"sed -n -e 's/a/b/pg' <'xaax' >'xbbx'" && $b : adjacent - } - } - - : replacement - : - { - : ecma-escape - : - { - $c <"sed <'xay' -e 's/a/\$b/' >'x\$by'" && $b : none - $c <"sed <'xay' -e 's/a/\$/' >'x\$y' " && $b : none-term - $c <"sed <'xay' -e 's/a/\$\$/' >'x\$y' " && $b : self - $c <"sed <'xay' -e 's/a/b\$&c/' >'xbacy'" && $b : match - $c <"sed <'xay' -e 's/a/b\$`c/' >'xbxcy'" && $b : match-precede - $c <"sed <'xay' -e \"s/a/b\\\$'c/\" >'xbycy'" && $b : match-follow - - : capture - : - $c <<EOI && $b - sed <'abcdefghij' -e 's/(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)/$1$10/' >'aj' - EOI - } - - : perl-escape - : - { - $c <"sed <'xay' -e 's/a/\\b/' >'xby' " && $b : none - $c <"sed <'xay' -e 's/a/\\/' >'xy' " && $b : none-term - $c <"sed <'xay' -e 's/a/\\\\/' >'x\\y'" && $b : self - - : capture - : - $c <<EOI && $b - sed <'abcdefghij' -e 's/(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)/\1\10/' >'aa0' - EOI - - : upper - : - { - $c <"sed <'xay' -e 's/a/\\U/' >'xy' " && $b : none - $c <"sed <'xay' -e 's/a/\\Uvz/' >'xVZy'" && $b : repl - $c <"sed <'xay' -e 's/a/\\Uv\\Ez/' >'xVzy'" && $b : end - $c <"sed <'aa' -e 's/a/v\\Uz/g' >'vZvZ'" && $b : locality - $c <"sed <'xay' -e 's/\(a\)/\\U\\1/' >'xAy' " && $b : capt - $c <"sed <'x-y' -e 's/\(a?\)-/\\U\\1z/' >'xZy' " && $b : capt-empty - $c <"sed <'xay' -e 's/a/\\uvz/' >'xVzy'" && $b : once - } - - : lower - : - { - $c <"sed <'xay' -e 's/a/\\lVZ/' >'xvZy'" && $b : once - } - } - } - - $c <"sed -e 's/a//' <:'b' >'b'" && $b : no-newline - $c <"sed -e 's/a//' <:'' " && $b : empty-stdin - - : empty-file - : - $c <<EOI && $b - touch f; - sed -e 's/a//' f - EOI - } - } -} - -: in-place -: -{ - : no-file - : - $c <"sed -i -e 's/a/b/'" && $b 2>>/EOE != 0 - testscript:1:1: error: sed exit code 1 != 0 - info: stderr: test/1/stderr - sed: -i|--in-place option specified while reading from stdin - info: test id: 1 - EOE - - : edit - : - $c <<EOI && $b - cat <'foo' >=f; - sed -i -e 's/foo/bar/' f; - cat f >'bar' - EOI -} - -: big -: -: Sed a big file (about 100K) to test that the builtin is asynchronous. -: -{ - $c <<EOI && $b - s="--------------------------------" - s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" - s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" - s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" - cat <"$s" | sed -e 's/^x//' | cat >"$s" - EOI -} diff --git a/tests/test/script/builtin/sleep.testscript b/tests/test/script/builtin/sleep.testscript index 7d39548..6ba7e38 100644 --- a/tests/test/script/builtin/sleep.testscript +++ b/tests/test/script/builtin/sleep.testscript @@ -4,34 +4,6 @@ .include ../common.testscript -: unknown-option -: -$c <<EOI && $b -sleep -u 2>"sleep: unknown option '-u'" == 1 -EOI - : success : $c <'sleep 1' && $b - -: no-time -: -: Test passing no time interval. -: -$c <'sleep 2>"sleep: missing time interval" != 0' && $b - -: invalid-time -: -: Test passing invalid time interval. -: -$c <<EOI && $b -sleep 1a 2>"sleep: invalid time interval '1a'" != 0 -EOI - -: unexpected-arg -: -: Test passing extra argument. -: -$c <<EOI && $b -sleep 1 1 2>"sleep: unexpected argument '1'" != 0 -EOI diff --git a/tests/test/script/builtin/test.testscript b/tests/test/script/builtin/test.testscript deleted file mode 100644 index 9fde142..0000000 --- a/tests/test/script/builtin/test.testscript +++ /dev/null @@ -1,85 +0,0 @@ -# file : tests/test/script/builtin/test.testscript -# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file - -.include ../common.testscript - -: file -: -{ - : exists - : - $c <<EOI && $b - touch a; - test -f a - EOI - - : not-exists - : - $c <'test -f a == 1' && $b - - : not-file - : - $c <'test -f . == 1' && $b -} - -: dir -: -{ - : exists - : - $c <'test -d .' && $b - - : not-exists - : - $c <'test -d a == 1' && $b - - : not-dir - : - $c <<EOI && $b - touch a; - test -d a == 1 - EOI -} - -: options -: -{ - : unknown - : - $c <<EOI && $b - test -u 2>"test: unknown option '-u'" == 2 - EOI - - : none - : - $c <<EOI && $b - test 2>"test: either -f|--file or -d|--directory must be specified" == 2 - EOI - - : both-file-dir - : - $c <<EOI && $b - test -fd 2>"test: both -f|--file and -d|--directory specified" == 2 - EOI -} - -: args -: -{ - : none - : - $c <'test -f 2>"test: missing path" == 2' && $b - - : unexpected - : - $c <<EOI && $b - test -f a b 2>"test: unexpected argument 'b'" == 2 - EOI - - : empty-path - : - $c <<EOI && $b - test -d '' 2>"test: invalid path ''" == 2 - EOI -} diff --git a/tests/test/script/builtin/touch.testscript b/tests/test/script/builtin/touch.testscript index 731f023..e9549d2 100644 --- a/tests/test/script/builtin/touch.testscript +++ b/tests/test/script/builtin/touch.testscript @@ -4,91 +4,32 @@ .include ../common.testscript -: file -: -$c <'touch a' && $b - -: file-create -: -: Test that file is created. If it didn't then 'rm' would fail. -: -$c <<EOI && $b -touch a &!a; -rm a -EOI - -: file-update -: -: Test that existing file touch doesn't fail. -: -$c <<EOI && $b -cat <'' >=a; -touch a -EOI - -: no-cleanup -: -: Test that touching an existing file does not register cleanup. If it does -: then the file would be removed while leaving the embedded scope, and so the -: cleanup registered by the first touch would fail. -: -$c <<EOI && $b -{ - +touch a - { - touch ../a - } -} -EOI - -: unknown-option -: -$c <<EOI && $b -touch -u 2>"touch: unknown option '-u'" == 1 -EOI - -: no-args -: -: Test passing no arguments. -: -$c <'touch --no-cleanup 2>"touch: missing file" != 0' && $b - -: empty-path -: -: Test touching an empty path. -: -$c <<EOI && $b -touch '' 2>"touch: invalid path ''" != 0 -EOI - -: dir-update -: -: Test touching an existing directory. -: -$c <<EOI && $b -mkdir a; -touch a 2>~'%touch: cannot create/update .+: .+%' != 0 -EOI - -: after +: cleanup : { - : success + : enabled : - $c <<EOI && $b - touch a; - touch --after a b - EOI + $c <'touch a' && $b - : no-value + : disabled : $c <<EOI && $b - touch --after 2>"touch: missing value for option '--after'" != 0 - EOI + touch --no-cleanup a; + rm a + EOI - : not-exists + : existing + : + : Test that touching an existing file does not register cleanup. If it does + : then the file would be removed while leaving the embedded scope, and so + : the cleanup registered by the first touch would fail. : $c <<EOI && $b - touch --after a b 2>~"%touch: cannot obtain file '.+a' modification time: .+%" != 0 - EOI + { + +touch a + { + touch ../a + } + } + EOI } |