1########################################################################
2#                                                                      #
3#               This software is part of the ast package               #
4#          Copyright (c) 1982-2012 AT&T Intellectual Property          #
5#                      and is licensed under the                       #
6#                 Eclipse Public License, Version 1.0                  #
7#                    by AT&T Intellectual Property                     #
8#                                                                      #
9#                A copy of the License is available at                 #
10#          http://www.eclipse.org/org/documents/epl-v10.html           #
11#         (with md5 checksum b35adb5213ca9657e911e9befb180842)         #
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########################################################################
20########################################################################
21#                                                                      #
22#               This software is part of the ast package               #
23#          Copyright (c) 1982-2012 AT&T Intellectual Property          #
24#                      and is licensed under the                       #
25#                 Eclipse Public License, Version 1.0                  #
26#                    by AT&T Intellectual Property                     #
27#                                                                      #
28#                A copy of the License is available at                 #
29#          http://www.eclipse.org/org/documents/epl-v10.html           #
30#         (with md5 checksum b35adb5213ca9657e911e9befb180842)         #
31#                                                                      #
32#              Information and Software Systems Research               #
33#                            AT&T Research                             #
34#                           Florham Park NJ                            #
35#                                                                      #
36#              Roland Mainz <roland.mainz@nrubsig.org>                 #
37#                                                                      #
38########################################################################
39
40# test setup
41function err_exit
42{
43	print -u2 -n '\t'
44	print -u2 -r ${Command}[$1]: "${@:2}"
45	(( Errors++ ))
46}
47alias err_exit='err_exit $LINENO'
48
49# "nounset" disabled for now
50#set -o nounset
51Command=${0##*/}
52integer Errors=0 HAVE_signbit=0
53
54if	typeset -f .sh.math.signbit >/dev/null && (( signbit(-NaN) ))
55then	HAVE_signbit=1
56else	print -u2 "$0: warning: -lm does not support signbit(-NaN)"
57fi
58
59compound bracketstat=(
60	integer bopen=0
61	integer bclose=0
62)
63
64function count_brackets
65{
66	typeset x="$1"
67	typeset c
68
69	integer i
70	(( bracketstat.bopen=0 , bracketstat.bclose=0 ))
71
72	for (( i=0 ; i < ${#x} ; i++ )) ; do
73	        c="${x:i:1}"
74		[[ "$c" == '(' ]] && (( bracketstat.bopen++ ))
75		[[ "$c" == ')' ]] && (( bracketstat.bclose++ ))
76	done
77
78	(( bracketstat.bopen != bracketstat.bclose )) && return 1
79
80	return 0
81}
82
83# compound variable "cat" nr.1, using $ print "%B\n" ... #
84function cpvcat1
85{
86	set -o errexit
87	compound tmp
88
89	while read -C tmp ; do printf '%B\n' tmp ; done
90	return 0
91}
92
93# compound variable "cat" nr.2, using $ print "%#B\n" ... #
94function cpvcat2
95{
96	set -o errexit
97	compound tmp
98
99	while read -C tmp ; do printf '%#B\n' tmp ; done
100	return 0
101}
102
103# compound variable "cat" nr.3, using $ print -C ... #
104function cpvcat3
105{
106	set -o errexit
107	compound tmp
108
109	while read -C tmp ; do print -C tmp ; done
110	return 0
111}
112
113# compound variable "cat" nr.4, using $ print -v ... #
114function cpvcat4
115{
116	set -o errexit
117	compound tmp
118
119	while read -C tmp ; do print -v tmp ; done
120	return 0
121}
122
123typeset s
124
125# Test 1:
126# Check whether "read -C" leaves the file pointer at the next line
127# (and does not read beyond that point).
128# Data layout is:
129# -- snip --
130# <compound var>
131# hello
132# -- snip --
133# (additionally we test some extra stuff like bracket count)
134s=${
135	compound x=(
136		a=1 b=2
137		typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 )
138		typeset -A myarray2=( [a]=1 [b]=2 ["c d"]=3 [e]=4 ["f"]=5 [g]=6 [h]=7 [i]=8 [j]=9 [k]=10 )
139		typeset -A myarray3=(
140			[a]=(
141				float m1=0.5
142				float m2=0.6
143				foo="hello"
144			)
145			[b]=(
146				foo="bar"
147			)
148			["c d"]=(
149				integer at=90
150			)
151			[e]=(
152				compound nested_cpv=(
153					typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 )
154					typeset str=$'a \'string'
155				)
156			)
157			[f]=(
158				typeset g="f"
159			)
160			[a_nan]=(
161				float my_nan=-nan
162			)
163			[a_hexfloat]=(
164			       typeset -X my_hexfloat=1.1
165			)
166		)
167	)
168
169	{
170		printf "%B\n" x
171		print "hello"
172	} | cpvcat1 | cpvcat2 | cpvcat3 | cpvcat4 | {
173		read -C y
174		read s
175	}
176	print "x${s}x"
177} || err_exit "test returned exit code $?"
178
179[[ "${s}" == "xhellox" ]] || err_exit "Expected 'xhellox', got ${s}"
180count_brackets "$y" || err_exit "y: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
181count_brackets "$(print -v y)" || err_exit "y: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
182count_brackets "$(print -C y)" || err_exit "y: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
183
184# cleanup
185unset x y || err_exit "unset failed"
186[[ "$x" == '' ]] || err_exit "cleanup failed for x"
187[[ "$y" == '' ]] || err_exit "cleanup failed for y"
188
189
190# Test 2:
191# Same as test 1 except one more compound var following the "hello"
192# line.
193# Data layout is:
194# -- snip --
195# <compound var>
196# hello
197# <compound var>
198# -- snip --
199s=${
200	compound x=(
201		a=1 b=2
202		typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 )
203		typeset -A myarray2=( [a]=1 [b]=2 ["c d"]=3 [e]=4 ["f"]=5 [g]=6 [h]=7 [i]=8 [j]=9 [k]=10 )
204		compound -A myarray3=(
205			[a]=(
206				float m1=0.5
207				float m2=0.6
208				foo="hello"
209			)
210			[b]=(
211				foo="bar"
212			)
213			["c d"]=(
214				integer at=90
215			)
216			[e]=(
217				compound nested_cpv=(
218					typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 )
219					typeset str=$'a \'string'
220				)
221			)
222			[f]=(
223				typeset g="f"
224			)
225			[a_nan]=(
226				float my_nan=-nan
227			)
228			[a_hexfloat]=(
229			       typeset -X my_hexfloat=1.1
230			)
231		)
232	)
233
234	{
235		printf "%B\n" x
236		print "hello"
237		printf "%B\n" x
238	} | cpvcat1 | cpvcat2 | cpvcat3 | cpvcat4 | {
239		read -C y1
240		read s
241		read -C y2
242	}
243
244	print "x${s}x"
245} || err_exit "test returned exit code $?"
246
247[[ "${s}" == "xhellox" ]] || err_exit "Expected 'xhellox', got ${s}."
248[[ "${y1.myarray3[b].foo}" == "bar" ]] || err_exit "y1.myarray3[b].foo != bar"
249[[ "${y2.myarray3[b].foo}" == "bar" ]] || err_exit "y2.myarray3[b].foo != bar"
250[[ "$y1" != "" ]] || err_exit "y1 is empty"
251[[ "$y2" != "" ]] || err_exit "y2 is empty"
252(( ${#y1.myarray3[e].nested_cpv.myarray[@]} == 10 )) || err_exit "Expected 10 elements in y1.myarray3[e].nested_cpv, got ${#y1.myarray3[e].nested_cpv[@]}"
253(( ${#y2.myarray3[e].nested_cpv.myarray[@]} == 10 )) || err_exit "Expected 10 elements in y2.myarray3[e].nested_cpv, got ${#y2.myarray3[e].nested_cpv[@]}"
254(( isnan(y1.myarray3[a_nan].my_nan) ))   || err_exit "y1.myarray3[a_nan].my_nan not a NaN"
255(( isnan(y2.myarray3[a_nan].my_nan) ))   || err_exit "y2.myarray3[a_nan].my_nan not a NaN"
256if	(( HAVE_signbit ))
257then	(( signbit(y1.myarray3[a_nan].my_nan) )) || err_exit "y1.myarray3[a_nan].my_nan not negative"
258	(( signbit(y2.myarray3[a_nan].my_nan) )) || err_exit "y2.myarray3[a_nan].my_nan not negative"
259fi
260count_brackets "$y1" || err_exit "y1: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
261count_brackets "$(print -v y1)" || err_exit "y1: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
262count_brackets "$(print -C y1)" || err_exit "y1: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
263count_brackets "$y2" || err_exit "y2: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
264count_brackets "$(print -v y2)" || err_exit "y2: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
265count_brackets "$(print -C y2)" || err_exit "y2: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
266[[ "$y1" == "$y2" ]] || err_exit "Expected $(printf "%q\n" "${y1}") == $(printf "%q\n" "${y2}")."
267[[ "$x"  == "$y1" ]] || err_exit "Expected $(printf "%q\n" "${x}") == $(printf "%q\n" "${y1}")."
268
269# cleanup
270unset x y1 y2 || err_exit "unset failed"
271[[ "$x" == '' ]]  || err_exit "cleanup failed for x"
272[[ "$y1" == '' ]] || err_exit "cleanup failed for y1"
273[[ "$y2" == '' ]] || err_exit "cleanup failed for y2"
274
275
276# Test 3: Test compound variable copy operator vs. "read -C"
277compound x=(
278	a=1 b=2
279	typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 )
280	typeset -A myarray2=( [a]=1 [b]=2 ["c d"]=3 [e]=4 ["f"]=5 [g]=6 [h]=7 [i]=8 [j]=9 [k]=10 )
281	compound -A myarray3=(
282		[a]=(
283			float m1=0.5
284			float m2=0.6
285			foo="hello"
286		)
287		[b]=(
288			foo="bar"
289		)
290		["c d"]=(
291			integer at=90
292		)
293		[e]=(
294			compound nested_cpv=(
295				typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 )
296				typeset str=$'a \'string'
297			)
298		)
299		[f]=(
300			typeset g="f"
301		)
302		[a_nan]=(
303			float my_nan=-nan
304		)
305		[a_hexfloat]=(
306		       typeset -X my_hexfloat=1.1
307		)
308	)
309)
310
311compound x_copy=x || err_exit "x_copy copy failed"
312[[ "${x_copy}" != "" ]] || err_exit "x_copy should not be empty"
313count_brackets "${x_copy}" || err_exit "x_copy: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
314count_brackets "$(print -v x_copy)" || err_exit "x_copy: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
315count_brackets "$(print -C x_copy)" || err_exit "x_copy: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
316
317compound nested_cpv_copy
318
319nested_cpv_copy=x.myarray3[e].nested_cpv || err_exit "x.myarray3[e].nested_cpv copy failed"
320(( ${#nested_cpv_copy.myarray[@]} == 10 )) || err_exit "Expected 10 elements in nested_cpv_copy.myarray, got ${#nested_cpv_copy.myarray[@]}"
321
322# unset branch "x.myarray3[e].nested_cpv" of the variable tree "x" ...
323unset x.myarray3[e].nested_cpv || err_exit "unset x.myarray3[e].nested_cpv failed"
324[[ "${x.myarray3[e].nested_cpv}" == "" ]] || err_exit "x.myarray3[e].nested_cpv still has a value"
325
326# ... and restore it from the saved copy
327printf "%B\n" nested_cpv_copy | cpvcat1 | cpvcat2 | cpvcat3 | cpvcat4 | read -C x.myarray3[e].nested_cpv || err_exit "read failed"
328
329# compare copy of the original tree and the modified one
330[[ "${x}" == "${x_copy}" ]] || err_exit "x != x_copy"
331count_brackets "${x}" || err_exit "x: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
332count_brackets "$(print -v x)" || err_exit "x: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
333count_brackets "$(print -C x)" || err_exit "x: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}"
334(( ${#x.myarray3[e].nested_cpv.myarray[@]} == 10 )) || err_exit "Expected 10 elements in x.myarray3[e].nested_cpv, got ${#x.myarray3[e].nested_cpv[@]}"
335(( isnan(x.myarray3[a_nan].my_nan) ))   || err_exit "x.myarray3[a_nan].my_nan not a NaN"
336if	(( HAVE_signbit ))
337then	(( signbit(x.myarray3[a_nan].my_nan) )) || err_exit "x.myarray3[a_nan].my_nan not negative"
338fi
339
340# cleanup
341unset x x_copy nested_cpv_copy || err_exit "unset failed"
342
343
344# Test 4: Test "read -C" failure for missing bracket at the end
345typeset s
346s=$($SHELL -c 'compound myvar ; print "( unfinished=1" | read -C myvar 2>'/dev/null' || print "error $?"') || err_exit 'shell failed'
347[[ "$s" == 'error 3' ]] || err_exit "compound_read: expected error 3, got ${s}"
348
349
350# Test 5: Test "read -C" failure for missing bracket at the beginning
351typeset s
352s=$($SHELL -c 'compound myvar ; print "  unfinished=1 )" | read -C myvar 2>'/dev/null' || print "error $?"') || err_exit 'shell failed'
353[[ "$s" == 'error 3' ]] || err_exit "compound_read: expected error 3, got ${s}"
354
355
356# test6: Derived from the test2 for CR #6944386
357# ("compound v=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; print -C v prints trash")
358# which caused compound variables to be corrupted like this:
359# -- snip --
360# ksh93 -c 'compound v=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; print -v v'
361# (
362#        typeset -A -l -i ar=(
363#                [aa]=$'\004'
364#                [bb]=$'\t'
365#        )
366# )
367# -- snip --
368
369function test6
370{
371        compound out=( typeset stdout stderr ; integer res )
372	compound val
373	integer testid
374
375	compound -r -a tests=(
376		# subtests1:
377		( cmd='compound v=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; print -C v' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'			arrefname='ar' )
378		( cmd='compound v=( float   -A ar=( [aa]=4 [bb]=9 ) ; ) ; print -C v' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)' 			arrefname='ar' )
379		( cmd='compound v=( typeset -A ar=( [aa]=4 [bb]=9 ) ; ) ; print -C v' stdoutpattern=$'~(Alr)(.*\\[bb\\]=["\']*9.*)&(.*\\[aa\\]=["\']*4.*)'	arrefname='ar' )
380		( cmd='compound v=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; print -v v' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)' 			arrefname='ar' )
381		( cmd='compound v=( float   -A ar=( [aa]=4 [bb]=9 ) ; ) ; print -v v' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)' 			arrefname='ar' )
382		( cmd='compound v=( typeset -A ar=( [aa]=4 [bb]=9 ) ; ) ; print -v v' stdoutpattern=$'~(Alr)(.*\\[bb\\]=["\']*9.*)&(.*\\[aa\\]=["\']*4.*)' 	arrefname='ar' )
383
384		# subtests2: Same as subtests1 but variable "v" is inside "vx"
385		( cmd='compound vx=( compound v=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'		arrefname='v.ar' )
386		( cmd='compound vx=( compound v=( float   -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'		arrefname='v.ar' )
387		( cmd='compound vx=( compound v=( typeset -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=["\']*9.*)&(.*\\[aa\\]=["\']*4.*)'	arrefname='v.ar' )
388		( cmd='compound vx=( compound v=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'		arrefname='v.ar' )
389		( cmd='compound vx=( compound v=( float   -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'		arrefname='v.ar' )
390		( cmd='compound vx=( compound v=( typeset -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=["\']*9.*)&(.*\\[aa\\]=["\']*4.*)'	arrefname='v.ar' )
391
392		# subtests3: Same as subtests1 but variable "va" is an indexed array
393		( cmd='compound vx=( compound -a va=( [3]=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'			arrefname='va[3].ar' )
394		( cmd='compound vx=( compound -a va=( [3]=( float   -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)' 		arrefname='va[3].ar' )
395		( cmd='compound vx=( compound -a va=( [3]=( typeset -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=["\']*9.*)&(.*\\[aa\\]=["\']*4.*)'	arrefname='va[3].ar' )
396		( cmd='compound vx=( compound -a va=( [3]=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'			arrefname='va[3].ar' )
397		( cmd='compound vx=( compound -a va=( [3]=( float   -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'			arrefname='va[3].ar' )
398		( cmd='compound vx=( compound -a va=( [3]=( typeset -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=["\']*9.*)&(.*\\[aa\\]=["\']*4.*)'	arrefname='va[3].ar' )
399
400		# subtests4: Same as subtests1 but variable "va" is an 2d indexed array
401		( cmd='compound vx=( compound -a va=( [3][17]=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'		arrefname='va[3][17].ar' )
402 		( cmd='compound vx=( compound -a va=( [3][17]=( float	-A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'		arrefname='va[3][17].ar' )
403 		( cmd='compound vx=( compound -a va=( [3][17]=( typeset -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=["\']*9.*)&(.*\\[aa\\]=["\']*4.*)' arrefname='va[3][17].ar' )
404 		( cmd='compound vx=( compound -a va=( [3][17]=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'		arrefname='va[3][17].ar' )
405 		( cmd='compound vx=( compound -a va=( [3][17]=( float	-A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'		arrefname='va[3][17].ar' )
406 		( cmd='compound vx=( compound -a va=( [3][17]=( typeset -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=["\']*9.*)&(.*\\[aa\\]=["\']*4.*)' arrefname='va[3][17].ar' )
407
408		# subtests5: Same as subtests1 but variable "va" is an associative array
409		( cmd='compound vx=( compound -A va=( [l]=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'			arrefname='va[l].ar' )
410		( cmd='compound vx=( compound -A va=( [l]=( float   -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'			arrefname='va[l].ar' )
411		( cmd='compound vx=( compound -A va=( [l]=( typeset -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -C vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=["\']*9.*)&(.*\\[aa\\]=["\']*4.*)'	arrefname='va[l].ar' )
412		( cmd='compound vx=( compound -A va=( [l]=( integer -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'			arrefname='va[l].ar' )
413		( cmd='compound vx=( compound -A va=( [l]=( float   -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=9.*)&(.*\\[aa\\]=4.*)'			arrefname='va[l].ar' )
414		( cmd='compound vx=( compound -A va=( [l]=( typeset -A ar=( [aa]=4 [bb]=9 ) ; ) ; ) ; ) ; print -v vx' stdoutpattern=$'~(Alr)(.*\\[bb\\]=["\']*9.*)&(.*\\[aa\\]=["\']*4.*)'	arrefname='va[l].ar' )
415	)
416
417	for testid in "${!tests[@]}" ; do
418		nameref test=tests[testid]
419		typeset testname="test2/${testid}"
420
421	        out.stderr="${ { out.stdout="${ ${SHELL} -c "${test.cmd}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }"
422
423        	(( out.res == 0 )) || err_exit "${testname}: Test shell returned with exit code ${out.res}"
424        	[[ "${out.stdout}" == ${test.stdoutpattern} ]] || err_exit "${testname}: Expected match for ${test.stdoutpattern}, got $(printf "%q\n" "${out.stdout}")"
425        	[[ "${out.stderr}" == ""                    ]] || err_exit "${testname}: Expected empty stderr, got $(printf "%q\n" "${out.stderr}")"
426
427		read -C val <<<"${out.stdout}" || err_exit "${testname}: read -C val failed with exit code $?"
428		nameref ar="val.${test.arrefname}"
429		(( ar[aa] == 4 )) || err_exit "${testname}: Expected ar[aa] == 4, got ${ar[aa]}"
430		(( ar[bb] == 9 )) || err_exit "${testname}: Expected ar[bb] == 9, got ${ar[bb]}"
431	done
432
433	return 0
434}
435
436test6
437
438function test_3D_array_read_C
439{
440        compound out=( typeset stdout stderr ; integer res )
441	integer i
442	typeset -r -a tests=(
443		# ast-ksh.2010-03-09 will print "ksh93[1]: read: line 4: 0[0]: invalid variable name" for 3D arrays passed to read -C
444		'compound c=( typeset -a x ) ; for (( i=0 ; i < 3 ; i++ )) ; do for (( j=0 ; j < 3 ; j++ )) ; do for (( k=0 ; k < 3 ; k++ )) ; do c.x[i][j][k]="$i$j$k" ; done; done; done ; unset c.x[2][0][1] ; print -v c | read -C dummy'
445
446		# same test, 4D, fails with 'ksh[1]: read: line 4: 0: invalid variable name'
447		'compound c=( typeset -a x ) ; for (( i=0 ; i < 3 ; i++ )) ; do for (( j=0 ; j < 3 ; j++ )) ; do for (( k=0 ; k < 3 ; k++ )) ; do for (( l=0 ; l < 3 ; l++ )) ; do c.x[i][j][k][l]="$i$j$k$l" ; done; done; done ; done ; unset c.x[2][0][1][2] ; print -v c | read -C dummy'
448	)
449
450	for (( i=0 ; i < ${#tests[@]} ; i++ )) ; do
451		out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -c "${tests[i]}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }"
452
453	        [[ "${out.stdout}" == '' ]] || err_exit "$0/${i}: Expected empty stdout, got $(printf '%q\n' "${out.stdout}")"
454        	[[ "${out.stderr}" == '' ]] || err_exit "$0/${i}: Expected empty stderr, got $(printf '%q\n' "${out.stderr}")"
455	done
456
457	return 0
458}
459
460
461function test_access_2Darray_in_type_in_compound
462{
463        compound out=( typeset stdout stderr ; integer res )
464	integer i
465	typeset -r -a tests=(
466		# ast-ksh.2010-03-09 will print 'ksh: line 1: l.c.x[i][j]=: no parent'
467		'typeset -T c_t=(typeset -a x) ; compound l=( c_t c ) ; for ((i=0;i<3;i++));do for ((j=0;j<3;j++));do l.c.x[i][j]="" ; done; done; print -v l | read -C dummy'
468	)
469
470	for (( i=0 ; i < ${#tests[@]} ; i++ )) ; do
471		out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -c "${tests[i]}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }"
472
473	        [[ "${out.stdout}" == '' ]] || err_exit "$0/${i}: Expected empty stdout, got $(printf '%q\n' "${out.stdout}")"
474        	[[ "${out.stderr}" == '' ]] || err_exit "$0/${i}: Expected empty stderr, got $(printf '%q\n' "${out.stderr}")"
475	done
476
477	return 0
478}
479
480function test_read_type_crash
481{
482        compound out=( typeset stdout stderr ; integer res )
483	typeset -r test='
484typeset -T field_t=(
485	typeset -a f
486
487	function reset
488	{
489		integer i j
490
491		for (( i=0 ; i < 3 ; i++ )) ; do
492			for (( j=0 ; j < 3 ; j++ )) ; do
493				_.f[i][j]=""
494			done
495		done
496		return 0
497	}
498
499	function enumerate_empty_fields
500	{
501		integer i j
502
503		for (( i=0 ; i < 3 ; i++ )) ; do
504			for (( j=0 ; j < 3 ; j++ )) ; do
505				[[ "${_.f[i][j]}" == "" ]] && printf "[%d][%d]\n" i j
506			done
507		done
508		return 0
509	}
510
511	function setf
512	{
513		_.f[$1][$2]="$3"
514	}
515)
516
517set -o nounset
518
519compound c1=( field_t x )
520
521c1.x.reset
522
523print -v c1 | read -C c2
524print -v c2
525'
526
527	out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -c "${test}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }"
528
529        [[ "${out.stdout}" != '' ]] || err_exit "$0: Expected nonempty stdout."
530       	[[ "${out.stderr}" == '' ]] || err_exit "$0: Expected empty stderr, got $(printf '%q\n' "${out.stderr}")"
531
532	if [[ -f 'core' && -x '/usr/bin/pstack' ]] ; then
533		pstack 'core'
534		rm 'core'
535	fi
536
537	return 0
538}
539
540
541function test_read_C_into_array
542{
543        compound out=( typeset stdout stderr ; integer res )
544	# fixme:
545	# - The tests should cover 3D and 5D indexed arrays and namerefs to sub-dimensions of a 5D indexed array
546	compound -r -a tests=(
547		( cmd='             typeset -a -C l   ;                        printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l[4] ;      print -v l' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)' ) )
548		( cmd='             typeset -a -C l   ; nameref l4=l[4] ;      printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l4 ;        print -v l' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)' ) )
549
550		( cmd='             typeset -a -C l   ;                        printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l[4][6] ;   print -v l' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)&(.+\[6\].+)' ) )
551		( cmd='             typeset -a -C l   ; nameref l4=l[4][6] ;   printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l4 ;        print -v l' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)&(.+\[6\].+)' ) )
552
553		( cmd='             typeset -a -C l   ;                        printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l[4][6][9][11][15] ;   print -v l' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)&(.+\[6\].+)' '~(X)(.+\[9\].+)&(.+\[11\].+)&(.+\[15\].+)' ) )
554		( cmd='             typeset -a -C l   ; nameref l4=l[4][6][9][11][15] ;   printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l4 ;        print -v l' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)&(.+\[6\].+)' '~(X)(.+\[9\].+)&(.+\[11\].+)&(.+\[15\].+)' ) )
555
556		( cmd='             typeset -A -C l   ;                        printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l[4] ;      print -v l' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)' ) )
557		( cmd='             typeset -A -C l   ; nameref l4=l[4] ;      printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l4 ;        print -v l' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)' ) )
558		( cmd='compound c ; typeset -a -C c.l ;                        printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C c.l[4] ;    print -v c' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)' ) )
559		( cmd='compound c ; typeset -a -C c.l ; nameref l4=c.l[4] ;    printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l4 ;        print -v c' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)' ) )
560
561		( cmd='compound c ; typeset -a -C c.l ;                        printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C c.l[4][6] ; print -v c' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)&(.+\[6\].+)' ) )
562		( cmd='compound c ; typeset -a -C c.l ; nameref l4=c.l[4][6] ; printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l4 ;        print -v c' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)&(.+\[6\].+)' ) )
563
564		( cmd='compound c ; typeset -a -C c.l ;                        printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C c.l[4][6][9][11][15] ; print -v c' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)&(.+\[6\].+)' '~(X)(.+\[9\].+)&(.+\[11\].+)&(.+\[15\].+)'  ) )
565		( cmd='compound c ; typeset -a -C c.l ; nameref l4=c.[4][6][9][11][15] ; printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l4 ;         print -v c' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)&(.+\[6\].+)' '~(X)(.+\[9\].+)&(.+\[11\].+)&(.+\[15\].+)'  ) )
566
567		( cmd='compound c ; typeset -A -C c.l ;                        printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C c.l[4] ;    print -v c' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)' ) )
568		( cmd='compound c ; typeset -A -C c.l ; nameref l4=c.l[4] ;    printf "( typeset -a ar=( 1\n2\n3\n) b=1 )\n" | read -C l4 ;        print -v c' typeset -a stdoutpattern=( '~(X)(.+b=1.+)&(.+\[4\].+)' ) )
569	)
570	typeset cmd
571	typeset pat
572	integer i
573
574	compound -a test_variants
575
576	# build list of variations of the tests above
577	for (( i=0 ; i < ${#tests[@]} ; i++ )) ; do
578		nameref tst=tests[i]
579
580		# plain test
581		cmd="${tst.cmd}"
582		test_variants+=( testname="${0}/${i}/plain" cmd="$cmd" typeset -a stdoutpattern=( "${tst.stdoutpattern[@]}" ) )
583
584		# test with "read -C" in a function
585		cmd="${tst.cmd/~(E)read[[:space:]]+-C[[:space:]]+([[:alnum:]]+)[[:space:]]+\;/{ function rf { nameref val=\$1 \; read -C val \; } \; rf \1 \; } \; }"
586		test_variants+=( testname="${0}/${i}/read_in_function" cmd="$cmd" typeset -a stdoutpattern=( "${tst.stdoutpattern[@]}" ) )
587
588		# test with "read -C" in a nested function
589		cmd="${tst.cmd/~(E)read[[:space:]]+-C[[:space:]]+([[:alnum:]]+)[[:space:]]+\;/{ function rf2 { nameref val=\$1 \; read -C val \; } \; function rf { nameref val=\$1 \; rf2 val \; } \; rf \1 \; } \; }"
590		test_variants+=( testname="${0}/${i}/read_in_nested_function" cmd="$cmd" typeset -a stdoutpattern=( "${tst.stdoutpattern[@]}" ) )
591
592		# test with "read -C" in a nested function with target variable
593		# being a function-local variable of function "main"
594		cmd='function rf2 { nameref val=$1 ; read -C val ; } ; function rf { nameref val=$1 ; rf2 val ; } ; function main { '
595		cmd+="${tst.cmd/~(E)read[[:space:]]+-C[[:space:]]+([[:alnum:]]+)[[:space:]]+\;/rf \1 \; }"
596		cmd+=' ; } ; main'
597		test_variants+=( testname="${0}/${i}/read_into_localvar_in_nested_function" cmd="$cmd" typeset -a stdoutpattern=( "${tst.stdoutpattern[@]}" ) )
598	done
599
600	# run test variants
601	for (( i=0 ; i < ${#test_variants[@]} ; i++ )) ; do
602		nameref tv=test_variants[i]
603
604		out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -o errexit -c "${tv.cmd}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }"
605
606		for pat in "${tv.stdoutpattern[@]}" ; do
607			[[ "${out.stdout}" == ${pat} ]] || err_exit "${tv.testname}: Expected stdout of $(printf '%q\n' "${tv.cmd}") to match $(printf '%q\n' "${pat}"), got $(printf '%q\n' "${out.stdout}")"
608		done
609		[[ "${out.stderr}" == '' ]] || err_exit "${tv.testname}: Expected empty stderr for $(printf '%q\n' "${tv.cmd}"), got $(printf '%q\n' "${out.stderr}")"
610		(( out.res == 0 )) || err_exit "${tv.testname}: Unexpected exit code ${out.res} for $(printf '%q\n' "${tv.cmd}")"
611	done
612
613	return 0
614}
615
616
617# This test checks whether reading a compound variable value with
618# "read -C var" which contains special shell keywords or aliases
619# like "functions", "alias", "!" etc. in a string array causes the
620# shell to produce errors like this:
621# -- snip --
622# $ ksh93 -c 'print "( compound -A a1=( [4]=( typeset -a x=( alias ) ) ) ;
623# compound -A a2=( [4]=( typeset -a x=( ! ! ! alias ) ) ) )" | read -C c ; print -v c' 1>/dev/null
624# ksh93[1]: alias: c.a1[4].x: compound assignment requires sub-variable name
625# -- snip --
626# A 2nd issue indirectly tested here was that simple indexed string array
627# declarations in a function with the same special keywords did not work
628# either.
629# This happened in ast-ksh.2010-11-12 or older.
630function test_read_C_special_shell_keywords
631{
632	typeset -r -a testcmdpatterns=(
633		# this was the original testcase
634		'print "( compound -A a1=( [4]=( typeset -a x=( %keyword% ) ) ) ; compound -A a2=( [4]=( typeset -a x=( ! ! ! alias ) ) ) )" | read -C c ; print "X${c.a1[4].x[0]}X"'
635		# same as above but uses indexed arrays instead of associative arrays
636		'print "( compound -a a1=( [4]=( typeset -a x=( %keyword% ) ) ) ; compound -a a2=( [4]=( typeset -a x=( ! ! ! alias ) ) ) )" | read -C c ; print "X${c.a1[4].x[0]}X"'
637		# same as first testcase but uses a blank in the array index value
638		$'print "( compound -A a1=( [\'hello world\']=( typeset -a x=( %keyword% ) ) ) ; compound -A a2=( [\'hello world\']=( typeset -a x=( ! ! ! alias ) ) ) )" | read -C c ; print "X${c.a1[\'hello world\'].x[0]}X"'
639	)
640	typeset -r -a shell_special_words=(
641		'alias'
642		'compound'
643		'function'
644		'functions'
645		'integer'
646		'local'
647		'namespace'
648		'typeset'
649		'SECONDS'
650		'.sh.version'
651		'!'
652	)
653	integer spwi # shell_special_words index
654	integer tcpi # testcmdpatterns index
655	typeset testcmd
656	typeset testname
657	typeset shkeyword
658        compound out=( typeset stdout stderr ; integer res )
659
660	for (( tcpi=0 ; tcpi < ${#testcmdpatterns[@]} ; tcpi++ )) ; do
661		for (( spwi=0 ; spwi < ${#shell_special_words[@]} ; spwi++ )) ; do
662			shkeyword=${shell_special_words[spwi]}
663			testcmd="${testcmdpatterns[tcpi]//%keyword%/${shkeyword}}"
664			testname="${0}/${tcpi}/${spwi}/"
665
666			out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -o errexit -c "${testcmd}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }"
667
668        		[[ "${out.stdout}" == "X${shkeyword}X" ]] || err_exit "${testname}: Expected stdout to match $(printf '%q\n' "X${shkeyword}X"), got $(printf '%q\n' "${out.stdout}")"
669			[[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got $(printf '%q\n' "${out.stderr}")"
670			(( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}"
671		done
672	done
673
674	return 0
675}
676
677
678test_3D_array_read_C
679test_access_2Darray_in_type_in_compound
680test_read_type_crash
681test_read_C_into_array
682test_read_C_special_shell_keywords
683
684
685# tests done
686exit $((Errors<125?Errors:125))
687