Files

73 lines
1.7 KiB
Go

package openfolder
import (
"errors"
"os/exec"
"strings"
)
// OpenLocationCustom executes a user-defined command string, substituting %1
// with the given path. The command string is split into executable + arguments
// using shell-like quoting rules (double quotes are respected).
//
// Example command: doublecmd.exe -C -T -P L -L "%1"
func OpenLocationCustom(command string, path string) error {
expanded := strings.ReplaceAll(command, "%1", path)
args, err := splitCommand(expanded)
if err != nil {
return err
}
if len(args) == 0 {
return errors.New("open_location_command: empty command after expansion")
}
return exec.Command(args[0], args[1:]...).Start()
}
// splitCommand splits a command string into tokens, respecting double-quoted
// segments. Quotes are removed from the resulting tokens. Backslash escaping
// of a double quote (\") inside a quoted segment is supported.
func splitCommand(s string) ([]string, error) {
var tokens []string
var current strings.Builder
inQuote := false
hasToken := false
for i := 0; i < len(s); i++ {
ch := s[i]
switch {
case ch == '"':
inQuote = !inQuote
hasToken = true // even empty quotes produce a token part
case ch == '\\' && inQuote && i+1 < len(s) && s[i+1] == '"':
// escaped quote inside a quoted segment
current.WriteByte('"')
i++ // skip the next quote
case (ch == ' ' || ch == '\t') && !inQuote:
if hasToken {
tokens = append(tokens, current.String())
current.Reset()
hasToken = false
}
default:
current.WriteByte(ch)
hasToken = true
}
}
if inQuote {
return nil, errors.New("open_location_command: unterminated double quote")
}
if hasToken {
tokens = append(tokens, current.String())
}
return tokens, nil
}