xref: /titanic_44/usr/src/lib/libshell/common/tests/io.sh (revision 8339b41da2395f0525c46ceedfeb01961893ec44)
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
34unset HISTFILE
35
36function fun
37{
38	while  command exec 3>&1
39	do	break
40	done 2>   /dev/null
41	print -u3 good
42}
43print 'read -r a;print -r -u$1 -- "$a"' > $tmp/mycat
44chmod 755 $tmp/mycat
45for ((i=3; i < 10; i++))
46do
47	eval "a=\$(print foo | $tmp/mycat" $i $i'>&1 > /dev/null |cat)' 2> /dev/null
48	[[ $a == foo ]] || err_exit "bad file descriptor $i in comsub script"
49done
50exec 3> /dev/null
51[[ $(fun) == good ]] || err_exit 'file 3 closed before subshell completes'
52exec 3>&-
53cd $tmp || { err_exit "cd $tmp failed"; exit ; }
54print foo > file1
55print bar >> file1
56if	[[ $(<file1) != $'foo\nbar' ]]
57then	err_exit 'append (>>) not working'
58fi
59set -o noclobber
60exec 3<> file1
61read -u3 line
62exp=foo
63if	[[ $line != $exp ]]
64then	err_exit "read on <> fd failed -- expected '$exp', got '$line'"
65fi
66if	( 4> file1 ) 2> /dev/null
67then	err_exit 'noclobber not causing exclusive open'
68fi
69set +o noclobber
70
71FDFS=(
72	( dir=/proc/self/fd	semantics='open'	)
73	( dir=/proc/$$/fd	semantics='open'	)
74	( dir=/dev/fd		semantics='open|dup'	)
75	( dir=/dev/fd		semantics='dup'	)
76)
77for ((fdfs=0; fdfs<${#FDFS[@]}-1; fdfs++))
78do	[[ -e ${FDFS[fdfs].dir} ]] && { command : > ${FDFS[fdfs].dir}/1; } 2>/dev/null && break
79done
80
81exec 3<> file1
82if	command exec 4< ${FDFS[fdfs].dir}/3
83then	read -u3 got
84	read -u4 got
85	exp='foo|bar'
86	case $got in
87	foo)	semantics='open' ;;
88	bar)	semantics='dup' ;;
89	*)	semantics='failed' ;;
90	esac
91	[[ $semantics == @(${FDFS[fdfs].semantics}) ]] || err_exit "'4< ${FDFS[fdfs].dir}/3' $semantics semantics instead of ${FDFS[fdfs].semantics} -- expected '$exp', got '$got'"
92fi
93
94# 2004-11-25 ancient /dev/fd/N redirection bug fix
95got=$(
96	{
97		print -n 1
98		print -n 2 > ${FDFS[fdfs].dir}/2
99		print -n 3
100		print -n 4 > ${FDFS[fdfs].dir}/2
101	}  2>&1
102)
103exp='1234|4'
104case $got in
1051234)	semantics='dup' ;;
1064)	semantics='open' ;;
107*)	semantics='failed' ;;
108esac
109[[ $semantics == @(${FDFS[fdfs].semantics}) ]] || err_exit "${FDFS[fdfs].dir}/N $semantics semantics instead of ${FDFS[fdfs].semantics} -- expected '$exp', got '$got'"
110
111cat > close0 <<\!
112exec 0<&-
113echo $(./close1)
114!
115print "echo abc" > close1
116chmod +x close0 close1
117x=$(./close0)
118if	[[ $x != "abc" ]]
119then	err_exit "picked up file descriptor zero for opening script file"
120fi
121cat > close0 <<\!
122	for ((i=0; i < 1100; i++))
123	do	exec 4< /dev/null
124		read -u4
125	done
126	exit 0
127!
128./close0 2> /dev/null || err_exit "multiple exec 4< /dev/null can fail"
129$SHELL -c '
130	trap "rm -f in out" EXIT
131	for ((i = 0; i < 1000; i++))
132	do	print -r -- "This is a test"
133	done > in
134	> out
135	exec 1<> out
136	builtin cat
137	print -r -- "$(<in)"
138	cmp -s in out'  2> /dev/null
139[[ $? == 0 ]] || err_exit 'builtin cat truncates files'
140cat >| script <<-\!
141print hello
142( exec 3<&- 4<&-)
143exec 3<&- 4<&-
144print world
145!
146chmod +x script
147[[ $( $SHELL ./script) == $'hello\nworld' ]] || err_exit 'closing 3 & 4 causes script to fail'
148cd ~- || err_exit "cd back failed"
149( exec  > '' ) 2> /dev/null  && err_exit '> "" does not fail'
150unset x
151( exec > ${x} ) 2> /dev/null && err_exit '> $x, where x null does not fail'
152exec <<!
153foo
154bar
155!
156( exec 0< /dev/null)
157read line
158if	[[ $line != foo ]]
159then	err_exit 'file descriptor not restored after exec in subshell'
160fi
161exec 3>&- 4>&-
162[[ $( {
163	read -r line;print -r -- "$line"
164	(
165	        read -r line;print -r -- "$line"
166	) & wait
167	while	read -r line
168        do	print -r -- "$line"
169	done
170 } << !
171line 1
172line 2
173line 3
174!) == $'line 1\nline 2\nline 3' ]] || err_exit 'read error with subshells'
175# 2004-05-11 bug fix
176cat > $tmp/1 <<- ++EOF++
177	script=$tmp/2
178	trap "rm -f \$script" EXIT
179	exec 9> \$script
180	for ((i=3; i<9; i++))
181	do	eval "while read -u\$i; do : ;done \$i</dev/null"
182		print -u9 "exec \$i< /dev/null"
183	done
184	for ((i=0; i < 60; i++))
185	do	print -u9 -f "%.80c\n"  ' '
186	done
187	print -u9 'print ok'
188	exec 9<&-
189	chmod +x \$script
190	\$script
191++EOF++
192chmod +x $tmp/1
193[[ $($SHELL  $tmp/1) == ok ]]  || err_exit "parent i/o causes child script to fail"
194# 2004-12-20 redirection loss bug fix
195cat > $tmp/1 <<- \++EOF++
196	function a
197	{
198		trap 'print ok' EXIT
199		: > /dev/null
200	}
201	a
202++EOF++
203chmod +x $tmp/1
204[[ $($tmp/1) == ok ]] || err_exit "trap on EXIT loses last command redirection"
205print > /dev/null {n}> $tmp/1
206[[ ! -s $tmp/1 ]] && newio=1
207if	[[ $newio && $(print hello | while read -u$n; do print $REPLY; done {n}<&0) != hello ]]
208then	err_exit "{n}<&0 not working with for loop"
209fi
210[[ $({ read -r;read -u3 3<&0; print -- "$REPLY" ;} <<!
211hello
212world
213!) == world ]] || err_exit 'I/O not synchronized with <&'
214x="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNSPQRSTUVWXYZ1234567890"
215for ((i=0; i < 62; i++))
216do	printf "%.39c\n"  ${x:i:1}
217done >  $tmp/seek
218if	command exec 3<> $tmp/seek
219then	(( $(3<#) == 0 )) || err_exit "not at position 0"
220	(( $(3<# ((EOF))) == 40*62 )) || err_exit "not at end-of-file"
221	command exec 3<# ((40*8)) || err_exit "absolute seek fails"
222	read -u3
223	[[ $REPLY == +(i) ]] || err_exit "expected iiii..., got $REPLY"
224	[[ $(3<#) == $(3<# ((CUR)) ) ]] || err_exit '$(3<#)!=$(3<#((CUR)))'
225	command exec 3<# ((CUR+80))
226	read -u3
227	[[ $REPLY == {39}(l) ]] || err_exit "expected lll..., got $REPLY"
228	command exec 3<# ((EOF-80))
229	read -u3
230	[[ $REPLY == +(9) ]] || err_exit "expected 999..., got $REPLY"
231	command exec 3># ((80))
232	print -u3 -f "%.39c\n"  @
233	command exec 3># ((80))
234	read -u3
235	[[ $REPLY == +(@) ]] || err_exit "expected @@@..., got $REPLY"
236	read -u3
237	[[ $REPLY == +(d) ]] || err_exit "expected ddd..., got $REPLY"
238	command exec 3># ((EOF))
239	print -u3 -f "%.39c\n"  ^
240	(( $(3<# ((CUR-0))) == 40*63 )) || err_exit "not at extended end-of-file"
241	command exec 3<# ((40*62))
242	read -u3
243	[[ $REPLY == +(^) ]] || err_exit "expected ddd..., got $REPLY"
244	command exec 3<# ((0))
245	command exec 3<# *jjjj*
246	read -u3
247	[[  $REPLY == {39}(j) ]] || err_exit "<# pattern failed"
248	[[ $(command exec 3<## *llll*) = {39}(k) ]] || err_exit "<## pattern not saving standard output"
249	read -u3
250	[[  $REPLY == {39}(l) ]] || err_exit "<## pattern failed to position"
251	command exec 3<# *abc*
252	read -u3 && err_exit "not found pattern not positioning at eof"
253	cat $tmp/seek | read -r <# *WWW*
254	[[ $REPLY == *WWWWW* ]] || err_exit '<# not working for pipes'
255	{ < $tmp/seek <# ((2358336120)) ;} 2> /dev/null || err_exit 'long seek not working'
256else	err_exit "$tmp/seek: cannot open for reading"
257fi
258command exec 3<&- || 'cannot close 3'
259for ((i=0; i < 62; i++))
260do	printf "%.39c\n"  ${x:i:1}
261done >  $tmp/seek
262if	command exec {n}<> $tmp/seek
263then	{ command exec {n}<#((EOF)) ;} 2> /dev/null || err_exit '{n}<# not working'
264	if	$SHELL -c '{n}</dev/null' 2> /dev/null
265	then	(( $({n}<#) ==  40*62))  || err_exit '$({n}<#) not working'
266	else	err_exit 'not able to parse {n}</dev/null'
267	fi
268fi
269$SHELL -ic '
270{
271    print -u2  || exit 2
272    print -u3  || exit 3
273    print -u4  || exit 4
274    print -u5  || exit 5
275    print -u6  || exit 6
276    print -u7  || exit 7
277    print -u8  || exit 8
278    print -u9  || exit 9
279}  3> /dev/null 4> /dev/null 5> /dev/null 6> /dev/null 7> /dev/null 8> /dev/null 9> /dev/null' > /dev/null 2>&1
280exitval=$?
281(( exitval ))  && err_exit  "print to unit $exitval failed"
282$SHELL -c "{ > $tmp/1 ; date;} >&- 2> /dev/null" > $tmp/2
283[[ -s $tmp/1 || -s $tmp/2 ]] && err_exit 'commands with standard output closed produce output'
284$SHELL -c "$SHELL -c ': 3>&1' 1>&- 2>/dev/null" && err_exit 'closed standard output not passed to subshell'
285[[ $(cat  <<- \EOF | $SHELL
286	do_it_all()
287	{
288	 	dd 2>/dev/null  # not a ksh93 buildin
289	 	return $?
290	}
291	do_it_all ; exit $?
292	hello world
293EOF) == 'hello world' ]] || err_exit 'invalid readahead on stdin'
294$SHELL -c 'exec 3>; /dev/null'  2> /dev/null && err_exit '>; with exec should be an error'
295$SHELL -c ': 3>; /dev/null'  2> /dev/null || err_exit '>; not working with at all'
296print hello > $tmp/1
297if	! $SHELL -c "false >; $tmp/1"  2> /dev/null
298then	[[ $(<$tmp/1) == hello ]] || err_exit '>; not preserving file on failure'
299fi
300if	! $SHELL -c "sed -e 's/hello/hello world/' $tmp/1" >; $tmp/1  2> /dev/null
301then	[[ $(<$tmp/1) == 'hello world' ]] || err_exit '>; not updating file on success'
302fi
303
304$SHELL -c 'exec 3<>; /dev/null'  2> /dev/null && err_exit '<>; with exec should be an error'
305$SHELL -c ': 3<>; /dev/null'  2> /dev/null || err_exit '<>; not working with at all'
306print $'hello\nworld' > $tmp/1
307if      ! $SHELL -c "false <>; $tmp/1"  2> /dev/null
308then    [[ $(<$tmp/1) == $'hello\nworld' ]] || err_exit '<>; not preserving file on failure'
309fi
310if	! $SHELL -c "head -1 $tmp/1" <>; $tmp/1  2> /dev/null
311then	[[ $(<$tmp/1) == hello ]] || err_exit '<>; not truncating file on success of head'
312fi
313print $'hello\nworld' > $tmp/1
314if	! $SHELL -c head  < $tmp/1 <#((6)) <>; $tmp/1  2> /dev/null
315then	[[ $(<$tmp/1) == world ]] || err_exit '<>; not truncating file on success of behead'
316fi
317
318unset y
319read -n1 y <<!
320abc
321!
322if      [[ $y != a ]]
323then    err_exit  'read -n1 not working'
324fi
325unset a
326{ read -N3 a; read -N1 b;}  <<!
327abcdefg
328!
329[[ $a == abc ]] || err_exit 'read -N3 here-document not working'
330[[ $b == d ]] || err_exit 'read -N1 here-document not working'
331read -n3 a <<!
332abcdefg
333!
334[[ $a == abc ]] || err_exit 'read -n3 here-document not working'
335(print -n a;sleep 1; print -n bcde) | { read -N3 a; read -N1 b;}
336[[ $a == abc ]] || err_exit 'read -N3 from pipe not working'
337[[ $b == d ]] || err_exit 'read -N1 from pipe not working'
338(print -n a;sleep 1; print -n bcde) |read -n3 a
339[[ $a == a ]] || err_exit 'read -n3 from pipe not working'
340if	mkfifo $tmp/fifo 2> /dev/null
341then	(print -n a; sleep 1;print -n bcde)  > $tmp/fifo &
342	{
343	read -u5 -n3  -t2 a  || err_exit 'read -n3 from fifo timedout'
344	read -u5 -n1 -t2 b || err_exit 'read -n1 from fifo timedout'
345	} 5< $tmp/fifo
346	[[ $a == a ]] || err_exit 'read -n3 from fifo not working'
347	rm -f $tmp/fifo
348	mkfifo $tmp/fifo 2> /dev/null
349	(print -n a; sleep 1;print -n bcde)  > $tmp/fifo &
350	{
351	read -u5 -N3 -t2 a || err_exit 'read -N3 from fifo timed out'
352	read -u5 -N1 -t2 b || err_exit 'read -N1 from fifo timedout'
353	} 5< $tmp/fifo
354	[[ $a == abc ]] || err_exit 'read -N3 from fifo not working'
355	[[ $b == d ]] || err_exit 'read -N1 from fifo not working'
356fi
357(
358	print -n 'prompt1: '
359	sleep .1
360	print line2
361	sleep .1
362	print -n 'prompt2: '
363	sleep .1
364) | {
365	read -t2 -n 1000 line1
366	read -t2 -n 1000 line2
367	read -t2 -n 1000 line3
368	read -t2 -n 1000 line4
369}
370[[ $? == 0 ]]		 	&& err_exit 'should have timed out'
371[[ $line1 == 'prompt1: ' ]] 	|| err_exit "line1 should be 'prompt1: '"
372[[ $line2 == line2 ]]		|| err_exit "line2 should be line2"
373[[ $line3 == 'prompt2: ' ]]	|| err_exit "line3 should be 'prompt2: '"
374[[ ! $line4 ]]			|| err_exit "line4 should be empty"
375
376if	$SHELL -c "export LC_ALL=en_US.UTF-8; c=$'\342\202\254'; [[ \${#c} == 1 ]]" 2>/dev/null
377then	lc_utf8=en_US.UTF-8
378else	lc_utf8=''
379fi
380
381typeset -a e o=(-n2 -N2)
382integer i
383set -- \
384	'a'	'bcd'	'a bcd'	'ab cd' \
385	'ab'	'cd'	'ab cd'	'ab cd' \
386	'abc'	'd'	'ab cd'	'ab cd' \
387	'abcd'	''	'ab cd'	'ab cd'
388while	(( $# >= 3 ))
389do	a=$1
390	b=$2
391	e[0]=$3
392	e[1]=$4
393	shift 4
394	for ((i = 0; i < 2; i++))
395	do	for lc_all in C $lc_utf8
396		do	g=$(LC_ALL=$lc_all $SHELL -c "{ print -n '$a'; sleep 0.2; print -n '$b'; sleep 0.2; } | { read ${o[i]} a; print -n \$a; read a; print -n \ \$a; }")
397			[[ $g == "${e[i]}" ]] || err_exit "LC_ALL=$lc_all read ${o[i]} from pipe '$a $b' failed -- expected '${e[i]}', got '$g'"
398		done
399	done
400done
401
402if	[[ $lc_utf8 ]]
403then	export LC_ALL=en_US.UTF-8
404	typeset -a c=( '' 'A' $'\303\274' $'\342\202\254' )
405	integer i w
406	typeset o
407	if	(( ${#c[2]} == 1 && ${#c[3]} == 1 ))
408	then	for i in 1 2 3
409		do	for o in n N
410			do	for w in 1 2 3
411				do	print -nr "${c[w]}" | read -${o}${i} g
412					if	[[ $o == N ]] && (( i > 1 ))
413					then	e=''
414					else	e=${c[w]}
415					fi
416					[[ $g == "$e" ]] || err_exit "read -${o}${i} failed for '${c[w]}' -- expected '$e', got '$g'"
417				done
418			done
419		done
420	fi
421fi
422
423exec 3<&2
424file=$tmp/file
425redirect 5>$file 2>&5
426print -u5 -f 'This is a test\n'
427print -u2 OK
428exec 2<&3
429exp=$'This is a test\nOK'
430got=$(< $file)
431[[ $got == $exp ]] || err_exit "output garbled when stderr is duped -- expected $(printf %q "$exp"), got $(printf %q "$got")"
432print 'hello world' > $file
4331<>; $file  1># ((5))
434(( $(wc -c < $file) == 5 )) || err_exit "$file was not truncate to 5 bytes"
435
436$SHELL -c "PS4=':2:'
437	exec 1> $tmp/21.out 2> $tmp/22.out
438	set -x
439	printf ':1:A:'
440	print \$(:)
441	print :1:Z:" 1> $tmp/11.out 2> $tmp/12.out
442[[ -s $tmp/11.out ]] && err_exit "standard output leaked past redirection"
443[[ -s $tmp/12.out ]] && err_exit "standard error leaked past redirection"
444exp=$':1:A:\n:1:Z:'
445got=$(<$tmp/21.out)
446[[ $exp == "$got" ]] || err_exit "standard output garbled -- expected $(printf %q "$exp"), got $(printf %q "$got")"
447exp=$':2:printf :1:A:\n:2::\n:2:print\n:2:print :1:Z:'
448got=$(<$tmp/22.out)
449[[ $exp == "$got" ]] || err_exit "standard error garbled -- expected $(printf %q "$exp"), got $(printf %q "$got")"
450
451exit $((Errors))
452