1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

Merge branch 'main' into ls_LZ

This commit is contained in:
Terts Diepraam 2023-06-26 15:52:52 +02:00 committed by GitHub
commit 873c6c36f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 509 additions and 303 deletions

1
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1 @@
github: uutils

View file

@ -319,7 +319,7 @@ jobs:
shell: bash
run: |
RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items
- uses: DavidAnson/markdownlint-cli2-action@v10
- uses: DavidAnson/markdownlint-cli2-action@v11
with:
command: fix
globs: |

View file

@ -205,7 +205,7 @@ jobs:
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
# https://github.com/uutils/coreutils/issues/4294
# https://github.com/uutils/coreutils/issues/4295
IGNORE_INTERMITTENT='${path_UUTILS}/.github/workflows/ignore-intermittent.txt'
IGNORE_INTERMITTENT="${path_UUTILS}/.github/workflows/ignore-intermittent.txt"
mkdir -p ${{ steps.vars.outputs.path_reference }}

View file

@ -35,7 +35,7 @@ jobs:
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.3
- name: Prepare, build and test
uses: vmactions/freebsd-vm@v0.3.0
uses: vmactions/freebsd-vm@v0.3.1
with:
usesh: true
# We need jq to run show-utils.sh and bash to use inline shell string replacement
@ -125,7 +125,7 @@ jobs:
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.3
- name: Prepare, build and test
uses: vmactions/freebsd-vm@v0.3.0
uses: vmactions/freebsd-vm@v0.3.1
with:
usesh: true
# sync: sshfs

201
Cargo.lock generated
View file

