mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-26 14:43:24 +00:00
Windows Command
environment variables are case-preserving
But comparing is case-insensitive.
This commit is contained in:
parent
f94942d842
commit
8345538fec
@ -68,6 +68,10 @@ pub type ADDRESS_FAMILY = USHORT;
|
||||
pub const TRUE: BOOL = 1;
|
||||
pub const FALSE: BOOL = 0;
|
||||
|
||||
pub const CSTR_LESS_THAN: c_int = 1;
|
||||
pub const CSTR_EQUAL: c_int = 2;
|
||||
pub const CSTR_GREATER_THAN: c_int = 3;
|
||||
|
||||
pub const FILE_ATTRIBUTE_READONLY: DWORD = 0x1;
|
||||
pub const FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10;
|
||||
pub const FILE_ATTRIBUTE_REPARSE_POINT: DWORD = 0x400;
|
||||
@ -1072,6 +1076,14 @@ extern "system" {
|
||||
pub fn ReleaseSRWLockShared(SRWLock: PSRWLOCK);
|
||||
pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN;
|
||||
pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN;
|
||||
|
||||
pub fn CompareStringOrdinal(
|
||||
lpString1: LPCWSTR,
|
||||
cchCount1: c_int,
|
||||
lpString2: LPCWSTR,
|
||||
cchCount2: c_int,
|
||||
bIgnoreCase: BOOL,
|
||||
) -> c_int;
|
||||
}
|
||||
|
||||
// Functions that aren't available on every version of Windows that we support,
|
||||
|
@ -4,6 +4,7 @@
|
||||
mod tests;
|
||||
|
||||
use crate::borrow::Borrow;
|
||||
use crate::cmp;
|
||||
use crate::collections::BTreeMap;
|
||||
use crate::convert::{TryFrom, TryInto};
|
||||
use crate::env;
|
||||
@ -34,32 +35,76 @@ use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
|
||||
// Command
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[derive(Clone, Debug, Eq)]
|
||||
#[doc(hidden)]
|
||||
pub struct EnvKey(OsString);
|
||||
pub struct EnvKey {
|
||||
os_string: OsString,
|
||||
// This stores a UTF-16 encoded string to workaround the mismatch between
|
||||
// Rust's OsString (WTF-8) and the Windows API string type (UTF-16).
|
||||
// Normally converting on every API call is acceptable but here
|
||||
// `c::CompareStringOrdinal` will be called for every use of `==`.
|
||||
utf16: Vec<u16>,
|
||||
}
|
||||
|
||||
// Windows environment variables preserve their case but comparisons use
|
||||
// simplified case folding. So we call `CompareStringOrdinal` to get the OS to
|
||||
// perform the comparison.
|
||||
impl Ord for EnvKey {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
unsafe {
|
||||
let result = c::CompareStringOrdinal(
|
||||
self.utf16.as_ptr(),
|
||||
self.utf16.len() as _,
|
||||
other.utf16.as_ptr(),
|
||||
other.utf16.len() as _,
|
||||
c::TRUE,
|
||||
);
|
||||
match result {
|
||||
c::CSTR_LESS_THAN => cmp::Ordering::Less,
|
||||
c::CSTR_EQUAL => cmp::Ordering::Equal,
|
||||
c::CSTR_GREATER_THAN => cmp::Ordering::Greater,
|
||||
// `CompareStringOrdinal` should never fail so long as the parameters are correct.
|
||||
_ => panic!("comparing environment keys failed: {}", Error::last_os_error()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl PartialOrd for EnvKey {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl PartialEq for EnvKey {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.utf16.len() != other.utf16.len() {
|
||||
false
|
||||
} else {
|
||||
self.cmp(other) == cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OsString> for EnvKey {
|
||||
fn from(mut k: OsString) -> Self {
|
||||
k.make_ascii_uppercase();
|
||||
EnvKey(k)
|
||||
fn from(k: OsString) -> Self {
|
||||
EnvKey { utf16: k.encode_wide().collect(), os_string: k }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EnvKey> for OsString {
|
||||
fn from(k: EnvKey) -> Self {
|
||||
k.0
|
||||
k.os_string
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<OsStr> for EnvKey {
|
||||
fn borrow(&self) -> &OsStr {
|
||||
&self.0
|
||||
&self.os_string
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<OsStr> for EnvKey {
|
||||
fn as_ref(&self) -> &OsStr {
|
||||
&self.0
|
||||
&self.os_string
|
||||
}
|
||||
}
|
||||
|
||||
@ -531,7 +576,8 @@ fn make_envp(maybe_env: Option<BTreeMap<EnvKey, OsString>>) -> io::Result<(*mut
|
||||
let mut blk = Vec::new();
|
||||
|
||||
for (k, v) in env {
|
||||
blk.extend(ensure_no_nuls(k.0)?.encode_wide());
|
||||
ensure_no_nuls(k.os_string)?;
|
||||
blk.extend(k.utf16);
|
||||
blk.push('=' as u16);
|
||||
blk.extend(ensure_no_nuls(v)?.encode_wide());
|
||||
blk.push(0);
|
||||
|
@ -1,5 +1,7 @@
|
||||
use super::make_command_line;
|
||||
use crate::env;
|
||||
use crate::ffi::{OsStr, OsString};
|
||||
use crate::process::Command;
|
||||
|
||||
#[test]
|
||||
fn test_make_command_line() {
|
||||
@ -41,3 +43,62 @@ fn test_make_command_line() {
|
||||
"\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\""
|
||||
);
|
||||
}
|
||||
|
||||
// On Windows, environment args are case preserving but comparisons are case-insensitive.
|
||||
// See: #85242
|
||||
#[test]
|
||||
fn windows_env_unicode_case() {
|
||||
let test_cases = [
|
||||
("ä", "Ä"),
|
||||
("ß", "SS"),
|
||||
("Ä", "Ö"),
|
||||
("Ä", "Ö"),
|
||||
("I", "İ"),
|
||||
("I", "i"),
|
||||
("I", "ı"),
|
||||
("i", "I"),
|
||||
("i", "İ"),
|
||||
("i", "ı"),
|
||||
("İ", "I"),
|
||||
("İ", "i"),
|
||||
("İ", "ı"),
|
||||
("ı", "I"),
|
||||
("ı", "i"),
|
||||
("ı", "İ"),
|
||||
("ä", "Ä"),
|
||||
("ß", "SS"),
|
||||
("Ä", "Ö"),
|
||||
("Ä", "Ö"),
|
||||
("I", "İ"),
|
||||
("I", "i"),
|
||||
("I", "ı"),
|
||||
("i", "I"),
|
||||
("i", "İ"),
|
||||
("i", "ı"),
|
||||
("İ", "I"),
|
||||
("İ", "i"),
|
||||
("İ", "ı"),
|
||||
("ı", "I"),
|
||||
("ı", "i"),
|
||||
("ı", "İ"),
|
||||
];
|
||||
// Test that `cmd.env` matches `env::set_var` when setting two strings that
|
||||
// may (or may not) be case-folded when compared.
|
||||
for (a, b) in test_cases.iter() {
|
||||
let mut cmd = Command::new("cmd");
|
||||
cmd.env(a, "1");
|
||||
cmd.env(b, "2");
|
||||
env::set_var(a, "1");
|
||||
env::set_var(b, "2");
|
||||
|
||||
for (key, value) in cmd.get_envs() {
|
||||
assert_eq!(
|
||||
env::var(key).ok(),
|
||||
value.map(|s| s.to_string_lossy().into_owned()),
|
||||
"command environment mismatch: {} {}",
|
||||
a,
|
||||
b
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user