summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/windows/os
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/std/src/sys/windows/os.rs328
-rw-r--r--library/std/src/sys/windows/os/tests.rs13
-rw-r--r--library/std/src/sys/windows/os_str.rs226
3 files changed, 567 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 000000000..bcac996c0
--- /dev/null
+++ b/library/std/src/sys/windows/os.rs
@@ -0,0 +1,328 @@
+//! Implementation of `std::os` functionality for Windows.
+
+#![allow(nonstandard_style)]
+
+#[cfg(test)]
+mod tests;
+
+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 {errnum} (FormatMessageW() returned error {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.is_null() {
+ 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) -> Option<OsString> {
+ let k = to_u16s(k).ok()?;
+ super::fill_utf16_buf(
+ |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
+ |buf| OsStringExt::from_wide(buf),
+ )
+ .ok()
+}
+
+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::GetTempPath2W(sz, buf) }, super::os2path).unwrap()
+}
+
+#[cfg(not(target_vendor = "uwp"))]
+fn home_dir_crt() -> Option<PathBuf> {
+ unsafe {
+ // The magic constant -4 can be used as the token passed to GetUserProfileDirectoryW below
+ // instead of us having to go through these multiple steps to get a token. However this is
+ // not implemented on Windows 7, only Windows 8 and up. When we drop support for Windows 7
+ // we can simplify this code. See #90144 for details.
+ 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::from_raw_handle(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 }
+}
diff --git a/library/std/src/sys/windows/os/tests.rs b/library/std/src/sys/windows/os/tests.rs
new file mode 100644
index 000000000..458d6e11c
--- /dev/null
+++ b/library/std/src/sys/windows/os/tests.rs
@@ -0,0 +1,13 @@
+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")
+ );
+}
diff --git a/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs
new file mode 100644
index 000000000..11883f150
--- /dev/null
+++ b/library/std/src/sys/windows/os_str.rs
@@ -0,0 +1,226 @@
+/// The underlying OsString/OsStr implementation on Windows is a
+/// wrapper around the "WTF-8" encoding; see the `wtf8` module for more.
+use crate::borrow::Cow;
+use crate::collections::TryReserveError;
+use crate::fmt;
+use crate::mem;
+use crate::rc::Rc;
+use crate::sync::Arc;
+use crate::sys_common::wtf8::{Wtf8, Wtf8Buf};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+
+#[derive(Clone, Hash)]
+pub struct Buf {
+ pub inner: Wtf8Buf,
+}
+
+impl IntoInner<Wtf8Buf> for Buf {
+ fn into_inner(self) -> Wtf8Buf {
+ self.inner
+ }
+}
+
+impl FromInner<Wtf8Buf> for Buf {
+ fn from_inner(inner: Wtf8Buf) -> Self {
+ Buf { inner }
+ }
+}
+
+impl AsInner<Wtf8> for Buf {
+ fn as_inner(&self) -> &Wtf8 {
+ &self.inner
+ }
+}
+
+impl fmt::Debug for Buf {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(self.as_slice(), formatter)
+ }
+}
+
+impl fmt::Display for Buf {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self.as_slice(), formatter)
+ }
+}
+
+#[repr(transparent)]
+pub struct Slice {
+ pub inner: Wtf8,
+}
+
+impl fmt::Debug for Slice {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&self.inner, formatter)
+ }
+}
+
+impl fmt::Display for Slice {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(&self.inner, formatter)
+ }
+}
+
+impl Buf {
+ pub fn with_capacity(capacity: usize) -> Buf {
+ Buf { inner: Wtf8Buf::with_capacity(capacity) }
+ }
+
+ pub fn clear(&mut self) {
+ self.inner.clear()
+ }
+
+ pub fn capacity(&self) -> usize {
+ self.inner.capacity()
+ }
+
+ pub fn from_string(s: String) -> Buf {
+ Buf { inner: Wtf8Buf::from_string(s) }
+ }
+
+ pub fn as_slice(&self) -> &Slice {
+ // SAFETY: Slice is just a wrapper for Wtf8,
+ // and self.inner.as_slice() returns &Wtf8.
+ // Therefore, transmuting &Wtf8 to &Slice is safe.
+ unsafe { mem::transmute(self.inner.as_slice()) }
+ }
+
+ pub fn as_mut_slice(&mut self) -> &mut Slice {
+ // SAFETY: Slice is just a wrapper for Wtf8,
+ // and self.inner.as_mut_slice() returns &mut Wtf8.
+ // Therefore, transmuting &mut Wtf8 to &mut Slice is safe.
+ // Additionally, care should be taken to ensure the slice
+ // is always valid Wtf8.
+ unsafe { mem::transmute(self.inner.as_mut_slice()) }
+ }
+
+ pub fn into_string(self) -> Result<String, Buf> {
+ self.inner.into_string().map_err(|buf| Buf { inner: buf })
+ }
+
+ pub fn push_slice(&mut self, s: &Slice) {
+ self.inner.push_wtf8(&s.inner)
+ }
+
+ pub fn reserve(&mut self, additional: usize) {
+ self.inner.reserve(additional)
+ }
+
+ pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
+ self.inner.try_reserve(additional)
+ }
+
+ pub fn reserve_exact(&mut self, additional: usize) {
+ self.inner.reserve_exact(additional)
+ }
+
+ pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
+ self.inner.try_reserve_exact(additional)
+ }
+
+ pub fn shrink_to_fit(&mut self) {
+ self.inner.shrink_to_fit()
+ }
+
+ #[inline]
+ pub fn shrink_to(&mut self, min_capacity: usize) {
+ self.inner.shrink_to(min_capacity)
+ }
+
+ #[inline]
+ pub fn into_box(self) -> Box<Slice> {
+ unsafe { mem::transmute(self.inner.into_box()) }
+ }
+
+ #[inline]
+ pub fn from_box(boxed: Box<Slice>) -> Buf {
+ let inner: Box<Wtf8> = unsafe { mem::transmute(boxed) };
+ Buf { inner: Wtf8Buf::from_box(inner) }
+ }
+
+ #[inline]
+ pub fn into_arc(&self) -> Arc<Slice> {
+ self.as_slice().into_arc()
+ }
+
+ #[inline]
+ pub fn into_rc(&self) -> Rc<Slice> {
+ self.as_slice().into_rc()
+ }
+}
+
+impl Slice {
+ #[inline]
+ pub fn from_str(s: &str) -> &Slice {
+ unsafe { mem::transmute(Wtf8::from_str(s)) }
+ }
+
+ pub fn to_str(&self) -> Option<&str> {
+ self.inner.as_str()
+ }
+
+ pub fn to_string_lossy(&self) -> Cow<'_, str> {
+ self.inner.to_string_lossy()
+ }
+
+ pub fn to_owned(&self) -> Buf {
+ let mut buf = Wtf8Buf::with_capacity(self.inner.len());
+ buf.push_wtf8(&self.inner);
+ Buf { inner: buf }
+ }
+
+ pub fn clone_into(&self, buf: &mut Buf) {
+ self.inner.clone_into(&mut buf.inner)
+ }
+
+ #[inline]
+ pub fn into_box(&self) -> Box<Slice> {
+ unsafe { mem::transmute(self.inner.into_box()) }
+ }
+
+ pub fn empty_box() -> Box<Slice> {
+ unsafe { mem::transmute(Wtf8::empty_box()) }
+ }
+
+ #[inline]
+ pub fn into_arc(&self) -> Arc<Slice> {
+ let arc = self.inner.into_arc();
+ unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) }
+ }
+
+ #[inline]
+ pub fn into_rc(&self) -> Rc<Slice> {
+ let rc = self.inner.into_rc();
+ unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) }
+ }
+
+ #[inline]
+ pub fn make_ascii_lowercase(&mut self) {
+ self.inner.make_ascii_lowercase()
+ }
+
+ #[inline]
+ pub fn make_ascii_uppercase(&mut self) {
+ self.inner.make_ascii_uppercase()
+ }
+
+ #[inline]
+ pub fn to_ascii_lowercase(&self) -> Buf {
+ Buf { inner: self.inner.to_ascii_lowercase() }
+ }
+
+ #[inline]
+ pub fn to_ascii_uppercase(&self) -> Buf {
+ Buf { inner: self.inner.to_ascii_uppercase() }
+ }
+
+ #[inline]
+ pub fn is_ascii(&self) -> bool {
+ self.inner.is_ascii()
+ }
+
+ #[inline]
+ pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
+ self.inner.eq_ignore_ascii_case(&other.inner)
+ }
+}