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}