fix: correct shell quotes for Unicode on Windows (#2285)

This commit is contained in:
三咲雅 · Misaki Masa 2025-02-04 09:41:37 +08:00 committed by sxyazi
parent c41f2e21e0
commit 1cc9b2475c
No known key found for this signature in database

View file

@ -1,4 +1,4 @@
use std::{borrow::Cow, iter::repeat}; use std::{borrow::Cow, iter::repeat_n};
pub fn escape_str(s: &str) -> Cow<str> { pub fn escape_str(s: &str) -> Cow<str> {
let bytes = s.as_bytes(); let bytes = s.as_bytes();
@ -6,8 +6,8 @@ pub fn escape_str(s: &str) -> Cow<str> {
return Cow::Borrowed(s); return Cow::Borrowed(s);
} }
let mut escaped = String::with_capacity(bytes.len() + 2); let mut escaped = Vec::with_capacity(bytes.len() + 2);
escaped.push('"'); escaped.push(b'"');
let mut chars = bytes.iter().copied().peekable(); let mut chars = bytes.iter().copied().peekable();
loop { loop {
@ -18,24 +18,24 @@ pub fn escape_str(s: &str) -> Cow<str> {
match chars.next() { match chars.next() {
Some(b'"') => { Some(b'"') => {
escaped.reserve(slashes * 2 + 2); escaped.reserve(slashes * 2 + 2);
escaped.extend(repeat('\\').take(slashes * 2 + 1)); escaped.extend(repeat_n(b'\\', slashes * 2 + 1));
escaped.push('"'); escaped.push(b'"');
} }
Some(c) => { Some(b) => {
escaped.reserve(slashes + 1); escaped.reserve(slashes + 1);
escaped.extend(repeat('\\').take(slashes)); escaped.extend(repeat_n(b'\\', slashes));
escaped.push(c as _); escaped.push(b);
} }
None => { None => {
escaped.reserve(slashes * 2); escaped.reserve(slashes * 2);
escaped.extend(repeat('\\').take(slashes * 2)); escaped.extend(repeat_n(b'\\', slashes * 2));
break; break;
} }
} }
} }
escaped.push('"'); escaped.push(b'"');
escaped.into() Cow::Owned(unsafe { String::from_utf8_unchecked(escaped) })
} }
#[cfg(windows)] #[cfg(windows)]
@ -59,17 +59,17 @@ pub fn escape_os_str(s: &std::ffi::OsStr) -> Cow<std::ffi::OsStr> {
match chars.next() { match chars.next() {
Some(c) if c == b'"' as _ => { Some(c) if c == b'"' as _ => {
escaped.reserve(slashes * 2 + 2); escaped.reserve(slashes * 2 + 2);
escaped.extend(repeat(b'\\' as u16).take(slashes * 2 + 1)); escaped.extend(repeat_n(b'\\' as u16, slashes * 2 + 1));
escaped.push(b'"' as _); escaped.push(b'"' as _);
} }
Some(c) => { Some(c) => {
escaped.reserve(slashes + 1); escaped.reserve(slashes + 1);
escaped.extend(repeat(b'\\' as u16).take(slashes)); escaped.extend(repeat_n(b'\\' as u16, slashes));
escaped.push(c); escaped.push(c);
} }
None => { None => {
escaped.reserve(slashes * 2); escaped.reserve(slashes * 2);
escaped.extend(repeat(b'\\' as u16).take(slashes * 2)); escaped.extend(repeat_n(b'\\' as u16, slashes * 2));
break; break;
} }
} }
@ -137,6 +137,8 @@ mod tests {
assert_eq!(escape_str(r#"--features="default""#), r#""--features=\"default\"""#); assert_eq!(escape_str(r#"--features="default""#), r#""--features=\"default\"""#);
assert_eq!(escape_str(r#""--features=\"default\"""#), r#""\"--features=\\\"default\\\"\"""#); assert_eq!(escape_str(r#""--features=\"default\"""#), r#""\"--features=\\\"default\\\"\"""#);
assert_eq!(escape_str("linker=gcc -L/foo -Wl,bar"), r#""linker=gcc -L/foo -Wl,bar""#); assert_eq!(escape_str("linker=gcc -L/foo -Wl,bar"), r#""linker=gcc -L/foo -Wl,bar""#);
assert_eq!(escape_str("이것은 테스트"), r#""이것은 테스트""#);
} }
#[cfg(windows)] #[cfg(windows)]