Merge branch 'main' into mkdir-fix

This commit is contained in:
Sylvestre Ledru 2022-03-15 12:03:47 +01:00 committed by GitHub
commit 9dea9b4b83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
246 changed files with 2840 additions and 942 deletions

7
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 5

View file

@ -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:

View file

@ -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

View file

@ -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
View file

@ -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]]

View file

@ -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"] }

View file

@ -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

View file

@ -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
View file

@ -0,0 +1,5 @@
{
"extends": [
"config:base"
]
}

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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());
}
}
}

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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")))]

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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());
}

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

197
src/uu/df/src/blocks.rs Normal file
View 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
View 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(()),
}
}
}

View file

@ -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());
}
}
}

View 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 })
}
}

View file

@ -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 "
);
}
}

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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
View file

@ -0,0 +1 @@
../../../LICENSE

View file

@ -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