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    unsafe extern "C" {
117        pub(crate) fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize;
118    }
119}
120
121// Note: we may offer the lower-level WriteFile in the future.
122#[cfg(all(windows, miri))]
123mod write {
124    use core::ffi::c_void;
125    use core::sync::atomic::{AtomicPtr, Ordering};
126
127    #[cfg(not(miri))]
128    type BOOL = i32;
129    type DWORD = u32;
130    type ULONG = u32;
131    type HANDLE = *mut c_void;
132    type NTSTATUS = i32;
133
134    const INVALID_HANDLE_VALUE: isize = -1;
135    const STD_OUTPUT_HANDLE: DWORD = (-11i32) as DWORD;
136    const STD_ERROR_HANDLE: DWORD = (-12i32) as DWORD;
137
138    // https://learn.microsoft.com/windows-hardware/drivers/ddi/wdm/ns-wdm-_io_status_block
139    // The first field is a pointer-sized union; model it as `usize`.
140    #[cfg(miri)]
141    #[cfg(target_pointer_width = "64")]
142    #[repr(C)]
143    struct IO_STATUS_BLOCK {
144        status_or_ptr: usize,
145        information: usize,
146    }
147
148    #[cfg(miri)]
149    #[cfg(target_pointer_width = "32")]
150    #[repr(C)]
151    struct IO_STATUS_BLOCK {
152        status_or_ptr: u32,
153        information: u32,
154    }
155
156    unsafe extern "system" {
157        fn GetStdHandle(nStdHandle: DWORD) -> HANDLE;
158
159        #[cfg(not(miri))]
160        fn WriteFile(
161            hFile: HANDLE,
162            lpBuffer: *const c_void,
163            nNumberOfBytesToWrite: DWORD,
164            lpNumberOfBytesWritten: *mut DWORD,
165            lpOverlapped: *mut c_void,
166        ) -> BOOL;
167
168        #[cfg(miri)]
169        fn NtWriteFile(
170            FileHandle: HANDLE,
171            Event: HANDLE,
172            ApcRoutine: *mut c_void,
173            ApcContext: *mut c_void,
174            IoStatusBlock: *mut IO_STATUS_BLOCK,
175            Buffer: *const c_void,
176            Length: ULONG,
177            ByteOffset: *mut c_void,
178            Key: *mut c_void,
179        ) -> NTSTATUS;
180    }
181
182    #[inline]
183    fn cached_std_handle(fd: i32) -> Option<(DWORD, &'static AtomicPtr<c_void>)> {
184        static STD_OUTPUT: AtomicPtr<c_void> = AtomicPtr::new(INVALID_HANDLE_VALUE as _);
185        static STD_ERROR: AtomicPtr<c_void> = AtomicPtr::new(INVALID_HANDLE_VALUE as _);
186
187        match fd {
188            1 => Some((STD_OUTPUT_HANDLE, &STD_OUTPUT)),
189            2 => Some((STD_ERROR_HANDLE, &STD_ERROR)),
190            _ => None,
191        }
192    }
193
194    #[inline]
195    fn handle_from_fd(fd: i32) -> Option<HANDLE> {
196        let (std_handle, which) = cached_std_handle(fd)?;
197
198        // Races are OK - we will get the same value each time assuming nobody
199        // is calling SetStdHandle at the same time.
200        let mut handle = which.load(Ordering::Relaxed);
201        if handle.is_null() || handle as isize == INVALID_HANDLE_VALUE {
202            handle = unsafe { GetStdHandle(std_handle) } as *mut c_void;
203            if handle.is_null() || handle as isize == INVALID_HANDLE_VALUE {
204                return None;
205            }
206            which.store(handle, Ordering::Relaxed);
207        }
208
209        Some(handle as HANDLE)
210    }
211
212    #[cfg(miri)]
213    pub(crate) unsafe fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize {
214        let h = match handle_from_fd(fd) {
215            Some(h) => h,
216            None => return -1,
217        };
218
219        let len: ULONG = match ULONG::try_from(nbyte) {
220            Ok(v) => v,
221            Err(_) => ULONG::MAX,
222        };
223
224        let mut iosb = IO_STATUS_BLOCK {
225            status_or_ptr: 0,
226            information: 0,
227        };
228
229        let status = unsafe {
230            NtWriteFile(
231                h,
232                core::ptr::null_mut(),
233                core::ptr::null_mut(),
234                core::ptr::null_mut(),
235                &mut iosb,
236                buf as *const c_void,
237                len,
238                core::ptr::null_mut(),
239                core::ptr::null_mut(),
240            )
241        };
242
243        // NT_SUCCESS: status >= 0
244        if status < 0 {
245            -1
246        } else {
247            iosb.information as isize
248        }
249    }
250
251    #[cfg(not(miri))]
252    pub(crate) unsafe fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize {
253        let h = match handle_from_fd(fd) {
254            Some(h) => h,
255            None => return -1,
256        };
257
258        let to_write: DWORD = match DWORD::try_from(nbyte) {
259            Ok(v) => v,
260            Err(_) => DWORD::MAX,
261        };
262
263        let mut written: DWORD = 0;
264        let ok = unsafe {
265            WriteFile(
266                h,
267                buf as *const c_void,
268                to_write,
269                &mut written,
270                core::ptr::null_mut(),
271            )
272        };
273
274        if ok == 0 {
275            -1
276        } else {
277            written as isize
278        }
279    }
280}
281
282unsafe fn libc_write(handle: i32, bytes: &[u8]) -> Option<usize> {
283    usize::try_from(unsafe { write::write(handle as _, bytes.as_ptr() as _, bytes.len() as _) })
284        .ok()
285}
286
287/// Macro for printing to the standard output, with a newline.
288///
289/// Does not panic on failure to write - instead silently ignores errors.
290///
291/// See [`println!`](https://doc.rust-lang.org/std/macro.println.html) for
292/// full documentation.
293///
294/// You may wish to `use libc_print::std_name::*` to use a replacement
295/// `println!` macro instead of this longer name.
296#[macro_export]
297macro_rules! libc_println {
298    () => { $crate::libc_println!("") };
299    ($($arg:tt)*) => {
300        {
301            #[allow(unused_must_use)]
302            {
303                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
304                stm.write_fmt(format_args!($($arg)*));
305                stm.write_nl();
306            }
307        }
308    };
309}
310
311/// Macro for printing to the standard output.
312///
313/// Does not panic on failure to write - instead silently ignores errors.
314///
315/// See [`print!`](https://doc.rust-lang.org/std/macro.print.html) for
316/// full documentation.
317///
318/// You may wish to `use libc_print::std_name::*` to use a replacement
319/// `print!` macro instead of this longer name.
320#[macro_export]
321macro_rules! libc_print {
322    ($($arg:tt)*) => {
323        {
324            #[allow(unused_must_use)]
325            {
326                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
327                stm.write_fmt(format_args!($($arg)*));
328            }
329        }
330    };
331}
332
333/// Macro for printing to the standard error, with a newline.
334///
335/// Does not panic on failure to write - instead silently ignores errors.
336///
337/// See [`eprintln!`](https://doc.rust-lang.org/std/macro.eprintln.html) for
338/// full documentation.
339///
340/// You may wish to `use libc_print::std_name::*` to use a replacement
341/// `eprintln!` macro instead of this longer name.
342#[macro_export]
343macro_rules! libc_eprintln {
344    () => { $crate::libc_eprintln!("") };
345    ($($arg:tt)*) => {
346        {
347            #[allow(unused_must_use)]
348            {
349                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
350                stm.write_fmt(format_args!($($arg)*));
351                stm.write_nl();
352            }
353        }
354    };
355}
356
357/// Macro for printing to the standard error.
358///
359/// Does not panic on failure to write - instead silently ignores errors.
360///
361/// See [`eprint!`](https://doc.rust-lang.org/std/macro.eprint.html) for
362/// full documentation.
363///
364/// You may wish to `use libc_print::std_name::*` to use a replacement
365/// `eprint!` macro instead of this longer name.
366#[macro_export]
367macro_rules! libc_eprint {
368    ($($arg:tt)*) => {
369        {
370            #[allow(unused_must_use)]
371            {
372                let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
373                stm.write_fmt(format_args!($($arg)*));
374            }
375        }
376    };
377}
378
379/// Macro for printing a static string to the standard output.
380///
381/// Does not panic on failure to write - instead silently ignores errors.
382#[macro_export]
383macro_rules! libc_write {
384    ($arg:expr) => {
385        #[allow(unused_must_use)]
386        {
387            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
388            stm.write_str($arg);
389        }
390    };
391}
392
393/// Macro for printing a static string to the standard error.
394///
395/// Does not panic on failure to write - instead silently ignores errors.
396#[macro_export]
397macro_rules! libc_ewrite {
398    ($arg:expr) => {{
399        #[allow(unused_must_use)]
400        {
401            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
402            stm.write_str($arg);
403        }
404    }};
405}
406
407/// Macro for printing a static string to the standard output, with a newline.
408///
409/// Does not panic on failure to write - instead silently ignores errors.
410#[macro_export]
411macro_rules! libc_writeln {
412    ($arg:expr) => {
413        #[allow(unused_must_use)]
414        {
415            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
416            stm.write_str($arg);
417            stm.write_nl();
418        }
419    };
420}
421
422/// Macro for printing a static string to the standard error, with a newline.
423///
424/// Does not panic on failure to write - instead silently ignores errors.
425#[macro_export]
426macro_rules! libc_ewriteln {
427    ($arg:expr) => {{
428        #[allow(unused_must_use)]
429        {
430            let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
431            stm.write_str($arg);
432            stm.write_nl();
433        }
434    }};
435}
436
437/// Prints and returns the value of a given expression for quick and dirty
438/// debugging.
439///
440/// An example:
441///
442/// ```rust
443/// let a = 2;
444/// let b = dbg!(a * 2) + 1;
445/// //      ^-- prints: [src/main.rs:2] a * 2 = 4
446/// assert_eq!(b, 5);
447/// ```
448///
449/// See [dbg!](https://doc.rust-lang.org/std/macro.dbg.html) for full documentation.
450///
451/// You may wish to `use libc_print::std_name::*` to use a replacement
452/// `dbg!` macro instead of this longer name.
453#[macro_export]
454macro_rules! libc_dbg {
455    () => {
456        $crate::libc_eprintln!("[{}:{}]", ::core::file!(), ::core::line!())
457    };
458    ($val:expr $(,)?) => {
459        match $val {
460            tmp => {
461                $crate::libc_eprintln!("[{}:{}] {} = {:#?}", ::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp);
462                tmp
463            }
464        }
465    };
466    ($($val:expr),+ $(,)?) => {
467        ($($crate::libc_dbg!($val)),+,)
468    };
469}
470
471/// This package contains the `libc_print` macros, but using the stdlib names
472/// such as `println!`, `print!`, etc.
473pub mod std_name {
474    pub use super::libc_dbg as dbg;
475    pub use super::libc_eprint as eprint;
476    pub use super::libc_eprintln as eprintln;
477    pub use super::libc_print as print;
478    pub use super::libc_println as println;
479
480    #[cfg(test)]
481    mod tests_std_name {
482        use super::{eprintln, println};
483
484        #[test]
485        fn test_stdout() {
486            println!("stdout fd = {}", crate::__LIBC_STDOUT);
487        }
488
489        #[test]
490        fn test_stderr() {
491            eprintln!("stderr fd = {}", crate::__LIBC_STDERR);
492        }
493    }
494}
495
496#[cfg(test)]
497mod tests {
498    #[test]
499    fn test_stdout() {
500        super::libc_println!("stdout fd = {}", super::__LIBC_STDOUT);
501    }
502
503    #[test]
504    fn test_stderr() {
505        super::libc_eprintln!("stderr fd = {}", super::__LIBC_STDERR);
506    }
507
508    #[test]
509    fn test_stdout_write() {
510        super::libc_writeln!("stdout!");
511    }
512
513    #[test]
514    fn test_stderr_write() {
515        super::libc_ewriteln!("stderr!");
516    }
517
518    #[test]
519    fn test_dbg() {
520        let a = 2;
521        let b = libc_dbg!(a * 2) + 1;
522        assert_eq!(b, 5);
523    }
524
525    #[test]
526    fn test_in_closure_expression() {
527        use super::std_name::*;
528        // https://github.com/mmastrac/rust-libc-print/issues/86
529        let _ = Result::<(), ()>::Ok(()).unwrap_or_else(|err| eprintln!("error: {:?}", err));
530    }
531}