mirror of
https://github.com/uutils/coreutils
synced 2024-11-16 01:38:04 +00:00
Merge branch 'main' into mkdir-fix
This commit is contained in:
commit
9dea9b4b83
246 changed files with 2840 additions and 942 deletions
7
.github/dependabot.yml
vendored
Normal file
7
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 5
|
68
.github/workflows/CICD.yml
vendored
68
.github/workflows/CICD.yml
vendored
|
@ -5,7 +5,7 @@ name: CICD
|
|||
# spell-checker:ignore (jargon) SHAs deps dequote softprops subshell toolchain
|
||||
# spell-checker:ignore (names) CodeCOV MacOS MinGW Peltoche rivy
|
||||
# spell-checker:ignore (shell/tools) choco clippy dmake dpkg esac fakeroot gmake grcov halium lcov libssl mkdir popd printf pushd rsync rustc rustfmt rustup shopt xargs
|
||||
# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend pell runtest tempfile testsuite uutils DESTDIR sizemulti Swatinem
|
||||
# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend pell runtest tempfile testsuite uutils DESTDIR multisize Swatinem
|
||||
|
||||
# ToDO: [2021-06; rivy] change from `cargo-tree` to `cargo tree` once MSRV is >= 1.45
|
||||
|
||||
|
@ -433,6 +433,58 @@ jobs:
|
|||
make test
|
||||
|
||||
|
||||
build_rust_stable:
|
||||
name: Build/stable
|
||||
needs: [ min_version, deps ]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
- { os: macos-latest , features: feat_os_macos }
|
||||
- { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install `rust` toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
default: true
|
||||
profile: minimal # minimal component installation (ie, no documentation)
|
||||
- name: Test
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
|
||||
|
||||
build_rust_nightly:
|
||||
name: Build/nightly
|
||||
needs: [ min_version, deps ]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
- { os: macos-latest , features: feat_os_macos }
|
||||
- { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install `rust` toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
default: true
|
||||
profile: minimal # minimal component installation (ie, no documentation)
|
||||
- name: Test
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
|
||||
|
||||
compute_size:
|
||||
name: Binary sizes
|
||||
needs: [ min_version, deps ]
|
||||
|
@ -473,8 +525,8 @@ jobs:
|
|||
--arg date "$(date --rfc-email)" \
|
||||
--arg sha "$GITHUB_SHA" \
|
||||
--arg size "$SIZE" \
|
||||
--arg sizemulti "$SIZEMULTI" \
|
||||
'{($date): { sha: $sha, size: $size, sizemulti: $sizemulti, }}' > size-result.json
|
||||
--arg multisize "$SIZEMULTI" \
|
||||
'{($date): { sha: $sha, size: $size, multisize: $multisize, }}' > size-result.json
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: size-result
|
||||
|
@ -505,7 +557,6 @@ jobs:
|
|||
#- { os: ubuntu-18.04 , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
|
||||
#- { os: ubuntu-18.04 , target: x86_64-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross }
|
||||
- { os: macos-latest , target: x86_64-apple-darwin , features: feat_os_macos }
|
||||
- { os: windows-latest , target: i686-pc-windows-gnu , features: feat_os_windows }
|
||||
- { os: windows-latest , target: i686-pc-windows-msvc , features: feat_os_windows }
|
||||
- { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly
|
||||
- { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows }
|
||||
|
@ -631,12 +682,8 @@ jobs:
|
|||
esac
|
||||
- name: rust toolchain ~ install
|
||||
uses: actions-rs/toolchain@v1
|
||||
# env:
|
||||
# # Override auto-detection of RAM for Rustc install.
|
||||
# # https://github.com/rust-lang/rustup/issues/2229#issuecomment-585855925
|
||||
# RUSTUP_UNPACK_RAM: "21474836480"
|
||||
with:
|
||||
toolchain: ${{ steps.vars.outputs.TOOLCHAIN }}
|
||||
toolchain: ${{ env.RUST_MIN_SRV }}
|
||||
target: ${{ matrix.job.target }}
|
||||
default: true
|
||||
profile: minimal # minimal component installation (ie, no documentation)
|
||||
|
@ -688,18 +735,21 @@ jobs:
|
|||
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
|
||||
command: build
|
||||
args: --release --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
|
||||
toolchain: ${{ env.RUST_MIN_SRV }}
|
||||
- name: Test
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
|
||||
command: test
|
||||
args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
|
||||
toolchain: ${{ env.RUST_MIN_SRV }}
|
||||
- name: Test individual utilities
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
|
||||
command: test
|
||||
args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }}
|
||||
toolchain: ${{ env.RUST_MIN_SRV }}
|
||||
- name: Archive executable artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
|
|
1
.github/workflows/GnuTests.yml
vendored
1
.github/workflows/GnuTests.yml
vendored
|
@ -201,7 +201,6 @@ jobs:
|
|||
gnu_coverage:
|
||||
name: Run GNU tests with coverage
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- name: Checkout code uutil
|
||||
uses: actions/checkout@v2
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Contributing to coreutils
|
||||
|
||||
Contributions are very welcome, and should target Rust's master branch until the
|
||||
Contributions are very welcome, and should target Rust's main branch until the
|
||||
standard libraries are stabilized. You may *claim* an item on the to-do list by
|
||||
following these steps:
|
||||
|
||||
|
|
40
Cargo.lock
generated
40
Cargo.lock
generated
|
@ -890,15 +890,6 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.4"
|
||||
|
@ -1153,9 +1144,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
|||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe3179b85e1fd8b14447cbebadb75e45a1002f541b925f0bfec366d56a81c56d"
|
||||
checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -1328,9 +1319,9 @@ checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
|
||||
|
||||
[[package]]
|
||||
name = "onig"
|
||||
|
@ -1384,9 +1375,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ouroboros"
|
||||
version = "0.10.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84236d64f1718c387232287cf036eb6632a5ecff226f4ff9dccb8c2b79ba0bde"
|
||||
checksum = "71643f290d126e18ac2598876d01e1d57aed164afc78fdb6e2a0c6589a1f6662"
|
||||
dependencies = [
|
||||
"aliasable",
|
||||
"ouroboros_macro",
|
||||
|
@ -1395,9 +1386,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ouroboros_macro"
|
||||
version = "0.10.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f463857a6eb96c0136b1d56e56c718350cef30412ec065b48294799a088bca68"
|
||||
checksum = "ed9a247206016d424fe8497bc611e510887af5c261fbbf977877c4bb55ca4d82"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"proc-macro-error",
|
||||
|
@ -2009,9 +2000,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
|||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.23.0"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb"
|
||||
checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
|
@ -2217,9 +2208,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
|
@ -2241,9 +2232,9 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
|||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7"
|
||||
checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8"
|
||||
|
||||
[[package]]
|
||||
name = "unix_socket"
|
||||
|
@ -2394,7 +2385,6 @@ dependencies = [
|
|||
"clap 3.0.10",
|
||||
"libc",
|
||||
"uucore",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2905,7 +2895,6 @@ version = "0.0.12"
|
|||
dependencies = [
|
||||
"chrono",
|
||||
"clap 3.0.10",
|
||||
"getopts",
|
||||
"itertools",
|
||||
"quick-error",
|
||||
"regex",
|
||||
|
@ -3181,6 +3170,7 @@ dependencies = [
|
|||
"filetime",
|
||||
"time",
|
||||
"uucore",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -375,7 +375,7 @@ pretty_assertions = "1"
|
|||
rand = "0.8"
|
||||
regex = "1.0"
|
||||
sha1 = { version="0.10", features=["std"] }
|
||||
tempfile = "3.2.0"
|
||||
tempfile = "3"
|
||||
time = "0.1"
|
||||
unindent = "0.1"
|
||||
uucore = { version=">=0.0.11", package="uucore", path="src/uucore", features=["entries", "process"] }
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) Jordi Boggiano
|
||||
Copyright (c) Jordi Boggiano and many others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
|
14
README.md
14
README.md
|
@ -2,7 +2,7 @@
|
|||
|
||||
[![Crates.io](https://img.shields.io/crates/v/coreutils.svg)](https://crates.io/crates/coreutils)
|
||||
[![Discord](https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat)](https://discord.gg/wQVJbvJ)
|
||||
[![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/coreutils/blob/master/LICENSE)
|
||||
[![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/coreutils/blob/main/LICENSE)
|
||||
[![LOC](https://tokei.rs/b1/github/uutils/coreutils?category=code)](https://github.com/Aaronepower/tokei)
|
||||
[![dependency status](https://deps.rs/repo/github/uutils/coreutils/status.svg)](https://deps.rs/repo/github/uutils/coreutils)
|
||||
|
||||
|
@ -410,12 +410,12 @@ To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
|
|||
| link | | |
|
||||
| ln | | |
|
||||
| logname | | |
|
||||
| ~~md5sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha1sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha224sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha256sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha384sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha512sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~md5sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha1sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha224sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha256sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha384sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha512sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| mkdir | | |
|
||||
| mkfifo | | |
|
||||
| mknod | | |
|
||||
|
|
5
renovate.json
Normal file
5
renovate.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "arch ~ (uutils) display machine architecture"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/arch"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/arch"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/arch/LICENSE
Symbolic link
1
src/uu/arch/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "base32 ~ (uutils) decode/encode input (base32-encoding)"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/base32"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/base32"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/base32/LICENSE
Symbolic link
1
src/uu/base32/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "base64 ~ (uutils) decode/encode input (base64-encoding)"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/base64"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/base64"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/base64/LICENSE
Symbolic link
1
src/uu/base64/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "basename ~ (uutils) display PATHNAME with leading directory components removed"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/basename"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/basename"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/basename/LICENSE
Symbolic link
1
src/uu/basename/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "basenc ~ (uutils) decode/encode input"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/basenc"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/basenc"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/basenc/LICENSE
Symbolic link
1
src/uu/basenc/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "cat ~ (uutils) concatenate and display input"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/cat"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cat"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/cat/LICENSE
Symbolic link
1
src/uu/cat/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -5,7 +5,7 @@ authors = ["uutils developers"]
|
|||
license = "MIT"
|
||||
description = "chcon ~ (uutils) change file security context"
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/chcon"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chcon"
|
||||
keywords = ["coreutils", "uutils", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/chcon/LICENSE
Symbolic link
1
src/uu/chcon/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "chgrp ~ (uutils) change the group ownership of FILE"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/chgrp"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chgrp"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/chgrp/LICENSE
Symbolic link
1
src/uu/chgrp/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "chmod ~ (uutils) change mode of FILE"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/chmod"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chmod"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
@ -18,7 +18,6 @@ path = "src/chmod.rs"
|
|||
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["fs", "mode"] }
|
||||
walkdir = "2.2"
|
||||
|
||||
[[bin]]
|
||||
name = "chmod"
|
||||
|
|
1
src/uu/chmod/LICENSE
Symbolic link
1
src/uu/chmod/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -18,7 +18,6 @@ use uucore::libc::mode_t;
|
|||
#[cfg(not(windows))]
|
||||
use uucore::mode;
|
||||
use uucore::{format_usage, show_error, InvalidEncodingHandling};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
static ABOUT: &str = "Change the mode of each FILE to MODE.
|
||||
With --reference, change the mode of each FILE to that of RFILE.";
|
||||
|
@ -227,9 +226,19 @@ impl Chmoder {
|
|||
if !self.recursive {
|
||||
r = self.chmod_file(file).and(r);
|
||||
} else {
|
||||
for entry in WalkDir::new(&filename).into_iter().filter_map(|e| e.ok()) {
|
||||
let file = entry.path();
|
||||
r = self.chmod_file(file).and(r);
|
||||
r = self.walk_dir(file);
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
fn walk_dir(&self, file_path: &Path) -> UResult<()> {
|
||||
let mut r = self.chmod_file(file_path);
|
||||
if !is_symlink(file_path) && file_path.is_dir() {
|
||||
for dir_entry in file_path.read_dir()? {
|
||||
let path = dir_entry?.path();
|
||||
if !is_symlink(&path) {
|
||||
r = self.walk_dir(path.as_path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "chown ~ (uutils) change the ownership of FILE"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/chown"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chown"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/chown/LICENSE
Symbolic link
1
src/uu/chown/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "chroot ~ (uutils) run COMMAND under a new root directory"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/chroot"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chroot"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/chroot/LICENSE
Symbolic link
1
src/uu/chroot/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "cksum ~ (uutils) display CRC and size of input"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/cksum"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cksum"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/cksum/LICENSE
Symbolic link
1
src/uu/cksum/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "comm ~ (uutils) compare sorted inputs"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/comm"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/comm"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/comm/LICENSE
Symbolic link
1
src/uu/comm/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -10,7 +10,7 @@ license = "MIT"
|
|||
description = "cp ~ (uutils) copy SOURCE to DESTINATION"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/cp"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cp"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/cp/LICENSE
Symbolic link
1
src/uu/cp/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -1245,7 +1245,8 @@ fn handle_existing_dest(source: &Path, dest: &Path, options: &Options) -> CopyRe
|
|||
}
|
||||
|
||||
/// Copy the a file from `source` to `dest`. `source` will be dereferenced if
|
||||
/// `options.dereference` is set to true. `dest` will always be dereferenced.
|
||||
/// `options.dereference` is set to true. `dest` will be dereferenced only if
|
||||
/// the source was not a symlink.
|
||||
///
|
||||
/// Behavior when copying to existing files is contingent on the
|
||||
/// `options.overwrite` mode. If a file is skipped, the return type
|
||||
|
@ -1295,13 +1296,32 @@ fn copy_file(
|
|||
let context = context.as_str();
|
||||
|
||||
// canonicalize dest and source so that later steps can work with the paths directly
|
||||
let dest = canonicalize(dest, MissingHandling::Missing, ResolveMode::Physical).unwrap();
|
||||
let source = if options.dereference {
|
||||
canonicalize(source, MissingHandling::Missing, ResolveMode::Physical).unwrap()
|
||||
} else {
|
||||
source.to_owned()
|
||||
};
|
||||
|
||||
let source_file_type = fs::symlink_metadata(&source).context(context)?.file_type();
|
||||
let source_is_symlink = source_file_type.is_symlink();
|
||||
|
||||
#[cfg(unix)]
|
||||
let source_is_fifo = source_file_type.is_fifo();
|
||||
#[cfg(not(unix))]
|
||||
let source_is_fifo = false;
|
||||
|
||||
let dest_already_exists_as_symlink = fs::symlink_metadata(&dest)
|
||||
.map(|meta| meta.file_type().is_symlink())
|
||||
.unwrap_or(false);
|
||||
|
||||
let dest = if !(source_is_symlink && dest_already_exists_as_symlink) {
|
||||
canonicalize(dest, MissingHandling::Missing, ResolveMode::Physical).unwrap()
|
||||
} else {
|
||||
// Don't canonicalize a symlink copied over another symlink, because
|
||||
// then we'll end up overwriting the destination's target.
|
||||
dest.to_path_buf()
|
||||
};
|
||||
|
||||
let dest_permissions = if dest.exists() {
|
||||
dest.symlink_metadata().context(context)?.permissions()
|
||||
} else {
|
||||
|
@ -1327,10 +1347,27 @@ fn copy_file(
|
|||
|
||||
match options.copy_mode {
|
||||
CopyMode::Link => {
|
||||
if dest.exists() {
|
||||
let backup_path =
|
||||
backup_control::get_backup_path(options.backup, &dest, &options.backup_suffix);
|
||||
if let Some(backup_path) = backup_path {
|
||||
backup_dest(&dest, &backup_path)?;
|
||||
fs::remove_file(&dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
fs::hard_link(&source, &dest).context(context)?;
|
||||
}
|
||||
CopyMode::Copy => {
|
||||
copy_helper(&source, &dest, options, context, symlinked_files)?;
|
||||
copy_helper(
|
||||
&source,
|
||||
&dest,
|
||||
options,
|
||||
context,
|
||||
source_is_symlink,
|
||||
source_is_fifo,
|
||||
symlinked_files,
|
||||
)?;
|
||||
}
|
||||
CopyMode::SymLink => {
|
||||
symlink_file(&source, &dest, context, symlinked_files)?;
|
||||
|
@ -1346,10 +1383,26 @@ fn copy_file(
|
|||
if src_time <= dest_time {
|
||||
return Ok(());
|
||||
} else {
|
||||
copy_helper(&source, &dest, options, context, symlinked_files)?;
|
||||
copy_helper(
|
||||
&source,
|
||||
&dest,
|
||||
options,
|
||||
context,
|
||||
source_is_symlink,
|
||||
source_is_fifo,
|
||||
symlinked_files,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
copy_helper(&source, &dest, options, context, symlinked_files)?;
|
||||
copy_helper(
|
||||
&source,
|
||||
&dest,
|
||||
options,
|
||||
context,
|
||||
source_is_symlink,
|
||||
source_is_fifo,
|
||||
symlinked_files,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
CopyMode::AttrOnly => {
|
||||
|
@ -1388,6 +1441,8 @@ fn copy_helper(
|
|||
dest: &Path,
|
||||
options: &Options,
|
||||
context: &str,
|
||||
source_is_symlink: bool,
|
||||
source_is_fifo: bool,
|
||||
symlinked_files: &mut HashSet<FileInformation>,
|
||||
) -> CopyResult<()> {
|
||||
if options.parents {
|
||||
|
@ -1395,23 +1450,15 @@ fn copy_helper(
|
|||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
let file_type = fs::symlink_metadata(&source)?.file_type();
|
||||
let is_symlink = file_type.is_symlink();
|
||||
|
||||
#[cfg(unix)]
|
||||
let is_fifo = file_type.is_fifo();
|
||||
#[cfg(not(unix))]
|
||||
let is_fifo = false;
|
||||
|
||||
if source.as_os_str() == "/dev/null" {
|
||||
/* workaround a limitation of fs::copy
|
||||
* https://github.com/rust-lang/rust/issues/79390
|
||||
*/
|
||||
File::create(dest).context(dest.display().to_string())?;
|
||||
} else if is_fifo && options.recursive {
|
||||
} else if source_is_fifo && options.recursive {
|
||||
#[cfg(unix)]
|
||||
copy_fifo(dest, options.overwrite)?;
|
||||
} else if is_symlink {
|
||||
} else if source_is_symlink {
|
||||
copy_link(source, dest, symlinked_files)?;
|
||||
} else if options.reflink_mode != ReflinkMode::Never {
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "csplit ~ (uutils) Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ..., and output byte counts of each piece to standard output"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/ls"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/csplit/LICENSE
Symbolic link
1
src/uu/csplit/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "cut ~ (uutils) display byte/field columns of input lines"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/cut"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cut"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/cut/LICENSE
Symbolic link
1
src/uu/cut/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "date ~ (uutils) display or set the current time"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/date"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/date"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/date/LICENSE
Symbolic link
1
src/uu/date/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "dd ~ (uutils) copy and convert files"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/dd"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dd"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
@ -22,7 +22,7 @@ libc = "0.2"
|
|||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "^3"
|
||||
tempfile = "3"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
signal-hook = "0.3.9"
|
||||
|
|
1
src/uu/dd/LICENSE
Symbolic link
1
src/uu/dd/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -807,15 +807,16 @@ mod tests {
|
|||
#[test]
|
||||
fn test_parse_bytes_with_opt_multiplier() {
|
||||
assert_eq!(parse_bytes_with_opt_multiplier("123").unwrap(), 123);
|
||||
assert_eq!(parse_bytes_with_opt_multiplier("123c").unwrap(), 123 * 1);
|
||||
assert_eq!(parse_bytes_with_opt_multiplier("123c").unwrap(), 123); // 123 * 1
|
||||
assert_eq!(parse_bytes_with_opt_multiplier("123w").unwrap(), 123 * 2);
|
||||
assert_eq!(parse_bytes_with_opt_multiplier("123b").unwrap(), 123 * 512);
|
||||
assert_eq!(parse_bytes_with_opt_multiplier("123x3").unwrap(), 123 * 3);
|
||||
assert_eq!(parse_bytes_with_opt_multiplier("123k").unwrap(), 123 * 1024);
|
||||
assert_eq!(parse_bytes_with_opt_multiplier("1x2x3").unwrap(), 1 * 2 * 3);
|
||||
assert_eq!(parse_bytes_with_opt_multiplier("1x2x3").unwrap(), 6); // 1 * 2 * 3
|
||||
|
||||
assert_eq!(
|
||||
parse_bytes_with_opt_multiplier("1wx2cx3w").unwrap(),
|
||||
(1 * 2) * (2 * 1) * (3 * 2)
|
||||
2 * 2 * (3 * 2) // (1 * 2) * (2 * 1) * (3 * 2)
|
||||
);
|
||||
assert!(parse_bytes_with_opt_multiplier("123asdf").is_err());
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "df ~ (uutils) display file system information"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/df"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/df"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/df/LICENSE
Symbolic link
1
src/uu/df/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
197
src/uu/df/src/blocks.rs
Normal file
197
src/uu/df/src/blocks.rs
Normal file
|
@ -0,0 +1,197 @@
|
|||
// * This file is part of the uutils coreutils package.
|
||||
// *
|
||||
// * For the full copyright and license information, please view the LICENSE
|
||||
// * file that was distributed with this source code.
|
||||
//! Types for representing and displaying block sizes.
|
||||
use crate::{OPT_BLOCKSIZE, OPT_HUMAN_READABLE, OPT_HUMAN_READABLE_2};
|
||||
use clap::ArgMatches;
|
||||
use std::fmt;
|
||||
use std::num::ParseIntError;
|
||||
|
||||
/// The first ten powers of 1024.
|
||||
const IEC_BASES: [u128; 10] = [
|
||||
1,
|
||||
1_024,
|
||||
1_048_576,
|
||||
1_073_741_824,
|
||||
1_099_511_627_776,
|
||||
1_125_899_906_842_624,
|
||||
1_152_921_504_606_846_976,
|
||||
1_180_591_620_717_411_303_424,
|
||||
1_208_925_819_614_629_174_706_176,
|
||||
1_237_940_039_285_380_274_899_124_224,
|
||||
];
|
||||
|
||||
/// Suffixes for the first nine multi-byte unit suffixes.
|
||||
const SUFFIXES: [char; 9] = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
|
||||
|
||||
/// Convert a multiple of 1024 into a string like "12K" or "34M".
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Powers of 1024 become "1K", "1M", "1G", etc.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// assert_eq!(to_magnitude_and_suffix_1024(1024).unwrap(), "1K");
|
||||
/// assert_eq!(to_magnitude_and_suffix_1024(1024 * 1024).unwrap(), "1M");
|
||||
/// assert_eq!(to_magnitude_and_suffix_1024(1024 * 1024 * 1024).unwrap(), "1G");
|
||||
/// ```
|
||||
///
|
||||
/// Multiples of those powers affect the magnitude part of the
|
||||
/// returned string:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// assert_eq!(to_magnitude_and_suffix_1024(123 * 1024).unwrap(), "123K");
|
||||
/// assert_eq!(to_magnitude_and_suffix_1024(456 * 1024 * 1024).unwrap(), "456M");
|
||||
/// assert_eq!(to_magnitude_and_suffix_1024(789 * 1024 * 1024 * 1024).unwrap(), "789G");
|
||||
/// ```
|
||||
fn to_magnitude_and_suffix_1024(n: u128) -> Result<String, ()> {
|
||||
// Find the smallest power of 1024 that is larger than `n`. That
|
||||
// number indicates which units and suffix to use.
|
||||
for i in 0..IEC_BASES.len() - 1 {
|
||||
if n < IEC_BASES[i + 1] {
|
||||
return Ok(format!("{}{}", n / IEC_BASES[i], SUFFIXES[i]));
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
|
||||
/// Convert a number into a magnitude and a multi-byte unit suffix.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the number is too large to represent.
|
||||
fn to_magnitude_and_suffix(n: u128) -> Result<String, ()> {
|
||||
if n % 1024 == 0 {
|
||||
to_magnitude_and_suffix_1024(n)
|
||||
} else {
|
||||
// TODO Implement this, probably using code from `numfmt`.
|
||||
Ok("1kB".into())
|
||||
}
|
||||
}
|
||||
|
||||
/// A block size to use in condensing the display of a large number of bytes.
|
||||
///
|
||||
/// The [`BlockSize::Bytes`] variant represents a static block
|
||||
/// size. The [`BlockSize::HumanReadableDecimal`] and
|
||||
/// [`BlockSize::HumanReadableBinary`] variants represent dynamic
|
||||
/// block sizes: as the number of bytes increases, the divisor
|
||||
/// increases as well (for example, from 1 to 1,000 to 1,000,000 and
|
||||
/// so on in the case of [`BlockSize::HumanReadableDecimal`]).
|
||||
///
|
||||
/// The default variant is `Bytes(1024)`.
|
||||
pub(crate) enum BlockSize {
|
||||
/// A fixed number of bytes.
|
||||
///
|
||||
/// The number must be positive.
|
||||
Bytes(u64),
|
||||
|
||||
/// Use the largest divisor corresponding to a unit, like B, K, M, G, etc.
|
||||
///
|
||||
/// This variant represents powers of 1,000. Contrast with
|
||||
/// [`BlockSize::HumanReadableBinary`], which represents powers of
|
||||
/// 1,024.
|
||||
HumanReadableDecimal,
|
||||
|
||||
/// Use the largest divisor corresponding to a unit, like B, K, M, G, etc.
|
||||
///
|
||||
/// This variant represents powers of 1,024. Contrast with
|
||||
/// [`BlockSize::HumanReadableDecimal`], which represents powers
|
||||
/// of 1,000.
|
||||
HumanReadableBinary,
|
||||
}
|
||||
|
||||
impl Default for BlockSize {
|
||||
fn default() -> Self {
|
||||
Self::Bytes(1024)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn block_size_from_matches(matches: &ArgMatches) -> Result<BlockSize, ParseIntError> {
|
||||
if matches.is_present(OPT_HUMAN_READABLE) {
|
||||
Ok(BlockSize::HumanReadableBinary)
|
||||
} else if matches.is_present(OPT_HUMAN_READABLE_2) {
|
||||
Ok(BlockSize::HumanReadableDecimal)
|
||||
} else if matches.is_present(OPT_BLOCKSIZE) {
|
||||
let s = matches.value_of(OPT_BLOCKSIZE).unwrap();
|
||||
Ok(BlockSize::Bytes(s.parse()?))
|
||||
} else {
|
||||
Ok(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BlockSize {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::HumanReadableBinary => write!(f, "Size"),
|
||||
Self::HumanReadableDecimal => write!(f, "Size"),
|
||||
Self::Bytes(n) => match to_magnitude_and_suffix(*n as u128) {
|
||||
Ok(s) => write!(f, "{}-blocks", s),
|
||||
Err(_) => Err(fmt::Error),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::blocks::{to_magnitude_and_suffix, BlockSize};
|
||||
|
||||
#[test]
|
||||
fn test_to_magnitude_and_suffix_powers_of_1024() {
|
||||
assert_eq!(to_magnitude_and_suffix(1024).unwrap(), "1K");
|
||||
assert_eq!(to_magnitude_and_suffix(2048).unwrap(), "2K");
|
||||
assert_eq!(to_magnitude_and_suffix(4096).unwrap(), "4K");
|
||||
assert_eq!(to_magnitude_and_suffix(1024 * 1024).unwrap(), "1M");
|
||||
assert_eq!(to_magnitude_and_suffix(2 * 1024 * 1024).unwrap(), "2M");
|
||||
assert_eq!(to_magnitude_and_suffix(1024 * 1024 * 1024).unwrap(), "1G");
|
||||
assert_eq!(
|
||||
to_magnitude_and_suffix(34 * 1024 * 1024 * 1024).unwrap(),
|
||||
"34G"
|
||||
);
|
||||
}
|
||||
|
||||
// TODO We have not yet implemented this behavior, but when we do,
|
||||
// uncomment this test.
|
||||
|
||||
// #[test]
|
||||
// fn test_to_magnitude_and_suffix_not_powers_of_1024() {
|
||||
// assert_eq!(to_magnitude_and_suffix(1).unwrap(), "1B");
|
||||
// assert_eq!(to_magnitude_and_suffix(999).unwrap(), "999B");
|
||||
|
||||
// assert_eq!(to_magnitude_and_suffix(1000).unwrap(), "1kB");
|
||||
// assert_eq!(to_magnitude_and_suffix(1001).unwrap(), "1.1kB");
|
||||
// assert_eq!(to_magnitude_and_suffix(1023).unwrap(), "1.1kB");
|
||||
// assert_eq!(to_magnitude_and_suffix(1025).unwrap(), "1.1kB");
|
||||
// assert_eq!(to_magnitude_and_suffix(999_000).unwrap(), "999kB");
|
||||
|
||||
// assert_eq!(to_magnitude_and_suffix(999_001).unwrap(), "1MB");
|
||||
// assert_eq!(to_magnitude_and_suffix(999_999).unwrap(), "1MB");
|
||||
// assert_eq!(to_magnitude_and_suffix(1_000_000).unwrap(), "1MB");
|
||||
// assert_eq!(to_magnitude_and_suffix(1_000_001).unwrap(), "1.1MB");
|
||||
// assert_eq!(to_magnitude_and_suffix(1_100_000).unwrap(), "1.1MB");
|
||||
// assert_eq!(to_magnitude_and_suffix(1_100_001).unwrap(), "1.2MB");
|
||||
// assert_eq!(to_magnitude_and_suffix(1_900_000).unwrap(), "1.9MB");
|
||||
// assert_eq!(to_magnitude_and_suffix(1_900_001).unwrap(), "2MB");
|
||||
// assert_eq!(to_magnitude_and_suffix(9_900_000).unwrap(), "9.9MB");
|
||||
// assert_eq!(to_magnitude_and_suffix(9_900_001).unwrap(), "10MB");
|
||||
// assert_eq!(to_magnitude_and_suffix(999_000_000).unwrap(), "999MB");
|
||||
|
||||
// assert_eq!(to_magnitude_and_suffix(999_000_001).unwrap(), "1GB");
|
||||
// assert_eq!(to_magnitude_and_suffix(1_000_000_000).unwrap(), "1GB");
|
||||
// // etc.
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn test_block_size_display() {
|
||||
assert_eq!(format!("{}", BlockSize::HumanReadableBinary), "Size");
|
||||
assert_eq!(format!("{}", BlockSize::HumanReadableDecimal), "Size");
|
||||
assert_eq!(format!("{}", BlockSize::Bytes(1024)), "1K-blocks");
|
||||
assert_eq!(format!("{}", BlockSize::Bytes(2 * 1024)), "2K-blocks");
|
||||
assert_eq!(
|
||||
format!("{}", BlockSize::Bytes(3 * 1024 * 1024)),
|
||||
"3M-blocks"
|
||||
);
|
||||
}
|
||||
}
|
166
src/uu/df/src/columns.rs
Normal file
166
src/uu/df/src/columns.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
// * This file is part of the uutils coreutils package.
|
||||
// *
|
||||
// * For the full copyright and license information, please view the LICENSE
|
||||
// * file that was distributed with this source code.
|
||||
// spell-checker:ignore itotal iused iavail ipcent pcent squashfs
|
||||
use crate::{OPT_INODES, OPT_OUTPUT, OPT_PRINT_TYPE};
|
||||
use clap::ArgMatches;
|
||||
|
||||
/// The columns in the output table produced by `df`.
|
||||
///
|
||||
/// The [`Row`] struct has a field corresponding to each of the
|
||||
/// variants of this enumeration.
|
||||
///
|
||||
/// [`Row`]: crate::table::Row
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub(crate) enum Column {
|
||||
/// The source of the mount point, usually a device.
|
||||
Source,
|
||||
|
||||
/// Total number of blocks.
|
||||
Size,
|
||||
|
||||
/// Number of used blocks.
|
||||
Used,
|
||||
|
||||
/// Number of available blocks.
|
||||
Avail,
|
||||
|
||||
/// Percentage of blocks used out of total number of blocks.
|
||||
Pcent,
|
||||
|
||||
/// The mount point.
|
||||
Target,
|
||||
|
||||
/// Total number of inodes.
|
||||
Itotal,
|
||||
|
||||
/// Number of used inodes.
|
||||
Iused,
|
||||
|
||||
/// Number of available inodes.
|
||||
Iavail,
|
||||
|
||||
/// Percentage of inodes used out of total number of inodes.
|
||||
Ipcent,
|
||||
|
||||
/// The filename given as a command-line argument.
|
||||
File,
|
||||
|
||||
/// The filesystem type, like "ext4" or "squashfs".
|
||||
Fstype,
|
||||
|
||||
/// Percentage of bytes available to non-privileged processes.
|
||||
#[cfg(target_os = "macos")]
|
||||
Capacity,
|
||||
}
|
||||
|
||||
impl Column {
|
||||
/// Convert from command-line arguments to sequence of columns.
|
||||
///
|
||||
/// The set of columns that will appear in the output table can be
|
||||
/// specified by command-line arguments. This function converts
|
||||
/// those arguments to a [`Vec`] of [`Column`] variants.
|
||||
pub(crate) fn from_matches(matches: &ArgMatches) -> Vec<Self> {
|
||||
match (
|
||||
matches.is_present(OPT_PRINT_TYPE),
|
||||
matches.is_present(OPT_INODES),
|
||||
matches.occurrences_of(OPT_OUTPUT) > 0,
|
||||
) {
|
||||
(false, false, false) => vec![
|
||||
Self::Source,
|
||||
Self::Size,
|
||||
Self::Used,
|
||||
Self::Avail,
|
||||
#[cfg(target_os = "macos")]
|
||||
Self::Capacity,
|
||||
Self::Pcent,
|
||||
Self::Target,
|
||||
],
|
||||
(false, false, true) => {
|
||||
matches
|
||||
.values_of(OPT_OUTPUT)
|
||||
.unwrap()
|
||||
.map(|s| {
|
||||
// Unwrapping here should not panic because the
|
||||
// command-line argument parsing library should be
|
||||
// responsible for ensuring each comma-separated
|
||||
// string is a valid column label.
|
||||
Self::parse(s).unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
(false, true, false) => vec![
|
||||
Self::Source,
|
||||
Self::Itotal,
|
||||
Self::Iused,
|
||||
Self::Iavail,
|
||||
Self::Ipcent,
|
||||
Self::Target,
|
||||
],
|
||||
(true, false, false) => vec![
|
||||
Self::Source,
|
||||
Self::Fstype,
|
||||
Self::Size,
|
||||
Self::Used,
|
||||
Self::Avail,
|
||||
#[cfg(target_os = "macos")]
|
||||
Self::Capacity,
|
||||
Self::Pcent,
|
||||
Self::Target,
|
||||
],
|
||||
(true, true, false) => vec![
|
||||
Self::Source,
|
||||
Self::Fstype,
|
||||
Self::Itotal,
|
||||
Self::Iused,
|
||||
Self::Iavail,
|
||||
Self::Ipcent,
|
||||
Self::Target,
|
||||
],
|
||||
// The command-line arguments -T and -i are each mutually
|
||||
// exclusive with --output, so the command-line argument
|
||||
// parser should reject those combinations before we get
|
||||
// to this point in the code.
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a column name to the corresponding enumeration variant.
|
||||
///
|
||||
/// There are twelve valid column names, one for each variant:
|
||||
///
|
||||
/// - "source"
|
||||
/// - "fstype"
|
||||
/// - "itotal"
|
||||
/// - "iused"
|
||||
/// - "iavail"
|
||||
/// - "ipcent"
|
||||
/// - "size"
|
||||
/// - "used"
|
||||
/// - "avail"
|
||||
/// - "pcent"
|
||||
/// - "file"
|
||||
/// - "target"
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the string `s` is not one of the valid column names.
|
||||
fn parse(s: &str) -> Result<Self, ()> {
|
||||
match s {
|
||||
"source" => Ok(Self::Source),
|
||||
"fstype" => Ok(Self::Fstype),
|
||||
"itotal" => Ok(Self::Itotal),
|
||||
"iused" => Ok(Self::Iused),
|
||||
"iavail" => Ok(Self::Iavail),
|
||||
"ipcent" => Ok(Self::Ipcent),
|
||||
"size" => Ok(Self::Size),
|
||||
"used" => Ok(Self::Used),
|
||||
"avail" => Ok(Self::Avail),
|
||||
"pcent" => Ok(Self::Pcent),
|
||||
"file" => Ok(Self::File),
|
||||
"target" => Ok(Self::Target),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,22 +5,24 @@
|
|||
//
|
||||
// For the full copyright and license information, please view the LICENSE file
|
||||
// that was distributed with this source code.
|
||||
// spell-checker:ignore itotal iused iavail ipcent pcent
|
||||
// spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs lofs
|
||||
mod blocks;
|
||||
mod columns;
|
||||
mod filesystem;
|
||||
mod table;
|
||||
|
||||
#[cfg(unix)]
|
||||
use uucore::fsext::statfs;
|
||||
use uucore::fsext::{read_fs_list, FsUsage, MountInfo};
|
||||
use uucore::{error::UResult, format_usage};
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
use uucore::format_usage;
|
||||
use uucore::fsext::{read_fs_list, MountInfo};
|
||||
|
||||
use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::blocks::{block_size_from_matches, BlockSize};
|
||||
use crate::columns::Column;
|
||||
use crate::filesystem::Filesystem;
|
||||
use crate::table::{DisplayRow, Header, Row};
|
||||
|
||||
static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\
|
||||
|
@ -49,145 +51,93 @@ static OUTPUT_FIELD_LIST: [&str; 12] = [
|
|||
"file", "target",
|
||||
];
|
||||
|
||||
/// Store names of file systems as a selector.
|
||||
/// Note: `exclude` takes priority over `include`.
|
||||
#[derive(Default)]
|
||||
struct FsSelector {
|
||||
include: HashSet<String>,
|
||||
exclude: HashSet<String>,
|
||||
}
|
||||
|
||||
/// A block size to use in condensing the display of a large number of bytes.
|
||||
/// Parameters that control the behavior of `df`.
|
||||
///
|
||||
/// The [`BlockSize::Bytes`] variant represents a static block
|
||||
/// size. The [`BlockSize::HumanReadableDecimal`] and
|
||||
/// [`BlockSize::HumanReadableBinary`] variants represent dynamic
|
||||
/// block sizes: as the number of bytes increases, the divisor
|
||||
/// increases as well (for example, from 1 to 1,000 to 1,000,000 and
|
||||
/// so on in the case of [`BlockSize::HumanReadableDecimal`]).
|
||||
///
|
||||
/// The default variant is `Bytes(1024)`.
|
||||
enum BlockSize {
|
||||
/// A fixed number of bytes.
|
||||
///
|
||||
/// The number must be positive.
|
||||
Bytes(u64),
|
||||
|
||||
/// Use the largest divisor corresponding to a unit, like B, K, M, G, etc.
|
||||
///
|
||||
/// This variant represents powers of 1,000. Contrast with
|
||||
/// [`BlockSize::HumanReadableBinary`], which represents powers of
|
||||
/// 1,024.
|
||||
HumanReadableDecimal,
|
||||
|
||||
/// Use the largest divisor corresponding to a unit, like B, K, M, G, etc.
|
||||
///
|
||||
/// This variant represents powers of 1,024. Contrast with
|
||||
/// [`BlockSize::HumanReadableDecimal`], which represents powers
|
||||
/// of 1,000.
|
||||
HumanReadableBinary,
|
||||
}
|
||||
|
||||
impl Default for BlockSize {
|
||||
fn default() -> Self {
|
||||
Self::Bytes(1024)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ArgMatches> for BlockSize {
|
||||
fn from(matches: &ArgMatches) -> Self {
|
||||
if matches.is_present(OPT_HUMAN_READABLE) {
|
||||
Self::HumanReadableBinary
|
||||
} else if matches.is_present(OPT_HUMAN_READABLE_2) {
|
||||
Self::HumanReadableDecimal
|
||||
} else {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
/// Most of these parameters control which rows and which columns are
|
||||
/// displayed. The `block_size` determines the units to use when
|
||||
/// displaying numbers of bytes or inodes.
|
||||
struct Options {
|
||||
show_local_fs: bool,
|
||||
show_all_fs: bool,
|
||||
show_listed_fs: bool,
|
||||
show_fs_type: bool,
|
||||
show_inode_instead: bool,
|
||||
block_size: BlockSize,
|
||||
fs_selector: FsSelector,
|
||||
|
||||
/// Optional list of filesystem types to include in the output table.
|
||||
///
|
||||
/// If this is not `None`, only filesystems that match one of
|
||||
/// these types will be listed.
|
||||
include: Option<Vec<String>>,
|
||||
|
||||
/// Optional list of filesystem types to exclude from the output table.
|
||||
///
|
||||
/// If this is not `None`, filesystems that match one of these
|
||||
/// types will *not* be listed.
|
||||
exclude: Option<Vec<String>>,
|
||||
|
||||
/// Whether to show a final row comprising the totals for each column.
|
||||
show_total: bool,
|
||||
|
||||
/// Sequence of columns to display in the output table.
|
||||
columns: Vec<Column>,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
show_local_fs: Default::default(),
|
||||
show_all_fs: Default::default(),
|
||||
show_listed_fs: Default::default(),
|
||||
block_size: Default::default(),
|
||||
include: Default::default(),
|
||||
exclude: Default::default(),
|
||||
show_total: Default::default(),
|
||||
columns: vec![
|
||||
Column::Source,
|
||||
Column::Size,
|
||||
Column::Used,
|
||||
Column::Avail,
|
||||
Column::Pcent,
|
||||
Column::Target,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum OptionsError {
|
||||
InvalidBlockSize,
|
||||
}
|
||||
|
||||
impl fmt::Display for OptionsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
// TODO This should include the raw string provided as the argument.
|
||||
//
|
||||
// TODO This needs to vary based on whether `--block-size`
|
||||
// or `-B` were provided.
|
||||
Self::InvalidBlockSize => write!(f, "invalid --block-size argument"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Convert command-line arguments into [`Options`].
|
||||
fn from(matches: &ArgMatches) -> Self {
|
||||
Self {
|
||||
fn from(matches: &ArgMatches) -> Result<Self, OptionsError> {
|
||||
Ok(Self {
|
||||
show_local_fs: matches.is_present(OPT_LOCAL),
|
||||
show_all_fs: matches.is_present(OPT_ALL),
|
||||
show_listed_fs: false,
|
||||
show_fs_type: matches.is_present(OPT_PRINT_TYPE),
|
||||
show_inode_instead: matches.is_present(OPT_INODES),
|
||||
block_size: BlockSize::from(matches),
|
||||
fs_selector: FsSelector::from(matches),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Filesystem {
|
||||
mount_info: MountInfo,
|
||||
usage: FsUsage,
|
||||
}
|
||||
|
||||
impl FsSelector {
|
||||
/// Convert command-line arguments into a [`FsSelector`].
|
||||
///
|
||||
/// This function reads the include and exclude sets from
|
||||
/// [`ArgMatches`] and returns the corresponding [`FsSelector`]
|
||||
/// instance.
|
||||
fn from(matches: &ArgMatches) -> Self {
|
||||
let include = HashSet::from_iter(matches.values_of_lossy(OPT_TYPE).unwrap_or_default());
|
||||
let exclude = HashSet::from_iter(
|
||||
matches
|
||||
.values_of_lossy(OPT_EXCLUDE_TYPE)
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
Self { include, exclude }
|
||||
}
|
||||
|
||||
fn should_select(&self, fs_type: &str) -> bool {
|
||||
if self.exclude.contains(fs_type) {
|
||||
return false;
|
||||
}
|
||||
self.include.is_empty() || self.include.contains(fs_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl Filesystem {
|
||||
// TODO: resolve uuid in `mount_info.dev_name` if exists
|
||||
fn new(mount_info: MountInfo) -> Option<Self> {
|
||||
let _stat_path = if !mount_info.mount_dir.is_empty() {
|
||||
mount_info.mount_dir.clone()
|
||||
} else {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
mount_info.dev_name.clone()
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// On windows, we expect the volume id
|
||||
mount_info.dev_id.clone()
|
||||
}
|
||||
};
|
||||
#[cfg(unix)]
|
||||
let usage = FsUsage::new(statfs(_stat_path).ok()?);
|
||||
#[cfg(windows)]
|
||||
let usage = FsUsage::new(Path::new(&_stat_path));
|
||||
Some(Self { mount_info, usage })
|
||||
block_size: block_size_from_matches(matches)
|
||||
.map_err(|_| OptionsError::InvalidBlockSize)?,
|
||||
include: matches.values_of_lossy(OPT_TYPE),
|
||||
exclude: matches.values_of_lossy(OPT_EXCLUDE_TYPE),
|
||||
show_total: matches.is_present(OPT_TOTAL),
|
||||
columns: Column::from_matches(matches),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether to display the mount info given the inclusion settings.
|
||||
fn is_included(mi: &MountInfo, paths: &[String], opt: &Options) -> bool {
|
||||
fn is_included(mi: &MountInfo, opt: &Options) -> bool {
|
||||
// Don't show remote filesystems if `--local` has been given.
|
||||
if mi.remote && opt.show_local_fs {
|
||||
return false;
|
||||
|
@ -199,14 +149,15 @@ fn is_included(mi: &MountInfo, paths: &[String], opt: &Options) -> bool {
|
|||
}
|
||||
|
||||
// Don't show filesystems if they have been explicitly excluded.
|
||||
if !opt.fs_selector.should_select(&mi.fs_type) {
|
||||
return false;
|
||||
if let Some(ref excludes) = opt.exclude {
|
||||
if excludes.contains(&mi.fs_type) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't show filesystems other than the ones specified on the
|
||||
// command line, if any.
|
||||
if !paths.is_empty() && !paths.contains(&mi.mount_dir) {
|
||||
return false;
|
||||
if let Some(ref includes) = opt.include {
|
||||
if !includes.contains(&mi.fs_type) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
|
@ -258,15 +209,13 @@ fn is_best(previous: &[MountInfo], mi: &MountInfo) -> bool {
|
|||
|
||||
/// Keep only the specified subset of [`MountInfo`] instances.
|
||||
///
|
||||
/// If `paths` is non-empty, this function excludes any [`MountInfo`]
|
||||
/// that is not mounted at the specified path.
|
||||
///
|
||||
/// The `opt` argument specifies a variety of ways of excluding
|
||||
/// [`MountInfo`] instances; see [`Options`] for more information.
|
||||
///
|
||||
/// Finally, if there are duplicate entries, the one with the shorter
|
||||
/// path is kept.
|
||||
fn filter_mount_list(vmi: Vec<MountInfo>, paths: &[String], opt: &Options) -> Vec<MountInfo> {
|
||||
|
||||
fn filter_mount_list(vmi: Vec<MountInfo>, opt: &Options) -> Vec<MountInfo> {
|
||||
let mut result = vec![];
|
||||
for mi in vmi {
|
||||
// TODO The running time of the `is_best()` function is linear
|
||||
|
@ -274,21 +223,45 @@ fn filter_mount_list(vmi: Vec<MountInfo>, paths: &[String], opt: &Options) -> Ve
|
|||
// this loop quadratic in the length of `vmi`. This could be
|
||||
// improved by a more efficient implementation of `is_best()`,
|
||||
// but `vmi` is probably not very long in practice.
|
||||
if is_included(&mi, paths, opt) && is_best(&result, &mi) {
|
||||
if is_included(&mi, opt) && is_best(&result, &mi) {
|
||||
result.push(mi);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Assign 1 `MountInfo` entry to each path
|
||||
/// `lofs` entries are skipped and dummy mount points are skipped
|
||||
/// Only the longest matching prefix for that path is considered
|
||||
/// `lofs` is for Solaris style loopback filesystem and is present in Solaris and FreeBSD.
|
||||
/// It works similar to symlinks
|
||||
fn get_point_list(vmi: &[MountInfo], paths: &[String]) -> Vec<MountInfo> {
|
||||
paths
|
||||
.iter()
|
||||
.map(|p| {
|
||||
vmi.iter()
|
||||
.filter(|mi| mi.fs_type.ne("lofs"))
|
||||
.filter(|mi| !mi.dummy)
|
||||
.filter(|mi| p.starts_with(&mi.mount_dir))
|
||||
.max_by_key(|mi| mi.mount_dir.len())
|
||||
.unwrap()
|
||||
.clone()
|
||||
})
|
||||
.collect::<Vec<MountInfo>>()
|
||||
}
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().get_matches_from(args);
|
||||
|
||||
let paths: Vec<String> = matches
|
||||
// Canonicalize the input_paths and then convert to string
|
||||
let paths = matches
|
||||
.values_of(OPT_PATHS)
|
||||
.map(|v| v.map(ToString::to_string).collect())
|
||||
.unwrap_or_default();
|
||||
.unwrap_or_default()
|
||||
.map(Path::new)
|
||||
.filter_map(|v| v.canonicalize().ok())
|
||||
.filter_map(|v| v.into_os_string().into_string().ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
|
@ -298,18 +271,32 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
}
|
||||
}
|
||||
|
||||
let opt = Options::from(&matches);
|
||||
let opt = Options::from(&matches).map_err(|e| USimpleError::new(1, format!("{}", e)))?;
|
||||
|
||||
let mounts = read_fs_list();
|
||||
let data: Vec<Row> = filter_mount_list(mounts, &paths, &opt)
|
||||
|
||||
let op_mount_points: Vec<MountInfo> = if paths.is_empty() {
|
||||
// Get all entries
|
||||
filter_mount_list(mounts, &opt)
|
||||
} else {
|
||||
// Get Point for each input_path
|
||||
get_point_list(&mounts, &paths)
|
||||
};
|
||||
let data: Vec<Row> = op_mount_points
|
||||
.into_iter()
|
||||
.filter_map(Filesystem::new)
|
||||
.filter(|fs| fs.usage.blocks != 0 || opt.show_all_fs || opt.show_listed_fs)
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
|
||||
println!("{}", Header::new(&opt));
|
||||
let mut total = Row::new("total");
|
||||
for row in data {
|
||||
println!("{}", DisplayRow::new(row, &opt));
|
||||
println!("{}", DisplayRow::new(&row, &opt));
|
||||
total += row;
|
||||
}
|
||||
if opt.show_total {
|
||||
println!("{}", DisplayRow::new(&total, &opt));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -431,3 +418,268 @@ pub fn uu_app<'a>() -> App<'a> {
|
|||
)
|
||||
.arg(Arg::new(OPT_PATHS).multiple_occurrences(true))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
mod mount_info_lt {
|
||||
|
||||
use crate::mount_info_lt;
|
||||
use uucore::fsext::MountInfo;
|
||||
|
||||
/// Instantiate a [`MountInfo`] with the given fields.
|
||||
fn mount_info(dev_name: &str, mount_root: &str, mount_dir: &str) -> MountInfo {
|
||||
MountInfo {
|
||||
dev_id: String::new(),
|
||||
dev_name: String::from(dev_name),
|
||||
fs_type: String::new(),
|
||||
mount_dir: String::from(mount_dir),
|
||||
mount_option: String::new(),
|
||||
mount_root: String::from(mount_root),
|
||||
remote: false,
|
||||
dummy: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_absolute() {
|
||||
// Prefer device name "/dev/foo" over "dev_foo".
|
||||
let m1 = mount_info("/dev/foo", "/", "/mnt/bar");
|
||||
let m2 = mount_info("dev_foo", "/", "/mnt/bar");
|
||||
assert!(!mount_info_lt(&m1, &m2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shorter() {
|
||||
// Prefer mount directory "/mnt/bar" over "/mnt/bar/baz"...
|
||||
let m1 = mount_info("/dev/foo", "/", "/mnt/bar");
|
||||
let m2 = mount_info("/dev/foo", "/", "/mnt/bar/baz");
|
||||
assert!(!mount_info_lt(&m1, &m2));
|
||||
|
||||
// ..but prefer mount root "/root" over "/".
|
||||
let m1 = mount_info("/dev/foo", "/root", "/mnt/bar");
|
||||
let m2 = mount_info("/dev/foo", "/", "/mnt/bar/baz");
|
||||
assert!(mount_info_lt(&m1, &m2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_over_mounted() {
|
||||
// Prefer the earlier entry if the devices are different but
|
||||
// the mount directory is the same.
|
||||
let m1 = mount_info("/dev/foo", "/", "/mnt/baz");
|
||||
let m2 = mount_info("/dev/bar", "/", "/mnt/baz");
|
||||
assert!(!mount_info_lt(&m1, &m2));
|
||||
}
|
||||
}
|
||||
|
||||
mod is_best {
|
||||
|
||||
use crate::is_best;
|
||||
use uucore::fsext::MountInfo;
|
||||
|
||||
/// Instantiate a [`MountInfo`] with the given fields.
|
||||
fn mount_info(dev_id: &str, mount_dir: &str) -> MountInfo {
|
||||
MountInfo {
|
||||
dev_id: String::from(dev_id),
|
||||
dev_name: String::new(),
|
||||
fs_type: String::new(),
|
||||
mount_dir: String::from(mount_dir),
|
||||
mount_option: String::new(),
|
||||
mount_root: String::new(),
|
||||
remote: false,
|
||||
dummy: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty() {
|
||||
let m = mount_info("0", "/mnt/bar");
|
||||
assert!(is_best(&[], &m));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_different_dev_id() {
|
||||
let m1 = mount_info("0", "/mnt/bar");
|
||||
let m2 = mount_info("1", "/mnt/bar");
|
||||
assert!(is_best(&[m1.clone()], &m2));
|
||||
assert!(is_best(&[m2], &m1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_same_dev_id() {
|
||||
// There are several conditions under which a `MountInfo` is
|
||||
// considered "better" than the others, we're just checking
|
||||
// one condition in this test.
|
||||
let m1 = mount_info("0", "/mnt/bar");
|
||||
let m2 = mount_info("0", "/mnt/bar/baz");
|
||||
assert!(!is_best(&[m1.clone()], &m2));
|
||||
assert!(is_best(&[m2], &m1));
|
||||
}
|
||||
}
|
||||
|
||||
mod is_included {
|
||||
|
||||
use crate::{is_included, Options};
|
||||
use uucore::fsext::MountInfo;
|
||||
|
||||
/// Instantiate a [`MountInfo`] with the given fields.
|
||||
fn mount_info(fs_type: &str, mount_dir: &str, remote: bool, dummy: bool) -> MountInfo {
|
||||
MountInfo {
|
||||
dev_id: String::new(),
|
||||
dev_name: String::new(),
|
||||
fs_type: String::from(fs_type),
|
||||
mount_dir: String::from(mount_dir),
|
||||
mount_option: String::new(),
|
||||
mount_root: String::new(),
|
||||
remote,
|
||||
dummy,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remote_included() {
|
||||
let opt = Default::default();
|
||||
let m = mount_info("ext4", "/mnt/foo", true, false);
|
||||
assert!(is_included(&m, &opt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remote_excluded() {
|
||||
let opt = Options {
|
||||
show_local_fs: true,
|
||||
..Default::default()
|
||||
};
|
||||
let m = mount_info("ext4", "/mnt/foo", true, false);
|
||||
assert!(!is_included(&m, &opt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dummy_included() {
|
||||
let opt = Options {
|
||||
show_all_fs: true,
|
||||
show_listed_fs: true,
|
||||
..Default::default()
|
||||
};
|
||||
let m = mount_info("ext4", "/mnt/foo", false, true);
|
||||
assert!(is_included(&m, &opt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dummy_excluded() {
|
||||
let opt = Default::default();
|
||||
let m = mount_info("ext4", "/mnt/foo", false, true);
|
||||
assert!(!is_included(&m, &opt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exclude_match() {
|
||||
let exclude = Some(vec![String::from("ext4")]);
|
||||
let opt = Options {
|
||||
exclude,
|
||||
..Default::default()
|
||||
};
|
||||
let m = mount_info("ext4", "/mnt/foo", false, false);
|
||||
assert!(!is_included(&m, &opt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exclude_no_match() {
|
||||
let exclude = Some(vec![String::from("tmpfs")]);
|
||||
let opt = Options {
|
||||
exclude,
|
||||
..Default::default()
|
||||
};
|
||||
let m = mount_info("ext4", "/mnt/foo", false, false);
|
||||
assert!(is_included(&m, &opt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_include_match() {
|
||||
let include = Some(vec![String::from("ext4")]);
|
||||
let opt = Options {
|
||||
include,
|
||||
..Default::default()
|
||||
};
|
||||
let m = mount_info("ext4", "/mnt/foo", false, false);
|
||||
assert!(is_included(&m, &opt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_include_no_match() {
|
||||
let include = Some(vec![String::from("tmpfs")]);
|
||||
let opt = Options {
|
||||
include,
|
||||
..Default::default()
|
||||
};
|
||||
let m = mount_info("ext4", "/mnt/foo", false, false);
|
||||
assert!(!is_included(&m, &opt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_include_and_exclude_match_neither() {
|
||||
let include = Some(vec![String::from("tmpfs")]);
|
||||
let exclude = Some(vec![String::from("squashfs")]);
|
||||
let opt = Options {
|
||||
include,
|
||||
exclude,
|
||||
..Default::default()
|
||||
};
|
||||
let m = mount_info("ext4", "/mnt/foo", false, false);
|
||||
assert!(!is_included(&m, &opt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_include_and_exclude_match_exclude() {
|
||||
let include = Some(vec![String::from("tmpfs")]);
|
||||
let exclude = Some(vec![String::from("ext4")]);
|
||||
let opt = Options {
|
||||
include,
|
||||
exclude,
|
||||
..Default::default()
|
||||
};
|
||||
let m = mount_info("ext4", "/mnt/foo", false, false);
|
||||
assert!(!is_included(&m, &opt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_include_and_exclude_match_include() {
|
||||
let include = Some(vec![String::from("ext4")]);
|
||||
let exclude = Some(vec![String::from("squashfs")]);
|
||||
let opt = Options {
|
||||
include,
|
||||
exclude,
|
||||
..Default::default()
|
||||
};
|
||||
let m = mount_info("ext4", "/mnt/foo", false, false);
|
||||
assert!(is_included(&m, &opt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_include_and_exclude_match_both() {
|
||||
// TODO The same filesystem type in both `include` and
|
||||
// `exclude` should cause an error, but currently does
|
||||
// not.
|
||||
let include = Some(vec![String::from("ext4")]);
|
||||
let exclude = Some(vec![String::from("ext4")]);
|
||||
let opt = Options {
|
||||
include,
|
||||
exclude,
|
||||
..Default::default()
|
||||
};
|
||||
let m = mount_info("ext4", "/mnt/foo", false, false);
|
||||
assert!(!is_included(&m, &opt));
|
||||
}
|
||||
}
|
||||
|
||||
mod filter_mount_list {
|
||||
|
||||
use crate::filter_mount_list;
|
||||
|
||||
#[test]
|
||||
fn test_empty() {
|
||||
let opt = Default::default();
|
||||
let mount_infos = vec![];
|
||||
assert!(filter_mount_list(mount_infos, &opt).is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
55
src/uu/df/src/filesystem.rs
Normal file
55
src/uu/df/src/filesystem.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
// * This file is part of the uutils coreutils package.
|
||||
// *
|
||||
// * For the full copyright and license information, please view the LICENSE
|
||||
// * file that was distributed with this source code.
|
||||
//! Provides a summary representation of a filesystem.
|
||||
//!
|
||||
//! A [`Filesystem`] struct represents a device containing a
|
||||
//! filesystem mounted at a particular directory. It also includes
|
||||
//! information on amount of space available and amount of space used.
|
||||
#[cfg(windows)]
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(unix)]
|
||||
use uucore::fsext::statfs;
|
||||
use uucore::fsext::{FsUsage, MountInfo};
|
||||
|
||||
/// Summary representation of a filesystem.
|
||||
///
|
||||
/// A [`Filesystem`] struct represents a device containing a
|
||||
/// filesystem mounted at a particular directory. The
|
||||
/// [`Filesystem::mount_info`] field exposes that information. The
|
||||
/// [`Filesystem::usage`] field provides information on the amount of
|
||||
/// space available on the filesystem and the amount of space used.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Filesystem {
|
||||
/// Information about the mounted device, mount directory, and related options.
|
||||
pub mount_info: MountInfo,
|
||||
|
||||
/// Information about the amount of space used on the filesystem.
|
||||
pub usage: FsUsage,
|
||||
}
|
||||
|
||||
impl Filesystem {
|
||||
// TODO: resolve uuid in `mount_info.dev_name` if exists
|
||||
pub(crate) fn new(mount_info: MountInfo) -> Option<Self> {
|
||||
let _stat_path = if !mount_info.mount_dir.is_empty() {
|
||||
mount_info.mount_dir.clone()
|
||||
} else {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
mount_info.dev_name.clone()
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// On windows, we expect the volume id
|
||||
mount_info.dev_id.clone()
|
||||
}
|
||||
};
|
||||
#[cfg(unix)]
|
||||
let usage = FsUsage::new(statfs(_stat_path).ok()?);
|
||||
#[cfg(windows)]
|
||||
let usage = FsUsage::new(Path::new(&_stat_path));
|
||||
Some(Self { mount_info, usage })
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// *
|
||||
// * For the full copyright and license information, please view the LICENSE
|
||||
// * file that was distributed with this source code.
|
||||
// spell-checker:ignore tmpfs
|
||||
// spell-checker:ignore tmpfs Pcent Itotal Iused Iavail Ipcent
|
||||
//! The filesystem usage data table.
|
||||
//!
|
||||
//! A table comprises a header row ([`Header`]) and a collection of
|
||||
|
@ -11,10 +11,13 @@
|
|||
//! [`DisplayRow`] implements [`std::fmt::Display`].
|
||||
use number_prefix::NumberPrefix;
|
||||
|
||||
use crate::{BlockSize, Filesystem, Options};
|
||||
use crate::columns::Column;
|
||||
use crate::filesystem::Filesystem;
|
||||
use crate::{BlockSize, Options};
|
||||
use uucore::fsext::{FsUsage, MountInfo};
|
||||
|
||||
use std::fmt;
|
||||
use std::ops::AddAssign;
|
||||
|
||||
/// A row in the filesystem usage data table.
|
||||
///
|
||||
|
@ -67,6 +70,63 @@ pub(crate) struct Row {
|
|||
inodes_usage: Option<f64>,
|
||||
}
|
||||
|
||||
impl Row {
|
||||
pub(crate) fn new(source: &str) -> Self {
|
||||
Self {
|
||||
fs_device: source.into(),
|
||||
fs_type: "-".into(),
|
||||
fs_mount: "-".into(),
|
||||
bytes: 0,
|
||||
bytes_used: 0,
|
||||
bytes_free: 0,
|
||||
bytes_usage: None,
|
||||
#[cfg(target_os = "macos")]
|
||||
bytes_capacity: None,
|
||||
inodes: 0,
|
||||
inodes_used: 0,
|
||||
inodes_free: 0,
|
||||
inodes_usage: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Row {
|
||||
/// Sum the numeric values of two rows.
|
||||
///
|
||||
/// The `Row::fs_device` field is set to `"total"` and the
|
||||
/// remaining `String` fields are set to `"-"`.
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
let bytes = self.bytes + rhs.bytes;
|
||||
let bytes_used = self.bytes_used + rhs.bytes_used;
|
||||
let inodes = self.inodes + rhs.inodes;
|
||||
let inodes_used = self.inodes_used + rhs.inodes_used;
|
||||
*self = Self {
|
||||
fs_device: "total".into(),
|
||||
fs_type: "-".into(),
|
||||
fs_mount: "-".into(),
|
||||
bytes,
|
||||
bytes_used,
|
||||
bytes_free: self.bytes_free + rhs.bytes_free,
|
||||
bytes_usage: if bytes == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(bytes_used as f64 / bytes as f64)
|
||||
},
|
||||
// TODO Figure out how to compute this.
|
||||
#[cfg(target_os = "macos")]
|
||||
bytes_capacity: None,
|
||||
inodes,
|
||||
inodes_used,
|
||||
inodes_free: self.inodes_free + rhs.inodes_free,
|
||||
inodes_usage: if inodes == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(inodes_used as f64 / inodes as f64)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Filesystem> for Row {
|
||||
fn from(fs: Filesystem) -> Self {
|
||||
let MountInfo {
|
||||
|
@ -120,7 +180,7 @@ impl From<Filesystem> for Row {
|
|||
/// The `options` control how the information in the row gets displayed.
|
||||
pub(crate) struct DisplayRow<'a> {
|
||||
/// The data in this row.
|
||||
row: Row,
|
||||
row: &'a Row,
|
||||
|
||||
/// Options that control how to display the data.
|
||||
options: &'a Options,
|
||||
|
@ -135,7 +195,7 @@ pub(crate) struct DisplayRow<'a> {
|
|||
|
||||
impl<'a> DisplayRow<'a> {
|
||||
/// Instantiate this struct.
|
||||
pub(crate) fn new(row: Row, options: &'a Options) -> Self {
|
||||
pub(crate) fn new(row: &'a Row, options: &'a Options) -> Self {
|
||||
Self { row, options }
|
||||
}
|
||||
|
||||
|
@ -164,59 +224,40 @@ impl<'a> DisplayRow<'a> {
|
|||
fn percentage(fraction: Option<f64>) -> String {
|
||||
match fraction {
|
||||
None => "-".to_string(),
|
||||
Some(x) => format!("{:.0}%", 100.0 * x),
|
||||
Some(x) => format!("{:.0}%", (100.0 * x).ceil()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the bytes data for this row.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If there is a problem writing to `f`.
|
||||
///
|
||||
/// If the scaling factor is not 1000, 1024, or a negative number.
|
||||
fn fmt_bytes(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{0: >12} ", self.scaled(self.row.bytes)?)?;
|
||||
write!(f, "{0: >12} ", self.scaled(self.row.bytes_used)?)?;
|
||||
write!(f, "{0: >12} ", self.scaled(self.row.bytes_free)?)?;
|
||||
#[cfg(target_os = "macos")]
|
||||
write!(
|
||||
f,
|
||||
"{0: >12} ",
|
||||
DisplayRow::percentage(self.row.bytes_capacity)
|
||||
)?;
|
||||
write!(f, "{0: >5} ", DisplayRow::percentage(self.row.bytes_usage))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the inodes data for this row.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If there is a problem writing to `f`.
|
||||
///
|
||||
/// If the scaling factor is not 1000, 1024, or a negative number.
|
||||
fn fmt_inodes(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{0: >12} ", self.scaled(self.row.inodes)?)?;
|
||||
write!(f, "{0: >12} ", self.scaled(self.row.inodes_used)?)?;
|
||||
write!(f, "{0: >12} ", self.scaled(self.row.inodes_free)?)?;
|
||||
write!(f, "{0: >5} ", DisplayRow::percentage(self.row.inodes_usage))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DisplayRow<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{0: <16} ", self.row.fs_device)?;
|
||||
if self.options.show_fs_type {
|
||||
write!(f, "{0: <5} ", self.row.fs_type)?;
|
||||
for column in &self.options.columns {
|
||||
match column {
|
||||
Column::Source => write!(f, "{0: <16} ", self.row.fs_device)?,
|
||||
Column::Size => write!(f, "{0: >12} ", self.scaled(self.row.bytes)?)?,
|
||||
Column::Used => write!(f, "{0: >12} ", self.scaled(self.row.bytes_used)?)?,
|
||||
Column::Avail => write!(f, "{0: >12} ", self.scaled(self.row.bytes_free)?)?,
|
||||
Column::Pcent => {
|
||||
write!(f, "{0: >5} ", DisplayRow::percentage(self.row.bytes_usage))?;
|
||||
}
|
||||
Column::Target => write!(f, "{0: <16}", self.row.fs_mount)?,
|
||||
Column::Itotal => write!(f, "{0: >12} ", self.scaled(self.row.inodes)?)?,
|
||||
Column::Iused => write!(f, "{0: >12} ", self.scaled(self.row.inodes_used)?)?,
|
||||
Column::Iavail => write!(f, "{0: >12} ", self.scaled(self.row.inodes_free)?)?,
|
||||
Column::Ipcent => {
|
||||
write!(f, "{0: >5} ", DisplayRow::percentage(self.row.inodes_usage))?;
|
||||
}
|
||||
// TODO Implement this.
|
||||
Column::File => {}
|
||||
Column::Fstype => write!(f, "{0: <5} ", self.row.fs_type)?,
|
||||
#[cfg(target_os = "macos")]
|
||||
Column::Capacity => write!(
|
||||
f,
|
||||
"{0: >12} ",
|
||||
DisplayRow::percentage(self.row.bytes_capacity)
|
||||
)?,
|
||||
}
|
||||
}
|
||||
if self.options.show_inode_instead {
|
||||
self.fmt_inodes(f)?;
|
||||
} else {
|
||||
self.fmt_bytes(f)?;
|
||||
}
|
||||
write!(f, "{0: <16}", self.row.fs_mount)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -238,30 +279,29 @@ impl<'a> Header<'a> {
|
|||
|
||||
impl fmt::Display for Header<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{0: <16} ", "Filesystem")?;
|
||||
if self.options.show_fs_type {
|
||||
write!(f, "{0: <5} ", "Type")?;
|
||||
for column in &self.options.columns {
|
||||
match column {
|
||||
Column::Source => write!(f, "{0: <16} ", "Filesystem")?,
|
||||
// `Display` is implemented for `BlockSize`, but
|
||||
// `Display` only works when formatting an object into
|
||||
// an empty format, `{}`. So we use `format!()` first
|
||||
// to create the string, then use `write!()` to align
|
||||
// the string and pad with spaces.
|
||||
Column::Size => write!(f, "{0: >12} ", format!("{}", self.options.block_size))?,
|
||||
Column::Used => write!(f, "{0: >12} ", "Used")?,
|
||||
Column::Avail => write!(f, "{0: >12} ", "Available")?,
|
||||
Column::Pcent => write!(f, "{0: >5} ", "Use%")?,
|
||||
Column::Target => write!(f, "{0: <16} ", "Mounted on")?,
|
||||
Column::Itotal => write!(f, "{0: >12} ", "Inodes")?,
|
||||
Column::Iused => write!(f, "{0: >12} ", "IUsed")?,
|
||||
Column::Iavail => write!(f, "{0: >12} ", "IFree")?,
|
||||
Column::Ipcent => write!(f, "{0: >5} ", "IUse%")?,
|
||||
Column::File => write!(f, "{0: <16}", "File")?,
|
||||
Column::Fstype => write!(f, "{0: <5} ", "Type")?,
|
||||
#[cfg(target_os = "macos")]
|
||||
Column::Capacity => write!(f, "{0: >12} ", "Capacity")?,
|
||||
}
|
||||
}
|
||||
if self.options.show_inode_instead {
|
||||
write!(f, "{0: >12} ", "Inodes")?;
|
||||
write!(f, "{0: >12} ", "IUsed")?;
|
||||
write!(f, "{0: >12} ", "IFree")?;
|
||||
write!(f, "{0: >5} ", "IUse%")?;
|
||||
} else {
|
||||
// TODO Support arbitrary positive scaling factors (from
|
||||
// the `--block-size` command-line argument).
|
||||
if let BlockSize::Bytes(_) = self.options.block_size {
|
||||
write!(f, "{0: >12} ", "1k-blocks")?;
|
||||
} else {
|
||||
write!(f, "{0: >12} ", "Size")?;
|
||||
};
|
||||
write!(f, "{0: >12} ", "Used")?;
|
||||
write!(f, "{0: >12} ", "Available")?;
|
||||
#[cfg(target_os = "macos")]
|
||||
write!(f, "{0: >12} ", "Capacity")?;
|
||||
write!(f, "{0: >5} ", "Use%")?;
|
||||
}
|
||||
write!(f, "{0: <16} ", "Mounted on")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -269,34 +309,53 @@ impl fmt::Display for Header<'_> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::columns::Column;
|
||||
use crate::table::{DisplayRow, Header, Row};
|
||||
use crate::{BlockSize, Options};
|
||||
|
||||
const COLUMNS_WITH_FS_TYPE: [Column; 7] = [
|
||||
Column::Source,
|
||||
Column::Fstype,
|
||||
Column::Size,
|
||||
Column::Used,
|
||||
Column::Avail,
|
||||
Column::Pcent,
|
||||
Column::Target,
|
||||
];
|
||||
const COLUMNS_WITH_INODES: [Column; 6] = [
|
||||
Column::Source,
|
||||
Column::Itotal,
|
||||
Column::Iused,
|
||||
Column::Iavail,
|
||||
Column::Ipcent,
|
||||
Column::Target,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_header_display() {
|
||||
let options = Default::default();
|
||||
assert_eq!(
|
||||
Header::new(&options).to_string(),
|
||||
"Filesystem 1k-blocks Used Available Use% Mounted on "
|
||||
"Filesystem 1K-blocks Used Available Use% Mounted on "
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_display_fs_type() {
|
||||
let options = Options {
|
||||
show_fs_type: true,
|
||||
columns: COLUMNS_WITH_FS_TYPE.to_vec(),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
Header::new(&options).to_string(),
|
||||
"Filesystem Type 1k-blocks Used Available Use% Mounted on "
|
||||
"Filesystem Type 1K-blocks Used Available Use% Mounted on "
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_display_inode() {
|
||||
let options = Options {
|
||||
show_inode_instead: true,
|
||||
columns: COLUMNS_WITH_INODES.to_vec(),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
|
@ -305,6 +364,18 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_display_block_size_1024() {
|
||||
let options = Options {
|
||||
block_size: BlockSize::Bytes(3 * 1024),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
Header::new(&options).to_string(),
|
||||
"Filesystem 3K-blocks Used Available Use% Mounted on "
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_display_human_readable_binary() {
|
||||
let options = Options {
|
||||
|
@ -354,7 +425,7 @@ mod tests {
|
|||
inodes_usage: Some(0.2),
|
||||
};
|
||||
assert_eq!(
|
||||
DisplayRow::new(row, &options).to_string(),
|
||||
DisplayRow::new(&row, &options).to_string(),
|
||||
"my_device 100 25 75 25% my_mount "
|
||||
);
|
||||
}
|
||||
|
@ -362,8 +433,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_row_display_fs_type() {
|
||||
let options = Options {
|
||||
columns: COLUMNS_WITH_FS_TYPE.to_vec(),
|
||||
block_size: BlockSize::Bytes(1),
|
||||
show_fs_type: true,
|
||||
..Default::default()
|
||||
};
|
||||
let row = Row {
|
||||
|
@ -385,7 +456,7 @@ mod tests {
|
|||
inodes_usage: Some(0.2),
|
||||
};
|
||||
assert_eq!(
|
||||
DisplayRow::new(row, &options).to_string(),
|
||||
DisplayRow::new(&row, &options).to_string(),
|
||||
"my_device my_type 100 25 75 25% my_mount "
|
||||
);
|
||||
}
|
||||
|
@ -393,8 +464,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_row_display_inodes() {
|
||||
let options = Options {
|
||||
columns: COLUMNS_WITH_INODES.to_vec(),
|
||||
block_size: BlockSize::Bytes(1),
|
||||
show_inode_instead: true,
|
||||
..Default::default()
|
||||
};
|
||||
let row = Row {
|
||||
|
@ -416,7 +487,7 @@ mod tests {
|
|||
inodes_usage: Some(0.2),
|
||||
};
|
||||
assert_eq!(
|
||||
DisplayRow::new(row, &options).to_string(),
|
||||
DisplayRow::new(&row, &options).to_string(),
|
||||
"my_device 10 2 8 20% my_mount "
|
||||
);
|
||||
}
|
||||
|
@ -425,7 +496,7 @@ mod tests {
|
|||
fn test_row_display_human_readable_si() {
|
||||
let options = Options {
|
||||
block_size: BlockSize::HumanReadableDecimal,
|
||||
show_fs_type: true,
|
||||
columns: COLUMNS_WITH_FS_TYPE.to_vec(),
|
||||
..Default::default()
|
||||
};
|
||||
let row = Row {
|
||||
|
@ -447,7 +518,7 @@ mod tests {
|
|||
inodes_usage: Some(0.2),
|
||||
};
|
||||
assert_eq!(
|
||||
DisplayRow::new(row, &options).to_string(),
|
||||
DisplayRow::new(&row, &options).to_string(),
|
||||
"my_device my_type 4.0k 1.0k 3.0k 25% my_mount "
|
||||
);
|
||||
}
|
||||
|
@ -456,7 +527,7 @@ mod tests {
|
|||
fn test_row_display_human_readable_binary() {
|
||||
let options = Options {
|
||||
block_size: BlockSize::HumanReadableBinary,
|
||||
show_fs_type: true,
|
||||
columns: COLUMNS_WITH_FS_TYPE.to_vec(),
|
||||
..Default::default()
|
||||
};
|
||||
let row = Row {
|
||||
|
@ -478,8 +549,38 @@ mod tests {
|
|||
inodes_usage: Some(0.2),
|
||||
};
|
||||
assert_eq!(
|
||||
DisplayRow::new(row, &options).to_string(),
|
||||
DisplayRow::new(&row, &options).to_string(),
|
||||
"my_device my_type 4.0Ki 1.0Ki 3.0Ki 25% my_mount "
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_row_display_round_up_usage() {
|
||||
let options = Options {
|
||||
block_size: BlockSize::Bytes(1),
|
||||
..Default::default()
|
||||
};
|
||||
let row = Row {
|
||||
fs_device: "my_device".to_string(),
|
||||
fs_type: "my_type".to_string(),
|
||||
fs_mount: "my_mount".to_string(),
|
||||
|
||||
bytes: 100,
|
||||
bytes_used: 25,
|
||||
bytes_free: 75,
|
||||
bytes_usage: Some(0.251),
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
bytes_capacity: Some(0.5),
|
||||
|
||||
inodes: 10,
|
||||
inodes_used: 2,
|
||||
inodes_free: 8,
|
||||
inodes_usage: Some(0.2),
|
||||
};
|
||||
assert_eq!(
|
||||
DisplayRow::new(&row, &options).to_string(),
|
||||
"my_device 100 25 75 26% my_mount "
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "dircolors ~ (uutils) display commands to set LS_COLORS"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/dircolors"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dircolors"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/dircolors/LICENSE
Symbolic link
1
src/uu/dircolors/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "dirname ~ (uutils) display parent directory of PATHNAME"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/dirname"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dirname"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/dirname/LICENSE
Symbolic link
1
src/uu/dirname/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "du ~ (uutils) display disk usage"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/du"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/du"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/du/LICENSE
Symbolic link
1
src/uu/du/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "echo ~ (uutils) display TEXT"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/echo"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/echo"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/echo/LICENSE
Symbolic link
1
src/uu/echo/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
2
src/uu/env/Cargo.toml
vendored
2
src/uu/env/Cargo.toml
vendored
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "env ~ (uutils) set each NAME to VALUE in the environment and run COMMAND"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/env"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/env"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/env/LICENSE
vendored
Symbolic link
1
src/uu/env/LICENSE
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "expand ~ (uutils) convert input tabs to spaces"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/expand"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/expand"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/expand/LICENSE
Symbolic link
1
src/uu/expand/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "expr ~ (uutils) display the value of EXPRESSION"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/expr"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/expr"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/expr/LICENSE
Symbolic link
1
src/uu/expr/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "factor ~ (uutils) display the prime factors of each NUMBER"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/factor/LICENSE
Symbolic link
1
src/uu/factor/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "false ~ (uutils) do nothing and fail"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/false"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/false"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/false/LICENSE
Symbolic link
1
src/uu/false/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "fmt ~ (uutils) reformat each paragraph of input"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/fmt"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/fmt"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/fmt/LICENSE
Symbolic link
1
src/uu/fmt/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "fold ~ (uutils) wrap each line of input"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/fold"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/fold"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/fold/LICENSE
Symbolic link
1
src/uu/fold/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "groups ~ (uutils) display group memberships for USERNAME"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/groups"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/groups"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/groups/LICENSE
Symbolic link
1
src/uu/groups/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "hashsum ~ (uutils) display or check input digests"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/hashsum"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hashsum"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/hashsum/LICENSE
Symbolic link
1
src/uu/hashsum/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "head ~ (uutils) display the first lines of input"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/head"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/head"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/head/LICENSE
Symbolic link
1
src/uu/head/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "hostid ~ (uutils) display the numeric identifier of the current host"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/hostid"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hostid"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/hostid/LICENSE
Symbolic link
1
src/uu/hostid/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "hostname ~ (uutils) display or set the host name of the current host"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/hostname"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hostname"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/hostname/LICENSE
Symbolic link
1
src/uu/hostname/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "id ~ (uutils) display user and group information for USER"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/id"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/id"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/id/LICENSE
Symbolic link
1
src/uu/id/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -9,7 +9,7 @@ license = "MIT"
|
|||
description = "install ~ (uutils) copy files from SOURCE to DESTINATION (with specified attributes)"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/install"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/install"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/install/LICENSE
Symbolic link
1
src/uu/install/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "join ~ (uutils) merge lines from inputs with matching join fields"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/join"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/join"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/join/LICENSE
Symbolic link
1
src/uu/join/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "kill ~ (uutils) send a signal to a process"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/kill"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/kill"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/kill/LICENSE
Symbolic link
1
src/uu/kill/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "link ~ (uutils) create a hard (file system) link to FILE"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/link"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/link"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/link/LICENSE
Symbolic link
1
src/uu/link/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "ln ~ (uutils) create a (file system) link to TARGET"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/ln"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ln"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
1
src/uu/ln/LICENSE
Symbolic link
1
src/uu/ln/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
description = "logname ~ (uutils) display the login name of the current user"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/master/src/uu/logname"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/logname"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue