xref: /titanic_44/usr/src/lib/libshell/common/tests/builtins.sh (revision 4eaa471005973e11a6110b69fe990530b3b95a38)
1########################################################################
2#                                                                      #
3#               This software is part of the ast package               #
4#          Copyright (c) 1982-2008 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
28# test shell builtin commands
29Command=${0##*/}
30integer Errors=0
31builtin getconf
32: ${foo=bar} || err_exit ": failed"
33[[ $foo = bar ]] || err_exit ": side effects failed"
34set -- - foobar
35[[ $# = 2 && $1 = - && $2 = foobar ]] || err_exit "set -- - foobar failed"
36set -- -x foobar
37[[ $# = 2 && $1 = -x && $2 = foobar ]] || err_exit "set -- -x foobar failed"
38getopts :x: foo || err_exit "getopts :x: returns false"
39[[ $foo = x && $OPTARG = foobar ]] || err_exit "getopts :x: failed"
40OPTIND=1
41getopts :r:s var -r
42if	[[ $var != : || $OPTARG != r ]]
43then	err_exit "'getopts :r:s var -r' not working"
44fi
45OPTIND=1
46getopts :d#u OPT -d 16177
47if	[[ $OPT != d || $OPTARG != 16177 ]]
48then	err_exit "'getopts :d#u OPT=d OPTARG=16177' failed -- OPT=$OPT OPTARG=$OPTARG"
49fi
50OPTIND=1
51while getopts 'ab' option -a -b
52do	[[ $OPTIND == $((OPTIND)) ]] || err_exit "OPTIND optimization bug"
53done
54
55USAGE=$'[-][S:server?Operate on the specified \asubservice\a:]:[subservice:=pmserver]
56    {
57        [p:pmserver]
58        [r:repserver]
59        [11:notifyd]
60    }'
61set pmser p rep r notifyd -11
62while	(( $# > 1 ))
63do	OPTIND=1
64	getopts "$USAGE" OPT -S $1
65	[[ $OPT == S && $OPTARG == $2 ]] || err_exit "OPT=$OPT OPTARG=$OPTARG -- expected OPT=S OPTARG=$2"
66	shift 2
67done
68
69false ${foo=bar} &&  err_exit "false failed"
70read <<!
71hello world
72!
73[[ $REPLY = 'hello world' ]] || err_exit "read builtin failed"
74print x:y | IFS=: read a b
75if	[[ $a != x ]]
76then	err_exit "IFS=: read ... not working"
77fi
78read <<!
79hello \
80world
81!
82[[ $REPLY = 'hello world' ]] || err_exit "read continuation failed"
83read -d x <<!
84hello worldxfoobar
85!
86[[ $REPLY = 'hello world' ]] || err_exit "read builtin failed"
87read <<\!
88hello \
89	world \
90
91!
92[[ $REPLY == 'hello 	world' ]] || err_exit "read continuation2 failed"
93print "one\ntwo" | { read line
94	print $line | /bin/cat > /dev/null
95	read line
96}
97read <<\!
98\
99a\
100\
101\
102b
103!
104if	[[ $REPLY != ab ]]
105then	err_exit "read multiple continuation failed"
106fi
107if	[[ $line != two ]]
108then	err_exit "read from pipeline failed"
109fi
110line=two
111read line < /dev/null
112if	[[ $line != "" ]]
113then	err_exit "read from /dev/null failed"
114fi
115if	[[ $(print -R -) != - ]]
116then	err_exit "print -R not working correctly"
117fi
118if	[[ $(print -- -) != - ]]
119then	err_exit "print -- not working correctly"
120fi
121print -f "hello%nbar\n" size > /dev/null
122if	((	size != 5 ))
123then	err_exit "%n format of printf not working"
124fi
125print -n -u2 2>&1-
126[[ -w /dev/fd/1 ]] || err_exit "2<&1- with built-ins has side effects"
127x=$0
128if	[[ $(eval 'print $0') != $x ]]
129then	err_exit '$0 not correct for eval'
130fi
131$SHELL -c 'read x <<< hello' 2> /dev/null || err_exit 'syntax <<< not recognized'
132($SHELL -c 'read x[1] <<< hello') 2> /dev/null || err_exit 'read x[1] not working'
133unset x
134readonly x
135set -- $(readonly)
136if      [[ " $@ " != *" x "* ]]
137then    err_exit 'unset readonly variables are not displayed'
138fi
139if	[[ $(	for i in foo bar
140		do	print $i
141			continue 10
142		done
143	    ) != $'foo\nbar' ]]
144then	err_exit 'continue breaks out of loop'
145fi
146(continue bad 2>/dev/null && err_exit 'continue bad should return an error')
147(break bad 2>/dev/null && err_exit 'break bad should return an error')
148(continue 0 2>/dev/null && err_exit 'continue 0 should return an error')
149(break 0 2>/dev/null && err_exit 'break 0 should return an error')
150breakfun() { break;}
151continuefun() { continue;}
152for fun in break continue
153do	if	[[ $(	for i in foo
154			do	${fun}fun
155				print $i
156			done
157		) != foo ]]
158	then	err_exit "$fun call in ${fun}fun breaks out of for loop"
159	fi
160done
161if	[[ $(print -f "%b" "\a\n\v\b\r\f\E\03\\oo") != $'\a\n\v\b\r\f\E\03\\oo' ]]
162then	err_exit 'print -f "%b" not working'
163fi
164if	[[ $(print -f "%P" "[^x].*b\$") != '*[!x]*b' ]]
165then	err_exit 'print -f "%P" not working'
166fi
167if	[[ $(abc: for i in foo bar;do print $i;break abc;done) != foo ]]
168then	err_exit 'break labels not working'
169fi
170if	[[ $(command -v if)	!= if ]]
171then	err_exit	'command -v not working'
172fi
173read -r var <<\!
174
175!
176if	[[ $var != "" ]]
177then	err_exit "read -r of blank line not working"
178fi
179mkdir -p /tmp/ksh$$/a/b/c 2>/dev/null || err_exit  "mkdir -p failed"
180$SHELL -c "cd /tmp/ksh$$/a/b; cd c" 2>/dev/null || err_exit "initial script relative cd fails"
181rm -r /tmp/ksh$$ || err_exit "rm -r /tmp/ksh$$ failed"
182trap 'print HUP' HUP
183if	[[ $(trap) != "trap -- 'print HUP' HUP" ]]
184then	err_exit '$(trap) not working'
185fi
186if	[[ $(trap -p HUP) != 'print HUP' ]]
187then	err_exit '$(trap -p HUP) not working'
188fi
189[[ $($SHELL -c 'trap "print ok" SIGTERM; kill -s SIGTERM $$' 2> /dev/null) == ok ]] || err_exit 'SIGTERM not recognized'
190[[ $($SHELL -c 'trap "print ok" sigterm; kill -s sigterm $$' 2> /dev/null) == ok ]] || err_exit 'SIGTERM not recognized'
191[[ $($SHELL -c '( trap "" TERM);kill $$;print bad' == bad) ]] 2> /dev/null && err_exit 'trap ignored in subshell causes it to be ignored by parent'
192${SHELL} -c 'kill -1 -$$' 2> /dev/null
193[[ $(kill -l $?) == HUP ]] || err_exit 'kill -1 -pid not working'
194${SHELL} -c 'kill -1 -$$' 2> /dev/null
195[[ $(kill -l $?) == HUP ]] || err_exit 'kill -n1 -pid not working'
196${SHELL} -c 'kill -s HUP -$$' 2> /dev/null
197[[ $(kill -l $?) == HUP ]] || err_exit 'kill -HUP -pid not working'
198n=123
199typeset -A base
200base[o]=8#
201base[x]=16#
202base[X]=16#
203for i in d i o u x X
204do	if	(( $(( ${base[$i]}$(printf "%$i" $n) )) != n  ))
205	then	err_exit "printf %$i not working"
206	fi
207done
208if	[[ $( trap 'print done' EXIT) != done ]]
209then	err_exit 'trap on EXIT not working'
210fi
211if	[[ $( trap 'print done' EXIT; trap - EXIT) == done ]]
212then	err_exit 'trap on EXIT not being cleared'
213fi
214if	[[ $(type test) != 'test is a shell builtin' ]]
215then	err_exit 'whence -v test not a builtin'
216fi
217builtin -d test
218if	[[ $(type test) == *builtin* ]]
219then	err_exit 'whence -v test after builtin -d incorrect'
220fi
221typeset -Z3 percent=$(printf '%o\n' "'%'")
222forrmat=\\${percent}s
223if      [[ $(printf "$forrmat") != %s ]]
224then    err_exit "printf $forrmat not working"
225fi
226if	(( $(printf 'x\0y' | wc -c) != 3 ))
227then	err_exit 'printf \0 not working'
228fi
229if	[[ $(printf "%bx%s\n" 'f\to\cbar') != $'f\to' ]]
230then	err_exit 'printf %bx%s\n  not working'
231fi
232alpha=abcdefghijklmnop
233if	[[ $(printf "%10.*s\n" 5 $alpha) != '     abcde' ]]
234then	err_exit 'printf %10.%s\n  not working'
235fi
236float x2=.0000625
237if	[[ $(printf "%10.5E\n" x2) != 6.25000E-05 ]]
238then	err_exit 'printf "%10.5E" not normalizing correctly'
239fi
240x2=.000000001
241if	[[ $(printf "%g\n" x2 2>/dev/null) != 1e-09 ]]
242then	err_exit 'printf "%g" not working correctly'
243fi
244#FIXME#($SHELL read -s foobar <<\!
245#FIXME#testing
246#FIXME#!
247#FIXME#) 2> /dev/null || err_exit ksh read -s var fails
248if	[[ $(printf +3 2>/dev/null) !=   +3 ]]
249then	err_exit 'printf is not processing formats beginning with + correctly'
250fi
251if	printf "%d %d\n" 123bad 78 >/dev/null 2>/dev/null
252then	err_exit "printf not exiting non-zero with conversion errors"
253fi
254if	[[ $(trap --version 2> /dev/null;print done) != done ]]
255then	err_exit 'trap builtin terminating after --version'
256fi
257if	[[ $(set --version 2> /dev/null;print done) != done ]]
258then	err_exit 'set builtin terminating after --veresion'
259fi
260unset -f foobar
261function foobar
262{
263	print 'hello world'
264}
265OPTIND=1
266if	[[ $(getopts  $'[+?X\ffoobar\fX]' v --man 2>&1) != *'Xhello world'X* ]]
267then	err_exit '\f...\f not working in getopts usage strings'
268fi
269if	[[	$(printf '%H\n' $'<>"& \'\tabc') != '&lt;&gt;&quot;&amp;&nbsp;&apos;&#9;abc' ]]
270then	err_exit 'printf %H not working'
271fi
272if	[[	$(printf '%R %R %R %R\n' 'a.b' '*.c' '^'  '!(*.*)') != '^a\.b$ \.c$ ^\^$ ^(.*\..*)!$' ]]
273then	err_exit 'printf %R not working'
274fi
275if	[[ $(printf '%..:c\n' abc) != a:b:c ]]
276then	err_exit	"printf '%..:c' not working"
277fi
278if	[[ $(printf '%..*c\n' : abc) != a:b:c ]]
279then	err_exit	"printf '%..*c' not working"
280fi
281if	[[ $(printf '%..:s\n' abc def ) != abc:def ]]
282then	err_exit	"printf '%..:s' not working"
283fi
284if	[[ $(printf '%..*s\n' : abc def) != abc:def ]]
285then	err_exit	"printf '%..*s' not working"
286fi
287[[ $(printf '%q\n') == '' ]] || err_exit 'printf "%q" with missing arguments'
288# we won't get hit by the one second boundary twice, right?
289[[ $(printf '%T\n' now) == "$(date)" ]] ||
290[[ $(printf '%T\n' now) == "$(date)" ]] ||
291err_exit 'printf "%T" now'
292behead()
293{
294	read line
295	left=$(cat)
296}
297print $'line1\nline2' | behead
298if	[[ $left != line2 ]]
299then	err_exit  "read reading ahead on a pipe"
300fi
301print -n $'{ read -r line;print $line;}\nhello' > /tmp/ksh$$
302chmod 755 /tmp/ksh$$
303trap 'rm -rf /tmp/ksh$$' EXIT
304if	[[ $($SHELL < /tmp/ksh$$) != hello ]]
305then	err_exit 'read of incomplete line not working correctly'
306fi
307set -f
308set -- *
309if      [[ $1 != '*' ]]
310then    err_exit 'set -f not working'
311fi
312unset pid1 pid2
313false &
314pid1=$!
315pid2=$(
316	wait $pid1
317	(( $? == 127 )) || err_exit "job known to subshell"
318	print $!
319)
320wait $pid1
321(( $? == 1 )) || err_exit "wait not saving exit value"
322wait $pid2
323(( $? == 127 )) || err_exit "subshell job known to parent"
324set --noglob
325ifs=$IFS
326IFS=,
327set -- $(getconf LIBPATH)
328IFS=$ifs
329env=
330for v
331do	IFS=:
332	set -- $v
333	IFS=$ifs
334	eval [[ \$$2 ]] && env="$env $2=\"\$$2\""
335done
336set --glob
337if	[[ $(foo=bar; eval foo=\$foo $env exec -c \$SHELL -c \'print \$foo\') != bar ]]
338then	err_exit '"name=value exec -c ..." not working'
339fi
340$SHELL -c 'OPTIND=-1000000; getopts a opt -a' 2> /dev/null
341[[ $? == 1 ]] || err_exit 'getopts with negative OPTIND not working'
342getopts 'n#num' opt  -n 3
343[[ $OPTARG == 3 ]] || err_exit 'getopts with numerical arguments failed'
344if	[[ $($SHELL -c $'printf \'%2$s %1$s\n\' world hello') != 'hello world' ]]
345then	err_exit 'printf %2$s %1$s not working'
346fi
347val=$(( 'C' ))
348set -- \
349	"'C"	$val	0	\
350	"'C'"	$val	0	\
351	'"C'	$val	0	\
352	'"C"'	$val	0	\
353	"'CX"	$val	1	\
354	"'CX'"	$val	1	\
355	"'C'X"	$val	1	\
356	'"CX'	$val	1	\
357	'"CX"'	$val	1	\
358	'"C"X'	$val	1
359while (( $# >= 3 ))
360do	arg=$1 val=$2 code=$3
361	shift 3
362	for fmt in '%d' '%g'
363	do	out=$(printf "$fmt" "$arg" 2>/dev/null)
364		err=$(printf "$fmt" "$arg" 2>&1 >/dev/null)
365		printf "$fmt" "$arg" >/dev/null 2>&1
366		ret=$?
367		[[ $out == $val ]] || err_exit "printf $fmt $arg failed -- expected $val, got $out"
368		if	(( $code ))
369		then	[[ $err ]] || err_exit "printf $fmt $arg failed, error message expected"
370		else	[[ $err ]] && err_exit "$err: printf $fmt $arg failed, error message not expected -- got '$err'"
371		fi
372		(( $ret == $code )) || err_exit "printf $fmt $arg failed -- expected exit code $code, got $ret"
373	done
374done
375((n=0))
376((n++)); ARGC[$n]=1 ARGV[$n]=""
377((n++)); ARGC[$n]=2 ARGV[$n]="-a"
378((n++)); ARGC[$n]=4 ARGV[$n]="-a -v 2"
379((n++)); ARGC[$n]=4 ARGV[$n]="-a -v 2 x"
380((n++)); ARGC[$n]=4 ARGV[$n]="-a -v 2 x y"
381for ((i=1; i<=n; i++))
382do	set -- ${ARGV[$i]}
383	OPTIND=0
384	while	getopts -a tst "av:" OPT
385	do	:
386	done
387	if	[[ $OPTIND != ${ARGC[$i]} ]]
388	then	err_exit "\$OPTIND after getopts loop incorrect -- expected ${ARGC[$i]}, got $OPTIND"
389	fi
390done
391options=ab:c
392optarg=foo
393set -- -a -b $optarg -c bar
394while	getopts $options opt
395do	case $opt in
396	a|c)	[[ $OPTARG ]] && err_exit "getopts $options \$OPTARG for flag $opt failed, expected \"\", got \"$OPTARG\"" ;;
397	b)	[[ $OPTARG == $optarg ]] || err_exit "getopts $options \$OPTARG failed -- \"$optarg\" expected, got \"$OPTARG\"" ;;
398	*)	err_exit "getopts $options failed -- got flag $opt" ;;
399	esac
400done
401function longline
402{
403	integer i
404	for((i=0; i < $1; i++))
405	do	print argument$i
406	done
407}
408# test command -x option
409integer sum=0 n=10000
410if	! ${SHELL:-ksh} -c 'print $#' count $(longline $n) > /dev/null  2>&1
411then	for i in $(command command -x ${SHELL:-ksh} -c 'print $#;[[ $1 != argument0 ]]' count $(longline $n) 2> /dev/null)
412	do	((sum += $i))
413	done
414	(( sum == n )) || err_exit "command -x processed only $sum arguments"
415	command -p command -x ${SHELL:-ksh} -c 'print $#;[[ $1 == argument0 ]]' count $(longline $n) > /dev/null  2>&1
416	[[ $? != 1 ]] && err_exit 'incorrect exit status for command -x'
417fi
418# test command -x option with extra arguments
419integer sum=0 n=10000
420if      ! ${SHELL:-ksh} -c 'print $#' count $(longline $n) > /dev/null  2>&1
421then    for i in $(command command -x ${SHELL:-ksh} -c 'print $#;[[ $1 != argument0 ]]' count $(longline $n) one two three) #2> /dev/null)
422	do      ((sum += $i))
423	done
424	(( sum  > n )) || err_exit "command -x processed only $sum arguments"
425	(( (sum-n)%3==0 )) || err_exit "command -x processed only $sum arguments"
426	(( sum == n+3)) && err_exit "command -x processed only $sum arguments"
427	command -p command -x ${SHELL:-ksh} -c 'print $#;[[ $1 == argument0 ]]' count $(longline $n) > /dev/null  2>&1
428	[[ $? != 1 ]] && err_exit 'incorrect exit status for command -x'
429fi
430# test for debug trap
431[[ $(typeset -i i=0
432	trap 'print $i' DEBUG
433	while (( i <2))
434	do	(( i++))
435	done) == $'0\n0\n1\n1\n2' ]]  || err_exit  "DEBUG trap not working"
436getconf UNIVERSE - ucb
437[[ $($SHELL -c 'echo -3') == -3 ]] || err_exit "echo -3 not working in ucb universe"
438typeset -F3 start_x=SECONDS total_t delay=0.02
439typeset reps=50 leeway=5
440sleep $(( 2 * leeway * reps * delay )) |
441for (( i=0 ; i < reps ; i++ ))
442do	read -N1 -t $delay
443done
444(( total_t = SECONDS - start_x ))
445if	(( total_t > leeway * reps * delay ))
446then	err_exit "read -t in pipe taking $total_t secs - $(( reps * delay )) minimum - too long"
447elif	(( total_t < reps * delay ))
448then	err_exit "read -t in pipe taking $total_t secs - $(( reps * delay )) minimum - too fast"
449fi
450$SHELL -c 'sleep $(printf "%a" .95)' 2> /dev/null || err_exit "sleep doesn't except %a format constants"
451$SHELL -c 'test \( ! -e \)' 2> /dev/null ; [[ $? == 1 ]] || err_exit 'test \( ! -e \) not working'
452[[ $(ulimit) == "$(ulimit -fS)" ]] || err_exit 'ulimit is not the same as ulimit -fS'
453tmpfile=${TMP-/tmp}/ksh$$.2
454trap 'rm -f /tmp/ksh$$ "$tmpfile"' EXIT
455print $'\nprint -r -- "${.sh.file} ${LINENO} ${.sh.lineno}"' > $tmpfile
456[[ $( . "$tmpfile") == "$tmpfile 2 1" ]] || err_exit 'dot command not working'
457print -r -- "'xxx" > $tmpfile
458[[ $($SHELL -c ". $tmpfile"$'\n print ok' 2> /dev/null) == ok ]] || err_exit 'syntax error in dot command affects next command'
459
460float sec=$SECONDS del=4
461exec 3>&2 2>/dev/null
462$SHELL -c "( sleep 1; kill -ALRM \$\$ ) & sleep $del" 2> /dev/null
463exitval=$?
464(( sec = SECONDS - sec ))
465exec 2>&3-
466(( exitval )) && err_exit "sleep doesn't exit 0 with ALRM interupt"
467(( sec > (del - 1) )) || err_exit "ALRM signal causes sleep to terminate prematurely -- expected 3 sec, got $sec"
468
469exit $((Errors))
470