Skip to content

Commit 84ee96c

Browse files
committed
Fix #2859; script error when cwd name contains %
The `string.gsub()` function in Lua always uses Lua patterns (which are similar to regular expressions). Cmder's custom prompt wants to perform simple plain text find/replace operations on strings. `string.gsub()` is the right Lua function for that, but since it always uses Lua patterns it's necessary to apply escaping to the input strings otherwise they can get misinterpreted and cause runtime errors. For example, if the current working directory name contains a percent sign, such as literally "My%20Home". This change fixes that. It introduces a helper function `gsub_plain()` which behaves like `string.gsub()` but applies appropriate escaping to convert the plain text input strings into the corresponding Lua patterns so that it can achieve plain text find/replace operations. It also introduces separate helper functions for escaping the `find` and `replace` parameters for `string.gsub()`, since they have different escaping rules.
1 parent e9750ab commit 84ee96c

File tree

1 file changed

+42
-16
lines changed

1 file changed

+42
-16
lines changed

vendor/clink.lua

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,37 @@ local function get_unknown_color()
5151
end
5252

5353
---
54-
-- Makes a string safe to use as the replacement in string.gsub
54+
-- Escapes special characters in a string.gsub `find` parameter, so that it
55+
-- can be matched as a literal plain text string, i.e. disable Lua pattern
56+
-- matching. See "Patterns" (https://www.lua.org/manual/5.2/manual.html#6.4.1).
57+
-- @param {string} text Text to escape
58+
-- @returns {string} Escaped text
5559
---
56-
local function verbatim(s)
57-
s = string.gsub(s, "%%", "%%%%")
58-
return s
60+
local function escape_gsub_find_arg(text)
61+
return text and text:gsub("([-+*?.%%()%[%]$^])", "%%%1") or ""
62+
end
63+
64+
---
65+
-- Escapes special characters in a string.gsub `replace` parameter, so that it
66+
-- can be replaced as a literal plain text string, i.e. disable Lua pattern
67+
-- matching. See "Patterns" (https://www.lua.org/manual/5.2/manual.html#6.4.1).
68+
-- @param {string} text Text to escape
69+
-- @returns {string} Escaped text
70+
---
71+
local function escape_gsub_replace_arg(text)
72+
return text and text:gsub("%%", "%%%%") or ""
73+
end
74+
75+
---
76+
-- Perform string.sub, but disable Lua pattern matching and just treat both
77+
-- the `find` and `replace` parameters as a literal plain text replacement.
78+
-- @param {string} str Text in which to perform find and replace
79+
-- @param {string} find Text to find (plain text; not a Lua pattern)
80+
-- @param {string} replace Replacement text (plain text; not a Lua pattern)
81+
-- @returns {string} Copy of the input `str` with `find` replaced by `replace`
82+
---
83+
local function gsub_plain(str, find, replace)
84+
return string.gsub(str, escape_gsub_find_arg(find), escape_gsub_replace_arg(replace))
5985
end
6086

6187
-- Extracts only the folder name from the input Path
@@ -153,7 +179,7 @@ local function set_prompt_filter()
153179
end
154180

155181
if prompt_useHomeSymbol and string.find(cwd, clink.get_env("HOME")) then
156-
cwd = string.gsub(cwd, clink.get_env("HOME"), prompt_homeSymbol)
182+
cwd = gsub_plain(cwd, clink.get_env("HOME"), prompt_homeSymbol)
157183
end
158184

159185
local uah = ''
@@ -176,14 +202,14 @@ local function set_prompt_filter()
176202
local version_control = prompt_includeVersionControl and "{git}{hg}{svn}" or ""
177203

178204
local prompt = "{uah}{cwd}" .. version_control .. cr .. get_lamb_color() .. "{env}{lamb}\x1b[0m "
179-
prompt = string.gsub(prompt, "{uah}", uah)
180-
prompt = string.gsub(prompt, "{cwd}", cwd)
181-
prompt = string.gsub(prompt, "{env}", env)
182-
clink.prompt.value = string.gsub(prompt, "{lamb}", prompt_lambSymbol)
205+
prompt = gsub_plain(prompt, "{uah}", uah)
206+
prompt = gsub_plain(prompt, "{cwd}", cwd)
207+
prompt = gsub_plain(prompt, "{env}", env)
208+
clink.prompt.value = gsub_plain(prompt, "{lamb}", prompt_lambSymbol)
183209
end
184210

185211
local function percent_prompt_filter()
186-
clink.prompt.value = string.gsub(clink.prompt.value, "{percent}", "%%")
212+
clink.prompt.value = gsub_plain(clink.prompt.value, "{percent}", "%")
187213
end
188214

189215
---
@@ -532,13 +558,13 @@ local function git_prompt_filter()
532558
color = colors.conflict
533559
end
534560

535-
clink.prompt.value = string.gsub(clink.prompt.value, "{git}", " "..color.."("..verbatim(branch)..")")
561+
clink.prompt.value = gsub_plain(clink.prompt.value, "{git}", " "..color.."("..branch..")")
536562
return false
537563
end
538564
end
539565

540566
-- No git present or not in git file
541-
clink.prompt.value = string.gsub(clink.prompt.value, "{git}", "")
567+
clink.prompt.value = gsub_plain(clink.prompt.value, "{git}", "")
542568
return false
543569
end
544570

@@ -577,13 +603,13 @@ local function hg_prompt_filter()
577603
end
578604

579605
local result = color .. "(" .. branch .. ")"
580-
clink.prompt.value = string.gsub(clink.prompt.value, "{hg}", " "..verbatim(result))
606+
clink.prompt.value = gsub_plain(clink.prompt.value, "{hg}", " "..result)
581607
return false
582608
end
583609
end
584610

585611
-- No hg present or not in hg repo
586-
clink.prompt.value = string.gsub(clink.prompt.value, "{hg}", "")
612+
clink.prompt.value = gsub_plain(clink.prompt.value, "{hg}", "")
587613
end
588614

589615
local function svn_prompt_filter()
@@ -636,13 +662,13 @@ local function svn_prompt_filter()
636662
color = colors.dirty
637663
end
638664

639-
clink.prompt.value = string.gsub(clink.prompt.value, "{svn}", " "..color.."("..verbatim(branch)..")")
665+
clink.prompt.value = gsub_plain(clink.prompt.value, "{svn}", " "..color.."("..branch..")")
640666
return false
641667
end
642668
end
643669

644670
-- No svn present or not in svn file
645-
clink.prompt.value = string.gsub(clink.prompt.value, "{svn}", "")
671+
clink.prompt.value = gsub_plain(clink.prompt.value, "{svn}", "")
646672
return false
647673
end
648674

0 commit comments

Comments
 (0)