tab completion and glob results are now properly escaped before being parsed

This commit is contained in:
2026-03-11 18:48:07 -04:00
parent bb3db444db
commit f279159873
3 changed files with 75 additions and 27 deletions

View File

@@ -86,6 +86,11 @@ impl Expander {
'outer: while let Some(ch) = chars.next() {
match ch {
markers::ESCAPE => {
if let Some(next_ch) = chars.next() {
cur_word.push(next_ch);
}
}
markers::DUB_QUOTE | markers::SNG_QUOTE | markers::SUBSH => {
while let Some(q_ch) = chars.next() {
match q_ch {
@@ -634,8 +639,10 @@ pub fn expand_glob(raw: &str) -> ShResult<String> {
{
let entry =
entry.map_err(|_| ShErr::simple(ShErrKind::SyntaxErr, "Invalid filename found in glob"))?;
let entry_raw = entry.to_str().ok_or_else(|| ShErr::simple(ShErrKind::SyntaxErr, "Non-UTF8 filename found in glob"))?;
let escaped = escape_str(entry_raw, true);
words.push(entry.to_str().unwrap().to_string())
words.push(escaped)
}
Ok(words.join(" "))
}
@@ -989,6 +996,7 @@ pub fn unescape_str(raw: &str) -> String {
'~' if first_char => result.push(markers::TILDE_SUB),
'\\' => {
if let Some(next_ch) = chars.next() {
result.push(markers::ESCAPE);
result.push(next_ch)
}
}
@@ -1311,6 +1319,62 @@ pub fn unescape_str(raw: &str) -> String {
result
}
/// Opposite of unescape_str - escapes a string to be executed as literal text
/// Used for completion results, and glob filename matches.
pub fn escape_str(raw: &str, use_marker: bool) -> String {
let mut result = String::new();
let mut chars = raw.chars();
while let Some(ch) = chars.next() {
match ch {
'\''|
'"' |
'\\' |
'|' |
'&' |
';' |
'(' |
')' |
'<' |
'>' |
'$' |
'*' |
'!' |
'`' |
'{' |
'?' |
'[' |
'#' |
' ' |
'\t'|
'\n' => {
if use_marker {
result.push(markers::ESCAPE);
} else {
result.push('\\');
}
result.push(ch);
continue;
}
'~' if result.is_empty() => {
if use_marker {
result.push(markers::ESCAPE);
} else {
result.push('\\');
}
result.push(ch);
continue;
}
_ => {
result.push(ch);
continue;
}
}
}
result
}
pub fn unescape_math(raw: &str) -> String {
let mut chars = raw.chars().peekable();
let mut result = String::new();
@@ -2858,7 +2922,8 @@ mod tests {
#[test]
fn unescape_backslash() {
let result = unescape_str("hello\\nworld");
assert_eq!(result, "hellonworld");
let expected = format!("hello{}nworld", markers::ESCAPE);
assert_eq!(result, expected);
}
#[test]