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