mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 12:07:46 +00:00
Merge branch 'main' into ls-version-cmp
This commit is contained in:
commit
10bca71b09
68 changed files with 1336 additions and 660 deletions
16
.github/workflows/FixPR.yml
vendored
16
.github/workflows/FixPR.yml
vendored
|
@ -31,12 +31,12 @@ jobs:
|
||||||
id: vars
|
id: vars
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## VARs setup
|
|
||||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
|
||||||
# surface MSRV from CICD workflow
|
# surface MSRV from CICD workflow
|
||||||
RUST_MIN_SRV=$(grep -P "^\s+RUST_MIN_SRV:" .github/workflows/CICD.yml | grep -Po "(?<=\x22)\d+[.]\d+(?:[.]\d+)?(?=\x22)" )
|
RUST_MIN_SRV=$(grep -P "^\s+RUST_MIN_SRV:" .github/workflows/CICD.yml | grep -Po "(?<=\x22)\d+[.]\d+(?:[.]\d+)?(?=\x22)" )
|
||||||
outputs RUST_MIN_SRV
|
echo "RUST_MIN_SRV=${RUST_MIN_SRV}" >> $GITHUB_OUTPUT
|
||||||
- uses: dtolnay/rust-toolchain@${{ steps.vars.outputs.RUST_MIN_SRV }}
|
- uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: ${{ steps.vars.outputs.RUST_MIN_SRV }}
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Ensure updated 'Cargo.lock'
|
- name: Ensure updated 'Cargo.lock'
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -67,7 +67,7 @@ jobs:
|
||||||
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
||||||
uses: EndBug/add-and-commit@v9
|
uses: EndBug/add-and-commit@v9
|
||||||
with:
|
with:
|
||||||
branch: ${{ env.BRANCH_TARGET }}
|
new_branch: ${{ env.BRANCH_TARGET }}
|
||||||
default_author: github_actions
|
default_author: github_actions
|
||||||
message: "maint ~ refresh 'Cargo.lock'"
|
message: "maint ~ refresh 'Cargo.lock'"
|
||||||
add: Cargo.lock
|
add: Cargo.lock
|
||||||
|
@ -90,13 +90,11 @@ jobs:
|
||||||
id: vars
|
id: vars
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## VARs setup
|
|
||||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
|
||||||
# target-specific options
|
# target-specific options
|
||||||
# * CARGO_FEATURES_OPTION
|
# * CARGO_FEATURES_OPTION
|
||||||
CARGO_FEATURES_OPTION='' ;
|
CARGO_FEATURES_OPTION='' ;
|
||||||
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
||||||
outputs CARGO_FEATURES_OPTION
|
echo "CARGO_FEATURES_OPTION=${CARGO_FEATURES_OPTION}" >> $GITHUB_OUTPUT
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
|
@ -114,7 +112,7 @@ jobs:
|
||||||
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
||||||
uses: EndBug/add-and-commit@v9
|
uses: EndBug/add-and-commit@v9
|
||||||
with:
|
with:
|
||||||
branch: ${{ env.BRANCH_TARGET }}
|
new_branch: ${{ env.BRANCH_TARGET }}
|
||||||
default_author: github_actions
|
default_author: github_actions
|
||||||
message: "maint ~ rustfmt (`cargo fmt`)"
|
message: "maint ~ rustfmt (`cargo fmt`)"
|
||||||
env:
|
env:
|
||||||
|
|
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
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,6 +5,7 @@ target/
|
||||||
/busybox/
|
/busybox/
|
||||||
/.vscode/
|
/.vscode/
|
||||||
/.vs/
|
/.vs/
|
||||||
|
/public/
|
||||||
*~
|
*~
|
||||||
.*.swp
|
.*.swp
|
||||||
.*.swo
|
.*.swo
|
||||||
|
|
1
.vscode/cSpell.json
vendored
1
.vscode/cSpell.json
vendored
|
@ -19,6 +19,7 @@
|
||||||
// files to ignore (globs supported)
|
// files to ignore (globs supported)
|
||||||
"ignorePaths": [
|
"ignorePaths": [
|
||||||
"Cargo.lock",
|
"Cargo.lock",
|
||||||
|
"oranda.json",
|
||||||
"target/**",
|
"target/**",
|
||||||
"tests/**/fixtures/**",
|
"tests/**/fixtures/**",
|
||||||
"src/uu/dd/test-resources/**",
|
"src/uu/dd/test-resources/**",
|
||||||
|
|
370
Cargo.lock
generated
370
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"
|
||||||
|
@ -133,10 +121,11 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bigdecimal"
|
name = "bigdecimal"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744"
|
checksum = "5274a6b6e0ee020148397245b973e30163b7bffbc6d473613f850cb99888581e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"libm",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
@ -169,7 +158,7 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
"which",
|
"which",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -215,12 +204,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "1.5.0"
|
version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5"
|
checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"once_cell",
|
|
||||||
"regex-automata",
|
"regex-automata",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -245,9 +233,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.77"
|
version = "1.0.79"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cexpr"
|
name = "cexpr"
|
||||||
|
@ -336,16 +324,6 @@ dependencies = [
|
||||||
"roff",
|
"roff",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "codespan-reporting"
|
|
||||||
version = "0.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
|
||||||
dependencies = [
|
|
||||||
"termcolor",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -371,6 +349,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"
|
||||||
|
@ -562,7 +562,7 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"regex",
|
"regex",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -574,7 +574,7 @@ checksum = "76071bb9c8c4dd2b5eb209907deab7b031323cf1be3dfdc6ec5d37f4f187d8a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -589,7 +589,7 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -701,7 +701,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
|
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -720,50 +720,6 @@ version = "0.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
|
checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cxx"
|
|
||||||
version = "1.0.82"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"cxxbridge-flags",
|
|
||||||
"cxxbridge-macro",
|
|
||||||
"link-cplusplus",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cxx-build"
|
|
||||||
version = "1.0.82"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"codespan-reporting",
|
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"scratch",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cxxbridge-flags"
|
|
||||||
version = "1.0.82"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cxxbridge-macro"
|
|
||||||
version = "1.0.82"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "data-encoding"
|
name = "data-encoding"
|
||||||
version = "2.4.0"
|
version = "2.4.0"
|
||||||
|
@ -787,7 +743,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772"
|
checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -809,9 +765,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 +915,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fundu"
|
name = "fundu"
|
||||||
version = "1.0.0"
|
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 = "47af3b646bdd738395be2db903fc11a5923b5e206016b8d4ad6db890bcae9bd5"
|
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"
|
||||||
|
@ -1016,7 +984,7 @@ checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1107,19 +1075,16 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hashbrown"
|
||||||
version = "0.1.19"
|
version = "0.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex"
|
name = "hex"
|
||||||
|
@ -1144,16 +1109,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "humantime_to_duration"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "714764645f21cc70c4c151d7798dd158409641f37ad820bed65224aae403cbed"
|
|
||||||
dependencies = [
|
|
||||||
"regex",
|
|
||||||
"time",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.53"
|
version = "0.1.53"
|
||||||
|
@ -1170,12 +1125,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone-haiku"
|
name = "iana-time-zone-haiku"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cxx",
|
"cc",
|
||||||
"cxx-build",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1225,7 +1179,7 @@ version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.3.1",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
@ -1236,7 +1190,7 @@ version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
|
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.3.1",
|
"hermit-abi",
|
||||||
"io-lifetimes",
|
"io-lifetimes",
|
||||||
"rustix 0.37.19",
|
"rustix 0.37.19",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
|
@ -1244,9 +1198,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",
|
||||||
]
|
]
|
||||||
|
@ -1309,9 +1263,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"
|
||||||
|
@ -1324,13 +1278,10 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "link-cplusplus"
|
name = "libm"
|
||||||
version = "1.0.7"
|
version = "0.2.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
|
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
|
@ -1519,11 +1470,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_cpus"
|
name = "num_cpus"
|
||||||
version = "1.14.0"
|
version = "1.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
|
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.1.19",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1572,12 +1523,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]]
|
||||||
|
@ -1589,29 +1540,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"
|
||||||
|
@ -1662,18 +1590,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 +1619,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,34 +1679,16 @@ 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"
|
||||||
version = "1.0.47"
|
version = "1.0.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -1815,9 +1725,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.21"
|
version = "1.0.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -1909,9 +1819,21 @@ checksum = "f1bfbf25d7eb88ddcbb1ec3d755d0634da8f7657b2cb8b74089121409ab8228f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.8.4"
|
version = "1.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
|
checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick 1.0.1",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e9aaecc05d5c4b5f7da074b9a0d1a0867e71fd36e7fc0482d8bcfe8e8fc56290"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick 1.0.1",
|
"aho-corasick 1.0.1",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -1919,22 +1841,22 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-syntax"
|
||||||
version = "0.1.10"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "relative-path"
|
||||||
version = "0.7.2"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
|
checksum = "4bf2521270932c3c7bed1a59151222bd7643c79310f2916f01925e1e16255698"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rlimit"
|
name = "rlimit"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8a29d87a652dc4d43c586328706bb5cdff211f3f39a530f240b53f7221dab8e"
|
checksum = "9b5b8be0bc0ef630d24f8fa836b3a3463479b2343b29f9a8fa905c71a8c7b69b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
@ -1947,9 +1869,9 @@ checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rstest"
|
name = "rstest"
|
||||||
version = "0.17.0"
|
version = "0.18.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de1bb486a691878cd320c2f0d319ba91eeaa2e894066d8b5f8f117c000e9d962"
|
checksum = "2b96577ca10cb3eade7b337eb46520108a67ca2818a24d0b63f41fd62bc9651c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"futures-timer",
|
"futures-timer",
|
||||||
|
@ -1959,23 +1881,26 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rstest_macros"
|
name = "rstest_macros"
|
||||||
version = "0.17.0"
|
version = "0.18.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "290ca1a1c8ca7edb7c3283bd44dc35dd54fdec6253a3912e201ba1072018fca8"
|
checksum = "225e674cf31712b8bb15fdbca3ec0c1b9d825c5a24407ff2b7e005fb6a29ba03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"glob",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"relative-path",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"syn",
|
"syn 2.0.23",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[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",
|
||||||
|
@ -2040,10 +1965,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scratch"
|
name = "self_cell"
|
||||||
version = "1.0.2"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
|
checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selinux"
|
name = "selinux"
|
||||||
|
@ -2177,9 +2102,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.10.0"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smawk"
|
name = "smawk"
|
||||||
|
@ -2217,9 +2142,20 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.103"
|
version = "1.0.109"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2249,15 +2185,6 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "termcolor"
|
|
||||||
version = "1.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "terminal_size"
|
name = "terminal_size"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
|
@ -2297,7 +2224,7 @@ checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2329,6 +2256,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 +2283,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",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3099,9 +3035,9 @@ dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"itertools",
|
"itertools",
|
||||||
"memchr",
|
"memchr",
|
||||||
"ouroboros",
|
|
||||||
"rand",
|
"rand",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"self_cell",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"uucore",
|
"uucore",
|
||||||
|
@ -3235,7 +3171,7 @@ version = "0.0.19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"filetime",
|
"filetime",
|
||||||
"humantime_to_duration",
|
"parse_datetime",
|
||||||
"time",
|
"time",
|
||||||
"uucore",
|
"uucore",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
|
@ -3484,7 +3420,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3506,7 +3442,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
31
Cargo.toml
31
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 datetime uuhelp
|
# spell-checker:ignore (libs) bigdecimal datetime fundu gethostid kqueue libselinux mangen memmap procfs uuhelp
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "coreutils"
|
name = "coreutils"
|
||||||
|
@ -257,9 +257,9 @@ feat_os_windows_legacy = [
|
||||||
test = ["uu_test"]
|
test = ["uu_test"]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
bigdecimal = "0.3"
|
bigdecimal = "0.4"
|
||||||
binary-heap-plus = "0.5.0"
|
binary-heap-plus = "0.5.0"
|
||||||
bstr = "1.5"
|
bstr = "1.6"
|
||||||
bytecount = "0.6.3"
|
bytecount = "0.6.3"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
chrono = { version = "^0.4.26", default-features = false, features = [
|
chrono = { version = "^0.4.26", default-features = false, features = [
|
||||||
|
@ -280,18 +280,19 @@ 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 = "1.0.0"
|
fundu = "1.1.0"
|
||||||
gcd = "2.3"
|
gcd = "2.3"
|
||||||
glob = "0.3.1"
|
glob = "0.3.1"
|
||||||
half = "2.2"
|
half = "2.2"
|
||||||
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"
|
||||||
|
memmap2 = "0.7"
|
||||||
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.1", features = ["macos_kqueue"] }
|
notify = { version = "=6.0.1", features = ["macos_kqueue"] }
|
||||||
|
@ -300,23 +301,23 @@ 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"
|
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"] }
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
rayon = "1.7"
|
rayon = "1.7"
|
||||||
redox_syscall = "0.3"
|
redox_syscall = "0.3"
|
||||||
regex = "1.8.4"
|
regex = "1.9.1"
|
||||||
rstest = "0.17.0"
|
rstest = "0.18.1"
|
||||||
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.11", features = ["union"] }
|
||||||
tempfile = "3.6.0"
|
tempfile = "3.6.0"
|
||||||
term_grid = "0.1.5"
|
term_grid = "0.1.5"
|
||||||
terminal_size = "0.2.6"
|
terminal_size = "0.2.6"
|
||||||
|
@ -491,11 +492,11 @@ uucore = { workspace = true, features = ["entries", "process", "signals"] }
|
||||||
walkdir = { workspace = true }
|
walkdir = { workspace = true }
|
||||||
is-terminal = { workspace = true }
|
is-terminal = { workspace = true }
|
||||||
hex-literal = "0.4.1"
|
hex-literal = "0.4.1"
|
||||||
rstest = "0.17.0"
|
rstest = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
|
||||||
procfs = { version = "0.15", default-features = false }
|
procfs = { version = "0.15", default-features = false }
|
||||||
rlimit = "0.9.1"
|
rlimit = "0.10.0"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dev-dependencies]
|
[target.'cfg(unix)'.dev-dependencies]
|
||||||
nix = { workspace = true, features = ["process", "signal", "user"] }
|
nix = { workspace = true, features = ["process", "signal", "user"] }
|
||||||
|
|
13
README.md
13
README.md
|
@ -1,6 +1,7 @@
|
||||||
<!-- markdownlint-disable MD033 MD041 MD002 -->
|
<!-- markdownlint-disable MD033 MD041 MD002 -->
|
||||||
<!-- markdownlint-disable commands-show-output no-duplicate-heading -->
|
<!-- markdownlint-disable commands-show-output no-duplicate-heading -->
|
||||||
<!-- spell-checker:ignore markdownlint ; (options) DESTDIR UTILNAME manpages reimplementation -->
|
<!-- spell-checker:ignore markdownlint ; (options) DESTDIR UTILNAME manpages reimplementation oranda -->
|
||||||
|
<div class="oranda-hide">
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||

|

|
||||||
|
@ -19,11 +20,14 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
uutils coreutils is a cross-platform reimplementation of the GNU coreutils in
|
uutils coreutils is a cross-platform reimplementation of the GNU coreutils in
|
||||||
[Rust](http://www.rust-lang.org). While all programs have been implemented, some
|
[Rust](http://www.rust-lang.org). While all programs have been implemented, some
|
||||||
options might be missing or different behavior might be experienced.
|
options might be missing or different behavior might be experienced.
|
||||||
|
|
||||||
|
<div class="oranda-hide">
|
||||||
|
|
||||||
To install it:
|
To install it:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
@ -31,6 +35,8 @@ cargo install coreutils
|
||||||
~/.cargo/bin/coreutils
|
~/.cargo/bin/coreutils
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- markdownlint-disable-next-line MD026 -->
|
<!-- markdownlint-disable-next-line MD026 -->
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
@ -42,6 +48,8 @@ uutils aims to work on as many platforms as possible, to be able to use the same
|
||||||
utils on Linux, Mac, Windows and other platforms. This ensures, for example,
|
utils on Linux, Mac, Windows and other platforms. This ensures, for example,
|
||||||
that scripts can be easily transferred between platforms.
|
that scripts can be easily transferred between platforms.
|
||||||
|
|
||||||
|
<div class="oranda-hide">
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
uutils has both user and developer documentation available:
|
uutils has both user and developer documentation available:
|
||||||
|
@ -52,6 +60,7 @@ uutils has both user and developer documentation available:
|
||||||
Both can also be generated locally, the instructions for that can be found in
|
Both can also be generated locally, the instructions for that can be found in
|
||||||
the [coreutils docs](https://github.com/uutils/uutils.github.io) repository.
|
the [coreutils docs](https://github.com/uutils/uutils.github.io) repository.
|
||||||
|
|
||||||
|
|
||||||
<!-- ANCHOR: build (this mark is needed for mdbook) -->
|
<!-- ANCHOR: build (this mark is needed for mdbook) -->
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
@ -301,6 +310,8 @@ See <https://github.com/uutils/coreutils/issues/3336> for the main meta bugs
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
</div> <!-- close oranda-hide div -->
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
|
To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
|
||||||
|
|
|
@ -59,8 +59,6 @@ highlight = "all"
|
||||||
# introduces it.
|
# introduces it.
|
||||||
# spell-checker: disable
|
# spell-checker: disable
|
||||||
skip = [
|
skip = [
|
||||||
# is-terminal
|
|
||||||
{ name = "hermit-abi", version = "0.3.1" },
|
|
||||||
# procfs
|
# procfs
|
||||||
{ name = "rustix", version = "0.36.14" },
|
{ name = "rustix", version = "0.36.14" },
|
||||||
# rustix
|
# rustix
|
||||||
|
@ -87,6 +85,10 @@ skip = [
|
||||||
{ 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" },
|
||||||
|
# ordered-multimap (via rust-ini)
|
||||||
|
{ name = "hashbrown", version = "0.13.2" },
|
||||||
|
# various crates
|
||||||
|
{ name = "syn", version = "1.0.109" },
|
||||||
]
|
]
|
||||||
# spell-checker: enable
|
# spell-checker: enable
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,20 @@ 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.
|
||||||
|
|
||||||
|
## `ls`
|
||||||
|
|
||||||
|
GNU `ls` provides two ways to use a long listing format: `-l` and `--format=long`. We support a
|
||||||
|
third way: `--long`.
|
||||||
|
|
||||||
|
## `du`
|
||||||
|
|
||||||
|
`du` allows `birth` and `creation` as values for the `--time` argument to show the creation time.
|
||||||
|
|
4
docs/src/oranda.css
Normal file
4
docs/src/oranda.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.logo {
|
||||||
|
display: block;
|
||||||
|
height: 170px;
|
||||||
|
}
|
13
oranda.json
Normal file
13
oranda.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"name": "uutils coreutils"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"changelog": true
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"theme": "light",
|
||||||
|
"logo": "docs/src/logo.svg",
|
||||||
|
"additional_css": ["docs/src/oranda.css"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -383,6 +383,7 @@ pub fn uu_app() -> Command {
|
||||||
backup_control::BACKUP_CONTROL_LONG_HELP
|
backup_control::BACKUP_CONTROL_LONG_HELP
|
||||||
))
|
))
|
||||||
.infer_long_args(true)
|
.infer_long_args(true)
|
||||||
|
.args_override_self(true)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::TARGET_DIRECTORY)
|
Arg::new(options::TARGET_DIRECTORY)
|
||||||
.short('t')
|
.short('t')
|
||||||
|
|
|
@ -26,14 +26,13 @@ use uucore::{format_usage, help_about, help_usage, show};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use windows_sys::Win32::{Foundation::SYSTEMTIME, System::SystemInformation::SetSystemTime};
|
use windows_sys::Win32::{Foundation::SYSTEMTIME, System::SystemInformation::SetSystemTime};
|
||||||
|
|
||||||
|
use uucore::shortcut_value_parser::ShortcutValueParser;
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
const DATE: &str = "date";
|
const DATE: &str = "date";
|
||||||
const HOURS: &str = "hours";
|
const HOURS: &str = "hours";
|
||||||
const MINUTES: &str = "minutes";
|
const MINUTES: &str = "minutes";
|
||||||
const SECONDS: &str = "seconds";
|
const SECONDS: &str = "seconds";
|
||||||
const HOUR: &str = "hour";
|
|
||||||
const MINUTE: &str = "minute";
|
|
||||||
const SECOND: &str = "second";
|
|
||||||
const NS: &str = "ns";
|
const NS: &str = "ns";
|
||||||
|
|
||||||
const ABOUT: &str = help_about!("date.md");
|
const ABOUT: &str = help_about!("date.md");
|
||||||
|
@ -110,9 +109,9 @@ enum Iso8601Format {
|
||||||
impl<'a> From<&'a str> for Iso8601Format {
|
impl<'a> From<&'a str> for Iso8601Format {
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self {
|
||||||
match s {
|
match s {
|
||||||
HOURS | HOUR => Self::Hours,
|
HOURS => Self::Hours,
|
||||||
MINUTES | MINUTE => Self::Minutes,
|
MINUTES => Self::Minutes,
|
||||||
SECONDS | SECOND => Self::Seconds,
|
SECONDS => Self::Seconds,
|
||||||
NS => Self::Ns,
|
NS => Self::Ns,
|
||||||
DATE => Self::Date,
|
DATE => Self::Date,
|
||||||
// Note: This is caught by clap via `possible_values`
|
// Note: This is caught by clap via `possible_values`
|
||||||
|
@ -131,7 +130,7 @@ impl<'a> From<&'a str> for Rfc3339Format {
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self {
|
||||||
match s {
|
match s {
|
||||||
DATE => Self::Date,
|
DATE => Self::Date,
|
||||||
SECONDS | SECOND => Self::Seconds,
|
SECONDS => Self::Seconds,
|
||||||
NS => Self::Ns,
|
NS => Self::Ns,
|
||||||
// Should be caught by clap
|
// Should be caught by clap
|
||||||
_ => panic!("Invalid format: {s}"),
|
_ => panic!("Invalid format: {s}"),
|
||||||
|
@ -317,7 +316,9 @@ pub fn uu_app() -> Command {
|
||||||
.short('I')
|
.short('I')
|
||||||
.long(OPT_ISO_8601)
|
.long(OPT_ISO_8601)
|
||||||
.value_name("FMT")
|
.value_name("FMT")
|
||||||
.value_parser([DATE, HOUR, HOURS, MINUTE, MINUTES, SECOND, SECONDS, NS])
|
.value_parser(ShortcutValueParser::new([
|
||||||
|
DATE, HOURS, MINUTES, SECONDS, NS,
|
||||||
|
]))
|
||||||
.num_args(0..=1)
|
.num_args(0..=1)
|
||||||
.default_missing_value(OPT_DATE)
|
.default_missing_value(OPT_DATE)
|
||||||
.help(ISO_8601_HELP_STRING),
|
.help(ISO_8601_HELP_STRING),
|
||||||
|
@ -333,7 +334,7 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(OPT_RFC_3339)
|
Arg::new(OPT_RFC_3339)
|
||||||
.long(OPT_RFC_3339)
|
.long(OPT_RFC_3339)
|
||||||
.value_name("FMT")
|
.value_name("FMT")
|
||||||
.value_parser([DATE, SECOND, SECONDS, NS])
|
.value_parser(ShortcutValueParser::new([DATE, SECONDS, NS]))
|
||||||
.help(RFC_3339_HELP_STRING),
|
.help(RFC_3339_HELP_STRING),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
|
|
@ -13,7 +13,7 @@ Copy, and optionally convert, a file system resource
|
||||||
|
|
||||||
### Operands
|
### Operands
|
||||||
|
|
||||||
- `Bs=BYTES` : read and write up to BYTES bytes at a time (default: 512);
|
- `bs=BYTES` : read and write up to BYTES bytes at a time (default: 512);
|
||||||
overwrites `ibs` and `obs`.
|
overwrites `ibs` and `obs`.
|
||||||
- `cbs=BYTES` : the 'conversion block size' in bytes. Applies to the
|
- `cbs=BYTES` : the 'conversion block size' in bytes. Applies to the
|
||||||
`conv=block`, and `conv=unblock` operations.
|
`conv=block`, and `conv=unblock` operations.
|
||||||
|
@ -114,7 +114,7 @@ Copy, and optionally convert, a file system resource
|
||||||
|
|
||||||
### General Flags
|
### General Flags
|
||||||
|
|
||||||
- `Direct` : use direct I/O for data.
|
- `direct` : use direct I/O for data.
|
||||||
- `directory` : fail unless the given input (if used as an iflag) or
|
- `directory` : fail unless the given input (if used as an iflag) or
|
||||||
output (if used as an oflag) is a directory.
|
output (if used as an oflag) is a directory.
|
||||||
- `dsync` : use synchronized I/O for data.
|
- `dsync` : use synchronized I/O for data.
|
||||||
|
|
|
@ -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;
|
||||||
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
|
if alarm.is_triggered() {
|
||||||
if prog_update.duration.as_secs() >= progress_as_secs {
|
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
|
||||||
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<()> {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -306,7 +306,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
|
||||||
// if there is no program name for some reason, default to "hashsum"
|
// if there is no program name for some reason, default to "hashsum"
|
||||||
let program = args.next().unwrap_or_else(|| OsString::from(NAME));
|
let program = args.next().unwrap_or_else(|| OsString::from(NAME));
|
||||||
let binary_name = Path::new(&program)
|
let binary_name = Path::new(&program)
|
||||||
.file_name()
|
.file_stem()
|
||||||
.unwrap_or_else(|| OsStr::new(NAME))
|
.unwrap_or_else(|| OsStr::new(NAME))
|
||||||
.to_string_lossy();
|
.to_string_lossy();
|
||||||
|
|
||||||
|
|
|
@ -3010,6 +3010,20 @@ fn display_inode(metadata: &Metadata) -> String {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -> String {
|
fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -> String {
|
||||||
let substitute_string = "?".to_string();
|
let substitute_string = "?".to_string();
|
||||||
|
// If we must dereference, ensure that the symlink is actually valid even if the system
|
||||||
|
// does not support SELinux.
|
||||||
|
// Conforms to the GNU coreutils where a dangling symlink results in exit code 1.
|
||||||
|
if must_dereference {
|
||||||
|
match get_metadata(p_buf, must_dereference) {
|
||||||
|
Err(err) => {
|
||||||
|
// The Path couldn't be dereferenced, so return early and set exit code 1
|
||||||
|
// to indicate a minor error
|
||||||
|
show!(LsError::IOErrorContext(err, p_buf.to_path_buf(), false));
|
||||||
|
return substitute_string;
|
||||||
|
}
|
||||||
|
Ok(md) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
if config.selinux_supported {
|
if config.selinux_supported {
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
{
|
{
|
||||||
|
|
|
@ -50,10 +50,11 @@ pub mod options {
|
||||||
pub const FILES: &str = "files";
|
pub const FILES: &str = "files";
|
||||||
}
|
}
|
||||||
|
|
||||||
const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";
|
const MULTI_FILE_TOP_PROMPT: &str = "\r::::::::::::::\n\r{}\n\r::::::::::::::\n";
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
clean_print: bool,
|
clean_print: bool,
|
||||||
|
from_line: usize,
|
||||||
lines: Option<u16>,
|
lines: Option<u16>,
|
||||||
print_over: bool,
|
print_over: bool,
|
||||||
silent: bool,
|
silent: bool,
|
||||||
|
@ -72,8 +73,13 @@ impl Options {
|
||||||
(None, Some(number)) if number > 0 => Some(number + 1),
|
(None, Some(number)) if number > 0 => Some(number + 1),
|
||||||
(_, _) => None,
|
(_, _) => None,
|
||||||
};
|
};
|
||||||
|
let from_line = match matches.get_one::<usize>(options::FROM_LINE).copied() {
|
||||||
|
Some(number) if number > 1 => number - 1,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
Self {
|
Self {
|
||||||
clean_print: matches.get_flag(options::CLEAN_PRINT),
|
clean_print: matches.get_flag(options::CLEAN_PRINT),
|
||||||
|
from_line,
|
||||||
lines,
|
lines,
|
||||||
print_over: matches.get_flag(options::PRINT_OVER),
|
print_over: matches.get_flag(options::PRINT_OVER),
|
||||||
silent: matches.get_flag(options::SILENT),
|
silent: matches.get_flag(options::SILENT),
|
||||||
|
@ -90,7 +96,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let options = Options::from(&matches);
|
let mut options = Options::from(&matches);
|
||||||
|
|
||||||
let mut buff = String::new();
|
let mut buff = String::new();
|
||||||
|
|
||||||
|
@ -115,9 +121,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
format!("cannot open {}: No such file or directory", file.quote()),
|
format!("cannot open {}: No such file or directory", file.quote()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if length > 1 {
|
|
||||||
buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap()));
|
|
||||||
}
|
|
||||||
let opened_file = match File::open(file) {
|
let opened_file = match File::open(file) {
|
||||||
Err(why) => {
|
Err(why) => {
|
||||||
terminal::disable_raw_mode().unwrap();
|
terminal::disable_raw_mode().unwrap();
|
||||||
|
@ -130,14 +133,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
};
|
};
|
||||||
let mut reader = BufReader::new(opened_file);
|
let mut reader = BufReader::new(opened_file);
|
||||||
reader.read_to_string(&mut buff).unwrap();
|
reader.read_to_string(&mut buff).unwrap();
|
||||||
more(&buff, &mut stdout, next_file.copied(), &options)?;
|
more(
|
||||||
|
&buff,
|
||||||
|
&mut stdout,
|
||||||
|
length > 1,
|
||||||
|
file.to_str(),
|
||||||
|
next_file.copied(),
|
||||||
|
&mut options,
|
||||||
|
)?;
|
||||||
buff.clear();
|
buff.clear();
|
||||||
}
|
}
|
||||||
reset_term(&mut stdout);
|
reset_term(&mut stdout);
|
||||||
} else if !std::io::stdin().is_terminal() {
|
} else if !std::io::stdin().is_terminal() {
|
||||||
stdin().read_to_string(&mut buff).unwrap();
|
stdin().read_to_string(&mut buff).unwrap();
|
||||||
let mut stdout = setup_term();
|
let mut stdout = setup_term();
|
||||||
more(&buff, &mut stdout, None, &options)?;
|
more(&buff, &mut stdout, false, None, None, &mut options)?;
|
||||||
reset_term(&mut stdout);
|
reset_term(&mut stdout);
|
||||||
} else {
|
} else {
|
||||||
return Err(UUsageError::new(1, "bad usage"));
|
return Err(UUsageError::new(1, "bad usage"));
|
||||||
|
@ -179,6 +189,22 @@ pub fn uu_app() -> Command {
|
||||||
.help("Squeeze multiple blank lines into one")
|
.help("Squeeze multiple blank lines into one")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::PLAIN)
|
||||||
|
.short('u')
|
||||||
|
.long(options::PLAIN)
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.hide(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::FROM_LINE)
|
||||||
|
.short('F')
|
||||||
|
.long(options::FROM_LINE)
|
||||||
|
.num_args(1)
|
||||||
|
.value_name("number")
|
||||||
|
.value_parser(value_parser!(usize))
|
||||||
|
.help("Display file beginning from line number"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::LINES)
|
Arg::new(options::LINES)
|
||||||
.short('n')
|
.short('n')
|
||||||
|
@ -191,7 +217,6 @@ pub fn uu_app() -> Command {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::NUMBER)
|
Arg::new(options::NUMBER)
|
||||||
.long(options::NUMBER)
|
.long(options::NUMBER)
|
||||||
.required(false)
|
|
||||||
.num_args(1)
|
.num_args(1)
|
||||||
.value_parser(value_parser!(u16).range(0..))
|
.value_parser(value_parser!(u16).range(0..))
|
||||||
.help("Same as --lines"),
|
.help("Same as --lines"),
|
||||||
|
@ -210,21 +235,6 @@ pub fn uu_app() -> Command {
|
||||||
.long(options::NO_PAUSE)
|
.long(options::NO_PAUSE)
|
||||||
.help("Suppress pause after form feed"),
|
.help("Suppress pause after form feed"),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::new(options::PLAIN)
|
|
||||||
.short('u')
|
|
||||||
.long(options::PLAIN)
|
|
||||||
.help("Suppress underlining and bold"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::FROM_LINE)
|
|
||||||
.short('F')
|
|
||||||
.allow_hyphen_values(true)
|
|
||||||
.required(false)
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("number")
|
|
||||||
.help("Display file beginning from line number"),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::PATTERN)
|
Arg::new(options::PATTERN)
|
||||||
.short('P')
|
.short('P')
|
||||||
|
@ -273,8 +283,10 @@ fn reset_term(_: &mut usize) {}
|
||||||
fn more(
|
fn more(
|
||||||
buff: &str,
|
buff: &str,
|
||||||
stdout: &mut Stdout,
|
stdout: &mut Stdout,
|
||||||
|
multiple_file: bool,
|
||||||
|
file: Option<&str>,
|
||||||
next_file: Option<&str>,
|
next_file: Option<&str>,
|
||||||
options: &Options,
|
options: &mut Options,
|
||||||
) -> UResult<()> {
|
) -> UResult<()> {
|
||||||
let (cols, mut rows) = terminal::size().unwrap();
|
let (cols, mut rows) = terminal::size().unwrap();
|
||||||
if let Some(number) = options.lines {
|
if let Some(number) = options.lines {
|
||||||
|
@ -284,8 +296,23 @@ fn more(
|
||||||
let lines = break_buff(buff, usize::from(cols));
|
let lines = break_buff(buff, usize::from(cols));
|
||||||
|
|
||||||
let mut pager = Pager::new(rows, lines, next_file, options);
|
let mut pager = Pager::new(rows, lines, next_file, options);
|
||||||
|
|
||||||
|
if multiple_file {
|
||||||
|
execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
|
||||||
|
stdout.write_all(
|
||||||
|
MULTI_FILE_TOP_PROMPT
|
||||||
|
.replace("{}", file.unwrap_or_default())
|
||||||
|
.as_bytes(),
|
||||||
|
)?;
|
||||||
|
pager.content_rows -= 3;
|
||||||
|
}
|
||||||
pager.draw(stdout, None);
|
pager.draw(stdout, None);
|
||||||
if pager.should_close() {
|
if multiple_file {
|
||||||
|
options.from_line = 0;
|
||||||
|
pager.content_rows += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if pager.should_close() && next_file.is_none() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +433,7 @@ impl<'a> Pager<'a> {
|
||||||
fn new(rows: u16, lines: Vec<String>, next_file: Option<&'a str>, options: &Options) -> Self {
|
fn new(rows: u16, lines: Vec<String>, next_file: Option<&'a str>, options: &Options) -> Self {
|
||||||
let line_count = lines.len();
|
let line_count = lines.len();
|
||||||
Self {
|
Self {
|
||||||
upper_mark: 0,
|
upper_mark: options.from_line,
|
||||||
content_rows: rows.saturating_sub(1),
|
content_rows: rows.saturating_sub(1),
|
||||||
lines,
|
lines,
|
||||||
next_file,
|
next_file,
|
||||||
|
@ -472,10 +499,10 @@ impl<'a> Pager<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, stdout: &mut std::io::Stdout, wrong_key: Option<char>) {
|
fn draw(&mut self, stdout: &mut std::io::Stdout, wrong_key: Option<char>) {
|
||||||
|
self.draw_lines(stdout);
|
||||||
let lower_mark = self
|
let lower_mark = self
|
||||||
.line_count
|
.line_count
|
||||||
.min(self.upper_mark.saturating_add(self.content_rows.into()));
|
.min(self.upper_mark.saturating_add(self.content_rows.into()));
|
||||||
self.draw_lines(stdout);
|
|
||||||
self.draw_prompt(stdout, lower_mark, wrong_key);
|
self.draw_prompt(stdout, lower_mark, wrong_key);
|
||||||
stdout.flush().unwrap();
|
stdout.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -535,7 +562,6 @@ impl<'a> Pager<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let status = format!("--More--({status_inner})");
|
let status = format!("--More--({status_inner})");
|
||||||
|
|
||||||
let banner = match (self.silent, wrong_key) {
|
let banner = match (self.silent, wrong_key) {
|
||||||
(true, Some(key)) => format!(
|
(true, Some(key)) => format!(
|
||||||
"{status} [Unknown key: '{key}'. Press 'h' for instructions. (unimplemented)]"
|
"{status} [Unknown key: '{key}'. Press 'h' for instructions. (unimplemented)]"
|
||||||
|
|
|
@ -22,7 +22,7 @@ use std::os::unix;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::os::windows;
|
use std::os::windows;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use uucore::backup_control::{self, BackupMode};
|
use uucore::backup_control::{self, source_is_target_backup, 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_or_one_way_symlink_to_same_file, are_hardlinks_to_same_file};
|
use uucore::fs::{are_hardlinks_or_one_way_symlink_to_same_file, are_hardlinks_to_same_file};
|
||||||
|
@ -251,6 +251,17 @@ fn parse_paths(files: &[OsString], b: &Behavior) -> Vec<PathBuf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> {
|
fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> {
|
||||||
|
if b.backup == BackupMode::SimpleBackup && source_is_target_backup(source, target, &b.suffix) {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::NotFound,
|
||||||
|
format!(
|
||||||
|
"backing up {} might destroy source; {} not moved",
|
||||||
|
target.quote(),
|
||||||
|
source.quote()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
if source.symlink_metadata().is_err() {
|
if source.symlink_metadata().is_err() {
|
||||||
return Err(MvError::NoSuchFile(source.quote().to_string()).into());
|
return Err(MvError::NoSuchFile(source.quote().to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#nl
|
# nl
|
||||||
|
|
||||||
```
|
```
|
||||||
nl [OPTION]... [FILE]...
|
nl [OPTION]... [FILE]...
|
||||||
|
|
|
@ -29,7 +29,7 @@ fn parse_style(chars: &[char]) -> Result<crate::NumberingStyle, String> {
|
||||||
pub fn parse_options(settings: &mut crate::Settings, opts: &clap::ArgMatches) -> Vec<String> {
|
pub fn parse_options(settings: &mut crate::Settings, opts: &clap::ArgMatches) -> Vec<String> {
|
||||||
// This vector holds error messages encountered.
|
// This vector holds error messages encountered.
|
||||||
let mut errs: Vec<String> = vec![];
|
let mut errs: Vec<String> = vec![];
|
||||||
settings.renumber = !opts.contains_id(options::NO_RENUMBER);
|
settings.renumber = opts.get_flag(options::NO_RENUMBER);
|
||||||
match opts.get_one::<String>(options::NUMBER_SEPARATOR) {
|
match opts.get_one::<String>(options::NUMBER_SEPARATOR) {
|
||||||
None => {}
|
None => {}
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -210,7 +213,8 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(options::NO_RENUMBER)
|
Arg::new(options::NO_RENUMBER)
|
||||||
.short('p')
|
.short('p')
|
||||||
.long(options::NO_RENUMBER)
|
.long(options::NO_RENUMBER)
|
||||||
.help("do not reset line numbers at logical pages"),
|
.help("do not reset line numbers at logical pages")
|
||||||
|
.action(ArgAction::SetFalse),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::NUMBER_SEPARATOR)
|
Arg::new(options::NUMBER_SEPARATOR)
|
||||||
|
|
|
@ -11,11 +11,13 @@ use crate::options::*;
|
||||||
use crate::units::{Result, Unit};
|
use crate::units::{Result, Unit};
|
||||||
use clap::{crate_version, parser::ValueSource, Arg, ArgAction, ArgMatches, Command};
|
use clap::{crate_version, parser::ValueSource, Arg, ArgAction, ArgMatches, Command};
|
||||||
use std::io::{BufRead, Write};
|
use std::io::{BufRead, Write};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use units::{IEC_BASES, SI_BASES};
|
use units::{IEC_BASES, SI_BASES};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::UResult;
|
use uucore::error::{UError, UResult};
|
||||||
use uucore::ranges::Range;
|
use uucore::ranges::Range;
|
||||||
use uucore::{format_usage, help_about, help_section, help_usage};
|
use uucore::{format_usage, help_about, help_section, help_usage, show, show_error};
|
||||||
|
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
|
@ -28,12 +30,8 @@ const USAGE: &str = help_usage!("numfmt.md");
|
||||||
|
|
||||||
fn handle_args<'a>(args: impl Iterator<Item = &'a str>, options: &NumfmtOptions) -> UResult<()> {
|
fn handle_args<'a>(args: impl Iterator<Item = &'a str>, options: &NumfmtOptions) -> UResult<()> {
|
||||||
for l in args {
|
for l in args {
|
||||||
match format_and_print(l, options) {
|
format_and_handle_validation(l, options)?;
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(e) => Err(NumfmtError::FormattingError(e.to_string())),
|
|
||||||
}?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,23 +39,41 @@ fn handle_buffer<R>(input: R, options: &NumfmtOptions) -> UResult<()>
|
||||||
where
|
where
|
||||||
R: BufRead,
|
R: BufRead,
|
||||||
{
|
{
|
||||||
let mut lines = input.lines();
|
for (idx, line_result) in input.lines().by_ref().enumerate() {
|
||||||
for (idx, line) in lines.by_ref().enumerate() {
|
match line_result {
|
||||||
match line {
|
Ok(line) if idx < options.header => {
|
||||||
Ok(l) if idx < options.header => {
|
println!("{line}");
|
||||||
println!("{l}");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(l) => match format_and_print(&l, options) {
|
Ok(line) => format_and_handle_validation(line.as_ref(), options),
|
||||||
Ok(_) => Ok(()),
|
Err(err) => return Err(Box::new(NumfmtError::IoError(err.to_string()))),
|
||||||
Err(e) => Err(NumfmtError::FormattingError(e.to_string())),
|
|
||||||
},
|
|
||||||
Err(e) => Err(NumfmtError::IoError(e.to_string())),
|
|
||||||
}?;
|
}?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_and_handle_validation(input_line: &str, options: &NumfmtOptions) -> UResult<()> {
|
||||||
|
let handled_line = format_and_print(input_line, options);
|
||||||
|
|
||||||
|
if let Err(error_message) = handled_line {
|
||||||
|
match options.invalid {
|
||||||
|
InvalidModes::Abort => {
|
||||||
|
return Err(Box::new(NumfmtError::FormattingError(error_message)));
|
||||||
|
}
|
||||||
|
InvalidModes::Fail => {
|
||||||
|
show!(NumfmtError::FormattingError(error_message));
|
||||||
|
}
|
||||||
|
InvalidModes::Warn => {
|
||||||
|
show_error!("{}", error_message);
|
||||||
|
}
|
||||||
|
InvalidModes::Ignore => {}
|
||||||
|
};
|
||||||
|
println!("{}", input_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_unit(s: &str) -> Result<Unit> {
|
fn parse_unit(s: &str) -> Result<Unit> {
|
||||||
match s {
|
match s {
|
||||||
"auto" => Ok(Unit::Auto),
|
"auto" => Ok(Unit::Auto),
|
||||||
|
@ -201,6 +217,9 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
|
||||||
.get_one::<String>(options::SUFFIX)
|
.get_one::<String>(options::SUFFIX)
|
||||||
.map(|s| s.to_owned());
|
.map(|s| s.to_owned());
|
||||||
|
|
||||||
|
let invalid =
|
||||||
|
InvalidModes::from_str(args.get_one::<String>(options::INVALID).unwrap()).unwrap();
|
||||||
|
|
||||||
Ok(NumfmtOptions {
|
Ok(NumfmtOptions {
|
||||||
transform,
|
transform,
|
||||||
padding,
|
padding,
|
||||||
|
@ -210,6 +229,7 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
|
||||||
round,
|
round,
|
||||||
suffix,
|
suffix,
|
||||||
format,
|
format,
|
||||||
|
invalid,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,10 +360,7 @@ pub fn uu_app() -> Command {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::ROUND)
|
Arg::new(options::ROUND)
|
||||||
.long(options::ROUND)
|
.long(options::ROUND)
|
||||||
.help(
|
.help("use METHOD for rounding when scaling")
|
||||||
"use METHOD for rounding when scaling; METHOD can be: up,\
|
|
||||||
down, from-zero, towards-zero, nearest",
|
|
||||||
)
|
|
||||||
.value_name("METHOD")
|
.value_name("METHOD")
|
||||||
.default_value("from-zero")
|
.default_value("from-zero")
|
||||||
.value_parser(["up", "down", "from-zero", "towards-zero", "nearest"]),
|
.value_parser(["up", "down", "from-zero", "towards-zero", "nearest"]),
|
||||||
|
@ -357,6 +374,14 @@ pub fn uu_app() -> Command {
|
||||||
)
|
)
|
||||||
.value_name("SUFFIX"),
|
.value_name("SUFFIX"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::INVALID)
|
||||||
|
.long(options::INVALID)
|
||||||
|
.help("set the failure mode for invalid input")
|
||||||
|
.default_value("abort")
|
||||||
|
.value_parser(["abort", "fail", "warn", "ignore"])
|
||||||
|
.value_name("INVALID"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::NUMBER)
|
Arg::new(options::NUMBER)
|
||||||
.hide(true)
|
.hide(true)
|
||||||
|
@ -366,9 +391,11 @@ pub fn uu_app() -> Command {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use uucore::error::get_exit_code;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
handle_buffer, parse_unit_size, parse_unit_size_suffix, FormatOptions, NumfmtOptions,
|
handle_args, handle_buffer, parse_unit_size, parse_unit_size_suffix, FormatOptions,
|
||||||
Range, RoundMethod, TransformOptions, Unit,
|
InvalidModes, NumfmtOptions, Range, RoundMethod, TransformOptions, Unit,
|
||||||
};
|
};
|
||||||
use std::io::{BufReader, Error, ErrorKind, Read};
|
use std::io::{BufReader, Error, ErrorKind, Read};
|
||||||
struct MockBuffer {}
|
struct MockBuffer {}
|
||||||
|
@ -394,6 +421,7 @@ mod tests {
|
||||||
round: RoundMethod::Nearest,
|
round: RoundMethod::Nearest,
|
||||||
suffix: None,
|
suffix: None,
|
||||||
format: FormatOptions::default(),
|
format: FormatOptions::default(),
|
||||||
|
invalid: InvalidModes::Abort,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,6 +437,20 @@ mod tests {
|
||||||
assert_eq!(result.code(), 1);
|
assert_eq!(result.code(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn broken_buffer_returns_io_error_after_header() {
|
||||||
|
let mock_buffer = MockBuffer {};
|
||||||
|
let mut options = get_valid_options();
|
||||||
|
options.header = 0;
|
||||||
|
let result = handle_buffer(BufReader::new(mock_buffer), &options)
|
||||||
|
.expect_err("returned Ok after receiving IO error");
|
||||||
|
let result_debug = format!("{:?}", result);
|
||||||
|
let result_display = format!("{}", result);
|
||||||
|
assert_eq!(result_debug, "IoError(\"broken pipe\")");
|
||||||
|
assert_eq!(result_display, "broken pipe");
|
||||||
|
assert_eq!(result.code(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn non_numeric_returns_formatting_error() {
|
fn non_numeric_returns_formatting_error() {
|
||||||
let input_value = b"135\nhello";
|
let input_value = b"135\nhello";
|
||||||
|
@ -431,6 +473,66 @@ mod tests {
|
||||||
assert!(result.is_ok(), "did not return Ok for valid input");
|
assert!(result.is_ok(), "did not return Ok for valid input");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn warn_returns_ok_for_invalid_input() {
|
||||||
|
let input_value = b"5\n4Q\n";
|
||||||
|
let mut options = get_valid_options();
|
||||||
|
options.invalid = InvalidModes::Warn;
|
||||||
|
let result = handle_buffer(BufReader::new(&input_value[..]), &options);
|
||||||
|
assert!(result.is_ok(), "did not return Ok for invalid input");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ignore_returns_ok_for_invalid_input() {
|
||||||
|
let input_value = b"5\n4Q\n";
|
||||||
|
let mut options = get_valid_options();
|
||||||
|
options.invalid = InvalidModes::Ignore;
|
||||||
|
let result = handle_buffer(BufReader::new(&input_value[..]), &options);
|
||||||
|
assert!(result.is_ok(), "did not return Ok for invalid input");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn buffer_fail_returns_status_2_for_invalid_input() {
|
||||||
|
let input_value = b"5\n4Q\n";
|
||||||
|
let mut options = get_valid_options();
|
||||||
|
options.invalid = InvalidModes::Fail;
|
||||||
|
handle_buffer(BufReader::new(&input_value[..]), &options).unwrap();
|
||||||
|
assert!(
|
||||||
|
get_exit_code() == 2,
|
||||||
|
"should set exit code 2 for formatting errors"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn abort_returns_status_2_for_invalid_input() {
|
||||||
|
let input_value = b"5\n4Q\n";
|
||||||
|
let mut options = get_valid_options();
|
||||||
|
options.invalid = InvalidModes::Abort;
|
||||||
|
let result = handle_buffer(BufReader::new(&input_value[..]), &options);
|
||||||
|
assert!(result.is_err(), "did not return err for invalid input");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn args_fail_returns_status_2_for_invalid_input() {
|
||||||
|
let input_value = ["5", "4Q"].into_iter();
|
||||||
|
let mut options = get_valid_options();
|
||||||
|
options.invalid = InvalidModes::Fail;
|
||||||
|
handle_args(input_value, &options).unwrap();
|
||||||
|
assert!(
|
||||||
|
get_exit_code() == 2,
|
||||||
|
"should set exit code 2 for formatting errors"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn args_warn_returns_status_0_for_invalid_input() {
|
||||||
|
let input_value = ["5", "4Q"].into_iter();
|
||||||
|
let mut options = get_valid_options();
|
||||||
|
options.invalid = InvalidModes::Warn;
|
||||||
|
let result = handle_args(input_value, &options);
|
||||||
|
assert!(result.is_ok(), "did not return ok for invalid input");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_unit_size() {
|
fn test_parse_unit_size() {
|
||||||
assert_eq!(1, parse_unit_size("1").unwrap());
|
assert_eq!(1, parse_unit_size("1").unwrap());
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub const FROM_UNIT: &str = "from-unit";
|
||||||
pub const FROM_UNIT_DEFAULT: &str = "1";
|
pub const FROM_UNIT_DEFAULT: &str = "1";
|
||||||
pub const HEADER: &str = "header";
|
pub const HEADER: &str = "header";
|
||||||
pub const HEADER_DEFAULT: &str = "1";
|
pub const HEADER_DEFAULT: &str = "1";
|
||||||
|
pub const INVALID: &str = "invalid";
|
||||||
pub const NUMBER: &str = "NUMBER";
|
pub const NUMBER: &str = "NUMBER";
|
||||||
pub const PADDING: &str = "padding";
|
pub const PADDING: &str = "padding";
|
||||||
pub const ROUND: &str = "round";
|
pub const ROUND: &str = "round";
|
||||||
|
@ -29,6 +30,14 @@ pub struct TransformOptions {
|
||||||
pub to_unit: usize,
|
pub to_unit: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum InvalidModes {
|
||||||
|
Abort,
|
||||||
|
Fail,
|
||||||
|
Warn,
|
||||||
|
Ignore,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct NumfmtOptions {
|
pub struct NumfmtOptions {
|
||||||
pub transform: TransformOptions,
|
pub transform: TransformOptions,
|
||||||
pub padding: isize,
|
pub padding: isize,
|
||||||
|
@ -38,6 +47,7 @@ pub struct NumfmtOptions {
|
||||||
pub round: RoundMethod,
|
pub round: RoundMethod,
|
||||||
pub suffix: Option<String>,
|
pub suffix: Option<String>,
|
||||||
pub format: FormatOptions,
|
pub format: FormatOptions,
|
||||||
|
pub invalid: InvalidModes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
@ -227,6 +237,20 @@ impl FromStr for FormatOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for InvalidModes {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"abort" => Ok(Self::Abort),
|
||||||
|
"fail" => Ok(Self::Fail),
|
||||||
|
"warn" => Ok(Self::Warn),
|
||||||
|
"ignore" => Ok(Self::Ignore),
|
||||||
|
unknown => Err(format!("Unknown invalid mode: {unknown}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -336,4 +360,21 @@ mod tests {
|
||||||
assert_eq!(expected_options, "%0'0'0'f".parse().unwrap());
|
assert_eq!(expected_options, "%0'0'0'f".parse().unwrap());
|
||||||
assert_eq!(expected_options, "%'0'0'0f".parse().unwrap());
|
assert_eq!(expected_options, "%'0'0'0f".parse().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_set_invalid_mode() {
|
||||||
|
assert_eq!(Ok(InvalidModes::Abort), InvalidModes::from_str("abort"));
|
||||||
|
assert_eq!(Ok(InvalidModes::Abort), InvalidModes::from_str("ABORT"));
|
||||||
|
|
||||||
|
assert_eq!(Ok(InvalidModes::Fail), InvalidModes::from_str("fail"));
|
||||||
|
assert_eq!(Ok(InvalidModes::Fail), InvalidModes::from_str("FAIL"));
|
||||||
|
|
||||||
|
assert_eq!(Ok(InvalidModes::Ignore), InvalidModes::from_str("ignore"));
|
||||||
|
assert_eq!(Ok(InvalidModes::Ignore), InvalidModes::from_str("IGNORE"));
|
||||||
|
|
||||||
|
assert_eq!(Ok(InvalidModes::Warn), InvalidModes::from_str("warn"));
|
||||||
|
assert_eq!(Ok(InvalidModes::Warn), InvalidModes::from_str("WARN"));
|
||||||
|
|
||||||
|
assert!(InvalidModes::from_str("something unknown").is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
|
@ -31,7 +31,7 @@ const USAGE: &str = help_usage!("seq.md");
|
||||||
|
|
||||||
const OPT_SEPARATOR: &str = "separator";
|
const OPT_SEPARATOR: &str = "separator";
|
||||||
const OPT_TERMINATOR: &str = "terminator";
|
const OPT_TERMINATOR: &str = "terminator";
|
||||||
const OPT_WIDTHS: &str = "widths";
|
const OPT_EQUAL_WIDTH: &str = "equal-width";
|
||||||
const OPT_FORMAT: &str = "format";
|
const OPT_FORMAT: &str = "format";
|
||||||
|
|
||||||
const ARG_NUMBERS: &str = "numbers";
|
const ARG_NUMBERS: &str = "numbers";
|
||||||
|
@ -40,7 +40,7 @@ const ARG_NUMBERS: &str = "numbers";
|
||||||
struct SeqOptions<'a> {
|
struct SeqOptions<'a> {
|
||||||
separator: String,
|
separator: String,
|
||||||
terminator: String,
|
terminator: String,
|
||||||
widths: bool,
|
equal_width: bool,
|
||||||
format: Option<&'a str>,
|
format: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.map(|s| s.as_str())
|
.map(|s| s.as_str())
|
||||||
.unwrap_or("\n")
|
.unwrap_or("\n")
|
||||||
.to_string(),
|
.to_string(),
|
||||||
widths: matches.get_flag(OPT_WIDTHS),
|
equal_width: matches.get_flag(OPT_EQUAL_WIDTH),
|
||||||
format: matches.get_one::<String>(OPT_FORMAT).map(|s| s.as_str()),
|
format: matches.get_one::<String>(OPT_FORMAT).map(|s| s.as_str()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
(first, increment, last),
|
(first, increment, last),
|
||||||
&options.separator,
|
&options.separator,
|
||||||
&options.terminator,
|
&options.terminator,
|
||||||
options.widths,
|
options.equal_width,
|
||||||
padding,
|
padding,
|
||||||
options.format,
|
options.format,
|
||||||
)
|
)
|
||||||
|
@ -137,7 +137,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
largest_dec,
|
largest_dec,
|
||||||
&options.separator,
|
&options.separator,
|
||||||
&options.terminator,
|
&options.terminator,
|
||||||
options.widths,
|
options.equal_width,
|
||||||
padding,
|
padding,
|
||||||
options.format,
|
options.format,
|
||||||
),
|
),
|
||||||
|
@ -170,9 +170,9 @@ pub fn uu_app() -> Command {
|
||||||
.help("Terminator character (defaults to \\n)"),
|
.help("Terminator character (defaults to \\n)"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(OPT_WIDTHS)
|
Arg::new(OPT_EQUAL_WIDTH)
|
||||||
.short('w')
|
.short('w')
|
||||||
.long("widths")
|
.long("equal-width")
|
||||||
.help("Equalize widths of all numbers by padding with zeros")
|
.help("Equalize widths of all numbers by padding with zeros")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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,21 +16,22 @@ 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};
|
||||||
|
|
||||||
/// The chunk that is passed around between threads.
|
self_cell!(
|
||||||
/// `lines` consist of slices into `buffer`.
|
/// The chunk that is passed around between threads.
|
||||||
#[self_referencing(pub_extras)]
|
pub struct Chunk {
|
||||||
#[derive(Debug)]
|
owner: Vec<u8>,
|
||||||
pub struct Chunk {
|
|
||||||
pub buffer: Vec<u8>,
|
#[covariant]
|
||||||
#[borrows(buffer)]
|
dependent: ChunkContents,
|
||||||
#[covariant]
|
}
|
||||||
pub contents: ChunkContents<'this>,
|
|
||||||
}
|
impl {Debug}
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ChunkContents<'a> {
|
pub struct ChunkContents<'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 {
|
||||||
|
|
|
@ -267,7 +267,11 @@ impl NumberType {
|
||||||
let num_chunks = n_str
|
let num_chunks = n_str
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?;
|
.map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?;
|
||||||
Ok(Self::Bytes(num_chunks))
|
if num_chunks > 0 {
|
||||||
|
Ok(Self::Bytes(num_chunks))
|
||||||
|
} else {
|
||||||
|
Err(NumberTypeError::NumberOfChunks(s.to_string()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
["l", n_str] => {
|
["l", n_str] => {
|
||||||
let num_chunks = n_str
|
let num_chunks = n_str
|
||||||
|
@ -357,6 +361,20 @@ impl fmt::Display for StrategyError {
|
||||||
impl Strategy {
|
impl Strategy {
|
||||||
/// Parse a strategy from the command-line arguments.
|
/// Parse a strategy from the command-line arguments.
|
||||||
fn from(matches: &ArgMatches) -> Result<Self, StrategyError> {
|
fn from(matches: &ArgMatches) -> Result<Self, StrategyError> {
|
||||||
|
fn get_and_parse(
|
||||||
|
matches: &ArgMatches,
|
||||||
|
option: &str,
|
||||||
|
strategy: fn(u64) -> Strategy,
|
||||||
|
error: fn(ParseSizeError) -> StrategyError,
|
||||||
|
) -> Result<Strategy, StrategyError> {
|
||||||
|
let s = matches.get_one::<String>(option).unwrap();
|
||||||
|
let n = parse_size(s).map_err(error)?;
|
||||||
|
if n > 0 {
|
||||||
|
Ok(strategy(n))
|
||||||
|
} else {
|
||||||
|
Err(error(ParseSizeError::ParseFailure(s.to_string())))
|
||||||
|
}
|
||||||
|
}
|
||||||
// Check that the user is not specifying more than one strategy.
|
// Check that the user is not specifying more than one strategy.
|
||||||
//
|
//
|
||||||
// Note: right now, this exact behavior cannot be handled by
|
// Note: right now, this exact behavior cannot be handled by
|
||||||
|
@ -370,20 +388,17 @@ impl Strategy {
|
||||||
) {
|
) {
|
||||||
(false, false, false, false) => Ok(Self::Lines(1000)),
|
(false, false, false, false) => Ok(Self::Lines(1000)),
|
||||||
(true, false, false, false) => {
|
(true, false, false, false) => {
|
||||||
let s = matches.get_one::<String>(OPT_LINES).unwrap();
|
get_and_parse(matches, OPT_LINES, Self::Lines, StrategyError::Lines)
|
||||||
let n = parse_size(s).map_err(StrategyError::Lines)?;
|
|
||||||
Ok(Self::Lines(n))
|
|
||||||
}
|
}
|
||||||
(false, true, false, false) => {
|
(false, true, false, false) => {
|
||||||
let s = matches.get_one::<String>(OPT_BYTES).unwrap();
|
get_and_parse(matches, OPT_BYTES, Self::Bytes, StrategyError::Bytes)
|
||||||
let n = parse_size(s).map_err(StrategyError::Bytes)?;
|
|
||||||
Ok(Self::Bytes(n))
|
|
||||||
}
|
|
||||||
(false, false, true, false) => {
|
|
||||||
let s = matches.get_one::<String>(OPT_LINE_BYTES).unwrap();
|
|
||||||
let n = parse_size(s).map_err(StrategyError::Bytes)?;
|
|
||||||
Ok(Self::LineBytes(n))
|
|
||||||
}
|
}
|
||||||
|
(false, false, true, false) => get_and_parse(
|
||||||
|
matches,
|
||||||
|
OPT_LINE_BYTES,
|
||||||
|
Self::LineBytes,
|
||||||
|
StrategyError::Bytes,
|
||||||
|
),
|
||||||
(false, false, false, true) => {
|
(false, false, false, true) => {
|
||||||
let s = matches.get_one::<String>(OPT_NUMBER).unwrap();
|
let s = matches.get_one::<String>(OPT_NUMBER).unwrap();
|
||||||
let number_type = NumberType::from(s).map_err(StrategyError::NumberType)?;
|
let number_type = NumberType::from(s).map_err(StrategyError::NumberType)?;
|
||||||
|
|
|
@ -3,14 +3,15 @@
|
||||||
// * 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 clocal tcgetattr tcsetattr tcsanow tiocgwinsz tiocswinsz cfgetospeed ushort
|
// spell-checker:ignore clocal tcgetattr tcsetattr tcsanow tiocgwinsz tiocswinsz cfgetospeed cfsetospeed ushort
|
||||||
|
|
||||||
mod flags;
|
mod flags;
|
||||||
|
|
||||||
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
||||||
use nix::libc::{c_ushort, O_NONBLOCK, TIOCGWINSZ, TIOCSWINSZ};
|
use nix::libc::{c_ushort, O_NONBLOCK, TIOCGWINSZ, TIOCSWINSZ};
|
||||||
use nix::sys::termios::{
|
use nix::sys::termios::{
|
||||||
cfgetospeed, tcgetattr, tcsetattr, ControlFlags, InputFlags, LocalFlags, OutputFlags, Termios,
|
cfgetospeed, cfsetospeed, tcgetattr, tcsetattr, ControlFlags, InputFlags, LocalFlags,
|
||||||
|
OutputFlags, Termios,
|
||||||
};
|
};
|
||||||
use nix::{ioctl_read_bad, ioctl_write_ptr_bad};
|
use nix::{ioctl_read_bad, ioctl_write_ptr_bad};
|
||||||
use std::io::{self, stdout};
|
use std::io::{self, stdout};
|
||||||
|
@ -244,12 +245,30 @@ fn print_terminal_size(termios: &Termios, opts: &Options) -> nix::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_in_save_format(termios: &Termios) {
|
||||||
|
print!(
|
||||||
|
"{:x}:{:x}:{:x}:{:x}",
|
||||||
|
termios.input_flags.bits(),
|
||||||
|
termios.output_flags.bits(),
|
||||||
|
termios.control_flags.bits(),
|
||||||
|
termios.local_flags.bits()
|
||||||
|
);
|
||||||
|
for cc in termios.control_chars {
|
||||||
|
print!(":{cc:x}");
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
fn print_settings(termios: &Termios, opts: &Options) -> nix::Result<()> {
|
fn print_settings(termios: &Termios, opts: &Options) -> nix::Result<()> {
|
||||||
print_terminal_size(termios, opts)?;
|
if opts.save {
|
||||||
print_flags(termios, opts, CONTROL_FLAGS);
|
print_in_save_format(termios);
|
||||||
print_flags(termios, opts, INPUT_FLAGS);
|
} else {
|
||||||
print_flags(termios, opts, OUTPUT_FLAGS);
|
print_terminal_size(termios, opts)?;
|
||||||
print_flags(termios, opts, LOCAL_FLAGS);
|
print_flags(termios, opts, CONTROL_FLAGS);
|
||||||
|
print_flags(termios, opts, INPUT_FLAGS);
|
||||||
|
print_flags(termios, opts, OUTPUT_FLAGS);
|
||||||
|
print_flags(termios, opts, LOCAL_FLAGS);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,6 +309,8 @@ fn print_flags<T: TermiosFlag>(termios: &Termios, opts: &Options, flags: &[Flag<
|
||||||
/// The value inside the `Break` variant of the `ControlFlow` indicates whether
|
/// The value inside the `Break` variant of the `ControlFlow` indicates whether
|
||||||
/// the setting has been applied.
|
/// the setting has been applied.
|
||||||
fn apply_setting(termios: &mut Termios, s: &str) -> ControlFlow<bool> {
|
fn apply_setting(termios: &mut Termios, s: &str) -> ControlFlow<bool> {
|
||||||
|
apply_baud_rate_flag(termios, s)?;
|
||||||
|
|
||||||
let (remove, name) = match s.strip_prefix('-') {
|
let (remove, name) = match s.strip_prefix('-') {
|
||||||
Some(s) => (true, s),
|
Some(s) => (true, s),
|
||||||
None => (false, s),
|
None => (false, s),
|
||||||
|
@ -332,6 +353,39 @@ fn apply_flag<T: TermiosFlag>(
|
||||||
ControlFlow::Continue(())
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_baud_rate_flag(termios: &mut Termios, input: &str) -> ControlFlow<bool> {
|
||||||
|
// BSDs use a u32 for the baud rate, so any decimal number applies.
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "ios",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "openbsd"
|
||||||
|
))]
|
||||||
|
if let Ok(n) = input.parse::<u32>() {
|
||||||
|
cfsetospeed(termios, n).expect("Failed to set baud rate");
|
||||||
|
return ControlFlow::Break(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other platforms use an enum.
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "ios",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "openbsd"
|
||||||
|
)))]
|
||||||
|
for (text, baud_rate) in BAUD_RATES {
|
||||||
|
if *text == input {
|
||||||
|
cfsetospeed(termios, *baud_rate).expect("Failed to set baud rate");
|
||||||
|
return ControlFlow::Break(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn uu_app() -> Command {
|
pub fn uu_app() -> Command {
|
||||||
Command::new(uucore::util_name())
|
Command::new(uucore::util_name())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
|
|
|
@ -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.7"
|
memmap2 = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
uucore = { workspace = true }
|
uucore = { workspace = true }
|
||||||
|
|
|
@ -7,12 +7,11 @@
|
||||||
|
|
||||||
use crate::paths::Input;
|
use crate::paths::Input;
|
||||||
use crate::{parse, platform, Quotable};
|
use crate::{parse, platform, Quotable};
|
||||||
use clap::crate_version;
|
use clap::{crate_version, value_parser};
|
||||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||||
use fundu::{DurationParser, SaturatingInto};
|
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::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use uucore::error::{UResult, USimpleError, UUsageError};
|
use uucore::error::{UResult, USimpleError, UUsageError};
|
||||||
|
@ -141,7 +140,8 @@ pub struct Settings {
|
||||||
pub use_polling: bool,
|
pub use_polling: bool,
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
pub presume_input_pipe: bool,
|
pub presume_input_pipe: bool,
|
||||||
pub inputs: VecDeque<Input>,
|
/// `FILE(s)` positional arguments
|
||||||
|
pub inputs: Vec<Input>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
|
@ -173,11 +173,11 @@ impl Settings {
|
||||||
}
|
}
|
||||||
settings.mode = FilterMode::from_obsolete_args(args);
|
settings.mode = FilterMode::from_obsolete_args(args);
|
||||||
let input = if let Some(name) = name {
|
let input = if let Some(name) = name {
|
||||||
Input::from(&name)
|
Input::from(name)
|
||||||
} else {
|
} else {
|
||||||
Input::default()
|
Input::default()
|
||||||
};
|
};
|
||||||
settings.inputs.push_back(input);
|
settings.inputs.push(input);
|
||||||
settings
|
settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,19 +285,13 @@ impl Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut inputs: VecDeque<Input> = matches
|
settings.inputs = matches
|
||||||
.get_many::<String>(options::ARG_FILES)
|
.get_many::<OsString>(options::ARG_FILES)
|
||||||
.map(|v| v.map(|string| Input::from(&string)).collect())
|
.map(|v| v.map(Input::from).collect())
|
||||||
.unwrap_or_default();
|
.unwrap_or_else(|| vec![Input::default()]);
|
||||||
|
|
||||||
// apply default and add '-' to inputs if none is present
|
settings.verbose =
|
||||||
if inputs.is_empty() {
|
settings.inputs.len() > 1 && !matches.get_flag(options::verbosity::QUIET);
|
||||||
inputs.push_front(Input::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.verbose = inputs.len() > 1 && !matches.get_flag(options::verbosity::QUIET);
|
|
||||||
|
|
||||||
settings.inputs = inputs;
|
|
||||||
|
|
||||||
Ok(settings)
|
Ok(settings)
|
||||||
}
|
}
|
||||||
|
@ -593,6 +587,7 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(options::ARG_FILES)
|
Arg::new(options::ARG_FILES)
|
||||||
.action(ArgAction::Append)
|
.action(ArgAction::Append)
|
||||||
.num_args(1..)
|
.num_args(1..)
|
||||||
|
.value_parser(value_parser!(OsString))
|
||||||
.value_hint(clap::ValueHint::FilePath),
|
.value_hint(clap::ValueHint::FilePath),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ use crate::follow::files::{FileHandling, PathData};
|
||||||
use crate::paths::{Input, InputKind, MetadataExtTail, PathExtTail};
|
use crate::paths::{Input, InputKind, MetadataExtTail, PathExtTail};
|
||||||
use crate::{platform, text};
|
use crate::{platform, text};
|
||||||
use notify::{RecommendedWatcher, RecursiveMode, Watcher, WatcherKind};
|
use notify::{RecommendedWatcher, RecursiveMode, Watcher, WatcherKind};
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::io::BufRead;
|
use std::io::BufRead;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::mpsc::{self, channel, Receiver};
|
use std::sync::mpsc::{self, channel, Receiver};
|
||||||
|
@ -270,7 +269,7 @@ impl Observer {
|
||||||
self.follow_name() && self.retry
|
self.follow_name() && self.retry
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_files(&mut self, inputs: &VecDeque<Input>) -> UResult<()> {
|
fn init_files(&mut self, inputs: &Vec<Input>) -> UResult<()> {
|
||||||
if let Some(watcher_rx) = &mut self.watcher_rx {
|
if let Some(watcher_rx) = &mut self.watcher_rx {
|
||||||
for input in inputs {
|
for input in inputs {
|
||||||
match input.kind() {
|
match input.kind() {
|
||||||
|
|
|
@ -20,6 +20,28 @@ pub enum InputKind {
|
||||||
Stdin,
|
Stdin,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
impl From<&OsStr> for InputKind {
|
||||||
|
fn from(value: &OsStr) -> Self {
|
||||||
|
if value == OsStr::new("-") {
|
||||||
|
Self::Stdin
|
||||||
|
} else {
|
||||||
|
Self::File(PathBuf::from(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
impl From<&OsStr> for InputKind {
|
||||||
|
fn from(value: &OsStr) -> Self {
|
||||||
|
if value == OsStr::new(text::DASH) {
|
||||||
|
Self::Stdin
|
||||||
|
} else {
|
||||||
|
Self::File(PathBuf::from(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
kind: InputKind,
|
kind: InputKind,
|
||||||
|
@ -27,22 +49,13 @@ pub struct Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn from<T: AsRef<OsStr>>(string: &T) -> Self {
|
pub fn from<T: AsRef<OsStr>>(string: T) -> Self {
|
||||||
let kind = if string.as_ref() == Path::new(text::DASH) {
|
let string = string.as_ref();
|
||||||
InputKind::Stdin
|
|
||||||
} else {
|
|
||||||
InputKind::File(PathBuf::from(string.as_ref()))
|
|
||||||
};
|
|
||||||
|
|
||||||
|
let kind = string.into();
|
||||||
let display_name = match kind {
|
let display_name = match kind {
|
||||||
InputKind::File(_) => string.as_ref().to_string_lossy().to_string(),
|
InputKind::File(_) => string.to_string_lossy().to_string(),
|
||||||
InputKind::Stdin => {
|
InputKind::Stdin => text::STDIN_HEADER.to_string(),
|
||||||
if cfg!(unix) {
|
|
||||||
text::STDIN_HEADER.to_string()
|
|
||||||
} else {
|
|
||||||
string.as_ref().to_string_lossy().to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Self { kind, display_name }
|
Self { kind, display_name }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# spell-checker:ignore humantime
|
# spell-checker:ignore datetime
|
||||||
[package]
|
[package]
|
||||||
name = "uu_touch"
|
name = "uu_touch"
|
||||||
version = "0.0.19"
|
version = "0.0.19"
|
||||||
|
@ -18,8 +18,7 @@ path = "src/touch.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
filetime = { workspace = true }
|
filetime = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
# TODO: use workspace dependency (0.3) when switching from time to chrono
|
parse_datetime = { workspace = true }
|
||||||
humantime_to_duration = "0.2.1"
|
|
||||||
time = { workspace = true, features = [
|
time = { workspace = true, features = [
|
||||||
"parsing",
|
"parsing",
|
||||||
"formatting",
|
"formatting",
|
||||||
|
|
|
@ -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";
|
||||||
|
@ -84,13 +84,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
) {
|
) {
|
||||||
(Some(reference), Some(date)) => {
|
(Some(reference), Some(date)) => {
|
||||||
let (atime, mtime) = stat(Path::new(reference), !matches.get_flag(options::NO_DEREF))?;
|
let (atime, mtime) = stat(Path::new(reference), !matches.get_flag(options::NO_DEREF))?;
|
||||||
if let Ok(offset) = humantime_to_duration::from_str(date) {
|
if let Ok(offset) = parse_datetime::from_str(date) {
|
||||||
let mut seconds = offset.whole_seconds();
|
let seconds = offset.num_seconds();
|
||||||
let mut nanos = offset.subsec_nanoseconds();
|
let nanos = offset.num_nanoseconds().unwrap_or(0) % 1_000_000_000;
|
||||||
if nanos < 0 {
|
|
||||||
nanos += 1_000_000_000;
|
|
||||||
seconds -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ref_atime_secs = atime.unix_seconds();
|
let ref_atime_secs = atime.unix_seconds();
|
||||||
let ref_atime_nanos = atime.nanoseconds();
|
let ref_atime_nanos = atime.nanoseconds();
|
||||||
|
@ -120,12 +116,12 @@ 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())
|
||||||
};
|
};
|
||||||
(timestamp, timestamp)
|
(timestamp, timestamp)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -243,7 +239,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 +251,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 +284,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)
|
||||||
|
@ -299,7 +295,7 @@ pub fn uu_app() -> Command {
|
||||||
equivalent to -m",
|
equivalent to -m",
|
||||||
)
|
)
|
||||||
.value_name("WORD")
|
.value_name("WORD")
|
||||||
.value_parser(["access", "atime", "use"]),
|
.value_parser(["access", "atime", "use", "modify", "mtime"]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(ARG_FILES)
|
Arg::new(ARG_FILES)
|
||||||
|
@ -311,7 +307,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,
|
||||||
])
|
])
|
||||||
|
@ -445,9 +441,13 @@ fn parse_date(s: &str) -> UResult<FileTime> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(duration) = humantime_to_duration::from_str(s) {
|
if let Ok(duration) = parse_datetime::from_str(s) {
|
||||||
let now_local = time::OffsetDateTime::now_local().unwrap();
|
let now_local = time::OffsetDateTime::now_local().unwrap();
|
||||||
let diff = now_local.checked_add(duration).unwrap();
|
let diff = now_local
|
||||||
|
.checked_add(time::Duration::nanoseconds(
|
||||||
|
duration.num_nanoseconds().unwrap(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
return Ok(local_dt_to_filetime(diff));
|
return Ok(local_dt_to_filetime(diff));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub use crate::mods::version_cmp;
|
||||||
pub use crate::parser::parse_glob;
|
pub use crate::parser::parse_glob;
|
||||||
pub use crate::parser::parse_size;
|
pub use crate::parser::parse_size;
|
||||||
pub use crate::parser::parse_time;
|
pub use crate::parser::parse_time;
|
||||||
|
pub use crate::parser::shortcut_value_parser;
|
||||||
|
|
||||||
// * feature-gated modules
|
// * feature-gated modules
|
||||||
#[cfg(feature = "encoding")]
|
#[cfg(feature = "encoding")]
|
||||||
|
|
|
@ -438,6 +438,32 @@ fn existing_backup_path(path: &Path, suffix: &str) -> PathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the source file is likely to be the simple backup file for the target file.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `source` - A Path reference that holds the source (backup) file path.
|
||||||
|
/// * `target` - A Path reference that holds the target file path.
|
||||||
|
/// * `suffix` - Str that holds the backup suffix.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::path::Path;
|
||||||
|
/// use uucore::backup_control::source_is_target_backup;
|
||||||
|
/// let source = Path::new("data.txt~");
|
||||||
|
/// let target = Path::new("data.txt");
|
||||||
|
/// let suffix = String::from("~");
|
||||||
|
///
|
||||||
|
/// assert_eq!(source_is_target_backup(&source, &target, &suffix), true);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub fn source_is_target_backup(source: &Path, target: &Path, suffix: &str) -> bool {
|
||||||
|
let source_filename = source.to_string_lossy();
|
||||||
|
let target_backup_filename = format!("{}{suffix}", target.to_string_lossy());
|
||||||
|
source_filename == target_backup_filename
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Tests for this module
|
// Tests for this module
|
||||||
//
|
//
|
||||||
|
@ -626,4 +652,30 @@ mod tests {
|
||||||
let result = determine_backup_suffix(&matches);
|
let result = determine_backup_suffix(&matches);
|
||||||
assert_eq!(result, "-v");
|
assert_eq!(result, "-v");
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_source_is_target_backup() {
|
||||||
|
let source = Path::new("data.txt.bak");
|
||||||
|
let target = Path::new("data.txt");
|
||||||
|
let suffix = String::from(".bak");
|
||||||
|
|
||||||
|
assert!(source_is_target_backup(&source, &target, &suffix));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_source_is_not_target_backup() {
|
||||||
|
let source = Path::new("data.txt");
|
||||||
|
let target = Path::new("backup.txt");
|
||||||
|
let suffix = String::from(".bak");
|
||||||
|
|
||||||
|
assert!(!source_is_target_backup(&source, &target, &suffix));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_source_is_target_backup_with_tilde_suffix() {
|
||||||
|
let source = Path::new("example~");
|
||||||
|
let target = Path::new("example");
|
||||||
|
let suffix = String::from("~");
|
||||||
|
|
||||||
|
assert!(source_is_target_backup(&source, &target, &suffix));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod parse_glob;
|
pub mod parse_glob;
|
||||||
pub mod parse_size;
|
pub mod parse_size;
|
||||||
pub mod parse_time;
|
pub mod parse_time;
|
||||||
|
pub mod shortcut_value_parser;
|
||||||
|
|
141
src/uucore/src/lib/parser/shortcut_value_parser.rs
Normal file
141
src/uucore/src/lib/parser/shortcut_value_parser.rs
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
use clap::{
|
||||||
|
builder::{PossibleValue, TypedValueParser},
|
||||||
|
error::{ContextKind, ContextValue, ErrorKind},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ShortcutValueParser(Vec<PossibleValue>);
|
||||||
|
|
||||||
|
/// `ShortcutValueParser` is similar to clap's `PossibleValuesParser`: it verifies that the value is
|
||||||
|
/// from an enumerated set of `PossibleValue`.
|
||||||
|
///
|
||||||
|
/// Whereas `PossibleValuesParser` only accepts exact matches, `ShortcutValueParser` also accepts
|
||||||
|
/// shortcuts as long as they are unambiguous.
|
||||||
|
impl ShortcutValueParser {
|
||||||
|
pub fn new(values: impl Into<Self>) -> Self {
|
||||||
|
values.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypedValueParser for ShortcutValueParser {
|
||||||
|
type Value = String;
|
||||||
|
|
||||||
|
fn parse_ref(
|
||||||
|
&self,
|
||||||
|
cmd: &clap::Command,
|
||||||
|
arg: Option<&clap::Arg>,
|
||||||
|
value: &std::ffi::OsStr,
|
||||||
|
) -> Result<Self::Value, clap::Error> {
|
||||||
|
let value = value
|
||||||
|
.to_str()
|
||||||
|
.ok_or(clap::Error::new(ErrorKind::InvalidUtf8))?;
|
||||||
|
|
||||||
|
let matched_values: Vec<_> = self
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.filter(|x| x.get_name().starts_with(value))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if matched_values.len() == 1 {
|
||||||
|
Ok(matched_values[0].get_name().to_string())
|
||||||
|
} else {
|
||||||
|
let mut err = clap::Error::new(ErrorKind::InvalidValue).with_cmd(cmd);
|
||||||
|
|
||||||
|
if let Some(arg) = arg {
|
||||||
|
err.insert(
|
||||||
|
ContextKind::InvalidArg,
|
||||||
|
ContextValue::String(arg.to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
err.insert(
|
||||||
|
ContextKind::InvalidValue,
|
||||||
|
ContextValue::String(value.to_string()),
|
||||||
|
);
|
||||||
|
|
||||||
|
err.insert(
|
||||||
|
ContextKind::ValidValue,
|
||||||
|
ContextValue::Strings(self.0.iter().map(|x| x.get_name().to_string()).collect()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
|
||||||
|
Some(Box::new(self.0.iter().cloned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, T> From<I> for ShortcutValueParser
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = T>,
|
||||||
|
T: Into<PossibleValue>,
|
||||||
|
{
|
||||||
|
fn from(values: I) -> Self {
|
||||||
|
Self(values.into_iter().map(|t| t.into()).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
use clap::{builder::TypedValueParser, error::ErrorKind, Command};
|
||||||
|
|
||||||
|
use super::ShortcutValueParser;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_ref() {
|
||||||
|
let cmd = Command::new("cmd");
|
||||||
|
let parser = ShortcutValueParser::new(["abcd"]);
|
||||||
|
let values = ["a", "ab", "abc", "abcd"];
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
let result = parser.parse_ref(&cmd, None, OsStr::new(value));
|
||||||
|
assert_eq!("abcd", result.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_ref_with_invalid_value() {
|
||||||
|
let cmd = Command::new("cmd");
|
||||||
|
let parser = ShortcutValueParser::new(["abcd"]);
|
||||||
|
let invalid_values = ["e", "abe", "abcde"];
|
||||||
|
|
||||||
|
for invalid_value in invalid_values {
|
||||||
|
let result = parser.parse_ref(&cmd, None, OsStr::new(invalid_value));
|
||||||
|
assert_eq!(ErrorKind::InvalidValue, result.unwrap_err().kind());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_ref_with_ambiguous_value() {
|
||||||
|
let cmd = Command::new("cmd");
|
||||||
|
let parser = ShortcutValueParser::new(["abcd", "abef"]);
|
||||||
|
let ambiguous_values = ["a", "ab"];
|
||||||
|
|
||||||
|
for ambiguous_value in ambiguous_values {
|
||||||
|
let result = parser.parse_ref(&cmd, None, OsStr::new(ambiguous_value));
|
||||||
|
assert_eq!(ErrorKind::InvalidValue, result.unwrap_err().kind());
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = parser.parse_ref(&cmd, None, OsStr::new("abc"));
|
||||||
|
assert_eq!("abcd", result.unwrap());
|
||||||
|
|
||||||
|
let result = parser.parse_ref(&cmd, None, OsStr::new("abe"));
|
||||||
|
assert_eq!("abef", result.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn test_parse_ref_with_invalid_utf8() {
|
||||||
|
use std::os::unix::prelude::OsStrExt;
|
||||||
|
|
||||||
|
let parser = ShortcutValueParser::new(["abcd"]);
|
||||||
|
let cmd = Command::new("cmd");
|
||||||
|
|
||||||
|
let result = parser.parse_ref(&cmd, None, OsStr::from_bytes(&[0xc3 as u8, 0x28 as u8]));
|
||||||
|
assert_eq!(ErrorKind::InvalidUtf8, result.unwrap_err().kind());
|
||||||
|
}
|
||||||
|
}
|
|
@ -158,6 +158,20 @@ fn test_cp_recurse() {
|
||||||
assert_eq!(at.read(TEST_COPY_TO_FOLDER_NEW_FILE), "Hello, World!\n");
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_NEW_FILE), "Hello, World!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
fn test_cp_recurse_several() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
ucmd.arg("-r")
|
||||||
|
.arg("-r")
|
||||||
|
.arg(TEST_COPY_FROM_FOLDER)
|
||||||
|
.arg(TEST_COPY_TO_FOLDER_NEW)
|
||||||
|
.succeeds();
|
||||||
|
|
||||||
|
// Check the content of the destination file that was copied.
|
||||||
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_NEW_FILE), "Hello, World!\n");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cp_with_dirs_t() {
|
fn test_cp_with_dirs_t() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
@ -1110,12 +1124,12 @@ fn test_cp_parents_with_permissions_copy_file() {
|
||||||
at.mkdir_all("p1/p2");
|
at.mkdir_all("p1/p2");
|
||||||
at.touch(file);
|
at.touch(file);
|
||||||
|
|
||||||
let p1_mode = 0o0777;
|
|
||||||
let p2_mode = 0o0711;
|
|
||||||
let file_mode = 0o0702;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
|
let p1_mode = 0o0777;
|
||||||
|
let p2_mode = 0o0711;
|
||||||
|
let file_mode = 0o0702;
|
||||||
|
|
||||||
at.set_mode("p1", p1_mode);
|
at.set_mode("p1", p1_mode);
|
||||||
at.set_mode("p1/p2", p2_mode);
|
at.set_mode("p1/p2", p2_mode);
|
||||||
at.set_mode(file, file_mode);
|
at.set_mode(file, file_mode);
|
||||||
|
@ -1151,12 +1165,12 @@ fn test_cp_parents_with_permissions_copy_dir() {
|
||||||
at.mkdir_all(dir2);
|
at.mkdir_all(dir2);
|
||||||
at.touch(file);
|
at.touch(file);
|
||||||
|
|
||||||
let p1_mode = 0o0777;
|
|
||||||
let p2_mode = 0o0711;
|
|
||||||
let file_mode = 0o0702;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
|
let p1_mode = 0o0777;
|
||||||
|
let p2_mode = 0o0711;
|
||||||
|
let file_mode = 0o0702;
|
||||||
|
|
||||||
at.set_mode("p1", p1_mode);
|
at.set_mode("p1", p1_mode);
|
||||||
at.set_mode("p1/p2", p2_mode);
|
at.set_mode("p1/p2", p2_mode);
|
||||||
at.set_mode(file, file_mode);
|
at.set_mode(file, file_mode);
|
||||||
|
@ -2345,12 +2359,8 @@ fn test_dir_recursive_copy() {
|
||||||
let scene = TestScenario::new(util_name!());
|
let scene = TestScenario::new(util_name!());
|
||||||
let at = &scene.fixtures;
|
let at = &scene.fixtures;
|
||||||
|
|
||||||
at.mkdir("parent1");
|
at.mkdir_all("parent1/child");
|
||||||
at.mkdir("parent2");
|
at.mkdir_all("parent2/child1/child2/child3");
|
||||||
at.mkdir("parent1/child");
|
|
||||||
at.mkdir("parent2/child1");
|
|
||||||
at.mkdir("parent2/child1/child2");
|
|
||||||
at.mkdir("parent2/child1/child2/child3");
|
|
||||||
|
|
||||||
// case-1: copy parent1 -> parent1: should fail
|
// case-1: copy parent1 -> parent1: should fail
|
||||||
scene
|
scene
|
||||||
|
@ -2724,7 +2734,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"));
|
||||||
|
|
||||||
|
@ -3127,25 +3137,39 @@ fn test_cp_debug_sparse_auto() {
|
||||||
let ts = TestScenario::new(util_name!());
|
let ts = TestScenario::new(util_name!());
|
||||||
let at = &ts.fixtures;
|
let at = &ts.fixtures;
|
||||||
at.touch("a");
|
at.touch("a");
|
||||||
let result = ts
|
|
||||||
.ucmd()
|
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||||
|
ts.ucmd()
|
||||||
.arg("--debug")
|
.arg("--debug")
|
||||||
.arg("--sparse=auto")
|
.arg("--sparse=auto")
|
||||||
.arg("a")
|
.arg("a")
|
||||||
.arg("b")
|
.arg("b")
|
||||||
.succeeds();
|
.succeeds();
|
||||||
let stdout_str = result.stdout_str();
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
if !stdout_str
|
|
||||||
.contains("copy offload: unknown, reflink: unsupported, sparse detection: unsupported")
|
|
||||||
{
|
{
|
||||||
panic!("Failure: stdout was \n{stdout_str}");
|
let result = ts
|
||||||
}
|
.ucmd()
|
||||||
|
.arg("--debug")
|
||||||
|
.arg("--sparse=auto")
|
||||||
|
.arg("a")
|
||||||
|
.arg("b")
|
||||||
|
.succeeds();
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
let stdout_str = result.stdout_str();
|
||||||
if !stdout_str.contains("copy offload: unknown, reflink: unsupported, sparse detection: no") {
|
|
||||||
panic!("Failure: stdout was \n{stdout_str}");
|
#[cfg(target_os = "macos")]
|
||||||
|
if !stdout_str
|
||||||
|
.contains("copy offload: unknown, reflink: unsupported, sparse detection: unsupported")
|
||||||
|
{
|
||||||
|
panic!("Failure: stdout was \n{stdout_str}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
if !stdout_str.contains("copy offload: unknown, reflink: unsupported, sparse detection: no")
|
||||||
|
{
|
||||||
|
panic!("Failure: stdout was \n{stdout_str}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -258,7 +258,7 @@ fn test_type_option() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(target_os = "freebsd"))] // FIXME: fix this test for FreeBSD
|
#[cfg(not(any(target_os = "freebsd", target_os = "windows")))] // FIXME: fix test for FreeBSD & Win
|
||||||
fn test_type_option_with_file() {
|
fn test_type_option_with_file() {
|
||||||
let fs_type = new_ucmd!()
|
let fs_type = new_ucmd!()
|
||||||
.args(&["--output=fstype", "."])
|
.args(&["--output=fstype", "."])
|
||||||
|
@ -806,7 +806,7 @@ fn test_output_file_all_filesystems() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(target_os = "freebsd"))] // FIXME: fix this test for FreeBSD
|
#[cfg(not(any(target_os = "freebsd", target_os = "windows")))] // FIXME: fix test for FreeBSD & Win
|
||||||
fn test_output_file_specific_files() {
|
fn test_output_file_specific_files() {
|
||||||
// Create three files.
|
// Create three files.
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
@ -825,7 +825,7 @@ fn test_output_file_specific_files() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(target_os = "freebsd"))] // FIXME: fix this test for FreeBSD
|
#[cfg(not(any(target_os = "freebsd", target_os = "windows")))] // FIXME: fix test for FreeBSD & Win
|
||||||
fn test_file_column_width_if_filename_contains_unicode_chars() {
|
fn test_file_column_width_if_filename_contains_unicode_chars() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
at.touch("äöü.txt");
|
at.touch("äöü.txt");
|
||||||
|
@ -848,7 +848,7 @@ fn test_output_field_no_more_than_once() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(target_os = "freebsd"))] // FIXME: fix this test for FreeBSD
|
#[cfg(not(any(target_os = "freebsd", target_os = "windows")))] // FIXME: fix test for FreeBSD & Win
|
||||||
fn test_nonexistent_file() {
|
fn test_nonexistent_file() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.arg("does-not-exist")
|
.arg("does-not-exist")
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
// spell-checker:ignore (paths) sublink subwords azerty azeaze xcwww azeaz amaz azea qzerty tazerty tsublink
|
// spell-checker:ignore (paths) sublink subwords azerty azeaze xcwww azeaz amaz azea qzerty tazerty tsublink
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
#[cfg(not(windows))]
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
@ -580,97 +581,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")))]
|
#[cfg(target_os = "windows")]
|
||||||
_du_apparent_size(result.stdout_str());
|
{
|
||||||
}
|
result.stdout_contains_line("1\ta\\b\\file2");
|
||||||
|
result.stdout_contains_line("1\ta\\b\\file1");
|
||||||
#[cfg(target_os = "windows")]
|
result.stdout_contains_line("1\ta\\b");
|
||||||
fn _du_apparent_size(s: &str) {
|
result.stdout_contains_line("1\ta");
|
||||||
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.
|
|
||||||
"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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]
|
||||||
|
|
|
@ -2312,56 +2312,15 @@ fn test_ls_version_sort() {
|
||||||
let scene = TestScenario::new(util_name!());
|
let scene = TestScenario::new(util_name!());
|
||||||
let at = &scene.fixtures;
|
let at = &scene.fixtures;
|
||||||
for filename in [
|
for filename in [
|
||||||
"a2",
|
"a2", "b1", "b20", "a1.4", "b3", "b11", "b20b", "b20a", "a100", "a1.13", "aa", "a1", "aaa",
|
||||||
"b1",
|
"abab", "ab", "a01.40", "a001.001",
|
||||||
"b20",
|
|
||||||
"a1.4",
|
|
||||||
"a1.40",
|
|
||||||
"b3",
|
|
||||||
"b11",
|
|
||||||
"b20b",
|
|
||||||
"b20a",
|
|
||||||
"a100",
|
|
||||||
"a1.13",
|
|
||||||
"aa",
|
|
||||||
"a1",
|
|
||||||
"aaa",
|
|
||||||
"a1.00000040",
|
|
||||||
"abab",
|
|
||||||
"ab",
|
|
||||||
"a01.40",
|
|
||||||
"a001.001",
|
|
||||||
"a01.0000001",
|
|
||||||
"a01.001",
|
|
||||||
"a001.01",
|
|
||||||
] {
|
] {
|
||||||
at.touch(filename);
|
at.touch(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut expected = vec![
|
let mut expected = vec![
|
||||||
"a1",
|
"a1", "a001.001", "a1.4", "a1.13", "a01.40", "a2", "a100", "aa", "aaa", "ab", "abab", "b1",
|
||||||
"a001.001",
|
"b3", "b11", "b20", "b20a", "b20b", "", // because of '\n' at the end of the output
|
||||||
"a001.01",
|
|
||||||
"a01.0000001",
|
|
||||||
"a01.001",
|
|
||||||
"a1.4",
|
|
||||||
"a1.13",
|
|
||||||
"a01.40",
|
|
||||||
"a1.00000040",
|
|
||||||
"a1.40",
|
|
||||||
"a2",
|
|
||||||
"a100",
|
|
||||||
"aa",
|
|
||||||
"aaa",
|
|
||||||
"ab",
|
|
||||||
"abab",
|
|
||||||
"b1",
|
|
||||||
"b3",
|
|
||||||
"b11",
|
|
||||||
"b20",
|
|
||||||
"b20a",
|
|
||||||
"b20b",
|
|
||||||
"", // because of '\n' at the end of the output
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let result = scene.ucmd().arg("-1v").succeeds();
|
let result = scene.ucmd().arg("-1v").succeeds();
|
||||||
|
@ -3137,6 +3096,16 @@ fn test_ls_dangling_symlinks() {
|
||||||
.stderr_contains("No such file or directory")
|
.stderr_contains("No such file or directory")
|
||||||
.stdout_contains(if cfg!(windows) { "dangle" } else { "? dangle" });
|
.stdout_contains(if cfg!(windows) { "dangle" } else { "? dangle" });
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("-LZ")
|
||||||
|
.arg("temp_dir")
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_contains("cannot access")
|
||||||
|
.stderr_contains("No such file or directory")
|
||||||
|
.stdout_contains(if cfg!(windows) { "dangle" } else { "? dangle" });
|
||||||
|
|
||||||
scene
|
scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.arg("-Ll")
|
.arg("-Ll")
|
||||||
|
|
|
@ -21,9 +21,15 @@ fn test_valid_arg() {
|
||||||
new_ucmd!().arg("-s").succeeds();
|
new_ucmd!().arg("-s").succeeds();
|
||||||
new_ucmd!().arg("--squeeze").succeeds();
|
new_ucmd!().arg("--squeeze").succeeds();
|
||||||
|
|
||||||
|
new_ucmd!().arg("-u").succeeds();
|
||||||
|
new_ucmd!().arg("--plain").succeeds();
|
||||||
|
|
||||||
new_ucmd!().arg("-n").arg("10").succeeds();
|
new_ucmd!().arg("-n").arg("10").succeeds();
|
||||||
new_ucmd!().arg("--lines").arg("0").succeeds();
|
new_ucmd!().arg("--lines").arg("0").succeeds();
|
||||||
new_ucmd!().arg("--number").arg("0").succeeds();
|
new_ucmd!().arg("--number").arg("0").succeeds();
|
||||||
|
|
||||||
|
new_ucmd!().arg("-F").arg("10").succeeds();
|
||||||
|
new_ucmd!().arg("--from-line").arg("0").succeeds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +40,42 @@ fn test_invalid_arg() {
|
||||||
|
|
||||||
new_ucmd!().arg("--lines").arg("-10").fails();
|
new_ucmd!().arg("--lines").arg("-10").fails();
|
||||||
new_ucmd!().arg("--number").arg("-10").fails();
|
new_ucmd!().arg("--number").arg("-10").fails();
|
||||||
|
|
||||||
|
new_ucmd!().arg("--from-line").arg("-10").fails();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_argument_from_file() {
|
||||||
|
if std::io::stdout().is_terminal() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
|
||||||
|
let file = "test_file";
|
||||||
|
|
||||||
|
at.write(file, "1\n2");
|
||||||
|
|
||||||
|
// output all lines
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("-F")
|
||||||
|
.arg("0")
|
||||||
|
.arg(file)
|
||||||
|
.succeeds()
|
||||||
|
.no_stderr()
|
||||||
|
.stdout_contains("1")
|
||||||
|
.stdout_contains("2");
|
||||||
|
|
||||||
|
// output only the second line
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("-F")
|
||||||
|
.arg("2")
|
||||||
|
.arg(file)
|
||||||
|
.succeeds()
|
||||||
|
.no_stderr()
|
||||||
|
.stdout_contains("2")
|
||||||
|
.stdout_does_not_contain("1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -510,6 +510,22 @@ fn test_mv_same_hardlink_backup_simple() {
|
||||||
.succeeds();
|
.succeeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(all(unix, not(target_os = "android")))]
|
||||||
|
fn test_mv_same_hardlink_backup_simple_destroy() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let file_a = "test_mv_same_file_a~";
|
||||||
|
let file_b = "test_mv_same_file_a";
|
||||||
|
at.touch(file_a);
|
||||||
|
at.touch(file_b);
|
||||||
|
|
||||||
|
ucmd.arg(file_a)
|
||||||
|
.arg(file_b)
|
||||||
|
.arg("--b=simple")
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("backing up 'test_mv_same_file_a' might destroy source");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mv_same_file_not_dot_dir() {
|
fn test_mv_same_file_not_dot_dir() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
|
|
@ -71,3 +71,10 @@ fn test_sections_and_styles() {
|
||||||
}
|
}
|
||||||
// spell-checker:enable
|
// spell-checker:enable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_no_renumber() {
|
||||||
|
for arg in ["-p", "--no-renumber"] {
|
||||||
|
new_ucmd!().arg(arg).succeeds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -666,8 +666,79 @@ fn test_invalid_stdin_number_in_middle_of_input() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_argument_number_returns_status_2() {
|
fn test_invalid_stdin_number_with_warn_returns_status_0() {
|
||||||
new_ucmd!().args(&["hello"]).fails().code_is(2);
|
new_ucmd!()
|
||||||
|
.args(&["--invalid=warn"])
|
||||||
|
.pipe_in("4Q")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("4Q\n")
|
||||||
|
.stderr_is("numfmt: invalid suffix in input: '4Q'\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_stdin_number_with_ignore_returns_status_0() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--invalid=ignore"])
|
||||||
|
.pipe_in("4Q")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only("4Q\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_stdin_number_with_abort_returns_status_2() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--invalid=abort"])
|
||||||
|
.pipe_in("4Q")
|
||||||
|
.fails()
|
||||||
|
.code_is(2)
|
||||||
|
.stderr_only("numfmt: invalid suffix in input: '4Q'\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_stdin_number_with_fail_returns_status_2() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--invalid=fail"])
|
||||||
|
.pipe_in("4Q")
|
||||||
|
.fails()
|
||||||
|
.code_is(2)
|
||||||
|
.stdout_is("4Q\n")
|
||||||
|
.stderr_is("numfmt: invalid suffix in input: '4Q'\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_arg_number_with_warn_returns_status_0() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--invalid=warn", "4Q"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("4Q\n")
|
||||||
|
.stderr_is("numfmt: invalid suffix in input: '4Q'\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_arg_number_with_ignore_returns_status_0() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--invalid=ignore", "4Q"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only("4Q\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_arg_number_with_abort_returns_status_2() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--invalid=abort", "4Q"])
|
||||||
|
.fails()
|
||||||
|
.code_is(2)
|
||||||
|
.stderr_only("numfmt: invalid suffix in input: '4Q'\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_arg_number_with_fail_returns_status_2() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--invalid=fail", "4Q"])
|
||||||
|
.fails()
|
||||||
|
.code_is(2)
|
||||||
|
.stdout_is("4Q\n")
|
||||||
|
.stderr_is("numfmt: invalid suffix in input: '4Q'\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -208,10 +208,13 @@ fn test_separator_and_terminator() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_equalize_widths() {
|
fn test_equalize_widths() {
|
||||||
new_ucmd!()
|
let args = ["-w", "--equal-width"];
|
||||||
.args(&["-w", "5", "10"])
|
for arg in args {
|
||||||
.run()
|
new_ucmd!()
|
||||||
.stdout_is("05\n06\n07\n08\n09\n10\n");
|
.args(&[arg, "5", "10"])
|
||||||
|
.run()
|
||||||
|
.stdout_is("05\n06\n07\n08\n09\n10\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -758,3 +758,36 @@ fn test_round_robin() {
|
||||||
assert_eq!(file_read("xaa"), "1\n3\n5\n");
|
assert_eq!(file_read("xaa"), "1\n3\n5\n");
|
||||||
assert_eq!(file_read("xab"), "2\n4\n");
|
assert_eq!(file_read("xab"), "2\n4\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_split_invalid_input() {
|
||||||
|
// Test if stdout/stderr for '--lines' option is correct
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
at.touch("file");
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["--lines", "0", "file"])
|
||||||
|
.fails()
|
||||||
|
.no_stdout()
|
||||||
|
.stderr_contains("split: invalid number of lines: 0");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["-C", "0", "file"])
|
||||||
|
.fails()
|
||||||
|
.no_stdout()
|
||||||
|
.stderr_contains("split: invalid number of bytes: 0");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["-b", "0", "file"])
|
||||||
|
.fails()
|
||||||
|
.no_stdout()
|
||||||
|
.stderr_contains("split: invalid number of bytes: 0");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["-n", "0", "file"])
|
||||||
|
.fails()
|
||||||
|
.no_stdout()
|
||||||
|
.stderr_contains("split: invalid number of chunks: 0");
|
||||||
|
}
|
||||||
|
|
|
@ -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"))]
|
||||||
|
|
|
@ -145,12 +145,8 @@ fn test_stdin_redirect_offset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(all(not(target_vendor = "apple"), not(target_os = "windows")))] // FIXME: for currently not working platforms
|
#[cfg(all(not(target_vendor = "apple")))] // FIXME: for currently not working platforms
|
||||||
fn test_stdin_redirect_offset2() {
|
fn test_stdin_redirect_offset2() {
|
||||||
// FIXME: windows: Failed because of difference in printed header. See below.
|
|
||||||
// actual : ==> - <==
|
|
||||||
// expected: ==> standard input <==
|
|
||||||
|
|
||||||
// like test_stdin_redirect_offset but with multiple files
|
// like test_stdin_redirect_offset but with multiple files
|
||||||
|
|
||||||
let ts = TestScenario::new(util_name!());
|
let ts = TestScenario::new(util_name!());
|
||||||
|
|
|
@ -204,19 +204,23 @@ fn test_touch_set_cymdhms_time() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_touch_set_only_atime() {
|
fn test_touch_set_only_atime() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let atime_args = ["-a", "--time=access", "--time=atime", "--time=use"];
|
||||||
let file = "test_touch_set_only_atime";
|
let file = "test_touch_set_only_atime";
|
||||||
|
|
||||||
ucmd.args(&["-t", "201501011234", "-a", file])
|
for atime_arg in atime_args {
|
||||||
.succeeds()
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
.no_stderr();
|
|
||||||
|
|
||||||
assert!(at.file_exists(file));
|
ucmd.args(&["-t", "201501011234", atime_arg, file])
|
||||||
|
.succeeds()
|
||||||
|
.no_stderr();
|
||||||
|
|
||||||
let start_of_year = str_to_filetime("%Y%m%d%H%M", "201501010000");
|
assert!(at.file_exists(file));
|
||||||
let (atime, mtime) = get_file_times(&at, file);
|
|
||||||
assert!(atime != mtime);
|
let start_of_year = str_to_filetime("%Y%m%d%H%M", "201501010000");
|
||||||
assert_eq!(atime.unix_seconds() - start_of_year.unix_seconds(), 45240);
|
let (atime, mtime) = get_file_times(&at, file);
|
||||||
|
assert!(atime != mtime);
|
||||||
|
assert_eq!(atime.unix_seconds() - start_of_year.unix_seconds(), 45240);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -301,19 +305,23 @@ fn test_touch_set_both_time_and_date() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_touch_set_only_mtime() {
|
fn test_touch_set_only_mtime() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let mtime_args = ["-m", "--time=modify", "--time=mtime"];
|
||||||
let file = "test_touch_set_only_mtime";
|
let file = "test_touch_set_only_mtime";
|
||||||
|
|
||||||
ucmd.args(&["-t", "201501011234", "-m", file])
|
for mtime_arg in mtime_args {
|
||||||
.succeeds()
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
.no_stderr();
|
|
||||||
|
|
||||||
assert!(at.file_exists(file));
|
ucmd.args(&["-t", "201501011234", mtime_arg, file])
|
||||||
|
.succeeds()
|
||||||
|
.no_stderr();
|
||||||
|
|
||||||
let start_of_year = str_to_filetime("%Y%m%d%H%M", "201501010000");
|
assert!(at.file_exists(file));
|
||||||
let (atime, mtime) = get_file_times(&at, file);
|
|
||||||
assert!(atime != mtime);
|
let start_of_year = str_to_filetime("%Y%m%d%H%M", "201501010000");
|
||||||
assert_eq!(mtime.unix_seconds() - start_of_year.unix_seconds(), 45240);
|
let (atime, mtime) = get_file_times(&at, file);
|
||||||
|
assert!(atime != mtime);
|
||||||
|
assert_eq!(mtime.unix_seconds() - start_of_year.unix_seconds(), 45240);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
|
@ -11,28 +11,31 @@ ME="${0}"
|
||||||
ME_dir="$(dirname -- "$(readlink -fm -- "${ME}")")"
|
ME_dir="$(dirname -- "$(readlink -fm -- "${ME}")")"
|
||||||
REPO_main_dir="$(dirname -- "${ME_dir}")"
|
REPO_main_dir="$(dirname -- "${ME_dir}")"
|
||||||
|
|
||||||
echo "ME='${ME}'"
|
|
||||||
echo "ME_dir='${ME_dir}'"
|
|
||||||
echo "REPO_main_dir='${REPO_main_dir}'"
|
|
||||||
|
|
||||||
### * config (from environment with fallback defaults); note: GNU is expected to be a sibling repo directory
|
### * config (from environment with fallback defaults); note: GNU is expected to be a sibling repo directory
|
||||||
|
|
||||||
path_UUTILS=${path_UUTILS:-${REPO_main_dir}}
|
path_UUTILS=${path_UUTILS:-${REPO_main_dir}}
|
||||||
path_GNU="$(readlink -fm -- "${path_GNU:-${path_UUTILS}/../gnu}")"
|
path_GNU="$(readlink -fm -- "${path_GNU:-${path_UUTILS}/../gnu}")"
|
||||||
|
|
||||||
echo "path_UUTILS='${path_UUTILS}'"
|
|
||||||
echo "path_GNU='${path_GNU}'"
|
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
if test ! -d "${path_GNU}"; then
|
if test ! -d "${path_GNU}"; then
|
||||||
echo "Could not find GNU (expected at '${path_GNU}')"
|
echo "Could not find GNU coreutils (expected at '${path_GNU}')"
|
||||||
|
echo "Run the following to download into the expected path:"
|
||||||
echo "git clone --recurse-submodules https://github.com/coreutils/coreutils.git \"${path_GNU}\""
|
echo "git clone --recurse-submodules https://github.com/coreutils/coreutils.git \"${path_GNU}\""
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
|
echo "ME='${ME}'"
|
||||||
|
echo "ME_dir='${ME_dir}'"
|
||||||
|
echo "REPO_main_dir='${REPO_main_dir}'"
|
||||||
|
|
||||||
|
echo "path_UUTILS='${path_UUTILS}'"
|
||||||
|
echo "path_GNU='${path_GNU}'"
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
UU_MAKE_PROFILE=${UU_MAKE_PROFILE:-release}
|
UU_MAKE_PROFILE=${UU_MAKE_PROFILE:-release}
|
||||||
echo "UU_MAKE_PROFILE='${UU_MAKE_PROFILE}'"
|
echo "UU_MAKE_PROFILE='${UU_MAKE_PROFILE}'"
|
||||||
|
|
||||||
|
@ -232,3 +235,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