1#![allow(nonstandard_style)]
4
5#[cfg(test)]
6mod tests;
7
8#[cfg(not(target_vendor = "uwp"))]
9use super::api::WinError;
10use super::{api, to_u16s};
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::{c, cvt};
17use crate::{fmt, io, ptr, slice};
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 Env {
80 base: *mut c::WCHAR,
81 iter: EnvIterator,
82}
83
84pub struct EnvStrDebug<'a> {
86 iter: &'a EnvIterator,
87}
88
89impl fmt::Debug for EnvStrDebug<'_> {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 let Self { iter } = self;
92 let iter: EnvIterator = (*iter).clone();
93 let mut list = f.debug_list();
94 for (a, b) in iter {
95 list.entry(&(a.to_str().unwrap(), b.to_str().unwrap()));
96 }
97 list.finish()
98 }
99}
100
101impl Env {
102 pub fn str_debug(&self) -> impl fmt::Debug + '_ {
103 let Self { base: _, iter } = self;
104 EnvStrDebug { iter }
105 }
106}
107
108impl fmt::Debug for Env {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 let Self { base: _, iter } = self;
111 f.debug_list().entries(iter.clone()).finish()
112 }
113}
114
115impl Iterator for Env {
116 type Item = (OsString, OsString);
117
118 fn next(&mut self) -> Option<(OsString, OsString)> {
119 let Self { base: _, iter } = self;
120 iter.next()
121 }
122}
123
124#[derive(Clone)]
125struct EnvIterator(*mut c::WCHAR);
126
127impl Iterator for EnvIterator {
128 type Item = (OsString, OsString);
129
130 fn next(&mut self) -> Option<(OsString, OsString)> {
131 let Self(cur) = self;
132 loop {
133 unsafe {
134 if **cur == 0 {
135 return None;
136 }
137 let p = *cur as *const u16;
138 let mut len = 0;
139 while *p.add(len) != 0 {
140 len += 1;
141 }
142 let s = slice::from_raw_parts(p, len);
143 *cur = cur.add(len + 1);
144
145 let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) {
151 Some(p) => p,
152 None => continue,
153 };
154 return Some((
155 OsStringExt::from_wide(&s[..pos]),
156 OsStringExt::from_wide(&s[pos + 1..]),
157 ));
158 }
159 }
160 }
161}
162
163impl Drop for Env {
164 fn drop(&mut self) {
165 unsafe {
166 c::FreeEnvironmentStringsW(self.base);
167 }
168 }
169}
170
171pub fn env() -> Env {
172 unsafe {
173 let ch = c::GetEnvironmentStringsW();
174 if ch.is_null() {
175 panic!("failure getting env string from OS: {}", io::Error::last_os_error());
176 }
177 Env { base: ch, iter: EnvIterator(ch) }
178 }
179}
180
181pub struct SplitPaths<'a> {
182 data: EncodeWide<'a>,
183 must_yield: bool,
184}
185
186pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
187 SplitPaths { data: unparsed.encode_wide(), must_yield: true }
188}
189
190impl<'a> Iterator for SplitPaths<'a> {
191 type Item = PathBuf;
192 fn next(&mut self) -> Option<PathBuf> {
193 let must_yield = self.must_yield;
207 self.must_yield = false;
208
209 let mut in_progress = Vec::new();
210 let mut in_quote = false;
211 for b in self.data.by_ref() {
212 if b == '"' as u16 {
213 in_quote = !in_quote;
214 } else if b == ';' as u16 && !in_quote {
215 self.must_yield = true;
216 break;
217 } else {
218 in_progress.push(b)
219 }
220 }
221
222 if !must_yield && in_progress.is_empty() {
223 None
224 } else {
225 Some(super::os2path(&in_progress))
226 }
227 }
228}
229
230#[derive(Debug)]
231pub struct JoinPathsError;
232
233pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
234where
235 I: Iterator<Item = T>,
236 T: AsRef<OsStr>,
237{
238 let mut joined = Vec::new();
239 let sep = b';' as u16;
240
241 for (i, path) in paths.enumerate() {
242 let path = path.as_ref();
243 if i > 0 {
244 joined.push(sep)
245 }
246 let v = path.encode_wide().collect::<Vec<u16>>();
247 if v.contains(&(b'"' as u16)) {
248 return Err(JoinPathsError);
249 } else if v.contains(&sep) {
250 joined.push(b'"' as u16);
251 joined.extend_from_slice(&v[..]);
252 joined.push(b'"' as u16);
253 } else {
254 joined.extend_from_slice(&v[..]);
255 }
256 }
257
258 Ok(OsStringExt::from_wide(&joined[..]))
259}
260
261impl fmt::Display for JoinPathsError {
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 "path segment contains `\"`".fmt(f)
264 }
265}
266
267impl StdError for JoinPathsError {
268 #[allow(deprecated)]
269 fn description(&self) -> &str {
270 "failed to join paths"
271 }
272}
273
274pub fn current_exe() -> io::Result<PathBuf> {
275 super::fill_utf16_buf(
276 |buf, sz| unsafe { c::GetModuleFileNameW(ptr::null_mut(), buf, sz) },
277 super::os2path,
278 )
279}
280
281pub fn getcwd() -> io::Result<PathBuf> {
282 super::fill_utf16_buf(|buf, sz| unsafe { c::GetCurrentDirectoryW(sz, buf) }, super::os2path)
283}
284
285pub fn chdir(p: &path::Path) -> io::Result<()> {
286 let p: &OsStr = p.as_ref();
287 let mut p = p.encode_wide().collect::<Vec<_>>();
288 p.push(0);
289
290 cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop)
291}
292
293pub fn getenv(k: &OsStr) -> Option<OsString> {
294 let k = to_u16s(k).ok()?;
295 super::fill_utf16_buf(
296 |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
297 OsStringExt::from_wide,
298 )
299 .ok()
300}
301
302pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
303 unsafe {
305 let k = to_u16s(k)?;
306 let v = to_u16s(v)?;
307
308 cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop)
309 }
310}
311
312pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
313 unsafe {
315 let v = to_u16s(n)?;
316 cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop)
317 }
318}
319
320pub fn temp_dir() -> PathBuf {
321 super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap()
322}
323
324#[cfg(all(not(target_vendor = "uwp"), not(target_vendor = "win7")))]
325fn home_dir_crt() -> Option<PathBuf> {
326 unsafe {
327 const CURRENT_PROCESS_TOKEN: usize = -4_isize as usize;
329
330 super::fill_utf16_buf(
331 |buf, mut sz| {
332 match c::GetUserProfileDirectoryW(
335 ptr::without_provenance_mut(CURRENT_PROCESS_TOKEN),
336 buf,
337 &mut sz,
338 ) {
339 0 if api::get_last_error() != WinError::INSUFFICIENT_BUFFER => 0,
340 0 => sz,
341 _ => sz - 1, }
343 },
344 super::os2path,
345 )
346 .ok()
347 }
348}
349
350#[cfg(target_vendor = "win7")]
351fn home_dir_crt() -> Option<PathBuf> {
352 unsafe {
353 use crate::sys::handle::Handle;
354
355 let me = c::GetCurrentProcess();
356 let mut token = ptr::null_mut();
357 if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
358 return None;
359 }
360 let _handle = Handle::from_raw_handle(token);
361 super::fill_utf16_buf(
362 |buf, mut sz| {
363 match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
364 0 if api::get_last_error() != WinError::INSUFFICIENT_BUFFER => 0,
365 0 => sz,
366 _ => sz - 1, }
368 },
369 super::os2path,
370 )
371 .ok()
372 }
373}
374
375#[cfg(target_vendor = "uwp")]
376fn home_dir_crt() -> Option<PathBuf> {
377 None
378}
379
380pub fn home_dir() -> Option<PathBuf> {
381 crate::env::var_os("USERPROFILE")
382 .filter(|s| !s.is_empty())
383 .map(PathBuf::from)
384 .or_else(home_dir_crt)
385}
386
387pub fn exit(code: i32) -> ! {
388 unsafe { c::ExitProcess(code as u32) }
389}
390
391pub fn getpid() -> u32 {
392 unsafe { c::GetCurrentProcessId() }
393}