about summary refs log tree commit diff
path: root/library/std/src/sys/windows/os.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/windows/os.rs')
-rw-r--r--library/std/src/sys/windows/os.rs347
1 files changed, 347 insertions, 0 deletions
diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs
new file mode 100644
index 00000000000..a0da2498bb7
--- /dev/null
+++ b/library/std/src/sys/windows/os.rs
@@ -0,0 +1,347 @@
+//! Implementation of `std::os` functionality for Windows.
+
+#![allow(nonstandard_style)]
+
+use crate::os::windows::prelude::*;
+
+use crate::error::Error as StdError;
+use crate::ffi::{OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::os::windows::ffi::EncodeWide;
+use crate::path::{self, PathBuf};
+use crate::ptr;
+use crate::slice;
+use crate::sys::{c, cvt};
+
+use super::to_u16s;
+
+pub fn errno() -> i32 {
+    unsafe { c::GetLastError() as i32 }
+}
+
+/// Gets a detailed string description for the given error number.
+pub fn error_string(mut errnum: i32) -> String {
+    // This value is calculated from the macro
+    // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
+    let langId = 0x0800 as c::DWORD;
+
+    let mut buf = [0 as c::WCHAR; 2048];
+
+    unsafe {
+        let mut module = ptr::null_mut();
+        let mut flags = 0;
+
+        // NTSTATUS errors may be encoded as HRESULT, which may returned from
+        // GetLastError. For more information about Windows error codes, see
+        // `[MS-ERREF]`: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a
+        if (errnum & c::FACILITY_NT_BIT as i32) != 0 {
+            // format according to https://support.microsoft.com/en-us/help/259693
+            const NTDLL_DLL: &[u16] = &[
+                'N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, '.' as _, 'D' as _, 'L' as _,
+                'L' as _, 0,
+            ];
+            module = c::GetModuleHandleW(NTDLL_DLL.as_ptr());
+
+            if !module.is_null() {
+                errnum ^= c::FACILITY_NT_BIT as i32;
+                flags = c::FORMAT_MESSAGE_FROM_HMODULE;
+            }
+        }
+
+        let res = c::FormatMessageW(
+            flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS,
+            module,
+            errnum as c::DWORD,
+            langId,
+            buf.as_mut_ptr(),
+            buf.len() as c::DWORD,
+            ptr::null(),
+        ) as usize;
+        if res == 0 {
+            // Sometimes FormatMessageW can fail e.g., system doesn't like langId,
+            let fm_err = errno();
+            return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err);
+        }
+
+        match String::from_utf16(&buf[..res]) {
+            Ok(mut msg) => {
+                // Trim trailing CRLF inserted by FormatMessageW
+                let len = msg.trim_end().len();
+                msg.truncate(len);
+                msg
+            }
+            Err(..) => format!(
+                "OS Error {} (FormatMessageW() returned \
+                 invalid UTF-16)",
+                errnum
+            ),
+        }
+    }
+}
+
+pub struct Env {
+    base: c::LPWCH,
+    cur: c::LPWCH,
+}
+
+impl Iterator for Env {
+    type Item = (OsString, OsString);
+
+    fn next(&mut self) -> Option<(OsString, OsString)> {
+        loop {
+            unsafe {
+                if *self.cur == 0 {
+                    return None;
+                }
+                let p = self.cur as *const u16;
+                let mut len = 0;
+                while *p.offset(len) != 0 {
+                    len += 1;
+                }
+                let s = slice::from_raw_parts(p, len as usize);
+                self.cur = self.cur.offset(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 as usize == 0 {
+            panic!("failure getting env string from OS: {}", io::Error::last_os_error());
+        }
+        Env { base: ch, cur: ch }
+    }
+}
+
+pub struct SplitPaths<'a> {
+    data: EncodeWide<'a>,
+    must_yield: bool,
+}
+
+pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
+    SplitPaths { data: unparsed.encode_wide(), must_yield: true }
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+    type Item = PathBuf;
+    fn next(&mut self) -> Option<PathBuf> {
+        // On Windows, the PATH environment variable is semicolon separated.
+        // Double quotes are used as a way of introducing literal semicolons
+        // (since c:\some;dir is a valid Windows path). Double quotes are not
+        // themselves permitted in path names, so there is no way to escape a
+        // double quote.  Quoted regions can appear in arbitrary locations, so
+        //
+        //   c:\foo;c:\som"e;di"r;c:\bar
+        //
+        // Should parse as [c:\foo, c:\some;dir, c:\bar].
+        //
+        // (The above is based on testing; there is no clear reference available
+        // for the grammar.)
+
+        let must_yield = self.must_yield;
+        self.must_yield = false;
+
+        let mut in_progress = Vec::new();
+        let mut in_quote = false;
+        for b in self.data.by_ref() {
+            if b == '"' as u16 {
+                in_quote = !in_quote;
+            } else if b == ';' as u16 && !in_quote {
+                self.must_yield = true;
+                break;
+            } else {
+                in_progress.push(b)
+            }
+        }
+
+        if !must_yield && in_progress.is_empty() {
+            None
+        } else {
+            Some(super::os2path(&in_progress))
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct JoinPathsError;
+
+pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
+where
+    I: Iterator<Item = T>,
+    T: AsRef<OsStr>,
+{
+    let mut joined = Vec::new();
+    let sep = b';' as u16;
+
+    for (i, path) in paths.enumerate() {
+        let path = path.as_ref();
+        if i > 0 {
+            joined.push(sep)
+        }
+        let v = path.encode_wide().collect::<Vec<u16>>();
+        if v.contains(&(b'"' as u16)) {
+            return Err(JoinPathsError);
+        } else if v.contains(&sep) {
+            joined.push(b'"' as u16);
+            joined.extend_from_slice(&v[..]);
+            joined.push(b'"' as u16);
+        } else {
+            joined.extend_from_slice(&v[..]);
+        }
+    }
+
+    Ok(OsStringExt::from_wide(&joined[..]))
+}
+
+impl fmt::Display for JoinPathsError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        "path segment contains `\"`".fmt(f)
+    }
+}
+
+impl StdError for JoinPathsError {
+    #[allow(deprecated)]
+    fn description(&self) -> &str {
+        "failed to join paths"
+    }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+    super::fill_utf16_buf(
+        |buf, sz| unsafe { c::GetModuleFileNameW(ptr::null_mut(), buf, sz) },
+        super::os2path,
+    )
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+    super::fill_utf16_buf(|buf, sz| unsafe { c::GetCurrentDirectoryW(sz, buf) }, super::os2path)
+}
+
+pub fn chdir(p: &path::Path) -> io::Result<()> {
+    let p: &OsStr = p.as_ref();
+    let mut p = p.encode_wide().collect::<Vec<_>>();
+    p.push(0);
+
+    cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop)
+}
+
+pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
+    let k = to_u16s(k)?;
+    let res = super::fill_utf16_buf(
+        |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
+        |buf| OsStringExt::from_wide(buf),
+    );
+    match res {
+        Ok(value) => Ok(Some(value)),
+        Err(e) => {
+            if e.raw_os_error() == Some(c::ERROR_ENVVAR_NOT_FOUND as i32) {
+                Ok(None)
+            } else {
+                Err(e)
+            }
+        }
+    }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    let k = to_u16s(k)?;
+    let v = to_u16s(v)?;
+
+    cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }).map(drop)
+}
+
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+    let v = to_u16s(n)?;
+    cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop)
+}
+
+pub fn temp_dir() -> PathBuf {
+    super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPathW(sz, buf) }, super::os2path).unwrap()
+}
+
+#[cfg(not(target_vendor = "uwp"))]
+fn home_dir_crt() -> Option<PathBuf> {
+    unsafe {
+        use crate::sys::handle::Handle;
+
+        let me = c::GetCurrentProcess();
+        let mut token = ptr::null_mut();
+        if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
+            return None;
+        }
+        let _handle = Handle::new(token);
+        super::fill_utf16_buf(
+            |buf, mut sz| {
+                match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
+                    0 if c::GetLastError() != c::ERROR_INSUFFICIENT_BUFFER => 0,
+                    0 => sz,
+                    _ => sz - 1, // sz includes the null terminator
+                }
+            },
+            super::os2path,
+        )
+        .ok()
+    }
+}
+
+#[cfg(target_vendor = "uwp")]
+fn home_dir_crt() -> Option<PathBuf> {
+    None
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+    crate::env::var_os("HOME")
+        .or_else(|| crate::env::var_os("USERPROFILE"))
+        .map(PathBuf::from)
+        .or_else(|| home_dir_crt())
+}
+
+pub fn exit(code: i32) -> ! {
+    unsafe { c::ExitProcess(code as c::UINT) }
+}
+
+pub fn getpid() -> u32 {
+    unsafe { c::GetCurrentProcessId() as u32 }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::io::Error;
+    use crate::sys::c;
+
+    // tests `error_string` above
+    #[test]
+    fn ntstatus_error() {
+        const STATUS_UNSUCCESSFUL: u32 = 0xc000_0001;
+        assert!(
+            !Error::from_raw_os_error((STATUS_UNSUCCESSFUL | c::FACILITY_NT_BIT) as _)
+                .to_string()
+                .contains("FormatMessageW() returned error")
+        );
+    }
+}