Skip to content

Commit aba62bd

Browse files
akinomyogascop
authored andcommitted
fix(scp): work around incomplete triple backslashes for remote paths
1 parent 670c120 commit aba62bd

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

completions/ssh

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,27 @@ _comp_xfunc_scp_compgen_remote_files()
541541
local REPLY=$cur
542542
if [[ ! $_less_escaping ]]; then
543543
# unescape (3 backslashes to 1 for chars we escaped)
544-
REPLY=$(command sed -e 's/\\\\\\\('"$_comp_cmd_scp__path_esc"'\)/\\\1/g' <<<"$REPLY")
544+
#
545+
# In the following while-loop, we essentially do the following:
546+
#
547+
# REPLY=$(command sed -e 's/\\\\\\\('"$_comp_cmd_scp__path_esc"'\|$\)/\\\1/g' <<<"$REPLY")
548+
#
549+
# We implement this by the Bash built-in features because POSIX BRE
550+
# does not support \|. POSIX sed newly standardized the "-E" flag to
551+
# use POSIX ERE in POSIX.1-2024, but older implementations of sed are
552+
# still expected to remain in the market. Also, we can avoid the fork
553+
# cost by implementing this using built-in features.
554+
#
555+
# Note: We need to store \\\\\\ in a variable to work around "shopt -s
556+
# compat31".
557+
local _tail=$REPLY _regex_triple_backslashes='\\\\\\('$_comp_cmd_scp__path_esc'|$)(.*)$'
558+
REPLY=
559+
while [[ $_tail && $_tail =~ $_regex_triple_backslashes ]]; do
560+
# shellcheck disable=SC1003
561+
REPLY=${_tail::${#_tail}-${#BASH_REMATCH}}'\'${BASH_REMATCH[1]}
562+
_tail=${BASH_REMATCH[2]}
563+
done
564+
REPLY+=$_tail
545565
fi
546566
_comp_dequote_incomplete "$REPLY"
547567
local cur_val=${REPLY-}

test/t/test_scp.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ def test_remote_path_with_backslash(self, bash):
133133
[r"abc\ def.txt", r"abc\\\ def.txt"]
134134
) or completion == sorted([r"abc\\\ def.txt", r"abc\\\\\\\ def.txt"])
135135

136+
def test_remote_path_with_backslash_2(self, bash):
137+
assert_bash_exec(
138+
bash, r"ssh() { [[ $1 == abc ]]; printf '%s\n' 'abc OK'; }"
139+
)
140+
completion = assert_complete(bash, "scp remote_host:abc\\\\\\")
141+
assert_bash_exec(bash, "unset -f ssh")
142+
143+
assert completion == "OK"
144+
136145
def test_xfunc_remote_files(self, live_pwd, bash):
137146
def prefix_paths(prefix, paths):
138147
return [f"{prefix}{path}" for path in paths]

0 commit comments

Comments
 (0)