2021-12-29 05:33:38 +02:00
package oscommands
import (
2022-01-03 06:15:26 +02:00
"fmt"
2021-12-29 05:33:38 +02:00
"os"
"strings"
2021-12-31 01:46:34 +02:00
"github.com/jesseduffield/lazygit/pkg/secureexec"
2021-12-29 05:33:38 +02:00
"github.com/mgutz/str"
)
type ICmdObjBuilder interface {
// New returns a new command object based on the string provided
New ( cmdStr string ) ICmdObj
// NewShell takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'`
NewShell ( commandStr string ) ICmdObj
// NewFromArgs takes a slice of strings like []string{"git", "commit"} and returns a new command object. This can be useful when you don't want to worry about whitespace and quoting and stuff.
NewFromArgs ( args [ ] string ) ICmdObj
// Quote wraps a string in quotes with any necessary escaping applied. The reason for bundling this up with the other methods in this interface is that we basically always need to make use of this when creating new command objects.
Quote ( str string ) string
}
type CmdObjBuilder struct {
2022-01-05 03:01:59 +02:00
runner ICmdObjRunner
platform * Platform
2021-12-29 05:33:38 +02:00
}
2021-12-30 04:11:58 +02:00
// poor man's version of explicitly saying that struct X implements interface Y
var _ ICmdObjBuilder = & CmdObjBuilder { }
2021-12-29 05:33:38 +02:00
func ( self * CmdObjBuilder ) New ( cmdStr string ) ICmdObj {
args := str . ToArgv ( cmdStr )
2021-12-31 01:46:34 +02:00
cmd := secureexec . Command ( args [ 0 ] , args [ 1 : ] ... )
2021-12-29 05:33:38 +02:00
cmd . Env = os . Environ ( )
return & CmdObj {
2022-01-05 02:57:32 +02:00
cmdStr : cmdStr ,
cmd : cmd ,
runner : self . runner ,
2021-12-29 05:33:38 +02:00
}
}
func ( self * CmdObjBuilder ) NewFromArgs ( args [ ] string ) ICmdObj {
2021-12-31 01:46:34 +02:00
cmd := secureexec . Command ( args [ 0 ] , args [ 1 : ] ... )
2021-12-29 05:33:38 +02:00
cmd . Env = os . Environ ( )
return & CmdObj {
2022-01-05 02:57:32 +02:00
cmdStr : strings . Join ( args , " " ) ,
cmd : cmd ,
runner : self . runner ,
2021-12-29 05:33:38 +02:00
}
}
func ( self * CmdObjBuilder ) NewShell ( commandStr string ) ICmdObj {
2022-01-03 06:15:26 +02:00
quotedCommand := ""
// Windows does not seem to like quotes around the command
if self . platform . OS == "windows" {
quotedCommand = strings . NewReplacer (
"^" , "^^" ,
"&" , "^&" ,
"|" , "^|" ,
"<" , "^<" ,
">" , "^>" ,
"%" , "^%" ,
) . Replace ( commandStr )
} else {
quotedCommand = self . Quote ( commandStr )
}
shellCommand := fmt . Sprintf ( "%s %s %s" , self . platform . Shell , self . platform . ShellArg , quotedCommand )
return self . New ( shellCommand )
2021-12-29 05:33:38 +02:00
}
func ( self * CmdObjBuilder ) CloneWithNewRunner ( decorate func ( ICmdObjRunner ) ICmdObjRunner ) * CmdObjBuilder {
decoratedRunner := decorate ( self . runner )
return & CmdObjBuilder {
2022-01-05 03:01:59 +02:00
runner : decoratedRunner ,
platform : self . platform ,
2021-12-29 05:33:38 +02:00
}
}
2022-01-05 03:01:59 +02:00
func ( self * CmdObjBuilder ) Quote ( message string ) string {
var quote string
if self . platform . OS == "windows" {
quote = ` \" `
message = strings . NewReplacer (
` " ` , ` "'"'" ` ,
` \" ` , ` \\" ` ,
) . Replace ( message )
} else {
quote = ` " `
message = strings . NewReplacer (
` \ ` , ` \\ ` ,
` " ` , ` \" ` ,
` $ ` , ` \$ ` ,
"`" , "\\`" ,
) . Replace ( message )
}
return quote + message + quote
}