Skip to main content

uucore_procs/
lib.rs

1// This file is part of the uutils coreutils package.
2//
3// For the full copyright and license information, please view the LICENSE
4// file that was distributed with this source code.
5//
6// spell-checker:ignore SIGSEGV
7
8//! A collection of procedural macros for uutils.
9#![deny(missing_docs)]
10
11use proc_macro::TokenStream;
12use quote::quote;
13
14//## rust proc-macro background info
15//* ref: <https://dev.to/naufraghi/procedural-macro-in-rust-101-k3f> @@ <http://archive.is/Vbr5e>
16//* ref: [path construction from LitStr](https://oschwald.github.io/maxminddb-rust/syn/struct.LitStr.html) @@ <http://archive.is/8YDua>
17
18/// A procedural macro to define the main function of a uutils binary.
19///
20/// This macro handles:
21/// - SIGPIPE state capture at process startup (before Rust runtime overrides it)
22/// - SIGPIPE restoration to default if parent didn't explicitly ignore it
23/// - Disabling Rust signal handlers for proper core dumps
24/// - Error handling and exit code management
25#[proc_macro_attribute]
26pub fn main(args: TokenStream, stream: TokenStream) -> TokenStream {
27    let stream = proc_macro2::TokenStream::from(stream);
28    // Some utils e.g. true does not require signals
29    let signals = !args.to_string().contains("no_signals");
30
31    let new = quote!(
32        // Initialize SIGPIPE state capture at process startup (Unix only).
33        // This must be at module level to set up the .init_array static that runs
34        // before main() to capture whether SIGPIPE was ignored by the parent process.
35        #[cfg(all(#signals, unix))]
36        uucore::init_startup_state_capture!();
37
38        pub fn uumain(args: impl uucore::Args) -> i32 {
39            #stream
40
41            // Restore SIGPIPE to default if it wasn't explicitly ignored by parent.
42            // The Rust runtime ignores SIGPIPE, but we need to respect the parent's
43            // signal disposition for proper pipeline behavior (GNU compatibility).
44            // needed even for true --version
45            #[cfg(unix)]
46            if !uucore::signals::sigpipe_was_ignored() {
47                let _ = uucore::signals::enable_pipe_errors();
48            }
49
50            // disable rust signal handlers (otherwise processes don't dump core after e.g. one SIGSEGV)
51            #[cfg(all(#signals, unix))]
52            uucore::disable_rust_signal_handlers().expect("Disabling rust signal handlers failed");
53            let result = uumain(args);
54            match result {
55                Ok(()) => uucore::error::get_exit_code(),
56                Err(e) => {
57                    let s = format!("{e}");
58                    if s != "" {
59                        uucore::show_error!("{s}");
60                    }
61                    if e.usage() {
62                        use std::io::{stderr, Write as _};
63                        let _ = writeln!(stderr(),"Try '{} --help' for more information.", uucore::execution_phrase());
64                    }
65                    e.code()
66                }
67            }
68        }
69    );
70
71    TokenStream::from(new)
72}