blob: c4466931a28949645319fff6253abaafb6bde81f [file] [log] [blame]
#!/usr/bin/wish
# This file provides many valuable data processing procedures like
# lappend_unique, get_var, etc.
proc lappend_unique { args } {
# Add the each entry to a list if and only if they do not already exist in
# the list.
# Description of argument(s):
# args The first argument should be the list
# name. All other arguments are items to be
# added to the list.
set list_name [lindex $args 0]
# Remove first entry from args list.
set args [lreplace $args 0 0]
upvar 1 $list_name list
if { ! [info exists list] } { set list {} }
foreach arg $args {
if { [lsearch -exact $list "${arg}"] != -1 } { continue }
lappend list $arg
}
}
proc lsubtract {main_list_name removal_list} {
upvar $main_list_name ref_main_list
# Remove any entry from the main list that is contained in removal list.
# Description of argument(s):
# main_list_name The name of your master list.
# removal_list The list of items to be removed from
# master list.
# For each element in the removal list, find the corresponding entry in the
# master list and remove it.
for {set removal_ix 0} {$removal_ix < [llength $removal_list ]}\
{incr removal_ix} {
set value [lindex $removal_list $removal_ix]
set master_ix [lsearch $ref_main_list $value]
set ref_main_list [lreplace $ref_main_list $master_ix $master_ix]
}
}
proc list_map { list expression } {
# Create and return a new list where each element of the new list is a
# result of running the given expression on the corresponding entry from the
# original list.
# Description of argument(s):
# list A list to be operated on.
# expression A command expression to be run on each
# element in the list (e.g. '[string range
# $x 1 end]').
foreach x $list {
set cmd_buf "lappend new_list ${expression}"
eval $cmd_buf
}
return $new_list
}
proc list_filter { list expression } {
# Create and return a new list consisting of all elements of the original
# list that do NOT pass the expression.
# Description of argument(s):
# list A list to be operated on.
# expression A command expression to be run on each
# element in the list (e.g. 'regexp
# -expanded {^[[:blank:]]*\#|^[[:blank:]]*$}
# $x', 'string equal $x ""', etc.).
set new_list {}
foreach x $list {
set cmd_buf "set result \[${expression}\]"
eval $cmd_buf
if { ! $result } { lappend new_list $x }
}
return $new_list
}
proc list_filter_comments { list } {
# Filter comments from list and return new_list as a result.
# Description of argument(s):
# list A list to be operated on.
set comment_regexp {^[[:blank:]]*\#|^[[:blank:]]*$}
set new_list [list_filter $list "regexp -expanded {$comment_regexp} \$x"]
return $new_list
}
proc get_var { var_var { default ""} } {
upvar 1 $var_var var_ref
# Return the value of the variable expression or the value of default if
# the variable is not defined.
# Example use:
# set PATH [get_var ::env(PATH) "/usr/bin"]
# Description of argument(s):
# var_var The name of a variable (e.g.
# "::env(NANOSECOND)" or "var1").
# default The default value to return if the
# variable named in var_var does not exist.
expr { [info exists var_ref] ? [return $var_ref] : [return $default] }
}
proc set_var_default { var_name { default ""} } {
upvar 1 $var_name var_ref
# If the variable named in var_name is either blank or non-existent, set
# its value to the default.
# Example use:
# set_var_default indent 0
# Description of argument(s):
# var_name The name of a variable.
# default The default value to assign to the
# variable if the variable named in var_name
# is blank or non-existent.
if { ! ([info exists var_ref] && $var_ref != "") } {
set var_ref $default
}
}
proc split_path {path dir_path base_name} {
upvar $dir_path ref_dir_path
upvar $base_name ref_base_name
# Split a path into it's dir_path and base_name. The dir_path variable
# will include a trailing slash.
# Description of argument(s):
# path The directory or file path.
# dir_path The variable to contain the resulting
# directory path which will include a
# trailing slash.
# base_name The variable to contain the resulting base
# directory or file name.
set ref_dir_path "[file dirname ${path}]/"
set ref_base_name "[file tail $path]"
}
proc read_properties_file {parm_file_path} {
# Read properties files and return key/value pairs as a list.
# Description of argument(s):
# parm_file_path The path to the properties file.
# The properties file must have the following format:
# var_name=var_value
# Comment lines (those beginning with a "#") and blank lines are allowed
# and will be ignored. Leading and trailing single or double quotes will be
# stripped from the value. E.g.
# var1="This one"
# Quotes are stripped so the resulting value for var1 is:
# This one
# Suggestion: The caller can then process the result as an array or a
# dictionary.
# Example usage:
# array set properties [read_properties_file $file_path]
# print_var properties
# With the following result...
# properties:
# properties(command): string
# Or...
# set properties [read_properties_file $file_path]
# print_dict properties
# With the following result...
# properties:
# properties[command]: string
# Initialize properties array.
set properties [list]
# Read the entire file into a list, filtering comments out.
set file_descriptor [open $parm_file_path r]
set file_data [list_filter_comments [split [read $file_descriptor] "\n"]]
close $file_descriptor
foreach line $file_data {
# Split <var_name>=<var_value> into component parts.
set pair [split $line =]
lappend properties [lindex ${pair} 0]
lappend properties [string trim [lindex ${pair} 1] {"}]
}
return $properties
}
proc convert_array_key {key {convert_commands} {prefix ""} } {
# Convert the key according to the caller's convert_commands and return the
# result.
# This is designed as a helper procedure to be called by convert_array_keys.
# See convert_array_keys for description of arguments.
set new_key $key
foreach command $convert_commands {
if { $command == "prefix" } {
regsub -all "^$prefix" $new_key {} new_key
set new_key "$prefix$new_key"
} elseif { $command == "rm_prefix" } {
regsub -all "^$prefix" $new_key {} new_key
set new_key "$new_key"
}
if { $command == "upper" } {
set new_key [string toupper $new_key]
} elseif { $command == "lower" } {
set new_key [string tolower $new_key]
}
}
return $new_key
}
proc convert_array_keys {source_arr target_arr {convert_commands}\
{prefix ""} } {
upvar $source_arr source_arr_ref
upvar $target_arr target_arr_ref
# Convert the keys of source_arr according to the caller's convert_commands
# and put the resulting array in target_arr. If this procedure fails for any
# reason, it will return non-zero.
# Note that despite the name of this procedure, it will also work on a
# dictionary. In other words, if source_arr is NOT an array, it will be
# processed as a dictionary and target_arr will be created as a dictionary
# as well.
# Description of argument(s):
# source_arr The source array that is to be converted.
# target_arr The target array that results from the
# conversion.
# convert_commands A list of custom commands that indicate
# the type of conversion(s) the caller
# wishes to see. Currently the accepted
# values are as follows:
# upper Convert key value to uppercase.
# lower Convert key value to lowercase.
# prefix Prepend prefix to the key, provided that
# it does not already exist. If upper or
# lower is included in convert_commands
# list, the prefix will be converted to the
# specified case as well.
# rm_prefix Remove a prefix that is prepended,
# provided that it exists.
# prefix The prefix to be used for "prefix" and
# "rm_prefix" commands (see convert_commands
# text above).
# Validate arguments.
if { [lsearch $convert_commands lower] != -1 } {
if { [lsearch $convert_commands upper] != -1 } {
return -code error "Cannot convert to both upper and lower cases."
}
}
if { [lsearch $convert_commands rm_prefix] != -1} {
if { [lsearch $convert_commands prefix] != -1} {
return -code error "Cannot add and remove a prefix."
}
}
if { [lsearch $convert_commands prefix] != -1 ||\
[lsearch $convert_commands rm_prefix] != -1 } {
if { [lsearch $convert_commands upper] != -1 } {
set prefix [string toupper $prefix]
} elseif { [lsearch $convert_commands lower] != -1 } {
set prefix [string tolower $prefix]
}
}
if { [array exists source_arr_ref] } {
# Initialize targ array.
array set target_arr_ref {}
# Walk the source array doing the conversion specified in convert_commands.
set search_token [array startsearch source_arr_ref]
while {[array anymore source_arr_ref $search_token]} {
set key [array nextelement source_arr_ref $search_token]
set value $source_arr_ref($key)
set new_key [convert_array_key $key $convert_commands $prefix]
set cmd_buf "set target_arr_ref($new_key) $value"
eval $cmd_buf
}
array donesearch source_arr_ref $search_token
} else {
# Initialize targ dictionary.
set target_arr_ref [list]
# Walk the source dictionary doing the conversion specified in
# convert_commands.
foreach {key value} $source_arr_ref {
set new_key [convert_array_key $key $convert_commands $prefix]
set cmd_buf "dict append target_arr_ref $new_key \$value"
eval $cmd_buf
}
}
}
proc expand_shell_string {buffer} {
upvar $buffer ref_buffer
# Call upon the shell to expand the string in "buffer", i.e. the shell will
# make substitutions for environment variables and glob expressions.
# Description of argument(s):
# buffer The buffer to be expanded.
# This is done to keep echo from interpreting all of the double quotes away.
regsub -all {\"} $ref_buffer "\\\"" ref_buffer
# Bash will compress extra space delimiters if you don't quote the string.
# So, we quote the argument to echo.
if {[catch {set ref_buffer [exec bash -c "echo \"$ref_buffer\""]} result]} {
puts stderr $result
exit 1
}
}
proc add_trailing_string { buffer { add_string "/" } } {
upvar $buffer ref_buffer
# Add the add string to the end of the buffer if and only if it doesn't
# already end with the add_string.
# Description of argument(s):
# buffer The buffer to be modified.
# add_string The string to conditionally append to the
# buffer.
regsub -all "${add_string}$" $ref_buffer {} ref_buffer
set ref_buffer "${ref_buffer}${add_string}"
}