Skip to content

Commit d0564ea

Browse files
authored
Merge pull request microsoft#165632 from rwe/serialize-message-bash
shellIntegration-bash.sh: escape values in "E" (executed command) and "P" (property KV) codes
2 parents b6bafe1 + 30dac70 commit d0564ea

File tree

1 file changed

+48
-10
lines changed

1 file changed

+48
-10
lines changed

src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,47 @@ if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then
3737
builtin return
3838
fi
3939

40+
# The property (P) and command (E) codes embed values which require escaping.
41+
# Backslashes are doubled. Non-alphanumeric characters are converted to escaped hex.
42+
__vsc_escape_value() {
43+
# Process text byte by byte, not by codepoint.
44+
builtin local LC_ALL=C str="${1}" i byte token out=''
45+
46+
for (( i=0; i < "${#str}"; ++i )); do
47+
byte="${str:$i:1}"
48+
49+
# Backslashes must be doubled.
50+
if [ "$byte" = "\\" ]; then
51+
token="\\\\"
52+
# Conservatively pass alphanumerics through.
53+
elif [[ "$byte" == [0-9A-Za-z] ]]; then
54+
token="$byte"
55+
# Hex-encode anything else.
56+
# (Importantly including: semicolon, newline, and control chars).
57+
else
58+
# The printf '0x%02X' "'$byte'" converts the character to a hex integer.
59+
# See printf's specification:
60+
# > If the leading character is a single-quote or double-quote, the value shall be the numeric value in the
61+
# > underlying codeset of the character following the single-quote or double-quote.
62+
# However, the result is a sign-extended int, so a high bit like 0xD7 becomes 0xFFF…FD7
63+
# We mask that word with 0xFF to get lowest 8 bits, and then encode that byte as "\xD7" per our escaping scheme.
64+
builtin printf -v token '\\x%02X' "$(( $(builtin printf '0x%X' "'$byte'") & 0xFF ))"
65+
# |________| ^^^ ^^^ ^^^^^^ ^^^^^^^ |______|
66+
# | | | | | |
67+
# store in `token` -+ | | the hex value -----------+ | |
68+
# the '\x…'-prefixed -----+ | of the byte as an integer --------+ |
69+
# 0-padded, two hex digits --+ masked to one byte (due to sign extension) -+
70+
fi
71+
72+
out+="$token"
73+
done
74+
75+
builtin printf '%s\n' "${out}"
76+
}
77+
4078
# Send the IsWindows property if the environment looks like Windows
4179
if [[ "$(uname -s)" =~ ^CYGWIN*|MINGW*|MSYS* ]]; then
42-
builtin printf "\x1b]633;P;IsWindows=True\x07"
80+
builtin printf '\e]633;P;IsWindows=True\a'
4381
fi
4482

4583
# Allow verifying $BASH_COMMAND doesn't have aliases resolved via history when the right HISTCONTROL
@@ -59,35 +97,35 @@ __vsc_in_command_execution="1"
5997
__vsc_current_command=""
6098

6199
__vsc_prompt_start() {
62-
builtin printf "\033]633;A\007"
100+
builtin printf '\e]633;A\a'
63101
}
64102

65103
__vsc_prompt_end() {
66-
builtin printf "\033]633;B\007"
104+
builtin printf '\e]633;B\a'
67105
}
68106

69107
__vsc_update_cwd() {
70-
builtin printf "\033]633;P;Cwd=%s\007" "$PWD"
108+
builtin printf '\e]633;P;Cwd=%s\a' "$(__vsc_escape_value "$PWD")"
71109
}
72110

73111
__vsc_command_output_start() {
74-
builtin printf "\033]633;C\007"
75-
builtin printf "\033]633;E;%s\007" "$__vsc_current_command"
112+
builtin printf '\e]633;C\a'
113+
builtin printf '\e]633;E;%s\a' "$(__vsc_escape_value "${__vsc_current_command}")"
76114
}
77115

78116
__vsc_continuation_start() {
79-
builtin printf "\033]633;F\007"
117+
builtin printf '\e]633;F\a'
80118
}
81119

82120
__vsc_continuation_end() {
83-
builtin printf "\033]633;G\007"
121+
builtin printf '\e]633;G\a'
84122
}
85123

86124
__vsc_command_complete() {
87125
if [ "$__vsc_current_command" = "" ]; then
88-
builtin printf "\033]633;D\007"
126+
builtin printf '\e]633;D\a'
89127
else
90-
builtin printf "\033]633;D;%s\007" "$__vsc_status"
128+
builtin printf '\e]633;D;%s\a' "$__vsc_status"
91129
fi
92130
__vsc_update_cwd
93131
}

0 commit comments

Comments
 (0)