aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-07-22 22:37:40 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-07-24 21:37:58 +0300
commit72945d9c8eee18aed1550c3f42dc41e6a8a0f029 (patch)
tree9fdfe1c3bf48d0e303dc435c40116dacf48286b3
parent83bb02cada0b894d9134cc5489999e0f0fe8bd7c (diff)
Use CLI-generated classes to parse testscript builtin options
-rw-r--r--doc/testscript.cli89
-rw-r--r--libbuild2/buildfile40
-rw-r--r--libbuild2/config/utility.hxx2
-rw-r--r--libbuild2/config/utility.txx2
-rw-r--r--libbuild2/test/script/builtin-options.cxx3759
-rw-r--r--libbuild2/test/script/builtin-options.hxx1025
-rw-r--r--libbuild2/test/script/builtin-options.ixx326
-rw-r--r--libbuild2/test/script/builtin.cli103
-rw-r--r--libbuild2/test/script/builtin.cxx601
-rw-r--r--libbuild2/test/script/runner.cxx57
-rw-r--r--tests/test/script/builtin/cat.testscript6
-rw-r--r--tests/test/script/builtin/cp.testscript6
-rw-r--r--tests/test/script/builtin/ln.testscript8
-rw-r--r--tests/test/script/builtin/mkdir.testscript8
-rw-r--r--tests/test/script/builtin/mv.testscript6
-rw-r--r--tests/test/script/builtin/rm.testscript6
-rw-r--r--tests/test/script/builtin/rmdir.testscript6
-rw-r--r--tests/test/script/builtin/sed.testscript10
-rw-r--r--tests/test/script/builtin/sleep.testscript6
-rw-r--r--tests/test/script/builtin/test.testscript58
-rw-r--r--tests/test/script/builtin/touch.testscript8
-rw-r--r--tests/test/script/runner/exit.testscript2
-rw-r--r--tests/test/script/runner/set.testscript18
23 files changed, 5727 insertions, 425 deletions
diff --git a/doc/testscript.cli b/doc/testscript.cli
index 4433375..8b9509d 100644
--- a/doc/testscript.cli
+++ b/doc/testscript.cli
@@ -2311,7 +2311,11 @@ if the \i{dst-dir/src-name} filesystem entry already exists.
\dl|
-\li|\n\c{-p}
+\li|\n\c{-R|-r|--recursive}
+
+ Copy directories recursively.|
+
+\li|\n\c{-p|--preserve}
Copy permissions as well as modification and access times.||
@@ -2323,18 +2327,27 @@ registered for cleanup.
\h#builtins-diff|\c{diff}|
\
-diff [-u] [-U <num>] <file1> <file2>
+diff [-u|-U <num>] <file1> <file2>
\
Compare the contents of \i{file1} and \i{file2}.
The \c{diff} utility is not a builtin. Instead, the test platform is expected
to provide a (reasonably) POSIX-compatible implementation. It should at least
-supports the \c{-u} (unified output format) and \c{-U} (unified output format
-with \i{num} lines of context) options and recognize the \c{-} file name as
+supports the \c{-u} and \c{-U} options and recognize the \c{-} file name as
an instruction to read from \c{stdin}. On Windows, GNU \c{diff} can be assumed
(provided as part of the \c{build2} toolchain).
+\dl|
+
+\li|\n\c{-u}
+
+ Produce output in the unified format.|
+
+\li|\n\c{-U <num>}
+
+ Produce output in the unified output format with \i{num} lines of context.||
+
\h#builtins-echo|\c{echo}|
@@ -2408,6 +2421,13 @@ creation is not supported. If hard link creation is not supported either,
then \c{ln} falls back to copying the content, recursively in case of a
directory target.
+\dl|
+
+\li|\n\c{-s|--symbolic}
+
+ Create symbolic links. Note that creation of hard links is currently not
+ supported, so this option is always required.||
+
Unless the \c{--no-cleanup} option is specified, created filesystem entries
that are inside the script working directory are automatically registered for
cleanup.
@@ -2424,7 +2444,7 @@ directories must exist and the directory itself must not exist.
\dl|
-\li|\n\c{-p}
+\li|\n\c{-p|--parents}
Create missing leading directories and ignore directories that already
exist.||
@@ -2462,7 +2482,7 @@ Where \i{src-name} is the last path component in \i{src-path}.
\dl|
-\li|\n\c{-f}
+\li|\n\c{-f|--force}
Do not fail if a source path is outside the script working directory.||
@@ -2495,11 +2515,11 @@ is specified.
\dl|
-\li|\n\c{-r}
+\li|\n\c{-r|--recursive}
Remove directories and their contents recursively.|
-\li|\n\c{-f}
+\li|\n\c{-f|--force}
Do not fail if no path is specified, the path does not exist, or is outside
the script working directory.||
@@ -2522,7 +2542,7 @@ working directory unless the \c{-f} option is specified.
\dl|
-\li|\n\c{-f}
+\li|\n\c{-f|--force}
Do not fail if no directory is specified, the directory does not exist, or
is outside the script working directory.||
@@ -2546,16 +2566,16 @@ is ECMAScript (more specifically, ECMA-262-based C++11 regular expressions).
\dl|
-\li|\n\c{-n}
+\li|\n\c{-n|--quiet}
Suppress automatic printing of the pattern space at the end of the script
execution.|
-\li|\n\c{-i}
+\li|\n\c{-i|--in-place}
Edit \i{file} in place.|
-\li|\n\c{-e <script>}\n
+\li|\n\c{-e|--expression <script>}\n
Editing commands to be executed (required).||
@@ -2611,7 +2631,7 @@ are supported.
\h#builtins-set|\c{set}|
\
-set [-e|--exact] [(-n|--newline)|(-w|--whitespace)] [<attr>] <var>
+set [-e] [-n|-w] [<attr>] <var>
\
Set variable from the \c{stdin} input.
@@ -2628,22 +2648,21 @@ echo 'bar' | set foo && echo $foo # foo
echo $foo # bar
\
-Unless the \c{-e|--exact} option is specified, a single final newline is
-ignored in the input.
+Unless the \c{-e} option is specified, a single final newline is ignored in
+the input.
-If the \c{-n|--newline} option is specified, then the input is split into a
-list of elements at newlines, including a final blank element in case of
-\c{-e|--exact}. Multiple consecutive newlines are not collapsed.
+If the \c{-n} option is specified, then the input is split into a list of
+elements at newlines, including a final blank element in case of \c{-e}.
+Multiple consecutive newlines are not collapsed.
-If the \c{-w|--whitespace} option is specified, then the input is split into a
-list of elements at whitespaces, including a final blank element in case of
-\c{-e|--exact}. In this mode if \c{-e|--exact} is not specified, then all (and
-not just newline) trailing whitespaces are ignored. Multiple consecutive
-whitespaces (including newlines) are collapsed.
+If the \c{-w} option is specified, then the input is split into a list of
+elements at whitespaces, including a final blank element in case of \c{-e}. In
+this mode if \c{-e} is not specified, then all (and not just newline) trailing
+whitespaces are ignored. Multiple consecutive whitespaces (including newlines)
+are collapsed.
-If neither \c{-n|--newline} nor \c{-w|--whitespace} is specified, then the
-entire input is used as a single element, including a final newline in case
-of \c{-e|--exact}.
+If neither \c{-n} nor \c{-w} is specified, then the entire input is used as a
+single element, including a final newline in case of \c{-e}.
If the \i{attr} argument is specified, then it must contain a list of value
attributes enclosed in \c{[]}, for example:
@@ -2660,6 +2679,20 @@ foo = FOO
set [null] $foo <-
\
+\dl|
+
+\li|\n\c{-e|--exact}
+
+ Do not ignore the final newline.|
+
+\li|\n\c{-n|--newline}
+
+ Split the input into a list of elements at newlines.|
+
+\li|\n\c{-w|--whitespace}
+
+ Split the input into a list of elements at whitespaces.||
+
\h#builtins-sleep|\c{sleep}|
@@ -2683,11 +2716,11 @@ Test the specified \i{path} according to one of the following options. Succeed
\dl|
-\li|\n\c{-f}
+\li|\n\c{-f|--file}
Path exists and is to a regular file.|
-\li|\n\c{-d}
+\li|\n\c{-d|--directory}
Path exists and is to a directory.||
diff --git a/libbuild2/buildfile b/libbuild2/buildfile
index edabfc3..b015d21 100644
--- a/libbuild2/buildfile
+++ b/libbuild2/buildfile
@@ -14,7 +14,9 @@ lib{build2}: libul{build2}: {hxx ixx txx cxx}{* -config -version -*.test...} \
# without them or with their alternative implementations.
#
for m: config dist install test
- libul{build2}: $m/{hxx ixx txx cxx}{** -**.test...}
+ libul{build2}: $m/{hxx ixx txx cxx}{** -**-options -**.test...}
+
+libul{build2}: test/script/{hxx ixx cxx}{builtin-options}
libul{build2}: $int_libs
@@ -87,6 +89,42 @@ if $version.pre_release
else
lib{build2}: bin.lib.version = @"-$version.major.$version.minor"
+# Generated options parser.
+#
+test/script/
+{
+ if $cli.configured
+ {
+ cli.cxx{builtin-options}: cli{builtin}
+
+ cli.options += -I $src_root --include-with-brackets \
+--include-prefix libbuild2/test/script --guard-prefix LIBBUILD2_TEST_SCRIPT \
+--cli-namespace build2::test::script::cli --generate-vector-scanner \
+--generate-specifier --suppress-usage
+
+ cli.cxx{*}:
+ {
+ # Include the generated cli files into the distribution and don't remove
+ # them when cleaning in src (so that clean results in a state identical
+ # to distributed). But don't install their headers since they are only
+ # used internally in the testscript implementation.
+ #
+ dist = true
+ clean = ($src_root != $out_root)
+ install = false
+
+ # We keep the generated code in the repository so copy it back to src in
+ # case of a forwarded configuration.
+ #
+ backlink = overwrite
+ }
+ }
+ else
+ # No install for the pre-generated case.
+ #
+ hxx{builtin-options}@./ ixx{builtin-options}@./: install = false
+}
+
# Install into the libbuild2/ subdirectory of, say, /usr/include/
# recreating subdirectories.
#
diff --git a/libbuild2/config/utility.hxx b/libbuild2/config/utility.hxx
index e41aaa7..a063693 100644
--- a/libbuild2/config/utility.hxx
+++ b/libbuild2/config/utility.hxx
@@ -23,7 +23,7 @@ namespace build2
// Set, if necessary, a required config.* variable.
//
// If override is true and the variable doesn't come from this root scope
- // or from the command line (i.e., it is inherited from the amalgamtion),
+ // or from the command line (i.e., it is inherited from the amalgamation),
// then its value is "overridden" to the default value on this root scope.
// See save_variable() for more information on save_flags.
//
diff --git a/libbuild2/config/utility.txx b/libbuild2/config/utility.txx
index d2ffa69..b422ff4 100644
--- a/libbuild2/config/utility.txx
+++ b/libbuild2/config/utility.txx
@@ -28,7 +28,7 @@ namespace build2
lookup l (org.first);
// The interaction with command line overrides can get tricky. For
- // example, the override to defaul value could make (non-recursive)
+ // example, the override to default value could make (non-recursive)
// command line override in the outer scope no longer apply. So what we
// are going to do is first ignore overrides and perform the normal
// logic on the original. Then we apply the overrides on the result.
diff --git a/libbuild2/test/script/builtin-options.cxx b/libbuild2/test/script/builtin-options.cxx
new file mode 100644
index 0000000..95cd051
--- /dev/null
+++ b/libbuild2/test/script/builtin-options.cxx
@@ -0,0 +1,3759 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <libbuild2/test/script/builtin-options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <ostream>
+#include <sstream>
+
+namespace build2
+{
+ namespace test
+ {
+ namespace script
+ {
+ namespace cli
+ {
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ ++i_;
+ else
+ throw eos_reached ();
+ }
+
+ // vector_scanner
+ //
+ bool vector_scanner::
+ more ()
+ {
+ return i_ < v_.size ();
+ }
+
+ const char* vector_scanner::
+ peek ()
+ {
+ if (i_ < v_.size ())
+ return v_[i_].c_str ();
+ else
+ throw eos_reached ();
+ }
+
+ const char* vector_scanner::
+ next ()
+ {
+ if (i_ < v_.size ())
+ return v_[i_++].c_str ();
+ else
+ throw eos_reached ();
+ }
+
+ void vector_scanner::
+ skip ()
+ {
+ if (i_ < v_.size ())
+ ++i_;
+ else
+ throw eos_reached ();
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, bool& xs, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, scanner& s)
+ {
+ s.next ();
+ x = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::set<X> >
+ {
+ static void
+ parse (std::set<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V>
+ struct parser<std::map<K, V> >
+ {
+ static void
+ parse (std::map<K, V>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o), 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+ }
+ }
+ }
+}
+
+#include <map>
+#include <cstring>
+
+namespace build2
+{
+ namespace test
+ {
+ 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
+ //
+
+ set_options::
+ set_options ()
+ : exact_ (),
+ newline_ (),
+ whitespace_ ()
+ {
+ }
+
+ set_options::
+ set_options (int& argc,
+ char** argv,
+ bool erase,
+ ::build2::test::script::cli::unknown_mode opt,
+ ::build2::test::script::cli::unknown_mode arg)
+ : exact_ (),
+ newline_ (),
+ whitespace_ ()
+ {
+ ::build2::test::script::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ set_options::
+ set_options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::build2::test::script::cli::unknown_mode opt,
+ ::build2::test::script::cli::unknown_mode arg)
+ : exact_ (),
+ newline_ (),
+ whitespace_ ()
+ {
+ ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ set_options::
+ set_options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::build2::test::script::cli::unknown_mode opt,
+ ::build2::test::script::cli::unknown_mode arg)
+ : exact_ (),
+ newline_ (),
+ whitespace_ ()
+ {
+ ::build2::test::script::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ set_options::
+ set_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)
+ : exact_ (),
+ newline_ (),
+ whitespace_ ()
+ {
+ ::build2::test::script::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ set_options::
+ set_options (::build2::test::script::cli::scanner& s,
+ ::build2::test::script::cli::unknown_mode opt,
+ ::build2::test::script::cli::unknown_mode arg)
+ : exact_ (),
+ newline_ (),
+ whitespace_ ()
+ {
+ _parse (s, opt, arg);
+ }
+
+ typedef
+ std::map<std::string, void (*) (set_options&, ::build2::test::script::cli::scanner&)>
+ _cli_set_options_map;
+
+ static _cli_set_options_map _cli_set_options_map_;
+
+ struct _cli_set_options_map_init
+ {
+ _cli_set_options_map_init ()
+ {
+ _cli_set_options_map_["--exact"] =
+ &::build2::test::script::cli::thunk< set_options, bool, &set_options::exact_ >;
+ _cli_set_options_map_["-e"] =
+ &::build2::test::script::cli::thunk< set_options, bool, &set_options::exact_ >;
+ _cli_set_options_map_["--newline"] =
+ &::build2::test::script::cli::thunk< set_options, bool, &set_options::newline_ >;
+ _cli_set_options_map_["-n"] =
+ &::build2::test::script::cli::thunk< set_options, bool, &set_options::newline_ >;
+ _cli_set_options_map_["--whitespace"] =
+ &::build2::test::script::cli::thunk< set_options, bool, &set_options::whitespace_ >;
+ _cli_set_options_map_["-w"] =
+ &::build2::test::script::cli::thunk< set_options, bool, &set_options::whitespace_ >;
+ }
+ };
+
+ static _cli_set_options_map_init _cli_set_options_map_init_;
+
+ bool set_options::
+ _parse (const char* o, ::build2::test::script::cli::scanner& s)
+ {
+ _cli_set_options_map::const_iterator i (_cli_set_options_map_.find (o));
+
+ if (i != _cli_set_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool set_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;
+ }
+
+ // 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;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/libbuild2/test/script/builtin-options.hxx b/libbuild2/test/script/builtin-options.hxx
new file mode 100644
index 0000000..7ff0bac
--- /dev/null
+++ b/libbuild2/test/script/builtin-options.hxx
@@ -0,0 +1,1025 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBBUILD2_TEST_SCRIPT_BUILTIN_OPTIONS_HXX
+#define LIBBUILD2_TEST_SCRIPT_BUILTIN_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <vector>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace build2
+{
+ namespace test
+ {
+ namespace script
+ {
+ namespace cli
+ {
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc, char** argv, bool erase = false);
+ argv_scanner (int start, int& argc, char** argv, bool erase = false);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ private:
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class vector_scanner: public scanner
+ {
+ public:
+ vector_scanner (const std::vector<std::string>&, std::size_t start = 0);
+
+ std::size_t
+ end () const;
+
+ void
+ reset (std::size_t start = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ private:
+ const std::vector<std::string>& v_;
+ std::size_t i_;
+ };
+
+ template <typename X>
+ struct parser;
+ }
+ }
+ }
+}
+
+#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:
+ set_options ();
+
+ set_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);
+
+ set_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);
+
+ set_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);
+
+ set_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);
+
+ set_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&
+ exact () const;
+
+ const bool&
+ newline () const;
+
+ const bool&
+ whitespace () 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 exact_;
+ 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_;
+ };
+ }
+ }
+}
+
+#include <libbuild2/test/script/builtin-options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBBUILD2_TEST_SCRIPT_BUILTIN_OPTIONS_HXX
diff --git a/libbuild2/test/script/builtin-options.ixx b/libbuild2/test/script/builtin-options.ixx
new file mode 100644
index 0000000..55fd6d2
--- /dev/null
+++ b/libbuild2/test/script/builtin-options.ixx
@@ -0,0 +1,326 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace build2
+{
+ namespace test
+ {
+ namespace script
+ {
+ namespace cli
+ {
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc, char** argv, bool erase)
+ : i_ (1), argc_ (argc), argv_ (argv), erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start, int& argc, char** argv, bool erase)
+ : i_ (start), argc_ (argc), argv_ (argv), erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // vector_scanner
+ //
+ inline vector_scanner::
+ vector_scanner (const std::vector<std::string>& v, std::size_t i)
+ : v_ (v), i_ (i)
+ {
+ }
+
+ inline std::size_t vector_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ inline void vector_scanner::
+ reset (std::size_t i)
+ {
+ i_ = i;
+ }
+ }
+ }
+ }
+}
+
+namespace build2
+{
+ namespace test
+ {
+ 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
+ //
+
+ inline const bool& set_options::
+ exact () const
+ {
+ return this->exact_;
+ }
+
+ inline const bool& set_options::
+ newline () const
+ {
+ return this->newline_;
+ }
+
+ inline const bool& set_options::
+ whitespace () const
+ {
+ 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_;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/libbuild2/test/script/builtin.cli b/libbuild2/test/script/builtin.cli
new file mode 100644
index 0000000..8b8de73
--- /dev/null
+++ b/libbuild2/test/script/builtin.cli
@@ -0,0 +1,103 @@
+// file : libbuild2/test/script/builtin.cli
+// 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.
+ //
+
+ 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
index ab57d4f..e572411 100644
--- a/libbuild2/test/script/builtin.cxx
+++ b/libbuild2/test/script/builtin.cxx
@@ -18,6 +18,7 @@
#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
@@ -169,6 +170,13 @@ namespace build2
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)
@@ -188,14 +196,16 @@ namespace build2
{
// Print STDIN.
//
- if (args.empty ())
+ if (!scan.more ())
copy (cin);
// Print files.
//
- for (auto i (args.begin ()); i != args.end (); ++i)
+ while (scan.more ())
{
- if (*i == "-")
+ string f (scan.next ());
+
+ if (f == "-")
{
if (!cin.eof ())
{
@@ -206,7 +216,7 @@ namespace build2
continue;
}
- p = parse_path (*i, sp.wd_path);
+ p = parse_path (move (f), sp.wd_path);
ifdstream is (p, ifdstream::binary);
copy (is);
@@ -244,6 +254,10 @@ namespace build2
{
// Diagnostics has already been issued.
}
+ catch (const cli::exception& e)
+ {
+ error (false) << e;
+ }
cerr.close ();
return r;
@@ -281,7 +295,7 @@ namespace build2
cpfile (from, to, f);
if (!exists && cleanup)
- sp.clean ({cleanup_type::always, to}, true);
+ sp.clean ({cleanup_type::always, to}, true /* implicit */);
}
catch (const system_error& e)
{
@@ -308,7 +322,7 @@ namespace build2
throw_generic_error (EEXIST);
if (cleanup)
- sp.clean ({cleanup_type::always, to}, true);
+ sp.clean ({cleanup_type::always, to}, true /* implicit */);
for (const auto& de: dir_iterator (from,
false /* ignore_dangling */))
@@ -343,10 +357,10 @@ namespace build2
}
}
- // cp [-p] [--no-cleanup] <src-file> <dst-file>
- // cp [-p] [--no-cleanup] -R|-r <src-dir> <dst-dir>
- // cp [-p] [--no-cleanup] <src-file>... <dst-dir>/
- // cp [-p] [--no-cleanup] -R|-r <src-path>... <dst-dir>/
+ // 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.
//
@@ -369,49 +383,37 @@ namespace build2
in.close ();
out.close ();
- auto i (args.begin ());
- auto e (args.end ());
-
- // Process options.
+ // Parse arguments.
//
- bool recursive (false);
- bool attrs (false);
- bool cleanup (true);
- for (; i != e; ++i)
- {
- const string& o (*i);
-
- if (o == "-R" || o == "-r")
- recursive = true;
- else if (o == "-p")
- attrs = true;
- else if (o == "--no-cleanup")
- cleanup = false;
- else
- {
- if (o == "--")
- ++i;
-
- break;
- }
- }
+ cli::vector_scanner scan (args);
+ cp_options ops (scan);
// Copy files or directories.
//
- if (i == e)
+ 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 (*j++, wd));
- e = j.base ();
+ 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
@@ -419,7 +421,7 @@ namespace build2
//
if (!dst.to_directory ())
{
- path src (parse_path (*i++, wd));
+ 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.
@@ -428,14 +430,14 @@ namespace build2
error () << "multiple source paths without trailing separator "
<< "for destination directory";
- if (!recursive)
+ if (!ops.recursive ())
// Synopsis 1: make a file copy at the specified path.
//
cpfile (sp,
src,
dst,
true /* overwrite */,
- attrs,
+ ops.preserve (),
cleanup,
fail);
else
@@ -443,7 +445,7 @@ namespace build2
//
cpdir (sp,
path_cast<dir_path> (src), path_cast<dir_path> (dst),
- attrs,
+ ops.preserve (),
cleanup,
fail);
}
@@ -451,9 +453,9 @@ namespace build2
{
for (; i != e; ++i)
{
- path src (parse_path (*i, wd));
+ path src (parse_path (move (*i), wd));
- if (recursive && dir_exists (src))
+ 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.
@@ -461,7 +463,7 @@ namespace build2
cpdir (sp,
path_cast<dir_path> (src),
path_cast<dir_path> (dst / src.leaf ()),
- attrs,
+ ops.preserve (),
cleanup,
fail);
else
@@ -472,7 +474,7 @@ namespace build2
src,
dst / src.leaf (),
true /* overwrite */,
- attrs,
+ ops.preserve (),
cleanup,
fail);
}
@@ -494,6 +496,10 @@ namespace build2
{
// Diagnostics has already been issued.
}
+ catch (const cli::exception& e)
+ {
+ error (false) << e;
+ }
cerr.close ();
return r;
@@ -617,7 +623,7 @@ namespace build2
mksymlink (target, link, dir);
if (cleanup)
- sp.clean ({cleanup_type::always, link}, true);
+ sp.clean ({cleanup_type::always, link}, true /* implicit */);
}
catch (const system_error& e)
{
@@ -636,7 +642,7 @@ namespace build2
mkhardlink (target, link, dir);
if (cleanup)
- sp.clean ({cleanup_type::always, link}, true);
+ sp.clean ({cleanup_type::always, link}, true /* implicit */);
}
catch (const system_error& e)
{
@@ -666,8 +672,8 @@ namespace build2
}
}
- // ln [--no-cleanup] -s <target-path> <link-path>
- // ln [--no-cleanup] -s <target-path>... <link-dir>/
+ // ln [--no-cleanup] -s|--symbolic <target-path> <link-path>
+ // ln [--no-cleanup] -s|--symbolic <target-path>... <link-dir>/
//
// Note: can be executed synchronously.
//
@@ -690,50 +696,40 @@ namespace build2
in.close ();
out.close ();
- auto i (args.begin ());
- auto e (args.end ());
-
- // Process options.
+ // Parse arguments.
//
- bool cleanup (true);
- bool symlink (false);
-
- for (; i != e; ++i)
- {
- const string& o (*i);
-
- if (o == "--no-cleanup")
- cleanup = false;
- else if (o == "-s")
- symlink = true;
- else
- {
- if (o == "--")
- ++i;
+ cli::vector_scanner scan (args);
+ ln_options ops (scan);
- break;
- }
- }
-
- if (!symlink)
- error () << "missing -s option";
+ if (!ops.symbolic ())
+ error () << "missing -s|--symbolic option";
// Create file or directory symlinks.
//
- if (i == e)
+ 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 (*j++, wd));
- e = j.base ();
+ 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
@@ -741,7 +737,7 @@ namespace build2
//
if (!link.to_directory ())
{
- path target (parse_path (*i++, wd));
+ 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.
@@ -757,7 +753,7 @@ namespace build2
{
for (; i != e; ++i)
{
- path target (parse_path (*i, wd));
+ path target (parse_path (move (*i), wd));
// Synopsis 2: create a target path symlink in the specified
// directory.
@@ -782,6 +778,10 @@ namespace build2
{
// Diagnostics has already been issued.
}
+ catch (const cli::exception& e)
+ {
+ error (false) << e;
+ }
cerr.close ();
return r;
@@ -806,11 +806,11 @@ namespace build2
try_mkdir (p); // Returns success or throws.
if (cleanup)
- sp.clean ({cleanup_type::always, p}, true);
+ sp.clean ({cleanup_type::always, p}, true /* implicit */);
}
}
- // mkdir [--no-cleanup] [-p] <dir>...
+ // 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
@@ -837,47 +837,31 @@ namespace build2
in.close ();
out.close ();
- auto i (args.begin ());
- auto e (args.end ());
-
- // Process options.
+ // Parse arguments.
//
- bool parent (false);
- bool cleanup (true);
- for (; i != e; ++i)
- {
- const string& o (*i);
-
- if (o == "-p")
- parent = true;
- else if (o == "--no-cleanup")
- cleanup = false;
- else
- {
- if (*i == "--")
- ++i;
-
- break;
- }
- }
+ cli::vector_scanner scan (args);
+ mkdir_options ops (scan);
// Create directories.
//
- if (i == e)
+ if (!scan.more ())
error () << "missing directory";
- for (; i != e; ++i)
+ bool cleanup (!ops.no_cleanup ());
+
+ while (scan.more ())
{
- dir_path p (path_cast<dir_path> (parse_path (*i, sp.wd_path)));
+ dir_path p (path_cast<dir_path> (parse_path (scan.next (),
+ sp.wd_path)));
try
{
- if (parent)
+ 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);
+ sp.clean ({cleanup_type::always, p}, true /* implicit */);
}
else // == mkdir_status::already_exists
throw_generic_error (EEXIST);
@@ -904,6 +888,10 @@ namespace build2
{
// Diagnostics has already been issued.
}
+ catch (const cli::exception& e)
+ {
+ error (false) << e;
+ }
cerr.close ();
return r;
@@ -913,8 +901,8 @@ namespace build2
return 1;
}
- // mv [--no-cleanup] [-f] <src-path> <dst-path>
- // mv [--no-cleanup] [-f] <src-path>... <dst-dir>/
+ // mv [--no-cleanup] [-f|--force] <src-path> <dst-path>
+ // mv [--no-cleanup] [-f|--force] <src-path>... <dst-dir>/
//
// Note: can be executed synchronously.
//
@@ -937,50 +925,38 @@ namespace build2
in.close ();
out.close ();
- auto i (args.begin ());
- auto e (args.end ());
-
- // Process options.
+ // Parse arguments.
//
- bool no_cleanup (false);
- bool force (false);
- for (; i != e; ++i)
- {
- const string& o (*i);
-
- if (o == "--no-cleanup")
- no_cleanup = true;
- else if (*i == "-f")
- force = true;
- else
- {
- if (o == "--")
- ++i;
-
- break;
- }
- }
+ cli::vector_scanner scan (args);
+ mv_options ops (scan);
// Move filesystem entries.
//
- if (i == e)
+ 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 (*j++, wd));
- e = j.base ();
+ path dst (parse_path (move (*j++), wd));
+ auto e (j.base ());
if (i == e)
error () << "missing source path";
- auto mv = [no_cleanup, force, &wd, &sp, &error] (const path& from,
- const path& to)
+ auto mv = [ops, &wd, &sp, &error] (const path& from, const path& to)
{
const dir_path& rwd (sp.root->wd_path);
- if (!from.sub (rwd) && !force)
+ if (!from.sub (rwd) && !ops.force ())
error () << "'" << from << "' is out of working directory '"
<< rwd << "'";
@@ -1016,7 +992,7 @@ namespace build2
// Unless suppressed, adjust the cleanups that are sub-paths of
// the source path.
//
- if (!no_cleanup)
+ 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
@@ -1086,7 +1062,7 @@ namespace build2
//
if (!dst.to_directory ())
{
- path src (parse_path (*i++, wd));
+ 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.
@@ -1105,7 +1081,7 @@ namespace build2
//
for (; i != e; ++i)
{
- path src (parse_path (*i, wd));
+ path src (parse_path (move (*i), wd));
mv (src, dst / src.leaf ());
}
}
@@ -1126,6 +1102,10 @@ namespace build2
{
// Diagnostics has already been issued.
}
+ catch (const cli::exception& e)
+ {
+ error (false) << e;
+ }
cerr.close ();
return r;
@@ -1135,7 +1115,7 @@ namespace build2
return 1;
}
- // rm [-r] [-f] <path>...
+ // 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
@@ -1165,41 +1145,24 @@ namespace build2
in.close ();
out.close ();
- auto i (args.begin ());
- auto e (args.end ());
-
- // Process options.
+ // Parse arguments.
//
- bool dir (false);
- bool force (false);
- for (; i != e; ++i)
- {
- if (*i == "-r")
- dir = true;
- else if (*i == "-f")
- force = true;
- else
- {
- if (*i == "--")
- ++i;
-
- break;
- }
- }
+ cli::vector_scanner scan (args);
+ rm_options ops (scan);
// Remove entries.
//
- if (i == e && !force)
+ if (!scan.more () && !ops.force ())
error () << "missing file";
const dir_path& wd (sp.wd_path);
const dir_path& rwd (sp.root->wd_path);
- for (; i != e; ++i)
+ while (scan.more ())
{
- path p (parse_path (*i, wd));
+ path p (parse_path (scan.next (), wd));
- if (!p.sub (rwd) && !force)
+ if (!p.sub (rwd) && !ops.force ())
error () << "'" << p << "' is out of working directory '" << rwd
<< "'";
@@ -1209,7 +1172,7 @@ namespace build2
if (dir_exists (d))
{
- if (!dir)
+ if (!ops.recursive ())
error () << "'" << p << "' is a directory";
if (wd.sub (d))
@@ -1221,7 +1184,8 @@ namespace build2
//
try_rmdir_r (d);
}
- else if (try_rmfile (p) == rmfile_status::not_exist && !force)
+ else if (try_rmfile (p) == rmfile_status::not_exist &&
+ !ops.force ())
throw_generic_error (ENOENT);
}
catch (const system_error& e)
@@ -1246,6 +1210,10 @@ namespace build2
{
// Diagnostics has already been issued.
}
+ catch (const cli::exception& e)
+ {
+ error (false) << e;
+ }
cerr.close ();
return r;
@@ -1255,7 +1223,7 @@ namespace build2
return 1;
}
- // rmdir [-f] <path>...
+ // rmdir [-f|--force] <path>...
//
// Note: can be executed synchronously.
//
@@ -1278,42 +1246,28 @@ namespace build2
in.close ();
out.close ();
- auto i (args.begin ());
- auto e (args.end ());
-
- // Process options.
+ // Parse arguments.
//
- bool force (false);
- for (; i != e; ++i)
- {
- if (*i == "-f")
- force = true;
- else
- {
- if (*i == "--")
- ++i;
-
- break;
- }
- }
+ cli::vector_scanner scan (args);
+ rmdir_options ops (scan);
// Remove directories.
//
- if (i == e && !force)
+ if (!scan.more () && !ops.force ())
error () << "missing directory";
const dir_path& wd (sp.wd_path);
const dir_path& rwd (sp.root->wd_path);
- for (; i != e; ++i)
+ while (scan.more ())
{
- dir_path p (path_cast<dir_path> (parse_path (*i, wd)));
+ 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) && !force)
+ if (!p.sub (rwd) && !ops.force ())
error () << "'" << p << "' is out of working directory '"
<< rwd << "'";
@@ -1323,7 +1277,7 @@ namespace build2
if (s == rmdir_status::not_empty)
throw_generic_error (ENOTEMPTY);
- else if (s == rmdir_status::not_exist && !force)
+ else if (s == rmdir_status::not_exist && !ops.force ())
throw_generic_error (ENOENT);
}
catch (const system_error& e)
@@ -1348,6 +1302,10 @@ namespace build2
{
// Diagnostics has already been issued.
}
+ catch (const cli::exception& e)
+ {
+ error (false) << e;
+ }
cerr.close ();
return r;
@@ -1357,7 +1315,7 @@ namespace build2
return 1;
}
- // sed [-n] [-i] -e <script> [<file>]
+ // sed [-n|--quiet] [-i|--in-place] -e|--expression <script> [<file>]
//
// Note: must be executed asynchronously.
//
@@ -1388,132 +1346,108 @@ namespace build2
ifdstream cin (move (in), ifdstream::badbit);
ofdstream cout (move (out));
- auto i (args.begin ());
- auto e (args.end ());
+ // Parse arguments.
+ //
+ cli::vector_scanner scan (args);
+ sed_options ops (scan);
+
+ if (ops.expression ().empty ())
+ error () << "missing script";
- // Process options.
+ // Only a single script is supported.
//
- bool auto_prn (true);
- bool in_place (false);
+ if (ops.expression ().size () != 1)
+ error () << "multiple scripts";
- struct substitute
+ struct
{
string regex;
string replacement;
bool icase = false;
bool global = false;
bool print = false;
- };
- optional<substitute> subst;
+ } subst;
- for (; i != e; ++i)
{
- const string& o (*i);
-
- if (o == "-n")
- auto_prn = false;
- else if (o == "-i")
- in_place = true;
- else if (o == "-e")
- {
- // Only a single script is supported.
- //
- if (subst)
- error () << "multiple scripts";
-
- // If option has no value then bail out and report.
- //
- if (++i == e)
- break;
+ const string& v (ops.expression ()[0]);
+ if (v.empty ())
+ error () << "empty script";
- const string& v (*i);
- if (v.empty ())
- error () << "empty script";
+ if (v[0] != 's')
+ error () << "only 's' command supported";
- if (v[0] != 's')
- error () << "only 's' command supported";
-
- // Parse the substitute command.
- //
- if (v.size () < 2)
- error () << "no delimiter for 's' command";
+ // 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";
+ 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";
+ size_t p (v.find (delim, 2));
+ if (p == string::npos)
+ error () << "unterminated 's' command regex";
- subst = substitute ();
- subst->regex.assign (v, 2, p - 2);
+ 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";
+ // 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";
+ 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);
+ subst.replacement.assign (v, b, p - b);
- // Parse the substitute command flags.
- //
- char c;
- for (++p; (c = v[p]) != '\0'; ++p)
+ // Parse the substitute command flags.
+ //
+ char c;
+ for (++p; (c = v[p]) != '\0'; ++p)
+ {
+ switch (c)
{
- switch (c)
+ case 'i': subst.icase = true; break;
+ case 'g': subst.global = true; break;
+ case 'p': subst.print = true; break;
+ default:
{
- 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 << "'";
- }
+ error () << "invalid 's' command flag '" << c << "'";
}
}
}
- else
- {
- if (o == "--")
- ++i;
-
- break;
- }
}
- if (!subst)
- error () << "missing script";
-
// Path of a file to edit. An empty path represents stdin.
//
path p;
- if (i != e)
+ if (scan.more ())
{
- if (*i != "-")
- p = parse_path (*i, sp.wd_path);
+ string f (scan.next ());
- ++i;
+ if (f != "-")
+ p = parse_path (move (f), sp.wd_path);
}
- if (i != e)
- error () << "unexpected argument '" << *i << "'";
+ 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 (in_place)
+ if (ops.in_place ())
{
if (p.empty ())
- error () << "-i option specified while reading from stdin";
+ error () << "-i|--in-place option specified while reading from "
+ << "stdin";
try
{
@@ -1543,8 +1477,8 @@ namespace build2
// Note that ECMAScript is implied if no grammar flag is specified.
//
- regex re (subst->regex,
- subst->icase ? regex::icase : regex::ECMAScript);
+ regex re (subst.regex,
+ subst.icase ? regex::icase : regex::ECMAScript);
// Edit a file or STDIN.
//
@@ -1566,22 +1500,22 @@ namespace build2
auto r (regex_replace_search (
s,
re,
- subst->replacement,
- subst->global
+ 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 (auto_prn || (r.second && subst->print))
+ if (!ops.quiet () || (r.second && subst.print))
cout << r.first << '\n';
}
cin.close ();
cout.close ();
- if (in_place)
+ if (ops.in_place ())
{
mvfile (
tp, p,
@@ -1629,6 +1563,10 @@ namespace build2
{
// Diagnostics has already been issued.
}
+ catch (const cli::exception& e)
+ {
+ error (false) << e;
+ }
cerr.close ();
return r;
@@ -1661,17 +1599,19 @@ namespace build2
in.close ();
out.close ();
- if (args.empty ())
- error () << "missing time interval";
+ // Parse arguments.
+ //
+ cli::vector_scanner scan (args);
+ sleep_options ops (scan); // Makes sure no options passed.
- if (args.size () > 1)
- error () << "unexpected argument '" << args[1] << "'";
+ if (!scan.more ())
+ error () << "missing time interval";
uint64_t n;
for (;;) // Breakout loop.
{
- const string& a (args[0]);
+ string a (scan.next ());
// Note: strtoull() allows these.
//
@@ -1687,6 +1627,11 @@ namespace build2
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).
//
@@ -1704,6 +1649,10 @@ namespace build2
{
// Diagnostics has already been issued.
}
+ catch (const cli::exception& e)
+ {
+ error (false) << e;
+ }
cerr.close ();
return r;
@@ -1713,7 +1662,7 @@ namespace build2
return 1;
}
- // test -f|-d <path>
+ // test (-f|--file)|(-d|--directory) <path>
//
// Note: can be executed synchronously.
//
@@ -1736,22 +1685,28 @@ namespace build2
in.close ();
out.close ();
- if (args.size () < 2)
- error () << "missing path";
+ // Parse arguments.
+ //
+ cli::vector_scanner scan (args);
+ test_options ops (scan); // Makes sure no options passed.
- bool file (args[0] == "-f");
+ if (!ops.file () && !ops.directory ())
+ error () << "either -f|--file or -d|--directory must be specified";
- if (!file && args[0] != "-d")
- error () << "invalid option";
+ if (ops.file () && ops.directory ())
+ error () << "both -f|--file and -d|--directory specified";
- if (args.size () > 2)
- error () << "unexpected argument '" << args[2] << "'";
+ if (!scan.more ())
+ error () << "missing path";
+
+ path p (parse_path (scan.next (), sp.wd_path));
- path p (parse_path (args[1], sp.wd_path));
+ if (scan.more ())
+ error () << "unexpected argument '" << scan.next () << "'";
try
{
- r = (file ? file_exists (p) : dir_exists (p)) ? 0 : 1;
+ r = (ops.file () ? file_exists (p) : dir_exists (p)) ? 0 : 1;
}
catch (const system_error& e)
{
@@ -1772,6 +1727,10 @@ namespace build2
{
// Diagnostics has already been issued.
}
+ catch (const cli::exception& e)
+ {
+ error (false) << e;
+ }
cerr.close ();
return r;
@@ -1811,6 +1770,11 @@ namespace build2
in.close ();
out.close ();
+ // Parse arguments.
+ //
+ cli::vector_scanner scan (args);
+ touch_options ops (scan);
+
auto mtime = [&error] (const path& p) -> timestamp
{
try
@@ -1831,51 +1795,26 @@ namespace build2
return timestamp ();
};
- auto i (args.begin ());
- auto e (args.end ());
-
- // Process options.
- //
- bool cleanup (true);
optional<timestamp> after;
- for (; i != e; ++i)
- {
- const string& o (*i);
-
- if (o == "--no-cleanup")
- cleanup = false;
- else if (o == "--after")
- {
- if (++i == e)
- error () << "missing --after option value";
+ if (ops.after_specified ())
+ after = mtime (parse_path (ops.after (), sp.wd_path));
- after = mtime (parse_path (*i, sp.wd_path));
- }
- else
- {
- if (o == "--")
- ++i;
-
- break;
- }
- }
-
- if (i == e)
+ if (!scan.more ())
error () << "missing file";
// Create files.
//
- for (; i != e; ++i)
+ while (scan.more ())
{
- path p (parse_path (*i, sp.wd_path));
+ 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) && cleanup)
- sp.clean ({cleanup_type::always, p}, true);
+ if (touch_file (p) && !ops.no_cleanup ())
+ sp.clean ({cleanup_type::always, p}, true /* implicit */);
if (after)
{
@@ -1905,6 +1844,10 @@ namespace build2
{
// Diagnostics has already been issued.
}
+ catch (const cli::exception& e)
+ {
+ error (false) << e;
+ }
cerr.close ();
return r;
diff --git a/libbuild2/test/script/runner.cxx b/libbuild2/test/script/runner.cxx
index 6c1becd..a1eb9ec 100644
--- a/libbuild2/test/script/runner.cxx
+++ b/libbuild2/test/script/runner.cxx
@@ -19,6 +19,7 @@
#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;
using namespace butl;
@@ -1010,45 +1011,23 @@ namespace build2
//
ifdstream cin (move (in), ifdstream::badbit);
- auto i (args.begin ());
- auto e (args.end ());
-
- // Process options.
+ // Parse arguments.
//
- bool exact (false);
- bool newline (false);
- bool whitespace (false);
-
- for (; i != e; ++i)
- {
- const string& o (*i);
-
- if (o == "-e" || o == "--exact")
- exact = true;
- else if (o == "-n" || o == "--newline")
- newline = true;
- else if (o == "-w" || o == "--whitespace")
- whitespace = true;
- else
- {
- if (*i == "--")
- ++i;
+ cli::vector_scanner scan (args);
+ set_options ops (scan);
- break;
- }
- }
+ if (ops.whitespace () && ops.newline ())
+ fail (ll) << "both -n|--newline and -w|--whitespace specified";
- // Process arguments.
- //
- if (i == e)
+ if (!scan.more ())
fail (ll) << "missing variable name";
- const string& a (*i++); // Either attributes or variable name.
- const string* ats (i == e ? nullptr : &a);
- const string& vname (i == e ? a : *i++);
+ string a (scan.next ()); // Either attributes or variable name.
+ const string* ats (!scan.more () ? nullptr : &a);
+ const string& vname (!scan.more () ? a : scan.next ());
- if (i != e)
- fail (ll) << "unexpected argument '" << *i << "'";
+ if (scan.more ())
+ fail (ll) << "unexpected argument '" << scan.next () << "'";
if (ats != nullptr && ats->empty ())
fail (ll) << "empty variable attributes";
@@ -1069,7 +1048,7 @@ namespace build2
// target or as a part of msvcrt junk production (see above).
//
string s;
- if (whitespace)
+ if (ops.whitespace ())
cin >> s;
else
{
@@ -1088,9 +1067,9 @@ namespace build2
//
if (cin.fail ())
{
- if (exact)
+ if (ops.exact ())
{
- if (whitespace || newline)
+ if (ops.whitespace () || ops.newline ())
ns.emplace_back (move (s)); // Reuse empty string.
else if (ns.empty ())
ns.emplace_back ("\n");
@@ -1101,7 +1080,7 @@ namespace build2
break;
}
- if (whitespace || newline || ns.empty ())
+ if (ops.whitespace () || ops.newline () || ns.empty ())
ns.emplace_back (move (s));
else
{
@@ -1157,6 +1136,10 @@ namespace build2
{
fail (ll) << "set: " << e;
}
+ catch (const cli::exception& e)
+ {
+ fail (ll) << "set: " << e;
+ }
}
static bool
diff --git a/tests/test/script/builtin/cat.testscript b/tests/test/script/builtin/cat.testscript
index d885b53..b23efcf 100644
--- a/tests/test/script/builtin/cat.testscript
+++ b/tests/test/script/builtin/cat.testscript
@@ -4,6 +4,12 @@
.include ../common.testscript
+: unknown-option
+:
+$c <<EOI && $b
+cat -u 2>"cat: unknown option '-u'" == 1
+EOI
+
: in
:
$c <<EOI && $b
diff --git a/tests/test/script/builtin/cp.testscript b/tests/test/script/builtin/cp.testscript
index 0d465e4..46828df 100644
--- a/tests/test/script/builtin/cp.testscript
+++ b/tests/test/script/builtin/cp.testscript
@@ -4,6 +4,12 @@
.include ../common.testscript
+: unknown-option
+:
+$c <<EOI && $b
+cp -u 2>"cp: unknown option '-u'" == 1
+EOI
+
: args
:
{
diff --git a/tests/test/script/builtin/ln.testscript b/tests/test/script/builtin/ln.testscript
index 4e1ea92..072c197 100644
--- a/tests/test/script/builtin/ln.testscript
+++ b/tests/test/script/builtin/ln.testscript
@@ -4,12 +4,18 @@
.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 option" == 1' && $b
+ $c <'ln 2>"ln: missing -s|--symbolic option" == 1' && $b
: none
:
diff --git a/tests/test/script/builtin/mkdir.testscript b/tests/test/script/builtin/mkdir.testscript
index 573f73d..d1a3b4a 100644
--- a/tests/test/script/builtin/mkdir.testscript
+++ b/tests/test/script/builtin/mkdir.testscript
@@ -4,6 +4,12 @@
.include ../common.testscript
+: unknown-option
+:
+$c <<EOI && $b
+mkdir -u 2>"mkdir: unknown option '-u'" == 1
+EOI
+
: dirs
:
$c <<EOI && $b
@@ -28,7 +34,7 @@ $c <'mkdir -p a a a/b a/b' && $b
:
$c <<EOI && $b
mkdir -p -- -p;
-touch -p/a
+touch -- -p/a
EOI
: no-args
diff --git a/tests/test/script/builtin/mv.testscript b/tests/test/script/builtin/mv.testscript
index 313acf8..eb4860e 100644
--- a/tests/test/script/builtin/mv.testscript
+++ b/tests/test/script/builtin/mv.testscript
@@ -4,6 +4,12 @@
.include ../common.testscript
+: unknown-option
+:
+$c <<EOI && $b
+mv -u 2>"mv: unknown option '-u'" == 1
+EOI
+
: args
:
{
diff --git a/tests/test/script/builtin/rm.testscript b/tests/test/script/builtin/rm.testscript
index 0cc6f7c..46ed566 100644
--- a/tests/test/script/builtin/rm.testscript
+++ b/tests/test/script/builtin/rm.testscript
@@ -4,6 +4,12 @@
.include ../common.testscript
+: unknown-option
+:
+$c <<EOI && $b
+rm -u 2>"rm: unknown option '-u'" == 1
+EOI
+
: no-args
:
{
diff --git a/tests/test/script/builtin/rmdir.testscript b/tests/test/script/builtin/rmdir.testscript
index 1b19509..9f0c945 100644
--- a/tests/test/script/builtin/rmdir.testscript
+++ b/tests/test/script/builtin/rmdir.testscript
@@ -4,6 +4,12 @@
.include ../common.testscript
+: unknown-option
+:
+$c <<EOI && $b
+rmdir -u 2>"rmdir: unknown option '-u'" == 1
+EOI
+
: no-args
:
{
diff --git a/tests/test/script/builtin/sed.testscript b/tests/test/script/builtin/sed.testscript
index e3c0965..4a79fe3 100644
--- a/tests/test/script/builtin/sed.testscript
+++ b/tests/test/script/builtin/sed.testscript
@@ -4,6 +4,12 @@
.include ../common.testscript
+: unknown-option
+:
+$c <<EOI && $b
+sed -u 2>"sed: unknown option '-u'" == 1
+EOI
+
: arg
:
{
@@ -31,7 +37,7 @@
$c <'sed -e' && $b 2>>/EOE != 0
testscript:1:1: error: sed exit code 1 != 0
info: stderr: test/1/stderr
- sed: missing script
+ sed: missing value for option '-e'
info: test id: 1
EOE
@@ -321,7 +327,7 @@
$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 option specified while reading from stdin
+ sed: -i|--in-place option specified while reading from stdin
info: test id: 1
EOE
diff --git a/tests/test/script/builtin/sleep.testscript b/tests/test/script/builtin/sleep.testscript
index c43418d..7d39548 100644
--- a/tests/test/script/builtin/sleep.testscript
+++ b/tests/test/script/builtin/sleep.testscript
@@ -4,6 +4,12 @@
.include ../common.testscript
+: unknown-option
+:
+$c <<EOI && $b
+sleep -u 2>"sleep: unknown option '-u'" == 1
+EOI
+
: success
:
$c <'sleep 1' && $b
diff --git a/tests/test/script/builtin/test.testscript b/tests/test/script/builtin/test.testscript
index 650f9ef..dceb229 100644
--- a/tests/test/script/builtin/test.testscript
+++ b/tests/test/script/builtin/test.testscript
@@ -42,30 +42,44 @@
EOI
}
-: no-args
+: options
:
-: Test passing no arguments.
-:
-$c <'test 2>"test: missing path" == 2' && $b
+{
+ : unknown
+ :
+ $c <<EOI && $b
+ test -u 2>"test: unknown option '-u'" == 2
+ EOI
-: invalid-option
-:
-: Test passing invalid option.
-:
-$c <'test -c a 2>"test: invalid option" == 2' && $b
+ : none
+ :
+ $c <<EOI && $b
+ test 2>"test: either -f|--file or -d|--directory must be specified" == 2
+ EOI
-: unexpected-arg
-:
-: Test passing extra argument.
-:
-$c <<EOI && $b
-test -f a b 2>"test: unexpected argument 'b'" == 2
-EOI
+ : both-file-dir
+ :
+ $c <<EOI && $b
+ test -f -d 2>"test: both -f|--file and -d|--directory specified" == 2
+ EOI
+}
-: empty-path
+: args
:
-: Test testing an empty path.
-:
-$c <<EOI && $b
-test -d '' 2>"test: invalid path ''" == 2
-EOI
+{
+ : 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 a43386b..731f023 100644
--- a/tests/test/script/builtin/touch.testscript
+++ b/tests/test/script/builtin/touch.testscript
@@ -41,6 +41,12 @@ $c <<EOI && $b
}
EOI
+: unknown-option
+:
+$c <<EOI && $b
+touch -u 2>"touch: unknown option '-u'" == 1
+EOI
+
: no-args
:
: Test passing no arguments.
@@ -77,7 +83,7 @@ EOI
: no-value
:
$c <<EOI && $b
- touch --after 2>'touch: missing --after option value' != 0
+ touch --after 2>"touch: missing value for option '--after'" != 0
EOI
: not-exists
diff --git a/tests/test/script/runner/exit.testscript b/tests/test/script/runner/exit.testscript
index 261b02d..b461155 100644
--- a/tests/test/script/runner/exit.testscript
+++ b/tests/test/script/runner/exit.testscript
@@ -96,7 +96,7 @@ empty_id = ''
: Note that we also test that cleanups are executed.
:
$c <<EOI && $b >>EOO
- touch -f a;
+ touch a;
echo foo >| && exit && echo bar >|;
echo baz >|
echo box >|
diff --git a/tests/test/script/runner/set.testscript b/tests/test/script/runner/set.testscript
index 28d6686..edbff3e 100644
--- a/tests/test/script/runner/set.testscript
+++ b/tests/test/script/runner/set.testscript
@@ -40,6 +40,24 @@
EOE
}
+: options
+:
+{
+ : unknown
+ :
+ $c <'set -u' && $b 2>>EOE != 0
+ testscript:1:1: error: set: unknown option '-u'
+ info: test id: 1
+ EOE
+
+ : both-newline-whitespace
+ :
+ $c <'set -nw' && $b 2>>EOE != 0
+ testscript:1:1: error: both -n|--newline and -w|--whitespace specified
+ info: test id: 1
+ EOE
+}
+
: arguments
:
{