aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/testscript.cli26
-rw-r--r--libbuild2/build/script/lexer+command-line.test.testscript36
-rw-r--r--libbuild2/build/script/lexer.cxx10
-rw-r--r--libbuild2/build/script/lexer.hxx6
-rw-r--r--libbuild2/build/script/parser+here-document.test.testscript102
-rw-r--r--libbuild2/build/script/parser+here-string.test.testscript24
-rw-r--r--libbuild2/build/script/parser+pipe-expr.test.testscript4
-rw-r--r--libbuild2/build/script/parser+redirect.test.testscript152
-rw-r--r--libbuild2/build/script/parser+regex.test.testscript87
-rw-r--r--libbuild2/build/script/parser.cxx6
-rw-r--r--libbuild2/cc/lexer.cxx13
-rw-r--r--libbuild2/lexer.hxx4
-rw-r--r--libbuild2/script/lexer+command-expansion.test.testscript102
-rw-r--r--libbuild2/script/lexer.cxx162
-rw-r--r--libbuild2/script/lexer.hxx53
-rw-r--r--libbuild2/script/lexer.test.cxx12
-rw-r--r--libbuild2/script/parser.cxx68
-rw-r--r--libbuild2/script/parser.hxx3
-rw-r--r--libbuild2/script/run.cxx13
-rw-r--r--libbuild2/script/script.cxx118
-rw-r--r--libbuild2/script/script.hxx21
-rw-r--r--libbuild2/script/token.cxx43
-rw-r--r--libbuild2/script/token.hxx24
-rw-r--r--libbuild2/test/script/lexer.cxx10
-rw-r--r--libbuild2/test/script/lexer.hxx6
-rw-r--r--libbuild2/test/script/parser+redirect.test.testscript8
-rw-r--r--libbuild2/test/script/parser+regex.test.testscript5
-rw-r--r--libbuild2/test/script/parser.cxx5
-rw-r--r--tests/test/script/runner/redirect.testscript195
29 files changed, 967 insertions, 351 deletions
diff --git a/doc/testscript.cli b/doc/testscript.cli
index 6b47dad..665446e 100644
--- a/doc/testscript.cli
+++ b/doc/testscript.cli
@@ -545,7 +545,7 @@ complete picture:
\
$* 'World' >'Hello, World!' : command-name
-$* 'John' 'Jane' >EOO : command-names
+$* 'John' 'Jane' >>EOO : command-names
Hello, Jane!
Hello, John!
EOO
@@ -1536,9 +1536,9 @@ stderr: '2'(out-redirect)
in-redirect: '<-'|\
'<|'|\
- '<'{':'?'/'?} <text>|\
- '<<'{':'?'/'?} <here-end>|\
- '<<<' <file>
+ ('<='|'<<<') <file>|\
+ ('<<='|'<<'){':'?'/'?} <here-end>|\
+ ('<<<='|'<'){':'?'/'?} <text>
out-redirect: '>-'|\
'>|'|\
@@ -1546,9 +1546,9 @@ out-redirect: '>-'|\
'>=' <file>|\
'>+' <file>|\
'>&' ('1'|'2')|\
- '>'{':'?'/'?}'~'? <text>|\
- '>>'{':'?'/'?}'~'? <here-end>|\
- '>>>' <file>
+ ('>?'|'>>>') <file>|\
+ ('>>?'|'>>'){':'?'/'?}'~'? <here-end>|\
+ ('>>>?'|'>'){':'?'/'?}'~'? <text>
here-document:
*<text>
@@ -1866,9 +1866,9 @@ $* a1>-
\
in-redirect: '<-'|\
'<|'|\
- '<'{':'?'/'?} <text>|\
- '<<'{':'?'/'?} <here-end>|\
- '<<<' <file>
+ ('<='|'<<<') <file>|\
+ ('<<='|'<<'){':'?'/'?} <here-end>|\
+ ('<<<='|'<'){':'?'/'?} <text>
\
The \c{stdin} data can come from a pipe, here-string (\c{<}), here-document
@@ -1905,9 +1905,9 @@ out-redirect: '>-'|\
'>=' <file>|\
'>+' <file>|\
'>&' ('1'|'2')|\
- '>'{':'?'/'?}'~'? <text>|\
- '>>'{':'?'/'?}'~'? <here-end>|\
- '>>>' <file>
+ ('>?'|'>>>') <file>|\
+ ('>>?'|'>>'){':'?'/'?}'~'? <here-end>|\
+ ('>>>?'|'>'){':'?'/'?}'~'? <text>
\
The \c{stdout} and \c{stderr} data can go to a pipe (\c{stdout} only), file
diff --git a/libbuild2/build/script/lexer+command-line.test.testscript b/libbuild2/build/script/lexer+command-line.test.testscript
index 1777583..3eceae8 100644
--- a/libbuild2/build/script/lexer+command-line.test.testscript
+++ b/libbuild2/build/script/lexer+command-line.test.testscript
@@ -47,24 +47,36 @@ test.arguments = command-line
: str
:
- $* <"cmd <a 1>b" >>EOO
+ $* <"cmd <<<=a 1>>>?b" >>EOO
'cmd'
- <
+ <<<=
'a'
'1'
- >
+ >>>?
'b'
<newline>
EOO
: str-nn
:
- $* <"cmd <:a 1>:b" >>EOO
+ $* <"cmd <<<=:a 1>>>?:b" >>EOO
'cmd'
- <:
+ <<<=:
'a'
'1'
- >:
+ >>>?:
+ 'b'
+ <newline>
+ EOO
+
+ : str-nn-alias
+ :
+ $* <"cmd <<<:a 1>>>?:b" >>EOO
+ 'cmd'
+ <<<:
+ 'a'
+ '1'
+ >>>?:
'b'
<newline>
EOO
@@ -83,26 +95,26 @@ test.arguments = command-line
: doc-nn
:
- $* <"cmd <<:EOI 1>>:EOO" >>EOO
+ $* <"cmd <<:EOI 1>>?:EOO" >>EOO
'cmd'
<<:
'EOI'
'1'
- >>:
+ >>?:
'EOO'
<newline>
EOO
: file-cmp
:
- $* <"cmd <<<in >>>out 2>>>err" >>EOO
+ $* <"cmd <=in >?out 2>?err" >>EOO
'cmd'
- <<<
+ <=
'in'
- >>>
+ >?
'out'
'2'
- >>>
+ >?
'err'
<newline>
EOO
diff --git a/libbuild2/build/script/lexer.cxx b/libbuild2/build/script/lexer.cxx
index de93b6b..716d898 100644
--- a/libbuild2/build/script/lexer.cxx
+++ b/libbuild2/build/script/lexer.cxx
@@ -13,6 +13,14 @@ namespace build2
{
using type = token_type;
+ build2::script::redirect_aliases lexer::redirect_aliases {
+ type (type::in_file),
+ type (type::in_doc),
+ type (type::in_str),
+ type (type::out_file_ovr),
+ type (type::out_file_app),
+ nullopt};
+
void lexer::
mode (build2::lexer_mode m,
char ps,
@@ -192,7 +200,7 @@ namespace build2
m == lexer_mode::first_token ||
m == lexer_mode::second_token)
{
- if (optional<token> t = next_cmd_op (c, sep, m))
+ if (optional<token> t = next_cmd_op (c, sep))
return move (*t);
}
diff --git a/libbuild2/build/script/lexer.hxx b/libbuild2/build/script/lexer.hxx
index f755cea..7d919e5 100644
--- a/libbuild2/build/script/lexer.hxx
+++ b/libbuild2/build/script/lexer.hxx
@@ -48,7 +48,8 @@ namespace build2
const char* escapes = nullptr)
: base_lexer (is, name, line,
nullptr /* escapes */,
- false /* set_mode */)
+ false /* set_mode */,
+ redirect_aliases)
{
mode (m, '\0', escapes);
}
@@ -62,6 +63,9 @@ namespace build2
virtual token
next () override;
+ public:
+ static redirect_aliases_type redirect_aliases;
+
private:
token
next_line ();
diff --git a/libbuild2/build/script/parser+here-document.test.testscript b/libbuild2/build/script/parser+here-document.test.testscript
index 8ea6373..f56a5e1 100644
--- a/libbuild2/build/script/parser+here-document.test.testscript
+++ b/libbuild2/build/script/parser+here-document.test.testscript
@@ -6,49 +6,85 @@
{
: missing-newline
:
+ $* <'cmd <<=' 2>>EOE != 0
+ buildfile:11:8: error: expected here-document end marker
+ EOE
+
+ : missing-newline-alias
+ :
$* <'cmd <<' 2>>EOE != 0
buildfile:11:7: error: expected here-document end marker
EOE
: missing-exit
:
+ $* <'cmd <<= != 0' 2>>EOE != 0
+ buildfile:11:9: error: expected here-document end marker
+ EOE
+
+ : missing-exit-alias
+ :
$* <'cmd << != 0' 2>>EOE != 0
buildfile:11:8: error: expected here-document end marker
EOE
: missing-empty
:
+ $* <'cmd <<=""' 2>>EOE != 0
+ buildfile:11:8: error: expected here-document end marker
+ EOE
+
+ : missing-empty-alias
+ :
$* <'cmd <<""' 2>>EOE != 0
buildfile:11:7: error: expected here-document end marker
EOE
: unseparated-expansion
:
+ $* <'cmd <<=FOO$foo' 2>>EOE != 0
+ buildfile:11:11: error: here-document end marker must be literal
+ EOE
+
+ : unseparated-expansion-alias
+ :
$* <'cmd <<FOO$foo' 2>>EOE != 0
buildfile:11:10: error: here-document end marker must be literal
EOE
: quoted-single-partial
:
- $* <"cmd <<F'O'O" 2>>EOE != 0
- buildfile:11:7: error: partially-quoted here-document end marker
+ $* <"cmd <<=F'O'O" 2>>EOE != 0
+ buildfile:11:8: error: partially-quoted here-document end marker
EOE
: quoted-double-partial
:
- $* <'cmd <<"FO"O' 2>>EOE != 0
- buildfile:11:7: error: partially-quoted here-document end marker
+ $* <'cmd <<="FO"O' 2>>EOE != 0
+ buildfile:11:8: error: partially-quoted here-document end marker
EOE
: quoted-mixed
:
- $* <"cmd <<\"FO\"'O'" 2>>EOE != 0
- buildfile:11:7: error: partially-quoted here-document end marker
+ $* <"cmd <<=\"FO\"'O'" 2>>EOE != 0
+ buildfile:11:8: error: partially-quoted here-document end marker
EOE
: unseparated
:
$* <<EOI >>EOO
+ cmd <<=EOF!=0
+ foo
+ EOF
+ EOI
+ cmd <<=EOF != 0
+ foo
+ EOF
+ EOO
+
+ : unseparated-alias
+ :
+ $* <<EOI >>EOO
cmd <<EOF!=0
foo
EOF
@@ -61,6 +97,18 @@
: quoted-single
:
$* <<EOI >>EOO
+ cmd <<='EOF'
+ foo
+ EOF
+ EOI
+ cmd <<=EOF
+ foo
+ EOF
+ EOO
+
+ : quoted-single-alias
+ :
+ $* <<EOI >>EOO
cmd <<'EOF'
foo
EOF
@@ -73,6 +121,18 @@
: quoted-double
:
$* <<EOI >>EOO
+ cmd <<="EOF"
+ foo
+ EOF
+ EOI
+ cmd <<=EOF
+ foo
+ EOF
+ EOO
+
+ : quoted-double-alias
+ :
+ $* <<EOI >>EOO
cmd <<"EOF"
foo
EOF
@@ -89,13 +149,13 @@
: basic
:
$* <<EOI >>EOO
- cmd <<EOF
+ cmd <<=EOF
foo
bar
baz
EOF
EOI
- cmd <<EOF
+ cmd <<=EOF
foo
bar
baz
@@ -105,14 +165,14 @@
: blank
:
$* <<EOI >>EOO
- cmd <<EOF
+ cmd <<=EOF
foo
bar
EOF
EOI
- cmd <<EOF
+ cmd <<=EOF
foo
@@ -123,11 +183,11 @@
: non-ws-prefix
:
$* <<EOI >>EOO
- cmd <<EOF
+ cmd <<=EOF
x EOF
EOF
EOI
- cmd <<EOF
+ cmd <<=EOF
x EOF
EOF
EOO
@@ -137,11 +197,11 @@
:
$* <<EOI >>EOO
x = foo bar
- cmd <<"EOF"
+ cmd <<="EOF"
$x
EOF
EOI
- cmd <<EOF
+ cmd <<=EOF
foo bar
EOF
EOO
@@ -151,11 +211,11 @@
:
$* <<EOI >>EOO
x = foo
- cmd <<"EOF"
+ cmd <<="EOF"
$x bar $x
EOF
EOI
- cmd <<EOF
+ cmd <<=EOF
foo bar foo
EOF
EOO
@@ -163,7 +223,7 @@
: unindented
:
$* <<EOI 2>>EOE != 0
- cmd <<EOF
+ cmd <<=EOF
bar
EOF
EOI
@@ -174,7 +234,7 @@
: blank
:
$* <<EOI >>EOO
-cmd <<EOF
+cmd <<=EOF
foo
@@ -182,7 +242,7 @@ bar
EOF
EOI
-cmd <<EOF
+cmd <<=EOF
foo
@@ -196,14 +256,14 @@ EOO
: Note: they are still recognized in eval contexts.
:
$* <<EOI >>EOO
-cmd <<"EOF"
+cmd <<="EOF"
'single'
"double"
b'o't"h"
('single' "double")
EOF
EOI
-cmd <<EOF
+cmd <<=EOF
'single'
"double"
b'o't"h"
diff --git a/libbuild2/build/script/parser+here-string.test.testscript b/libbuild2/build/script/parser+here-string.test.testscript
index 8ca1499..f857c57 100644
--- a/libbuild2/build/script/parser+here-string.test.testscript
+++ b/libbuild2/build/script/parser+here-string.test.testscript
@@ -4,15 +4,31 @@
: empty
:
$* <<EOI >>EOO
-cmd <""
+cmd <<<=""
EOI
-cmd <''
+cmd <<<=''
EOO
: empty-nn
:
$* <<EOI >>EOO
-cmd <:""
+cmd <<<=:""
EOI
-cmd <:''
+cmd <<<=:''
+EOO
+
+: empty-alias
+:
+$* <<EOI >>EOO
+cmd <<<""
+EOI
+cmd <<<''
+EOO
+
+: empty-nn-alias
+:
+$* <<EOI >>EOO
+cmd <<<:""
+EOI
+cmd <<<:''
EOO
diff --git a/libbuild2/build/script/parser+pipe-expr.test.testscript b/libbuild2/build/script/parser+pipe-expr.test.testscript
index 3dd6b1b..a6ca12e 100644
--- a/libbuild2/build/script/parser+pipe-expr.test.testscript
+++ b/libbuild2/build/script/parser+pipe-expr.test.testscript
@@ -36,7 +36,7 @@ EOO
: here-doc
:
$* <<EOI >>EOO
-cmd1 <<EOI1 | cmd2 >>EOO2 && cmd3 <<EOI3 2>&1 | cmd4 2>>EOE4 >>EOO4
+cmd1 <<=EOI1 | cmd2 >>?EOO2 && cmd3 <<=EOI3 2>&1 | cmd4 2>>?EOE4 >>?EOO4
input
one
EOI1
@@ -53,7 +53,7 @@ output
four
EOO4
EOI
-cmd1 <<EOI1 | cmd2 >>EOO2 && cmd3 <<EOI3 2>&1 | cmd4 >>EOO4 2>>EOE4
+cmd1 <<=EOI1 | cmd2 >>?EOO2 && cmd3 <<=EOI3 2>&1 | cmd4 >>?EOO4 2>>?EOE4
input
one
EOI1
diff --git a/libbuild2/build/script/parser+redirect.test.testscript b/libbuild2/build/script/parser+redirect.test.testscript
index 641381e..82c04ea 100644
--- a/libbuild2/build/script/parser+redirect.test.testscript
+++ b/libbuild2/build/script/parser+redirect.test.testscript
@@ -21,9 +21,9 @@
: portable-path
:
$* <<EOI >>EOO
- cmd </foo >/bar 2>/baz
+ cmd <<<=/foo >>>?/bar 2>>>?/baz
EOI
- cmd </foo >/bar 2>/baz
+ cmd <<<=/foo >>>?/bar 2>>>?/baz
EOO
}
@@ -33,9 +33,9 @@
: portable-path
:
$* <<EOI >>EOO
- cmd >/~%foo% 2>/~%bar%
+ cmd >>>?/~%foo% 2>>>?/~%bar%
EOI
- cmd >/~%foo% 2>/~%bar%
+ cmd >>>?/~%foo% 2>>>?/~%bar%
EOO
}
}
@@ -49,7 +49,7 @@
: portable-path
:
$* <<EOI >>EOO
- cmd <</EOI_ >/EOO_ 2>/EOE_
+ cmd <<=/EOI_ >>?/EOO_ 2>>?/EOE_
foo
EOI_
bar
@@ -57,7 +57,7 @@
baz
EOE_
EOI
- cmd <</EOI_ >/EOO_ 2>/EOE_
+ cmd <<=/EOI_ >>?/EOO_ 2>>?/EOE_
foo
EOI_
bar
@@ -72,11 +72,35 @@
: in-out
:
$* <<EOI >>EOO
- cmd <<:/EOF >>:/EOF
+ cmd <<=:/EOF >>?:/EOF
foo
EOF
EOI
- cmd <<:/EOF >>:/EOF
+ cmd <<=:/EOF >>?:/EOF
+ foo
+ EOF
+ EOO
+
+ : in-alias-out
+ :
+ $* <<EOI >>EOO
+ cmd <<:/EOF >>?:/EOF
+ foo
+ EOF
+ EOI
+ cmd <<:/EOF >>?:/EOF
+ foo
+ EOF
+ EOO
+
+ : out-in-alias
+ :
+ $* <<EOI >>EOO
+ cmd >>?:/EOF <<:/EOF
+ foo
+ EOF
+ EOI
+ cmd <<:/EOF >>?:/EOF
foo
EOF
EOO
@@ -87,21 +111,21 @@
: modifiers
:
$* <<EOI 2>>EOE != 0
- cmd <<:/EOF >>:EOF
+ cmd <<=:/EOF >>?:EOF
foo
EOF
EOI
- buildfile:11:16: error: different modifiers for shared here-document 'EOF'
+ buildfile:11:18: error: different modifiers for shared here-document 'EOF'
EOE
: quoting
:
$* <<EOI 2>>EOE != 0
- cmd <<EOF >>"EOF"
+ cmd <<=EOF >>?"EOF"
foo
EOF
EOI
- buildfile:11:13: error: different quoting for shared here-document 'EOF'
+ buildfile:11:15: error: different quoting for shared here-document 'EOF'
EOE
}
}
@@ -113,13 +137,13 @@
: portable-path
:
$* <<EOI >>EOO
- cmd >/~%EOF% 2>/~%EOE%
+ cmd >>?/~%EOF% 2>>?/~%EOE%
foo
EOF
bar
EOE
EOI
- cmd >/~%EOF% 2>/~%EOE%
+ cmd >>?/~%EOF% 2>>?/~%EOE%
foo
EOF
bar
@@ -132,11 +156,11 @@
: in-out
:
$* <<EOI >>EOO
- cmd >>~/EOF/ 2>>~/EOF/
+ cmd >>?~/EOF/ 2>>?~/EOF/
foo
EOF
EOI
- cmd >>~/EOF/ 2>>~/EOF/
+ cmd >>?~/EOF/ 2>>?~/EOF/
foo
EOF
EOO
@@ -147,21 +171,21 @@
: introducers
:
$* <<EOI 2>>EOE != 0
- cmd >>~/EOF/ 2>>~%EOF%
+ cmd >>?~/EOF/ 2>>?~%EOF%
foo
EOF
EOI
- buildfile:11:18: error: different introducers for shared here-document regex 'EOF'
+ buildfile:11:20: error: different introducers for shared here-document regex 'EOF'
EOE
: flags
:
$* <<EOI 2>>EOE != 0
- cmd >>~/EOF/ 2>>~/EOF/i
+ cmd >>?~/EOF/ 2>>?~/EOF/i
foo
EOF
EOI
- buildfile:11:18: error: different global flags for shared here-document regex 'EOF'
+ buildfile:11:20: error: different global flags for shared here-document regex 'EOF'
EOE
}
}
@@ -179,23 +203,23 @@
: string
:
$* <<EOI >>EOO
- cmd >>EOF >bar
+ cmd >>?EOF >>>?bar
foo
EOF
EOI
- cmd >bar
+ cmd >>>?bar
EOO
: regex
:
$* <<EOI >>EOO
- cmd >>FOO >>~/BAR/
+ cmd >>?FOO >>?~/BAR/
foo
FOO
bar
BAR
EOI
- cmd >>~/BAR/
+ cmd >>?~/BAR/
bar
BAR
EOO
@@ -215,11 +239,11 @@
: different-modifiers
:
$* <<EOI 2>>EOE != 0
- cmd >>EOF >>/EOF
+ cmd >>?EOF >>?/EOF
foo
EOF
EOI
- buildfile:11:14: error: different modifiers for shared here-document 'EOF'
+ buildfile:11:16: error: different modifiers for shared here-document 'EOF'
EOE
}
}
@@ -260,9 +284,9 @@
: cmp
:
$* <<EOI >>EOO
- cmd 0<<<a 1>>>b 2>>>c
+ cmd 0<=a 1>?b 2>?c
EOI
- cmd <<<a >>>b 2>>>c
+ cmd <=a >?b 2>?c
EOO
: write
@@ -276,9 +300,9 @@
: quote
:
$* <<EOI >>EOO
- cmd 0<<<"a f" 1>="b f" 2>+"c f"
+ cmd 0<="a f" 1>="b f" 2>+"c f"
EOI
- cmd <<<'a f' >='b f' 2>+'c f'
+ cmd <='a f' >='b f' 2>+'c f'
EOO
: in
@@ -287,17 +311,37 @@
: missed
:
$* <<EOI 2>>EOE !=0
- cmd <<<
+ cmd <=
EOI
- buildfile:11:8: error: missing stdin file
+ buildfile:11:7: error: missing stdin file
EOE
: empty
:
$* <<EOI 2>>EOE !=0
- cmd <<<""
+ cmd <=""
EOI
- buildfile:11:8: error: empty stdin redirect path
+ buildfile:11:7: error: empty stdin redirect path
+ EOE
+ }
+
+ : in-alias
+ :
+ {
+ : missed
+ :
+ $* <<EOI 2>>EOE !=0
+ cmd <
+ EOI
+ buildfile:11:6: error: missing stdin file
+ EOE
+
+ : empty
+ :
+ $* <<EOI 2>>EOE !=0
+ cmd <""
+ EOI
+ buildfile:11:6: error: empty stdin redirect path
EOE
}
@@ -321,6 +365,26 @@
EOE
}
+ : out-alias
+ :
+ {
+ : missed
+ :
+ $* <<EOI 2>>EOE !=0
+ cmd >
+ EOI
+ buildfile:11:6: error: missing stdout file
+ EOE
+
+ : empty
+ :
+ $* <<EOI 2>>EOE !=0
+ cmd >""
+ EOI
+ buildfile:11:6: error: empty stdout redirect path
+ EOE
+ }
+
: err
:
{
@@ -340,6 +404,26 @@
buildfile:11:8: error: empty stderr redirect path
EOE
}
+
+ : err-alias
+ :
+ {
+ : missed
+ :
+ $* <<EOI 2>>EOE !=0
+ cmd 2>
+ EOI
+ buildfile:11:7: error: missing stderr file
+ EOE
+
+ : empty
+ :
+ $* <<EOI 2>>EOE !=0
+ cmd 2>""
+ EOI
+ buildfile:11:7: error: empty stderr redirect path
+ EOE
+ }
}
: merge
diff --git a/libbuild2/build/script/parser+regex.test.testscript b/libbuild2/build/script/parser+regex.test.testscript
index 4a47e73..625bfdf 100644
--- a/libbuild2/build/script/parser+regex.test.testscript
+++ b/libbuild2/build/script/parser+regex.test.testscript
@@ -9,61 +9,61 @@
{
: missed
:
- $* <'cmd >~' 2>>EOE != 0
- buildfile:11:7: error: missing stdout here-string regex
+ $* <'cmd >>>?~' 2>>EOE != 0
+ buildfile:11:10: error: missing stdout here-string regex
EOE
: no-introducer
:
- $* <'cmd >~""' 2>>EOE != 0
- buildfile:11:7: error: no introducer character in stdout regex redirect
+ $* <'cmd >>>?~""' 2>>EOE != 0
+ buildfile:11:10: error: no introducer character in stdout regex redirect
EOE
: no-term-introducer
:
- $* <'cmd >~/' 2>>EOE != 0
- buildfile:11:7: error: no closing introducer character in stdout regex redirect
+ $* <'cmd >>>?~/' 2>>EOE != 0
+ buildfile:11:10: error: no closing introducer character in stdout regex redirect
EOE
: portable-path-introducer
:
- $* <'cmd >/~/foo/' 2>>EOE != 0
- buildfile:11:8: error: portable path modifier and '/' introducer in stdout regex redirect
+ $* <'cmd >>>?/~/foo/' 2>>EOE != 0
+ buildfile:11:11: error: portable path modifier and '/' introducer in stdout regex redirect
EOE
: empty
:
- $* <'cmd >~//' 2>>EOE != 0
- buildfile:11:7: error: stdout regex redirect is empty
+ $* <'cmd >>>?~//' 2>>EOE != 0
+ buildfile:11:10: error: stdout regex redirect is empty
EOE
: no-flags
:
- $* <'cmd >~/fo*/' >'cmd >~/fo*/'
+ $* <'cmd >>>?~/fo*/' >'cmd >>>?~/fo*/'
: idot
:
- $* <'cmd >~/fo*/d' >'cmd >~/fo*/d'
+ $* <'cmd >>>?~/fo*/d' >'cmd >>>?~/fo*/d'
: icase
:
- $* <'cmd >~/fo*/i' >'cmd >~/fo*/i'
+ $* <'cmd >>>?~/fo*/i' >'cmd >>>?~/fo*/i'
: invalid-flags1
:
- $* <'cmd >~/foo/z' 2>>EOE != 0
- buildfile:11:7: error: junk at the end of stdout regex redirect
+ $* <'cmd >>>?~/foo/z' 2>>EOE != 0
+ buildfile:11:10: error: junk at the end of stdout regex redirect
EOE
: invalid-flags2
:
- $* <'cmd >~/foo/iz' 2>>EOE != 0
- buildfile:11:7: error: junk at the end of stdout regex redirect
+ $* <'cmd >>>?~/foo/iz' 2>>EOE != 0
+ buildfile:11:10: error: junk at the end of stdout regex redirect
EOE
: no-newline
:
- $* <'cmd >:~/fo*/' >'cmd >:~/fo*/'
+ $* <'cmd >>>?:~/fo*/' >'cmd >>>?:~/fo*/'
}
: stderr
@@ -71,8 +71,8 @@
{
: missed
:
- $* <'cmd 2>~' 2>>EOE != 0
- buildfile:11:8: error: missing stderr here-string regex
+ $* <'cmd 2>>>?~' 2>>EOE != 0
+ buildfile:11:11: error: missing stderr here-string regex
EOE
: no-introducer
@@ -81,15 +81,15 @@
: All we need is to make sure that the proper description is passed to
: the parse_regex() function.
:
- $* <'cmd 2>~""' 2>>EOE != 0
- buildfile:11:8: error: no introducer character in stderr regex redirect
+ $* <'cmd 2>>>?~""' 2>>EOE != 0
+ buildfile:11:11: error: no introducer character in stderr regex redirect
EOE
}
: modifier-last
:
- $* <'cmd >~/x' 2>>EOE != 0
- buildfile:11:7: error: no closing introducer character in stdout regex redirect
+ $* <'cmd >>>?~/x' 2>>EOE != 0
+ buildfile:11:10: error: no closing introducer character in stdout regex redirect
EOE
}
@@ -101,14 +101,14 @@
{
: missed
:
- $* <'cmd >>~' 2>>EOE != 0
- buildfile:11:8: error: expected here-document regex end marker
+ $* <'cmd >>?~' 2>>EOE != 0
+ buildfile:11:9: error: expected here-document regex end marker
EOE
: portable-path-introducer
:
$* <<EOI 2>>EOE != 0
- cmd >>/~/EOO/
+ cmd >>?/~/EOO/
foo
EOO
EOI
@@ -118,7 +118,7 @@
: unterminated-line-char
:
$* <<EOI 2>>EOE != 0
- cmd >>~/EOO/
+ cmd >>?~/EOO/
/
EOO
EOI
@@ -128,7 +128,7 @@
: empty
:
$* <<EOI 2>>EOE != 0
- cmd >>:~/EOO/
+ cmd >>?:~/EOO/
EOO
EOI
buildfile:12:1: error: empty here-document regex
@@ -137,7 +137,7 @@
: no-flags
:
$* <<EOI >>EOO
- cmd 2>>~/EOE/
+ cmd 2>>?~/EOE/
foo
/?
/foo/
@@ -149,7 +149,7 @@
//*
EOE
EOI
- cmd 2>>~/EOE/
+ cmd 2>>?~/EOE/
foo
/?
/foo/
@@ -162,15 +162,18 @@
EOE
EOO
- : no-newline
+ : no-newline-str
+ :
+ $* <'cmd >>>?:~/fo*/' >'cmd >>>?:~/fo*/'
+
+ : no-newline-doc
:
- $* <'cmd >:~/fo*/' >'cmd >:~/fo*/'
$* <<EOI >>EOO
- cmd 2>>:~/EOE/
+ cmd 2>>?:~/EOE/
foo
EOE
EOI
- cmd 2>>:~/EOE/
+ cmd 2>>?:~/EOE/
foo
EOE
EOO
@@ -181,11 +184,11 @@
: idot
:
$* <<EOI >>EOO
- cmd 2>>~/EOE/d
+ cmd 2>>?~/EOE/d
foo
EOE
EOI
- cmd 2>>~/EOE/d
+ cmd 2>>?~/EOE/d
foo
EOE
EOO
@@ -193,11 +196,11 @@
: icase
:
$* <<EOI >>EOO
- cmd 2>>~/EOE/i
+ cmd 2>>?~/EOE/i
foo
EOE
EOI
- cmd 2>>~/EOE/i
+ cmd 2>>?~/EOE/i
foo
EOE
EOO
@@ -209,14 +212,14 @@
{
: missed
:
- $* <'cmd 2>>~' 2>>EOE != 0
- buildfile:11:9: error: expected here-document regex end marker
+ $* <'cmd 2>>?~' 2>>EOE != 0
+ buildfile:11:10: error: expected here-document regex end marker
EOE
}
: modifier-last
:
- $* <'cmd >>~:/FOO/' 2>>EOE != 0
+ $* <'cmd >>?~:/FOO/' 2>>EOE != 0
buildfile:11:5: error: no closing introducer character in here-document regex end marker
EOE
}
diff --git a/libbuild2/build/script/parser.cxx b/libbuild2/build/script/parser.cxx
index 86019ba..60000c2 100644
--- a/libbuild2/build/script/parser.cxx
+++ b/libbuild2/build/script/parser.cxx
@@ -141,7 +141,7 @@ namespace build2
pair<command_expr, here_docs> p;
if (lt != line_type::cmd_else && lt != line_type::cmd_end)
- p = parse_command_expr (t, tt);
+ p = parse_command_expr (t, tt, lexer::redirect_aliases);
if (tt != type::newline)
fail (t) << "expected newline instead of " << t;
@@ -232,7 +232,9 @@ namespace build2
//
assert (!pre_parse_);
- pair<command_expr, here_docs> p (parse_command_expr (t, tt));
+ pair<command_expr, here_docs> p (
+ parse_command_expr (t, tt, lexer::redirect_aliases));
+
assert (tt == type::newline);
parse_here_documents (t, tt, p);
diff --git a/libbuild2/cc/lexer.cxx b/libbuild2/cc/lexer.cxx
index d57f5eb..d2be3d8 100644
--- a/libbuild2/cc/lexer.cxx
+++ b/libbuild2/cc/lexer.cxx
@@ -48,8 +48,8 @@ namespace build2
auto lexer::
peek (bool e) -> xchar
{
- if (unget_)
- return ungetc_;
+ if (ungetn_ != 0)
+ return ungetb_[ungetn_ - 1];
if (unpeek_)
return unpeekc_;
@@ -98,11 +98,8 @@ namespace build2
inline auto lexer::
get (bool e) -> xchar
{
- if (unget_)
- {
- unget_ = false;
- return ungetc_;
- }
+ if (ungetn_ != 0)
+ return ungetb_[--ungetn_];
else
{
xchar c (peek (e));
@@ -117,7 +114,7 @@ namespace build2
// Increment the logical line similar to how base will increment the
// physical (the column counts are the same).
//
- if (log_line_ && c == '\n' && !unget_)
+ if (log_line_ && c == '\n' && ungetn_ == 0)
++*log_line_;
base::get (c);
diff --git a/libbuild2/lexer.hxx b/libbuild2/lexer.hxx
index 8dd58c8..749668e 100644
--- a/libbuild2/lexer.hxx
+++ b/libbuild2/lexer.hxx
@@ -106,7 +106,7 @@ namespace build2
};
class LIBBUILD2_SYMEXPORT lexer:
- public butl::char_scanner<butl::utf8_validator>
+ public butl::char_scanner<butl::utf8_validator, 2>
{
public:
// If escape is not NULL then only escape sequences with characters from
@@ -256,7 +256,7 @@ namespace build2
namespace butl // ADL
{
inline build2::location
- get_location (const butl::char_scanner<butl::utf8_validator>::xchar& c,
+ get_location (const butl::char_scanner<butl::utf8_validator, 2>::xchar& c,
const void* data)
{
using namespace build2;
diff --git a/libbuild2/script/lexer+command-expansion.test.testscript b/libbuild2/script/lexer+command-expansion.test.testscript
index 9422cba..f4d69d2 100644
--- a/libbuild2/script/lexer+command-expansion.test.testscript
+++ b/libbuild2/script/lexer+command-expansion.test.testscript
@@ -113,17 +113,37 @@ test.arguments = command-expansion
{
: newline
:
- $* <:"0<a b" >>EOO
+ $* <:"0<<<=a b" >>EOO
'0'
- <
+ <<<=
'a b'
EOO
: no-newline
:
- $* <:"0<:a b" >>EOO
+ $* <:"0<<<=:a b" >>EOO
'0'
- <:
+ <<<=:
+ 'a b'
+ EOO
+ }
+
+ : in-alias
+ :
+ {
+ : newline
+ :
+ $* <:"0<<<a b" >>EOO
+ '0'
+ <<<
+ 'a b'
+ EOO
+
+ : no-newline
+ :
+ $* <:"0<<<:a b" >>EOO
+ '0'
+ <<<:
'a b'
EOO
}
@@ -133,17 +153,17 @@ test.arguments = command-expansion
{
: newline
:
- $* <:"1>a b" >>EOO
+ $* <:"1>>>?a b" >>EOO
'1'
- >
+ >>>?
'a b'
EOO
: no-newline
:
- $* <:"1>:a b" >>EOO
+ $* <:"1>>>?:a b" >>EOO
'1'
- >:
+ >>>?:
'a b'
EOO
}
@@ -157,6 +177,26 @@ test.arguments = command-expansion
{
: newline
:
+ $* <:"0<<=E O I" >>EOO
+ '0'
+ <<=
+ 'E O I'
+ EOO
+
+ : no-newline
+ :
+ $* <:"0<<=:E O I" >>EOO
+ '0'
+ <<=:
+ 'E O I'
+ EOO
+ }
+
+ : in-alias
+ :
+ {
+ : newline
+ :
$* <:"0<<E O I" >>EOO
'0'
<<
@@ -177,17 +217,17 @@ test.arguments = command-expansion
{
: newline
:
- $* <:"1>>E O O" >>EOO
+ $* <:"1>>?E O O" >>EOO
'1'
- >>
+ >>?
'E O O'
EOO
: no-newline
:
- $* <:"1>>:E O O" >>EOO
+ $* <:"1>>?:E O O" >>EOO
'1'
- >>:
+ >>?:
'E O O'
EOO
}
@@ -198,9 +238,17 @@ test.arguments = command-expansion
{
: in
:
- $* <:"0<<<a b" >>EOO
+ $* <:"0<=a b" >>EOO
'0'
- <<<
+ <=
+ 'a b'
+ EOO
+
+ : in-alias
+ :
+ $* <:"0<a b" >>EOO
+ '0'
+ <
'a b'
EOO
@@ -212,6 +260,14 @@ test.arguments = command-expansion
'a b'
EOO
+ : out-alias
+ :
+ $* <:"1>a b" >>EOO
+ '1'
+ >
+ 'a b'
+ EOO
+
: out-app
:
$* <:"1>+a b" >>EOO
@@ -219,8 +275,26 @@ test.arguments = command-expansion
>+
'a b'
EOO
+
+ : out-app-alias
+ :
+ $* <:"1>>a b" >>EOO
+ '1'
+ >>
+ 'a b'
+ EOO
}
+: no-out-alias
+:
+$* <:"1>>>a b" >>EOO
+'1'
+>>
+>
+'a b'
+EOO
+
+
: cleanup
:
{
diff --git a/libbuild2/script/lexer.cxx b/libbuild2/script/lexer.cxx
index fff9307..d78e999 100644
--- a/libbuild2/script/lexer.cxx
+++ b/libbuild2/script/lexer.cxx
@@ -165,7 +165,7 @@ namespace build2
//
if (m == lexer_mode::command_expansion)
{
- if (optional<token> t = next_cmd_op (c, sep, m))
+ if (optional<token> t = next_cmd_op (c, sep))
return move (*t);
}
@@ -176,14 +176,12 @@ namespace build2
}
optional<token> lexer::
- next_cmd_op (const xchar& c, bool sep, lexer_mode m)
+ next_cmd_op (const xchar& c, bool sep)
{
- auto make_token = [&sep, &m, &c] (type t, string v = string ())
+ auto make_token = [&sep, &c] (type t, string v = string ())
{
- bool q (m == lexer_mode::here_line_double);
-
return token (t, move (v), sep,
- (q ? quote_type::double_ : quote_type::unquoted), q,
+ quote_type::unquoted, false,
c.line, c.column,
token_printer);
};
@@ -247,89 +245,183 @@ namespace build2
//
case '<':
{
- type r (type::in_str);
+ optional<type> r;
xchar p (peek ());
- if (p == '|' || p == '-' || p == '<')
+ if (p == '|' || p == '-' || p == '=' || p == '<') // <| <- <= <<
{
- get ();
+ xchar c (get ());
switch (p)
{
- case '|': return make_token (type::in_pass);
- case '-': return make_token (type::in_null);
- case '<':
+ case '|': return make_token (type::in_pass); // <|
+ case '-': return make_token (type::in_null); // <-
+ case '=': return make_token (type::in_file); // <=
+ case '<': // <<
{
- r = type::in_doc;
p = peek ();
- if (p == '<')
+ if (p == '=' || p == '<') // <<= <<<
{
- get ();
- r = type::in_file;
+ xchar c (get ());
+
+ switch (p)
+ {
+ case '=':
+ {
+ r = type::in_doc; // <<=
+ break;
+ }
+ case '<':
+ {
+ p = peek ();
+
+ if (p == '=')
+ {
+ get ();
+ r = type::in_str; // <<<=
+ }
+
+ if (!r && redirect_aliases.lll)
+ r = type::in_lll; // <<<
+
+ // We can still end up with the << or < redirect alias,
+ // if any of them is present.
+ //
+ if (!r)
+ unget (c);
+ }
+
+ break;
+ }
}
+
+ if (!r && redirect_aliases.ll)
+ r = type::in_ll; // <<
+
+ // We can still end up with the < redirect alias, if it is
+ // present.
+ //
+ if (!r)
+ unget (c);
+
break;
}
}
}
+ if (!r && redirect_aliases.l)
+ r = type::in_l; // <
+
+ if (!r)
+ return nullopt;
+
// Handle modifiers.
//
const char* mods (nullptr);
- switch (r)
+
+ switch (redirect_aliases.resolve (*r))
{
case type::in_str:
case type::in_doc: mods = ":/"; break;
}
- return make_token_with_modifiers (r, mods);
+ token t (make_token_with_modifiers (*r, mods));
+
+ return t;
}
// >
//
case '>':
{
- type r (type::out_str);
+ optional<type> r;
xchar p (peek ());
- if (p == '|' || p == '-' || p == '!' || p == '&' ||
- p == '=' || p == '+' || p == '>')
+ if (p == '|' || p == '-' || p == '!' || p == '&' || // >| >- >! >&
+ p == '=' || p == '+' || p == '?' || p == '>') // >= >+ >? >>
{
- get ();
+ xchar c (get ());
switch (p)
{
- case '|': return make_token (type::out_pass);
- case '-': return make_token (type::out_null);
- case '!': return make_token (type::out_trace);
- case '&': return make_token (type::out_merge);
- case '=': return make_token (type::out_file_ovr);
- case '+': return make_token (type::out_file_app);
- case '>':
+ case '|': return make_token (type::out_pass); // >|
+ case '-': return make_token (type::out_null); // >-
+ case '!': return make_token (type::out_trace); // >!
+ case '&': return make_token (type::out_merge); // >&
+ case '=': return make_token (type::out_file_ovr); // >=
+ case '+': return make_token (type::out_file_app); // >+
+ case '?': return make_token (type::out_file_cmp); // >?
+ case '>': // >>
{
- r = type::out_doc;
p = peek ();
- if (p == '>')
+ if (p == '?' || p == '>') // >>? >>>
{
- get ();
- r = type::out_file_cmp;
+ xchar c (get ());
+
+ switch (p)
+ {
+ case '?':
+ {
+ r = type::out_doc; // >>?
+ break;
+ }
+ case '>':
+ {
+ p = peek ();
+
+ if (p == '?')
+ {
+ get ();
+ r = type::out_str; // >>>?
+ }
+
+ if (!r && redirect_aliases.ggg)
+ r = type::out_ggg; // >>>
+
+ // We can still end up with the >> or > redirect alias,
+ // if any of themis present.
+ //
+ if (!r)
+ unget (c);
+ }
+
+ break;
+ }
}
+
+ if (!r && redirect_aliases.gg)
+ r = type::out_gg; // >>
+
+ // We can still end up with the > redirect alias, if it is
+ // present.
+ //
+ if (!r)
+ unget (c);
+
break;
}
}
}
+ if (!r && redirect_aliases.g)
+ r = type::out_g; // >
+
+ if (!r)
+ return nullopt;
+
// Handle modifiers.
//
const char* mods (nullptr);
const char* stop (nullptr);
- switch (r)
+
+ switch (redirect_aliases.resolve (*r))
{
case type::out_str:
case type::out_doc: mods = ":/~"; stop = "~"; break;
}
- return make_token_with_modifiers (r, mods, stop);
+ return make_token_with_modifiers (*r, mods, stop);
}
}
diff --git a/libbuild2/script/lexer.hxx b/libbuild2/script/lexer.hxx
index c0d617d..bdeba66 100644
--- a/libbuild2/script/lexer.hxx
+++ b/libbuild2/script/lexer.hxx
@@ -33,21 +33,58 @@ namespace build2
lexer_mode (base_type v): base_type (v) {}
};
+ // Redirects the <, <<, <<<, >, >>, and >>> aliases resolve to.
+ //
+ struct redirect_aliases
+ {
+ optional<token_type> l; // <
+ optional<token_type> ll; // <<
+ optional<token_type> lll; // <<<
+ optional<token_type> g; // >
+ optional<token_type> gg; // >>
+ optional<token_type> ggg; // >>>
+
+ // If the token type is a redirect alias then return the token type
+ // it resolves to and the passed type otherwise. It's the caller's
+ // responsibility to make sure that the corresponding alias is present.
+ //
+ token_type
+ resolve (token_type t) const noexcept
+ {
+ switch (t)
+ {
+ case token_type::in_l: assert (l); return *l;
+ case token_type::in_ll: assert (ll); return *ll;
+ case token_type::in_lll: assert (lll); return *lll;
+ case token_type::out_g: assert (g); return *g;
+ case token_type::out_gg: assert (gg); return *gg;
+ case token_type::out_ggg: assert (ggg); return *ggg;
+ }
+
+ return t;
+ }
+ };
+
class lexer: public build2::lexer
{
public:
using base_lexer = build2::lexer;
using base_mode = build2::lexer_mode;
- // Note that neither the name nor escape arguments are copied.
+ using redirect_aliases_type = script::redirect_aliases;
+
+ // Note that none of the name, redirect aliases, and escape arguments
+ // are copied.
//
lexer (istream& is,
const path_name& name,
lexer_mode m,
+ const redirect_aliases_type& ra,
const char* escapes = nullptr)
: base_lexer (is, name, 1 /* line */,
nullptr /* escapes */,
- false /* set_mode */)
+ false /* set_mode */),
+ redirect_aliases (ra)
{
mode (m, '\0', escapes);
}
@@ -69,19 +106,23 @@ namespace build2
virtual token
next () override;
+ public:
+ const redirect_aliases_type& redirect_aliases;
+
protected:
lexer (istream& is, const path_name& name, uint64_t line,
const char* escapes,
- bool set_mode)
- : base_lexer (is, name, line, escapes, set_mode) {}
+ bool set_mode,
+ const redirect_aliases_type& ra)
+ : base_lexer (is, name, line, escapes, set_mode),
+ redirect_aliases (ra) {}
// Return the next token if it is a command operator (|, ||, &&,
// redirect, or cleanup) and nullopt otherwise.
//
optional<token>
next_cmd_op (const xchar&, // The token first character (last got char).
- bool sep, // The token is separated.
- lexer_mode); // The current (potentially "expired") mode.
+ bool sep); // The token is separated.
private:
token
diff --git a/libbuild2/script/lexer.test.cxx b/libbuild2/script/lexer.test.cxx
index 24fe335..b8de241 100644
--- a/libbuild2/script/lexer.test.cxx
+++ b/libbuild2/script/lexer.test.cxx
@@ -37,7 +37,17 @@ namespace build2
cin.exceptions (istream::failbit | istream::badbit);
path_name in ("<stdin>");
- lexer l (cin, in, m);
+
+ using type = token_type;
+
+ redirect_aliases ra {type (type::in_file),
+ type (type::in_doc),
+ type (type::in_str),
+ type (type::out_file_ovr),
+ type (type::out_file_app),
+ nullopt};
+
+ lexer l (cin, in, m, ra);
// No use printing eos since we will either get it or loop forever.
//
diff --git a/libbuild2/script/parser.cxx b/libbuild2/script/parser.cxx
index 6c651a6..aa60111 100644
--- a/libbuild2/script/parser.cxx
+++ b/libbuild2/script/parser.cxx
@@ -97,7 +97,8 @@ namespace build2
}
pair<command_expr, parser::here_docs> parser::
- parse_command_expr (token& t, type& tt)
+ parse_command_expr (token& t, type& tt,
+ const redirect_aliases& ra)
{
// enter: first token of the command line
// leave: <newline> or unknown token
@@ -201,7 +202,7 @@ namespace build2
{
assert (r); // Must already be present.
- if (r->modifiers.find (':') == string::npos)
+ if (r->modifiers ().find (':') == string::npos)
w += '\n';
r->str = move (w);
};
@@ -218,7 +219,7 @@ namespace build2
case 2: what = "stderr regex redirect"; break;
}
- check_regex_mod (r->modifiers, w, l, what);
+ check_regex_mod (r->modifiers (), w, l, what);
regex_parts rp (parse_regex (w, l, what));
@@ -233,7 +234,7 @@ namespace build2
// Note that the position is synthetic, but that's ok as we don't
// expect any diagnostics to refer this line.
//
- if (r->modifiers.find (':') == string::npos)
+ if (r->modifiers ().find (':') == string::npos)
re.lines.emplace_back (l.line, l.column, string (), false);
};
@@ -382,9 +383,24 @@ namespace build2
// Parse the redirect operator.
//
- auto parse_redirect =
- [&c, &expr, &p, &mod, &hd, this] (token& t, const location& l)
+ // If the token type is the redirect alias then tt must contain the type
+ // the alias resolves to and the token type otherwise. Note that this
+ // argument defines the redirect semantics. Also note that the token is
+ // saved into the redirect to keep the modifiers and the original
+ // representation.
+ //
+ auto parse_redirect = [&c, &expr, &p, &mod, &hd, this]
+ (token&& t, type tt, const location& l)
{
+ // The redirect alias token type must be resolved.
+ //
+ assert (tt != type::in_l &&
+ tt != type::in_ll &&
+ tt != type::in_lll &&
+ tt != type::out_g &&
+ tt != type::out_gg &&
+ tt != type::out_ggg);
+
// Our semantics is the last redirect seen takes effect.
//
assert (p == pending::none && mod.empty ());
@@ -415,8 +431,6 @@ namespace build2
c.arguments.pop_back ();
}
- type tt (t.type);
-
// Validate/set default file descriptor.
//
switch (tt)
@@ -452,7 +466,9 @@ namespace build2
}
}
- mod = move (t.value);
+ // Don't move as we will save the token into the redirect object.
+ //
+ mod = t.value;
// Handle the none redirect (no data allowed) in the switch construct
// if/when the respective syntax is invented.
@@ -516,7 +532,7 @@ namespace build2
// Don't move as still may be used for pending here-document end
// marker processing.
//
- r->modifiers = mod;
+ r->token = move (t);
switch (rt)
{
@@ -640,6 +656,8 @@ namespace build2
for (bool done (false); !done; l = get_location (t))
{
+ tt = ra.resolve (tt);
+
switch (tt)
{
case type::newline:
@@ -878,7 +896,7 @@ namespace build2
case type::out_file_ovr:
case type::out_file_app:
{
- parse_redirect (t, l);
+ parse_redirect (move (t), tt, l);
break;
}
@@ -1053,11 +1071,11 @@ namespace build2
// args = 'x=\"foo bar\"'
// cmd $args # cmd x="foo bar"
//
-
istringstream is (s);
path_name in ("<string>");
lexer lex (is, in,
lexer_mode::command_expansion,
+ ra,
"\'\"\\");
// Treat the first "sub-token" as always separated from what
@@ -1075,7 +1093,7 @@ namespace build2
for (; t.type != type::eos; t = lex.next ())
{
- type tt (t.type);
+ type tt (ra.resolve (t.type));
l = build2::get_location (t, in);
// Re-lexing double-quotes will recognize $, ( inside as
@@ -1155,7 +1173,7 @@ namespace build2
case type::out_file_ovr:
case type::out_file_app:
{
- parse_redirect (t, l);
+ parse_redirect (move (t), tt, l);
break;
}
@@ -1309,8 +1327,17 @@ namespace build2
{
command& c (p.first[i->expr].pipe[i->pipe]);
- (i->fd == 0 ? c.in : i->fd == 1 ? c.out : c.err) =
- redirect (redirect_type::here_doc_ref, *r);
+ optional<redirect>& ir (i->fd == 0 ? c.in :
+ i->fd == 1 ? c.out :
+ c.err);
+
+ // Must be present since it is referenced by here-doc.
+ //
+ assert (ir);
+
+ // Note: preserve the original representation.
+ //
+ ir = redirect (redirect_type::here_doc_ref, *r, move (ir->token));
}
}
@@ -1660,6 +1687,8 @@ namespace build2
build2::parser::lexer_ = l;
}
+ static redirect_aliases no_redirect_aliases;
+
void parser::
apply_value_attributes (const variable* var,
value& lhs,
@@ -1671,7 +1700,12 @@ namespace build2
path_ = &name;
istringstream is (attributes);
- lexer l (is, name, lexer_mode::attributes);
+
+ // Note that the redirect alias information is not used in the
+ // attributes lexer mode.
+ //
+ lexer l (is, name, lexer_mode::attributes, no_redirect_aliases);
+
set_lexer (&l);
token t;
diff --git a/libbuild2/script/parser.hxx b/libbuild2/script/parser.hxx
index ecd9f5a..d47f88e 100644
--- a/libbuild2/script/parser.hxx
+++ b/libbuild2/script/parser.hxx
@@ -12,6 +12,7 @@
#include <libbuild2/diagnostics.hxx>
#include <libbuild2/script/token.hxx>
+#include <libbuild2/script/lexer.hxx> // redirect_aliases
#include <libbuild2/script/script.hxx>
namespace build2
@@ -86,7 +87,7 @@ namespace build2
using here_docs = vector<here_doc>;
pair<command_expr, here_docs>
- parse_command_expr (token&, token_type&);
+ parse_command_expr (token&, token_type&, const redirect_aliases&);
command_exit
parse_command_exit (token&, token_type&);
diff --git a/libbuild2/script/run.cxx b/libbuild2/script/run.cxx
index 3474ccb..870a70f 100644
--- a/libbuild2/script/run.cxx
+++ b/libbuild2/script/run.cxx
@@ -266,7 +266,7 @@ namespace build2
eop = path (op + ".orig");
save (eop,
- transform (rd.str, false /* regex */, rd.modifiers, env),
+ transform (rd.str, false /* regex */, rd.modifiers (), env),
ll);
env.clean_special (eop);
@@ -432,14 +432,14 @@ namespace build2
if (l.regex) // Regex (possibly empty),
{
r += rl.intro;
- r += transform (l.value, true /* regex */, rd.modifiers, env);
+ r += transform (l.value, true /* regex */, rd.modifiers (), env);
r += rl.intro;
r += l.flags;
}
else if (!l.special.empty ()) // Special literal.
r += rl.intro;
else // Textual literal.
- r += transform (l.value, false /* regex */, rd.modifiers, env);
+ r += transform (l.value, false /* regex */, rd.modifiers (), env);
r += l.special;
return r;
@@ -517,7 +517,7 @@ namespace build2
{
string s (transform (l.value,
true /* regex */,
- rd.modifiers,
+ rd.modifiers (),
env));
c = line_char (
@@ -556,7 +556,8 @@ namespace build2
//
rls += line_char (transform (l.value,
false /* regex */,
- rd.modifiers, env),
+ rd.modifiers (),
+ env),
pool);
}
@@ -1091,7 +1092,7 @@ namespace build2
isp = std_path ("stdin");
save (isp,
- transform (in.str, false /* regex */, in.modifiers, env),
+ transform (in.str, false /* regex */, in.modifiers (), env),
ll);
env.clean_special (isp);
diff --git a/libbuild2/script/script.cxx b/libbuild2/script/script.cxx
index a93315f..5a2eda3 100644
--- a/libbuild2/script/script.cxx
+++ b/libbuild2/script/script.cxx
@@ -235,44 +235,56 @@ namespace build2
to_stream_q (o, s.str ());
};
- auto print_redirect = [&o, print_path] (const redirect& r,
- const char* prefix)
+ auto print_redirect = [&o, print_path] (const redirect& r, int fd)
{
- o << ' ' << prefix;
+ const redirect& er (r.effective ());
- size_t n (string::traits_type::length (prefix));
- assert (n > 0);
+ // Print the none redirect (no data allowed) if/when the respective
+ // syntax is invented.
+ //
+ if (er.type == redirect_type::none)
+ return;
+
+ o << ' ';
- char d (prefix[n - 1]); // Redirect direction.
+ // Print the redirect file descriptor.
+ //
+ if (fd == 2)
+ o << fd;
+
+ // Print the redirect original representation and the modifiers, if
+ // present.
+ //
+ r.token.printer (o, r.token, print_mode::raw);
- switch (r.type)
+ // Print the rest of the redirect (file path, etc).
+ //
+ switch (er.type)
{
- case redirect_type::none: assert (false); break;
- case redirect_type::pass: o << '|'; break;
- case redirect_type::null: o << '-'; break;
- case redirect_type::trace: o << '!'; break;
- case redirect_type::merge: o << '&' << r.fd; break;
+ case redirect_type::none: assert (false); break;
+ case redirect_type::here_doc_ref: assert (false); break;
+
+ case redirect_type::pass:
+ case redirect_type::null:
+ case redirect_type::trace: break;
+ case redirect_type::merge: o << er.fd; break;
+
+ case redirect_type::file:
+ {
+ print_path (er.file.path);
+ break;
+ }
case redirect_type::here_str_literal:
case redirect_type::here_doc_literal:
{
- bool doc (r.type == redirect_type::here_doc_literal);
-
- // For here-document add another '>' or '<'. Note that here end
- // marker never needs to be quoted.
- //
- if (doc)
- o << d;
-
- o << r.modifiers;
-
- if (doc)
- o << r.end;
+ if (er.type == redirect_type::here_doc_literal)
+ o << er.end;
else
{
- const string& v (r.str);
+ const string& v (er.str);
to_stream_q (o,
- r.modifiers.find (':') == string::npos
+ er.modifiers ().find (':') == string::npos
? string (v, 0, v.size () - 1) // Strip newline.
: v);
}
@@ -283,20 +295,10 @@ namespace build2
case redirect_type::here_str_regex:
case redirect_type::here_doc_regex:
{
- bool doc (r.type == redirect_type::here_doc_regex);
-
- // For here-document add another '>' or '<'. Note that here end
- // marker never needs to be quoted.
- //
- if (doc)
- o << d;
-
- o << r.modifiers;
+ const regex_lines& re (er.regex);
- const regex_lines& re (r.regex);
-
- if (doc)
- o << re.intro + r.end + re.intro + re.flags;
+ if (er.type == redirect_type::here_doc_regex)
+ o << re.intro + er.end + re.intro + re.flags;
else
{
assert (!re.lines.empty ()); // Regex can't be empty.
@@ -307,23 +309,6 @@ namespace build2
break;
}
-
- case redirect_type::file:
- {
- // For stdin or stdout-comparison redirect add '>>' or '<<' (and
- // so make it '<<<' or '>>>'). Otherwise add '+' or '=' (and so
- // make it '>+' or '>=').
- //
- if (d == '<' || r.file.mode == redirect_fmode::compare)
- o << d << d;
- else
- o << (r.file.mode == redirect_fmode::append ? '+' : '=');
-
- print_path (r.file.path);
- break;
- }
-
- case redirect_type::here_doc_ref: assert (false); break;
}
};
@@ -358,7 +343,7 @@ namespace build2
}
}
- o << (r.modifiers.find (':') == string::npos ? "" : "\n") << r.end;
+ o << (r.modifiers ().find (':') == string::npos ? "" : "\n") << r.end;
};
if ((m & command_to_stream::header) == command_to_stream::header)
@@ -377,17 +362,14 @@ namespace build2
// Redirects.
//
- // Print the none redirect (no data allowed) if/when the respective
- // syntax is invened.
- //
- if (c.in && c.in->effective ().type != redirect_type::none)
- print_redirect (c.in->effective (), "<");
+ if (c.in)
+ print_redirect (*c.in, 0);
- if (c.out && c.out->effective ().type != redirect_type::none)
- print_redirect (c.out->effective (), ">");
+ if (c.out)
+ print_redirect (*c.out, 1);
- if (c.err && c.err->effective ().type != redirect_type::none)
- print_redirect (c.err->effective (), "2>");
+ if (c.err)
+ print_redirect (*c.err, 2);
for (const auto& p: c.cleanups)
{
@@ -513,7 +495,7 @@ namespace build2
redirect::
redirect (redirect&& r) noexcept
: type (r.type),
- modifiers (move (r.modifiers)),
+ token (move (r.token)),
end (move (r.end)),
end_line (r.end_line),
end_column (r.end_column)
@@ -593,7 +575,7 @@ namespace build2
redirect::
redirect (const redirect& r)
: type (r.type),
- modifiers (r.modifiers),
+ token (r.token),
end (r.end),
end_line (r.end_line),
end_column (r.end_column)
diff --git a/libbuild2/script/script.hxx b/libbuild2/script/script.hxx
index abb2fd7..22747a8 100644
--- a/libbuild2/script/script.hxx
+++ b/libbuild2/script/script.hxx
@@ -8,7 +8,7 @@
#include <libbuild2/forward.hxx>
#include <libbuild2/utility.hxx>
-#include <libbuild2/token.hxx> // replay_tokens
+#include <libbuild2/token.hxx>
#include <libbuild2/variable.hxx>
namespace build2
@@ -54,7 +54,7 @@ namespace build2
// Note that the exact spacing and partial quoting may not be restored due
// to the information loss.
//
- LIBBUILD2_SYMEXPORT void
+ void
dump (ostream&, const string& ind, const lines&);
// Parse object model.
@@ -167,7 +167,10 @@ namespace build2
reference_wrapper<const redirect> ref; // Note: no chains.
};
- string modifiers; // Redirect modifiers.
+ // Modifiers and the original representation (potentially an alias).
+ //
+ build2::token token;
+
string end; // Here-document end marker (no regex intro/flags).
uint64_t end_line; // Here-document end marker location.
uint64_t end_column;
@@ -179,8 +182,10 @@ namespace build2
// Create redirect of the reference type.
//
- redirect (redirect_type t, const redirect& r)
- : type (redirect_type::here_doc_ref), ref (r)
+ redirect (redirect_type t, const redirect& r, build2::token tk)
+ : type (redirect_type::here_doc_ref),
+ ref (r),
+ token (move (tk))
{
// There is no support (and need) for reference chains.
//
@@ -217,6 +222,12 @@ namespace build2
{
return type == redirect_type::here_doc_ref ? ref.get () : *this;
}
+
+ const string&
+ modifiers () const noexcept
+ {
+ return token.value;
+ }
};
// cleanup
diff --git a/libbuild2/script/token.cxx b/libbuild2/script/token.cxx
index 6c9de87..1c612a5 100644
--- a/libbuild2/script/token.cxx
+++ b/libbuild2/script/token.cxx
@@ -20,24 +20,31 @@ namespace build2
switch (t.type)
{
- case token_type::clean: os << q << '&' << v << q; break;
- case token_type::pipe: os << q << '|' << q; break;
-
- case token_type::in_pass: os << q << "<|" << q; break;
- case token_type::in_null: os << q << "<-" << q; break;
- case token_type::in_str: os << q << '<' << v << q; break;
- case token_type::in_doc: os << q << "<<" << v << q; break;
- case token_type::in_file: os << q << "<<<" << q; break;
-
- case token_type::out_pass: os << q << ">|" << q; break;
- case token_type::out_null: os << q << ">-" << q; break;
- case token_type::out_trace: os << q << ">!" << q; break;
- case token_type::out_merge: os << q << ">&" << q; break;
- case token_type::out_str: os << q << '>' << v << q; break;
- case token_type::out_doc: os << q << ">>" << v << q; break;
- case token_type::out_file_cmp: os << q << ">>>" << v << q; break;
- case token_type::out_file_ovr: os << q << ">=" << v << q; break;
- case token_type::out_file_app: os << q << ">+" << v << q; break;
+ case token_type::clean: os << q << '&' << v << q; break;
+ case token_type::pipe: os << q << '|' << q; break;
+
+ case token_type::in_pass: os << q << "<|" << q; break;
+ case token_type::in_null: os << q << "<-" << q; break;
+ case token_type::in_file: os << q << "<=" << q; break;
+ case token_type::in_doc: os << q << "<<=" << v << q; break;
+ case token_type::in_str: os << q << "<<<=" << v << q; break;
+
+ case token_type::out_pass: os << q << ">|" << q; break;
+ case token_type::out_null: os << q << ">-" << q; break;
+ case token_type::out_trace: os << q << ">!" << q; break;
+ case token_type::out_merge: os << q << ">&" << q; break;
+ case token_type::out_file_ovr: os << q << ">=" << q; break;
+ case token_type::out_file_app: os << q << ">+" << q; break;
+ case token_type::out_file_cmp: os << q << ">?" << q; break;
+ case token_type::out_doc: os << q << ">>?" << v << q; break;
+ case token_type::out_str: os << q << ">>>?" << v << q; break;
+
+ case token_type::in_l: os << q << '<' << v << q; break;
+ case token_type::in_ll: os << q << "<<" << v << q; break;
+ case token_type::in_lll: os << q << "<<<" << v << q; break;
+ case token_type::out_g: os << q << '>' << v << q; break;
+ case token_type::out_gg: os << q << ">>" << v << q; break;
+ case token_type::out_ggg: os << q << ">>>" << v << q; break;
default: build2::token_printer (os, t, m);
}
diff --git a/libbuild2/script/token.hxx b/libbuild2/script/token.hxx
index a2ccaee..0186bd9 100644
--- a/libbuild2/script/token.hxx
+++ b/libbuild2/script/token.hxx
@@ -22,23 +22,33 @@ namespace build2
// NOTE: remember to update token_printer()!
pipe = base_type::value_next, // |
- clean, // &{?!} (modifiers in value)
+ clean, // &{?!} (modifiers in value)
in_pass, // <|
in_null, // <-
- in_str, // <{:/} (modifiers in value)
- in_doc, // <<{:/} (modifiers in value)
- in_file, // <<<
+ in_file, // <=
+ in_doc, // <<={:/} (modifiers in value)
+ in_str, // <<<={:/} (modifiers in value)
out_pass, // >|
out_null, // >-
out_trace, // >!
out_merge, // >&
- out_str, // >{:/~} (modifiers in value)
- out_doc, // >>{:/~} (modifiers in value)
- out_file_cmp, // >>>
out_file_ovr, // >=
out_file_app, // >+
+ out_file_cmp, // >?
+ out_doc, // >>?{:/~} (modifiers in value)
+ out_str, // >>>?{:/~} (modifiers in value)
+
+ // The modifiers are in the token value, if the redirect the alias
+ // resolves to supports the modifiers.
+ //
+ in_l, // <
+ in_ll, // <<
+ in_lll, // <<<
+ out_g, // >
+ out_gg, // >>
+ out_ggg, // >>>
value_next
};
diff --git a/libbuild2/test/script/lexer.cxx b/libbuild2/test/script/lexer.cxx
index 0e8691c..a94109b 100644
--- a/libbuild2/test/script/lexer.cxx
+++ b/libbuild2/test/script/lexer.cxx
@@ -15,6 +15,14 @@ namespace build2
{
using type = token_type;
+ build2::script::redirect_aliases lexer::redirect_aliases {
+ type (type::in_str),
+ type (type::in_doc),
+ type (type::in_file),
+ type (type::out_str),
+ type (type::out_doc),
+ type (type::out_file_cmp)};
+
void lexer::
mode (base_mode m, char ps, optional<const char*> esc, uintptr_t data)
{
@@ -239,7 +247,7 @@ namespace build2
m == lexer_mode::first_token ||
m == lexer_mode::second_token)
{
- if (optional<token> t = next_cmd_op (c, sep, m))
+ if (optional<token> t = next_cmd_op (c, sep))
return move (*t);
}
diff --git a/libbuild2/test/script/lexer.hxx b/libbuild2/test/script/lexer.hxx
index 4b6c53a..452e794 100644
--- a/libbuild2/test/script/lexer.hxx
+++ b/libbuild2/test/script/lexer.hxx
@@ -48,7 +48,8 @@ namespace build2
const char* escapes = nullptr)
: base_lexer (is, name, 1 /* line */,
nullptr /* escapes */,
- false /* set_mode */)
+ false /* set_mode */,
+ redirect_aliases)
{
mode (m, '\0', escapes);
}
@@ -62,6 +63,9 @@ namespace build2
virtual token
next () override;
+ public:
+ static redirect_aliases_type redirect_aliases;
+
private:
token
next_line ();
diff --git a/libbuild2/test/script/parser+redirect.test.testscript b/libbuild2/test/script/parser+redirect.test.testscript
index 3858808..79530e0 100644
--- a/libbuild2/test/script/parser+redirect.test.testscript
+++ b/libbuild2/test/script/parser+redirect.test.testscript
@@ -49,7 +49,7 @@
: portable-path
:
$* <<EOI >>EOO
- cmd <</EOI_ >/EOO_ 2>/EOE_
+ cmd <</EOI_ >>/EOO_ 2>>/EOE_
foo
EOI_
bar
@@ -57,7 +57,7 @@
baz
EOE_
EOI
- cmd <</EOI_ >/EOO_ 2>/EOE_
+ cmd <</EOI_ >>/EOO_ 2>>/EOE_
foo
EOI_
bar
@@ -113,13 +113,13 @@
: portable-path
:
$* <<EOI >>EOO
- cmd >/~%EOF% 2>/~%EOE%
+ cmd >>/~%EOF% 2>>/~%EOE%
foo
EOF
bar
EOE
EOI
- cmd >/~%EOF% 2>/~%EOE%
+ cmd >>/~%EOF% 2>>/~%EOE%
foo
EOF
bar
diff --git a/libbuild2/test/script/parser+regex.test.testscript b/libbuild2/test/script/parser+regex.test.testscript
index 8627304..db418b3 100644
--- a/libbuild2/test/script/parser+regex.test.testscript
+++ b/libbuild2/test/script/parser+regex.test.testscript
@@ -162,9 +162,12 @@
EOE
EOO
- : no-newline
+ : no-newline-str
:
$* <'cmd >:~/fo*/' >'cmd >:~/fo*/'
+
+ : no-newline-doc
+ :
$* <<EOI >>EOO
cmd 2>>:~/EOE/
foo
diff --git a/libbuild2/test/script/parser.cxx b/libbuild2/test/script/parser.cxx
index 798b743..c206e0a 100644
--- a/libbuild2/test/script/parser.cxx
+++ b/libbuild2/test/script/parser.cxx
@@ -428,7 +428,7 @@ namespace build2
pair<command_expr, here_docs> p;
if (lt != line_type::cmd_else && lt != line_type::cmd_end)
- p = parse_command_expr (t, tt);
+ p = parse_command_expr (t, tt, lexer::redirect_aliases);
// Colon and semicolon are only valid in test command lines and
// after 'end' in if-else. Note that we still recognize them
@@ -1248,7 +1248,8 @@ namespace build2
// Note: this one is only used during execution.
- pair<command_expr, here_docs> p (parse_command_expr (t, tt));
+ pair<command_expr, here_docs> p (
+ parse_command_expr (t, tt, lexer::redirect_aliases));
switch (tt)
{
diff --git a/tests/test/script/runner/redirect.testscript b/tests/test/script/runner/redirect.testscript
index b8fe74d..0fe3aa3 100644
--- a/tests/test/script/runner/redirect.testscript
+++ b/tests/test/script/runner/redirect.testscript
@@ -80,10 +80,14 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: literal
:
{
- $c <'$* -i 0 <foo' && $b : in
- $c <'$* -o foo >foo' && $b : out
- $c <'$* -e foo 2>foo' && $b : err
- $c <'$* -i 1 <foo >foo' && $b : inout
+ $c <'$* -i 0 <<<=foo' && $b : in
+ $c <'$* -i 0 <foo' && $b : in-alias
+ $c <'$* -o foo >>>?foo' && $b : out
+ $c <'$* -o foo >foo' && $b : out-alias
+ $c <'$* -e foo 2>>>?foo' && $b : err
+ $c <'$* -e foo 2>foo' && $b : err-alias
+ $c <'$* -i 1 <<<=foo >>>?foo' && $b : inout
+ $c <'$* -i 1 <foo >foo' && $b : inout-alias
: inout-fail
:
@@ -101,11 +105,16 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
info: test id: 1
EOE
- $c <'$* -i 2 <foo 2>foo' && $b : inerr
- $c <'$* -i 1 -e bar <foo 1>foo 2>bar' && $b : inout-err
- $c <'$* -o "" >""' && $b : empty
- $c <'$* -i 1 <:"foo" >:"foo"' && $b : no-newline
- $c <'$* -i 1 <:"" >:""' && $b : no-newline-empty
+ $c <'$* -i 2 <<<=foo 2>>>?foo' && $b : inerr
+ $c <'$* -i 2 <foo 2>foo' && $b : inerr-alias
+ $c <'$* -i 1 -e bar <<<=foo 1>>>?foo 2>>>?bar' && $b : inout-err
+ $c <'$* -i 1 -e bar <foo 1>foo 2>bar' && $b : inout-err-alias
+ $c <'$* -o "" >>>?""' && $b : empty
+ $c <'$* -o "" >""' && $b : empty-alias
+ $c <'$* -i 1 <<<=:"foo" >>>?:"foo"' && $b : no-newline
+ $c <'$* -i 1 <:"foo" >:"foo"' && $b : no-newline-alias
+ $c <'$* -i 1 <<<=:"" >>>?:""' && $b : no-newline-empty
+ $c <'$* -i 1 <:"" >:""' && $b : no-newline-empty-alias
: no-newline-fail1
:
@@ -132,6 +141,15 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: merge
:
$c <<EOI && $b
+ $* -o foo -e bar 2>>?EOE 1>&2
+ foo
+ bar
+ EOE
+ EOI
+
+ : merge-alias
+ :
+ $c <<EOI && $b
$* -o foo -e bar 2>>EOE 1>&2
foo
bar
@@ -155,6 +173,10 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
{
: match
:
+ $c <'$* -o foo >>>?~/Foo?/i' && $b
+
+ : match-alias
+ :
$c <'$* -o foo >~/Foo?/i' && $b
: mismatch
@@ -209,6 +231,15 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: in
:
$c <<EOI && $b
+ $* -i 0 <<=EOO
+ foo
+ bar
+ EOO
+ EOI
+
+ : in-alias
+ :
+ $c <<EOI && $b
$* -i 0 <<EOO
foo
bar
@@ -218,6 +249,15 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: out
:
$c <<EOI && $b
+ $* -o foo -o bar >>?EOO
+ foo
+ bar
+ EOO
+ EOI
+
+ : out-alias
+ :
+ $c <<EOI && $b
$* -o foo -o bar >>EOO
foo
bar
@@ -227,6 +267,15 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: err
:
$c <<EOI && $b
+ $* -e foo -e bar 2>>?EOO
+ foo
+ bar
+ EOO
+ EOI
+
+ : err-alias
+ :
+ $c <<EOI && $b
$* -e foo -e bar 2>>EOO
foo
bar
@@ -236,6 +285,18 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: inout
:
$c <<EOI && $b
+ $* -i 1 <<=EOF >>?EOO
+ foo
+ bar
+ EOF
+ foo
+ bar
+ EOO
+ EOI
+
+ : inout-alias
+ :
+ $c <<EOI && $b
$* -i 1 <<EOF >>EOO
foo
bar
@@ -248,6 +309,19 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: inerr
:
$c <<EOI && $b
+ $* -i 2 <<=EOF 2>>?EOE
+ foo
+ bar
+ EOF
+ foo
+ bar
+ EOE
+ EOI
+
+
+ : inerr-alias
+ :
+ $c <<EOI && $b
$* -i 2 <<EOF 2>>EOE
foo
bar
@@ -260,6 +334,14 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: empty
:
$c <<EOI && $b
+ $* -i 1 <<=EOF >>?EOO
+ EOF
+ EOO
+ EOI
+
+ : empty-alias
+ :
+ $c <<EOI && $b
$* -i 1 <<EOF >>EOO
EOF
EOO
@@ -268,16 +350,43 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: shared
:
$c <<EOI && $b
+ $* -i 1 <<=EOF >>?EOF
+ foo
+ bar
+ EOF
+ EOI
+
+ : shared-alias
+ :
+ $c <<EOI && $b
$* -i 1 <<EOF >>EOF
foo
bar
EOF
EOI
+ : shared-in-alias
+ :
+ $c <<EOI && $b
+ $* -i 1 <<EOF >>?EOF
+ foo
+ bar
+ EOF
+ EOI
+
+ : shared-out-alias
+ :
+ $c <<EOI && $b
+ $* -i 1 <<=EOF >>EOF
+ foo
+ bar
+ EOF
+ EOI
+
: extra-newline
:
$c <<EOI && $b
- $* -i 1 <<EOF >>EOO
+ $* -i 1 <<=EOF >>?EOO
EOF
@@ -287,7 +396,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: no-newline
:
$c <<EOI && $b
- $* -i 1 <<:EOF >>:EOO
+ $* -i 1 <<=:EOF >>?:EOO
foo
EOF
foo
@@ -297,7 +406,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: no-newline-fail1
:
$c <<EOI && $b 2>>~/EOE/ != 0
- $* -i 1 <<:EOF >>EOO
+ $* -i 1 <<=:EOF >>?EOO
foo
EOF
foo
@@ -314,7 +423,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: no-newline-fail2
:
$c <<EOI && $b 2>>~/EOE/ != 0
- $* -i 1 <<EOF >>:EOO
+ $* -i 1 <<=EOF >>?:EOO
foo
EOF
foo
@@ -331,7 +440,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: no-newline-empty
:
$c <<EOI && $b
- $* -i 1 <<:EOF >>:EOO
+ $* -i 1 <<=:EOF >>?:EOO
EOF
EOO
EOI
@@ -339,7 +448,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: no-newline-extra-newline
:
$c <<EOI && $b
- $* -i 1 <<:EOF >>:EOO
+ $* -i 1 <<=:EOF >>?:EOO
EOF
@@ -349,7 +458,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: merge
:
$c <<EOI && $b
- $* -i 1 <<EOF -e baz >>EOO 2>&1
+ $* -i 1 <<=EOF -e baz >>?EOO 2>&1
foo
bar
EOF
@@ -387,6 +496,14 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: in
:
$c <<"EOI" && $b
+ \$* -i 1 <<=/EOF >>>?'foo$ps'
+ foo/
+ EOF
+ EOI
+
+ : in-alias
+ :
+ $c <<"EOI" && $b
\$* -i 1 <</EOF >'foo$ps'
foo/
EOF
@@ -395,6 +512,14 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: out
:
$c <<"EOI" && $b
+ \$* -i 1 <<<='foo$ps' >>?/EOO
+ foo/
+ EOO
+ EOI
+
+ : out-alias
+ :
+ $c <<"EOI" && $b
\$* -i 1 <'foo$ps' >>/EOO
foo/
EOO
@@ -403,6 +528,14 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: err
:
$c <<"EOI" && $b
+ \$* -i 2 <<<='foo$ps' 2>>?/EOE
+ foo/
+ EOE
+ EOI
+
+ : err-alias
+ :
+ $c <<"EOI" && $b
\$* -i 2 <'foo$ps' 2>>/EOE
foo/
EOE
@@ -419,7 +552,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: match
:
$c <<EOI && $b
- $* -o foo -o foo -o bar >>~/EOO/i
+ $* -o foo -o foo -o bar >>?~/EOO/i
/FO*/*
bar
/*
@@ -429,7 +562,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: match-empty
:
$c <<EOI && $b
- $* >>:~/EOO/
+ $* >>?:~/EOO/
/.{0}
EOO
EOI
@@ -437,7 +570,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: shared
:
$c <<EOI && $b
- $* -o foo -e foo >>~/EOF/ 2>>~/EOF/
+ $* -o foo -e foo >>?~/EOF/ 2>>?~/EOF/
foo
EOF
EOI
@@ -445,7 +578,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: mismatch
:
$c <<EOI && $b 2>>/~%EOE%d != 0
- $* -o foo >>~/EOO/
+ $* -o foo >>?~/EOO/
bar
EOO
EOI
@@ -459,7 +592,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
: mismatch-icase
:
$c <<EOI && $b 2>>/~%EOE%d != 0
- $* -o foo >>~/EOO/i
+ $* -o foo >>?~/EOO/i
bar
EOO
EOI
@@ -479,6 +612,13 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
:
$c <<EOI && $b
$* -o foo >=out;
+ $* -i 1 <=out >foo
+ EOI
+
+ : in-alias
+ :
+ $c <<EOI && $b
+ $* -o foo >=out;
$* -i 1 <<<out >foo
EOI
@@ -490,6 +630,17 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
$c <<EOI && $b
$* -o foo >=out;
$* -e bar 2>+out;
+ $* -i 1 <<EOF >?out
+ foo
+ bar
+ EOF
+ EOI
+
+ : match-alias
+ :
+ $c <<EOI && $b
+ $* -o foo >=out;
+ $* -e bar 2>+out;
$* -i 1 <<EOF >>>out
foo
bar
@@ -500,7 +651,7 @@ psr = ($cxx.target.class != 'windows' ? '/' : '\\') # Path separator in regex.
:
$c <<EOI && $b 2>>/~%EOE%d != 0
$* -o foo >=out;
- $* -o bar >>>out
+ $* -o bar >?out
EOI
%testscript:2: error: ../../../../../driver(.exe)? stdout doesn't match expected%
info: stdout: test/1/stdout-2