1########################################################################
2#                                                                      #
3#               This software is part of the ast package               #
4#          Copyright (c) 1982-2010 AT&T Intellectual Property          #
5#                      and is licensed under the                       #
6#                  Common Public License, Version 1.0                  #
7#                    by AT&T Intellectual Property                     #
8#                                                                      #
9#                A copy of the License is available at                 #
10#            http://www.opensource.org/licenses/cpl1.0.txt             #
11#         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         #
12#                                                                      #
13#              Information and Software Systems Research               #
14#                            AT&T Research                             #
15#                           Florham Park NJ                            #
16#                                                                      #
17#                  David Korn <dgk@research.att.com>                   #
18#                                                                      #
19########################################################################
20function err_exit
21{
22	print -u2 -n "\t"
23	print -u2 -r ${Command}[$1]: "${@:2}"
24	let Errors+=1
25}
26alias err_exit='err_exit $LINENO'
27
28Command=${0##*/}
29integer Errors=0
30
31tmp=$(mktemp -dt) || { err_exit mktemp -dt failed; exit 1; }
32trap "cd /; rm -rf $tmp" EXIT
33
34[[ ${.sh.version} == "$KSH_VERSION" ]] || err_exit '.sh.version != KSH_VERSION'
35unset ss
36[[ ${@ss} ]] && err_exit '${@ss} should be empty string when ss is unset'
37[[ ${!ss} == ss ]] ||  err_exit '${!ss} should be ss when ss is unset'
38[[ ${#ss} == 0 ]] ||  err_exit '${#ss} should be 0 when ss is unset'
39# RANDOM
40if	(( RANDOM==RANDOM || $RANDOM==$RANDOM ))
41then	err_exit RANDOM variable not working
42fi
43# SECONDS
44sleep 3
45if	(( SECONDS < 2 ))
46then	err_exit SECONDS variable not working
47fi
48# _
49set abc def
50if	[[ $_ != def ]]
51then	err_exit _ variable not working
52fi
53# ERRNO
54#set abc def
55#rm -f foobar#
56#ERRNO=
57#2> /dev/null < foobar#
58#if	(( ERRNO == 0 ))
59#then	err_exit ERRNO variable not working
60#fi
61# PWD
62if	[[ !  $PWD -ef . ]]
63then	err_exit PWD variable failed, not equivalent to .
64fi
65# PPID
66exp=$$
67got=${ $SHELL -c 'print $PPID'; }
68if	[[ ${ $SHELL -c 'print $PPID'; } != $$ ]]
69then	err_exit "PPID variable failed -- expected '$exp', got '$got'"
70fi
71# OLDPWD
72old=$PWD
73cd /
74if	[[ $OLDPWD != $old ]]
75then	err_exit "OLDPWD variable failed -- expected '$old', got '$OLDPWD'"
76fi
77cd $old || err_exit cd failed
78# REPLY
79read <<-!
80	foobar
81	!
82if	[[ $REPLY != foobar ]]
83then	err_exit REPLY variable not working
84fi
85integer save=$LINENO
86# LINENO
87LINENO=10
88#
89#  These lines intentionally left blank
90#
91if	(( LINENO != 13))
92then	err_exit LINENO variable not working
93fi
94LINENO=save+10
95IFS=:
96x=a::b::c
97if	[[ $x != a::b::c ]]
98then	err_exit "word splitting on constants"
99fi
100set -- $x
101if	[[ $# != 5 ]]
102then	err_exit ":: doesn't separate null arguments "
103fi
104set x
105if	x$1=0 2> /dev/null
106then	err_exit "x\$1=value treated as an assignment"
107fi
108# check for attributes across subshells
109typeset -i x=3
110y=1/0
111if	( typeset x=y ) 2> /dev/null
112then	err_exit "attributes not passed to subshells"
113fi
114unset x
115function x.set
116{
117	nameref foo=${.sh.name}.save
118	foo=${.sh.value}
119	.sh.value=$0
120}
121x=bar
122if	[[ $x != x.set ]]
123then	err_exit 'x.set does not override assignment'
124fi
125x.get()
126{
127	nameref foo=${.sh.name}.save
128	.sh.value=$foo
129}
130
131if	[[ $x != bar ]]
132then	err_exit 'x.get does not work correctly'
133fi
134typeset +n foo
135unset foo
136foo=bar
137(
138	unset foo
139	set +u
140	if	[[ $foo != '' ]]
141	then	err_exit '$foo not null after unset in subsehll'
142	fi
143)
144if	[[ $foo != bar ]]
145then	err_exit 'unset foo in subshell produces side effect '
146fi
147unset foo
148if	[[ $( { : ${foo?hi there} ; } 2>&1) != *'hi there' ]]
149then	err_exit '${foo?hi there} with foo unset does not print hi there on 2'
150fi
151x=$0
152set foobar
153if	[[ ${@:0} != "$x foobar" ]]
154then	err_exit '${@:0} not expanding correctly'
155fi
156set --
157if	[[ ${*:0:1} != "$0" ]]
158then	err_exit '${@:0} not expanding correctly'
159fi
160ACCESS=0
161function COUNT.set
162{
163        (( ACCESS++ ))
164}
165COUNT=0
166(( COUNT++ ))
167if	(( COUNT != 1 || ACCESS!=2 ))
168then	err_exit " set discipline failure COUNT=$COUNT ACCESS=$ACCESS"
169fi
170LANG=C > /dev/null 2>&1
171if	[[ $LANG != C ]]
172then	err_exit "C locale not working"
173fi
174unset RANDOM
175unset -n foo
176foo=junk
177function foo.get
178{
179	.sh.value=stuff
180	unset -f foo.get
181}
182if	[[ $foo != stuff ]]
183then	err_exit "foo.get discipline not working"
184fi
185if	[[ $foo != junk ]]
186then	err_exit "foo.get discipline not working after unset"
187fi
188# special variables
189set -- 1 2 3 4 5 6 7 8 9 10
190sleep 1000 &
191if	[[ $(print -r -- ${#10}) != 2 ]]
192then	err_exit '${#10}, where ${10}=10 not working'
193fi
194for i in @ '*' ! '#' - '?' '$'
195do	false
196	eval foo='$'$i bar='$'{$i}
197	if	[[ ${foo} != "${bar}" ]]
198	then	err_exit "\$$i not equal to \${$i}"
199	fi
200	command eval bar='$'{$i%?} 2> /dev/null || err_exit "\${$i%?} gives syntax error"
201	if	[[ $i != [@*] && ${foo%?} != "$bar"  ]]
202	then	err_exit "\${$i%?} not correct"
203	fi
204	command eval bar='$'{$i#?} 2> /dev/null || err_exit "\${$i#?} gives syntax error"
205	if	[[ $i != [@*] && ${foo#?} != "$bar"  ]]
206	then	err_exit "\${$i#?} not correct"
207	fi
208	command eval foo='$'{$i} bar='$'{#$i} || err_exit "\${#$i} gives synta
209x error"
210	if	[[ $i != @([@*]) && ${#foo} != "$bar" ]]
211	then	err_exit "\${#$i} not correct"
212	fi
213done
214kill $!
215unset x
216CDPATH=/
217x=$(cd ${tmp#/})
218if	[[ $x != $tmp ]]
219then	err_exit 'CDPATH does not display new directory'
220fi
221CDPATH=/:
222x=$(cd ${tmp%/*}; cd ${tmp##*/})
223if	[[ $x ]]
224then	err_exit 'CDPATH displays new directory when not used'
225fi
226x=$(cd ${tmp#/})
227if	[[ $x != $tmp ]]
228then	err_exit "CDPATH ${tmp#/} does not display new directory"
229fi
230TMOUT=100
231(TMOUT=20)
232if	(( TMOUT !=100 ))
233then	err_exit 'setting TMOUT in subshell affects parent'
234fi
235unset y
236function setdisc # var
237{
238        eval function $1.get'
239        {
240                .sh.value=good
241        }
242        '
243}
244y=bad
245setdisc y
246if	[[ $y != good ]]
247then	err_exit 'setdisc function not working'
248fi
249integer x=$LINENO
250: $'\
251'
252if	(( LINENO != x+3  ))
253then	err_exit '\<newline> gets linenumber count wrong'
254fi
255set --
256set -- "${@-}"
257if	(( $# !=1 ))
258then	err_exit	'"${@-}" not expanding to null string'
259fi
260for i in : % + / 3b '**' '***' '@@' '{' '[' '}' !!  '*a' '$foo'
261do      (eval : \${"$i"} 2> /dev/null) && err_exit "\${$i} not an syntax error"
262done
263unset IFS
264( IFS='  ' ; read -r a b c <<-!
265	x  y z
266	!
267	if	[[ $b ]]
268	then	err_exit 'IFS="  " not causing adjacent space to be null string'
269	fi
270)
271read -r a b c <<-!
272x  y z
273!
274if	[[ $b != y ]]
275then	err_exit 'IFS not restored after subshell'
276fi
277
278# The next part generates 3428 IFS set/read tests.
279
280unset IFS x
281function split
282{
283	i=$1 s=$2 r=$3
284	IFS=': '
285	set -- $i
286	IFS=' '
287	g="[$#]"
288	while	:
289	do	case $# in
290		0)	break ;;
291		esac
292		g="$g($1)"
293		shift
294	done
295	case "$g" in
296	"$s")	;;
297	*)	err_exit "IFS=': '; set -- '$i'; expected '$s' got '$g'" ;;
298	esac
299	print "$i" | IFS=": " read arg rem; g="($arg)($rem)"
300	case "$g" in
301	"$r")	;;
302	*)	err_exit "IFS=': '; read '$i'; expected '$r' got '$g'" ;;
303	esac
304}
305for str in 	\
306	'-'	\
307	'a'	\
308	'- -'	\
309	'- a'	\
310	'a -'	\
311	'a b'	\
312	'- - -'	\
313	'- - a'	\
314	'- a -'	\
315	'- a b'	\
316	'a - -'	\
317	'a - b'	\
318	'a b -'	\
319	'a b c'
320do
321	IFS=' '
322	set x $str
323	shift
324	case $# in
325	0)	continue ;;
326	esac
327	f1=$1
328	case $f1 in
329	'-')	f1='' ;;
330	esac
331	shift
332	case $# in
333	0)	for d0 in '' ' '
334		do
335			for d1 in '' ' ' ':' ' :' ': ' ' : '
336			do
337				case $f1$d1 in
338				'')	split "$d0$f1$d1" "[0]" "()()" ;;
339				' ')	;;
340				*)	split "$d0$f1$d1" "[1]($f1)" "($f1)()" ;;
341				esac
342			done
343		done
344		continue
345		;;
346	esac
347	f2=$1
348	case $f2 in
349	'-')	f2='' ;;
350	esac
351	shift
352	case $# in
353	0)	for d0 in '' ' '
354		do
355			for d1 in ' ' ':' ' :' ': ' ' : '
356			do
357				case ' ' in
358				$f1$d1|$d1$f2)	continue ;;
359				esac
360				for d2 in '' ' ' ':' ' :' ': ' ' : '
361				do
362					case $f2$d2 in
363					'')	split "$d0$f1$d1$f2$d2" "[1]($f1)" "($f1)()" ;;
364					' ')	;;
365					*)	split "$d0$f1$d1$f2$d2" "[2]($f1)($f2)" "($f1)($f2)" ;;
366					esac
367				done
368			done
369		done
370		continue
371		;;
372	esac
373	f3=$1
374	case $f3 in
375	'-')	f3='' ;;
376	esac
377	shift
378	case $# in
379	0)	for d0 in '' ' '
380		do
381			for d1 in ':' ' :' ': ' ' : '
382			do
383				case ' ' in
384				$f1$d1|$d1$f2)	continue ;;
385				esac
386				for d2 in ' ' ':' ' :' ': ' ' : '
387				do
388					case $f2$d2 in
389					' ')	continue ;;
390					esac
391					case ' ' in
392					$f2$d2|$d2$f3)	continue ;;
393					esac
394					for d3 in '' ' ' ':' ' :' ': ' ' : '
395					do
396						case $f3$d3 in
397						'')	split "$d0$f1$d1$f2$d2$f3$d3" "[2]($f1)($f2)" "($f1)($f2)" ;;
398						' ')	;;
399						*)	x=$f2$d2$f3$d3
400							x=${x#' '}
401							x=${x%' '}
402							split "$d0$f1$d1$f2$d2$f3$d3" "[3]($f1)($f2)($f3)" "($f1)($x)"
403							;;
404						esac
405					done
406				done
407			done
408		done
409		continue
410		;;
411	esac
412done
413unset IFS
414
415if	[[ $( (print ${12345:?}) 2>&1) != *12345* ]]
416then	err_exit 'incorrect error message with ${12345?}'
417fi
418unset foobar
419if	[[ $( (print ${foobar:?}) 2>&1) != *foobar* ]]
420then	err_exit 'incorrect error message with ${foobar?}'
421fi
422unset bar
423if	[[ $( (print ${bar:?bam}) 2>&1) != *bar*bam* ]]
424then	err_exit 'incorrect error message with ${foobar?}'
425fi
426{ $SHELL -c '
427function foo
428{
429	typeset SECONDS=0
430	sleep 1.5
431	print $SECONDS
432
433}
434x=$(foo)
435(( x >1 && x < 2 ))
436'
437} 2> /dev/null   || err_exit 'SECONDS not working in function'
438cat > $tmp/script <<-\!
439	posixfun()
440	{
441		unset x
442	 	nameref x=$1
443	 	print  -r -- "$x"
444	}
445	function fun
446	{
447	 	nameref x=$1
448	 	print  -r -- "$x"
449	}
450	if	[[ $1 ]]
451	then	file=${.sh.file}
452	else	print -r -- "${.sh.file}"
453	fi
454!
455chmod +x $tmp/script
456. $tmp/script  1
457[[ $file == $tmp/script ]] || err_exit ".sh.file not working for dot scripts"
458[[ $($SHELL $tmp/script) == $tmp/script ]] || err_exit ".sh.file not working for scripts"
459[[ $(posixfun .sh.file) == $tmp/script ]] || err_exit ".sh.file not working for posix functions"
460[[ $(fun .sh.file) == $tmp/script ]] || err_exit ".sh.file not working for functions"
461[[ $(posixfun .sh.fun) == posixfun ]] || err_exit ".sh.fun not working for posix functions"
462[[ $(fun .sh.fun) == fun ]] || err_exit ".sh.fun not working for functions"
463[[ $(posixfun .sh.subshell) == 1 ]] || err_exit ".sh.subshell not working for posix functions"
464[[ $(fun .sh.subshell) == 1 ]] || err_exit ".sh.subshell not working for functions"
465(
466    [[ $(posixfun .sh.subshell) == 2 ]]  || err_exit ".sh.subshell not working for posix functions in subshells"
467    [[ $(fun .sh.subshell) == 2 ]]  || err_exit ".sh.subshell not working for functions in subshells"
468    (( .sh.subshell == 1 )) || err_exit ".sh.subshell not working in a subshell"
469)
470TIMEFORMAT='this is a test'
471[[ $({ { time :;} 2>&1;}) == "$TIMEFORMAT" ]] || err_exit 'TIMEFORMAT not working'
472: ${.sh.version}
473[[ $(alias integer) == *.sh.* ]] && err_exit '.sh. prefixed to alias name'
474: ${.sh.version}
475[[ $(whence rm) == *.sh.* ]] && err_exit '.sh. prefixed to tracked alias name'
476: ${.sh.version}
477[[ $(cd /bin;env | grep PWD=) == *.sh.* ]] && err_exit '.sh. prefixed to PWD'
478# unset discipline bug fix
479dave=dave
480function dave.unset
481{
482    unset dave
483}
484unset dave
485[[ $(typeset +f) == *dave.* ]] && err_exit 'unset discipline not removed'
486
487print 'print ${VAR}' > $tmp/script
488unset VAR
489VAR=new $tmp/script > $tmp/out
490got=$(<$tmp/out)
491[[ $got == new ]] || err_exit "previously unset environment variable not passed to script, expected 'new', got '$got'"
492[[ ! $VAR ]] || err_exit "previously unset environment variable set after script, expected '', got '$VAR'"
493unset VAR
494VAR=old
495VAR=new $tmp/script > $tmp/out
496got=$(<$tmp/out)
497[[ $got == new ]] || err_exit "environment variable covering local variable not passed to script, expected 'new', got '$got'"
498[[ $VAR == old ]] || err_exit "previously set local variable changed after script, expected 'old', got '$VAR'"
499unset VAR
500export VAR=old
501VAR=new $tmp/script > $tmp/out
502got=$(<$tmp/out)
503[[ $got == new ]] || err_exit "environment variable covering environment variable not passed to script, expected 'new', got '$got'"
504[[ $VAR == old ]] || err_exit "previously set environment variable changed after script, expected 'old', got '$VAR'"
505
506(
507	unset dave
508	function  dave.append
509	{
510		.sh.value+=$dave
511		dave=
512	}
513	dave=foo; dave+=bar
514	[[ $dave == barfoo ]] || exit 2
515) 2> /dev/null
516case $? in
5170)	 ;;
5181)	 err_exit 'append discipline not implemented';;
519*)	 err_exit 'append discipline not working';;
520esac
521.sh.foobar=hello
522{
523	function .sh.foobar.get
524	{
525		.sh.value=world
526	}
527} 2> /dev/null || err_exit "cannot add get discipline to .sh.foobar"
528[[ ${.sh.foobar} == world ]]  || err_exit 'get discipline for .sh.foobar not working'
529x='a|b'
530IFS='|'
531set -- $x
532[[ $2 == b ]] || err_exit '$2 should be b after set'
533exec 3>&2 2> /dev/null
534set -x
535( IFS= ) 2> /dev/null
536set +x
537exec 2>&3-
538set -- $x
539[[ $2 == b ]] || err_exit '$2 should be b after subshell'
540: & pid=$!
541( : & )
542[[ $pid == $! ]] || err_exit '$! value not preserved across subshells'
543unset foo
544typeset -A foo
545function foo.set
546{
547	case ${.sh.subscript} in
548	bar)	if	((.sh.value > 1 ))
549	        then	.sh.value=5
550			foo[barrier_hit]=yes
551		fi
552		;;
553	barrier_hit)
554		if	[[ ${.sh.value} = yes ]]
555		then	foo[barrier_not_hit]=no
556		else	foo[barrier_not_hit]=yes
557		fi
558		;;
559	esac
560}
561foo[barrier_hit]=no
562foo[bar]=1
563(( foo[bar] == 1 )) || err_exit 'foo[bar] should be 1'
564[[ ${foo[barrier_hit]} == no ]] || err_exit 'foo[barrier_hit] should be no'
565[[ ${foo[barrier_not_hit]} == yes ]] || err_exit 'foo[barrier_not_hit] should be yes'
566foo[barrier_hit]=no
567foo[bar]=2
568(( foo[bar] == 5 )) || err_exit 'foo[bar] should be 5'
569[[ ${foo[barrier_hit]} == yes ]] || err_exit 'foo[barrier_hit] should be yes'
570[[ ${foo[barrier_not_hit]} == no ]] || err_exit 'foo[barrier_not_hit] should be no'
571unset x
572typeset -i x
573function x.set
574{
575	typeset sub=${.sh.subscript}
576	(( sub > 0 )) && (( x[sub-1]= x[sub-1] + .sh.value ))
577}
578x[0]=0 x[1]=1 x[2]=2 x[3]=3
579[[ ${x[@]} == '12 8 5 3' ]] || err_exit 'set discipline for indexed array not working correctly'
580float seconds
581((SECONDS=3*4))
582seconds=SECONDS
583(( seconds < 12 || seconds > 12.1 )) &&  err_exit "SECONDS is $seconds and should be close to 12"
584unset a
585function a.set
586{
587	print -r -- "${.sh.name}=${.sh.value}"
588}
589[[ $(a=1) == a=1 ]] || err_exit 'set discipline not working in subshell assignment'
590[[ $(a=1 :) == a=1 ]] || err_exit 'set discipline not working in subshell command'
591
592[[ ${.sh.subshell} == 0 ]] || err_exit '${.sh.subshell} should be 0'
593(
594	[[ ${.sh.subshell} == 1 ]] || err_exit '${.sh.subshell} should be 1'
595	(
596		[[ ${.sh.subshell} == 2 ]] || err_exit '${.sh.subshell} should be 2'
597	)
598)
599
600set -- {1..32768}
601(( $# == 32768 )) || err_exit "\$# failed -- expected 32768, got $#"
602set --
603
604unset r v x
605path=$PATH
606x=foo
607for v in EDITOR VISUAL OPTIND CDPATH FPATH PATH ENV LINENO RANDOM SECONDS _
608do	nameref r=$v
609	unset $v
610	if	( $SHELL -c "unset $v; : \$$v" ) 2>/dev/null
611	then	[[ $r ]] && err_exit "unset $v failed -- expected '', got '$r'"
612		r=$x
613		[[ $r == $x ]] || err_exit "$v=$x failed -- expected '$x', got '$r'"
614	else	err_exit "unset $v; : \$$v failed"
615	fi
616done
617
618x=x
619for v in LC_ALL LC_CTYPE LC_MESSAGES LC_COLLATE LC_NUMERIC
620do	nameref r=$v
621	unset $v
622	[[ $r ]] && err_exit "unset $v failed -- expected '', got '$r'"
623	d=$($SHELL -c "$v=$x" 2>&1)
624	[[ $d ]] || err_exit "$v=$x failed -- expected locale diagnostic"
625	{ g=$( r=$x; print -- $r ); } 2>/dev/null
626	[[ $g == '' ]] || err_exit "$v=$x failed -- expected '', got '$g'"
627	{ g=$( r=C; r=$x; print -- $r ); } 2>/dev/null
628	[[ $g == 'C' ]] || err_exit "$v=C; $v=$x failed -- expected 'C', got '$g'"
629done
630PATH=$path
631
632cd $tmp
633
634print print -n zzz > zzz
635chmod +x zzz
636exp='aaazzz'
637got=$($SHELL -c 'unset SHLVL; print -n aaa; ./zzz' 2>&1) >/dev/null 2>&1
638[[ $got == "$exp" ]] || err_exit "unset SHLVL causes script failure -- expected '$exp', got '$got'"
639
640mkdir glean
641for cmd in date ok
642do	exp="$cmd ok"
643	rm -f $cmd
644	print print $exp > glean/$cmd
645	chmod +x glean/$cmd
646	got=$(CDPATH=:.. $SHELL -c "PATH=:/bin:/usr/bin; date > /dev/null; cd glean && ./$cmd" 2>&1)
647	[[ $got == "$exp" ]] || err_exit "cd with CDPATH after PATH change failed -- expected '$exp', got '$got'"
648done
649
650v=LC_CTYPE
651unset $v
652[[ -v $v ]] && err_exit "unset $v; [[ -v $v ]] failed"
653eval $v=C
654[[ -v $v ]] || err_exit "$v=C; [[ -v $v ]] failed"
655
656cmd='set --nounset; unset foo; : ${!foo*}'
657$SHELL -c "$cmd" 2>/dev/null || err_exit "'$cmd' exit status $?, expected 0"
658
659exit $((Errors))
660