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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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}