error_code/
system.rs

1use crate::{Category, ErrorCode};
2#[cfg(not(windows))]
3use crate::posix::{message, is_would_block};
4#[cfg(not(windows))]
5pub(crate) use crate::posix::get_last_error;
6use crate::types::c_int;
7
8use core::ptr;
9
10/// System error category, suitable for all environments.
11///
12/// On UNIX system it is equivalent of [Posix](struct.PosixCategory.html)
13///
14/// On Windows it uses winapi error functions
15pub static SYSTEM_CATEGORY: Category = Category {
16    name: "OSError",
17    message,
18    equivalent,
19    is_would_block,
20};
21
22fn equivalent(code: c_int, other: &ErrorCode) -> bool {
23    ptr::eq(&SYSTEM_CATEGORY, other.category()) && code == other.raw_code()
24}
25
26#[cfg(windows)]
27#[inline]
28pub(crate) fn get_last_error() -> c_int {
29    unsafe {
30        GetLastError() as c_int
31   }
32}
33
34#[cfg(windows)]
35fn message(code: c_int, out: &mut crate::MessageBuf) -> &str {
36    use crate::MESSAGE_BUF_SIZE;
37    use core::{slice, mem};
38
39    const CP_UTF8: crate::types::c_ulong = 65001;
40    const FORMAT_MESSAGE_FROM_SYSTEM: crate::types::c_ulong = 0x00001000;
41    const FORMAT_MESSAGE_IGNORE_INSERTS: crate::types::c_ulong = 0x00000200;
42    const FMT_FLAGS: crate::types::c_ulong = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM;
43
44    let mut buff = [mem::MaybeUninit::<u16>::uninit(); MESSAGE_BUF_SIZE * 2];
45    let mut len = unsafe {
46        FormatMessageW(FMT_FLAGS, ptr::null(), code as crate::types::c_ulong, 0, buff.as_mut_ptr() as *mut u16, buff.len() as _, ptr::null_mut())
47    };
48
49    if len == 0 {
50        match get_last_error() {
51            //Buffer doesn't have enough space
52            //But it is completely written so we'll take what we can
53            122 => len = buff.len() as crate::types::c_ulong,
54            //System cannot find specified error code
55            317 => return crate::posix::message(code, out),
56            _ => return crate::utils::write_message_buf(out, crate::FAIL_ERROR_FORMAT),
57        }
58    }
59
60    let res = unsafe {
61        WideCharToMultiByte(CP_UTF8, 0,
62                            buff.as_ptr() as _, len as _,
63                            out.as_mut_ptr() as *mut i8, out.len() as _,
64                            ptr::null(), ptr::null_mut())
65    };
66
67    match res {
68        0 => match get_last_error() {
69            122 => crate::utils::write_message_buf(out, "<Truncated>"),
70            _ => crate::utils::write_fallback_code(out, code),
71        }
72        len => {
73            let out = unsafe {
74                slice::from_raw_parts(out.as_ptr() as *const u8, len as _)
75            };
76            //It seems WinAPI always supposed to have at the end null char.
77            //But just to be safe let's check for it and only then remove.
78            let actual_len = if let Some(null_idx) = out.iter().position(|b| *b == b'\0' || *b == b'\r') {
79                null_idx
80            } else {
81                len as usize
82            };
83
84            unsafe {
85                core::str::from_utf8_unchecked(
86                    slice::from_raw_parts(out.as_ptr(), actual_len)
87                )
88            }
89        }
90    }
91}
92
93#[cfg(windows)]
94#[inline]
95fn is_would_block(code: c_int) -> bool {
96    code == 10035 || crate::posix::is_would_block(code)
97}
98
99#[cfg(windows)]
100extern "system" {
101    fn GetLastError() -> crate::types::c_ulong;
102    fn FormatMessageW(dwFlags: crate::types::c_ulong, lpSource: *const u8, dwMessageId: crate::types::c_ulong, dwLanguageId: crate::types::c_ulong, lpBuffer: *mut u16, nSize: crate::types::c_ulong, Arguments: *mut i8) -> u32;
103    fn WideCharToMultiByte(page: crate::types::c_uint, flags: crate::types::c_ulong, wide_str: *const u16, wide_str_len: c_int, multi_str: *mut i8, multi_str_len: c_int, default_char: *const i8, used_default_char: *mut bool) -> c_int;
104}