mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 12:07:46 +00:00
Merge branch 'main' into ls_LZ
This commit is contained in:
commit
873c6c36f7
42 changed files with 509 additions and 303 deletions
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
github: uutils
|
2
.github/workflows/CICD.yml
vendored
2
.github/workflows/CICD.yml
vendored
|
@ -319,7 +319,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items
|
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:
|
with:
|
||||||
command: fix
|
command: fix
|
||||||
globs: |
|
globs: |
|
||||||
|
|
2
.github/workflows/GnuTests.yml
vendored
2
.github/workflows/GnuTests.yml
vendored
|
@ -205,7 +205,7 @@ jobs:
|
||||||
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
|
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
|
||||||
# https://github.com/uutils/coreutils/issues/4294
|
# https://github.com/uutils/coreutils/issues/4294
|
||||||
# https://github.com/uutils/coreutils/issues/4295
|
# 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 }}
|
mkdir -p ${{ steps.vars.outputs.path_reference }}
|
||||||
|
|
||||||
|
|
4
.github/workflows/freebsd.yml
vendored
4
.github/workflows/freebsd.yml
vendored
|
@ -35,7 +35,7 @@ jobs:
|
||||||
- name: Run sccache-cache
|
- name: Run sccache-cache
|
||||||
uses: mozilla-actions/sccache-action@v0.0.3
|
uses: mozilla-actions/sccache-action@v0.0.3
|
||||||
- name: Prepare, build and test
|
- name: Prepare, build and test
|
||||||
uses: vmactions/freebsd-vm@v0.3.0
|
uses: vmactions/freebsd-vm@v0.3.1
|
||||||
with:
|
with:
|
||||||
usesh: true
|
usesh: true
|
||||||
# We need jq to run show-utils.sh and bash to use inline shell string replacement
|
# 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
|
- name: Run sccache-cache
|
||||||
uses: mozilla-actions/sccache-action@v0.0.3
|
uses: mozilla-actions/sccache-action@v0.0.3
|
||||||
- name: Prepare, build and test
|
- name: Prepare, build and test
|
||||||
uses: vmactions/freebsd-vm@v0.3.0
|
uses: vmactions/freebsd-vm@v0.3.1
|
||||||
with:
|
with:
|
||||||
usesh: true
|
usesh: true
|
||||||
# sync: sshfs
|
# sync: sshfs
|
||||||
|
|
201
Cargo.lock
generated
201
Cargo.lock
generated
|
@ -2,12 +2,6 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "Inflector"
|
|
||||||
version = "0.11.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler"
|
name = "adler"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
@ -43,12 +37,6 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aliasable"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android-tzdata"
|
name = "android-tzdata"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -192,9 +180,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake3"
|
name = "blake3"
|
||||||
version = "1.3.3"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef"
|
checksum = "729b71f35bd3fa1a4c86b85d32c8b9069ea7fe14f7a53cfabb65f62d4265b888"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayref",
|
"arrayref",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
@ -371,6 +359,28 @@ dependencies = [
|
||||||
"windows-sys 0.45.0",
|
"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]]
|
[[package]]
|
||||||
name = "constant_time_eq"
|
name = "constant_time_eq"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
@ -809,9 +819,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dlv-list"
|
name = "dlv-list"
|
||||||
version = "0.3.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
|
checksum = "d529fd73d344663edfd598ccb3f344e46034db51ebd103518eae34338248ad73"
|
||||||
|
dependencies = [
|
||||||
|
"const-random",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dns-lookup"
|
name = "dns-lookup"
|
||||||
|
@ -956,9 +969,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fundu"
|
name = "fundu"
|
||||||
version = "0.5.1"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
|
@ -1073,9 +1095,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.8"
|
version = "0.2.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -1106,6 +1128,12 @@ dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
|
@ -1154,16 +1182,6 @@ dependencies = [
|
||||||
"time",
|
"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]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.53"
|
version = "0.1.53"
|
||||||
|
@ -1254,9 +1272,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.5"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
@ -1319,9 +1337,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.146"
|
version = "0.2.147"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
|
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
|
@ -1405,9 +1423,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memmap2"
|
name = "memmap2"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f9ff02d2efdc645fca1ee55f45545b996e7da776b5b60c4e170334457551693"
|
checksum = "180d4b35be83d33392d1d1bfbd2ae1eca7ff5de1a94d3fc87faaa99a069e7cbd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
@ -1472,9 +1490,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notify"
|
name = "notify"
|
||||||
version = "6.0.0"
|
version = "6.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d9ba6c734de18ca27c8cef5cd7058aa4ac9f63596131e4c7e41e579319032a2"
|
checksum = "5738a2795d57ea20abec2d6d76c6081186709c0024187cd5977265eda6598b51"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
@ -1582,12 +1600,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-multimap"
|
name = "ordered-multimap"
|
||||||
version = "0.4.3"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
|
checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dlv-list",
|
"dlv-list",
|
||||||
"hashbrown",
|
"hashbrown 0.13.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1599,29 +1617,6 @@ dependencies = [
|
||||||
"unicode-width",
|
"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]]
|
[[package]]
|
||||||
name = "output_vt100"
|
name = "output_vt100"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -1654,6 +1649,16 @@ dependencies = [
|
||||||
"windows-sys 0.45.0",
|
"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]]
|
[[package]]
|
||||||
name = "peeking_take_while"
|
name = "peeking_take_while"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -1662,18 +1667,18 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.11.1"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_shared",
|
"phf_shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf_codegen"
|
name = "phf_codegen"
|
||||||
version = "0.11.1"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
|
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_generator",
|
"phf_generator",
|
||||||
"phf_shared",
|
"phf_shared",
|
||||||
|
@ -1691,9 +1696,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf_shared"
|
name = "phf_shared"
|
||||||
version = "0.11.1"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"siphasher",
|
"siphasher",
|
||||||
]
|
]
|
||||||
|
@ -1751,28 +1756,10 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error"
|
name = "proc-macro-hack"
|
||||||
version = "1.0.4"
|
version = "0.5.20+deprecated"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||||
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",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
|
@ -1973,9 +1960,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-ini"
|
name = "rust-ini"
|
||||||
version = "0.18.0"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
|
checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"ordered-multimap",
|
"ordered-multimap",
|
||||||
|
@ -2045,6 +2032,12 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
|
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "self_cell"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selinux"
|
name = "selinux"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -2096,9 +2089,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.6"
|
version = "0.10.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
|
@ -2329,6 +2322,15 @@ dependencies = [
|
||||||
"time-core",
|
"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]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
|
@ -2347,7 +2349,7 @@ version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
|
checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown",
|
"hashbrown 0.12.3",
|
||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2539,8 +2541,8 @@ version = "0.0.19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"humantime_to_duration 0.3.1",
|
|
||||||
"libc",
|
"libc",
|
||||||
|
"parse_datetime",
|
||||||
"uucore",
|
"uucore",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
@ -2562,6 +2564,7 @@ name = "uu_df"
|
||||||
version = "0.0.19"
|
version = "0.0.19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"tempfile",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"uucore",
|
"uucore",
|
||||||
]
|
]
|
||||||
|
@ -3098,9 +3101,9 @@ dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"itertools",
|
"itertools",
|
||||||
"memchr",
|
"memchr",
|
||||||
"ouroboros",
|
|
||||||
"rand",
|
"rand",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"self_cell",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"uucore",
|
"uucore",
|
||||||
|
@ -3234,7 +3237,7 @@ version = "0.0.19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"filetime",
|
"filetime",
|
||||||
"humantime_to_duration 0.2.1",
|
"humantime_to_duration",
|
||||||
"time",
|
"time",
|
||||||
"uucore",
|
"uucore",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
|
|
24
Cargo.toml
24
Cargo.toml
|
@ -1,7 +1,7 @@
|
||||||
# coreutils (uutils)
|
# coreutils (uutils)
|
||||||
# * see the repository LICENSE, README, and CONTRIBUTING files for more information
|
# * 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]
|
[package]
|
||||||
name = "coreutils"
|
name = "coreutils"
|
||||||
|
@ -280,30 +280,29 @@ filetime = "0.2"
|
||||||
fnv = "1.0.7"
|
fnv = "1.0.7"
|
||||||
fs_extra = "1.3.0"
|
fs_extra = "1.3.0"
|
||||||
fts-sys = "0.2"
|
fts-sys = "0.2"
|
||||||
fundu = "0.5.1"
|
fundu = "1.1.0"
|
||||||
gcd = "2.3"
|
gcd = "2.3"
|
||||||
glob = "0.3.1"
|
glob = "0.3.1"
|
||||||
half = "2.2"
|
half = "2.2"
|
||||||
humantime_to_duration = "0.3.1"
|
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
is-terminal = "0.4.7"
|
is-terminal = "0.4.7"
|
||||||
itertools = "0.10.5"
|
itertools = "0.11.0"
|
||||||
libc = "0.2.146"
|
libc = "0.2.147"
|
||||||
lscolors = { version = "0.14.0", default-features = false, features = [
|
lscolors = { version = "0.14.0", default-features = false, features = [
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
] }
|
] }
|
||||||
memchr = "2"
|
memchr = "2"
|
||||||
nix = { version = "0.26", default-features = false }
|
nix = { version = "0.26", default-features = false }
|
||||||
nom = "7.1.3"
|
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-bigint = "0.4.3"
|
||||||
num-traits = "0.2.15"
|
num-traits = "0.2.15"
|
||||||
number_prefix = "0.4"
|
number_prefix = "0.4"
|
||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
onig = { version = "~6.4", default-features = false }
|
onig = { version = "~6.4", default-features = false }
|
||||||
ouroboros = "0.15.6"
|
parse_datetime = "0.4.0"
|
||||||
phf = "0.11.1"
|
phf = "0.11.2"
|
||||||
phf_codegen = "0.11.1"
|
phf_codegen = "0.11.2"
|
||||||
platform-info = "2.0.1"
|
platform-info = "2.0.1"
|
||||||
quick-error = "2.0.1"
|
quick-error = "2.0.1"
|
||||||
rand = { version = "0.8", features = ["small_rng"] }
|
rand = { version = "0.8", features = ["small_rng"] }
|
||||||
|
@ -312,8 +311,9 @@ rayon = "1.7"
|
||||||
redox_syscall = "0.3"
|
redox_syscall = "0.3"
|
||||||
regex = "1.8.4"
|
regex = "1.8.4"
|
||||||
rstest = "0.17.0"
|
rstest = "0.17.0"
|
||||||
rust-ini = "0.18.0"
|
rust-ini = "0.19.0"
|
||||||
same-file = "1.0.6"
|
same-file = "1.0.6"
|
||||||
|
self_cell = "1.0.1"
|
||||||
selinux = "0.4"
|
selinux = "0.4"
|
||||||
signal-hook = "0.3.15"
|
signal-hook = "0.3.15"
|
||||||
smallvec = { version = "1.10", features = ["union"] }
|
smallvec = { version = "1.10", features = ["union"] }
|
||||||
|
@ -335,10 +335,10 @@ zip = { version = "0.6.6", default_features = false, features = ["deflate"] }
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
md-5 = "0.10.5"
|
md-5 = "0.10.5"
|
||||||
sha1 = "0.10.5"
|
sha1 = "0.10.5"
|
||||||
sha2 = "0.10.6"
|
sha2 = "0.10.7"
|
||||||
sha3 = "0.10.8"
|
sha3 = "0.10.8"
|
||||||
blake2b_simd = "1.0.1"
|
blake2b_simd = "1.0.1"
|
||||||
blake3 = "1.3.3"
|
blake3 = "1.4.0"
|
||||||
sm3 = "0.4.2"
|
sm3 = "0.4.2"
|
||||||
digest = "0.10.7"
|
digest = "0.10.7"
|
||||||
|
|
||||||
|
|
13
deny.toml
13
deny.toml
|
@ -63,23 +63,32 @@ skip = [
|
||||||
{ name = "hermit-abi", version = "0.3.1" },
|
{ name = "hermit-abi", version = "0.3.1" },
|
||||||
# procfs
|
# procfs
|
||||||
{ name = "rustix", version = "0.36.14" },
|
{ name = "rustix", version = "0.36.14" },
|
||||||
|
# rustix
|
||||||
{ name = "linux-raw-sys", version = "0.1.4" },
|
{ name = "linux-raw-sys", version = "0.1.4" },
|
||||||
# various crates
|
# various crates
|
||||||
{ name = "windows-sys", version = "0.45.0" },
|
{ name = "windows-sys", version = "0.45.0" },
|
||||||
|
# windows-sys
|
||||||
{ name = "windows-targets", version = "0.42.2" },
|
{ name = "windows-targets", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
{ name = "windows_aarch64_gnullvm", version = "0.42.2" },
|
{ name = "windows_aarch64_gnullvm", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
{ name = "windows_aarch64_msvc", version = "0.42.2" },
|
{ name = "windows_aarch64_msvc", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
{ name = "windows_i686_gnu", version = "0.42.2" },
|
{ name = "windows_i686_gnu", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
{ name = "windows_i686_msvc", version = "0.42.2" },
|
{ name = "windows_i686_msvc", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
{ name = "windows_x86_64_gnu", version = "0.42.2" },
|
{ name = "windows_x86_64_gnu", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
{ name = "windows_x86_64_gnullvm", version = "0.42.2" },
|
{ name = "windows_x86_64_gnullvm", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
{ name = "windows_x86_64_msvc", version = "0.42.2" },
|
{ name = "windows_x86_64_msvc", version = "0.42.2" },
|
||||||
# tempfile
|
# tempfile
|
||||||
{ name = "redox_syscall", version = "0.3.5" },
|
{ name = "redox_syscall", version = "0.3.5" },
|
||||||
# cpp_macros
|
# cpp_macros
|
||||||
{ name = "aho-corasick", version = "0.7.19" },
|
{ name = "aho-corasick", version = "0.7.19" },
|
||||||
# touch, can be remove when touch switches from time to chrono
|
# ordered-multimap (via rust-ini)
|
||||||
{ name = "humantime_to_duration", version = "0.2.1" },
|
{ name = "hashbrown", version = "0.13.2" },
|
||||||
]
|
]
|
||||||
# spell-checker: enable
|
# spell-checker: enable
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,11 @@ feature is adopted from [FreeBSD](https://www.freebsd.org/cgi/man.cgi?cut).
|
||||||
|
|
||||||
## `fmt`
|
## `fmt`
|
||||||
|
|
||||||
`fmt` has additional flags for prefixes: `-P/--skip-prefix`, `-x/--exact-prefix`, and
|
`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
|
`-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
|
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.
|
number of spaces representing a tab when determining the line length.
|
||||||
|
|
||||||
|
## `seq`
|
||||||
|
|
||||||
|
`seq` provides `-t`/`--terminator` to set the terminator character.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# spell-checker:ignore humantime
|
# spell-checker:ignore datetime
|
||||||
[package]
|
[package]
|
||||||
name = "uu_date"
|
name = "uu_date"
|
||||||
version = "0.0.19"
|
version = "0.0.19"
|
||||||
|
@ -19,7 +19,7 @@ path = "src/date.rs"
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
uucore = { workspace = true }
|
uucore = { workspace = true }
|
||||||
humantime_to_duration = { workspace = true }
|
parse_datetime = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (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::format::{Item, StrftimeItems};
|
||||||
use chrono::{DateTime, Duration, FixedOffset, Local, Offset, Utc};
|
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) {
|
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)
|
DateSource::Human(duration)
|
||||||
} else {
|
} else {
|
||||||
DateSource::Custom(date.into())
|
DateSource::Custom(date.into())
|
||||||
|
|
|
@ -36,9 +36,12 @@ use std::os::unix::{
|
||||||
io::{AsRawFd, FromRawFd},
|
io::{AsRawFd, FromRawFd},
|
||||||
};
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::mpsc;
|
use std::sync::{
|
||||||
|
atomic::{AtomicBool, Ordering::Relaxed},
|
||||||
|
mpsc, Arc,
|
||||||
|
};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use clap::{crate_version, Arg, Command};
|
use clap::{crate_version, Arg, Command};
|
||||||
use gcd::Gcd;
|
use gcd::Gcd;
|
||||||
|
@ -75,6 +78,45 @@ struct Settings {
|
||||||
status: Option<StatusLevel>,
|
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
|
/// A number in blocks or bytes
|
||||||
///
|
///
|
||||||
/// Some values (seek, skip, iseek, oseek) can have values either in blocks or in 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,
|
// of its report includes the throughput in bytes per second,
|
||||||
// which requires knowing how long the process has been
|
// which requires knowing how long the process has been
|
||||||
// running.
|
// running.
|
||||||
let start = time::Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
// A good buffer size for reading.
|
// A good buffer size for reading.
|
||||||
//
|
//
|
||||||
|
@ -780,7 +822,6 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||||
// information.
|
// information.
|
||||||
let (prog_tx, rx) = mpsc::channel();
|
let (prog_tx, rx) = mpsc::channel();
|
||||||
let output_thread = thread::spawn(gen_prog_updater(rx, i.settings.status));
|
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
|
// Optimization: if no blocks are to be written, then don't
|
||||||
// bother allocating any buffers.
|
// 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.
|
// This is the max size needed.
|
||||||
let mut buf = vec![BUF_INIT_BYTE; bsize];
|
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
|
// Index in the input file where we are reading bytes and in
|
||||||
// the output file where we are writing bytes.
|
// 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.
|
// error.
|
||||||
rstat += rstat_update;
|
rstat += rstat_update;
|
||||||
wstat += wstat_update;
|
wstat += wstat_update;
|
||||||
|
if alarm.is_triggered() {
|
||||||
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
|
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;
|
|
||||||
prog_tx.send(prog_update).unwrap_or(());
|
prog_tx.send(prog_update).unwrap_or(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -885,7 +931,7 @@ fn finalize<T>(
|
||||||
output: &mut Output,
|
output: &mut Output,
|
||||||
rstat: ReadStat,
|
rstat: ReadStat,
|
||||||
wstat: WriteStat,
|
wstat: WriteStat,
|
||||||
start: time::Instant,
|
start: Instant,
|
||||||
prog_tx: &mpsc::Sender<ProgUpdate>,
|
prog_tx: &mpsc::Sender<ProgUpdate>,
|
||||||
output_thread: thread::JoinHandle<T>,
|
output_thread: thread::JoinHandle<T>,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
|
|
|
@ -19,6 +19,9 @@ clap = { workspace = true }
|
||||||
uucore = { workspace = true, features = ["libc", "fsext"] }
|
uucore = { workspace = true, features = ["libc", "fsext"] }
|
||||||
unicode-width = { workspace = true }
|
unicode-width = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "df"
|
name = "df"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub(crate) struct Filesystem {
|
||||||
/// This function returns the element of `mounts` on which `path` is
|
/// This function returns the element of `mounts` on which `path` is
|
||||||
/// mounted. If there are no matches, this function returns
|
/// mounted. If there are no matches, this function returns
|
||||||
/// [`None`]. If there are two or more matches, then the single
|
/// [`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
|
/// If `canonicalize` is `true`, then the `path` is canonicalized
|
||||||
/// before checking whether it matches any mount directories.
|
/// before checking whether it matches any mount directories.
|
||||||
|
@ -68,9 +68,19 @@ where
|
||||||
path.as_ref().to_path_buf()
|
path.as_ref().to_path_buf()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Find the potential mount point that matches entered path
|
||||||
let maybe_mount_point = mounts
|
let maybe_mount_point = mounts
|
||||||
.iter()
|
.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(|| {
|
maybe_mount_point.or_else(|| {
|
||||||
mounts
|
mounts
|
||||||
|
@ -211,10 +221,16 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dev_name_match() {
|
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");
|
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 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]));
|
assert!(mount_info_eq(actual, &mounts[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ impl Stat {
|
||||||
return Ok(Self {
|
return Ok(Self {
|
||||||
path: path.to_path_buf(),
|
path: path.to_path_buf(),
|
||||||
is_dir: metadata.is_dir(),
|
is_dir: metadata.is_dir(),
|
||||||
size: metadata.len(),
|
size: if path.is_dir() { 0 } else { metadata.len() },
|
||||||
blocks: metadata.blocks(),
|
blocks: metadata.blocks(),
|
||||||
inodes: 1,
|
inodes: 1,
|
||||||
inode: Some(file_info),
|
inode: Some(file_info),
|
||||||
|
@ -162,7 +162,7 @@ impl Stat {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path: path.to_path_buf(),
|
path: path.to_path_buf(),
|
||||||
is_dir: metadata.is_dir(),
|
is_dir: metadata.is_dir(),
|
||||||
size: metadata.len(),
|
size: if path.is_dir() { 0 } else { metadata.len() },
|
||||||
blocks: size_on_disk / 1024 * 2,
|
blocks: size_on_disk / 1024 * 2,
|
||||||
inode: file_info,
|
inode: file_info,
|
||||||
inodes: 1,
|
inodes: 1,
|
||||||
|
|
|
@ -25,7 +25,7 @@ use std::path::{Path, PathBuf};
|
||||||
use uucore::backup_control::{self, BackupMode};
|
use uucore::backup_control::{self, BackupMode};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError};
|
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::update_control::{self, UpdateMode};
|
||||||
use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show};
|
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());
|
return Err(MvError::NoSuchFile(source.quote().to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.eq(target) || are_hardlinks_to_same_file(source, target))
|
if (source.eq(target)
|
||||||
&& b.backup != BackupMode::SimpleBackup
|
|| 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() {
|
if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() {
|
||||||
return Err(
|
return Err(
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
// * file that was distributed with this source code.
|
// * file that was distributed with this source code.
|
||||||
// *
|
// *
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) corasick memchr
|
|
||||||
|
|
||||||
use clap::{crate_version, Arg, ArgAction, Command};
|
use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{stdin, BufRead, BufReader, Read};
|
use std::io::{stdin, BufRead, BufReader, Read};
|
||||||
|
@ -18,8 +16,8 @@ use uucore::{format_usage, help_about, help_usage};
|
||||||
|
|
||||||
mod helper;
|
mod helper;
|
||||||
|
|
||||||
static ABOUT: &str = help_about!("nl.md");
|
const ABOUT: &str = help_about!("nl.md");
|
||||||
static USAGE: &str = help_usage!("nl.md");
|
const USAGE: &str = help_usage!("nl.md");
|
||||||
|
|
||||||
// Settings store options used by nl to produce its output.
|
// Settings store options used by nl to produce its output.
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
|
@ -42,6 +40,24 @@ pub struct Settings {
|
||||||
number_separator: String,
|
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.
|
// NumberingStyle stores which lines are to be numbered.
|
||||||
// The possible options are:
|
// The possible options are:
|
||||||
// 1. Number all lines
|
// 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)?;
|
let matches = uu_app().try_get_matches_from(args)?;
|
||||||
|
|
||||||
// A mutable settings object, initialized with the defaults.
|
let mut settings = Settings::default();
|
||||||
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"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update the settings from the command line options, and terminate the
|
// Update the settings from the command line options, and terminate the
|
||||||
// program if some options could not successfully be parsed.
|
// program if some options could not successfully be parsed.
|
||||||
|
|
|
@ -43,7 +43,7 @@ pub fn parse_number_of_bytes(s: &str) -> Result<u64, ParseSizeError> {
|
||||||
len -= 1;
|
len -= 1;
|
||||||
}
|
}
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
Some('E') => {
|
Some('E') if radix != 16 => {
|
||||||
multiply = 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
|
multiply = 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
|
||||||
len -= 1;
|
len -= 1;
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,7 @@ fn test_parse_number_of_bytes() {
|
||||||
|
|
||||||
// hex input
|
// hex input
|
||||||
assert_eq!(15, parse_number_of_bytes("0xf").unwrap());
|
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!(15, parse_number_of_bytes("0XF").unwrap());
|
||||||
assert_eq!(27, parse_number_of_bytes("0x1b").unwrap());
|
assert_eq!(27, parse_number_of_bytes("0x1b").unwrap());
|
||||||
assert_eq!(16 * 1024, parse_number_of_bytes("0x10k").unwrap());
|
assert_eq!(16 * 1024, parse_number_of_bytes("0x10k").unwrap());
|
||||||
|
|
|
@ -5,5 +5,5 @@ Display numbers from FIRST to LAST, in steps of INCREMENT.
|
||||||
```
|
```
|
||||||
seq [OPTION]... LAST
|
seq [OPTION]... LAST
|
||||||
seq [OPTION]... FIRST LAST
|
seq [OPTION]... FIRST LAST
|
||||||
seq [OPTION]... FIRST INCREMENT LAST";
|
seq [OPTION]... FIRST INCREMENT LAST
|
||||||
```
|
```
|
||||||
|
|
|
@ -532,7 +532,9 @@ fn wipe_name(orig_path: &Path, verbose: bool) -> Option<PathBuf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync every file rename
|
// 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");
|
.expect("Failed to open renamed file for syncing");
|
||||||
new_file.sync_all().expect("Failed to sync renamed file");
|
new_file.sync_all().expect("Failed to sync renamed file");
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ use uucore::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::{crate_version, Arg, ArgAction, Command};
|
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");
|
static ABOUT: &str = help_about!("sleep.md");
|
||||||
const USAGE: &str = help_usage!("sleep.md");
|
const USAGE: &str = help_usage!("sleep.md");
|
||||||
|
@ -63,7 +63,7 @@ pub fn uu_app() -> Command {
|
||||||
fn sleep(args: &[&str]) -> UResult<()> {
|
fn sleep(args: &[&str]) -> UResult<()> {
|
||||||
let mut arg_error = false;
|
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 parser = DurationParser::with_time_units(&[Second, Minute, Hour, Day]);
|
||||||
|
|
||||||
let sleep_dur = args
|
let sleep_dur = args
|
||||||
|
@ -91,7 +91,9 @@ fn sleep(args: &[&str]) -> UResult<()> {
|
||||||
None
|
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 {
|
if arg_error {
|
||||||
return Err(UUsageError::new(1, ""));
|
return Err(UUsageError::new(1, ""));
|
||||||
|
|
|
@ -22,9 +22,9 @@ ctrlc = { workspace = true }
|
||||||
fnv = { workspace = true }
|
fnv = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
memchr = { workspace = true }
|
memchr = { workspace = true }
|
||||||
ouroboros = { workspace = true }
|
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
rayon = { workspace = true }
|
rayon = { workspace = true }
|
||||||
|
self_cell = { workspace = true }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
unicode-width = { workspace = true }
|
unicode-width = { workspace = true }
|
||||||
uucore = { workspace = true, features = ["fs"] }
|
uucore = { workspace = true, features = ["fs"] }
|
||||||
|
|
|
@ -16,22 +16,23 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use memchr::memchr_iter;
|
use memchr::memchr_iter;
|
||||||
use ouroboros::self_referencing;
|
use self_cell::self_cell;
|
||||||
use uucore::error::{UResult, USimpleError};
|
use uucore::error::{UResult, USimpleError};
|
||||||
|
|
||||||
use crate::{numeric_str_cmp::NumInfo, GeneralF64ParseResult, GlobalSettings, Line, SortError};
|
use crate::{numeric_str_cmp::NumInfo, GeneralF64ParseResult, GlobalSettings, Line, SortError};
|
||||||
|
|
||||||
|
self_cell!(
|
||||||
/// The chunk that is passed around between threads.
|
/// The chunk that is passed around between threads.
|
||||||
/// `lines` consist of slices into `buffer`.
|
|
||||||
#[self_referencing(pub_extras)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
pub buffer: Vec<u8>,
|
owner: Vec<u8>,
|
||||||
#[borrows(buffer)]
|
|
||||||
#[covariant]
|
#[covariant]
|
||||||
pub contents: ChunkContents<'this>,
|
dependent: ChunkContents,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl {Debug}
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ChunkContents<'a> {
|
pub struct ChunkContents<'a> {
|
||||||
pub lines: Vec<Line<'a>>,
|
pub lines: Vec<Line<'a>>,
|
||||||
|
@ -48,7 +49,7 @@ pub struct LineData<'a> {
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
/// Destroy this chunk and return its components to be reused.
|
/// Destroy this chunk and return its components to be reused.
|
||||||
pub fn recycle(mut self) -> RecycledChunk {
|
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.lines.clear();
|
||||||
contents.line_data.selections.clear();
|
contents.line_data.selections.clear();
|
||||||
contents.line_data.num_infos.clear();
|
contents.line_data.num_infos.clear();
|
||||||
|
@ -81,15 +82,15 @@ impl Chunk {
|
||||||
selections: recycled_contents.1,
|
selections: recycled_contents.1,
|
||||||
num_infos: recycled_contents.2,
|
num_infos: recycled_contents.2,
|
||||||
parsed_floats: recycled_contents.3,
|
parsed_floats: recycled_contents.3,
|
||||||
buffer: self.into_heads().buffer,
|
buffer: self.into_owner(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lines(&self) -> &Vec<Line> {
|
pub fn lines(&self) -> &Vec<Line> {
|
||||||
&self.borrow_contents().lines
|
&self.borrow_dependent().lines
|
||||||
}
|
}
|
||||||
pub fn line_data(&self) -> &LineData {
|
pub fn line_data(&self) -> &LineData {
|
||||||
&self.borrow_contents().line_data
|
&self.borrow_dependent().line_data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,7 @@ fn reader_writer<
|
||||||
/// The function that is executed on the sorter thread.
|
/// The function that is executed on the sorter thread.
|
||||||
fn sorter(receiver: &Receiver<Chunk>, sender: &SyncSender<Chunk>, settings: &GlobalSettings) {
|
fn sorter(receiver: &Receiver<Chunk>, sender: &SyncSender<Chunk>, settings: &GlobalSettings) {
|
||||||
while let Ok(mut payload) = receiver.recv() {
|
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);
|
sort_by(&mut contents.lines, settings, &contents.line_data);
|
||||||
});
|
});
|
||||||
if sender.send(payload).is_err() {
|
if sender.send(payload).is_err() {
|
||||||
|
|
|
@ -288,7 +288,7 @@ impl<'a> FileMerger<'a> {
|
||||||
file_number: file.file_number,
|
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];
|
let current_line = &contents.lines[file.line_idx];
|
||||||
if settings.unique {
|
if settings.unique {
|
||||||
if let Some(prev) = &prev {
|
if let Some(prev) = &prev {
|
||||||
|
|
|
@ -173,7 +173,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let path = Path::new(&f);
|
let path = Path::new(&f);
|
||||||
if let Err(e) = open(path, OFlag::O_NONBLOCK, Mode::empty()) {
|
if let Err(e) = open(path, OFlag::O_NONBLOCK, Mode::empty()) {
|
||||||
if e != Errno::EACCES || (e == Errno::EACCES && path.is_dir()) {
|
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() {
|
if !Path::new(&f).exists() {
|
||||||
return Err(USimpleError::new(
|
return Err(USimpleError::new(
|
||||||
1,
|
1,
|
||||||
format!("cannot stat {}: No such file or directory", f.quote()),
|
format!("error opening {}: No such file or directory", f.quote()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ path = "src/tac.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
memchr = { workspace = true }
|
memchr = { workspace = true }
|
||||||
memmap2 = "0.6"
|
memmap2 = "0.7"
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
uucore = { workspace = true }
|
uucore = { workspace = true }
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::paths::Input;
|
||||||
use crate::{parse, platform, Quotable};
|
use crate::{parse, platform, Quotable};
|
||||||
use clap::crate_version;
|
use clap::crate_version;
|
||||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||||
use fundu::DurationParser;
|
use fundu::{DurationParser, SaturatingInto};
|
||||||
use is_terminal::IsTerminal;
|
use is_terminal::IsTerminal;
|
||||||
use same_file::Handle;
|
use same_file::Handle;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -235,12 +235,15 @@ impl Settings {
|
||||||
// `DURATION::MAX` or `infinity` was given
|
// `DURATION::MAX` or `infinity` was given
|
||||||
// * not applied here but it supports customizable time units and provides better error
|
// * not applied here but it supports customizable time units and provides better error
|
||||||
// messages
|
// messages
|
||||||
settings.sleep_sec =
|
settings.sleep_sec = match DurationParser::without_time_units().parse(source) {
|
||||||
DurationParser::without_time_units()
|
Ok(duration) => SaturatingInto::<std::time::Duration>::saturating_into(duration),
|
||||||
.parse(source)
|
Err(_) => {
|
||||||
.map_err(|_| {
|
return Err(UUsageError::new(
|
||||||
UUsageError::new(1, format!("invalid number of seconds: '{source}'"))
|
1,
|
||||||
})?;
|
format!("invalid number of seconds: '{source}'"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(s) = matches.get_one::<String>(options::MAX_UNCHANGED_STATS) {
|
if let Some(s) = matches.get_one::<String>(options::MAX_UNCHANGED_STATS) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
// For the full copyright and license information, please view the LICENSE file
|
// For the full copyright and license information, please view the LICENSE file
|
||||||
// that was distributed with this source code.
|
// 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::builder::ValueParser;
|
||||||
use clap::{crate_version, Arg, ArgAction, ArgGroup, Command};
|
use clap::{crate_version, Arg, ArgAction, ArgGroup, Command};
|
||||||
|
@ -29,7 +29,7 @@ pub mod options {
|
||||||
pub mod sources {
|
pub mod sources {
|
||||||
pub static DATE: &str = "date";
|
pub static DATE: &str = "date";
|
||||||
pub static REFERENCE: &str = "reference";
|
pub static REFERENCE: &str = "reference";
|
||||||
pub static CURRENT: &str = "current";
|
pub static TIMESTAMP: &str = "timestamp";
|
||||||
}
|
}
|
||||||
pub static HELP: &str = "help";
|
pub static HELP: &str = "help";
|
||||||
pub static ACCESS: &str = "access";
|
pub static ACCESS: &str = "access";
|
||||||
|
@ -120,9 +120,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
(timestamp, timestamp)
|
(timestamp, timestamp)
|
||||||
}
|
}
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
let timestamp =
|
let timestamp = if let Some(ts) = matches.get_one::<String>(options::sources::TIMESTAMP)
|
||||||
if let Some(current) = matches.get_one::<String>(options::sources::CURRENT) {
|
{
|
||||||
parse_timestamp(current)?
|
parse_timestamp(ts)?
|
||||||
} else {
|
} else {
|
||||||
local_dt_to_filetime(time::OffsetDateTime::now_local().unwrap())
|
local_dt_to_filetime(time::OffsetDateTime::now_local().unwrap())
|
||||||
};
|
};
|
||||||
|
@ -243,7 +243,7 @@ pub fn uu_app() -> Command {
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::sources::CURRENT)
|
Arg::new(options::sources::TIMESTAMP)
|
||||||
.short('t')
|
.short('t')
|
||||||
.help("use [[CC]YY]MMDDhhmm[.ss] instead of the current time")
|
.help("use [[CC]YY]MMDDhhmm[.ss] instead of the current time")
|
||||||
.value_name("STAMP"),
|
.value_name("STAMP"),
|
||||||
|
@ -255,7 +255,7 @@ pub fn uu_app() -> Command {
|
||||||
.allow_hyphen_values(true)
|
.allow_hyphen_values(true)
|
||||||
.help("parse argument and use it instead of current time")
|
.help("parse argument and use it instead of current time")
|
||||||
.value_name("STRING")
|
.value_name("STRING")
|
||||||
.conflicts_with(options::sources::CURRENT),
|
.conflicts_with(options::sources::TIMESTAMP),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::MODIFICATION)
|
Arg::new(options::MODIFICATION)
|
||||||
|
@ -288,7 +288,7 @@ pub fn uu_app() -> Command {
|
||||||
.value_name("FILE")
|
.value_name("FILE")
|
||||||
.value_parser(ValueParser::os_string())
|
.value_parser(ValueParser::os_string())
|
||||||
.value_hint(clap::ValueHint::AnyPath)
|
.value_hint(clap::ValueHint::AnyPath)
|
||||||
.conflicts_with(options::sources::CURRENT),
|
.conflicts_with(options::sources::TIMESTAMP),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::TIME)
|
Arg::new(options::TIME)
|
||||||
|
@ -311,7 +311,7 @@ pub fn uu_app() -> Command {
|
||||||
.group(
|
.group(
|
||||||
ArgGroup::new(options::SOURCES)
|
ArgGroup::new(options::SOURCES)
|
||||||
.args([
|
.args([
|
||||||
options::sources::CURRENT,
|
options::sources::TIMESTAMP,
|
||||||
options::sources::DATE,
|
options::sources::DATE,
|
||||||
options::sources::REFERENCE,
|
options::sources::REFERENCE,
|
||||||
])
|
])
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
// cSpell:ignore strs
|
// 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::error::Error;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
@ -44,6 +44,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
|
||||||
pub fn uu_app() -> Command {
|
pub fn uu_app() -> Command {
|
||||||
Command::new(uucore::util_name())
|
Command::new(uucore::util_name())
|
||||||
|
.version(crate_version!())
|
||||||
.about(ABOUT)
|
.about(ABOUT)
|
||||||
.override_usage(format_usage(USAGE))
|
.override_usage(format_usage(USAGE))
|
||||||
.arg(
|
.arg(
|
||||||
|
|
|
@ -25,7 +25,7 @@ dunce = "1.0.4"
|
||||||
wild = "2.1"
|
wild = "2.1"
|
||||||
glob = "0.3.1"
|
glob = "0.3.1"
|
||||||
# * optional
|
# * optional
|
||||||
itertools = { version = "0.10.5", optional = true }
|
itertools = { version = "0.11.0", optional = true }
|
||||||
thiserror = { workspace = true, optional = true }
|
thiserror = { workspace = true, optional = true }
|
||||||
time = { workspace = true, optional = true, features = [
|
time = { workspace = true, optional = true, features = [
|
||||||
"formatting",
|
"formatting",
|
||||||
|
@ -36,7 +36,7 @@ time = { workspace = true, optional = true, features = [
|
||||||
data-encoding = { version = "2.4", optional = true }
|
data-encoding = { version = "2.4", optional = true }
|
||||||
data-encoding-macro = { version = "0.1.13", optional = true }
|
data-encoding-macro = { version = "0.1.13", optional = true }
|
||||||
z85 = { version = "3.0.5", 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 }
|
once_cell = { workspace = true }
|
||||||
os_display = "0.1.3"
|
os_display = "0.1.3"
|
||||||
|
|
||||||
|
|
|
@ -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.
|
/// * `bool` - Returns `true` if the paths are hard links to the same file, and `false` otherwise.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub fn are_hardlinks_to_same_file(source: &Path, target: &Path) -> bool {
|
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) {
|
let source_metadata = match fs::metadata(source) {
|
||||||
Ok(metadata) => metadata,
|
Ok(metadata) => metadata,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_metadata = match fs::metadata(target) {
|
let target_metadata = match fs::symlink_metadata(target) {
|
||||||
Ok(metadata) => metadata,
|
Ok(metadata) => metadata,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -396,7 +396,7 @@ impl Display for UIoError {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
use std::io::ErrorKind::*;
|
use std::io::ErrorKind::*;
|
||||||
|
|
||||||
let mut message;
|
let message;
|
||||||
let message = if self.inner.raw_os_error().is_some() {
|
let message = if self.inner.raw_os_error().is_some() {
|
||||||
// These are errors that come directly from the OS.
|
// These are errors that come directly from the OS.
|
||||||
// We want to normalize their messages across systems,
|
// We want to normalize their messages across systems,
|
||||||
|
@ -424,7 +424,6 @@ impl Display for UIoError {
|
||||||
// (https://github.com/rust-lang/rust/issues/86442)
|
// (https://github.com/rust-lang/rust/issues/86442)
|
||||||
// are stabilized, we should add them to the match statement.
|
// are stabilized, we should add them to the match statement.
|
||||||
message = strip_errno(&self.inner);
|
message = strip_errno(&self.inner);
|
||||||
capitalize(&mut message);
|
|
||||||
&message
|
&message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -435,7 +434,6 @@ impl Display for UIoError {
|
||||||
// a file that was not found.
|
// a file that was not found.
|
||||||
// There are also errors with entirely custom messages.
|
// There are also errors with entirely custom messages.
|
||||||
message = self.inner.to_string();
|
message = self.inner.to_string();
|
||||||
capitalize(&mut message);
|
|
||||||
&message
|
&message
|
||||||
};
|
};
|
||||||
if let Some(ctx) = &self.context {
|
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.
|
/// Strip the trailing " (os error XX)" from io error strings.
|
||||||
pub fn strip_errno(err: &std::io::Error) -> String {
|
pub fn strip_errno(err: &std::io::Error) -> String {
|
||||||
let mut msg = err.to_string();
|
let mut msg = err.to_string();
|
||||||
|
|
|
@ -2724,7 +2724,7 @@ fn test_copy_dir_preserve_permissions_inaccessible_file() {
|
||||||
ucmd.args(&["-p", "-R", "d1", "d2"])
|
ucmd.args(&["-p", "-R", "d1", "d2"])
|
||||||
.fails()
|
.fails()
|
||||||
.code_is(1)
|
.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.dir_exists("d2"));
|
||||||
assert!(!at.file_exists("d2/f"));
|
assert!(!at.file_exists("d2/f"));
|
||||||
|
|
||||||
|
|
|
@ -580,97 +580,58 @@ fn test_du_invalid_threshold() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_du_apparent_size() {
|
fn test_du_apparent_size() {
|
||||||
let ts = TestScenario::new(util_name!());
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
let result = ts.ucmd().arg("--apparent-size").succeeds();
|
|
||||||
|
|
||||||
#[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"]));
|
result.stdout_contains_line("1\ta/b/file2");
|
||||||
assert_eq!(result.stdout_str(), result_reference.stdout_str());
|
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")]
|
#[cfg(target_os = "windows")]
|
||||||
fn _du_apparent_size(s: &str) {
|
{
|
||||||
assert_eq!(
|
result.stdout_contains_line("1\ta\\b\\file2");
|
||||||
s,
|
result.stdout_contains_line("1\ta\\b\\file1");
|
||||||
"1\t.\\subdir\\deeper\\deeper_dir
|
result.stdout_contains_line("1\ta\\b");
|
||||||
1\t.\\subdir\\deeper
|
result.stdout_contains_line("1\ta");
|
||||||
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.
|
|
||||||
"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_du_bytes() {
|
fn test_du_bytes() {
|
||||||
let ts = TestScenario::new(util_name!());
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
let result = ts.ucmd().arg("--bytes").succeeds();
|
|
||||||
|
|
||||||
#[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"]));
|
result.stdout_contains_line("6\ta/b/file2");
|
||||||
assert_eq!(result.stdout_str(), result_reference.stdout_str());
|
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")]
|
#[cfg(target_os = "windows")]
|
||||||
result.stdout_contains("5145\t.\\subdir\n");
|
{
|
||||||
#[cfg(target_vendor = "apple")]
|
result.stdout_contains_line("6\ta\\b\\file2");
|
||||||
result.stdout_contains("5625\t./subdir\n");
|
result.stdout_contains_line("3\ta\\b\\file1");
|
||||||
#[cfg(target_os = "freebsd")]
|
result.stdout_contains_line("9\ta\\b");
|
||||||
result.stdout_contains("7193\t./subdir\n");
|
result.stdout_contains_line("9\ta");
|
||||||
#[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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -417,6 +417,83 @@ fn test_mv_same_hardlink() {
|
||||||
.stderr_is(format!("mv: '{file_a}' and '{file_b}' are the same file\n",));
|
.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]
|
#[test]
|
||||||
#[cfg(all(unix, not(target_os = "android")))]
|
#[cfg(all(unix, not(target_os = "android")))]
|
||||||
fn test_mv_same_hardlink_backup_simple() {
|
fn test_mv_same_hardlink_backup_simple() {
|
||||||
|
|
|
@ -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]
|
#[test]
|
||||||
fn test_skip_bytes_error() {
|
fn test_skip_bytes_error() {
|
||||||
let input = "12345";
|
let input = "12345";
|
||||||
|
|
|
@ -18,7 +18,7 @@ fn test_shred_remove() {
|
||||||
at.touch(file_b);
|
at.touch(file_b);
|
||||||
|
|
||||||
// Shred file_a.
|
// 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.
|
// file_a was deleted, file_b exists.
|
||||||
assert!(!at.file_exists(file_a));
|
assert!(!at.file_exists(file_a));
|
||||||
|
|
|
@ -41,7 +41,7 @@ fn test_sync_no_existing_files() {
|
||||||
.arg("--data")
|
.arg("--data")
|
||||||
.arg("do-no-exist")
|
.arg("do-no-exist")
|
||||||
.fails()
|
.fails()
|
||||||
.stderr_contains("cannot stat");
|
.stderr_contains("error opening");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -63,9 +63,9 @@ fn test_sync_no_permission_dir() {
|
||||||
|
|
||||||
ts.ccmd("chmod").arg("0").arg(dir).succeeds();
|
ts.ccmd("chmod").arg("0").arg(dir).succeeds();
|
||||||
let result = ts.ucmd().arg("--data").arg(dir).fails();
|
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();
|
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"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
|
|
@ -35,6 +35,11 @@ fn test_invalid_arg() {
|
||||||
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_version() {
|
||||||
|
new_ucmd!().arg("--version").succeeds();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
fn test_simple() {
|
||||||
run(NO_ARGS, b"y\ny\ny\ny\n");
|
run(NO_ARGS, b"y\ny\ny\ny\n");
|
||||||
|
|
|
@ -659,6 +659,17 @@ impl CmdResult {
|
||||||
self
|
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]
|
#[track_caller]
|
||||||
pub fn stderr_contains<T: AsRef<str>>(&self, cmp: T) -> &Self {
|
pub fn stderr_contains<T: AsRef<str>>(&self, cmp: T) -> &Self {
|
||||||
assert!(
|
assert!(
|
||||||
|
|
|
@ -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|^([^#]*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
|
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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue