fcoreutils 0.22.0

High-performance GNU coreutils replacement with SIMD and parallelism
Documentation
// funame — print system information
//
// Usage: uname [OPTION]...

#[cfg(unix)]
use std::ffi::CStr;
#[cfg(unix)]
use std::io::Write;
use std::process;

const TOOL_NAME: &str = "uname";
const VERSION: &str = env!("CARGO_PKG_VERSION");

#[allow(unused_variables, unused_assignments)]
fn main() {
    coreutils_rs::common::reset_sigpipe();

    let mut show_sysname = false;
    let mut show_nodename = false;
    let mut show_release = false;
    let mut show_version = false;
    let mut show_machine = false;
    let mut show_processor = false;
    let mut show_hardware = false;
    let mut show_os = false;
    let mut any_flag = false;

    for arg in std::env::args().skip(1) {
        match arg.as_str() {
            "--help" => {
                println!("Usage: {} [OPTION]...", TOOL_NAME);
                println!("Print certain system information.  With no OPTION, same as -s.");
                println!();
                println!("  -a, --all                print all information");
                println!("  -s, --kernel-name        print the kernel name");
                println!("  -n, --nodename           print the network node hostname");
                println!("  -r, --kernel-release     print the kernel release");
                println!("  -v, --kernel-version     print the kernel version");
                println!("  -m, --machine            print the machine hardware name");
                println!("  -p, --processor          print the processor type");
                println!("  -i, --hardware-platform  print the hardware platform");
                println!("  -o, --operating-system   print the operating system");
                println!("      --help     display this help and exit");
                println!("      --version  output version information and exit");
                return;
            }
            "--version" => {
                println!("{} (fcoreutils) {}", TOOL_NAME, VERSION);
                return;
            }
            "-a" | "--all" => {
                show_sysname = true;
                show_nodename = true;
                show_release = true;
                show_version = true;
                show_machine = true;
                show_processor = true;
                show_hardware = true;
                show_os = true;

                any_flag = true;
            }
            "-s" | "--kernel-name" => {
                show_sysname = true;
                any_flag = true;
            }
            "-n" | "--nodename" => {
                show_nodename = true;
                any_flag = true;
            }
            "-r" | "--kernel-release" => {
                show_release = true;
                any_flag = true;
            }
            "-v" | "--kernel-version" => {
                show_version = true;
                any_flag = true;
            }
            "-m" | "--machine" => {
                show_machine = true;
                any_flag = true;
            }
            "-p" | "--processor" => {
                show_processor = true;
                any_flag = true;
            }
            "-i" | "--hardware-platform" => {
                show_hardware = true;
                any_flag = true;
            }
            "-o" | "--operating-system" => {
                show_os = true;
                any_flag = true;
            }
            s if s.starts_with('-') && s.len() > 1 && !s.starts_with("--") => {
                for ch in s[1..].chars() {
                    match ch {
                        'a' => {
                            show_sysname = true;
                            show_nodename = true;
                            show_release = true;
                            show_version = true;
                            show_machine = true;
                            show_processor = true;
                            show_hardware = true;
                            show_os = true;
                        }
                        's' => show_sysname = true,
                        'n' => show_nodename = true,
                        'r' => show_release = true,
                        'v' => show_version = true,
                        'm' => show_machine = true,
                        'p' => show_processor = true,
                        'i' => show_hardware = true,
                        'o' => show_os = true,
                        _ => {
                            eprintln!("{}: invalid option -- '{}'", TOOL_NAME, ch);
                            eprintln!("Try '{} --help' for more information.", TOOL_NAME);
                            process::exit(1);
                        }
                    }
                    any_flag = true;
                }
            }
            _ => {
                eprintln!("{}: extra operand '{}'", TOOL_NAME, arg);
                eprintln!("Try '{} --help' for more information.", TOOL_NAME);
                process::exit(1);
            }
        }
    }

    // Default: -s
    if !any_flag {
        show_sysname = true;
    }

    #[cfg(unix)]
    {
        // SAFETY: zeroed utsname is valid, uname fills it in
        let mut uts: libc::utsname = unsafe { std::mem::zeroed() };
        if unsafe { libc::uname(&mut uts) } != 0 {
            eprintln!("{}: cannot get system name", TOOL_NAME);
            process::exit(1);
        }

        let sysname = unsafe { CStr::from_ptr(uts.sysname.as_ptr()) };
        let nodename = unsafe { CStr::from_ptr(uts.nodename.as_ptr()) };
        let release = unsafe { CStr::from_ptr(uts.release.as_ptr()) };
        let version = unsafe { CStr::from_ptr(uts.version.as_ptr()) };
        let machine = unsafe { CStr::from_ptr(uts.machine.as_ptr()) };
        let machine_bytes = machine.to_bytes();

        // On Linux, -p (processor) and -i (hardware platform) return the machine
        // architecture. Every major distro (Debian, Ubuntu, Fedora, RHEL, Arch)
        // patches GNU coreutils to return the machine arch instead of "unknown".
        // We match the distro-patched behavior since that's what users expect.
        // On macOS, GNU uname maps arm64 -> "arm" and x86_64 -> "i386".
        #[cfg(target_os = "linux")]
        let processor = machine_bytes;
        #[cfg(target_os = "macos")]
        let processor: &[u8] = match machine_bytes {
            b"arm64" => b"arm",
            b"x86_64" => b"i386",
            _ => machine_bytes,
        };
        #[cfg(not(any(target_os = "linux", target_os = "macos")))]
        let processor: &[u8] = b"unknown";
        #[cfg(target_os = "linux")]
        let hardware = machine_bytes;
        #[cfg(target_os = "macos")]
        let hardware: &[u8] = match machine_bytes {
            b"arm64" => b"arm",
            b"x86_64" => b"i386",
            _ => machine_bytes,
        };
        #[cfg(not(any(target_os = "linux", target_os = "macos")))]
        let hardware: &[u8] = b"unknown";

        // Write fields directly to stdout, space-separated, avoiding Vec + join
        let stdout = std::io::stdout();
        let mut out = stdout.lock();
        let mut first = true;
        macro_rules! field {
            ($cond:expr, $val:expr) => {
                if $cond {
                    if !first {
                        let _ = out.write_all(b" ");
                    }
                    let _ = out.write_all($val);
                    first = false;
                }
            };
        }
        field!(show_sysname, sysname.to_bytes());
        field!(show_nodename, nodename.to_bytes());
        field!(show_release, release.to_bytes());
        field!(show_version, version.to_bytes());
        field!(show_machine, machine_bytes);
        field!(show_processor, processor);
        field!(show_hardware, hardware);
        #[cfg(target_os = "linux")]
        field!(show_os, b"GNU/Linux");
        #[cfg(target_os = "macos")]
        field!(show_os, b"Darwin");
        #[cfg(not(any(target_os = "linux", target_os = "macos")))]
        field!(show_os, b"unknown");
        let _ = out.write_all(b"\n");
    }

    #[cfg(not(unix))]
    {
        eprintln!("{}: not supported on this platform", TOOL_NAME);
        process::exit(1);
    }
}

