@@ -77,97 +77,126 @@ proc is_Cygwin {} {
77
77
78
78
# #####################################################################
79
79
# #
80
- # # PATH lookup
80
+ # # PATH lookup. Sanitize $PATH, assure exec/open use only that
81
+
82
+ if {[is_Windows]} {
83
+ set _path_sep {;}
84
+ set _search_exe .exe
85
+ } else {
86
+ set _path_sep {:}
87
+ set _search_exe {}
88
+ }
89
+
90
+ if {[is_Windows]} {
91
+ set gitguidir [file dirname [info script]]
92
+ regsub -all ";" $gitguidir "\\ ;" gitguidir
93
+ set env(PATH) " $gitguidir ;$env(PATH) "
94
+ }
81
95
82
96
set _search_path {}
83
- proc _which {what args} {
84
- global env _search_exe _search_path
97
+ set _path_seen [ dict create]
98
+ foreach p [ split $env(PATH) $_path_sep ] {
99
+ # Keep only absolute paths, getting rid of ., empty, etc.
100
+ if {[ file pathtype $p ] ne {absolute}} {
101
+ continue
102
+ }
103
+ # Keep only the first occurence of any duplicates.
104
+ set norm_p [ file normalize $p ]
105
+ if {[ dict exists $_path_seen $norm_p ] } {
106
+ continue
107
+ }
108
+ dict set _path_seen $norm_p 1
109
+ lappend _search_path $norm_p
110
+ }
111
+ unset _path_seen
85
112
86
- if {$_search_path eq {}} {
87
- if {[is_Windows]} {
88
- set gitguidir [file dirname [info script]]
89
- regsub -all ";" $gitguidir "\\ ;" gitguidir
90
- set env(PATH) " $gitguidir ;$env(PATH) "
91
- set _search_path [ split $env(PATH) {;}]
92
- # Skip empty `PATH` elements
93
- set _search_path [ lsearch -all -inline -not -exact \
94
- $_search_path " " ]
95
- set _search_exe .exe
113
+ set env(PATH) [ join $_search_path $_path_sep ]
114
+
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 {}
96
124
} else {
97
- set _search_path [ split $env(PATH) :]
98
- set _search_exe {}
125
+ set suffix $_search_exe
99
126
}
100
- }
101
-
102
- if {[ is_Windows] && [ lsearch -exact $args -script] >= 0} {
103
- set suffix {}
104
- } else {
105
- set suffix $_search_exe
106
- }
107
127
108
- foreach p $_search_path {
109
- set p [ file join $p $what$suffix ]
110
- if {[ file exists $p ] } {
111
- 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
+ }
112
133
}
134
+ return {}
113
135
}
114
- return {}
115
- }
116
136
117
- proc sanitize_command_line {command_line from_index} {
118
- set i $from_index
119
- while {$i < [ llength $command_line ] } {
120
- set cmd [ lindex $command_line $i ]
121
- if {[ llength [file split $cmd ] ] < 2} {
122
- set fullpath [ _which $cmd ]
123
- if {$fullpath eq " " } {
124
- 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
+ }
125
155
}
126
- lset command_line $i $fullpath
127
156
}
157
+ return $command_line
158
+ }
159
+
160
+ # Override `exec` to avoid unsafe PATH lookup
128
161
129
- # handle piped commands, e.g. `exec A | B`
130
- for {incr i} {$i < [ llength $command_line ] } {incr i} {
131
- if {[ lindex $command_line $i ] eq " |" } {
162
+ rename exec real_exec
163
+
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 " --" } {
132
169
incr i
133
170
break
134
171
}
172
+ if {[ string range $arg 0 0] ne " -" } {
173
+ break
174
+ }
135
175
}
176
+ set args [ sanitize_command_line $args $i ]
177
+ uplevel 1 real_exec $args
136
178
}
137
- return $command_line
138
- }
139
179
140
- # Override `exec ` to avoid unsafe PATH lookup
180
+ # Override `open ` to avoid unsafe PATH lookup
141
181
142
- rename exec real_exec
182
+ rename open real_open
143
183
144
- proc exec {args} {
145
- # skip options
146
- for {set i 0} {$i < [ llength $args ] } {incr i} {
147
- set arg [ lindex $args $i ]
148
- if {$arg eq " --" } {
149
- incr i
150
- break
151
- }
152
- if {[ string range $arg 0 0] ne " -" } {
153
- 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]"
154
189
}
190
+ uplevel 1 real_open $args
155
191
}
156
- set args [ sanitize_command_line $args $i ]
157
- uplevel 1 real_exec $args
158
- }
159
192
160
- # Override `open` to avoid unsafe PATH lookup
161
-
162
- 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.
163
196
164
- proc open {args} {
165
- set arg0 [ lindex $args 0]
166
- if {[ string range $arg0 0 0] eq " |" } {
167
- set command_line [ string trim [string range $arg0 1 end] ]
168
- lset args 0 " | [sanitize_command_line $command_line 0]"
197
+ proc _which {what args} {
198
+ return [ lindex [auto_execok $what ] 0]
169
199
}
170
- uplevel 1 real_open $args
171
200
}
172
201
173
202
######################################################################
@@ -304,15 +333,37 @@ if {$_trace >= 0} {
304
333
# branches).
305
334
set _last_merged_branch {}
306
335
307
- proc shellpath {} {
308
- global _shellpath env
309
- if {[ string match @@* $_shellpath ] } {
310
- if {[ info exists env(SHELL)] } {
311
- return $env(SHELL)
312
- } else {
313
- return /bin/sh
314
- }
336
+ # for testing, allow unconfigured _shellpath
337
+ if {[ string match @@* $_shellpath ] } {
338
+ if {[ info exists env(SHELL)] } {
339
+ set _shellpath $env(SHELL)
340
+ } else {
341
+ set _shellpath /bin/sh
315
342
}
343
+ }
344
+
345
+ if {[ is_Windows] } {
346
+ set _shellpath [ exec cygpath -m $_shellpath ]
347
+ }
348
+
349
+ if {![ file executable $_shellpath ] || \
350
+ !([ file pathtype $_shellpath ] eq {absolute})} {
351
+ set errmsg " The defined shell ('$_shellpath ') is not usable, \
352
+ it must be an absolute path to an executable."
353
+ puts stderr $errmsg
354
+
355
+ catch {wm withdraw .}
356
+ tk_messageBox \
357
+ -icon error \
358
+ -type ok \
359
+ -title " git-gui: configuration error" \
360
+ -message $errmsg
361
+ exit 1
362
+ }
363
+
364
+
365
+ proc shellpath {} {
366
+ global _shellpath
316
367
return $_shellpath
317
368
}
318
369
@@ -524,32 +575,13 @@ proc _git_cmd {name} {
524
575
return $v
525
576
}
526
577
527
- # Test a file for a hashbang to identify executable scripts on Windows.
528
- proc is_shellscript {filename } {
529
- if {![file exists $filename ]} {return 0}
530
- set f [open $filename r]
531
- fconfigure $f -encoding binary
532
- set magic [read $f 2]
533
- close $f
534
- return [expr {$magic eq " #!" }]
535
- }
536
-
537
- # Run a command connected via pipes on stdout.
578
+ # Run a shell command connected via pipes on stdout.
538
579
# This is for use with textconv filters and uses sh -c "..." to allow it to
539
- # contain a command with arguments. On windows we must check for shell
540
- # scripts specifically otherwise just call the filter command.
580
+ # contain a command with arguments. We presume this
581
+ # to be a shellscript that the configured shell (/bin/sh by default) knows
582
+ # how to run.
541
583
proc open_cmd_pipe {cmd path} {
542
- global env
543
- if {![file executable [shellpath]]} {
544
- set exe [auto_execok [lindex $cmd 0]]
545
- if {[is_shellscript [lindex $exe 0]]} {
546
- set run [linsert [auto_execok sh] end -c " $cmd \"\$ 0\" " $path ]
547
- } else {
548
- set run [concat $exe [lrange $cmd 1 end] $path ]
549
- }
550
- } else {
551
- set run [list [shellpath] -c " $cmd \"\$ 0\" " $path ]
552
- }
584
+ set run [list [shellpath] -c " $cmd \"\$ 0\" " $path ]
553
585
return [open |$run r]
554
586
}
555
587
@@ -2760,17 +2792,16 @@ if {![is_bare]} {
2760
2792
2761
2793
if {[is_Windows]} {
2762
2794
# Use /git-bash.exe if available
2763
- set normalized [file normalize $::argv0 ]
2764
- regsub "/mingw../libexec/git-core/git-gui$" \
2765
- $normalized "/git-bash.exe" cmdLine
2766
- if {$cmdLine != $normalized && [file exists $cmdLine ]} {
2767
- set cmdLine [list " Git Bash" $cmdLine &]
2795
+ set _git_bash [exec cygpath -m /git-bash.exe]
2796
+ if {[file executable $_git_bash ]} {
2797
+ set _bash_cmdline [list " Git Bash" $_git_bash &]
2768
2798
} else {
2769
- set cmdLine [list " Git Bash" bash --login -l &]
2799
+ set _bash_cmdline [list " Git Bash" bash --login -l &]
2770
2800
}
2771
2801
.mbar.repository add command \
2772
2802
-label [mc " Git Bash" ] \
2773
- -command {eval exec [auto_execok start] $cmdLine }
2803
+ -command {eval exec [list [_which cmd] /c start] $_bash_cmdline }
2804
+ unset _git_bash
2774
2805
}
2775
2806
2776
2807
if {[is_Windows] || ![is_bare]} {
0 commit comments