print_no_std/
lib.rs

1#![deny(warnings)]
2#![allow(clippy::unnecessary_literal_unwrap)]
3
4#![no_std]
5
6use core::fmt::{self};
7
8#[doc(hidden)]
9pub use core::write as std_write;
10#[doc(hidden)]
11pub use core::writeln as std_writeln;
12
13#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
14enum WriteErrorStrategy {
15    Passthrough,
16    Panic,
17    //Ignore
18}
19
20#[cfg(all(not(target_os="dos"), windows))]
21mod winapi {
22    use core::cmp::min;
23    use core::fmt::{self};
24    use core::mem::size_of;
25    use core::ptr::null_mut;
26    use crate::WriteErrorStrategy;
27    use errno_no_std::errno;
28    use iter_identify_first_last::IteratorIdentifyFirstLastExt;
29    use winapi::shared::minwindef::DWORD;
30    use winapi::um::consoleapi::{GetConsoleMode, WriteConsoleW};
31    use winapi::um::fileapi::WriteFile;
32    use winapi::um::handleapi::INVALID_HANDLE_VALUE;
33    use winapi::um::processenv::GetStdHandle;
34    use winapi::um::winbase::{STD_ERROR_HANDLE, STD_OUTPUT_HANDLE};
35
36    pub type StdHandle = u32;
37
38    pub const STDOUT: StdHandle = STD_OUTPUT_HANDLE;
39
40    pub const STDERR: StdHandle = STD_ERROR_HANDLE;
41
42    pub fn write_str(std_handle: StdHandle, s: &str, error_strategy: WriteErrorStrategy) -> fmt::Result {
43        let handle = unsafe { GetStdHandle(std_handle) };
44        if handle.is_null() || handle == INVALID_HANDLE_VALUE {
45            match error_strategy {
46                WriteErrorStrategy::Passthrough => return Err(fmt::Error),
47                WriteErrorStrategy::Panic => panic!("cannot get std handle"),
48                //WriteErrorStrategy::Ignore => return Ok(()),
49            }
50        }
51        let mut mode: u32 = 0;
52        if unsafe { GetConsoleMode(handle, &mut mode as *mut _) } == 0 {
53            let mut s = s;
54            while !s.is_empty() {
55                let mut written: u32 = 0;
56                assert!(size_of::<u32>() <= size_of::<usize>());
57                let len = min(s.len(), u32::MAX as usize);
58                if unsafe { WriteFile(
59                    handle,
60                    s.as_ptr() as _,
61                    len as u32,
62                    &mut written as *mut _,
63                    null_mut()
64                ) } == 0 {
65                    match error_strategy {
66                        WriteErrorStrategy::Passthrough => return Err(fmt::Error),
67                        WriteErrorStrategy::Panic => Err(errno()).unwrap(),
68                        //WriteErrorStrategy::Ignore => return Ok(()),
69                    }
70                }
71                assert_eq!(written, len as u32);
72                s = &s[len ..];
73            }
74        } else {
75            let mut buf = [0u16; 128];
76            let mut buf_offset = 0;
77            for (is_last, c) in s.chars().identify_last() {
78                buf_offset += c.encode_utf16(&mut buf[buf_offset ..]).len();
79                if is_last || buf.len() - buf_offset < 2 {
80                    let mut written: DWORD = 0;
81                    if unsafe { WriteConsoleW(
82                        handle,
83                        buf.as_ptr() as _,
84                        buf_offset.try_into().unwrap(),
85                        &mut written as *mut _,
86                        null_mut()
87                    ) } == 0 {
88                        match error_strategy {
89                            WriteErrorStrategy::Passthrough => return Err(fmt::Error),
90                            WriteErrorStrategy::Panic => Err(errno()).unwrap(),
91                            //WriteErrorStrategy::Ignore => return Ok(()),
92                        }
93                    }
94                    assert_eq!(written, buf_offset.try_into().unwrap());
95                    buf_offset = 0;
96                }
97            }
98        }
99        Ok(())
100    }
101}
102
103#[cfg(not(windows))]
104mod posix {
105    use crate::WriteErrorStrategy;
106    use core::fmt::{self};
107    use errno_no_std::{Errno, errno};
108    use libc::{c_int, iconv, iconv_t, iconv_open, nl_langinfo, iconv_close, CODESET, E2BIG};
109
110    pub type StdHandle = c_int;
111
112    pub const STDOUT: StdHandle = 1;
113
114    pub const STDERR: StdHandle = 2;
115
116    const ICONV_ERR: iconv_t = (-1isize) as usize as iconv_t;
117
118    pub fn write_str(std_handle: StdHandle, s: &str, error_strategy: WriteErrorStrategy) -> fmt::Result {
119        let conv = unsafe { iconv_open(nl_langinfo(CODESET), c"UTF-8".as_ptr() as _) };
120        if conv == ICONV_ERR {
121            match error_strategy {
122                WriteErrorStrategy::Passthrough => return Err(fmt::Error),
123                WriteErrorStrategy::Panic => Err(errno()).unwrap(),
124                //WriteErrorStrategy::Ignore => return Ok(()),
125            }
126        }
127        let mut buf = [0u8; 128];
128        let mut iconv_in = s.as_ptr();
129        let mut iconv_in_len = s.len();
130        loop {
131            let mut iconv_out = (buf[..]).as_mut_ptr();
132            let mut iconv_out_len = buf.len();
133            let iconv_res = unsafe { iconv(
134                conv,
135                (&mut iconv_in) as *mut _ as _,
136                (&mut iconv_in_len) as *mut _,
137                (&mut iconv_out) as *mut _ as _,
138                (&mut iconv_out_len) as *mut _,
139            ) };
140            let stop = if iconv_res == (-1isize) as usize {
141                let err = errno();
142                if err != Errno(E2BIG) {
143                    match error_strategy {
144                        WriteErrorStrategy::Passthrough => return Err(fmt::Error),
145                        WriteErrorStrategy::Panic => Err(err).unwrap(),
146                        //WriteErrorStrategy::Ignore => return Ok(()),
147                    }
148                }
149                false
150            } else {
151                debug_assert_eq!(iconv_in_len, 0);
152                true
153            };
154            let written = buf.len() - iconv_out_len;
155            if unsafe { libc::write(std_handle, buf.as_ptr() as _, written) } == -1 {
156                let err = errno();
157                match error_strategy {
158                    WriteErrorStrategy::Passthrough => return Err(fmt::Error),
159                    WriteErrorStrategy::Panic => Err(err).unwrap(),
160                    //WriteErrorStrategy::Ignore => return Ok(()),
161                }
162            }
163            if stop { break; }
164        }
165        if unsafe { iconv_close(conv) } == -1 {
166            let err = errno();
167            match error_strategy {
168                WriteErrorStrategy::Passthrough => return Err(fmt::Error),
169                WriteErrorStrategy::Panic => Err(err).unwrap(),
170                //WriteErrorStrategy::Ignore => return Ok(()),
171            }
172        }
173        Ok(())
174    }
175}
176
177#[cfg(target_os="dos")]
178mod dos {
179    use crate::WriteErrorStrategy;
180    use core::fmt::{self};
181    use core::fmt::Write as fmt_Write;
182    use dos_cp::DosStdout;
183
184    pub type StdHandle = ();
185
186    pub const STDOUT: () = ();
187
188    pub const STDERR: () = ();
189
190    pub fn write_str((): StdHandle, s: &str, error_strategy: WriteErrorStrategy) -> fmt::Result {
191        if let Err(e) = (DosStdout { panic: error_strategy == WriteErrorStrategy::Panic }).write_str(s) {
192            if error_strategy == WriteErrorStrategy::Passthrough { Err(e) } else { Ok(()) }
193        } else {
194            Ok(())
195        }
196    }
197}
198
199#[cfg(all(not(target_os="dos"), windows))]
200use winapi::*;
201#[cfg(not(windows))]
202use posix::*;
203#[cfg(target_os="dos")]
204use dos::*;
205
206pub struct Stdout { pub panic: bool }
207
208pub struct Stderr { pub panic: bool }
209
210impl Stdout {
211    pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
212        <Self as fmt::Write>::write_fmt(self, args)
213    }
214}
215
216impl Stderr {
217    pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
218        <Self as fmt::Write>::write_fmt(self, args)
219    }
220}
221
222impl fmt::Write for Stdout {
223    fn write_str(&mut self, s: &str) -> fmt::Result {
224        let error_strategy = if self.panic {
225            WriteErrorStrategy::Panic
226        } else {
227            WriteErrorStrategy::Passthrough
228        };
229        write_str(STDOUT, s, error_strategy)
230    }
231}
232
233impl fmt::Write for Stderr {
234    fn write_str(&mut self, s: &str) -> fmt::Result {
235        let error_strategy = if self.panic {
236            WriteErrorStrategy::Panic
237        } else {
238            WriteErrorStrategy::Passthrough
239        };
240        write_str(STDERR, s, error_strategy)
241    }
242}
243
244#[macro_export]
245macro_rules! print {
246    (
247        $($arg:tt)*
248    ) => {
249        $crate::std_write!($crate::Stdout { panic: true }, $($arg)*).unwrap()
250    };
251}
252
253#[macro_export]
254macro_rules! println {
255    (
256    ) => {
257        $crate::std_writeln!($crate::Stdout { panic: true }).unwrap()
258    };
259    (
260        $($arg:tt)*
261    ) => {
262        $crate::std_writeln!($crate::Stdout { panic: true }, $($arg)*).unwrap()
263    };
264}
265
266#[macro_export]
267macro_rules! eprint {
268    (
269        $($arg:tt)*
270    ) => {
271        $crate::std_write!($crate::Stderr { panic: true }, $($arg)*).unwrap()
272    };
273}
274
275#[macro_export]
276macro_rules! eprintln {
277    (
278    ) => {
279        $crate::std_writeln!($crate::Stderr { panic: true }).unwrap()
280    };
281    (
282        $($arg:tt)*
283    ) => {
284        $crate::std_writeln!($crate::Stderr { panic: true }, $($arg)*).unwrap()
285    };
286}