aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/test/script/lexer.cxx3
-rw-r--r--build2/test/script/parser.cxx59
-rw-r--r--build2/test/script/script2
-rw-r--r--build2/test/script/script.cxx17
-rw-r--r--build2/test/script/token1
-rw-r--r--build2/test/script/token.cxx1
-rw-r--r--unit-tests/test/script/lexer/command-line.test12
-rw-r--r--unit-tests/test/script/lexer/script-line.test8
-rw-r--r--unit-tests/test/script/parser/redirect.test71
9 files changed, 163 insertions, 11 deletions
diff --git a/build2/test/script/lexer.cxx b/build2/test/script/lexer.cxx
index 98d52e7..003e4ac 100644
--- a/build2/test/script/lexer.cxx
+++ b/build2/test/script/lexer.cxx
@@ -288,7 +288,7 @@ namespace build2
{
xchar p (peek ());
- if (p == '+' || p == '-' || p == ':' || p == '>')
+ if (p == '+' || p == '-' || p == '&' || p == ':' || p == '>')
{
get ();
@@ -296,6 +296,7 @@ namespace build2
{
case '+': return make_token (type::out_pass);
case '-': return make_token (type::out_null);
+ case '&': return make_token (type::out_merge);
case ':': return make_token (type::out_str_nn);
case '>':
{
diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx
index ae8df12..70f1ef2 100644
--- a/build2/test/script/parser.cxx
+++ b/build2/test/script/parser.cxx
@@ -564,9 +564,11 @@ namespace build2
in_string,
in_document,
in_file,
+ out_merge,
out_string,
out_document,
out_file,
+ err_merge,
err_string,
err_document,
err_file
@@ -592,6 +594,24 @@ namespace build2
auto add_word =
[&c, &p, &nn, &app, &hd, this] (string&& w, const location& l)
{
+ auto add_merge = [&l, this] (redirect& r, const string& w, int fd)
+ {
+ try
+ {
+ size_t n;
+ if (stoi (w, &n) != fd || n != w.size ())
+ throw invalid_argument (string ());
+ }
+ catch (const exception&)
+ {
+ fail (l) << "invalid " << (fd == 1 ? "stderr" : "stdout")
+ << " merge redirect file descriptor '" << w << "'" <<
+ info << "must be " << fd;
+ }
+
+ r.fd = fd;
+ };
+
auto add_here_str = [&nn] (redirect& r, string&& w)
{
if (!nn) w += '\n';
@@ -642,14 +662,17 @@ namespace build2
break;
}
- case pending::in_document: add_here_end (c.in, move (w)); break;
- case pending::out_document: add_here_end (c.out, move (w)); break;
- case pending::err_document: add_here_end (c.err, move (w)); break;
+ case pending::out_merge: add_merge (c.out, w, 2); break;
+ case pending::err_merge: add_merge (c.err, w, 1); break;
case pending::in_string: add_here_str (c.in, move (w)); break;
case pending::out_string: add_here_str (c.out, move (w)); break;
case pending::err_string: add_here_str (c.err, move (w)); break;
+ case pending::in_document: add_here_end (c.in, move (w)); break;
+ case pending::out_document: add_here_end (c.out, move (w)); break;
+ case pending::err_document: add_here_end (c.err, move (w)); break;
+
case pending::in_file: add_file (c.in, "stdin", move (w)); break;
case pending::out_file: add_file (c.out, "stdout", move (w)); break;
case pending::err_file: add_file (c.err, "stderr", move (w)); break;
@@ -673,9 +696,11 @@ namespace build2
case pending::in_string: what = "stdin here-string"; break;
case pending::in_document: what = "stdin here-document end"; break;
case pending::in_file: what = "stdin file"; break;
+ case pending::out_merge: what = "stdout file descriptor"; break;
case pending::out_string: what = "stdout here-string"; break;
case pending::out_document: what = "stdout here-document end"; break;
case pending::out_file: what = "stdout file"; break;
+ case pending::err_merge: what = "stderr file descriptor"; break;
case pending::err_string: what = "stderr here-string"; break;
case pending::err_document: what = "stderr here-document end"; break;
case pending::err_file: what = "stderr file"; break;
@@ -741,6 +766,7 @@ namespace build2
}
case type::out_pass:
case type::out_null:
+ case type::out_merge:
case type::out_str:
case type::out_str_nn:
case type::out_doc:
@@ -764,6 +790,8 @@ namespace build2
case type::in_null:
case type::out_null: rt = redirect_type::null; break;
+ case type::out_merge: rt = redirect_type::merge; break;
+
case type::in_str_nn:
case type::out_str_nn: nn = true; // Fall through.
case type::in_str:
@@ -788,6 +816,14 @@ namespace build2
case redirect_type::pass:
case redirect_type::null:
break;
+ case redirect_type::merge:
+ switch (fd)
+ {
+ case 0: assert (false); break;
+ case 1: p = pending::out_merge; break;
+ case 2: p = pending::err_merge; break;
+ }
+ break;
case redirect_type::here_string:
switch (fd)
{
@@ -842,6 +878,8 @@ namespace build2
case type::in_null:
case type::out_null:
+ case type::out_merge:
+
case type::in_str:
case type::in_doc:
case type::out_str:
@@ -904,6 +942,8 @@ namespace build2
case type::in_null:
case type::out_null:
+ case type::out_merge:
+
case type::in_str:
case type::in_doc:
case type::out_str:
@@ -1050,6 +1090,8 @@ namespace build2
case type::in_null:
case type::out_null:
+ case type::out_merge:
+
case type::in_str:
case type::out_str:
@@ -1089,11 +1131,18 @@ namespace build2
}
}
- // Verify we don't have anything pending to be filled.
- //
if (!pre_parse_)
+ {
+ // Verify we don't have anything pending to be filled.
+ //
check_pending (l);
+ if (c.out.type == redirect_type::merge &&
+ c.err.type == redirect_type::merge)
+ fail (l) << "stdout and stderr merge redirects" <<
+ info << "should not be specified at the same time";
+ }
+
// While we no longer need to recognize command line operators, we
// also don't expect a valid test trailer to contain them. So we are
// going to continue lexing in the script_line mode.
diff --git a/build2/test/script/script b/build2/test/script/script
index 574760e..f0d8f21 100644
--- a/build2/test/script/script
+++ b/build2/test/script/script
@@ -43,6 +43,7 @@ namespace build2
none,
pass,
null,
+ merge,
here_string,
here_document,
file
@@ -67,6 +68,7 @@ namespace build2
union
{
+ int fd; // Used with 'merge' type.
string str; // Note: includes trailing newline, if required.
doc_type doc;
file_type file;
diff --git a/build2/test/script/script.cxx b/build2/test/script/script.cxx
index ac11546..8de023f 100644
--- a/build2/test/script/script.cxx
+++ b/build2/test/script/script.cxx
@@ -43,9 +43,10 @@ namespace build2
switch (r.type)
{
- case redirect_type::none: assert (false); break;
- case redirect_type::pass: o << '+'; break;
- case redirect_type::null: o << '-'; break;
+ case redirect_type::none: assert (false); break;
+ case redirect_type::pass: o << '+'; break;
+ case redirect_type::null: o << '-'; break;
+ case redirect_type::merge: o << '&' << r.fd; break;
case redirect_type::here_string:
{
@@ -140,7 +141,8 @@ namespace build2
{
case redirect_type::none:
case redirect_type::pass:
- case redirect_type::null: break;
+ case redirect_type::null:
+ case redirect_type::merge: break;
case redirect_type::here_string: new (&str) string (); break;
case redirect_type::here_document: new (&doc) doc_type (); break;
@@ -158,6 +160,8 @@ namespace build2
case redirect_type::pass:
case redirect_type::null: break;
+ case redirect_type::merge: fd = r.fd; break;
+
case redirect_type::here_string:
{
new (&str) string (move (r.str));
@@ -186,6 +190,8 @@ namespace build2
case redirect_type::pass:
case redirect_type::null: break;
+ case redirect_type::merge: fd = r.fd; break;
+
case redirect_type::here_string: new (&str) string (r.str); break;
case redirect_type::here_document: new (&doc) doc_type (r.doc); break;
case redirect_type::file:
@@ -203,7 +209,8 @@ namespace build2
{
case redirect_type::none:
case redirect_type::pass:
- case redirect_type::null: break;
+ case redirect_type::null:
+ case redirect_type::merge: break;
case redirect_type::here_string: str.~string (); break;
case redirect_type::here_document: doc.~doc_type (); break;
diff --git a/build2/test/script/token b/build2/test/script/token
index d22b758..a1374fe 100644
--- a/build2/test/script/token
+++ b/build2/test/script/token
@@ -44,6 +44,7 @@ namespace build2
out_pass, // >+
out_null, // >-
+ out_merge, // >&
out_str, // >
out_str_nn, // >:
out_doc, // >>
diff --git a/build2/test/script/token.cxx b/build2/test/script/token.cxx
index 376a92e..1cd0859 100644
--- a/build2/test/script/token.cxx
+++ b/build2/test/script/token.cxx
@@ -41,6 +41,7 @@ namespace build2
case token_type::out_pass: os << q << ">+" << q; break;
case token_type::out_null: os << q << ">-" << q; break;
+ case token_type::out_merge: os << q << ">&" << q; break;
case token_type::out_str: os << q << '>' << q; break;
case token_type::out_str_nn: os << q << ">:" << q; break;
case token_type::out_doc: os << q << ">>" << q; break;
diff --git a/unit-tests/test/script/lexer/command-line.test b/unit-tests/test/script/lexer/command-line.test
index 765b59a..4a71d46 100644
--- a/unit-tests/test/script/lexer/command-line.test
+++ b/unit-tests/test/script/lexer/command-line.test
@@ -40,6 +40,18 @@ $* <:"1 >-" >>EOO # arg-out-null-redirect
>-
EOO
+$* <:"1>&2" >>EOO # merge-redirect
+'1'
+>&
+'2'
+EOO
+
+$* <:"1 >&2" >>EOO # arg-merge-redirect
+'1 '
+>&
+'2'
+EOO
+
$* <:"0<a b" >>EOO # in-str-redirect
'0'
<
diff --git a/unit-tests/test/script/lexer/script-line.test b/unit-tests/test/script/lexer/script-line.test
index a217591..36d440c 100644
--- a/unit-tests/test/script/lexer/script-line.test
+++ b/unit-tests/test/script/lexer/script-line.test
@@ -33,6 +33,14 @@ $* <"cmd <- 1>-" >>EOO # null-redirect
<newline>
EOO
+$* <"cmd 1>&2" >>EOO # merge-redirect
+'cmd'
+'1'
+>&
+'2'
+<newline>
+EOO
+
$* <"cmd <a 1>b" >>EOO # str-redirect
'cmd'
<
diff --git a/unit-tests/test/script/parser/redirect.test b/unit-tests/test/script/parser/redirect.test
index 83b40cd..3497fd9 100644
--- a/unit-tests/test/script/parser/redirect.test
+++ b/unit-tests/test/script/parser/redirect.test
@@ -3,3 +3,74 @@ cmd 0<<<a 1>>>b 2>>>&c
EOI
cmd <<<a >>>b 2>>>&c
EOO
+
+$* <<EOI >>EOO # out-merge1
+cmd 1>&2
+EOI
+cmd >&2
+EOO
+
+$* <<EOI >>EOO # out-merge2
+cmd 1>&2 2>&1 2>a
+EOI
+cmd >&2 2>a
+EOO
+
+$* <<EOI 2>>EOE != 0 # out-merge-fail1
+cmd 1>&a
+EOI
+testscript:1:8: error: invalid stdout merge redirect file descriptor 'a'
+ info: must be 2
+EOE
+
+$* <<EOI 2>>EOE != 0 # out-merge-fail2
+cmd 1>&1
+EOI
+testscript:1:8: error: invalid stdout merge redirect file descriptor '1'
+ info: must be 2
+EOE
+
+$* <<EOI 2>>EOE != 0 # out-merge-fail3
+cmd 1>&
+EOI
+testscript:1:8: error: missing stdout file descriptor
+EOE
+
+$* <<EOI >>EOO # err-merge1
+cmd 2>&1
+EOI
+cmd 2>&1
+EOO
+
+$* <<EOI >>EOO # err-merge2
+cmd 1>&2 2>&1 >a
+EOI
+cmd >a 2>&1
+EOO
+
+$* <<EOI 2>>EOE != 0 # err-merge-fail1
+cmd 2>&2
+EOI
+testscript:1:8: error: invalid stderr merge redirect file descriptor '2'
+ info: must be 1
+EOE
+
+$* <<EOI 2>>EOE != 0 # err-merge-fail2
+cmd 2>&2
+EOI
+testscript:1:8: error: invalid stderr merge redirect file descriptor '2'
+ info: must be 1
+EOE
+
+$* <<EOI 2>>EOE != 0 # err-merge-fail3
+cmd 2>&
+EOI
+testscript:1:8: error: missing stderr file descriptor
+EOE
+
+$* <<EOI 2>>EOE != 0 # merge-fail1
+cmd 1>&2 2>&1
+EOI
+testscript:1:14: error: stdout and stderr merge redirects
+ info: should not be specified both
+EOE