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