1#!/usr/bin/wish 2 3# This file provides many valuable data processing procedures like 4# lappend_unique, get_var, etc. 5 6 7proc lappend_unique { args } { 8 9 # Add the each entry to a list if and only if they do not already exist in 10 # the list. 11 12 # Description of argument(s): 13 # args The first argument should be the list 14 # name. All other arguments are items to be 15 # added to the list. 16 17 set list_name [lindex $args 0] 18 # Remove first entry from args list. 19 set args [lreplace $args 0 0] 20 21 upvar 1 $list_name list 22 23 if { ! [info exists list] } { set list {} } 24 25 foreach arg $args { 26 if { [lsearch -exact $list "${arg}"] != -1 } { continue } 27 lappend list $arg 28 } 29 30} 31 32 33proc lsubtract {main_list_name removal_list} { 34 upvar $main_list_name ref_main_list 35 36 # Remove any entry from the main list that is contained in removal list. 37 38 # Description of argument(s): 39 # main_list_name The name of your master list. 40 # removal_list The list of items to be removed from 41 # master list. 42 43 # For each element in the removal list, find the corresponding entry in the 44 # master list and remove it. 45 for {set removal_ix 0} {$removal_ix < [llength $removal_list ]}\ 46 {incr removal_ix} { 47 set value [lindex $removal_list $removal_ix] 48 set master_ix [lsearch $ref_main_list $value] 49 set ref_main_list [lreplace $ref_main_list $master_ix $master_ix] 50 } 51 52} 53 54 55proc list_map { list expression } { 56 57 # Create and return a new list where each element of the new list is a 58 # result of running the given expression on the corresponding entry from the 59 # original list. 60 61 # Description of argument(s): 62 # list A list to be operated on. 63 # expression A command expression to be run on each 64 # element in the list (e.g. '[string range 65 # $x 1 end]'). 66 67 foreach x $list { 68 set cmd_buf "lappend new_list ${expression}" 69 eval $cmd_buf 70 } 71 72 return $new_list 73 74} 75 76 77proc list_filter { list expression } { 78 79 # Create and return a new list consisting of all elements of the original 80 # list that do NOT pass the expression. 81 82 # Description of argument(s): 83 # list A list to be operated on. 84 # expression A command expression to be run on each 85 # element in the list (e.g. 'regexp 86 # -expanded {^[[:blank:]]*\#|^[[:blank:]]*$} 87 # $x', 'string equal $x ""', etc.). 88 89 set new_list {} 90 91 foreach x $list { 92 set cmd_buf "set result \[${expression}\]" 93 eval $cmd_buf 94 if { ! $result } { lappend new_list $x } 95 } 96 97 return $new_list 98 99} 100 101 102proc list_filter_comments { list } { 103 104 # Filter comments from list and return new_list as a result. 105 106 # Description of argument(s): 107 # list A list to be operated on. 108 109 set comment_regexp {^[[:blank:]]*\#|^[[:blank:]]*$} 110 111 set new_list [list_filter $list "regexp -expanded {$comment_regexp} \$x"] 112 113 return $new_list 114 115} 116 117 118proc get_var { var_var { default ""} } { 119 upvar 1 $var_var var_ref 120 121 # Return the value of the variable expression or the value of default if 122 # the variable is not defined. 123 124 # Example use: 125 # set PATH [get_var ::env(PATH) "/usr/bin"] 126 127 # Description of argument(s): 128 # var_var The name of a variable (e.g. 129 # "::env(NANOSECOND)" or "var1"). 130 # default The default value to return if the 131 # variable named in var_var does not exist. 132 133 expr { [info exists var_ref] ? [return $var_ref] : [return $default] } 134 135} 136 137 138proc set_var_default { var_name { default ""} } { 139 upvar 1 $var_name var_ref 140 141 # If the variable named in var_name is either blank or non-existent, set 142 # its value to the default. 143 144 # Example use: 145 # set_var_default indent 0 146 147 # Description of argument(s): 148 # var_name The name of a variable. 149 # default The default value to assign to the 150 # variable if the variable named in var_name 151 # is blank or non-existent. 152 153 if { ! ([info exists var_ref] && $var_ref != "") } { 154 set var_ref $default 155 } 156 157} 158 159 160proc split_path {path dir_path base_name} { 161 upvar $dir_path ref_dir_path 162 upvar $base_name ref_base_name 163 164 # Split a path into it's dir_path and base_name. The dir_path variable 165 # will include a trailing slash. 166 167 # Description of argument(s): 168 # path The directory or file path. 169 # dir_path The variable to contain the resulting 170 # directory path which will include a 171 # trailing slash. 172 # base_name The variable to contain the resulting base 173 # directory or file name. 174 175 set ref_dir_path "[file dirname ${path}]/" 176 set ref_base_name "[file tail $path]" 177 178} 179 180 181proc read_properties_file {parm_file_path} { 182 183 # Read properties files and return key/value pairs as a list. 184 185 # Description of argument(s): 186 # parm_file_path The path to the properties file. 187 188 # The properties file must have the following format: 189 # var_name=var_value 190 # Comment lines (those beginning with a "#") and blank lines are allowed 191 # and will be ignored. Leading and trailing single or double quotes will be 192 # stripped from the value. E.g. 193 # var1="This one" 194 # Quotes are stripped so the resulting value for var1 is: 195 # This one 196 197 # Suggestion: The caller can then process the result as an array or a 198 # dictionary. 199 200 # Example usage: 201 202 # array set properties [read_properties_file $file_path] 203 # print_var properties 204 205 # With the following result... 206 207 # properties: 208 # properties(command): string 209 210 # Or... 211 212 # set properties [read_properties_file $file_path] 213 # print_dict properties 214 215 # With the following result... 216 217 # properties: 218 # properties[command]: string 219 220 # Initialize properties array. 221 222 set properties [list] 223 224 # Read the entire file into a list, filtering comments out. 225 set file_descriptor [open $parm_file_path r] 226 set file_data [list_filter_comments [split [read $file_descriptor] "\n"]] 227 close $file_descriptor 228 229 foreach line $file_data { 230 # Split <var_name>=<var_value> into component parts. 231 set pair [split $line =] 232 lappend properties [lindex ${pair} 0] 233 lappend properties [string trim [lindex ${pair} 1] {"}] 234 } 235 236 return $properties 237 238} 239 240 241proc convert_array_key {key {convert_commands} {prefix ""} } { 242 243 # Convert the key according to the caller's convert_commands and return the 244 # result. 245 246 # This is designed as a helper procedure to be called by convert_array_keys. 247 248 # See convert_array_keys for description of arguments. 249 250 set new_key $key 251 foreach command $convert_commands { 252 if { $command == "prefix" } { 253 regsub -all "^$prefix" $new_key {} new_key 254 set new_key "$prefix$new_key" 255 } elseif { $command == "rm_prefix" } { 256 regsub -all "^$prefix" $new_key {} new_key 257 set new_key "$new_key" 258 } 259 if { $command == "upper" } { 260 set new_key [string toupper $new_key] 261 } elseif { $command == "lower" } { 262 set new_key [string tolower $new_key] 263 } 264 } 265 266 return $new_key 267 268} 269 270 271proc convert_array_keys {source_arr target_arr {convert_commands}\ 272 {prefix ""} } { 273 upvar $source_arr source_arr_ref 274 upvar $target_arr target_arr_ref 275 276 # Convert the keys of source_arr according to the caller's convert_commands 277 # and put the resulting array in target_arr. If this procedure fails for any 278 # reason, it will return non-zero. 279 280 # Note that despite the name of this procedure, it will also work on a 281 # dictionary. In other words, if source_arr is NOT an array, it will be 282 # processed as a dictionary and target_arr will be created as a dictionary 283 # as well. 284 285 # Description of argument(s): 286 # source_arr The source array that is to be converted. 287 # target_arr The target array that results from the 288 # conversion. 289 # convert_commands A list of custom commands that indicate 290 # the type of conversion(s) the caller 291 # wishes to see. Currently the accepted 292 # values are as follows: 293 # upper Convert key value to uppercase. 294 # lower Convert key value to lowercase. 295 # prefix Prepend prefix to the key, provided that 296 # it does not already exist. If upper or 297 # lower is included in convert_commands 298 # list, the prefix will be converted to the 299 # specified case as well. 300 # rm_prefix Remove a prefix that is prepended, 301 # provided that it exists. 302 # prefix The prefix to be used for "prefix" and 303 # "rm_prefix" commands (see convert_commands 304 # text above). 305 306 # Validate arguments. 307 if { [lsearch $convert_commands lower] != -1 } { 308 if { [lsearch $convert_commands upper] != -1 } { 309 return -code error "Cannot convert to both upper and lower cases." 310 } 311 } 312 313 if { [lsearch $convert_commands rm_prefix] != -1} { 314 if { [lsearch $convert_commands prefix] != -1} { 315 return -code error "Cannot add and remove a prefix." 316 } 317 } 318 319 if { [lsearch $convert_commands prefix] != -1 ||\ 320 [lsearch $convert_commands rm_prefix] != -1 } { 321 if { [lsearch $convert_commands upper] != -1 } { 322 set prefix [string toupper $prefix] 323 } elseif { [lsearch $convert_commands lower] != -1 } { 324 set prefix [string tolower $prefix] 325 } 326 } 327 328 if { [array exists source_arr_ref] } { 329 # Initialize targ array. 330 array set target_arr_ref {} 331 # Walk the source array doing the conversion specified in convert_commands. 332 set search_token [array startsearch source_arr_ref] 333 while {[array anymore source_arr_ref $search_token]} { 334 set key [array nextelement source_arr_ref $search_token] 335 set value $source_arr_ref($key) 336 337 set new_key [convert_array_key $key $convert_commands $prefix] 338 set cmd_buf "set target_arr_ref($new_key) $value" 339 eval $cmd_buf 340 } 341 array donesearch source_arr_ref $search_token 342 343 } else { 344 # Initialize targ dictionary. 345 set target_arr_ref [list] 346 # Walk the source dictionary doing the conversion specified in 347 # convert_commands. 348 foreach {key value} $source_arr_ref { 349 set new_key [convert_array_key $key $convert_commands $prefix] 350 set cmd_buf "dict append target_arr_ref $new_key \$value" 351 eval $cmd_buf 352 } 353 } 354 355} 356 357 358proc expand_shell_string {buffer} { 359 upvar $buffer ref_buffer 360 361 # Call upon the shell to expand the string in "buffer", i.e. the shell will 362 # make substitutions for environment variables and glob expressions. 363 364 # Description of argument(s): 365 # buffer The buffer to be expanded. 366 367 # This is done to keep echo from interpreting all of the double quotes away. 368 regsub -all {\"} $ref_buffer "\\\"" ref_buffer 369 370 # Bash will compress extra space delimiters if you don't quote the string. 371 # So, we quote the argument to echo. 372 if {[catch {set ref_buffer [exec bash -c "echo \"$ref_buffer\""]} result]} { 373 puts stderr $result 374 exit 1 375 } 376 377} 378 379 380proc add_trailing_string { buffer { add_string "/" } } { 381 upvar $buffer ref_buffer 382 383 # Add the add string to the end of the buffer if and only if it doesn't 384 # already end with the add_string. 385 386 # Description of argument(s): 387 # buffer The buffer to be modified. 388 # add_string The string to conditionally append to the 389 # buffer. 390 391 regsub -all "${add_string}$" $ref_buffer {} ref_buffer 392 set ref_buffer "${ref_buffer}${add_string}" 393 394} 395 396 397