4
4
//! [`shell-escape`]: https://crates.io/crates/shell-escape
5
5
//! [`shell-escape::unix`]: https://docs.rs/shell-escape/latest/src/shell_escape/lib.rs.html#101
6
6
7
+ use std:: {
8
+ borrow:: Cow ,
9
+ ffi:: { OsStr , OsString } ,
10
+ os:: unix:: ffi:: OsStrExt ,
11
+ os:: unix:: ffi:: OsStringExt ,
12
+ } ;
7
13
8
- fn whitelisted ( ch : char ) -> bool {
9
- match ch {
10
- 'a' ..='z' | 'A' ..='Z' | '0' ..='9' | '-' | '_' | '=' | '/' | ',' | '.' | '+' => true ,
14
+
15
+ fn whitelisted ( byte : u8 ) -> bool {
16
+ match byte {
17
+ b'a' ..=b'z' | b'A' ..=b'Z' | b'0' ..=b'9' | b'-' | b'_' | b'=' | b'/' | b',' | b'.' | b'+' => true ,
11
18
_ => false ,
12
19
}
13
20
}
@@ -20,62 +27,74 @@ fn whitelisted(ch: char) -> bool {
20
27
///
21
28
/// [`shell-escape::unix::escape`]: https://docs.rs/shell-escape/latest/src/shell_escape/lib.rs.html#101
22
29
///
23
- pub fn escape ( s : & [ u8 ] ) -> String {
24
- let all_whitelisted = s. iter ( ) . all ( |x| whitelisted ( * x as char ) ) ;
30
+ pub fn escape ( s : & OsStr ) -> Cow < ' _ , OsStr > {
31
+ let s = s. as_bytes ( ) ;
32
+ let all_whitelisted = s. iter ( ) . all ( |x| whitelisted ( * x) ) ;
25
33
26
34
if !s. is_empty ( ) && all_whitelisted {
27
- // All bytes are whitelisted and valid single-byte UTF-8,
28
- // so we can build the original string and return as is.
29
- return String :: from_utf8 ( s. to_vec ( ) ) . unwrap ( ) ;
35
+ return OsString :: from_vec ( s. to_vec ( ) ) . into ( ) ;
30
36
}
31
37
32
- let mut escaped = String :: with_capacity ( s. len ( ) + 2 ) ;
33
- escaped. push ( '\'' ) ;
38
+ let mut escaped = Vec :: with_capacity ( s. len ( ) + 2 ) ;
39
+ escaped. push ( b '\'') ;
34
40
35
41
for & b in s {
36
42
match b {
37
43
b'\'' | b'!' => {
38
- escaped. push_str ( "'\\ " ) ;
39
- escaped. push ( b as char ) ;
40
- escaped. push ( '\'' ) ;
44
+ escaped. push ( b'\'' ) ;
45
+ escaped. push ( b'\\' ) ;
46
+ escaped. push ( b) ;
47
+ escaped. push ( b'\'' ) ;
41
48
}
42
- _ => escaped. push ( b as char ) ,
49
+ _ => escaped. push ( b) ,
43
50
}
44
51
}
45
- escaped. push ( '\'' ) ;
46
- escaped
52
+ escaped. push ( b '\'') ;
53
+ OsString :: from_vec ( escaped) . into ( )
47
54
}
48
55
49
56
50
57
#[ cfg( test) ]
51
58
mod tests {
52
59
use super :: * ;
53
60
61
+ fn test_escape_case ( input : & str , expected : & str ) {
62
+ let input_os_str = OsStr :: from_bytes ( input. as_bytes ( ) ) ;
63
+ let observed_os_str = escape ( input_os_str) ;
64
+ let expected_os_str = OsStr :: from_bytes ( expected. as_bytes ( ) ) ;
65
+ assert_eq ! ( observed_os_str, expected_os_str) ;
66
+ }
67
+
54
68
// These tests are courtesy of the `shell-escape` crate.
55
69
#[ test]
56
70
fn test_escape ( ) {
57
- assert_eq ! (
58
- escape ( b "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=/,.+") ,
71
+ test_escape_case (
72
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=/,.+" ,
59
73
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=/,.+"
60
74
) ;
61
- assert_eq ! (
62
- escape ( b "--aaa=bbb-ccc") ,
75
+ test_escape_case (
76
+ "--aaa=bbb-ccc" ,
63
77
"--aaa=bbb-ccc"
64
78
) ;
65
- assert_eq ! (
66
- escape ( b "linker=gcc -L/foo -Wl,bar") ,
79
+ test_escape_case (
80
+ "linker=gcc -L/foo -Wl,bar" ,
67
81
r#"'linker=gcc -L/foo -Wl,bar'"#
68
82
) ;
69
- assert_eq ! (
70
- escape ( br #"--features="default""#) ,
83
+ test_escape_case (
84
+ r #"--features="default""#,
71
85
r#"'--features="default"'"#
72
86
) ;
73
- assert_eq ! (
74
- escape ( br #"'!\$`\\\n "#) ,
87
+ test_escape_case (
88
+ r #"'!\$`\\\n "#,
75
89
r#"''\'''\!'\$`\\\n '"#
76
90
) ;
77
- assert_eq ! ( escape( b"" ) , r#"''"# ) ;
78
- assert_eq ! ( escape( b" " ) , r#"' '"# ) ;
79
- assert_eq ! ( escape( b"\xC4 b" ) , r#"'Äb'"# ) ;
91
+ test_escape_case (
92
+ "" ,
93
+ r#"''"#
94
+ ) ;
95
+ test_escape_case (
96
+ " " ,
97
+ r#"' '"#
98
+ ) ;
80
99
}
81
100
}
0 commit comments