std\sys\env/
windows.rs

1use crate::ffi::{OsStr, OsString};
2use crate::os::windows::prelude::*;
3use crate::sys::pal::{c, cvt, fill_utf16_buf, to_u16s};
4use crate::{fmt, io, ptr, slice};
5
6pub struct Env {
7    base: *mut c::WCHAR,
8    iter: EnvIterator,
9}
10
11// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
12pub struct EnvStrDebug<'a> {
13    iter: &'a EnvIterator,
14}
15
16impl fmt::Debug for EnvStrDebug<'_> {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        let Self { iter } = self;
19        let iter: EnvIterator = (*iter).clone();
20        let mut list = f.debug_list();
21        for (a, b) in iter {
22            list.entry(&(a.to_str().unwrap(), b.to_str().unwrap()));
23        }
24        list.finish()
25    }
26}
27
28impl Env {
29    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
30        let Self { base: _, iter } = self;
31        EnvStrDebug { iter }
32    }
33}
34
35impl fmt::Debug for Env {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        let Self { base: _, iter } = self;
38        f.debug_list().entries(iter.clone()).finish()
39    }
40}
41
42impl Iterator for Env {
43    type Item = (OsString, OsString);
44
45    fn next(&mut self) -> Option<(OsString, OsString)> {
46        let Self { base: _, iter } = self;
47        iter.next()
48    }
49}
50
51#[derive(Clone)]
52struct EnvIterator(*mut c::WCHAR);
53
54impl Iterator for EnvIterator {
55    type Item = (OsString, OsString);
56
57    fn next(&mut self) -> Option<(OsString, OsString)> {
58        let Self(cur) = self;
59        loop {
60            unsafe {
61                if **cur == 0 {
62                    return None;
63                }
64                let p = *cur as *const u16;
65                let mut len = 0;
66                while *p.add(len) != 0 {
67                    len += 1;
68                }
69                let s = slice::from_raw_parts(p, len);
70                *cur = cur.add(len + 1);
71
72                // Windows allows environment variables to start with an equals
73                // symbol (in any other position, this is the separator between
74                // variable name and value). Since`s` has at least length 1 at
75                // this point (because the empty string terminates the array of
76                // environment variables), we can safely slice.
77                let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) {
78                    Some(p) => p,
79                    None => continue,
80                };
81                return Some((
82                    OsStringExt::from_wide(&s[..pos]),
83                    OsStringExt::from_wide(&s[pos + 1..]),
84                ));
85            }
86        }
87    }
88}
89
90impl Drop for Env {
91    fn drop(&mut self) {
92        unsafe {
93            c::FreeEnvironmentStringsW(self.base);
94        }
95    }
96}
97
98pub fn env() -> Env {
99    unsafe {
100        let ch = c::GetEnvironmentStringsW();
101        if ch.is_null() {
102            panic!("failure getting env string from OS: {}", io::Error::last_os_error());
103        }
104        Env { base: ch, iter: EnvIterator(ch) }
105    }
106}
107
108pub fn getenv(k: &OsStr) -> Option<OsString> {
109    let k = to_u16s(k).ok()?;
110    fill_utf16_buf(
111        |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
112        OsStringExt::from_wide,
113    )
114    .ok()
115}
116
117pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
118    // SAFETY: We ensure that k and v are null-terminated wide strings.
119    unsafe {
120        let k = to_u16s(k)?;
121        let v = to_u16s(v)?;
122
123        cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop)
124    }
125}
126
127pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
128    // SAFETY: We ensure that v is a null-terminated wide strings.
129    unsafe {
130        let v = to_u16s(n)?;
131        cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop)
132    }
133}