Merge remote-tracking branch 'origin/main' into lossy-string-cleanup

This commit is contained in:
Jan Verbeek
2025-11-21 21:59:40 +01:00
34 changed files with 1520 additions and 1305 deletions
+18 -18
View File
@@ -36,7 +36,7 @@ jobs:
name: Style/cargo-deny
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: EmbarkStudios/cargo-deny-action@v2
@@ -55,7 +55,7 @@ jobs:
- { os: macos-latest , features: "feat_Tier1,feat_require_unix,feat_require_unix_utmpx" }
- { os: windows-latest , features: feat_os_windows }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@nightly
@@ -109,7 +109,7 @@ jobs:
# - { os: macos-latest , features: feat_os_macos }
# - { os: windows-latest , features: feat_os_windows }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@master
@@ -168,7 +168,7 @@ jobs:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@master
@@ -238,7 +238,7 @@ jobs:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -265,7 +265,7 @@ jobs:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -398,7 +398,7 @@ jobs:
- { os: macos-latest , features: feat_os_macos }
- { os: windows-latest , features: feat_os_windows }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -427,7 +427,7 @@ jobs:
- { os: macos-latest , features: feat_os_macos }
- { os: windows-latest , features: feat_os_windows }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@nightly
@@ -453,7 +453,7 @@ jobs:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -593,7 +593,7 @@ jobs:
- { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows }
- { os: windows-latest , target: aarch64-pc-windows-msvc , features: feat_os_windows, use-cross: use-cross , skip-tests: true }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@master
@@ -875,7 +875,7 @@ jobs:
run: |
## VARs setup
echo "TEST_SUMMARY_FILE=busybox-result.json" >> $GITHUB_OUTPUT
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: Swatinem/rust-cache@v2
@@ -958,7 +958,7 @@ jobs:
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
TEST_SUMMARY_FILE="toybox-result.json"
outputs TEST_SUMMARY_FILE
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@master
@@ -1047,7 +1047,7 @@ jobs:
# FIXME: Re-enable Code Coverage on windows, which currently fails due to "profiler_builtins". See #6686.
# - { os: windows-latest , features: windows, toolchain: nightly-x86_64-pc-windows-gnu }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.job.toolchain }}
@@ -1164,7 +1164,7 @@ jobs:
- { os: macos-latest , features: feat_os_macos }
- { os: windows-latest , features: feat_os_windows }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -1191,7 +1191,7 @@ jobs:
- { os: macos-latest , features: feat_os_macos }
# - { os: windows-latest , features: feat_os_windows } https://github.com/uutils/coreutils/issues/7044
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -1212,7 +1212,7 @@ jobs:
needs: [ min_version, deps ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -1254,7 +1254,7 @@ jobs:
- { os: windows-latest , features: feat_os_windows }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -1275,7 +1275,7 @@ jobs:
needs: [ min_version, deps ]
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
+2 -2
View File
@@ -29,7 +29,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Run ShellCheck
@@ -47,7 +47,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Setup shfmt
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Initialize job variables
+5 -5
View File
@@ -37,7 +37,7 @@ jobs:
steps:
#### Get the code, setup cache
- name: Checkout code (uutils)
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
path: 'uutils'
persist-credentials: false
@@ -59,7 +59,7 @@ jobs:
with:
workspaces: "./uutils -> target"
- name: Checkout code (GNU coreutils)
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: 'coreutils/coreutils'
path: 'gnu'
@@ -170,7 +170,7 @@ jobs:
steps:
#### Get the code, setup cache
- name: Checkout code (uutils)
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
path: 'uutils'
persist-credentials: false
@@ -192,7 +192,7 @@ jobs:
with:
workspaces: "./uutils -> target"
- name: Checkout code (GNU coreutils)
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: 'coreutils/coreutils'
path: 'gnu'
@@ -329,7 +329,7 @@ jobs:
outputs TEST_SUMMARY_FILE AGGREGATED_SUMMARY_FILE
- name: Checkout code (uutils)
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
path: 'uutils'
persist-credentials: false
+1 -1
View File
@@ -80,7 +80,7 @@ jobs:
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Collect information about runner
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
- { package: uu_wc }
- { package: uu_factor }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
+6 -6
View File
@@ -32,7 +32,7 @@ jobs:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@master
@@ -82,7 +82,7 @@ jobs:
- { os: macos-latest , features: feat_os_macos }
- { os: windows-latest , features: feat_os_windows }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@master
@@ -156,7 +156,7 @@ jobs:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Initialize workflow variables
@@ -194,7 +194,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
@@ -206,7 +206,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
@@ -230,7 +230,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Run test in devcontainer
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install/setup prerequisites
shell: bash
+2 -2
View File
@@ -34,7 +34,7 @@ jobs:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: Swatinem/rust-cache@v2
@@ -130,7 +130,7 @@ jobs:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: Swatinem/rust-cache@v2
+4 -4
View File
@@ -21,7 +21,7 @@ jobs:
name: Build and test uufuzz examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -55,7 +55,7 @@ jobs:
name: Build the fuzzers
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@nightly
@@ -99,7 +99,7 @@ jobs:
- { name: fuzz_non_utf8_paths, should_pass: true }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@nightly
@@ -211,7 +211,7 @@ jobs:
runs-on: ubuntu-latest
if: always()
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Download all stats
+12 -12
View File
@@ -36,7 +36,7 @@ jobs:
- { os: macos-latest , features: "feat_os_macos" }
- { os: windows-latest , features: "feat_os_windows" }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -76,7 +76,7 @@ jobs:
name: L10n/Fluent Syntax Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Setup Python
@@ -131,7 +131,7 @@ jobs:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -301,7 +301,7 @@ jobs:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -416,7 +416,7 @@ jobs:
- { os: ubuntu-latest , features: "feat_os_unix" }
- { os: macos-latest , features: "feat_os_macos" }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -567,7 +567,7 @@ jobs:
- { os: ubuntu-latest , features: "feat_os_unix" }
- { os: macos-latest , features: "feat_os_macos" }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -900,7 +900,7 @@ jobs:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -1131,7 +1131,7 @@ jobs:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -1151,7 +1151,7 @@ jobs:
name: L10n/Locale Embedding - Cat Utility
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -1183,7 +1183,7 @@ jobs:
name: L10n/Locale Embedding - Ls Utility
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -1215,7 +1215,7 @@ jobs:
name: L10n/Locale Embedding - Multicall Binary
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
@@ -1252,7 +1252,7 @@ jobs:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
+2 -2
View File
@@ -31,7 +31,7 @@ jobs:
job:
- { features: unix }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Prepare, build and test
@@ -121,7 +121,7 @@ jobs:
job:
- { features: unix }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Prepare, build and test
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
job:
- { os: windows-latest, distribution: Ubuntu-24.04, features: feat_os_unix}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Install WSL2
Generated
+13 -13
View File
@@ -345,18 +345,18 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.52"
version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8"
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.52"
version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1"
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
dependencies = [
"anstream",
"anstyle",
@@ -367,9 +367,9 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.5.60"
version = "4.5.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e602857739c5a4291dfa33b5a298aeac9006185229a700e5810a3ef7272d971"
checksum = "39615915e2ece2550c0149addac32fb5bd312c657f43845bb9088cb9c8a7c992"
dependencies = [
"clap",
]
@@ -1578,7 +1578,7 @@ dependencies = [
"portable-atomic",
"portable-atomic-util",
"serde_core",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -1876,7 +1876,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -2034,9 +2034,9 @@ dependencies = [
[[package]]
name = "parse_datetime"
version = "0.13.2"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4955561bc7aa4c40afcfd2a8c34297b13164ae9ac3b30ac348737befdc98e4c"
checksum = "acea383beda9652270f3c9678d83aa58cbfc16880343cae0c0c8c7d6c0974132"
dependencies = [
"jiff",
"num-traits",
@@ -2441,7 +2441,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.11.0",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -2747,7 +2747,7 @@ dependencies = [
"getrandom 0.3.3",
"once_cell",
"rustix",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -4410,7 +4410,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
+2 -2
View File
@@ -450,7 +450,7 @@ install: build install-manpages install-completions install-locales
mkdir -p $(INSTALLDIR_BIN)
ifneq (,$(and $(findstring stdbuf,$(UTILS)),$(findstring feat_external_libstdbuf,$(CARGOFLAGS))))
mkdir -p $(DESTDIR)$(LIBSTDBUF_DIR)
$(INSTALL) -m 755 $(BUILDDIR)/deps/libstdbuf* $(DESTDIR)$(LIBSTDBUF_DIR)/
$(INSTALL) -m 755 $(BUILDDIR)/deps/libstdbuf.* $(DESTDIR)$(LIBSTDBUF_DIR)/
endif
ifeq (${MULTICALL}, y)
$(INSTALL) -m 755 $(BUILDDIR)/coreutils $(INSTALLDIR_BIN)/$(PROG_PREFIX)coreutils
@@ -473,7 +473,7 @@ endif
uninstall:
ifneq ($(OS),Windows_NT)
rm -f $(DESTDIR)$(LIBSTDBUF_DIR)/libstdbuf*
rm -f $(DESTDIR)$(LIBSTDBUF_DIR)/libstdbuf.*
-rm -d $(DESTDIR)$(LIBSTDBUF_DIR) 2>/dev/null || true
endif
ifeq (${MULTICALL}, y)
+27 -311
View File
@@ -3,271 +3,25 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (ToDO) fname, algo
// spell-checker:ignore (ToDO) fname, algo, bitlen
use clap::builder::ValueParser;
use clap::{Arg, ArgAction, Command};
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{BufReader, Read, Write, stdin, stdout};
use std::iter;
use std::path::Path;
use uucore::checksum::compute::{
ChecksumComputeOptions, figure_out_output_format, perform_checksum_computation,
};
use uucore::checksum::validate::{
ChecksumValidateOptions, ChecksumVerbose, perform_checksum_validation,
};
use uucore::checksum::{
ALGORITHM_OPTIONS_BLAKE2B, ALGORITHM_OPTIONS_BSD, ALGORITHM_OPTIONS_CRC,
ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SHA2, ALGORITHM_OPTIONS_SHA3,
ALGORITHM_OPTIONS_SYSV, ChecksumError, ChecksumOptions, ChecksumVerbose, HashAlgorithm,
LEGACY_ALGORITHMS, SUPPORTED_ALGORITHMS, calculate_blake2b_length_str, detect_algo,
digest_reader, perform_checksum_validation, sanitize_sha2_sha3_length_str,
AlgoKind, ChecksumError, SUPPORTED_ALGORITHMS, SizedAlgoKind, calculate_blake2b_length_str,
sanitize_sha2_sha3_length_str,
};
use uucore::display::Quotable;
use uucore::translate;
use uucore::{
encoding,
error::{FromIo, UResult, USimpleError},
format_usage,
line_ending::LineEnding,
os_str_as_bytes, show,
sum::Digest,
};
struct Options {
algo_name: &'static str,
digest: Box<dyn Digest + 'static>,
output_bits: usize,
length: Option<usize>,
output_format: OutputFormat,
line_ending: LineEnding,
}
/// Reading mode used to compute digest.
///
/// On most linux systems, this is irrelevant, as there is no distinction
/// between text and binary files. Refer to GNU's cksum documentation for more
/// information.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ReadingMode {
Binary,
Text,
}
impl ReadingMode {
#[inline]
fn as_char(&self) -> char {
match self {
Self::Binary => '*',
Self::Text => ' ',
}
}
}
/// Whether to write the digest as hexadecimal or encoded in base64.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DigestFormat {
Hexadecimal,
Base64,
}
impl DigestFormat {
#[inline]
fn is_base64(&self) -> bool {
*self == Self::Base64
}
}
/// Holds the representation that shall be used for printing a checksum line
#[derive(Debug, PartialEq, Eq)]
enum OutputFormat {
/// Raw digest
Raw,
/// Selected for older algorithms which had their custom formatting
///
/// Default for crc, sysv, bsd
Legacy,
/// `$ALGO_NAME ($FILENAME) = $DIGEST`
Tagged(DigestFormat),
/// '$DIGEST $FLAG$FILENAME'
/// where 'flag' depends on the reading mode
///
/// Default for standalone checksum utilities
Untagged(DigestFormat, ReadingMode),
}
impl OutputFormat {
#[inline]
fn is_raw(&self) -> bool {
*self == Self::Raw
}
}
fn print_legacy_checksum(
options: &Options,
filename: &OsStr,
sum: &str,
size: usize,
) -> UResult<()> {
debug_assert!(LEGACY_ALGORITHMS.contains(&options.algo_name));
// Print the sum
match options.algo_name {
ALGORITHM_OPTIONS_SYSV => print!(
"{} {}",
sum.parse::<u16>().unwrap(),
size.div_ceil(options.output_bits),
),
ALGORITHM_OPTIONS_BSD => {
// The BSD checksum output is 5 digit integer
let bsd_width = 5;
print!(
"{:0bsd_width$} {:bsd_width$}",
sum.parse::<u16>().unwrap(),
size.div_ceil(options.output_bits),
);
}
ALGORITHM_OPTIONS_CRC | ALGORITHM_OPTIONS_CRC32B => {
print!("{sum} {size}");
}
_ => unreachable!("Not a legacy algorithm"),
}
// Print the filename after a space if not stdin
if filename != "-" {
print!(" ");
let _dropped_result = stdout().write_all(os_str_as_bytes(filename)?);
}
Ok(())
}
fn print_tagged_checksum(options: &Options, filename: &OsStr, sum: &String) -> UResult<()> {
// Print algo name and opening parenthesis.
print!(
"{} (",
match (options.algo_name, options.length) {
// Multiply the length by 8, as we want to print the length in bits.
(ALGORITHM_OPTIONS_BLAKE2B, Some(l)) => format!("BLAKE2b-{}", l * 8),
(ALGORITHM_OPTIONS_BLAKE2B, None) => "BLAKE2b".into(),
(name, _) => name.to_ascii_uppercase(),
}
);
// Print filename
let _dropped_result = stdout().write_all(os_str_as_bytes(filename)?);
// Print closing parenthesis and sum
print!(") = {sum}");
Ok(())
}
fn print_untagged_checksum(
filename: &OsStr,
sum: &String,
reading_mode: ReadingMode,
) -> UResult<()> {
// Print checksum and reading mode flag
print!("{sum} {}", reading_mode.as_char());
// Print filename
let _dropped_result = stdout().write_all(os_str_as_bytes(filename)?);
Ok(())
}
/// Calculate checksum
///
/// # Arguments
///
/// * `options` - CLI options for the assigning checksum algorithm
/// * `files` - A iterator of [`OsStr`] which is a bunch of files that are using for calculating checksum
fn cksum<'a, I>(mut options: Options, files: I) -> UResult<()>
where
I: Iterator<Item = &'a OsStr>,
{
let mut files = files.peekable();
while let Some(filename) = files.next() {
// Check that in raw mode, we are not provided with several files.
if options.output_format.is_raw() && files.peek().is_some() {
return Err(Box::new(ChecksumError::RawMultipleFiles));
}
let filepath = Path::new(filename);
let stdin_buf;
let file_buf;
if filepath.is_dir() {
show!(USimpleError::new(
1,
translate!("cksum-error-is-directory", "file" => filepath.maybe_quote())
));
continue;
}
// Handle the file input
let mut file = BufReader::new(if filename == "-" {
stdin_buf = stdin();
Box::new(stdin_buf) as Box<dyn Read>
} else {
file_buf = match File::open(filepath) {
Ok(file) => file,
Err(err) => {
show!(err.map_err_context(|| filepath.maybe_quote().to_string()));
continue;
}
};
Box::new(file_buf) as Box<dyn Read>
});
let (sum_hex, sz) =
digest_reader(&mut options.digest, &mut file, false, options.output_bits)
.map_err_context(|| translate!("cksum-error-failed-to-read-input"))?;
// Encodes the sum if df is Base64, leaves as-is otherwise.
let encode_sum = |sum: String, df: DigestFormat| {
if df.is_base64() {
encoding::for_cksum::BASE64.encode(&hex::decode(sum).unwrap())
} else {
sum
}
};
match options.output_format {
OutputFormat::Raw => {
let bytes = match options.algo_name {
ALGORITHM_OPTIONS_CRC | ALGORITHM_OPTIONS_CRC32B => {
sum_hex.parse::<u32>().unwrap().to_be_bytes().to_vec()
}
ALGORITHM_OPTIONS_SYSV | ALGORITHM_OPTIONS_BSD => {
sum_hex.parse::<u16>().unwrap().to_be_bytes().to_vec()
}
_ => hex::decode(sum_hex).unwrap(),
};
// Cannot handle multiple files anyway, output immediately.
stdout().write_all(&bytes)?;
return Ok(());
}
OutputFormat::Legacy => {
print_legacy_checksum(&options, filename, &sum_hex, sz)?;
}
OutputFormat::Tagged(digest_format) => {
print_tagged_checksum(&options, filename, &encode_sum(sum_hex, digest_format))?;
}
OutputFormat::Untagged(digest_format, reading_mode) => {
print_untagged_checksum(
filename,
&encode_sum(sum_hex, digest_format),
reading_mode,
)?;
}
}
print!("{}", options.line_ending);
}
Ok(())
}
use uucore::error::UResult;
use uucore::line_ending::LineEnding;
use uucore::{format_usage, translate};
mod options {
pub const ALGORITHM: &str = "algorithm";
@@ -330,46 +84,9 @@ fn handle_tag_text_binary_flags<S: AsRef<OsStr>>(
Ok((tag, binary))
}
/// Use already-processed arguments to decide the output format.
fn figure_out_output_format(
algo: &HashAlgorithm,
tag: bool,
binary: bool,
raw: bool,
base64: bool,
) -> OutputFormat {
// Raw output format takes precedence over anything else.
if raw {
return OutputFormat::Raw;
}
// Then, if the algo is legacy, takes precedence over the rest
if LEGACY_ALGORITHMS.contains(&algo.name) {
return OutputFormat::Legacy;
}
let digest_format = if base64 {
DigestFormat::Base64
} else {
DigestFormat::Hexadecimal
};
// After that, decide between tagged and untagged output
if tag {
OutputFormat::Tagged(digest_format)
} else {
let reading_mode = if binary {
ReadingMode::Binary
} else {
ReadingMode::Text
};
OutputFormat::Untagged(digest_format, reading_mode)
}
}
/// Sanitize the `--length` argument depending on `--algorithm` and `--length`.
fn maybe_sanitize_length(
algo_cli: Option<&str>,
algo_cli: Option<AlgoKind>,
input_length: Option<&str>,
) -> UResult<Option<usize>> {
match (algo_cli, input_length) {
@@ -377,12 +94,12 @@ fn maybe_sanitize_length(
(_, None) => Ok(None),
// For SHA2 and SHA3, if a length is provided, ensure it is correct.
(Some(algo @ (ALGORITHM_OPTIONS_SHA2 | ALGORITHM_OPTIONS_SHA3)), Some(s_len)) => {
(Some(algo @ (AlgoKind::Sha2 | AlgoKind::Sha3)), Some(s_len)) => {
sanitize_sha2_sha3_length_str(algo, s_len).map(Some)
}
// For BLAKE2b, if a length is provided, validate it.
(Some(ALGORITHM_OPTIONS_BLAKE2B), Some(len)) => calculate_blake2b_length_str(len),
(Some(AlgoKind::Blake2b), Some(len)) => calculate_blake2b_length_str(len),
// For any other provided algorithm, check if length is 0.
// Otherwise, this is an error.
@@ -399,7 +116,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let algo_cli = matches
.get_one::<String>(options::ALGORITHM)
.map(String::as_str);
.map(AlgoKind::from_cksum)
.transpose()?;
let input_length = matches
.get_one::<String>(options::LENGTH)
@@ -416,7 +134,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if check {
// cksum does not support '--check'ing legacy algorithms
if algo_cli.is_some_and(|algo_name| LEGACY_ALGORITHMS.contains(&algo_name)) {
if algo_cli.is_some_and(AlgoKind::is_legacy) {
return Err(ChecksumError::AlgorithmNotSupportedWithCheck.into());
}
@@ -436,8 +154,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// Execute the checksum validation based on the presence of files or the use of stdin
let verbose = ChecksumVerbose::new(status, quiet, warn);
let opts = ChecksumOptions {
binary: binary_flag,
let opts = ChecksumValidateOptions {
ignore_missing,
strict,
verbose,
@@ -449,31 +166,30 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// Not --check
// Set the default algorithm to CRC when not '--check'ing.
let algo_name = algo_cli.unwrap_or(ALGORITHM_OPTIONS_CRC);
let algo_kind = algo_cli.unwrap_or(AlgoKind::Crc);
let (tag, binary) = handle_tag_text_binary_flags(std::env::args_os())?;
let algo = detect_algo(algo_name, length)?;
let algo = SizedAlgoKind::from_unsized(algo_kind, length)?;
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO));
let output_format = figure_out_output_format(
&algo,
algo,
tag,
binary,
matches.get_flag(options::RAW),
matches.get_flag(options::BASE64),
);
let opts = Options {
algo_name: algo.name,
digest: (algo.create_fn)(),
output_bits: algo.bits,
length,
let opts = ChecksumComputeOptions {
algo_kind: algo,
output_format,
line_ending,
binary: false,
no_names: false,
};
cksum(opts, files)?;
perform_checksum_computation(opts, files)?;
Ok(())
}
+9
View File
@@ -434,6 +434,15 @@ fn process_utf8_line<W: Write>(line: &str, ctx: &mut FoldContext<'_, W>) -> URes
let mut iter = line.char_indices().peekable();
while let Some((byte_idx, ch)) = iter.next() {
// Include combining characters with the base character
while let Some(&(_, next_ch)) = iter.peek() {
if unicode_width::UnicodeWidthChar::width(next_ch).unwrap_or(1) == 0 {
iter.next();
} else {
break;
}
}
let next_idx = iter.peek().map(|(idx, _)| *idx).unwrap_or(line_bytes.len());
if ch == '\n' {
+1 -1
View File
@@ -19,7 +19,7 @@ path = "src/hashsum.rs"
[dependencies]
clap = { workspace = true }
uucore = { workspace = true, features = ["checksum", "sum"] }
uucore = { workspace = true, features = ["checksum", "encoding", "sum"] }
fluent = { workspace = true }
[[bin]]
+65 -203
View File
@@ -3,54 +3,28 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (ToDO) algo, algoname, regexes, nread, nonames
// spell-checker:ignore (ToDO) algo, algoname, bitlen, regexes, nread, nonames
use clap::ArgAction;
use clap::builder::ValueParser;
use clap::value_parser;
use clap::{Arg, ArgMatches, Command};
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{BufReader, Read, stdin};
use std::iter;
use std::num::ParseIntError;
use std::path::Path;
use uucore::checksum::ChecksumError;
use uucore::checksum::ChecksumOptions;
use uucore::checksum::ChecksumVerbose;
use uucore::checksum::HashAlgorithm;
use uucore::checksum::calculate_blake2b_length;
use uucore::checksum::create_sha3;
use uucore::checksum::detect_algo;
use uucore::checksum::digest_reader;
use uucore::checksum::escape_filename;
use uucore::checksum::perform_checksum_validation;
use uucore::display::{Quotable, print_verbatim};
use uucore::error::{UResult, strip_errno};
use uucore::format_usage;
use uucore::sum::{Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
use uucore::translate;
use clap::builder::ValueParser;
use clap::{Arg, ArgAction, ArgMatches, Command, value_parser};
use uucore::checksum::compute::{
ChecksumComputeOptions, figure_out_output_format, perform_checksum_computation,
};
use uucore::checksum::validate::{
ChecksumValidateOptions, ChecksumVerbose, perform_checksum_validation,
};
use uucore::checksum::{AlgoKind, ChecksumError, SizedAlgoKind, calculate_blake2b_length};
use uucore::error::UResult;
use uucore::line_ending::LineEnding;
use uucore::{format_usage, translate};
const NAME: &str = "hashsum";
// Using the same read buffer size as GNU
const READ_BUFFER_SIZE: usize = 32 * 1024;
struct Options<'a> {
algoname: &'static str,
digest: Box<dyn Digest + 'static>,
binary: bool,
binary_name: &'a str,
//check: bool,
tag: bool,
nonames: bool,
//status: bool,
//quiet: bool,
//strict: bool,
//warn: bool,
output_bits: usize,
zero: bool,
//ignore_missing: bool,
}
/// Creates a hasher instance based on the command-line flags.
///
@@ -64,10 +38,10 @@ struct Options<'a> {
/// the output length in bits or an Err if multiple hash algorithms are specified or if a
/// required flag is missing.
#[allow(clippy::cognitive_complexity)]
fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<HashAlgorithm> {
let mut alg: Option<HashAlgorithm> = None;
fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<(AlgoKind, Option<usize>)> {
let mut alg: Option<(AlgoKind, Option<usize>)> = None;
let mut set_or_err = |new_alg: HashAlgorithm| -> UResult<()> {
let mut set_or_err = |new_alg: (AlgoKind, Option<usize>)| -> UResult<()> {
if alg.is_some() {
return Err(ChecksumError::CombineMultipleAlgorithms.into());
}
@@ -76,80 +50,57 @@ fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<HashAlgorithm> {
};
if matches.get_flag("md5") {
set_or_err(detect_algo("md5sum", None)?)?;
set_or_err((AlgoKind::Md5, None))?;
}
if matches.get_flag("sha1") {
set_or_err(detect_algo("sha1sum", None)?)?;
set_or_err((AlgoKind::Sha1, None))?;
}
if matches.get_flag("sha224") {
set_or_err(detect_algo("sha224sum", None)?)?;
set_or_err((AlgoKind::Sha224, None))?;
}
if matches.get_flag("sha256") {
set_or_err(detect_algo("sha256sum", None)?)?;
set_or_err((AlgoKind::Sha256, None))?;
}
if matches.get_flag("sha384") {
set_or_err(detect_algo("sha384sum", None)?)?;
set_or_err((AlgoKind::Sha384, None))?;
}
if matches.get_flag("sha512") {
set_or_err(detect_algo("sha512sum", None)?)?;
set_or_err((AlgoKind::Sha512, None))?;
}
if matches.get_flag("b2sum") {
set_or_err(detect_algo("b2sum", None)?)?;
set_or_err((AlgoKind::Blake2b, None))?;
}
if matches.get_flag("b3sum") {
set_or_err(detect_algo("b3sum", None)?)?;
set_or_err((AlgoKind::Blake3, None))?;
}
if matches.get_flag("sha3") {
match matches.get_one::<usize>("bits") {
Some(bits) => set_or_err(create_sha3(*bits)?)?,
Some(bits @ (224 | 256 | 384 | 512)) => set_or_err((AlgoKind::Sha3, Some(*bits)))?,
Some(bits) => return Err(ChecksumError::InvalidLengthForSha(bits.to_string()).into()),
None => return Err(ChecksumError::LengthRequired("SHA3".into()).into()),
}
}
if matches.get_flag("sha3-224") {
set_or_err(HashAlgorithm {
name: "SHA3-224",
create_fn: Box::new(|| Box::new(Sha3_224::new())),
bits: 224,
})?;
set_or_err((AlgoKind::Sha3, Some(224)))?;
}
if matches.get_flag("sha3-256") {
set_or_err(HashAlgorithm {
name: "SHA3-256",
create_fn: Box::new(|| Box::new(Sha3_256::new())),
bits: 256,
})?;
set_or_err((AlgoKind::Sha3, Some(256)))?;
}
if matches.get_flag("sha3-384") {
set_or_err(HashAlgorithm {
name: "SHA3-384",
create_fn: Box::new(|| Box::new(Sha3_384::new())),
bits: 384,
})?;
set_or_err((AlgoKind::Sha3, Some(384)))?;
}
if matches.get_flag("sha3-512") {
set_or_err(HashAlgorithm {
name: "SHA3-512",
create_fn: Box::new(|| Box::new(Sha3_512::new())),
bits: 512,
})?;
set_or_err((AlgoKind::Sha3, Some(512)))?;
}
if matches.get_flag("shake128") {
match matches.get_one::<usize>("bits") {
Some(bits) => set_or_err(HashAlgorithm {
name: "SHAKE128",
create_fn: Box::new(|| Box::new(Shake128::new())),
bits: *bits,
})?,
Some(bits) => set_or_err((AlgoKind::Shake128, Some(*bits)))?,
None => return Err(ChecksumError::LengthRequired("SHAKE128".into()).into()),
}
}
if matches.get_flag("shake256") {
match matches.get_one::<usize>("bits") {
Some(bits) => set_or_err(HashAlgorithm {
name: "SHAKE256",
create_fn: Box::new(|| Box::new(Shake256::new())),
bits: *bits,
})?,
Some(bits) => set_or_err((AlgoKind::Shake256, Some(*bits)))?,
None => return Err(ChecksumError::LengthRequired("SHAKE256".into()).into()),
}
}
@@ -199,10 +150,10 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
None => None,
};
let algo = if is_hashsum_bin {
let (algo_kind, length) = if is_hashsum_bin {
create_algorithm_from_flags(&matches)?
} else {
detect_algo(&binary_name, length)?
(AlgoKind::from_bin_name(&binary_name)?, length)
};
let binary = if matches.get_flag("binary") {
@@ -214,9 +165,9 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
};
let check = matches.get_flag("check");
let status = matches.get_flag("status");
let quiet = matches.get_flag("quiet") || status;
let quiet = matches.get_flag("quiet");
let strict = matches.get_flag("strict");
let warn = matches.get_flag("warn") && !status;
let warn = matches.get_flag("warn");
let ignore_missing = matches.get_flag("ignore-missing");
if ignore_missing && !check {
@@ -228,16 +179,14 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
// on Windows, allow --binary/--text to be used with --check
// and keep the behavior of defaulting to binary
#[cfg(not(windows))]
let binary = {
{
let text_flag = matches.get_flag("text");
let binary_flag = matches.get_flag("binary");
if binary_flag || text_flag {
return Err(ChecksumError::BinaryTextConflict.into());
}
false
};
}
// Execute the checksum validation based on the presence of files or the use of stdin
// Determine the source of input: a list of files or stdin.
@@ -248,52 +197,51 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
let verbose = ChecksumVerbose::new(status, quiet, warn);
let opts = ChecksumOptions {
binary,
let opts = ChecksumValidateOptions {
ignore_missing,
strict,
verbose,
};
// Execute the checksum validation
return perform_checksum_validation(
input.iter().copied(),
Some(algo.name),
Some(algo.bits),
opts,
);
return perform_checksum_validation(input.iter().copied(), Some(algo_kind), length, opts);
} else if quiet {
return Err(ChecksumError::QuietNotCheck.into());
} else if strict {
return Err(ChecksumError::StrictNotCheck.into());
}
let nonames = *matches
let no_names = *matches
.try_get_one("no-names")
.unwrap_or(None)
.unwrap_or(&false);
let zero = matches.get_flag("zero");
let line_ending = LineEnding::from_zero_flag(matches.get_flag("zero"));
let opts = Options {
algoname: algo.name,
digest: (algo.create_fn)(),
output_bits: algo.bits,
let algo = SizedAlgoKind::from_unsized(algo_kind, length)?;
let opts = ChecksumComputeOptions {
algo_kind: algo,
output_format: figure_out_output_format(
algo,
matches.get_flag(options::TAG),
binary,
/* raw */ false,
/* base64: */ false,
),
line_ending,
binary,
binary_name: &binary_name,
tag: matches.get_flag("tag"),
nonames,
//status,
//quiet,
//warn,
zero,
//ignore_missing,
no_names,
};
let files = matches.get_many::<OsString>(options::FILE).map_or_else(
// No files given, read from stdin.
|| Box::new(iter::once(OsStr::new("-"))) as Box<dyn Iterator<Item = &OsStr>>,
// At least one file given, read from them.
|files| Box::new(files.map(OsStr::new)) as Box<dyn Iterator<Item = &OsStr>>,
);
// Show the hashsum of the input
match matches.get_many::<OsString>(options::FILE) {
Some(files) => hashsum(opts, files.map(|f| f.as_os_str())),
None => hashsum(opts, iter::once(OsStr::new("-"))),
}
perform_checksum_computation(opts, files)
}
mod options {
@@ -524,89 +472,3 @@ fn uu_app(binary_name: &str) -> (Command, bool) {
(command, is_hashsum_bin)
}
#[allow(clippy::cognitive_complexity)]
fn hashsum<'a, I>(mut options: Options, files: I) -> UResult<()>
where
I: Iterator<Item = &'a OsStr>,
{
let binary_marker = if options.binary { "*" } else { " " };
let mut err_found = None;
for filename in files {
let filename = Path::new(filename);
let mut file = BufReader::with_capacity(
READ_BUFFER_SIZE,
if filename == OsStr::new("-") {
Box::new(stdin()) as Box<dyn Read>
} else {
let file_buf = match File::open(filename) {
Ok(f) => f,
Err(e) => {
eprintln!(
"{}: {}: {}",
options.binary_name,
filename.maybe_quote(),
strip_errno(&e)
);
err_found = Some(ChecksumError::Io(e));
continue;
}
};
Box::new(file_buf) as Box<dyn Read>
},
);
let sum = match digest_reader(
&mut options.digest,
&mut file,
options.binary,
options.output_bits,
) {
Ok((sum, _)) => sum,
Err(e) => {
eprintln!(
"{}: {}: {}",
options.binary_name,
filename.maybe_quote(),
strip_errno(&e)
);
err_found = Some(ChecksumError::Io(e));
continue;
}
};
let (escaped_filename, prefix) = escape_filename(filename);
if options.tag {
if options.algoname == "blake2b" {
if options.digest.output_bits() == 512 {
println!("BLAKE2b ({escaped_filename}) = {sum}");
} else {
// special case for BLAKE2b with non-default output length
println!(
"BLAKE2b-{} ({escaped_filename}) = {sum}",
options.digest.output_bits()
);
}
} else {
println!(
"{prefix}{} ({escaped_filename}) = {sum}",
options.algoname.to_ascii_uppercase()
);
}
} else if options.nonames {
println!("{sum}");
} else if options.zero {
// with zero, we don't escape the filename
print!("{sum} {binary_marker}");
print_verbatim(filename).unwrap();
print!("\0");
} else {
println!("{prefix}{sum} {binary_marker}{escaped_filename}");
}
}
match err_found {
None => Ok(()),
Some(e) => Err(Box::new(e)),
}
}

Some files were not shown because too many files have changed in this diff Show More