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