1########################################################################
2#                                                                      #
3#               This software is part of the ast package               #
4#          Copyright (c) 1982-2011 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# variable tree test #002
22# Propose of this test is whether ksh93 handles global variable trees
23# and function-local variable trees the same way, including "nameref"
24# and "unset" handling.
25#
26
27function err_exit
28{
29	print -u2 -n "\t"
30	print -u2 -r ${Command}[$1]: "${@:2}"
31	(( Errors+=1 ))
32}
33
34alias err_exit='err_exit $LINENO'
35
36# "built_tree1" and "built_tree2" are identical except the way how they test
37# whether a variable exists:
38# - "built_tree1" uses "${varname}" != "", e.g. looking whether the variable
39#    as non-zero length content
40# - "built_tree2" uses "! (unset varname)", e.g. "unset" in a subshell
41function build_tree1
42{
43#set -o errexit -o xtrace
44	typeset index
45	typeset s
46	typeset i
47	typeset dummy
48	typeset a b c d e f
49
50	nameref dest_tree="$1" # destination tree
51	nameref srcdata="$2"   # source data
52	typeset tree_mode="$3" # mode to define the type of leads
53
54	typeset -A dest_tree.l1
55
56	for index in "${!srcdata.hashnodes[@]}" ; do
57		nameref node=srcdata.hashnodes["${index}"]
58
59		for i in "${node.xlfd[@]}" ; do
60			IFS='-' read dummy a b c d e f <<<"$i"
61
62			if [[ "$a" == "" ]] ; then
63				a="$dummy"
64			fi
65
66			[[ "$a" == "" ]] && a='-'
67			[[ "$b" == "" ]] && b='-'
68			[[ "$c" == "" ]] && c='-'
69
70			if [[ "${dest_tree.l1["$a"]}" == "" ]] ; then
71			#if ! (unset dest_tree.l1["$a"]) ; then
72				typeset -A dest_tree.l1["$a"].l2
73			fi
74
75			if [[ "${dest_tree.l1["$a"].l2["$b"]}" == "" ]] ; then
76			#if ! (unset dest_tree.l1["$a"].l2["$b"]) ; then
77				typeset -A dest_tree.l1["$a"].l2["$b"].l3
78			fi
79
80			if [[ "${!dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[*]}" == "" ]] ; then
81				typeset -A dest_tree.l1["$a"].l2["$b"].l3["$c"].entries
82			fi
83
84			typeset new_index
85			if [[ "${tree_mode}" == "leaf_name" ]] ; then
86				new_index=$(( ${#dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[@]}+1 ))
87			else
88				new_index="${node.name}"
89
90				# skip if the leaf node already exists
91				if [[ "${dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}]}" != "" ]] ; then
92					continue
93				fi
94			fi
95
96			add_tree_leaf dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}] "${index}" "${tree_mode}"
97		done
98	done
99
100	return 0
101}
102
103# "built_tree1" and "built_tree2" are identical except the way how they test
104# whether a variable exists:
105# - "built_tree1" uses "${varname}" != "", e.g. looking whether the variable
106#    as non-zero length content
107# - "built_tree2" uses "! (unset varname)", e.g. "unset" in a subshell
108function build_tree2
109{
110#set -o errexit -o xtrace
111	typeset index
112	typeset s
113	typeset i
114	typeset dummy
115	typeset a b c d e f
116
117	nameref dest_tree="$1" # destination tree
118	nameref srcdata="$2"   # source data
119	typeset tree_mode="$3" # mode to define the type of leads
120
121	typeset -A dest_tree.l1
122
123	for index in "${!srcdata.hashnodes[@]}" ; do
124		nameref node=srcdata.hashnodes["${index}"]
125
126		for i in "${node.xlfd[@]}" ; do
127			IFS='-' read dummy a b c d e f <<<"$i"
128
129			if [[ "$a" == "" ]] ; then
130				a="$dummy"
131			fi
132
133			[[ "$a" == "" ]] && a='-'
134			[[ "$b" == "" ]] && b='-'
135			[[ "$c" == "" ]] && c='-'
136
137			#if [[ "${dest_tree.l1["$a"]}" == "" ]] ; then
138			if ! (unset dest_tree.l1["$a"]) ; then
139				typeset -A dest_tree.l1["$a"].l2
140			fi
141
142			#if [[ "${dest_tree.l1["$a"].l2["$b"]}" == "" ]] ; then
143			if ! (unset dest_tree.l1["$a"].l2["$b"]) ; then
144				typeset -A dest_tree.l1["$a"].l2["$b"].l3
145			fi
146
147			if [[ "${!dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[*]}" == "" ]] ; then
148				typeset -A dest_tree.l1["$a"].l2["$b"].l3["$c"].entries
149			fi
150
151			typeset new_index
152			if [[ "${tree_mode}" == "leaf_name" ]] ; then
153				new_index=$(( ${#dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[@]}+1 ))
154			else
155				new_index="${node.name}"
156
157				# skip if the leaf node already exists
158				if [[ "${dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}]}" != "" ]] ; then
159					continue
160				fi
161			fi
162
163			add_tree_leaf dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}] "${index}" "${tree_mode}"
164		done
165	done
166
167	return 0
168}
169
170
171function add_tree_leaf
172{
173	nameref tree_leafnode="$1"
174	nameref data_node=srcdata.hashnodes["$2"]
175	typeset add_mode="$3"
176
177	case "${add_mode}" in
178		"leaf_name")
179			tree_leafnode="${data_node.name}"
180			return 0
181			;;
182		"leaf_compound")
183			tree_leafnode=(
184				typeset name="${data_node.name}"
185				typeset -a filenames=( "${data_node.filenames[@]}" )
186				typeset -a comments=( "${data_node.comments[@]}" )
187				typeset -a xlfd=( "${data_node.xlfd[@]}" )
188			)
189			return 0
190			;;
191		*)
192			print -u2 -f "ERROR: Unknown mode %s in add_tree_leaf\n" "${add_mode}"
193			return 1
194			;;
195	esac
196
197	# not reached
198	return 1
199}
200
201# "mysrcdata_local" and "mysrcdata_global" must be identical
202typeset mysrcdata_global=(
203	typeset -A hashnodes=(
204		[abcd]=(
205			name='abcd'
206			typeset -a xlfd=(
207				'-urw-itc zapfchancery-medium-i-normal--0-0-0-0-p-0-iso8859-1'
208				'-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-adobe-fontspecific'
209				'-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-sun-fontspecific'
210			)
211			typeset -a comments=(
212				'comment 1'
213				'comment 2'
214				'comment 3'
215			)
216			typeset -a filenames=(
217				'/home/foo/abcd_1'
218				'/home/foo/abcd_2'
219				'/home/foo/abcd_3'
220			)
221		)
222	)
223)
224
225mytree_global1=()
226mytree_global2=()
227
228function main
229{
230	# "mysrcdata_local" and "mysrcdata_global" must be identical
231	typeset mysrcdata_local=(
232		typeset -A hashnodes=(
233			[abcd]=(
234				name='abcd'
235				typeset -a xlfd=(
236					'-urw-itc zapfchancery-medium-i-normal--0-0-0-0-p-0-iso8859-1'
237					'-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-adobe-fontspecific'
238					'-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-sun-fontspecific'
239				)
240				typeset -a comments=(
241					'comment 1'
242					'comment 2'
243					'comment 3'
244				)
245				typeset -a filenames=(
246					'/home/foo/abcd_1'
247					'/home/foo/abcd_2'
248					'/home/foo/abcd_3'
249				)
250			)
251		)
252	)
253
254	#### Build tree using global tree variables
255	build_tree1 mytree_global1 mysrcdata_global leaf_compound || \
256		err_exit 'build_tree1 mytree_global1 mysrcdata_global leaf_compound returned an error'
257	(( $(print -r -- "${mytree_global1}" | wc -l) > 10 )) || err_exit "compound tree 'mytree_global1' too small"
258
259	build_tree2 mytree_global2 mysrcdata_global leaf_compound || \
260		err_exit 'build_tree2 mytree_global2 mysrcdata_global leaf_compound returned an error'
261	(( $(print -r -- "${mytree_global2}" | wc -l) > 10 )) || err_exit "compound tree 'mytree_global2' too small"
262
263
264	#### build tree using local tree variables
265	mytree_local1=()
266	mytree_local2=()
267
268	build_tree1 mytree_local1 mysrcdata_local leaf_compound || \
269		err_exit 'build_tree1 mytree_local1 mysrcdata_local leaf_compound returned an error'
270	(( $(print -r -- "${mytree_local1}" | wc -l) > 10 )) || err_exit "compound tree 'mytree_local1' too small"
271
272	build_tree2 mytree_local2 mysrcdata_local leaf_compound || \
273		err_exit 'build_tree2 mytree_local2 mysrcdata_local leaf_compound returned an error'
274	(( $(print -r -- "${mytree_local2}" | wc -l) > 10 )) || err_exit "compound tree 'mytree_local2' too small"
275
276
277	#### Compare treess
278	if [[ "${mytree_global1}" != "${mytree_local1}" ]] ; then
279		err_exit "compound trees 'mytree_global1' and 'mytree_local1' not identical"
280	fi
281
282	if [[ "${mytree_global1}" != "${mytree_global2}" ]] ; then
283		err_exit "compound trees 'mytree_global1' and 'mytree_global2' not identical"
284	fi
285
286	if [[ "${mytree_local1}" != "${mytree_local2}" ]] ; then
287		err_exit "compound trees 'mytree_local1' and 'mytree_local2' not identical"
288	fi
289
290
291	#### test "unset" in a subshell
292	(  unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]'  ) || \
293		err_exit "try 1: variable 'mytree_global1.l1[urw].l2[itc zapfdingbats]' not found"
294	(  unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]'  ) || \
295		err_exit "try 2: variable 'mytree_global1.l1[urw].l2[itc zapfdingbats]' not found"
296
297	# remove parent node (array element) and then check whether the child is gone, too:
298	(
299		unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]'
300		[[ -v 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]'} ]]
301	) && err_exit "global: parent node removed (array element), child still exists"
302	(
303		unset 'mytree_local1.l1[urw].l2[itc zapfdingbats]'
304		[[ -v 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' ]]
305	) && err_exit "local: parent node removed (array element), child still exists"
306
307	# remove parent node  (array variable) and then check whether the child is gone, too:
308	(
309		unset 'mytree_local1.l1[urw].l2'
310		[[ -v 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' ]]
311	) && err_exit "global: parent node removed (array variable), child still exists"
312	(
313		unset 'mytree_local1.l1[urw].l2'
314		[[ -v 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' ]]
315	) && err_exit "local: parent node removed (array variable), child still exists"
316
317
318	#### test "unset" and compare trees
319	unset 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' ||
320		err_exit "variable 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' not found"
321
322	[[ "${mytree_global1}" != "${mytree_local1}" ]] || err_exit "mytree_global1 and mytree_local1 should differ"
323
324	unset 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' ||
325		err_exit "variable 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' not found"
326
327	# Compare trees (after "unset")
328	if [[ "${mytree_global1}" != "${mytree_local1}" ]] ; then
329		err_exit "compound trees 'mytree_local1' and 'mytree_global1' not identical after unset"
330	fi
331}
332
333main
334
335exit $((Errors<125?Errors:125))
336