#[cfg(all(test, unix))]
mod tests {
    use std::process::Command;

    fn cmd() -> Command {
        let mut path = std::env::current_exe().unwrap();
        path.pop();
        path.pop();
        path.push("funame");
        Command::new(path)
    }

    #[test]
    fn test_uname_default() {
        let output = cmd().output().unwrap();
        assert_eq!(output.status.code(), Some(0));
        let stdout = String::from_utf8_lossy(&output.stdout);
        let name = stdout.trim();
        assert!(!name.is_empty());
        // Default is kernel name (e.g., "Linux")
        assert!(name == "Linux" || name == "Darwin" || !name.is_empty());
    }

    #[test]
    fn test_uname_all() {
        let output = cmd().arg("-a").output().unwrap();
        assert_eq!(output.status.code(), Some(0));
        let stdout = String::from_utf8_lossy(&output.stdout);
        let parts: Vec<&str> = stdout.split_whitespace().collect();
        assert!(
            parts.len() >= 5,
            "uname -a should have multiple fields, got: {:?}",
            parts
        );
    }

    #[test]
    fn test_uname_each_flag() {
        for flag in ["-s", "-n", "-r", "-v", "-m"] {
            let output = cmd().arg(flag).output().unwrap();
            assert_eq!(output.status.code(), Some(0), "Failed for flag {}", flag);
            let stdout = String::from_utf8_lossy(&output.stdout);
            assert!(!stdout.trim().is_empty(), "Empty output for flag {}", flag);
        }
    }

