mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
cksum: Implement option -a
Implement option -a --algorithm. Move digest to src/uucore/src/lib/features and rename it to hash. fix lint fix Cargo.toml
This commit is contained in:
parent
d21448dc74
commit
678a11dcf2
17 changed files with 509 additions and 135 deletions
27
Cargo.lock
generated
27
Cargo.lock
generated
|
@ -2053,6 +2053,15 @@ dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sm3"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f943a7c5e3089f2bd046221d1e9f4fa59396bf0fe966360983649683086215da"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
|
@ -2390,7 +2399,14 @@ dependencies = [
|
||||||
name = "uu_cksum"
|
name = "uu_cksum"
|
||||||
version = "0.0.17"
|
version = "0.0.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"blake2b_simd",
|
||||||
|
"blake3",
|
||||||
"clap",
|
"clap",
|
||||||
|
"hex",
|
||||||
|
"md-5",
|
||||||
|
"sha1",
|
||||||
|
"sha2",
|
||||||
|
"sm3",
|
||||||
"uucore",
|
"uucore",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2606,6 +2622,7 @@ dependencies = [
|
||||||
"sha1",
|
"sha1",
|
||||||
"sha2",
|
"sha2",
|
||||||
"sha3",
|
"sha3",
|
||||||
|
"sm3",
|
||||||
"uucore",
|
"uucore",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3299,17 +3316,27 @@ dependencies = [
|
||||||
name = "uucore"
|
name = "uucore"
|
||||||
version = "0.0.17"
|
version = "0.0.17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"blake2b_simd",
|
||||||
|
"blake3",
|
||||||
"clap",
|
"clap",
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"data-encoding-macro",
|
"data-encoding-macro",
|
||||||
|
"digest",
|
||||||
"dns-lookup",
|
"dns-lookup",
|
||||||
"dunce",
|
"dunce",
|
||||||
"glob",
|
"glob",
|
||||||
|
"hex",
|
||||||
"itertools",
|
"itertools",
|
||||||
"libc",
|
"libc",
|
||||||
|
"md-5",
|
||||||
|
"memchr",
|
||||||
"nix",
|
"nix",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"os_display",
|
"os_display",
|
||||||
|
"sha1",
|
||||||
|
"sha2",
|
||||||
|
"sha3",
|
||||||
|
"sm3",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
"uucore_procs",
|
"uucore_procs",
|
||||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -332,6 +332,16 @@ windows-sys = { version="0.42.0", default-features=false }
|
||||||
xattr = "0.2.3"
|
xattr = "0.2.3"
|
||||||
zip = { version = "0.6.3", default_features=false, features=["deflate"] }
|
zip = { version = "0.6.3", default_features=false, features=["deflate"] }
|
||||||
|
|
||||||
|
hex = "0.4.3"
|
||||||
|
md-5 = "0.10.5"
|
||||||
|
sha1 = "0.10.1"
|
||||||
|
sha2 = "0.10.2"
|
||||||
|
sha3 = "0.10.6"
|
||||||
|
blake2b_simd = "1.0.1"
|
||||||
|
blake3 = "1.3.2"
|
||||||
|
sm3 = "0.4.1"
|
||||||
|
digest = "0.10.6"
|
||||||
|
|
||||||
uucore = { version=">=0.0.17", package="uucore", path="src/uucore" }
|
uucore = { version=">=0.0.17", package="uucore", path="src/uucore" }
|
||||||
uucore_procs = { version=">=0.0.17", package="uucore_procs", path="src/uucore_procs" }
|
uucore_procs = { version=">=0.0.17", package="uucore_procs", path="src/uucore_procs" }
|
||||||
uu_ls = { version=">=0.0.17", path="src/uu/ls" }
|
uu_ls = { version=">=0.0.17", path="src/uu/ls" }
|
||||||
|
|
|
@ -16,7 +16,14 @@ path = "src/cksum.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { workspace=true }
|
clap = { workspace=true }
|
||||||
uucore = { workspace=true }
|
uucore = { version=">=0.0.17", package="uucore", path="../../uucore", features=["sum"] }
|
||||||
|
hex = { workspace=true }
|
||||||
|
md-5 = { workspace=true }
|
||||||
|
sha1 = { workspace=true }
|
||||||
|
sha2 = { workspace=true }
|
||||||
|
blake2b_simd = { workspace=true }
|
||||||
|
blake3 = { workspace=true }
|
||||||
|
sm3 = { workspace=true }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "cksum"
|
name = "cksum"
|
||||||
|
|
|
@ -5,109 +5,147 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) fname
|
// spell-checker:ignore (ToDO) fname, algo
|
||||||
use clap::{crate_version, Arg, Command};
|
use clap::{crate_version, Arg, Command};
|
||||||
|
use hex::encode;
|
||||||
|
use md5::Md5;
|
||||||
|
use sha1::Sha1;
|
||||||
|
use sha2::{Sha224, Sha256, Sha384, Sha512};
|
||||||
|
use std::ffi::OsStr;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, stdin, BufReader, Read};
|
use std::io::{self, stdin, BufReader, Read};
|
||||||
|
use std::iter;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use uucore::display::Quotable;
|
use uucore::{
|
||||||
use uucore::error::{FromIo, UResult};
|
error::{FromIo, UResult},
|
||||||
use uucore::{format_usage, show};
|
format_usage,
|
||||||
|
sum::{div_ceil, Digest, DigestWriter, BSD, CRC, SYSV},
|
||||||
// NOTE: CRC_TABLE_LEN *must* be <= 256 as we cast 0..CRC_TABLE_LEN to u8
|
};
|
||||||
const CRC_TABLE_LEN: usize = 256;
|
|
||||||
const CRC_TABLE: [u32; CRC_TABLE_LEN] = generate_crc_table();
|
|
||||||
|
|
||||||
const USAGE: &str = "{} [OPTIONS] [FILE]...";
|
const USAGE: &str = "{} [OPTIONS] [FILE]...";
|
||||||
const ABOUT: &str = "Print CRC and size for each file";
|
const ABOUT: &str = "Print CRC and size for each file";
|
||||||
|
|
||||||
const fn generate_crc_table() -> [u32; CRC_TABLE_LEN] {
|
fn detect_algo(program: &str) -> (&'static str, Box<dyn Digest + 'static>, usize) {
|
||||||
let mut table = [0; CRC_TABLE_LEN];
|
match program {
|
||||||
|
"sysv" => ("SYSV", Box::new(SYSV::new()) as Box<dyn Digest>, 512),
|
||||||
let mut i = 0;
|
"bsd" => ("BSD", Box::new(BSD::new()) as Box<dyn Digest>, 1024),
|
||||||
while i < CRC_TABLE_LEN {
|
"crc" => ("CRC", Box::new(CRC::new()) as Box<dyn Digest>, 256),
|
||||||
table[i] = crc_entry(i as u8);
|
"md5" => ("MD5", Box::new(Md5::new()) as Box<dyn Digest>, 128),
|
||||||
|
"sha1" => ("SHA1", Box::new(Sha1::new()) as Box<dyn Digest>, 160),
|
||||||
i += 1;
|
"sha224" => ("SHA224", Box::new(Sha224::new()) as Box<dyn Digest>, 224),
|
||||||
|
"sha256" => ("SHA256", Box::new(Sha256::new()) as Box<dyn Digest>, 256),
|
||||||
|
"sha384" => ("SHA384", Box::new(Sha384::new()) as Box<dyn Digest>, 384),
|
||||||
|
"sha512" => ("SHA512", Box::new(Sha512::new()) as Box<dyn Digest>, 512),
|
||||||
|
"blake2b" => (
|
||||||
|
"BLAKE2",
|
||||||
|
Box::new(blake2b_simd::State::new()) as Box<dyn Digest>,
|
||||||
|
512,
|
||||||
|
),
|
||||||
|
"sm3" => ("SM3", Box::new(sm3::Sm3::new()) as Box<dyn Digest>, 512),
|
||||||
|
_ => panic!("unknown algorithm"),
|
||||||
}
|
}
|
||||||
|
|
||||||
table
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn crc_entry(input: u8) -> u32 {
|
struct Options {
|
||||||
let mut crc = (input as u32) << 24;
|
algo_name: &'static str,
|
||||||
|
digest: Box<dyn Digest + 'static>,
|
||||||
let mut i = 0;
|
output_bits: usize,
|
||||||
while i < 8 {
|
|
||||||
let if_condition = crc & 0x8000_0000;
|
|
||||||
let if_body = (crc << 1) ^ 0x04c1_1db7;
|
|
||||||
let else_body = crc << 1;
|
|
||||||
|
|
||||||
// NOTE: i feel like this is easier to understand than emulating an if statement in bitwise
|
|
||||||
// ops
|
|
||||||
let condition_table = [else_body, if_body];
|
|
||||||
|
|
||||||
crc = condition_table[(if_condition != 0) as usize];
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
crc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn crc_update(crc: u32, input: u8) -> u32 {
|
fn cksum<'a, I>(mut options: Options, files: I) -> UResult<()>
|
||||||
(crc << 8) ^ CRC_TABLE[((crc >> 24) as usize ^ input as usize) & 0xFF]
|
where
|
||||||
}
|
I: Iterator<Item = &'a OsStr>,
|
||||||
|
{
|
||||||
#[inline]
|
for filename in files {
|
||||||
fn crc_final(mut crc: u32, mut length: usize) -> u32 {
|
let filename = Path::new(filename);
|
||||||
while length != 0 {
|
let stdin_buf;
|
||||||
crc = crc_update(crc, length as u8);
|
let file_buf;
|
||||||
length >>= 8;
|
let not_file = filename == OsStr::new("-");
|
||||||
}
|
let mut file = BufReader::new(if not_file {
|
||||||
|
stdin_buf = stdin();
|
||||||
!crc
|
Box::new(stdin_buf) as Box<dyn Read>
|
||||||
}
|
} else if filename.is_dir() {
|
||||||
|
|
||||||
fn init_byte_array() -> Vec<u8> {
|
|
||||||
vec![0; 1024 * 1024]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn cksum(fname: &str) -> io::Result<(u32, usize)> {
|
|
||||||
let mut crc = 0u32;
|
|
||||||
let mut size = 0usize;
|
|
||||||
|
|
||||||
let mut rd: Box<dyn Read> = match fname {
|
|
||||||
"-" => Box::new(stdin()),
|
|
||||||
_ => {
|
|
||||||
let p = Path::new(fname);
|
|
||||||
|
|
||||||
// Directories should not give an error, but should be interpreted
|
|
||||||
// as empty files to match GNU semantics.
|
|
||||||
if p.is_dir() {
|
|
||||||
Box::new(BufReader::new(io::empty())) as Box<dyn Read>
|
Box::new(BufReader::new(io::empty())) as Box<dyn Read>
|
||||||
} else {
|
} else {
|
||||||
Box::new(BufReader::new(File::open(p)?)) as Box<dyn Read>
|
file_buf =
|
||||||
}
|
File::open(filename).map_err_context(|| filename.to_str().unwrap().to_string())?;
|
||||||
}
|
Box::new(file_buf) as Box<dyn Read>
|
||||||
};
|
});
|
||||||
|
let (sum, sz) = digest_read(&mut options.digest, &mut file, options.output_bits)
|
||||||
|
.map_err_context(|| "failed to read input".to_string())?;
|
||||||
|
|
||||||
let mut bytes = init_byte_array();
|
// Refer to GNU sum.c implementation. The BSD checksum output is 5 digit integer
|
||||||
loop {
|
// https://github.com/coreutils/coreutils/blob/master/src/sum.c
|
||||||
let num_bytes = rd.read(&mut bytes)?;
|
let bsd_width = 5;
|
||||||
if num_bytes == 0 {
|
match (options.algo_name, not_file) {
|
||||||
return Ok((crc_final(crc, size), size));
|
("SYSV", true) => println!(
|
||||||
|
"{} {}",
|
||||||
|
sum.parse::<u16>().unwrap(),
|
||||||
|
div_ceil(sz, options.output_bits)
|
||||||
|
),
|
||||||
|
("SYSV", false) => println!(
|
||||||
|
"{} {} {}",
|
||||||
|
sum.parse::<u16>().unwrap(),
|
||||||
|
div_ceil(sz, options.output_bits),
|
||||||
|
filename.display()
|
||||||
|
),
|
||||||
|
("BSD", true) => println!(
|
||||||
|
"{:0bsd_width$} {:bsd_width$}",
|
||||||
|
sum.parse::<u16>().unwrap(),
|
||||||
|
div_ceil(sz, options.output_bits)
|
||||||
|
),
|
||||||
|
("BSD", false) => println!(
|
||||||
|
"{:0bsd_width$} {:bsd_width$} {}",
|
||||||
|
sum.parse::<u16>().unwrap(),
|
||||||
|
div_ceil(sz, options.output_bits),
|
||||||
|
filename.display()
|
||||||
|
),
|
||||||
|
(_, true) => println!("{sum} {sz}"),
|
||||||
|
(_, false) => println!("{sum} {sz} {}", filename.display()),
|
||||||
}
|
}
|
||||||
for &b in bytes[..num_bytes].iter() {
|
|
||||||
crc = crc_update(crc, b);
|
|
||||||
}
|
}
|
||||||
size += num_bytes;
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn digest_read<T: Read>(
|
||||||
|
digest: &mut Box<dyn Digest>,
|
||||||
|
reader: &mut BufReader<T>,
|
||||||
|
output_bits: usize,
|
||||||
|
) -> io::Result<(String, usize)> {
|
||||||
|
digest.reset();
|
||||||
|
|
||||||
|
// Read bytes from `reader` and write those bytes to `digest`.
|
||||||
|
//
|
||||||
|
// If `binary` is `false` and the operating system is Windows, then
|
||||||
|
// `DigestWriter` replaces "\r\n" with "\n" before it writes the
|
||||||
|
// bytes into `digest`. Otherwise, it just inserts the bytes as-is.
|
||||||
|
//
|
||||||
|
// In order to support replacing "\r\n", we must call `finalize()`
|
||||||
|
// in order to support the possibility that the last character read
|
||||||
|
// from the reader was "\r". (This character gets buffered by
|
||||||
|
// `DigestWriter` and only written if the following character is
|
||||||
|
// "\n". But when "\r" is the last character read, we need to force
|
||||||
|
// it to be written.)
|
||||||
|
let mut digest_writer = DigestWriter::new(digest, true);
|
||||||
|
let output_size = std::io::copy(reader, &mut digest_writer)? as usize;
|
||||||
|
digest_writer.finalize();
|
||||||
|
|
||||||
|
if digest.output_bits() > 0 {
|
||||||
|
Ok((digest.result_str(), output_size))
|
||||||
|
} else {
|
||||||
|
// Assume it's SHAKE. result_str() doesn't work with shake (as of 8/30/2016)
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
bytes.resize((output_bits + 7) / 8, 0);
|
||||||
|
digest.hash_finalize(&mut bytes);
|
||||||
|
Ok((encode(bytes), output_size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod options {
|
mod options {
|
||||||
pub static FILE: &str = "file";
|
pub static FILE: &str = "file";
|
||||||
|
pub static ALGORITHM: &str = "algorithm";
|
||||||
}
|
}
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
|
@ -116,23 +154,23 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
|
||||||
let matches = uu_app().try_get_matches_from(args)?;
|
let matches = uu_app().try_get_matches_from(args)?;
|
||||||
|
|
||||||
let files: Vec<String> = match matches.get_many::<String>(options::FILE) {
|
let algo_name: &str = match matches.get_one::<String>(options::ALGORITHM) {
|
||||||
Some(v) => v.clone().map(|v| v.to_owned()).collect(),
|
Some(v) => v,
|
||||||
None => vec![],
|
None => "crc",
|
||||||
};
|
};
|
||||||
|
|
||||||
if files.is_empty() {
|
let (name, algo, bits) = detect_algo(algo_name);
|
||||||
let (crc, size) = cksum("-")?;
|
let opts = Options {
|
||||||
println!("{crc} {size}");
|
algo_name: name,
|
||||||
return Ok(());
|
digest: algo,
|
||||||
}
|
output_bits: bits,
|
||||||
|
|
||||||
for fname in &files {
|
|
||||||
match cksum(fname.as_ref()).map_err_context(|| format!("{}", fname.maybe_quote())) {
|
|
||||||
Ok((crc, size)) => println!("{crc} {size} {fname}"),
|
|
||||||
Err(err) => show!(err),
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
match matches.get_many::<String>(options::FILE) {
|
||||||
|
Some(files) => cksum(opts, files.map(OsStr::new))?,
|
||||||
|
None => cksum(opts, iter::once(OsStr::new("-")))?,
|
||||||
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,4 +186,11 @@ pub fn uu_app() -> Command {
|
||||||
.action(clap::ArgAction::Append)
|
.action(clap::ArgAction::Append)
|
||||||
.value_hint(clap::ValueHint::FilePath),
|
.value_hint(clap::ValueHint::FilePath),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::ALGORITHM)
|
||||||
|
.long(options::ALGORITHM)
|
||||||
|
.short('a')
|
||||||
|
.help("select the digest type to use. See DIGEST below")
|
||||||
|
.value_name("ALGORITHM"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,18 +15,19 @@ edition = "2021"
|
||||||
path = "src/hashsum.rs"
|
path = "src/hashsum.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
digest = "0.10.6"
|
|
||||||
clap = { workspace=true }
|
clap = { workspace=true }
|
||||||
hex = "0.4.3"
|
|
||||||
memchr = { workspace=true }
|
|
||||||
md-5 = "0.10.5"
|
|
||||||
regex = { workspace=true }
|
|
||||||
sha1 = "0.10.1"
|
|
||||||
sha2 = "0.10.2"
|
|
||||||
sha3 = "0.10.6"
|
|
||||||
blake2b_simd = "1.0.1"
|
|
||||||
blake3 = "1.3.2"
|
|
||||||
uucore = { workspace=true }
|
uucore = { workspace=true }
|
||||||
|
memchr = { workspace=true }
|
||||||
|
regex = { workspace=true }
|
||||||
|
hex = { workspace=true }
|
||||||
|
md-5 = { workspace=true }
|
||||||
|
sha1 = { workspace=true }
|
||||||
|
sha2 = { workspace=true }
|
||||||
|
sha3 = { workspace=true }
|
||||||
|
blake2b_simd = { workspace=true }
|
||||||
|
blake3 = { workspace=true }
|
||||||
|
sm3 = { workspace=true }
|
||||||
|
digest = { workspace=true }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "hashsum"
|
name = "hashsum"
|
||||||
|
|
|
@ -9,11 +9,6 @@
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) algo, algoname, regexes, nread, nonames
|
// spell-checker:ignore (ToDO) algo, algoname, regexes, nread, nonames
|
||||||
|
|
||||||
mod digest;
|
|
||||||
|
|
||||||
use self::digest::Digest;
|
|
||||||
use self::digest::DigestWriter;
|
|
||||||
|
|
||||||
use clap::builder::ValueParser;
|
use clap::builder::ValueParser;
|
||||||
use clap::crate_version;
|
use clap::crate_version;
|
||||||
use clap::ArgAction;
|
use clap::ArgAction;
|
||||||
|
@ -36,6 +31,7 @@ use uucore::crash;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UError, UResult};
|
use uucore::error::{FromIo, UError, UResult};
|
||||||
use uucore::show_warning;
|
use uucore::show_warning;
|
||||||
|
use uucore::sum::{Digest, DigestWriter};
|
||||||
|
|
||||||
const NAME: &str = "hashsum";
|
const NAME: &str = "hashsum";
|
||||||
|
|
||||||
|
@ -680,7 +676,7 @@ fn digest_reader<T: Read>(
|
||||||
// Assume it's SHAKE. result_str() doesn't work with shake (as of 8/30/2016)
|
// Assume it's SHAKE. result_str() doesn't work with shake (as of 8/30/2016)
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
bytes.resize((output_bits + 7) / 8, 0);
|
bytes.resize((output_bits + 7) / 8, 0);
|
||||||
digest.result(&mut bytes);
|
digest.hash_finalize(&mut bytes);
|
||||||
Ok(encode(bytes))
|
Ok(encode(bytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,17 @@ libc = { version="0.2.137", optional=true }
|
||||||
once_cell = { workspace=true }
|
once_cell = { workspace=true }
|
||||||
os_display = "0.1.3"
|
os_display = "0.1.3"
|
||||||
|
|
||||||
|
digest = { workspace=true }
|
||||||
|
hex = { workspace=true }
|
||||||
|
memchr = { workspace=true }
|
||||||
|
md-5 = { workspace=true }
|
||||||
|
sha1 = { workspace=true }
|
||||||
|
sha2 = { workspace=true }
|
||||||
|
sha3 = { workspace=true }
|
||||||
|
blake2b_simd = { workspace=true }
|
||||||
|
blake3 = { workspace=true }
|
||||||
|
sm3 = { workspace=true }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
walkdir = { workspace=true, optional=true }
|
walkdir = { workspace=true, optional=true }
|
||||||
nix = { workspace=true, features = ["fs", "uio", "zerocopy"] }
|
nix = { workspace=true, features = ["fs", "uio", "zerocopy"] }
|
||||||
|
@ -66,3 +77,4 @@ utf8 = []
|
||||||
utmpx = ["time", "time/macros", "libc", "dns-lookup"]
|
utmpx = ["time", "time/macros", "libc", "dns-lookup"]
|
||||||
wide = []
|
wide = []
|
||||||
pipes = []
|
pipes = []
|
||||||
|
sum = []
|
||||||
|
|
|
@ -12,6 +12,8 @@ pub mod lines;
|
||||||
pub mod memo;
|
pub mod memo;
|
||||||
#[cfg(feature = "ringbuffer")]
|
#[cfg(feature = "ringbuffer")]
|
||||||
pub mod ringbuffer;
|
pub mod ringbuffer;
|
||||||
|
#[cfg(feature = "sum")]
|
||||||
|
pub mod sum;
|
||||||
#[cfg(feature = "memo")]
|
#[cfg(feature = "memo")]
|
||||||
mod tokenize;
|
mod tokenize;
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@ pub trait Digest {
|
||||||
fn new() -> Self
|
fn new() -> Self
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
fn input(&mut self, input: &[u8]);
|
fn hash_update(&mut self, input: &[u8]);
|
||||||
fn result(&mut self, out: &mut [u8]);
|
fn hash_finalize(&mut self, out: &mut [u8]);
|
||||||
fn reset(&mut self);
|
fn reset(&mut self);
|
||||||
fn output_bits(&self) -> usize;
|
fn output_bits(&self) -> usize;
|
||||||
fn output_bytes(&self) -> usize {
|
fn output_bytes(&self) -> usize {
|
||||||
|
@ -25,7 +25,7 @@ pub trait Digest {
|
||||||
}
|
}
|
||||||
fn result_str(&mut self) -> String {
|
fn result_str(&mut self) -> String {
|
||||||
let mut buf: Vec<u8> = vec![0; self.output_bytes()];
|
let mut buf: Vec<u8> = vec![0; self.output_bytes()];
|
||||||
self.result(&mut buf);
|
self.hash_finalize(&mut buf);
|
||||||
encode(buf)
|
encode(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,11 +35,11 @@ impl Digest for blake2b_simd::State {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input(&mut self, input: &[u8]) {
|
fn hash_update(&mut self, input: &[u8]) {
|
||||||
self.update(input);
|
self.update(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn result(&mut self, out: &mut [u8]) {
|
fn hash_finalize(&mut self, out: &mut [u8]) {
|
||||||
let hash_result = &self.finalize();
|
let hash_result = &self.finalize();
|
||||||
out.copy_from_slice(hash_result.as_bytes());
|
out.copy_from_slice(hash_result.as_bytes());
|
||||||
}
|
}
|
||||||
|
@ -58,11 +58,11 @@ impl Digest for blake3::Hasher {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input(&mut self, input: &[u8]) {
|
fn hash_update(&mut self, input: &[u8]) {
|
||||||
self.update(input);
|
self.update(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn result(&mut self, out: &mut [u8]) {
|
fn hash_finalize(&mut self, out: &mut [u8]) {
|
||||||
let hash_result = &self.finalize();
|
let hash_result = &self.finalize();
|
||||||
out.copy_from_slice(hash_result.as_bytes());
|
out.copy_from_slice(hash_result.as_bytes());
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,194 @@ impl Digest for blake3::Hasher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use sm3::{Digest as SM3_D, Sm3};
|
||||||
|
|
||||||
|
impl Digest for Sm3 {
|
||||||
|
fn new() -> Self {
|
||||||
|
<Self as sm3::Digest>::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_update(&mut self, input: &[u8]) {
|
||||||
|
self.update(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_finalize(&mut self, out: &mut [u8]) {
|
||||||
|
out.copy_from_slice(&self.clone().finalize());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
*self = <Self as sm3::Digest>::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_bits(&self) -> usize {
|
||||||
|
256
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: CRC_TABLE_LEN *must* be <= 256 as we cast 0..CRC_TABLE_LEN to u8
|
||||||
|
const CRC_TABLE_LEN: usize = 256;
|
||||||
|
|
||||||
|
pub struct CRC {
|
||||||
|
state: u32,
|
||||||
|
size: usize,
|
||||||
|
crc_table: [u32; CRC_TABLE_LEN],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CRC {
|
||||||
|
fn generate_crc_table() -> [u32; CRC_TABLE_LEN] {
|
||||||
|
let mut table = [0; CRC_TABLE_LEN];
|
||||||
|
|
||||||
|
for (i, elt) in table.iter_mut().enumerate().take(CRC_TABLE_LEN) {
|
||||||
|
*elt = Self::crc_entry(i as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
table
|
||||||
|
}
|
||||||
|
fn crc_entry(input: u8) -> u32 {
|
||||||
|
let mut crc = (input as u32) << 24;
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < 8 {
|
||||||
|
let if_condition = crc & 0x8000_0000;
|
||||||
|
let if_body = (crc << 1) ^ 0x04c1_1db7;
|
||||||
|
let else_body = crc << 1;
|
||||||
|
|
||||||
|
// NOTE: i feel like this is easier to understand than emulating an if statement in bitwise
|
||||||
|
// ops
|
||||||
|
let condition_table = [else_body, if_body];
|
||||||
|
|
||||||
|
crc = condition_table[(if_condition != 0) as usize];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
crc
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, input: u8) {
|
||||||
|
self.state = (self.state << 8)
|
||||||
|
^ self.crc_table[((self.state >> 24) as usize ^ input as usize) & 0xFF];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Digest for CRC {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
state: 0,
|
||||||
|
size: 0,
|
||||||
|
crc_table: Self::generate_crc_table(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_update(&mut self, input: &[u8]) {
|
||||||
|
for &elt in input.iter() {
|
||||||
|
self.update(elt);
|
||||||
|
}
|
||||||
|
self.size += input.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_finalize(&mut self, out: &mut [u8]) {
|
||||||
|
let mut sz = self.size;
|
||||||
|
while sz != 0 {
|
||||||
|
self.update(sz as u8);
|
||||||
|
sz >>= 8;
|
||||||
|
}
|
||||||
|
self.state = !self.state;
|
||||||
|
out.copy_from_slice(&self.state.to_ne_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn result_str(&mut self) -> String {
|
||||||
|
let mut _out: Vec<u8> = vec![0; 4];
|
||||||
|
self.hash_finalize(&mut _out);
|
||||||
|
format!("{}", self.state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
*self = Self::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_bits(&self) -> usize {
|
||||||
|
256
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can be replaced with usize::div_ceil once it is stabilized.
|
||||||
|
// This implementation approach is optimized for when `b` is a constant,
|
||||||
|
// particularly a power of two.
|
||||||
|
pub fn div_ceil(a: usize, b: usize) -> usize {
|
||||||
|
(a + b - 1) / b
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BSD {
|
||||||
|
state: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Digest for BSD {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { state: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_update(&mut self, input: &[u8]) {
|
||||||
|
for &byte in input.iter() {
|
||||||
|
self.state = (self.state >> 1) + ((self.state & 1) << 15);
|
||||||
|
self.state = self.state.wrapping_add(u16::from(byte));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_finalize(&mut self, out: &mut [u8]) {
|
||||||
|
out.copy_from_slice(&self.state.to_ne_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn result_str(&mut self) -> String {
|
||||||
|
let mut _out: Vec<u8> = vec![0; 2];
|
||||||
|
self.hash_finalize(&mut _out);
|
||||||
|
format!("{}", self.state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
*self = Self::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_bits(&self) -> usize {
|
||||||
|
128
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SYSV {
|
||||||
|
state: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Digest for SYSV {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { state: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_update(&mut self, input: &[u8]) {
|
||||||
|
for &byte in input.iter() {
|
||||||
|
self.state = self.state.wrapping_add(u32::from(byte));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_finalize(&mut self, out: &mut [u8]) {
|
||||||
|
self.state = (self.state & 0xffff) + (self.state >> 16);
|
||||||
|
self.state = (self.state & 0xffff) + (self.state >> 16);
|
||||||
|
out.copy_from_slice(&(self.state as u16).to_ne_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn result_str(&mut self) -> String {
|
||||||
|
let mut _out: Vec<u8> = vec![0; 2];
|
||||||
|
self.hash_finalize(&mut _out);
|
||||||
|
format!("{}", self.state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
*self = Self::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_bits(&self) -> usize {
|
||||||
|
512
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Implements the Digest trait for sha2 / sha3 algorithms with fixed output
|
// Implements the Digest trait for sha2 / sha3 algorithms with fixed output
|
||||||
macro_rules! impl_digest_common {
|
macro_rules! impl_digest_common {
|
||||||
($type: ty, $size: expr) => {
|
($type: ty, $size: expr) => {
|
||||||
|
@ -84,16 +272,16 @@ macro_rules! impl_digest_common {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input(&mut self, input: &[u8]) {
|
fn hash_update(&mut self, input: &[u8]) {
|
||||||
digest::Digest::update(self, input);
|
digest::Digest::update(self, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn result(&mut self, out: &mut [u8]) {
|
fn hash_finalize(&mut self, out: &mut [u8]) {
|
||||||
digest::Digest::finalize_into_reset(self, out.into());
|
digest::Digest::finalize_into_reset(self, out.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
*self = Self::new();
|
*self = <Self as Digest>::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn output_bits(&self) -> usize {
|
fn output_bits(&self) -> usize {
|
||||||
|
@ -111,11 +299,11 @@ macro_rules! impl_digest_shake {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input(&mut self, input: &[u8]) {
|
fn hash_update(&mut self, input: &[u8]) {
|
||||||
digest::Update::update(self, input);
|
digest::Update::update(self, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn result(&mut self, out: &mut [u8]) {
|
fn hash_finalize(&mut self, out: &mut [u8]) {
|
||||||
digest::ExtendableOutputReset::finalize_xof_reset_into(self, out);
|
digest::ExtendableOutputReset::finalize_xof_reset_into(self, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +336,7 @@ impl_digest_shake!(sha3::Shake256);
|
||||||
///
|
///
|
||||||
/// This struct wraps a [`Digest`] and provides a [`Write`]
|
/// This struct wraps a [`Digest`] and provides a [`Write`]
|
||||||
/// implementation that passes input bytes directly to the
|
/// implementation that passes input bytes directly to the
|
||||||
/// [`Digest::input`].
|
/// [`Digest::hash_update`].
|
||||||
///
|
///
|
||||||
/// On Windows, if `binary` is `false`, then the [`write`]
|
/// On Windows, if `binary` is `false`, then the [`write`]
|
||||||
/// implementation replaces instances of "\r\n" with "\n" before passing
|
/// implementation replaces instances of "\r\n" with "\n" before passing
|
||||||
|
@ -182,7 +370,7 @@ impl<'a> DigestWriter<'a> {
|
||||||
|
|
||||||
pub fn finalize(&mut self) -> bool {
|
pub fn finalize(&mut self) -> bool {
|
||||||
if self.was_last_character_carriage_return {
|
if self.was_last_character_carriage_return {
|
||||||
self.digest.input(&[b'\r']);
|
self.digest.hash_update(&[b'\r']);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -193,14 +381,14 @@ impl<'a> DigestWriter<'a> {
|
||||||
impl<'a> Write for DigestWriter<'a> {
|
impl<'a> Write for DigestWriter<'a> {
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
self.digest.input(buf);
|
self.digest.hash_update(buf);
|
||||||
Ok(buf.len())
|
Ok(buf.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
if self.binary {
|
if self.binary {
|
||||||
self.digest.input(buf);
|
self.digest.hash_update(buf);
|
||||||
return Ok(buf.len());
|
return Ok(buf.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +401,7 @@ impl<'a> Write for DigestWriter<'a> {
|
||||||
// call to `write()`.
|
// call to `write()`.
|
||||||
let n = buf.len();
|
let n = buf.len();
|
||||||
if self.was_last_character_carriage_return && n > 0 && buf[0] != b'\n' {
|
if self.was_last_character_carriage_return && n > 0 && buf[0] != b'\n' {
|
||||||
self.digest.input(&[b'\r']);
|
self.digest.hash_update(&[b'\r']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, find all occurrences of "\r\n", inputting the slice
|
// Next, find all occurrences of "\r\n", inputting the slice
|
||||||
|
@ -221,7 +409,7 @@ impl<'a> Write for DigestWriter<'a> {
|
||||||
// the beginning of this "\r\n".
|
// the beginning of this "\r\n".
|
||||||
let mut i_prev = 0;
|
let mut i_prev = 0;
|
||||||
for i in memmem::find_iter(buf, b"\r\n") {
|
for i in memmem::find_iter(buf, b"\r\n") {
|
||||||
self.digest.input(&buf[i_prev..i]);
|
self.digest.hash_update(&buf[i_prev..i]);
|
||||||
i_prev = i + 1;
|
i_prev = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,10 +421,10 @@ impl<'a> Write for DigestWriter<'a> {
|
||||||
// blocks of the input.
|
// blocks of the input.
|
||||||
if n > 0 && buf[n - 1] == b'\r' {
|
if n > 0 && buf[n - 1] == b'\r' {
|
||||||
self.was_last_character_carriage_return = true;
|
self.was_last_character_carriage_return = true;
|
||||||
self.digest.input(&buf[i_prev..n - 1]);
|
self.digest.hash_update(&buf[i_prev..n - 1]);
|
||||||
} else {
|
} else {
|
||||||
self.was_last_character_carriage_return = false;
|
self.was_last_character_carriage_return = false;
|
||||||
self.digest.input(&buf[i_prev..n]);
|
self.digest.hash_update(&buf[i_prev..n]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even though we dropped a "\r" for each "\r\n" we found, we
|
// Even though we dropped a "\r" for each "\r\n" we found, we
|
||||||
|
@ -272,14 +460,14 @@ mod tests {
|
||||||
let mut writer_crlf = DigestWriter::new(&mut digest, false);
|
let mut writer_crlf = DigestWriter::new(&mut digest, false);
|
||||||
writer_crlf.write_all(&[b'\r']).unwrap();
|
writer_crlf.write_all(&[b'\r']).unwrap();
|
||||||
writer_crlf.write_all(&[b'\n']).unwrap();
|
writer_crlf.write_all(&[b'\n']).unwrap();
|
||||||
writer_crlf.finalize();
|
writer_crlf.hash_finalize();
|
||||||
let result_crlf = digest.result_str();
|
let result_crlf = digest.result_str();
|
||||||
|
|
||||||
// We expect "\r\n" to be replaced with "\n" in text mode on Windows.
|
// We expect "\r\n" to be replaced with "\n" in text mode on Windows.
|
||||||
let mut digest = Box::new(md5::Md5::new()) as Box<dyn Digest>;
|
let mut digest = Box::new(md5::Md5::new()) as Box<dyn Digest>;
|
||||||
let mut writer_lf = DigestWriter::new(&mut digest, false);
|
let mut writer_lf = DigestWriter::new(&mut digest, false);
|
||||||
writer_lf.write_all(&[b'\n']).unwrap();
|
writer_lf.write_all(&[b'\n']).unwrap();
|
||||||
writer_lf.finalize();
|
writer_lf.hash_finalize();
|
||||||
let result_lf = digest.result_str();
|
let result_lf = digest.result_str();
|
||||||
|
|
||||||
assert_eq!(result_crlf, result_lf);
|
assert_eq!(result_crlf, result_lf);
|
|
@ -46,6 +46,8 @@ pub use crate::features::lines;
|
||||||
pub use crate::features::memo;
|
pub use crate::features::memo;
|
||||||
#[cfg(feature = "ringbuffer")]
|
#[cfg(feature = "ringbuffer")]
|
||||||
pub use crate::features::ringbuffer;
|
pub use crate::features::ringbuffer;
|
||||||
|
#[cfg(feature = "sum")]
|
||||||
|
pub use crate::features::sum;
|
||||||
|
|
||||||
// * (platform-specific) feature-gated modules
|
// * (platform-specific) feature-gated modules
|
||||||
// ** non-windows (i.e. Unix + Fuchsia)
|
// ** non-windows (i.e. Unix + Fuchsia)
|
||||||
|
|
|
@ -114,3 +114,79 @@ fn test_stdin_larger_than_128_bytes() {
|
||||||
assert_eq!(cksum, 945_881_979);
|
assert_eq!(cksum, 945_881_979);
|
||||||
assert_eq!(bytes_cnt, 2058);
|
assert_eq!(bytes_cnt, 2058);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sha1_single_file() {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("-a=sha1")
|
||||||
|
.arg("lorem_ipsum.txt")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("ab1dd0bae1d8883a3d18a66de6afbd28252cfbef 772 lorem_ipsum.txt\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sm3_single_file() {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("-a=sm3")
|
||||||
|
.arg("lorem_ipsum.txt")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is(
|
||||||
|
"6d296b805d060bfed22808df308dbb9b4317794dd4ed6740a10770a782699bc2 772 lorem_ipsum.txt\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bsd_single_file() {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("-a=bsd")
|
||||||
|
.arg("lorem_ipsum.txt")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only_fixture("bsd_single_file.expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bsd_multiple_files() {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("-a=bsd")
|
||||||
|
.arg("lorem_ipsum.txt")
|
||||||
|
.arg("alice_in_wonderland.txt")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only_fixture("bsd_multiple_files.expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bsd_stdin() {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("-a=bsd")
|
||||||
|
.pipe_in_fixture("lorem_ipsum.txt")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only_fixture("bsd_stdin.expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sysv_single_file() {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("-a=sysv")
|
||||||
|
.arg("lorem_ipsum.txt")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only_fixture("sysv_single_file.expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sysv_multiple_files() {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("-a=sysv")
|
||||||
|
.arg("lorem_ipsum.txt")
|
||||||
|
.arg("alice_in_wonderland.txt")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only_fixture("sysv_multiple_files.expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sysv_stdin() {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("-a=sysv")
|
||||||
|
.pipe_in_fixture("lorem_ipsum.txt")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only_fixture("sysv_stdin.expected");
|
||||||
|
}
|
||||||
|
|
2
tests/fixtures/cksum/bsd_multiple_files.expected
vendored
Normal file
2
tests/fixtures/cksum/bsd_multiple_files.expected
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
08109 1 lorem_ipsum.txt
|
||||||
|
01814 1 alice_in_wonderland.txt
|
1
tests/fixtures/cksum/bsd_single_file.expected
vendored
Normal file
1
tests/fixtures/cksum/bsd_single_file.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
08109 1 lorem_ipsum.txt
|
1
tests/fixtures/cksum/bsd_stdin.expected
vendored
Normal file
1
tests/fixtures/cksum/bsd_stdin.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
08109 1
|
2
tests/fixtures/cksum/sysv_multiple_files.expected
vendored
Normal file
2
tests/fixtures/cksum/sysv_multiple_files.expected
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
6985 2 lorem_ipsum.txt
|
||||||
|
27441 1 alice_in_wonderland.txt
|
1
tests/fixtures/cksum/sysv_single_file.expected
vendored
Normal file
1
tests/fixtures/cksum/sysv_single_file.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
6985 2 lorem_ipsum.txt
|
1
tests/fixtures/cksum/sysv_stdin.expected
vendored
Normal file
1
tests/fixtures/cksum/sysv_stdin.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
6985 2
|
Loading…
Add table
Add a link
Reference in a new issue