1mod args;
2mod vars;
3mod path;
4pub mod consts;
5
6use crate::io::Error;
7use crate::path::{PathBuf, MAX_PATH_EXTENDED};
8use crate::util::{get_last_windows_error, get_proc_from_module};
9use alloc::string::String;
10use alloc::vec::Vec;
11use alloc::{format, vec};
12use core::iter::once;
13use core::ptr::null_mut;
14use spin::Lazy;
15use windows_sys::Win32::Foundation::{CloseHandle, FALSE};
16use windows_sys::Win32::Security::TOKEN_QUERY;
17use windows_sys::Win32::Storage::FileSystem::GetTempPathW;
18use windows_sys::Win32::System::Environment::{GetCurrentDirectoryW, SetCurrentDirectoryW};
19use windows_sys::Win32::System::LibraryLoader::GetModuleFileNameW;
20use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
21use windows_sys::Win32::UI::Shell::GetUserProfileDirectoryW;
22pub use args::*;
23pub use vars::*;
24pub use path::*;
25
26pub fn current_exe() -> crate::io::Result<PathBuf> {
27 let mut path = vec![0u16; MAX_PATH_EXTENDED + 1];
28 let path_len = unsafe { GetModuleFileNameW(null_mut(), path.as_mut_ptr(), path.len() as u32) } as usize;
29 let err = get_last_windows_error();
30 assert_ne!(path_len, 0, "current_exe() failed: {err}");
31 let data = String::from_utf16(&path[..path_len]).expect("GetModuleFileNameW returned non UTF-16");
32
33 Ok(data.into())
34}
35
36pub fn current_dir() -> crate::io::Result<PathBuf> {
37 let mut path = vec![0u16; MAX_PATH_EXTENDED + 1];
38 let path_len = unsafe { GetCurrentDirectoryW(path.len() as u32, path.as_mut_ptr()) } as usize;
39 let err = get_last_windows_error();
40 assert_ne!(path_len, 0, "current_exe() failed: {err}");
41 let data = String::from_utf16(&path[..path_len]).expect("GetCurrentDirectoryW returned non UTF-16");
42
43 Ok(data.into())
44}
45
46pub fn home_dir() -> Option<PathBuf> {
47 if let Ok(v) = var("USERPROFILE") {
48 return Some(v.into())
49 };
50
51 let mut data = vec![0u16; MAX_PATH_EXTENDED + 1];
52 let handle = unsafe { GetCurrentProcess() };
53 let mut token_handle = null_mut();
54 let token = unsafe { OpenProcessToken(handle, TOKEN_QUERY, &mut token_handle) };
55
56 if token == FALSE {
57 return None
58 }
59
60 unsafe {
61 let mut len = data.len() as u32;
62 let success = GetUserProfileDirectoryW(token_handle, data.as_mut_ptr(), &mut len);
63 let err = get_last_windows_error();
64 CloseHandle(token_handle);
65 if success == FALSE {
66 return None
67 }
68 }
69
70 let null = data.iter().position(|s| *s == 0).unwrap_or(data.len());
71 String::from_utf16(&data[..null]).ok().map(Into::into)
72}
73
74pub fn set_current_dir<P: AsRef<str>>(path: P) -> crate::io::Result<()> {
75 let path: Vec<u16> = path.as_ref().encode_utf16().chain(once(0)).collect();
76 let success = unsafe { SetCurrentDirectoryW(path.as_ptr()) };
77 let last_err = get_last_windows_error();
78 match success {
79 0 => Err(Error { reason: format!("Cannot set path: {last_err}") }),
80 _ => Ok(())
81 }
82}
83
84type GetTempPathWFn = unsafe extern "system" fn (len: u32, *mut u16) -> u32;
85
86static GET_TEMP_PATH: Lazy<GetTempPathWFn> = Lazy::new(|| {
87 let get_temp_path: Option<GetTempPathWFn> = get_proc_from_module!(
88 "kernel32.dll",
89 "GetTempPath2W"
90 );
91
92 match get_temp_path {
93 Some(t) => t,
94 None => GetTempPathW
95 }
96});
97
98pub fn temp_dir() -> PathBuf {
113 let mut buffer = vec![0u16; MAX_PATH_EXTENDED + 1];
114 let length = unsafe { GET_TEMP_PATH(buffer.len() as u32, buffer.as_mut_ptr()) };
115 buffer.truncate(length as usize);
116
117 let data = String::from_utf16(&buffer[..length as usize]).expect("GetTempPath(2)W returned non UTF-16");
118 data.into()
119}