    #[test]
    fn test_uname_combined() {
        let output = cmd().arg("-sr").output().unwrap();
        assert_eq!(output.status.code(), Some(0));
        let stdout = String::from_utf8_lossy(&output.stdout);
        let parts: Vec<&str> = stdout.split_whitespace().collect();
        assert_eq!(parts.len(), 2, "uname -sr should have 2 fields");
    }

    #[test]
    fn test_uname_matches_gnu() {
        // Only compare flags with deterministic output; -p and -i are
        // platform-dependent and may differ from GNU on some systems
        for flag in ["-s", "-r", "-m", "-n"] {
            let gnu = Command::new("uname").arg(flag).output();
            if let Ok(gnu) = gnu {
                if !gnu.status.success() || gnu.stdout.is_empty() {
                    continue;
                }
                let ours = cmd().arg(flag).output().unwrap();
                assert_eq!(ours.stdout, gnu.stdout, "STDOUT mismatch for {}", flag);
            }
        }
    }

    #[test]
    fn test_uname_sysname() {
        let output = cmd().arg("-s").output().unwrap();
        assert!(output.status.success());
        let stdout = String::from_utf8_lossy(&output.stdout);
        assert!(stdout.trim() == "Linux" || stdout.trim() == "Darwin");
    }

    #[test]
    fn test_uname_all_field_count() {
        let output = cmd().arg("-a").output().unwrap();
        assert!(output.status.success());
        let stdout = String::from_utf8_lossy(&output.stdout);
        // -a output should have multiple space-separated fields
        assert!(stdout.split_whitespace().count() >= 3);
    }

    #[test]
    fn test_uname_nodename() {
        let output = cmd().arg("-n").output().unwrap();
        assert!(output.status.success());
        let stdout = String::from_utf8_lossy(&output.stdout);
        assert!(!stdout.trim().is_empty());
    }

    #[test]
    fn test_uname_release() {
        let output = cmd().arg("-r").output().unwrap();
        assert!(output.status.success());
        let stdout = String::from_utf8_lossy(&output.stdout);
        assert!(!stdout.trim().is_empty());
    }

    #[test]
    fn test_uname_machine() {
        let output = cmd().arg("-m").output().unwrap();
        assert!(output.status.success());
        let stdout = String::from_utf8_lossy(&output.stdout);
        let machine = stdout.trim();
        assert!(
            !machine.is_empty(),
            "uname -m should produce non-empty output"
        );
    }

    #[test]
    fn test_uname_combined_flags() {
        let output = cmd().args(["-s", "-n", "-r"]).output().unwrap();
        assert!(output.status.success());
        let stdout = String::from_utf8_lossy(&output.stdout);
        assert!(stdout.split_whitespace().count() >= 3);
    }

    #[test]
    fn test_uname_default_equals_s() {
        let default = cmd().output().unwrap();
        let s = cmd().arg("-s").output().unwrap();
        assert_eq!(default.stdout, s.stdout);
    }
}