git-gui: override exec and open only on Windows

Since aae9560a355d (Work around Tcl's default `PATH` lookup, 2022-11-23), git-gui overrides exec and open on all platforms. But, this was done in response to Tcl adding elements to $PATH on Windows, while exec, open, and auto_execok honor $PATH as given on all other platforms. Let's do the override only on Windows, restoring others to using their native exec and open. These honor the sanitized $PATH as that is written out to env(PATH) in a previous commit. auto_execok is also safe on these platforms, so can be used for _which. Signed-off-by: Mark Levedahl <mlevedahl@gmail.com> Signed-off-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Taylor Blau <me@ttaylorr.com>

Mark Levedahl committed Apr 11, 2025 at 10:58 UTC a1ccd2512072cf52835050f4c97a4fba9f0ec8f9
1 file changed +65 -55
git-gui.sh
+65 -55
@@ -112,81 +112,91 @@ unset _path_seen
112
113 set env(PATH) [join $_search_path $_path_sep]
114
115 -proc _which {what args} {
116 - global _search_exe _search_path
117 -
118 - if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
119 - set suffix {}
120 - } elseif {[is_Windows] && [string match *$_search_exe [string tolower $what]]} {
121 - # The search string already has the file extension
122 - set suffix {}
123 - } else {
124 - set suffix $_search_exe
125 - }
115 +if {[is_Windows]} {
116 + proc _which {what args} {
117 + global _search_exe _search_path
118 +
119 + if {[lsearch -exact $args -script] >= 0} {
120 + set suffix {}
121 + } elseif {[string match *$_search_exe [string tolower $what]]} {
122 + # The search string already has the file extension
123 + set suffix {}
124 + } else {
125 + set suffix $_search_exe
126 + }
127
127 - foreach p $_search_path {
128 - set p [file join $p $what$suffix]
129 - if {[file exists $p]} {
130 - return [file normalize $p]
128 + foreach p $_search_path {
129 + set p [file join $p $what$suffix]
130 + if {[file exists $p]} {
131 + return [file normalize $p]
132 + }
133 }
134 + return {}
135 }
133 - return {}
134 -}
136
136 -proc sanitize_command_line {command_line from_index} {
137 - set i $from_index
138 - while {$i < [llength $command_line]} {
139 - set cmd [lindex $command_line $i]
140 - if {[llength [file split $cmd]] < 2} {
141 - set fullpath [_which $cmd]
142 - if {$fullpath eq ""} {
143 - throw {NOT-FOUND} "$cmd not found in PATH"
137 + proc sanitize_command_line {command_line from_index} {
138 + set i $from_index
139 + while {$i < [llength $command_line]} {
140 + set cmd [lindex $command_line $i]
141 + if {[llength [file split $cmd]] < 2} {
142 + set fullpath [_which $cmd]
143 + if {$fullpath eq ""} {
144 + throw {NOT-FOUND} "$cmd not found in PATH"
145 + }
146 + lset command_line $i $fullpath
147 + }
148 +
149 + # handle piped commands, e.g. `exec A | B`
150 + for {incr i} {$i < [llength $command_line]} {incr i} {
151 + if {[lindex $command_line $i] eq "|"} {
152 + incr i
153 + break
154 + }
155 }
145 - lset command_line $i $fullpath
156 }
157 + return $command_line
158 + }
159 +
160 + # Override `exec` to avoid unsafe PATH lookup
161 +
162 + rename exec real_exec
163
148 - # handle piped commands, e.g. `exec A | B`
149 - for {incr i} {$i < [llength $command_line]} {incr i} {
150 - if {[lindex $command_line $i] eq "|"} {
164 + proc exec {args} {
165 + # skip options
166 + for {set i 0} {$i < [llength $args]} {incr i} {
167 + set arg [lindex $args $i]
168 + if {$arg eq "--"} {
169 incr i
170 break
171 }
172 + if {[string range $arg 0 0] ne "-"} {
173 + break
174 + }
175 }
176 + set args [sanitize_command_line $args $i]
177 + uplevel 1 real_exec $args
178 }
156 - return $command_line
157 -}
179
159 -# Override `exec` to avoid unsafe PATH lookup
180 + # Override `open` to avoid unsafe PATH lookup
181
161 -rename exec real_exec
182 + rename open real_open
183
163 -proc exec {args} {
164 - # skip options
165 - for {set i 0} {$i < [llength $args]} {incr i} {
166 - set arg [lindex $args $i]
167 - if {$arg eq "--"} {
168 - incr i
169 - break
170 - }
171 - if {[string range $arg 0 0] ne "-"} {
172 - break
184 + proc open {args} {
185 + set arg0 [lindex $args 0]
186 + if {[string range $arg0 0 0] eq "|"} {
187 + set command_line [string trim [string range $arg0 1 end]]
188 + lset args 0 "| [sanitize_command_line $command_line 0]"
189 }
190 + uplevel 1 real_open $args
191 }
175 - set args [sanitize_command_line $args $i]
176 - uplevel 1 real_exec $args
177 -}
178 -
179 -# Override `open` to avoid unsafe PATH lookup
192
181 -rename open real_open
193 +} else {
194 + # On non-Windows platforms, auto_execok, exec, and open are safe, and will
195 + # use the sanitized search path. But, we need _which for these.
196
183 -proc open {args} {
184 - set arg0 [lindex $args 0]
185 - if {[string range $arg0 0 0] eq "|"} {
186 - set command_line [string trim [string range $arg0 1 end]]
187 - lset args 0 "| [sanitize_command_line $command_line 0]"
197 + proc _which {what args} {
198 + return [lindex [auto_execok $what] 0]
199 }
189 - uplevel 1 real_open $args
200 }
201
202 ######################################################################