@ -2,12 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "Inflector"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
[[package]]
name = "adler"
version = "1.0.2"
@ -43,12 +37,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "aliasable"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
[[package]]
name = "android-tzdata"
version = "0.1.1"
@ -192,9 +180,9 @@ dependencies = [
[[package]]
name = "blake3"
version = "1.3.3"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef"
checksum = "729b71f35bd3fa1a4c86b85d32c8b9069ea7fe14f7a53cfabb65f62d4265b888"
dependencies = [
"arrayref",
"arrayvec",
@ -371,6 +359,28 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "const-random"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e"
dependencies = [
"const-random-macro",
"proc-macro-hack",
]
[[package]]
name = "const-random-macro"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb"
dependencies = [
"getrandom",
"once_cell",
"proc-macro-hack",
"tiny-keccak",
]
[[package]]
name = "constant_time_eq"
version = "0.2.4"
@ -809,9 +819,12 @@ dependencies = [
[[package]]
name = "dlv-list"
version = "0.3.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
checksum = "d529fd73d344663edfd598ccb3f344e46034db51ebd103518eae34338248ad73"
dependencies = [
"const-random",
]
[[package]]
name = "dns-lookup"
@ -956,9 +969,18 @@ dependencies = [
[[package]]
name = "fundu"
version = "0.5.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a37cfff04a32112c22c5497b20b0b09100fca406e76afd47b2ba5ab33d7a851"
checksum = "d579dcb632d86591bdd7fc445e705b96cb2a7fb5488d918d956f392b6148e898"
dependencies = [
"fundu-core",
]
[[package]]
name = "fundu-core"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a363b75dd1e4b5bd2cdc305c47399c524cae24638b368b66b1a4c2a36482801f"
[[package]]
name = "futures"
@ -1073,9 +1095,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.8"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [
"cfg-if",
"libc",
@ -1106,6 +1128,12 @@ dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -1154,16 +1182,6 @@ dependencies = [
"time",
]
[[package]]
name = "humantime_to_duration"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a80a233096ddccb74e62145f3a49cacea6a2669ee90f6e144e15fe28f4037c4"
dependencies = [
"chrono",
"regex",
]
[[package]]
name = "iana-time-zone"
version = "0.1.53"
@ -1254,9 +1272,9 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.10.5"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
@ -1319,9 +1337,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.146"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "libloading"
@ -1405,9 +1423,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memmap2"
version = "0.6.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9ff02d2efdc645fca1ee55f45545b996e7da776b5b60c4e170334457551693"
checksum = "180d4b35be83d33392d1d1bfbd2ae1eca7ff5de1a94d3fc87faaa99a069e7cbd"
dependencies = [
"libc",
]
@ -1472,9 +1490,9 @@ dependencies = [
[[package]]
name = "notify"
version = "6.0.0"
version = "6.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d9ba6c734de18ca27c8cef5cd7058aa4ac9f63596131e4c7e41e579319032a2"
checksum = "5738a2795d57ea20abec2d6d76c6081186709c0024187cd5977265eda6598b51"
dependencies = [
"bitflags",
"crossbeam-channel",
@ -1582,12 +1600,12 @@ dependencies = [
[[package]]
name = "ordered-multimap"
version = "0.4.3"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
dependencies = [
"dlv-list",
"hashbrown",
"hashbrown 0.13.2",
]
[[package]]
@ -1599,29 +1617,6 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "ouroboros"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db"
dependencies = [
"aliasable",
"ouroboros_macro",
]
[[package]]
name = "ouroboros_macro"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7"
dependencies = [
"Inflector",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "output_vt100"
version = "0.1.3"
@ -1654,6 +1649,16 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "parse_datetime"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fecceaede7767a9a98058687a321bc91742eff7670167a34104afb30fc8757df"
dependencies = [
"chrono",
"regex",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
@ -1662,18 +1667,18 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "phf"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
dependencies = [
"phf_generator",
"phf_shared",
@ -1691,9 +1696,9 @@ dependencies = [
[[package]]
name = "phf_shared"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
@ -1751,28 +1756,10 @@ dependencies = [
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
@ -1973,9 +1960,9 @@ dependencies = [
[[package]]
name = "rust-ini"
version = "0.18.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
dependencies = [
"cfg-if",
"ordered-multimap",
@ -2045,6 +2032,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
[[package]]
name = "self_cell"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6"
[[package]]
name = "selinux"
version = "0.4.0"
@ -2096,9 +2089,9 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.6"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
dependencies = [
"cfg-if",
"cpufeatures",
@ -2329,6 +2322,15 @@ dependencies = [
"time-core",
]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]]
name = "typenum"
version = "1.15.0"
@ -2347,7 +2349,7 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
dependencies = [
"hashbrown",
"hashbrown 0.12.3",
"regex",
]
@ -2539,8 +2541,8 @@ version = "0.0.19"
dependencies = [
"chrono",
"clap",
"humantime_to_duration 0.3.1",
"libc",
"parse_datetime",
"uucore",
"windows-sys 0.48.0",
]
@ -2562,6 +2564,7 @@ name = "uu_df"
version = "0.0.19"
dependencies = [
"clap",
"tempfile",
"unicode-width",
"uucore",
]
@ -3098,9 +3101,9 @@ dependencies = [
"fnv",
"itertools",
"memchr",
"ouroboros",
"rand",
"rayon",
"self_cell",
"tempfile",
"unicode-width",
"uucore",
@ -3234,7 +3237,7 @@ version = "0.0.19"
dependencies = [
"clap",
"filetime",
"humantime_to_duration 0.2.1",
"humantime_to_duration",
"time",
"uucore",
"windows-sys 0.48.0",

View file

@ -1,7 +1,7 @@
# coreutils (uutils)
# * see the repository LICENSE, README, and CONTRIBUTING files for more information
# spell-checker:ignore (libs) libselinux gethostid procfs bigdecimal kqueue fundu mangen humantime uuhelp
# spell-checker:ignore (libs) libselinux gethostid procfs bigdecimal kqueue fundu mangen datetime uuhelp
[package]
name = "coreutils"
@ -280,30 +280,29 @@ filetime = "0.2"
fnv = "1.0.7"
fs_extra = "1.3.0"
fts-sys = "0.2"
fundu = "0.5.1"
fundu = "1.1.0"
gcd = "2.3"
glob = "0.3.1"
half = "2.2"
humantime_to_duration = "0.3.1"
indicatif = "0.17"
is-terminal = "0.4.7"
itertools = "0.10.5"
libc = "0.2.146"
itertools = "0.11.0"
libc = "0.2.147"
lscolors = { version = "0.14.0", default-features = false, features = [
"nu-ansi-term",
] }
memchr = "2"
nix = { version = "0.26", default-features = false }
nom = "7.1.3"
notify = { version = "=6.0.0", features = ["macos_kqueue"] }
notify = { version = "=6.0.1", features = ["macos_kqueue"] }
num-bigint = "0.4.3"
num-traits = "0.2.15"
number_prefix = "0.4"
once_cell = "1.18.0"
onig = { version = "~6.4", default-features = false }
ouroboros = "0.15.6"
phf = "0.11.1"
phf_codegen = "0.11.1"
parse_datetime = "0.4.0"
phf = "0.11.2"
phf_codegen = "0.11.2"
platform-info = "2.0.1"
quick-error = "2.0.1"
rand = { version = "0.8", features = ["small_rng"] }
@ -312,8 +311,9 @@ rayon = "1.7"
redox_syscall = "0.3"
regex = "1.8.4"
rstest = "0.17.0"
rust-ini = "0.18.0"
rust-ini = "0.19.0"
same-file = "1.0.6"
self_cell = "1.0.1"
selinux = "0.4"
signal-hook = "0.3.15"
smallvec = { version = "1.10", features = ["union"] }
@ -335,10 +335,10 @@ zip = { version = "0.6.6", default_features = false, features = ["deflate"] }
hex = "0.4.3"
md-5 = "0.10.5"
sha1 = "0.10.5"
sha2 = "0.10.6"
sha2 = "0.10.7"
sha3 = "0.10.8"
blake2b_simd = "1.0.1"
blake3 = "1.3.3"
blake3 = "1.4.0"
sm3 = "0.4.2"
digest = "0.10.7"

View file

@ -63,23 +63,32 @@ skip = [
{ name = "hermit-abi", version = "0.3.1" },
# procfs
{ name = "rustix", version = "0.36.14" },
# rustix
{ name = "linux-raw-sys", version = "0.1.4" },
# various crates
{ name = "windows-sys", version = "0.45.0" },
# windows-sys
{ name = "windows-targets", version = "0.42.2" },
# windows-targets
{ name = "windows_aarch64_gnullvm", version = "0.42.2" },
# windows-targets
{ name = "windows_aarch64_msvc", version = "0.42.2" },
# windows-targets
{ name = "windows_i686_gnu", version = "0.42.2" },
# windows-targets
{ name = "windows_i686_msvc", version = "0.42.2" },
# windows-targets
{ name = "windows_x86_64_gnu", version = "0.42.2" },
# windows-targets
{ name = "windows_x86_64_gnullvm", version = "0.42.2" },
# windows-targets
{ name = "windows_x86_64_msvc", version = "0.42.2" },
# tempfile
{ name = "redox_syscall", version = "0.3.5" },
# cpp_macros
{ name = "aho-corasick", version = "0.7.19" },
# touch, can be remove when touch switches from time to chrono
{ name = "humantime_to_duration", version = "0.2.1" },
# ordered-multimap (via rust-ini)
{ name = "hashbrown", version = "0.13.2" },
]
# spell-checker: enable

View file

@ -61,7 +61,11 @@ feature is adopted from [FreeBSD](https://www.freebsd.org/cgi/man.cgi?cut).
## `fmt`
`fmt` has additional flags for prefixes: `-P/--skip-prefix`, `-x/--exact-prefix`, and
`-X/--exact-skip-prefix`. With `-m/--preserve-headers`, an attempt is made to detect and preserve
mail headers in the input. `-q/--quick` breaks lines more quickly. And `-T/--tab-width` defines the
`fmt` has additional flags for prefixes: `-P`/`--skip-prefix`, `-x`/`--exact-prefix`, and
`-X`/`--exact-skip-prefix`. With `-m`/`--preserve-headers`, an attempt is made to detect and preserve
mail headers in the input. `-q`/`--quick` breaks lines more quickly. And `-T`/`--tab-width` defines the
number of spaces representing a tab when determining the line length.
## `seq`
`seq` provides `-t`/`--terminator` to set the terminator character.

View file

@ -1,4 +1,4 @@
# spell-checker:ignore humantime
# spell-checker:ignore datetime
[package]
name = "uu_date"
version = "0.0.19"
@ -19,7 +19,7 @@ path = "src/date.rs"
chrono = { workspace = true }
clap = { workspace = true }
uucore = { workspace = true }
humantime_to_duration = { workspace = true }
parse_datetime = { workspace = true }
[target.'cfg(unix)'.dependencies]
libc = { workspace = true }

View file

@ -6,7 +6,7 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes humantime
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes
use chrono::format::{Item, StrftimeItems};
use chrono::{DateTime, Duration, FixedOffset, Local, Offset, Utc};
@ -170,7 +170,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
};
let date_source = if let Some(date) = matches.get_one::<String>(OPT_DATE) {
if let Ok(duration) = humantime_to_duration::from_str(date.as_str()) {
if let Ok(duration) = parse_datetime::from_str(date.as_str()) {
DateSource::Human(duration)
} else {
DateSource::Custom(date.into())

View file

@ -36,9 +36,12 @@ use std::os::unix::{
io::{AsRawFd, FromRawFd},
};
use std::path::Path;
use std::sync::mpsc;
use std::sync::{
atomic::{AtomicBool, Ordering::Relaxed},
mpsc, Arc,
};
use std::thread;
use std::time;
use std::time::{Duration, Instant};
use clap::{crate_version, Arg, Command};
use gcd::Gcd;
@ -75,6 +78,45 @@ struct Settings {
status: Option<StatusLevel>,
}
/// A timer which triggers on a given interval
///
/// After being constructed with [`Alarm::with_interval`], [`Alarm::is_triggered`]
/// will return true once per the given [`Duration`].
///
/// Can be cloned, but the trigger status is shared across all instances so only
/// the first caller each interval will yield true.
///
/// When all instances are dropped the background thread will exit on the next interval.
#[derive(Debug, Clone)]
pub struct Alarm {
interval: Duration,
trigger: Arc<AtomicBool>,
}
impl Alarm {
pub fn with_interval(interval: Duration) -> Self {
let trigger = Arc::new(AtomicBool::default());
let weak_trigger = Arc::downgrade(&trigger);
thread::spawn(move || {
while let Some(trigger) = weak_trigger.upgrade() {
thread::sleep(interval);
trigger.store(true, Relaxed);
}
});
Self { interval, trigger }
}
pub fn is_triggered(&self) -> bool {
self.trigger.swap(false, Relaxed)
}
pub fn get_interval(&self) -> Duration {
self.interval
}
}
/// A number in blocks or bytes
///
/// Some values (seek, skip, iseek, oseek) can have values either in blocks or in bytes.
@ -760,7 +802,7 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
// of its report includes the throughput in bytes per second,
// which requires knowing how long the process has been
// running.
let start = time::Instant::now();
let start = Instant::now();
// A good buffer size for reading.
//
@ -780,7 +822,6 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
// information.
let (prog_tx, rx) = mpsc::channel();
let output_thread = thread::spawn(gen_prog_updater(rx, i.settings.status));
let mut progress_as_secs = 0;
// Optimization: if no blocks are to be written, then don't
// bother allocating any buffers.
@ -813,6 +854,12 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
// This is the max size needed.
let mut buf = vec![BUF_INIT_BYTE; bsize];
// Spawn a timer thread to provide a scheduled signal indicating when we
// should send an update of our progress to the reporting thread.
//
// This avoids the need to query the OS monotonic clock for every block.
let alarm = Alarm::with_interval(Duration::from_secs(1));
// Index in the input file where we are reading bytes and in
// the output file where we are writing bytes.
//
@ -871,9 +918,8 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
// error.
rstat += rstat_update;
wstat += wstat_update;
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
if prog_update.duration.as_secs() >= progress_as_secs {
progress_as_secs = prog_update.duration.as_secs() + 1;
if alarm.is_triggered() {
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
prog_tx.send(prog_update).unwrap_or(());
}
}
@ -885,7 +931,7 @@ fn finalize<T>(
output: &mut Output,
rstat: ReadStat,
wstat: WriteStat,
start: time::Instant,
start: Instant,
prog_tx: &mpsc::Sender<ProgUpdate>,
output_thread: thread::JoinHandle<T>,
) -> std::io::Result<()> {

View file

@ -19,6 +19,9 @@ clap = { workspace = true }
uucore = { workspace = true, features = ["libc", "fsext"] }
unicode-width = { workspace = true }
[dev-dependencies]
tempfile = "3"
[[bin]]
name = "df"
path = "src/main.rs"

View file

@ -42,7 +42,7 @@ pub(crate) struct Filesystem {
/// This function returns the element of `mounts` on which `path` is
/// mounted. If there are no matches, this function returns
/// [`None`]. If there are two or more matches, then the single
/// [`MountInfo`] with the longest mount directory is returned.
/// [`MountInfo`] with the device name corresponding to the entered path.
///
/// If `canonicalize` is `true`, then the `path` is canonicalized
/// before checking whether it matches any mount directories.
@ -68,9 +68,19 @@ where
path.as_ref().to_path_buf()
};
// Find the potential mount point that matches entered path
let maybe_mount_point = mounts
.iter()
.find(|mi| mi.dev_name.eq(&path.to_string_lossy()));
// Create pair MountInfo, canonicalized device name
// TODO Abstract from accessing real filesystem to
// make code more testable
.map(|m| (m, std::fs::canonicalize(&m.dev_name)))
// Ignore non existing paths
.filter(|m| m.1.is_ok())
.map(|m| (m.0, m.1.ok().unwrap()))
// Try to find canonicalized device name corresponding to entered path
.find(|m| m.1.eq(&path))
.map(|m| m.0);
maybe_mount_point.or_else(|| {
mounts
@ -211,10 +221,16 @@ mod tests {
#[test]
fn test_dev_name_match() {
let tmp = tempfile::TempDir::new().expect("Failed to create temp dir");
let dev_name = std::fs::canonicalize(tmp.path())
.expect("Failed to canonicalize tmp path")
.to_string_lossy()
.to_string();
let mut mount_info = mount_info("/foo");
mount_info.dev_name = "/dev/sda2".to_string();
mount_info.dev_name = dev_name.clone();
let mounts = [mount_info];
let actual = mount_info_from_path(&mounts, "/dev/sda2", false).unwrap();
let actual = mount_info_from_path(&mounts, dev_name, false).unwrap();
assert!(mount_info_eq(actual, &mounts[0]));
}
}

View file

@ -145,7 +145,7 @@ impl Stat {
return Ok(Self {
path: path.to_path_buf(),
is_dir: metadata.is_dir(),
size: metadata.len(),
size: if path.is_dir() { 0 } else { metadata.len() },
blocks: metadata.blocks(),
inodes: 1,
inode: Some(file_info),
@ -162,7 +162,7 @@ impl Stat {
Ok(Self {
path: path.to_path_buf(),
is_dir: metadata.is_dir(),
size: metadata.len(),
size: if path.is_dir() { 0 } else { metadata.len() },
blocks: size_on_disk / 1024 * 2,
inode: file_info,
inodes: 1,

View file

@ -25,7 +25,7 @@ use std::path::{Path, PathBuf};
use uucore::backup_control::{self, BackupMode};
use uucore::display::Quotable;
use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError};
use uucore::fs::are_hardlinks_to_same_file;
use uucore::fs::{are_hardlinks_or_one_way_symlink_to_same_file, are_hardlinks_to_same_file};
use uucore::update_control::{self, UpdateMode};
use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show};
@ -255,8 +255,10 @@ fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> {
return Err(MvError::NoSuchFile(source.quote().to_string()).into());
}
if (source.eq(target) || are_hardlinks_to_same_file(source, target))
&& b.backup != BackupMode::SimpleBackup
if (source.eq(target)
|| are_hardlinks_to_same_file(source, target)
|| are_hardlinks_or_one_way_symlink_to_same_file(source, target))
&& b.backup == BackupMode::NoBackup
{
if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() {
return Err(

View file

@ -1,4 +1,4 @@
#nl
# nl
```
nl [OPTION]... [FILE]...

View file

@ -6,8 +6,6 @@
// * file that was distributed with this source code.
// *
// spell-checker:ignore (ToDO) corasick memchr
use clap::{crate_version, Arg, ArgAction, Command};
use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Read};
@ -18,8 +16,8 @@ use uucore::{format_usage, help_about, help_usage};
mod helper;
static ABOUT: &str = help_about!("nl.md");
static USAGE: &str = help_usage!("nl.md");
const ABOUT: &str = help_about!("nl.md");
const USAGE: &str = help_usage!("nl.md");
// Settings store options used by nl to produce its output.
pub struct Settings {
@ -42,6 +40,24 @@ pub struct Settings {
number_separator: String,
}
impl Default for Settings {
fn default() -> Self {
Self {
header_numbering: NumberingStyle::NumberForNone,
body_numbering: NumberingStyle::NumberForAll,
footer_numbering: NumberingStyle::NumberForNone,
section_delimiter: ['\\', ':'],
starting_line_number: 1,
line_increment: 1,
join_blank_lines: 1,
number_width: 6,
number_format: NumberFormat::Right,
renumber: true,
number_separator: String::from("\t"),
}
}
}
// NumberingStyle stores which lines are to be numbered.
// The possible options are:
// 1. Number all lines
@ -87,20 +103,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().try_get_matches_from(args)?;
// A mutable settings object, initialized with the defaults.
let mut settings = Settings {
header_numbering: NumberingStyle::NumberForNone,
body_numbering: NumberingStyle::NumberForAll,
footer_numbering: NumberingStyle::NumberForNone,
section_delimiter: ['\\', ':'],
starting_line_number: 1,
line_increment: 1,
join_blank_lines: 1,
number_width: 6,
number_format: NumberFormat::Right,
renumber: true,
number_separator: String::from("\t"),
};
let mut settings = Settings::default();
// Update the settings from the command line options, and terminate the
// program if some options could not successfully be parsed.

View file

@ -43,7 +43,7 @@ pub fn parse_number_of_bytes(s: &str) -> Result<u64, ParseSizeError> {
len -= 1;
}
#[cfg(target_pointer_width = "64")]
Some('E') => {
Some('E') if radix != 16 => {
multiply = 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
len -= 1;
}
@ -84,6 +84,7 @@ fn test_parse_number_of_bytes() {
// hex input
assert_eq!(15, parse_number_of_bytes("0xf").unwrap());
assert_eq!(14, parse_number_of_bytes("0XE").unwrap());
assert_eq!(15, parse_number_of_bytes("0XF").unwrap());
assert_eq!(27, parse_number_of_bytes("0x1b").unwrap());
assert_eq!(16 * 1024, parse_number_of_bytes("0x10k").unwrap());

View file

@ -5,5 +5,5 @@ Display numbers from FIRST to LAST, in steps of INCREMENT.
```
seq [OPTION]... LAST
seq [OPTION]... FIRST LAST
seq [OPTION]... FIRST INCREMENT LAST";
seq [OPTION]... FIRST INCREMENT LAST
```

View file

@ -532,7 +532,9 @@ fn wipe_name(orig_path: &Path, verbose: bool) -> Option<PathBuf> {
}
// Sync every file rename
let new_file = File::open(new_path.clone())
let new_file = OpenOptions::new()
.write(true)
.open(new_path.clone())
.expect("Failed to open renamed file for syncing");
new_file.sync_all().expect("Failed to sync renamed file");

View file

@ -14,7 +14,7 @@ use uucore::{
};
use clap::{crate_version, Arg, ArgAction, Command};
use fundu::{self, DurationParser, ParseError};
use fundu::{self, DurationParser, ParseError, SaturatingInto};
static ABOUT: &str = help_about!("sleep.md");
const USAGE: &str = help_usage!("sleep.md");
@ -63,7 +63,7 @@ pub fn uu_app() -> Command {
fn sleep(args: &[&str]) -> UResult<()> {
let mut arg_error = false;
use fundu::TimeUnit::*;
use fundu::TimeUnit::{Day, Hour, Minute, Second};
let parser = DurationParser::with_time_units(&[Second, Minute, Hour, Day]);
let sleep_dur = args
@ -91,7 +91,9 @@ fn sleep(args: &[&str]) -> UResult<()> {
None
}
})
.fold(Duration::ZERO, |acc, n| acc.saturating_add(n));
.fold(Duration::ZERO, |acc, n| {
acc.saturating_add(SaturatingInto::<std::time::Duration>::saturating_into(n))
});
if arg_error {
return Err(UUsageError::new(1, ""));

View file

@ -22,9 +22,9 @@ ctrlc = { workspace = true }
fnv = { workspace = true }
itertools = { workspace = true }
memchr = { workspace = true }
ouroboros = { workspace = true }
rand = { workspace = true }
rayon = { workspace = true }
self_cell = { workspace = true }
tempfile = { workspace = true }
unicode-width = { workspace = true }
uucore = { workspace = true, features = ["fs"] }

View file

@ -16,21 +16,22 @@ use std::{
};
use memchr::memchr_iter;
use ouroboros::self_referencing;
use self_cell::self_cell;
use uucore::error::{UResult, USimpleError};
use crate::{numeric_str_cmp::NumInfo, GeneralF64ParseResult, GlobalSettings, Line, SortError};
/// The chunk that is passed around between threads.
/// `lines` consist of slices into `buffer`.
#[self_referencing(pub_extras)]
#[derive(Debug)]
pub struct Chunk {
pub buffer: Vec<u8>,
#[borrows(buffer)]
#[covariant]
pub contents: ChunkContents<'this>,
}
self_cell!(
/// The chunk that is passed around between threads.
pub struct Chunk {
owner: Vec<u8>,
#[covariant]
dependent: ChunkContents,
}
impl {Debug}
);
#[derive(Debug)]
pub struct ChunkContents<'a> {
@ -48,7 +49,7 @@ pub struct LineData<'a> {
impl Chunk {
/// Destroy this chunk and return its components to be reused.
pub fn recycle(mut self) -> RecycledChunk {
let recycled_contents = self.with_contents_mut(|contents| {
let recycled_contents = self.with_dependent_mut(|_, contents| {
contents.lines.clear();
contents.line_data.selections.clear();
contents.line_data.num_infos.clear();
@ -81,15 +82,15 @@ impl Chunk {
selections: recycled_contents.1,
num_infos: recycled_contents.2,
parsed_floats: recycled_contents.3,
buffer: self.into_heads().buffer,
buffer: self.into_owner(),
}
}
pub fn lines(&self) -> &Vec<Line> {
&self.borrow_contents().lines
&self.borrow_dependent().lines
}
pub fn line_data(&self) -> &LineData {
&self.borrow_contents().line_data
&self.borrow_dependent().line_data
}
}

View file

@ -158,7 +158,7 @@ fn reader_writer<
/// The function that is executed on the sorter thread.
fn sorter(receiver: &Receiver<Chunk>, sender: &SyncSender<Chunk>, settings: &GlobalSettings) {
while let Ok(mut payload) = receiver.recv() {
payload.with_contents_mut(|contents| {
payload.with_dependent_mut(|_, contents| {
sort_by(&mut contents.lines, settings, &contents.line_data);
});
if sender.send(payload).is_err() {

View file

@ -288,7 +288,7 @@ impl<'a> FileMerger<'a> {
file_number: file.file_number,
});
file.current_chunk.with_contents(|contents| {
file.current_chunk.with_dependent(|_, contents| {
let current_line = &contents.lines[file.line_idx];
if settings.unique {
if let Some(prev) = &prev {

View file

@ -173,7 +173,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let path = Path::new(&f);
if let Err(e) = open(path, OFlag::O_NONBLOCK, Mode::empty()) {
if e != Errno::EACCES || (e == Errno::EACCES && path.is_dir()) {
return e.map_err_context(|| format!("cannot stat {}", f.quote()))?;
return e.map_err_context(|| format!("error opening {}", f.quote()))?;
}
}
}
@ -183,7 +183,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if !Path::new(&f).exists() {
return Err(USimpleError::new(
1,
format!("cannot stat {}: No such file or directory", f.quote()),
format!("error opening {}: No such file or directory", f.quote()),
));
}
}

View file

@ -18,7 +18,7 @@ path = "src/tac.rs"
[dependencies]
memchr = { workspace = true }
memmap2 = "0.6"
memmap2 = "0.7"
regex = { workspace = true }
clap = { workspace = true }
uucore = { workspace = true }

View file

@ -9,7 +9,7 @@ use crate::paths::Input;
use crate::{parse, platform, Quotable};
use clap::crate_version;
use clap::{Arg, ArgAction, ArgMatches, Command};
use fundu::DurationParser;
use fundu::{DurationParser, SaturatingInto};
use is_terminal::IsTerminal;
use same_file::Handle;
use std::collections::VecDeque;
@ -235,12 +235,15 @@ impl Settings {
// `DURATION::MAX` or `infinity` was given
// * not applied here but it supports customizable time units and provides better error
// messages
settings.sleep_sec =
DurationParser::without_time_units()
.parse(source)
.map_err(|_| {
UUsageError::new(1, format!("invalid number of seconds: '{source}'"))
})?;
settings.sleep_sec = match DurationParser::without_time_units().parse(source) {
Ok(duration) => SaturatingInto::<std::time::Duration>::saturating_into(duration),
Err(_) => {
return Err(UUsageError::new(
1,
format!("invalid number of seconds: '{source}'"),
))
}
}
}
if let Some(s) = matches.get_one::<String>(options::MAX_UNCHANGED_STATS) {

View file

@ -6,7 +6,7 @@
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
// spell-checker:ignore (ToDO) filetime strptime utcoff strs datetime MMDDhhmm clapv PWSTR lpszfilepath hresult mktime YYYYMMDDHHMM YYMMDDHHMM DATETIME YYYYMMDDHHMMS subsecond humantime
// spell-checker:ignore (ToDO) filetime datetime MMDDhhmm lpszfilepath mktime YYYYMMDDHHMM YYMMDDHHMM DATETIME YYYYMMDDHHMMS subsecond humantime
use clap::builder::ValueParser;
use clap::{crate_version, Arg, ArgAction, ArgGroup, Command};
@ -29,7 +29,7 @@ pub mod options {
pub mod sources {
pub static DATE: &str = "date";
pub static REFERENCE: &str = "reference";
pub static CURRENT: &str = "current";
pub static TIMESTAMP: &str = "timestamp";
}
pub static HELP: &str = "help";
pub static ACCESS: &str = "access";
@ -120,12 +120,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
(timestamp, timestamp)
}
(None, None) => {
let timestamp =
if let Some(current) = matches.get_one::<String>(options::sources::CURRENT) {
parse_timestamp(current)?
} else {
local_dt_to_filetime(time::OffsetDateTime::now_local().unwrap())
};
let timestamp = if let Some(ts) = matches.get_one::<String>(options::sources::TIMESTAMP)
{
parse_timestamp(ts)?
} else {
local_dt_to_filetime(time::OffsetDateTime::now_local().unwrap())
};
(timestamp, timestamp)
}
};
@ -243,7 +243,7 @@ pub fn uu_app() -> Command {
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::sources::CURRENT)
Arg::new(options::sources::TIMESTAMP)
.short('t')
.help("use [[CC]YY]MMDDhhmm[.ss] instead of the current time")
.value_name("STAMP"),
@ -255,7 +255,7 @@ pub fn uu_app() -> Command {
.allow_hyphen_values(true)
.help("parse argument and use it instead of current time")
.value_name("STRING")
.conflicts_with(options::sources::CURRENT),
.conflicts_with(options::sources::TIMESTAMP),
)
.arg(
Arg::new(options::MODIFICATION)
@ -288,7 +288,7 @@ pub fn uu_app() -> Command {
.value_name("FILE")
.value_parser(ValueParser::os_string())
.value_hint(clap::ValueHint::AnyPath)
.conflicts_with(options::sources::CURRENT),
.conflicts_with(options::sources::TIMESTAMP),
)
.arg(
Arg::new(options::TIME)
@ -311,7 +311,7 @@ pub fn uu_app() -> Command {
.group(
ArgGroup::new(options::SOURCES)
.args([
options::sources::CURRENT,
options::sources::TIMESTAMP,
options::sources::DATE,
options::sources::REFERENCE,
])

View file

@ -9,7 +9,7 @@
// cSpell:ignore strs
use clap::{builder::ValueParser, Arg, ArgAction, Command};
use clap::{builder::ValueParser, crate_version, Arg, ArgAction, Command};
use std::error::Error;
use std::ffi::OsString;
use std::io::{self, Write};
@ -44,6 +44,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
pub fn uu_app() -> Command {
Command::new(uucore::util_name())
.version(crate_version!())
.about(ABOUT)
.override_usage(format_usage(USAGE))
.arg(

View file

@ -25,7 +25,7 @@ dunce = "1.0.4"
wild = "2.1"
glob = "0.3.1"
# * optional
itertools = { version = "0.10.5", optional = true }
itertools = { version = "0.11.0", optional = true }
thiserror = { workspace = true, optional = true }
time = { workspace = true, optional = true, features = [
"formatting",
@ -36,7 +36,7 @@ time = { workspace = true, optional = true, features = [
data-encoding = { version = "2.4", optional = true }
data-encoding-macro = { version = "0.1.13", optional = true }
z85 = { version = "3.0.5", optional = true }
libc = { version = "0.2.146", optional = true }
libc = { version = "0.2.147", optional = true }
once_cell = { workspace = true }
os_display = "0.1.3"

View file

@ -635,12 +635,42 @@ 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,
};
source_metadata.ino() == target_metadata.ino() && source_metadata.dev() == target_metadata.dev()
}
#[cfg(not(unix))]
pub fn are_hardlinks_or_one_way_symlink_to_same_file(_source: &Path, _target: &Path) -> bool {
false
}
/// Checks if either two paths are hard links to the same file or if the source path is a symbolic link which when fully resolved points to target path
///
/// # Arguments
///
/// * `source` - A reference to a `Path` representing the source path.
/// * `target` - A reference to a `Path` representing the target path.
///
/// # Returns
///
/// * `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::metadata(target) {
let target_metadata = match fs::symlink_metadata(target) {
Ok(metadata) => metadata,
Err(_) => return false,
};

View file

@ -396,7 +396,7 @@ impl Display for UIoError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
use std::io::ErrorKind::*;
let mut message;
let message;
let message = if self.inner.raw_os_error().is_some() {
// These are errors that come directly from the OS.
// We want to normalize their messages across systems,
@ -424,7 +424,6 @@ impl Display for UIoError {
// (https://github.com/rust-lang/rust/issues/86442)
// are stabilized, we should add them to the match statement.
message = strip_errno(&self.inner);
capitalize(&mut message);
&message
}
}
@ -435,7 +434,6 @@ impl Display for UIoError {
// a file that was not found.
// There are also errors with entirely custom messages.
message = self.inner.to_string();
capitalize(&mut message);
&message
};
if let Some(ctx) = &self.context {
@ -446,13 +444,6 @@ impl Display for UIoError {
}
}
/// Capitalize the first character of an ASCII string.
fn capitalize(text: &mut str) {
if let Some(first) = text.get_mut(..1) {
first.make_ascii_uppercase();
}
}
/// Strip the trailing " (os error XX)" from io error strings.
pub fn strip_errno(err: &std::io::Error) -> String {
let mut msg = err.to_string();

View file

@ -2724,7 +2724,7 @@ fn test_copy_dir_preserve_permissions_inaccessible_file() {
ucmd.args(&["-p", "-R", "d1", "d2"])
.fails()
.code_is(1)
.stderr_only("cp: cannot open 'd1/f' for reading: Permission denied\n");
.stderr_only("cp: cannot open 'd1/f' for reading: permission denied\n");
assert!(at.dir_exists("d2"));
assert!(!at.file_exists("d2/f"));

View file

@ -580,97 +580,58 @@ fn test_du_invalid_threshold() {
#[test]
fn test_du_apparent_size() {
let ts = TestScenario::new(util_name!());
let result = ts.ucmd().arg("--apparent-size").succeeds();
let (at, mut ucmd) = at_and_ucmd!();
#[cfg(any(target_os = "linux", target_os = "android"))]
at.mkdir_all("a/b");
at.write("a/b/file1", "foo");
at.write("a/b/file2", "foobar");
let result = ucmd.args(&["--apparent-size", "--all", "a"]).succeeds();
#[cfg(not(target_os = "windows"))]
{
let result_reference = unwrap_or_return!(expected_result(&ts, &["--apparent-size"]));
assert_eq!(result.stdout_str(), result_reference.stdout_str());
result.stdout_contains_line("1\ta/b/file2");
result.stdout_contains_line("1\ta/b/file1");
result.stdout_contains_line("1\ta/b");
result.stdout_contains_line("1\ta");
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
_du_apparent_size(result.stdout_str());
}
#[cfg(target_os = "windows")]
fn _du_apparent_size(s: &str) {
assert_eq!(
s,
"1\t.\\subdir\\deeper\\deeper_dir
1\t.\\subdir\\deeper
6\t.\\subdir\\links
6\t.\\subdir
6\t.
"
);
}
#[cfg(target_vendor = "apple")]
fn _du_apparent_size(s: &str) {
assert_eq!(
s,
"1\t./subdir/deeper/deeper_dir
1\t./subdir/deeper
6\t./subdir/links
6\t./subdir
6\t.
"
);
}
#[cfg(target_os = "freebsd")]
fn _du_apparent_size(s: &str) {
assert_eq!(
s,
"1\t./subdir/deeper/deeper_dir
2\t./subdir/deeper
6\t./subdir/links
8\t./subdir
8\t.
"
);
}
#[cfg(all(
not(target_vendor = "apple"),
not(target_os = "windows"),
not(target_os = "freebsd")
))]
fn _du_apparent_size(s: &str) {
assert_eq!(
s,
"5\t./subdir/deeper/deeper_dir
9\t./subdir/deeper
10\t./subdir/links
22\t./subdir
26\t.
"
);
#[cfg(target_os = "windows")]
{
result.stdout_contains_line("1\ta\\b\\file2");
result.stdout_contains_line("1\ta\\b\\file1");
result.stdout_contains_line("1\ta\\b");
result.stdout_contains_line("1\ta");
}
}
#[test]
fn test_du_bytes() {
let ts = TestScenario::new(util_name!());
let result = ts.ucmd().arg("--bytes").succeeds();
let (at, mut ucmd) = at_and_ucmd!();
#[cfg(any(target_os = "linux", target_os = "android"))]
at.mkdir_all("a/b");
at.write("a/b/file1", "foo");
at.write("a/b/file2", "foobar");
let result = ucmd.args(&["--bytes", "--all", "a"]).succeeds();
#[cfg(not(target_os = "windows"))]
{
let result_reference = unwrap_or_return!(expected_result(&ts, &["--bytes"]));
assert_eq!(result.stdout_str(), result_reference.stdout_str());
result.stdout_contains_line("6\ta/b/file2");
result.stdout_contains_line("3\ta/b/file1");
result.stdout_contains_line("9\ta/b");
result.stdout_contains_line("9\ta");
}
#[cfg(target_os = "windows")]
result.stdout_contains("5145\t.\\subdir\n");
#[cfg(target_vendor = "apple")]
result.stdout_contains("5625\t./subdir\n");
#[cfg(target_os = "freebsd")]
result.stdout_contains("7193\t./subdir\n");
#[cfg(all(
not(target_vendor = "apple"),
not(target_os = "windows"),
not(target_os = "freebsd"),
not(target_os = "linux"),
not(target_os = "android"),
))]
result.stdout_contains("21529\t./subdir\n");
{
result.stdout_contains_line("6\ta\\b\\file2");
result.stdout_contains_line("3\ta\\b\\file1");
result.stdout_contains_line("9\ta\\b");
result.stdout_contains_line("9\ta");
}
}
#[test]

View file

@ -417,6 +417,83 @@ fn test_mv_same_hardlink() {
.stderr_is(format!("mv: '{file_a}' and '{file_b}' are the same file\n",));
}
#[test]
#[cfg(all(unix, not(target_os = "android")))]
fn test_mv_same_symlink() {
let (at, mut ucmd) = at_and_ucmd!();
let file_a = "test_mv_same_file_a";
let file_b = "test_mv_same_file_b";
let file_c = "test_mv_same_file_c";
at.touch(file_a);
at.symlink_file(file_a, file_b);
ucmd.arg(file_b)
.arg(file_a)
.fails()
.stderr_is(format!("mv: '{file_b}' and '{file_a}' are the same file\n",));
let (at2, mut ucmd2) = at_and_ucmd!();
at2.touch(file_a);
at2.symlink_file(file_a, file_b);
ucmd2.arg(file_a).arg(file_b).succeeds();
assert!(at2.file_exists(file_b));
assert!(!at2.file_exists(file_a));
let (at3, mut ucmd3) = at_and_ucmd!();
at3.touch(file_a);
at3.symlink_file(file_a, file_b);
at3.symlink_file(file_b, file_c);
ucmd3.arg(file_c).arg(file_b).succeeds();
assert!(!at3.symlink_exists(file_c));
assert!(at3.symlink_exists(file_b));
let (at4, mut ucmd4) = at_and_ucmd!();
at4.touch(file_a);
at4.symlink_file(file_a, file_b);
at4.symlink_file(file_b, file_c);
ucmd4
.arg(file_c)
.arg(file_a)
.fails()
.stderr_is(format!("mv: '{file_c}' and '{file_a}' are the same file\n",));
}
#[test]
#[cfg(all(unix, not(target_os = "android")))]
fn test_mv_hardlink_to_symlink() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "file";
let symlink_file = "symlink";
let hardlink_to_symlink_file = "hardlink_to_symlink";
at.touch(file);
at.symlink_file(file, symlink_file);
at.hard_link(symlink_file, hardlink_to_symlink_file);
ucmd.arg(symlink_file).arg(hardlink_to_symlink_file).fails();
let (at2, mut ucmd2) = at_and_ucmd!();
at2.touch(file);
at2.symlink_file(file, symlink_file);
at2.hard_link(symlink_file, hardlink_to_symlink_file);
ucmd2
.arg("--backup")
.arg(symlink_file)
.arg(hardlink_to_symlink_file)
.succeeds();
assert!(!at2.symlink_exists(symlink_file));
assert!(at2.symlink_exists(&format!("{hardlink_to_symlink_file}~")));
}
#[test]
#[cfg(all(unix, not(target_os = "android")))]
fn test_mv_same_hardlink_backup_simple() {

View file

@ -627,6 +627,35 @@ fn test_skip_bytes() {
));
}
#[test]
fn test_skip_bytes_hex() {
let input = "abcdefghijklmnopq"; // spell-checker:disable-line
new_ucmd!()
.arg("-c")
.arg("--skip-bytes=0xB")
.run_piped_stdin(input.as_bytes())
.no_stderr()
.success()
.stdout_is(unindent(
"
0000013 l m n o p q
0000021
",
));
new_ucmd!()
.arg("-c")
.arg("--skip-bytes=0xE")
.run_piped_stdin(input.as_bytes())
.no_stderr()
.success()
.stdout_is(unindent(
"
0000016 o p q
0000021
",
));
}
#[test]
fn test_skip_bytes_error() {
let input = "12345";

View file

@ -18,7 +18,7 @@ fn test_shred_remove() {
at.touch(file_b);
// Shred file_a.
scene.ucmd().arg("-u").arg(file_a).run();
scene.ucmd().arg("-u").arg(file_a).succeeds();
// file_a was deleted, file_b exists.
assert!(!at.file_exists(file_a));

View file

@ -41,7 +41,7 @@ fn test_sync_no_existing_files() {
.arg("--data")
.arg("do-no-exist")
.fails()
.stderr_contains("cannot stat");
.stderr_contains("error opening");
}
#[test]
@ -63,9 +63,9 @@ fn test_sync_no_permission_dir() {
ts.ccmd("chmod").arg("0").arg(dir).succeeds();
let result = ts.ucmd().arg("--data").arg(dir).fails();
result.stderr_contains("sync: cannot stat 'foo': Permission denied");
result.stderr_contains("sync: error opening 'foo': Permission denied");
let result = ts.ucmd().arg(dir).fails();
result.stderr_contains("sync: cannot stat 'foo': Permission denied");
result.stderr_contains("sync: error opening 'foo': Permission denied");
}
#[cfg(not(target_os = "windows"))]

View file

@ -35,6 +35,11 @@ fn test_invalid_arg() {
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
}
#[test]
fn test_version() {
new_ucmd!().arg("--version").succeeds();
}
#[test]
fn test_simple() {
run(NO_ARGS, b"y\ny\ny\ny\n");

View file

@ -659,6 +659,17 @@ impl CmdResult {
self
}
#[track_caller]
pub fn stdout_contains_line<T: AsRef<str>>(&self, cmp: T) -> &Self {
assert!(
self.stdout_str().lines().any(|line| line == cmp.as_ref()),
"'{}' does not contain line '{}'",
self.stdout_str(),
cmp.as_ref()
);
self
}
#[track_caller]
pub fn stderr_contains<T: AsRef<str>>(&self, cmp: T) -> &Self {
assert!(

View file

@ -232,3 +232,8 @@ sed -i -e "s/Try 'mv --help' for more information/For more information, try '--h
sed -i -E "s|^([^#]*2_31.*)$|#\1|g" tests/misc/printf-cov.pl
sed -i -e "s/du: invalid -t argument/du: invalid --threshold argument/" -e "s/du: option requires an argument/error: a value is required for '--threshold <SIZE>' but none was supplied/" -e "/Try 'du --help' for more information./d" tests/du/threshold.sh
# disable two kind of tests:
# "hostid BEFORE --help" doesn't fail for GNU. we fail. we are probably doing better
# "hostid BEFORE --help AFTER " same for this
sed -i -e "s/env \$prog \$BEFORE \$opt > out2/env \$prog \$BEFORE \$opt > out2 #/" -e "s/env \$prog \$BEFORE \$opt AFTER > out3/env \$prog \$BEFORE \$opt AFTER > out3 #/" -e "s/compare exp out2/compare exp out2 #/" -e "s/compare exp out3/compare exp out3 #/" tests/misc/help-version-getopt.sh