incr i
break
}
+ if {[string range $arg 0 0] ne "-"} {
+ break
+ }
}
+ set args [sanitize_command_line $args $i]
+ uplevel 1 real_exec $args
}
- return $command_line
- }
- # Override `exec` to avoid unsafe PATH lookup
+ # Override `open` to avoid unsafe PATH lookup
- rename exec real_exec
+ rename open real_open
- proc exec {args} {
- # skip options
- for {set i 0} {$i < [llength $args]} {incr i} {
- set arg [lindex $args $i]
- if {$arg eq "--"} {
- incr i
- break
- }
- if {[string range $arg 0 0] ne "-"} {
- break
+ proc open {args} {
+ set arg0 [lindex $args 0]
+ if {[string range $arg0 0 0] eq "|"} {
+ set command_line [string trim [string range $arg0 1 end]]
+ lset args 0 "| [sanitize_command_line $command_line 0]"
}
+ uplevel 1 real_open $args
}
- set args [sanitize_command_line $args $i]
- uplevel 1 real_exec $args
- }
- # Override `open` to avoid unsafe PATH lookup
-
- rename open real_open
+ } else {
+ # On non-Windows platforms, auto_execok, exec, and open are safe, and will
+ # use the sanitized search path. But, we need _which for these.
- proc open {args} {
- set arg0 [lindex $args 0]
- if {[string range $arg0 0 0] eq "|"} {
- set command_line [string trim [string range $arg0 1 end]]
- lset args 0 "| [sanitize_command_line $command_line 0]"
+ proc _which {what args} {
+ return [lindex [auto_execok $what] 0]
}
- uplevel 1 real_open $args
}
+# Wrap exec/open to sanitize arguments
+
+# unsafe arguments begin with redirections or the pipe or background operators
+proc is_arg_unsafe {arg} {
+ regexp {^([<|>&]|2>)} $arg
+}
+
+proc make_arg_safe {arg} {
+ if {[is_arg_unsafe $arg]} {
+ set arg [file join . $arg]
+ }
+ return $arg
+}
+
+proc make_arglist_safe {arglist} {
+ set res {}
+ foreach arg $arglist {
+ lappend res [make_arg_safe $arg]
+ }
+ return $res
+}
+
+# executes one command
+# no redirections or pipelines are possible
+# cmd is a list that specifies the command and its arguments
+# calls `exec` and returns its value
+proc safe_exec {cmd} {
+ eval exec [make_arglist_safe $cmd]
+}
+
+# executes one command in the background
+# no redirections or pipelines are possible
+# cmd is a list that specifies the command and its arguments
+# calls `exec` and returns its value
+proc safe_exec_bg {cmd} {
+ eval exec [make_arglist_safe $cmd] &
+}
+
+proc safe_open_file {filename flags} {
+ # a file name starting with "|" would attempt to run a process
+ # but such a file name must be treated as a relative path
+ # hide the "|" behind "./"
+ if {[string index $filename 0] eq "|"} {
+ set filename [file join . $filename]
+ }
+ open $filename $flags
+}
+
+# End exec/open wrappers
+
######################################################################
##
## locate our library
return $v
}
- # Test a file for a hashbang to identify executable scripts on Windows.
- proc is_shellscript {filename} {
- if {![file exists $filename]} {return 0}
- set f [safe_open_file $filename r]
- fconfigure $f -encoding binary
- set magic [read $f 2]
- close $f
- return [expr {$magic eq "#!"}]
- }
-
- # Run a command connected via pipes on stdout.
+ # Run a shell command connected via pipes on stdout.
# This is for use with textconv filters and uses sh -c "..." to allow it to
- # contain a command with arguments. On windows we must check for shell
- # scripts specifically otherwise just call the filter command.
+ # contain a command with arguments. We presume this
+ # to be a shellscript that the configured shell (/bin/sh by default) knows
+ # how to run.
proc open_cmd_pipe {cmd path} {
- global env
- if {![file executable [shellpath]]} {
- set exe [auto_execok [lindex $cmd 0]]
- if {[is_shellscript [lindex $exe 0]]} {
- set run [linsert [auto_execok sh] end -c "$cmd \"\$0\"" $path]
- } else {
- set run [concat $exe [lrange $cmd 1 end] $path]
- }
- } else {
- set run [list [shellpath] -c "$cmd \"\$0\"" $path]
- }
+ set run [list [shellpath] -c "$cmd \"\$0\"" $path]
+ set run [make_arglist_safe $run]
return [open |$run r]
}
if {[is_Windows]} {
# Use /git-bash.exe if available
- set normalized [file normalize $::argv0]
- regsub "/mingw../libexec/git-core/git-gui$" \
- $normalized "/git-bash.exe" cmdLine
- if {$cmdLine != $normalized && [file exists $cmdLine]} {
- set cmdLine [list "Git Bash" $cmdLine]
+ set _git_bash [exec cygpath -m /git-bash.exe]
+ if {[file executable $_git_bash]} {
- set _bash_cmdline [list "Git Bash" $_git_bash &]
++ set _bash_cmdline [list "Git Bash" $_git_bash]
} else {
- set cmdLine [list "Git Bash" bash --login -l]
- set _bash_cmdline [list "Git Bash" bash --login -l &]
++ set _bash_cmdline [list "Git Bash" bash --login -l]
}
.mbar.repository add command \
-label [mc "Git Bash"] \
- -command {safe_exec_bg [concat [list [auto_execok start]] $cmdLine]}
- -command {eval exec [list [_which cmd] /c start] $_bash_cmdline}
++ -command {safe_exec_bg [concat [list [_which cmd] /c start] $_bash_cmdline]}
+ unset _git_bash
}
if {[is_Windows] || ![is_bare]} {