std\sys\pal\windows/
os.rs1#![allow(nonstandard_style)]
4
5#[cfg(test)]
6mod tests;
7
8use super::api;
9#[cfg(not(target_vendor = "uwp"))]
10use super::api::WinError;
11use crate::ffi::{OsStr, OsString};
12use crate::os::windows::ffi::EncodeWide;
13use crate::os::windows::prelude::*;
14use crate::path::{self, PathBuf};
15use crate::sys::pal::{c, cvt};
16use crate::{fmt, io, ptr};
17
18pub fn errno() -> i32 {
19 api::get_last_error().code as i32
20}
21
22pub fn error_string(mut errnum: i32) -> String {
24 let mut buf = [0 as c::WCHAR; 2048];
25
26 unsafe {
27 let mut module = ptr::null_mut();
28 let mut flags = 0;
29
30 if (errnum & c::FACILITY_NT_BIT as i32) != 0 {
34 const NTDLL_DLL: &[u16] = &[
36 'N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, '.' as _, 'D' as _, 'L' as _,
37 'L' as _, 0,
38 ];
39 module = c::GetModuleHandleW(NTDLL_DLL.as_ptr());
40
41 if !module.is_null() {
42 errnum ^= c::FACILITY_NT_BIT as i32;
43 flags = c::FORMAT_MESSAGE_FROM_HMODULE;
44 }
45 }
46
47 let res = c::FormatMessageW(
48 flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS,
49 module,
50 errnum as u32,
51 0,
52 buf.as_mut_ptr(),
53 buf.len() as u32,
54 ptr::null(),
55 ) as usize;
56 if res == 0 {
57 let fm_err = errno();
59 return format!("OS Error {errnum} (FormatMessageW() returned error {fm_err})");
60 }
61
62 match String::from_utf16(&buf[..res]) {
63 Ok(mut msg) => {
64 let len = msg.trim_end().len();
66 msg.truncate(len);
67 msg
68 }
69 Err(..) => format!(
70 "OS Error {} (FormatMessageW() returned \
71 invalid UTF-16)",
72 errnum
73 ),
74 }
75 }
76}
77
78pub struct SplitPaths<'a> {
79 data: EncodeWide<'a>,
80 must_yield: bool,
81}
82
83pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
84 SplitPaths { data: unparsed.encode_wide(), must_yield: true }
85}
86
87impl<'a> Iterator for SplitPaths<'a> {
88 type Item = PathBuf;
89 fn next(&mut self) -> Option<PathBuf> {
90 let must_yield = self.must_yield;
104 self.must_yield = false;
105
106 let mut in_progress = Vec::new();
107 let mut in_quote = false;
108 for b in self.data.by_ref() {
109 if b == '"' as u16 {
110 in_quote = !in_quote;
111 } else if b == ';' as u16 && !in_quote {
112 self.must_yield = true;
113 break;
114 } else {
115 in_progress.push(b)
116 }
117 }
118
119 if !must_yield && in_progress.is_empty() {
120 None
121 } else {
122 Some(super::os2path(&in_progress))
123 }
124 }
125}
126
127#[derive(Debug)]
128pub struct JoinPathsError;
129
130pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
131where
132 I: Iterator<Item = T>,
133 T: AsRef<OsStr>,
134{
135 let mut joined = Vec::new();
136 let sep = b';' as u16;
137
138 for (i, path) in paths.enumerate() {
139 let path = path.as_ref();
140 if i > 0 {
141 joined.push(sep)
142 }
143 let v = path.encode_wide().collect::<Vec<u16>>();
144 if v.contains(&(b'"' as u16)) {
145 return Err(JoinPathsError);
146 } else if v.contains(&sep) {
147 joined.push(b'"' as u16);
148 joined.extend_from_slice(&v[..]);
149 joined.push(b'"' as u16);
150 } else {
151 joined.extend_from_slice(&v[..]);
152 }
153 }
154
155 Ok(OsStringExt::from_wide(&joined[..]))
156}
157
158impl fmt::Display for JoinPathsError {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160 "path segment contains `\"`".fmt(f)
161 }
162}
163
164impl crate::error::Error for JoinPathsError {}
165
166pub fn current_exe() -> io::Result<PathBuf> {
167 super::fill_utf16_buf(
168 |buf, sz| unsafe { c::GetModuleFileNameW(ptr::null_mut(), buf, sz) },
169 super::os2path,
170 )
171}
172
173pub fn getcwd() -> io::Result<PathBuf> {
174 super::fill_utf16_buf(|buf, sz| unsafe { c::GetCurrentDirectoryW(sz, buf) }, super::os2path)
175}
176
177pub fn chdir(p: &path::Path) -> io::Result<()> {
178 let p: &OsStr = p.as_ref();
179 let mut p = p.encode_wide().collect::<Vec<_>>();
180 p.push(0);
181
182 cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop)
183}
184
185pub fn temp_dir() -> PathBuf {
186 super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap()
187}
188
189#[cfg(all(not(target_vendor = "uwp"), not(target_vendor = "win7")))]
190fn home_dir_crt() -> Option<PathBuf> {
191 unsafe {
192 const CURRENT_PROCESS_TOKEN: usize = -4_isize as usize;
194
195 super::fill_utf16_buf(
196 |buf, mut sz| {
197 match c::GetUserProfileDirectoryW(
200 ptr::without_provenance_mut(CURRENT_PROCESS_TOKEN),
201 buf,
202 &mut sz,
203 ) {
204 0 if api::get_last_error() != WinError::INSUFFICIENT_BUFFER => 0,
205 0 => sz,
206 _ => sz - 1, }
208 },
209 super::os2path,
210 )
211 .ok()
212 }
213}
214
215#[cfg(target_vendor = "win7")]
216fn home_dir_crt() -> Option<PathBuf> {
217 unsafe {
218 use crate::sys::handle::Handle;
219
220 let me = c::GetCurrentProcess();
221 let mut token = ptr::null_mut();
222 if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
223 return None;
224 }
225 let _handle = Handle::from_raw_handle(token);
226 super::fill_utf16_buf(
227 |buf, mut sz| {
228 match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
229 0 if api::get_last_error() != WinError::INSUFFICIENT_BUFFER => 0,
230 0 => sz,
231 _ => sz - 1, }
233 },
234 super::os2path,
235 )
236 .ok()
237 }
238}
239
240#[cfg(target_vendor = "uwp")]
241fn home_dir_crt() -> Option<PathBuf> {
242 None
243}
244
245pub fn home_dir() -> Option<PathBuf> {
246 crate::env::var_os("USERPROFILE")
247 .filter(|s| !s.is_empty())
248 .map(PathBuf::from)
249 .or_else(home_dir_crt)
250}
251
252pub fn exit(code: i32) -> ! {
253 unsafe { c::ExitProcess(code as u32) }
254}
255
256pub fn getpid() -> u32 {
257 unsafe { c::GetCurrentProcessId() }
258}