mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
Merge branch 'main' into sort-mem-percent
This commit is contained in:
commit
4f83924092
56 changed files with 890 additions and 411 deletions
4
.github/workflows/CICD.yml
vendored
4
.github/workflows/CICD.yml
vendored
|
@ -417,14 +417,14 @@ jobs:
|
|||
--arg multisize "$SIZE_MULTI" \
|
||||
'{($date): { sha: $sha, size: $size, multisize: $multisize, }}' > size-result.json
|
||||
- name: Download the previous individual size result
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
uses: dawidd6/action-download-artifact@v8
|
||||
with:
|
||||
workflow: CICD.yml
|
||||
name: individual-size-result
|
||||
repo: uutils/coreutils
|
||||
path: dl
|
||||
- name: Download the previous size result
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
uses: dawidd6/action-download-artifact@v8
|
||||
with:
|
||||
workflow: CICD.yml
|
||||
name: size-result
|
||||
|
|
4
.github/workflows/GnuTests.yml
vendored
4
.github/workflows/GnuTests.yml
vendored
|
@ -91,7 +91,7 @@ jobs:
|
|||
working-directory: ${{ steps.vars.outputs.path_GNU }}
|
||||
|
||||
- name: Retrieve reference artifacts
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
uses: dawidd6/action-download-artifact@v8
|
||||
# ref: <https://github.com/dawidd6/action-download-artifact>
|
||||
continue-on-error: true ## don't break the build for missing reference artifacts (may be expired or just not generated yet)
|
||||
with:
|
||||
|
@ -105,7 +105,7 @@ jobs:
|
|||
run: |
|
||||
## Install dependencies
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y autoconf autopoint bison texinfo gperf gcc g++ gdb python3-pyinotify jq valgrind libexpect-perl libacl1-dev libattr1-dev libcap-dev libselinux1-dev attr
|
||||
sudo apt-get install -y autoconf autopoint bison texinfo gperf gcc g++ gdb python3-pyinotify jq valgrind libexpect-perl libacl1-dev libattr1-dev libcap-dev libselinux1-dev attr quilt
|
||||
- name: Add various locales
|
||||
shell: bash
|
||||
run: |
|
||||
|
|
2
.github/workflows/ignore-intermittent.txt
vendored
2
.github/workflows/ignore-intermittent.txt
vendored
|
@ -1,3 +1,5 @@
|
|||
tests/tail/inotify-dir-recreate
|
||||
tests/timeout/timeout
|
||||
tests/rm/rm1
|
||||
tests/misc/stdbuf
|
||||
tests/misc/usage_vs_getopt
|
||||
|
|
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -337,18 +337,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.26"
|
||||
version = "4.5.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783"
|
||||
checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.26"
|
||||
version = "4.5.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121"
|
||||
checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -861,7 +861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1276,7 +1276,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1996,7 +1996,7 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2241,7 +2241,7 @@ dependencies = [
|
|||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix 0.38.43",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2617,9 +2617,7 @@ name = "uu_date"
|
|||
version = "0.0.29"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"clap",
|
||||
"iana-time-zone",
|
||||
"libc",
|
||||
"parse_datetime",
|
||||
"uucore",
|
||||
|
@ -2879,11 +2877,9 @@ version = "0.0.29"
|
|||
dependencies = [
|
||||
"ansi-width",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"clap",
|
||||
"glob",
|
||||
"hostname",
|
||||
"iana-time-zone",
|
||||
"lscolors",
|
||||
"number_prefix",
|
||||
"once_cell",
|
||||
|
@ -3472,6 +3468,8 @@ version = "0.0.29"
|
|||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"blake3",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"clap",
|
||||
"crc32fast",
|
||||
"data-encoding",
|
||||
|
@ -3481,6 +3479,7 @@ dependencies = [
|
|||
"dunce",
|
||||
"glob",
|
||||
"hex",
|
||||
"iana-time-zone",
|
||||
"itertools 0.14.0",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
|
@ -3656,7 +3655,7 @@ version = "0.1.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -576,6 +576,7 @@ semicolon_if_nothing_returned = "warn"
|
|||
single_char_pattern = "warn"
|
||||
explicit_iter_loop = "warn"
|
||||
if_not_else = "warn"
|
||||
manual_if_else = "warn"
|
||||
|
||||
all = { level = "deny", priority = -1 }
|
||||
cargo = { level = "warn", priority = -1 }
|
||||
|
|
|
@ -241,6 +241,8 @@ DEBUG=1 bash util/run-gnu-test.sh tests/misc/sm3sum.pl
|
|||
|
||||
Note that GNU test suite relies on individual utilities (not the multicall binary).
|
||||
|
||||
You also need to install [quilt](https://savannah.nongnu.org/projects/quilt), a tool used to manage a stack of patches for modifying GNU tests.
|
||||
|
||||
On FreeBSD, you need to install packages for GNU coreutils and sed (used in shell scripts instead of system commands):
|
||||
|
||||
```shell
|
||||
|
|
|
@ -125,16 +125,13 @@ fn basename(fullname: &str, suffix: &str) -> String {
|
|||
|
||||
// Convert to path buffer and get last path component
|
||||
let pb = PathBuf::from(path);
|
||||
match pb.components().last() {
|
||||
Some(c) => {
|
||||
let name = c.as_os_str().to_str().unwrap();
|
||||
if name == suffix {
|
||||
name.to_string()
|
||||
} else {
|
||||
name.strip_suffix(suffix).unwrap_or(name).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
None => String::new(),
|
||||
}
|
||||
pb.components().next_back().map_or_else(String::new, |c| {
|
||||
let name = c.as_os_str().to_str().unwrap();
|
||||
if name == suffix {
|
||||
name.to_string()
|
||||
} else {
|
||||
name.strip_suffix(suffix).unwrap_or(name).to_string()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -13,8 +13,9 @@ use std::iter;
|
|||
use std::path::Path;
|
||||
use uucore::checksum::{
|
||||
calculate_blake2b_length, detect_algo, digest_reader, perform_checksum_validation,
|
||||
ChecksumError, ChecksumOptions, ALGORITHM_OPTIONS_BLAKE2B, ALGORITHM_OPTIONS_BSD,
|
||||
ALGORITHM_OPTIONS_CRC, ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SYSV, SUPPORTED_ALGORITHMS,
|
||||
ChecksumError, ChecksumOptions, ChecksumVerbose, ALGORITHM_OPTIONS_BLAKE2B,
|
||||
ALGORITHM_OPTIONS_BSD, ALGORITHM_OPTIONS_CRC, ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SYSV,
|
||||
SUPPORTED_ALGORITHMS,
|
||||
};
|
||||
use uucore::{
|
||||
encoding,
|
||||
|
@ -322,13 +323,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
|| iter::once(OsStr::new("-")).collect::<Vec<_>>(),
|
||||
|files| files.map(OsStr::new).collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
let verbose = ChecksumVerbose::new(status, quiet, warn);
|
||||
|
||||
let opts = ChecksumOptions {
|
||||
binary: binary_flag,
|
||||
ignore_missing,
|
||||
quiet,
|
||||
status,
|
||||
strict,
|
||||
warn,
|
||||
verbose,
|
||||
};
|
||||
|
||||
return perform_checksum_validation(files.iter().copied(), algo_option, length, opts);
|
||||
|
@ -462,19 +464,22 @@ pub fn uu_app() -> Command {
|
|||
.short('w')
|
||||
.long("warn")
|
||||
.help("warn about improperly formatted checksum lines")
|
||||
.action(ArgAction::SetTrue),
|
||||
.action(ArgAction::SetTrue)
|
||||
.overrides_with_all([options::STATUS, options::QUIET]),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::STATUS)
|
||||
.long("status")
|
||||
.help("don't output anything, status code shows success")
|
||||
.action(ArgAction::SetTrue),
|
||||
.action(ArgAction::SetTrue)
|
||||
.overrides_with_all([options::WARN, options::QUIET]),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::QUIET)
|
||||
.long(options::QUIET)
|
||||
.help("don't print OK for each successfully verified file")
|
||||
.action(ArgAction::SetTrue),
|
||||
.action(ArgAction::SetTrue)
|
||||
.overrides_with_all([options::WARN, options::STATUS]),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::IGNORE_MISSING)
|
||||
|
|
|
@ -20,10 +20,8 @@ path = "src/date.rs"
|
|||
[dependencies]
|
||||
chrono = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
uucore = { workspace = true }
|
||||
uucore = { workspace = true, features = ["custom-tz-fmt"] }
|
||||
parse_datetime = { workspace = true }
|
||||
chrono-tz = { workspace = true }
|
||||
iana-time-zone = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = { workspace = true }
|
||||
|
|
|
@ -6,17 +6,16 @@
|
|||
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes
|
||||
|
||||
use chrono::format::{Item, StrftimeItems};
|
||||
use chrono::{DateTime, FixedOffset, Local, Offset, TimeDelta, TimeZone, Utc};
|
||||
use chrono::{DateTime, FixedOffset, Local, Offset, TimeDelta, Utc};
|
||||
#[cfg(windows)]
|
||||
use chrono::{Datelike, Timelike};
|
||||
use chrono_tz::{OffsetName, Tz};
|
||||
use clap::{crate_version, Arg, ArgAction, Command};
|
||||
use iana_time_zone::get_timezone;
|
||||
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
|
||||
use libc::{clock_settime, timespec, CLOCK_REALTIME};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::PathBuf;
|
||||
use uucore::custom_tz_fmt::custom_time_format;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::FromIo;
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
|
@ -274,21 +273,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
for date in dates {
|
||||
match date {
|
||||
Ok(date) => {
|
||||
// TODO - Revisit when chrono 0.5 is released. https://github.com/chronotope/chrono/issues/970
|
||||
let tz = match std::env::var("TZ") {
|
||||
// TODO Support other time zones...
|
||||
Ok(s) if s == "UTC0" || s.is_empty() => Tz::Etc__UTC,
|
||||
_ => match get_timezone() {
|
||||
Ok(tz_str) => tz_str.parse().unwrap(),
|
||||
Err(_) => Tz::Etc__UTC,
|
||||
},
|
||||
};
|
||||
let offset = tz.offset_from_utc_date(&Utc::now().date_naive());
|
||||
let tz_abbreviation = offset.abbreviation();
|
||||
// GNU `date` uses `%N` for nano seconds, however crate::chrono uses `%f`
|
||||
let format_string = &format_string
|
||||
.replace("%N", "%f")
|
||||
.replace("%Z", tz_abbreviation.unwrap_or("UTC"));
|
||||
let format_string = custom_time_format(format_string);
|
||||
// Refuse to pass this string to chrono as it is crashing in this crate
|
||||
if format_string.contains("%#z") {
|
||||
return Err(USimpleError::new(
|
||||
|
@ -298,7 +283,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
}
|
||||
// Hack to work around panic in chrono,
|
||||
// TODO - remove when a fix for https://github.com/chronotope/chrono/issues/623 is released
|
||||
let format_items = StrftimeItems::new(format_string);
|
||||
let format_items = StrftimeItems::new(format_string.as_str());
|
||||
if format_items.clone().any(|i| i == Item::Error) {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
|
|
|
@ -54,7 +54,7 @@ fn is_over_mounted(mounts: &[MountInfo], mount: &MountInfo) -> bool {
|
|||
let last_mount_for_dir = mounts
|
||||
.iter()
|
||||
.filter(|m| m.mount_dir == mount.mount_dir)
|
||||
.last();
|
||||
.next_back();
|
||||
|
||||
if let Some(lmi) = last_mount_for_dir {
|
||||
lmi.dev_name != mount.dev_name
|
||||
|
|
13
src/uu/env/src/env.rs
vendored
13
src/uu/env/src/env.rs
vendored
|
@ -130,14 +130,11 @@ fn parse_signal_opt<'a>(opts: &mut Options<'a>, opt: &'a OsStr) -> UResult<()> {
|
|||
}
|
||||
});
|
||||
for sig in sig_vec {
|
||||
let sig_str = match sig.to_str() {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("{}: invalid signal", sig.quote()),
|
||||
))
|
||||
}
|
||||
let Some(sig_str) = sig.to_str() else {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("{}: invalid signal", sig.quote()),
|
||||
));
|
||||
};
|
||||
let sig_val = parse_signal_value(sig_str)?;
|
||||
if !opts.ignore_signal.contains(&sig_val) {
|
||||
|
|
|
@ -255,9 +255,8 @@ impl ParagraphStream<'_> {
|
|||
if l_slice.starts_with("From ") {
|
||||
true
|
||||
} else {
|
||||
let colon_posn = match l_slice.find(':') {
|
||||
Some(n) => n,
|
||||
None => return false,
|
||||
let Some(colon_posn) = l_slice.find(':') else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// header field must be nonzero length
|
||||
|
@ -560,12 +559,11 @@ impl<'a> Iterator for WordSplit<'a> {
|
|||
|
||||
// find the start of the next word, and record if we find a tab character
|
||||
let (before_tab, after_tab, word_start) =
|
||||
match self.analyze_tabs(&self.string[old_position..]) {
|
||||
(b, a, Some(s)) => (b, a, s + old_position),
|
||||
(_, _, None) => {
|
||||
self.position = self.length;
|
||||
return None;
|
||||
}
|
||||
if let (b, a, Some(s)) = self.analyze_tabs(&self.string[old_position..]) {
|
||||
(b, a, s + old_position)
|
||||
} else {
|
||||
self.position = self.length;
|
||||
return None;
|
||||
};
|
||||
|
||||
// find the beginning of the next whitespace
|
||||
|
|
|
@ -24,6 +24,7 @@ use uucore::checksum::escape_filename;
|
|||
use uucore::checksum::perform_checksum_validation;
|
||||
use uucore::checksum::ChecksumError;
|
||||
use uucore::checksum::ChecksumOptions;
|
||||
use uucore::checksum::ChecksumVerbose;
|
||||
use uucore::checksum::HashAlgorithm;
|
||||
use uucore::error::{FromIo, UResult};
|
||||
use uucore::sum::{Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
|
||||
|
@ -240,13 +241,14 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
|
|||
|| iter::once(OsStr::new("-")).collect::<Vec<_>>(),
|
||||
|files| files.map(OsStr::new).collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
let verbose = ChecksumVerbose::new(status, quiet, warn);
|
||||
|
||||
let opts = ChecksumOptions {
|
||||
binary,
|
||||
ignore_missing,
|
||||
quiet,
|
||||
status,
|
||||
strict,
|
||||
warn,
|
||||
verbose,
|
||||
};
|
||||
|
||||
// Execute the checksum validation
|
||||
|
@ -356,14 +358,16 @@ pub fn uu_app_common() -> Command {
|
|||
.short('q')
|
||||
.long(options::QUIET)
|
||||
.help("don't print OK for each successfully verified file")
|
||||
.action(ArgAction::SetTrue),
|
||||
.action(ArgAction::SetTrue)
|
||||
.overrides_with_all([options::STATUS, options::WARN]),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::STATUS)
|
||||
.short('s')
|
||||
.long("status")
|
||||
.help("don't output anything, status code shows success")
|
||||
.action(ArgAction::SetTrue),
|
||||
.action(ArgAction::SetTrue)
|
||||
.overrides_with_all([options::QUIET, options::WARN]),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::STRICT)
|
||||
|
@ -382,7 +386,8 @@ pub fn uu_app_common() -> Command {
|
|||
.short('w')
|
||||
.long("warn")
|
||||
.help("warn about improperly formatted checksum lines")
|
||||
.action(ArgAction::SetTrue),
|
||||
.action(ArgAction::SetTrue)
|
||||
.overrides_with_all([options::QUIET, options::STATUS]),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("zero")
|
||||
|
|
|
@ -91,9 +91,8 @@ fn process_num_block(
|
|||
}
|
||||
if let Some(n) = multiplier {
|
||||
options.push(OsString::from("-c"));
|
||||
let num = match num.checked_mul(n) {
|
||||
Some(n) => n,
|
||||
None => return Some(Err(ParseError::Overflow)),
|
||||
let Some(num) = num.checked_mul(n) else {
|
||||
return Some(Err(ParseError::Overflow));
|
||||
};
|
||||
options.push(OsString::from(format!("{num}")));
|
||||
} else {
|
||||
|
|
|
@ -652,7 +652,7 @@ fn copy_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> UR
|
|||
}
|
||||
|
||||
let mut targetpath = target_dir.to_path_buf();
|
||||
let filename = sourcepath.components().last().unwrap();
|
||||
let filename = sourcepath.components().next_back().unwrap();
|
||||
targetpath.push(filename);
|
||||
|
||||
show_if_err!(copy(sourcepath, &targetpath, b));
|
||||
|
|
|
@ -154,12 +154,7 @@ fn handle_obsolete(args: &mut Vec<String>) -> Option<usize> {
|
|||
}
|
||||
|
||||
fn table() {
|
||||
// GNU kill doesn't list the EXIT signal with --table, so we ignore it, too
|
||||
for (idx, signal) in ALL_SIGNALS
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, s)| **s != "EXIT")
|
||||
{
|
||||
for (idx, signal) in ALL_SIGNALS.iter().enumerate() {
|
||||
println!("{idx: >#2} {signal}");
|
||||
}
|
||||
}
|
||||
|
@ -183,8 +178,7 @@ fn print_signal(signal_name_or_value: &str) -> UResult<()> {
|
|||
}
|
||||
|
||||
fn print_signals() {
|
||||
// GNU kill doesn't list the EXIT signal with --list, so we ignore it, too
|
||||
for signal in ALL_SIGNALS.iter().filter(|x| **x != "EXIT") {
|
||||
for signal in ALL_SIGNALS {
|
||||
println!("{signal}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,9 @@ path = "src/ls.rs"
|
|||
[dependencies]
|
||||
ansi-width = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
chrono-tz = { workspace = true }
|
||||
clap = { workspace = true, features = ["env"] }
|
||||
glob = { workspace = true }
|
||||
hostname = { workspace = true }
|
||||
iana-time-zone = { workspace = true }
|
||||
lscolors = { workspace = true }
|
||||
number_prefix = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
|
@ -31,6 +29,7 @@ selinux = { workspace = true, optional = true }
|
|||
terminal_size = { workspace = true }
|
||||
uucore = { workspace = true, features = [
|
||||
"colors",
|
||||
"custom-tz-fmt",
|
||||
"entries",
|
||||
"format",
|
||||
"fs",
|
||||
|
|
|
@ -27,14 +27,12 @@ use std::{
|
|||
use std::{collections::HashSet, io::IsTerminal};
|
||||
|
||||
use ansi_width::ansi_width;
|
||||
use chrono::{DateTime, Local, TimeDelta, TimeZone, Utc};
|
||||
use chrono_tz::{OffsetName, Tz};
|
||||
use chrono::{DateTime, Local, TimeDelta};
|
||||
use clap::{
|
||||
builder::{NonEmptyStringValueParser, PossibleValue, ValueParser},
|
||||
crate_version, Arg, ArgAction, Command,
|
||||
};
|
||||
use glob::{MatchOptions, Pattern};
|
||||
use iana_time_zone::get_timezone;
|
||||
use lscolors::LsColors;
|
||||
use term_grid::{Direction, Filling, Grid, GridOptions};
|
||||
|
||||
|
@ -60,6 +58,7 @@ use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR};
|
|||
use uucore::line_ending::LineEnding;
|
||||
use uucore::quoting_style::{self, escape_name, QuotingStyle};
|
||||
use uucore::{
|
||||
custom_tz_fmt,
|
||||
display::Quotable,
|
||||
error::{set_exit_code, UError, UResult},
|
||||
format_usage,
|
||||
|
@ -345,31 +344,6 @@ fn is_recent(time: DateTime<Local>) -> bool {
|
|||
time + TimeDelta::try_seconds(31_556_952 / 2).unwrap() > Local::now()
|
||||
}
|
||||
|
||||
/// Get the alphabetic abbreviation of the current timezone.
|
||||
///
|
||||
/// For example, "UTC" or "CET" or "PDT".
|
||||
fn timezone_abbrev() -> String {
|
||||
let tz = match std::env::var("TZ") {
|
||||
// TODO Support other time zones...
|
||||
Ok(s) if s == "UTC0" || s.is_empty() => Tz::Etc__UTC,
|
||||
_ => match get_timezone() {
|
||||
Ok(tz_str) => tz_str.parse().unwrap(),
|
||||
Err(_) => Tz::Etc__UTC,
|
||||
},
|
||||
};
|
||||
let offset = tz.offset_from_utc_date(&Utc::now().date_naive());
|
||||
offset.abbreviation().unwrap_or("UTC").to_string()
|
||||
}
|
||||
|
||||
/// Format the given time according to a custom format string.
|
||||
fn custom_time_format(fmt: &str, time: DateTime<Local>) -> String {
|
||||
// TODO Refactor the common code from `ls` and `date` for rendering dates.
|
||||
// TODO - Revisit when chrono 0.5 is released. https://github.com/chronotope/chrono/issues/970
|
||||
// GNU `date` uses `%N` for nano seconds, however the `chrono` crate uses `%f`.
|
||||
let fmt = fmt.replace("%N", "%f").replace("%Z", &timezone_abbrev());
|
||||
time.format(&fmt).to_string()
|
||||
}
|
||||
|
||||
impl TimeStyle {
|
||||
/// Format the given time according to this time format style.
|
||||
fn format(&self, time: DateTime<Local>) -> String {
|
||||
|
@ -386,7 +360,9 @@ impl TimeStyle {
|
|||
//So it's not yet implemented
|
||||
(Self::Locale, true) => time.format("%b %e %H:%M").to_string(),
|
||||
(Self::Locale, false) => time.format("%b %e %Y").to_string(),
|
||||
(Self::Format(e), _) => custom_time_format(e, time),
|
||||
(Self::Format(fmt), _) => time
|
||||
.format(custom_tz_fmt::custom_time_format(fmt).as_str())
|
||||
.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -403,8 +379,8 @@ fn parse_time_style(options: &clap::ArgMatches) -> Result<TimeStyle, LsError> {
|
|||
//If both FULL_TIME and TIME_STYLE are present
|
||||
//The one added last is dominant
|
||||
if options.get_flag(options::FULL_TIME)
|
||||
&& options.indices_of(options::FULL_TIME).unwrap().last()
|
||||
> options.indices_of(options::TIME_STYLE).unwrap().last()
|
||||
&& options.indices_of(options::FULL_TIME).unwrap().next_back()
|
||||
> options.indices_of(options::TIME_STYLE).unwrap().next_back()
|
||||
{
|
||||
Ok(TimeStyle::FullIso)
|
||||
} else {
|
||||
|
|
|
@ -101,8 +101,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
let options = SeqOptions {
|
||||
separator: matches
|
||||
.get_one::<String>(OPT_SEPARATOR)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or("\n")
|
||||
.map_or("\n", |s| s.as_str())
|
||||
.to_string(),
|
||||
terminator: matches
|
||||
.get_one::<String>(OPT_TERMINATOR)
|
||||
|
@ -150,13 +149,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
|
||||
let precision = select_precision(first_precision, increment_precision, last_precision);
|
||||
|
||||
let format = match options.format {
|
||||
Some(f) => {
|
||||
let f = Format::<num_format::Float>::parse(f)?;
|
||||
Some(f)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let format = options
|
||||
.format
|
||||
.map(Format::<num_format::Float>::parse)
|
||||
.transpose()?;
|
||||
|
||||
let result = print_seq(
|
||||
(first.number, increment.number, last.number),
|
||||
precision,
|
||||
|
@ -164,12 +161,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
&options.terminator,
|
||||
options.equal_width,
|
||||
padding,
|
||||
&format,
|
||||
format.as_ref(),
|
||||
);
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) if err.kind() == ErrorKind::BrokenPipe => Ok(()),
|
||||
Err(e) => Err(e.map_err_context(|| "write error".into())),
|
||||
Err(err) => Err(err.map_err_context(|| "write error".into())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,7 +260,7 @@ fn print_seq(
|
|||
terminator: &str,
|
||||
pad: bool,
|
||||
padding: usize,
|
||||
format: &Option<Format<num_format::Float>>,
|
||||
format: Option<&Format<num_format::Float>>,
|
||||
) -> std::io::Result<()> {
|
||||
let stdout = stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
|
|
|
@ -48,7 +48,7 @@ rand = "0.8.3"
|
|||
```rust
|
||||
use rand::prelude::*;
|
||||
fn main() {
|
||||
let suffixes = ['k', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
|
||||
let suffixes = ['k', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q'];
|
||||
let mut rng = thread_rng();
|
||||
for _ in 0..100000 {
|
||||
println!(
|
||||
|
|
|
@ -224,11 +224,8 @@ fn read_write_loop<I: WriteableTmpFile>(
|
|||
let mut sender_option = Some(sender);
|
||||
let mut tmp_files = vec![];
|
||||
loop {
|
||||
let chunk = match receiver.recv() {
|
||||
Ok(it) => it,
|
||||
_ => {
|
||||
return Ok(ReadResult::WroteChunksToFile { tmp_files });
|
||||
}
|
||||
let Ok(chunk) = receiver.recv() else {
|
||||
return Ok(ReadResult::WroteChunksToFile { tmp_files });
|
||||
};
|
||||
|
||||
let tmp_file = write::<I>(
|
||||
|
|
|
@ -82,7 +82,10 @@ impl NumInfo {
|
|||
if Self::is_invalid_char(char, &mut had_decimal_pt, parse_settings) {
|
||||
return if let Some(start) = start {
|
||||
let has_si_unit = parse_settings.accept_si_units
|
||||
&& matches!(char, 'K' | 'k' | 'M' | 'G' | 'T' | 'P' | 'E' | 'Z' | 'Y');
|
||||
&& matches!(
|
||||
char,
|
||||
'K' | 'k' | 'M' | 'G' | 'T' | 'P' | 'E' | 'Z' | 'Y' | 'R' | 'Q'
|
||||
);
|
||||
(
|
||||
Self { exponent, sign },
|
||||
start..if has_si_unit { idx + 1 } else { idx },
|
||||
|
@ -176,6 +179,8 @@ fn get_unit(unit: Option<char>) -> u8 {
|
|||
'E' => 6,
|
||||
'Z' => 7,
|
||||
'Y' => 8,
|
||||
'R' => 9,
|
||||
'Q' => 10,
|
||||
_ => 0,
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -34,6 +34,7 @@ use std::ffi::{OsStr, OsString};
|
|||
use std::fs::{File, OpenOptions};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
|
||||
use std::num::IntErrorKind;
|
||||
use std::ops::Range;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
@ -288,7 +289,7 @@ impl GlobalSettings {
|
|||
// GNU sort (8.32) invalid: b, B, 1B, p, e, z, y
|
||||
let size = Parser::default()
|
||||
.with_allow_list(&[
|
||||
"b", "k", "K", "m", "M", "g", "G", "t", "T", "P", "E", "Z", "Y", "%",
|
||||
"b", "k", "K", "m", "M", "g", "G", "t", "T", "P", "E", "Z", "Y", "R", "Q", "%",
|
||||
])
|
||||
.with_default_unit("K")
|
||||
.with_b_byte_count(true)
|
||||
|
@ -534,8 +535,9 @@ impl<'a> Line<'a> {
|
|||
} else {
|
||||
// include a trailing si unit
|
||||
if selector.settings.mode == SortMode::HumanNumeric
|
||||
&& self.line[selection.end..initial_selection.end]
|
||||
.starts_with(&['k', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'][..])
|
||||
&& self.line[selection.end..initial_selection.end].starts_with(
|
||||
&['k', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q'][..],
|
||||
)
|
||||
{
|
||||
selection.end += 1;
|
||||
}
|
||||
|
@ -696,9 +698,17 @@ impl KeyPosition {
|
|||
.ok_or_else(|| format!("invalid key {}", key.quote()))?;
|
||||
let char = field_and_char.next();
|
||||
|
||||
let field = field
|
||||
.parse()
|
||||
.map_err(|e| format!("failed to parse field index {}: {}", field.quote(), e))?;
|
||||
let field = match field.parse::<usize>() {
|
||||
Ok(f) => f,
|
||||
Err(e) if *e.kind() == IntErrorKind::PosOverflow => usize::MAX,
|
||||
Err(e) => {
|
||||
return Err(format!(
|
||||
"failed to parse field index {} {}",
|
||||
field.quote(),
|
||||
e
|
||||
))
|
||||
}
|
||||
};
|
||||
if field == 0 {
|
||||
return Err("field index can not be 0".to_string());
|
||||
}
|
||||
|
@ -1361,14 +1371,14 @@ pub fn uu_app() -> Command {
|
|||
options::check::QUIET,
|
||||
options::check::DIAGNOSE_FIRST,
|
||||
]))
|
||||
.conflicts_with(options::OUTPUT)
|
||||
.conflicts_with_all([options::OUTPUT, options::check::CHECK_SILENT])
|
||||
.help("check for sorted input; do not sort"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::check::CHECK_SILENT)
|
||||
.short('C')
|
||||
.long(options::check::CHECK_SILENT)
|
||||
.conflicts_with(options::OUTPUT)
|
||||
.conflicts_with_all([options::OUTPUT, options::check::CHECK])
|
||||
.help(
|
||||
"exit successfully if the given file is already sorted, \
|
||||
and exit with status 1 otherwise.",
|
||||
|
|
|
@ -1408,18 +1408,15 @@ where
|
|||
};
|
||||
|
||||
let bytes = line.as_slice();
|
||||
match kth_chunk {
|
||||
Some(chunk_number) => {
|
||||
if (i % num_chunks) == (chunk_number - 1) as usize {
|
||||
stdout_writer.write_all(bytes)?;
|
||||
}
|
||||
if let Some(chunk_number) = kth_chunk {
|
||||
if (i % num_chunks) == (chunk_number - 1) as usize {
|
||||
stdout_writer.write_all(bytes)?;
|
||||
}
|
||||
None => {
|
||||
let writer = out_files.get_writer(i % num_chunks, settings)?;
|
||||
let writer_stdin_open = custom_write_all(bytes, writer, settings)?;
|
||||
if !writer_stdin_open {
|
||||
closed_writers += 1;
|
||||
}
|
||||
} else {
|
||||
let writer = out_files.get_writer(i % num_chunks, settings)?;
|
||||
let writer_stdin_open = custom_write_all(bytes, writer, settings)?;
|
||||
if !writer_stdin_open {
|
||||
closed_writers += 1;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
|
|
|
@ -34,9 +34,8 @@ pub enum ParseError {
|
|||
/// Parses obsolete syntax
|
||||
/// tail -\[NUM\]\[bcl\]\[f\] and tail +\[NUM\]\[bcl\]\[f\]
|
||||
pub fn parse_obsolete(src: &OsString) -> Option<Result<ObsoleteArgs, ParseError>> {
|
||||
let mut rest = match src.to_str() {
|
||||
Some(src) => src,
|
||||
None => return Some(Err(ParseError::InvalidEncoding)),
|
||||
let Some(mut rest) = src.to_str() else {
|
||||
return Some(Err(ParseError::InvalidEncoding));
|
||||
};
|
||||
let sign = if let Some(r) = rest.strip_prefix('-') {
|
||||
rest = r;
|
||||
|
@ -86,9 +85,8 @@ pub fn parse_obsolete(src: &OsString) -> Option<Result<ObsoleteArgs, ParseError>
|
|||
}
|
||||
|
||||
let multiplier = if mode == 'b' { 512 } else { 1 };
|
||||
let num = match num.checked_mul(multiplier) {
|
||||
Some(n) => n,
|
||||
None => return Some(Err(ParseError::Overflow)),
|
||||
let Some(num) = num.checked_mul(multiplier) else {
|
||||
return Some(Err(ParseError::Overflow));
|
||||
};
|
||||
|
||||
Some(Ok(ObsoleteArgs {
|
||||
|
|
|
@ -210,13 +210,8 @@ fn integers(a: &OsStr, b: &OsStr, op: &OsStr) -> ParseResult<bool> {
|
|||
fn files(a: &OsStr, b: &OsStr, op: &OsStr) -> ParseResult<bool> {
|
||||
// Don't manage the error. GNU doesn't show error when doing
|
||||
// test foo -nt bar
|
||||
let f_a = match fs::metadata(a) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
let f_b = match fs::metadata(b) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return Ok(false),
|
||||
let (Ok(f_a), Ok(f_b)) = (fs::metadata(a), fs::metadata(b)) else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
Ok(match op.to_str() {
|
||||
|
@ -290,11 +285,8 @@ fn path(path: &OsStr, condition: &PathCondition) -> bool {
|
|||
fs::metadata(path)
|
||||
};
|
||||
|
||||
let metadata = match metadata {
|
||||
Ok(metadata) => metadata,
|
||||
Err(_) => {
|
||||
return false;
|
||||
}
|
||||
let Ok(metadata) = metadata else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let file_type = metadata.file_type();
|
||||
|
|
|
@ -129,6 +129,7 @@ pub fn uu_app() -> Command {
|
|||
.arg(
|
||||
Arg::new(options::FOREGROUND)
|
||||
.long(options::FOREGROUND)
|
||||
.short('f')
|
||||
.help(
|
||||
"when not running timeout directly from a shell prompt, allow \
|
||||
COMMAND to read from the TTY and get TTY signals; in this mode, \
|
||||
|
@ -148,6 +149,7 @@ pub fn uu_app() -> Command {
|
|||
.arg(
|
||||
Arg::new(options::PRESERVE_STATUS)
|
||||
.long(options::PRESERVE_STATUS)
|
||||
.short('p')
|
||||
.help("exit with the same status as COMMAND, even when the command times out")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
|
|
|
@ -599,14 +599,11 @@ fn parse_timestamp(s: &str) -> UResult<FileTime> {
|
|||
|
||||
let local = NaiveDateTime::parse_from_str(&ts, format)
|
||||
.map_err(|_| USimpleError::new(1, format!("invalid date ts format {}", ts.quote())))?;
|
||||
let mut local = match chrono::Local.from_local_datetime(&local) {
|
||||
LocalResult::Single(dt) => dt,
|
||||
_ => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid date ts format {}", ts.quote()),
|
||||
))
|
||||
}
|
||||
let LocalResult::Single(mut local) = chrono::Local.from_local_datetime(&local) else {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid date ts format {}", ts.quote()),
|
||||
));
|
||||
};
|
||||
|
||||
// Chrono caps seconds at 59, but 60 is valid. It might be a leap second
|
||||
|
|
|
@ -171,12 +171,9 @@ impl Uniq {
|
|||
|
||||
// Convert the leftover bytes to UTF-8 for character-based -w
|
||||
// If invalid UTF-8, just compare them as individual bytes (fallback).
|
||||
let string_after_skip = match std::str::from_utf8(fields_to_check) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
// Fallback: if invalid UTF-8, treat them as single-byte “chars”
|
||||
return closure(&mut fields_to_check.iter().map(|&b| b as char));
|
||||
}
|
||||
let Ok(string_after_skip) = std::str::from_utf8(fields_to_check) else {
|
||||
// Fallback: if invalid UTF-8, treat them as single-byte “chars”
|
||||
return closure(&mut fields_to_check.iter().map(|&b| b as char));
|
||||
};
|
||||
|
||||
let total_chars = string_after_skip.chars().count();
|
||||
|
|
|
@ -18,6 +18,8 @@ edition = "2021"
|
|||
path = "src/lib/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
chrono = { workspace = true }
|
||||
chrono-tz = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
uucore_procs = { workspace = true }
|
||||
number_prefix = { workspace = true }
|
||||
|
@ -25,6 +27,7 @@ dns-lookup = { workspace = true, optional = true }
|
|||
dunce = { version = "1.0.4", optional = true }
|
||||
wild = "2.2.1"
|
||||
glob = { workspace = true }
|
||||
iana-time-zone = { workspace = true }
|
||||
lazy_static = "1.4.0"
|
||||
# * optional
|
||||
itertools = { workspace = true, optional = true }
|
||||
|
@ -114,4 +117,5 @@ utf8 = []
|
|||
utmpx = ["time", "time/macros", "libc", "dns-lookup"]
|
||||
version-cmp = []
|
||||
wide = []
|
||||
custom-tz-fmt = []
|
||||
tty = []
|
||||
|
|
|
@ -12,6 +12,8 @@ pub mod buf_copy;
|
|||
pub mod checksum;
|
||||
#[cfg(feature = "colors")]
|
||||
pub mod colors;
|
||||
#[cfg(feature = "custom-tz-fmt")]
|
||||
pub mod custom_tz_fmt;
|
||||
#[cfg(feature = "encoding")]
|
||||
pub mod encoding;
|
||||
#[cfg(feature = "format")]
|
||||
|
|
|
@ -19,7 +19,7 @@ use std::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
error::{set_exit_code, FromIo, UError, UResult, USimpleError},
|
||||
error::{FromIo, UError, UResult, USimpleError},
|
||||
os_str_as_bytes, os_str_from_bytes, read_os_string_lines, show, show_error, show_warning_caps,
|
||||
sum::{
|
||||
Blake2b, Blake3, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha3_224,
|
||||
|
@ -130,10 +130,12 @@ impl From<ChecksumError> for LineCheckError {
|
|||
enum FileCheckError {
|
||||
/// a generic UError was encountered in sub-functions
|
||||
UError(Box<dyn UError>),
|
||||
/// the checksum file is improperly formatted.
|
||||
ImproperlyFormatted,
|
||||
/// reading of the checksum file failed
|
||||
CantOpenChecksumFile,
|
||||
/// processing of the file is considered as a failure regarding the
|
||||
/// provided flags. This however does not stop the processing of
|
||||
/// further files.
|
||||
Failed,
|
||||
}
|
||||
|
||||
impl From<Box<dyn UError>> for FileCheckError {
|
||||
|
@ -148,15 +150,57 @@ impl From<ChecksumError> for FileCheckError {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy)]
|
||||
pub enum ChecksumVerbose {
|
||||
Status,
|
||||
Quiet,
|
||||
Normal,
|
||||
Warning,
|
||||
}
|
||||
|
||||
impl ChecksumVerbose {
|
||||
pub fn new(status: bool, quiet: bool, warn: bool) -> Self {
|
||||
use ChecksumVerbose::*;
|
||||
|
||||
// Assume only one of the three booleans will be enabled at once.
|
||||
// This is ensured by clap's overriding arguments.
|
||||
match (status, quiet, warn) {
|
||||
(true, _, _) => Status,
|
||||
(_, true, _) => Quiet,
|
||||
(_, _, true) => Warning,
|
||||
_ => Normal,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn over_status(self) -> bool {
|
||||
self > Self::Status
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn over_quiet(self) -> bool {
|
||||
self > Self::Quiet
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn at_least_warning(self) -> bool {
|
||||
self >= Self::Warning
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ChecksumVerbose {
|
||||
fn default() -> Self {
|
||||
Self::Normal
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct regroups CLI flags.
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct ChecksumOptions {
|
||||
pub binary: bool,
|
||||
pub ignore_missing: bool,
|
||||
pub quiet: bool,
|
||||
pub status: bool,
|
||||
pub strict: bool,
|
||||
pub warn: bool,
|
||||
pub verbose: ChecksumVerbose,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
@ -235,20 +279,19 @@ pub fn create_sha3(bits: Option<usize>) -> UResult<HashAlgorithm> {
|
|||
}
|
||||
|
||||
#[allow(clippy::comparison_chain)]
|
||||
fn cksum_output(res: &ChecksumResult, status: bool) {
|
||||
fn print_cksum_report(res: &ChecksumResult) {
|
||||
if res.bad_format == 1 {
|
||||
show_warning_caps!("{} line is improperly formatted", res.bad_format);
|
||||
} else if res.bad_format > 1 {
|
||||
show_warning_caps!("{} lines are improperly formatted", res.bad_format);
|
||||
}
|
||||
|
||||
if !status {
|
||||
if res.failed_cksum == 1 {
|
||||
show_warning_caps!("{} computed checksum did NOT match", res.failed_cksum);
|
||||
} else if res.failed_cksum > 1 {
|
||||
show_warning_caps!("{} computed checksums did NOT match", res.failed_cksum);
|
||||
}
|
||||
if res.failed_cksum == 1 {
|
||||
show_warning_caps!("{} computed checksum did NOT match", res.failed_cksum);
|
||||
} else if res.failed_cksum > 1 {
|
||||
show_warning_caps!("{} computed checksums did NOT match", res.failed_cksum);
|
||||
}
|
||||
|
||||
if res.failed_open_file == 1 {
|
||||
show_warning_caps!("{} listed file could not be read", res.failed_open_file);
|
||||
} else if res.failed_open_file > 1 {
|
||||
|
@ -284,10 +327,10 @@ impl FileChecksumResult {
|
|||
|
||||
/// The cli options might prevent to display on the outcome of the
|
||||
/// comparison on STDOUT.
|
||||
fn can_display(&self, opts: ChecksumOptions) -> bool {
|
||||
fn can_display(&self, verbose: ChecksumVerbose) -> bool {
|
||||
match self {
|
||||
FileChecksumResult::Ok => !opts.status && !opts.quiet,
|
||||
FileChecksumResult::Failed => !opts.status,
|
||||
FileChecksumResult::Ok => verbose.over_quiet(),
|
||||
FileChecksumResult::Failed => verbose.over_status(),
|
||||
FileChecksumResult::CantOpen => true,
|
||||
}
|
||||
}
|
||||
|
@ -310,9 +353,9 @@ fn print_file_report<W: Write>(
|
|||
filename: &[u8],
|
||||
result: FileChecksumResult,
|
||||
prefix: &str,
|
||||
opts: ChecksumOptions,
|
||||
verbose: ChecksumVerbose,
|
||||
) {
|
||||
if result.can_display(opts) {
|
||||
if result.can_display(verbose) {
|
||||
let _ = write!(w, "{prefix}");
|
||||
let _ = w.write_all(filename);
|
||||
let _ = writeln!(w, ": {result}");
|
||||
|
@ -589,7 +632,7 @@ fn get_file_to_check(
|
|||
filename_bytes,
|
||||
FileChecksumResult::CantOpen,
|
||||
"",
|
||||
opts,
|
||||
opts.verbose,
|
||||
);
|
||||
};
|
||||
match File::open(filename) {
|
||||
|
@ -648,12 +691,11 @@ fn get_input_file(filename: &OsStr) -> UResult<Box<dyn Read>> {
|
|||
fn identify_algo_name_and_length(
|
||||
line_info: &LineInfo,
|
||||
algo_name_input: Option<&str>,
|
||||
last_algo: &mut Option<String>,
|
||||
) -> Option<(String, Option<usize>)> {
|
||||
let algorithm = line_info
|
||||
.algo_name
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.to_lowercase();
|
||||
let algo_from_line = line_info.algo_name.clone().unwrap_or_default();
|
||||
let algorithm = algo_from_line.to_lowercase();
|
||||
*last_algo = Some(algo_from_line);
|
||||
|
||||
// check if we are called with XXXsum (example: md5sum) but we detected a different algo parsing the file
|
||||
// (for example SHA1 (f) = d...)
|
||||
|
@ -711,7 +753,7 @@ fn compute_and_check_digest_from_file(
|
|||
filename,
|
||||
FileChecksumResult::from_bool(checksum_correct),
|
||||
prefix,
|
||||
opts,
|
||||
opts.verbose,
|
||||
);
|
||||
|
||||
if checksum_correct {
|
||||
|
@ -726,11 +768,13 @@ fn process_algo_based_line(
|
|||
line_info: &LineInfo,
|
||||
cli_algo_name: Option<&str>,
|
||||
opts: ChecksumOptions,
|
||||
last_algo: &mut Option<String>,
|
||||
) -> Result<(), LineCheckError> {
|
||||
let filename_to_check = line_info.filename.as_slice();
|
||||
|
||||
let (algo_name, algo_byte_len) = identify_algo_name_and_length(line_info, cli_algo_name)
|
||||
.ok_or(LineCheckError::ImproperlyFormatted)?;
|
||||
let (algo_name, algo_byte_len) =
|
||||
identify_algo_name_and_length(line_info, cli_algo_name, last_algo)
|
||||
.ok_or(LineCheckError::ImproperlyFormatted)?;
|
||||
|
||||
// If the digest bitlen is known, we can check the format of the expected
|
||||
// checksum with it.
|
||||
|
@ -789,13 +833,13 @@ fn process_non_algo_based_line(
|
|||
/// matched the expected.
|
||||
/// If the comparison didn't happen, return a `LineChecksumError`.
|
||||
fn process_checksum_line(
|
||||
filename_input: &OsStr,
|
||||
line: &OsStr,
|
||||
i: usize,
|
||||
cli_algo_name: Option<&str>,
|
||||
cli_algo_length: Option<usize>,
|
||||
opts: ChecksumOptions,
|
||||
cached_regex: &mut Option<LineFormat>,
|
||||
last_algo: &mut Option<String>,
|
||||
) -> Result<(), LineCheckError> {
|
||||
let line_bytes = os_str_as_bytes(line)?;
|
||||
|
||||
|
@ -806,34 +850,19 @@ fn process_checksum_line(
|
|||
|
||||
// Use `LineInfo` to extract the data of a line.
|
||||
// Then, depending on its format, apply a different pre-treatment.
|
||||
if let Some(line_info) = LineInfo::parse(line, cached_regex) {
|
||||
if line_info.format == LineFormat::AlgoBased {
|
||||
process_algo_based_line(&line_info, cli_algo_name, opts)
|
||||
} else if let Some(cli_algo) = cli_algo_name {
|
||||
// If we match a non-algo based regex, we expect a cli argument
|
||||
// to give us the algorithm to use
|
||||
process_non_algo_based_line(i, &line_info, cli_algo, cli_algo_length, opts)
|
||||
} else {
|
||||
// We have no clue of what algorithm to use
|
||||
return Err(LineCheckError::ImproperlyFormatted);
|
||||
}
|
||||
} else {
|
||||
if opts.warn {
|
||||
let algo = if let Some(algo_name_input) = cli_algo_name {
|
||||
algo_name_input.to_uppercase()
|
||||
} else {
|
||||
"Unknown algorithm".to_string()
|
||||
};
|
||||
eprintln!(
|
||||
"{}: {}: {}: improperly formatted {} checksum line",
|
||||
util_name(),
|
||||
&filename_input.maybe_quote(),
|
||||
i + 1,
|
||||
algo
|
||||
);
|
||||
}
|
||||
let Some(line_info) = LineInfo::parse(line, cached_regex) else {
|
||||
return Err(LineCheckError::ImproperlyFormatted);
|
||||
};
|
||||
|
||||
Err(LineCheckError::ImproperlyFormatted)
|
||||
if line_info.format == LineFormat::AlgoBased {
|
||||
process_algo_based_line(&line_info, cli_algo_name, opts, last_algo)
|
||||
} else if let Some(cli_algo) = cli_algo_name {
|
||||
// If we match a non-algo based regex, we expect a cli argument
|
||||
// to give us the algorithm to use
|
||||
process_non_algo_based_line(i, &line_info, cli_algo, cli_algo_length, opts)
|
||||
} else {
|
||||
// We have no clue of what algorithm to use
|
||||
return Err(LineCheckError::ImproperlyFormatted);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -856,7 +885,6 @@ fn process_checksum_file(
|
|||
Err(e) => {
|
||||
// Could not read the file, show the error and continue to the next file
|
||||
show_error!("{e}");
|
||||
set_exit_code(1);
|
||||
return Err(FileCheckError::CantOpenChecksumFile);
|
||||
}
|
||||
}
|
||||
|
@ -868,16 +896,20 @@ fn process_checksum_file(
|
|||
// cached_regex is used to ensure that several non algo-based checksum line
|
||||
// will use the same regex.
|
||||
let mut cached_regex = None;
|
||||
// last_algo caches the algorithm used in the last line to print a warning
|
||||
// message for the current line if improperly formatted.
|
||||
// Behavior tested in gnu_cksum_c::test_warn
|
||||
let mut last_algo = None;
|
||||
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
let line_result = process_checksum_line(
|
||||
filename_input,
|
||||
line,
|
||||
i,
|
||||
cli_algo_name,
|
||||
cli_algo_length,
|
||||
opts,
|
||||
&mut cached_regex,
|
||||
&mut last_algo,
|
||||
);
|
||||
|
||||
// Match a first time to elude critical UErrors, and increment the total
|
||||
|
@ -893,7 +925,26 @@ fn process_checksum_file(
|
|||
match line_result {
|
||||
Ok(()) => res.correct += 1,
|
||||
Err(DigestMismatch) => res.failed_cksum += 1,
|
||||
Err(ImproperlyFormatted) => res.bad_format += 1,
|
||||
Err(ImproperlyFormatted) => {
|
||||
res.bad_format += 1;
|
||||
|
||||
if opts.verbose.at_least_warning() {
|
||||
let algo = if let Some(algo_name_input) = cli_algo_name {
|
||||
Cow::Owned(algo_name_input.to_uppercase())
|
||||
} else if let Some(algo) = &last_algo {
|
||||
Cow::Borrowed(algo.as_str())
|
||||
} else {
|
||||
Cow::Borrowed("Unknown algorithm")
|
||||
};
|
||||
eprintln!(
|
||||
"{}: {}: {}: improperly formatted {} checksum line",
|
||||
util_name(),
|
||||
&filename_input.maybe_quote(),
|
||||
i + 1,
|
||||
algo
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(CantOpenFile | FileIsDirectory) => res.failed_open_file += 1,
|
||||
Err(FileNotFound) if !opts.ignore_missing => res.failed_open_file += 1,
|
||||
_ => continue,
|
||||
|
@ -903,36 +954,43 @@ fn process_checksum_file(
|
|||
// not a single line correctly formatted found
|
||||
// return an error
|
||||
if res.total_properly_formatted() == 0 {
|
||||
if !opts.status {
|
||||
if opts.verbose.over_status() {
|
||||
log_no_properly_formatted(get_filename_for_output(filename_input, input_is_stdin));
|
||||
}
|
||||
set_exit_code(1);
|
||||
return Err(FileCheckError::ImproperlyFormatted);
|
||||
return Err(FileCheckError::Failed);
|
||||
}
|
||||
|
||||
// if any incorrectly formatted line, show it
|
||||
cksum_output(&res, opts.status);
|
||||
if opts.verbose.over_status() {
|
||||
print_cksum_report(&res);
|
||||
}
|
||||
|
||||
if opts.ignore_missing && res.correct == 0 {
|
||||
// we have only bad format
|
||||
// and we had ignore-missing
|
||||
eprintln!(
|
||||
"{}: {}: no file was verified",
|
||||
util_name(),
|
||||
filename_input.maybe_quote(),
|
||||
);
|
||||
set_exit_code(1);
|
||||
if opts.verbose.over_status() {
|
||||
eprintln!(
|
||||
"{}: {}: no file was verified",
|
||||
util_name(),
|
||||
filename_input.maybe_quote(),
|
||||
);
|
||||
}
|
||||
return Err(FileCheckError::Failed);
|
||||
}
|
||||
|
||||
// strict means that we should have an exit code.
|
||||
if opts.strict && res.bad_format > 0 {
|
||||
set_exit_code(1);
|
||||
return Err(FileCheckError::Failed);
|
||||
}
|
||||
|
||||
// if we have any failed checksum verification, we set an exit code
|
||||
// except if we have ignore_missing
|
||||
if (res.failed_cksum > 0 || res.failed_open_file > 0) && !opts.ignore_missing {
|
||||
set_exit_code(1);
|
||||
// If a file was missing, return an error unless we explicitly ignore it.
|
||||
if res.failed_open_file > 0 && !opts.ignore_missing {
|
||||
return Err(FileCheckError::Failed);
|
||||
}
|
||||
|
||||
// Obviously, if a checksum failed at some point, report the error.
|
||||
if res.failed_cksum > 0 {
|
||||
return Err(FileCheckError::Failed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -950,16 +1008,23 @@ pub fn perform_checksum_validation<'a, I>(
|
|||
where
|
||||
I: Iterator<Item = &'a OsStr>,
|
||||
{
|
||||
let mut failed = false;
|
||||
|
||||
// if cksum has several input files, it will print the result for each file
|
||||
for filename_input in files {
|
||||
use FileCheckError::*;
|
||||
match process_checksum_file(filename_input, algo_name_input, length_input, opts) {
|
||||
Err(UError(e)) => return Err(e),
|
||||
Err(CantOpenChecksumFile | ImproperlyFormatted) | Ok(_) => continue,
|
||||
Err(Failed | CantOpenChecksumFile) => failed = true,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
if failed {
|
||||
Err(USimpleError::new(1, ""))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn digest_reader<T: Read>(
|
||||
|
@ -1416,7 +1481,7 @@ mod tests {
|
|||
|
||||
for (filename, result, prefix, expected) in cases {
|
||||
let mut buffer: Vec<u8> = vec![];
|
||||
print_file_report(&mut buffer, filename, *result, prefix, opts);
|
||||
print_file_report(&mut buffer, filename, *result, prefix, opts.verbose);
|
||||
assert_eq!(&buffer, expected)
|
||||
}
|
||||
}
|
||||
|
|
58
src/uucore/src/lib/features/custom_tz_fmt.rs
Normal file
58
src/uucore/src/lib/features/custom_tz_fmt.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use chrono::{TimeZone, Utc};
|
||||
use chrono_tz::{OffsetName, Tz};
|
||||
use iana_time_zone::get_timezone;
|
||||
|
||||
/// Get the alphabetic abbreviation of the current timezone.
|
||||
///
|
||||
/// For example, "UTC" or "CET" or "PDT"
|
||||
fn timezone_abbreviation() -> String {
|
||||
let tz = match std::env::var("TZ") {
|
||||
// TODO Support other time zones...
|
||||
Ok(s) if s == "UTC0" || s.is_empty() => Tz::Etc__UTC,
|
||||
_ => match get_timezone() {
|
||||
Ok(tz_str) => tz_str.parse().unwrap(),
|
||||
Err(_) => Tz::Etc__UTC,
|
||||
},
|
||||
};
|
||||
|
||||
let offset = tz.offset_from_utc_date(&Utc::now().date_naive());
|
||||
offset.abbreviation().unwrap_or("UTC").to_string()
|
||||
}
|
||||
|
||||
/// Adapt the given string to be accepted by the chrono library crate.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// fmt: the format of the string
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// A string that can be used as parameter of the chrono functions that use formats
|
||||
pub fn custom_time_format(fmt: &str) -> String {
|
||||
// TODO - Revisit when chrono 0.5 is released. https://github.com/chronotope/chrono/issues/970
|
||||
// GNU `date` uses `%N` for nano seconds, however the `chrono` crate uses `%f`.
|
||||
fmt.replace("%N", "%f")
|
||||
.replace("%Z", timezone_abbreviation().as_ref())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{custom_time_format, timezone_abbreviation};
|
||||
|
||||
#[test]
|
||||
fn test_custom_time_format() {
|
||||
assert_eq!(custom_time_format("%Y-%m-%d %H-%M-%S"), "%Y-%m-%d %H-%M-%S");
|
||||
assert_eq!(custom_time_format("%d-%m-%Y %H-%M-%S"), "%d-%m-%Y %H-%M-%S");
|
||||
assert_eq!(custom_time_format("%Y-%m-%d %H-%M-%S"), "%Y-%m-%d %H-%M-%S");
|
||||
assert_eq!(
|
||||
custom_time_format("%Y-%m-%d %H-%M-%S.%N"),
|
||||
"%Y-%m-%d %H-%M-%S.%f"
|
||||
);
|
||||
assert_eq!(custom_time_format("%Z"), timezone_abbreviation());
|
||||
}
|
||||
}
|
|
@ -652,14 +652,10 @@ pub fn are_hardlinks_to_same_file(_source: &Path, _target: &Path) -> bool {
|
|||
/// * `bool` - Returns `true` if the paths are hard links to the same file, and `false` otherwise.
|
||||
#[cfg(unix)]
|
||||
pub fn are_hardlinks_to_same_file(source: &Path, target: &Path) -> bool {
|
||||
let source_metadata = match fs::symlink_metadata(source) {
|
||||
Ok(metadata) => metadata,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
let target_metadata = match fs::symlink_metadata(target) {
|
||||
Ok(metadata) => metadata,
|
||||
Err(_) => return false,
|
||||
let (Ok(source_metadata), Ok(target_metadata)) =
|
||||
(fs::symlink_metadata(source), fs::symlink_metadata(target))
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
source_metadata.ino() == target_metadata.ino() && source_metadata.dev() == target_metadata.dev()
|
||||
|
@ -682,14 +678,10 @@ pub fn are_hardlinks_or_one_way_symlink_to_same_file(_source: &Path, _target: &P
|
|||
/// * `bool` - Returns `true` if either of above conditions are true, and `false` otherwise.
|
||||
#[cfg(unix)]
|
||||
pub fn are_hardlinks_or_one_way_symlink_to_same_file(source: &Path, target: &Path) -> bool {
|
||||
let source_metadata = match fs::metadata(source) {
|
||||
Ok(metadata) => metadata,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
let target_metadata = match fs::symlink_metadata(target) {
|
||||
Ok(metadata) => metadata,
|
||||
Err(_) => return false,
|
||||
let (Ok(source_metadata), Ok(target_metadata)) =
|
||||
(fs::metadata(source), fs::symlink_metadata(target))
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
source_metadata.ino() == target_metadata.ino() && source_metadata.dev() == target_metadata.dev()
|
||||
|
|
|
@ -79,13 +79,10 @@ pub fn apply_xattrs<P: AsRef<Path>>(
|
|||
/// `true` if the file has extended attributes (indicating an ACL), `false` otherwise.
|
||||
pub fn 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,
|
||||
}
|
||||
xattr::list(file).is_ok_and(|acl| {
|
||||
// if we have extra attributes, we have an acl
|
||||
acl.count() > 0
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the permissions bits of a file or directory which has Access Control List (ACL) entries based on its
|
||||
|
@ -132,7 +129,7 @@ pub fn get_acl_perm_bits_from_xattr<P: AsRef<Path>>(source: P) -> u32 {
|
|||
|
||||
for entry in acl_entries.chunks_exact(4) {
|
||||
// Third byte and fourth byte will be the perm bits
|
||||
perm = (perm << 3) | entry[2] as u32 | entry[3] as u32;
|
||||
perm = (perm << 3) | u32::from(entry[2]) | u32::from(entry[3]);
|
||||
}
|
||||
return perm;
|
||||
}
|
||||
|
|
|
@ -273,18 +273,15 @@ impl ChownExecutor {
|
|||
#[allow(clippy::cognitive_complexity)]
|
||||
fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
|
||||
let path = root.as_ref();
|
||||
let meta = match self.obtain_meta(path, self.dereference) {
|
||||
Some(m) => m,
|
||||
_ => {
|
||||
if self.verbosity.level == VerbosityLevel::Verbose {
|
||||
println!(
|
||||
"failed to change ownership of {} to {}",
|
||||
path.quote(),
|
||||
self.raw_owner
|
||||
);
|
||||
}
|
||||
return 1;
|
||||
let Some(meta) = self.obtain_meta(path, self.dereference) else {
|
||||
if self.verbosity.level == VerbosityLevel::Verbose {
|
||||
println!(
|
||||
"failed to change ownership of {} to {}",
|
||||
path.quote(),
|
||||
self.raw_owner
|
||||
);
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
|
||||
if self.recursive
|
||||
|
@ -370,17 +367,15 @@ impl ChownExecutor {
|
|||
Ok(entry) => entry,
|
||||
};
|
||||
let path = entry.path();
|
||||
let meta = match self.obtain_meta(path, self.dereference) {
|
||||
Some(m) => m,
|
||||
_ => {
|
||||
ret = 1;
|
||||
if entry.file_type().is_dir() {
|
||||
// Instruct walkdir to skip this directory to avoid getting another error
|
||||
// when walkdir tries to query the children of this directory.
|
||||
iterator.skip_current_dir();
|
||||
}
|
||||
continue;
|
||||
|
||||
let Some(meta) = self.obtain_meta(path, self.dereference) else {
|
||||
ret = 1;
|
||||
if entry.file_type().is_dir() {
|
||||
// Instruct walkdir to skip this directory to avoid getting another error
|
||||
// when walkdir tries to query the children of this directory.
|
||||
iterator.skip_current_dir();
|
||||
}
|
||||
continue;
|
||||
};
|
||||
|
||||
if self.preserve_root && is_root(path, self.traverse_symlinks == TraverseSymlinks::All)
|
||||
|
@ -425,24 +420,18 @@ impl ChownExecutor {
|
|||
|
||||
fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
|
||||
let path = path.as_ref();
|
||||
|
||||
let meta = get_metadata(path, follow);
|
||||
|
||||
match meta {
|
||||
Err(e) => {
|
||||
match self.verbosity.level {
|
||||
VerbosityLevel::Silent => (),
|
||||
_ => show_error!(
|
||||
get_metadata(path, follow)
|
||||
.inspect_err(|e| {
|
||||
if self.verbosity.level != VerbosityLevel::Silent {
|
||||
show_error!(
|
||||
"cannot {} {}: {}",
|
||||
if follow { "dereference" } else { "access" },
|
||||
path.quote(),
|
||||
strip_errno(&e)
|
||||
),
|
||||
strip_errno(e)
|
||||
);
|
||||
}
|
||||
None
|
||||
}
|
||||
Ok(meta) => Some(meta),
|
||||
}
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -46,6 +46,8 @@ pub use crate::features::buf_copy;
|
|||
pub use crate::features::checksum;
|
||||
#[cfg(feature = "colors")]
|
||||
pub use crate::features::colors;
|
||||
#[cfg(feature = "custom-tz-fmt")]
|
||||
pub use crate::features::custom_tz_fmt;
|
||||
#[cfg(feature = "encoding")]
|
||||
pub use crate::features::encoding;
|
||||
#[cfg(feature = "format")]
|
||||
|
|
|
@ -269,16 +269,9 @@ impl<'parser> Parser<'parser> {
|
|||
|
||||
/// Same as `parse()` but tries to return u64
|
||||
pub fn parse_u64(&self, size: &str) -> Result<u64, ParseSizeError> {
|
||||
match self.parse(size) {
|
||||
Ok(num_u128) => {
|
||||
let num_u64 = match u64::try_from(num_u128) {
|
||||
Ok(n) => n,
|
||||
Err(_) => return Err(ParseSizeError::size_too_big(size)),
|
||||
};
|
||||
Ok(num_u64)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
self.parse(size).and_then(|num_u128| {
|
||||
u64::try_from(num_u128).map_err(|_| ParseSizeError::size_too_big(size))
|
||||
})
|
||||
}
|
||||
|
||||
/// Same as `parse_u64()`, except returns `u64::MAX` on overflow
|
||||
|
|
|
@ -49,9 +49,8 @@ pub fn from_str(string: &str) -> Result<Duration, String> {
|
|||
if len == 0 {
|
||||
return Err("empty string".to_owned());
|
||||
}
|
||||
let slice = match string.get(..len - 1) {
|
||||
Some(s) => s,
|
||||
None => return Err(format!("invalid time interval {}", string.quote())),
|
||||
let Some(slice) = string.get(..len - 1) else {
|
||||
return Err(format!("invalid time interval {}", string.quote()));
|
||||
};
|
||||
let (numstr, times) = match string.chars().next_back().unwrap() {
|
||||
's' => (slice, 1),
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
// spell-checker:ignore (words) asdf algo algos asha mgmt xffname hexa GFYEQ HYQK Yqxb
|
||||
// spell-checker:ignore (words) asdf algo algos asha mgmt xffname hexa GFYEQ HYQK Yqxb dont
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
|
||||
|
@ -1284,6 +1284,18 @@ fn test_several_files_error_mgmt() {
|
|||
.stderr_contains("incorrect: no properly ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_unknown_checksum_file() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("missing")
|
||||
.fails()
|
||||
.stderr_only("cksum: missing: No such file or directory\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_comment_line() {
|
||||
// A comment in a checksum file shall be discarded unnoticed.
|
||||
|
@ -1811,6 +1823,373 @@ mod gnu_cksum_base64 {
|
|||
}
|
||||
}
|
||||
|
||||
/// This module reimplements the cksum-c.sh GNU test.
|
||||
mod gnu_cksum_c {
|
||||
use super::*;
|
||||
|
||||
const INVALID_SUM: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaafdb57c725157cb40b5aee8d937b8351477e";
|
||||
|
||||
fn make_scene() -> TestScenario {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.write("input", "9\n7\n1\n4\n2\n6\n3\n5\n8\n10\n");
|
||||
|
||||
let algos: &[&[&str]] = &[
|
||||
&["-a", "sha384"],
|
||||
&["-a", "blake2b"],
|
||||
&["-a", "blake2b", "-l", "384"],
|
||||
&["-a", "sm3"],
|
||||
];
|
||||
|
||||
for args in algos {
|
||||
let result = scene.ucmd().args(args).succeeds();
|
||||
let stdout = result.stdout();
|
||||
at.append_bytes("CHECKSUMS", stdout);
|
||||
}
|
||||
|
||||
scene
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_signed_checksums() {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_individual_digests_in_mixed_file() {
|
||||
let scene = make_scene();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("-a")
|
||||
.arg("sm3")
|
||||
.arg("CHECKSUMS")
|
||||
.succeeds();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_against_older_non_hex_formats() {
|
||||
let scene = make_scene();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-c")
|
||||
.arg("-a")
|
||||
.arg("crc")
|
||||
.arg("CHECKSUMS")
|
||||
.fails();
|
||||
|
||||
let crc_cmd = scene.ucmd().arg("-a").arg("crc").arg("input").succeeds();
|
||||
let crc_cmd_out = crc_cmd.stdout();
|
||||
scene.fixtures.write_bytes("CHECKSUMS.crc", crc_cmd_out);
|
||||
|
||||
scene.ucmd().arg("-c").arg("CHECKSUMS.crc").fails();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status() {
|
||||
let scene = make_scene();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--status")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.succeeds()
|
||||
.no_output();
|
||||
}
|
||||
|
||||
fn make_scene_with_comment() -> TestScenario {
|
||||
let scene = make_scene();
|
||||
|
||||
scene
|
||||
.fixtures
|
||||
.append("CHECKSUMS", "# Very important comment\n");
|
||||
|
||||
scene
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status_with_comment() {
|
||||
let scene = make_scene_with_comment();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--status")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.succeeds()
|
||||
.no_output();
|
||||
}
|
||||
|
||||
fn make_scene_with_invalid_line() -> TestScenario {
|
||||
let scene = make_scene_with_comment();
|
||||
|
||||
scene.fixtures.append("CHECKSUMS", "invalid_line\n");
|
||||
|
||||
scene
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_strict() {
|
||||
let scene = make_scene_with_invalid_line();
|
||||
|
||||
// without strict, succeeds
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.succeeds()
|
||||
.stderr_contains("1 line is improperly formatted");
|
||||
|
||||
// with strict, fails
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--strict")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.fails()
|
||||
.stderr_contains("1 line is improperly formatted");
|
||||
}
|
||||
|
||||
fn make_scene_with_two_invalid_lines() -> TestScenario {
|
||||
let scene = make_scene_with_comment();
|
||||
|
||||
scene
|
||||
.fixtures
|
||||
.append("CHECKSUMS", "invalid_line\ninvalid_line\n");
|
||||
|
||||
scene
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_strict_plural_checks() {
|
||||
let scene = make_scene_with_two_invalid_lines();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--strict")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.fails()
|
||||
.stderr_contains("2 lines are improperly formatted");
|
||||
}
|
||||
|
||||
fn make_scene_with_incorrect_checksum() -> TestScenario {
|
||||
let scene = make_scene_with_two_invalid_lines();
|
||||
|
||||
scene
|
||||
.fixtures
|
||||
.append("CHECKSUMS", &format!("SM3 (input) = {INVALID_SUM}\n"));
|
||||
|
||||
scene
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_with_incorrect_checksum() {
|
||||
let scene = make_scene_with_incorrect_checksum();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.fails()
|
||||
.stdout_contains("input: FAILED")
|
||||
.stderr_contains("1 computed checksum did NOT match");
|
||||
|
||||
// also fails with strict
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--strict")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.fails()
|
||||
.stdout_contains("input: FAILED")
|
||||
.stderr_contains("1 computed checksum did NOT match");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status_with_errors() {
|
||||
let scene = make_scene_with_incorrect_checksum();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--status")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.fails()
|
||||
.no_output();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_with_non_existing_file() {
|
||||
let scene = make_scene();
|
||||
scene
|
||||
.fixtures
|
||||
.write("CHECKSUMS2", &format!("SM3 (input2) = {INVALID_SUM}\n"));
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS2")
|
||||
.fails()
|
||||
.stdout_contains("input2: FAILED open or read")
|
||||
.stderr_contains("1 listed file could not be read");
|
||||
|
||||
// also fails with strict
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--strict")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS2")
|
||||
.fails()
|
||||
.stdout_contains("input2: FAILED open or read")
|
||||
.stderr_contains("1 listed file could not be read");
|
||||
}
|
||||
|
||||
fn make_scene_with_another_improperly_formatted() -> TestScenario {
|
||||
let scene = make_scene_with_incorrect_checksum();
|
||||
|
||||
scene.fixtures.append(
|
||||
"CHECKSUMS",
|
||||
&format!("BLAKE2b (missing-file) = {INVALID_SUM}\n"),
|
||||
);
|
||||
|
||||
scene
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_warn() {
|
||||
let scene = make_scene_with_another_improperly_formatted();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--warn")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.run()
|
||||
.stderr_contains("CHECKSUMS: 6: improperly formatted SM3 checksum line")
|
||||
.stderr_contains("CHECKSUMS: 9: improperly formatted BLAKE2b checksum line");
|
||||
}
|
||||
|
||||
fn make_scene_with_checksum_missing() -> TestScenario {
|
||||
let scene = make_scene_with_another_improperly_formatted();
|
||||
|
||||
scene.fixtures.write(
|
||||
"CHECKSUMS-missing",
|
||||
&format!("SM3 (nonexistent) = {INVALID_SUM}\n"),
|
||||
);
|
||||
|
||||
scene
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ignore_missing() {
|
||||
let scene = make_scene_with_checksum_missing();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--ignore-missing")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS-missing")
|
||||
.fails()
|
||||
.stdout_does_not_contain("nonexistent: No such file or directory")
|
||||
.stdout_does_not_contain("nonexistent: FAILED open or read")
|
||||
.stderr_contains("CHECKSUMS-missing: no file was verified");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status_and_warn() {
|
||||
let scene = make_scene_with_checksum_missing();
|
||||
|
||||
// --status before --warn
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--status")
|
||||
.arg("--warn")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.fails()
|
||||
.stderr_contains("CHECKSUMS: 9: improperly formatted BLAKE2b checksum line")
|
||||
.stderr_contains("WARNING: 3 lines are improperly formatted")
|
||||
.stderr_contains("WARNING: 1 computed checksum did NOT match");
|
||||
|
||||
// --warn before --status (status hides the results)
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--warn")
|
||||
.arg("--status")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.fails()
|
||||
.stderr_does_not_contain("CHECKSUMS: 9: improperly formatted BLAKE2b checksum line")
|
||||
.stderr_does_not_contain("WARNING: 3 lines are improperly formatted")
|
||||
.stderr_does_not_contain("WARNING: 1 computed checksum did NOT match");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status_and_ignore_missing() {
|
||||
let scene = make_scene_with_checksum_missing();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--status")
|
||||
.arg("--ignore-missing")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS")
|
||||
.fails()
|
||||
.no_output();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status_warn_and_ignore_missing() {
|
||||
let scene = make_scene_with_checksum_missing();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--status")
|
||||
.arg("--warn")
|
||||
.arg("--ignore-missing")
|
||||
.arg("--check")
|
||||
.arg("CHECKSUMS-missing")
|
||||
.fails()
|
||||
.stderr_contains("CHECKSUMS-missing: no file was verified")
|
||||
.stdout_does_not_contain("nonexistent: No such file or directory");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_several_files_dont_exist() {
|
||||
let scene = make_scene();
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("non-existing-1")
|
||||
.arg("non-existing-2")
|
||||
.fails()
|
||||
.stderr_contains("non-existing-1: No such file or directory")
|
||||
.stderr_contains("non-existing-2: No such file or directory");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_several_files_empty() {
|
||||
let scene = make_scene();
|
||||
scene.fixtures.touch("empty-1");
|
||||
scene.fixtures.touch("empty-2");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--check")
|
||||
.arg("empty-1")
|
||||
.arg("empty-2")
|
||||
.fails()
|
||||
.stderr_contains("empty-1: no properly formatted checksum lines found")
|
||||
.stderr_contains("empty-2: no properly formatted checksum lines found");
|
||||
}
|
||||
}
|
||||
|
||||
/// The tests in this module check the behavior of cksum when given different
|
||||
/// checksum formats and algorithms in the same file, while specifying an
|
||||
/// algorithm on CLI or not.
|
||||
|
|
|
@ -950,7 +950,7 @@ mod tests_split_iterator {
|
|||
| '*' | '?' | '[' | '#' | '˜' | '=' | '%' => {
|
||||
special = true;
|
||||
}
|
||||
_ => continue,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ fn test_kill_list_all_signals() {
|
|||
.stdout_contains("KILL")
|
||||
.stdout_contains("TERM")
|
||||
.stdout_contains("HUP")
|
||||
.stdout_does_not_contain("EXIT");
|
||||
.stdout_contains("EXIT");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -80,15 +80,16 @@ fn test_kill_list_all_signals_as_table() {
|
|||
.succeeds()
|
||||
.stdout_contains("KILL")
|
||||
.stdout_contains("TERM")
|
||||
.stdout_contains("HUP");
|
||||
.stdout_contains("HUP")
|
||||
.stdout_contains("EXIT");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kill_table_starts_at_1() {
|
||||
fn test_kill_table_starts_at_0() {
|
||||
new_ucmd!()
|
||||
.arg("-t")
|
||||
.succeeds()
|
||||
.stdout_matches(&Regex::new("^\\s?1\\sHUP").unwrap());
|
||||
.stdout_matches(&Regex::new("^\\s?0\\sEXIT").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -104,6 +105,7 @@ fn test_kill_table_lists_all_vertically() {
|
|||
assert!(signals.contains(&"KILL"));
|
||||
assert!(signals.contains(&"TERM"));
|
||||
assert!(signals.contains(&"HUP"));
|
||||
assert!(signals.contains(&"EXIT"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -143,6 +145,7 @@ fn test_kill_list_all_vertically() {
|
|||
assert!(signals.contains(&"KILL"));
|
||||
assert!(signals.contains(&"TERM"));
|
||||
assert!(signals.contains(&"HUP"));
|
||||
assert!(signals.contains(&"EXIT"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1315,3 +1315,30 @@ fn test_same_sort_mode_twice() {
|
|||
fn test_args_override() {
|
||||
new_ucmd!().args(&["-f", "-f"]).pipe_in("foo").succeeds();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_k_overflow() {
|
||||
let input = "2\n1\n";
|
||||
let output = "1\n2\n";
|
||||
new_ucmd!()
|
||||
.args(&["-k", "18446744073709551616"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_is(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_human_blocks_r_and_q() {
|
||||
let input = "1Q\n1R\n";
|
||||
let output = "1R\n1Q\n";
|
||||
new_ucmd!()
|
||||
.args(&["-h"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_is(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_args_check_conflict() {
|
||||
new_ucmd!().arg("-c").arg("-C").fails();
|
||||
}
|
||||
|
|
|
@ -65,13 +65,11 @@ fn test_zero_timeout() {
|
|||
new_ucmd!()
|
||||
.args(&["-v", "0", "sleep", ".1"])
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.no_stdout();
|
||||
.no_output();
|
||||
new_ucmd!()
|
||||
.args(&["-v", "0", "-s0", "-k0", "sleep", ".1"])
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.no_stdout();
|
||||
.no_output();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -82,15 +80,27 @@ fn test_command_empty_args() {
|
|||
.stderr_contains("timeout: empty string");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_foreground() {
|
||||
for arg in ["-f", "--foreground"] {
|
||||
new_ucmd!()
|
||||
.args(&[arg, ".1", "sleep", "10"])
|
||||
.fails()
|
||||
.code_is(124)
|
||||
.no_output();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_preserve_status() {
|
||||
new_ucmd!()
|
||||
.args(&["--preserve-status", ".1", "sleep", "10"])
|
||||
.fails()
|
||||
// 128 + SIGTERM = 128 + 15
|
||||
.code_is(128 + 15)
|
||||
.no_stderr()
|
||||
.no_stdout();
|
||||
for arg in ["-p", "--preserve-status"] {
|
||||
new_ucmd!()
|
||||
.args(&[arg, ".1", "sleep", "10"])
|
||||
.fails()
|
||||
// 128 + SIGTERM = 128 + 15
|
||||
.code_is(128 + 15)
|
||||
.no_output();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -102,8 +112,7 @@ fn test_preserve_status_even_when_send_signal() {
|
|||
.args(&["-s", cont_spelling, "--preserve-status", ".1", "sleep", "2"])
|
||||
.succeeds()
|
||||
.code_is(0)
|
||||
.no_stderr()
|
||||
.no_stdout();
|
||||
.no_output();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,14 +122,12 @@ fn test_dont_overflow() {
|
|||
.args(&["9223372036854775808d", "sleep", "0"])
|
||||
.succeeds()
|
||||
.code_is(0)
|
||||
.no_stderr()
|
||||
.no_stdout();
|
||||
.no_output();
|
||||
new_ucmd!()
|
||||
.args(&["-k", "9223372036854775808d", "10", "sleep", "0"])
|
||||
.succeeds()
|
||||
.code_is(0)
|
||||
.no_stderr()
|
||||
.no_stdout();
|
||||
.no_output();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -153,8 +160,7 @@ fn test_kill_after_long() {
|
|||
new_ucmd!()
|
||||
.args(&["--kill-after=1", "1", "sleep", "0"])
|
||||
.succeeds()
|
||||
.no_stdout()
|
||||
.no_stderr();
|
||||
.no_output();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -94,6 +94,12 @@ if [ "$(uname)" == "Linux" ]; then
|
|||
export SELINUX_ENABLED=1
|
||||
fi
|
||||
|
||||
# Set up quilt for patch management
|
||||
export QUILT_PATCHES="${ME_dir}/gnu-patches/"
|
||||
cd "$path_GNU"
|
||||
quilt push -a
|
||||
cd -
|
||||
|
||||
"${MAKE}" PROFILE="${UU_MAKE_PROFILE}"
|
||||
|
||||
cp "${UU_BUILD_DIR}/install" "${UU_BUILD_DIR}/ginstall" # The GNU tests rename this script before running, to avoid confusion with the make target
|
||||
|
@ -206,8 +212,6 @@ grep -rlE '/usr/local/bin/\s?/usr/local/bin' init.cfg tests/* | xargs -r sed -Ei
|
|||
# we should not regress our project just to match what GNU is going.
|
||||
# So, do some changes on the fly
|
||||
|
||||
eval cat "$path_UUTILS/util/gnu-patches/*.patch" | patch -N -r - -d "$path_GNU" -p 1 -i - || true
|
||||
|
||||
sed -i -e "s|rm: cannot remove 'e/slink'|rm: cannot remove 'e'|g" tests/rm/fail-eacces.sh
|
||||
|
||||
sed -i -e "s|rm: cannot remove 'a/b/file'|rm: cannot remove 'a'|g" tests/rm/cycle.sh
|
||||
|
|
10
util/gnu-patches/series
Normal file
10
util/gnu-patches/series
Normal file
|
@ -0,0 +1,10 @@
|
|||
tests_factor_factor.pl.patch
|
||||
tests_cksum_base64.patch
|
||||
tests_comm.pl.patch
|
||||
tests_cut_error_msg.patch
|
||||
tests_dup_source.patch
|
||||
tests_env_env-S.pl.patch
|
||||
tests_invalid_opt.patch
|
||||
tests_ls_no_cap.patch
|
||||
tests_sort_merge.pl.patch
|
||||
tests_tsort.patch
|
|
@ -1,8 +1,8 @@
|
|||
diff --git a/tests/cksum/cksum-base64.pl b/tests/cksum/cksum-base64.pl
|
||||
index a037a1628..c6d87d447 100755
|
||||
--- a/tests/cksum/cksum-base64.pl
|
||||
+++ b/tests/cksum/cksum-base64.pl
|
||||
@@ -91,8 +91,8 @@ my $prog = 'cksum';
|
||||
Index: gnu/tests/cksum/cksum-base64.pl
|
||||
===================================================================
|
||||
--- gnu.orig/tests/cksum/cksum-base64.pl
|
||||
+++ gnu/tests/cksum/cksum-base64.pl
|
||||
@@ -92,8 +92,8 @@ my $prog = 'cksum';
|
||||
my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose);
|
||||
|
||||
# Ensure hash names from cksum --help match those in @pairs above.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
diff --git a/tests/misc/comm.pl b/tests/misc/comm.pl
|
||||
index 5bd5f56d7..8322d92ba 100755
|
||||
--- a/tests/misc/comm.pl
|
||||
+++ b/tests/misc/comm.pl
|
||||
Index: gnu/tests/misc/comm.pl
|
||||
===================================================================
|
||||
--- gnu.orig/tests/misc/comm.pl
|
||||
+++ gnu/tests/misc/comm.pl
|
||||
@@ -73,18 +73,24 @@ my @Tests =
|
||||
|
||||
# invalid missing command line argument (1)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
diff --git a/tests/cut/cut.pl b/tests/cut/cut.pl
|
||||
index 1670db02e..ed633792a 100755
|
||||
--- a/tests/cut/cut.pl
|
||||
+++ b/tests/cut/cut.pl
|
||||
Index: gnu/tests/cut/cut.pl
|
||||
===================================================================
|
||||
--- gnu.orig/tests/cut/cut.pl
|
||||
+++ gnu/tests/cut/cut.pl
|
||||
@@ -29,13 +29,15 @@ my $mb_locale = $ENV{LOCALE_FR_UTF8};
|
||||
|
||||
my $prog = 'cut';
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
diff --git a/tests/mv/dup-source.sh b/tests/mv/dup-source.sh
|
||||
index 7bcd82fc3..0f9005296 100755
|
||||
--- a/tests/mv/dup-source.sh
|
||||
+++ b/tests/mv/dup-source.sh
|
||||
@@ -83,7 +83,7 @@ $i: cannot stat 'a': No such file or directory
|
||||
Index: gnu/tests/mv/dup-source.sh
|
||||
===================================================================
|
||||
--- gnu.orig/tests/mv/dup-source.sh
|
||||
+++ gnu/tests/mv/dup-source.sh
|
||||
@@ -83,7 +83,7 @@ $i: cannot stat 'a': No such file or dir
|
||||
$i: cannot stat 'a': No such file or directory
|
||||
$i: cannot stat 'b': No such file or directory
|
||||
$i: cannot move './b' to a subdirectory of itself, 'b/b'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
diff --git a/tests/env/env-S.pl b/tests/env/env-S.pl
|
||||
index 710ca82cf..af7cf6efa 100755
|
||||
--- a/tests/env/env-S.pl
|
||||
+++ b/tests/env/env-S.pl
|
||||
Index: gnu/tests/env/env-S.pl
|
||||
===================================================================
|
||||
--- gnu.orig/tests/env/env-S.pl
|
||||
+++ gnu/tests/env/env-S.pl
|
||||
@@ -209,27 +209,28 @@ my @Tests =
|
||||
{ERR=>"$prog: no terminating quote in -S string\n"}],
|
||||
['err5', q[-S'A=B\\q'], {EXIT=>125},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
diff --git a/tests/factor/factor.pl b/tests/factor/factor.pl
|
||||
index b1406c266..3d97cd6a5 100755
|
||||
--- a/tests/factor/factor.pl
|
||||
+++ b/tests/factor/factor.pl
|
||||
Index: gnu/tests/factor/factor.pl
|
||||
===================================================================
|
||||
--- gnu.orig/tests/factor/factor.pl
|
||||
+++ gnu/tests/factor/factor.pl
|
||||
@@ -61,12 +61,14 @@ my @Tests =
|
||||
# Map newer glibc diagnostic to expected.
|
||||
# Also map OpenBSD 5.1's "unknown option" to expected "invalid option".
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
diff --git a/tests/misc/invalid-opt.pl b/tests/misc/invalid-opt.pl
|
||||
index 4b9c4c184..4ccd89482 100755
|
||||
--- a/tests/misc/invalid-opt.pl
|
||||
+++ b/tests/misc/invalid-opt.pl
|
||||
Index: gnu/tests/misc/invalid-opt.pl
|
||||
===================================================================
|
||||
--- gnu.orig/tests/misc/invalid-opt.pl
|
||||
+++ gnu/tests/misc/invalid-opt.pl
|
||||
@@ -74,23 +74,13 @@ foreach my $prog (@built_programs)
|
||||
defined $out
|
||||
or $out = '';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
diff --git a/tests/sort/sort-merge.pl b/tests/sort/sort-merge.pl
|
||||
index 89eed0c64..c2f5aa7e5 100755
|
||||
--- a/tests/sort/sort-merge.pl
|
||||
+++ b/tests/sort/sort-merge.pl
|
||||
Index: gnu/tests/sort/sort-merge.pl
|
||||
===================================================================
|
||||
--- gnu.orig/tests/sort/sort-merge.pl
|
||||
+++ gnu/tests/sort/sort-merge.pl
|
||||
@@ -43,22 +43,22 @@ my @Tests =
|
||||
# check validation of --batch-size option
|
||||
['nmerge-0', "-m --batch-size=0", @inputs,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
diff --git a/tests/misc/tsort.pl b/tests/misc/tsort.pl
|
||||
index 70bdc474c..4fd420a4e 100755
|
||||
--- a/tests/misc/tsort.pl
|
||||
+++ b/tests/misc/tsort.pl
|
||||
Index: gnu/tests/misc/tsort.pl
|
||||
===================================================================
|
||||
--- gnu.orig/tests/misc/tsort.pl
|
||||
+++ gnu/tests/misc/tsort.pl
|
||||
@@ -54,8 +54,10 @@ my @Tests =
|
||||
|
||||
['only-one', {IN => {f => ""}}, {IN => {g => ""}},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue