mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 03:57:44 +00:00
Merge branch 'main' into fmt_implement_default_for_fmtoptions
This commit is contained in:
commit
a3b80e1bef
234 changed files with 4854 additions and 2994 deletions
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
github: uutils
|
25
.github/workflows/CICD.yml
vendored
25
.github/workflows/CICD.yml
vendored
|
@ -220,7 +220,7 @@ jobs:
|
||||||
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
|
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
|
||||||
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
|
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
|
||||||
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
|
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
|
||||||
S=$(cargo clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- -W clippy::manual_string_new -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
|
S=$(cargo clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- -W clippy::default_trait_access -W clippy::manual_string_new -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
|
||||||
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
|
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
|
||||||
|
|
||||||
style_spellcheck:
|
style_spellcheck:
|
||||||
|
@ -319,7 +319,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items
|
RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items
|
||||||
- uses: DavidAnson/markdownlint-cli2-action@v10
|
- uses: DavidAnson/markdownlint-cli2-action@v11
|
||||||
with:
|
with:
|
||||||
command: fix
|
command: fix
|
||||||
globs: |
|
globs: |
|
||||||
|
@ -452,8 +452,20 @@ jobs:
|
||||||
test -f /tmp/usr/local/share/man/man1/whoami.1
|
test -f /tmp/usr/local/share/man/man1/whoami.1
|
||||||
# Check that the completion is present
|
# Check that the completion is present
|
||||||
test -f /tmp/usr/local/share/zsh/site-functions/_install
|
test -f /tmp/usr/local/share/zsh/site-functions/_install
|
||||||
|
test -f /tmp/usr/local/share/bash-completion/completions/head
|
||||||
|
test -f /tmp/usr/local/share/fish/vendor_completions.d/cat.fish
|
||||||
env:
|
env:
|
||||||
RUST_BACKTRACE: "1"
|
RUST_BACKTRACE: "1"
|
||||||
|
- name: "`make uninstall`"
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
DESTDIR=/tmp/ make uninstall
|
||||||
|
# Check that the manpage is not present
|
||||||
|
! test -f /tmp/usr/local/share/man/man1/whoami.1
|
||||||
|
# Check that the completion is not present
|
||||||
|
! test -f /tmp/usr/local/share/zsh/site-functions/_install
|
||||||
|
! test -f /tmp/usr/local/share/bash-completion/completions/head
|
||||||
|
! test -f /tmp/usr/local/share/fish/vendor_completions.d/cat.fish
|
||||||
|
|
||||||
build_rust_stable:
|
build_rust_stable:
|
||||||
name: Build/stable
|
name: Build/stable
|
||||||
|
@ -1048,6 +1060,15 @@ jobs:
|
||||||
name: toybox-result.json
|
name: toybox-result.json
|
||||||
path: ${{ steps.vars.outputs.TEST_SUMMARY_FILE }}
|
path: ${{ steps.vars.outputs.TEST_SUMMARY_FILE }}
|
||||||
|
|
||||||
|
toml_format:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Check
|
||||||
|
run: npx --yes @taplo/cli fmt --check
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
name: Code Coverage
|
name: Code Coverage
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
|
74
.github/workflows/CheckScripts.yml
vendored
Normal file
74
.github/workflows/CheckScripts.yml
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
name: CheckScripts
|
||||||
|
|
||||||
|
# spell-checker:ignore ludeeus mfinelli
|
||||||
|
|
||||||
|
env:
|
||||||
|
SCRIPT_DIR: 'util'
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'util/**/*.sh'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'util/**/*.sh'
|
||||||
|
|
||||||
|
# End the current execution if there is a new changeset in the PR.
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
shell_check:
|
||||||
|
name: ShellScript/Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Run ShellCheck
|
||||||
|
uses: ludeeus/action-shellcheck@master
|
||||||
|
env:
|
||||||
|
SHELLCHECK_OPTS: -s bash
|
||||||
|
with:
|
||||||
|
severity: warning
|
||||||
|
scandir: ${{ env.SCRIPT_DIR }}
|
||||||
|
format: tty
|
||||||
|
|
||||||
|
shell_fmt:
|
||||||
|
name: ShellScript/Format
|
||||||
|
# no need to run in pr events
|
||||||
|
# shfmt will be performed on main branch when the PR is merged
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ shell_check ]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup shfmt
|
||||||
|
uses: mfinelli/setup-shfmt@v2
|
||||||
|
- name: Run shfmt
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# show differs first for every files that need to be formatted
|
||||||
|
# fmt options: bash syntax, 4 spaces indent, indent for switch-case
|
||||||
|
echo "## show the differences between formatted and original scripts..."
|
||||||
|
find ${{ env.SCRIPT_DIR }} -name "*.sh" -print0 | xargs -0 shfmt -ln=bash -i 4 -ci -d || true
|
||||||
|
# perform a shell format
|
||||||
|
echo "## perform a shell format..."
|
||||||
|
# ignore the error code because `-d` will always return false when the file has difference
|
||||||
|
find ${{ env.SCRIPT_DIR }} -name "*.sh" -print0 | xargs -0 shfmt -ln=bash -i 4 -ci -w
|
||||||
|
- name: Commit any changes
|
||||||
|
uses: EndBug/add-and-commit@v9
|
||||||
|
with:
|
||||||
|
default_author: github_actions
|
||||||
|
message: "style: auto format by CI (shfmt)"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
16
.github/workflows/FixPR.yml
vendored
16
.github/workflows/FixPR.yml
vendored
|
@ -31,12 +31,12 @@ jobs:
|
||||||
id: vars
|
id: vars
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## VARs setup
|
|
||||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
|
||||||
# surface MSRV from CICD workflow
|
# surface MSRV from CICD workflow
|
||||||
RUST_MIN_SRV=$(grep -P "^\s+RUST_MIN_SRV:" .github/workflows/CICD.yml | grep -Po "(?<=\x22)\d+[.]\d+(?:[.]\d+)?(?=\x22)" )
|
RUST_MIN_SRV=$(grep -P "^\s+RUST_MIN_SRV:" .github/workflows/CICD.yml | grep -Po "(?<=\x22)\d+[.]\d+(?:[.]\d+)?(?=\x22)" )
|
||||||
outputs RUST_MIN_SRV
|
echo "RUST_MIN_SRV=${RUST_MIN_SRV}" >> $GITHUB_OUTPUT
|
||||||
- uses: dtolnay/rust-toolchain@${{ steps.vars.outputs.RUST_MIN_SRV }}
|
- uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: ${{ steps.vars.outputs.RUST_MIN_SRV }}
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Ensure updated 'Cargo.lock'
|
- name: Ensure updated 'Cargo.lock'
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -67,7 +67,7 @@ jobs:
|
||||||
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
||||||
uses: EndBug/add-and-commit@v9
|
uses: EndBug/add-and-commit@v9
|
||||||
with:
|
with:
|
||||||
branch: ${{ env.BRANCH_TARGET }}
|
new_branch: ${{ env.BRANCH_TARGET }}
|
||||||
default_author: github_actions
|
default_author: github_actions
|
||||||
message: "maint ~ refresh 'Cargo.lock'"
|
message: "maint ~ refresh 'Cargo.lock'"
|
||||||
add: Cargo.lock
|
add: Cargo.lock
|
||||||
|
@ -90,13 +90,11 @@ jobs:
|
||||||
id: vars
|
id: vars
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## VARs setup
|
|
||||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
|
||||||
# target-specific options
|
# target-specific options
|
||||||
# * CARGO_FEATURES_OPTION
|
# * CARGO_FEATURES_OPTION
|
||||||
CARGO_FEATURES_OPTION='' ;
|
CARGO_FEATURES_OPTION='' ;
|
||||||
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
||||||
outputs CARGO_FEATURES_OPTION
|
echo "CARGO_FEATURES_OPTION=${CARGO_FEATURES_OPTION}" >> $GITHUB_OUTPUT
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
|
@ -114,7 +112,7 @@ jobs:
|
||||||
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
||||||
uses: EndBug/add-and-commit@v9
|
uses: EndBug/add-and-commit@v9
|
||||||
with:
|
with:
|
||||||
branch: ${{ env.BRANCH_TARGET }}
|
new_branch: ${{ env.BRANCH_TARGET }}
|
||||||
default_author: github_actions
|
default_author: github_actions
|
||||||
message: "maint ~ rustfmt (`cargo fmt`)"
|
message: "maint ~ rustfmt (`cargo fmt`)"
|
||||||
env:
|
env:
|
||||||
|
|
2
.github/workflows/GnuTests.yml
vendored
2
.github/workflows/GnuTests.yml
vendored
|
@ -205,7 +205,7 @@ jobs:
|
||||||
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
|
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
|
||||||
# https://github.com/uutils/coreutils/issues/4294
|
# https://github.com/uutils/coreutils/issues/4294
|
||||||
# https://github.com/uutils/coreutils/issues/4295
|
# https://github.com/uutils/coreutils/issues/4295
|
||||||
IGNORE_INTERMITTENT='${path_UUTILS}/.github/workflows/ignore-intermittent.txt'
|
IGNORE_INTERMITTENT="${path_UUTILS}/.github/workflows/ignore-intermittent.txt"
|
||||||
|
|
||||||
mkdir -p ${{ steps.vars.outputs.path_reference }}
|
mkdir -p ${{ steps.vars.outputs.path_reference }}
|
||||||
|
|
||||||
|
|
4
.github/workflows/freebsd.yml
vendored
4
.github/workflows/freebsd.yml
vendored
|
@ -35,7 +35,7 @@ jobs:
|
||||||
- name: Run sccache-cache
|
- name: Run sccache-cache
|
||||||
uses: mozilla-actions/sccache-action@v0.0.3
|
uses: mozilla-actions/sccache-action@v0.0.3
|
||||||
- name: Prepare, build and test
|
- name: Prepare, build and test
|
||||||
uses: vmactions/freebsd-vm@v0.3.0
|
uses: vmactions/freebsd-vm@v0.3.1
|
||||||
with:
|
with:
|
||||||
usesh: true
|
usesh: true
|
||||||
# We need jq to run show-utils.sh and bash to use inline shell string replacement
|
# We need jq to run show-utils.sh and bash to use inline shell string replacement
|
||||||
|
@ -125,7 +125,7 @@ jobs:
|
||||||
- name: Run sccache-cache
|
- name: Run sccache-cache
|
||||||
uses: mozilla-actions/sccache-action@v0.0.3
|
uses: mozilla-actions/sccache-action@v0.0.3
|
||||||
- name: Prepare, build and test
|
- name: Prepare, build and test
|
||||||
uses: vmactions/freebsd-vm@v0.3.0
|
uses: vmactions/freebsd-vm@v0.3.1
|
||||||
with:
|
with:
|
||||||
usesh: true
|
usesh: true
|
||||||
# sync: sshfs
|
# sync: sshfs
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,6 +5,7 @@ target/
|
||||||
/busybox/
|
/busybox/
|
||||||
/.vscode/
|
/.vscode/
|
||||||
/.vs/
|
/.vs/
|
||||||
|
/public/
|
||||||
*~
|
*~
|
||||||
.*.swp
|
.*.swp
|
||||||
.*.swo
|
.*.swo
|
||||||
|
|
1
.vscode/cSpell.json
vendored
1
.vscode/cSpell.json
vendored
|
@ -19,6 +19,7 @@
|
||||||
// files to ignore (globs supported)
|
// files to ignore (globs supported)
|
||||||
"ignorePaths": [
|
"ignorePaths": [
|
||||||
"Cargo.lock",
|
"Cargo.lock",
|
||||||
|
"oranda.json",
|
||||||
"target/**",
|
"target/**",
|
||||||
"tests/**/fixtures/**",
|
"tests/**/fixtures/**",
|
||||||
"src/uu/dd/test-resources/**",
|
"src/uu/dd/test-resources/**",
|
||||||
|
|
|
@ -356,6 +356,7 @@ The Coreutils have different implementations, with different levels of completio
|
||||||
* [V lang](https://github.com/vlang/coreutils)
|
* [V lang](https://github.com/vlang/coreutils)
|
||||||
* [SerenityOS](https://github.com/SerenityOS/serenity/tree/master/Userland/Utilities)
|
* [SerenityOS](https://github.com/SerenityOS/serenity/tree/master/Userland/Utilities)
|
||||||
* [Initial Unix](https://github.com/dspinellis/unix-history-repo)
|
* [Initial Unix](https://github.com/dspinellis/unix-history-repo)
|
||||||
|
* [Perl Power Tools](https://metacpan.org/pod/PerlPowerTools)
|
||||||
|
|
||||||
However, when reimplementing the tools/options in Rust, don't read their source codes
|
However, when reimplementing the tools/options in Rust, don't read their source codes
|
||||||
when they are using reciprocal licenses (ex: GNU GPL, GNU LGPL, etc).
|
when they are using reciprocal licenses (ex: GNU GPL, GNU LGPL, etc).
|
||||||
|
|
940
Cargo.lock
generated
940
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
308
Cargo.toml
308
Cargo.toml
|
@ -1,11 +1,11 @@
|
||||||
# coreutils (uutils)
|
# coreutils (uutils)
|
||||||
# * see the repository LICENSE, README, and CONTRIBUTING files for more information
|
# * see the repository LICENSE, README, and CONTRIBUTING files for more information
|
||||||
|
|
||||||
# spell-checker:ignore (libs) libselinux gethostid procfs bigdecimal kqueue fundu mangen humantime
|
# spell-checker:ignore (libs) bigdecimal datetime fundu gethostid kqueue libselinux mangen memmap procfs uuhelp
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "coreutils"
|
name = "coreutils"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust"
|
description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust"
|
||||||
|
@ -31,7 +31,7 @@ windows = [ "feat_os_windows" ]
|
||||||
nightly = []
|
nightly = []
|
||||||
test_unimplemented = []
|
test_unimplemented = []
|
||||||
# * only build `uudoc` when `--feature uudoc` is activated
|
# * only build `uudoc` when `--feature uudoc` is activated
|
||||||
uudoc = ["zip", "dep:help_parser"]
|
uudoc = ["zip", "dep:uuhelp_parser"]
|
||||||
## features
|
## features
|
||||||
# "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`)
|
# "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`)
|
||||||
# NOTE:
|
# NOTE:
|
||||||
|
@ -42,7 +42,13 @@ feat_acl = ["cp/feat_acl"]
|
||||||
# NOTE:
|
# NOTE:
|
||||||
# * The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time.
|
# * The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time.
|
||||||
# * Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time.
|
# * Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time.
|
||||||
feat_selinux = ["cp/selinux", "id/selinux", "ls/selinux", "selinux", "feat_require_selinux"]
|
feat_selinux = [
|
||||||
|
"cp/selinux",
|
||||||
|
"id/selinux",
|
||||||
|
"ls/selinux",
|
||||||
|
"selinux",
|
||||||
|
"feat_require_selinux",
|
||||||
|
]
|
||||||
##
|
##
|
||||||
## feature sets
|
## feature sets
|
||||||
## (common/core and Tier1) feature sets
|
## (common/core and Tier1) feature sets
|
||||||
|
@ -177,9 +183,7 @@ feat_os_unix_android = [
|
||||||
# ** NOTE: these `feat_require_...` sets should be minimized as much as possible to encourage cross-platform availability of utilities
|
# ** NOTE: these `feat_require_...` sets should be minimized as much as possible to encourage cross-platform availability of utilities
|
||||||
#
|
#
|
||||||
# "feat_require_crate_cpp" == set of utilities requiring the `cpp` crate (which fail to compile on several platforms; as of 2020-04-23)
|
# "feat_require_crate_cpp" == set of utilities requiring the `cpp` crate (which fail to compile on several platforms; as of 2020-04-23)
|
||||||
feat_require_crate_cpp = [
|
feat_require_crate_cpp = ["stdbuf"]
|
||||||
"stdbuf",
|
|
||||||
]
|
|
||||||
# "feat_require_unix" == set of utilities requiring support which is only available on unix platforms (as of 2020-04-23)
|
# "feat_require_unix" == set of utilities requiring support which is only available on unix platforms (as of 2020-04-23)
|
||||||
feat_require_unix = [
|
feat_require_unix = [
|
||||||
"chgrp",
|
"chgrp",
|
||||||
|
@ -203,21 +207,11 @@ feat_require_unix = [
|
||||||
]
|
]
|
||||||
# "feat_require_unix_utmpx" == set of utilities requiring unix utmp/utmpx support
|
# "feat_require_unix_utmpx" == set of utilities requiring unix utmp/utmpx support
|
||||||
# * ref: <https://wiki.musl-libc.org/faq.html#Q:-Why-is-the-utmp/wtmp-functionality-only-implemented-as-stubs?>
|
# * ref: <https://wiki.musl-libc.org/faq.html#Q:-Why-is-the-utmp/wtmp-functionality-only-implemented-as-stubs?>
|
||||||
feat_require_unix_utmpx = [
|
feat_require_unix_utmpx = ["pinky", "uptime", "users", "who"]
|
||||||
"pinky",
|
|
||||||
"uptime",
|
|
||||||
"users",
|
|
||||||
"who",
|
|
||||||
]
|
|
||||||
# "feat_require_unix_hostid" == set of utilities requiring gethostid in libc (only some unixes provide)
|
# "feat_require_unix_hostid" == set of utilities requiring gethostid in libc (only some unixes provide)
|
||||||
feat_require_unix_hostid = [
|
feat_require_unix_hostid = ["hostid"]
|
||||||
"hostid",
|
|
||||||
]
|
|
||||||
# "feat_require_selinux" == set of utilities depending on SELinux.
|
# "feat_require_selinux" == set of utilities depending on SELinux.
|
||||||
feat_require_selinux = [
|
feat_require_selinux = ["chcon", "runcon"]
|
||||||
"chcon",
|
|
||||||
"runcon",
|
|
||||||
]
|
|
||||||
## (alternate/newer/smaller platforms) feature sets
|
## (alternate/newer/smaller platforms) feature sets
|
||||||
# "feat_os_unix_fuchsia" == set of utilities which can be built/run on the "Fuchsia" OS (refs: <https://fuchsia.dev>; <https://en.wikipedia.org/wiki/Google_Fuchsia>)
|
# "feat_os_unix_fuchsia" == set of utilities which can be built/run on the "Fuchsia" OS (refs: <https://fuchsia.dev>; <https://en.wikipedia.org/wiki/Google_Fuchsia>)
|
||||||
feat_os_unix_fuchsia = [
|
feat_os_unix_fuchsia = [
|
||||||
|
@ -263,18 +257,22 @@ feat_os_windows_legacy = [
|
||||||
test = ["uu_test"]
|
test = ["uu_test"]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
bigdecimal = "0.3"
|
bigdecimal = "0.4"
|
||||||
binary-heap-plus = "0.5.0"
|
binary-heap-plus = "0.5.0"
|
||||||
bstr = "1.5"
|
bstr = "1.6"
|
||||||
bytecount = "0.6.3"
|
bytecount = "0.6.3"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
chrono = { version="^0.4.26", default-features=false, features=["std", "alloc", "clock"]}
|
chrono = { version = "^0.4.26", default-features = false, features = [
|
||||||
|
"std",
|
||||||
|
"alloc",
|
||||||
|
"clock",
|
||||||
|
] }
|
||||||
clap = { version = "4.3", features = ["wrap_help", "cargo"] }
|
clap = { version = "4.3", features = ["wrap_help", "cargo"] }
|
||||||
clap_complete = "4.3"
|
clap_complete = "4.3"
|
||||||
clap_mangen = "0.2"
|
clap_mangen = "0.2"
|
||||||
compare = "0.1.0"
|
compare = "0.1.0"
|
||||||
coz = { version = "0.1.3" }
|
coz = { version = "0.1.3" }
|
||||||
crossterm = ">=0.26.1"
|
crossterm = ">=0.27.0"
|
||||||
ctrlc = { version = "3.4", features = ["termination"] }
|
ctrlc = { version = "3.4", features = ["termination"] }
|
||||||
exacl = "0.10.0"
|
exacl = "0.10.0"
|
||||||
file_diff = "1.0.0"
|
file_diff = "1.0.0"
|
||||||
|
@ -282,42 +280,45 @@ filetime = "0.2"
|
||||||
fnv = "1.0.7"
|
fnv = "1.0.7"
|
||||||
fs_extra = "1.3.0"
|
fs_extra = "1.3.0"
|
||||||
fts-sys = "0.2"
|
fts-sys = "0.2"
|
||||||
fundu = "0.5.1"
|
fundu = "2.0.0"
|
||||||
gcd = "2.3"
|
gcd = "2.3"
|
||||||
glob = "0.3.1"
|
glob = "0.3.1"
|
||||||
half = "2.2"
|
half = "2.2"
|
||||||
humantime_to_duration = "0.2.1"
|
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
is-terminal = "0.4.7"
|
is-terminal = "0.4.7"
|
||||||
itertools = "0.10.5"
|
itertools = "0.11.0"
|
||||||
libc = "0.2.144"
|
libc = "0.2.147"
|
||||||
lscolors = { version = "0.14.0", default-features=false, features = ["nu-ansi-term"] }
|
lscolors = { version = "0.15.0", default-features = false, features = [
|
||||||
|
"nu-ansi-term",
|
||||||
|
] }
|
||||||
memchr = "2"
|
memchr = "2"
|
||||||
|
memmap2 = "0.7"
|
||||||
nix = { version = "0.26", default-features = false }
|
nix = { version = "0.26", default-features = false }
|
||||||
nom = "7.1.3"
|
nom = "7.1.3"
|
||||||
notify = { version = "=6.0.0", features=["macos_kqueue"]}
|
notify = { version = "=6.0.1", features = ["macos_kqueue"] }
|
||||||
num-bigint = "0.4.3"
|
num-bigint = "0.4.3"
|
||||||
num-traits = "0.2.15"
|
num-traits = "0.2.16"
|
||||||
number_prefix = "0.4"
|
number_prefix = "0.4"
|
||||||
once_cell = "1.17.2"
|
once_cell = "1.18.0"
|
||||||
onig = { version = "~6.4", default-features = false }
|
onig = { version = "~6.4", default-features = false }
|
||||||
ouroboros = "0.15.6"
|
parse_datetime = "0.4.0"
|
||||||
phf = "0.11.1"
|
phf = "0.11.2"
|
||||||
phf_codegen = "0.11.1"
|
phf_codegen = "0.11.2"
|
||||||
platform-info = "2.0.1"
|
platform-info = "2.0.2"
|
||||||
quick-error = "2.0.1"
|
quick-error = "2.0.1"
|
||||||
rand = { version = "0.8", features = ["small_rng"] }
|
rand = { version = "0.8", features = ["small_rng"] }
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
rayon = "1.7"
|
rayon = "1.7"
|
||||||
redox_syscall = "0.3"
|
redox_syscall = "0.3"
|
||||||
regex = "1.8.3"
|
regex = "1.9.3"
|
||||||
rstest = "0.17.0"
|
rstest = "0.18.1"
|
||||||
rust-ini = "0.18.0"
|
rust-ini = "0.19.0"
|
||||||
same-file = "1.0.6"
|
same-file = "1.0.6"
|
||||||
|
self_cell = "1.0.1"
|
||||||
selinux = "0.4"
|
selinux = "0.4"
|
||||||
signal-hook = "0.3.15"
|
signal-hook = "0.3.17"
|
||||||
smallvec = { version = "1.10", features = ["union"] }
|
smallvec = { version = "1.11", features = ["union"] }
|
||||||
tempfile = "3.5.0"
|
tempfile = "3.6.0"
|
||||||
term_grid = "0.1.5"
|
term_grid = "0.1.5"
|
||||||
terminal_size = "0.2.6"
|
terminal_size = "0.2.6"
|
||||||
textwrap = { version = "0.16.0", features = ["terminal_size"] }
|
textwrap = { version = "0.16.0", features = ["terminal_size"] }
|
||||||
|
@ -329,21 +330,21 @@ utf-8 = "0.7.6"
|
||||||
walkdir = "2.3"
|
walkdir = "2.3"
|
||||||
winapi-util = "0.1.5"
|
winapi-util = "0.1.5"
|
||||||
windows-sys = { version = "0.48.0", default-features = false }
|
windows-sys = { version = "0.48.0", default-features = false }
|
||||||
xattr = "1.0.0"
|
xattr = "1.0.1"
|
||||||
zip = { version = "0.6.6", default_features = false, features = ["deflate"] }
|
zip = { version = "0.6.6", default_features = false, features = ["deflate"] }
|
||||||
|
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
md-5 = "0.10.5"
|
md-5 = "0.10.5"
|
||||||
sha1 = "0.10.5"
|
sha1 = "0.10.5"
|
||||||
sha2 = "0.10.6"
|
sha2 = "0.10.7"
|
||||||
sha3 = "0.10.8"
|
sha3 = "0.10.8"
|
||||||
blake2b_simd = "1.0.1"
|
blake2b_simd = "1.0.1"
|
||||||
blake3 = "1.3.3"
|
blake3 = "1.4.0"
|
||||||
sm3 = "0.4.2"
|
sm3 = "0.4.2"
|
||||||
digest = "0.10.7"
|
digest = "0.10.7"
|
||||||
|
|
||||||
uucore = { version=">=0.0.18", package="uucore", path="src/uucore" }
|
uucore = { version = ">=0.0.19", package = "uucore", path = "src/uucore" }
|
||||||
uucore_procs = { version=">=0.0.18", package="uucore_procs", path="src/uucore_procs" }
|
uucore_procs = { version = ">=0.0.19", package = "uucore_procs", path = "src/uucore_procs" }
|
||||||
uu_ls = { version = ">=0.0.18", path = "src/uu/ls" }
|
uu_ls = { version = ">=0.0.18", path = "src/uu/ls" }
|
||||||
uu_base32 = { version = ">=0.0.18", path = "src/uu/base32" }
|
uu_base32 = { version = ">=0.0.18", path = "src/uu/base32" }
|
||||||
|
|
||||||
|
@ -358,113 +359,113 @@ selinux = { workspace=true, optional = true }
|
||||||
textwrap = { workspace = true }
|
textwrap = { workspace = true }
|
||||||
zip = { workspace = true, optional = true }
|
zip = { workspace = true, optional = true }
|
||||||
|
|
||||||
help_parser = { path="src/help_parser", optional = true }
|
uuhelp_parser = { optional = true, version = ">=0.0.19", path = "src/uuhelp_parser" }
|
||||||
|
|
||||||
# * uutils
|
# * uutils
|
||||||
uu_test = { optional=true, version="0.0.18", package="uu_test", path="src/uu/test" }
|
uu_test = { optional = true, version = "0.0.20", package = "uu_test", path = "src/uu/test" }
|
||||||
#
|
#
|
||||||
arch = { optional=true, version="0.0.18", package="uu_arch", path="src/uu/arch" }
|
arch = { optional = true, version = "0.0.20", package = "uu_arch", path = "src/uu/arch" }
|
||||||
base32 = { optional=true, version="0.0.18", package="uu_base32", path="src/uu/base32" }
|
base32 = { optional = true, version = "0.0.20", package = "uu_base32", path = "src/uu/base32" }
|
||||||
base64 = { optional=true, version="0.0.18", package="uu_base64", path="src/uu/base64" }
|
base64 = { optional = true, version = "0.0.20", package = "uu_base64", path = "src/uu/base64" }
|
||||||
basename = { optional=true, version="0.0.18", package="uu_basename", path="src/uu/basename" }
|
basename = { optional = true, version = "0.0.20", package = "uu_basename", path = "src/uu/basename" }
|
||||||
basenc = { optional=true, version="0.0.18", package="uu_basenc", path="src/uu/basenc" }
|
basenc = { optional = true, version = "0.0.20", package = "uu_basenc", path = "src/uu/basenc" }
|
||||||
cat = { optional=true, version="0.0.18", package="uu_cat", path="src/uu/cat" }
|
cat = { optional = true, version = "0.0.20", package = "uu_cat", path = "src/uu/cat" }
|
||||||
chcon = { optional=true, version="0.0.18", package="uu_chcon", path="src/uu/chcon" }
|
chcon = { optional = true, version = "0.0.20", package = "uu_chcon", path = "src/uu/chcon" }
|
||||||
chgrp = { optional=true, version="0.0.18", package="uu_chgrp", path="src/uu/chgrp" }
|
chgrp = { optional = true, version = "0.0.20", package = "uu_chgrp", path = "src/uu/chgrp" }
|
||||||
chmod = { optional=true, version="0.0.18", package="uu_chmod", path="src/uu/chmod" }
|
chmod = { optional = true, version = "0.0.20", package = "uu_chmod", path = "src/uu/chmod" }
|
||||||
chown = { optional=true, version="0.0.18", package="uu_chown", path="src/uu/chown" }
|
chown = { optional = true, version = "0.0.20", package = "uu_chown", path = "src/uu/chown" }
|
||||||
chroot = { optional=true, version="0.0.18", package="uu_chroot", path="src/uu/chroot" }
|
chroot = { optional = true, version = "0.0.20", package = "uu_chroot", path = "src/uu/chroot" }
|
||||||
cksum = { optional=true, version="0.0.18", package="uu_cksum", path="src/uu/cksum" }
|
cksum = { optional = true, version = "0.0.20", package = "uu_cksum", path = "src/uu/cksum" }
|
||||||
comm = { optional=true, version="0.0.18", package="uu_comm", path="src/uu/comm" }
|
comm = { optional = true, version = "0.0.20", package = "uu_comm", path = "src/uu/comm" }
|
||||||
cp = { optional=true, version="0.0.18", package="uu_cp", path="src/uu/cp" }
|
cp = { optional = true, version = "0.0.20", package = "uu_cp", path = "src/uu/cp" }
|
||||||
csplit = { optional=true, version="0.0.18", package="uu_csplit", path="src/uu/csplit" }
|
csplit = { optional = true, version = "0.0.20", package = "uu_csplit", path = "src/uu/csplit" }
|
||||||
cut = { optional=true, version="0.0.18", package="uu_cut", path="src/uu/cut" }
|
cut = { optional = true, version = "0.0.20", package = "uu_cut", path = "src/uu/cut" }
|
||||||
date = { optional=true, version="0.0.18", package="uu_date", path="src/uu/date" }
|
date = { optional = true, version = "0.0.20", package = "uu_date", path = "src/uu/date" }
|
||||||
dd = { optional=true, version="0.0.18", package="uu_dd", path="src/uu/dd" }
|
dd = { optional = true, version = "0.0.20", package = "uu_dd", path = "src/uu/dd" }
|
||||||
df = { optional=true, version="0.0.18", package="uu_df", path="src/uu/df" }
|
df = { optional = true, version = "0.0.20", package = "uu_df", path = "src/uu/df" }
|
||||||
dir = { optional=true, version="0.0.18", package="uu_dir", path="src/uu/dir" }
|
dir = { optional = true, version = "0.0.20", package = "uu_dir", path = "src/uu/dir" }
|
||||||
dircolors= { optional=true, version="0.0.18", package="uu_dircolors", path="src/uu/dircolors" }
|
dircolors = { optional = true, version = "0.0.20", package = "uu_dircolors", path = "src/uu/dircolors" }
|
||||||
dirname = { optional=true, version="0.0.18", package="uu_dirname", path="src/uu/dirname" }
|
dirname = { optional = true, version = "0.0.20", package = "uu_dirname", path = "src/uu/dirname" }
|
||||||
du = { optional=true, version="0.0.18", package="uu_du", path="src/uu/du" }
|
du = { optional = true, version = "0.0.20", package = "uu_du", path = "src/uu/du" }
|
||||||
echo = { optional=true, version="0.0.18", package="uu_echo", path="src/uu/echo" }
|
echo = { optional = true, version = "0.0.20", package = "uu_echo", path = "src/uu/echo" }
|
||||||
env = { optional=true, version="0.0.18", package="uu_env", path="src/uu/env" }
|
env = { optional = true, version = "0.0.20", package = "uu_env", path = "src/uu/env" }
|
||||||
expand = { optional=true, version="0.0.18", package="uu_expand", path="src/uu/expand" }
|
expand = { optional = true, version = "0.0.20", package = "uu_expand", path = "src/uu/expand" }
|
||||||
expr = { optional=true, version="0.0.18", package="uu_expr", path="src/uu/expr" }
|
expr = { optional = true, version = "0.0.20", package = "uu_expr", path = "src/uu/expr" }
|
||||||
factor = { optional=true, version="0.0.18", package="uu_factor", path="src/uu/factor" }
|
factor = { optional = true, version = "0.0.20", package = "uu_factor", path = "src/uu/factor" }
|
||||||
false = { optional=true, version="0.0.18", package="uu_false", path="src/uu/false" }
|
false = { optional = true, version = "0.0.20", package = "uu_false", path = "src/uu/false" }
|
||||||
fmt = { optional=true, version="0.0.18", package="uu_fmt", path="src/uu/fmt" }
|
fmt = { optional = true, version = "0.0.20", package = "uu_fmt", path = "src/uu/fmt" }
|
||||||
fold = { optional=true, version="0.0.18", package="uu_fold", path="src/uu/fold" }
|
fold = { optional = true, version = "0.0.20", package = "uu_fold", path = "src/uu/fold" }
|
||||||
groups = { optional=true, version="0.0.18", package="uu_groups", path="src/uu/groups" }
|
groups = { optional = true, version = "0.0.20", package = "uu_groups", path = "src/uu/groups" }
|
||||||
hashsum = { optional=true, version="0.0.18", package="uu_hashsum", path="src/uu/hashsum" }
|
hashsum = { optional = true, version = "0.0.20", package = "uu_hashsum", path = "src/uu/hashsum" }
|
||||||
head = { optional=true, version="0.0.18", package="uu_head", path="src/uu/head" }
|
head = { optional = true, version = "0.0.20", package = "uu_head", path = "src/uu/head" }
|
||||||
hostid = { optional=true, version="0.0.18", package="uu_hostid", path="src/uu/hostid" }
|
hostid = { optional = true, version = "0.0.20", package = "uu_hostid", path = "src/uu/hostid" }
|
||||||
hostname = { optional=true, version="0.0.18", package="uu_hostname", path="src/uu/hostname" }
|
hostname = { optional = true, version = "0.0.20", package = "uu_hostname", path = "src/uu/hostname" }
|
||||||
id = { optional=true, version="0.0.18", package="uu_id", path="src/uu/id" }
|
id = { optional = true, version = "0.0.20", package = "uu_id", path = "src/uu/id" }
|
||||||
install = { optional=true, version="0.0.18", package="uu_install", path="src/uu/install" }
|
install = { optional = true, version = "0.0.20", package = "uu_install", path = "src/uu/install" }
|
||||||
join = { optional=true, version="0.0.18", package="uu_join", path="src/uu/join" }
|
join = { optional = true, version = "0.0.20", package = "uu_join", path = "src/uu/join" }
|
||||||
kill = { optional=true, version="0.0.18", package="uu_kill", path="src/uu/kill" }
|
kill = { optional = true, version = "0.0.20", package = "uu_kill", path = "src/uu/kill" }
|
||||||
link = { optional=true, version="0.0.18", package="uu_link", path="src/uu/link" }
|
link = { optional = true, version = "0.0.20", package = "uu_link", path = "src/uu/link" }
|
||||||
ln = { optional=true, version="0.0.18", package="uu_ln", path="src/uu/ln" }
|
ln = { optional = true, version = "0.0.20", package = "uu_ln", path = "src/uu/ln" }
|
||||||
ls = { optional=true, version="0.0.18", package="uu_ls", path="src/uu/ls" }
|
ls = { optional = true, version = "0.0.20", package = "uu_ls", path = "src/uu/ls" }
|
||||||
logname = { optional=true, version="0.0.18", package="uu_logname", path="src/uu/logname" }
|
logname = { optional = true, version = "0.0.20", package = "uu_logname", path = "src/uu/logname" }
|
||||||
mkdir = { optional=true, version="0.0.18", package="uu_mkdir", path="src/uu/mkdir" }
|
mkdir = { optional = true, version = "0.0.20", package = "uu_mkdir", path = "src/uu/mkdir" }
|
||||||
mkfifo = { optional=true, version="0.0.18", package="uu_mkfifo", path="src/uu/mkfifo" }
|
mkfifo = { optional = true, version = "0.0.20", package = "uu_mkfifo", path = "src/uu/mkfifo" }
|
||||||
mknod = { optional=true, version="0.0.18", package="uu_mknod", path="src/uu/mknod" }
|
mknod = { optional = true, version = "0.0.20", package = "uu_mknod", path = "src/uu/mknod" }
|
||||||
mktemp = { optional=true, version="0.0.18", package="uu_mktemp", path="src/uu/mktemp" }
|
mktemp = { optional = true, version = "0.0.20", package = "uu_mktemp", path = "src/uu/mktemp" }
|
||||||
more = { optional=true, version="0.0.18", package="uu_more", path="src/uu/more" }
|
more = { optional = true, version = "0.0.20", package = "uu_more", path = "src/uu/more" }
|
||||||
mv = { optional=true, version="0.0.18", package="uu_mv", path="src/uu/mv" }
|
mv = { optional = true, version = "0.0.20", package = "uu_mv", path = "src/uu/mv" }
|
||||||
nice = { optional=true, version="0.0.18", package="uu_nice", path="src/uu/nice" }
|
nice = { optional = true, version = "0.0.20", package = "uu_nice", path = "src/uu/nice" }
|
||||||
nl = { optional=true, version="0.0.18", package="uu_nl", path="src/uu/nl" }
|
nl = { optional = true, version = "0.0.20", package = "uu_nl", path = "src/uu/nl" }
|
||||||
nohup = { optional=true, version="0.0.18", package="uu_nohup", path="src/uu/nohup" }
|
nohup = { optional = true, version = "0.0.20", package = "uu_nohup", path = "src/uu/nohup" }
|
||||||
nproc = { optional=true, version="0.0.18", package="uu_nproc", path="src/uu/nproc" }
|
nproc = { optional = true, version = "0.0.20", package = "uu_nproc", path = "src/uu/nproc" }
|
||||||
numfmt = { optional=true, version="0.0.18", package="uu_numfmt", path="src/uu/numfmt" }
|
numfmt = { optional = true, version = "0.0.20", package = "uu_numfmt", path = "src/uu/numfmt" }
|
||||||
od = { optional=true, version="0.0.18", package="uu_od", path="src/uu/od" }
|
od = { optional = true, version = "0.0.20", package = "uu_od", path = "src/uu/od" }
|
||||||
paste = { optional=true, version="0.0.18", package="uu_paste", path="src/uu/paste" }
|
paste = { optional = true, version = "0.0.20", package = "uu_paste", path = "src/uu/paste" }
|
||||||
pathchk = { optional=true, version="0.0.18", package="uu_pathchk", path="src/uu/pathchk" }
|
pathchk = { optional = true, version = "0.0.20", package = "uu_pathchk", path = "src/uu/pathchk" }
|
||||||
pinky = { optional=true, version="0.0.18", package="uu_pinky", path="src/uu/pinky" }
|
pinky = { optional = true, version = "0.0.20", package = "uu_pinky", path = "src/uu/pinky" }
|
||||||
pr = { optional=true, version="0.0.18", package="uu_pr", path="src/uu/pr" }
|
pr = { optional = true, version = "0.0.20", package = "uu_pr", path = "src/uu/pr" }
|
||||||
printenv = { optional=true, version="0.0.18", package="uu_printenv", path="src/uu/printenv" }
|
printenv = { optional = true, version = "0.0.20", package = "uu_printenv", path = "src/uu/printenv" }
|
||||||
printf = { optional=true, version="0.0.18", package="uu_printf", path="src/uu/printf" }
|
printf = { optional = true, version = "0.0.20", package = "uu_printf", path = "src/uu/printf" }
|
||||||
ptx = { optional=true, version="0.0.18", package="uu_ptx", path="src/uu/ptx" }
|
ptx = { optional = true, version = "0.0.20", package = "uu_ptx", path = "src/uu/ptx" }
|
||||||
pwd = { optional=true, version="0.0.18", package="uu_pwd", path="src/uu/pwd" }
|
pwd = { optional = true, version = "0.0.20", package = "uu_pwd", path = "src/uu/pwd" }
|
||||||
readlink = { optional=true, version="0.0.18", package="uu_readlink", path="src/uu/readlink" }
|
readlink = { optional = true, version = "0.0.20", package = "uu_readlink", path = "src/uu/readlink" }
|
||||||
realpath = { optional=true, version="0.0.18", package="uu_realpath", path="src/uu/realpath" }
|
realpath = { optional = true, version = "0.0.20", package = "uu_realpath", path = "src/uu/realpath" }
|
||||||
relpath = { optional=true, version="0.0.18", package="uu_relpath", path="src/uu/relpath" }
|
relpath = { optional = true, version = "0.0.20", package = "uu_relpath", path = "src/uu/relpath" }
|
||||||
rm = { optional=true, version="0.0.18", package="uu_rm", path="src/uu/rm" }
|
rm = { optional = true, version = "0.0.20", package = "uu_rm", path = "src/uu/rm" }
|
||||||
rmdir = { optional=true, version="0.0.18", package="uu_rmdir", path="src/uu/rmdir" }
|
rmdir = { optional = true, version = "0.0.20", package = "uu_rmdir", path = "src/uu/rmdir" }
|
||||||
runcon = { optional=true, version="0.0.18", package="uu_runcon", path="src/uu/runcon" }
|
runcon = { optional = true, version = "0.0.20", package = "uu_runcon", path = "src/uu/runcon" }
|
||||||
seq = { optional=true, version="0.0.18", package="uu_seq", path="src/uu/seq" }
|
seq = { optional = true, version = "0.0.20", package = "uu_seq", path = "src/uu/seq" }
|
||||||
shred = { optional=true, version="0.0.18", package="uu_shred", path="src/uu/shred" }
|
shred = { optional = true, version = "0.0.20", package = "uu_shred", path = "src/uu/shred" }
|
||||||
shuf = { optional=true, version="0.0.18", package="uu_shuf", path="src/uu/shuf" }
|
shuf = { optional = true, version = "0.0.20", package = "uu_shuf", path = "src/uu/shuf" }
|
||||||
sleep = { optional=true, version="0.0.18", package="uu_sleep", path="src/uu/sleep" }
|
sleep = { optional = true, version = "0.0.20", package = "uu_sleep", path = "src/uu/sleep" }
|
||||||
sort = { optional=true, version="0.0.18", package="uu_sort", path="src/uu/sort" }
|
sort = { optional = true, version = "0.0.20", package = "uu_sort", path = "src/uu/sort" }
|
||||||
split = { optional=true, version="0.0.18", package="uu_split", path="src/uu/split" }
|
split = { optional = true, version = "0.0.20", package = "uu_split", path = "src/uu/split" }
|
||||||
stat = { optional=true, version="0.0.18", package="uu_stat", path="src/uu/stat" }
|
stat = { optional = true, version = "0.0.20", package = "uu_stat", path = "src/uu/stat" }
|
||||||
stdbuf = { optional=true, version="0.0.18", package="uu_stdbuf", path="src/uu/stdbuf" }
|
stdbuf = { optional = true, version = "0.0.20", package = "uu_stdbuf", path = "src/uu/stdbuf" }
|
||||||
stty = { optional=true, version="0.0.18", package="uu_stty", path="src/uu/stty" }
|
stty = { optional = true, version = "0.0.20", package = "uu_stty", path = "src/uu/stty" }
|
||||||
sum = { optional=true, version="0.0.18", package="uu_sum", path="src/uu/sum" }
|
sum = { optional = true, version = "0.0.20", package = "uu_sum", path = "src/uu/sum" }
|
||||||
sync = { optional=true, version="0.0.18", package="uu_sync", path="src/uu/sync" }
|
sync = { optional = true, version = "0.0.20", package = "uu_sync", path = "src/uu/sync" }
|
||||||
tac = { optional=true, version="0.0.18", package="uu_tac", path="src/uu/tac" }
|
tac = { optional = true, version = "0.0.20", package = "uu_tac", path = "src/uu/tac" }
|
||||||
tail = { optional=true, version="0.0.18", package="uu_tail", path="src/uu/tail" }
|
tail = { optional = true, version = "0.0.20", package = "uu_tail", path = "src/uu/tail" }
|
||||||
tee = { optional=true, version="0.0.18", package="uu_tee", path="src/uu/tee" }
|
tee = { optional = true, version = "0.0.20", package = "uu_tee", path = "src/uu/tee" }
|
||||||
timeout = { optional=true, version="0.0.18", package="uu_timeout", path="src/uu/timeout" }
|
timeout = { optional = true, version = "0.0.20", package = "uu_timeout", path = "src/uu/timeout" }
|
||||||
touch = { optional=true, version="0.0.18", package="uu_touch", path="src/uu/touch" }
|
touch = { optional = true, version = "0.0.20", package = "uu_touch", path = "src/uu/touch" }
|
||||||
tr = { optional=true, version="0.0.18", package="uu_tr", path="src/uu/tr" }
|
tr = { optional = true, version = "0.0.20", package = "uu_tr", path = "src/uu/tr" }
|
||||||
true = { optional=true, version="0.0.18", package="uu_true", path="src/uu/true" }
|
true = { optional = true, version = "0.0.20", package = "uu_true", path = "src/uu/true" }
|
||||||
truncate = { optional=true, version="0.0.18", package="uu_truncate", path="src/uu/truncate" }
|
truncate = { optional = true, version = "0.0.20", package = "uu_truncate", path = "src/uu/truncate" }
|
||||||
tsort = { optional=true, version="0.0.18", package="uu_tsort", path="src/uu/tsort" }
|
tsort = { optional = true, version = "0.0.20", package = "uu_tsort", path = "src/uu/tsort" }
|
||||||
tty = { optional=true, version="0.0.18", package="uu_tty", path="src/uu/tty" }
|
tty = { optional = true, version = "0.0.20", package = "uu_tty", path = "src/uu/tty" }
|
||||||
uname = { optional=true, version="0.0.18", package="uu_uname", path="src/uu/uname" }
|
uname = { optional = true, version = "0.0.20", package = "uu_uname", path = "src/uu/uname" }
|
||||||
unexpand = { optional=true, version="0.0.18", package="uu_unexpand", path="src/uu/unexpand" }
|
unexpand = { optional = true, version = "0.0.20", package = "uu_unexpand", path = "src/uu/unexpand" }
|
||||||
uniq = { optional=true, version="0.0.18", package="uu_uniq", path="src/uu/uniq" }
|
uniq = { optional = true, version = "0.0.20", package = "uu_uniq", path = "src/uu/uniq" }
|
||||||
unlink = { optional=true, version="0.0.18", package="uu_unlink", path="src/uu/unlink" }
|
unlink = { optional = true, version = "0.0.20", package = "uu_unlink", path = "src/uu/unlink" }
|
||||||
uptime = { optional=true, version="0.0.18", package="uu_uptime", path="src/uu/uptime" }
|
uptime = { optional = true, version = "0.0.20", package = "uu_uptime", path = "src/uu/uptime" }
|
||||||
users = { optional=true, version="0.0.18", package="uu_users", path="src/uu/users" }
|
users = { optional = true, version = "0.0.20", package = "uu_users", path = "src/uu/users" }
|
||||||
vdir = { optional=true, version="0.0.18", package="uu_vdir", path="src/uu/vdir" }
|
vdir = { optional = true, version = "0.0.20", package = "uu_vdir", path = "src/uu/vdir" }
|
||||||
wc = { optional=true, version="0.0.18", package="uu_wc", path="src/uu/wc" }
|
wc = { optional = true, version = "0.0.20", package = "uu_wc", path = "src/uu/wc" }
|
||||||
who = { optional=true, version="0.0.18", package="uu_who", path="src/uu/who" }
|
who = { optional = true, version = "0.0.20", package = "uu_who", path = "src/uu/who" }
|
||||||
whoami = { optional=true, version="0.0.18", package="uu_whoami", path="src/uu/whoami" }
|
whoami = { optional = true, version = "0.0.20", package = "uu_whoami", path = "src/uu/whoami" }
|
||||||
yes = { optional=true, version="0.0.18", package="uu_yes", path="src/uu/yes" }
|
yes = { optional = true, version = "0.0.20", package = "uu_yes", path = "src/uu/yes" }
|
||||||
|
|
||||||
# this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)"
|
# this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)"
|
||||||
# factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" }
|
# factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" }
|
||||||
|
@ -491,15 +492,14 @@ uucore = { workspace=true, features=["entries", "process", "signals"] }
|
||||||
walkdir = { workspace = true }
|
walkdir = { workspace = true }
|
||||||
is-terminal = { workspace = true }
|
is-terminal = { workspace = true }
|
||||||
hex-literal = "0.4.1"
|
hex-literal = "0.4.1"
|
||||||
rstest = "0.17.0"
|
rstest = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
|
||||||
procfs = { version = "0.15", default-features = false }
|
procfs = { version = "0.15", default-features = false }
|
||||||
rlimit = "0.9.1"
|
rlimit = "0.10.1"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dev-dependencies]
|
[target.'cfg(unix)'.dev-dependencies]
|
||||||
nix = { workspace = true, features = ["process", "signal", "user"] }
|
nix = { workspace = true, features = ["process", "signal", "user"] }
|
||||||
rust-users = { version="0.11", package="users" }
|
|
||||||
rand_pcg = "0.3"
|
rand_pcg = "0.3"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
32
GNUmakefile
32
GNUmakefile
|
@ -1,4 +1,4 @@
|
||||||
# spell-checker:ignore (misc) testsuite runtest findstring (targets) busytest toybox distclean pkgs nextest ; (vars/env) BINDIR BUILDDIR CARGOFLAGS DESTDIR DOCSDIR INSTALLDIR INSTALLEES MULTICALL DATAROOTDIR TESTDIR
|
# spell-checker:ignore (misc) testsuite runtest findstring (targets) busytest toybox distclean pkgs nextest ; (vars/env) BINDIR BUILDDIR CARGOFLAGS DESTDIR DOCSDIR INSTALLDIR INSTALLEES MULTICALL DATAROOTDIR TESTDIR manpages
|
||||||
|
|
||||||
# Config options
|
# Config options
|
||||||
PROFILE ?= debug
|
PROFILE ?= debug
|
||||||
|
@ -337,7 +337,21 @@ clean:
|
||||||
distclean: clean
|
distclean: clean
|
||||||
$(CARGO) clean $(CARGOFLAGS) && $(CARGO) update $(CARGOFLAGS)
|
$(CARGO) clean $(CARGOFLAGS) && $(CARGO) update $(CARGOFLAGS)
|
||||||
|
|
||||||
install: build
|
manpages: build-coreutils
|
||||||
|
mkdir -p $(BUILDDIR)/man/
|
||||||
|
$(foreach prog, $(INSTALLEES), \
|
||||||
|
$(BUILDDIR)/coreutils manpage $(prog) > $(BUILDDIR)/man/$(PROG_PREFIX)$(prog).1; \
|
||||||
|
)
|
||||||
|
|
||||||
|
completions: build-coreutils
|
||||||
|
mkdir -p $(BUILDDIR)/completions/zsh $(BUILDDIR)/completions/bash $(BUILDDIR)/completions/fish
|
||||||
|
$(foreach prog, $(INSTALLEES), \
|
||||||
|
$(BUILDDIR)/coreutils completion $(prog) zsh > $(BUILDDIR)/completions/zsh/_$(PROG_PREFIX)$(prog); \
|
||||||
|
$(BUILDDIR)/coreutils completion $(prog) bash > $(BUILDDIR)/completions/bash/$(PROG_PREFIX)$(prog); \
|
||||||
|
$(BUILDDIR)/coreutils completion $(prog) fish > $(BUILDDIR)/completions/fish/$(PROG_PREFIX)$(prog).fish; \
|
||||||
|
)
|
||||||
|
|
||||||
|
install: build manpages completions
|
||||||
mkdir -p $(INSTALLDIR_BIN)
|
mkdir -p $(INSTALLDIR_BIN)
|
||||||
ifeq (${MULTICALL}, y)
|
ifeq (${MULTICALL}, y)
|
||||||
$(INSTALL) $(BUILDDIR)/coreutils $(INSTALLDIR_BIN)/$(PROG_PREFIX)coreutils
|
$(INSTALL) $(BUILDDIR)/coreutils $(INSTALLDIR_BIN)/$(PROG_PREFIX)coreutils
|
||||||
|
@ -349,15 +363,18 @@ else
|
||||||
$(INSTALL) $(BUILDDIR)/$(prog) $(INSTALLDIR_BIN)/$(PROG_PREFIX)$(prog);)
|
$(INSTALL) $(BUILDDIR)/$(prog) $(INSTALLDIR_BIN)/$(PROG_PREFIX)$(prog);)
|
||||||
$(if $(findstring test,$(INSTALLEES)), $(INSTALL) $(BUILDDIR)/test $(INSTALLDIR_BIN)/$(PROG_PREFIX)[)
|
$(if $(findstring test,$(INSTALLEES)), $(INSTALL) $(BUILDDIR)/test $(INSTALLDIR_BIN)/$(PROG_PREFIX)[)
|
||||||
endif
|
endif
|
||||||
|
mkdir -p $(DESTDIR)$(DATAROOTDIR)/man/man1
|
||||||
|
$(foreach prog, $(INSTALLEES), \
|
||||||
|
$(INSTALL) $(BUILDDIR)/man/$(PROG_PREFIX)$(prog).1 $(DESTDIR)$(DATAROOTDIR)/man/man1/; \
|
||||||
|
)
|
||||||
|
|
||||||
mkdir -p $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions
|
mkdir -p $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions
|
||||||
mkdir -p $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions
|
mkdir -p $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions
|
||||||
mkdir -p $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d
|
mkdir -p $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d
|
||||||
mkdir -p $(DESTDIR)$(DATAROOTDIR)/man/man1
|
|
||||||
$(foreach prog, $(INSTALLEES), \
|
$(foreach prog, $(INSTALLEES), \
|
||||||
$(BUILDDIR)/coreutils completion $(prog) zsh > $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/_$(PROG_PREFIX)$(prog); \
|
$(INSTALL) $(BUILDDIR)/completions/zsh/_$(PROG_PREFIX)$(prog) $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/; \
|
||||||
$(BUILDDIR)/coreutils completion $(prog) bash > $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/$(PROG_PREFIX)$(prog); \
|
$(INSTALL) $(BUILDDIR)/completions/bash/$(PROG_PREFIX)$(prog) $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/; \
|
||||||
$(BUILDDIR)/coreutils completion $(prog) fish > $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d/$(PROG_PREFIX)$(prog).fish; \
|
$(INSTALL) $(BUILDDIR)/completions/fish/$(PROG_PREFIX)$(prog).fish $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d/; \
|
||||||
$(BUILDDIR)/coreutils manpage $(prog) > $(DESTDIR)$(DATAROOTDIR)/man/man1/$(PROG_PREFIX)$(prog).1; \
|
|
||||||
)
|
)
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
|
@ -369,5 +386,6 @@ endif
|
||||||
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/_$(PROG_PREFIX),$(PROGS))
|
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/zsh/site-functions/_$(PROG_PREFIX),$(PROGS))
|
||||||
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/$(PROG_PREFIX),$(PROGS))
|
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/bash-completion/completions/$(PROG_PREFIX),$(PROGS))
|
||||||
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d/$(PROG_PREFIX),$(addsuffix .fish,$(PROGS)))
|
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/fish/vendor_completions.d/$(PROG_PREFIX),$(addsuffix .fish,$(PROGS)))
|
||||||
|
rm -f $(addprefix $(DESTDIR)$(DATAROOTDIR)/man/man1/$(PROG_PREFIX),$(addsuffix .1,$(PROGS)))
|
||||||
|
|
||||||
.PHONY: all build build-coreutils build-pkgs test distclean clean busytest install uninstall
|
.PHONY: all build build-coreutils build-pkgs test distclean clean busytest install uninstall
|
||||||
|
|
146
Makefile.toml
146
Makefile.toml
|
@ -20,15 +20,12 @@ run_task = "_init"
|
||||||
|
|
||||||
[tasks._init]
|
[tasks._init]
|
||||||
private = true
|
private = true
|
||||||
dependencies = [
|
dependencies = ["_init-vars"]
|
||||||
"_init-vars",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks._init-vars]
|
[tasks._init-vars]
|
||||||
private = true
|
private = true
|
||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
script = [
|
script = ['''
|
||||||
'''
|
|
||||||
# reset build/test flags
|
# reset build/test flags
|
||||||
set_env CARGO_MAKE_CARGO_BUILD_TEST_FLAGS ""
|
set_env CARGO_MAKE_CARGO_BUILD_TEST_FLAGS ""
|
||||||
# determine features
|
# determine features
|
||||||
|
@ -90,54 +87,36 @@ for arg in "${args_utils_list}"
|
||||||
end
|
end
|
||||||
args_utils = trim "${args_utils}"
|
args_utils = trim "${args_utils}"
|
||||||
set_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS "${args_utils}"
|
set_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS "${args_utils}"
|
||||||
'''
|
''']
|
||||||
]
|
|
||||||
|
|
||||||
### tasks
|
### tasks
|
||||||
|
|
||||||
[tasks.default]
|
[tasks.default]
|
||||||
description = "## *DEFAULT* Build (debug-mode) and test project"
|
description = "## *DEFAULT* Build (debug-mode) and test project"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["action-build-debug", "test-terse"]
|
||||||
"action-build-debug",
|
|
||||||
"test-terse",
|
|
||||||
]
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
||||||
[tasks.build]
|
[tasks.build]
|
||||||
description = "## Build (release-mode) project"
|
description = "## Build (release-mode) project"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["core::pre-build", "action-build-release", "core::post-build"]
|
||||||
"core::pre-build",
|
|
||||||
"action-build-release",
|
|
||||||
"core::post-build",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.build-debug]
|
[tasks.build-debug]
|
||||||
description = "## Build (debug-mode) project"
|
description = "## Build (debug-mode) project"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["action-build-debug"]
|
||||||
"action-build-debug",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.build-examples]
|
[tasks.build-examples]
|
||||||
description = "## Build (release-mode) project example(s); usage: `cargo make (build-examples | examples) [EXAMPLE]...`"
|
description = "## Build (release-mode) project example(s); usage: `cargo make (build-examples | examples) [EXAMPLE]...`"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["core::pre-build", "action-build-examples", "core::post-build"]
|
||||||
"core::pre-build",
|
|
||||||
"action-build-examples",
|
|
||||||
"core::post-build",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.build-features]
|
[tasks.build-features]
|
||||||
description = "## Build (with features; release-mode) project; usage: `cargo make (build-features | features) FEATURE...`"
|
description = "## Build (with features; release-mode) project; usage: `cargo make (build-features | features) FEATURE...`"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["core::pre-build", "action-build-features", "core::post-build"]
|
||||||
"core::pre-build",
|
|
||||||
"action-build-features",
|
|
||||||
"core::post-build",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.build-release]
|
[tasks.build-release]
|
||||||
alias = "build"
|
alias = "build"
|
||||||
|
@ -148,9 +127,7 @@ alias = "build-debug"
|
||||||
[tasks.example]
|
[tasks.example]
|
||||||
description = "hidden singular-form alias for 'examples'"
|
description = "hidden singular-form alias for 'examples'"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["examples"]
|
||||||
"examples",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.examples]
|
[tasks.examples]
|
||||||
alias = "build-examples"
|
alias = "build-examples"
|
||||||
|
@ -161,17 +138,12 @@ alias = "build-features"
|
||||||
[tasks.format]
|
[tasks.format]
|
||||||
description = "## Format code files (with `cargo fmt`; includes tests)"
|
description = "## Format code files (with `cargo fmt`; includes tests)"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["action-format", "action-format-tests"]
|
||||||
"action-format",
|
|
||||||
"action-format-tests",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.help]
|
[tasks.help]
|
||||||
description = "## Display help"
|
description = "## Display help"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["action-display-help"]
|
||||||
"action-display-help",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.install]
|
[tasks.install]
|
||||||
description = "## Install project binary (to $HOME/.cargo/bin)"
|
description = "## Install project binary (to $HOME/.cargo/bin)"
|
||||||
|
@ -182,10 +154,7 @@ args = ["install", "--path", "."]
|
||||||
[tasks.lint]
|
[tasks.lint]
|
||||||
description = "## Display lint report"
|
description = "## Display lint report"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["action-clippy", "action-fmt_report"]
|
||||||
"action-clippy",
|
|
||||||
"action-fmt_report",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.release]
|
[tasks.release]
|
||||||
alias = "build"
|
alias = "build"
|
||||||
|
@ -193,48 +162,32 @@ alias = "build"
|
||||||
[tasks.test]
|
[tasks.test]
|
||||||
description = "## Run project tests"
|
description = "## Run project tests"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["core::pre-test", "core::test", "core::post-test"]
|
||||||
"core::pre-test",
|
|
||||||
"core::test",
|
|
||||||
"core::post-test",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.test-terse]
|
[tasks.test-terse]
|
||||||
description = "## Run project tests (with terse/summary output)"
|
description = "## Run project tests (with terse/summary output)"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["core::pre-test", "action-test_quiet", "core::post-test"]
|
||||||
"core::pre-test",
|
|
||||||
"action-test_quiet",
|
|
||||||
"core::post-test",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.test-util]
|
[tasks.test-util]
|
||||||
description = "## Test (individual) utilities; usage: `cargo make (test-util | test-uutil) [UTIL_NAME...]`"
|
description = "## Test (individual) utilities; usage: `cargo make (test-util | test-uutil) [UTIL_NAME...]`"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["action-test-utils"]
|
||||||
"action-test-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.test-utils]
|
[tasks.test-utils]
|
||||||
description = "hidden plural-form alias for 'test-util'"
|
description = "hidden plural-form alias for 'test-util'"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["test-util"]
|
||||||
"test-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.test-uutil]
|
[tasks.test-uutil]
|
||||||
description = "hidden alias for 'test-util'"
|
description = "hidden alias for 'test-util'"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["test-util"]
|
||||||
"test-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.test-uutils]
|
[tasks.test-uutils]
|
||||||
description = "hidden alias for 'test-util'"
|
description = "hidden alias for 'test-util'"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["test-util"]
|
||||||
"test-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.uninstall]
|
[tasks.uninstall]
|
||||||
description = "## Remove project binary (from $HOME/.cargo/bin)"
|
description = "## Remove project binary (from $HOME/.cargo/bin)"
|
||||||
|
@ -255,23 +208,17 @@ dependencies = [
|
||||||
[tasks.utils]
|
[tasks.utils]
|
||||||
description = "hidden plural-form alias for 'util'"
|
description = "hidden plural-form alias for 'util'"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["util"]
|
||||||
"util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.uutil]
|
[tasks.uutil]
|
||||||
description = "hidden alias for 'util'"
|
description = "hidden alias for 'util'"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["util"]
|
||||||
"util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.uutils]
|
[tasks.uutils]
|
||||||
description = "hidden plural-form alias for 'util'"
|
description = "hidden plural-form alias for 'util'"
|
||||||
category = "[project]"
|
category = "[project]"
|
||||||
dependencies = [
|
dependencies = ["util"]
|
||||||
"util",
|
|
||||||
]
|
|
||||||
|
|
||||||
### actions
|
### actions
|
||||||
|
|
||||||
|
@ -288,18 +235,27 @@ args = ["build", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )" ]
|
||||||
[tasks.action-build-examples]
|
[tasks.action-build-examples]
|
||||||
description = "`cargo build (--examples|(--example EXAMPLE)...)`"
|
description = "`cargo build (--examples|(--example EXAMPLE)...)`"
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["build", "--release", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )", "${CARGO_MAKE_TASK_BUILD_EXAMPLES_ARGS}" ]
|
args = [
|
||||||
|
"build",
|
||||||
|
"--release",
|
||||||
|
"@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )",
|
||||||
|
"${CARGO_MAKE_TASK_BUILD_EXAMPLES_ARGS}",
|
||||||
|
]
|
||||||
|
|
||||||
[tasks.action-build-features]
|
[tasks.action-build-features]
|
||||||
description = "`cargo build --release --features FEATURES`"
|
description = "`cargo build --release --features FEATURES`"
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["build", "--release", "--no-default-features", "--features", "${CARGO_MAKE_TASK_BUILD_FEATURES_ARGS}" ]
|
args = [
|
||||||
|
"build",
|
||||||
|
"--release",
|
||||||
|
"--no-default-features",
|
||||||
|
"--features",
|
||||||
|
"${CARGO_MAKE_TASK_BUILD_FEATURES_ARGS}",
|
||||||
|
]
|
||||||
|
|
||||||
[tasks.action-build-utils]
|
[tasks.action-build-utils]
|
||||||
description = "Build individual utilities"
|
description = "Build individual utilities"
|
||||||
dependencies = [
|
dependencies = ["action-determine-utils"]
|
||||||
"action-determine-utils",
|
|
||||||
]
|
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
# args = ["build", "@@remove-empty(CARGO_MAKE_TASK_BUILD_UTILS_ARGS)" ]
|
# args = ["build", "@@remove-empty(CARGO_MAKE_TASK_BUILD_UTILS_ARGS)" ]
|
||||||
args = ["build", "--release", "@@split(CARGO_MAKE_TASK_BUILD_UTILS_ARGS, )"]
|
args = ["build", "--release", "@@split(CARGO_MAKE_TASK_BUILD_UTILS_ARGS, )"]
|
||||||
|
@ -311,8 +267,7 @@ args = ["clippy", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )"]
|
||||||
|
|
||||||
[tasks.action-determine-utils]
|
[tasks.action-determine-utils]
|
||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
script = [
|
script = ['''
|
||||||
'''
|
|
||||||
package_options = get_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS
|
package_options = get_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS
|
||||||
if is_empty "${package_options}"
|
if is_empty "${package_options}"
|
||||||
show_utils = get_env CARGO_MAKE_VAR_SHOW_UTILS
|
show_utils = get_env CARGO_MAKE_VAR_SHOW_UTILS
|
||||||
|
@ -335,13 +290,11 @@ if is_empty "${package_options}"
|
||||||
package_options = trim "${package_options}"
|
package_options = trim "${package_options}"
|
||||||
end_if
|
end_if
|
||||||
set_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS "${package_options}"
|
set_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS "${package_options}"
|
||||||
'''
|
''']
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.action-determine-tests]
|
[tasks.action-determine-tests]
|
||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
script = [
|
script = ['''
|
||||||
'''
|
|
||||||
test_files = glob_array tests/**/*.rs
|
test_files = glob_array tests/**/*.rs
|
||||||
for file in ${test_files}
|
for file in ${test_files}
|
||||||
file = replace "${file}" "\\" "/"
|
file = replace "${file}" "\\" "/"
|
||||||
|
@ -354,8 +307,7 @@ for file in ${test_files}
|
||||||
end_if
|
end_if
|
||||||
end
|
end
|
||||||
set_env CARGO_MAKE_VAR_TESTS "${tests}"
|
set_env CARGO_MAKE_VAR_TESTS "${tests}"
|
||||||
'''
|
''']
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.action-format]
|
[tasks.action-format]
|
||||||
description = "`cargo fmt`"
|
description = "`cargo fmt`"
|
||||||
|
@ -364,9 +316,7 @@ args = ["fmt"]
|
||||||
|
|
||||||
[tasks.action-format-tests]
|
[tasks.action-format-tests]
|
||||||
description = "`cargo fmt` tests"
|
description = "`cargo fmt` tests"
|
||||||
dependencies = [
|
dependencies = ["action-determine-tests"]
|
||||||
"action-determine-tests",
|
|
||||||
]
|
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["fmt", "--", "@@split(CARGO_MAKE_VAR_TESTS, )"]
|
args = ["fmt", "--", "@@split(CARGO_MAKE_VAR_TESTS, )"]
|
||||||
|
|
||||||
|
@ -381,13 +331,15 @@ args = ["fmt", "--", "--check"]
|
||||||
[tasks.action-spellcheck-codespell]
|
[tasks.action-spellcheck-codespell]
|
||||||
description = "`codespell` spellcheck repository"
|
description = "`codespell` spellcheck repository"
|
||||||
command = "codespell" # (from `pip install codespell`)
|
command = "codespell" # (from `pip install codespell`)
|
||||||
args = [".", "--skip=*/.git,./target,./tests/fixtures", "--ignore-words-list=mut,od"]
|
args = [
|
||||||
|
".",
|
||||||
|
"--skip=*/.git,./target,./tests/fixtures",
|
||||||
|
"--ignore-words-list=mut,od",
|
||||||
|
]
|
||||||
|
|
||||||
[tasks.action-test-utils]
|
[tasks.action-test-utils]
|
||||||
description = "Build individual utilities"
|
description = "Build individual utilities"
|
||||||
dependencies = [
|
dependencies = ["action-determine-utils"]
|
||||||
"action-determine-utils",
|
|
||||||
]
|
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
# args = ["build", "@@remove-empty(CARGO_MAKE_TASK_BUILD_UTILS_ARGS)" ]
|
# args = ["build", "@@remove-empty(CARGO_MAKE_TASK_BUILD_UTILS_ARGS)" ]
|
||||||
args = ["test", "@@split(CARGO_MAKE_TASK_BUILD_UTILS_ARGS, )"]
|
args = ["test", "@@split(CARGO_MAKE_TASK_BUILD_UTILS_ARGS, )"]
|
||||||
|
@ -399,8 +351,7 @@ args = ["test", "--quiet", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )"]
|
||||||
|
|
||||||
[tasks.action-display-help]
|
[tasks.action-display-help]
|
||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
script = [
|
script = ['''
|
||||||
'''
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "usage: `cargo make TARGET [ARGS...]`"
|
echo "usage: `cargo make TARGET [ARGS...]`"
|
||||||
echo ""
|
echo ""
|
||||||
|
@ -432,5 +383,4 @@ script = [
|
||||||
end_if
|
end_if
|
||||||
end
|
end
|
||||||
echo ""
|
echo ""
|
||||||
'''
|
''']
|
||||||
]
|
|
||||||
|
|
13
README.md
13
README.md
|
@ -1,6 +1,7 @@
|
||||||
<!-- markdownlint-disable MD033 MD041 MD002 -->
|
<!-- markdownlint-disable MD033 MD041 MD002 -->
|
||||||
<!-- markdownlint-disable commands-show-output no-duplicate-heading -->
|
<!-- markdownlint-disable commands-show-output no-duplicate-heading -->
|
||||||
<!-- spell-checker:ignore markdownlint ; (options) DESTDIR UTILNAME manpages reimplementation -->
|
<!-- spell-checker:ignore markdownlint ; (options) DESTDIR UTILNAME manpages reimplementation oranda -->
|
||||||
|
<div class="oranda-hide">
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||

|

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

|

|
||||||
|
|
||||||
|
</div> <!-- close oranda-hide div -->
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
|
To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
|
||||||
|
|
4
build.rs
4
build.rs
|
@ -38,14 +38,14 @@ pub fn main() {
|
||||||
let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap();
|
let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap();
|
||||||
|
|
||||||
mf.write_all(
|
mf.write_all(
|
||||||
"type UtilityMap<T> = phf::Map<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\
|
"type UtilityMap<T> = phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\
|
||||||
\n\
|
\n\
|
||||||
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n"
|
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n"
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut phf_map = phf_codegen::Map::<&str>::new();
|
let mut phf_map = phf_codegen::OrderedMap::<&str>::new();
|
||||||
for krate in &crates {
|
for krate in &crates {
|
||||||
let map_value = format!("({krate}::uumain, {krate}::uu_app)");
|
let map_value = format!("({krate}::uumain, {krate}::uu_app)");
|
||||||
match krate.as_ref() {
|
match krate.as_ref() {
|
||||||
|
|
49
deny.toml
49
deny.toml
|
@ -27,7 +27,6 @@ allow = [
|
||||||
"BSD-2-Clause-FreeBSD",
|
"BSD-2-Clause-FreeBSD",
|
||||||
"BSD-3-Clause",
|
"BSD-3-Clause",
|
||||||
"CC0-1.0",
|
"CC0-1.0",
|
||||||
"MPL-2.0", # XXX considered copyleft?
|
|
||||||
"Unicode-DFS-2016",
|
"Unicode-DFS-2016",
|
||||||
]
|
]
|
||||||
copyleft = "deny"
|
copyleft = "deny"
|
||||||
|
@ -59,29 +58,37 @@ highlight = "all"
|
||||||
# introduces it.
|
# introduces it.
|
||||||
# spell-checker: disable
|
# spell-checker: disable
|
||||||
skip = [
|
skip = [
|
||||||
# is-terminal
|
# procfs
|
||||||
{ name = "hermit-abi", version = "0.3.1" },
|
{ name = "rustix", version = "0.36.15" },
|
||||||
# is-terminal
|
# rustix
|
||||||
{ name = "rustix", version = "0.36.14" },
|
|
||||||
# is-terminal (via rustix)
|
|
||||||
{ name = "io-lifetimes", version = "1.0.5" },
|
|
||||||
# is-terminal
|
|
||||||
{ name = "linux-raw-sys", version = "0.1.4" },
|
{ name = "linux-raw-sys", version = "0.1.4" },
|
||||||
# is-terminal
|
{ name = "linux-raw-sys", version = "0.3.8" },
|
||||||
{ name = "windows-sys", version = "0.45.0" },
|
|
||||||
{ name = "windows-targets", version = "0.42.2" },
|
|
||||||
{ name = "windows_aarch64_gnullvm", version = "0.42.2" },
|
|
||||||
{ name = "windows_aarch64_msvc", version = "0.42.2" },
|
|
||||||
{ name = "windows_i686_gnu", version = "0.42.2" },
|
|
||||||
{ name = "windows_i686_msvc", version = "0.42.2" },
|
|
||||||
{ name = "windows_x86_64_gnu", version = "0.42.2" },
|
|
||||||
{ name = "windows_x86_64_gnullvm", version = "0.42.2" },
|
|
||||||
{ name = "windows_x86_64_msvc", version = "0.42.2" },
|
|
||||||
|
|
||||||
# tempfile
|
# tempfile
|
||||||
{ name = "redox_syscall", version = "0.3.5" },
|
{ name = "rustix", version = "0.37.23" },
|
||||||
|
# various crates
|
||||||
|
{ name = "windows-sys", version = "0.45.0" },
|
||||||
|
# windows-sys
|
||||||
|
{ name = "windows-targets", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
|
{ name = "windows_aarch64_gnullvm", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
|
{ name = "windows_aarch64_msvc", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
|
{ name = "windows_i686_gnu", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
|
{ name = "windows_i686_msvc", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
|
{ name = "windows_x86_64_gnu", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
|
{ name = "windows_x86_64_gnullvm", version = "0.42.2" },
|
||||||
|
# windows-targets
|
||||||
|
{ name = "windows_x86_64_msvc", version = "0.42.2" },
|
||||||
# cpp_macros
|
# cpp_macros
|
||||||
{ name = "aho-corasick", version = "0.7.19" },
|
{ name = "aho-corasick", version = "0.7.20" },
|
||||||
|
# various crates
|
||||||
|
{ name = "syn", version = "1.0.109" },
|
||||||
|
# various crates
|
||||||
|
{ name = "bitflags", version = "1.3.2" },
|
||||||
]
|
]
|
||||||
# spell-checker: enable
|
# spell-checker: enable
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ src = "src"
|
||||||
title = "uutils Documentation"
|
title = "uutils Documentation"
|
||||||
|
|
||||||
[output.html]
|
[output.html]
|
||||||
git-repository-url = "https://github.com/rust-lang/cargo/tree/master/src/doc/src"
|
git-repository-url = "https://github.com/uutils/coreutils/tree/main/docs/src"
|
||||||
|
|
||||||
[preprocessor.toc]
|
[preprocessor.toc]
|
||||||
command = "mdbook-toc"
|
command = "mdbook-toc"
|
||||||
|
|
|
@ -61,7 +61,20 @@ feature is adopted from [FreeBSD](https://www.freebsd.org/cgi/man.cgi?cut).
|
||||||
|
|
||||||
## `fmt`
|
## `fmt`
|
||||||
|
|
||||||
`fmt` has additional flags for prefixes: `-P/--skip-prefix`, `-x/--exact-prefix`, and
|
`fmt` has additional flags for prefixes: `-P`/`--skip-prefix`, `-x`/`--exact-prefix`, and
|
||||||
`-X/--exact-skip-prefix`. With `-m/--preserve-headers`, an attempt is made to detect and preserve
|
`-X`/`--exact-skip-prefix`. With `-m`/`--preserve-headers`, an attempt is made to detect and preserve
|
||||||
mail headers in the input. `-q/--quick` breaks lines more quickly. And `-T/--tab-width` defines the
|
mail headers in the input. `-q`/`--quick` breaks lines more quickly. And `-T`/`--tab-width` defines the
|
||||||
number of spaces representing a tab when determining the line length.
|
number of spaces representing a tab when determining the line length.
|
||||||
|
|
||||||
|
## `seq`
|
||||||
|
|
||||||
|
`seq` provides `-t`/`--terminator` to set the terminator character.
|
||||||
|
|
||||||
|
## `ls`
|
||||||
|
|
||||||
|
GNU `ls` provides two ways to use a long listing format: `-l` and `--format=long`. We support a
|
||||||
|
third way: `--long`.
|
||||||
|
|
||||||
|
## `du`
|
||||||
|
|
||||||
|
`du` allows `birth` and `creation` as values for the `--time` argument to show the creation time.
|
||||||
|
|
|
@ -37,7 +37,7 @@ apk update uutils-coreutils
|
||||||
|
|
||||||
### Arch
|
### Arch
|
||||||
|
|
||||||
[](https://archlinux.org/packages/community/x86_64/uutils-coreutils/)
|
[](https://archlinux.org/packages/extra/x86_64/uutils-coreutils/)
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pacman -S uutils-coreutils
|
pacman -S uutils-coreutils
|
||||||
|
|
4
docs/src/oranda.css
Normal file
4
docs/src/oranda.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.logo {
|
||||||
|
display: block;
|
||||||
|
height: 170px;
|
||||||
|
}
|
13
oranda.json
Normal file
13
oranda.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"name": "uutils coreutils"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"changelog": true
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"theme": "light",
|
||||||
|
"logo": "docs/src/logo.svg",
|
||||||
|
"additional_css": ["docs/src/oranda.css"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -212,8 +212,15 @@ fn gen_manpage<T: uucore::Args>(
|
||||||
|
|
||||||
fn gen_coreutils_app<T: uucore::Args>(util_map: &UtilityMap<T>) -> Command {
|
fn gen_coreutils_app<T: uucore::Args>(util_map: &UtilityMap<T>) -> Command {
|
||||||
let mut command = Command::new("coreutils");
|
let mut command = Command::new("coreutils");
|
||||||
for (_, (_, sub_app)) in util_map {
|
for (name, (_, sub_app)) in util_map {
|
||||||
command = command.subcommand(sub_app());
|
// Recreate a small subcommand with only the relevant info
|
||||||
|
// (name & short description)
|
||||||
|
let about = sub_app()
|
||||||
|
.get_about()
|
||||||
|
.expect("Could not get the 'about'")
|
||||||
|
.to_string();
|
||||||
|
let sub_app = Command::new(name).about(about);
|
||||||
|
command = command.subcommand(sub_app);
|
||||||
}
|
}
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
// spell-checker:ignore tldr
|
// spell-checker:ignore tldr uuhelp
|
||||||
|
|
||||||
use clap::Command;
|
use clap::Command;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -178,7 +178,7 @@ impl<'a, 'b> MDWriter<'a, 'b> {
|
||||||
|
|
||||||
fn usage(&mut self) -> io::Result<()> {
|
fn usage(&mut self) -> io::Result<()> {
|
||||||
if let Some(markdown) = &self.markdown {
|
if let Some(markdown) = &self.markdown {
|
||||||
let usage = help_parser::parse_usage(markdown);
|
let usage = uuhelp_parser::parse_usage(markdown);
|
||||||
let usage = usage.replace("{}", self.name);
|
let usage = usage.replace("{}", self.name);
|
||||||
|
|
||||||
writeln!(self.w, "\n```")?;
|
writeln!(self.w, "\n```")?;
|
||||||
|
@ -191,7 +191,7 @@ impl<'a, 'b> MDWriter<'a, 'b> {
|
||||||
|
|
||||||
fn about(&mut self) -> io::Result<()> {
|
fn about(&mut self) -> io::Result<()> {
|
||||||
if let Some(markdown) = &self.markdown {
|
if let Some(markdown) = &self.markdown {
|
||||||
writeln!(self.w, "{}", help_parser::parse_about(markdown))
|
writeln!(self.w, "{}", uuhelp_parser::parse_about(markdown))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ impl<'a, 'b> MDWriter<'a, 'b> {
|
||||||
|
|
||||||
fn after_help(&mut self) -> io::Result<()> {
|
fn after_help(&mut self) -> io::Result<()> {
|
||||||
if let Some(markdown) = &self.markdown {
|
if let Some(markdown) = &self.markdown {
|
||||||
if let Some(after_help) = help_parser::parse_section("after help", markdown) {
|
if let Some(after_help) = uuhelp_parser::parse_section("after help", markdown) {
|
||||||
return writeln!(self.w, "\n\n{after_help}");
|
return writeln!(self.w, "\n\n{after_help}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "help_parser"
|
|
||||||
version = "0.0.18"
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT"
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_arch"
|
name = "uu_arch"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "arch ~ (uutils) display machine architecture"
|
description = "arch ~ (uutils) display machine architecture"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_base32"
|
name = "uu_base32"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "base32 ~ (uutils) decode/encode input (base32-encoding)"
|
description = "base32 ~ (uutils) decode/encode input (base32-encoding)"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_base64"
|
name = "uu_base64"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "base64 ~ (uutils) decode/encode input (base64-encoding)"
|
description = "base64 ~ (uutils) decode/encode input (base64-encoding)"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_basename"
|
name = "uu_basename"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "basename ~ (uutils) display PATHNAME with leading directory components removed"
|
description = "basename ~ (uutils) display PATHNAME with leading directory components removed"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_basenc"
|
name = "uu_basenc"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "basenc ~ (uutils) decode/encode input"
|
description = "basenc ~ (uutils) decode/encode input"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_cat"
|
name = "uu_cat"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "cat ~ (uutils) concatenate and display input"
|
description = "cat ~ (uutils) concatenate and display input"
|
||||||
|
|
|
@ -290,7 +290,6 @@ pub fn uu_app() -> Command {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::SHOW_NONPRINTING_TABS)
|
Arg::new(options::SHOW_NONPRINTING_TABS)
|
||||||
.short('t')
|
.short('t')
|
||||||
.long(options::SHOW_NONPRINTING_TABS)
|
|
||||||
.help("equivalent to -vT")
|
.help("equivalent to -vT")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_chcon"
|
name = "uu_chcon"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "chcon ~ (uutils) change file security context"
|
description = "chcon ~ (uutils) change file security context"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_chgrp"
|
name = "uu_chgrp"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "chgrp ~ (uutils) change the group ownership of FILE"
|
description = "chgrp ~ (uutils) change the group ownership of FILE"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_chmod"
|
name = "uu_chmod"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "chmod ~ (uutils) change mode of FILE"
|
description = "chmod ~ (uutils) change mode of FILE"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_chown"
|
name = "uu_chown"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "chown ~ (uutils) change the ownership of FILE"
|
description = "chown ~ (uutils) change the ownership of FILE"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_chroot"
|
name = "uu_chroot"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "chroot ~ (uutils) run COMMAND under a new root directory"
|
description = "chroot ~ (uutils) run COMMAND under a new root directory"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_cksum"
|
name = "uu_cksum"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "cksum ~ (uutils) display CRC and size of input"
|
description = "cksum ~ (uutils) display CRC and size of input"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_comm"
|
name = "uu_comm"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "comm ~ (uutils) compare sorted inputs"
|
description = "comm ~ (uutils) compare sorted inputs"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_cp"
|
name = "uu_cp"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = [
|
authors = [
|
||||||
"Jordy Dickinson <jordy.dickinson@gmail.com>",
|
"Jordy Dickinson <jordy.dickinson@gmail.com>",
|
||||||
"Joshua S. Miller <jsmiller@uchicago.edu>",
|
"Joshua S. Miller <jsmiller@uchicago.edu>",
|
||||||
|
|
|
@ -43,7 +43,8 @@ use uucore::fs::{
|
||||||
};
|
};
|
||||||
use uucore::update_control::{self, UpdateMode};
|
use uucore::update_control::{self, UpdateMode};
|
||||||
use uucore::{
|
use uucore::{
|
||||||
crash, format_usage, help_about, help_section, help_usage, prompt_yes, show_error, show_warning,
|
crash, format_usage, help_about, help_section, help_usage, prompt_yes, show_error,
|
||||||
|
show_warning, util_name,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::copydir::copy_directory;
|
use crate::copydir::copy_directory;
|
||||||
|
@ -229,10 +230,79 @@ pub struct Options {
|
||||||
backup_suffix: String,
|
backup_suffix: String,
|
||||||
target_dir: Option<PathBuf>,
|
target_dir: Option<PathBuf>,
|
||||||
update: UpdateMode,
|
update: UpdateMode,
|
||||||
|
debug: bool,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
progress_bar: bool,
|
progress_bar: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enum representing various debug states of the offload and reflink actions.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)] // All of them are used on Linux
|
||||||
|
enum OffloadReflinkDebug {
|
||||||
|
Unknown,
|
||||||
|
No,
|
||||||
|
Yes,
|
||||||
|
Avoided,
|
||||||
|
Unsupported,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representing various debug states of the sparse detection.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)] // silent for now until we use them
|
||||||
|
enum SparseDebug {
|
||||||
|
Unknown,
|
||||||
|
No,
|
||||||
|
Zeros,
|
||||||
|
SeekHole,
|
||||||
|
SeekHoleZeros,
|
||||||
|
Unsupported,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Struct that contains the debug state for each action in a file copy operation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CopyDebug {
|
||||||
|
offload: OffloadReflinkDebug,
|
||||||
|
reflink: OffloadReflinkDebug,
|
||||||
|
sparse_detection: SparseDebug,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OffloadReflinkDebug {
|
||||||
|
fn to_string(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::No => "no",
|
||||||
|
Self::Yes => "yes",
|
||||||
|
Self::Avoided => "avoided",
|
||||||
|
Self::Unsupported => "unsupported",
|
||||||
|
Self::Unknown => "unknown",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SparseDebug {
|
||||||
|
fn to_string(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::No => "no",
|
||||||
|
Self::Zeros => "zeros",
|
||||||
|
Self::SeekHole => "SEEK_HOLE",
|
||||||
|
Self::SeekHoleZeros => "SEEK_HOLE + zeros",
|
||||||
|
Self::Unsupported => "unsupported",
|
||||||
|
Self::Unknown => "unknown",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function prints the debug information of a file copy operation if
|
||||||
|
/// no hard link or symbolic link is required, and data copy is required.
|
||||||
|
/// It prints the debug information of the offload, reflink, and sparse detection actions.
|
||||||
|
fn show_debug(copy_debug: &CopyDebug) {
|
||||||
|
println!(
|
||||||
|
"copy offload: {}, reflink: {}, sparse detection: {}",
|
||||||
|
copy_debug.offload.to_string(),
|
||||||
|
copy_debug.reflink.to_string(),
|
||||||
|
copy_debug.sparse_detection.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const ABOUT: &str = help_about!("cp.md");
|
const ABOUT: &str = help_about!("cp.md");
|
||||||
const USAGE: &str = help_usage!("cp.md");
|
const USAGE: &str = help_usage!("cp.md");
|
||||||
const AFTER_HELP: &str = help_section!("after help", "cp.md");
|
const AFTER_HELP: &str = help_section!("after help", "cp.md");
|
||||||
|
@ -269,6 +339,7 @@ mod options {
|
||||||
pub const STRIP_TRAILING_SLASHES: &str = "strip-trailing-slashes";
|
pub const STRIP_TRAILING_SLASHES: &str = "strip-trailing-slashes";
|
||||||
pub const SYMBOLIC_LINK: &str = "symbolic-link";
|
pub const SYMBOLIC_LINK: &str = "symbolic-link";
|
||||||
pub const TARGET_DIRECTORY: &str = "target-directory";
|
pub const TARGET_DIRECTORY: &str = "target-directory";
|
||||||
|
pub const DEBUG: &str = "debug";
|
||||||
pub const VERBOSE: &str = "verbose";
|
pub const VERBOSE: &str = "verbose";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,6 +383,7 @@ pub fn uu_app() -> Command {
|
||||||
backup_control::BACKUP_CONTROL_LONG_HELP
|
backup_control::BACKUP_CONTROL_LONG_HELP
|
||||||
))
|
))
|
||||||
.infer_long_args(true)
|
.infer_long_args(true)
|
||||||
|
.args_override_self(true)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::TARGET_DIRECTORY)
|
Arg::new(options::TARGET_DIRECTORY)
|
||||||
.short('t')
|
.short('t')
|
||||||
|
@ -369,6 +441,12 @@ pub fn uu_app() -> Command {
|
||||||
.help("remove any trailing slashes from each SOURCE argument")
|
.help("remove any trailing slashes from each SOURCE argument")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::DEBUG)
|
||||||
|
.long(options::DEBUG)
|
||||||
|
.help("explain how a file is copied. Implies -v")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::VERBOSE)
|
Arg::new(options::VERBOSE)
|
||||||
.short('v')
|
.short('v')
|
||||||
|
@ -438,6 +516,7 @@ pub fn uu_app() -> Command {
|
||||||
PRESERVABLE_ATTRIBUTES,
|
PRESERVABLE_ATTRIBUTES,
|
||||||
))
|
))
|
||||||
.num_args(0..)
|
.num_args(0..)
|
||||||
|
.require_equals(true)
|
||||||
.value_name("ATTR_LIST")
|
.value_name("ATTR_LIST")
|
||||||
.overrides_with_all([
|
.overrides_with_all([
|
||||||
options::ARCHIVE,
|
options::ARCHIVE,
|
||||||
|
@ -839,7 +918,8 @@ impl Options {
|
||||||
one_file_system: matches.get_flag(options::ONE_FILE_SYSTEM),
|
one_file_system: matches.get_flag(options::ONE_FILE_SYSTEM),
|
||||||
parents: matches.get_flag(options::PARENTS),
|
parents: matches.get_flag(options::PARENTS),
|
||||||
update: update_mode,
|
update: update_mode,
|
||||||
verbose: matches.get_flag(options::VERBOSE),
|
debug: matches.get_flag(options::DEBUG),
|
||||||
|
verbose: matches.get_flag(options::VERBOSE) || matches.get_flag(options::DEBUG),
|
||||||
strip_trailing_slashes: matches.get_flag(options::STRIP_TRAILING_SLASHES),
|
strip_trailing_slashes: matches.get_flag(options::STRIP_TRAILING_SLASHES),
|
||||||
reflink_mode: {
|
reflink_mode: {
|
||||||
if let Some(reflink) = matches.get_one::<String>(options::REFLINK) {
|
if let Some(reflink) = matches.get_one::<String>(options::REFLINK) {
|
||||||
|
@ -1025,23 +1105,21 @@ fn preserve_hardlinks(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When handling errors, we don't always want to show them to the user. This function handles that.
|
/// When handling errors, we don't always want to show them to the user. This function handles that.
|
||||||
/// If the error is printed, returns true, false otherwise.
|
fn show_error_if_needed(error: &Error) {
|
||||||
fn show_error_if_needed(error: &Error) -> bool {
|
|
||||||
match error {
|
match error {
|
||||||
// When using --no-clobber, we don't want to show
|
// When using --no-clobber, we don't want to show
|
||||||
// an error message
|
// an error message
|
||||||
Error::NotAllFilesCopied => (),
|
Error::NotAllFilesCopied => {
|
||||||
|
// Need to return an error code
|
||||||
|
}
|
||||||
Error::Skipped => {
|
Error::Skipped => {
|
||||||
// touch a b && echo "n"|cp -i a b && echo $?
|
// touch a b && echo "n"|cp -i a b && echo $?
|
||||||
// should return an error from GNU 9.2
|
// should return an error from GNU 9.2
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
show_error!("{}", error);
|
show_error!("{}", error);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy all `sources` to `target`. Returns an
|
/// Copy all `sources` to `target`. Returns an
|
||||||
|
@ -1098,11 +1176,10 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu
|
||||||
options,
|
options,
|
||||||
&mut symlinked_files,
|
&mut symlinked_files,
|
||||||
) {
|
) {
|
||||||
if show_error_if_needed(&error) {
|
show_error_if_needed(&error);
|
||||||
non_fatal_errors = true;
|
non_fatal_errors = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
seen_sources.insert(source);
|
seen_sources.insert(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1177,13 +1254,23 @@ fn copy_source(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OverwriteMode {
|
impl OverwriteMode {
|
||||||
fn verify(&self, path: &Path) -> CopyResult<()> {
|
fn verify(&self, path: &Path, verbose: bool) -> CopyResult<()> {
|
||||||
match *self {
|
match *self {
|
||||||
Self::NoClobber => Err(Error::NotAllFilesCopied),
|
Self::NoClobber => {
|
||||||
|
if verbose {
|
||||||
|
println!("skipped {}", path.quote());
|
||||||
|
} else {
|
||||||
|
eprintln!("{}: not replacing {}", util_name(), path.quote());
|
||||||
|
}
|
||||||
|
Err(Error::NotAllFilesCopied)
|
||||||
|
}
|
||||||
Self::Interactive(_) => {
|
Self::Interactive(_) => {
|
||||||
if prompt_yes!("overwrite {}?", path.quote()) {
|
if prompt_yes!("overwrite {}?", path.quote()) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
if verbose {
|
||||||
|
println!("skipped {}", path.quote());
|
||||||
|
}
|
||||||
Err(Error::Skipped)
|
Err(Error::Skipped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1391,7 +1478,7 @@ fn handle_existing_dest(
|
||||||
return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into());
|
return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
options.overwrite.verify(dest)?;
|
options.overwrite.verify(dest, options.verbose)?;
|
||||||
|
|
||||||
let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix);
|
let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix);
|
||||||
if let Some(backup_path) = backup_path {
|
if let Some(backup_path) = backup_path {
|
||||||
|
@ -1749,11 +1836,11 @@ fn copy_helper(
|
||||||
File::create(dest).context(dest.display().to_string())?;
|
File::create(dest).context(dest.display().to_string())?;
|
||||||
} else if source_is_fifo && options.recursive && !options.copy_contents {
|
} else if source_is_fifo && options.recursive && !options.copy_contents {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
copy_fifo(dest, options.overwrite)?;
|
copy_fifo(dest, options.overwrite, options.verbose)?;
|
||||||
} else if source_is_symlink {
|
} else if source_is_symlink {
|
||||||
copy_link(source, dest, symlinked_files)?;
|
copy_link(source, dest, symlinked_files)?;
|
||||||
} else {
|
} else {
|
||||||
copy_on_write(
|
let copy_debug = copy_on_write(
|
||||||
source,
|
source,
|
||||||
dest,
|
dest,
|
||||||
options.reflink_mode,
|
options.reflink_mode,
|
||||||
|
@ -1762,6 +1849,10 @@ fn copy_helper(
|
||||||
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
||||||
source_is_fifo,
|
source_is_fifo,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
if !options.attributes_only && options.debug {
|
||||||
|
show_debug(©_debug);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1770,9 +1861,9 @@ fn copy_helper(
|
||||||
// "Copies" a FIFO by creating a new one. This workaround is because Rust's
|
// "Copies" a FIFO by creating a new one. This workaround is because Rust's
|
||||||
// built-in fs::copy does not handle FIFOs (see rust-lang/rust/issues/79390).
|
// built-in fs::copy does not handle FIFOs (see rust-lang/rust/issues/79390).
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn copy_fifo(dest: &Path, overwrite: OverwriteMode) -> CopyResult<()> {
|
fn copy_fifo(dest: &Path, overwrite: OverwriteMode, verbose: bool) -> CopyResult<()> {
|
||||||
if dest.exists() {
|
if dest.exists() {
|
||||||
overwrite.verify(dest)?;
|
overwrite.verify(dest, verbose)?;
|
||||||
fs::remove_file(dest)?;
|
fs::remove_file(dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ use quick_error::ResultExt;
|
||||||
|
|
||||||
use uucore::mode::get_umask;
|
use uucore::mode::get_umask;
|
||||||
|
|
||||||
use crate::{CopyResult, ReflinkMode, SparseMode};
|
use crate::{CopyDebug, CopyResult, OffloadReflinkDebug, ReflinkMode, SparseDebug, SparseMode};
|
||||||
|
|
||||||
// From /usr/include/linux/fs.h:
|
// From /usr/include/linux/fs.h:
|
||||||
// #define FICLONE _IOW(0x94, 9, int)
|
// #define FICLONE _IOW(0x94, 9, int)
|
||||||
|
@ -145,24 +145,51 @@ pub(crate) fn copy_on_write(
|
||||||
sparse_mode: SparseMode,
|
sparse_mode: SparseMode,
|
||||||
context: &str,
|
context: &str,
|
||||||
source_is_fifo: bool,
|
source_is_fifo: bool,
|
||||||
) -> CopyResult<()> {
|
) -> CopyResult<CopyDebug> {
|
||||||
|
let mut copy_debug = CopyDebug {
|
||||||
|
offload: OffloadReflinkDebug::Unknown,
|
||||||
|
reflink: OffloadReflinkDebug::Unsupported,
|
||||||
|
sparse_detection: SparseDebug::No,
|
||||||
|
};
|
||||||
|
|
||||||
let result = match (reflink_mode, sparse_mode) {
|
let result = match (reflink_mode, sparse_mode) {
|
||||||
(ReflinkMode::Never, SparseMode::Always) => sparse_copy(source, dest),
|
(ReflinkMode::Never, SparseMode::Always) => {
|
||||||
(ReflinkMode::Never, _) => std::fs::copy(source, dest).map(|_| ()),
|
copy_debug.sparse_detection = SparseDebug::Zeros;
|
||||||
(ReflinkMode::Auto, SparseMode::Always) => sparse_copy(source, dest),
|
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||||
|
copy_debug.reflink = OffloadReflinkDebug::No;
|
||||||
|
sparse_copy(source, dest)
|
||||||
|
}
|
||||||
|
(ReflinkMode::Never, _) => {
|
||||||
|
copy_debug.sparse_detection = SparseDebug::No;
|
||||||
|
copy_debug.reflink = OffloadReflinkDebug::No;
|
||||||
|
std::fs::copy(source, dest).map(|_| ())
|
||||||
|
}
|
||||||
|
(ReflinkMode::Auto, SparseMode::Always) => {
|
||||||
|
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||||
|
copy_debug.sparse_detection = SparseDebug::Zeros;
|
||||||
|
copy_debug.reflink = OffloadReflinkDebug::Unsupported;
|
||||||
|
sparse_copy(source, dest)
|
||||||
|
}
|
||||||
|
|
||||||
(ReflinkMode::Auto, _) => {
|
(ReflinkMode::Auto, _) => {
|
||||||
|
copy_debug.sparse_detection = SparseDebug::No;
|
||||||
|
copy_debug.reflink = OffloadReflinkDebug::Unsupported;
|
||||||
if source_is_fifo {
|
if source_is_fifo {
|
||||||
copy_fifo_contents(source, dest).map(|_| ())
|
copy_fifo_contents(source, dest).map(|_| ())
|
||||||
} else {
|
} else {
|
||||||
clone(source, dest, CloneFallback::FSCopy)
|
clone(source, dest, CloneFallback::FSCopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(ReflinkMode::Always, SparseMode::Auto) => clone(source, dest, CloneFallback::Error),
|
(ReflinkMode::Always, SparseMode::Auto) => {
|
||||||
|
copy_debug.sparse_detection = SparseDebug::No;
|
||||||
|
copy_debug.reflink = OffloadReflinkDebug::Yes;
|
||||||
|
|
||||||
|
clone(source, dest, CloneFallback::Error)
|
||||||
|
}
|
||||||
(ReflinkMode::Always, _) => {
|
(ReflinkMode::Always, _) => {
|
||||||
return Err("`--reflink=always` can be used only with --sparse=auto".into())
|
return Err("`--reflink=always` can be used only with --sparse=auto".into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
result.context(context)?;
|
result.context(context)?;
|
||||||
Ok(())
|
Ok(copy_debug)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use quick_error::ResultExt;
|
use quick_error::ResultExt;
|
||||||
|
|
||||||
use crate::{CopyResult, ReflinkMode, SparseMode};
|
use crate::{CopyDebug, CopyResult, OffloadReflinkDebug, ReflinkMode, SparseDebug, SparseMode};
|
||||||
|
|
||||||
/// Copies `source` to `dest` using copy-on-write if possible.
|
/// Copies `source` to `dest` using copy-on-write if possible.
|
||||||
///
|
///
|
||||||
|
@ -24,10 +24,15 @@ pub(crate) fn copy_on_write(
|
||||||
sparse_mode: SparseMode,
|
sparse_mode: SparseMode,
|
||||||
context: &str,
|
context: &str,
|
||||||
source_is_fifo: bool,
|
source_is_fifo: bool,
|
||||||
) -> CopyResult<()> {
|
) -> CopyResult<CopyDebug> {
|
||||||
if sparse_mode != SparseMode::Auto {
|
if sparse_mode != SparseMode::Auto {
|
||||||
return Err("--sparse is only supported on linux".to_string().into());
|
return Err("--sparse is only supported on linux".to_string().into());
|
||||||
}
|
}
|
||||||
|
let mut copy_debug = CopyDebug {
|
||||||
|
offload: OffloadReflinkDebug::Unknown,
|
||||||
|
reflink: OffloadReflinkDebug::Unsupported,
|
||||||
|
sparse_detection: SparseDebug::Unsupported,
|
||||||
|
};
|
||||||
|
|
||||||
// Extract paths in a form suitable to be passed to a syscall.
|
// Extract paths in a form suitable to be passed to a syscall.
|
||||||
// The unwrap() is safe because they come from the command-line and so contain non nul
|
// The unwrap() is safe because they come from the command-line and so contain non nul
|
||||||
|
@ -72,6 +77,7 @@ pub(crate) fn copy_on_write(
|
||||||
return Err(format!("failed to clone {source:?} from {dest:?}: {error}").into())
|
return Err(format!("failed to clone {source:?} from {dest:?}: {error}").into())
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
copy_debug.reflink = OffloadReflinkDebug::Yes;
|
||||||
if source_is_fifo {
|
if source_is_fifo {
|
||||||
let mut src_file = File::open(source)?;
|
let mut src_file = File::open(source)?;
|
||||||
let mut dst_file = File::create(dest)?;
|
let mut dst_file = File::create(dest)?;
|
||||||
|
@ -83,5 +89,5 @@ pub(crate) fn copy_on_write(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(copy_debug)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use quick_error::ResultExt;
|
use quick_error::ResultExt;
|
||||||
|
|
||||||
use crate::{CopyResult, ReflinkMode, SparseMode};
|
use crate::{CopyDebug, CopyResult, OffloadReflinkDebug, ReflinkMode, SparseDebug, SparseMode};
|
||||||
|
|
||||||
/// Copies `source` to `dest` for systems without copy-on-write
|
/// Copies `source` to `dest` for systems without copy-on-write
|
||||||
pub(crate) fn copy_on_write(
|
pub(crate) fn copy_on_write(
|
||||||
|
@ -17,7 +17,7 @@ pub(crate) fn copy_on_write(
|
||||||
reflink_mode: ReflinkMode,
|
reflink_mode: ReflinkMode,
|
||||||
sparse_mode: SparseMode,
|
sparse_mode: SparseMode,
|
||||||
context: &str,
|
context: &str,
|
||||||
) -> CopyResult<()> {
|
) -> CopyResult<CopyDebug> {
|
||||||
if reflink_mode != ReflinkMode::Never {
|
if reflink_mode != ReflinkMode::Never {
|
||||||
return Err("--reflink is only supported on linux and macOS"
|
return Err("--reflink is only supported on linux and macOS"
|
||||||
.to_string()
|
.to_string()
|
||||||
|
@ -26,8 +26,12 @@ pub(crate) fn copy_on_write(
|
||||||
if sparse_mode != SparseMode::Auto {
|
if sparse_mode != SparseMode::Auto {
|
||||||
return Err("--sparse is only supported on linux".to_string().into());
|
return Err("--sparse is only supported on linux".to_string().into());
|
||||||
}
|
}
|
||||||
|
let copy_debug = CopyDebug {
|
||||||
|
offload: OffloadReflinkDebug::Unsupported,
|
||||||
|
reflink: OffloadReflinkDebug::Unsupported,
|
||||||
|
sparse_detection: SparseDebug::Unsupported,
|
||||||
|
};
|
||||||
fs::copy(source, dest).context(context)?;
|
fs::copy(source, dest).context(context)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(copy_debug)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_csplit"
|
name = "uu_csplit"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "csplit ~ (uutils) Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ..., and output byte counts of each piece to standard output"
|
description = "csplit ~ (uutils) Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ..., and output byte counts of each piece to standard output"
|
||||||
|
|
|
@ -127,7 +127,7 @@ where
|
||||||
I: Iterator<Item = (usize, io::Result<String>)>,
|
I: Iterator<Item = (usize, io::Result<String>)>,
|
||||||
{
|
{
|
||||||
// split the file based on patterns
|
// split the file based on patterns
|
||||||
for pattern in patterns.into_iter() {
|
for pattern in patterns {
|
||||||
let pattern_as_str = pattern.to_string();
|
let pattern_as_str = pattern.to_string();
|
||||||
let is_skip = matches!(pattern, patterns::Pattern::SkipToMatch(_, _, _));
|
let is_skip = matches!(pattern, patterns::Pattern::SkipToMatch(_, _, _));
|
||||||
match pattern {
|
match pattern {
|
||||||
|
@ -552,6 +552,109 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[uucore::main]
|
||||||
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
let args = args.collect_ignore();
|
||||||
|
|
||||||
|
let matches = uu_app().try_get_matches_from(args)?;
|
||||||
|
|
||||||
|
// get the file to split
|
||||||
|
let file_name = matches.get_one::<String>(options::FILE).unwrap();
|
||||||
|
|
||||||
|
// get the patterns to split on
|
||||||
|
let patterns: Vec<String> = matches
|
||||||
|
.get_many::<String>(options::PATTERN)
|
||||||
|
.unwrap()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect();
|
||||||
|
let patterns = patterns::get_patterns(&patterns[..])?;
|
||||||
|
let options = CsplitOptions::new(&matches);
|
||||||
|
if file_name == "-" {
|
||||||
|
let stdin = io::stdin();
|
||||||
|
Ok(csplit(&options, patterns, stdin.lock())?)
|
||||||
|
} else {
|
||||||
|
let file = File::open(file_name)
|
||||||
|
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
|
||||||
|
let file_metadata = file
|
||||||
|
.metadata()
|
||||||
|
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
|
||||||
|
if !file_metadata.is_file() {
|
||||||
|
return Err(CsplitError::NotRegularFile(file_name.to_string()).into());
|
||||||
|
}
|
||||||
|
Ok(csplit(&options, patterns, BufReader::new(file))?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> Command {
|
||||||
|
Command::new(uucore::util_name())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
.override_usage(format_usage(USAGE))
|
||||||
|
.infer_long_args(true)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::SUFFIX_FORMAT)
|
||||||
|
.short('b')
|
||||||
|
.long(options::SUFFIX_FORMAT)
|
||||||
|
.value_name("FORMAT")
|
||||||
|
.help("use sprintf FORMAT instead of %02d"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::PREFIX)
|
||||||
|
.short('f')
|
||||||
|
.long(options::PREFIX)
|
||||||
|
.value_name("PREFIX")
|
||||||
|
.help("use PREFIX instead of 'xx'"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::KEEP_FILES)
|
||||||
|
.short('k')
|
||||||
|
.long(options::KEEP_FILES)
|
||||||
|
.help("do not remove output files on errors")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::SUPPRESS_MATCHED)
|
||||||
|
.long(options::SUPPRESS_MATCHED)
|
||||||
|
.help("suppress the lines matching PATTERN")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::DIGITS)
|
||||||
|
.short('n')
|
||||||
|
.long(options::DIGITS)
|
||||||
|
.value_name("DIGITS")
|
||||||
|
.help("use specified number of digits instead of 2"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::QUIET)
|
||||||
|
.short('s')
|
||||||
|
.long(options::QUIET)
|
||||||
|
.visible_alias("silent")
|
||||||
|
.help("do not print counts of output file sizes")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::ELIDE_EMPTY_FILES)
|
||||||
|
.short('z')
|
||||||
|
.long(options::ELIDE_EMPTY_FILES)
|
||||||
|
.help("remove empty output files")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::FILE)
|
||||||
|
.hide(true)
|
||||||
|
.required(true)
|
||||||
|
.value_hint(clap::ValueHint::FilePath),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::PATTERN)
|
||||||
|
.hide(true)
|
||||||
|
.action(clap::ArgAction::Append)
|
||||||
|
.required(true),
|
||||||
|
)
|
||||||
|
.after_help(AFTER_HELP)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -714,106 +817,3 @@ mod tests {
|
||||||
assert!(input_splitter.next().is_none());
|
assert!(input_splitter.next().is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[uucore::main]
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|
||||||
let args = args.collect_ignore();
|
|
||||||
|
|
||||||
let matches = uu_app().try_get_matches_from(args)?;
|
|
||||||
|
|
||||||
// get the file to split
|
|
||||||
let file_name = matches.get_one::<String>(options::FILE).unwrap();
|
|
||||||
|
|
||||||
// get the patterns to split on
|
|
||||||
let patterns: Vec<String> = matches
|
|
||||||
.get_many::<String>(options::PATTERN)
|
|
||||||
.unwrap()
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.collect();
|
|
||||||
let patterns = patterns::get_patterns(&patterns[..])?;
|
|
||||||
let options = CsplitOptions::new(&matches);
|
|
||||||
if file_name == "-" {
|
|
||||||
let stdin = io::stdin();
|
|
||||||
Ok(csplit(&options, patterns, stdin.lock())?)
|
|
||||||
} else {
|
|
||||||
let file = File::open(file_name)
|
|
||||||
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
|
|
||||||
let file_metadata = file
|
|
||||||
.metadata()
|
|
||||||
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
|
|
||||||
if !file_metadata.is_file() {
|
|
||||||
return Err(CsplitError::NotRegularFile(file_name.to_string()).into());
|
|
||||||
}
|
|
||||||
Ok(csplit(&options, patterns, BufReader::new(file))?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uu_app() -> Command {
|
|
||||||
Command::new(uucore::util_name())
|
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.override_usage(format_usage(USAGE))
|
|
||||||
.infer_long_args(true)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::SUFFIX_FORMAT)
|
|
||||||
.short('b')
|
|
||||||
.long(options::SUFFIX_FORMAT)
|
|
||||||
.value_name("FORMAT")
|
|
||||||
.help("use sprintf FORMAT instead of %02d"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::PREFIX)
|
|
||||||
.short('f')
|
|
||||||
.long(options::PREFIX)
|
|
||||||
.value_name("PREFIX")
|
|
||||||
.help("use PREFIX instead of 'xx'"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::KEEP_FILES)
|
|
||||||
.short('k')
|
|
||||||
.long(options::KEEP_FILES)
|
|
||||||
.help("do not remove output files on errors")
|
|
||||||
.action(ArgAction::SetTrue),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::SUPPRESS_MATCHED)
|
|
||||||
.long(options::SUPPRESS_MATCHED)
|
|
||||||
.help("suppress the lines matching PATTERN")
|
|
||||||
.action(ArgAction::SetTrue),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::DIGITS)
|
|
||||||
.short('n')
|
|
||||||
.long(options::DIGITS)
|
|
||||||
.value_name("DIGITS")
|
|
||||||
.help("use specified number of digits instead of 2"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::QUIET)
|
|
||||||
.short('s')
|
|
||||||
.long(options::QUIET)
|
|
||||||
.visible_alias("silent")
|
|
||||||
.help("do not print counts of output file sizes")
|
|
||||||
.action(ArgAction::SetTrue),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::ELIDE_EMPTY_FILES)
|
|
||||||
.short('z')
|
|
||||||
.long(options::ELIDE_EMPTY_FILES)
|
|
||||||
.help("remove empty output files")
|
|
||||||
.action(ArgAction::SetTrue),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::FILE)
|
|
||||||
.hide(true)
|
|
||||||
.required(true)
|
|
||||||
.value_hint(clap::ValueHint::FilePath),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::PATTERN)
|
|
||||||
.hide(true)
|
|
||||||
.action(clap::ArgAction::Append)
|
|
||||||
.required(true),
|
|
||||||
)
|
|
||||||
.after_help(AFTER_HELP)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_cut"
|
name = "uu_cut"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "cut ~ (uutils) display byte/field columns of input lines"
|
description = "cut ~ (uutils) display byte/field columns of input lines"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# spell-checker:ignore humantime
|
# spell-checker:ignore datetime
|
||||||
[package]
|
[package]
|
||||||
name = "uu_date"
|
name = "uu_date"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "date ~ (uutils) display or set the current time"
|
description = "date ~ (uutils) display or set the current time"
|
||||||
|
@ -17,17 +17,18 @@ path = "src/date.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
#/ TODO: check if we can avoid chrono+time
|
|
||||||
time = { workspace=true }
|
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
uucore = { workspace = true }
|
uucore = { workspace = true }
|
||||||
humantime_to_duration = { workspace=true }
|
parse_datetime = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
windows-sys = { workspace=true, features = ["Win32_Foundation", "Win32_System_SystemInformation"] }
|
windows-sys = { workspace = true, features = [
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_System_SystemInformation",
|
||||||
|
] }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "date"
|
name = "date"
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes humantime
|
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes
|
||||||
|
|
||||||
use chrono::format::{Item, StrftimeItems};
|
use chrono::format::{Item, StrftimeItems};
|
||||||
use chrono::{DateTime, Duration as ChronoDuration, FixedOffset, Local, Offset, Utc};
|
use chrono::{DateTime, Duration, FixedOffset, Local, Offset, Utc};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use chrono::{Datelike, Timelike};
|
use chrono::{Datelike, Timelike};
|
||||||
use clap::{crate_version, Arg, ArgAction, Command};
|
use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
|
@ -18,7 +18,6 @@ use libc::{clock_settime, timespec, CLOCK_REALTIME};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use time::Duration;
|
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
#[cfg(not(any(target_os = "redox")))]
|
#[cfg(not(any(target_os = "redox")))]
|
||||||
use uucore::error::FromIo;
|
use uucore::error::FromIo;
|
||||||
|
@ -27,14 +26,13 @@ use uucore::{format_usage, help_about, help_usage, show};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use windows_sys::Win32::{Foundation::SYSTEMTIME, System::SystemInformation::SetSystemTime};
|
use windows_sys::Win32::{Foundation::SYSTEMTIME, System::SystemInformation::SetSystemTime};
|
||||||
|
|
||||||
|
use uucore::shortcut_value_parser::ShortcutValueParser;
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
const DATE: &str = "date";
|
const DATE: &str = "date";
|
||||||
const HOURS: &str = "hours";
|
const HOURS: &str = "hours";
|
||||||
const MINUTES: &str = "minutes";
|
const MINUTES: &str = "minutes";
|
||||||
const SECONDS: &str = "seconds";
|
const SECONDS: &str = "seconds";
|
||||||
const HOUR: &str = "hour";
|
|
||||||
const MINUTE: &str = "minute";
|
|
||||||
const SECOND: &str = "second";
|
|
||||||
const NS: &str = "ns";
|
const NS: &str = "ns";
|
||||||
|
|
||||||
const ABOUT: &str = help_about!("date.md");
|
const ABOUT: &str = help_about!("date.md");
|
||||||
|
@ -111,9 +109,9 @@ enum Iso8601Format {
|
||||||
impl<'a> From<&'a str> for Iso8601Format {
|
impl<'a> From<&'a str> for Iso8601Format {
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self {
|
||||||
match s {
|
match s {
|
||||||
HOURS | HOUR => Self::Hours,
|
HOURS => Self::Hours,
|
||||||
MINUTES | MINUTE => Self::Minutes,
|
MINUTES => Self::Minutes,
|
||||||
SECONDS | SECOND => Self::Seconds,
|
SECONDS => Self::Seconds,
|
||||||
NS => Self::Ns,
|
NS => Self::Ns,
|
||||||
DATE => Self::Date,
|
DATE => Self::Date,
|
||||||
// Note: This is caught by clap via `possible_values`
|
// Note: This is caught by clap via `possible_values`
|
||||||
|
@ -132,7 +130,7 @@ impl<'a> From<&'a str> for Rfc3339Format {
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self {
|
||||||
match s {
|
match s {
|
||||||
DATE => Self::Date,
|
DATE => Self::Date,
|
||||||
SECONDS | SECOND => Self::Seconds,
|
SECONDS => Self::Seconds,
|
||||||
NS => Self::Ns,
|
NS => Self::Ns,
|
||||||
// Should be caught by clap
|
// Should be caught by clap
|
||||||
_ => panic!("Invalid format: {s}"),
|
_ => panic!("Invalid format: {s}"),
|
||||||
|
@ -171,7 +169,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let date_source = if let Some(date) = matches.get_one::<String>(OPT_DATE) {
|
let date_source = if let Some(date) = matches.get_one::<String>(OPT_DATE) {
|
||||||
if let Ok(duration) = humantime_to_duration::from_str(date.as_str()) {
|
if let Ok(duration) = parse_datetime::from_str(date.as_str()) {
|
||||||
DateSource::Human(duration)
|
DateSource::Human(duration)
|
||||||
} else {
|
} else {
|
||||||
DateSource::Custom(date.into())
|
DateSource::Custom(date.into())
|
||||||
|
@ -226,13 +224,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let iter = std::iter::once(date);
|
let iter = std::iter::once(date);
|
||||||
Box::new(iter)
|
Box::new(iter)
|
||||||
}
|
}
|
||||||
DateSource::Human(ref input) => {
|
DateSource::Human(relative_time) => {
|
||||||
// Get the current DateTime<FixedOffset> and convert the input time::Duration to chrono::Duration
|
// Get the current DateTime<FixedOffset> for things like "1 year ago"
|
||||||
// for things like "1 year ago"
|
|
||||||
let current_time = DateTime::<FixedOffset>::from(Local::now());
|
let current_time = DateTime::<FixedOffset>::from(Local::now());
|
||||||
let input_chrono = ChronoDuration::seconds(input.as_seconds_f32() as i64)
|
let iter = std::iter::once(Ok(current_time + relative_time));
|
||||||
+ ChronoDuration::nanoseconds(input.subsec_nanoseconds() as i64);
|
|
||||||
let iter = std::iter::once(Ok(current_time + input_chrono));
|
|
||||||
Box::new(iter)
|
Box::new(iter)
|
||||||
}
|
}
|
||||||
DateSource::File(ref path) => {
|
DateSource::File(ref path) => {
|
||||||
|
@ -321,7 +316,9 @@ pub fn uu_app() -> Command {
|
||||||
.short('I')
|
.short('I')
|
||||||
.long(OPT_ISO_8601)
|
.long(OPT_ISO_8601)
|
||||||
.value_name("FMT")
|
.value_name("FMT")
|
||||||
.value_parser([DATE, HOUR, HOURS, MINUTE, MINUTES, SECOND, SECONDS, NS])
|
.value_parser(ShortcutValueParser::new([
|
||||||
|
DATE, HOURS, MINUTES, SECONDS, NS,
|
||||||
|
]))
|
||||||
.num_args(0..=1)
|
.num_args(0..=1)
|
||||||
.default_missing_value(OPT_DATE)
|
.default_missing_value(OPT_DATE)
|
||||||
.help(ISO_8601_HELP_STRING),
|
.help(ISO_8601_HELP_STRING),
|
||||||
|
@ -337,7 +334,7 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(OPT_RFC_3339)
|
Arg::new(OPT_RFC_3339)
|
||||||
.long(OPT_RFC_3339)
|
.long(OPT_RFC_3339)
|
||||||
.value_name("FMT")
|
.value_name("FMT")
|
||||||
.value_parser([DATE, SECOND, SECONDS, NS])
|
.value_parser(ShortcutValueParser::new([DATE, SECONDS, NS]))
|
||||||
.help(RFC_3339_HELP_STRING),
|
.help(RFC_3339_HELP_STRING),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_dd"
|
name = "uu_dd"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "dd ~ (uutils) copy and convert files"
|
description = "dd ~ (uutils) copy and convert files"
|
||||||
|
|
|
@ -13,7 +13,7 @@ Copy, and optionally convert, a file system resource
|
||||||
|
|
||||||
### Operands
|
### Operands
|
||||||
|
|
||||||
- `Bs=BYTES` : read and write up to BYTES bytes at a time (default: 512);
|
- `bs=BYTES` : read and write up to BYTES bytes at a time (default: 512);
|
||||||
overwrites `ibs` and `obs`.
|
overwrites `ibs` and `obs`.
|
||||||
- `cbs=BYTES` : the 'conversion block size' in bytes. Applies to the
|
- `cbs=BYTES` : the 'conversion block size' in bytes. Applies to the
|
||||||
`conv=block`, and `conv=unblock` operations.
|
`conv=block`, and `conv=unblock` operations.
|
||||||
|
@ -114,7 +114,7 @@ Copy, and optionally convert, a file system resource
|
||||||
|
|
||||||
### General Flags
|
### General Flags
|
||||||
|
|
||||||
- `Direct` : use direct I/O for data.
|
- `direct` : use direct I/O for data.
|
||||||
- `directory` : fail unless the given input (if used as an iflag) or
|
- `directory` : fail unless the given input (if used as an iflag) or
|
||||||
output (if used as an oflag) is a directory.
|
output (if used as an oflag) is a directory.
|
||||||
- `dsync` : use synchronized I/O for data.
|
- `dsync` : use synchronized I/O for data.
|
||||||
|
|
|
@ -36,9 +36,12 @@ use std::os::unix::{
|
||||||
io::{AsRawFd, FromRawFd},
|
io::{AsRawFd, FromRawFd},
|
||||||
};
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::mpsc;
|
use std::sync::{
|
||||||
|
atomic::{AtomicBool, Ordering::Relaxed},
|
||||||
|
mpsc, Arc,
|
||||||
|
};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use clap::{crate_version, Arg, Command};
|
use clap::{crate_version, Arg, Command};
|
||||||
use gcd::Gcd;
|
use gcd::Gcd;
|
||||||
|
@ -75,6 +78,45 @@ struct Settings {
|
||||||
status: Option<StatusLevel>,
|
status: Option<StatusLevel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A timer which triggers on a given interval
|
||||||
|
///
|
||||||
|
/// After being constructed with [`Alarm::with_interval`], [`Alarm::is_triggered`]
|
||||||
|
/// will return true once per the given [`Duration`].
|
||||||
|
///
|
||||||
|
/// Can be cloned, but the trigger status is shared across all instances so only
|
||||||
|
/// the first caller each interval will yield true.
|
||||||
|
///
|
||||||
|
/// When all instances are dropped the background thread will exit on the next interval.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Alarm {
|
||||||
|
interval: Duration,
|
||||||
|
trigger: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Alarm {
|
||||||
|
pub fn with_interval(interval: Duration) -> Self {
|
||||||
|
let trigger = Arc::new(AtomicBool::default());
|
||||||
|
|
||||||
|
let weak_trigger = Arc::downgrade(&trigger);
|
||||||
|
thread::spawn(move || {
|
||||||
|
while let Some(trigger) = weak_trigger.upgrade() {
|
||||||
|
thread::sleep(interval);
|
||||||
|
trigger.store(true, Relaxed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self { interval, trigger }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_triggered(&self) -> bool {
|
||||||
|
self.trigger.swap(false, Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_interval(&self) -> Duration {
|
||||||
|
self.interval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A number in blocks or bytes
|
/// A number in blocks or bytes
|
||||||
///
|
///
|
||||||
/// Some values (seek, skip, iseek, oseek) can have values either in blocks or in bytes.
|
/// Some values (seek, skip, iseek, oseek) can have values either in blocks or in bytes.
|
||||||
|
@ -760,7 +802,7 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||||
// of its report includes the throughput in bytes per second,
|
// of its report includes the throughput in bytes per second,
|
||||||
// which requires knowing how long the process has been
|
// which requires knowing how long the process has been
|
||||||
// running.
|
// running.
|
||||||
let start = time::Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
// A good buffer size for reading.
|
// A good buffer size for reading.
|
||||||
//
|
//
|
||||||
|
@ -780,7 +822,6 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||||
// information.
|
// information.
|
||||||
let (prog_tx, rx) = mpsc::channel();
|
let (prog_tx, rx) = mpsc::channel();
|
||||||
let output_thread = thread::spawn(gen_prog_updater(rx, i.settings.status));
|
let output_thread = thread::spawn(gen_prog_updater(rx, i.settings.status));
|
||||||
let mut progress_as_secs = 0;
|
|
||||||
|
|
||||||
// Optimization: if no blocks are to be written, then don't
|
// Optimization: if no blocks are to be written, then don't
|
||||||
// bother allocating any buffers.
|
// bother allocating any buffers.
|
||||||
|
@ -813,6 +854,12 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||||
// This is the max size needed.
|
// This is the max size needed.
|
||||||
let mut buf = vec![BUF_INIT_BYTE; bsize];
|
let mut buf = vec![BUF_INIT_BYTE; bsize];
|
||||||
|
|
||||||
|
// Spawn a timer thread to provide a scheduled signal indicating when we
|
||||||
|
// should send an update of our progress to the reporting thread.
|
||||||
|
//
|
||||||
|
// This avoids the need to query the OS monotonic clock for every block.
|
||||||
|
let alarm = Alarm::with_interval(Duration::from_secs(1));
|
||||||
|
|
||||||
// Index in the input file where we are reading bytes and in
|
// Index in the input file where we are reading bytes and in
|
||||||
// the output file where we are writing bytes.
|
// the output file where we are writing bytes.
|
||||||
//
|
//
|
||||||
|
@ -871,9 +918,8 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||||
// error.
|
// error.
|
||||||
rstat += rstat_update;
|
rstat += rstat_update;
|
||||||
wstat += wstat_update;
|
wstat += wstat_update;
|
||||||
|
if alarm.is_triggered() {
|
||||||
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
|
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
|
||||||
if prog_update.duration.as_secs() >= progress_as_secs {
|
|
||||||
progress_as_secs = prog_update.duration.as_secs() + 1;
|
|
||||||
prog_tx.send(prog_update).unwrap_or(());
|
prog_tx.send(prog_update).unwrap_or(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -885,7 +931,7 @@ fn finalize<T>(
|
||||||
output: &mut Output,
|
output: &mut Output,
|
||||||
rstat: ReadStat,
|
rstat: ReadStat,
|
||||||
wstat: WriteStat,
|
wstat: WriteStat,
|
||||||
start: time::Instant,
|
start: Instant,
|
||||||
prog_tx: &mpsc::Sender<ProgUpdate>,
|
prog_tx: &mpsc::Sender<ProgUpdate>,
|
||||||
output_thread: thread::JoinHandle<T>,
|
output_thread: thread::JoinHandle<T>,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
|
|
|
@ -498,7 +498,7 @@ mod tests {
|
||||||
|
|
||||||
fn prog_update_write(n: u128) -> ProgUpdate {
|
fn prog_update_write(n: u128) -> ProgUpdate {
|
||||||
ProgUpdate {
|
ProgUpdate {
|
||||||
read_stat: Default::default(),
|
read_stat: ReadStat::default(),
|
||||||
write_stat: WriteStat {
|
write_stat: WriteStat {
|
||||||
bytes_total: n,
|
bytes_total: n,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -510,8 +510,8 @@ mod tests {
|
||||||
|
|
||||||
fn prog_update_duration(duration: Duration) -> ProgUpdate {
|
fn prog_update_duration(duration: Duration) -> ProgUpdate {
|
||||||
ProgUpdate {
|
ProgUpdate {
|
||||||
read_stat: Default::default(),
|
read_stat: ReadStat::default(),
|
||||||
write_stat: Default::default(),
|
write_stat: WriteStat::default(),
|
||||||
duration,
|
duration,
|
||||||
complete: false,
|
complete: false,
|
||||||
}
|
}
|
||||||
|
@ -557,8 +557,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_prog_update_write_prog_line() {
|
fn test_prog_update_write_prog_line() {
|
||||||
let prog_update = ProgUpdate {
|
let prog_update = ProgUpdate {
|
||||||
read_stat: Default::default(),
|
read_stat: ReadStat::default(),
|
||||||
write_stat: Default::default(),
|
write_stat: WriteStat::default(),
|
||||||
duration: Duration::new(1, 0), // one second
|
duration: Duration::new(1, 0), // one second
|
||||||
complete: false,
|
complete: false,
|
||||||
};
|
};
|
||||||
|
@ -613,8 +613,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn write_transfer_stats() {
|
fn write_transfer_stats() {
|
||||||
let prog_update = ProgUpdate {
|
let prog_update = ProgUpdate {
|
||||||
read_stat: Default::default(),
|
read_stat: ReadStat::default(),
|
||||||
write_stat: Default::default(),
|
write_stat: WriteStat::default(),
|
||||||
duration: Duration::new(1, 0), // one second
|
duration: Duration::new(1, 0), // one second
|
||||||
complete: false,
|
complete: false,
|
||||||
};
|
};
|
||||||
|
@ -634,8 +634,8 @@ mod tests {
|
||||||
fn write_final_transfer_stats() {
|
fn write_final_transfer_stats() {
|
||||||
// Tests the formatting of the final statistics written after a progress line.
|
// Tests the formatting of the final statistics written after a progress line.
|
||||||
let prog_update = ProgUpdate {
|
let prog_update = ProgUpdate {
|
||||||
read_stat: Default::default(),
|
read_stat: ReadStat::default(),
|
||||||
write_stat: Default::default(),
|
write_stat: WriteStat::default(),
|
||||||
duration: Duration::new(1, 0), // one second
|
duration: Duration::new(1, 0), // one second
|
||||||
complete: false,
|
complete: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_df"
|
name = "uu_df"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "df ~ (uutils) display file system information"
|
description = "df ~ (uutils) display file system information"
|
||||||
|
@ -19,6 +19,9 @@ clap = { workspace=true }
|
||||||
uucore = { workspace = true, features = ["libc", "fsext"] }
|
uucore = { workspace = true, features = ["libc", "fsext"] }
|
||||||
unicode-width = { workspace = true }
|
unicode-width = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "df"
|
name = "df"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
|
@ -746,7 +746,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_remote_included() {
|
fn test_remote_included() {
|
||||||
let opt = Default::default();
|
let opt = Options::default();
|
||||||
let m = mount_info("ext4", "/mnt/foo", true, false);
|
let m = mount_info("ext4", "/mnt/foo", true, false);
|
||||||
assert!(is_included(&m, &opt));
|
assert!(is_included(&m, &opt));
|
||||||
}
|
}
|
||||||
|
@ -773,7 +773,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dummy_excluded() {
|
fn test_dummy_excluded() {
|
||||||
let opt = Default::default();
|
let opt = Options::default();
|
||||||
let m = mount_info("ext4", "/mnt/foo", false, true);
|
let m = mount_info("ext4", "/mnt/foo", false, true);
|
||||||
assert!(!is_included(&m, &opt));
|
assert!(!is_included(&m, &opt));
|
||||||
}
|
}
|
||||||
|
@ -864,11 +864,11 @@ mod tests {
|
||||||
|
|
||||||
mod filter_mount_list {
|
mod filter_mount_list {
|
||||||
|
|
||||||
use crate::filter_mount_list;
|
use crate::{filter_mount_list, Options};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_empty() {
|
fn test_empty() {
|
||||||
let opt = Default::default();
|
let opt = Options::default();
|
||||||
let mount_infos = vec![];
|
let mount_infos = vec![];
|
||||||
assert!(filter_mount_list(mount_infos, &opt).is_empty());
|
assert!(filter_mount_list(mount_infos, &opt).is_empty());
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub(crate) struct Filesystem {
|
||||||
/// This function returns the element of `mounts` on which `path` is
|
/// This function returns the element of `mounts` on which `path` is
|
||||||
/// mounted. If there are no matches, this function returns
|
/// mounted. If there are no matches, this function returns
|
||||||
/// [`None`]. If there are two or more matches, then the single
|
/// [`None`]. If there are two or more matches, then the single
|
||||||
/// [`MountInfo`] with the longest mount directory is returned.
|
/// [`MountInfo`] with the device name corresponding to the entered path.
|
||||||
///
|
///
|
||||||
/// If `canonicalize` is `true`, then the `path` is canonicalized
|
/// If `canonicalize` is `true`, then the `path` is canonicalized
|
||||||
/// before checking whether it matches any mount directories.
|
/// before checking whether it matches any mount directories.
|
||||||
|
@ -68,9 +68,19 @@ where
|
||||||
path.as_ref().to_path_buf()
|
path.as_ref().to_path_buf()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Find the potential mount point that matches entered path
|
||||||
let maybe_mount_point = mounts
|
let maybe_mount_point = mounts
|
||||||
.iter()
|
.iter()
|
||||||
.find(|mi| mi.dev_name.eq(&path.to_string_lossy()));
|
// Create pair MountInfo, canonicalized device name
|
||||||
|
// TODO Abstract from accessing real filesystem to
|
||||||
|
// make code more testable
|
||||||
|
.map(|m| (m, std::fs::canonicalize(&m.dev_name)))
|
||||||
|
// Ignore non existing paths
|
||||||
|
.filter(|m| m.1.is_ok())
|
||||||
|
.map(|m| (m.0, m.1.ok().unwrap()))
|
||||||
|
// Try to find canonicalized device name corresponding to entered path
|
||||||
|
.find(|m| m.1.eq(&path))
|
||||||
|
.map(|m| m.0);
|
||||||
|
|
||||||
maybe_mount_point.or_else(|| {
|
maybe_mount_point.or_else(|| {
|
||||||
mounts
|
mounts
|
||||||
|
@ -148,12 +158,12 @@ mod tests {
|
||||||
// Create a fake `MountInfo` with the given directory name.
|
// Create a fake `MountInfo` with the given directory name.
|
||||||
fn mount_info(mount_dir: &str) -> MountInfo {
|
fn mount_info(mount_dir: &str) -> MountInfo {
|
||||||
MountInfo {
|
MountInfo {
|
||||||
dev_id: Default::default(),
|
dev_id: String::default(),
|
||||||
dev_name: Default::default(),
|
dev_name: String::default(),
|
||||||
fs_type: Default::default(),
|
fs_type: String::default(),
|
||||||
mount_dir: String::from(mount_dir),
|
mount_dir: String::from(mount_dir),
|
||||||
mount_option: Default::default(),
|
mount_option: String::default(),
|
||||||
mount_root: Default::default(),
|
mount_root: String::default(),
|
||||||
remote: Default::default(),
|
remote: Default::default(),
|
||||||
dummy: Default::default(),
|
dummy: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -211,10 +221,16 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dev_name_match() {
|
fn test_dev_name_match() {
|
||||||
|
let tmp = tempfile::TempDir::new().expect("Failed to create temp dir");
|
||||||
|
let dev_name = std::fs::canonicalize(tmp.path())
|
||||||
|
.expect("Failed to canonicalize tmp path")
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
let mut mount_info = mount_info("/foo");
|
let mut mount_info = mount_info("/foo");
|
||||||
mount_info.dev_name = "/dev/sda2".to_string();
|
mount_info.dev_name = dev_name.clone();
|
||||||
let mounts = [mount_info];
|
let mounts = [mount_info];
|
||||||
let actual = mount_info_from_path(&mounts, "/dev/sda2", false).unwrap();
|
let actual = mount_info_from_path(&mounts, dev_name, false).unwrap();
|
||||||
assert!(mount_info_eq(actual, &mounts[0]));
|
assert!(mount_info_eq(actual, &mounts[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -513,7 +513,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_header() {
|
fn test_default_header() {
|
||||||
let options = Default::default();
|
let options = Options::default();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Header::get_headers(&options),
|
Header::get_headers(&options),
|
||||||
vec!(
|
vec!(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_dir"
|
name = "uu_dir"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "shortcut to ls -C -b"
|
description = "shortcut to ls -C -b"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_dircolors"
|
name = "uu_dircolors"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "dircolors ~ (uutils) display commands to set LS_COLORS"
|
description = "dircolors ~ (uutils) display commands to set LS_COLORS"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_dirname"
|
name = "uu_dirname"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "dirname ~ (uutils) display parent directory of PATHNAME"
|
description = "dirname ~ (uutils) display parent directory of PATHNAME"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_du"
|
name = "uu_du"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "du ~ (uutils) display disk usage"
|
description = "du ~ (uutils) display disk usage"
|
||||||
|
@ -22,7 +22,10 @@ clap = { workspace=true }
|
||||||
uucore = { workspace = true }
|
uucore = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
windows-sys = { workspace=true, features = ["Win32_Storage_FileSystem", "Win32_Foundation"] }
|
windows-sys = { workspace = true, features = [
|
||||||
|
"Win32_Storage_FileSystem",
|
||||||
|
"Win32_Foundation",
|
||||||
|
] }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "du"
|
name = "du"
|
||||||
|
|
|
@ -145,7 +145,7 @@ impl Stat {
|
||||||
return Ok(Self {
|
return Ok(Self {
|
||||||
path: path.to_path_buf(),
|
path: path.to_path_buf(),
|
||||||
is_dir: metadata.is_dir(),
|
is_dir: metadata.is_dir(),
|
||||||
size: metadata.len(),
|
size: if path.is_dir() { 0 } else { metadata.len() },
|
||||||
blocks: metadata.blocks(),
|
blocks: metadata.blocks(),
|
||||||
inodes: 1,
|
inodes: 1,
|
||||||
inode: Some(file_info),
|
inode: Some(file_info),
|
||||||
|
@ -162,7 +162,7 @@ impl Stat {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path: path.to_path_buf(),
|
path: path.to_path_buf(),
|
||||||
is_dir: metadata.is_dir(),
|
is_dir: metadata.is_dir(),
|
||||||
size: metadata.len(),
|
size: if path.is_dir() { 0 } else { metadata.len() },
|
||||||
blocks: size_on_disk / 1024 * 2,
|
blocks: size_on_disk / 1024 * 2,
|
||||||
inode: file_info,
|
inode: file_info,
|
||||||
inodes: 1,
|
inodes: 1,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_echo"
|
name = "uu_echo"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "echo ~ (uutils) display TEXT"
|
description = "echo ~ (uutils) display TEXT"
|
||||||
|
|
2
src/uu/env/Cargo.toml
vendored
2
src/uu/env/Cargo.toml
vendored
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_env"
|
name = "uu_env"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "env ~ (uutils) set each NAME to VALUE in the environment and run COMMAND"
|
description = "env ~ (uutils) set each NAME to VALUE in the environment and run COMMAND"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_expand"
|
name = "uu_expand"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "expand ~ (uutils) convert input tabs to spaces"
|
description = "expand ~ (uutils) convert input tabs to spaces"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_expr"
|
name = "uu_expr"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "expr ~ (uutils) display the value of EXPRESSION"
|
description = "expr ~ (uutils) display the value of EXPRESSION"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_factor"
|
name = "uu_factor"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "factor ~ (uutils) display the prime factors of each NUMBER"
|
description = "factor ~ (uutils) display the prime factors of each NUMBER"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_false"
|
name = "uu_false"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "false ~ (uutils) do nothing and fail"
|
description = "false ~ (uutils) do nothing and fail"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_fmt"
|
name = "uu_fmt"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "fmt ~ (uutils) reformat each paragraph of input"
|
description = "fmt ~ (uutils) reformat each paragraph of input"
|
||||||
|
|
|
@ -598,7 +598,7 @@ impl<'a> Iterator for WordSplit<'a> {
|
||||||
self.prev_punct && (before_tab.is_some() || word_start_relative > 1);
|
self.prev_punct && (before_tab.is_some() || word_start_relative > 1);
|
||||||
|
|
||||||
// now record whether this word ends in punctuation
|
// now record whether this word ends in punctuation
|
||||||
self.prev_punct = match self.string[..self.position].chars().rev().next() {
|
self.prev_punct = match self.string[..self.position].chars().next_back() {
|
||||||
Some(ch) => WordSplit::is_punctuation(ch),
|
Some(ch) => WordSplit::is_punctuation(ch),
|
||||||
_ => panic!("fatal: expected word not to be empty"),
|
_ => panic!("fatal: expected word not to be empty"),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_fold"
|
name = "uu_fold"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "fold ~ (uutils) wrap each line of input"
|
description = "fold ~ (uutils) wrap each line of input"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_groups"
|
name = "uu_groups"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "groups ~ (uutils) display group memberships for USERNAME"
|
description = "groups ~ (uutils) display group memberships for USERNAME"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_hashsum"
|
name = "uu_hashsum"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "hashsum ~ (uutils) display or check input digests"
|
description = "hashsum ~ (uutils) display or check input digests"
|
||||||
|
|
|
@ -306,7 +306,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
|
||||||
// if there is no program name for some reason, default to "hashsum"
|
// if there is no program name for some reason, default to "hashsum"
|
||||||
let program = args.next().unwrap_or_else(|| OsString::from(NAME));
|
let program = args.next().unwrap_or_else(|| OsString::from(NAME));
|
||||||
let binary_name = Path::new(&program)
|
let binary_name = Path::new(&program)
|
||||||
.file_name()
|
.file_stem()
|
||||||
.unwrap_or_else(|| OsStr::new(NAME))
|
.unwrap_or_else(|| OsStr::new(NAME))
|
||||||
.to_string_lossy();
|
.to_string_lossy();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_head"
|
name = "uu_head"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "head ~ (uutils) display the first lines of input"
|
description = "head ~ (uutils) display the first lines of input"
|
||||||
|
|
|
@ -575,7 +575,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_options_correct_defaults() {
|
fn test_options_correct_defaults() {
|
||||||
let opts: HeadOptions = Default::default();
|
let opts = HeadOptions::default();
|
||||||
|
|
||||||
assert!(!opts.verbose);
|
assert!(!opts.verbose);
|
||||||
assert!(!opts.quiet);
|
assert!(!opts.quiet);
|
||||||
|
@ -622,12 +622,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn test_arg_iterate_bad_encoding() {
|
fn test_arg_iterate_bad_encoding() {
|
||||||
#[allow(clippy::invalid_utf8_in_unchecked)]
|
use std::os::unix::ffi::OsStringExt;
|
||||||
let invalid = unsafe { std::str::from_utf8_unchecked(b"\x80\x81") };
|
let invalid = OsString::from_vec(vec![b'\x80', b'\x81']);
|
||||||
// this arises from a conversion from OsString to &str
|
// this arises from a conversion from OsString to &str
|
||||||
assert!(
|
assert!(arg_iterate(vec![OsString::from("head"), invalid].into_iter()).is_err());
|
||||||
arg_iterate(vec![OsString::from("head"), OsString::from(invalid)].into_iter()).is_err()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn read_early_exit() {
|
fn read_early_exit() {
|
||||||
|
|
|
@ -114,7 +114,13 @@ pub fn parse_num(src: &str) -> Result<(u64, bool), ParseSizeError> {
|
||||||
return Err(ParseSizeError::ParseFailure(src.to_string()));
|
return Err(ParseSizeError::ParseFailure(src.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_size(size_string).map(|n| (n, all_but_last))
|
// remove leading zeros so that size is interpreted as decimal, not octal
|
||||||
|
let trimmed_string = size_string.trim_start_matches('0');
|
||||||
|
if trimmed_string.is_empty() {
|
||||||
|
Ok((0, all_but_last))
|
||||||
|
} else {
|
||||||
|
parse_size(trimmed_string).map(|n| (n, all_but_last))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_hostid"
|
name = "uu_hostid"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "hostid ~ (uutils) display the numeric identifier of the current host"
|
description = "hostid ~ (uutils) display the numeric identifier of the current host"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_hostname"
|
name = "uu_hostname"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "hostname ~ (uutils) display or set the host name of the current host"
|
description = "hostname ~ (uutils) display or set the host name of the current host"
|
||||||
|
@ -20,7 +20,10 @@ hostname = { version = "0.3", features = ["set"] }
|
||||||
uucore = { workspace = true, features = ["wide"] }
|
uucore = { workspace = true, features = ["wide"] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
windows-sys = { workspace=true, features = ["Win32_Networking_WinSock", "Win32_Foundation"] }
|
windows-sys = { workspace = true, features = [
|
||||||
|
"Win32_Networking_WinSock",
|
||||||
|
"Win32_Foundation",
|
||||||
|
] }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "hostname"
|
name = "hostname"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_id"
|
name = "uu_id"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "id ~ (uutils) display user and group information for USER"
|
description = "id ~ (uutils) display user and group information for USER"
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_install"
|
name = "uu_install"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = [
|
authors = ["Ben Eills <ben@beneills.com>", "uutils developers"]
|
||||||
"Ben Eills <ben@beneills.com>",
|
|
||||||
"uutils developers",
|
|
||||||
]
|
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "install ~ (uutils) copy files from SOURCE to DESTINATION (with specified attributes)"
|
description = "install ~ (uutils) copy files from SOURCE to DESTINATION (with specified attributes)"
|
||||||
|
|
||||||
|
@ -24,9 +21,6 @@ file_diff = { workspace=true }
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
uucore = { workspace = true, features = ["fs", "mode", "perms", "entries"] }
|
uucore = { workspace = true, features = ["fs", "mode", "perms", "entries"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
time = { workspace=true }
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "install"
|
name = "install"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_join"
|
name = "uu_join"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "join ~ (uutils) merge lines from inputs with matching join fields"
|
description = "join ~ (uutils) merge lines from inputs with matching join fields"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_kill"
|
name = "uu_kill"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "kill ~ (uutils) send a signal to a process"
|
description = "kill ~ (uutils) send a signal to a process"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_link"
|
name = "uu_link"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "link ~ (uutils) create a hard (file system) link to FILE"
|
description = "link ~ (uutils) create a hard (file system) link to FILE"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_ln"
|
name = "uu_ln"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "ln ~ (uutils) create a (file system) link to TARGET"
|
description = "ln ~ (uutils) create a (file system) link to TARGET"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_logname"
|
name = "uu_logname"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "logname ~ (uutils) display the login name of the current user"
|
description = "logname ~ (uutils) display the login name of the current user"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_ls"
|
name = "uu_ls"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "ls ~ (uutils) display directory contents"
|
description = "ls ~ (uutils) display directory contents"
|
||||||
|
|
|
@ -17,6 +17,8 @@ use lscolors::LsColors;
|
||||||
use number_prefix::NumberPrefix;
|
use number_prefix::NumberPrefix;
|
||||||
use once_cell::unsync::OnceCell;
|
use once_cell::unsync::OnceCell;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::num::IntErrorKind;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::os::windows::fs::MetadataExt;
|
use std::os::windows::fs::MetadataExt;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -294,6 +296,7 @@ enum Sort {
|
||||||
Time,
|
Time,
|
||||||
Version,
|
Version,
|
||||||
Extension,
|
Extension,
|
||||||
|
Width,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
@ -496,6 +499,7 @@ fn extract_sort(options: &clap::ArgMatches) -> Sort {
|
||||||
"size" => Sort::Size,
|
"size" => Sort::Size,
|
||||||
"version" => Sort::Version,
|
"version" => Sort::Version,
|
||||||
"extension" => Sort::Extension,
|
"extension" => Sort::Extension,
|
||||||
|
"width" => Sort::Width,
|
||||||
// below should never happen as clap already restricts the values.
|
// below should never happen as clap already restricts the values.
|
||||||
_ => unreachable!("Invalid field for --sort"),
|
_ => unreachable!("Invalid field for --sort"),
|
||||||
}
|
}
|
||||||
|
@ -655,6 +659,19 @@ fn extract_indicator_style(options: &clap::ArgMatches) -> IndicatorStyle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_width(s: &str) -> Result<u16, LsError> {
|
||||||
|
let radix = match s.starts_with('0') && s.len() > 1 {
|
||||||
|
true => 8,
|
||||||
|
false => 10,
|
||||||
|
};
|
||||||
|
match u16::from_str_radix(s, radix) {
|
||||||
|
Ok(x) => Ok(x),
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
IntErrorKind::PosOverflow => Ok(u16::MAX),
|
||||||
|
_ => Err(LsError::InvalidLineWidth(s.into())),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Config {
|
impl Config {
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
pub fn from(options: &clap::ArgMatches) -> UResult<Self> {
|
pub fn from(options: &clap::ArgMatches) -> UResult<Self> {
|
||||||
|
@ -793,20 +810,7 @@ impl Config {
|
||||||
};
|
};
|
||||||
|
|
||||||
let width = match options.get_one::<String>(options::WIDTH) {
|
let width = match options.get_one::<String>(options::WIDTH) {
|
||||||
Some(x) => {
|
Some(x) => parse_width(x)?,
|
||||||
if x.starts_with('0') && x.len() > 1 {
|
|
||||||
// Read number as octal
|
|
||||||
match u16::from_str_radix(x, 8) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(_) => return Err(LsError::InvalidLineWidth(x.into()).into()),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match x.parse::<u16>() {
|
|
||||||
Ok(u) => u,
|
|
||||||
Err(_) => return Err(LsError::InvalidLineWidth(x.into()).into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => match terminal_size::terminal_size() {
|
None => match terminal_size::terminal_size() {
|
||||||
Some((width, _)) => width.0,
|
Some((width, _)) => width.0,
|
||||||
None => match std::env::var_os("COLUMNS") {
|
None => match std::env::var_os("COLUMNS") {
|
||||||
|
@ -1200,6 +1204,7 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(options::quoting::LITERAL)
|
Arg::new(options::quoting::LITERAL)
|
||||||
.short('N')
|
.short('N')
|
||||||
.long(options::quoting::LITERAL)
|
.long(options::quoting::LITERAL)
|
||||||
|
.alias("l")
|
||||||
.help("Use literal quoting style. Equivalent to `--quoting-style=literal`")
|
.help("Use literal quoting style. Equivalent to `--quoting-style=literal`")
|
||||||
.overrides_with_all([
|
.overrides_with_all([
|
||||||
options::QUOTING_STYLE,
|
options::QUOTING_STYLE,
|
||||||
|
@ -1322,9 +1327,9 @@ pub fn uu_app() -> Command {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::SORT)
|
Arg::new(options::SORT)
|
||||||
.long(options::SORT)
|
.long(options::SORT)
|
||||||
.help("Sort by <field>: name, none (-U), time (-t), size (-S) or extension (-X)")
|
.help("Sort by <field>: name, none (-U), time (-t), size (-S), extension (-X) or width")
|
||||||
.value_name("field")
|
.value_name("field")
|
||||||
.value_parser(["name", "none", "time", "size", "version", "extension"])
|
.value_parser(["name", "none", "time", "size", "version", "extension", "width"])
|
||||||
.require_equals(true)
|
.require_equals(true)
|
||||||
.overrides_with_all([
|
.overrides_with_all([
|
||||||
options::SORT,
|
options::SORT,
|
||||||
|
@ -1929,14 +1934,22 @@ fn sort_entries(entries: &mut [PathData], config: &Config, out: &mut BufWriter<S
|
||||||
Sort::Size => entries.sort_by_key(|k| Reverse(k.md(out).map(|md| md.len()).unwrap_or(0))),
|
Sort::Size => entries.sort_by_key(|k| Reverse(k.md(out).map(|md| md.len()).unwrap_or(0))),
|
||||||
// The default sort in GNU ls is case insensitive
|
// The default sort in GNU ls is case insensitive
|
||||||
Sort::Name => entries.sort_by(|a, b| a.display_name.cmp(&b.display_name)),
|
Sort::Name => entries.sort_by(|a, b| a.display_name.cmp(&b.display_name)),
|
||||||
Sort::Version => entries
|
Sort::Version => entries.sort_by(|a, b| {
|
||||||
.sort_by(|a, b| version_cmp(&a.p_buf.to_string_lossy(), &b.p_buf.to_string_lossy())),
|
version_cmp(&a.p_buf.to_string_lossy(), &b.p_buf.to_string_lossy())
|
||||||
|
.then(a.p_buf.to_string_lossy().cmp(&b.p_buf.to_string_lossy()))
|
||||||
|
}),
|
||||||
Sort::Extension => entries.sort_by(|a, b| {
|
Sort::Extension => entries.sort_by(|a, b| {
|
||||||
a.p_buf
|
a.p_buf
|
||||||
.extension()
|
.extension()
|
||||||
.cmp(&b.p_buf.extension())
|
.cmp(&b.p_buf.extension())
|
||||||
.then(a.p_buf.file_stem().cmp(&b.p_buf.file_stem()))
|
.then(a.p_buf.file_stem().cmp(&b.p_buf.file_stem()))
|
||||||
}),
|
}),
|
||||||
|
Sort::Width => entries.sort_by(|a, b| {
|
||||||
|
a.display_name
|
||||||
|
.len()
|
||||||
|
.cmp(&b.display_name.len())
|
||||||
|
.then(a.display_name.cmp(&b.display_name))
|
||||||
|
}),
|
||||||
Sort::None => {}
|
Sort::None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3000,6 +3013,20 @@ fn display_inode(metadata: &Metadata) -> String {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -> String {
|
fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -> String {
|
||||||
let substitute_string = "?".to_string();
|
let substitute_string = "?".to_string();
|
||||||
|
// If we must dereference, ensure that the symlink is actually valid even if the system
|
||||||
|
// does not support SELinux.
|
||||||
|
// Conforms to the GNU coreutils where a dangling symlink results in exit code 1.
|
||||||
|
if must_dereference {
|
||||||
|
match get_metadata(p_buf, must_dereference) {
|
||||||
|
Err(err) => {
|
||||||
|
// The Path couldn't be dereferenced, so return early and set exit code 1
|
||||||
|
// to indicate a minor error
|
||||||
|
show!(LsError::IOErrorContext(err, p_buf.to_path_buf(), false));
|
||||||
|
return substitute_string;
|
||||||
|
}
|
||||||
|
Ok(md) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
if config.selinux_supported {
|
if config.selinux_supported {
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_mkdir"
|
name = "uu_mkdir"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "mkdir ~ (uutils) create DIRECTORY"
|
description = "mkdir ~ (uutils) create DIRECTORY"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_mkfifo"
|
name = "uu_mkfifo"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "mkfifo ~ (uutils) create FIFOs (named pipes)"
|
description = "mkfifo ~ (uutils) create FIFOs (named pipes)"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_mknod"
|
name = "uu_mknod"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "mknod ~ (uutils) create special file NAME of TYPE"
|
description = "mknod ~ (uutils) create special file NAME of TYPE"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_mktemp"
|
name = "uu_mktemp"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "mktemp ~ (uutils) create and display a temporary file or directory from TEMPLATE"
|
description = "mktemp ~ (uutils) create and display a temporary file or directory from TEMPLATE"
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
// spell-checker:ignore (paths) GPGHome findxs
|
// spell-checker:ignore (paths) GPGHome findxs
|
||||||
|
|
||||||
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
use clap::{builder::ValueParser, crate_version, Arg, ArgAction, ArgMatches, Command};
|
||||||
use uucore::display::{println_verbatim, Quotable};
|
use uucore::display::{println_verbatim, Quotable};
|
||||||
use uucore::error::{FromIo, UError, UResult, UUsageError};
|
use uucore::error::{FromIo, UError, UResult, UUsageError};
|
||||||
use uucore::{format_usage, help_about, help_usage};
|
use uucore::{format_usage, help_about, help_usage};
|
||||||
|
@ -38,6 +38,7 @@ static OPT_DRY_RUN: &str = "dry-run";
|
||||||
static OPT_QUIET: &str = "quiet";
|
static OPT_QUIET: &str = "quiet";
|
||||||
static OPT_SUFFIX: &str = "suffix";
|
static OPT_SUFFIX: &str = "suffix";
|
||||||
static OPT_TMPDIR: &str = "tmpdir";
|
static OPT_TMPDIR: &str = "tmpdir";
|
||||||
|
static OPT_P: &str = "p";
|
||||||
static OPT_T: &str = "t";
|
static OPT_T: &str = "t";
|
||||||
|
|
||||||
static ARG_TEMPLATE: &str = "template";
|
static ARG_TEMPLATE: &str = "template";
|
||||||
|
@ -130,7 +131,7 @@ struct Options {
|
||||||
/// The directory in which to create the temporary file.
|
/// The directory in which to create the temporary file.
|
||||||
///
|
///
|
||||||
/// If `None`, the file will be created in the current directory.
|
/// If `None`, the file will be created in the current directory.
|
||||||
tmpdir: Option<String>,
|
tmpdir: Option<PathBuf>,
|
||||||
|
|
||||||
/// The suffix to append to the temporary file, if any.
|
/// The suffix to append to the temporary file, if any.
|
||||||
suffix: Option<String>,
|
suffix: Option<String>,
|
||||||
|
@ -142,73 +143,33 @@ struct Options {
|
||||||
template: String,
|
template: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decide whether the argument to `--tmpdir` should actually be the template.
|
|
||||||
///
|
|
||||||
/// This function is required to work around a limitation of `clap`,
|
|
||||||
/// the command-line argument parsing library. In case the command
|
|
||||||
/// line is
|
|
||||||
///
|
|
||||||
/// ```sh
|
|
||||||
/// mktemp --tmpdir XXX
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// the program should behave like
|
|
||||||
///
|
|
||||||
/// ```sh
|
|
||||||
/// mktemp --tmpdir=${TMPDIR:-/tmp} XXX
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// However, `clap` thinks that `XXX` is the value of the `--tmpdir`
|
|
||||||
/// option. This function returns `true` in this case and `false`
|
|
||||||
/// in all other cases.
|
|
||||||
fn is_tmpdir_argument_actually_the_template(matches: &ArgMatches) -> bool {
|
|
||||||
if !matches.contains_id(ARG_TEMPLATE) {
|
|
||||||
if let Some(tmpdir) = matches.get_one::<String>(OPT_TMPDIR) {
|
|
||||||
if !Path::new(tmpdir).is_dir() && tmpdir.contains("XXX") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
fn from(matches: &ArgMatches) -> Self {
|
fn from(matches: &ArgMatches) -> Self {
|
||||||
// Special case to work around a limitation of `clap`; see
|
let tmpdir = matches
|
||||||
// `is_tmpdir_argument_actually_the_template()` for more
|
.get_one::<PathBuf>(OPT_TMPDIR)
|
||||||
// information.
|
.or_else(|| matches.get_one::<PathBuf>(OPT_P))
|
||||||
//
|
.cloned();
|
||||||
// Fixed in clap 3
|
let (tmpdir, template) = match matches.get_one::<String>(ARG_TEMPLATE) {
|
||||||
// See https://github.com/clap-rs/clap/pull/1587
|
|
||||||
let (tmpdir, template) = if is_tmpdir_argument_actually_the_template(matches) {
|
|
||||||
let tmpdir = Some(env::temp_dir().display().to_string());
|
|
||||||
let template = matches.get_one::<String>(OPT_TMPDIR).unwrap().to_string();
|
|
||||||
(tmpdir, template)
|
|
||||||
} else {
|
|
||||||
// If no template argument is given, `--tmpdir` is implied.
|
// If no template argument is given, `--tmpdir` is implied.
|
||||||
match matches.get_one::<String>(ARG_TEMPLATE) {
|
|
||||||
None => {
|
None => {
|
||||||
let tmpdir = match matches.get_one::<String>(OPT_TMPDIR) {
|
let tmpdir = Some(tmpdir.unwrap_or_else(env::temp_dir));
|
||||||
None => Some(env::temp_dir().display().to_string()),
|
|
||||||
Some(tmpdir) => Some(tmpdir.to_string()),
|
|
||||||
};
|
|
||||||
let template = DEFAULT_TEMPLATE;
|
let template = DEFAULT_TEMPLATE;
|
||||||
(tmpdir, template.to_string())
|
(tmpdir, template.to_string())
|
||||||
}
|
}
|
||||||
Some(template) => {
|
Some(template) => {
|
||||||
let tmpdir = if env::var(TMPDIR_ENV_VAR).is_ok() && matches.get_flag(OPT_T) {
|
let tmpdir = if env::var(TMPDIR_ENV_VAR).is_ok() && matches.get_flag(OPT_T) {
|
||||||
env::var(TMPDIR_ENV_VAR).ok()
|
env::var_os(TMPDIR_ENV_VAR).map(|t| t.into())
|
||||||
} else if matches.contains_id(OPT_TMPDIR) {
|
} else if tmpdir.is_some() {
|
||||||
matches.get_one::<String>(OPT_TMPDIR).map(String::from)
|
tmpdir
|
||||||
} else if matches.get_flag(OPT_T) {
|
} else if matches.get_flag(OPT_T) || matches.contains_id(OPT_TMPDIR) {
|
||||||
// mktemp -t foo.xxx should export in TMPDIR
|
// If --tmpdir is given without an argument, or -t is given
|
||||||
Some(env::temp_dir().display().to_string())
|
// export in TMPDIR
|
||||||
|
Some(env::temp_dir())
|
||||||
} else {
|
} else {
|
||||||
matches.get_one::<String>(OPT_TMPDIR).map(String::from)
|
None
|
||||||
};
|
};
|
||||||
(tmpdir, template.to_string())
|
(tmpdir, template.to_string())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
directory: matches.get_flag(OPT_DIRECTORY),
|
directory: matches.get_flag(OPT_DIRECTORY),
|
||||||
|
@ -372,7 +333,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
|
||||||
if env::var("POSIXLY_CORRECT").is_ok() {
|
if env::var("POSIXLY_CORRECT").is_ok() {
|
||||||
// If POSIXLY_CORRECT was set, template MUST be the last argument.
|
// If POSIXLY_CORRECT was set, template MUST be the last argument.
|
||||||
if is_tmpdir_argument_actually_the_template(&matches) || matches.contains_id(ARG_TEMPLATE) {
|
if matches.contains_id(ARG_TEMPLATE) {
|
||||||
// Template argument was provided, check if was the last one.
|
// Template argument was provided, check if was the last one.
|
||||||
if args.last().unwrap() != &options.template {
|
if args.last().unwrap() != &options.template {
|
||||||
return Err(Box::new(MkTempError::TooManyTemplates));
|
return Err(Box::new(MkTempError::TooManyTemplates));
|
||||||
|
@ -444,8 +405,16 @@ pub fn uu_app() -> Command {
|
||||||
.value_name("SUFFIX"),
|
.value_name("SUFFIX"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(OPT_TMPDIR)
|
Arg::new(OPT_P)
|
||||||
.short('p')
|
.short('p')
|
||||||
|
.help("short form of --tmpdir")
|
||||||
|
.value_name("DIR")
|
||||||
|
.num_args(1)
|
||||||
|
.value_parser(ValueParser::path_buf())
|
||||||
|
.value_hint(clap::ValueHint::DirPath),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(OPT_TMPDIR)
|
||||||
.long(OPT_TMPDIR)
|
.long(OPT_TMPDIR)
|
||||||
.help(
|
.help(
|
||||||
"interpret TEMPLATE relative to DIR; if DIR is not specified, use \
|
"interpret TEMPLATE relative to DIR; if DIR is not specified, use \
|
||||||
|
@ -457,6 +426,10 @@ pub fn uu_app() -> Command {
|
||||||
// Allows use of default argument just by setting --tmpdir. Else,
|
// Allows use of default argument just by setting --tmpdir. Else,
|
||||||
// use provided input to generate tmpdir
|
// use provided input to generate tmpdir
|
||||||
.num_args(0..=1)
|
.num_args(0..=1)
|
||||||
|
// Require an equals to avoid ambiguity if no tmpdir is supplied
|
||||||
|
.require_equals(true)
|
||||||
|
.overrides_with(OPT_P)
|
||||||
|
.value_parser(ValueParser::path_buf())
|
||||||
.value_hint(clap::ValueHint::DirPath),
|
.value_hint(clap::ValueHint::DirPath),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_more"
|
name = "uu_more"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "more ~ (uutils) input perusal filter"
|
description = "more ~ (uutils) input perusal filter"
|
||||||
|
|
|
@ -14,10 +14,10 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
use clap::{crate_version, value_parser, Arg, ArgAction, ArgMatches, Command};
|
||||||
use crossterm::event::KeyEventKind;
|
use crossterm::event::KeyEventKind;
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor::MoveTo,
|
cursor::{MoveTo, MoveUp},
|
||||||
event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
|
event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
|
||||||
execute, queue,
|
execute, queue,
|
||||||
style::Attribute,
|
style::Attribute,
|
||||||
|
@ -50,19 +50,37 @@ pub mod options {
|
||||||
pub const FILES: &str = "files";
|
pub const FILES: &str = "files";
|
||||||
}
|
}
|
||||||
|
|
||||||
const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";
|
const MULTI_FILE_TOP_PROMPT: &str = "\r::::::::::::::\n\r{}\n\r::::::::::::::\n";
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
silent: bool,
|
|
||||||
clean_print: bool,
|
clean_print: bool,
|
||||||
|
from_line: usize,
|
||||||
|
lines: Option<u16>,
|
||||||
print_over: bool,
|
print_over: bool,
|
||||||
|
silent: bool,
|
||||||
squeeze: bool,
|
squeeze: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
fn from(matches: &ArgMatches) -> Self {
|
fn from(matches: &ArgMatches) -> Self {
|
||||||
|
let lines = match (
|
||||||
|
matches.get_one::<u16>(options::LINES).copied(),
|
||||||
|
matches.get_one::<u16>(options::NUMBER).copied(),
|
||||||
|
) {
|
||||||
|
// We add 1 to the number of lines to display because the last line
|
||||||
|
// is used for the banner
|
||||||
|
(Some(number), _) if number > 0 => Some(number + 1),
|
||||||
|
(None, Some(number)) if number > 0 => Some(number + 1),
|
||||||
|
(_, _) => None,
|
||||||
|
};
|
||||||
|
let from_line = match matches.get_one::<usize>(options::FROM_LINE).copied() {
|
||||||
|
Some(number) if number > 1 => number - 1,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
Self {
|
Self {
|
||||||
clean_print: matches.get_flag(options::CLEAN_PRINT),
|
clean_print: matches.get_flag(options::CLEAN_PRINT),
|
||||||
|
from_line,
|
||||||
|
lines,
|
||||||
print_over: matches.get_flag(options::PRINT_OVER),
|
print_over: matches.get_flag(options::PRINT_OVER),
|
||||||
silent: matches.get_flag(options::SILENT),
|
silent: matches.get_flag(options::SILENT),
|
||||||
squeeze: matches.get_flag(options::SQUEEZE),
|
squeeze: matches.get_flag(options::SQUEEZE),
|
||||||
|
@ -78,7 +96,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let options = Options::from(&matches);
|
let mut options = Options::from(&matches);
|
||||||
|
|
||||||
let mut buff = String::new();
|
let mut buff = String::new();
|
||||||
|
|
||||||
|
@ -103,9 +121,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
format!("cannot open {}: No such file or directory", file.quote()),
|
format!("cannot open {}: No such file or directory", file.quote()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if length > 1 {
|
|
||||||
buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap()));
|
|
||||||
}
|
|
||||||
let opened_file = match File::open(file) {
|
let opened_file = match File::open(file) {
|
||||||
Err(why) => {
|
Err(why) => {
|
||||||
terminal::disable_raw_mode().unwrap();
|
terminal::disable_raw_mode().unwrap();
|
||||||
|
@ -118,14 +133,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
};
|
};
|
||||||
let mut reader = BufReader::new(opened_file);
|
let mut reader = BufReader::new(opened_file);
|
||||||
reader.read_to_string(&mut buff).unwrap();
|
reader.read_to_string(&mut buff).unwrap();
|
||||||
more(&buff, &mut stdout, next_file.copied(), &options)?;
|
more(
|
||||||
|
&buff,
|
||||||
|
&mut stdout,
|
||||||
|
length > 1,
|
||||||
|
file.to_str(),
|
||||||
|
next_file.copied(),
|
||||||
|
&mut options,
|
||||||
|
)?;
|
||||||
buff.clear();
|
buff.clear();
|
||||||
}
|
}
|
||||||
reset_term(&mut stdout);
|
reset_term(&mut stdout);
|
||||||
} else if !std::io::stdin().is_terminal() {
|
} else if !std::io::stdin().is_terminal() {
|
||||||
stdin().read_to_string(&mut buff).unwrap();
|
stdin().read_to_string(&mut buff).unwrap();
|
||||||
let mut stdout = setup_term();
|
let mut stdout = setup_term();
|
||||||
more(&buff, &mut stdout, None, &options)?;
|
more(&buff, &mut stdout, false, None, None, &mut options)?;
|
||||||
reset_term(&mut stdout);
|
reset_term(&mut stdout);
|
||||||
} else {
|
} else {
|
||||||
return Err(UUsageError::new(1, "bad usage"));
|
return Err(UUsageError::new(1, "bad usage"));
|
||||||
|
@ -167,6 +189,38 @@ pub fn uu_app() -> Command {
|
||||||
.help("Squeeze multiple blank lines into one")
|
.help("Squeeze multiple blank lines into one")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::PLAIN)
|
||||||
|
.short('u')
|
||||||
|
.long(options::PLAIN)
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.hide(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::FROM_LINE)
|
||||||
|
.short('F')
|
||||||
|
.long(options::FROM_LINE)
|
||||||
|
.num_args(1)
|
||||||
|
.value_name("number")
|
||||||
|
.value_parser(value_parser!(usize))
|
||||||
|
.help("Display file beginning from line number"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::LINES)
|
||||||
|
.short('n')
|
||||||
|
.long(options::LINES)
|
||||||
|
.value_name("number")
|
||||||
|
.num_args(1)
|
||||||
|
.value_parser(value_parser!(u16).range(0..))
|
||||||
|
.help("The number of lines per screen full"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::NUMBER)
|
||||||
|
.long(options::NUMBER)
|
||||||
|
.num_args(1)
|
||||||
|
.value_parser(value_parser!(u16).range(0..))
|
||||||
|
.help("Same as --lines"),
|
||||||
|
)
|
||||||
// The commented arguments below are unimplemented:
|
// The commented arguments below are unimplemented:
|
||||||
/*
|
/*
|
||||||
.arg(
|
.arg(
|
||||||
|
@ -181,37 +235,6 @@ pub fn uu_app() -> Command {
|
||||||
.long(options::NO_PAUSE)
|
.long(options::NO_PAUSE)
|
||||||
.help("Suppress pause after form feed"),
|
.help("Suppress pause after form feed"),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::new(options::PLAIN)
|
|
||||||
.short('u')
|
|
||||||
.long(options::PLAIN)
|
|
||||||
.help("Suppress underlining and bold"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::LINES)
|
|
||||||
.short('n')
|
|
||||||
.long(options::LINES)
|
|
||||||
.value_name("number")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("The number of lines per screen full"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::NUMBER)
|
|
||||||
.allow_hyphen_values(true)
|
|
||||||
.long(options::NUMBER)
|
|
||||||
.required(false)
|
|
||||||
.takes_value(true)
|
|
||||||
.help("Same as --lines"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(options::FROM_LINE)
|
|
||||||
.short('F')
|
|
||||||
.allow_hyphen_values(true)
|
|
||||||
.required(false)
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("number")
|
|
||||||
.help("Display file beginning from line number"),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::PATTERN)
|
Arg::new(options::PATTERN)
|
||||||
.short('P')
|
.short('P')
|
||||||
|
@ -260,15 +283,36 @@ fn reset_term(_: &mut usize) {}
|
||||||
fn more(
|
fn more(
|
||||||
buff: &str,
|
buff: &str,
|
||||||
stdout: &mut Stdout,
|
stdout: &mut Stdout,
|
||||||
|
multiple_file: bool,
|
||||||
|
file: Option<&str>,
|
||||||
next_file: Option<&str>,
|
next_file: Option<&str>,
|
||||||
options: &Options,
|
options: &mut Options,
|
||||||
) -> UResult<()> {
|
) -> UResult<()> {
|
||||||
let (cols, rows) = terminal::size().unwrap();
|
let (cols, mut rows) = terminal::size().unwrap();
|
||||||
|
if let Some(number) = options.lines {
|
||||||
|
rows = number;
|
||||||
|
}
|
||||||
|
|
||||||
let lines = break_buff(buff, usize::from(cols));
|
let lines = break_buff(buff, usize::from(cols));
|
||||||
|
|
||||||
let mut pager = Pager::new(rows, lines, next_file, options);
|
let mut pager = Pager::new(rows, lines, next_file, options);
|
||||||
|
|
||||||
|
if multiple_file {
|
||||||
|
execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
|
||||||
|
stdout.write_all(
|
||||||
|
MULTI_FILE_TOP_PROMPT
|
||||||
|
.replace("{}", file.unwrap_or_default())
|
||||||
|
.as_bytes(),
|
||||||
|
)?;
|
||||||
|
pager.content_rows -= 3;
|
||||||
|
}
|
||||||
pager.draw(stdout, None);
|
pager.draw(stdout, None);
|
||||||
if pager.should_close() {
|
if multiple_file {
|
||||||
|
options.from_line = 0;
|
||||||
|
pager.content_rows += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if pager.should_close() && next_file.is_none() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,6 +371,7 @@ fn more(
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
pager.page_up();
|
pager.page_up();
|
||||||
|
paging_add_back_message(options, stdout)?;
|
||||||
}
|
}
|
||||||
Event::Key(KeyEvent {
|
Event::Key(KeyEvent {
|
||||||
code: KeyCode::Char('j'),
|
code: KeyCode::Char('j'),
|
||||||
|
@ -347,7 +392,7 @@ fn more(
|
||||||
pager.prev_line();
|
pager.prev_line();
|
||||||
}
|
}
|
||||||
Event::Resize(col, row) => {
|
Event::Resize(col, row) => {
|
||||||
pager.page_resize(col, row);
|
pager.page_resize(col, row, options.lines);
|
||||||
}
|
}
|
||||||
Event::Key(KeyEvent {
|
Event::Key(KeyEvent {
|
||||||
code: KeyCode::Char(k),
|
code: KeyCode::Char(k),
|
||||||
|
@ -388,7 +433,7 @@ impl<'a> Pager<'a> {
|
||||||
fn new(rows: u16, lines: Vec<String>, next_file: Option<&'a str>, options: &Options) -> Self {
|
fn new(rows: u16, lines: Vec<String>, next_file: Option<&'a str>, options: &Options) -> Self {
|
||||||
let line_count = lines.len();
|
let line_count = lines.len();
|
||||||
Self {
|
Self {
|
||||||
upper_mark: 0,
|
upper_mark: options.from_line,
|
||||||
content_rows: rows.saturating_sub(1),
|
content_rows: rows.saturating_sub(1),
|
||||||
lines,
|
lines,
|
||||||
next_file,
|
next_file,
|
||||||
|
@ -447,15 +492,17 @@ impl<'a> Pager<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Deal with column size changes.
|
// TODO: Deal with column size changes.
|
||||||
fn page_resize(&mut self, _: u16, row: u16) {
|
fn page_resize(&mut self, _: u16, row: u16, option_line: Option<u16>) {
|
||||||
|
if option_line.is_none() {
|
||||||
self.content_rows = row.saturating_sub(1);
|
self.content_rows = row.saturating_sub(1);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, stdout: &mut std::io::Stdout, wrong_key: Option<char>) {
|
fn draw(&mut self, stdout: &mut std::io::Stdout, wrong_key: Option<char>) {
|
||||||
|
self.draw_lines(stdout);
|
||||||
let lower_mark = self
|
let lower_mark = self
|
||||||
.line_count
|
.line_count
|
||||||
.min(self.upper_mark.saturating_add(self.content_rows.into()));
|
.min(self.upper_mark.saturating_add(self.content_rows.into()));
|
||||||
self.draw_lines(stdout);
|
|
||||||
self.draw_prompt(stdout, lower_mark, wrong_key);
|
self.draw_prompt(stdout, lower_mark, wrong_key);
|
||||||
stdout.flush().unwrap();
|
stdout.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -515,7 +562,6 @@ impl<'a> Pager<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let status = format!("--More--({status_inner})");
|
let status = format!("--More--({status_inner})");
|
||||||
|
|
||||||
let banner = match (self.silent, wrong_key) {
|
let banner = match (self.silent, wrong_key) {
|
||||||
(true, Some(key)) => format!(
|
(true, Some(key)) => format!(
|
||||||
"{status} [Unknown key: '{key}'. Press 'h' for instructions. (unimplemented)]"
|
"{status} [Unknown key: '{key}'. Press 'h' for instructions. (unimplemented)]"
|
||||||
|
@ -536,6 +582,14 @@ impl<'a> Pager<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn paging_add_back_message(options: &Options, stdout: &mut std::io::Stdout) -> UResult<()> {
|
||||||
|
if options.lines.is_some() {
|
||||||
|
execute!(stdout, MoveUp(1))?;
|
||||||
|
stdout.write_all("\n\r...back 1 page\n".as_bytes())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Break the lines on the cols of the terminal
|
// Break the lines on the cols of the terminal
|
||||||
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
|
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
|
||||||
let mut lines = Vec::with_capacity(buff.lines().count());
|
let mut lines = Vec::with_capacity(buff.lines().count());
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_mv"
|
name = "uu_mv"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "mv ~ (uutils) move (rename) SOURCE to DESTINATION"
|
description = "mv ~ (uutils) move (rename) SOURCE to DESTINATION"
|
||||||
|
|
|
@ -22,10 +22,10 @@ use std::os::unix;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::os::windows;
|
use std::os::windows;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use uucore::backup_control::{self, BackupMode};
|
use uucore::backup_control::{self, source_is_target_backup, BackupMode};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError};
|
use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError};
|
||||||
use uucore::fs::are_hardlinks_to_same_file;
|
use uucore::fs::{are_hardlinks_or_one_way_symlink_to_same_file, are_hardlinks_to_same_file};
|
||||||
use uucore::update_control::{self, UpdateMode};
|
use uucore::update_control::{self, UpdateMode};
|
||||||
use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show};
|
use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show};
|
||||||
|
|
||||||
|
@ -251,12 +251,25 @@ fn parse_paths(files: &[OsString], b: &Behavior) -> Vec<PathBuf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> {
|
fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> {
|
||||||
|
if b.backup == BackupMode::SimpleBackup && source_is_target_backup(source, target, &b.suffix) {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::NotFound,
|
||||||
|
format!(
|
||||||
|
"backing up {} might destroy source; {} not moved",
|
||||||
|
target.quote(),
|
||||||
|
source.quote()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
if source.symlink_metadata().is_err() {
|
if source.symlink_metadata().is_err() {
|
||||||
return Err(MvError::NoSuchFile(source.quote().to_string()).into());
|
return Err(MvError::NoSuchFile(source.quote().to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.eq(target) || are_hardlinks_to_same_file(source, target))
|
if (source.eq(target)
|
||||||
&& b.backup != BackupMode::SimpleBackup
|
|| are_hardlinks_to_same_file(source, target)
|
||||||
|
|| are_hardlinks_or_one_way_symlink_to_same_file(source, target))
|
||||||
|
&& b.backup == BackupMode::NoBackup
|
||||||
{
|
{
|
||||||
if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() {
|
if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() {
|
||||||
return Err(
|
return Err(
|
||||||
|
@ -387,7 +400,7 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> UR
|
||||||
}
|
}
|
||||||
|
|
||||||
match rename(sourcepath, &targetpath, b, multi_progress.as_ref()) {
|
match rename(sourcepath, &targetpath, b, multi_progress.as_ref()) {
|
||||||
Err(e) if e.to_string() == "" => set_exit_code(1),
|
Err(e) if e.to_string().is_empty() => set_exit_code(1),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let e = e.map_err_context(|| {
|
let e = e.map_err_context(|| {
|
||||||
format!(
|
format!(
|
||||||
|
@ -420,9 +433,7 @@ fn rename(
|
||||||
let mut backup_path = None;
|
let mut backup_path = None;
|
||||||
|
|
||||||
if to.exists() {
|
if to.exists() {
|
||||||
if (b.update == UpdateMode::ReplaceIfOlder || b.update == UpdateMode::ReplaceNone)
|
if b.update == UpdateMode::ReplaceIfOlder && b.overwrite == OverwriteMode::Interactive {
|
||||||
&& b.overwrite == OverwriteMode::Interactive
|
|
||||||
{
|
|
||||||
// `mv -i --update old new` when `new` exists doesn't move anything
|
// `mv -i --update old new` when `new` exists doesn't move anything
|
||||||
// and exit with 0
|
// and exit with 0
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_nice"
|
name = "uu_nice"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "nice ~ (uutils) run PROGRAM with modified scheduling priority"
|
description = "nice ~ (uutils) run PROGRAM with modified scheduling priority"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_nl"
|
name = "uu_nl"
|
||||||
version = "0.0.18"
|
version = "0.0.20"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "nl ~ (uutils) display input with added line numbers"
|
description = "nl ~ (uutils) display input with added line numbers"
|
||||||
|
|
|
@ -5,3 +5,19 @@ nl [OPTION]... [FILE]...
|
||||||
```
|
```
|
||||||
|
|
||||||
Number lines of files
|
Number lines of files
|
||||||
|
|
||||||
|
## After Help
|
||||||
|
|
||||||
|
`STYLE` is one of:
|
||||||
|
|
||||||
|
* `a` number all lines
|
||||||
|
* `t` number only nonempty lines
|
||||||
|
* `n` number no lines
|
||||||
|
* `pBRE` number only lines that contain a match for the basic regular
|
||||||
|
expression, `BRE`
|
||||||
|
|
||||||
|
`FORMAT` is one of:
|
||||||
|
|
||||||
|
* `ln` left justified, no leading zeros
|
||||||
|
* `rn` right justified, no leading zeros
|
||||||
|
* `rz` right justified, leading zeros
|
||||||
|
|
|
@ -5,17 +5,15 @@ use crate::options;
|
||||||
// parse_style parses a style string into a NumberingStyle.
|
// parse_style parses a style string into a NumberingStyle.
|
||||||
fn parse_style(chars: &[char]) -> Result<crate::NumberingStyle, String> {
|
fn parse_style(chars: &[char]) -> Result<crate::NumberingStyle, String> {
|
||||||
if chars.len() == 1 && chars[0] == 'a' {
|
if chars.len() == 1 && chars[0] == 'a' {
|
||||||
Ok(crate::NumberingStyle::NumberForAll)
|
Ok(crate::NumberingStyle::All)
|
||||||
} else if chars.len() == 1 && chars[0] == 't' {
|
} else if chars.len() == 1 && chars[0] == 't' {
|
||||||
Ok(crate::NumberingStyle::NumberForNonEmpty)
|
Ok(crate::NumberingStyle::NonEmpty)
|
||||||
} else if chars.len() == 1 && chars[0] == 'n' {
|
} else if chars.len() == 1 && chars[0] == 'n' {
|
||||||
Ok(crate::NumberingStyle::NumberForNone)
|
Ok(crate::NumberingStyle::None)
|
||||||
} else if chars.len() > 1 && chars[0] == 'p' {
|
} else if chars.len() > 1 && chars[0] == 'p' {
|
||||||
let s: String = chars[1..].iter().cloned().collect();
|
let s: String = chars[1..].iter().cloned().collect();
|
||||||
match regex::Regex::new(&s) {
|
match regex::Regex::new(&s) {
|
||||||
Ok(re) => Ok(crate::NumberingStyle::NumberForRegularExpression(Box::new(
|
Ok(re) => Ok(crate::NumberingStyle::Regex(Box::new(re))),
|
||||||
re,
|
|
||||||
))),
|
|
||||||
Err(_) => Err(String::from("Illegal regular expression")),
|
Err(_) => Err(String::from("Illegal regular expression")),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -29,30 +27,14 @@ fn parse_style(chars: &[char]) -> Result<crate::NumberingStyle, String> {
|
||||||
pub fn parse_options(settings: &mut crate::Settings, opts: &clap::ArgMatches) -> Vec<String> {
|
pub fn parse_options(settings: &mut crate::Settings, opts: &clap::ArgMatches) -> Vec<String> {
|
||||||
// This vector holds error messages encountered.
|
// This vector holds error messages encountered.
|
||||||
let mut errs: Vec<String> = vec![];
|
let mut errs: Vec<String> = vec![];
|
||||||
settings.renumber = !opts.contains_id(options::NO_RENUMBER);
|
settings.renumber = opts.get_flag(options::NO_RENUMBER);
|
||||||
match opts.get_one::<String>(options::NUMBER_SEPARATOR) {
|
if let Some(val) = opts.get_one::<String>(options::NUMBER_SEPARATOR) {
|
||||||
None => {}
|
|
||||||
Some(val) => {
|
|
||||||
settings.number_separator = val.to_owned();
|
settings.number_separator = val.to_owned();
|
||||||
}
|
}
|
||||||
}
|
settings.number_format = opts
|
||||||
match opts.get_one::<String>(options::NUMBER_FORMAT) {
|
.get_one::<String>(options::NUMBER_FORMAT)
|
||||||
None => {}
|
.map(Into::into)
|
||||||
Some(val) => match val.as_str() {
|
.unwrap_or_default();
|
||||||
"ln" => {
|
|
||||||
settings.number_format = crate::NumberFormat::Left;
|
|
||||||
}
|
|
||||||
"rn" => {
|
|
||||||
settings.number_format = crate::NumberFormat::Right;
|
|
||||||
}
|
|
||||||
"rz" => {
|
|
||||||
settings.number_format = crate::NumberFormat::RightZero;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
errs.push(String::from("Illegal value for -n"));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
match opts.get_one::<String>(options::BODY_NUMBERING) {
|
match opts.get_one::<String>(options::BODY_NUMBERING) {
|
||||||
None => {}
|
None => {}
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
|
@ -95,53 +77,25 @@ pub fn parse_options(settings: &mut crate::Settings, opts: &clap::ArgMatches) ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match opts.get_one::<String>(options::LINE_INCREMENT) {
|
match opts.get_one::<usize>(options::NUMBER_WIDTH) {
|
||||||
None => {}
|
None => {}
|
||||||
Some(val) => {
|
Some(num) if *num > 0 => settings.number_width = *num,
|
||||||
let conv: Option<u64> = val.parse().ok();
|
Some(_) => errs.push(String::from(
|
||||||
match conv {
|
"Invalid line number field width: ‘0’: Numerical result out of range",
|
||||||
None => {
|
)),
|
||||||
errs.push(String::from("Illegal value for -i"));
|
|
||||||
}
|
}
|
||||||
Some(num) => settings.line_increment = num,
|
match opts.get_one::<u64>(options::JOIN_BLANK_LINES) {
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match opts.get_one::<String>(options::NUMBER_WIDTH) {
|
|
||||||
None => {}
|
None => {}
|
||||||
Some(val) => {
|
Some(num) if *num > 0 => settings.join_blank_lines = *num,
|
||||||
let conv: Option<usize> = val.parse().ok();
|
Some(_) => errs.push(String::from(
|
||||||
match conv {
|
"Invalid line number of blank lines: ‘0’: Numerical result out of range",
|
||||||
None => {
|
)),
|
||||||
errs.push(String::from("Illegal value for -w"));
|
|
||||||
}
|
|
||||||
Some(num) => settings.number_width = num,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match opts.get_one::<String>(options::STARTING_LINE_NUMBER) {
|
|
||||||
None => {}
|
|
||||||
Some(val) => {
|
|
||||||
let conv: Option<u64> = val.parse().ok();
|
|
||||||
match conv {
|
|
||||||
None => {
|
|
||||||
errs.push(String::from("Illegal value for -v"));
|
|
||||||
}
|
|
||||||
Some(num) => settings.starting_line_number = num,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match opts.get_one::<String>(options::JOIN_BLANK_LINES) {
|
|
||||||
None => {}
|
|
||||||
Some(val) => {
|
|
||||||
let conv: Option<u64> = val.parse().ok();
|
|
||||||
match conv {
|
|
||||||
None => {
|
|
||||||
errs.push(String::from("Illegal value for -l"));
|
|
||||||
}
|
|
||||||
Some(num) => settings.join_blank_lines = num,
|
|
||||||
}
|
}
|
||||||
|
if let Some(num) = opts.get_one::<i64>(options::LINE_INCREMENT) {
|
||||||
|
settings.line_increment = *num;
|
||||||
}
|
}
|
||||||
|
if let Some(num) = opts.get_one::<i64>(options::STARTING_LINE_NUMBER) {
|
||||||
|
settings.starting_line_number = *num;
|
||||||
}
|
}
|
||||||
errs
|
errs
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue