1#!/usr/bin/wish
2
3# This file provides shell command procedures cmd_fnc and t_cmd_fnc.
4
5my_source [list print.tcl]
6
7
8proc cmd_fnc { cmd_buf { quiet {} } { test_mode {} } { print_output {} }\
9  { show_err {} } { ignore_err {} } { acceptable_shell_rcs {} } } {
10
11  # Run the given command in a shell and return the shell return code and the
12  # output as a 2 element list.
13
14  # Example usage:
15  # set result [cmd_fnc "date"].
16
17  # Example output:
18
19  # #(CST) 2018/01/17 16:23:28.951643 -    0.001086 - Issuing: date
20  # Mon Feb 19 10:12:10 CST 2018
21  # result:
22  #   result[0]:                                      0x00000000
23  #   result[1]:                                      Mon Feb 19 10:12:10 CST
24  # 2018
25
26  # Note: Because of the way this procedure processes parms, the user can
27  # specify blank values as a way of skipping parms.  In the following
28  # example, the caller is indicating that they wish to have quiet and
29  # test_mode take their normal defaults but have print_output be 0.:
30  # cmd_fnc "date" "" "" 0
31
32  # Description of argument(s):
33  # cmd_buf                         The command string to be run in a shell.
34  # quiet                           Indicates whether this procedure should
35  #                                 run the print_issuing() procedure which
36  #                                 prints "Issuing: <cmd string>" to stdout.
37  #                                 The default value is 0.
38  # test_mode                       If test_mode is set, this procedure will
39  #                                 not actually run the command.  If
40  #                                 print_output is set, it will print
41  #                                 "(test_mode) Issuing: <cmd string>" to
42  #                                 stdout.  The default value is 0.
43  # print_output                    If this is set, this procedure will print
44  #                                 the stdout/stderr generated by the shell
45  #                                 command.  The default value is 1.
46  # show_err                        If show_err is set, this procedure will
47  #                                 print a standardized error report if the
48  #                                 shell command returns non-zero.  The
49  #                                 default value is 1.
50  # ignore_err                      If ignore_err is set, this procedure will
51  #                                 not fail if the shell command fails.
52  #                                 However, if ignore_err is not set, this
53  #                                 procedure will exit 1 if the shell command
54  #                                 fails.  The default value is 1.
55  # acceptable_shell_rcs            A list of acceptable shell rcs.  If the
56  #                                 shell return code is found in this list,
57  #                                 the shell command is considered
58  #                                 successful.  The default value is {0}.
59
60  # Set defaults.
61  set_var_default quiet [get_stack_var quiet 0 2]
62  set_var_default test_mode 0
63  set_var_default print_output 1
64  set_var_default show_err 1
65  set_var_default ignore_err 1
66  set_var_default acceptable_shell_rcs 0
67
68  qpissuing $cmd_buf $test_mode
69
70  if { $test_mode } { return [list 0 ""] }
71
72  set shell_rc 0
73
74  if { [ catch {set out_buf [eval exec bash -c {$cmd_buf}]} result ] } {
75    set out_buf $result
76    set shell_rc [lindex $::errorCode 2]
77  }
78
79  if { $print_output } { puts "${out_buf}" }
80
81  # Check whether return code is acceptable.
82  if { [lsearch -exact $acceptable_shell_rcs ${shell_rc}] == -1 } {
83    # The command failed.
84    append error_message "The prior shell command failed.\n"
85    append error_message [sprint_var shell_rc "" "" 1]
86    if { $acceptable_shell_rcs != 0 } {
87      # acceptable_shell_rcs contains more than just a single element equal
88      # to 0.
89      append error_message "\n"
90      append error_message [sprint_list acceptable_shell_rcs "" "" 1]
91    }
92    if { ! $print_output } {
93      append error_message "out_buf:\n${out_buf}"
94    }
95    if { $show_err } {
96      print_error_report $error_message
97    }
98
99    if { ! $ignore_err } {
100      exit 1
101    }
102
103  }
104
105  return [list $shell_rc $out_buf]
106
107}
108
109
110proc t_cmd_fnc { args } {
111
112  # Call cmd_fnc with test_mode equal to the test_mode setting found by
113  # searching up the call stack.  See cmd_fnc (above) for details for all
114  # other arguments.
115
116  # We wish to obtain a value for test_mode by searching up the call stack.
117  # This value will govern whether the command specified actually gets
118  # executed.
119  set_var_default test_mode [get_stack_var test_mode 0 2]
120
121  # Since we wish to manipulate the value of test_mode, which is the third
122  # positional parm, we must make sure we have at least 3 parms.  We will now
123  # append blank values to the args list as needed to ensure that we have the
124  # minimum 3 parms.
125  set min_args 3
126  for {set ix [llength $args]} {$ix < $min_args} {incr ix} {
127    lappend args {}
128  }
129
130  # Now replace the caller's test_mode value with the value obtained from the
131  # call stack search.  It does not matter what value is specified by the
132  # caller for test_mode.  It will be replaced.  The whole point of calling
133  # t_cmd_fnc is to allow it to set the test_mode.
134  set args [lreplace $args 2 2 $test_mode]
135
136  return [cmd_fnc {*}$args]
137
138}
139