mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
Merge branch 'main' into fix-printf-hex-alternate-zero
This commit is contained in:
commit
4557821adf
34 changed files with 401 additions and 103 deletions
24
.github/workflows/CICD.yml
vendored
24
.github/workflows/CICD.yml
vendored
|
@ -612,9 +612,23 @@ jobs:
|
|||
run: |
|
||||
## Install/setup prerequisites
|
||||
case '${{ matrix.job.target }}' in
|
||||
arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
|
||||
aarch64-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
|
||||
*-redox*) sudo apt-get -y update ; sudo apt-get -y install fuse3 libfuse-dev ;;
|
||||
arm-unknown-linux-gnueabihf)
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install gcc-arm-linux-gnueabihf
|
||||
;;
|
||||
aarch64-unknown-linux-*)
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install gcc-aarch64-linux-gnu
|
||||
;;
|
||||
*-redox*)
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install fuse3 libfuse-dev
|
||||
;;
|
||||
# Update binutils if MinGW due to https://github.com/rust-lang/rust/issues/112368
|
||||
x86_64-pc-windows-gnu)
|
||||
C:/msys64/usr/bin/pacman.exe -Syu --needed mingw-w64-x86_64-gcc --noconfirm
|
||||
echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH
|
||||
;;
|
||||
esac
|
||||
case '${{ matrix.job.os }}' in
|
||||
macos-latest) brew install coreutils ;; # needed for testing
|
||||
|
@ -984,6 +998,10 @@ jobs:
|
|||
echo "foo" > /home/runner/.plan
|
||||
;;
|
||||
esac
|
||||
case '${{ matrix.job.os }}' in
|
||||
# Update binutils if MinGW due to https://github.com/rust-lang/rust/issues/112368
|
||||
windows-latest) C:/msys64/usr/bin/pacman.exe -Syu --needed mingw-w64-x86_64-gcc --noconfirm ; echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH ;;
|
||||
esac
|
||||
- name: Initialize toolchain-dependent workflow variables
|
||||
id: dep_vars
|
||||
shell: bash
|
||||
|
|
70
Cargo.lock
generated
70
Cargo.lock
generated
|
@ -320,15 +320,15 @@ checksum = "120133d4db2ec47efe2e26502ee984747630c67f51974fca0b6c1340cf2368d3"
|
|||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.7"
|
||||
version = "0.15.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
|
||||
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"unicode-width",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -813,14 +813,14 @@ checksum = "31a7a908b8f32538a2143e59a6e4e2508988832d5d4d6f7c156b3cbc762643a5"
|
|||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.22"
|
||||
version = "0.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
|
||||
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.3.5",
|
||||
"windows-sys 0.48.0",
|
||||
"redox_syscall",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1293,14 +1293,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.6"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
|
||||
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1455,13 +1455,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.8"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
|
||||
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.3.5",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.48.0",
|
||||
]
|
||||
|
@ -1589,7 +1589,7 @@ dependencies = [
|
|||
"hex",
|
||||
"lazy_static",
|
||||
"procfs-core",
|
||||
"rustix 0.38.28",
|
||||
"rustix 0.38.30",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1689,18 +1689,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded0bce2d41cc3c57aefa284708ced249a64acb01745dbbe72bd78610bfd644c"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
@ -1831,9 +1822,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.28"
|
||||
version = "0.38.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
|
||||
checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
|
@ -1995,9 +1986,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.0"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||
checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e"
|
||||
|
||||
[[package]]
|
||||
name = "smawk"
|
||||
|
@ -2045,15 +2036,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.8.1"
|
||||
version = "3.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
|
||||
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall 0.4.0",
|
||||
"rustix 0.38.28",
|
||||
"windows-sys 0.48.0",
|
||||
"redox_syscall",
|
||||
"rustix 0.38.30",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2072,7 +2063,7 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
|
||||
dependencies = [
|
||||
"rustix 0.38.28",
|
||||
"rustix 0.38.30",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
|
@ -2616,6 +2607,7 @@ dependencies = [
|
|||
"unicode-width",
|
||||
"uucore",
|
||||
"uutils_term_grid",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3014,7 +3006,7 @@ version = "0.0.23"
|
|||
dependencies = [
|
||||
"clap",
|
||||
"libc",
|
||||
"redox_syscall 0.4.0",
|
||||
"redox_syscall",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
|
@ -3574,13 +3566,13 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
|||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "1.2.0"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "914566e6413e7fa959cc394fb30e563ba80f3541fbd40816d4c05a0fc3f2a0f1"
|
||||
checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.12",
|
||||
"rustix 0.38.28",
|
||||
"rustix 0.38.30",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -318,8 +318,8 @@ same-file = "1.0.6"
|
|||
self_cell = "1.0.3"
|
||||
selinux = "0.4"
|
||||
signal-hook = "0.3.17"
|
||||
smallvec = { version = "1.11", features = ["union"] }
|
||||
tempfile = "3.8.1"
|
||||
smallvec = { version = "1.12", features = ["union"] }
|
||||
tempfile = "3.9.0"
|
||||
uutils_term_grid = "0.3"
|
||||
terminal_size = "0.3.0"
|
||||
textwrap = { version = "0.16.0", features = ["terminal_size"] }
|
||||
|
@ -331,7 +331,7 @@ utf-8 = "0.7.6"
|
|||
walkdir = "2.4"
|
||||
winapi-util = "0.1.6"
|
||||
windows-sys = { version = "0.48.0", default-features = false }
|
||||
xattr = "1.2.0"
|
||||
xattr = "1.3.1"
|
||||
zip = { version = "0.6.6", default-features = false, features = ["deflate"] }
|
||||
|
||||
hex = "0.4.3"
|
||||
|
|
|
@ -62,7 +62,7 @@ skip = [
|
|||
{ name = "linux-raw-sys", version = "0.3.8" },
|
||||
# terminal_size
|
||||
{ name = "rustix", version = "0.37.26" },
|
||||
# various crates
|
||||
# notify
|
||||
{ name = "windows-sys", version = "0.45.0" },
|
||||
# various crates
|
||||
{ name = "windows-sys", version = "0.48.0" },
|
||||
|
@ -102,8 +102,6 @@ skip = [
|
|||
{ name = "syn", version = "1.0.109" },
|
||||
# various crates
|
||||
{ name = "bitflags", version = "1.3.2" },
|
||||
# various crates
|
||||
{ name = "redox_syscall", version = "0.3.5" },
|
||||
# clap_builder, textwrap
|
||||
{ name = "terminal_size", version = "0.2.6" },
|
||||
]
|
||||
|
|
|
@ -232,7 +232,10 @@ pub fn run_gnu_cmd(
|
|||
|
||||
let output = if let Some(input_str) = pipe_input {
|
||||
// We have an pipe input
|
||||
command.stdin(Stdio::piped()).stdout(Stdio::piped());
|
||||
command
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
|
||||
let mut child = command.spawn().expect("Failed to execute command");
|
||||
let child_stdin = child.stdin.as_mut().unwrap();
|
||||
|
|
|
@ -5,15 +5,18 @@
|
|||
|
||||
// spell-checker:ignore (ToDO) fname, algo
|
||||
use clap::{crate_version, value_parser, Arg, ArgAction, Command};
|
||||
use hex::decode;
|
||||
use hex::encode;
|
||||
use std::error::Error;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Display;
|
||||
use std::fs::File;
|
||||
use std::io::{self, stdin, BufReader, Read};
|
||||
use std::io::{self, stdin, stdout, BufReader, Read, Write};
|
||||
use std::iter;
|
||||
use std::path::Path;
|
||||
use uucore::{
|
||||
error::{FromIo, UResult},
|
||||
format_usage, help_about, help_section, help_usage,
|
||||
error::{FromIo, UError, UResult, USimpleError},
|
||||
format_usage, help_about, help_section, help_usage, show,
|
||||
sum::{
|
||||
div_ceil, Blake2b, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha512, Sm3,
|
||||
BSD, CRC, SYSV,
|
||||
|
@ -36,6 +39,31 @@ const ALGORITHM_OPTIONS_SHA512: &str = "sha512";
|
|||
const ALGORITHM_OPTIONS_BLAKE2B: &str = "blake2b";
|
||||
const ALGORITHM_OPTIONS_SM3: &str = "sm3";
|
||||
|
||||
#[derive(Debug)]
|
||||
enum CkSumError {
|
||||
RawMultipleFiles,
|
||||
}
|
||||
|
||||
impl UError for CkSumError {
|
||||
fn code(&self) -> i32 {
|
||||
match self {
|
||||
Self::RawMultipleFiles => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for CkSumError {}
|
||||
|
||||
impl Display for CkSumError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::RawMultipleFiles => {
|
||||
write!(f, "the --raw option is not supported with multiple files")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_algo(
|
||||
program: &str,
|
||||
length: Option<usize>,
|
||||
|
@ -110,6 +138,7 @@ struct Options {
|
|||
output_bits: usize,
|
||||
untagged: bool,
|
||||
length: Option<usize>,
|
||||
raw: bool,
|
||||
}
|
||||
|
||||
/// Calculate checksum
|
||||
|
@ -123,6 +152,11 @@ fn cksum<'a, I>(mut options: Options, files: I) -> UResult<()>
|
|||
where
|
||||
I: Iterator<Item = &'a OsStr>,
|
||||
{
|
||||
let files: Vec<_> = files.collect();
|
||||
if options.raw && files.len() > 1 {
|
||||
return Err(Box::new(CkSumError::RawMultipleFiles));
|
||||
}
|
||||
|
||||
for filename in files {
|
||||
let filename = Path::new(filename);
|
||||
let stdin_buf;
|
||||
|
@ -134,13 +168,35 @@ where
|
|||
} else if filename.is_dir() {
|
||||
Box::new(BufReader::new(io::empty())) as Box<dyn Read>
|
||||
} else {
|
||||
file_buf =
|
||||
File::open(filename).map_err_context(|| filename.to_str().unwrap().to_string())?;
|
||||
file_buf = match File::open(filename) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
show!(err.map_err_context(|| filename.to_string_lossy().to_string()));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
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())?;
|
||||
|
||||
if filename.is_dir() {
|
||||
show!(USimpleError::new(
|
||||
1,
|
||||
format!("{}: Is a directory", filename.display())
|
||||
));
|
||||
continue;
|
||||
}
|
||||
if options.raw {
|
||||
let bytes = match options.algo_name {
|
||||
ALGORITHM_OPTIONS_CRC => sum.parse::<u32>().unwrap().to_be_bytes().to_vec(),
|
||||
ALGORITHM_OPTIONS_SYSV | ALGORITHM_OPTIONS_BSD => {
|
||||
sum.parse::<u16>().unwrap().to_be_bytes().to_vec()
|
||||
}
|
||||
_ => decode(sum).unwrap(),
|
||||
};
|
||||
stdout().write_all(&bytes)?;
|
||||
return Ok(());
|
||||
}
|
||||
// The BSD checksum output is 5 digit integer
|
||||
let bsd_width = 5;
|
||||
match (options.algo_name, not_file) {
|
||||
|
@ -169,13 +225,6 @@ where
|
|||
(ALGORITHM_OPTIONS_CRC, true) => println!("{sum} {sz}"),
|
||||
(ALGORITHM_OPTIONS_CRC, false) => println!("{sum} {sz} {}", filename.display()),
|
||||
(ALGORITHM_OPTIONS_BLAKE2B, _) if !options.untagged => {
|
||||
if filename.is_dir() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
format!("{}: Is a directory", filename.display()),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
if let Some(length) = options.length {
|
||||
// Multiply by 8 here, as we want to print the length in bits.
|
||||
println!("BLAKE2b-{} ({}) = {sum}", length * 8, filename.display());
|
||||
|
@ -238,6 +287,7 @@ mod options {
|
|||
pub const FILE: &str = "file";
|
||||
pub const UNTAGGED: &str = "untagged";
|
||||
pub const LENGTH: &str = "length";
|
||||
pub const RAW: &str = "raw";
|
||||
}
|
||||
|
||||
#[uucore::main]
|
||||
|
@ -298,6 +348,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
output_bits: bits,
|
||||
length,
|
||||
untagged: matches.get_flag(options::UNTAGGED),
|
||||
raw: matches.get_flag(options::RAW),
|
||||
};
|
||||
|
||||
match matches.get_many::<String>(options::FILE) {
|
||||
|
@ -354,5 +405,11 @@ pub fn uu_app() -> Command {
|
|||
.help("digest length in bits; must not exceed the max for the blake2 algorithm and must be a multiple of 8")
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::RAW)
|
||||
.long(options::RAW)
|
||||
.help("emit a raw binary digest, not hexadecimal")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.after_help(AFTER_HELP)
|
||||
}
|
||||
|
|
|
@ -150,6 +150,7 @@ pub enum TargetType {
|
|||
}
|
||||
|
||||
/// Copy action to perform
|
||||
#[derive(PartialEq)]
|
||||
pub enum CopyMode {
|
||||
Link,
|
||||
SymLink,
|
||||
|
@ -1509,7 +1510,7 @@ fn backup_dest(dest: &Path, backup_path: &Path) -> CopyResult<PathBuf> {
|
|||
///
|
||||
/// Copying to the same file is only allowed if both `--backup` and
|
||||
/// `--force` are specified and the file is a regular file.
|
||||
fn is_forbidden_copy_to_same_file(
|
||||
fn is_forbidden_to_copy_to_same_file(
|
||||
source: &Path,
|
||||
dest: &Path,
|
||||
options: &Options,
|
||||
|
@ -1521,6 +1522,7 @@ fn is_forbidden_copy_to_same_file(
|
|||
options.dereference(source_in_command_line) || !source.is_symlink();
|
||||
paths_refer_to_same_file(source, dest, dereference_to_compare)
|
||||
&& !(options.force() && options.backup != BackupMode::NoBackup)
|
||||
&& !(dest.is_symlink() && options.backup != BackupMode::NoBackup)
|
||||
}
|
||||
|
||||
/// Back up, remove, or leave intact the destination file, depending on the options.
|
||||
|
@ -1532,7 +1534,7 @@ fn handle_existing_dest(
|
|||
) -> CopyResult<()> {
|
||||
// Disallow copying a file to itself, unless `--force` and
|
||||
// `--backup` are both specified.
|
||||
if is_forbidden_copy_to_same_file(source, dest, options, source_in_command_line) {
|
||||
if is_forbidden_to_copy_to_same_file(source, dest, options, source_in_command_line) {
|
||||
return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into());
|
||||
}
|
||||
|
||||
|
@ -1714,6 +1716,7 @@ fn copy_file(
|
|||
&& !options.force()
|
||||
&& options.backup == BackupMode::NoBackup
|
||||
&& source != dest
|
||||
|| (source == dest && options.copy_mode == CopyMode::Link)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
@ -510,6 +510,7 @@ pub fn uu_app() -> Command {
|
|||
.about(ABOUT)
|
||||
.after_help(AFTER_HELP)
|
||||
.infer_long_args(true)
|
||||
.args_override_self(true)
|
||||
.arg(
|
||||
Arg::new(options::BYTES)
|
||||
.short('b')
|
||||
|
|
|
@ -34,6 +34,9 @@ once_cell = { workspace = true }
|
|||
selinux = { workspace = true, optional = true }
|
||||
hostname = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
xattr = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
name = "ls"
|
||||
path = "src/main.rs"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm
|
||||
// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm getxattr
|
||||
|
||||
use clap::{
|
||||
builder::{NonEmptyStringValueParser, ValueParser},
|
||||
|
@ -36,6 +36,7 @@ use std::{
|
|||
};
|
||||
use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
|
@ -2620,6 +2621,18 @@ fn display_grid(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))]
|
||||
fn file_has_acl<P: AsRef<Path>>(file: P) -> bool {
|
||||
// don't use exacl here, it is doing more getxattr call then needed
|
||||
match xattr::list(file) {
|
||||
Ok(acl) => {
|
||||
// if we have extra attributes, we have an acl
|
||||
acl.count() > 0
|
||||
}
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// This writes to the BufWriter out a single string of the output of `ls -l`.
|
||||
///
|
||||
/// It writes the following keys, in order:
|
||||
|
@ -2663,9 +2676,14 @@ fn display_item_long(
|
|||
output_display += " ";
|
||||
}
|
||||
if let Some(md) = item.get_metadata(out) {
|
||||
#[cfg(any(not(unix), target_os = "android", target_os = "macos"))]
|
||||
// TODO: See how Mac should work here
|
||||
let is_acl_set = false;
|
||||
#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))]
|
||||
let is_acl_set = file_has_acl(item.display_name.as_os_str());
|
||||
write!(
|
||||
output_display,
|
||||
"{}{} {}",
|
||||
"{}{}{} {}",
|
||||
display_permissions(md, true),
|
||||
if item.security_context.len() > 1 {
|
||||
// GNU `ls` uses a "." character to indicate a file with a security context,
|
||||
|
@ -2674,6 +2692,12 @@ fn display_item_long(
|
|||
} else {
|
||||
""
|
||||
},
|
||||
if is_acl_set {
|
||||
// if acl has been set, we display a "+" at the end of the file permissions
|
||||
"+"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
pad_left(&display_symlink_count(md), padding.link_count)
|
||||
)
|
||||
.unwrap();
|
||||
|
|
|
@ -7,8 +7,8 @@ use clap::{crate_version, Arg, ArgAction, Command};
|
|||
use std::fs::File;
|
||||
use std::io::{stdin, BufRead, BufReader, Read};
|
||||
use std::path::Path;
|
||||
use uucore::error::{FromIo, UResult, USimpleError};
|
||||
use uucore::{format_usage, help_about, help_section, help_usage};
|
||||
use uucore::error::{set_exit_code, FromIo, UResult, USimpleError};
|
||||
use uucore::{format_usage, help_about, help_section, help_usage, show_error};
|
||||
|
||||
mod helper;
|
||||
|
||||
|
@ -205,9 +205,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
nl(&mut buffer, &mut stats, &settings)?;
|
||||
} else {
|
||||
let path = Path::new(file);
|
||||
let reader = File::open(path).map_err_context(|| file.to_string())?;
|
||||
let mut buffer = BufReader::new(reader);
|
||||
nl(&mut buffer, &mut stats, &settings)?;
|
||||
|
||||
if path.is_dir() {
|
||||
show_error!("{}: Is a directory", path.display());
|
||||
set_exit_code(1);
|
||||
} else {
|
||||
let reader = File::open(path).map_err_context(|| file.to_string())?;
|
||||
let mut buffer = BufReader::new(reader);
|
||||
nl(&mut buffer, &mut stats, &settings)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::ops::ControlFlow;
|
|||
|
||||
use clap::{crate_version, Arg, ArgAction, Command};
|
||||
use uucore::error::{UResult, UUsageError};
|
||||
use uucore::format::{parse_spec_and_escape, FormatArgument};
|
||||
use uucore::format::{parse_spec_and_escape, FormatArgument, FormatItem};
|
||||
use uucore::{format_usage, help_about, help_section, help_usage};
|
||||
|
||||
const VERSION: &str = "version";
|
||||
|
@ -38,14 +38,24 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
None => vec![],
|
||||
};
|
||||
|
||||
let mut format_seen = false;
|
||||
let mut args = values.iter().peekable();
|
||||
for item in parse_spec_and_escape(format_string.as_ref()) {
|
||||
if let Ok(FormatItem::Spec(_)) = item {
|
||||
format_seen = true;
|
||||
}
|
||||
match item?.write(stdout(), &mut args)? {
|
||||
ControlFlow::Continue(()) => {}
|
||||
ControlFlow::Break(()) => return Ok(()),
|
||||
};
|
||||
}
|
||||
|
||||
// Without format specs in the string, the iter would not consume any args,
|
||||
// leading to an infinite loop. Thus, we exit early.
|
||||
if !format_seen {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
while args.peek().is_some() {
|
||||
for item in parse_spec_and_escape(format_string.as_ref()) {
|
||||
match item?.write(stdout(), &mut args)? {
|
||||
|
|
|
@ -45,7 +45,7 @@ impl<'a, T: Iterator<Item = &'a FormatArgument>> ArgumentIter<'a> for T {
|
|||
};
|
||||
match next {
|
||||
FormatArgument::Char(c) => *c,
|
||||
FormatArgument::Unparsed(s) => s.chars().next().unwrap_or('\0'),
|
||||
FormatArgument::Unparsed(s) => s.bytes().next().map_or('\0', char::from),
|
||||
_ => '\0',
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@ pub fn parse_escape_code(rest: &mut &[u8]) -> EscapedChar {
|
|||
*rest = new_rest;
|
||||
match c {
|
||||
b'\\' => EscapedChar::Byte(b'\\'),
|
||||
b'"' => EscapedChar::Byte(b'"'),
|
||||
b'a' => EscapedChar::Byte(b'\x07'),
|
||||
b'b' => EscapedChar::Byte(b'\x08'),
|
||||
b'c' => EscapedChar::End,
|
||||
|
|
|
@ -146,10 +146,10 @@ impl Formatter for UnsignedInt {
|
|||
// We also need to take into account that 0 should not be 00
|
||||
// Since this is an unsigned int, we do not need to take the minus
|
||||
// sign into account.
|
||||
if x != 0 {
|
||||
format!("0{x:o}")
|
||||
} else {
|
||||
if x == 0 {
|
||||
format!("{x:o}")
|
||||
} else {
|
||||
format!("0{x:o}")
|
||||
}
|
||||
}
|
||||
UnsignedIntVariant::Hexadecimal(Case::Lowercase, Prefix::No) => {
|
||||
|
|
|
@ -81,15 +81,19 @@ fn test_nonexisting_file() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_folder() {
|
||||
fn test_one_nonexisting_file() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let folder_name = "a_folder";
|
||||
at.mkdir(folder_name);
|
||||
at.touch("abc.txt");
|
||||
at.touch("xyz.txt");
|
||||
|
||||
ucmd.arg(folder_name)
|
||||
.succeeds()
|
||||
.stdout_only(format!("4294967295 0 {folder_name}\n"));
|
||||
ucmd.arg("abc.txt")
|
||||
.arg("asdf.txt")
|
||||
.arg("xyz.txt")
|
||||
.fails()
|
||||
.stdout_contains_line("4294967295 0 xyz.txt")
|
||||
.stderr_contains("asdf.txt: No such file or directory")
|
||||
.stdout_contains_line("4294967295 0 abc.txt");
|
||||
}
|
||||
|
||||
// Make sure crc is correct for files larger than 32 bytes
|
||||
|
@ -288,15 +292,76 @@ fn test_length_is_zero() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_blake2b_fail_on_directory() {
|
||||
fn test_raw_single_file() {
|
||||
for algo in ALGOS {
|
||||
new_ucmd!()
|
||||
.arg("--raw")
|
||||
.arg("lorem_ipsum.txt")
|
||||
.arg(format!("--algorithm={algo}"))
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout_is_fixture_bytes(format!("raw/{algo}_single_file.expected"));
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_raw_multiple_files() {
|
||||
new_ucmd!()
|
||||
.arg("--raw")
|
||||
.arg("lorem_ipsum.txt")
|
||||
.arg("alice_in_wonderland.txt")
|
||||
.fails()
|
||||
.no_stdout()
|
||||
.stderr_contains("cksum: the --raw option is not supported with multiple files")
|
||||
.code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fail_on_folder() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let folder_name = "a_folder";
|
||||
at.mkdir(folder_name);
|
||||
|
||||
ucmd.arg("--algorithm=blake2b")
|
||||
.arg(folder_name)
|
||||
ucmd.arg(folder_name)
|
||||
.fails()
|
||||
.no_stdout()
|
||||
.stderr_contains(format!("cksum: {folder_name}: Is a directory"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_algorithms_fail_on_folder() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let folder_name = "a_folder";
|
||||
at.mkdir(folder_name);
|
||||
|
||||
for algo in ALGOS {
|
||||
scene
|
||||
.ucmd()
|
||||
.arg(format!("--algorithm={algo}"))
|
||||
.arg(folder_name)
|
||||
.fails()
|
||||
.no_stdout()
|
||||
.stderr_contains(format!("cksum: {folder_name}: Is a directory"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_folder_and_file() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let folder_name = "a_folder";
|
||||
at.mkdir(folder_name);
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg(folder_name)
|
||||
.arg("lorem_ipsum.txt")
|
||||
.fails()
|
||||
.stderr_contains(format!("cksum: {folder_name}: Is a directory"))
|
||||
.stdout_is_fixture("crc_single_file.expected");
|
||||
}
|
||||
|
|
|
@ -566,6 +566,22 @@ fn test_cp_arg_link_with_dest_hardlink_to_source() {
|
|||
assert!(at.file_exists(hardlink));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_cp_arg_link_with_same_file() {
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file = "file";
|
||||
|
||||
at.touch(file);
|
||||
|
||||
ucmd.args(&["--link", file, file]).succeeds();
|
||||
|
||||
assert_eq!(at.metadata(file).st_nlink(), 1);
|
||||
assert!(at.file_exists(file));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cp_arg_symlink() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
|
@ -715,6 +731,25 @@ fn test_cp_arg_backup_with_dest_a_symlink() {
|
|||
assert_eq!(original, at.resolve_link(backup));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cp_arg_backup_with_dest_a_symlink_to_source() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let source = "source";
|
||||
let source_content = "content";
|
||||
let symlink = "symlink";
|
||||
let backup = "symlink~";
|
||||
|
||||
at.write(source, source_content);
|
||||
at.symlink_file(source, symlink);
|
||||
|
||||
ucmd.arg("-b").arg(source).arg(symlink).succeeds();
|
||||
|
||||
assert!(!at.symlink_exists(symlink));
|
||||
assert_eq!(source_content, at.read(symlink));
|
||||
assert!(at.symlink_exists(backup));
|
||||
assert_eq!(source, at.resolve_link(backup));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cp_arg_backup_with_other_args() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
|
|
|
@ -255,3 +255,13 @@ fn test_equal_as_delimiter3() {
|
|||
.succeeds()
|
||||
.stdout_only_bytes("abZcd\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple() {
|
||||
let result = new_ucmd!()
|
||||
.args(&["-f2", "-d:", "-d="])
|
||||
.pipe_in("a=b\n")
|
||||
.succeeds();
|
||||
assert_eq!(result.stdout_str(), "b\n");
|
||||
assert_eq!(result.stderr_str(), "");
|
||||
}
|
||||
|
|
|
@ -391,7 +391,7 @@ fn test_read_backwards_bytes_proc_fs_version() {
|
|||
|
||||
let args = ["-c", "-1", "/proc/version"];
|
||||
let result = ts.ucmd().args(&args).succeeds();
|
||||
assert!(result.stdout().len() > 0);
|
||||
assert!(!result.stdout().is_empty());
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
|
@ -406,7 +406,7 @@ fn test_read_backwards_bytes_proc_fs_modules() {
|
|||
|
||||
let args = ["-c", "-1", "/proc/modules"];
|
||||
let result = ts.ucmd().args(&args).succeeds();
|
||||
assert!(result.stdout().len() > 0);
|
||||
assert!(!result.stdout().is_empty());
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
|
@ -421,7 +421,7 @@ fn test_read_backwards_lines_proc_fs_modules() {
|
|||
|
||||
let args = ["--lines", "-1", "/proc/modules"];
|
||||
let result = ts.ucmd().args(&args).succeeds();
|
||||
assert!(result.stdout().len() > 0);
|
||||
assert!(!result.stdout().is_empty());
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
|
|
|
@ -4293,3 +4293,39 @@ fn test_term_colorterm() {
|
|||
"exe"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
#[test]
|
||||
fn test_acl_display() {
|
||||
use std::process::Command;
|
||||
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
let path = "a42";
|
||||
at.mkdir(path);
|
||||
|
||||
let path = at.plus_as_string(path);
|
||||
// calling the command directly. xattr requires some dev packages to be installed
|
||||
// and it adds a complex dependency just for a test
|
||||
match Command::new("setfacl")
|
||||
.args(["-d", "-m", "group::rwx", &path])
|
||||
.status()
|
||||
.map(|status| status.code())
|
||||
{
|
||||
Ok(Some(0)) => {}
|
||||
Ok(_) => {
|
||||
println!("test skipped: setfacl failed");
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
println!("test skipped: setfacl failed with {}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-lda", &path])
|
||||
.succeeds()
|
||||
.stdout_contains("+");
|
||||
}
|
||||
|
|
|
@ -634,3 +634,20 @@ fn test_empty_section_delimiter() {
|
|||
.stdout_is(" 1\ta\n \n 2\tb\n");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_directory_as_input() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let dir = "dir";
|
||||
let file = "file";
|
||||
let content = "aaa";
|
||||
|
||||
at.mkdir(dir);
|
||||
at.write(file, content);
|
||||
|
||||
ucmd.arg(dir)
|
||||
.arg(file)
|
||||
.fails()
|
||||
.stderr_is(format!("nl: {dir}: Is a directory\n"))
|
||||
.stdout_contains(content);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,14 @@
|
|||
// file that was distributed with this source code.
|
||||
use crate::common::util::TestScenario;
|
||||
|
||||
#[test]
|
||||
fn test_no_args() {
|
||||
new_ucmd!()
|
||||
.fails()
|
||||
.no_stdout()
|
||||
.stderr_contains("pathchk: missing operand");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
||||
|
@ -11,8 +19,6 @@ fn test_invalid_arg() {
|
|||
|
||||
#[test]
|
||||
fn test_default_mode() {
|
||||
// test the default mode
|
||||
|
||||
// accept some reasonable default
|
||||
new_ucmd!().args(&["dir/file"]).succeeds().no_stdout();
|
||||
|
||||
|
@ -48,8 +54,6 @@ fn test_default_mode() {
|
|||
|
||||
#[test]
|
||||
fn test_posix_mode() {
|
||||
// test the posix mode
|
||||
|
||||
// accept some reasonable default
|
||||
new_ucmd!().args(&["-p", "dir/file"]).succeeds().no_stdout();
|
||||
|
||||
|
@ -74,8 +78,6 @@ fn test_posix_mode() {
|
|||
|
||||
#[test]
|
||||
fn test_posix_special() {
|
||||
// test the posix special mode
|
||||
|
||||
// accept some reasonable default
|
||||
new_ucmd!().args(&["-P", "dir/file"]).succeeds().no_stdout();
|
||||
|
||||
|
@ -115,8 +117,6 @@ fn test_posix_special() {
|
|||
|
||||
#[test]
|
||||
fn test_posix_all() {
|
||||
// test the posix special mode
|
||||
|
||||
// accept some reasonable default
|
||||
new_ucmd!()
|
||||
.args(&["-p", "-P", "dir/file"])
|
||||
|
@ -164,10 +164,3 @@ fn test_posix_all() {
|
|||
// fail on empty path
|
||||
new_ucmd!().args(&["-p", "-P", ""]).fails().no_stdout();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_args_parsing() {
|
||||
// fail on no args
|
||||
let empty_args: [String; 0] = [];
|
||||
new_ucmd!().args(&empty_args).fails().no_stdout();
|
||||
}
|
||||
|
|
|
@ -36,6 +36,11 @@ fn escaped_slash() {
|
|||
.stdout_only("hello\\ world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unescaped_double_quote() {
|
||||
new_ucmd!().args(&["\\\""]).succeeds().stdout_only("\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escaped_hex() {
|
||||
new_ucmd!().args(&["\\x41"]).succeeds().stdout_only("A");
|
||||
|
@ -665,3 +670,13 @@ fn sub_alternative_upper_hex() {
|
|||
.succeeds()
|
||||
.stdout_only("0x2A");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char_as_byte() {
|
||||
new_ucmd!().args(&["%c", "🙃"]).succeeds().stdout_only("ð");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_infinite_loop() {
|
||||
new_ucmd!().args(&["a", "b"]).succeeds().stdout_only("a");
|
||||
}
|
||||
|
|
1
tests/fixtures/cksum/raw/blake2b_single_file.expected
vendored
Normal file
1
tests/fixtures/cksum/raw/blake2b_single_file.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
— ‘‰å`Ãxœÿf†øWÑûþEtÞBãÀl«¹W^Jö0šaX´ÓÀ8Á´<C381>‚‹5<15>BÍÀ9m•Ã
|
1
tests/fixtures/cksum/raw/bsd_single_file.expected
vendored
Normal file
1
tests/fixtures/cksum/raw/bsd_single_file.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<1F>
|
1
tests/fixtures/cksum/raw/crc_single_file.expected
vendored
Normal file
1
tests/fixtures/cksum/raw/crc_single_file.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
訓h
|
BIN
tests/fixtures/cksum/raw/md5_single_file.expected
vendored
Normal file
BIN
tests/fixtures/cksum/raw/md5_single_file.expected
vendored
Normal file
Binary file not shown.
1
tests/fixtures/cksum/raw/sha1_single_file.expected
vendored
Normal file
1
tests/fixtures/cksum/raw/sha1_single_file.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<EFBFBD>к<>؈:=<18>m毽(%,<2C><>
|
BIN
tests/fixtures/cksum/raw/sha224_single_file.expected
vendored
Normal file
BIN
tests/fixtures/cksum/raw/sha224_single_file.expected
vendored
Normal file
Binary file not shown.
1
tests/fixtures/cksum/raw/sha256_single_file.expected
vendored
Normal file
1
tests/fixtures/cksum/raw/sha256_single_file.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
÷Ä PPà0’P
gê^‘ <09>SkE‚þœC[Ù+?
|
3
tests/fixtures/cksum/raw/sha384_single_file.expected
vendored
Normal file
3
tests/fixtures/cksum/raw/sha384_single_file.expected
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
K荵
|
||||
2吠鋳<19>シ<EFBFBD>マクアト/g胤コ囹LZ{WZ3Sゥ
|
||||
守Hヒ
|
1
tests/fixtures/cksum/raw/sha512_single_file.expected
vendored
Normal file
1
tests/fixtures/cksum/raw/sha512_single_file.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
–Td«%VªÕ޼sØšÒ!åYyu)ì¯ÀôfÁ•ÏöÖâÆ– |T,ýBn^Oऊ¡VgºD k!=Í<03>ú
|
1
tests/fixtures/cksum/raw/sm3_single_file.expected
vendored
Normal file
1
tests/fixtures/cksum/raw/sm3_single_file.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
m)k€]þÒ(ß0<C39F>»›CyMÔíg@¡p§‚i›Â
|
1
tests/fixtures/cksum/raw/sysv_single_file.expected
vendored
Normal file
1
tests/fixtures/cksum/raw/sysv_single_file.expected
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
I
|
Loading…
Add table
Add a link
Reference in a new issue