summaryrefslogtreecommitdiff
path: root/Test
diff options
context:
space:
mode:
Diffstat (limited to 'Test')
-rw-r--r--Test/A01grammar.ztst55
-rw-r--r--Test/A02alias.ztst7
-rw-r--r--Test/A04redirect.ztst5
-rw-r--r--Test/A05execution.ztst23
-rw-r--r--Test/A06assign.ztst28
-rw-r--r--Test/A08time.ztst100
-rw-r--r--Test/B01cd.ztst14
-rw-r--r--Test/B02typeset.ztst28
-rw-r--r--Test/B03print.ztst5
-rw-r--r--Test/B04read.ztst10
-rw-r--r--Test/C01arith.ztst8
-rw-r--r--Test/C02cond.ztst8
-rw-r--r--Test/C03traps.ztst228
-rw-r--r--Test/C04funcdef.ztst20
-rw-r--r--Test/D01prompt.ztst20
-rw-r--r--Test/D02glob.ztst26
-rw-r--r--Test/D04parameter.ztst137
-rw-r--r--Test/D06subscript.ztst14
-rw-r--r--Test/D07multibyte.ztst30
-rw-r--r--Test/D08cmdsubst.ztst8
-rw-r--r--Test/D09brace.ztst7
-rw-r--r--Test/D10nofork.ztst515
-rw-r--r--Test/E01options.ztst65
-rw-r--r--Test/K01nameref.ztst940
-rw-r--r--Test/K02parameter.ztst154
-rw-r--r--Test/README1
-rw-r--r--Test/V07pcre.ztst62
-rw-r--r--Test/V10private.ztst262
-rw-r--r--Test/V12zparseopts.ztst206
-rw-r--r--Test/X02zlevi.ztst18
-rw-r--r--Test/X03zlebindkey.ztst33
-rw-r--r--Test/X04zlehighlight.ztst18
-rw-r--r--Test/X05zleincarg.ztst562
-rw-r--r--Test/Y01completion.ztst106
-rw-r--r--Test/comptest8
-rw-r--r--Test/runtests.zsh1
-rwxr-xr-xTest/ztst.zsh31
37 files changed, 3611 insertions, 152 deletions
diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst
index 0312fe94e..660602caf 100644
--- a/Test/A01grammar.ztst
+++ b/Test/A01grammar.ztst
@@ -399,13 +399,6 @@
>This is name2
>This is still name2
- (time cat) >&/dev/null
-0:`time' keyword (status only)
-
- TIMEFMT='%E %mE %uE %* %m%mm %u%uu'; time (:)
-0:`time' keyword with custom TIMEFMT
-*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us %\* %m%mm %u%uu
-
if [[ -f foo && -d . && -n $ZTST_testdir ]]; then
true
else
@@ -970,3 +963,51 @@ F:its expectations.
0:Non-interactive shell command input is line buffered
>Value is first
>Value is second
+
+ fn() {
+ ! false
+ }
+0:! inverts the status of implicit return
+
+ fn () {
+ false
+ ! return
+ }
+ fn
+1:! does not affect return status of explicit return
+
+ msg=unset
+ for x in 1 2 3 4 5; do
+ continue && msg=set && print Not executed
+ print Not executed, neither.
+ done
+ print $msg
+0:continue causes immediate continuation
+>unset
+
+ msg=unset
+ () {
+ return && msg=set && print Not executed
+ print Not executed, not nor neither.
+ }
+ print $msg
+0:return causes immediate return
+>unset
+
+ msg=unset
+ for x in 1 2 3 4 5; do
+ ! continue || msg=set && print Not executed
+ print Not executed, neither.
+ done
+ print $msg
+0:! continue causes immediate continuation
+>unset
+
+ msg=unset
+ () {
+ ! return || msg=set && print Not executed
+ print Not executed, not nor neither.
+ }
+ print $msg
+0:! return causes immediate return
+>unset
diff --git a/Test/A02alias.ztst b/Test/A02alias.ztst
index ca415fa39..1c6969e74 100644
--- a/Test/A02alias.ztst
+++ b/Test/A02alias.ztst
@@ -123,7 +123,12 @@
eval 'badalias() { print does not work; }')
1:ALIAS_FUNC_DEF off by default.
?(eval):1: defining function based on alias `badalias'
-?(eval):1: parse error near `()'
+
+ (alias firstalias=notacommand
+ alias secondalias=firstalias
+ eval 'secondalias() { print does not work either; }')
+1:ALIAS_FUNC_DEF reports original alias if multiple
+?(eval):1: defining function based on alias `secondalias'
(alias goodalias=isafunc
setopt ALIAS_FUNC_DEF
diff --git a/Test/A04redirect.ztst b/Test/A04redirect.ztst
index 17f6dfa29..dc62efab3 100644
--- a/Test/A04redirect.ztst
+++ b/Test/A04redirect.ztst
@@ -3,9 +3,8 @@
%prep
mkdir redir.tmp && cd redir.tmp
- myfd=99
- (echo >&$myfd) 2>msg
- bad_fd_msg="${$(<msg)##*:}"
+ bad_fd_msg="${$( { exec 9>&-; echo >&9 } 2>&1)##*:}"
+ [[ -n "$bad_fd_msg" ]]
%test
diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst
index d95ee363c..07a24f9c8 100644
--- a/Test/A05execution.ztst
+++ b/Test/A05execution.ztst
@@ -2,7 +2,7 @@
storepath=($path)
- mkdir command.tmp command.tmp/dir1 command.tmp/dir2
+ mkdir command.tmp command.tmp/dir{1,2} command.tmp/{+,-}dir
cd command.tmp
@@ -21,7 +21,10 @@
print '#!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnyyy' >tstcmd-interp-too-long
print "#!${sh}\necho should not execute; exit 1" >xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn
- chmod 755 tstcmd dir1/tstcmd dir2/tstcmd
+ print 'echo no shebang in -dir' > -dir/tstcmd
+ print 'echo no shebang in +dir' > +dir/tstcmd
+
+ chmod 755 tstcmd dir{1,2}/tstcmd ./{-,+}dir/tstcmd
chmod 755 tstcmd-slashless tstcmd-arg tstcmd-interp-too-long
chmod 755 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn
@@ -396,6 +399,13 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline
# TBD: the 0 above is believed to be bogus and should also be turned
# into 127 when the ccorresponding bug is fixed in the main shell.
+ sleep 2 & pid=$!
+ kill -STOP $pid
+ sleep 1
+ kill -CONT $pid
+ wait $pid
+0:wait for stopped and continued process
+
# Without the outer subshell, the test harness reports the pre-46060 behaviour
# as "skipped" rather than "failed".
(( exit 130 ) | { sleep 1; echo hello })
@@ -415,3 +425,12 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline
(exit 4); repeat 0 do done
0:'repeat 0' resets lastval
+ -dir/tstcmd
+ +dir/tstcmd
+ PATH=-dir tstcmd
+ PATH=+dir tstcmd
+0:shebang-less scripts are to be run by sh even when their file paths start with - or + (workers/52515)
+>no shebang in -dir
+>no shebang in +dir
+>no shebang in -dir
+>no shebang in +dir
diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst
index f89edb888..9f779b9a8 100644
--- a/Test/A06assign.ztst
+++ b/Test/A06assign.ztst
@@ -296,13 +296,26 @@
# tests of var+=(array)
+ a=
+ a+=(1 2 3)
+ print "${(q@)a}"
+0:add array to empty parameter
+>'' 1 2 3
+
unset a
a+=(1 2 3)
- print -l $a
+ print "${(q@)a}"
0:add array to unset parameter
->1
->2
->3
+>1 2 3
+
+ () {
+ setopt localoptions typeset_to_unset
+ typeset a
+ a+=(1 2 3)
+ print "${(q@)a}"
+ }
+0:add array to declared unset parameter
+>1 2 3
a=(a)
a+=(b)
@@ -730,3 +743,10 @@
print $a
0:overwrite [2] character (string: "") with "xx"
>xx
+
+ ( sleep 1 &
+ x[1]=$!
+ typeset -p x
+ )
+0:regression workers/53033: assigning $! to array element
+*>typeset -g -a x=\( <-> \)
diff --git a/Test/A08time.ztst b/Test/A08time.ztst
new file mode 100644
index 000000000..071038d1f
--- /dev/null
+++ b/Test/A08time.ztst
@@ -0,0 +1,100 @@
+#
+# This file contains tests for the "time" reserved word
+#
+
+%prep
+
+ unset TIMEFMT
+
+%test
+
+ (time cat) >&/dev/null
+0:`time' keyword (status only)
+
+ ( TIMEFMT='%E %mE %uE %nE %* %m%mm %u%uu %n%nn'; time (:) )
+0:`time' keyword with custom TIMEFMT
+*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us [0-9]##ns %\* %m%mm %u%uu %n%nn
+
+ ( TIMEFMT='x %U %S %E'; time (:) )
+0:TIMEFMT %[USE] use centisecond precision
+*?x( <0-9>.<00-99>s)(#c3)
+
+ ( TIMEFMT='x %*U %*S %*E'; time (:) )
+0:TIMEFMT %*[USE] use millisecond precision
+*?x( <0-9>.<000-999>)(#c3)
+
+ ( TIMEFMT='%nU %nS'; time (read -k3 -t0.1) )
+1:TIMEFMT %nU and %nS are limited to microsecond precision
+*?(0|[1-9][0-9]#000)ns (0|[1-9][0-9]#000)ns
+
+# SECONDS (after - before) must be greater than the elapsed time, but not much
+# greater. 25% was picked arbitrarily as something that hopefully will prevent
+# the test from failing on slow machines
+ (
+ typeset -F SECONDS
+ TIMEFMT=%nE
+ a=$SECONDS
+ t=$( (time (read -k3 -t0.1)) 2>&1 )
+ b=$SECONDS
+ s=$(( b - a ))
+ t=$(( ${t%ns}.0 / 10**9 ))
+ echo $s $t $(( s > t )) $(( t > s - (s * 0.25) ))
+ )
+0:`time' elapsed time matches SECONDS
+*>[0-9.]## [0-9.]## 1 1
+
+# Again, the wide range here is an attempt to prevent this test from failing on
+# slow machines. We don't care about the exact time, just that it's vaguely sane
+# and that each representation has the same basis
+ ( TIMEFMT='%E %mE %uE %nE %*E'; time (read -k3 -t0.1) )
+1:TIMEFMT elapsed time values
+*?0.<10-50>s <10-500>ms <100000-500000>us <100000000-500000000>ns 0.<100-500>
+
+ time x=1
+0:`time' simple assignment
+*?shell*
+*?children*
+
+ time x=$(date)
+0:`time' assignment with external command
+*?shell*
+*?children*
+
+ x=0; time for ((i=1; i<=10000; ++i)); do ((x+=i)); done; echo $x
+0:`time' for-loop with arithmetic condition
+>50005000
+*?shell*
+*?children*
+
+ time echo $(x=0;for ((i=0; i<=100000; ++i)); do ((x+=i)); done; echo $x)
+0:`time' of a builtin with argument command substitution
+>5000050000
+*?shell*
+*?children*
+
+ time cat <(x=0;for ((i=0; i<=100000; ++i)); do ((x+=i)); done; echo $x)
+0:`time' of external command with process substitution
+>5000050000
+*?*user*system*cpu*total
+
+ print -u $ZTST_fd 'This test takes 2 seconds'
+ time builtin nonesuch $(sleep 2)
+1:`time' of nonexistent builtin with command substitution
+*?*: no such builtin: nonesuch
+*?shell*
+*?children*
+
+ time /no/such/commmand
+127:`time' of nonexistent external
+*?*no such file or directory: /no/such/commmand
+*?*user*system*cpu*total
+
+ ( setopt errexit; time false; print notreached )
+1:`time' of failed builtin with errexit
+*?shell*
+*?children*
+
+ ( setopt errexit; time expr 0; print notreached )
+1:`time' of failed external with errexit
+>0
+*?*user*system*cpu*total
diff --git a/Test/B01cd.ztst b/Test/B01cd.ztst
index bc6757549..e31341478 100644
--- a/Test/B01cd.ztst
+++ b/Test/B01cd.ztst
@@ -57,13 +57,13 @@
# subsequent whitespace being significant; lines are not subject to any
# substitution unless the `q' flag (see below) is set.
#
-# Each line of a '>' and '?' chunk may be preceded by a '*', so the line
-# starts '*>' or '*?'. This signifies that for any line with '*' in front
-# the actual output should be pattern matched against the corresponding
-# lines in the test output. Each line following '>' or '?' must be a
-# valid pattern, so characters special to patterns such as parentheses
-# must be quoted with a backslash. The EXTENDED_GLOB option is used for
-# all such patterns.
+# If any '>' or '?' line in a chunk is preceded by a '*', all lines in
+# that chunk with the same symbol are pattern matched against the
+# corresponding lines in the test output. For example, a '*>' anywhere
+# in the chunk causes all other '>' lines to use pattern matching.
+# Each line following '>' or '?' must be a valid pattern, so characters
+# special to patterns such as parentheses must be quoted with a
+# backslash. The EXTENDED_GLOB option is used for all such patterns.
#
# Each chunk of indented code is to be evaluated in one go and is to
# be followed by a line starting (in the first column) with
diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst
index 8b3988151..914eea92b 100644
--- a/Test/B02typeset.ztst
+++ b/Test/B02typeset.ztst
@@ -311,7 +311,7 @@
print $OUTER
0:Export of tied parameters
>i:n:n:e:r
->typeset -xT OUTER outer=( i n n e r )
+>local -xT OUTER outer=( i n n e r )
>typeset -aT OUTER outer=( i n n e r )
>OUTER=i:n:n:e:r
>outer=( i n n e r )
@@ -959,6 +959,20 @@
> [three]=''
>)
+ () {
+ local -h status
+ typeset -p status
+ }
+0:parameter hiding preserved by "typeset -p"
+>typeset -h status=''
+
+ () {
+ local status
+ typeset -p status
+ }
+0:read-only special params are output when localized
+>typeset -i10 -r status=0
+
(export PATH MANPATH
path=(/bin)
MANPATH=/
@@ -1085,12 +1099,12 @@
}
0: no array/hash in POSIX export/readonly -p
>zsh:
->typeset -arx zsh_exported_readonly_array=( 2 )
->typeset -Arx zsh_exported_readonly_hash=( [3]=3 )
->typeset -rx zsh_exported_readonly_scalar=1
->typeset -arx zsh_exported_readonly_array=( 2 )
->typeset -Arx zsh_exported_readonly_hash=( [3]=3 )
->typeset -rx zsh_exported_readonly_scalar=1
+>local -arx zsh_exported_readonly_array=( 2 )
+>local -Arx zsh_exported_readonly_hash=( [3]=3 )
+>local -rx zsh_exported_readonly_scalar=1
+>local -arx zsh_exported_readonly_array=( 2 )
+>local -Arx zsh_exported_readonly_hash=( [3]=3 )
+>local -rx zsh_exported_readonly_scalar=1
>sh:
>export zsh_exported_readonly_scalar=1
>readonly zsh_exported_readonly_scalar=1
diff --git a/Test/B03print.ztst b/Test/B03print.ztst
index 4d2cf9764..93a9669b0 100644
--- a/Test/B03print.ztst
+++ b/Test/B03print.ztst
@@ -305,8 +305,9 @@
foo+=$'\tone\ttwo\tthree\tfour\n'
foo+=$'\t\tone\t\ttwo\t\tthree\t\tfour'
foo+='\0' # regression test for multibyte tab expand
- print -x4 $foo | tr '\0' Z # avoid raw nul byte in expected output below
- print -X4 $foo | tr '\0' Z
+ # avoid raw nul byte in expected output below
+ print ${"$(print -x4 $foo)"/$'\0'/Z}
+ print ${"$(print -X4 $foo)"/$'\0'/Z}
0:Tab expansion by print
>one two three four
> one two three four
diff --git a/Test/B04read.ztst b/Test/B04read.ztst
index 25c3d4173..14bdaeef5 100644
--- a/Test/B04read.ztst
+++ b/Test/B04read.ztst
@@ -82,6 +82,16 @@
>Testing the
>null hypothesis
+ read -ed '' <<<$'one\0two'
+0:empty delimiter terminates at nulls
+>one
+
+ print -n $'first line\x80second line\x80' |
+ while read -d $'\x80' line; do print $line; done
+0:read with a delimiter >= 0x80
+>first line
+>second line
+
# Note that trailing NULLs are not stripped even if they are in
# $IFS; only whitespace characters contained in $IFS are stripped.
print -n $'Aaargh, I hate nulls.\0\0\0' | read line
diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst
index d0092fefa..ba9c65e5b 100644
--- a/Test/C01arith.ztst
+++ b/Test/C01arith.ztst
@@ -251,6 +251,14 @@
>5000
>255
+ set -- {101..120}
+ _10=42
+ echo $_10 : $1_0
+ echo $(( _10 )) : $(( 1_0 ))
+0:underscores in front of a numeric identifier is not a math constant
+>42 : 101_0
+>42 : 10
+
# Force floating point.
for expr in "3/4" "0x100/0x200" "0x30/0x10"; do
print $(( $expr ))
diff --git a/Test/C02cond.ztst b/Test/C02cond.ztst
index 4366b4142..daea5b4f8 100644
--- a/Test/C02cond.ztst
+++ b/Test/C02cond.ztst
@@ -111,10 +111,6 @@
if (( EUID == 0 )); then
print -u$ZTST_fd 'Warning: Not testing [[ ! -r file ]] (root reads anything)'
[[ -r zerolength && -r unmodish ]]
- elif [[ $OSTYPE = cygwin ]]; then
- print -u$ZTST_fd 'Warning: Not testing [[ ! -r file ]]
- (all files created by user may be readable)'
- [[ -r zerolength ]]
else
[[ -r zerolength && ! -r unmodish ]]
fi
@@ -148,9 +144,7 @@
print -ru $ZTST_fd 'This test may take two seconds...'
touch $newnewnew
- if [[ $OSTYPE == "cygwin" ]]; then
- ZTST_skip="[[ -N file ]] not supported on Cygwin"
- elif (( isnfs )); then
+ if (( isnfs )); then
ZTST_skip="[[ -N file ]] not supported with NFS"
elif ! zmodload -F zsh/stat b:zstat 2> /dev/null; then
ZTST_skip='[[ -N file ]] not tested; zsh/stat not available'
diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst
index f120809a7..87b7fd1f7 100644
--- a/Test/C03traps.ztst
+++ b/Test/C03traps.ztst
@@ -670,6 +670,22 @@ F:Must be tested with a top-level script rather than source or function
>before-out
>before-in
+ (set -o err_return
+ fn() {
+ print before-in
+ { false; true } && true
+ print after-in
+ }
+ print before-out
+ fn && true
+ print after-out
+ )
+0:ERR_RETURN not triggered on LHS of "&&" in function on LHS of "&&" (regression test)
+>before-out
+>before-in
+>after-in
+>after-out
+
mkdir -p zdotdir
print >zdotdir/.zshenv '
setopt norcs errreturn
@@ -713,7 +729,7 @@ F:Must be tested with a top-level script rather than source or function
fi
}
fn() {
- setopt err_return
+ setopt localoptions err_return
fn2 || true
}
fn
@@ -721,6 +737,37 @@ F:Must be tested with a top-level script rather than source or function
>Good
(setopt err_exit
+ ! true
+ print OK
+ )
+0:ERR_EXIT not triggered by "! true"
+>OK
+
+ (setopt err_exit
+ fn() { true }
+ ! fn
+ print OK
+ )
+0:ERR_EXIT not triggered by "! fn"
+>OK
+
+ (setopt err_exit
+ false && true
+ print OK
+ )
+0:ERR_EXIT not triggered by "false && true"
+>OK
+
+ (setopt err_exit
+ fn() {
+ false && true
+ }
+ fn
+ print OK
+ )
+1:ERR_EXIT not triggered by "false && true" but by return from fn
+
+ (setopt err_exit
for x in y; do
false && true
done
@@ -730,14 +777,15 @@ F:Must be tested with a top-level script rather than source or function
>OK
(setopt err_exit
- integer x=0
- while (( ! x++ )); do
- false && true
- done
+ fn() {
+ for x in y; do
+ false && true
+ done
+ }
+ fn
print OK
)
-0:ERR_EXIT not triggered by status 1 at end of while
->OK
+1:ERR_EXIT not triggered by status 1 at end of for but by return from fn
(setopt err_exit
repeat 1; do
@@ -749,6 +797,17 @@ F:Must be tested with a top-level script rather than source or function
>OK
(setopt err_exit
+ fn() {
+ repeat 1; do
+ false && true
+ done
+ }
+ fn
+ print OK
+ )
+1:ERR_EXIT not triggered by status 1 at end of repeat but by return from fn
+
+ (setopt err_exit
if true; then
false && true
fi
@@ -758,6 +817,71 @@ F:Must be tested with a top-level script rather than source or function
>OK
(setopt err_exit
+ fn() {
+ if true; then
+ false && true
+ fi
+ }
+ fn
+ print OK
+ )
+1:ERR_EXIT not triggered by status 1 at end of if but by return from fn
+
+ (setopt err_exit
+ loop=true
+ while print COND; $loop; do
+ loop=false
+ false && true
+ done
+ print OK
+ )
+0:ERR_EXIT not triggered by status 1 at end of while
+>COND
+>COND
+>OK
+
+ (setopt err_exit
+ fn() {
+ loop=true
+ while print COND; $loop; do
+ loop=false
+ false && true
+ done
+ }
+ fn
+ print OK
+ )
+1:ERR_EXIT not triggered by status 1 at end of while but by return from fn
+>COND
+>COND
+
+ (setopt err_exit
+ {
+ false && true
+ } always {
+ print ALWAYS
+ }
+ print OK
+ )
+0:ERR_EXIT not triggered by status 1 at end of always
+>ALWAYS
+>OK
+
+ (setopt err_exit
+ fn() {
+ {
+ false && true
+ } always {
+ print ALWAYS
+ }
+ }
+ fn
+ print OK
+ )
+1:ERR_EXIT not triggered by status 1 at end of always but by return from fn
+>ALWAYS
+
+ (setopt err_exit
{
false && true
}
@@ -766,6 +890,17 @@ F:Must be tested with a top-level script rather than source or function
0:ERR_EXIT not triggered by status 1 at end of { }
>OK
+ (setopt err_exit
+ fn() {
+ {
+ false && true
+ }
+ }
+ fn
+ print OK
+ )
+1:ERR_EXIT not triggered by status 1 at end of { } but by return from fn
+
unsetopt err_exit err_return
(setopt err_exit
for x in y; do
@@ -819,6 +954,74 @@ F:Must be tested with a top-level script rather than source or function
1:ERR_EXIT triggered by status 1 at end of anon func
>Still functioning
+ (setopt err_exit
+ loop=true; while print loop $? >&2; $loop; do loop=false; false && true; done
+ print done $? >&2
+ )
+0: ERR_EXIT neither triggered inside loop nor triggered by while statement
+?loop 0
+?loop 1
+?done 1
+
+ (setopt err_exit
+ { loop=true; while print loop $? >&2; $loop; do loop=false; false && true; done } || false
+ print done $? >&2
+ )
+1: ERR_EXIT not triggered inside loop but triggered by rhs of ||
+?loop 0
+?loop 1
+
+ (setopt err_exit
+ eval 'loop=true; while print loop $? >&2; $loop; do loop=false; false && true; done'
+ print done $? >&2
+ )
+1: ERR_EXIT not triggered inside loop but triggered by eval
+?loop 0
+?loop 1
+
+ (setopt err_exit
+ source <(echo 'loop=true; while print loop $? >&2; $loop; do loop=false; false && true; done')
+ print done $? >&2
+ )
+1: ERR_EXIT not triggered inside loop but triggered by source
+?loop 0
+?loop 1
+
+ (setopt err_exit
+ v=$(loop=true; while print loop $? >&2; $loop; do loop=false; false && true; done)
+ print done $? >&2
+ )
+1: ERR_EXIT not triggered inside loop but triggered by command substitution
+?loop 0
+?loop 1
+
+ ( set -e; true && {false; echo NOT REACHED} )
+ ( trap "print Trapped!" ERR; true && {false} )
+ ( trap "print Trapped!" ERR; true && if true; then false; fi )
+ ( trap "print Trapped!" ERR; true && {false} always {true} )
+ ( true && (set -e; false; echo NOT REACHED) )
+ ( true && (trap "print Trapped!" ERR; false) )
+ ( true && { set -e; false; echo NOT REACHED } )
+ ( true && { trap "print Trapped!" ERR; false } )
+ ( set -e; true && (false; echo one) || echo two )
+ ( set -e; true && { false; echo one; } || echo two )
+0:ERR_EXIT is triggered by last command in an AND-OR list
+>Trapped!
+>Trapped!
+>Trapped!
+>Trapped!
+>Trapped!
+>one
+>one
+
+ ( set -o ERR_RETURN; f() { false; echo NOT REACHED; }; f || true; echo OK )
+ ( set -o ERR_RETURN; f() { true && false; echo NOT REACHED; }; f || true; echo OK )
+ ( set -o ERR_RETURN; f() { true && { false }; echo NOT REACHED; }; f || true; echo OK )
+0:ERR_RETURN is triggered in function calls on the left of an AND-OR
+>OK
+>OK
+>OK
+
if zmodload zsh/system 2>/dev/null; then
(
trap 'echo TERM; exit 2' TERM
@@ -907,6 +1110,17 @@ F:Must be tested with a top-level script rather than source or function
>trap1
# As of 5.7.1-test-2, the output was "out1 fn1 trap1 fn2" (on separate lines).
+ TRAPEXIT() { echo This is TRAPEXIT; }
+ TRAPEXIT
+ TRAPEXIT
+ TRAPEXIT
+0:No memory problems with explicit call to TRAPEXIT.
+>This is TRAPEXIT
+>This is TRAPEXIT
+>This is TRAPEXIT
+>This is TRAPEXIT
+# Three explicit calls, one implicit call at function exit.
+
%clean
rm -f TRAPEXIT
diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst
index af469c527..b8509b25c 100644
--- a/Test/C04funcdef.ztst
+++ b/Test/C04funcdef.ztst
@@ -53,6 +53,26 @@
>b: redirection
>a: redirection
+ define_multiple() {
+ fn1 fn2 fn3() {
+ print This is $0
+ }
+ }
+ which -x2 define_multiple
+ define_multiple
+ fn1
+ fn2
+ fn3
+0: Safe output of multiple function definitions
+>define_multiple () {
+> function fn1 fn2 fn3 {
+> print This is $0
+> }
+>}
+>This is fn1
+>This is fn2
+>This is fn3
+
functions -M m1
m1() { (( $# )) }
print $(( m1() ))
diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst
index 6879e6fd1..f42e19714 100644
--- a/Test/D01prompt.ztst
+++ b/Test/D01prompt.ztst
@@ -70,6 +70,13 @@
>true
>false
+ sec=$SECONDS
+ eval "print -P '%(${sec}S.true.false)'"
+ eval "print -P '%($((sec+30))S.true.false)'"
+0:ternary prompt escape with test character S
+>true
+>false
+
print -P 'start %10<...<truncated at 10%<< Not truncated%3< ...<Not shown'
print -P 'start %10>...>truncated at 10%>> Not truncated%3> ...>Not shown'
0:prompt truncation
@@ -258,6 +265,19 @@
fi
0:Equivalence of terminal colour settings (background colour)
+ A1=${(%):-%s}
+ A2=${(%):-%u}
+ A3=${(%):-%s%u%s}
+ [[ $A3 = $A1$A2 ]]
+0:Attribute optimisation - preserve initial disabling of attribute but drop useless later one
+
+ : ${(%):-%K{blue}}
+ A1="${(%):-%b}x"
+ : ${(%):-%k}
+ A2="${(%):-%b}x"
+ [[ $A1 = $A2 && -n $A1 && -n $A2 ]]
+0:Don't restore attributes from earlier substitution after disabling bold
+
(RPS1=foo; echo $RPS1 $RPROMPT)
(RPS2=bar; echo $RPS2 $RPROMPT2)
-fD:RPS1 and RPROMPT are aliases (regression from 5.0.6) (workers/49600)
diff --git a/Test/D02glob.ztst b/Test/D02glob.ztst
index 850a535e5..4d88e5c27 100644
--- a/Test/D02glob.ztst
+++ b/Test/D02glob.ztst
@@ -817,6 +817,32 @@
*>*/glob.tmp/(flip|flop)
*>*/glob.tmp/(flip|flop)/trailing/components
+# The following set test an obscure problem with branches followed by
+# exclusions that shows up when the exclusion matches against
+# something other than the complete test string, hence the complicated
+# double negative.
+ [[ ab = (|a*)~^(*b) ]]
+0:Regression test for exclusion after branches: empty first alternative
+
+ [[ ab = (b|a*)~^(*b) ]]
+0:Regression test for exclusion after branches: non-empty first alternative
+
+ [[ ab = (b*|a*)~^(*b) ]]
+0:Regression test for exclusion after branches: full length first alternative
+
+# Corresponding tests where the exclusion should succeed, so the
+# match fails. It's hard to know how to provoke bugs here...
+ [[ abc = (|a*)~^(*b) ]]
+1:Regression test for exclusion after branches: failure case 1
+
+ [[ abc = (b|a*)~^(*b) ]]
+1:Regression test for exclusion after branches: failure case 2
+
+ [[ abc = (b*|a*)~^(*b) ]]
+1:Regression test for exclusion after branches: failure case 3
+
+# Careful: extendedglob off from this point.
+
unsetopt extendedglob
print -r -- ${(*)=${(@s.+.):-A+B}/(#b)(?)/-${(L)match[1]} ${match[1]}}
0:the '*' qualfier enables extended_glob for pattern matching
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index 6bf55b4db..ed25fd7a9 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -110,6 +110,11 @@
*>*foo:1: 1: no arguments given
>reached
+ message="expand me and remove quotes"
+ (: ${UNSET_PARAM?$message})
+1:${...?....} performs expansion on the message
+?(eval):2: UNSET_PARAM: expand me and remove quotes
+
print ${set1:+word1} ${set1+word2} ${null1:+word3} ${null1+word4}
print ${unset1:+word5} ${unset1+word6}
0:${...:+...}, ${...+...}
@@ -1217,6 +1222,7 @@
typeset -T STRING string
print $STRING $string
unset string
+ typeset -p string
STRING=x:y:z
print $STRING $string
STRING=a:b
@@ -1544,6 +1550,28 @@
>1
>1
+ # Integer
+ a=$SECONDS
+ sleep 1
+ b=$SECONDS
+ print -r - $a $b $(( b > a ))
+ # Float
+ typeset -F SECONDS
+ a=$SECONDS
+ repeat 10 :
+ b=$SECONDS
+ print -r - $a $b $(( b > a ))
+ # Assignment
+ a=$SECONDS
+ SECONDS=8888
+ repeat 10 :
+ b=$SECONDS
+ print -r - $(( a < 8888 )) $(( b > 8888 ))
+0:SECONDS
+*>[0-9]## [0-9]## 1
+*>[0-9]##.[0-9]## [0-9]##.[0-9]## 1
+*>1 1
+
foo=("|" "?")
[[ "|" = ${(j.|.)foo} ]] && print yes || print no
[[ "|" = ${(j.|.)~foo} ]] && print yes || print no
@@ -2275,6 +2303,27 @@ F:We do not care what $OLDPWD is, as long as it does not cause an error
F:As of this writing, var=$@ and var="$@" with null IFS have unspecified
F:behavior, see http://austingroupbugs.net/view.php?id=888
+ (
+ IFS=$'\x80'
+ if [[ $IFS = $' \t\n\0' ]]; then
+ echo OK # if $'\x80' is illegal (e.g. Linux)
+ else # otherwise (e.g. macOS), it should work as a separator
+ s=$'foo\x80\bar'
+ [[ ${${=s}[1]} = foo ]] && echo OK
+ fi
+ )
+0D:reset IFS to default if it contains illegal character
+>OK
+
+ (
+ unsetopt multibyte
+ IFS=$'\xc3\xa9'
+ s=$'foo\xc3bar\xa9boo'
+ echo ${${=s}[2]}
+ )
+0:eight bit chars in IFS should work if multibute option is off
+>bar
+
() {
setopt localoptions extendedglob
[[ $- = [[:alnum:]]## ]] || print Failed 1
@@ -2302,6 +2351,13 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888
>x
>y
+ a="string"
+ print ${(S)a//#%((#b)(*))/different}
+ print $match[1]
+0:Fully anchored string must be fully searched
+>different
+>string
+
my_width=6
my_index=1
my_options=Option1
@@ -2727,3 +2783,84 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888
1:parameter expansion flags parsing error gives a clue
?(eval):1: error in flags near position 7 in '${(zZ+x+):-}'
+ slash='/'
+ print -r -- x${slash/'/'}y
+0:(users/28784) substituting a single-quoted backslash, part #1: slash
+>xy
+
+ single_quote="'"
+ print -r -- x${single_quote/$'/'}y
+0:(users/28784) substituting a single-quoted backslash, part #2: single quote
+>x'y
+
+ control="foobar"
+ print -r -- x${control/'bar'}y
+0:(users/28784 inspired this) substituting a single-quoted backslash, part #3: control
+>xfooy
+
+ spacestring="string with spaces"
+ print ${spacestring:gs/[[:space:]]/ /}
+ print ${spacestring:g&}
+ print ${spacestring:gS/[[:space:]]//}
+ print ${spacestring:g&}
+0:Different behaviour of :s and :S modifiers
+>string with spaces
+>string with spaces
+>stringwithspaces
+>stringwithspaces
+
+ : ${(#X):-@}
+1:${(#X)...}: bad math expression
+?(eval):1: bad math expression: illegal character: @
+
+ echo a${(#):-@}z
+0:${(#)...}: bad math expression
+>az
+
+ printf "a%sz\n" ${(#):-@}
+0:${(#)...}: bad math expression, printf
+>az
+
+ a=( '1 +' '@' )
+ : ${(#X)a}
+1:${(#X)...}: array of bad math expressions
+?(eval):2: bad math expression: operand expected at end of string
+
+ printf "a%sz\n" ${(#)a}
+0:${(#)...}: array of bad math expressions, printf
+>az
+
+ if [[ ! -o multibyte ]]; then
+ ZTST_skip='(#X) accepts any byte if multibyte is off'
+ else
+ : ${(#X):-0x80}
+ fi
+1:${(#X)...}: out-of-range character
+?(eval):4: character not in range
+
+ [[ ${(#):-0x80} = $'\x80' ]] && echo OK
+0:${(#)...}: out-of-range character
+>OK
+
+ a=( 0x80 0x81 )
+ printf "%s\n" ${(#)a} |
+ while read x; do echo $(( #x )); done
+0:${(#)...}: array of out-of-range characters
+>128
+>129
+
+ if [[ ! -o multibyte ]]; then
+ ZTST_skip='(#X) accepts any byte if multibyte is off'
+ else
+ : ${(#X)a}
+ fi
+1:${(#X)...}: array of out-of-range characters
+?(eval):4: character not in range
+
+ (
+ export ZDOTDIR=${ echo $PWD/'\0360\0237\0224\0256' }
+ $ZTST_testdir/../Src/zsh -c 'echo $ZDOTDIR'
+ )
+0:regression for workers/53179 unicode ZDOTDIR
+F:output ignorable as long as not an error
+*>*
diff --git a/Test/D06subscript.ztst b/Test/D06subscript.ztst
index adbd398c4..57cdc027c 100644
--- a/Test/D06subscript.ztst
+++ b/Test/D06subscript.ztst
@@ -294,3 +294,17 @@ F:Regression test for workers/42297
[[ ${a[$i]} = ${a[i]} ]]
0f:Math evaluation of commas in array subscripts
F:In math, (($i)) should be the same as ((i)), see workers/47748.
+
+ string=$'foo\0bar'
+ echo ${string[(pws:\0:)1]}
+0:Word splitting by NUL
+>foo
+
+ string="a"
+ print ${string[(i)x]}
+ string=""
+ print ${string[(i)x]}
+0:Can check off end of zero length string
+F:Regression test for inconsistency of failed (i) on zero-length string
+>2
+>1
diff --git a/Test/D07multibyte.ztst b/Test/D07multibyte.ztst
index e2e9a25ef..413c4fe73 100644
--- a/Test/D07multibyte.ztst
+++ b/Test/D07multibyte.ztst
@@ -1,19 +1,7 @@
%prep
-# Find a UTF-8 locale.
- setopt multibyte
-# Don't let LC_* override our choice of locale.
- unset -m LC_\*
- mb_ok=
- langs=(en_{US,GB}.{UTF-,utf}8 en.UTF-8
- $(locale -a 2>/dev/null | egrep 'utf8|UTF-8'))
- for LANG in $langs; do
- if [[ é = ? ]]; then
- mb_ok=1
- break;
- fi
- done
- if [[ -z $mb_ok ]]; then
+ LANG=$(ZTST_find_UTF8)
+ if [[ -z $LANG ]]; then
ZTST_unimplemented="no UTF-8 locale or multibyte mode is not implemented"
else
print -u $ZTST_fd Testing multibyte with locale $LANG
@@ -224,6 +212,20 @@
>first
>second
+ read -ed £
+0:read with multibyte delimiter where bytes of delimiter also occur in input
+<one¤twoãthree£four
+>one¤twoãthree
+
+ read -ed $'\xa0' <<<$'first\xa0second'
+0:read delimited by a byte that isn't a valid multibyte character
+>first
+
+ read -ed $'\xc2'
+0:read delimited by a single byte terminates if the byte is part of a multibyte character
+<one£two
+>one
+
(IFS=«
read -d » -A array
print -l $array)
diff --git a/Test/D08cmdsubst.ztst b/Test/D08cmdsubst.ztst
index 04bf698aa..e415831a0 100644
--- a/Test/D08cmdsubst.ztst
+++ b/Test/D08cmdsubst.ztst
@@ -177,3 +177,11 @@
0:Alias expansion needed in parsing substitutions
>hi
>bye
+
+# This should silently print a blank line; the original problem was
+# a parse error as the last character of the unexpanded alias
+# was erased, symptom: "command not found: W"
+ alias WI='while {false}'
+ eval 'echo $(WI blah)'
+0:Aliases with braces in command substitution can cause havoc
+>
diff --git a/Test/D09brace.ztst b/Test/D09brace.ztst
index 580ed430f..961947b67 100644
--- a/Test/D09brace.ztst
+++ b/Test/D09brace.ztst
@@ -116,3 +116,10 @@
print -r {1..10}{..
0:Unmatched braces after matched braces are left alone.
>1{.. 2{.. 3{.. 4{.. 5{.. 6{.. 7{.. 8{.. 9{.. 10{..
+
+ () {
+ setopt localoptions no_multibyte
+ echo -E {$'\x80'..$'\x81'}
+ }
+0:range of 8bit chars, multibyte option unset
+>\M-^@ \M-^A
diff --git a/Test/D10nofork.ztst b/Test/D10nofork.ztst
new file mode 100644
index 000000000..5bb10266f
--- /dev/null
+++ b/Test/D10nofork.ztst
@@ -0,0 +1,515 @@
+# Tests for "nofork" command substitution.
+
+%prep
+ mkdir nofork.tmp
+ touch nofork.tmp/file{1,2}.txt
+
+ purr() { print -r -- "$@" }
+ purl() { print -rl -- "$@" }
+
+%test
+
+ REPLY=OUTER
+ purr ${| REPLY=INNER } $REPLY
+0:Basic substitution and REPLY scoping
+>INNER OUTER
+
+ reply=(x OUTER x)
+ purl ${{reply} reply=(\{ INNER \})} $reply
+0:Basic substitution, brace quoting, and array result
+>{
+>INNER
+>}
+>{
+>INNER
+>}
+
+ () {
+ setopt localoptions ignorebraces
+ purl ${{reply} reply=({ INNER })} $reply
+ }
+0:Basic substitution, ignorebraces, and array result
+>{
+>INNER
+>}
+>{
+>INNER
+>}
+
+ purr ${| REPLY=first}:${| REPLY=second}:$REPLY
+0:re-scoping of REPLY in one statement
+>first:second:OUTER
+
+ purr BEGIN${| printf -v REPLY '%s\n' one two three ; }END
+0:Adjacent words
+>BEGINone
+>two
+>three
+>END
+
+ purr "BEGIN${| printf -v REPLY '%s\n' one two three }END"
+0:Adjacent words and quoting, part 1
+>BEGINone
+>two
+>three
+>END
+
+ purr BEGIN"${| printf -v REPLY '%s\n' one two three }"END
+0:Adjacent words and quoting, part 2
+>BEGINone
+>two
+>three
+>END
+
+ purr BEGIN"${|
+ printf -v REPLY '%s\n'\
+ one two three
+ }"END
+0:Embedded newlines
+>BEGINone
+>two
+>three
+>END
+
+ purr BEGIN"${|
+ printf -v REPLY $'%s\n' one two three
+ }"END
+0:Embedded newlines and $'...'
+>BEGINone
+>two
+>three
+>END
+
+ purl ${| print -v REPLY one word here; setopt shwordsplit }
+ purl ${| print -v REPLY three words here }
+ purl "and ${| print -v REPLY one word here }"
+ unsetopt shwordsplit
+0:test word splitting on result
+F:setting option inside is too late for that substitution
+>one word here
+>three
+>words
+>here
+>and one word here
+
+ (
+ cd nofork.tmp
+ setopt globsubst
+ purr ${| REPLY=f* }
+ purr ${| REPLY=f? }*
+ unsetopt globsubst
+ purr ${| REPLY=f* }
+ purr ${| REPLY=f? }*
+ )
+1:globsubst on result
+>file1.txt file2.txt
+>file1.txt file2.txt
+>f*
+?(eval):8: no matches found: f?*
+
+ purr ${| REPLY=$'trailing newlines remain\n\n' }
+0:newline removal should not occur, part 1
+>trailing newlines remain
+>
+>
+
+ purr ${ echo $'one trailing newline\nremoved\n\n\n' }
+0:newline removal in ${ ... }, zsh mode
+>one trailing newline
+>removed
+>
+>
+>
+
+ () {
+ emulate -L ksh
+ purl ${ echo $'all trailing newlines\nremoved\n\n\n' }
+ purr "${ echo $'all trailing newlines\nremoved\n\n\n' }"
+ }
+0:newline removal in ${ ... }, emulation mode, shwordsplit
+>all
+>trailing
+>newlines
+>removed
+>all trailing newlines
+>removed
+
+ purr "${ echo $'no trailing newlines\nremoved\n\n\n' }"
+0:newline removal should not occur, part 2
+>no trailing newlines
+>removed
+>
+>
+>
+>
+
+ () {
+ purr ${| REPLY=$* ; shift 2 }
+ purr $*
+ } these are arguments
+0:access to context $argv
+>these are arguments
+>arguments
+
+ purr ${:-${| REPLY=${REPLY:-buried}}}
+ purr ${:-"${| REPLY=${REPLY:-more buried}}"}
+0:nofork inside parameter scope
+>buried
+>more buried
+
+ : ${(e):-'${| REPLY=oops'}
+1:unclosed braces are sometimes a bad substitution
+F:This seems silly, but see A01grammar ${(e):-'${'} test
+?(eval):1: bad substitution
+
+ purr ${| REPLY=oops
+1:other times lack of closing brace is merely unexpected
+F:Why not use this error in the previous case as well?
+?(eval):1: closing brace expected
+
+# Next tests check that the PS2 stack is properly managed on error
+
+ purr ${| REPLY=${REPLY:-buried}}}
+1:unbalanced braces, part 0
+?(eval):1: parse error near `}'
+
+ purr ${:-${| REPLY=${REPLY:-buried}}
+1:unbalanced braces, part 1
+?(eval):1: closing brace expected
+
+ purr ${:-"${| REPLY=${REPLY:-more buried}"}
+1:unbalanced braces, part 2
+?(eval):1: unmatched "
+
+ purr ${:-"${| REPLY=${REPLY:-more buried"}}}
+1:unbalanced braces, part 3
+?(eval):1: unmatched "
+
+ purr ${:-"${| REPLY=${REPLY:-more buried}}}"
+1:unbalanced braces, part 4
+?(eval):1: closing brace expected
+
+# Same tests with leading space (future-proofing)
+
+ purr ${ purr ${REPLY:-buried}}}
+1:unbalanced braces, part 0+
+?(eval):1: parse error near `}'
+
+ purr ${:-${ purr ${REPLY:-buried}}
+1:unbalanced braces, part 1+
+?(eval):1: closing brace expected
+
+ purr ${:-"${ purr ${REPLY:-more buried}"}
+1:unbalanced braces, part 2+
+?(eval):1: unmatched "
+
+ purr ${:-"${ purr ${REPLY:-more buried"}}}
+1:unbalanced braces, part 3+
+?(eval):1: unmatched "
+
+ purr ${:-"${ purr ${REPLY:-more buried}}}"
+1:unbalanced braces, part 4+
+?(eval):1: closing brace expected
+
+ purr "${ purr STDOUT }"
+0:capture stdout
+>STDOUT
+>
+
+# end PS2 stack tests
+
+ purr $(purr outside ${| REPLY=inside })
+ purr BEGIN$(purr outside ${| REPLY=inside })END
+ purr "BEGIN$(purr outside ${| REPLY=inside })END"
+ purr outside ${| REPLY=$(purr inside)}
+ purr "outside ${| REPLY=$(purr inside)}"
+0:mixing with forking cmdsubst
+>outside inside
+>BEGINoutside insideEND
+>BEGINoutside insideEND
+>outside inside
+>outside inside
+
+ purr `purr outside ${| REPLY=inside }`
+ purr "outside `purr ${| REPLY=inside }`"
+ purr outside ${| REPLY=`purr inside`}
+ purr "outside ${| REPLY=`purr inside`}"
+ purr outside "`purr ${| REPLY="${:-inside}"}`"
+ purr "outside ${| REPLY=`purr ${:-inside}`}"
+0:mixing with backticks
+>outside inside
+>outside inside
+>outside inside
+>outside inside
+>outside inside
+>outside inside
+
+ purr ${| REPLY=$(( 9 + 17 )) }
+ purr $(( 9 + ${| REPLY=17 } ))
+0:mixing with arithemetic
+>26
+>26
+
+ unset reply
+ purl ${{reply} reply=(1 2 ${| REPLY=3 } 4) }
+ typeset -p reply
+0:array behavior with global assignment
+>1
+>2
+>3
+>4
+>typeset -g -a reply=( 1 2 3 4 )
+
+ unset outer
+ purr "${|
+ outer=OUTER
+ REPLY=INNER
+ return 7
+ OUTER=NOTREACHED
+ } $outer $?"
+0:return statement inside, part 1
+F:status of "print" should hide return
+>INNER OUTER 7
+
+ unset outer
+ outer=${| REPLY=${| return 7}}
+7:return status propages in assignment like $(...)
+
+ unset outer
+ purr "${|
+ outer=OUTER
+ REPLY=INNER
+ return 7
+ OUTER=NOTREACHED
+ } $outer $?"
+ print REACHED $OUTER
+0:return statement inside, part 2
+>INNER OUTER 7
+>REACHED
+
+ unset outer
+ purr "${|
+ # Localoptions needed to avoid breaking test harness?
+ # The setopt command affects surrounding context
+ setopt localoptions errreturn
+ outer=OUTER
+ REPLY=INNER
+ false
+ OUTER=NOTREACHED
+ } $outer $?"
+ print REACHED $OUTER ${options[errreturn]}
+0:errreturn works inside and remains outside
+>INNER OUTER 1
+>REACHED on
+
+ (
+ unset outer
+ purr "${|
+ outer=OUTER
+ REPLY=INNER
+ exit 7
+ OUTER=NOTREACHED
+ } $outer $OUTER $?"
+ print NOT REACHED
+ )
+7:exit statement inside
+
+ (
+ unset outer
+ purr "${|
+ setopt errexit
+ outer=OUTER
+ REPLY=INNER
+ false
+ OUTER=NOTREACHED
+ } $outer $?"
+ print NOT REACHED
+ )
+1:errexit inside
+
+ outer=GLOBAL
+ purr "${|
+ local outer=LOCAL
+ REPLY=INNER
+ } $outer $?"
+0:local declaration inside
+>INNER GLOBAL 0
+
+ unset zz
+ outer=GLOBAL
+ purr "${{zz}
+ local outer=LOCAL
+ zz=NONLOCAL
+ } $outer $?"
+ print $zz
+0:local declaration, global assignment, part 1
+>NONLOCAL GLOBAL 0
+>NONLOCAL
+
+ unset zz
+ outer=GLOBAL
+ purr "${${|
+ local outer=LOCAL
+ zz=NONLOCAL
+ }:-$zz} $outer $?"
+0:local declaration, global assignment, part 2 (evaluation order)
+>NONLOCAL GLOBAL 0
+
+ : ${| fn1() { () { print -v REPLY $'Defined Function' ;} ;} }
+ print "IN${| fn2() { () { print "${:-Second }${|fn1}" ;} ;} }OUT"
+ fn2
+0:function definition, brace nesting, quote nesting
+>INOUT
+>Second Defined Function
+
+ <<-EOF
+ ${| REPLY=$'in a here document\n' }
+ EOF
+0:here-document behavior
+F:Fiddly here to get EOF past the test syntax
+>in a here document
+>
+
+ <<<${| REPLY="in a here string" }
+0:here-string behavior
+>in a here string
+
+ <<<"${ purr $'stdout as a here string' }"
+0:another capture stdout
+>stdout as a here string
+>
+
+ wrap=${| REPLY="REPLY in environment assignment" } typeset -p wrap
+ wrap=${ purr "capture in environment assignment" } typeset -p wrap
+0:assignment context
+>typeset -g wrap='REPLY in environment assignment'
+>typeset -g wrap='capture in environment assignment'
+
+# Repeat return and exit tests with stdout capture
+
+ purr "${
+ print INNER
+ return 7
+ } $?"
+0:return statement inside, part 1+
+F:status of "print" should hide return
+>INNER
+> 7
+
+ unset outer
+ outer=${ return 7 }
+7:return status propages in stdout capture
+
+ unset outer
+ purr "${
+ outer=OUTER
+ print INNER
+ return 7
+ OUTER=NOTREACHED
+ } $outer $?"
+ print REACHED $OUTER
+0:return statement inside, part 2+
+>INNER
+> OUTER 7
+>REACHED
+
+ unset outer
+ purr "${
+ # Localoptions needed to avoid breaking test harness?
+ # The setopt command affects surrounding context
+ setopt localoptions errreturn
+ outer=OUTER
+ print INNER
+ false
+ OUTER=NOTREACHED
+ } $outer $?"
+ print REACHED $OUTER ${options[errreturn]}
+0:errreturn works inside stdout capture
+>INNER
+> OUTER 1
+>REACHED on
+
+ (
+ unset outer
+ purr "${
+ outer=OUTER
+ print INNER
+ exit 7
+ OUTER=NOTREACHED
+ } $outer $OUTER $?"
+ print NOT REACHED
+ )
+7:exit statement inside stdout capture
+
+ (
+ unset outer
+ purr "${
+ setopt errexit
+ outer=OUTER
+ print INNER
+ false
+ OUTER=NOTREACHED
+ } $outer $?"
+ print NOT REACHED
+ )
+1:errexit inside stdout capture
+
+ setopt ignorebraces
+0:dummy test to set option soon enough
+F:must do this before evaluating the next test block
+
+ purr ${| REPLY=${REPLY:-buried}}}
+0:ignored braces, part 1
+>buried}
+
+ # Global $REPLY still set from earlier test
+ purr "${ purr ${REPLY:+buried}}}"
+0:ignored braces, part 2
+>buried
+>}
+
+ purr ${ { echo nested ;} }
+0:ignored braces, part 3
+>nested
+
+ purr ${ { echo nested } } DONE
+1:ignored braces, part 4
+?(eval):3: parse error near `}'
+
+ unsetopt ignorebraces
+ # "break" blocks function calls in outer loop
+ # Could use print, but that might get fixed
+ repeat 3 do purr ${
+ for x in 1 2 3 4
+ do (( x == 3 )) && break 2
+ # use error output to confirm loop count
+ print -u 2 $x
+ done
+ } XX
+ done
+0:break N propagates
+?1
+?2
+
+ # Cannot "purr": break skips pending function calls
+ # Use "repeat" to avoid infinite loop on failure
+ repeat 3 do; echo ${|REPLY=x; break }; done
+ repeat 3 do; echo ${{x} x=y; break }; done
+ repeat 3 do; echo ${ echo z; break }; done
+0:break after assignment completes the assignment
+>x
+>y
+>z
+
+ # Subshell because error exits
+ ( purr ${ echo ${unset?oops} } )
+1:error handling (without crashing)
+*?*unset: oops
+
+ purr ${ .zsh.cmdsubst=error }
+1:reserved parameter name (without crashing)
+*?*.zsh.cmdsubst: can't modify read-only parameter
+
+%clean
+
+ unfunction purr purl
diff --git a/Test/E01options.ztst b/Test/E01options.ztst
index 2acbfd357..363846f5c 100644
--- a/Test/E01options.ztst
+++ b/Test/E01options.ztst
@@ -416,6 +416,9 @@
1:NO_EXEC does recognize bad substitution syntax
*?* bad substitution
+ (setopt noexec; : $(<nonexistentfile))
+0:NO_EXEC does not attempt to read files in $(<....)
+
setopt NO_eval_lineno
eval 'print $LINENO'
setopt eval_lineno
@@ -558,12 +561,14 @@
foo=(one.c two.c three.c)
print ${foo:s/#%(#b)t(*).c/T${match[1]}.X/}
print *(#q:s/#(#b)tmp(*e)/'scrunchy${match[1]}'/)
+ print ${${:-"left[({})]over"}:fs/(\\{\\}|\\(\\)|\\[\\])//}
unsetopt histsubstpattern
0:HIST_SUBST_PATTERN option
>TINGcd TINGfile1 TINGfile2 homedir
>THUMPcd THUMPfile1 THUMPfile2
>one.c Two.X Three.X
>homedir scrunchyfile1 scrunchyfile2 tmpcd
+>leftover
setopt ignorebraces
echo X{a,b}Y
@@ -651,7 +656,7 @@
>noktarg1
>0 1
- showopt() { setopt | egrep 'localoptions|ksharrays'; }
+ showopt() { echo ${(FM)${(@f)"$(setopt)"}:#(localoptions|ksharrays)*} }
f1() { setopt localoptions ksharrays; showopt }
f2() { setopt ksharrays; showopt }
setopt kshoptionprint
@@ -1376,6 +1381,64 @@ F:Regression test for workers/41811
>1
>2
+ pipefailfn1() {
+ emulate -L zsh
+ setopt errreturn pipefail
+ false | { true; }
+ print "Shouldn't get here, status $?"
+ }
+ pipefailfn1
+1:PIPE_FAIL causes ERR_RETURN with complex end of pipeline: braces
+
+ pipefailfn2() {
+ emulate -L zsh
+ setopt errreturn pipefail
+ false | if true; then true; fi
+ print "Shouldn't get here, status $?"
+ }
+ pipefailfn2 || print Function failed, as expected
+0:PIPE_FAIL causes ERR_RETURN with complex end of pipeline: if
+>Function failed, as expected
+
+ pipefailfn3() {
+ emulate -L zsh
+ setopt errreturn pipefail
+ false | while true; do break; done
+ print "Shouldn't get here, status $?"
+ }
+ pipefailfn3 || print Function failed, as expected
+0:PIPE_FAIL causes ERR_RETURN with complex end of pipeline: while
+>Function failed, as expected
+
+ pipefailfn4() {
+ emulate -L zsh
+ setopt errreturn pipefail
+ false | true
+ print "Shouldn't get here, status $?"
+ }
+ pipefailfn4
+1:PIPE_FAIL causes ERR_RETURN in simple case
+
+ pipefailfn5() {
+ emulate -L zsh
+ setopt errreturn pipefail
+ false | { true | true; }
+ print "Shouldn't get here, status $?"
+ }
+ pipefailfn5 || print Function failed as expected
+0:PIPE_FAIL causes ERR_RETURN with nested successful pipe
+>Function failed as expected
+
+ pipefailfn6() {
+ emulate -L zsh
+ setopt errreturn pipefail
+ false | { false | true; }
+ print "Shouldn't get here, status $?"
+ }
+ pipefailfn6 || print Function failed as expected
+0:PIPE_FAIL causes ERR_RETURN with nested failed pipe
+>Function failed as expected
+
for (( i = 0; i < 10; i++ )); do
() {
print $i
diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst
new file mode 100644
index 000000000..1603ab1b9
--- /dev/null
+++ b/Test/K01nameref.ztst
@@ -0,0 +1,940 @@
+# Tests for named references
+
+%prep
+
+ # Required in order to declare an unset hash for substitution test
+ setopt TYPESET_TO_UNSET
+
+ : ${ZTST_continue::=1}
+
+%test
+
+ typeset -n ptr
+ typeset -n
+0:minimal declaration
+>ptr
+
+ typeset -n ptr=
+ typeset -n
+0:nameref placeholder
+>ptr=''
+
+ typeset -n ptr
+ ptr=var
+ typeset -n
+0:assign nameref placeholder
+>ptr=var
+
+ unset ptr
+ typeset -n ptr
+ typeset -n ptr=var
+ typeset -n
+0:assign placeholder with new typeset
+>ptr=var
+
+ typeset -n ptr1
+ typeset -n ptr2=ptr1
+ typeset -n
+0:chain ending in placeholder
+>ptr1
+>ptr2=ptr1
+
+ typeset ptr=var
+ typeset -n ptr
+ typeset -n
+0:convert scalar to nameref
+>ptr=var
+
+ typeset -n ptr=var
+ typeset +n ptr
+ typeset -p ptr
+0:remove nameref attribute
+>typeset ptr=var
+
+ typeset -n ptr=gvar
+ () {
+ local ptr
+ typeset -p ptr
+ }
+ typeset -p ptr
+0:Local non-reference hides outside reference
+>typeset ptr
+>typeset -n ptr=gvar
+
+ typeset -n ptr
+ typeset -t ptr
+ typeset -p ptr
+0:change type of a placeholder
+F:Other type changes are fatal errors, should this also be?
+>typeset -n ptr=''
+*?*ptr: can't change type of a named reference
+
+ typeset -n ptr=var
+ typeset -t ptr
+ typeset -p ptr var
+0:change type of referenced var
+>typeset -n ptr=var
+>typeset -t var
+
+ typeset var
+ unset var
+ typeset -n ptr=var
+ typeset -t ptr
+ typeset -p ptr var
+0:change type of unset referenced var
+F:regression - at one time this incorrectly applied the tag to "ptr"
+F:note this causes "var" to become set
+>typeset -n ptr=var
+>typeset -t var
+
+ typeset -n ptr=var[2]
+ typeset -t ptr
+1:change type of referenced array element
+*?*var\[2\]: can't change type via subscript reference
+
+ typeset -n ptr[1]=var
+1:illegal nameref name
+*?*reference variable cannot be an array
+
+ typeset var=value
+ typeset -n ptr=var
+ print $ptr
+0:basic nameref expansion, no braces
+>value
+
+ typeset var=value
+ typeset -n ptr=var
+ print ${ptr}
+0:basic nameref expansion, braces
+>value
+
+ typeset var=(val1 val2)
+ typeset -n ptr=var
+ print $ptr
+0:nameref array expansion
+>val1 val2
+
+ typeset -A var=(val1 val2)
+ typeset -n ptr=var
+ print ${(kv)ptr}
+0:nameref hash expansion
+>val1 val2
+
+ typeset -n ptr=var
+ typeset var=value
+ typeset -p ptr var
+ ptr=newvalue
+ typeset -p ptr var
+0:assign existing scalar via nameref
+>typeset -n ptr=var
+>typeset var=value
+>typeset -n ptr=var
+>typeset var=newvalue
+
+ typeset -n ptr=var
+ typeset var=value
+ unset ptr
+ typeset -p var
+0:unset via nameref
+
+ typeset -n ptr=var
+ typeset var=value
+ unset -n ptr
+ typeset -p var ptr
+0:unset of the nameref itself
+F:If earlier tests change, might get "no such variable" here
+>typeset var=value
+
+ typeset -n ptr=var
+ typeset var=value
+ typeset -p ptr var
+ typeset ptr=newvalue
+ typeset -p ptr var
+0:typeset existing scalar via nameref
+>typeset -n ptr=var
+>typeset var=value
+>typeset -n ptr=var
+>typeset var=newvalue
+
+ typeset -n ptr=var
+ ptr=value
+ typeset -p var ptr
+0:assign new scalar via nameref
+>typeset -g var=value
+>typeset -n ptr=var
+
+ unset var
+ typeset -n ptr=var
+ typeset var=(val1 val2)
+ typeset -p ptr var
+ ptr=(new1 new2)
+ typeset -p ptr var
+0:assign existing array via nameref
+>typeset -n ptr=var
+>typeset -a var=( val1 val2 )
+>typeset -n ptr=var
+>typeset -a var=( new1 new2 )
+
+ typeset -p ptr ptr1 ptr2 var
+1:check state of paramtab ONE
+F:unexpected side-effects of previous tests
+*?*no such variable: ptr
+*?*no such variable: ptr1
+*?*no such variable: ptr2
+*?*no such variable: var
+
+ typeset -n ptr=var
+ ptr=(val1 val2)
+ typeset -p var ptr
+0:assign new array via nameref
+>typeset -g -a var=( val1 val2 )
+>typeset -n ptr=var
+
+ unset var
+ typeset -n ptr2=var
+ typeset -n ptr1=ptr2
+ typeset var=value
+ typeset -p ptr1 ptr2 var
+ print $ptr1
+0:indirect nameref expansion
+>typeset -n ptr1=ptr2
+>typeset -n ptr2=var
+>typeset var=value
+>value
+
+ typeset -p ptr1 ptr2 var
+1:check state of paramtab TWO
+F:unexpected side-effects of previous tests
+*?*no such variable: ptr1
+*?*no such variable: ptr2
+*?*no such variable: var
+
+ typeset var
+ typeset -n ptr2=var
+ typeset -n ptr1=ptr2
+ typeset ptr1=newvalue
+ typeset -p ptr1 ptr2 var
+0:typeset existing parameter indirectly
+>typeset -n ptr1=ptr2
+>typeset -n ptr2=var
+>typeset var=newvalue
+
+ typeset var=value
+ typeset -n ptr2=var
+ typeset -n ptr1=ptr2
+ unset ptr1
+ typeset -p ptr1 ptr2 var
+0:unset parameter indirectly
+>typeset -n ptr1=ptr2
+>typeset -n ptr2=var
+
+ typeset -n ptr2=var
+ typeset -n ptr1=ptr2
+ typeset ptr1=newvalue
+ typeset -p ptr1 ptr2 var
+0:typeset new parameter indirectly
+>typeset -n ptr1=ptr2
+>typeset -n ptr2=var
+>typeset var=newvalue
+
+ unset var
+ typeset -n ptr2=var
+ typeset -n ptr1=ptr2
+ typeset var=value
+ typeset -p ptr1 ptr2 var
+ ptr1=newvalue
+ typeset -p ptr1 ptr2 var
+0:assign new parameter indirectly
+>typeset -n ptr1=ptr2
+>typeset -n ptr2=var
+>typeset var=value
+>typeset -n ptr1=ptr2
+>typeset -n ptr2=var
+>typeset var=newvalue
+
+ typeset -p ptr1 ptr2 var
+1:check state of paramtab THREE
+F:unexpected side-effects of previous tests
+*?*no such variable: ptr1
+*?*no such variable: ptr2
+*?*no such variable: var
+
+ typeset -a var
+ typeset -n ptr2=var
+ typeset -n ptr1=ptr2
+ typeset ptr1=(val1 val2)
+ typeset -p ptr1 ptr2 var
+0:typeset existing array indirectly
+>typeset -n ptr1=ptr2
+>typeset -n ptr2=var
+>typeset -a var=( val1 val2 )
+
+ typeset -n ptr2=var
+ typeset -n ptr1=ptr2
+ typeset ptr1=(val1 val2)
+ typeset -p ptr1 ptr2 var
+0:typeset new array indirectly
+>typeset -n ptr1=ptr2
+>typeset -n ptr2=var
+>typeset -a var=( val1 val2 )
+
+ typeset -p ptr1 ptr2
+1:check state of paramtab FOUR
+F:unexpected side-effects of previous tests
+*?*no such variable: ptr1
+*?*no such variable: ptr2
+
+ unset var
+ typeset -n ptr2=var
+ typeset -n ptr1=ptr2
+ ptr1=(val1 val2)
+ typeset -p ptr1 ptr2 var
+0:assign new array indirectly
+>typeset -n ptr1=ptr2
+>typeset -n ptr2=var
+>typeset -g -a var=( val1 val2 )
+
+ typeset -n ptr1=ptr2
+ typeset -n ptr2=ptr1
+1:direct nameref loop not allowed
+*?*invalid self reference
+
+ unset var
+ typeset -gn ptr1=var
+ typeset -p ptr1
+0:global reference to unset var
+>typeset -g -n ptr1=var
+
+ unset -n ptr1
+ typeset -gn ptr1
+ typeset -p ptr1
+ ptr1=ptr1
+1:global direct reference
+>typeset -g -n ptr1
+*?*invalid self reference
+
+ typeset -n ptr1=ptr2
+ typeset -n ptr2=ptr3
+ typeset -n ptr3=ptr1
+1:indirect nameref loop not allowed
+*?*invalid self reference
+
+ typeset -n ptr1 ptr2
+ ptr1=ptr2
+ ptr2=ptr1
+1:looping assignment not allowed
+*?*invalid self reference
+
+ unset -n ptr2
+ typeset -n ptr2='path[2]'
+ print -r -- $ptr2
+0q:nameref to array element, no braces
+>${path[2]}
+
+ unset -n ptr2
+ typeset -n ptr2='path[2]'
+ print -r -- ${ptr2}
+0q:nameref to array element, with braces
+>${path[2]}
+
+ unset -n ptr1
+ typeset -A hash=(x MISS y HIT)
+ typeset -n ptr1='hash[y]'
+ print -r -- $ptr1
+0:nameref to hash element, no braces
+>HIT
+
+ unset -n ptr1
+ typeset -A hash=(x MISS y HIT)
+ typeset -n ptr1='hash[y]'
+ print -r -- ${ptr1}
+0:nameref to hash element, with braces
+>HIT
+
+ unset -n ptr2
+ typeset -a ary=(1 2)
+ typeset -n ptr2='ary[2]'
+ ptr2=TWO
+ typeset -p ary
+0:assign array element by nameref
+>typeset -a ary=( 1 TWO )
+
+ unset -n ptr2
+ typeset -n ptr2='ary[2]'
+ ptr2=TWO
+ typeset -p ary
+0f:create array element by nameref
+F:ksh93 does not implement this either
+>typeset -a ary=( '' TWO )
+
+ unset -n ptr1
+ typeset -A hash=(x MISS y MISS)
+ typeset -n ptr1='hash[y]'
+ ptr1=HIT
+ typeset -p hash
+0:assign to hash element by nameref
+>typeset -A hash=( [x]=MISS [y]=HIT )
+
+ unset -n ptr1
+ typeset -A hash
+ typeset -n ptr1='hash[y]'
+ ptr1=HIT
+ typeset -p hash
+0f:create hash by element nameref
+F:ksh93 does not implement this either
+>typeset -A hash=( [y]=HIT )
+
+ unset -n ptr1
+ typeset -n ptr1='not[2]good'
+1:invalid nameref
+*?*invalid variable name: not\[2\]good
+
+ unset -n ptr1
+ unset hash
+ typeset -A hash
+ typeset -n ptr1='hash[y]'
+ print ${ptr1::=HIT}
+ typeset -p ptr1 hash
+0f:create hash by element substitution
+>HIT
+>typeset -n ptr1='hash[y]'
+>typeset -A hash=( [y]=HIT )
+
+ unset -n ptr
+ unset gval
+ typeset -n ptr=gval
+ gval=global
+ () { local gval=local; print $ptr; typeset -p ptr gval }
+0:up-reference part 1
+>global
+>typeset -g -n ptr=gval
+>typeset gval=local
+
+ typeset -p ptr ptr1 ptr2 val
+1:check state of paramtab FIVE
+F:unexpected side-effects of previous tests
+*?*no such variable: ptr
+*?*no such variable: ptr1
+*?*no such variable: ptr2
+*?*no such variable: val
+
+ unset gval
+ typeset -n ptr1=gval
+ typeset gval
+ () { typeset gval=local; ptr1=global }
+ typeset -p ptr1 gval
+0:up-reference assignment part 1
+F:All tests run inside a function, so "typeset gval" creates a local;
+F:if that were omitted, ptr1= assignment would create a true global
+F:and the output below would change to "typeset -g gval=global"
+>typeset -n ptr1=gval
+>typeset gval=global
+
+ typeset -p ptr ptr1 ptr2 val gval
+1:check state of paramtab SIX
+F:unexpected side-effects of previous tests
+*?*no such variable: ptr
+*?*no such variable: ptr1
+*?*no such variable: ptr2
+*?*no such variable: val
+*?*no such variable: gval
+
+ typeset gval=global
+ () {
+ typeset -n ptr=gval
+ local gval=local
+ print $ptr
+ }
+ typeset -p ptr gval
+1:up-reference part 2
+>global
+*?*no such variable: ptr
+>typeset gval=global
+
+ typeset -n ptr=gval
+ () {
+ local lval=local
+ typeset -n ptr=lval
+ ptr=LOCAL
+ typeset -p lval gval ptr
+ }
+ typeset -p ptr
+0:localized namerefs hide global namerefs
+*?*no such variable: gval
+>typeset lval=LOCAL
+>typeset -n ptr=lval
+>typeset -n ptr=gval
+
+ typeset -A var=(myself outside)
+ () {
+ typeset -n myself=var[myself]
+ local -h var
+ print -r -- $myself
+ typeset -p var
+ }
+0:up-reference part 3, hidden global
+>outside
+>typeset -h var
+
+ () {
+ typeset notdef
+ unset notdef
+ () {
+ typeset -n ptr=notdef
+ ptr=(DEFINED)
+ }
+ typeset -p notdef
+ }
+0:up-reference part 4, unset local and type change
+>typeset -a notdef=( DEFINED )
+
+ () {
+ typeset -n ptr1=ptr2
+ typeset -n ptr2
+ typeset -p ptr1 ptr2
+ typeset val=LOCAL
+ () {
+ ptr1=val
+ typeset -n
+ printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2"
+ }
+ typeset -p ptr1 ptr2
+ }
+ typeset -p ptr2
+1:up-reference part 5, stacked namerefs, end not in scope
+>typeset -n ptr1=ptr2
+>typeset -n ptr2
+>ptr1=ptr2
+>ptr2=val
+>ptr1=LOCAL
+>ptr2=LOCAL
+>typeset -n ptr1=ptr2
+>typeset -n ptr2=val
+*?*no such variable: ptr2
+
+ typeset ptr2
+ () {
+ typeset -n ptr1=ptr2
+ typeset -n ptr2
+ typeset -p ptr1 ptr2
+ typeset val=LOCAL
+ () {
+ ptr1=val
+ typeset -n
+ printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2"
+ }
+ typeset -p ptr1 ptr2
+ }
+ typeset -p ptr2
+0:up-reference part 6, stacked namerefs, end is in scope
+F:Same test, should part 5 output look like this?
+>typeset -n ptr1=ptr2
+>typeset -n ptr2
+>ptr1=ptr2
+>ptr2
+>ptr1=val
+>ptr2=
+>typeset -n ptr1=ptr2
+>typeset -n ptr2
+>typeset ptr2=val
+
+ () {
+ () {
+ local var
+ typeset -nu ptr1=var
+ ptr1=outer && print -u2 assignment expected to fail
+ typeset -n ptr2=var
+ ptr2=inner
+ typeset -n
+ printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2"
+ }
+ typeset -p var
+ }
+ typeset -p var
+1:up-reference part 7, upscope namerefs, end not in scope
+>ptr1=var
+>ptr2=var
+>ptr1=
+>ptr2=inner
+*?*typeset*: no such variable: var
+*?*typeset*: no such variable: var
+
+ typeset var
+ () {
+ () {
+ local var
+ typeset -nu ptr1=var
+ ptr1=outer || print -u2 assignment expected to succeed
+ typeset -n ptr2=var
+ ptr2=inner
+ typeset -n
+ printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2"
+ }
+ typeset -p var
+ }
+ typeset -p var
+0:up-reference part 8, upscope namerefs, end in scope
+>ptr1=var
+>ptr2=var
+>ptr1=outer
+>ptr2=inner
+>typeset -g var=outer
+>typeset var=outer
+
+ if zmodload zsh/parameter; then
+ () {
+ zmodload -u zsh/parameter
+ typeset -n myself=parameters[myself]
+ local -h parameters
+ print -r -- $myself
+ typeset -p parameters
+ }
+ else ZTST_skip='Cannot zmodload zsh/parameter, skipping autoload test'
+ fi
+0:up-reference part 9, autoloading with hidden special
+>nameref-local-nameref-local
+>typeset -h parameters
+
+ (
+ inner() { local -n var="${1:?}"; var=(alpha beta gamma); }
+ outer() { local -a foo=(outer); inner foo; typeset -p foo; }
+ foo=3 ; { outer foo } always { typeset -p foo }
+ )
+0:up-reference part 10, assignment to enclosing scope, types match
+>typeset -a foo=( alpha beta gamma )
+>typeset -g foo=3
+
+ (
+ inner() { local -n var="${1:?}"; var=(alpha beta gamma); }
+ outer() { local foo=outer; inner foo; typeset -p foo; }
+ foo=3 ; { outer foo } always { typeset -p foo }
+ )
+1:up-reference part 11, assignment to enclosing scope, type mismatch
+>typeset -g foo=3
+?inner: foo: attempt to assign array value to non-array
+
+ (
+ inner() { local -n var="${1:?}"; unset var; var=(alpha beta gamma); }
+ outer() { local foo=outer; inner foo; typeset -p foo; }
+ foo=3 ; { outer foo } always { typeset -p foo }
+ )
+0:up-reference part 12, assignment to enclosing scope, unset by reference
+>typeset -a foo=( alpha beta gamma )
+>typeset -g foo=3
+
+ (
+ inner() { local "${1:?}"; local -nu var="$1"; var=(alpha beta gamma); }
+ outer() { local -a foo=(outer); inner foo; typeset -p foo; }
+ foo=3 ; { outer foo } always { typeset -p foo }
+ )
+0:up-reference part 13, assignment to enclosing scope, skip local
+>typeset -a foo=( alpha beta gamma )
+>typeset -g foo=3
+
+ (
+ inner() { local "${1:?}"; local -nu var="$1";
+ typeset -g var=(alpha beta gamma); }
+ outer() { local -a foo=(outer); inner foo; typeset -p foo; }
+ foo=3 ; { outer foo } always { typeset -p foo }
+ )
+0f:up-reference part 14, typeset -g to enclosing scope, skip local
+F:typeset cannot bypass a name in the local scope, even via nameref
+>typeset -a foo=( alpha beta gamma )
+>typeset -g foo=3
+
+ if [[ $options[typesettounset] != on ]]; then
+ ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET'
+ setopt typesettounset
+ fi
+0:options reloaded
+F:Checking for a bug in zmodload that affects later tests
+
+ typeset ptr2=var2
+ typeset var2=GLOBAL
+ () {
+ typeset -n ptr1=ptr2
+ typeset ptr2=var1
+ typeset var1=VAR1
+ typeset var2=VAR2
+ print -r -- ${(P)ptr1}
+ }
+0:Order of evaluation with ${(P)...}
+>VAR2
+
+ ary=(one two three four)
+ typeset -n ptr=ary
+ print -r ${(j.:.)ptr//o/0}
+0:expansion flags and string replacement
+>0ne:tw0:three:f0ur
+
+ var=value
+ typeset -n ptr=var
+ myscalar=ptr
+ echo ${(P)myscalar}
+0:named references with (P), as ${(P)name_of_nameref}
+>value
+
+ var=value
+ myscalar=var
+ typeset -n ptr=myscalar
+ echo ${(P)ptr}
+0:named references with (P), as ${(P)nameref}
+>value
+
+ ary=( 'bry[1]' 'bry[2]' )
+ bry=( lorem ipsum )
+ typeset -n ptr='ary[2]'
+ print -r -- ${ptr}
+ print -r -- ${(P)ptr}
+0:named references with (P), array element to array element
+>bry[2]
+>ipsum
+
+ unset -n ref
+ unset var
+ typeset -n ref=var
+ typeset var=GLOBAL
+ () {
+ typeset -n ref=$1
+ print -r $ref
+ ref=RESET
+ typeset -p ref var
+ } ref
+ typeset -p ref var
+0:local reference points to same-name global reference, part 1
+>GLOBAL
+>typeset -n ref=ref
+>typeset -g var=RESET
+>typeset -n ref=var
+>typeset var=RESET
+
+ unset -n ref
+ unset var
+ typeset -n ref=var
+ () {
+ typeset -n ref=$1
+ print -r $ref
+ ref=RESET
+ typeset -p ref var
+ } ref
+ typeset -p ref var
+0:local reference points to same-name global reference, part 2
+>
+>typeset -n ref=ref
+>typeset -g var=RESET
+>typeset -n ref=var
+>typeset -g var=RESET
+
+ unset -n ref
+ unset one
+ typeset -n ref
+ typeset one=ONE
+ for ref in one ref two; do print -r $ref; done
+1:for-loop variable is a reference, part 1
+>ONE
+*?*ref: invalid self reference
+
+ unset -n ref
+ unset one
+ typeset -n ref
+ () {
+ typeset one=ONE
+ for ref in one ref two; do print -r ${(t)ref}; done
+ }
+1:for-loop variable is a reference, part 2
+>scalar-local
+*?*ref: invalid self reference
+
+ unset -n ref
+ unset one var
+ typeset -n ref=var
+ () {
+ typeset one=ONE
+ typeset -n ref=ref
+ for ref in one ref two; do
+ typeset -p ref
+ print -r $ref
+ done
+ typeset -p ref
+ }
+ typeset -p ref
+0:for-loop variable is a reference, part 3
+>typeset -n ref=one
+>ONE
+>typeset -n ref=ref
+>
+>typeset -n ref=two
+>
+>typeset -n ref=two
+>typeset -n ref=var
+
+ typeset -g .K01.scalar='RW'
+ typeset -gA .K01.assoc=(x y)
+ typeset -ga .K01.array=(z)
+ typeset -gi .K01.integer=0
+ typeset -gE .K01.double=0.0
+ typeset -gF .K01.float=0.0
+ typeset -gr .K01.readonly='RO'
+ typeset -n gref
+ for gref in ARGC .K01.{scalar,assoc,array,integer,double,float,readonly}
+ do
+ { unset gref } always { TRY_BLOCK_ERROR=0 }
+ done
+ typeset -p .K01.{scalar,assoc,array,integer,double,float,readonly}
+ unset .K01.{scalar,assoc,array,integer,double,float}
+0:unset various types via nameref, including a readonly special
+>typeset -g .K01.scalar
+>typeset -g -A .K01.assoc
+>typeset -g -a .K01.array
+>typeset -g -i .K01.integer
+>typeset -g -E .K01.double
+>typeset -g -F .K01.float
+>typeset -g -r .K01.readonly=RO
+*?*read-only variable: ARGC
+*?*read-only variable: .K01.readonly
+
+ unset -n ref
+ unset one
+ typeset -n ref
+ () {
+ setopt localoptions warn_nested_var
+ typeset one=ONE
+ for ref in one two; do print -r ${(t)ref}; done
+ typeset -n ref
+ for ref in one two; do print -r ${(t)ref}; done
+ }
+0:for-loop variable is a reference, part 4, warnings
+>scalar-local
+>
+>scalar-local
+>
+*?*reference ref*to local variable one
+
+ unset -n ref
+ typeset -n ref
+ () {
+ setopt localoptions warn_nested_var
+ typeset inner
+ ref=inner
+ }
+ typeset -p ref
+0:Global variable is a reference, warning
+>typeset -n ref=inner
+*?*reference ref*to local variable inner
+
+ typeset -n ptr='ary[$(echo 2)]'
+ typeset -a ary=(one two three)
+ print $ptr
+1:attempt deferred command substitution in subscript
+F:runs in `setopt noexec` so $(...) returns nothing
+*?*bad math expression: empty string
+
+ unset -n ref
+ typeset -n ref=GLOBAL
+ () {
+ typeset -gn ref=RESET
+ }
+ typeset -p ref
+0:reset global reference within function
+>typeset -n ref=RESET
+
+ unset -n ref
+ typeset -rn ref=RO
+ typeset -p ref
+ (typeset -n ref=RW)
+ print status: $? expected: 1
+ typeset +r -n ref
+ typeset -p ref
+ typeset -r +n ref
+ typeset -p ref
+ (typeset -rn ref)
+ print status: $? expected: 1
+ typeset +r -n ref=RW # Assignment occurs after type change,
+ typeset -p ref RO # so RO=RW here. Potentially confusing.
+ typeset -r -n ref=RX # No type change, so referent changes ...
+ typeset -p ref RO # ... and previous refererent does not.
+ typeset +rn ref=RW # Here ref=RW, again type changed first.
+ typeset -p ref
+0:add and remove readonly attribute with references
+>typeset -rn ref=RO
+*?*: ref: read-only reference
+>status: 1 expected: 1
+>typeset -n ref=RO
+>typeset -r ref=RO
+*?*: ref: read-only variable
+>status: 1 expected: 1
+>typeset -n ref=RO
+>typeset -g RO=RW
+>typeset -rn ref=RX
+>typeset -g RO=RW
+>typeset ref=RW
+
+ () {
+ typeset -n r1 r2=
+ typeset -p r1 r2
+ print -- ${(!)r1-unset}
+ print -- ${+r1}
+ typeset -p r1
+ }
+0:unset nameref remains unset when resolved
+F:relies on global TYPESET_TO_UNSET in %prep
+>typeset -n r1
+>typeset -n r2=''
+>unset
+>0
+>typeset -n r1
+
+ bar=xx
+ typeset -n foo=bar
+ () {
+ typeset -n foo; foo=zz
+ foo=zz || print -u2 foo: assignment failed
+ print $bar $zz
+ }
+ () { typeset -n foo; foo=zz; local zz; foo=zz; print $bar $zz }
+0:regression: local nameref may not in-scope a global parameter
+F:previously this could create an infinite recursion and crash
+>xx
+>xx zz
+*?*foo: assignment failed
+
+ typeset -nm foo=bar
+1:create nameref by pattern match not allowed
+*?*typeset:1: -m not allowed with -n
+
+#
+# The following tests are run in interactive mode, using PS1 as an
+# assignable special with side-effects. This crashed at one time.
+#
+
+ # Note bypassing TYPESET_TO_UNSET here
+ $ZTST_testdir/../Src/zsh -fis <<<$'
+ typeset -n p=PS1
+ () {
+ typeset -p p
+ local p
+ typeset -p p
+ p=xx
+ typeset -p p
+ }
+ '
+0:regression: assign to local that shadows global named reference
+>typeset -g -n p=PS1
+>typeset p=''
+>typeset p=xx
+*?*
+
+ # Note bypassing TYPESET_TO_UNSET here
+ $ZTST_testdir/../Src/zsh -fis <<<$'
+ () {
+ typeset p=PS1
+ typeset -n p
+ p=zz
+ }
+ typeset -p PS1
+ '
+0:regression - converting a string into a named reference
+>typeset PS1=zz
+*?*
+
+%clean
diff --git a/Test/K02parameter.ztst b/Test/K02parameter.ztst
new file mode 100644
index 000000000..0b1a8dd4a
--- /dev/null
+++ b/Test/K02parameter.ztst
@@ -0,0 +1,154 @@
+# Test parameter expansion with namespace syntax
+# (heavily borrowed from D04parameter.ztst)
+
+%prep
+
+%test
+
+ .k02.foo='the first parameter'
+ .k02.bar='the second parameter'
+ print -l $.k02.foo ${.k02.bar}
+0:Basic scalars with namespace
+F:Braces are required
+>$.k02.foo
+>the second parameter
+
+ typeset .k02.bar='the second parameter'
+ print -l ${.k02.bar}
+0:Scalar but with typeset
+>the second parameter
+
+ .k02.array1=(the first array)
+ .k02.array2=(the second array)
+ print -l $.k02.array1 ${.k02.array2}
+0:Basic arrays with namespace
+>$.k02.array1
+>the
+>second
+>array
+
+ typeset -a .k02.array2=(the second array)
+ print -l ${.k02.array2}
+0:Array but with typeset
+>the
+>second
+>array
+
+ setopt ksharrays
+ print -l ${.k02.array2}
+ unsetopt ksharrays
+0:Basic ksharray with namespace
+>the
+
+ setopt shwordsplit
+ print -l ${.k02.foo} ${==.k02.bar}
+ unsetopt shwordsplit
+0:Basic shwordsplit with namespace
+>the
+>first
+>parameter
+>the second parameter
+
+ print ${+.k02.foo} ${+.k02.notappearinginthistest}
+0:$+... and namespace
+>1 0
+
+ .k02.x=()
+ print ${+.k02.x} ${+.k02.x[1]} ${+.k02.x[(r)foo]} ${+.k02.x[(r)bar]}
+ .k02.x=(foo)
+ print ${+.k02.x} ${+.k02.x[1]} ${+.k02.x[(r)foo]} ${+.k02.x[(r)bar]}
+0:$+... with arrays and namespace
+>1 0 0 0
+>1 1 1 0
+
+ # See D04 for complete explanation.
+ # For K02 we're just testing that flag syntax works.
+ .k02.foo='<five> {six} (seven) >eight< }nine{ |forty-two| $many$ )ten( more'
+ .k02.array=(${(z).k02.foo})
+ print -l ${(Q).k02.array}
+0:${(z)...} and ${(Q)...} for some hard to parse cases
+><
+>five
+>>
+>{six}
+>(
+>seven
+>)
+>>
+>eight
+><
+>}nine{
+>|
+>forty-two
+>|
+>$many$
+>)
+>ten( more
+
+ .k02.array=(characters in an array)
+ print ${(c)#.k02.array}
+0:${(c)#...}
+>22
+
+ () {
+ typeset -n .k02.ref=.k02.array
+ emulate -L ksh
+ print -l ${!.k02.ref} ${(!).k02.ref} ${.k02.ref}
+ }
+0:namerefs with namespaces
+>.k02.array
+>.k02.array
+>characters
+
+ k.=empty
+ k.2=test
+ print ${k.} ${k.2}
+0:Parse without leading dot (future proofing)
+>empty test
+
+ .k=OK
+ print ${.k}
+0:Bare namespace is usable (ksh compatibility)
+>OK
+
+ .k.=empty
+1:Namespace must precede an identifier, assignment
+?(eval):1: not an identifier: .k.
+
+ typeset .k.=empty
+1:Namespace must precede an identifier, typeset
+?(eval):typeset:1: not valid in this context: .k.
+
+ print ${.k.}
+1:Namespace must precede an identifier, reference
+?(eval):1: bad substitution
+
+ .2.b=not
+1:Namespace identifier must not begin with a digit, assignment
+?(eval):1: not an identifier: .2.b
+
+ typeset .2.b=not
+1:Namespace identifier must not begin with a digit, typeset
+?(eval):typeset:1: not valid in this context: .2.b
+
+ print ${.2.b}
+1:Namespace identifier must not begin with a digit, reference
+?(eval):1: bad substitution
+
+ .not.2b=question
+1:Identifier starting with a digit must be all digits, assignment
+?(eval):1: not an identifier: .not.2b
+
+ typeset .not.2b=question
+1:Identifier starting with a digit must be all digits, typeset
+?(eval):typeset:1: not valid in this context: .not.2b
+
+ print ${.not.2b}
+1:Identifier starting with a digit must be all digits, reference
+?(eval):1: bad substitution
+
+ integer .var.d=0
+ float .var.f=.2
+ print $((.var.x = ++.var.d - -.var.f))
+0:Namespaces in math context
+>1.2
diff --git a/Test/README b/Test/README
index 670434ac3..b9d393d7c 100644
--- a/Test/README
+++ b/Test/README
@@ -6,6 +6,7 @@ scripts names:
C: shell commands with special syntax
D: substititution
E: options
+ K: features adopted from ksh
P: privileged (needs super-user privileges)
V: modules
W: builtin interactive commands and constructs
diff --git a/Test/V07pcre.ztst b/Test/V07pcre.ztst
index c9c844d2a..b8cd31c96 100644
--- a/Test/V07pcre.ztst
+++ b/Test/V07pcre.ztst
@@ -6,20 +6,8 @@
return 0
fi
setopt rematch_pcre
-# Find a UTF-8 locale.
- setopt multibyte
-# Don't let LC_* override our choice of locale.
- unset -m LC_\*
- mb_ok=
- langs=(en_{US,GB}.{UTF-,utf}8 en.UTF-8
- $(locale -a 2>/dev/null | egrep 'utf8|UTF-8'))
- for LANG in $langs; do
- if [[ é = ? ]]; then
- mb_ok=1
- break;
- fi
- done
- if [[ -z $mb_ok ]]; then
+ LANG=$(ZTST_find_UTF8)
+ if [[ -z $LANG ]]; then
ZTST_unimplemented="no UTF-8 locale or multibyte mode is not implemented"
else
print -u $ZTST_fd Testing PCRE multibyte with locale $LANG
@@ -120,6 +108,11 @@
>0 xo→t →t
>0 Xo→t →t
+ [[ foo =~ (pre)?f(o*)(opt(i)onal)?(y)* ]]
+ typeset -p match
+0:Empty string for optional captures that don't match
+>typeset -g -a match=( '' oo '' '' '' )
+
string="The following zip codes: 78884 90210 99513"
pcre_compile -m "\d{5}"
pcre_match -b -- $string && print "$MATCH; ZPCRE_OP: $ZPCRE_OP"
@@ -129,12 +122,17 @@
>78884; ZPCRE_OP: 25 30
>90210; ZPCRE_OP: 31 36
-# Embedded NULs allowed in plaintext, but not in RE (although \0 as two-chars allowed)
+# Embedded NULs allowed in plaintext, in RE, pcre supports \0 as two-chars
[[ $'a\0bc\0d' =~ '^(a\0.)(.+)$' ]]
print "${#MATCH}; ${#match[1]}; ${#match[2]}"
0:ensure ASCII NUL passes in and out of matched plaintext
>6; 3; 3
+# PCRE2 supports NULs also in the RE
+ [[ $'a\0b\0c' =~ $'^(.\0)+' ]] && print "${#MATCH}; ${#match[1]}"
+0:ensure ASCII NUL works also in the regex
+>4; 2
+
# Ensure the long-form infix operator works
[[ foo -pcre-match ^f..$ ]]
print $?
@@ -174,3 +172,37 @@
echo $match[2] )
0:regression for segmentation fault, workers/38307
>test
+
+ LANG_SAVE=$LANG
+ [[ é =~ '^.\z' ]]; echo $?
+ LANG=C
+ [[ é =~ '^..\z' ]]; echo $?
+ LANG=$LANG_SAVE
+ [[ é =~ '^.\z' ]]; echo $?
+0:switch between C/UTF-8 locales
+>0
+>0
+>0
+
+ [[ abc =~ 'a(d*)bc' ]] && print "$#MATCH; $#match; ${#match[1]}"
+0:empty capture
+>3; 1; 0
+
+ [[ category/name-12345 =~ '(?x)^
+ (?<category> [^/]* ) /
+ (?<package>
+ (?<name> \w+ ) -
+ (?<version> \d+ ))$' ]]
+ typeset -p1 .pcre.match
+0:named captures
+>typeset -g -A .pcre.match=(
+> [category]=category
+> [name]=name
+> [package]=name-12345
+> [version]=12345
+>)
+
+ pcre_compile 'cat(er(pillar)?)?'
+ pcre_match -d 'the caterpillar catchment' && print $match
+0:pcre_match -d
+>caterpillar cater cat
diff --git a/Test/V10private.ztst b/Test/V10private.ztst
index 56ffbc5b4..26004a2dc 100644
--- a/Test/V10private.ztst
+++ b/Test/V10private.ztst
@@ -10,6 +10,8 @@
sed -e 's,# test_zsh_param_private,zmodload zsh/param/private,' < $ZTST_srcdir/B02typeset.ztst > private.TMP/B02
fi
+ setopt TYPESET_TO_UNSET
+
%test
(zmodload -u zsh/param/private && zmodload zsh/param/private)
@@ -26,7 +28,7 @@
print $scalar_test
0:basic scope hiding
>toplevel
->local scalar_test
+>local hide scalar_test
>0
>toplevel
@@ -52,7 +54,7 @@
print $+unset_test
0:variable defined only in scope
>0
->local unset_test
+>local hide unset_test
>setme
>0
@@ -68,7 +70,7 @@
}
print $array_test
0:nested scope with different type, correctly restored
->local array_test
+>local hide array_test
>in function
>top level
@@ -111,7 +113,7 @@
typeset -a hash_test=(top level)
typeset -p hash_test
inner () {
- private -p hash_test
+ typeset -p hash_test
print ${(t)hash_test} ${(kv)hash_test}
}
outer () {
@@ -246,7 +248,7 @@ F:note "typeset" rather than "private" in output from outer
1:privates are not visible in anonymous functions, part 3
>X top level
>array_test not set
-?(anon):4: array_test: can't change parameter attribute
+?(anon):4: array_test: can't modify read-only parameter
F:future revision will create a global with this assignment
typeset -a array_test
@@ -299,6 +301,256 @@ F:future revision will create a global with this assignment
*>*
*>*
+ typeset top=TOP
+ () {
+ local -P -n test=top
+ print $top
+ () { print UP: $test }
+ }
+0:nameref can be declared private
+>TOP
+>UP:
+
+ () {
+ typeset -a ary
+ local -P -n ref=ary
+ {
+ (){
+ ref=XX # Should be an error
+ typeset -p ary ref
+ }
+ } always {
+ TRY_BLOCK_ERROR=0
+ typeset -p ary ref
+ }
+ }
+ typeset -p ary
+1:assignment to private nameref in wrong scope, part 1
+>typeset -a ary
+>typeset -hn ref=ary
+*?*ref: can't modify read-only parameter
+*?*no such variable: ary
+
+ () {
+ typeset -a ary
+ local -P -n ref=ary
+ {
+ (){
+ typeset ref=XX # Should create a local
+ typeset -p ary ref
+ }
+ } always {
+ TRY_BLOCK_ERROR=0
+ typeset -p ary ref
+ }
+ }
+ typeset -p ary
+1:assignment to private nameref in wrong scope, part 2
+>typeset -g -a ary
+>typeset ref=XX
+>typeset -a ary
+>typeset -hn ref=ary
+*?*no such variable: ary
+
+ () {
+ typeset -n ptr1=ptr2
+ private -n ptr2 # TYPESET_TO_UNSET makes this not a "placeholder"
+ typeset -p ptr1 ptr2
+ typeset val=LOCAL
+ () {
+ ptr1=val # Test dies here as ptr2 is private and unset
+ typeset -n
+ printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2"
+ }
+ typeset -p ptr1 ptr2
+ }
+ typeset -p ptr2
+1:up-reference for private namerefs, end unset and not in scope
+F:See K01nameref.ztst up-reference part 5
+F:Here ptr1 finds private ptr2 by scope mismatch
+>typeset -n ptr1=ptr2
+>typeset -hn ptr2
+*?*read-only variable: ptr2
+
+ () {
+ typeset -n ptr1=ptr2
+ private -n ptr2= # Assignment makes this a placeholder, not unset
+ typeset -p ptr1 ptr2
+ typeset val=LOCAL
+ () {
+ ptr1=val || print -u2 ptr1: assignment failed
+ typeset -n
+ printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2"
+ }
+ typeset -p ptr1 ptr2
+ }
+ typeset -p ptr2
+1:up-reference for private namerefs, end not in scope
+F:See K01nameref.ztst up-reference part 5
+F:Here ptr1 finds private ptr2 by scope mismatch
+>typeset -n ptr1=ptr2
+>typeset -hn ptr2=''
+>ptr1=ptr2
+>ptr1=
+>ptr2=
+>typeset -n ptr1=ptr2
+>typeset -hn ptr2=''
+*?*ptr1: assignment failed
+*?*no such variable: ptr2
+
+ typeset ptr2
+ () {
+ typeset -n ptr1=ptr2
+ private -n ptr2 # Set/unset is irrelevant, not referenced
+ typeset -p ptr1 ptr2
+ typeset val=LOCAL
+ () {
+ ptr1=val
+ typeset -n
+ printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2"
+ }
+ typeset -p ptr1 ptr2
+ }
+ typeset -p ptr2
+0:up-reference for private namerefs, end is in scope
+F:See K01typeset.ztst up-reference part 5
+F:Here ptr1 points to global ptr2 so assignment succeeds
+>typeset -n ptr1=ptr2
+>typeset -hn ptr2
+>ptr1=ptr2
+>ptr2=val
+>ptr1=val
+>ptr2=val
+>typeset -n ptr1=ptr2
+>typeset -hn ptr2
+>typeset ptr2=val
+
+ () {
+ setopt localoptions errreturn
+ private -n ptr2
+ typeset -n ptr1=ptr2
+ typeset -p ptr1 ptr2
+ typeset val=LOCAL
+ () {
+ ptr1=val
+ typeset -n
+ printf "v %s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2"
+ }
+ typeset -p ptr1 ptr2
+ }
+ typeset -p ptr1 ptr2
+1:up-reference for private namerefs, end is in scope but private
+F:Should we allow "public" namerefs to private parameters?
+*?*ptr2: invalid reference
+*?*no such variable: ptr1
+*?*no such variable: ptr2
+
+ () {
+ private x=1
+ unset x
+ x=2
+ }
+0:regression test for unset private
+
+ () {
+ private x=1
+ unset x
+ private x=2
+ print $x
+ }
+0:private may be called twice
+>2
+
+ () {
+ private x=1
+ private -a x
+ print $x
+ }
+1:private may not change parameter type
+?(anon):private:2: can't change type of private param: x
+
+ () {
+ private fd1 fd2
+ exec {fd1}>&1
+ print OK
+ () { exec {fd2}>&2 }
+ print BAD $fd2
+ }
+1:redirection cannot assign private in wrong scope
+F:Better if caught in checkclobberparam() but exec.c doesn't know scope
+>OK
+?(anon): fd2: can't modify read-only parameter
+
+ () {
+ private z=outer
+ print ${(t)z} $z
+ print ${|
+ print ${(t)z} $z
+ REPLY=$z
+ }
+ }
+0:nofork may read private in calling function
+>scalar-local-hide-special outer
+>scalar-local-hide-special outer
+>outer
+
+ () {
+ private z=outer
+ print ${(t)z} $z
+ print ${| REPLY=${{z} z=nofork} }
+ print ${(t)z} $z
+ }
+0:nofork may write to private in calling function
+>scalar-local-hide-special outer
+>nofork
+>scalar-local-hide-special nofork
+
+ () {
+ local q=outer
+ print ${|
+ private q=nofork
+ REPLY=${| REPLY=$q}
+ }
+ }
+0:nofork cannot see private in surrounding nofork
+>outer
+
+ () {
+ private z=outer
+ print ${(t)z} $z
+ print ${{z}
+ private q
+ z=${{q} q=nofork}
+ }
+ print ${(t)z} $z
+ }
+1:nofork may not change private in surrounding nofork
+>scalar-local-hide-special outer
+*?*: q: can't modify read-only parameter
+
+ () {
+ private q=outer
+ print ${|
+ () { REPLY="{$q}" }
+ }
+ print ${{q}
+ () { q=nofork }
+ }
+ }
+1:function may not access private from inside nofork
+>{}
+*?*: q: can't modify read-only parameter
+
+ () {
+ print ${|
+ private q
+ () { q=nofork }
+ }
+ }
+1:function may not access private declared in nofork
+*?*: q: can't modify read-only parameter
+
%clean
+ unsetopt TYPESET_TO_UNSET
rm -r private.TMP
diff --git a/Test/V12zparseopts.ztst b/Test/V12zparseopts.ztst
index 816e1d041..e6139ea5e 100644
--- a/Test/V12zparseopts.ztst
+++ b/Test/V12zparseopts.ztst
@@ -104,6 +104,14 @@
>ret: 0, optv: --aaa bar, opts: --aaa bar, argv: 1 2 3
() {
+ local -a optv argvv=( -a -b -c 1 2 3 )
+ zparseopts -D -a optv -v argvv - a b c
+ print -r - ret: $?, optv: $optv, argvv: $argvv, argv: $argv
+ } -x -y -z 7 8 9
+0:zparseopts -v
+>ret: 0, optv: -a -b -c, argvv: 1 2 3, argv: -x -y -z 7 8 9
+
+ () {
local -a optv aa ab
zparseopts -a optv - a=aa b:=ab c:- z
print -r - ret: $?, optv: $optv, aa: $aa, ab: $ab, argv: $argv
@@ -151,16 +159,35 @@
>ret: 0, optv: -+ -: -= -\, argv: 1 2 3
>ret: 0, optv: --::: foo, argv: 1 2 3
- for specs in '-foo: -foobar' '-foobar -foo:'; do
+ for specs in \
+ '-foo: -foobar' '-foobar -foo:' '-foo: -foobar:' '-foobar: -foo:'
+ do
() {
local -a optv
- zparseopts -a optv - $=specs
+ zparseopts -a optv -D - $=specs
print -r - ret: $?, optv: $optv, argv: $argv
} --foobar 1 2 3
done
-0:overlapping option specs (scan order)
->ret: 0, optv: --foobar, argv: --foobar 1 2 3
->ret: 0, optv: --foo bar, argv: --foobar 1 2 3
+0:overlapping option specs without -G (scan order)
+>ret: 0, optv: --foobar, argv: 1 2 3
+>ret: 0, optv: --foo bar, argv: 1 2 3
+>ret: 0, optv: --foobar 1, argv: 2 3
+>ret: 0, optv: --foo bar, argv: 1 2 3
+
+ for specs in \
+ '-foo: -foobar' '-foobar -foo:' '-foo: -foobar:' '-foobar: -foo:'
+ do
+ () {
+ local -a optv
+ zparseopts -a optv -D -G - $=specs
+ print -r - ret: $?, optv: $optv, argv: $argv
+ } --foobar 1 2 3
+ done
+0:overlapping option specs with -G (scan order)
+>ret: 0, optv: --foobar, argv: 1 2 3
+>ret: 0, optv: --foobar, argv: 1 2 3
+>ret: 0, optv: --foobar 1, argv: 2 3
+>ret: 0, optv: --foobar 1, argv: 2 3
() {
local -a optv
@@ -170,3 +197,172 @@
0:missing optarg
?(anon):zparseopts:2: missing argument for option: -c
>ret: 1, optv: , argv: -ab1 -c
+
+ for spec in -foo: -foo:- -foo::; do
+ () {
+ local -a optv
+ zparseopts -a optv -D -F - $=spec
+ print -r - ret: $?, optv: $optv, argv: $argv
+ } --foo=bar 1 2 3
+ done
+0:zparseopts without -G, =optarg handling
+>ret: 0, optv: --foo =bar, argv: 1 2 3
+>ret: 0, optv: --foo=bar, argv: 1 2 3
+>ret: 0, optv: --foo=bar, argv: 1 2 3
+
+ for spec in -foo: -foo:- -foo::; do
+ () {
+ local -a optv
+ zparseopts -a optv -D -F -G - $=spec
+ print -r - ret: $?, optv: $optv, argv: $argv
+ } --foo=bar 1 2 3
+ done
+0:zparseopts -G, single parameter, with =
+>ret: 0, optv: --foo bar, argv: 1 2 3
+>ret: 0, optv: --foo=bar, argv: 1 2 3
+>ret: 0, optv: --foo=bar, argv: 1 2 3
+
+ for spec in -foo: -foo:- -foo:: ; do
+ () {
+ local -a optv
+ zparseopts -a optv -D -F -G - $=spec
+ print -r - ret: $?, optv: $optv, argv: $argv
+ } --foobar 1 2 3
+ done
+0:zparseopts -G, single parameter, without =
+?(anon):zparseopts:2: bad option: --foobar
+>ret: 1, optv: , argv: --foobar 1 2 3
+?(anon):zparseopts:2: bad option: --foobar
+>ret: 1, optv: , argv: --foobar 1 2 3
+?(anon):zparseopts:2: bad option: --foobar
+>ret: 1, optv: , argv: --foobar 1 2 3
+
+ for spec in -foo: -foo:- -foo::; do
+ () {
+ local -a optv
+ zparseopts -a optv -D -F -G - $=spec
+ print -r - ret: $?, optv: $optv, argv: $argv
+ } --foo bar 1 2 3
+ done
+0:zparseopts -G, separate parameters
+>ret: 0, optv: --foo bar, argv: 1 2 3
+>ret: 0, optv: --foo=bar, argv: 1 2 3
+>ret: 0, optv: --foo=, argv: bar 1 2 3
+
+ for spec in -foo: -foo:- -foo::; do
+ () {
+ local -a optv
+ zparseopts -a optv -D -F -G - $=spec
+ print -r - ret: $?, optv: ${(j< >)${(q+)optv}}, argv: $argv
+ } --foo= 1 2 3
+ done
+0:zparseopts -G, empty optarg
+>ret: 0, optv: --foo '', argv: 1 2 3
+>ret: 0, optv: '--foo=', argv: 1 2 3
+>ret: 0, optv: '--foo=', argv: 1 2 3
+
+ for gopt in '' -G; do
+ for spec args in \
+ f: '-fbar 1 2 3' \
+ f: '-f=bar 1 2 3' \
+ f: '-f bar 1 2 3' \
+ f:- '-fbar 1 2 3' \
+ f:- '-f=bar 1 2 3' \
+ f:- '-f bar 1 2 3' \
+ f:: '-fbar 1 2 3' \
+ f:: '-f=bar 1 2 3' \
+ f:: '-f bar 1 2 3'
+ do
+ () {
+ local -a optv
+ zparseopts -a optv -D -F $gopt - $=spec
+ print -r - ret: $?, gopt: $gopt, optv: $optv, argv: $argv
+ } $=args
+ done
+ done
+0:short options, with and without -G
+>ret: 0, gopt: , optv: -f bar, argv: 1 2 3
+>ret: 0, gopt: , optv: -f =bar, argv: 1 2 3
+>ret: 0, gopt: , optv: -f bar, argv: 1 2 3
+>ret: 0, gopt: , optv: -fbar, argv: 1 2 3
+>ret: 0, gopt: , optv: -f=bar, argv: 1 2 3
+>ret: 0, gopt: , optv: -fbar, argv: 1 2 3
+>ret: 0, gopt: , optv: -fbar, argv: 1 2 3
+>ret: 0, gopt: , optv: -f=bar, argv: 1 2 3
+>ret: 0, gopt: , optv: -fbar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -f bar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -f =bar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -f bar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -fbar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -f=bar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -fbar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -fbar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -f=bar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -f, argv: bar 1 2 3
+
+ for gopt in '' -G; do
+ for spec args in \
+ foo: '-foobar 1 2 3' \
+ foo: '-foo=bar 1 2 3' \
+ foo: '-foo bar 1 2 3' \
+ foo:- '-foobar 1 2 3' \
+ foo:- '-foo=bar 1 2 3' \
+ foo:- '-foo bar 1 2 3' \
+ foo:: '-foobar 1 2 3' \
+ foo:: '-foo=bar 1 2 3' \
+ foo:: '-foo bar 1 2 3'
+ do
+ () {
+ local -a optv
+ zparseopts -a optv -D -F $gopt - $=spec
+ print -r - ret: $?, gopt: $gopt, optv: $optv, argv: $argv
+ } $=args
+ done
+ done
+0:Sun-style long options, with and without -G
+>ret: 0, gopt: , optv: -foo bar, argv: 1 2 3
+>ret: 0, gopt: , optv: -foo =bar, argv: 1 2 3
+>ret: 0, gopt: , optv: -foo bar, argv: 1 2 3
+>ret: 0, gopt: , optv: -foobar, argv: 1 2 3
+>ret: 0, gopt: , optv: -foo=bar, argv: 1 2 3
+>ret: 0, gopt: , optv: -foobar, argv: 1 2 3
+>ret: 0, gopt: , optv: -foobar, argv: 1 2 3
+>ret: 0, gopt: , optv: -foo=bar, argv: 1 2 3
+>ret: 0, gopt: , optv: -foobar, argv: 1 2 3
+?(anon):zparseopts:2: bad option: -f
+>ret: 1, gopt: -G, optv: , argv: -foobar 1 2 3
+>ret: 0, gopt: -G, optv: -foo bar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -foo bar, argv: 1 2 3
+?(anon):zparseopts:2: bad option: -f
+>ret: 1, gopt: -G, optv: , argv: -foobar 1 2 3
+>ret: 0, gopt: -G, optv: -foo=bar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -foo=bar, argv: 1 2 3
+?(anon):zparseopts:2: bad option: -f
+>ret: 1, gopt: -G, optv: , argv: -foobar 1 2 3
+>ret: 0, gopt: -G, optv: -foo=bar, argv: 1 2 3
+>ret: 0, gopt: -G, optv: -foo=, argv: bar 1 2 3
+
+ for term in - --; do
+ # With -D -E -G
+ () {
+ local -a optv
+ zparseopts -a optv -D -E -F -G - -foo -bar
+ print -r - ret: $?, term: $term, optv: $optv, argv: $argv
+ } --foo x --bar $term --baz
+ for gopt in '' -G; do
+ # With -D + with/without -G
+ () {
+ local -a optv
+ zparseopts -a optv -D -F $gopt - -foo -bar
+ print -r - ret: $?, term: $term, gopt: $gopt, optv: $optv, argv: $argv
+ } --foo $term --bar
+ done
+ done
+0:only -- acts as explicit parsing terminator with -G
+?(anon):zparseopts:2: bad option: --baz
+>ret: 1, term: -, optv: , argv: --foo x --bar - --baz
+>ret: 0, term: -, gopt: , optv: --foo, argv: --bar
+>ret: 0, term: -, gopt: -G, optv: --foo, argv: - --bar
+>ret: 0, term: --, optv: --foo --bar, argv: x -- --baz
+>ret: 0, term: --, gopt: , optv: --foo, argv: --bar
+>ret: 0, term: --, gopt: -G, optv: --foo, argv: --bar
diff --git a/Test/X02zlevi.ztst b/Test/X02zlevi.ztst
index 8146d6752..ccfb7b1c6 100644
--- a/Test/X02zlevi.ztst
+++ b/Test/X02zlevi.ztst
@@ -1,16 +1,7 @@
# Tests of the vi mode of ZLE
%prep
- unset -m LC_\*
- ZSH_TEST_LANG=
- langs=(en_{US,GB}.{UTF-,utf}8 en.UTF-8
- $(locale -a 2>/dev/null | egrep 'utf8|UTF-8'))
- for LANG in $langs; do
- if [[ é = ? ]]; then
- ZSH_TEST_LANG=$LANG
- break;
- fi
- done
+ ZSH_TEST_LANG=$(ZTST_find_UTF8)
if ( zmodload zsh/zpty 2>/dev/null ); then
. $ZTST_srcdir/comptest
comptestinit -v -z $ZTST_testdir/../Src/zsh
@@ -605,6 +596,13 @@
>BUFFER: 1ls `2` $(3) "4" $'5' ${6}
>CURSOR: 0
+ zpty_run 'bindkey -s -a "cw" "dwi"'
+ zletest $'one two\e0cwyksi'
+ zpty_run 'bindkey -r -a "cw"'
+0:for a vi command, wait to allow a longer binding to be used
+>BUFFER: yksitwo
+>CURSOR: 4
+
%clean
zmodload -ui zsh/zpty
diff --git a/Test/X03zlebindkey.ztst b/Test/X03zlebindkey.ztst
index 43692a85b..1b63b3920 100644
--- a/Test/X03zlebindkey.ztst
+++ b/Test/X03zlebindkey.ztst
@@ -3,16 +3,7 @@
# into bindings. The latter is particularly tricky with multibyte sequences.
%prep
- unset -m LC_\*
- ZSH_TEST_LANG=
- langs=(en_{US,GB}.{UTF-,utf}8 en.UTF-8
- $(locale -a 2>/dev/null | egrep 'utf8|UTF-8'))
- for LANG in $langs; do
- if [[ é = ? ]]; then
- ZSH_TEST_LANG=$LANG
- break;
- fi
- done
+ ZSH_TEST_LANG=$(ZTST_find_UTF8)
if ( zmodload zsh/zpty 2>/dev/null ); then
. $ZTST_srcdir/comptest
comptestinit -z $ZTST_testdir/../Src/zsh
@@ -46,6 +37,28 @@
>"^Xy" "bar"
>"^Xy" undefined-key
+ zpty_run 'bindkey -s "\e[" altbracket'
+ zletest $'$\C-A\e[17~'
+ zpty_run 'bindkey -r "\e["'
+0:binding to CSI introduction is not used if a full sequence arrives
+>BUFFER: $
+>CURSOR: 0
+
+ zpty_run 'bindkey -s "\e[1" altbracketone'
+ zletest $'$\C-A\e[17~'
+ zpty_run 'bindkey -r "\e[1"'
+0:binding to longer prefix of a CSI sequence is used
+# we assume the user knows what they're doing
+>BUFFER: altbracketone7~$
+>CURSOR: 15
+
+ zpty_run 'bindkey -s "\e[" altbracket'
+ zletest $'$\C-A\e[177'
+ zpty_run 'bindkey -r "\e["'
+0:use prefix binding where we don't have a CSI sequence
+>BUFFER: altbracket177$
+>CURSOR: 13
+
# As we're only looking at definitions here, we don't
# bother using the pseudo-terminal; just test in the normal fashion.
bindkey -e
diff --git a/Test/X04zlehighlight.ztst b/Test/X04zlehighlight.ztst
index f84c02505..87a59fde5 100644
--- a/Test/X04zlehighlight.ztst
+++ b/Test/X04zlehighlight.ztst
@@ -40,7 +40,7 @@
# Fix e^Mexit - match ((?)\r(?)), if \2 == \3, then replace with \2
# otherwise replace with \1 stripped out of leading/trailing [[:space:]]
REPLY=${REPLY//(#b)((?(#c0,1))$cm(?(#c0,1)))/${${${(M)match[2]:#${match[3]}}:+${match[2]}}:-${${match[1]##[[:space:]]##}%%[[:space:]]##}}}
- [[ -n "$REPLY" ]] && print -r -- ${${REPLY%%[[:space:]]##}##[[:space:]]##}
+ [[ -n "$REPLY" ]] && print -r -- ${${REPLY%%${~cm}*}##[[:space:]]##}
done
}
zpty_stop() {
@@ -79,7 +79,7 @@
zpty_line 1 p # the line of interest, preserving escapes ("p")
zpty_stop
0:region highlight - standout overlapping on other region_highlight entry
->0m27m24mtr7mu27me word2 word3
+>0mtr7mu0me word2 word3
zpty_start
zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=green" ); }'
@@ -90,7 +90,7 @@
zpty_line 1 p # the line of interest, preserving escapes ("p")
zpty_stop
0:basic region_highlight with 8 colors
->0m27m24mCDE|32|trueCDE|39|
+>0mCDE|32|true
zpty_start
zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=someplugin" ); typeset -p region_highlight }'
@@ -145,7 +145,7 @@
zpty_line 1 p # the line of interest, preserving escapes ("p")
zpty_stop
0:basic region_highlight with true-color (hex-triplets)
->0m27m24m38;2;4;8;16mtrueCDE|39|
+>0m38;2;4;8;16mtrue
zpty_start
zpty_input 'zmodload zsh/nearcolor'
@@ -157,7 +157,7 @@
zpty_line 1 p # the line of interest, preserving escapes ("p")
zpty_stop
0:basic region_highlight with near-color (hex-triplets at input)
->0m27m24mCDE|3232|trueCDE|39|
+>0mCDE|3232|true
zpty_start
zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=green" ); rh2; }'
@@ -169,7 +169,7 @@
zpty_line 1 p # the line of interest, preserving escapes ("p")
zpty_stop
0:overlapping region_highlight with 8 colors
->0m27m24mCDE|32|tCDE|31|rCDE|39|CDE|32|ueCDE|39|
+>0mCDE|32|tCDE|31|rCDE|32|ue
zpty_start
zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=#00cc00" ); rh2; }'
@@ -181,7 +181,7 @@
zpty_line 1 p # the line of interest, preserving escapes ("p")
zpty_stop
0:overlapping region_highlight with true-color
->0m27m24m38;2;0;204;0mt38;2;204;0;0mrCDE|39|38;2;0;204;0mueCDE|39|
+>0m38;2;0;204;0mt38;2;204;0;0mr38;2;0;204;0mue
zpty_start
zpty_input 'zmodload zsh/nearcolor'
@@ -194,7 +194,7 @@
zpty_line 1 p # the line of interest, preserving escapes ("p")
zpty_stop
0:overlapping region_highlight with near-color (hex-triplets at input)
->0m27m24mCDE|340|tCDE|3160|rCDE|39|CDE|340|ueCDE|39|
+>0mCDE|340|tCDE|3160|rCDE|340|ue
zpty_start
zpty_input 'f () { zle clear-screen; zle g -f nolast; BUFFER=": ${(q)LASTWIDGET}" }; zle -N f'
@@ -205,7 +205,7 @@
zpty_line 1 p
zpty_stop
0:zle $widgetname -f nolast
->0m27m24m0m27m24m: clear-screen
+>0m0m: clear-screen
%clean
diff --git a/Test/X05zleincarg.ztst b/Test/X05zleincarg.ztst
new file mode 100644
index 000000000..f712e9426
--- /dev/null
+++ b/Test/X05zleincarg.ztst
@@ -0,0 +1,562 @@
+# Tests the incarg ZLE widget
+
+%prep
+ ZSH_TEST_LANG=$(ZTST_find_UTF8)
+ if ( zmodload zsh/zpty 2>/dev/null ); then
+ . $ZTST_srcdir/comptest
+ comptestinit -v -z $ZTST_testdir/../Src/zsh
+ zpty_run '
+ autoload -Uz incarg
+ for name in {,vim-,vim-backward-}{,sync-}{inc,dec}arg; do
+ zle -N "$name" incarg
+ done
+ bindkey -v "^N" incarg
+ bindkey -v "^P" decarg
+ bindkey -v "^F" sync-incarg
+ bindkey -v "^B" sync-decarg
+ bindkey -a "^N" vim-incarg
+ bindkey -a "^P" vim-decarg
+ bindkey -a "^F" vim-sync-incarg
+ bindkey -a "^B" vim-sync-decarg
+ bindkey -a "^E" vim-backward-incarg
+ bindkey -a "^Y" vim-backward-decarg
+ unset TMUX_PANE ITERM_SESSION_ID
+ tmux() {
+ echo "$TMUX_PANE"
+ }
+ '
+ else
+ ZTST_unimplemented="the zsh/zpty module is not available"
+ fi
+
+%test
+
+# Basic increment & decrement
+
+ zletest $'0\C-n'
+0:incarg increments an integer
+>BUFFER: 1
+>CURSOR: 1
+
+ zletest $'0\C-p'
+0:decarg decrements an integer
+>BUFFER: -1
+>CURSOR: 2
+
+ zletest $'echo 0\e0\C-n'
+0:vim-incarg increments an integer
+>BUFFER: echo 1
+>CURSOR: 5
+
+ zletest $'echo 0\e0\C-p'
+0:vim-decarg decrements an integer
+>BUFFER: echo -1
+>CURSOR: 6
+
+ zletest $'echo 0 foo\e\C-e'
+0:vim-backward-incarg increments an integer
+>BUFFER: echo 1 foo
+>CURSOR: 5
+
+ zletest $'echo 0 foo\e\C-y'
+0:vim-backward-decarg decrements an integer
+>BUFFER: echo -1 foo
+>CURSOR: 6
+
+# sync- variants
+
+ zletest $'0\C-f'
+0:sync-incarg does nothing on unsupported terminals
+>BUFFER: 0
+>CURSOR: 1
+
+ zpty_run 'TMUX_PANE=0'
+ zletest $'0\C-f'
+ zpty_run 'unset TMUX_PANE'
+0:sync-incarg does nothing on tmux in pane 0
+>BUFFER: 0
+>CURSOR: 1
+
+ zpty_run 'TMUX_PANE=1'
+ zletest $'0\C-f'
+ zpty_run 'unset TMUX_PANE'
+0:sync-incarg increments by 1 on tmux in pane 1
+>BUFFER: 1
+>CURSOR: 1
+
+ zpty_run 'TMUX_PANE=2'
+ zletest $'0\C-f'
+ zpty_run 'unset TMUX_PANE'
+0:sync-incarg increments by 2 on tmux in pane 2
+>BUFFER: 2
+>CURSOR: 1
+
+ zpty_run 'ITERM_SESSION_ID=w0t0p0:00000000-0000-0000-0000-000000000000'
+ zletest $'0\C-f'
+ zpty_run 'unset ITERM_SESSION_ID'
+0:sync-incarg does nothing on tmux in pane 0
+>BUFFER: 0
+>CURSOR: 1
+
+ zpty_run 'ITERM_SESSION_ID=w0t0p1:00000000-0000-0000-0000-000000000000'
+ zletest $'0\C-f'
+ zpty_run 'unset ITERM_SESSION_ID'
+0:sync-incarg increments by 1 on tmux in pane 1
+>BUFFER: 1
+>CURSOR: 1
+
+ zpty_run 'ITERM_SESSION_ID=w0t0p2:00000000-0000-0000-0000-000000000000'
+ zletest $'0\C-f'
+ zpty_run 'unset ITERM_SESSION_ID'
+0:sync-incarg increments by 2 on tmux in pane 2
+>BUFFER: 2
+>CURSOR: 1
+
+ zpty_run 'TMUX_PANE=1'
+ zpty_run 'ITERM_SESSION_ID=w0t0p2:00000000-0000-0000-0000-000000000000'
+ zletest $'0\C-f'
+ zpty_run 'unset TMUX_PANE ITERM_SESSION_ID'
+0:sync-incarg prioritizes tmux pane number over iTerm2's
+>BUFFER: 1
+>CURSOR: 1
+
+ zletest $'0\e2\C-n'
+0:incarg changes the incremented amount based on the numeric argument
+>BUFFER: 2
+>CURSOR: 0
+
+ zpty_run 'incarg=3'
+ zletest $'0\e\C-n'
+ zpty_run 'unset incarg'
+0:incarg changes the default incremented amount based on the incarg variable
+>BUFFER: 3
+>CURSOR: 0
+
+ zpty_run 'incarg=3'
+ zletest $'0\e2\C-n'
+ zpty_run 'unset incarg'
+0:incarg prioritizes the numeric argument over the incarg variable
+>BUFFER: 2
+>CURSOR: 0
+
+ zpty_run 'TMUX_PANE=2'
+ zletest $'0\e2\C-f'
+ zpty_run 'unset TMUX_PANE'
+0:The sync- variants of incarg takes the numeric argument into account
+>BUFFER: 4
+>CURSOR: 0
+
+# Leading zeros
+
+ zletest $'000\C-n'
+0:incarg preserves leading zeros of decimal integers
+>BUFFER: 001
+>CURSOR: 3
+
+ zletest $'-001\C-n\C-n'
+0:incarg preserves leading zeros when the digit turns from negative to positive
+>BUFFER: 001
+>CURSOR: 3
+
+ zletest $'001\C-p\C-p'
+0:incarg preserves leading zeros when the digit turns from positive to negative
+>BUFFER: -001
+>CURSOR: 4
+
+ zletest $'001\e1000\C-n'
+0:incarg works when the result has more number of digits than the original
+>BUFFER: 1001
+>CURSOR: 3
+
+ zletest $'001\e2000\C-p'
+0:decargs works on integers with leading zeros when the result has more digits than the original
+>BUFFER: -1999
+>CURSOR: 4
+
+ zletest $'-000\C-n'
+0:incarg produces the correct number of zeros when incrementing integers starting with -0
+>BUFFER: 001
+>CURSOR: 3
+
+ zletest $'-000\C-p'
+0:decarg produces the correct number of zeros when incrementing integers starting with -0
+>BUFFER: -001
+>CURSOR: 4
+
+ zpty_run 'incarg=0'
+ zletest $'-000\C-n'
+ zpty_run 'unset incarg'
+0:incarg removes the sign when the target integer starts with -0 and the increment amount is 0
+>BUFFER: 000
+>CURSOR: 3
+
+ zletest $'-0\C-n'
+0:incarg turns -0 into 1
+>BUFFER: 1
+>CURSOR: 1
+
+ zletest $'-0\C-p'
+0:decarg turns -0 into -1
+>BUFFER: -1
+>CURSOR: 2
+
+ zpty_run 'incarg=0'
+ zletest $'-0\C-n'
+ zpty_run 'unset incarg'
+0:incarg turns -0 into 0 when the increment amount is 0
+>BUFFER: 0
+>CURSOR: 1
+
+# Binaries
+
+ zletest $'0b11\C-n'
+0:incarg can increment a binary integer
+>BUFFER: 0b100
+>CURSOR: 5
+
+ zletest $'0B11\C-n'
+0:incarg can increment a binary integer with an upper case prefix
+>BUFFER: 0B100
+>CURSOR: 5
+
+ zletest $'0b100\C-p'
+0:decarg can decrement a binary integer
+>BUFFER: 0b11
+>CURSOR: 4
+
+ zletest $'0b0011\C-n'
+0:incarg can preserve leading zeros of binaries
+>BUFFER: 0b0100
+>CURSOR: 6
+
+ zletest $'0b001\e8\C-n'
+0:incarg works on binaries when the result has more zeros than the original
+>BUFFER: 0b1001
+>CURSOR: 5
+
+ zletest $'0b0\C-p'
+0:decarg fails to produce a negative binary value
+>BUFFER: 0b0
+>CURSOR: 3
+
+# Octals
+
+ zletest $'0o7\C-n'
+0:incarg can increment an octal integer
+>BUFFER: 0o10
+>CURSOR: 4
+
+ zletest $'0O7\C-n'
+0:incarg can increment an octal integer with an upper case prefix
+>BUFFER: 0O10
+>CURSOR: 4
+
+ zletest $'0o10\C-p'
+0:decarg can decrement an octal integer
+>BUFFER: 0o7
+>CURSOR: 3
+
+ zletest $'0o0\C-p'
+0:decarg fails to produce a negative octal value
+>BUFFER: 0o0
+>CURSOR: 3
+
+# Hexadecimals
+
+ zletest $'0x9\C-n'
+0:incarg can increment a hexadecimal integer
+>BUFFER: 0xa
+>CURSOR: 3
+
+ zletest $'0X9\C-n'
+0:incarg can increment a hexadecimal integer with an upper case prefix
+>BUFFER: 0XA
+>CURSOR: 3
+
+ zletest $'0xf\C-n'
+0:incarg can increment a hexadecimal integer with no numeric digit
+>BUFFER: 0x10
+>CURSOR: 4
+
+ zletest $'0x10\C-p'
+0:decarg can decrement a hexadecimal integer
+>BUFFER: 0xf
+>CURSOR: 3
+
+ zletest $'0x0\C-p'
+0:decarg fails to produce a negative hexadecimal value
+>BUFFER: 0x0
+>CURSOR: 3
+
+ zletest $'0x0b1\C-n'
+0:incarg interprets integers starting with 0x0b as a hexadecimal
+>BUFFER: 0x0b2
+>CURSOR: 5
+
+ zletest $'0x0b1\e\C-e'
+0:vim-backward-incarg interprets integers starting with 0x0b as a hexadecimal
+>BUFFER: 0x0b2
+>CURSOR: 4
+
+# Cursor position - incarg
+
+ zletest $'echo 012ab\eF i\C-n'
+0:incarg does nothing when the cursor is placed just to the left of an integer
+>BUFFER: echo 012ab
+>CURSOR: 4
+
+ zletest $'echo 012ab\eF0i\C-n'
+0:incarg works when the cursor is placed at the leftmost digit of an integer
+>BUFFER: echo 013ab
+>CURSOR: 8
+
+ zletest $'echo 012ab\eF1i\C-n'
+0:incarg works when the cursor is placed at the inner digit of an integer
+>BUFFER: echo 013ab
+>CURSOR: 8
+
+ zletest $'echo 012ab\eF2i\C-n'
+0:incarg works when the cursor is placed at the rightmost digit of an integer
+>BUFFER: echo 013ab
+>CURSOR: 8
+
+ zletest $'echo 012ab\eFai\C-n'
+0:incarg works when the cursor is placed just to the right of an integer
+>BUFFER: echo 013ab
+>CURSOR: 8
+
+ zletest $'echo 012ab\ei\C-n'
+0:incarg does nothing when the cursor is placed more than a single letter away to the right
+>BUFFER: echo 012ab
+>CURSOR: 9
+
+ zletest $'10x9\e0\C-n'
+0:incarg turns [0-9]0x[0-9a-f] into [0-9]1x[0-9a-f] when the cursor is at the left of x
+>BUFFER: 11x9
+>CURSOR: 1
+
+ zletest $'10x9\eFx\C-n'
+0:incarg takes [0-9]0x[0-9a-f] and increments the hexadecimal part when the cursor is on x
+>BUFFER: 10xa
+>CURSOR: 3
+
+ zletest $'10x9\e\C-n'
+0:incarg takes [0-9]0x[0-9a-f] and increments the hexadecimal part when the cursor is at the right of x
+>BUFFER: 10xa
+>CURSOR: 3
+
+ zletest $'10b1\e0\C-n'
+0:incarg turns [0-9]0b[01] into [0-9]1b[01] when the cursor is at the left of b
+>BUFFER: 11b1
+>CURSOR: 1
+
+ zletest $'10b1\eFb\C-n'
+0:incarg takes [0-9]0b[01] and increments the binary part when the cursor is on b
+>BUFFER: 10b10
+>CURSOR: 4
+
+ zletest $'10b1\e\C-n'
+0:incarg takes [0-9]0b[01] and increments binary part when the cursor is at the right of b
+>BUFFER: 10b10
+>CURSOR: 4
+
+ zletest $'10o7\e0\C-n'
+0:incarg turns [0-9]0o[0-7] into [0-9]1o[0-7] when the cursor is at the left of o
+>BUFFER: 11o7
+>CURSOR: 1
+
+ zletest $'10o7\eFo\C-n'
+0:incarg takes [0-9]0o[0-7] and increments the octal part when the cursor is on o
+>BUFFER: 10o10
+>CURSOR: 4
+
+ zletest $'10o7\e\C-n'
+0:incarg takes [0-9]0o[0-7] and increments the octal part when the cursor is at the right of o
+>BUFFER: 10o10
+>CURSOR: 4
+
+ zletest $'0b0x9\eF0\C-n'
+0:incarg takes 0b0x[0-9a-f] and increments the binary part when the cursor is at the left of x
+>BUFFER: 0b1x9
+>CURSOR: 2
+
+ zletest $'0b0x9\eFx\C-n'
+0:incarg takes 0b0x[0-9a-f] and increments the hexadecimal part when the cursor is on x
+>BUFFER: 0b0xa
+>CURSOR: 4
+
+ zletest $'0b0x9\e\C-n'
+0:incarg takes 0b0x[0-9a-f] and increments the hexadecimal part when the cursor is at the right of x
+>BUFFER: 0b0xa
+>CURSOR: 4
+
+# Cursor position - vim-incarg
+
+ zletest $'echo 012ab\eF \C-n'
+0:vim-incarg works when the cursor is placed to the left of an integer
+>BUFFER: echo 013ab
+>CURSOR: 7
+
+ zletest $'echo 012ab\eF0\C-n'
+0:vim-incarg works when the cursor is placed at the leftmost digit of an integer
+>BUFFER: echo 013ab
+>CURSOR: 7
+
+ zletest $'echo 012ab\eF1\C-n'
+0:vim-incarg works when the cursor is placed at the inner digit of an integer
+>BUFFER: echo 013ab
+>CURSOR: 7
+
+ zletest $'echo 012ab\eF2\C-n'
+0:incarg works when the cursor is placed at the rightmost digit of an integer
+>BUFFER: echo 013ab
+>CURSOR: 7
+
+ zletest $'echo 012ab\eFa\C-n'
+0:vim-incarg does nothing when the cursor is placed to the right of an integer
+>BUFFER: echo 012ab
+>CURSOR: 8
+
+ zletest $'echo 012ab\ei\C-n'
+0:vim-incarg does nothing when the cursor is placed more than a single letter away to the right
+>BUFFER: echo 012ab
+>CURSOR: 9
+
+# Cursor position - vim-backward-incarg
+
+ zletest $'echo 012ab\eF \C-e'
+0:vim-backward-incarg does nothing when the cursor is placed just to the left of an integer
+>BUFFER: echo 012ab
+>CURSOR: 4
+
+ zletest $'echo 012ab\eF0\C-e'
+0:vim-backward-incarg works when the cursor is placed at the leftmost digit of an integer
+>BUFFER: echo 013ab
+>CURSOR: 7
+
+ zletest $'echo 012ab\eF1\C-e'
+0:vim-backward-incarg works when the cursor is placed at the inner digit of an integer
+>BUFFER: echo 013ab
+>CURSOR: 7
+
+ zletest $'echo 012ab\eF2\C-e'
+0:vim-backward-incarg works when the cursor is placed at the rightmost digit of an integer
+>BUFFER: echo 013ab
+>CURSOR: 7
+
+ zletest $'echo 012ab\eFa\C-e'
+0:vim-backward-incarg works when the cursor is placed just to the right of an integer
+>BUFFER: echo 013ab
+>CURSOR: 7
+
+ zletest $'echo 012ab\e\C-e'
+0:vim-backward-incarg works when the cursor is placed more than a single letter away to the right
+>BUFFER: echo 013ab
+>CURSOR: 7
+
+ zletest $'10x9\eFx\C-e'
+0:vim-backward-incarg will take [0-9]0x[0-9a-f] and increment the hexadecimal part when the cursor is on x
+>BUFFER: 10xa
+>CURSOR: 3
+
+ zletest $'10x9\e\C-e'
+0:vim-backward-incarg will take [0-9]0x[0-9a-f] and increment the hexadecimal part when the cursor is on the right of x
+>BUFFER: 10xa
+>CURSOR: 3
+
+ zletest $'10b1\e0\C-e'
+0:vim-backward-incarg will turn [0-9]0b[01] into [0-9]1b[01] when the cursor is at the left of b
+>BUFFER: 11b1
+>CURSOR: 1
+
+ zletest $'10b1\eFb\C-e'
+0:vim-backward-incarg will take [0-9]0b[01] and increment the binary part when the cursor is on b
+>BUFFER: 10b10
+>CURSOR: 4
+
+ zletest $'10b1\e\C-e'
+0:vim-backward-incarg will take [0-9]0b[01] and increment the binary part when the cursor is on the right of b
+>BUFFER: 10b10
+>CURSOR: 4
+
+ zletest $'10o7\e0\C-e'
+0:vim-backward-incarg will turn [0-9]0o[0-7] into [0-9]1o[0-7] when the cursor is at the left of o
+>BUFFER: 11o7
+>CURSOR: 1
+
+ zletest $'10o7\eFo\C-e'
+0:vim-backward-incarg will take [0-9]0o[0-7] and increment the octal part when the cursor is on o
+>BUFFER: 10o10
+>CURSOR: 4
+
+ zletest $'10o7\e\C-e'
+0:vim-backward-incarg will take [0-9]0o[0-7] and increment the octal part when the cursor is at the right of o
+>BUFFER: 10o10
+>CURSOR: 4
+
+ zletest $'0b0x9\eF0\C-e'
+0:vim-backward-incarg will take 0b0x[0-9a-f] and increment the binary 0b0 when the cursor is on the left of x
+>BUFFER: 0b1x9
+>CURSOR: 2
+
+ zletest $'0b0x9\eFx\C-e'
+0:vim-backward-incarg will take 0b0x[0-9a-f] and increment the hexadecimal part when the cursor is on x
+>BUFFER: 0b0xa
+>CURSOR: 4
+
+ zletest $'0b0x9\e\C-e'
+0:vim-backward-incarg will take 0b0x[0-9a-f] and increment the hexadecimal part when the cursor is at the right of x
+>BUFFER: 0b0xa
+>CURSOR: 4
+
+# Repeats
+
+ zletest $'echo 0\e0\C-n.'
+0:vim-incarg is compatible with the repeat command
+>BUFFER: echo 2
+>CURSOR: 5
+
+ zletest $'echo 0\e0\C-p.'
+0:vim-decarg is compatible with the repeat command
+>BUFFER: echo -2
+>CURSOR: 6
+
+ zletest $'echo 0 foo\e\C-e.'
+0:vim-backward-incarg is compatible with the repeat command
+>BUFFER: echo 2 foo
+>CURSOR: 5
+
+ zletest $'echo 0\e010\C-n.'
+0:Repeats of vim-incarg takes the numeric argument into account
+>BUFFER: echo 20
+>CURSOR: 6
+
+ zletest $'echo 0 foo\e10\C-e.'
+0:Repeats of vim-backward-incarg takes the numeric argument into account
+>BUFFER: echo 20 foo
+>CURSOR: 6
+
+ zpty_run 'TMUX_PANE=0'
+ zletest $'echo 0\e0\C-f.'
+ zpty_run 'unset TMUX_PANE'
+0:Repeats of vim-sync-incarg work in pane 0
+>BUFFER: echo 0
+>CURSOR: 5
+
+ zpty_run 'TMUX_PANE=1'
+ zletest $'echo 0\e0\C-f.'
+ zpty_run 'unset TMUX_PANE'
+0:Repeats of vim-sync-incarg work in pane 1
+>BUFFER: echo 2
+>CURSOR: 5
+
+ zpty_run 'TMUX_PANE=2'
+ zletest $'echo 0\e0\C-f.'
+ zpty_run 'unset TMUX_PANE'
+0:Repeats of vim-sync-incarg work in pane 2
+>BUFFER: echo 4
+>CURSOR: 5
+
+%clean
+
+ zmodload -ui zsh/zpty
diff --git a/Test/Y01completion.ztst b/Test/Y01completion.ztst
index 6af0efc6d..769b8c9e4 100644
--- a/Test/Y01completion.ztst
+++ b/Test/Y01completion.ztst
@@ -1,16 +1,7 @@
# Tests for completion system.
%prep
- unset -m LC_\*
- ZSH_TEST_LANG=
- langs=(en_{US,GB}.{UTF-,utf}8 en.UTF-8
- $(locale -a 2>/dev/null | egrep 'utf8|UTF-8'))
- for LANG in $langs; do
- if [[ é = ? ]]; then
- ZSH_TEST_LANG=$LANG
- break;
- fi
- done
+ ZSH_TEST_LANG=$(ZTST_find_UTF8)
if ( zmodload zsh/zpty 2>/dev/null ); then
. $ZTST_srcdir/comptest
mkdir comp.tmp
@@ -44,6 +35,54 @@
>line: {: dir1/}{}
>line: {: dir2/}{}
+ comptest $': d\t\t\t\t\t \t'
+0:unambiguous prefix and autoremovable suffix
+>line: {: dir}{}
+>line: {: dir}{}
+>DESCRIPTION:{file}
+>DI:{dir1}
+>DI:{dir2}
+>line: {: dir1/}{}
+>line: {: dir2/}{}
+>line: {: dir1/}{}
+>line: {: dir1 }{}
+>DESCRIPTION:{file}
+>DI:{dir1}
+>DI:{dir2}
+>FI:{file1}
+>FI:{file2}
+
+ comptest $': suf\ebd\t\t\t\t\t \t'
+0:unambiguous prefix and autoremovable suffix with _prefix completer
+>line: {: dir}{suf}
+>line: {: dir}{suf}
+>DESCRIPTION:{file}
+>DI:{dir1}
+>DI:{dir2}
+>line: {: dir1/}{suf}
+>line: {: dir2/}{suf}
+>line: {: dir1/}{suf}
+>line: {: dir1 }{suf}
+>DESCRIPTION:{file}
+>DI:{dir1}
+>DI:{dir2}
+>FI:{file1}
+>FI:{file2}
+F:regression test workers/51641
+
+ comptesteval 'comptest-postfunc() { compstate[insert]=1 compstate[list]= }'
+ comptest $': \t \t'
+0:compstate[insert]=1 compstate[list]=
+>line: {: dir1/}{}
+>line: {: dir1 dir1/}{}
+
+ comptest $': suf\eb\t \t'
+0:compstate[insert]=1 compstate[list]= with _prefix completer
+>line: {: dir1/}{suf}
+>line: {: dir1 dir1/}{suf}
+F:regression test workers/51641
+
+ comptesteval 'comptest-postfunc() {}'
comptest $': *\t\t\t\t\t\t'
0:_expand shows file types
>line: {: dir1/}{}
@@ -73,16 +112,37 @@
>NO:{file2}
comptesteval $'zstyle -d \'*\' glob'
- comptesteval '_users () { compadd user1 user2 }'
+ comptesteval '_users () { compadd zuser1 zuser2 }'
comptest $': ~\t\t\t\t\t'
0:tilde
->line: {: ~user}{}
->line: {: ~user}{}
->NO:{user1}
->NO:{user2}
->line: {: ~user1}{}
->line: {: ~user2}{}
->line: {: ~user1}{}
+>line: {: ~zuser}{}
+>line: {: ~zuser}{}
+>NO:{zuser1}
+>NO:{zuser2}
+>line: {: ~zuser1}{}
+>line: {: ~zuser2}{}
+>line: {: ~zuser1}{}
+
+ comptesteval 'zsh_directory_name() { compadd "$expl[@]" -- name/1 name2 }'
+ comptest $': ~[\t\t\t\t'
+0:dynamic directory names after ~[
+>line: {: ~[name}{}
+>line: {: ~[name}{}
+>DESCRIPTION:{dynamically named directory}
+>NO:{name/1}
+>NO:{name2}
+>line: {: ~[name/1]}{}
+>line: {: ~[name2]}{}
+
+ comptest $': ~[]\C-b\t\t\t\t'
+0:dynamic directory names inside ~[...]
+>line: {: ~[name}{]}
+>line: {: ~[name}{]}
+>DESCRIPTION:{dynamically named directory}
+>NO:{name/1}
+>NO:{name2}
+>line: {: ~[name/1}{]}
+>line: {: ~[name2}{]}
comptest $'echo ;:\C-b\C-b\t'
0:directories and files before separator
@@ -269,10 +329,10 @@ F:regression test workers/31611
comptesteval "typeset -a bar=({$'\\0'..$'\\C-?'})"
comptesteval 'typeset -A bat=( "$bar[@]" )'
comptesteval 'typeset bay="$bar"'
- comptesteval 'zstyle ":completion:*:parameters" extra-verbose yes'
+ comptesteval 'zstyle ":completion:*:parameters" verbose yes'
comptesteval 'zstyle ":completion:*" fake-parameters bar bat bay'
comptest $': $ba\t'
-0:extra-verbose shows parameter values
+0:verbose shows parameter values
>line: {: $ba}{}
>DESCRIPTION:{parameter}
>NO:{bar -- ( '^@' '^A' '^B' '^C' '^D' '^E' '^F' '^G' '^H' '\t' '\n' '^K' '^L' '}
@@ -282,15 +342,15 @@ F:regression test workers/31611
comptesteval "path=( $ZTST_srcdir:A )"
comptesteval 'typeset -H paths=HIDDEN'
comptest $': $path\t'
-0:extra-verbose doesn't show special or hidden parameter values
+0:verbose doesn't show special or hidden parameter values
>line: {: $path}{}
>DESCRIPTION:{parameter}
>NO:{path}
>NO:{paths}
- comptesteval 'zstyle -d ":completion:*:parameters" extra-verbose'
+ comptesteval 'zstyle -d ":completion:*:parameters" verbose'
comptest $': $ba\t'
-0:parameter values not shown without extra-verbose
+0:parameter values not shown without verbose
>line: {: $ba}{}
>DESCRIPTION:{parameter}
>NO:{bar}
diff --git a/Test/comptest b/Test/comptest
index 79c69979a..39ad14768 100644
--- a/Test/comptest
+++ b/Test/comptest
@@ -40,7 +40,7 @@ KEYTIMEOUT=1
setopt zle
autoload -U compinit
compinit -u
-zstyle ":completion:*" completer _expand _complete _ignored
+zstyle ":completion:*" completer _expand _complete _prefix _ignored
zstyle ":completion:*:default" list-colors "no=<NO>" "fi=<FI>" "di=<DI>" "ln=<LN>" "pi=<PI>" "so=<SO>" "bd=<BD>" "cd=<CD>" "ex=<EX>" "mi=<MI>" "tc=<TC>" "sp=<SP>" "lc=<LC>" "ec=<EC>\n" "rc=<RC>"
zstyle ":completion:*" group-name ""
zstyle ":completion:*:messages" format "<MESSAGE>%d</MESSAGE>
@@ -51,6 +51,12 @@ zstyle ":completion:*:options" verbose yes
zstyle ":completion:*:values" verbose yes
setopt noalwayslastprompt listrowsfirst completeinword
zmodload zsh/complist
+zle -C complete-word complete-word complete-word-with-postfunc
+complete-word-with-postfunc() {
+ local +h -a comppostfuncs=( comptest-postfunc )
+ _main_complete "$@"
+}
+comptest-postfunc() {}
complete-word-with-report () {
print -lr "<WIDGET><complete-word>"
zle complete-word
diff --git a/Test/runtests.zsh b/Test/runtests.zsh
index b66d579b6..538663f50 100644
--- a/Test/runtests.zsh
+++ b/Test/runtests.zsh
@@ -15,6 +15,7 @@ for file in "${(f)ZTST_testlist}"; do
(( skipped++ ))
elif (( $retval )); then
(( failure++ ))
+ (( $retval > 128 )) && print "$file: failed: SIG$signals[$retval - 127]."
else
(( success++ ))
fi
diff --git a/Test/ztst.zsh b/Test/ztst.zsh
index aca275c1c..1d05baddf 100755
--- a/Test/ztst.zsh
+++ b/Test/ztst.zsh
@@ -25,17 +25,22 @@
# still not be good enough. Maybe we should trick it somehow.
emulate -R zsh
-# Ensure the locale does not screw up sorting. Don't supply a locale
-# unless there's one set, to minimise problems.
-[[ -n $LC_ALL ]] && LC_ALL=C
-[[ -n $LC_CTYPE ]] && LC_CTYPE=C
-[[ -n $LC_COLLATE ]] && LC_COLLATE=C
-[[ -n $LC_NUMERIC ]] && LC_NUMERIC=C
-[[ -n $LC_MESSAGES ]] && LC_MESSAGES=C
-[[ -n $LANG ]] && LANG=C
-# Test file may (or may not) set LANG to other locales. In either case,
-# LANG must be passed to child zsh.
-export LANG
+# By default tests are run in C locale. LANG must be passed to child zsh.
+unset -m LC_\*
+export LANG=C
+
+# find UTF-8 locale
+ZTST_find_UTF8 () {
+ setopt multibyte
+ local langs=(en_{US,GB}.{UTF-,utf}8 en.UTF-8
+ ${(M)$(locale -a 2>/dev/null):#*.(utf8|UTF-8)})
+ for LANG in $langs; do
+ if [[ é = ? ]]; then
+ echo $LANG
+ return
+ fi
+ done
+}
# Don't propagate variables that are set by default in the shell.
typeset +x WORDCHARS
@@ -321,6 +326,7 @@ ZTST_diff() {
emulate -L zsh
setopt extendedglob
+ local -a diff_arg
local diff_out
integer diff_pat diff_ret
@@ -337,6 +343,7 @@ ZTST_diff() {
;;
esac
shift
+ [[ $OSTYPE != solaris* ]] && diff_arg=( -a )
if (( diff_pat )); then
local -a diff_lines1 diff_lines2
@@ -377,7 +384,7 @@ ZTST_diff() {
diff_ret=1
fi
else
- diff_out=$(diff -a "$@")
+ diff_out=$(diff $diff_arg "$@")
diff_ret="$?"
if [[ "$diff_ret" != "0" ]]; then
print -r -- "$diff_out"