Skip to main content

libc_print/
lib.rs

1//! Implements `println!`, `eprintln!` and `dbg!` on top of the `libc `crate without requiring
2//! the use of an allocator.
3//!
4//! Allows you to use these macros in a #!\[no_std\] context, or in a situation where the
5//! traditional Rust streams might not be available (ie: at process shutdown time).
6//!
7//! [`libc_writeln`] and [`libc_ewriteln`] are provided for cases where you may not wish
8//! to pull in the overhead of the formatter code and simply wish to print C-style strings.
9//!
10//! ## Usage
11//!
12//! Exactly as you'd use `println!`, `eprintln!` and `dbg!`.
13//!
14//! ```rust
15//! # use libc_print::*;
16//! // Use the default `libc_`-prefixed macros:
17//! # fn test1()
18//! # {
19//! libc_println!("Hello {}!", "stdout");
20//! libc_eprintln!("Hello {}!", "stderr");
21//! let a = 2;
22//! let b = libc_dbg!(a * 2) + 1;
23//! assert_eq!(b, 5);
24//! # }
25//! ```
26//!
27//! Or you can import aliases to `std` names:
28//!
29//! ```rust
30//! use libc_print::std_name::{println, eprintln, dbg};
31//!
32//! # fn test2()
33//! # {
34//! println!("Hello {}!", "stdout");
35//! eprintln!("Hello {}!", "stderr");
36//! let a = 2;
37//! let b = dbg!(a * 2) + 1;
38//! assert_eq!(b, 5);
39//! # }
40//! ```
41
42#![no_std]
43#![warn(unsafe_op_in_unsafe_fn)]
44
45use core::convert::TryFrom;
46
47// These constants are used by the macros but we don't want to expose
48// them to library users.
49#[doc(hidden)]
50pub const __LIBC_NEWLINE: &str = "\n";
51#[doc(hidden)]
52pub const __LIBC_STDOUT: i32 = 1;
53#[doc(hidden)]
54pub const __LIBC_STDERR: i32 = 2;
55
56#[doc(hidden)]
57pub struct __LibCWriter(i32);
58
59impl core::fmt::Write for __LibCWriter {
60    #[inline]
61    fn write_str(&mut self, s: &str) -> core::fmt::Result {
62        __libc_println(self.0, s)
63    }
64}
65
66impl __LibCWriter {
67    #[inline]
68    pub fn new(handle: i32) -> __LibCWriter {
69        __LibCWriter(handle)
70    }
71
72    #[inline]
73    pub fn write_fmt(&mut self, args: core::fmt::Arguments) -> core::fmt::Result {
74        core::fmt::Write::write_fmt(self, args)
75    }
76
77    #[inline]
78    pub fn write_str(&mut self, s: &str) -> core::fmt::Result {
79        __libc_println(self.0, s)
80    }
81
82    #[inline]
83    pub fn write_nl(&mut self) -> core::fmt::Result {
84        __libc_println(self.0, __LIBC_NEWLINE)
85    }
86}
87
88#[doc(hidden)]
89#[inline]
90pub fn __libc_println(handle: i32, msg: &str) -> core::fmt::Result {
91    let msg = msg.as_bytes();
92
93    let mut written = 0;
94    while written < msg.len() {
95        match unsafe { libc_write(handle, &msg[written..]) } {
96            // Ignore errors
97            None | Some(0) => break,
98            Some(res) => written += res,
99        }
100    }
101
102    Ok(())
103}
104
105#[cfg(all(
106    not(all(windows, miri)),
107    not(any(all(target_family = "wasm", target_os = "unknown"), target_os = "none"))
108))]
109mod write {
110    pub(crate) use libc::write;
111}
112
113#[cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "none"))]
114mod write {
115    // The user is required to provide this
116    #[cfg_attr(all(target_family = "wasm", target_os = "unknown"), link(wasm_import_module = "env"))]
117    unsafe extern "C" {
118        pub(crate) fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize;
119    }
120}
121
122// Note: we may offer the lower-level WriteFile in the future.
123#[cfg(all(windows, miri))]
124mod write {
125    use core::ffi::c_void;
126    use core::sync::atomic::{AtomicPtr, Ordering};
127
128    #[cfg(not(miri))]
129    type BOOL = i32;
130    type DWORD = u32;
131    type ULONG = u32;
132    type HANDLE = *mut c_void;
133    type NTSTATUS = i32;
134
135    const INVALID_HANDLE_VALUE: isize = -1;
136    const STD_OUTPUT_HANDLE: DWORD = (-11i32) as DWORD;
137    const STD_ERROR_HANDLE: DWORD = (-12i32) as DWORD;
138
139    // https://learn.microsoft.com/windows-hardware/drivers/ddi/wdm/ns-wdm-_io_status_block
140    // The first field is a pointer-sized union; model it as `usize`.
141    #[cfg(miri)]
142    #[cfg(target_pointer_width = "64")]
143    #[repr(C)]
144    struct IO_STATUS_BLOCK {
145        status_or_ptr: usize,
146        information: usize,
147    }
148
149    #[cfg(miri)]
150    #[cfg(target_pointer_width = "32")]
151    #[repr(C)]
152    struct IO_STATUS_BLOCK {
153        status_or_ptr: u32,
154        information: u32,
155    }
156
157    unsafe extern "system" {
158        fn GetStdHandle(nStdHandle: DWORD) -> HANDLE;
159
160        #[cfg(not(miri))]
161        fn WriteFile(
162            hFile: HANDLE,
163            lpBuffer: *const c_void,
164            nNumberOfBytesToWrite: DWORD,
165            lpNumberOfBytesWritten: *mut DWORD,
166            lpOverlapped: *mut c_void,
167        ) -> BOOL;
168
169        #[cfg(miri)]
170        fn NtWriteFile(
171            FileHandle: HANDLE,
172            Event: HANDLE,
173            ApcRoutine: *mut c_void,
174            ApcContext: *mut c_void,
175            IoStatusBlock: *mut IO_STATUS_BLOCK,
176            Buffer: *const c_void,
177            Length: ULONG,
178            ByteOffset: *mut c_void,
179            Key: *mut c_void,
180        ) -> NTSTATUS;
181    }
182
183    #[inline]
184    fn cached_std_handle(fd: i32) -> Option<(DWORD, &'static AtomicPtr<c_void>)> {
185        static STD_OUTPUT: AtomicPtr<c_void> = AtomicPtr::new(INVALID_HANDLE_VALUE as _);
186        static STD_ERROR: AtomicPtr<c_void> = AtomicPtr::new(INVALID_HANDLE_VALUE as _);
187
188        match fd {
189            1 => Some((STD_OUTPUT_HANDLE, &STD_OUTPUT)),
190            2 => Some((STD_ERROR_HANDLE, &STD_ERROR)),
191            _ => None,
192        }
193    }
194
195    #[inline]
196    fn handle_from_fd(fd: i32) -> Option<HANDLE> {
197        let (std_handle, which) = cached_std_handle(fd)?;
198
199        // Races are OK - we will get the same value each time assuming nobody
200        // is calling SetStdHandle at the same time.
201        let mut handle = which.load(Ordering::Relaxed);
202        if handle.is_null() || handle as isize == INVALID_HANDLE_VALUE {
203            handle = unsafe { GetStdHandle(std_handle) } as *mut c_void;
204            if handle.is_null() || handle as isize == INVALID_HANDLE_VALUE {
205                return None;
206            }
207            which.store(handle, Ordering::Relaxed);
208        }
209
210        Some(handle as HANDLE)
211    }
212
213    #[cfg(miri)]
214    pub(crate) unsafe fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize {
215        let h = match handle_from_fd(fd) {
216            Some(h) => h,
217            None => return -1,
218        };
219
220        let len: ULONG = match ULONG::try_from(nbyte) {
221            Ok(v) => v,
222            Err(_) => ULONG::MAX,
223        };
224
225        let mut iosb = IO_STATUS_BLOCK {
226            status_or_ptr: 0,
227            information: 0,
228        };
229
230        let status = unsafe {
231            NtWriteFile(
232                h,
233                core::ptr::null_mut(),
234                core::ptr::null_mut(),
235                core::ptr::null_mut(),
236                &mut iosb,
237                buf as *const c_void,
238                len,
239                core::ptr::null_mut(),
240                core::ptr::null_mut(),
241            )
242        };
243
244        // NT_SUCCESS: status >= 0
245        if status < 0 {
246            -1
247        } else {
248            iosb.information as isize
249        }
250    }
251
252    #[cfg(not(miri))]
253    pub(crate) unsafe fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize {
254        let h = match handle_from_fd(fd) {
255            Some(h) => h,
256            None => return -1,
257        };
258
259        let to_write: DWORD = match DWORD::try_from(nbyte) {
260            Ok(v) => v,
261            Err(_) => DWORD::MAX,
262        };
263
264        let mut written: DWORD = 0;
265        let ok = unsafe {
266            WriteFile(
267                h,
268                buf as *const c_void,
269                to_write,
270                &mut written,
271                core::ptr::null_mut(),
272            )
273        };
274
275        if ok == 0 {
276            -1
277        } else {
278            written as isize
279        }
280    }
281}
282
283unsafe fn libc_write(handle: i32, bytes: &[u8]) -> Option<usize> {
284    usize::try_from(unsafe { write::write(handle as _, bytes.as_ptr() as _, bytes.len() as _) })
285        .ok()
286}
287
288/// Macro for printing to the standard output, with a newline.
289///
290/// Does not panic on failure to write - instead silently ignores errors.
291///
292/// See [`println!`](https://doc.rust-lang.org/std/macro.println.html) for
293/// full documentation.
294///
295/// You may wish to `use libc_print::std_name::*` to use a replacement
296/// `println!` macro instead of this longer name.
297#[macro_export]
298macro_rules! libc_println {
299    () => { $crate::libc_println!("") };
300    ($($arg:tt)*) => {
301        {
302            #[allow(unused_must_use)]
303            {
304                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
305                stm.write_fmt(format_args!($($arg)*));
306                stm.write_nl();
307            }
308        }
309    };
310}
311
312/// Macro for printing to the standard output.
313///
314/// Does not panic on failure to write - instead silently ignores errors.
315///
316/// See [`print!`](https://doc.rust-lang.org/std/macro.print.html) for
317/// full documentation.
318///
319/// You may wish to `use libc_print::std_name::*` to use a replacement
320/// `print!` macro instead of this longer name.
321#[macro_export]
322macro_rules! libc_print {
323    ($($arg:tt)*) => {
324        {
325            #[allow(unused_must_use)]
326            {
327                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
328                stm.write_fmt(format_args!($($arg)*));
329            }
330        }
331    };
332}
333
334/// Macro for printing to the standard error, with a newline.
335///
336/// Does not panic on failure to write - instead silently ignores errors.
337///
338/// See [`eprintln!`](https://doc.rust-lang.org/std/macro.eprintln.html) for
339/// full documentation.
340///
341/// You may wish to `use libc_print::std_name::*` to use a replacement
342/// `eprintln!` macro instead of this longer name.
343#[macro_export]
344macro_rules! libc_eprintln {
345    () => { $crate::libc_eprintln!("") };
346    ($($arg:tt)*) => {
347        {
348            #[allow(unused_must_use)]
349            {
350                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
351                stm.write_fmt(format_args!($($arg)*));
352                stm.write_nl();
353            }
354        }
355    };
356}
357
358/// Macro for printing to the standard error.
359///
360/// Does not panic on failure to write - instead silently ignores errors.
361///
362/// See [`eprint!`](https://doc.rust-lang.org/std/macro.eprint.html) for
363/// full documentation.
364///
365/// You may wish to `use libc_print::std_name::*` to use a replacement
366/// `eprint!` macro instead of this longer name.
367#[macro_export]
368macro_rules! libc_eprint {
369    ($($arg:tt)*) => {
370        {
371            #[allow(unused_must_use)]
372            {
373                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
374                stm.write_fmt(format_args!($($arg)*));
375            }
376        }
377    };
378}
379
380/// Macro for printing a static string to the standard output.
381///
382/// Does not panic on failure to write - instead silently ignores errors.
383#[macro_export]
384macro_rules! libc_write {
385    ($arg:expr) => {
386        #[allow(unused_must_use)]
387        {
388            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
389            stm.write_str($arg);
390        }
391    };
392}
393
394/// Macro for printing a static string to the standard error.
395///
396/// Does not panic on failure to write - instead silently ignores errors.
397#[macro_export]
398macro_rules! libc_ewrite {
399    ($arg:expr) => {{
400        #[allow(unused_must_use)]
401        {
402            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
403            stm.write_str($arg);
404        }
405    }};
406}
407
408/// Macro for printing a static string to the standard output, with a newline.
409///
410/// Does not panic on failure to write - instead silently ignores errors.
411#[macro_export]
412macro_rules! libc_writeln {
413    ($arg:expr) => {
414        #[allow(unused_must_use)]
415        {
416            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
417            stm.write_str($arg);
418            stm.write_nl();
419        }
420    };
421}
422
423/// Macro for printing a static string to the standard error, with a newline.
424///
425/// Does not panic on failure to write - instead silently ignores errors.
426#[macro_export]
427macro_rules! libc_ewriteln {
428    ($arg:expr) => {{
429        #[allow(unused_must_use)]
430        {
431            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
432            stm.write_str($arg);
433            stm.write_nl();
434        }
435    }};
436}
437
438/// Prints and returns the value of a given expression for quick and dirty
439/// debugging.
440///
441/// An example:
442///
443/// ```rust
444/// let a = 2;
445/// let b = dbg!(a * 2) + 1;
446/// //      ^-- prints: [src/main.rs:2] a * 2 = 4
447/// assert_eq!(b, 5);
448/// ```
449///
450/// See [dbg!](https://doc.rust-lang.org/std/macro.dbg.html) for full documentation.
451///
452/// You may wish to `use libc_print::std_name::*` to use a replacement
453/// `dbg!` macro instead of this longer name.
454#[macro_export]
455macro_rules! libc_dbg {
456    () => {
457        $crate::libc_eprintln!("[{}:{}]", ::core::file!(), ::core::line!())
458    };
459    ($val:expr $(,)?) => {
460        match $val {
461            tmp => {
462                $crate::libc_eprintln!("[{}:{}] {} = {:#?}", ::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp);
463                tmp
464            }
465        }
466    };
467    ($($val:expr),+ $(,)?) => {
468        ($($crate::libc_dbg!($val)),+,)
469    };
470}
471
472/// This package contains the `libc_print` macros, but using the stdlib names
473/// such as `println!`, `print!`, etc.
474pub mod std_name {
475    pub use super::libc_dbg as dbg;
476    pub use super::libc_eprint as eprint;
477    pub use super::libc_eprintln as eprintln;
478    pub use super::libc_print as print;
479    pub use super::libc_println as println;
480
481    #[cfg(test)]
482    mod tests_std_name {
483        use super::{eprintln, println};
484
485        #[test]
486        fn test_stdout() {
487            println!("stdout fd = {}", crate::__LIBC_STDOUT);
488        }
489
490        #[test]
491        fn test_stderr() {
492            eprintln!("stderr fd = {}", crate::__LIBC_STDERR);
493        }
494    }
495}
496
497#[cfg(test)]
498mod tests {
499    #[test]
500    fn test_stdout() {
501        super::libc_println!("stdout fd = {}", super::__LIBC_STDOUT);
502    }
503
504    #[test]
505    fn test_stderr() {
506        super::libc_eprintln!("stderr fd = {}", super::__LIBC_STDERR);
507    }
508
509    #[test]
510    fn test_stdout_write() {
511        super::libc_writeln!("stdout!");
512    }
513
514    #[test]
515    fn test_stderr_write() {
516        super::libc_ewriteln!("stderr!");
517    }
518
519    #[test]
520    fn test_dbg() {
521        let a = 2;
522        let b = libc_dbg!(a * 2) + 1;
523        assert_eq!(b, 5);
524    }
525
526    #[test]
527    fn test_in_closure_expression() {
528        use super::std_name::*;
529        // https://github.com/mmastrac/rust-libc-print/issues/86
530        let _ = Result::<(), ()>::Ok(()).unwrap_or_else(|err| eprintln!("error: {:?}", err));
531    }
532}