blob: c4466931a28949645319fff6253abaafb6bde81f [file] [log] [blame]
Michael Walsh9d41d462018-02-15 16:34:32 -06001#!/usr/bin/wish
2
Michael Walsh58f9a512018-07-18 10:33:15 -05003# This file provides many valuable data processing procedures like
Michael Walsh9d41d462018-02-15 16:34:32 -06004# 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
Gunnar Mills948e2e22018-03-23 12:54:27 -0500185 # Description of argument(s):
Michael Walsh9d41d462018-02-15 16:34:32 -0600186 # 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
Michael Walsh58f9a512018-07-18 10:33:15 -0500241proc 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
Michael Walsh9d41d462018-02-15 16:34:32 -0600271proc 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
Michael Walsh58f9a512018-07-18 10:33:15 -0500277 # 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.
Michael Walsh9d41d462018-02-15 16:34:32 -0600284
Gunnar Mills948e2e22018-03-23 12:54:27 -0500285 # Description of argument(s):
Michael Walsh9d41d462018-02-15 16:34:32 -0600286 # 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:
Michael Walsh58f9a512018-07-18 10:33:15 -0500293 # 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.
Michael Walsh9d41d462018-02-15 16:34:32 -0600302 # 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
Michael Walsh58f9a512018-07-18 10:33:15 -0500328 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)
Michael Walsh9d41d462018-02-15 16:34:32 -0600336
Michael Walsh58f9a512018-07-18 10:33:15 -0500337 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
Michael Walsh9d41d462018-02-15 16:34:32 -0600340 }
Michael Walsh58f9a512018-07-18 10:33:15 -0500341 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 }
Michael Walsh9d41d462018-02-15 16:34:32 -0600353 }
Michael Walsh9d41d462018-02-15 16:34:32 -0600354
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
Gunnar Mills948e2e22018-03-23 12:54:27 -0500364 # Description of argument(s):
Michael Walsh9d41d462018-02-15 16:34:32 -0600365 # 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
Gunnar Mills948e2e22018-03-23 12:54:27 -0500386 # Description of argument(s):
Michael Walsh9d41d462018-02-15 16:34:32 -0600387 # 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