1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21
22#
23# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24#
25
26#
27# variable tree test #002
28# Propose of this test is whether ksh93 handles global variable trees
29# and function-local variable trees the same way, including "nameref"
30# and "unset" handling.
31#
32
33# test setup
34function err_exit
35{
36	print -u2 -n "\t"
37	print -u2 -r ${Command}[$1]: "${@:2}"
38	(( Errors < 127 && Errors++ ))
39}
40alias err_exit='err_exit $LINENO'
41
42# the test cannot use "nounset"
43Command=${0##*/}
44integer Errors=0
45
46# "built_tree1" and "built_tree2" are identical except the way how they test
47# whether a variable exists:
48# - "built_tree1" uses "${varname}" != "", e.g. looking whether the variable
49#    as non-zero length content
50# - "built_tree2" uses "! ([[ -v varname ]] ; res=$? ; unset varname ; exit $res)", e.g. "unset" in a subshell.
51function build_tree1
52{
53#set -o errexit -o xtrace
54	typeset index
55	typeset s
56	typeset i
57	typeset dummy
58	typeset a b c d e f
59
60	nameref dest_tree="$1" # destination tree
61	nameref srcdata="$2"   # source data
62	typeset tree_mode="$3" # mode to define the type of leads
63
64	typeset -A dest_tree.l1
65
66	for index in "${!srcdata.hashnodes[@]}" ; do
67		nameref node=srcdata.hashnodes["${index}"]
68
69		for i in "${node.xlfd[@]}" ; do
70			IFS='-' read dummy a b c d e f <<<"$i"
71
72			if [[ "$a" == "" ]] ; then
73				a="$dummy"
74			fi
75
76			[[ "$a" == "" ]] && a='-'
77			[[ "$b" == "" ]] && b='-'
78			[[ "$c" == "" ]] && c='-'
79
80			if [[ "${dest_tree.l1["$a"]}" == "" ]] ; then
81			#if ! (unset dest_tree.l1["$a"]) ; then
82				typeset -A dest_tree.l1["$a"].l2
83			fi
84
85			if [[ "${dest_tree.l1["$a"].l2["$b"]}" == "" ]] ; then
86			#if ! (unset dest_tree.l1["$a"].l2["$b"]) ; then
87				typeset -A dest_tree.l1["$a"].l2["$b"].l3
88			fi
89
90			if [[ "${!dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[*]}" == "" ]] ; then
91				typeset -A dest_tree.l1["$a"].l2["$b"].l3["$c"].entries
92			fi
93
94			typeset new_index
95			if [[ "${tree_mode}" == "leaf_name" ]] ; then
96				new_index=$(( ${#dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[@]}+1 ))
97			else
98				new_index="${node.name}"
99
100				# skip if the leaf node already exists
101				if [[ "${dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}]}" != "" ]] ; then
102					continue
103				fi
104			fi
105
106			add_tree_leaf dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}] "${index}" "${tree_mode}"
107		done
108	done
109
110	return 0
111}
112
113# "built_tree1" and "built_tree2" are identical except the way how they test
114# whether a variable exists:
115# - "built_tree1" uses "${varname}" != "", e.g. looking whether the variable
116#    as non-zero length content
117# - "built_tree2" uses "! ([[ -v varname ]] ; res=$? ; unset varname ; exit $res)", e.g. "unset" in a subshell.
118function build_tree2
119{
120#set -o errexit -o xtrace
121	typeset index
122	typeset s
123	typeset i
124	typeset dummy
125	typeset a b c d e f
126
127	nameref dest_tree="$1" # destination tree
128	nameref srcdata="$2"   # source data
129	typeset tree_mode="$3" # mode to define the type of leads
130
131	typeset -A dest_tree.l1
132
133	for index in "${!srcdata.hashnodes[@]}" ; do
134		nameref node=srcdata.hashnodes["${index}"]
135
136		for i in "${node.xlfd[@]}" ; do
137			IFS='-' read dummy a b c d e f <<<"$i"
138
139			if [[ "$a" == "" ]] ; then
140				a="$dummy"
141			fi
142
143			[[ "$a" == "" ]] && a='-'
144			[[ "$b" == "" ]] && b='-'
145			[[ "$c" == "" ]] && c='-'
146
147			#if [[ "${dest_tree.l1["$a"]}" == "" ]] ; then
148			if ! ([[ -v dest_tree.l1["$a"] ]] ; res=$? ; unset dest_tree.l1["$a"] ; exit $res) ; then
149				typeset -A dest_tree.l1["$a"].l2
150			fi
151
152			#if [[ "${dest_tree.l1["$a"].l2["$b"]}" == "" ]] ; then
153			if ! ([[ -v dest_tree.l1["$a"].l2["$b"] ]] ; res=$? ; unset dest_tree.l1["$a"].l2["$b"] ; exit $res) ; then
154				typeset -A dest_tree.l1["$a"].l2["$b"].l3
155			fi
156
157			if [[ "${!dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[*]}" == "" ]] ; then
158				typeset -A dest_tree.l1["$a"].l2["$b"].l3["$c"].entries
159			fi
160
161			typeset new_index
162			if [[ "${tree_mode}" == "leaf_name" ]] ; then
163				new_index=$(( ${#dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[@]}+1 ))
164			else
165				new_index="${node.name}"
166
167				# skip if the leaf node already exists
168				if [[ "${dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}]}" != "" ]] ; then
169					continue
170				fi
171			fi
172
173			add_tree_leaf dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}] "${index}" "${tree_mode}"
174		done
175	done
176
177	return 0
178}
179
180
181function add_tree_leaf
182{
183	nameref tree_leafnode="$1"
184	nameref data_node=srcdata.hashnodes["$2"]
185	typeset add_mode="$3"
186
187	case "${add_mode}" in
188		"leaf_name")
189			tree_leafnode="${data_node.name}"
190			return 0
191			;;
192		"leaf_compound")
193			tree_leafnode=(
194				typeset name="${data_node.name}"
195				typeset -a filenames=( "${data_node.filenames[@]}" )
196				typeset -a comments=( "${data_node.comments[@]}" )
197				typeset -a xlfd=( "${data_node.xlfd[@]}" )
198			)
199			return 0
200			;;
201		*)
202			print -u2 -f "ERROR: Unknown mode %s in add_tree_leaf\n" "${add_mode}"
203			return 1
204			;;
205	esac
206
207	# not reached
208	return 1
209}
210
211# "mysrcdata_local" and "mysrcdata_global" must be identical
212typeset mysrcdata_global=(
213	typeset -A hashnodes=(
214		[abcd]=(
215			name='abcd'
216			typeset -a xlfd=(
217				'-urw-itc zapfchancery-medium-i-normal--0-0-0-0-p-0-iso8859-1'
218				'-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-adobe-fontspecific'
219				'-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-sun-fontspecific'
220			)
221			typeset -a comments=(
222				'comment 1'
223				'comment 2'
224				'comment 3'
225			)
226			typeset -a filenames=(
227				'/home/foo/abcd_1'
228				'/home/foo/abcd_2'
229				'/home/foo/abcd_3'
230			)
231		)
232	)
233)
234
235mytree_global1=()
236mytree_global2=()
237
238function main
239{
240	# "mysrcdata_local" and "mysrcdata_global" must be identical
241	typeset mysrcdata_local=(
242		typeset -A hashnodes=(
243			[abcd]=(
244				name='abcd'
245				typeset -a xlfd=(
246					'-urw-itc zapfchancery-medium-i-normal--0-0-0-0-p-0-iso8859-1'
247					'-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-adobe-fontspecific'
248					'-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-sun-fontspecific'
249				)
250				typeset -a comments=(
251					'comment 1'
252					'comment 2'
253					'comment 3'
254				)
255				typeset -a filenames=(
256					'/home/foo/abcd_1'
257					'/home/foo/abcd_2'
258					'/home/foo/abcd_3'
259				)
260			)
261		)
262	)
263
264	#### Build tree using global tree variables
265	build_tree1 mytree_global1 mysrcdata_global leaf_compound || \
266		err_exit 'build_tree1 mytree_global1 mysrcdata_global leaf_compound returned an error'
267	(( $(print -r -- "${mytree_global1}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_global1' too small."
268
269	build_tree2 mytree_global2 mysrcdata_global leaf_compound || \
270		err_exit 'build_tree2 mytree_global2 mysrcdata_global leaf_compound returned an error'
271	(( $(print -r -- "${mytree_global2}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_global2' too small."
272
273
274	#### build tree using local tree variables
275	mytree_local1=()
276	mytree_local2=()
277
278	build_tree1 mytree_local1 mysrcdata_local leaf_compound || \
279		err_exit 'build_tree1 mytree_local1 mysrcdata_local leaf_compound returned an error'
280	(( $(print -r -- "${mytree_local1}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_local1' too small."
281
282	build_tree2 mytree_local2 mysrcdata_local leaf_compound || \
283		err_exit 'build_tree2 mytree_local2 mysrcdata_local leaf_compound returned an error'
284	(( $(print -r -- "${mytree_local2}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_local2' too small."
285
286
287	#### Compare treess
288	if [[ "${mytree_global1}" != "${mytree_local1}" ]] ; then
289		err_exit "Compound trees 'mytree_global1' and 'mytree_local1' not identical"
290		diff -u <( printf "%s\n" "${mytree_global1}" ) <( printf "%s\n" "${mytree_local1}" )
291	fi
292
293	if [[ "${mytree_global1}" != "${mytree_global2}" ]] ; then
294		err_exit "Compound trees 'mytree_global1' and 'mytree_global2' not identical"
295		diff -u <( printf "%s\n" "${mytree_global1}" ) <( printf "%s\n" "${mytree_global2}" )
296	fi
297
298	if [[ "${mytree_local1}" != "${mytree_local2}" ]] ; then
299		err_exit "Compound trees 'mytree_local1' and 'mytree_local2' not identical"
300		diff -u <( printf "%s\n" "${mytree_local1}" ) <( printf "%s\n" "${mytree_local2}" )
301	fi
302
303
304	#### test "unset" in a subshell
305	(  [[ -v 'mytree_global1.l1[urw].l2[itc zapfdingbats]' ]] ; res=$? ; unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]' ; exit $res ) || \
306		err_exit "Try 1: Variable 'mytree_global1.l1[urw].l2[itc zapfdingbats]' not found."
307	(  [[ -v 'mytree_global1.l1[urw].l2[itc zapfdingbats]' ]] ; res=$? ; unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]' ; exit $res ) || \
308		err_exit "Try 2: Variable 'mytree_global1.l1[urw].l2[itc zapfdingbats]' not found."
309
310	# remove parent node (array element) and then check whether the child is gone, too:
311	(
312		set -o errexit
313		unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]'
314		! [[ -v 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' ]]
315	) || err_exit "Global: Parent node removed (array element), child still exists"
316	(
317		set -o errexit
318		unset 'mytree_local1.l1[urw].l2[itc zapfdingbats]'
319		! [[ -v 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' ]]
320	) || err_exit "Local: Parent node removed (array element), child still exists"
321
322	# remove parent node  (array variable) and then check whether the child is gone, too:
323	(
324		set -o errexit
325		unset 'mytree_local1.l1[urw].l2'
326		! [[ -v 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' ]]
327	) || err_exit "Global: Parent node removed (array variable), child still exists"
328	(
329		set -o errexit
330		unset 'mytree_local1.l1[urw].l2'
331		! [[ -v 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' ]]
332	) || err_exit "Local: Parent node removed (array variable), child still exists"
333
334
335	#### test "unset" and compare trees
336	[[ -v 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' ]] ; res=$?
337	unset 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]'
338	(( res == 0 ))  ||
339		err_exit "Variable 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' not found."
340
341	[[ "${mytree_global1}" != "${mytree_local1}" ]] || err_exit "mytree_global1 and mytree_local1 should differ"
342
343	[[ -v 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' ]] ; res=$?
344	unset 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]'
345	(( res == 0 ))  ||
346		err_exit "Variable 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' not found."
347
348	# Compare trees (after "unset")
349	if [[ "${mytree_global1}" != "${mytree_local1}" ]] ; then
350		err_exit "Compound trees 'mytree_local1' and 'mytree_global1' not identical after unset"
351		diff -u <( printf "%s\n" "${mytree_global1}" ) <( printf "%s\n" "${mytree_local1}" )
352	fi
353}
354
355main
356
357# tests done
358exit $((Errors))
359