syd 3.58.0

rock-solid application kernel
Documentation
//
// Syd: rock-solid application kernel
// src/utils/syd-sum.rs: Calculate checksum of the given file or standard input.
//
// Copyright (c) 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

use std::{io::Write, process::ExitCode};

use nix::errno::Errno;
use syd::{
    eprintfln,
    hash::{hash, hash_list, hex_encode_lower},
    printfln,
};
// Set global allocator to GrapheneOS allocator.
#[cfg(all(
    not(target_os = "android"),
    not(target_arch = "loongarch64"),
    not(target_arch = "riscv64"),
    target_page_size_4k,
    target_pointer_width = "64"
))]
#[global_allocator]
static GLOBAL: hardened_malloc::HardenedMalloc = hardened_malloc::HardenedMalloc;

syd::main! {
    use lexopt::prelude::*;

    syd::set_sigpipe_dfl()?;

    // Parse CLI options.
    let mut opt_func: Option<String> = None;
    let mut opt_bino = false; // Binary output?
    let mut opt_path = None;

    let mut parser = lexopt::Parser::from_env();
    while let Some(arg) = parser.next()? {
        match arg {
            Short('h') => {
                help()?;
                return Ok(ExitCode::SUCCESS);
            }
            Short('b') => opt_bino = true,
            Short('x') => opt_bino = false,
            Short('a') => opt_func = Some(parser.value()?.to_str().ok_or(Errno::EINVAL)?.to_string()),
            Value(path) if opt_path.is_none() => {
                opt_path = Some(path.to_str().ok_or(Errno::EINVAL).map(String::from)?)
            }
            _ => return Err(arg.unexpected().into()),
        }
    }

    let opt_func = match opt_func {
        Some(f) if f == "list" => {
            for &(name, size) in hash_list() {
                printfln!("{name:<12} {size}")?;
            }
            return Ok(ExitCode::SUCCESS);
        }
        Some(f) => f,
        None => {
            eprintfln!("Error: -a <algorithm> is required.")?;
            eprintfln!("Run syd-sum -h for help.")?;
            return Ok(ExitCode::FAILURE);
        }
    };

    match opt_path.as_deref() {
        None | Some("-") => {
            let digest = hash(&opt_func, std::io::stdin())?;
            if opt_bino {
                std::io::stdout().write_all(&digest)?;
            } else {
                printfln!("{}", hex_encode_lower(&digest)?)?;
            }
        }
        Some(path) => {
            #[expect(clippy::disallowed_methods)]
            #[expect(clippy::disallowed_types)]
            let file = std::fs::File::open(path)?;
            let digest = hash(&opt_func, &file)?;
            if opt_bino {
                std::io::stdout().write_all(&digest)?;
            } else {
                printfln!("{} {path}", hex_encode_lower(&digest)?)?;
            }
        }
    }

    Ok(ExitCode::SUCCESS)
}

fn help() -> Result<(), Errno> {
    printfln!("Usage: syd-sum -a <algorithm> [-bhx] <file|->")?;
    printfln!("Given a file, print the checksum of the file.")?;
    printfln!("Given no positional arguments, calculate the checksum of standard input.")?;
    printfln!()?;
    printfln!("  -a <alg>  Hash algorithm (required).")?;
    printfln!("            Use `-a list' to list available algorithms and their digest sizes.")?;
    printfln!("            Examples: sha256, sha512, sha3-512, blake2b-256, md5, crc32c")?;
    printfln!("  -b        Print binary output rather than hex-encoded string.")?;
    printfln!("  -x        Print hexadecimal output (default).")?;
    printfln!("  -h        Display this help.")?;
    Ok(())
}