blob: 3c4d4a84cfd6b83f2c5c00b81d6597269f6dc24d [file] [log] [blame]
use crate::ffi::{OsStr, OsString};
use crate::os::windows::prelude::*;
use crate::sys::pal::{c, cvt, fill_utf16_buf, to_u16s};
use crate::{fmt, io, ptr, slice};
pub struct Env {
base: *mut c::WCHAR,
iter: EnvIterator,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
iter: &'a EnvIterator,
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
let iter: EnvIterator = (*iter).clone();
let mut list = f.debug_list();
for (a, b) in iter {
list.entry(&(a.to_str().unwrap(), b.to_str().unwrap()));
}
list.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { base: _, iter } = self;
EnvStrDebug { iter }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { base: _, iter } = self;
f.debug_list().entries(iter.clone()).finish()
}
}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
let Self { base: _, iter } = self;
iter.next()
}
}
#[derive(Clone)]
struct EnvIterator(*mut c::WCHAR);
impl Iterator for EnvIterator {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
let Self(cur) = self;
loop {
unsafe {
if **cur == 0 {
return None;
}
let p = *cur as *const u16;
let mut len = 0;
while *p.add(len) != 0 {
len += 1;
}
let s = slice::from_raw_parts(p, len);
*cur = cur.add(len + 1);
// Windows allows environment variables to start with an equals
// symbol (in any other position, this is the separator between
// variable name and value). Since`s` has at least length 1 at
// this point (because the empty string terminates the array of
// environment variables), we can safely slice.
let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) {
Some(p) => p,
None => continue,
};
return Some((
OsStringExt::from_wide(&s[..pos]),
OsStringExt::from_wide(&s[pos + 1..]),
));
}
}
}
}
impl Drop for Env {
fn drop(&mut self) {
unsafe {
c::FreeEnvironmentStringsW(self.base);
}
}
}
pub fn env() -> Env {
unsafe {
let ch = c::GetEnvironmentStringsW();
if ch.is_null() {
panic!("failure getting env string from OS: {}", io::Error::last_os_error());
}
Env { base: ch, iter: EnvIterator(ch) }
}
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
let k = to_u16s(k).ok()?;
fill_utf16_buf(
|buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
OsStringExt::from_wide,
)
.ok()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
// SAFETY: We ensure that k and v are null-terminated wide strings.
unsafe {
let k = to_u16s(k)?;
let v = to_u16s(v)?;
cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop)
}
}
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
// SAFETY: We ensure that v is a null-terminated wide strings.
unsafe {
let v = to_u16s(n)?;
cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop)
}
}