From 79e5d80e3eec820063d82725b7237ce92707b674 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Fri, 6 May 2022 14:01:23 +0200 Subject: [PATCH 01/24] stat: improve handling of stdin/fifo (fix #3485) * fix https://github.com/uutils/coreutils/issues/3485 * improve the workaround from #3280 * add tests --- src/uu/stat/src/stat.rs | 81 ++++++++++++++++------------ src/uucore/src/lib/features/fsext.rs | 5 +- tests/by-util/test_stat.rs | 34 +++++++++--- 3 files changed, 78 insertions(+), 42 deletions(-) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index cebc6c108..41c9ae4a9 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -19,7 +19,9 @@ use uucore::{entries, format_usage}; use clap::{crate_version, Arg, ArgMatches, Command}; use std::borrow::Cow; use std::convert::AsRef; +use std::ffi::{OsStr, OsString}; use std::os::unix::fs::{FileTypeExt, MetadataExt}; +use std::os::unix::prelude::OsStrExt; use std::path::Path; use std::{cmp, fs, iter}; @@ -221,7 +223,7 @@ pub struct Stater { follow: bool, show_fs: bool, from_user: bool, - files: Vec, + files: Vec, mount_list: Option>, default_tokens: Vec, default_dev_tokens: Vec, @@ -471,24 +473,10 @@ impl Stater { } fn new(matches: &ArgMatches) -> UResult { - let mut files: Vec = matches - .values_of(ARG_FILES) - .map(|v| v.map(ToString::to_string).collect()) + let files = matches + .values_of_os(ARG_FILES) + .map(|v| v.map(OsString::from).collect()) .unwrap_or_default(); - #[cfg(unix)] - if files.contains(&String::from("-")) { - let redirected_path = Path::new("/dev/stdin") - .canonicalize() - .expect("unable to canonicalize /dev/stdin") - .into_os_string() - .into_string() - .unwrap(); - for file in &mut files { - if file == "-" { - *file = redirected_path.clone(); - } - } - } let format_str = if matches.is_present(options::PRINTF) { matches .value_of(options::PRINTF) @@ -550,19 +538,37 @@ impl Stater { } fn exec(&self) -> i32 { + let mut stdin_is_fifo = false; + if cfg!(unix) { + if let Ok(md) = fs::metadata("/dev/stdin") { + stdin_is_fifo = md.file_type().is_fifo(); + } + } + let mut ret = 0; for f in &self.files { - ret |= self.do_stat(f.as_str()); + ret |= self.do_stat(f, stdin_is_fifo); } ret } - fn do_stat(&self, file: &str) -> i32 { - if !self.show_fs { - let result = if self.follow { - fs::metadata(file) + fn do_stat(&self, file: &OsStr, stdin_is_fifo: bool) -> i32 { + let display_name = file.to_string_lossy(); + let file: OsString = if cfg!(unix) && display_name.eq("-") { + if let Ok(p) = Path::new("/dev/stdin").canonicalize() { + p.into_os_string() } else { - fs::symlink_metadata(file) + OsString::from("/dev/stdin") + } + } else { + OsString::from(file) + }; + + if !self.show_fs { + let result = if self.follow || stdin_is_fifo && display_name.eq("-") { + fs::metadata(&file) + } else { + fs::symlink_metadata(&file) }; match result { Ok(meta) => { @@ -658,28 +664,32 @@ impl Stater { // mount point 'm' => { - arg = self.find_mount_point(file).unwrap(); + arg = self.find_mount_point(&file).unwrap(); output_type = OutputType::Str; } // file name 'n' => { - arg = file.to_owned(); + arg = display_name.to_string(); output_type = OutputType::Str; } // quoted file name with dereference if symbolic link 'N' => { if file_type.is_symlink() { - let dst = match fs::read_link(file) { + let dst = match fs::read_link(&file) { Ok(path) => path, Err(e) => { println!("{}", e); return 1; } }; - arg = format!("{} -> {}", file.quote(), dst.quote()); + arg = format!( + "{} -> {}", + display_name.quote(), + dst.quote() + ); } else { - arg = file.to_string(); + arg = display_name.to_string(); } output_type = OutputType::Str; } @@ -771,12 +781,16 @@ impl Stater { } } Err(e) => { - show_error!("cannot stat {}: {}", file.quote(), e); + show_error!("cannot stat {}: {}", display_name.quote(), e); return 1; } } } else { - match statfs(file) { + #[cfg(unix)] + let p = file.as_bytes(); + #[cfg(not(unix))] + let p = file.into_string().unwrap(); + match statfs(p) { Ok(meta) => { let tokens = &self.default_tokens; @@ -829,7 +843,7 @@ impl Stater { } // file name 'n' => { - arg = file.to_owned(); + arg = display_name.to_string(); output_type = OutputType::Str; } // block size (for faster transfers) @@ -866,7 +880,7 @@ impl Stater { Err(e) => { show_error!( "cannot read file system information for {}: {}", - file.quote(), + display_name.quote(), e ); return 1; @@ -1028,6 +1042,7 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(ARG_FILES) .multiple_occurrences(true) .takes_value(true) + .allow_invalid_utf8(true) .min_values(1), ) } diff --git a/src/uucore/src/lib/features/fsext.rs b/src/uucore/src/lib/features/fsext.rs index eeaf54061..511c1dd2c 100644 --- a/src/uucore/src/lib/features/fsext.rs +++ b/src/uucore/src/lib/features/fsext.rs @@ -84,6 +84,7 @@ use std::ffi::CString; use std::io::Error as IOError; #[cfg(unix)] use std::mem; +#[cfg(not(unix))] use std::path::Path; use std::time::UNIX_EPOCH; @@ -708,9 +709,9 @@ impl FsMeta for StatFs { } #[cfg(unix)] -pub fn statfs>(path: P) -> Result +pub fn statfs

(path: P) -> Result where - Vec: From

, + P: Into>, { match CString::new(path) { Ok(p) => { diff --git a/tests/by-util/test_stat.rs b/tests/by-util/test_stat.rs index 53e83bef6..c3ffb3be5 100644 --- a/tests/by-util/test_stat.rs +++ b/tests/by-util/test_stat.rs @@ -312,9 +312,21 @@ fn test_printf() { ts.ucmd().args(&args).succeeds().stdout_is(expected_stdout); } -#[cfg(unix)] #[test] -#[cfg(disable_until_fixed)] +#[cfg(unix)] +fn test_pipe_fifo() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkfifo("FIFO"); + ucmd.arg("FIFO") + .run() + .no_stderr() + .stdout_contains("fifo") + .stdout_contains("File: FIFO") + .succeeded(); +} + +#[test] +#[cfg(target_os = "linux")] fn test_stdin_pipe_fifo1() { // $ echo | stat - // File: - @@ -328,17 +340,26 @@ fn test_stdin_pipe_fifo1() { .stdout_contains("fifo") .stdout_contains("File: -") .succeeded(); + + new_ucmd!() + .args(&["-L", "-"]) + .set_stdin(std::process::Stdio::piped()) + .run() + .no_stderr() + .stdout_contains("fifo") + .stdout_contains("File: -") + .succeeded(); } -#[cfg(unix)] #[test] -#[cfg(disable_until_fixed)] +#[cfg(target_os = "linux")] fn test_stdin_pipe_fifo2() { // $ stat - // File: - // Size: 0 Blocks: 0 IO Block: 1024 character special file new_ucmd!() .arg("-") + .set_stdin(std::process::Stdio::null()) .run() .no_stderr() .stdout_contains("character special file") @@ -346,9 +367,8 @@ fn test_stdin_pipe_fifo2() { .succeeded(); } -#[cfg(unix)] #[test] -#[cfg(disable_until_fixed)] +#[cfg(target_os = "linux")] fn test_stdin_redirect() { // $ touch f && stat - < f // File: - @@ -359,7 +379,7 @@ fn test_stdin_redirect() { at.touch("f"); new_ucmd!() .arg("-") - .set_stdin(std::fs::File::open("f").unwrap()) + .set_stdin(std::fs::File::open(at.plus("f")).unwrap()) .run() .no_stderr() .stdout_contains("regular empty file") From eeec680c37bbc842723d59c2a7cc3c4b865e3225 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 May 2022 06:38:03 +0000 Subject: [PATCH 02/24] build(deps): bump codecov/codecov-action from 1 to 3 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 1 to 3. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v1...v3) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/CICD.yml | 2 +- .github/workflows/GnuTests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 04acd4c18..ebe82e47a 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -1100,7 +1100,7 @@ jobs: grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" echo ::set-output name=report::${COVERAGE_REPORT_FILE} - name: Upload coverage results (to Codecov.io) - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 # if: steps.vars.outputs.HAS_CODECOV_TOKEN with: # token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml index c8d3c8b94..75cb60d89 100644 --- a/.github/workflows/GnuTests.yml +++ b/.github/workflows/GnuTests.yml @@ -293,7 +293,7 @@ jobs: grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" echo ::set-output name=report::${COVERAGE_REPORT_FILE} - name: Upload coverage results (to Codecov.io) - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: file: ${{ steps.coverage.outputs.report }} flags: gnutests From 376f6a158b17fdf4b469903877957f8d162d0fc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 May 2022 06:38:05 +0000 Subject: [PATCH 03/24] build(deps): bump EndBug/add-and-commit from 7 to 9 Bumps [EndBug/add-and-commit](https://github.com/EndBug/add-and-commit) from 7 to 9. - [Release notes](https://github.com/EndBug/add-and-commit/releases) - [Changelog](https://github.com/EndBug/add-and-commit/blob/main/CHANGELOG.md) - [Commits](https://github.com/EndBug/add-and-commit/compare/v7...v9) --- updated-dependencies: - dependency-name: EndBug/add-and-commit dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/FixPR.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/FixPR.yml b/.github/workflows/FixPR.yml index 2a5382e27..00e0a608d 100644 --- a/.github/workflows/FixPR.yml +++ b/.github/workflows/FixPR.yml @@ -80,7 +80,7 @@ jobs: ## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors RUSTUP_TOOLCHAIN=stable cargo-tree tree --locked --all --no-dev-dependencies --no-indent --features ${{ matrix.job.features }} | grep -vE "$PWD" | sort --unique - name: Commit any changes (to '${{ env.BRANCH_TARGET }}') - uses: EndBug/add-and-commit@v7 + uses: EndBug/add-and-commit@v9 with: branch: ${{ env.BRANCH_TARGET }} default_author: github_actions @@ -130,7 +130,7 @@ jobs: # `cargo fmt` of tests find tests -name "*.rs" -print0 | xargs -0 cargo fmt -- - name: Commit any changes (to '${{ env.BRANCH_TARGET }}') - uses: EndBug/add-and-commit@v7 + uses: EndBug/add-and-commit@v9 with: branch: ${{ env.BRANCH_TARGET }} default_author: github_actions From 372d460d7a24c3bccad817a9ba9ff9eac71b6f53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 May 2022 06:38:08 +0000 Subject: [PATCH 04/24] build(deps): bump actions/cache from 2 to 3 Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 04acd4c18..5d49ed363 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -859,7 +859,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: AVD cache - uses: actions/cache@v2 + uses: actions/cache@v3 id: avd-cache with: path: | From fd057574d7fbd8381f9f63c2f9c939f7055c9e6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 May 2022 06:38:11 +0000 Subject: [PATCH 05/24] build(deps): bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/CICD.yml | 34 +++++++++++++++++----------------- .github/workflows/FixPR.yml | 4 ++-- .github/workflows/GnuTests.yml | 8 ++++---- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 04acd4c18..c6b7f10a8 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -24,7 +24,7 @@ jobs: name: Style/cargo-deny runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: EmbarkStudios/cargo-deny-action@v1 style_deps: @@ -43,7 +43,7 @@ jobs: - { os: macos-latest , features: "feat_Tier1,feat_require_unix,feat_require_unix_utmpx" } - { os: windows-latest , features: feat_os_windows } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Initialize workflow variables id: vars @@ -101,7 +101,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Initialize workflow variables id: vars @@ -165,7 +165,7 @@ jobs: - { os: macos-latest , features: feat_os_macos } - { os: windows-latest , features: feat_os_windows } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Initialize workflow variables id: vars @@ -223,7 +223,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Initialize workflow variables id: vars @@ -275,7 +275,7 @@ jobs: # - { os: macos-latest , features: feat_os_macos } # - { os: windows-latest , features: feat_os_windows } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Initialize workflow variables id: vars @@ -320,7 +320,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Initialize workflow variables id: vars @@ -397,7 +397,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Install `rust` toolchain uses: actions-rs/toolchain@v1 @@ -422,7 +422,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Install `rust` toolchain uses: actions-rs/toolchain@v1 @@ -452,7 +452,7 @@ jobs: - { os: macos-latest , features: feat_os_macos } - { os: windows-latest , features: feat_os_windows } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Install `rust` toolchain uses: actions-rs/toolchain@v1 @@ -478,7 +478,7 @@ jobs: - { os: macos-latest , features: feat_os_macos } - { os: windows-latest , features: feat_os_windows } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Install `rust` toolchain uses: actions-rs/toolchain@v1 @@ -502,7 +502,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Install dependencies shell: bash @@ -568,7 +568,7 @@ jobs: - { 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 } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Initialize workflow variables id: vars @@ -820,7 +820,7 @@ jobs: job: - { os: ubuntu-latest } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Install/setup prerequisites shell: bash @@ -857,7 +857,7 @@ jobs: env: TERMUX: v0.118.0 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: AVD cache uses: actions/cache@v2 id: avd-cache @@ -911,7 +911,7 @@ jobs: env: mem: 2048 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Prepare, build and test ## spell-checker:ignore (ToDO) sshfs usesh vmactions @@ -979,7 +979,7 @@ jobs: - { os: macos-latest , features: macos } - { os: windows-latest , features: windows } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 # - name: Reattach HEAD ## may be needed for accurate code coverage info # run: git checkout ${{ github.head_ref }} diff --git a/.github/workflows/FixPR.yml b/.github/workflows/FixPR.yml index 2a5382e27..67e914746 100644 --- a/.github/workflows/FixPR.yml +++ b/.github/workflows/FixPR.yml @@ -28,7 +28,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Initialize job variables id: vars @@ -100,7 +100,7 @@ jobs: job: - { os: ubuntu-latest , features: feat_os_unix } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v1 - name: Initialize job variables id: vars diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml index c8d3c8b94..ef919dde9 100644 --- a/.github/workflows/GnuTests.yml +++ b/.github/workflows/GnuTests.yml @@ -41,11 +41,11 @@ jobs: TEST_FULL_SUMMARY_FILE='gnu-full-result.json' outputs SUITE_LOG_FILE TEST_FILESET_PREFIX TEST_FILESET_SUFFIX TEST_LOGS_GLOB TEST_SUMMARY_FILE TEST_FULL_SUMMARY_FILE - name: Checkout code (uutil) - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: '${{ steps.vars.outputs.path_UUTILS }}' - name: Checkout code (GNU coreutils) - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: 'coreutils/coreutils' path: '${{ steps.vars.outputs.path_GNU }}' @@ -229,11 +229,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code uutil - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: 'uutils' - name: Checkout GNU coreutils - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: 'coreutils/coreutils' path: 'gnu' From 28c6403ffa78d29ea75862f65ac03743d35ab4d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 May 2022 06:38:13 +0000 Subject: [PATCH 06/24] build(deps): bump actions/upload-artifact from 2 to 3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/CICD.yml | 4 ++-- .github/workflows/GnuTests.yml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 04acd4c18..0e4a4807d 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -534,7 +534,7 @@ jobs: --arg size "$SIZE" \ --arg multisize "$SIZEMULTI" \ '{($date): { sha: $sha, size: $size, multisize: $multisize, }}' > size-result.json - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: size-result path: size-result.json @@ -762,7 +762,7 @@ jobs: args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} toolchain: ${{ env.RUST_MIN_SRV }} - name: Archive executable artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.PROJECT_NAME }}-${{ matrix.job.target }} path: target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }} diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml index c8d3c8b94..24b362a04 100644 --- a/.github/workflows/GnuTests.yml +++ b/.github/workflows/GnuTests.yml @@ -146,22 +146,22 @@ jobs: # Compress logs before upload (fails otherwise) gzip ${{ steps.vars.outputs.TEST_LOGS_GLOB }} - name: Reserve SHA1/ID of 'test-summary' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: "${{ steps.summary.outputs.HASH }}" path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}" - name: Reserve test results summary - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: test-summary path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}" - name: Reserve test logs - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: test-logs path: "${{ steps.vars.outputs.TEST_LOGS_GLOB }}" - name: Upload full json results - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: gnu-full-result.json path: ${{ steps.vars.outputs.TEST_FULL_SUMMARY_FILE }} From d906f09e6ed84c83360b26caff70b1e15cde9406 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Fri, 6 May 2022 14:01:23 +0200 Subject: [PATCH 07/24] stat: improve handling of stdin/fifo (fix #3485) * fix https://github.com/uutils/coreutils/issues/3485 * improve the workaround from #3280 * add tests --- src/uu/stat/src/stat.rs | 81 ++++++++++++++++------------ src/uucore/src/lib/features/fsext.rs | 5 +- tests/by-util/test_stat.rs | 34 +++++++++--- 3 files changed, 78 insertions(+), 42 deletions(-) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index cebc6c108..41c9ae4a9 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -19,7 +19,9 @@ use uucore::{entries, format_usage}; use clap::{crate_version, Arg, ArgMatches, Command}; use std::borrow::Cow; use std::convert::AsRef; +use std::ffi::{OsStr, OsString}; use std::os::unix::fs::{FileTypeExt, MetadataExt}; +use std::os::unix::prelude::OsStrExt; use std::path::Path; use std::{cmp, fs, iter}; @@ -221,7 +223,7 @@ pub struct Stater { follow: bool, show_fs: bool, from_user: bool, - files: Vec, + files: Vec, mount_list: Option>, default_tokens: Vec, default_dev_tokens: Vec, @@ -471,24 +473,10 @@ impl Stater { } fn new(matches: &ArgMatches) -> UResult { - let mut files: Vec = matches - .values_of(ARG_FILES) - .map(|v| v.map(ToString::to_string).collect()) + let files = matches + .values_of_os(ARG_FILES) + .map(|v| v.map(OsString::from).collect()) .unwrap_or_default(); - #[cfg(unix)] - if files.contains(&String::from("-")) { - let redirected_path = Path::new("/dev/stdin") - .canonicalize() - .expect("unable to canonicalize /dev/stdin") - .into_os_string() - .into_string() - .unwrap(); - for file in &mut files { - if file == "-" { - *file = redirected_path.clone(); - } - } - } let format_str = if matches.is_present(options::PRINTF) { matches .value_of(options::PRINTF) @@ -550,19 +538,37 @@ impl Stater { } fn exec(&self) -> i32 { + let mut stdin_is_fifo = false; + if cfg!(unix) { + if let Ok(md) = fs::metadata("/dev/stdin") { + stdin_is_fifo = md.file_type().is_fifo(); + } + } + let mut ret = 0; for f in &self.files { - ret |= self.do_stat(f.as_str()); + ret |= self.do_stat(f, stdin_is_fifo); } ret } - fn do_stat(&self, file: &str) -> i32 { - if !self.show_fs { - let result = if self.follow { - fs::metadata(file) + fn do_stat(&self, file: &OsStr, stdin_is_fifo: bool) -> i32 { + let display_name = file.to_string_lossy(); + let file: OsString = if cfg!(unix) && display_name.eq("-") { + if let Ok(p) = Path::new("/dev/stdin").canonicalize() { + p.into_os_string() } else { - fs::symlink_metadata(file) + OsString::from("/dev/stdin") + } + } else { + OsString::from(file) + }; + + if !self.show_fs { + let result = if self.follow || stdin_is_fifo && display_name.eq("-") { + fs::metadata(&file) + } else { + fs::symlink_metadata(&file) }; match result { Ok(meta) => { @@ -658,28 +664,32 @@ impl Stater { // mount point 'm' => { - arg = self.find_mount_point(file).unwrap(); + arg = self.find_mount_point(&file).unwrap(); output_type = OutputType::Str; } // file name 'n' => { - arg = file.to_owned(); + arg = display_name.to_string(); output_type = OutputType::Str; } // quoted file name with dereference if symbolic link 'N' => { if file_type.is_symlink() { - let dst = match fs::read_link(file) { + let dst = match fs::read_link(&file) { Ok(path) => path, Err(e) => { println!("{}", e); return 1; } }; - arg = format!("{} -> {}", file.quote(), dst.quote()); + arg = format!( + "{} -> {}", + display_name.quote(), + dst.quote() + ); } else { - arg = file.to_string(); + arg = display_name.to_string(); } output_type = OutputType::Str; } @@ -771,12 +781,16 @@ impl Stater { } } Err(e) => { - show_error!("cannot stat {}: {}", file.quote(), e); + show_error!("cannot stat {}: {}", display_name.quote(), e); return 1; } } } else { - match statfs(file) { + #[cfg(unix)] + let p = file.as_bytes(); + #[cfg(not(unix))] + let p = file.into_string().unwrap(); + match statfs(p) { Ok(meta) => { let tokens = &self.default_tokens; @@ -829,7 +843,7 @@ impl Stater { } // file name 'n' => { - arg = file.to_owned(); + arg = display_name.to_string(); output_type = OutputType::Str; } // block size (for faster transfers) @@ -866,7 +880,7 @@ impl Stater { Err(e) => { show_error!( "cannot read file system information for {}: {}", - file.quote(), + display_name.quote(), e ); return 1; @@ -1028,6 +1042,7 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(ARG_FILES) .multiple_occurrences(true) .takes_value(true) + .allow_invalid_utf8(true) .min_values(1), ) } diff --git a/src/uucore/src/lib/features/fsext.rs b/src/uucore/src/lib/features/fsext.rs index 3d7ca1c1f..787684e93 100644 --- a/src/uucore/src/lib/features/fsext.rs +++ b/src/uucore/src/lib/features/fsext.rs @@ -85,6 +85,7 @@ use std::ffi::CString; use std::io::Error as IOError; #[cfg(unix)] use std::mem; +#[cfg(not(unix))] use std::path::Path; use std::time::UNIX_EPOCH; @@ -709,9 +710,9 @@ impl FsMeta for StatFs { } #[cfg(unix)] -pub fn statfs>(path: P) -> Result +pub fn statfs

(path: P) -> Result where - Vec: From

, + P: Into>, { match CString::new(path) { Ok(p) => { diff --git a/tests/by-util/test_stat.rs b/tests/by-util/test_stat.rs index f1a687981..14aba96fc 100644 --- a/tests/by-util/test_stat.rs +++ b/tests/by-util/test_stat.rs @@ -346,9 +346,21 @@ fn test_printf() { ts.ucmd().args(&args).succeeds().stdout_is(expected_stdout); } -#[cfg(unix)] #[test] -#[cfg(disable_until_fixed)] +#[cfg(unix)] +fn test_pipe_fifo() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkfifo("FIFO"); + ucmd.arg("FIFO") + .run() + .no_stderr() + .stdout_contains("fifo") + .stdout_contains("File: FIFO") + .succeeded(); +} + +#[test] +#[cfg(target_os = "linux")] fn test_stdin_pipe_fifo1() { // $ echo | stat - // File: - @@ -362,17 +374,26 @@ fn test_stdin_pipe_fifo1() { .stdout_contains("fifo") .stdout_contains("File: -") .succeeded(); + + new_ucmd!() + .args(&["-L", "-"]) + .set_stdin(std::process::Stdio::piped()) + .run() + .no_stderr() + .stdout_contains("fifo") + .stdout_contains("File: -") + .succeeded(); } -#[cfg(unix)] #[test] -#[cfg(disable_until_fixed)] +#[cfg(target_os = "linux")] fn test_stdin_pipe_fifo2() { // $ stat - // File: - // Size: 0 Blocks: 0 IO Block: 1024 character special file new_ucmd!() .arg("-") + .set_stdin(std::process::Stdio::null()) .run() .no_stderr() .stdout_contains("character special file") @@ -380,9 +401,8 @@ fn test_stdin_pipe_fifo2() { .succeeded(); } -#[cfg(unix)] #[test] -#[cfg(disable_until_fixed)] +#[cfg(target_os = "linux")] fn test_stdin_redirect() { // $ touch f && stat - < f // File: - @@ -393,7 +413,7 @@ fn test_stdin_redirect() { at.touch("f"); new_ucmd!() .arg("-") - .set_stdin(std::fs::File::open("f").unwrap()) + .set_stdin(std::fs::File::open(at.plus("f")).unwrap()) .run() .no_stderr() .stdout_contains("regular empty file") From f9a5d758f0dfe86d2cbe10e50f7dce04fded3101 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 May 2022 21:16:35 +0000 Subject: [PATCH 08/24] build(deps): bump clap_complete from 3.1.3 to 3.1.4 Bumps [clap_complete](https://github.com/clap-rs/clap) from 3.1.3 to 3.1.4. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v3.1.3...clap_complete-v3.1.4) --- updated-dependencies: - dependency-name: clap_complete dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1079f8a65..608654e69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,9 +273,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7ca9141e27e6ebc52e3c378b0c07f3cea52db46ed1cc5861735fb697b56356" +checksum = "da92e6facd8d73c22745a5d3cbb59bdf8e46e3235c923e516527d8e81eec14a4" dependencies = [ "clap 3.1.15", ] From b2eb4e83917e13511b070c4a7f2c494bf1224e2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 May 2022 21:16:50 +0000 Subject: [PATCH 09/24] build(deps): bump unindent from 0.1.8 to 0.1.9 Bumps [unindent](https://github.com/dtolnay/indoc) from 0.1.8 to 0.1.9. - [Release notes](https://github.com/dtolnay/indoc/releases) - [Commits](https://github.com/dtolnay/indoc/compare/0.1.8...0.1.9) --- updated-dependencies: - dependency-name: unindent dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1079f8a65..d25d27e0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2027,9 +2027,9 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "unindent" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8" +checksum = "52fee519a3e570f7df377a06a1a7775cdbfb7aa460be7e08de2b1f0e69973a44" [[package]] name = "unix_socket" From 2874f1895097f112daf6d40b999da434681590bd Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Thu, 5 May 2022 18:05:16 -0400 Subject: [PATCH 10/24] mktemp: error on path separator in template prefix Correct the error that arises from a path separator in the prefix portion of a template argument provided to `mktemp`. Before this commit, the error message was incorrect: $ mktemp -t a/bXXX mktemp: failed to create file via template 'a/bXXX': No such file or directory (os error 2) at path "/tmp/a/bege" After this commit, the error message is correct: $ mktemp -t a/bXXX mktemp: invalid template, 'a/bXXX', contains directory separator The code was failing to check for a path separator in the prefix portion of the template. --- src/uu/mktemp/src/mktemp.rs | 28 ++++++++++++++++++++-------- tests/by-util/test_mktemp.rs | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index b14318679..94def4874 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -41,7 +41,12 @@ enum MkTempError { PersistError(PathBuf), MustEndInX(String), TooFewXs(String), - ContainsDirSeparator(String), + + /// The template prefix contains a path separator (e.g. `"a/bXXX"`). + PrefixContainsDirSeparator(String), + + /// The template suffix contains a path separator (e.g. `"XXXa/b"`). + SuffixContainsDirSeparator(String), InvalidTemplate(String), } @@ -56,7 +61,14 @@ impl Display for MkTempError { PersistError(p) => write!(f, "could not persist file {}", p.quote()), MustEndInX(s) => write!(f, "with --suffix, template {} must end in X", s.quote()), TooFewXs(s) => write!(f, "too few X's in template {}", s.quote()), - ContainsDirSeparator(s) => { + PrefixContainsDirSeparator(s) => { + write!( + f, + "invalid template, {}, contains directory separator", + s.quote() + ) + } + SuffixContainsDirSeparator(s) => { write!( f, "invalid suffix {}, contains directory separator", @@ -252,8 +264,12 @@ fn parse_template<'a>( } }; + if prefix.chars().any(is_separator) { + return Err(MkTempError::PrefixContainsDirSeparator(temp.into())); + } + if suf.chars().any(is_separator) { - return Err(MkTempError::ContainsDirSeparator(suf.into())); + return Err(MkTempError::SuffixContainsDirSeparator(suf.into())); } Ok((prefix, rand, suf)) @@ -352,11 +368,7 @@ mod tests { #[test] fn test_parse_template_errors() { - // TODO This should be an error as well, but we are not - // catching it just yet. A future commit will correct this. - // - // assert!(parse_template("a/bXXX", None).is_err()); - // + assert!(parse_template("a/bXXX", None).is_err()); assert!(parse_template("XXXa/b", None).is_err()); assert!(parse_template("XX", None).is_err()); assert!(parse_template("XXXabc", Some("def")).is_err()); diff --git a/tests/by-util/test_mktemp.rs b/tests/by-util/test_mktemp.rs index c28efc37b..9fce0e591 100644 --- a/tests/by-util/test_mktemp.rs +++ b/tests/by-util/test_mktemp.rs @@ -2,6 +2,8 @@ use crate::common::util::*; +use uucore::display::Quotable; + use std::path::PathBuf; use tempfile::tempdir; @@ -482,3 +484,15 @@ fn test_respect_template_directory() { assert_matches_template!(template, filename); assert!(at.file_exists(filename)); } + +/// Test that a template with a path separator is invalid. +#[test] +fn test_template_path_separator() { + new_ucmd!() + .args(&["-t", "a/bXXX"]) + .fails() + .stderr_only(format!( + "mktemp: invalid template, {}, contains directory separator\n", + "a/bXXX".quote() + )); +} From e26fed61b34caec8a5e1325275e1e5a5bfa2152a Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 10 May 2022 07:34:01 +0200 Subject: [PATCH 11/24] df: implement POSIX conform header line It also fixes #3195 --- src/uu/df/src/blocks.rs | 9 +++++ src/uu/df/src/df.rs | 18 ++++++++++ src/uu/df/src/table.rs | 66 +++++++++++++++++++++++++++--------- tests/by-util/test_df.rs | 73 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 147 insertions(+), 19 deletions(-) diff --git a/src/uu/df/src/blocks.rs b/src/uu/df/src/blocks.rs index bb6e06333..9a7e5c580 100644 --- a/src/uu/df/src/blocks.rs +++ b/src/uu/df/src/blocks.rs @@ -167,6 +167,15 @@ pub(crate) enum BlockSize { Bytes(u64), } +impl BlockSize { + /// Returns the associated value + pub(crate) fn as_u64(&self) -> u64 { + match *self { + Self::Bytes(n) => n, + } + } +} + impl Default for BlockSize { fn default() -> Self { if env::var("POSIXLY_CORRECT").is_ok() { diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 192cbcadf..a11dcf7a0 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -12,6 +12,7 @@ mod filesystem; mod table; use blocks::{HumanReadable, SizeFormat}; +use table::HeaderMode; use uucore::display::Quotable; use uucore::error::{UError, UResult, USimpleError}; use uucore::fsext::{read_fs_list, MountInfo}; @@ -72,6 +73,7 @@ struct Options { show_all_fs: bool, size_format: SizeFormat, block_size: BlockSize, + header_mode: HeaderMode, /// Optional list of filesystem types to include in the output table. /// @@ -99,6 +101,7 @@ impl Default for Options { show_all_fs: Default::default(), block_size: Default::default(), size_format: Default::default(), + header_mode: Default::default(), include: Default::default(), exclude: Default::default(), show_total: Default::default(), @@ -176,6 +179,21 @@ impl Options { ), ParseSizeError::ParseFailure(s) => OptionsError::InvalidBlockSize(s), })?, + header_mode: { + if matches.is_present(OPT_HUMAN_READABLE_BINARY) + || matches.is_present(OPT_HUMAN_READABLE_DECIMAL) + { + HeaderMode::HumanReadable + } else if matches.is_present(OPT_PORTABILITY) { + HeaderMode::PosixPortability + // is_present() doesn't work here, it always returns true because OPT_OUTPUT has + // default values and hence is always present + } else if matches.occurrences_of(OPT_OUTPUT) > 0 { + HeaderMode::Output + } else { + HeaderMode::Default + } + }, size_format: { if matches.is_present(OPT_HUMAN_READABLE_BINARY) { SizeFormat::HumanReadable(HumanReadable::Binary) diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 5309da38f..a9a37cfeb 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -289,6 +289,23 @@ impl<'a> RowFormatter<'a> { } } +/// A HeaderMode defines what header labels should be shown. +pub(crate) enum HeaderMode { + Default, + // the user used -h or -H + HumanReadable, + // the user used -P + PosixPortability, + // the user used --output + Output, +} + +impl Default for HeaderMode { + fn default() -> Self { + Self::Default + } +} + /// The data of the header row. struct Header {} @@ -302,15 +319,22 @@ impl Header { for column in &options.columns { let header = match column { Column::Source => String::from("Filesystem"), - Column::Size => match options.size_format { - SizeFormat::HumanReadable(_) => String::from("Size"), - SizeFormat::StaticBlockSize => { - format!("{}-blocks", options.block_size) + Column::Size => match options.header_mode { + HeaderMode::HumanReadable => String::from("Size"), + HeaderMode::PosixPortability => { + format!("{}-blocks", options.block_size.as_u64()) } + _ => format!("{}-blocks", options.block_size), }, Column::Used => String::from("Used"), - Column::Avail => String::from("Available"), - Column::Pcent => String::from("Use%"), + Column::Avail => match options.header_mode { + HeaderMode::HumanReadable | HeaderMode::Output => String::from("Avail"), + _ => String::from("Available"), + }, + Column::Pcent => match options.header_mode { + HeaderMode::PosixPortability => String::from("Capacity"), + _ => String::from("Use%"), + }, Column::Target => String::from("Mounted on"), Column::Itotal => String::from("Inodes"), Column::Iused => String::from("IUsed"), @@ -428,7 +452,7 @@ mod tests { use crate::blocks::{HumanReadable, SizeFormat}; use crate::columns::Column; - use crate::table::{Header, Row, RowFormatter}; + use crate::table::{Header, HeaderMode, Row, RowFormatter}; use crate::{BlockSize, Options}; const COLUMNS_WITH_FS_TYPE: [Column; 7] = [ @@ -548,37 +572,49 @@ mod tests { } #[test] - fn test_header_with_human_readable_binary() { + fn test_human_readable_header() { let options = Options { - size_format: SizeFormat::HumanReadable(HumanReadable::Binary), + header_mode: HeaderMode::HumanReadable, + ..Default::default() + }; + assert_eq!( + Header::get_headers(&options), + vec!("Filesystem", "Size", "Used", "Avail", "Use%", "Mounted on") + ); + } + + #[test] + fn test_posix_portability_header() { + let options = Options { + header_mode: HeaderMode::PosixPortability, ..Default::default() }; assert_eq!( Header::get_headers(&options), vec!( "Filesystem", - "Size", + "1024-blocks", "Used", "Available", - "Use%", + "Capacity", "Mounted on" ) ); } #[test] - fn test_header_with_human_readable_si() { + fn test_output_header() { let options = Options { - size_format: SizeFormat::HumanReadable(HumanReadable::Decimal), + header_mode: HeaderMode::Output, ..Default::default() }; assert_eq!( Header::get_headers(&options), vec!( "Filesystem", - "Size", + "1K-blocks", "Used", - "Available", + "Avail", "Use%", "Mounted on" ) diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 81a9eef72..3c5bfdeea 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -73,7 +73,7 @@ fn test_df_output() { "Filesystem", "Size", "Used", - "Available", + "Avail", "Capacity", "Use%", "Mounted", @@ -84,7 +84,7 @@ fn test_df_output() { "Filesystem", "Size", "Used", - "Available", + "Avail", "Use%", "Mounted", "on", @@ -107,7 +107,7 @@ fn test_df_output_overridden() { "Filesystem", "Size", "Used", - "Available", + "Avail", "Capacity", "Use%", "Mounted", @@ -118,7 +118,7 @@ fn test_df_output_overridden() { "Filesystem", "Size", "Used", - "Available", + "Avail", "Use%", "Mounted", "on", @@ -134,6 +134,46 @@ fn test_df_output_overridden() { assert_eq!(actual, expected); } +#[test] +fn test_default_headers() { + let expected = if cfg!(target_os = "macos") { + vec![ + "Filesystem", + "1K-blocks", + "Used", + "Available", + "Capacity", + "Use%", + "Mounted", + "on", + ] + } else { + vec![ + "Filesystem", + "1K-blocks", + "Used", + "Available", + "Use%", + "Mounted", + "on", + ] + }; + let output = new_ucmd!().succeeds().stdout_move_str(); + let actual = output.lines().take(1).collect::>()[0]; + let actual = actual.split_whitespace().collect::>(); + assert_eq!(actual, expected); +} + +#[test] +fn test_precedence_of_human_readable_header_over_output_header() { + let output = new_ucmd!() + .args(&["-H", "--output=size"]) + .succeeds() + .stdout_move_str(); + let header = output.lines().next().unwrap().to_string(); + assert_eq!(header.trim(), "Size"); +} + #[test] fn test_total_option_with_single_dash() { // These should fail because `-total` should have two dashes, @@ -443,6 +483,31 @@ fn test_block_size_with_suffix() { assert_eq!(get_header("1GB"), "1GB-blocks"); } +#[test] +fn test_block_size_in_posix_portability_mode() { + fn get_header(block_size: &str) -> String { + let output = new_ucmd!() + .args(&["-P", "-B", block_size]) + .succeeds() + .stdout_move_str(); + output + .lines() + .next() + .unwrap() + .to_string() + .split_whitespace() + .nth(1) + .unwrap() + .to_string() + } + + assert_eq!(get_header("1024"), "1024-blocks"); + assert_eq!(get_header("1K"), "1024-blocks"); + assert_eq!(get_header("1KB"), "1000-blocks"); + assert_eq!(get_header("1M"), "1048576-blocks"); + assert_eq!(get_header("1MB"), "1000000-blocks"); +} + #[test] fn test_too_large_block_size() { fn run_command(size: &str) { From a6b100a5ca044496e6cc649ea5cccb4e87c5db0b Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 10 May 2022 09:34:33 +0200 Subject: [PATCH 12/24] df: show error if provided block size is zero --- src/uu/df/src/blocks.rs | 13 +++++++++++-- tests/by-util/test_df.rs | 10 ++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/uu/df/src/blocks.rs b/src/uu/df/src/blocks.rs index bb6e06333..8759d161f 100644 --- a/src/uu/df/src/blocks.rs +++ b/src/uu/df/src/blocks.rs @@ -7,7 +7,10 @@ use crate::OPT_BLOCKSIZE; use clap::ArgMatches; use std::{env, fmt}; -use uucore::parse_size::{parse_size, ParseSizeError}; +use uucore::{ + display::Quotable, + parse_size::{parse_size, ParseSizeError}, +}; /// The first ten powers of 1024. const IEC_BASES: [u128; 10] = [ @@ -180,7 +183,13 @@ impl Default for BlockSize { pub(crate) fn block_size_from_matches(matches: &ArgMatches) -> Result { if matches.is_present(OPT_BLOCKSIZE) { let s = matches.value_of(OPT_BLOCKSIZE).unwrap(); - Ok(BlockSize::Bytes(parse_size(s)?)) + let bytes = parse_size(s)?; + + if bytes > 0 { + Ok(BlockSize::Bytes(bytes)) + } else { + Err(ParseSizeError::ParseFailure(format!("{}", s.quote()))) + } } else { Ok(Default::default()) } diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 81a9eef72..5f48f1106 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -465,6 +465,16 @@ fn test_invalid_block_size() { .arg("--block-size=x") .fails() .stderr_contains("invalid --block-size argument 'x'"); + + new_ucmd!() + .arg("--block-size=0") + .fails() + .stderr_contains("invalid --block-size argument '0'"); + + new_ucmd!() + .arg("--block-size=0K") + .fails() + .stderr_contains("invalid --block-size argument '0K'"); } #[test] From 08b6dd497521e71cc3d0c743061c611686b0f819 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sun, 8 May 2022 23:02:32 -0400 Subject: [PATCH 13/24] uucore(perms): better support nameless uids, gids Update the `wrap_chown()` function to support user IDs and group IDs that do not correspond to named users or groups, respectively. Before this commit, the result from `uid2usr()` and `gid2grp()` calls were unwrapped because we assumed a user name or group name, respectively, existed. However, this is not always true: for example, running the command `sudo chown 12345 f` works even if there is no named user with ID 12345. This commit expands `wrap_chown()` to work even if no name exists for the user or group. --- src/uucore/src/lib/features/perms.rs | 34 ++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/uucore/src/lib/features/perms.rs b/src/uucore/src/lib/features/perms.rs index d8e345313..ed9e1c001 100644 --- a/src/uucore/src/lib/features/perms.rs +++ b/src/uucore/src/lib/features/perms.rs @@ -92,22 +92,25 @@ pub fn wrap_chown>( ); if level == VerbosityLevel::Verbose { out = if verbosity.groups_only { + let gid = meta.gid(); format!( "{}\nfailed to change group of {} from {} to {}", out, path.quote(), - entries::gid2grp(meta.gid()).unwrap(), - entries::gid2grp(dest_gid).unwrap() + entries::gid2grp(gid).unwrap_or(gid.to_string()), + entries::gid2grp(dest_gid).unwrap_or(dest_gid.to_string()) ) } else { + let uid = meta.uid(); + let gid = meta.gid(); format!( "{}\nfailed to change ownership of {} from {}:{} to {}:{}", out, path.quote(), - entries::uid2usr(meta.uid()).unwrap(), - entries::gid2grp(meta.gid()).unwrap(), - entries::uid2usr(dest_uid).unwrap(), - entries::gid2grp(dest_gid).unwrap() + entries::uid2usr(uid).unwrap_or(uid.to_string()), + entries::gid2grp(gid).unwrap_or(gid.to_string()), + entries::uid2usr(dest_uid).unwrap_or(dest_uid.to_string()), + entries::gid2grp(dest_gid).unwrap_or(dest_gid.to_string()) ) }; }; @@ -119,21 +122,24 @@ pub fn wrap_chown>( if changed { match verbosity.level { VerbosityLevel::Changes | VerbosityLevel::Verbose => { + let gid = meta.gid(); out = if verbosity.groups_only { format!( "changed group of {} from {} to {}", path.quote(), - entries::gid2grp(meta.gid()).unwrap(), - entries::gid2grp(dest_gid).unwrap() + entries::gid2grp(gid).unwrap_or(gid.to_string()), + entries::gid2grp(dest_gid).unwrap_or(dest_gid.to_string()) ) } else { + let gid = meta.gid(); + let uid = meta.uid(); format!( "changed ownership of {} from {}:{} to {}:{}", path.quote(), - entries::uid2usr(meta.uid()).unwrap(), - entries::gid2grp(meta.gid()).unwrap(), - entries::uid2usr(dest_uid).unwrap(), - entries::gid2grp(dest_gid).unwrap() + entries::uid2usr(uid).unwrap_or(uid.to_string()), + entries::gid2grp(gid).unwrap_or(gid.to_string()), + entries::uid2usr(dest_uid).unwrap_or(dest_uid.to_string()), + entries::gid2grp(dest_gid).unwrap_or(dest_gid.to_string()) ) }; } @@ -150,8 +156,8 @@ pub fn wrap_chown>( format!( "ownership of {} retained as {}:{}", path.quote(), - entries::uid2usr(dest_uid).unwrap(), - entries::gid2grp(dest_gid).unwrap() + entries::uid2usr(dest_uid).unwrap_or(dest_uid.to_string()), + entries::gid2grp(dest_gid).unwrap_or(dest_gid.to_string()) ) }; } From 55550e1a6ec98e8657bb7b0807f2675424f4fc78 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 23 Apr 2022 12:39:43 -0400 Subject: [PATCH 14/24] chown: allow setting arbitrary numeric user ID Update `chown` to allow setting the owner of a file to a numeric user ID regardless of whether a corresponding username exists on the system. For example, $ touch f && sudo chown 12345 f succeeds even though there is no named user with ID 12345. Fixes #3380. --- src/uu/chown/src/chown.rs | 15 +++++++++++---- tests/by-util/test_chown.rs | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index 3add9df1f..c58696121 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -197,10 +197,17 @@ fn parse_spec(spec: &str, sep: char) -> UResult<(Option, Option)> { // So, try to parse it this way return parse_spec(spec, '.'); } else { - return Err(USimpleError::new( - 1, - format!("invalid user: {}", spec.quote()), - )); + // It's possible that the `user` string contains a + // numeric user ID, in which case, we respect that. + match user.parse() { + Ok(uid) => uid, + Err(_) => { + return Err(USimpleError::new( + 1, + format!("invalid user: {}", spec.quote()), + )) + } + } } } }) diff --git a/tests/by-util/test_chown.rs b/tests/by-util/test_chown.rs index 4470260f4..05a10fb65 100644 --- a/tests/by-util/test_chown.rs +++ b/tests/by-util/test_chown.rs @@ -418,6 +418,29 @@ fn test_chown_only_user_id() { .stderr_contains(&"failed to change"); } +/// Test for setting the owner to a user ID for a user that does not exist. +/// +/// For example: +/// +/// $ touch f && chown 12345 f +/// +/// succeeds with exit status 0 and outputs nothing. The owner of the +/// file is set to 12345, even though no user with that ID exists. +/// +/// This test must be run as root, because only the root user can +/// transfer ownership of a file. +#[test] +fn test_chown_only_user_id_nonexistent_user() { + let ts = TestScenario::new(util_name!()); + let at = ts.fixtures.clone(); + at.touch("f"); + if let Ok(result) = run_ucmd_as_root(&ts, &["12345", "f"]) { + result.success().no_stdout().no_stderr(); + } else { + print!("Test skipped; requires root user"); + } +} + #[test] // FixME: stderr = chown: ownership of 'test_chown_file1' retained as cuuser:wheel #[cfg(not(target_os = "freebsd"))] From 163df8abc1954fee060d354c775ed1f4ff99f66c Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 9 May 2022 10:19:57 -0400 Subject: [PATCH 15/24] fixup! chown: allow setting arbitrary numeric user ID --- src/uu/chown/src/chown.rs | 47 +++++++++++++++++++++++++++---------- tests/by-util/test_chown.rs | 23 ++++++++++++++++++ 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index c58696121..18c64d60e 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -167,17 +167,18 @@ pub fn uu_app<'a>() -> Command<'a> { ) } -/// Parse the username and groupname +/// Parse the owner/group specifier string into a user ID and a group ID. /// -/// In theory, it should be username:groupname -/// but ... -/// it can user.name:groupname -/// or username.groupname +/// The `spec` can be of the form: /// -/// # Arguments +/// * `"owner:group"`, +/// * `"owner"`, +/// * `":group"`, /// -/// * `spec` - The input from the user -/// * `sep` - Should be ':' or '.' +/// and the owner or group can be specified either as an ID or a +/// name. The `sep` argument specifies which character to use as a +/// separator between the owner and group; calling code should set +/// this to `':'`. fn parse_spec(spec: &str, sep: char) -> UResult<(Option, Option)> { assert!(['.', ':'].contains(&sep)); let mut args = spec.splitn(2, sep); @@ -215,11 +216,18 @@ fn parse_spec(spec: &str, sep: char) -> UResult<(Option, Option)> { None }; let gid = if !group.is_empty() { - Some( - Group::locate(group) - .map_err(|_| USimpleError::new(1, format!("invalid group: {}", spec.quote())))? - .gid, - ) + Some(match Group::locate(group) { + Ok(g) => g.gid, + Err(_) => match group.parse() { + Ok(gid) => gid, + Err(_) => { + return Err(USimpleError::new( + 1, + format!("invalid group: {}", spec.quote()), + )); + } + }, + }) } else { None }; @@ -238,4 +246,17 @@ mod test { assert!(format!("{}", parse_spec("::", ':').err().unwrap()).starts_with("invalid group: ")); assert!(format!("{}", parse_spec("..", ':').err().unwrap()).starts_with("invalid group: ")); } + + /// Test for parsing IDs that don't correspond to a named user or group. + #[test] + fn test_parse_spec_nameless_ids() { + // This assumes that there is no named user with ID 12345. + assert!(matches!(parse_spec("12345", ':'), Ok((Some(12345), None)))); + // This assumes that there is no named group with ID 54321. + assert!(matches!(parse_spec(":54321", ':'), Ok((None, Some(54321))))); + assert!(matches!( + parse_spec("12345:54321", ':'), + Ok((Some(12345), Some(54321))) + )); + } } diff --git a/tests/by-util/test_chown.rs b/tests/by-util/test_chown.rs index 05a10fb65..ef3fc0d33 100644 --- a/tests/by-util/test_chown.rs +++ b/tests/by-util/test_chown.rs @@ -484,6 +484,29 @@ fn test_chown_only_group_id() { .stderr_contains(&"failed to change"); } +/// Test for setting the group to a group ID for a group that does not exist. +/// +/// For example: +/// +/// $ touch f && chown :12345 f +/// +/// succeeds with exit status 0 and outputs nothing. The group of the +/// file is set to 12345, even though no group with that ID exists. +/// +/// This test must be run as root, because only the root user can +/// transfer ownership of a file. +#[test] +fn test_chown_only_group_id_nonexistent_group() { + let ts = TestScenario::new(util_name!()); + let at = ts.fixtures.clone(); + at.touch("f"); + if let Ok(result) = run_ucmd_as_root(&ts, &[":12345", "f"]) { + result.success().no_stdout().no_stderr(); + } else { + print!("Test skipped; requires root user"); + } +} + #[test] fn test_chown_owner_group_id() { // test chown 1111:1111 file.txt From f56903493c3717401dc2191584844f9c510e2340 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 9 May 2022 20:29:11 -0400 Subject: [PATCH 16/24] WIP Trying to diagnose 'invalid group: 1001:121' error in CI environment --- src/uu/chown/src/chown.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index 18c64d60e..9d2cad1c3 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -220,7 +220,8 @@ fn parse_spec(spec: &str, sep: char) -> UResult<(Option, Option)> { Ok(g) => g.gid, Err(_) => match group.parse() { Ok(gid) => gid, - Err(_) => { + Err(e) => { + eprintln!("error {}, user {}, group {}, uid {:?}", e, user, group, uid); return Err(USimpleError::new( 1, format!("invalid group: {}", spec.quote()), From 3029d83a36af853a95cebdd8363a41c3143659ab Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 9 May 2022 20:47:46 -0400 Subject: [PATCH 17/24] Revert "WIP Trying to diagnose 'invalid group: 1001:121' error in CI environment" This reverts commit 291fb3ad71a0e93705509a352fd95de7539402ed. --- src/uu/chown/src/chown.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index 9d2cad1c3..18c64d60e 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -220,8 +220,7 @@ fn parse_spec(spec: &str, sep: char) -> UResult<(Option, Option)> { Ok(g) => g.gid, Err(_) => match group.parse() { Ok(gid) => gid, - Err(e) => { - eprintln!("error {}, user {}, group {}, uid {:?}", e, user, group, uid); + Err(_) => { return Err(USimpleError::new( 1, format!("invalid group: {}", spec.quote()), From 5713de4d937ac7e03cbf32e926fb4044bd782d67 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 9 May 2022 20:50:20 -0400 Subject: [PATCH 18/24] fixup! uucore(perms): better support nameless uids, gids --- src/uucore/src/lib/features/perms.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/uucore/src/lib/features/perms.rs b/src/uucore/src/lib/features/perms.rs index ed9e1c001..edb714646 100644 --- a/src/uucore/src/lib/features/perms.rs +++ b/src/uucore/src/lib/features/perms.rs @@ -97,8 +97,8 @@ pub fn wrap_chown>( "{}\nfailed to change group of {} from {} to {}", out, path.quote(), - entries::gid2grp(gid).unwrap_or(gid.to_string()), - entries::gid2grp(dest_gid).unwrap_or(dest_gid.to_string()) + entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string()), + entries::gid2grp(dest_gid).unwrap_or_else(|_| dest_gid.to_string()) ) } else { let uid = meta.uid(); @@ -107,10 +107,10 @@ pub fn wrap_chown>( "{}\nfailed to change ownership of {} from {}:{} to {}:{}", out, path.quote(), - entries::uid2usr(uid).unwrap_or(uid.to_string()), - entries::gid2grp(gid).unwrap_or(gid.to_string()), - entries::uid2usr(dest_uid).unwrap_or(dest_uid.to_string()), - entries::gid2grp(dest_gid).unwrap_or(dest_gid.to_string()) + entries::uid2usr(uid).unwrap_or_else(|_| uid.to_string()), + entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string()), + entries::uid2usr(dest_uid).unwrap_or_else(|_| dest_uid.to_string()), + entries::gid2grp(dest_gid).unwrap_or_else(|_| dest_gid.to_string()) ) }; }; @@ -127,8 +127,8 @@ pub fn wrap_chown>( format!( "changed group of {} from {} to {}", path.quote(), - entries::gid2grp(gid).unwrap_or(gid.to_string()), - entries::gid2grp(dest_gid).unwrap_or(dest_gid.to_string()) + entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string()), + entries::gid2grp(dest_gid).unwrap_or_else(|_| dest_gid.to_string()) ) } else { let gid = meta.gid(); @@ -136,10 +136,10 @@ pub fn wrap_chown>( format!( "changed ownership of {} from {}:{} to {}:{}", path.quote(), - entries::uid2usr(uid).unwrap_or(uid.to_string()), - entries::gid2grp(gid).unwrap_or(gid.to_string()), - entries::uid2usr(dest_uid).unwrap_or(dest_uid.to_string()), - entries::gid2grp(dest_gid).unwrap_or(dest_gid.to_string()) + entries::uid2usr(uid).unwrap_or_else(|_| uid.to_string()), + entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string()), + entries::uid2usr(dest_uid).unwrap_or_else(|_| dest_uid.to_string()), + entries::gid2grp(dest_gid).unwrap_or_else(|_| dest_gid.to_string()) ) }; } @@ -156,8 +156,8 @@ pub fn wrap_chown>( format!( "ownership of {} retained as {}:{}", path.quote(), - entries::uid2usr(dest_uid).unwrap_or(dest_uid.to_string()), - entries::gid2grp(dest_gid).unwrap_or(dest_gid.to_string()) + entries::uid2usr(dest_uid).unwrap_or_else(|_| dest_uid.to_string()), + entries::gid2grp(dest_gid).unwrap_or_else(|_| dest_gid.to_string()) ) }; } From 598dc79b69d9eb6b711fd45b4b4fa7dd18d75413 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Thu, 12 May 2022 10:11:24 +0200 Subject: [PATCH 19/24] df: test default blocksize in POSIX mode --- tests/by-util/test_df.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 157818d95..9ff8e103d 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -435,6 +435,30 @@ fn test_default_block_size() { assert_eq!(header, "512B-blocks"); } +#[test] +fn test_default_block_size_in_posix_portability_mode() { + fn get_header(s: &str) -> String { + s.lines() + .next() + .unwrap() + .to_string() + .split_whitespace() + .nth(1) + .unwrap() + .to_string() + } + + let output = new_ucmd!().arg("-P").succeeds().stdout_move_str(); + assert_eq!(get_header(&output), "1024-blocks"); + + let output = new_ucmd!() + .arg("-P") + .env("POSIXLY_CORRECT", "1") + .succeeds() + .stdout_move_str(); + assert_eq!(get_header(&output), "512-blocks"); +} + #[test] fn test_block_size_1024() { fn get_header(block_size: u64) -> String { From 369026a92f1b0c3e3b17706e2061b98447f7ee9a Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Thu, 12 May 2022 22:34:08 +0200 Subject: [PATCH 20/24] tr: remove duplicate line from clap::Command --- src/uu/tr/src/tr.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs index dd8edc518..93465429f 100644 --- a/src/uu/tr/src/tr.rs +++ b/src/uu/tr/src/tr.rs @@ -143,7 +143,6 @@ pub fn uu_app<'a>() -> Command<'a> { .about(ABOUT) .override_usage(format_usage(USAGE)) .infer_long_args(true) - .infer_long_args(true) .arg( Arg::new(options::COMPLEMENT) .visible_short_alias('C') From fa94591b723b52a6b6435c876e1b42c61ed24929 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Thu, 12 May 2022 19:22:50 +0200 Subject: [PATCH 21/24] test_stat: expand scope for stdin tests On Android and macOS all/some tests for stdin fail with: `cannot stat '-': No such file or directory` Apparently the `/dev/stdin` redirect workaround doesn't work for these targets. --- tests/by-util/test_stat.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/by-util/test_stat.rs b/tests/by-util/test_stat.rs index 14aba96fc..681850467 100644 --- a/tests/by-util/test_stat.rs +++ b/tests/by-util/test_stat.rs @@ -360,12 +360,11 @@ fn test_pipe_fifo() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(all(unix, not(target_os = "android")))] fn test_stdin_pipe_fifo1() { // $ echo | stat - // File: - // Size: 0 Blocks: 0 IO Block: 4096 fifo - // use std::process::{Command, Stdio}; new_ucmd!() .arg("-") .set_stdin(std::process::Stdio::piped()) @@ -374,7 +373,6 @@ fn test_stdin_pipe_fifo1() { .stdout_contains("fifo") .stdout_contains("File: -") .succeeded(); - new_ucmd!() .args(&["-L", "-"]) .set_stdin(std::process::Stdio::piped()) @@ -386,7 +384,7 @@ fn test_stdin_pipe_fifo1() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(all(unix, not(target_os = "android")))] fn test_stdin_pipe_fifo2() { // $ stat - // File: - @@ -402,16 +400,15 @@ fn test_stdin_pipe_fifo2() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] fn test_stdin_redirect() { // $ touch f && stat - < f // File: - // Size: 0 Blocks: 0 IO Block: 4096 regular empty file - let ts = TestScenario::new(util_name!()); let at = &ts.fixtures; at.touch("f"); - new_ucmd!() + ts.ucmd() .arg("-") .set_stdin(std::fs::File::open(at.plus("f")).unwrap()) .run() From 5d628310fc522e1e23983044ed1e41cc6111f882 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 May 2022 06:45:15 +0000 Subject: [PATCH 22/24] build(deps): bump vmactions/freebsd-vm from 0.1.5 to 0.1.6 Bumps [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) from 0.1.5 to 0.1.6. - [Release notes](https://github.com/vmactions/freebsd-vm/releases) - [Commits](https://github.com/vmactions/freebsd-vm/compare/v0.1.5...v0.1.6) --- updated-dependencies: - dependency-name: vmactions/freebsd-vm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 079dc1f29..c58b725c4 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -915,7 +915,7 @@ jobs: - uses: Swatinem/rust-cache@v1 - name: Prepare, build and test ## spell-checker:ignore (ToDO) sshfs usesh vmactions - uses: vmactions/freebsd-vm@v0.1.5 + uses: vmactions/freebsd-vm@v0.1.6 with: usesh: true # sync: sshfs From 0acfa07d7707897e64ba3783cdc370f4e102388d Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Thu, 12 May 2022 22:53:25 +0200 Subject: [PATCH 23/24] all: add value hints --- src/uu/base32/src/base_common.rs | 7 ++++++- src/uu/basename/src/basename.rs | 1 + src/uu/cat/src/cat.rs | 3 ++- src/uu/chcon/src/chcon.rs | 3 +++ src/uu/chgrp/src/chgrp.rs | 1 + src/uu/chmod/src/chmod.rs | 4 +++- src/uu/chown/src/chown.rs | 1 + src/uu/chroot/src/chroot.rs | 2 ++ src/uu/cksum/src/cksum.rs | 3 ++- src/uu/comm/src/comm.rs | 12 ++++++++++-- src/uu/cp/src/cp.rs | 4 +++- src/uu/csplit/src/csplit.rs | 7 ++++++- src/uu/cut/src/cut.rs | 3 ++- src/uu/date/src/date.rs | 2 ++ src/uu/dd/src/dd.rs | 2 ++ src/uu/df/src/df.rs | 6 +++++- src/uu/dircolors/src/dircolors.rs | 1 + src/uu/dirname/src/dirname.rs | 7 ++++++- src/uu/du/src/du.rs | 2 ++ src/uu/env/src/env.rs | 2 ++ src/uu/expand/src/expand.rs | 1 + src/uu/fmt/src/fmt.rs | 3 ++- src/uu/fold/src/fold.rs | 3 ++- src/uu/groups/src/groups.rs | 3 ++- src/uu/hashsum/src/hashsum.rs | 1 + src/uu/head/src/head.rs | 6 +++++- src/uu/hostname/src/hostname.rs | 6 +++++- src/uu/id/src/id.rs | 3 ++- src/uu/install/src/install.rs | 11 ++++++++++- src/uu/join/src/join.rs | 2 ++ src/uu/link/src/link.rs | 1 + src/uu/ln/src/ln.rs | 2 ++ src/uu/ls/src/ls.rs | 3 ++- src/uu/mkdir/src/mkdir.rs | 3 ++- src/uu/mkfifo/src/mkfifo.rs | 3 ++- src/uu/mknod/src/mknod.rs | 3 ++- src/uu/mktemp/src/mktemp.rs | 3 ++- src/uu/more/src/more.rs | 3 ++- src/uu/mv/src/mv.rs | 6 ++++-- src/uu/nice/src/nice.rs | 6 +++++- src/uu/nl/src/nl.rs | 3 ++- src/uu/nohup/src/nohup.rs | 3 ++- src/uu/od/src/od.rs | 3 ++- src/uu/paste/src/paste.rs | 3 ++- src/uu/pathchk/src/pathchk.rs | 3 ++- src/uu/pinky/src/pinky.rs | 3 ++- src/uu/pr/src/pr.rs | 1 + src/uu/ptx/src/ptx.rs | 12 ++++++++---- src/uu/readlink/src/readlink.rs | 3 ++- src/uu/realpath/src/realpath.rs | 3 ++- src/uu/relpath/src/relpath.rs | 13 +++++++++++-- src/uu/rm/src/rm.rs | 1 + src/uu/rmdir/src/rmdir.rs | 3 ++- src/uu/runcon/src/runcon.rs | 3 ++- src/uu/shred/src/shred.rs | 3 ++- src/uu/shuf/src/shuf.rs | 12 +++++++++--- src/uu/sort/src/sort.rs | 15 ++++++++++----- src/uu/split/src/split.rs | 9 ++++++--- src/uu/stat/src/stat.rs | 3 ++- src/uu/stdbuf/src/stdbuf.rs | 3 ++- src/uu/sum/src/sum.rs | 3 ++- src/uu/sync/src/sync.rs | 3 ++- src/uu/tac/src/tac.rs | 3 ++- src/uu/tail/src/tail.rs | 3 ++- src/uu/tee/src/tee.rs | 6 +++++- src/uu/timeout/src/timeout.rs | 1 + src/uu/touch/src/touch.rs | 6 ++++-- src/uu/truncate/src/truncate.rs | 4 +++- src/uu/tsort/src/tsort.rs | 7 ++++++- src/uu/unexpand/src/unexpand.rs | 7 ++++++- src/uu/uniq/src/uniq.rs | 3 ++- src/uu/unlink/src/unlink.rs | 3 ++- src/uu/users/src/users.rs | 7 ++++++- src/uu/wc/src/wc.rs | 6 ++++-- src/uu/who/src/who.rs | 3 ++- src/uucore/src/lib/features/perms.rs | 1 + 76 files changed, 240 insertions(+), 73 deletions(-) diff --git a/src/uu/base32/src/base_common.rs b/src/uu/base32/src/base_common.rs index 148bad2f8..90221f4f6 100644 --- a/src/uu/base32/src/base_common.rs +++ b/src/uu/base32/src/base_common.rs @@ -123,7 +123,12 @@ pub fn base_app<'a>(about: &'a str, usage: &'a str) -> Command<'a> { ) // "multiple" arguments are used to check whether there is more than one // file passed in. - .arg(Arg::new(options::FILE).index(1).multiple_occurrences(true)) + .arg( + Arg::new(options::FILE) + .index(1) + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath), + ) } pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> UResult> { diff --git a/src/uu/basename/src/basename.rs b/src/uu/basename/src/basename.rs index 7b0a0a486..242a6a6e9 100644 --- a/src/uu/basename/src/basename.rs +++ b/src/uu/basename/src/basename.rs @@ -102,6 +102,7 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::NAME) .multiple_occurrences(true) + .value_hint(clap::ValueHint::AnyPath) .hide(true), ) .arg( diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index 67de917f7..cd10b5251 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -249,7 +249,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .hide(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::SHOW_ALL) diff --git a/src/uu/chcon/src/chcon.rs b/src/uu/chcon/src/chcon.rs index e49191ea3..3458a4cca 100644 --- a/src/uu/chcon/src/chcon.rs +++ b/src/uu/chcon/src/chcon.rs @@ -197,6 +197,7 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::REFERENCE) .takes_value(true) .value_name("RFILE") + .value_hint(clap::ValueHint::FilePath) .conflicts_with_all(&[options::USER, options::ROLE, options::TYPE, options::RANGE]) .help( "Use security context of RFILE, rather than specifying \ @@ -210,6 +211,7 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::USER) .takes_value(true) .value_name("USER") + .value_hint(clap::ValueHint::Username) .help("Set user USER in the target security context.") .allow_invalid_utf8(true), ) @@ -294,6 +296,7 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new("FILE") .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath) .min_values(1) .allow_invalid_utf8(true), ) diff --git a/src/uu/chgrp/src/chgrp.rs b/src/uu/chgrp/src/chgrp.rs index d7e8baafe..015f09982 100644 --- a/src/uu/chgrp/src/chgrp.rs +++ b/src/uu/chgrp/src/chgrp.rs @@ -113,6 +113,7 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::REFERENCE) .long(options::REFERENCE) .value_name("RFILE") + .value_hint(clap::ValueHint::FilePath) .help("use RFILE's group rather than specifying GROUP values") .takes_value(true) .multiple_occurrences(false), diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index 25a37c372..d4b81ee37 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -157,6 +157,7 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::REFERENCE) .long("reference") .takes_value(true) + .value_hint(clap::ValueHint::FilePath) .help("use RFILE's mode instead of MODE values"), ) .arg( @@ -170,7 +171,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .required_unless_present(options::MODE) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::AnyPath), ) } diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index 3add9df1f..e66e105fb 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -134,6 +134,7 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::REFERENCE) .help("use RFILE's owner and group rather than specifying OWNER:GROUP values") .value_name("RFILE") + .value_hint(clap::ValueHint::FilePath) .min_values(1), ) .arg( diff --git a/src/uu/chroot/src/chroot.rs b/src/uu/chroot/src/chroot.rs index e54cc3f8f..30cf75644 100644 --- a/src/uu/chroot/src/chroot.rs +++ b/src/uu/chroot/src/chroot.rs @@ -102,6 +102,7 @@ pub fn uu_app<'a>() -> Command<'a> { .infer_long_args(true) .arg( Arg::new(options::NEWROOT) + .value_hint(clap::ValueHint::DirPath) .hide(true) .required(true) .index(1), @@ -139,6 +140,7 @@ pub fn uu_app<'a>() -> Command<'a> { ) .arg( Arg::new(options::COMMAND) + .value_hint(clap::ValueHint::CommandName) .hide(true) .multiple_occurrences(true) .index(2), diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index e901e0820..e9a5c5620 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -150,6 +150,7 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .hide(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/comm/src/comm.rs b/src/uu/comm/src/comm.rs index 2207493d3..78ef4e5c2 100644 --- a/src/uu/comm/src/comm.rs +++ b/src/uu/comm/src/comm.rs @@ -173,6 +173,14 @@ pub fn uu_app<'a>() -> Command<'a> { .default_value(options::DELIMITER_DEFAULT) .hide_default_value(true), ) - .arg(Arg::new(options::FILE_1).required(true)) - .arg(Arg::new(options::FILE_2).required(true)) + .arg( + Arg::new(options::FILE_1) + .required(true) + .value_hint(clap::ValueHint::FilePath), + ) + .arg( + Arg::new(options::FILE_2) + .required(true) + .value_hint(clap::ValueHint::FilePath), + ) } diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 1b47b7828..41466abc8 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -314,6 +314,7 @@ pub fn uu_app<'a>() -> Command<'a> { .conflicts_with(options::NO_TARGET_DIRECTORY) .long(options::TARGET_DIRECTORY) .value_name(options::TARGET_DIRECTORY) + .value_hint(clap::ValueHint::DirPath) .takes_value(true) .validator(|s| { if Path::new(s).is_dir() { @@ -464,7 +465,8 @@ pub fn uu_app<'a>() -> Command<'a> { // END TODO .arg(Arg::new(options::PATHS) - .multiple_occurrences(true)) + .multiple_occurrences(true) + .value_hint(clap::ValueHint::AnyPath)) } #[uucore::main] diff --git a/src/uu/csplit/src/csplit.rs b/src/uu/csplit/src/csplit.rs index a0b739935..2ef6b34f9 100644 --- a/src/uu/csplit/src/csplit.rs +++ b/src/uu/csplit/src/csplit.rs @@ -797,7 +797,12 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::ELIDE_EMPTY_FILES) .help("remove empty output files"), ) - .arg(Arg::new(options::FILE).hide(true).required(true)) + .arg( + Arg::new(options::FILE) + .hide(true) + .required(true) + .value_hint(clap::ValueHint::FilePath), + ) .arg( Arg::new(options::PATTERN) .hide(true) diff --git a/src/uu/cut/src/cut.rs b/src/uu/cut/src/cut.rs index 2264f4f26..b809b765d 100644 --- a/src/uu/cut/src/cut.rs +++ b/src/uu/cut/src/cut.rs @@ -614,6 +614,7 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .hide(true) - .multiple_occurrences(true) + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath) ) } diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index 8946768d5..ca18856d1 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -272,6 +272,7 @@ pub fn uu_app<'a>() -> Command<'a> { .short('f') .long(OPT_FILE) .takes_value(true) + .value_hint(clap::ValueHint::FilePath) .help("like --date; once for each line of DATEFILE"), ) .arg( @@ -303,6 +304,7 @@ pub fn uu_app<'a>() -> Command<'a> { .short('r') .long(OPT_REFERENCE) .takes_value(true) + .value_hint(clap::ValueHint::AnyPath) .help("display the last modification time of FILE"), ) .arg( diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 3f1a54b1c..115da8bcc 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -742,6 +742,7 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::INFILE) .overrides_with(options::INFILE) .takes_value(true) + .value_hint(clap::ValueHint::FilePath) .require_equals(true) .value_name("FILE") .help("(alternatively if=FILE) specifies the file used for input. When not specified, stdin is used instead") @@ -751,6 +752,7 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::OUTFILE) .overrides_with(options::OUTFILE) .takes_value(true) + .value_hint(clap::ValueHint::FilePath) .require_equals(true) .value_name("FILE") .help("(alternatively of=FILE) specifies the file used for output. When not specified, stdout is used instead") diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index a11dcf7a0..1f59982b2 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -583,7 +583,11 @@ pub fn uu_app<'a>() -> Command<'a> { .multiple_occurrences(true) .help("limit listing to file systems not of type TYPE"), ) - .arg(Arg::new(OPT_PATHS).multiple_occurrences(true)) + .arg( + Arg::new(OPT_PATHS) + .multiple_occurrences(true) + .value_hint(clap::ValueHint::AnyPath), + ) } #[cfg(test)] diff --git a/src/uu/dircolors/src/dircolors.rs b/src/uu/dircolors/src/dircolors.rs index c76dbc0c1..78cf8d802 100644 --- a/src/uu/dircolors/src/dircolors.rs +++ b/src/uu/dircolors/src/dircolors.rs @@ -187,6 +187,7 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .hide(true) + .value_hint(clap::ValueHint::FilePath) .multiple_occurrences(true), ) } diff --git a/src/uu/dirname/src/dirname.rs b/src/uu/dirname/src/dirname.rs index 030c3981c..ac37e513b 100644 --- a/src/uu/dirname/src/dirname.rs +++ b/src/uu/dirname/src/dirname.rs @@ -88,5 +88,10 @@ pub fn uu_app<'a>() -> Command<'a> { .short('z') .help("separate output with NUL rather than newline"), ) - .arg(Arg::new(options::DIR).hide(true).multiple_occurrences(true)) + .arg( + Arg::new(options::DIR) + .hide(true) + .multiple_occurrences(true) + .value_hint(clap::ValueHint::AnyPath), + ) } diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index b29a938a4..d358d3e8f 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -884,6 +884,7 @@ pub fn uu_app<'a>() -> Command<'a> { .short('X') .long("exclude-from") .value_name("FILE") + .value_hint(clap::ValueHint::FilePath) .help("exclude files that match any pattern in FILE") .multiple_occurrences(true) @@ -913,6 +914,7 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .hide(true) + .value_hint(clap::ValueHint::AnyPath) .multiple_occurrences(true) ) } diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 2ec0ce1c6..a42621dad 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -144,6 +144,7 @@ pub fn uu_app<'a>() -> Command<'a> { .takes_value(true) .number_of_values(1) .value_name("DIR") + .value_hint(clap::ValueHint::DirPath) .help("change working directory to DIR")) .arg(Arg::new("null") .short('0') @@ -156,6 +157,7 @@ pub fn uu_app<'a>() -> Command<'a> { .takes_value(true) .number_of_values(1) .value_name("PATH") + .value_hint(clap::ValueHint::FilePath) .multiple_occurrences(true) .help("read and set variables from a \".env\"-style configuration file (prior to any \ unset and/or set)")) diff --git a/src/uu/expand/src/expand.rs b/src/uu/expand/src/expand.rs index b9d9309cd..117615162 100644 --- a/src/uu/expand/src/expand.rs +++ b/src/uu/expand/src/expand.rs @@ -207,6 +207,7 @@ pub fn uu_app<'a>() -> Command<'a> { .multiple_occurrences(true) .hide(true) .takes_value(true) + .value_hint(clap::ValueHint::FilePath) ) } diff --git a/src/uu/fmt/src/fmt.rs b/src/uu/fmt/src/fmt.rs index 3cb851674..27afb689f 100644 --- a/src/uu/fmt/src/fmt.rs +++ b/src/uu/fmt/src/fmt.rs @@ -344,6 +344,7 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(ARG_FILES) .multiple_occurrences(true) - .takes_value(true), + .takes_value(true) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/fold/src/fold.rs b/src/uu/fold/src/fold.rs index d24d31be9..b2806eb01 100644 --- a/src/uu/fold/src/fold.rs +++ b/src/uu/fold/src/fold.rs @@ -99,7 +99,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .hide(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/groups/src/groups.rs b/src/uu/groups/src/groups.rs index cd0d05b07..dd2188033 100644 --- a/src/uu/groups/src/groups.rs +++ b/src/uu/groups/src/groups.rs @@ -112,6 +112,7 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::USERS) .multiple_occurrences(true) .takes_value(true) - .value_name(options::USERS), + .value_name(options::USERS) + .value_hint(clap::ValueHint::Username), ) } diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index 244e4b928..872cb5c21 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -423,6 +423,7 @@ pub fn uu_app_common<'a>() -> Command<'a> { .index(1) .multiple_occurrences(true) .value_name("FILE") + .value_hint(clap::ValueHint::FilePath) .allow_invalid_utf8(true), ) } diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index b6d36a4ad..01f08ff93 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -107,7 +107,11 @@ pub fn uu_app<'a>() -> Command<'a> { .help("line delimiter is NUL, not newline") .overrides_with(options::ZERO_NAME), ) - .arg(Arg::new(options::FILES_NAME).multiple_occurrences(true)) + .arg( + Arg::new(options::FILES_NAME) + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath), + ) } #[derive(Debug, PartialEq)] diff --git a/src/uu/hostname/src/hostname.rs b/src/uu/hostname/src/hostname.rs index 757caed02..11b0d6cdb 100644 --- a/src/uu/hostname/src/hostname.rs +++ b/src/uu/hostname/src/hostname.rs @@ -105,7 +105,11 @@ pub fn uu_app<'a>() -> Command<'a> { .overrides_with_all(&[OPT_DOMAIN, OPT_IP_ADDRESS, OPT_FQDN, OPT_SHORT]) .help("Display the short hostname (the portion before the first dot) if possible"), ) - .arg(Arg::new(OPT_HOST).allow_invalid_utf8(true)) + .arg( + Arg::new(OPT_HOST) + .allow_invalid_utf8(true) + .value_hint(clap::ValueHint::Hostname), + ) } fn display_hostname(matches: &ArgMatches) -> UResult<()> { diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index cc23ce19f..bb8e261d2 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -443,7 +443,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::ARG_USERS) .multiple_occurrences(true) .takes_value(true) - .value_name(options::ARG_USERS), + .value_name(options::ARG_USERS) + .value_hint(clap::ValueHint::Username), ) } diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index 808898cfb..6750560d2 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -248,6 +248,7 @@ pub fn uu_app<'a>() -> Command<'a> { .help("set ownership (super-user only)") .value_name("OWNER") .takes_value(true) + .value_hint(clap::ValueHint::Username) ) .arg( Arg::new(OPT_PRESERVE_TIMESTAMPS) @@ -266,6 +267,7 @@ pub fn uu_app<'a>() -> Command<'a> { .long(OPT_STRIP_PROGRAM) .help("program used to strip binaries (no action Windows)") .value_name("PROGRAM") + .value_hint(clap::ValueHint::CommandName) ) .arg( backup_control::arguments::suffix() @@ -277,6 +279,7 @@ pub fn uu_app<'a>() -> Command<'a> { .long(OPT_TARGET_DIRECTORY) .help("move all SOURCE arguments into DIRECTORY") .value_name("DIRECTORY") + .value_hint(clap::ValueHint::DirPath) ) .arg( // TODO implement flag @@ -307,7 +310,13 @@ pub fn uu_app<'a>() -> Command<'a> { .help("(unimplemented) set security context of files and directories") .value_name("CONTEXT") ) - .arg(Arg::new(ARG_FILES).multiple_occurrences(true).takes_value(true).min_values(1)) + .arg( + Arg::new(ARG_FILES) + .multiple_occurrences(true) + .takes_value(true) + .min_values(1) + .value_hint(clap::ValueHint::AnyPath) + ) } /// Check for unimplemented command line arguments. diff --git a/src/uu/join/src/join.rs b/src/uu/join/src/join.rs index ef19fe328..20a8cbd28 100644 --- a/src/uu/join/src/join.rs +++ b/src/uu/join/src/join.rs @@ -801,12 +801,14 @@ FILENUM is 1 or 2, corresponding to FILE1 or FILE2", Arg::new("file1") .required(true) .value_name("FILE1") + .value_hint(clap::ValueHint::FilePath) .hide(true), ) .arg( Arg::new("file2") .required(true) .value_name("FILE2") + .value_hint(clap::ValueHint::FilePath) .hide(true), ) } diff --git a/src/uu/link/src/link.rs b/src/uu/link/src/link.rs index 3c959af33..1fc0c49ce 100644 --- a/src/uu/link/src/link.rs +++ b/src/uu/link/src/link.rs @@ -46,6 +46,7 @@ pub fn uu_app<'a>() -> Command<'a> { .min_values(2) .max_values(2) .takes_value(true) + .value_hint(clap::ValueHint::AnyPath) .allow_invalid_utf8(true), ) } diff --git a/src/uu/ln/src/ln.rs b/src/uu/ln/src/ln.rs index 088edf219..fdbb32311 100644 --- a/src/uu/ln/src/ln.rs +++ b/src/uu/ln/src/ln.rs @@ -232,6 +232,7 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::TARGET_DIRECTORY) .help("specify the DIRECTORY in which to create the links") .value_name("DIRECTORY") + .value_hint(clap::ValueHint::DirPath) .conflicts_with(options::NO_TARGET_DIRECTORY), ) .arg( @@ -257,6 +258,7 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(ARG_FILES) .multiple_occurrences(true) .takes_value(true) + .value_hint(clap::ValueHint::AnyPath) .required(true) .min_values(1), ) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 17eec91ec..20d960c51 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1402,7 +1402,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::PATHS) .multiple_occurrences(true) .takes_value(true) - .allow_invalid_utf8(true), + .value_hint(clap::ValueHint::AnyPath) + .allow_invalid_utf8(true) ) .after_help( "The TIME_STYLE argument can be full-iso, long-iso, iso. \ diff --git a/src/uu/mkdir/src/mkdir.rs b/src/uu/mkdir/src/mkdir.rs index b1d7583d3..6ba58c2bb 100644 --- a/src/uu/mkdir/src/mkdir.rs +++ b/src/uu/mkdir/src/mkdir.rs @@ -137,7 +137,8 @@ pub fn uu_app<'a>() -> Command<'a> { .multiple_occurrences(true) .takes_value(true) .min_values(1) - .allow_invalid_utf8(true), + .allow_invalid_utf8(true) + .value_hint(clap::ValueHint::DirPath), ) } diff --git a/src/uu/mkfifo/src/mkfifo.rs b/src/uu/mkfifo/src/mkfifo.rs index 756fd75cf..b14ae8bed 100644 --- a/src/uu/mkfifo/src/mkfifo.rs +++ b/src/uu/mkfifo/src/mkfifo.rs @@ -102,6 +102,7 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FIFO) .hide(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::AnyPath), ) } diff --git a/src/uu/mknod/src/mknod.rs b/src/uu/mknod/src/mknod.rs index 0e7c4be45..fdd57aa01 100644 --- a/src/uu/mknod/src/mknod.rs +++ b/src/uu/mknod/src/mknod.rs @@ -162,7 +162,8 @@ pub fn uu_app<'a>() -> Command<'a> { .value_name("NAME") .help("name of the new file") .required(true) - .index(1), + .index(1) + .value_hint(clap::ValueHint::AnyPath), ) .arg( Arg::new("type") diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index 94def4874..20b95f009 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -202,7 +202,8 @@ pub fn uu_app<'a>() -> Command<'a> { be an absolute name; unlike with -t, TEMPLATE may contain \ slashes, but mktemp creates only the final component", ) - .value_name("DIR"), + .value_name("DIR") + .value_hint(clap::ValueHint::DirPath), ) .arg(Arg::new(OPT_T).short('t').help( "Generate a template (using the supplied prefix and TMPDIR (TMP on windows) if set) \ diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index ab94bd435..711d2b214 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -183,7 +183,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::FILES) .required(false) .multiple_occurrences(true) - .help("Path to the files to be read"), + .help("Path to the files to be read") + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 1c2390f80..fa8284061 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -156,14 +156,15 @@ pub fn uu_app<'a>() -> Command<'a> { .help("move all SOURCE arguments into DIRECTORY") .takes_value(true) .value_name("DIRECTORY") + .value_hint(clap::ValueHint::DirPath) .conflicts_with(OPT_NO_TARGET_DIRECTORY) .allow_invalid_utf8(true) ) .arg( Arg::new(OPT_NO_TARGET_DIRECTORY) .short('T') - .long(OPT_NO_TARGET_DIRECTORY). - help("treat DEST as a normal file") + .long(OPT_NO_TARGET_DIRECTORY) + .help("treat DEST as a normal file") ) .arg( Arg::new(OPT_UPDATE) @@ -183,6 +184,7 @@ pub fn uu_app<'a>() -> Command<'a> { .min_values(2) .required(true) .allow_invalid_utf8(true) + .value_hint(clap::ValueHint::AnyPath) ) } diff --git a/src/uu/nice/src/nice.rs b/src/uu/nice/src/nice.rs index e78de828b..f0d663bcc 100644 --- a/src/uu/nice/src/nice.rs +++ b/src/uu/nice/src/nice.rs @@ -117,5 +117,9 @@ pub fn uu_app<'a>() -> Command<'a> { .takes_value(true) .allow_hyphen_values(true), ) - .arg(Arg::new(options::COMMAND).multiple_occurrences(true)) + .arg( + Arg::new(options::COMMAND) + .multiple_occurrences(true) + .value_hint(clap::ValueHint::CommandName), + ) } diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index 28cf3fb4d..e6ca7f931 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -154,7 +154,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .hide(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::BODY_NUMBERING) diff --git a/src/uu/nohup/src/nohup.rs b/src/uu/nohup/src/nohup.rs index 0d67ad466..6f605ac4f 100644 --- a/src/uu/nohup/src/nohup.rs +++ b/src/uu/nohup/src/nohup.rs @@ -126,7 +126,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::CMD) .hide(true) .required(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::CommandName), ) .trailing_var_arg(true) .infer_long_args(true) diff --git a/src/uu/od/src/od.rs b/src/uu/od/src/od.rs index 33fac39d3..168213dae 100644 --- a/src/uu/od/src/od.rs +++ b/src/uu/od/src/od.rs @@ -512,7 +512,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILENAME) .hide(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/paste/src/paste.rs b/src/uu/paste/src/paste.rs index 2826a19e3..682e66a5d 100644 --- a/src/uu/paste/src/paste.rs +++ b/src/uu/paste/src/paste.rs @@ -71,7 +71,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::FILE) .value_name("FILE") .multiple_occurrences(true) - .default_value("-"), + .default_value("-") + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/pathchk/src/pathchk.rs b/src/uu/pathchk/src/pathchk.rs index 6260590aa..0f21448cf 100644 --- a/src/uu/pathchk/src/pathchk.rs +++ b/src/uu/pathchk/src/pathchk.rs @@ -108,7 +108,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::PATH) .hide(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::AnyPath), ) } diff --git a/src/uu/pinky/src/pinky.rs b/src/uu/pinky/src/pinky.rs index 4a793944f..b5217afe4 100644 --- a/src/uu/pinky/src/pinky.rs +++ b/src/uu/pinky/src/pinky.rs @@ -179,7 +179,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::USER) .takes_value(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::Username), ) .arg( // Redefine the help argument to not include the short flag diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index ca12f0be0..682e9b6d4 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -372,6 +372,7 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::FILES) .multiple_occurrences(true) .multiple_values(true) + .value_hint(clap::ValueHint::FilePath) ) } diff --git a/src/uu/ptx/src/ptx.rs b/src/uu/ptx/src/ptx.rs index 2f253b580..9cdad7ccf 100644 --- a/src/uu/ptx/src/ptx.rs +++ b/src/uu/ptx/src/ptx.rs @@ -754,7 +754,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .hide(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::AUTO_REFERENCE) @@ -826,7 +827,8 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::BREAK_FILE) .help("word break characters in this FILE") .value_name("FILE") - .takes_value(true), + .takes_value(true) + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::IGNORE_CASE) @@ -849,7 +851,8 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::IGNORE_FILE) .help("read ignore word list from FILE") .value_name("FILE") - .takes_value(true), + .takes_value(true) + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::ONLY_FILE) @@ -857,7 +860,8 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::ONLY_FILE) .help("read only word list from this FILE") .value_name("FILE") - .takes_value(true), + .takes_value(true) + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::REFERENCES) diff --git a/src/uu/readlink/src/readlink.rs b/src/uu/readlink/src/readlink.rs index ba1e368f2..10d75e7ef 100644 --- a/src/uu/readlink/src/readlink.rs +++ b/src/uu/readlink/src/readlink.rs @@ -161,7 +161,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(ARG_FILES) .multiple_occurrences(true) - .takes_value(true), + .takes_value(true) + .value_hint(clap::ValueHint::AnyPath), ) } diff --git a/src/uu/realpath/src/realpath.rs b/src/uu/realpath/src/realpath.rs index bea89c19e..403e7c456 100644 --- a/src/uu/realpath/src/realpath.rs +++ b/src/uu/realpath/src/realpath.rs @@ -130,7 +130,8 @@ pub fn uu_app<'a>() -> Command<'a> { .multiple_occurrences(true) .takes_value(true) .required(true) - .min_values(1), + .min_values(1) + .value_hint(clap::ValueHint::AnyPath), ) } diff --git a/src/uu/relpath/src/relpath.rs b/src/uu/relpath/src/relpath.rs index 2e45ce927..336e2192d 100644 --- a/src/uu/relpath/src/relpath.rs +++ b/src/uu/relpath/src/relpath.rs @@ -87,6 +87,15 @@ pub fn uu_app<'a>() -> Command<'a> { .arg(Arg::new(options::DIR).short('d').takes_value(true).help( "If any of FROM and TO is not subpath of DIR, output absolute path instead of relative", )) - .arg(Arg::new(options::TO).required(true).takes_value(true)) - .arg(Arg::new(options::FROM).takes_value(true)) + .arg( + Arg::new(options::TO) + .required(true) + .takes_value(true) + .value_hint(clap::ValueHint::AnyPath), + ) + .arg( + Arg::new(options::FROM) + .takes_value(true) + .value_hint(clap::ValueHint::AnyPath), + ) } diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 0701387d9..4f69c8fb1 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -227,6 +227,7 @@ pub fn uu_app<'a>() -> Command<'a> { .multiple_occurrences(true) .takes_value(true) .min_values(1) + .value_hint(clap::ValueHint::AnyPath) ) } diff --git a/src/uu/rmdir/src/rmdir.rs b/src/uu/rmdir/src/rmdir.rs index 127b8fcf9..be4e53780 100644 --- a/src/uu/rmdir/src/rmdir.rs +++ b/src/uu/rmdir/src/rmdir.rs @@ -197,6 +197,7 @@ pub fn uu_app<'a>() -> Command<'a> { .takes_value(true) .min_values(1) .required(true) - .allow_invalid_utf8(true), + .allow_invalid_utf8(true) + .value_hint(clap::ValueHint::DirPath), ) } diff --git a/src/uu/runcon/src/runcon.rs b/src/uu/runcon/src/runcon.rs index 8c20319be..64df4f381 100644 --- a/src/uu/runcon/src/runcon.rs +++ b/src/uu/runcon/src/runcon.rs @@ -156,7 +156,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new("ARG") .multiple_occurrences(true) - .allow_invalid_utf8(true), + .allow_invalid_utf8(true) + .value_hint(clap::ValueHint::CommandName), ) // Once "ARG" is parsed, everything after that belongs to it. // diff --git a/src/uu/shred/src/shred.rs b/src/uu/shred/src/shred.rs index 05c8d1805..7f1c97c77 100644 --- a/src/uu/shred/src/shred.rs +++ b/src/uu/shred/src/shred.rs @@ -374,7 +374,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .hide(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/shuf/src/shuf.rs b/src/uu/shuf/src/shuf.rs index 0b62ec84a..6369fc9b5 100644 --- a/src/uu/shuf/src/shuf.rs +++ b/src/uu/shuf/src/shuf.rs @@ -158,14 +158,16 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::OUTPUT) .takes_value(true) .value_name("FILE") - .help("write result to FILE instead of standard output"), + .help("write result to FILE instead of standard output") + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::RANDOM_SOURCE) .long(options::RANDOM_SOURCE) .takes_value(true) .value_name("FILE") - .help("get random bytes from FILE"), + .help("get random bytes from FILE") + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::REPEAT) @@ -179,7 +181,11 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::ZERO_TERMINATED) .help("line delimiter is NUL, not newline"), ) - .arg(Arg::new(options::FILE).takes_value(true)) + .arg( + Arg::new(options::FILE) + .takes_value(true) + .value_hint(clap::ValueHint::FilePath), + ) } fn read_input_file(filename: &str) -> UResult> { diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 141a7dd2c..15becdc6b 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -1397,7 +1397,8 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::OUTPUT) .help("write output to FILENAME instead of stdout") .takes_value(true) - .value_name("FILENAME"), + .value_name("FILENAME") + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::REVERSE) @@ -1461,13 +1462,15 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::TMP_DIR) .help("use DIR for temporaries, not $TMPDIR or /tmp") .takes_value(true) - .value_name("DIR"), + .value_name("DIR") + .value_hint(clap::ValueHint::DirPath), ) .arg( Arg::new(options::COMPRESS_PROG) .long(options::COMPRESS_PROG) .help("compress temporary files with PROG, decompress with PROG -d; PROG has to take input from stdin and output to stdout") - .value_name("PROG"), + .value_name("PROG") + .value_hint(clap::ValueHint::CommandName), ) .arg( Arg::new(options::BATCH_SIZE) @@ -1482,7 +1485,8 @@ pub fn uu_app<'a>() -> Command<'a> { .takes_value(true) .value_name("NUL_FILES") .multiple_occurrences(true) - .allow_invalid_utf8(true), + .allow_invalid_utf8(true) + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::DEBUG) @@ -1493,7 +1497,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::FILES) .multiple_occurrences(true) .takes_value(true) - .allow_invalid_utf8(true), + .allow_invalid_utf8(true) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index c90c7a6b2..03905e877 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -111,9 +111,11 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(OPT_FILTER) .long(OPT_FILTER) .takes_value(true) + .value_name("COMMAND") + .value_hint(clap::ValueHint::CommandName) .help( - "write to shell COMMAND file name is $FILE (Currently not implemented for Windows)", - ), + "write to shell COMMAND; file name is $FILE (Currently not implemented for Windows)", + ), ) .arg( Arg::new(OPT_ELIDE_EMPTY_FILES) @@ -162,7 +164,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(ARG_INPUT) .takes_value(true) .default_value("-") - .index(1), + .index(1) + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(ARG_PREFIX) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 41c9ae4a9..e261ab813 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -1043,6 +1043,7 @@ pub fn uu_app<'a>() -> Command<'a> { .multiple_occurrences(true) .takes_value(true) .allow_invalid_utf8(true) - .min_values(1), + .min_values(1) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/stdbuf/src/stdbuf.rs b/src/uu/stdbuf/src/stdbuf.rs index 816c86717..e0025d9f1 100644 --- a/src/uu/stdbuf/src/stdbuf.rs +++ b/src/uu/stdbuf/src/stdbuf.rs @@ -232,6 +232,7 @@ pub fn uu_app<'a>() -> Command<'a> { .multiple_occurrences(true) .takes_value(true) .hide(true) - .required(true), + .required(true) + .value_hint(clap::ValueHint::CommandName), ) } diff --git a/src/uu/sum/src/sum.rs b/src/uu/sum/src/sum.rs index 501635910..9280fcf81 100644 --- a/src/uu/sum/src/sum.rs +++ b/src/uu/sum/src/sum.rs @@ -150,7 +150,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .multiple_occurrences(true) - .hide(true), + .hide(true) + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::BSD_COMPATIBLE) diff --git a/src/uu/sync/src/sync.rs b/src/uu/sync/src/sync.rs index 9baf9b182..6f34c11b7 100644 --- a/src/uu/sync/src/sync.rs +++ b/src/uu/sync/src/sync.rs @@ -217,7 +217,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(ARG_FILES) .multiple_occurrences(true) - .takes_value(true), + .takes_value(true) + .value_hint(clap::ValueHint::AnyPath), ) } diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index 3151b97e2..62d578ee9 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -91,7 +91,8 @@ pub fn uu_app<'a>() -> Command<'a> { .arg( Arg::new(options::FILE) .hide(true) - .multiple_occurrences(true), + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/tail/src/tail.rs b/src/uu/tail/src/tail.rs index 96b6daf79..8f5a62e61 100644 --- a/src/uu/tail/src/tail.rs +++ b/src/uu/tail/src/tail.rs @@ -351,7 +351,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::ARG_FILES) .multiple_occurrences(true) .takes_value(true) - .min_values(1), + .min_values(1) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/tee/src/tee.rs b/src/uu/tee/src/tee.rs index d9c2e78f1..7b6b66208 100644 --- a/src/uu/tee/src/tee.rs +++ b/src/uu/tee/src/tee.rs @@ -74,7 +74,11 @@ pub fn uu_app<'a>() -> Command<'a> { .short('i') .help("ignore interrupt signals (ignored on non-Unix platforms)"), ) - .arg(Arg::new(options::FILE).multiple_occurrences(true)) + .arg( + Arg::new(options::FILE) + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath), + ) } #[cfg(unix)] diff --git a/src/uu/timeout/src/timeout.rs b/src/uu/timeout/src/timeout.rs index 8374c124c..baafce4df 100644 --- a/src/uu/timeout/src/timeout.rs +++ b/src/uu/timeout/src/timeout.rs @@ -170,6 +170,7 @@ pub fn uu_app<'a>() -> Command<'a> { .index(2) .required(true) .multiple_occurrences(true) + .value_hint(clap::ValueHint::CommandName) ) .trailing_var_arg(true) .infer_long_args(true) diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index f71eb81d0..c250953bc 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -219,7 +219,8 @@ pub fn uu_app<'a>() -> Command<'a> { .long(options::sources::REFERENCE) .help("use this file's times instead of the current time") .value_name("FILE") - .allow_invalid_utf8(true), + .allow_invalid_utf8(true) + .value_hint(clap::ValueHint::AnyPath), ) .arg( Arg::new(options::TIME) @@ -238,7 +239,8 @@ pub fn uu_app<'a>() -> Command<'a> { .multiple_occurrences(true) .takes_value(true) .min_values(1) - .allow_invalid_utf8(true), + .allow_invalid_utf8(true) + .value_hint(clap::ValueHint::AnyPath), ) .group(ArgGroup::new(options::SOURCES).args(&[ options::sources::CURRENT, diff --git a/src/uu/truncate/src/truncate.rs b/src/uu/truncate/src/truncate.rs index 6c9d8197b..b3110dcc1 100644 --- a/src/uu/truncate/src/truncate.rs +++ b/src/uu/truncate/src/truncate.rs @@ -162,6 +162,7 @@ pub fn uu_app<'a>() -> Command<'a> { .required_unless_present(options::SIZE) .help("base the size of each file on the size of RFILE") .value_name("RFILE") + .value_hint(clap::ValueHint::FilePath) ) .arg( Arg::new(options::SIZE) @@ -176,7 +177,8 @@ pub fn uu_app<'a>() -> Command<'a> { .multiple_occurrences(true) .takes_value(true) .required(true) - .min_values(1)) + .min_values(1) + .value_hint(clap::ValueHint::FilePath)) } /// Truncate the named file to the specified size. diff --git a/src/uu/tsort/src/tsort.rs b/src/uu/tsort/src/tsort.rs index aecd492fe..72b8843e5 100644 --- a/src/uu/tsort/src/tsort.rs +++ b/src/uu/tsort/src/tsort.rs @@ -99,7 +99,12 @@ pub fn uu_app<'a>() -> Command<'a> { .override_usage(format_usage(USAGE)) .about(SUMMARY) .infer_long_args(true) - .arg(Arg::new(options::FILE).default_value("-").hide(true)) + .arg( + Arg::new(options::FILE) + .default_value("-") + .hide(true) + .value_hint(clap::ValueHint::FilePath), + ) } // We use String as a representation of node here diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index 70a763f3b..0ebfea613 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -109,7 +109,12 @@ pub fn uu_app<'a>() -> Command<'a> { .override_usage(format_usage(USAGE)) .about(SUMMARY) .infer_long_args(true) - .arg(Arg::new(options::FILE).hide(true).multiple_occurrences(true)) + .arg( + Arg::new(options::FILE) + .hide(true) + .multiple_occurrences(true) + .value_hint(clap::ValueHint::FilePath) + ) .arg( Arg::new(options::ALL) .short('a') diff --git a/src/uu/uniq/src/uniq.rs b/src/uu/uniq/src/uniq.rs index 32e0783ce..e71c21303 100644 --- a/src/uu/uniq/src/uniq.rs +++ b/src/uu/uniq/src/uniq.rs @@ -392,7 +392,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(ARG_FILES) .multiple_occurrences(true) .takes_value(true) - .max_values(2), + .max_values(2) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/unlink/src/unlink.rs b/src/uu/unlink/src/unlink.rs index fc72b4623..890935f1a 100644 --- a/src/uu/unlink/src/unlink.rs +++ b/src/uu/unlink/src/unlink.rs @@ -36,6 +36,7 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(OPT_PATH) .required(true) .hide(true) - .allow_invalid_utf8(true), + .allow_invalid_utf8(true) + .value_hint(clap::ValueHint::AnyPath), ) } diff --git a/src/uu/users/src/users.rs b/src/uu/users/src/users.rs index 79fac3b68..b1785d608 100644 --- a/src/uu/users/src/users.rs +++ b/src/uu/users/src/users.rs @@ -64,5 +64,10 @@ pub fn uu_app<'a>() -> Command<'a> { .about(ABOUT) .override_usage(format_usage(USAGE)) .infer_long_args(true) - .arg(Arg::new(ARG_FILES).takes_value(true).max_values(1)) + .arg( + Arg::new(ARG_FILES) + .takes_value(true) + .max_values(1) + .value_hint(clap::ValueHint::FilePath), + ) } diff --git a/src/uu/wc/src/wc.rs b/src/uu/wc/src/wc.rs index 968830c48..ba7c4c1af 100644 --- a/src/uu/wc/src/wc.rs +++ b/src/uu/wc/src/wc.rs @@ -210,7 +210,8 @@ pub fn uu_app<'a>() -> Command<'a> { "read input from the files specified by NUL-terminated names in file F; If F is - then read names from standard input", - ), + ) + .value_hint(clap::ValueHint::FilePath), ) .arg( Arg::new(options::LINES) @@ -234,7 +235,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(ARG_FILES) .multiple_occurrences(true) .takes_value(true) - .allow_invalid_utf8(true), + .allow_invalid_utf8(true) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uu/who/src/who.rs b/src/uu/who/src/who.rs index 47d96a381..6c07039d2 100644 --- a/src/uu/who/src/who.rs +++ b/src/uu/who/src/who.rs @@ -250,7 +250,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(options::FILE) .takes_value(true) .min_values(1) - .max_values(2), + .max_values(2) + .value_hint(clap::ValueHint::FilePath), ) } diff --git a/src/uucore/src/lib/features/perms.rs b/src/uucore/src/lib/features/perms.rs index d8e345313..df37e5a69 100644 --- a/src/uucore/src/lib/features/perms.rs +++ b/src/uucore/src/lib/features/perms.rs @@ -456,6 +456,7 @@ pub fn chown_base<'a>( command = command.arg( Arg::new(options::ARG_FILES) .value_name(options::ARG_FILES) + .value_hint(clap::ValueHint::FilePath) .multiple_occurrences(true) .takes_value(true) .required(true) From 996a84cb6f298b9a35f5a5d0ec40f73a8789a205 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Thu, 12 May 2022 09:09:10 +0200 Subject: [PATCH 24/24] df: round up values if block size is specified Fixes #3479 --- src/uu/df/src/table.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index a9a37cfeb..fac2ca663 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -233,7 +233,7 @@ impl<'a> RowFormatter<'a> { SizeFormat::HumanReadable(h) => self.scaled_human_readable(size, h), SizeFormat::StaticBlockSize => { let BlockSize::Bytes(d) = self.options.block_size; - (size / d).to_string() + (size as f64 / d as f64).ceil().to_string() } } } @@ -793,4 +793,28 @@ mod tests { let fmt = RowFormatter::new(&row, &options); assert_eq!(fmt.get_values(), vec!("26%")); } + + #[test] + fn test_row_formatter_with_round_up_byte_values() { + fn get_formatted_values(bytes: u64, bytes_used: u64, bytes_avail: u64) -> Vec { + let options = Options { + block_size: BlockSize::Bytes(1000), + columns: vec![Column::Size, Column::Used, Column::Avail], + ..Default::default() + }; + + let row = Row { + bytes, + bytes_used, + bytes_avail, + ..Default::default() + }; + RowFormatter::new(&row, &options).get_values() + } + + assert_eq!(get_formatted_values(100, 100, 0), vec!("1", "1", "0")); + assert_eq!(get_formatted_values(100, 99, 1), vec!("1", "1", "1")); + assert_eq!(get_formatted_values(1000, 1000, 0), vec!("1", "1", "0")); + assert_eq!(get_formatted_values(1001, 1000, 1), vec!("2", "1", "1")); + } }