|
4 | 4 | mod tests;
|
5 | 5 |
|
6 | 6 | use crate::borrow::Borrow;
|
| 7 | +use crate::cmp; |
7 | 8 | use crate::collections::BTreeMap;
|
8 | 9 | use crate::convert::{TryFrom, TryInto};
|
9 | 10 | use crate::env;
|
@@ -34,32 +35,76 @@ use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
|
34 | 35 | // Command
|
35 | 36 | ////////////////////////////////////////////////////////////////////////////////
|
36 | 37 |
|
37 |
| -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] |
| 38 | +#[derive(Clone, Debug, Eq)] |
38 | 39 | #[doc(hidden)]
|
39 |
| -pub struct EnvKey(OsString); |
| 40 | +pub struct EnvKey { |
| 41 | + os_string: OsString, |
| 42 | + // This stores a UTF-16 encoded string to workaround the mismatch between |
| 43 | + // Rust's OsString (WTF-8) and the Windows API string type (UTF-16). |
| 44 | + // Normally converting on every API call is acceptable but here |
| 45 | + // `c::CompareStringOrdinal` will be called for every use of `==`. |
| 46 | + utf16: Vec<u16>, |
| 47 | +} |
| 48 | + |
| 49 | +// Windows environment variables preserve their case but comparisons use |
| 50 | +// simplified case folding. So we call `CompareStringOrdinal` to get the OS to |
| 51 | +// perform the comparison. |
| 52 | +impl Ord for EnvKey { |
| 53 | + fn cmp(&self, other: &Self) -> cmp::Ordering { |
| 54 | + unsafe { |
| 55 | + let result = c::CompareStringOrdinal( |
| 56 | + self.utf16.as_ptr(), |
| 57 | + self.utf16.len() as _, |
| 58 | + other.utf16.as_ptr(), |
| 59 | + other.utf16.len() as _, |
| 60 | + c::TRUE, |
| 61 | + ); |
| 62 | + match result { |
| 63 | + c::CSTR_LESS_THAN => cmp::Ordering::Less, |
| 64 | + c::CSTR_EQUAL => cmp::Ordering::Equal, |
| 65 | + c::CSTR_GREATER_THAN => cmp::Ordering::Greater, |
| 66 | + // `CompareStringOrdinal` should never fail so long as the parameters are correct. |
| 67 | + _ => panic!("comparing environment keys failed: {}", Error::last_os_error()), |
| 68 | + } |
| 69 | + } |
| 70 | + } |
| 71 | +} |
| 72 | +impl PartialOrd for EnvKey { |
| 73 | + fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { |
| 74 | + Some(self.cmp(other)) |
| 75 | + } |
| 76 | +} |
| 77 | +impl PartialEq for EnvKey { |
| 78 | + fn eq(&self, other: &Self) -> bool { |
| 79 | + if self.utf16.len() != other.utf16.len() { |
| 80 | + false |
| 81 | + } else { |
| 82 | + self.cmp(other) == cmp::Ordering::Equal |
| 83 | + } |
| 84 | + } |
| 85 | +} |
40 | 86 |
|
41 | 87 | impl From<OsString> for EnvKey {
|
42 |
| - fn from(mut k: OsString) -> Self { |
43 |
| - k.make_ascii_uppercase(); |
44 |
| - EnvKey(k) |
| 88 | + fn from(k: OsString) -> Self { |
| 89 | + EnvKey { utf16: k.encode_wide().collect(), os_string: k } |
45 | 90 | }
|
46 | 91 | }
|
47 | 92 |
|
48 | 93 | impl From<EnvKey> for OsString {
|
49 | 94 | fn from(k: EnvKey) -> Self {
|
50 |
| - k.0 |
| 95 | + k.os_string |
51 | 96 | }
|
52 | 97 | }
|
53 | 98 |
|
54 | 99 | impl Borrow<OsStr> for EnvKey {
|
55 | 100 | fn borrow(&self) -> &OsStr {
|
56 |
| - &self.0 |
| 101 | + &self.os_string |
57 | 102 | }
|
58 | 103 | }
|
59 | 104 |
|
60 | 105 | impl AsRef<OsStr> for EnvKey {
|
61 | 106 | fn as_ref(&self) -> &OsStr {
|
62 |
| - &self.0 |
| 107 | + &self.os_string |
63 | 108 | }
|
64 | 109 | }
|
65 | 110 |
|
@@ -531,7 +576,8 @@ fn make_envp(maybe_env: Option<BTreeMap<EnvKey, OsString>>) -> io::Result<(*mut
|
531 | 576 | let mut blk = Vec::new();
|
532 | 577 |
|
533 | 578 | for (k, v) in env {
|
534 |
| - blk.extend(ensure_no_nuls(k.0)?.encode_wide()); |
| 579 | + ensure_no_nuls(k.os_string)?; |
| 580 | + blk.extend(k.utf16); |
535 | 581 | blk.push('=' as u16);
|
536 | 582 | blk.extend(ensure_no_nuls(v)?.encode_wide());
|
537 | 583 | blk.push(0);
|
|
0 commit comments