Keep completion words out of shell evaluation
This commit is contained in:
@@ -96,6 +96,46 @@ func CompletionScript(w io.Writer, executable, shell string) error {
|
||||
|
||||
func adjustBashCompletion(script, name string) (string, error) {
|
||||
var err error
|
||||
script, err = replaceCompletionSection(script, " local requestComp lastParam lastChar args", " # Extract the directive", ` local -a requestWords=()
|
||||
local word char quote decoded next i backtick=$'\x60'
|
||||
# Bash's completion helpers supply shell spelling, not evaluated argv.
|
||||
# Remove quoting and escapes without expanding substitutions or globs.
|
||||
for word in "${words[@]}"; do
|
||||
quote=
|
||||
decoded=
|
||||
for ((i=0; i<${#word}; i++)); do
|
||||
char=${word:i:1}
|
||||
case "$quote:$char" in
|
||||
"':'"|'":"') quote= ;;
|
||||
':"'|":'") quote=$char ;;
|
||||
:\\|'"':\\)
|
||||
if ((i+1 < ${#word})); then
|
||||
next=${word:i+1:1}
|
||||
if [[ $quote == '"' && $next != '\' && $next != '"' && $next != '$' && $next != "$backtick" ]]; then
|
||||
decoded+=$char
|
||||
continue
|
||||
fi
|
||||
((i+=1))
|
||||
decoded+=${word:i:1}
|
||||
else
|
||||
decoded+=$char
|
||||
fi ;;
|
||||
*) decoded+=$char ;;
|
||||
esac
|
||||
done
|
||||
requestWords+=("$decoded")
|
||||
done
|
||||
if [[ -z $cur && ${words[${#words[@]}-1]} != *= ]]; then
|
||||
# The Bash helpers may omit the empty word at a trailing space.
|
||||
if ((${#requestWords[@]} <= cword)); then requestWords+=(""); fi
|
||||
fi
|
||||
if [[ ${cur} == -*=* ]]; then cur="${cur#*=}"; fi
|
||||
out=$(COBRA_ACTIVE_HELP=0 "${requestWords[0]}" __complete "${requestWords[@]:1}" 2>/dev/null)
|
||||
|
||||
`)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
script, err = replaceCompletionText(script, `local out directive`, `local out directive completionPrefix completionOriginalCur`)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -145,29 +185,68 @@ func adjustBashCompletion(script, name string) (string, error) {
|
||||
|
||||
func adjustZshCompletion(script string) (string, error) {
|
||||
var err error
|
||||
script, err = replaceCompletionText(script, `words=("${=words[1,CURRENT]}")`, `words=("${(@)words[1,CURRENT]}")`)
|
||||
script, err = replaceCompletionText(script, `words=("${=words[1,CURRENT]}")`, `words=("${(@Q)words[1,CURRENT]}")`)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
script, err = replaceCompletionText(script, `=~ '-.*='`, `=~ '-[^=]*='`)
|
||||
script, err = replaceCompletionSection(script, " # For zsh, when completing a flag", " # Extract the directive", ` local -a prefixArgs orderArgs spaceArgs
|
||||
if [[ "$lastParam" == -*=* ]]; then
|
||||
flagPrefix="${lastParam%%=*}="
|
||||
fi
|
||||
out=$(COBRA_ACTIVE_HELP=0 "${words[1]}" __complete "${words[@]:1}" 2>/dev/null)
|
||||
|
||||
`)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return replaceCompletionText(script, `local activeHelpMarker="_activeHelp_ "`, `if [[ "$out" == `+completionPathPrefixMarker+`-* ]]; then
|
||||
script, err = replaceCompletionText(script, `local activeHelpMarker="_activeHelp_ "`, `if [[ "$out" == `+completionPathPrefixMarker+`-* ]]; then
|
||||
local pathPrefix="${out%%$'\n'*}"
|
||||
pathPrefix="${pathPrefix#`+completionPathPrefixMarker+`}"
|
||||
if [[ "$pathPrefix" != --* && "$lastParam" == "$pathPrefix"* ]]; then
|
||||
out="${out#*$'\n'}"
|
||||
if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
|
||||
flagPrefix="-P ${pathPrefix}"
|
||||
else
|
||||
flagPrefix=""
|
||||
compset -P "${(b)pathPrefix}"
|
||||
fi
|
||||
flagPrefix="$pathPrefix"
|
||||
fi
|
||||
fi
|
||||
if [[ -n "$flagPrefix" ]]; then
|
||||
prefixArgs=(-P "$flagPrefix")
|
||||
fi
|
||||
|
||||
local activeHelpMarker="_activeHelp_ "`)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return replaceCompletionSection(script, " if [ $((directive & shellCompDirectiveNoSpace))", "\n}\n", ` if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
|
||||
spaceArgs=(-S "")
|
||||
fi
|
||||
if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then
|
||||
orderArgs=(-V)
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
|
||||
_describe "${orderArgs[@]}" completions completions "${prefixArgs[@]}" "${spaceArgs[@]}"
|
||||
else
|
||||
if [[ -n "$flagPrefix" ]]; then
|
||||
compset -P "${(b)flagPrefix}"
|
||||
fi
|
||||
if [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
|
||||
_files -/
|
||||
else
|
||||
_files
|
||||
fi
|
||||
fi
|
||||
`)
|
||||
}
|
||||
|
||||
func replaceCompletionSection(script, start, end, replacement string) (string, error) {
|
||||
i := strings.Index(script, start)
|
||||
if i < 0 {
|
||||
return "", fmt.Errorf("unsupported generated completion script")
|
||||
}
|
||||
j := strings.Index(script[i+len(start):], end)
|
||||
if j < 0 {
|
||||
return "", fmt.Errorf("unsupported generated completion script")
|
||||
}
|
||||
return script[:i] + replacement + script[i+len(start)+j:], nil
|
||||
}
|
||||
|
||||
func replaceCompletionText(script, old, replacement string) (string, error) {
|
||||
|
||||
Reference in New Issue
Block a user