mirror of
https://github.com/uutils/coreutils
synced 2024-12-16 08:12:39 +00:00
Merge branch 'master' into id_selinux_context
This commit is contained in:
commit
f383f36164
260 changed files with 7579 additions and 4976 deletions
237
.github/workflows/CICD.yml
vendored
237
.github/workflows/CICD.yml
vendored
|
@ -7,6 +7,8 @@ name: CICD
|
||||||
# spell-checker:ignore (shell/tools) choco clippy dmake dpkg esac fakeroot gmake grcov halium lcov libssl mkdir popd printf pushd rustc rustfmt rustup shopt xargs
|
# spell-checker:ignore (shell/tools) choco clippy dmake dpkg esac fakeroot gmake grcov halium lcov libssl mkdir popd printf pushd rustc rustfmt rustup shopt xargs
|
||||||
# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend runtest tempfile testsuite uutils
|
# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend runtest tempfile testsuite uutils
|
||||||
|
|
||||||
|
# ToDO: [2021-06; rivy] change from `cargo-tree` to `cargo tree` once MSRV is >= 1.45
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PROJECT_NAME: coreutils
|
PROJECT_NAME: coreutils
|
||||||
PROJECT_DESC: "Core universal (cross-platform) utilities"
|
PROJECT_DESC: "Core universal (cross-platform) utilities"
|
||||||
|
@ -17,6 +19,40 @@ env:
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
code_deps:
|
||||||
|
name: Style/dependencies
|
||||||
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Initialize workflow variables
|
||||||
|
id: vars
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## VARs setup
|
||||||
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||||
|
# target-specific options
|
||||||
|
# * CARGO_FEATURES_OPTION
|
||||||
|
CARGO_FEATURES_OPTION='' ;
|
||||||
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
||||||
|
outputs CARGO_FEATURES_OPTION
|
||||||
|
- name: Install `rust` toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
default: true
|
||||||
|
profile: minimal # minimal component installation (ie, no documentation)
|
||||||
|
- name: "`cargo update` testing"
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## `cargo update` testing
|
||||||
|
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
|
||||||
|
cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::'Cargo.lock' file requires update (use \`cargo +${{ env.RUST_MIN_SRV }} update\`)" ; exit 1 ; }
|
||||||
|
|
||||||
code_format:
|
code_format:
|
||||||
name: Style/format
|
name: Style/format
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
@ -26,13 +62,13 @@ jobs:
|
||||||
job:
|
job:
|
||||||
- { os: ubuntu-latest , features: feat_os_unix }
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
- name: Initialize workflow variables
|
- name: Initialize workflow variables
|
||||||
id: vars
|
id: vars
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## VARs setup
|
## VARs setup
|
||||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||||
# target-specific options
|
# target-specific options
|
||||||
# * CARGO_FEATURES_OPTION
|
# * CARGO_FEATURES_OPTION
|
||||||
CARGO_FEATURES_OPTION='' ;
|
CARGO_FEATURES_OPTION='' ;
|
||||||
|
@ -48,36 +84,19 @@ jobs:
|
||||||
- name: "`fmt` testing"
|
- name: "`fmt` testing"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# `fmt` testing
|
## `fmt` testing
|
||||||
# * 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 fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::warning file=\1,line=\2::WARNING: \`cargo fmt\`: style violation/p" ; }
|
S=$(cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::error file=\1,line=\2::ERROR: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt \"\1\"\`)/p" ; exit 1 ; }
|
||||||
- name: "`fmt` testing of tests"
|
- name: "`fmt` testing of tests"
|
||||||
|
if: success() || failure() # run regardless of prior step success/failure
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# `fmt` testing of tests
|
## `fmt` testing of tests
|
||||||
# * 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=$(find tests -name "*.rs" -print0 | xargs -0 cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" | sed -E -n "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::warning file=\1,line=\2::WARNING: \`cargo fmt\`: style violation/p" ; }
|
S=$(find tests -name "*.rs" -print0 | xargs -0 cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::error file=\1,line=\2::ERROR: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt \"\1\"\`)/p" ; exit 1 ; }
|
||||||
|
|
||||||
code_spellcheck:
|
code_lint:
|
||||||
name: Style/spelling
|
name: Style/lint
|
||||||
runs-on: ${{ matrix.job.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
job:
|
|
||||||
- { os: ubuntu-latest }
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- name: Install/setup prerequisites
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell -g;
|
|
||||||
- name: Run `cspell`
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cspell --config .vscode/cSpell.json --no-summary --no-progress "**/*" | sed "s/\(.*\):\(.*\):\(.*\) - \(.*\)/::warning file=\1,line=\2,col=\3::cspell: \4/" || true
|
|
||||||
|
|
||||||
code_warnings:
|
|
||||||
name: Style/warnings
|
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: ${{ matrix.job.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
@ -87,13 +106,13 @@ jobs:
|
||||||
- { os: macos-latest , features: feat_os_macos }
|
- { os: macos-latest , features: feat_os_macos }
|
||||||
- { os: windows-latest , features: feat_os_windows }
|
- { os: windows-latest , features: feat_os_windows }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
- name: Initialize workflow variables
|
- name: Initialize workflow variables
|
||||||
id: vars
|
id: vars
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## VARs setup
|
## VARs setup
|
||||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||||
# target-specific options
|
# target-specific options
|
||||||
# * CARGO_FEATURES_OPTION
|
# * CARGO_FEATURES_OPTION
|
||||||
CARGO_FEATURES_OPTION='' ;
|
CARGO_FEATURES_OPTION='' ;
|
||||||
|
@ -106,13 +125,32 @@ jobs:
|
||||||
default: true
|
default: true
|
||||||
profile: minimal # minimal component installation (ie, no documentation)
|
profile: minimal # minimal component installation (ie, no documentation)
|
||||||
components: clippy
|
components: clippy
|
||||||
- name: "`clippy` testing"
|
- name: "`clippy` lint testing"
|
||||||
if: success() || failure() # run regardless of prior step success/failure
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# `clippy` testing
|
## `clippy` lint testing
|
||||||
# * 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 +nightly clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -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]+).*$/::warning file=\2,line=\3,col=\4::WARNING: \`cargo clippy\`: \1/p;" -e '}' ; }
|
S=$(cargo +nightly clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -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:]]+${PWD//\//\\/}\/(.*):([0-9]+):([0-9]+).*$/::error file=\2,line=\3,col=\4::ERROR: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; exit 1 ; }
|
||||||
|
|
||||||
|
code_spellcheck:
|
||||||
|
name: Style/spelling
|
||||||
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { os: ubuntu-latest }
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install/setup prerequisites
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## Install/setup prerequisites
|
||||||
|
sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell -g ;
|
||||||
|
- name: Run `cspell`
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## Run `cspell`
|
||||||
|
cspell --config .vscode/cSpell.json --no-summary --no-progress "**/*" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::error file=\1,line=\2,col=\3::ERROR: \4 (file:'\1', line:\2)/p"
|
||||||
|
|
||||||
min_version:
|
min_version:
|
||||||
name: MinRustV # Minimum supported rust version
|
name: MinRustV # Minimum supported rust version
|
||||||
|
@ -122,7 +160,7 @@ jobs:
|
||||||
job:
|
job:
|
||||||
- { os: ubuntu-latest , features: feat_os_unix }
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
- name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }})
|
- name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }})
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
|
@ -137,33 +175,32 @@ jobs:
|
||||||
use-tool-cache: true
|
use-tool-cache: true
|
||||||
env:
|
env:
|
||||||
RUSTUP_TOOLCHAIN: stable
|
RUSTUP_TOOLCHAIN: stable
|
||||||
- name: Confirm compatible 'Cargo.lock'
|
- name: Confirm MinSRV compatible 'Cargo.lock'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# Confirm compatible 'Cargo.lock'
|
## Confirm MinSRV compatible 'Cargo.lock'
|
||||||
# * 'Cargo.lock' is required to be in a format that `cargo` of MinSRV can interpret (eg, v1-format for MinSRV < v1.38)
|
# * 'Cargo.lock' is required to be in a format that `cargo` of MinSRV can interpret (eg, v1-format for MinSRV < v1.38)
|
||||||
cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::Incompatible 'Cargo.lock' format; try \`cargo +${{ env.RUST_MIN_SRV }} update\`" ; exit 1 ; }
|
cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::Incompatible (or out-of-date) 'Cargo.lock' file; update using \`cargo +${{ env.RUST_MIN_SRV }} update\`" ; exit 1 ; }
|
||||||
- name: Info
|
- name: Info
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# Info
|
## Info
|
||||||
## environment
|
# environment
|
||||||
echo "## environment"
|
echo "## environment"
|
||||||
echo "CI='${CI}'"
|
echo "CI='${CI}'"
|
||||||
## tooling info display
|
# tooling info display
|
||||||
echo "## tooling"
|
echo "## tooling"
|
||||||
which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true
|
which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true
|
||||||
rustup -V
|
rustup -V 2>/dev/null
|
||||||
rustup show active-toolchain
|
rustup show active-toolchain
|
||||||
cargo -V
|
cargo -V
|
||||||
rustc -V
|
rustc -V
|
||||||
cargo-tree tree -V
|
cargo-tree tree -V
|
||||||
## dependencies
|
# dependencies
|
||||||
echo "## dependency list"
|
echo "## dependency list"
|
||||||
cargo fetch --locked --quiet
|
cargo fetch --locked --quiet
|
||||||
## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors
|
## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors
|
||||||
RUSTUP_TOOLCHAIN=stable cargo-tree tree --frozen --all --no-dev-dependencies --no-indent --features ${{ matrix.job.features }} | grep -vE "$PWD" | sort --unique
|
RUSTUP_TOOLCHAIN=stable cargo-tree tree --locked --all --no-dev-dependencies --no-indent --features ${{ matrix.job.features }} | grep -vE "$PWD" | sort --unique
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
|
@ -172,8 +209,8 @@ jobs:
|
||||||
env:
|
env:
|
||||||
RUSTFLAGS: '-Awarnings'
|
RUSTFLAGS: '-Awarnings'
|
||||||
|
|
||||||
busybox_test:
|
build_makefile:
|
||||||
name: Busybox test suite
|
name: Build/Makefile
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: ${{ matrix.job.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
@ -181,49 +218,26 @@ jobs:
|
||||||
job:
|
job:
|
||||||
- { os: ubuntu-latest }
|
- { os: ubuntu-latest }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
- name: Install `rust` toolchain
|
- name: Install `rust` toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
default: true
|
default: true
|
||||||
profile: minimal # minimal component installation (ie, no documentation)
|
profile: minimal # minimal component installation (ie, no documentation)
|
||||||
- name: "prepare busytest"
|
- name: Install/setup prerequisites
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
make prepare-busytest
|
## Install/setup prerequisites
|
||||||
- name: "run busybox testsuite"
|
sudo apt-get -y update ; sudo apt-get -y install python3-sphinx ;
|
||||||
|
- name: "`make build`"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
bindir=$(pwd)/target/debug
|
|
||||||
cd tmp/busybox-*/testsuite
|
|
||||||
## S=$(bindir=$bindir ./runtest) && printf "%s\n" "$S" || { printf "%s\n" "$S" | grep "FAIL:" | sed -e "s/FAIL: /::warning ::Test failure:/g" ; }
|
|
||||||
output=$(bindir=$bindir ./runtest 2>&1 || true)
|
|
||||||
printf "%s\n" "${output}"
|
|
||||||
n_fails=$(echo "$output" | grep "^FAIL:\s" | wc --lines)
|
|
||||||
if [ $n_fails -gt 0 ] ; then echo "::warning ::${n_fails}+ test failures" ; fi
|
|
||||||
|
|
||||||
makefile_build:
|
|
||||||
name: Test the build target of the Makefile
|
|
||||||
runs-on: ${{ matrix.job.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
job:
|
|
||||||
- { os: ubuntu-latest }
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- name: Install `rust` toolchain
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
default: true
|
|
||||||
profile: minimal # minimal component installation (ie, no documentation)
|
|
||||||
- name: "Run make build"
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sudo apt-get -y update ; sudo apt-get -y install python3-sphinx libselinux1 libselinux1-dev ;
|
|
||||||
make build
|
make build
|
||||||
|
- name: "`make test`"
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
make test
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
|
@ -235,8 +249,6 @@ jobs:
|
||||||
# { os, target, cargo-options, features, use-cross, toolchain }
|
# { os, target, cargo-options, features, use-cross, toolchain }
|
||||||
- { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf , features: feat_os_unix_gnueabihf , use-cross: use-cross }
|
- { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf , features: feat_os_unix_gnueabihf , use-cross: use-cross }
|
||||||
- { os: ubuntu-latest , target: aarch64-unknown-linux-gnu , features: feat_os_unix_gnueabihf , use-cross: use-cross }
|
- { os: ubuntu-latest , target: aarch64-unknown-linux-gnu , features: feat_os_unix_gnueabihf , use-cross: use-cross }
|
||||||
- { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
|
|
||||||
- { os: ubuntu-16.04 , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
|
|
||||||
# - { os: ubuntu-18.04 , target: i586-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } ## note: older windows platform; not required, dev-FYI only
|
# - { os: ubuntu-18.04 , target: i586-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } ## note: older windows platform; not required, dev-FYI only
|
||||||
# - { os: ubuntu-18.04 , target: i586-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } ## note: older windows platform; not required, dev-FYI only
|
# - { os: ubuntu-18.04 , target: i586-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } ## note: older windows platform; not required, dev-FYI only
|
||||||
- { os: ubuntu-18.04 , target: i686-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
|
- { os: ubuntu-18.04 , target: i686-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
|
||||||
|
@ -249,11 +261,11 @@ jobs:
|
||||||
- { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly
|
- { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly
|
||||||
- { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows }
|
- { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
- name: Install/setup prerequisites
|
- name: Install/setup prerequisites
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## install/setup prerequisites
|
## Install/setup prerequisites
|
||||||
case '${{ matrix.job.target }}' in
|
case '${{ matrix.job.target }}' in
|
||||||
arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
|
arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
|
||||||
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
|
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
|
||||||
|
@ -267,7 +279,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## VARs setup
|
## VARs setup
|
||||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||||
# toolchain
|
# toolchain
|
||||||
TOOLCHAIN="stable" ## default to "stable" toolchain
|
TOOLCHAIN="stable" ## default to "stable" toolchain
|
||||||
# * specify alternate/non-default TOOLCHAIN for *-pc-windows-gnu targets; gnu targets on Windows are broken for the standard *-pc-windows-msvc toolchain (refs: GH:rust-lang/rust#47048, GH:rust-lang/rust#53454, GH:rust-lang/cargo#6754)
|
# * specify alternate/non-default TOOLCHAIN for *-pc-windows-gnu targets; gnu targets on Windows are broken for the standard *-pc-windows-msvc toolchain (refs: GH:rust-lang/rust#47048, GH:rust-lang/rust#53454, GH:rust-lang/cargo#6754)
|
||||||
|
@ -353,7 +365,7 @@ jobs:
|
||||||
- name: Create all needed build/work directories
|
- name: Create all needed build/work directories
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## create build/work space
|
## Create build/work space
|
||||||
mkdir -p '${{ steps.vars.outputs.STAGING }}'
|
mkdir -p '${{ steps.vars.outputs.STAGING }}'
|
||||||
mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}'
|
mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}'
|
||||||
mkdir -p '${{ steps.vars.outputs.STAGING }}/dpkg'
|
mkdir -p '${{ steps.vars.outputs.STAGING }}/dpkg'
|
||||||
|
@ -373,7 +385,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## Dependent VARs setup
|
## Dependent VARs setup
|
||||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
outputs() { step_id="dep_vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||||
# * determine sub-crate utility list
|
# * determine sub-crate utility list
|
||||||
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
|
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
|
||||||
echo UTILITY_LIST=${UTILITY_LIST}
|
echo UTILITY_LIST=${UTILITY_LIST}
|
||||||
|
@ -390,26 +402,26 @@ jobs:
|
||||||
- name: Info
|
- name: Info
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# Info
|
## Info
|
||||||
## commit info
|
# commit info
|
||||||
echo "## commit"
|
echo "## commit"
|
||||||
echo GITHUB_REF=${GITHUB_REF}
|
echo GITHUB_REF=${GITHUB_REF}
|
||||||
echo GITHUB_SHA=${GITHUB_SHA}
|
echo GITHUB_SHA=${GITHUB_SHA}
|
||||||
## environment
|
# environment
|
||||||
echo "## environment"
|
echo "## environment"
|
||||||
echo "CI='${CI}'"
|
echo "CI='${CI}'"
|
||||||
## tooling info display
|
# tooling info display
|
||||||
echo "## tooling"
|
echo "## tooling"
|
||||||
which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true
|
which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true
|
||||||
rustup -V
|
rustup -V 2>/dev/null
|
||||||
rustup show active-toolchain
|
rustup show active-toolchain
|
||||||
cargo -V
|
cargo -V
|
||||||
rustc -V
|
rustc -V
|
||||||
cargo-tree tree -V
|
cargo-tree tree -V
|
||||||
## dependencies
|
# dependencies
|
||||||
echo "## dependency list"
|
echo "## dependency list"
|
||||||
cargo fetch --locked --quiet
|
cargo fetch --locked --quiet
|
||||||
cargo-tree tree --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --all --no-dev-dependencies --no-indent | grep -vE "$PWD" | sort --unique
|
cargo-tree tree --locked --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --all --no-dev-dependencies --no-indent | grep -vE "$PWD" | sort --unique
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
|
@ -436,7 +448,7 @@ jobs:
|
||||||
- name: Package
|
- name: Package
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## package artifact(s)
|
## Package artifact(s)
|
||||||
# binary
|
# binary
|
||||||
cp 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/'
|
cp 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/'
|
||||||
# `strip` binary (if needed)
|
# `strip` binary (if needed)
|
||||||
|
@ -477,6 +489,37 @@ jobs:
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
test_busybox:
|
||||||
|
name: Tests/BusyBox test suite
|
||||||
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { os: ubuntu-latest }
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install `rust` toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
default: true
|
||||||
|
profile: minimal # minimal component installation (ie, no documentation)
|
||||||
|
- name: Install/setup prerequisites
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
make prepare-busytest
|
||||||
|
- name: "Run BusyBox test suite"
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## Run BusyBox test suite
|
||||||
|
bindir=$(pwd)/target/debug
|
||||||
|
cd tmp/busybox-*/testsuite
|
||||||
|
output=$(bindir=$bindir ./runtest 2>&1 || true)
|
||||||
|
printf "%s\n" "${output}"
|
||||||
|
n_fails=$(echo "$output" | grep "^FAIL:\s" | wc --lines)
|
||||||
|
if [ $n_fails -gt 0 ] ; then echo "::warning ::${n_fails}+ test failures" ; fi
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
name: Code Coverage
|
name: Code Coverage
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
@ -489,11 +532,11 @@ jobs:
|
||||||
- { os: macos-latest , features: macos }
|
- { os: macos-latest , features: macos }
|
||||||
- { os: windows-latest , features: windows }
|
- { os: windows-latest , features: windows }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
- name: Install/setup prerequisites
|
- name: Install/setup prerequisites
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## install/setup prerequisites
|
## Install/setup prerequisites
|
||||||
case '${{ matrix.job.os }}' in
|
case '${{ matrix.job.os }}' in
|
||||||
macos-latest) brew install coreutils ;; # needed for testing
|
macos-latest) brew install coreutils ;; # needed for testing
|
||||||
esac
|
esac
|
||||||
|
@ -504,7 +547,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## VARs setup
|
## VARs setup
|
||||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||||
# toolchain
|
# toolchain
|
||||||
TOOLCHAIN="nightly-${{ env.RUST_COV_SRV }}" ## default to "nightly" toolchain (required for certain required unstable compiler flags) ## !maint: refactor when stable channel has needed support
|
TOOLCHAIN="nightly-${{ env.RUST_COV_SRV }}" ## default to "nightly" toolchain (required for certain required unstable compiler flags) ## !maint: refactor when stable channel has needed support
|
||||||
# * specify gnu-type TOOLCHAIN for windows; `grcov` requires gnu-style code coverage data files
|
# * specify gnu-type TOOLCHAIN for windows; `grcov` requires gnu-style code coverage data files
|
||||||
|
@ -539,7 +582,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
## Dependent VARs setup
|
## Dependent VARs setup
|
||||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
outputs() { step_id="dep_vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||||
# * determine sub-crate utility list
|
# * determine sub-crate utility list
|
||||||
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
|
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
|
||||||
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)"
|
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)"
|
||||||
|
@ -587,7 +630,7 @@ jobs:
|
||||||
id: coverage
|
id: coverage
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# generate coverage data
|
## Generate coverage data
|
||||||
COVERAGE_REPORT_DIR="target/debug"
|
COVERAGE_REPORT_DIR="target/debug"
|
||||||
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
|
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
|
||||||
# GRCOV_IGNORE_OPTION='--ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*"' ## `grcov` ignores these params when passed as an environment variable (why?)
|
# GRCOV_IGNORE_OPTION='--ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*"' ## `grcov` ignores these params when passed as an environment variable (why?)
|
||||||
|
|
135
.github/workflows/FixPR.yml
vendored
Normal file
135
.github/workflows/FixPR.yml
vendored
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
name: FixPR
|
||||||
|
|
||||||
|
# Trigger automated fixes for PRs being merged (with associated commits)
|
||||||
|
|
||||||
|
# ToDO: [2021-06; rivy] change from `cargo-tree` to `cargo tree` once MSRV is >= 1.45
|
||||||
|
|
||||||
|
env:
|
||||||
|
BRANCH_TARGET: master
|
||||||
|
|
||||||
|
on:
|
||||||
|
# * only trigger on pull request closed to specific branches
|
||||||
|
# ref: https://github.community/t/trigger-workflow-only-on-pull-request-merge/17359/9
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master # == env.BRANCH_TARGET ## unfortunately, env context variables are only available in jobs/steps (see <https://github.community/t/how-to-use-env-context/16975/2>)
|
||||||
|
types: [ closed ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
code_deps:
|
||||||
|
# Refresh dependencies (ie, 'Cargo.lock') and show updated dependency tree
|
||||||
|
if: github.event.pull_request.merged == true ## only for PR merges
|
||||||
|
name: Update/dependencies
|
||||||
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Initialize job variables
|
||||||
|
id: vars
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## VARs setup
|
||||||
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||||
|
# 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)" )
|
||||||
|
outputs RUST_MIN_SRV
|
||||||
|
- name: Install `rust` toolchain (v${{ steps.vars.outputs.RUST_MIN_SRV }})
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ steps.vars.outputs.RUST_MIN_SRV }}
|
||||||
|
default: true
|
||||||
|
profile: minimal # minimal component installation (ie, no documentation)
|
||||||
|
- name: Install `cargo-tree` # for dependency information
|
||||||
|
uses: actions-rs/install@v0.1
|
||||||
|
with:
|
||||||
|
crate: cargo-tree
|
||||||
|
version: latest
|
||||||
|
use-tool-cache: true
|
||||||
|
env:
|
||||||
|
RUSTUP_TOOLCHAIN: stable
|
||||||
|
- name: Ensure updated 'Cargo.lock'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# Ensure updated 'Cargo.lock'
|
||||||
|
# * 'Cargo.lock' is required to be in a format that `cargo` of MinSRV can interpret (eg, v1-format for MinSRV < v1.38)
|
||||||
|
cargo fetch --locked --quiet || cargo +${{ steps.vars.outputs.RUST_MIN_SRV }} update
|
||||||
|
- name: Info
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# Info
|
||||||
|
## environment
|
||||||
|
echo "## environment"
|
||||||
|
echo "CI='${CI}'"
|
||||||
|
## tooling info display
|
||||||
|
echo "## tooling"
|
||||||
|
which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true
|
||||||
|
rustup -V 2>/dev/null
|
||||||
|
rustup show active-toolchain
|
||||||
|
cargo -V
|
||||||
|
rustc -V
|
||||||
|
cargo-tree tree -V
|
||||||
|
## dependencies
|
||||||
|
echo "## dependency list"
|
||||||
|
cargo fetch --locked --quiet
|
||||||
|
## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors
|
||||||
|
RUSTUP_TOOLCHAIN=stable cargo-tree tree --locked --all --no-dev-dependencies --no-indent --features ${{ matrix.job.features }} | grep -vE "$PWD" | sort --unique
|
||||||
|
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
||||||
|
uses: EndBug/add-and-commit@v7
|
||||||
|
with:
|
||||||
|
branch: ${{ env.BRANCH_TARGET }}
|
||||||
|
default_author: github_actions
|
||||||
|
message: "maint ~ refresh 'Cargo.lock'"
|
||||||
|
add: Cargo.lock
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
code_format:
|
||||||
|
# Recheck/refresh code formatting
|
||||||
|
if: github.event.pull_request.merged == true ## only for PR merges
|
||||||
|
name: Update/format
|
||||||
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Initialize job variables
|
||||||
|
id: vars
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## VARs setup
|
||||||
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||||
|
# target-specific options
|
||||||
|
# * CARGO_FEATURES_OPTION
|
||||||
|
CARGO_FEATURES_OPTION='' ;
|
||||||
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
||||||
|
outputs CARGO_FEATURES_OPTION
|
||||||
|
- name: Install `rust` toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
default: true
|
||||||
|
profile: minimal # minimal component installation (ie, no documentation)
|
||||||
|
components: rustfmt
|
||||||
|
- name: "`cargo fmt`"
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cargo fmt
|
||||||
|
- name: "`cargo fmt` tests"
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# `cargo fmt` of tests
|
||||||
|
find tests -name "*.rs" -print0 | xargs -0 cargo fmt --
|
||||||
|
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
||||||
|
uses: EndBug/add-and-commit@v7
|
||||||
|
with:
|
||||||
|
branch: ${{ env.BRANCH_TARGET }}
|
||||||
|
default_author: github_actions
|
||||||
|
message: "maint ~ rustfmt (`cargo fmt`)"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -1,4 +1,6 @@
|
||||||
name: GNU
|
name: GnuTests
|
||||||
|
|
||||||
|
# spell-checker:ignore (names) gnulib ; (utils) autopoint gperf pyinotify texinfo ; (vars) XPASS
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
@ -7,7 +9,6 @@ jobs:
|
||||||
name: Run GNU tests
|
name: Run GNU tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
# Checks out a copy of your repository on the ubuntu-latest machine
|
|
||||||
- name: Checkout code uutil
|
- name: Checkout code uutil
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
|
@ -18,7 +19,7 @@ jobs:
|
||||||
repository: 'coreutils/coreutils'
|
repository: 'coreutils/coreutils'
|
||||||
path: 'gnu'
|
path: 'gnu'
|
||||||
ref: v8.32
|
ref: v8.32
|
||||||
- name: Checkout GNU corelib
|
- name: Checkout GNU coreutils library (gnulib)
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: 'coreutils/gnulib'
|
repository: 'coreutils/gnulib'
|
||||||
|
@ -32,14 +33,16 @@ jobs:
|
||||||
default: true
|
default: true
|
||||||
profile: minimal # minimal component installation (ie, no documentation)
|
profile: minimal # minimal component installation (ie, no documentation)
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
- name: Install deps
|
- name: Install dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
## Install dependencies
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install autoconf autopoint bison texinfo gperf gcc g++ gdb python-pyinotify python3-sphinx jq
|
sudo apt-get install autoconf autopoint bison texinfo gperf gcc g++ gdb python-pyinotify python3-sphinx jq
|
||||||
- name: Build binaries
|
- name: Build binaries
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
## Build binaries
|
||||||
cd uutils
|
cd uutils
|
||||||
bash util/build-gnu.sh
|
bash util/build-gnu.sh
|
||||||
- name: Run GNU tests
|
- name: Run GNU tests
|
||||||
|
@ -53,9 +56,10 @@ jobs:
|
||||||
bash uutils/util/run-gnu-test.sh tests/id/setgid.sh
|
bash uutils/util/run-gnu-test.sh tests/id/setgid.sh
|
||||||
bash uutils/util/run-gnu-test.sh tests/id/zero.sh
|
bash uutils/util/run-gnu-test.sh tests/id/zero.sh
|
||||||
bash uutils/util/run-gnu-test.sh tests/id/gnu-zero-uids.sh
|
bash uutils/util/run-gnu-test.sh tests/id/gnu-zero-uids.sh
|
||||||
- name: Extract tests info
|
- name: Extract testing info
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
## Extract testing info
|
||||||
LOG_FILE=gnu/tests/test-suite.log
|
LOG_FILE=gnu/tests/test-suite.log
|
||||||
if test -f "$LOG_FILE"
|
if test -f "$LOG_FILE"
|
||||||
then
|
then
|
||||||
|
@ -65,7 +69,13 @@ jobs:
|
||||||
FAIL=$(sed -n "s/.*# FAIL: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
FAIL=$(sed -n "s/.*# FAIL: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
||||||
XPASS=$(sed -n "s/.*# XPASS: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
XPASS=$(sed -n "s/.*# XPASS: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
||||||
ERROR=$(sed -n "s/.*# ERROR: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
ERROR=$(sed -n "s/.*# ERROR: \(.*\)/\1/p" "$LOG_FILE"|tr -d '\r'|head -n1)
|
||||||
echo "::warning ::GNU testsuite = TOTAL: $TOTAL / PASS: $PASS / FAIL: $FAIL / ERROR: $ERROR"
|
if [[ "$TOTAL" -eq 0 || "$TOTAL" -eq 1 ]]; then
|
||||||
|
echo "Error in the execution, failing early"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
output="GNU tests summary = TOTAL: $TOTAL / PASS: $PASS / FAIL: $FAIL / ERROR: $ERROR"
|
||||||
|
echo "${output}"
|
||||||
|
if [[ "$FAIL" -gt 0 || "$ERROR" -gt 0 ]]; then echo "::warning ::${output}" ; fi
|
||||||
jq -n \
|
jq -n \
|
||||||
--arg date "$(date --rfc-email)" \
|
--arg date "$(date --rfc-email)" \
|
||||||
--arg sha "$GITHUB_SHA" \
|
--arg sha "$GITHUB_SHA" \
|
||||||
|
@ -79,12 +89,10 @@ jobs:
|
||||||
else
|
else
|
||||||
echo "::error ::Failed to get summary of test results"
|
echo "::error ::Failed to get summary of test results"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: test-report
|
name: test-report
|
||||||
path: gnu/tests/**/*.log
|
path: gnu/tests/**/*.log
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: gnu-result
|
name: gnu-result
|
|
@ -12,6 +12,7 @@ FIFOs
|
||||||
FQDN # fully qualified domain name
|
FQDN # fully qualified domain name
|
||||||
GID # group ID
|
GID # group ID
|
||||||
GIDs
|
GIDs
|
||||||
|
GNU
|
||||||
GNUEABI
|
GNUEABI
|
||||||
GNUEABIhf
|
GNUEABIhf
|
||||||
JFS
|
JFS
|
||||||
|
@ -45,6 +46,7 @@ Deno
|
||||||
EditorConfig
|
EditorConfig
|
||||||
FreeBSD
|
FreeBSD
|
||||||
Gmail
|
Gmail
|
||||||
|
GNU
|
||||||
Irix
|
Irix
|
||||||
MS-DOS
|
MS-DOS
|
||||||
MSDOS
|
MSDOS
|
||||||
|
|
|
@ -78,6 +78,7 @@ symlinks
|
||||||
syscall
|
syscall
|
||||||
syscalls
|
syscalls
|
||||||
tokenize
|
tokenize
|
||||||
|
toolchain
|
||||||
truthy
|
truthy
|
||||||
unbuffered
|
unbuffered
|
||||||
unescape
|
unescape
|
||||||
|
|
|
@ -58,6 +58,10 @@ Haitao Li
|
||||||
Inokentiy Babushkin
|
Inokentiy Babushkin
|
||||||
Inokentiy
|
Inokentiy
|
||||||
Babushkin
|
Babushkin
|
||||||
|
Jan Scheer * jhscheer
|
||||||
|
Jan
|
||||||
|
Scheer
|
||||||
|
jhscheer
|
||||||
Jeremiah Peschka
|
Jeremiah Peschka
|
||||||
Jeremiah
|
Jeremiah
|
||||||
Peschka
|
Peschka
|
||||||
|
|
|
@ -48,17 +48,19 @@ xattr
|
||||||
# * rust/rustc
|
# * rust/rustc
|
||||||
RUSTDOCFLAGS
|
RUSTDOCFLAGS
|
||||||
RUSTFLAGS
|
RUSTFLAGS
|
||||||
|
clippy
|
||||||
|
rustc
|
||||||
|
rustfmt
|
||||||
|
rustup
|
||||||
|
#
|
||||||
bitor # BitOr trait function
|
bitor # BitOr trait function
|
||||||
bitxor # BitXor trait function
|
bitxor # BitXor trait function
|
||||||
clippy
|
|
||||||
concat
|
concat
|
||||||
fract
|
fract
|
||||||
powi
|
powi
|
||||||
println
|
println
|
||||||
repr
|
repr
|
||||||
rfind
|
rfind
|
||||||
rustc
|
|
||||||
rustfmt
|
|
||||||
struct
|
struct
|
||||||
structs
|
structs
|
||||||
substr
|
substr
|
||||||
|
|
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -220,6 +220,7 @@ dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"strsim",
|
"strsim",
|
||||||
|
"term_size",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"vec_map",
|
"vec_map",
|
||||||
|
@ -261,6 +262,7 @@ version = "0.0.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"clap",
|
||||||
"conv",
|
"conv",
|
||||||
"filetime",
|
"filetime",
|
||||||
"glob 0.3.0",
|
"glob 0.3.0",
|
||||||
|
@ -1566,21 +1568,6 @@ dependencies = [
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
|
||||||
dependencies = [
|
|
||||||
"semver-parser",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver-parser"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -2107,6 +2094,7 @@ dependencies = [
|
||||||
name = "uu_expr"
|
name = "uu_expr"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
"libc",
|
"libc",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
@ -2134,6 +2122,7 @@ dependencies = [
|
||||||
name = "uu_false"
|
name = "uu_false"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
"uucore",
|
"uucore",
|
||||||
"uucore_procs",
|
"uucore_procs",
|
||||||
]
|
]
|
||||||
|
@ -2199,6 +2188,7 @@ dependencies = [
|
||||||
name = "uu_hostid"
|
name = "uu_hostid"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
"libc",
|
"libc",
|
||||||
"uucore",
|
"uucore",
|
||||||
"uucore_procs",
|
"uucore_procs",
|
||||||
|
@ -2476,6 +2466,7 @@ name = "uu_pr"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"clap",
|
||||||
"getopts",
|
"getopts",
|
||||||
"itertools 0.10.0",
|
"itertools 0.10.0",
|
||||||
"quick-error 2.0.1",
|
"quick-error 2.0.1",
|
||||||
|
@ -2498,6 +2489,7 @@ dependencies = [
|
||||||
name = "uu_printf"
|
name = "uu_printf"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
"itertools 0.8.2",
|
"itertools 0.8.2",
|
||||||
"uucore",
|
"uucore",
|
||||||
"uucore_procs",
|
"uucore_procs",
|
||||||
|
@ -2631,7 +2623,6 @@ dependencies = [
|
||||||
"ouroboros",
|
"ouroboros",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"rayon",
|
"rayon",
|
||||||
"semver",
|
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"uucore",
|
"uucore",
|
||||||
|
@ -2734,6 +2725,7 @@ dependencies = [
|
||||||
name = "uu_test"
|
name = "uu_test"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.1.57",
|
"redox_syscall 0.1.57",
|
||||||
"uucore",
|
"uucore",
|
||||||
|
@ -2777,6 +2769,7 @@ dependencies = [
|
||||||
name = "uu_true"
|
name = "uu_true"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
"uucore",
|
"uucore",
|
||||||
"uucore_procs",
|
"uucore_procs",
|
||||||
]
|
]
|
||||||
|
|
|
@ -63,6 +63,7 @@ feat_common_core = [
|
||||||
"more",
|
"more",
|
||||||
"mv",
|
"mv",
|
||||||
"nl",
|
"nl",
|
||||||
|
"numfmt",
|
||||||
"od",
|
"od",
|
||||||
"paste",
|
"paste",
|
||||||
"pr",
|
"pr",
|
||||||
|
@ -160,7 +161,6 @@ feat_require_unix = [
|
||||||
"mkfifo",
|
"mkfifo",
|
||||||
"mknod",
|
"mknod",
|
||||||
"nice",
|
"nice",
|
||||||
"numfmt",
|
|
||||||
"nohup",
|
"nohup",
|
||||||
"pathchk",
|
"pathchk",
|
||||||
"stat",
|
"stat",
|
||||||
|
@ -225,6 +225,7 @@ test = [ "uu_test" ]
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
lazy_static = { version="1.3" }
|
lazy_static = { version="1.3" }
|
||||||
textwrap = { version="=0.11.0", features=["term_size"] } # !maint: [2020-05-10; rivy] unstable crate using undocumented features; pinned currently, will review
|
textwrap = { version="=0.11.0", features=["term_size"] } # !maint: [2020-05-10; rivy] unstable crate using undocumented features; pinned currently, will review
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="src/uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="src/uucore" }
|
||||||
|
|
|
@ -314,6 +314,11 @@ else
|
||||||
endif
|
endif
|
||||||
$(foreach man, $(filter $(INSTALLEES), $(basename $(notdir $(wildcard $(DOCSDIR)/_build/man/*)))), \
|
$(foreach man, $(filter $(INSTALLEES), $(basename $(notdir $(wildcard $(DOCSDIR)/_build/man/*)))), \
|
||||||
cat $(DOCSDIR)/_build/man/$(man).1 | gzip > $(INSTALLDIR_MAN)/$(PROG_PREFIX)$(man).1.gz &&) :
|
cat $(DOCSDIR)/_build/man/$(man).1 | gzip > $(INSTALLDIR_MAN)/$(PROG_PREFIX)$(man).1.gz &&) :
|
||||||
|
$(foreach prog, $(INSTALLEES), \
|
||||||
|
$(BUILDDIR)/coreutils completion $(prog) zsh > $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(PROG_PREFIX)$(prog); \
|
||||||
|
$(BUILDDIR)/coreutils completion $(prog) bash > $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(PROG_PREFIX)$(prog); \
|
||||||
|
$(BUILDDIR)/coreutils completion $(prog) fish > $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/$(PROG_PREFIX)$(prog).fish; \
|
||||||
|
)
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
ifeq (${MULTICALL}, y)
|
ifeq (${MULTICALL}, y)
|
||||||
|
@ -321,6 +326,9 @@ ifeq (${MULTICALL}, y)
|
||||||
endif
|
endif
|
||||||
rm -f $(addprefix $(INSTALLDIR_MAN)/,$(PROG_PREFIX)coreutils.1.gz)
|
rm -f $(addprefix $(INSTALLDIR_MAN)/,$(PROG_PREFIX)coreutils.1.gz)
|
||||||
rm -f $(addprefix $(INSTALLDIR_BIN)/$(PROG_PREFIX),$(PROGS))
|
rm -f $(addprefix $(INSTALLDIR_BIN)/$(PROG_PREFIX),$(PROGS))
|
||||||
|
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(PROG_PREFIX),$(PROGS))
|
||||||
|
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(PROG_PREFIX),$(PROGS))
|
||||||
|
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/$(PROG_PREFIX),$(addsuffix .fish,$(PROGS)))
|
||||||
rm -f $(addprefix $(INSTALLDIR_MAN)/$(PROG_PREFIX),$(addsuffix .1.gz,$(PROGS)))
|
rm -f $(addprefix $(INSTALLDIR_MAN)/$(PROG_PREFIX),$(addsuffix .1.gz,$(PROGS)))
|
||||||
|
|
||||||
.PHONY: all build build-coreutils build-pkgs build-docs test distclean clean busytest install uninstall
|
.PHONY: all build build-coreutils build-pkgs build-docs test distclean clean busytest install uninstall
|
||||||
|
|
24
README.md
24
README.md
|
@ -134,6 +134,9 @@ $ cargo install --path .
|
||||||
|
|
||||||
This command will install uutils into Cargo's *bin* folder (*e.g.* `$HOME/.cargo/bin`).
|
This command will install uutils into Cargo's *bin* folder (*e.g.* `$HOME/.cargo/bin`).
|
||||||
|
|
||||||
|
This does not install files necessary for shell completion. For shell completion to work,
|
||||||
|
use `GNU Make` or see `Manually install shell completions`.
|
||||||
|
|
||||||
### GNU Make
|
### GNU Make
|
||||||
|
|
||||||
To install all available utilities:
|
To install all available utilities:
|
||||||
|
@ -179,6 +182,10 @@ Set install parent directory (default value is /usr/local):
|
||||||
$ make PREFIX=/my/path install
|
$ make PREFIX=/my/path install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Installing with `make` installs shell completions for all installed utilities
|
||||||
|
for `bash`, `fish` and `zsh`. Completions for `elvish` and `powershell` can also
|
||||||
|
be generated; See `Manually install shell completions`.
|
||||||
|
|
||||||
### NixOS
|
### NixOS
|
||||||
|
|
||||||
The [standard package set](https://nixos.org/nixpkgs/manual/) of [NixOS](https://nixos.org/)
|
The [standard package set](https://nixos.org/nixpkgs/manual/) of [NixOS](https://nixos.org/)
|
||||||
|
@ -188,6 +195,23 @@ provides this package out of the box since 18.03:
|
||||||
$ nix-env -iA nixos.uutils-coreutils
|
$ nix-env -iA nixos.uutils-coreutils
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Manually install shell completions
|
||||||
|
|
||||||
|
The `coreutils` binary can generate completions for the `bash`, `elvish`, `fish`, `powershell`
|
||||||
|
and `zsh` shells. It prints the result to stdout.
|
||||||
|
|
||||||
|
The syntax is:
|
||||||
|
```bash
|
||||||
|
cargo run completion <utility> <shell>
|
||||||
|
```
|
||||||
|
|
||||||
|
So, to install completions for `ls` on `bash` to `/usr/local/share/bash-completion/completions/ls`,
|
||||||
|
run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run completion ls bash > /usr/local/share/bash-completion/completions/ls
|
||||||
|
```
|
||||||
|
|
||||||
## Un-installation Instructions
|
## Un-installation Instructions
|
||||||
|
|
||||||
Un-installation differs depending on how you have installed uutils. If you used
|
Un-installation differs depending on how you have installed uutils. If you used
|
||||||
|
|
59
build.rs
59
build.rs
|
@ -43,7 +43,7 @@ pub fn main() {
|
||||||
let mut tf = File::create(Path::new(&out_dir).join("test_modules.rs")).unwrap();
|
let mut tf = File::create(Path::new(&out_dir).join("test_modules.rs")).unwrap();
|
||||||
|
|
||||||
mf.write_all(
|
mf.write_all(
|
||||||
"type UtilityMap<T> = HashMap<&'static str, fn(T) -> i32>;\n\
|
"type UtilityMap<T> = HashMap<&'static str, (fn(T) -> i32, fn() -> App<'static, 'static>)>;\n\
|
||||||
\n\
|
\n\
|
||||||
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n\
|
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n\
|
||||||
\tlet mut map = UtilityMap::new();\n\
|
\tlet mut map = UtilityMap::new();\n\
|
||||||
|
@ -54,10 +54,33 @@ pub fn main() {
|
||||||
|
|
||||||
for krate in crates {
|
for krate in crates {
|
||||||
match krate.as_ref() {
|
match krate.as_ref() {
|
||||||
|
// 'test' is named uu_test to avoid collision with rust core crate 'test'.
|
||||||
|
// It can also be invoked by name '[' for the '[ expr ] syntax'.
|
||||||
|
"uu_test" => {
|
||||||
|
mf.write_all(
|
||||||
|
format!(
|
||||||
|
"\
|
||||||
|
\tmap.insert(\"test\", ({krate}::uumain, {krate}::uu_app));\n\
|
||||||
|
\t\tmap.insert(\"[\", ({krate}::uumain, {krate}::uu_app));\n\
|
||||||
|
",
|
||||||
|
krate = krate
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tf.write_all(
|
||||||
|
format!(
|
||||||
|
"#[path=\"{dir}/test_test.rs\"]\nmod test_test;\n",
|
||||||
|
dir = util_tests_dir,
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
k if k.starts_with(override_prefix) => {
|
k if k.starts_with(override_prefix) => {
|
||||||
mf.write_all(
|
mf.write_all(
|
||||||
format!(
|
format!(
|
||||||
"\tmap.insert(\"{k}\", {krate}::uumain);\n",
|
"\tmap.insert(\"{k}\", ({krate}::uumain, {krate}::uu_app));\n",
|
||||||
k = krate[override_prefix.len()..].to_string(),
|
k = krate[override_prefix.len()..].to_string(),
|
||||||
krate = krate
|
krate = krate
|
||||||
)
|
)
|
||||||
|
@ -77,7 +100,7 @@ pub fn main() {
|
||||||
"false" | "true" => {
|
"false" | "true" => {
|
||||||
mf.write_all(
|
mf.write_all(
|
||||||
format!(
|
format!(
|
||||||
"\tmap.insert(\"{krate}\", r#{krate}::uumain);\n",
|
"\tmap.insert(\"{krate}\", (r#{krate}::uumain, r#{krate}::uu_app));\n",
|
||||||
krate = krate
|
krate = krate
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
|
@ -97,20 +120,20 @@ pub fn main() {
|
||||||
mf.write_all(
|
mf.write_all(
|
||||||
format!(
|
format!(
|
||||||
"\
|
"\
|
||||||
\tmap.insert(\"{krate}\", {krate}::uumain);\n\
|
\tmap.insert(\"{krate}\", ({krate}::uumain, {krate}::uu_app_custom));\n\
|
||||||
\t\tmap.insert(\"md5sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"md5sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"sha1sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"sha1sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"sha224sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"sha224sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"sha256sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"sha256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"sha384sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"sha384sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"sha512sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"sha512sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"sha3sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"sha3sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"sha3-224sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"sha3-224sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"sha3-256sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"sha3-256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"sha3-384sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"sha3-384sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"sha3-512sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"sha3-512sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"shake128sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"shake128sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
\t\tmap.insert(\"shake256sum\", {krate}::uumain);\n\
|
\t\tmap.insert(\"shake256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||||
",
|
",
|
||||||
krate = krate
|
krate = krate
|
||||||
)
|
)
|
||||||
|
@ -130,7 +153,7 @@ pub fn main() {
|
||||||
_ => {
|
_ => {
|
||||||
mf.write_all(
|
mf.write_all(
|
||||||
format!(
|
format!(
|
||||||
"\tmap.insert(\"{krate}\", {krate}::uumain);\n",
|
"\tmap.insert(\"{krate}\", ({krate}::uumain, {krate}::uu_app));\n",
|
||||||
krate = krate
|
krate = krate
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
|
|
|
@ -16,7 +16,7 @@ Synopsis
|
||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
``uutils`` is a program that contains that other coreutils commands, somewhat
|
``uutils`` is a program that contains other coreutils commands, somewhat
|
||||||
similar to Busybox.
|
similar to Busybox.
|
||||||
|
|
||||||
--help, -h print a help menu for PROGRAM displaying accepted options and
|
--help, -h print a help menu for PROGRAM displaying accepted options and
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
// 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.
|
||||||
|
|
||||||
|
use clap::App;
|
||||||
|
use clap::Arg;
|
||||||
|
use clap::Shell;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::hash_map::HashMap;
|
use std::collections::hash_map::HashMap;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
@ -52,7 +55,7 @@ fn main() {
|
||||||
let binary_as_util = name(&binary);
|
let binary_as_util = name(&binary);
|
||||||
|
|
||||||
// binary name equals util name?
|
// binary name equals util name?
|
||||||
if let Some(&uumain) = utils.get(binary_as_util) {
|
if let Some(&(uumain, _)) = utils.get(binary_as_util) {
|
||||||
process::exit(uumain((vec![binary.into()].into_iter()).chain(args)));
|
process::exit(uumain((vec![binary.into()].into_iter()).chain(args)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +77,12 @@ fn main() {
|
||||||
if let Some(util_os) = util_name {
|
if let Some(util_os) = util_name {
|
||||||
let util = util_os.as_os_str().to_string_lossy();
|
let util = util_os.as_os_str().to_string_lossy();
|
||||||
|
|
||||||
|
if util == "completion" {
|
||||||
|
gen_completions(args, utils);
|
||||||
|
}
|
||||||
|
|
||||||
match utils.get(&util[..]) {
|
match utils.get(&util[..]) {
|
||||||
Some(&uumain) => {
|
Some(&(uumain, _)) => {
|
||||||
process::exit(uumain((vec![util_os].into_iter()).chain(args)));
|
process::exit(uumain((vec![util_os].into_iter()).chain(args)));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -85,7 +92,7 @@ fn main() {
|
||||||
let util = util_os.as_os_str().to_string_lossy();
|
let util = util_os.as_os_str().to_string_lossy();
|
||||||
|
|
||||||
match utils.get(&util[..]) {
|
match utils.get(&util[..]) {
|
||||||
Some(&uumain) => {
|
Some(&(uumain, _)) => {
|
||||||
let code = uumain(
|
let code = uumain(
|
||||||
(vec![util_os, OsString::from("--help")].into_iter())
|
(vec![util_os, OsString::from("--help")].into_iter())
|
||||||
.chain(args),
|
.chain(args),
|
||||||
|
@ -113,3 +120,50 @@ fn main() {
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout
|
||||||
|
fn gen_completions<T: uucore::Args>(
|
||||||
|
args: impl Iterator<Item = OsString>,
|
||||||
|
util_map: UtilityMap<T>,
|
||||||
|
) -> ! {
|
||||||
|
let all_utilities: Vec<_> = std::iter::once("coreutils")
|
||||||
|
.chain(util_map.keys().copied())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let matches = App::new("completion")
|
||||||
|
.about("Prints completions to stdout")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("utility")
|
||||||
|
.possible_values(&all_utilities)
|
||||||
|
.required(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("shell")
|
||||||
|
.possible_values(&Shell::variants())
|
||||||
|
.required(true),
|
||||||
|
)
|
||||||
|
.get_matches_from(std::iter::once(OsString::from("completion")).chain(args));
|
||||||
|
|
||||||
|
let utility = matches.value_of("utility").unwrap();
|
||||||
|
let shell = matches.value_of("shell").unwrap();
|
||||||
|
|
||||||
|
let mut app = if utility == "coreutils" {
|
||||||
|
gen_coreutils_app(util_map)
|
||||||
|
} else {
|
||||||
|
util_map.get(utility).unwrap().1()
|
||||||
|
};
|
||||||
|
let shell: Shell = shell.parse().unwrap();
|
||||||
|
let bin_name = std::env::var("PROG_PREFIX").unwrap_or_default() + utility;
|
||||||
|
|
||||||
|
app.gen_completions_to(bin_name, shell, &mut io::stdout());
|
||||||
|
io::stdout().flush().unwrap();
|
||||||
|
process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_coreutils_app<T: uucore::Args>(util_map: UtilityMap<T>) -> App<'static, 'static> {
|
||||||
|
let mut app = App::new("coreutils");
|
||||||
|
for (_, (_, sub_app)) in util_map {
|
||||||
|
app = app.subcommand(sub_app());
|
||||||
|
}
|
||||||
|
app
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ path = "src/arch.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
platform-info = "0.1"
|
platform-info = "0.1"
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
|
@ -6,24 +6,32 @@
|
||||||
// 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.
|
||||||
|
|
||||||
|
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||||
|
#![allow(clippy::nonstandard_macro_braces)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
use platform_info::*;
|
use platform_info::*;
|
||||||
|
|
||||||
use clap::{crate_version, App};
|
use clap::{crate_version, App};
|
||||||
|
use uucore::error::{FromIo, UResult};
|
||||||
|
|
||||||
static ABOUT: &str = "Display machine architecture";
|
static ABOUT: &str = "Display machine architecture";
|
||||||
static SUMMARY: &str = "Determine architecture name for current machine.";
|
static SUMMARY: &str = "Determine architecture name for current machine.";
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
#[uucore_procs::gen_uumain]
|
||||||
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
uu_app().get_matches_from(args);
|
||||||
|
|
||||||
|
let uts = PlatformInfo::new().map_err_context(|| "cannot get system name".to_string())?;
|
||||||
|
println!("{}", uts.machine().trim());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
App::new(executable!())
|
App::new(executable!())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about(ABOUT)
|
.about(ABOUT)
|
||||||
.after_help(SUMMARY)
|
.after_help(SUMMARY)
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let uts = return_if_err!(1, PlatformInfo::new());
|
|
||||||
println!("{}", uts.machine().trim());
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/base32.rs"
|
path = "src/base32.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features = ["encoding"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features = ["encoding"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ extern crate uucore;
|
||||||
|
|
||||||
use std::io::{stdin, Read};
|
use std::io::{stdin, Read};
|
||||||
|
|
||||||
|
use clap::App;
|
||||||
use uucore::encoding::Format;
|
use uucore::encoding::Format;
|
||||||
|
|
||||||
pub mod base_common;
|
pub mod base_common;
|
||||||
|
@ -56,3 +57,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
base_common::base_app(executable!(), VERSION, ABOUT)
|
||||||
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ impl Config {
|
||||||
Some(mut values) => {
|
Some(mut values) => {
|
||||||
let name = values.next().unwrap();
|
let name = values.next().unwrap();
|
||||||
if values.len() != 0 {
|
if values.len() != 0 {
|
||||||
return Err(format!("extra operand ‘{}’", name));
|
return Err(format!("extra operand '{}'", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if name == "-" {
|
if name == "-" {
|
||||||
|
@ -58,7 +58,7 @@ impl Config {
|
||||||
.value_of(options::WRAP)
|
.value_of(options::WRAP)
|
||||||
.map(|num| {
|
.map(|num| {
|
||||||
num.parse::<usize>()
|
num.parse::<usize>()
|
||||||
.map_err(|e| format!("Invalid wrap size: ‘{}’: {}", num, e))
|
.map_err(|e| format!("Invalid wrap size: '{}': {}", num, e))
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
||||||
|
@ -78,10 +78,17 @@ pub fn parse_base_cmd_args(
|
||||||
about: &str,
|
about: &str,
|
||||||
usage: &str,
|
usage: &str,
|
||||||
) -> Result<Config, String> {
|
) -> Result<Config, String> {
|
||||||
let app = App::new(name)
|
let app = base_app(name, version, about).usage(usage);
|
||||||
|
let arg_list = args
|
||||||
|
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||||
|
.accept_any();
|
||||||
|
Config::from(app.get_matches_from(arg_list))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_app<'a>(name: &str, version: &'a str, about: &'a str) -> App<'static, 'a> {
|
||||||
|
App::new(name)
|
||||||
.version(version)
|
.version(version)
|
||||||
.about(about)
|
.about(about)
|
||||||
.usage(usage)
|
|
||||||
// Format arguments.
|
// Format arguments.
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::DECODE)
|
Arg::with_name(options::DECODE)
|
||||||
|
@ -106,11 +113,7 @@ pub fn parse_base_cmd_args(
|
||||||
)
|
)
|
||||||
// "multiple" arguments are used to check whether there is more than one
|
// "multiple" arguments are used to check whether there is more than one
|
||||||
// file passed in.
|
// file passed in.
|
||||||
.arg(Arg::with_name(options::FILE).index(1).multiple(true));
|
.arg(Arg::with_name(options::FILE).index(1).multiple(true))
|
||||||
let arg_list = args
|
|
||||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
|
||||||
.accept_any();
|
|
||||||
Config::from(app.get_matches_from(arg_list))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> Box<dyn Read + 'a> {
|
pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> Box<dyn Read + 'a> {
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/base64.rs"
|
path = "src/base64.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features = ["encoding"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features = ["encoding"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
uu_base32 = { version=">=0.0.6", package="uu_base32", path="../base32"}
|
uu_base32 = { version=">=0.0.6", package="uu_base32", path="../base32"}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
use uu_base32::base_common;
|
use uu_base32::base_common;
|
||||||
|
pub use uu_base32::uu_app;
|
||||||
|
|
||||||
use uucore::encoding::Format;
|
use uucore::encoding::Format;
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/basename.rs"
|
path = "src/basename.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.2"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
|
@ -40,31 +40,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
//
|
//
|
||||||
// Argument parsing
|
// Argument parsing
|
||||||
//
|
//
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(SUMMARY)
|
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::MULTIPLE)
|
|
||||||
.short("a")
|
|
||||||
.long(options::MULTIPLE)
|
|
||||||
.help("support multiple arguments and treat each as a NAME"),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(options::NAME).multiple(true).hidden(true))
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::SUFFIX)
|
|
||||||
.short("s")
|
|
||||||
.long(options::SUFFIX)
|
|
||||||
.value_name("SUFFIX")
|
|
||||||
.help("remove a trailing SUFFIX; implies -a"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::ZERO)
|
|
||||||
.short("z")
|
|
||||||
.long(options::ZERO)
|
|
||||||
.help("end each output line with NUL, not newline"),
|
|
||||||
)
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
// too few arguments
|
// too few arguments
|
||||||
if !matches.is_present(options::NAME) {
|
if !matches.is_present(options::NAME) {
|
||||||
|
@ -116,6 +92,32 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(SUMMARY)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::MULTIPLE)
|
||||||
|
.short("a")
|
||||||
|
.long(options::MULTIPLE)
|
||||||
|
.help("support multiple arguments and treat each as a NAME"),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name(options::NAME).multiple(true).hidden(true))
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::SUFFIX)
|
||||||
|
.short("s")
|
||||||
|
.long(options::SUFFIX)
|
||||||
|
.value_name("SUFFIX")
|
||||||
|
.help("remove a trailing SUFFIX; implies -a"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::ZERO)
|
||||||
|
.short("z")
|
||||||
|
.long(options::ZERO)
|
||||||
|
.help("end each output line with NUL, not newline"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn basename(fullname: &str, suffix: &str) -> String {
|
fn basename(fullname: &str, suffix: &str) -> String {
|
||||||
// Remove all platform-specific path separators from the end
|
// Remove all platform-specific path separators from the end
|
||||||
let path = fullname.trim_end_matches(is_separator);
|
let path = fullname.trim_end_matches(is_separator);
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/cat.rs"
|
path = "src/cat.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
atty = "0.2"
|
atty = "0.2"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) nonprint nonblank nonprinting
|
// spell-checker:ignore (ToDO) nonprint nonblank nonprinting
|
||||||
|
|
||||||
|
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||||
|
#![allow(clippy::nonstandard_macro_braces)]
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
extern crate unix_socket;
|
extern crate unix_socket;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -169,7 +172,65 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.collect_str(InvalidEncodingHandling::Ignore)
|
.collect_str(InvalidEncodingHandling::Ignore)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().get_matches_from(args);
|
||||||
|
|
||||||
|
let number_mode = if matches.is_present(options::NUMBER_NONBLANK) {
|
||||||
|
NumberingMode::NonEmpty
|
||||||
|
} else if matches.is_present(options::NUMBER) {
|
||||||
|
NumberingMode::All
|
||||||
|
} else {
|
||||||
|
NumberingMode::None
|
||||||
|
};
|
||||||
|
|
||||||
|
let show_nonprint = vec![
|
||||||
|
options::SHOW_ALL.to_owned(),
|
||||||
|
options::SHOW_NONPRINTING_ENDS.to_owned(),
|
||||||
|
options::SHOW_NONPRINTING_TABS.to_owned(),
|
||||||
|
options::SHOW_NONPRINTING.to_owned(),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.any(|v| matches.is_present(v));
|
||||||
|
|
||||||
|
let show_ends = vec![
|
||||||
|
options::SHOW_ENDS.to_owned(),
|
||||||
|
options::SHOW_ALL.to_owned(),
|
||||||
|
options::SHOW_NONPRINTING_ENDS.to_owned(),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.any(|v| matches.is_present(v));
|
||||||
|
|
||||||
|
let show_tabs = vec![
|
||||||
|
options::SHOW_ALL.to_owned(),
|
||||||
|
options::SHOW_TABS.to_owned(),
|
||||||
|
options::SHOW_NONPRINTING_TABS.to_owned(),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.any(|v| matches.is_present(v));
|
||||||
|
|
||||||
|
let squeeze_blank = matches.is_present(options::SQUEEZE_BLANK);
|
||||||
|
let files: Vec<String> = match matches.values_of(options::FILE) {
|
||||||
|
Some(v) => v.clone().map(|v| v.to_owned()).collect(),
|
||||||
|
None => vec!["-".to_owned()],
|
||||||
|
};
|
||||||
|
|
||||||
|
let options = OutputOptions {
|
||||||
|
show_ends,
|
||||||
|
number: number_mode,
|
||||||
|
show_nonprint,
|
||||||
|
show_tabs,
|
||||||
|
squeeze_blank,
|
||||||
|
};
|
||||||
|
let success = cat_files(files, &options).is_ok();
|
||||||
|
|
||||||
|
if success {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
.name(NAME)
|
.name(NAME)
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.usage(SYNTAX)
|
.usage(SYNTAX)
|
||||||
|
@ -229,61 +290,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.long(options::SHOW_NONPRINTING)
|
.long(options::SHOW_NONPRINTING)
|
||||||
.help("use ^ and M- notation, except for LF (\\n) and TAB (\\t)"),
|
.help("use ^ and M- notation, except for LF (\\n) and TAB (\\t)"),
|
||||||
)
|
)
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let number_mode = if matches.is_present(options::NUMBER_NONBLANK) {
|
|
||||||
NumberingMode::NonEmpty
|
|
||||||
} else if matches.is_present(options::NUMBER) {
|
|
||||||
NumberingMode::All
|
|
||||||
} else {
|
|
||||||
NumberingMode::None
|
|
||||||
};
|
|
||||||
|
|
||||||
let show_nonprint = vec![
|
|
||||||
options::SHOW_ALL.to_owned(),
|
|
||||||
options::SHOW_NONPRINTING_ENDS.to_owned(),
|
|
||||||
options::SHOW_NONPRINTING_TABS.to_owned(),
|
|
||||||
options::SHOW_NONPRINTING.to_owned(),
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.any(|v| matches.is_present(v));
|
|
||||||
|
|
||||||
let show_ends = vec![
|
|
||||||
options::SHOW_ENDS.to_owned(),
|
|
||||||
options::SHOW_ALL.to_owned(),
|
|
||||||
options::SHOW_NONPRINTING_ENDS.to_owned(),
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.any(|v| matches.is_present(v));
|
|
||||||
|
|
||||||
let show_tabs = vec![
|
|
||||||
options::SHOW_ALL.to_owned(),
|
|
||||||
options::SHOW_TABS.to_owned(),
|
|
||||||
options::SHOW_NONPRINTING_TABS.to_owned(),
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.any(|v| matches.is_present(v));
|
|
||||||
|
|
||||||
let squeeze_blank = matches.is_present(options::SQUEEZE_BLANK);
|
|
||||||
let files: Vec<String> = match matches.values_of(options::FILE) {
|
|
||||||
Some(v) => v.clone().map(|v| v.to_owned()).collect(),
|
|
||||||
None => vec!["-".to_owned()],
|
|
||||||
};
|
|
||||||
|
|
||||||
let options = OutputOptions {
|
|
||||||
show_ends,
|
|
||||||
number: number_mode,
|
|
||||||
show_nonprint,
|
|
||||||
show_tabs,
|
|
||||||
squeeze_blank,
|
|
||||||
};
|
|
||||||
let success = cat_files(files, &options).is_ok();
|
|
||||||
|
|
||||||
if success {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cat_handle<R: Read>(
|
fn cat_handle<R: Read>(
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/chgrp.rs"
|
path = "src/chgrp.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
walkdir = "2.2"
|
walkdir = "2.2"
|
||||||
|
|
|
@ -73,84 +73,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
|
|
||||||
let mut app = App::new(executable!())
|
let mut app = uu_app().usage(&usage[..]);
|
||||||
.version(VERSION)
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::verbosity::CHANGES)
|
|
||||||
.short("c")
|
|
||||||
.long(options::verbosity::CHANGES)
|
|
||||||
.help("like verbose but report only when a change is made"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::verbosity::SILENT)
|
|
||||||
.short("f")
|
|
||||||
.long(options::verbosity::SILENT),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::verbosity::QUIET)
|
|
||||||
.long(options::verbosity::QUIET)
|
|
||||||
.help("suppress most error messages"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::verbosity::VERBOSE)
|
|
||||||
.short("v")
|
|
||||||
.long(options::verbosity::VERBOSE)
|
|
||||||
.help("output a diagnostic for every file processed"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::dereference::DEREFERENCE)
|
|
||||||
.long(options::dereference::DEREFERENCE),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::dereference::NO_DEREFERENCE)
|
|
||||||
.short("h")
|
|
||||||
.long(options::dereference::NO_DEREFERENCE)
|
|
||||||
.help(
|
|
||||||
"affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::preserve_root::PRESERVE)
|
|
||||||
.long(options::preserve_root::PRESERVE)
|
|
||||||
.help("fail to operate recursively on '/'"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::preserve_root::NO_PRESERVE)
|
|
||||||
.long(options::preserve_root::NO_PRESERVE)
|
|
||||||
.help("do not treat '/' specially (the default)"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::REFERENCE)
|
|
||||||
.long(options::REFERENCE)
|
|
||||||
.value_name("RFILE")
|
|
||||||
.help("use RFILE's group rather than specifying GROUP values")
|
|
||||||
.takes_value(true)
|
|
||||||
.multiple(false),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::RECURSIVE)
|
|
||||||
.short("R")
|
|
||||||
.long(options::RECURSIVE)
|
|
||||||
.help("operate on files and directories recursively"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::traverse::TRAVERSE)
|
|
||||||
.short(options::traverse::TRAVERSE)
|
|
||||||
.help("if a command line argument is a symbolic link to a directory, traverse it"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::traverse::NO_TRAVERSE)
|
|
||||||
.short(options::traverse::NO_TRAVERSE)
|
|
||||||
.help("do not traverse any symbolic links (default)")
|
|
||||||
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::traverse::EVERY)
|
|
||||||
.short(options::traverse::EVERY)
|
|
||||||
.help("traverse every symbolic link to a directory encountered"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// we change the positional args based on whether
|
// we change the positional args based on whether
|
||||||
// --reference was used.
|
// --reference was used.
|
||||||
|
@ -274,6 +197,86 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
executor.exec()
|
executor.exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(VERSION)
|
||||||
|
.about(ABOUT)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::verbosity::CHANGES)
|
||||||
|
.short("c")
|
||||||
|
.long(options::verbosity::CHANGES)
|
||||||
|
.help("like verbose but report only when a change is made"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::verbosity::SILENT)
|
||||||
|
.short("f")
|
||||||
|
.long(options::verbosity::SILENT),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::verbosity::QUIET)
|
||||||
|
.long(options::verbosity::QUIET)
|
||||||
|
.help("suppress most error messages"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::verbosity::VERBOSE)
|
||||||
|
.short("v")
|
||||||
|
.long(options::verbosity::VERBOSE)
|
||||||
|
.help("output a diagnostic for every file processed"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::dereference::DEREFERENCE)
|
||||||
|
.long(options::dereference::DEREFERENCE),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::dereference::NO_DEREFERENCE)
|
||||||
|
.short("h")
|
||||||
|
.long(options::dereference::NO_DEREFERENCE)
|
||||||
|
.help(
|
||||||
|
"affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::preserve_root::PRESERVE)
|
||||||
|
.long(options::preserve_root::PRESERVE)
|
||||||
|
.help("fail to operate recursively on '/'"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::preserve_root::NO_PRESERVE)
|
||||||
|
.long(options::preserve_root::NO_PRESERVE)
|
||||||
|
.help("do not treat '/' specially (the default)"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::REFERENCE)
|
||||||
|
.long(options::REFERENCE)
|
||||||
|
.value_name("RFILE")
|
||||||
|
.help("use RFILE's group rather than specifying GROUP values")
|
||||||
|
.takes_value(true)
|
||||||
|
.multiple(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::RECURSIVE)
|
||||||
|
.short("R")
|
||||||
|
.long(options::RECURSIVE)
|
||||||
|
.help("operate on files and directories recursively"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::traverse::TRAVERSE)
|
||||||
|
.short(options::traverse::TRAVERSE)
|
||||||
|
.help("if a command line argument is a symbolic link to a directory, traverse it"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::traverse::NO_TRAVERSE)
|
||||||
|
.short(options::traverse::NO_TRAVERSE)
|
||||||
|
.help("do not traverse any symbolic links (default)")
|
||||||
|
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::traverse::EVERY)
|
||||||
|
.short(options::traverse::EVERY)
|
||||||
|
.help("traverse every symbolic link to a directory encountered"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
struct Chgrper {
|
struct Chgrper {
|
||||||
dest_gid: gid_t,
|
dest_gid: gid_t,
|
||||||
bit_flag: u8,
|
bit_flag: u8,
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/chmod.rs"
|
path = "src/chmod.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.3"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs", "mode"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs", "mode"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -61,11 +61,64 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
let after_help = get_long_usage();
|
let after_help = get_long_usage();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app()
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
.usage(&usage[..])
|
||||||
.after_help(&after_help[..])
|
.after_help(&after_help[..])
|
||||||
|
.get_matches_from(args);
|
||||||
|
|
||||||
|
let changes = matches.is_present(options::CHANGES);
|
||||||
|
let quiet = matches.is_present(options::QUIET);
|
||||||
|
let verbose = matches.is_present(options::VERBOSE);
|
||||||
|
let preserve_root = matches.is_present(options::PRESERVE_ROOT);
|
||||||
|
let recursive = matches.is_present(options::RECURSIVE);
|
||||||
|
let fmode = matches
|
||||||
|
.value_of(options::REFERENCE)
|
||||||
|
.and_then(|fref| match fs::metadata(fref) {
|
||||||
|
Ok(meta) => Some(meta.mode()),
|
||||||
|
Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err),
|
||||||
|
});
|
||||||
|
let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required
|
||||||
|
let cmode = if mode_had_minus_prefix {
|
||||||
|
// clap parsing is finished, now put prefix back
|
||||||
|
format!("-{}", modes)
|
||||||
|
} else {
|
||||||
|
modes.to_string()
|
||||||
|
};
|
||||||
|
let mut files: Vec<String> = matches
|
||||||
|
.values_of(options::FILE)
|
||||||
|
.map(|v| v.map(ToString::to_string).collect())
|
||||||
|
.unwrap_or_default();
|
||||||
|
let cmode = if fmode.is_some() {
|
||||||
|
// "--reference" and MODE are mutually exclusive
|
||||||
|
// if "--reference" was used MODE needs to be interpreted as another FILE
|
||||||
|
// it wasn't possible to implement this behavior directly with clap
|
||||||
|
files.push(cmode);
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(cmode)
|
||||||
|
};
|
||||||
|
|
||||||
|
let chmoder = Chmoder {
|
||||||
|
changes,
|
||||||
|
quiet,
|
||||||
|
verbose,
|
||||||
|
preserve_root,
|
||||||
|
recursive,
|
||||||
|
fmode,
|
||||||
|
cmode,
|
||||||
|
};
|
||||||
|
match chmoder.chmod(files) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => return e,
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::CHANGES)
|
Arg::with_name(options::CHANGES)
|
||||||
.long(options::CHANGES)
|
.long(options::CHANGES)
|
||||||
|
@ -120,55 +173,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.required_unless(options::MODE)
|
.required_unless(options::MODE)
|
||||||
.multiple(true),
|
.multiple(true),
|
||||||
)
|
)
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let changes = matches.is_present(options::CHANGES);
|
|
||||||
let quiet = matches.is_present(options::QUIET);
|
|
||||||
let verbose = matches.is_present(options::VERBOSE);
|
|
||||||
let preserve_root = matches.is_present(options::PRESERVE_ROOT);
|
|
||||||
let recursive = matches.is_present(options::RECURSIVE);
|
|
||||||
let fmode = matches
|
|
||||||
.value_of(options::REFERENCE)
|
|
||||||
.and_then(|fref| match fs::metadata(fref) {
|
|
||||||
Ok(meta) => Some(meta.mode()),
|
|
||||||
Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err),
|
|
||||||
});
|
|
||||||
let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required
|
|
||||||
let cmode = if mode_had_minus_prefix {
|
|
||||||
// clap parsing is finished, now put prefix back
|
|
||||||
format!("-{}", modes)
|
|
||||||
} else {
|
|
||||||
modes.to_string()
|
|
||||||
};
|
|
||||||
let mut files: Vec<String> = matches
|
|
||||||
.values_of(options::FILE)
|
|
||||||
.map(|v| v.map(ToString::to_string).collect())
|
|
||||||
.unwrap_or_default();
|
|
||||||
let cmode = if fmode.is_some() {
|
|
||||||
// "--reference" and MODE are mutually exclusive
|
|
||||||
// if "--reference" was used MODE needs to be interpreted as another FILE
|
|
||||||
// it wasn't possible to implement this behavior directly with clap
|
|
||||||
files.push(cmode);
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(cmode)
|
|
||||||
};
|
|
||||||
|
|
||||||
let chmoder = Chmoder {
|
|
||||||
changes,
|
|
||||||
quiet,
|
|
||||||
verbose,
|
|
||||||
preserve_root,
|
|
||||||
recursive,
|
|
||||||
fmode,
|
|
||||||
cmode,
|
|
||||||
};
|
|
||||||
match chmoder.chmod(files) {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(e) => return e,
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate 'args' and delete the first occurrence
|
// Iterate 'args' and delete the first occurrence
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/chown.rs"
|
path = "src/chown.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -73,101 +73,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::verbosity::CHANGES)
|
|
||||||
.short("c")
|
|
||||||
.long(options::verbosity::CHANGES)
|
|
||||||
.help("like verbose but report only when a change is made"),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(options::dereference::DEREFERENCE).long(options::dereference::DEREFERENCE).help(
|
|
||||||
"affect the referent of each symbolic link (this is the default), rather than the symbolic link itself",
|
|
||||||
))
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::dereference::NO_DEREFERENCE)
|
|
||||||
.short("h")
|
|
||||||
.long(options::dereference::NO_DEREFERENCE)
|
|
||||||
.help(
|
|
||||||
"affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::FROM)
|
|
||||||
.long(options::FROM)
|
|
||||||
.help(
|
|
||||||
"change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute",
|
|
||||||
)
|
|
||||||
.value_name("CURRENT_OWNER:CURRENT_GROUP"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::preserve_root::PRESERVE)
|
|
||||||
.long(options::preserve_root::PRESERVE)
|
|
||||||
.help("fail to operate recursively on '/'"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::preserve_root::NO_PRESERVE)
|
|
||||||
.long(options::preserve_root::NO_PRESERVE)
|
|
||||||
.help("do not treat '/' specially (the default)"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::verbosity::QUIET)
|
|
||||||
.long(options::verbosity::QUIET)
|
|
||||||
.help("suppress most error messages"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::RECURSIVE)
|
|
||||||
.short("R")
|
|
||||||
.long(options::RECURSIVE)
|
|
||||||
.help("operate on files and directories recursively"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::REFERENCE)
|
|
||||||
.long(options::REFERENCE)
|
|
||||||
.help("use RFILE's owner and group rather than specifying OWNER:GROUP values")
|
|
||||||
.value_name("RFILE")
|
|
||||||
.min_values(1),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(options::verbosity::SILENT).short("f").long(options::verbosity::SILENT))
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::traverse::TRAVERSE)
|
|
||||||
.short(options::traverse::TRAVERSE)
|
|
||||||
.help("if a command line argument is a symbolic link to a directory, traverse it")
|
|
||||||
.overrides_with_all(&[options::traverse::EVERY, options::traverse::NO_TRAVERSE]),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::traverse::EVERY)
|
|
||||||
.short(options::traverse::EVERY)
|
|
||||||
.help("traverse every symbolic link to a directory encountered")
|
|
||||||
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::NO_TRAVERSE]),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::traverse::NO_TRAVERSE)
|
|
||||||
.short(options::traverse::NO_TRAVERSE)
|
|
||||||
.help("do not traverse any symbolic links (default)")
|
|
||||||
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::verbosity::VERBOSE)
|
|
||||||
.long(options::verbosity::VERBOSE)
|
|
||||||
.help("output a diagnostic for every file processed"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(ARG_OWNER)
|
|
||||||
.multiple(false)
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(ARG_FILES)
|
|
||||||
.multiple(true)
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true)
|
|
||||||
.min_values(1),
|
|
||||||
)
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
/* First arg is the owner/group */
|
/* First arg is the owner/group */
|
||||||
let owner = matches.value_of(ARG_OWNER).unwrap();
|
let owner = matches.value_of(ARG_OWNER).unwrap();
|
||||||
|
@ -273,6 +179,102 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
executor.exec()
|
executor.exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::verbosity::CHANGES)
|
||||||
|
.short("c")
|
||||||
|
.long(options::verbosity::CHANGES)
|
||||||
|
.help("like verbose but report only when a change is made"),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name(options::dereference::DEREFERENCE).long(options::dereference::DEREFERENCE).help(
|
||||||
|
"affect the referent of each symbolic link (this is the default), rather than the symbolic link itself",
|
||||||
|
))
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::dereference::NO_DEREFERENCE)
|
||||||
|
.short("h")
|
||||||
|
.long(options::dereference::NO_DEREFERENCE)
|
||||||
|
.help(
|
||||||
|
"affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::FROM)
|
||||||
|
.long(options::FROM)
|
||||||
|
.help(
|
||||||
|
"change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute",
|
||||||
|
)
|
||||||
|
.value_name("CURRENT_OWNER:CURRENT_GROUP"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::preserve_root::PRESERVE)
|
||||||
|
.long(options::preserve_root::PRESERVE)
|
||||||
|
.help("fail to operate recursively on '/'"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::preserve_root::NO_PRESERVE)
|
||||||
|
.long(options::preserve_root::NO_PRESERVE)
|
||||||
|
.help("do not treat '/' specially (the default)"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::verbosity::QUIET)
|
||||||
|
.long(options::verbosity::QUIET)
|
||||||
|
.help("suppress most error messages"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::RECURSIVE)
|
||||||
|
.short("R")
|
||||||
|
.long(options::RECURSIVE)
|
||||||
|
.help("operate on files and directories recursively"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::REFERENCE)
|
||||||
|
.long(options::REFERENCE)
|
||||||
|
.help("use RFILE's owner and group rather than specifying OWNER:GROUP values")
|
||||||
|
.value_name("RFILE")
|
||||||
|
.min_values(1),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name(options::verbosity::SILENT).short("f").long(options::verbosity::SILENT))
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::traverse::TRAVERSE)
|
||||||
|
.short(options::traverse::TRAVERSE)
|
||||||
|
.help("if a command line argument is a symbolic link to a directory, traverse it")
|
||||||
|
.overrides_with_all(&[options::traverse::EVERY, options::traverse::NO_TRAVERSE]),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::traverse::EVERY)
|
||||||
|
.short(options::traverse::EVERY)
|
||||||
|
.help("traverse every symbolic link to a directory encountered")
|
||||||
|
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::NO_TRAVERSE]),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::traverse::NO_TRAVERSE)
|
||||||
|
.short(options::traverse::NO_TRAVERSE)
|
||||||
|
.help("do not traverse any symbolic links (default)")
|
||||||
|
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::verbosity::VERBOSE)
|
||||||
|
.long(options::verbosity::VERBOSE)
|
||||||
|
.help("output a diagnostic for every file processed"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(ARG_OWNER)
|
||||||
|
.multiple(false)
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(ARG_FILES)
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.min_values(1),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
|
fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
|
||||||
let args = spec.split_terminator(':').collect::<Vec<_>>();
|
let args = spec.split_terminator(':').collect::<Vec<_>>();
|
||||||
let usr_only = args.len() == 1 && !args[0].is_empty();
|
let usr_only = args.len() == 1 && !args[0].is_empty();
|
||||||
|
@ -281,7 +283,7 @@ fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
|
||||||
let uid = if usr_only || usr_grp {
|
let uid = if usr_only || usr_grp {
|
||||||
Some(
|
Some(
|
||||||
Passwd::locate(args[0])
|
Passwd::locate(args[0])
|
||||||
.map_err(|_| format!("invalid user: ‘{}’", spec))?
|
.map_err(|_| format!("invalid user: '{}'", spec))?
|
||||||
.uid(),
|
.uid(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -290,7 +292,7 @@ fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
|
||||||
let gid = if grp_only || usr_grp {
|
let gid = if grp_only || usr_grp {
|
||||||
Some(
|
Some(
|
||||||
Group::locate(args[1])
|
Group::locate(args[1])
|
||||||
.map_err(|_| format!("invalid group: ‘{}’", spec))?
|
.map_err(|_| format!("invalid group: '{}'", spec))?
|
||||||
.gid(),
|
.gid(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -36,54 +36,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(SYNTAX)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::NEWROOT)
|
|
||||||
.hidden(true)
|
|
||||||
.required(true)
|
|
||||||
.index(1),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::USER)
|
|
||||||
.short("u")
|
|
||||||
.long(options::USER)
|
|
||||||
.help("User (ID or name) to switch before running the program")
|
|
||||||
.value_name("USER"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::GROUP)
|
|
||||||
.short("g")
|
|
||||||
.long(options::GROUP)
|
|
||||||
.help("Group (ID or name) to switch to")
|
|
||||||
.value_name("GROUP"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::GROUPS)
|
|
||||||
.short("G")
|
|
||||||
.long(options::GROUPS)
|
|
||||||
.help("Comma-separated list of groups to switch to")
|
|
||||||
.value_name("GROUP1,GROUP2..."),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::USERSPEC)
|
|
||||||
.long(options::USERSPEC)
|
|
||||||
.help(
|
|
||||||
"Colon-separated user and group to switch to. \
|
|
||||||
Same as -u USER -g GROUP. \
|
|
||||||
Userspec has higher preference than -u and/or -g",
|
|
||||||
)
|
|
||||||
.value_name("USER:GROUP"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::COMMAND)
|
|
||||||
.hidden(true)
|
|
||||||
.multiple(true)
|
|
||||||
.index(2),
|
|
||||||
)
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let default_shell: &'static str = "/bin/sh";
|
let default_shell: &'static str = "/bin/sh";
|
||||||
let default_option: &'static str = "-i";
|
let default_option: &'static str = "-i";
|
||||||
|
@ -138,6 +91,56 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
.usage(SYNTAX)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::NEWROOT)
|
||||||
|
.hidden(true)
|
||||||
|
.required(true)
|
||||||
|
.index(1),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::USER)
|
||||||
|
.short("u")
|
||||||
|
.long(options::USER)
|
||||||
|
.help("User (ID or name) to switch before running the program")
|
||||||
|
.value_name("USER"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::GROUP)
|
||||||
|
.short("g")
|
||||||
|
.long(options::GROUP)
|
||||||
|
.help("Group (ID or name) to switch to")
|
||||||
|
.value_name("GROUP"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::GROUPS)
|
||||||
|
.short("G")
|
||||||
|
.long(options::GROUPS)
|
||||||
|
.help("Comma-separated list of groups to switch to")
|
||||||
|
.value_name("GROUP1,GROUP2..."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::USERSPEC)
|
||||||
|
.long(options::USERSPEC)
|
||||||
|
.help(
|
||||||
|
"Colon-separated user and group to switch to. \
|
||||||
|
Same as -u USER -g GROUP. \
|
||||||
|
Userspec has higher preference than -u and/or -g",
|
||||||
|
)
|
||||||
|
.value_name("USER:GROUP"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::COMMAND)
|
||||||
|
.hidden(true)
|
||||||
|
.multiple(true)
|
||||||
|
.index(2),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn set_context(root: &Path, options: &clap::ArgMatches) {
|
fn set_context(root: &Path, options: &clap::ArgMatches) {
|
||||||
let userspec_str = options.value_of(options::USERSPEC);
|
let userspec_str = options.value_of(options::USERSPEC);
|
||||||
let user_str = options.value_of(options::USER).unwrap_or_default();
|
let user_str = options.value_of(options::USER).unwrap_or_default();
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/cksum.rs"
|
path = "src/cksum.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -180,13 +180,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.collect_str(InvalidEncodingHandling::Ignore)
|
.collect_str(InvalidEncodingHandling::Ignore)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().get_matches_from(args);
|
||||||
.name(NAME)
|
|
||||||
.version(crate_version!())
|
|
||||||
.about(SUMMARY)
|
|
||||||
.usage(SYNTAX)
|
|
||||||
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let files: Vec<String> = match matches.values_of(options::FILE) {
|
let files: Vec<String> = match matches.values_of(options::FILE) {
|
||||||
Some(v) => v.clone().map(|v| v.to_owned()).collect(),
|
Some(v) => v.clone().map(|v| v.to_owned()).collect(),
|
||||||
|
@ -217,3 +211,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
exit_code
|
exit_code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.name(NAME)
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(SUMMARY)
|
||||||
|
.usage(SYNTAX)
|
||||||
|
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/comm.rs"
|
path = "src/comm.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -137,10 +137,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
|
|
||||||
|
let mut f1 = open_file(matches.value_of(options::FILE_1).unwrap()).unwrap();
|
||||||
|
let mut f2 = open_file(matches.value_of(options::FILE_2).unwrap()).unwrap();
|
||||||
|
|
||||||
|
comm(&mut f1, &mut f2, &matches);
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about(ABOUT)
|
.about(ABOUT)
|
||||||
.usage(&usage[..])
|
|
||||||
.after_help(LONG_HELP)
|
.after_help(LONG_HELP)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::COLUMN_1)
|
Arg::with_name(options::COLUMN_1)
|
||||||
|
@ -167,12 +177,4 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(options::FILE_1).required(true))
|
.arg(Arg::with_name(options::FILE_1).required(true))
|
||||||
.arg(Arg::with_name(options::FILE_2).required(true))
|
.arg(Arg::with_name(options::FILE_2).required(true))
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let mut f1 = open_file(matches.value_of(options::FILE_1).unwrap()).unwrap();
|
|
||||||
let mut f2 = open_file(matches.value_of(options::FILE_2).unwrap()).unwrap();
|
|
||||||
|
|
||||||
comm(&mut f1, &mut f2, &matches);
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ edition = "2018"
|
||||||
path = "src/cp.rs"
|
path = "src/cp.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
libc = "0.2.85"
|
libc = "0.2.85"
|
||||||
quick-error = "1.2.3"
|
quick-error = "1.2.3"
|
||||||
|
|
|
@ -290,13 +290,10 @@ static DEFAULT_ATTRIBUTES: &[Attribute] = &[
|
||||||
Attribute::Timestamps,
|
Attribute::Timestamps,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
let usage = get_usage();
|
App::new(executable!())
|
||||||
let matches = App::new(executable!())
|
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about(ABOUT)
|
.about(ABOUT)
|
||||||
.after_help(&*format!("{}\n{}", LONG_HELP, backup_control::BACKUP_CONTROL_LONG_HELP))
|
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(Arg::with_name(options::TARGET_DIRECTORY)
|
.arg(Arg::with_name(options::TARGET_DIRECTORY)
|
||||||
.short("t")
|
.short("t")
|
||||||
.conflicts_with(options::NO_TARGET_DIRECTORY)
|
.conflicts_with(options::NO_TARGET_DIRECTORY)
|
||||||
|
@ -464,6 +461,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
.arg(Arg::with_name(options::PATHS)
|
.arg(Arg::with_name(options::PATHS)
|
||||||
.multiple(true))
|
.multiple(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
let usage = get_usage();
|
||||||
|
let matches = uu_app()
|
||||||
|
.after_help(&*format!(
|
||||||
|
"{}\n{}",
|
||||||
|
LONG_HELP,
|
||||||
|
backup_control::BACKUP_CONTROL_LONG_HELP
|
||||||
|
))
|
||||||
|
.usage(&usage[..])
|
||||||
.get_matches_from(args);
|
.get_matches_from(args);
|
||||||
|
|
||||||
let options = crash_if_err!(EXIT_ERR, Options::from_matches(&matches));
|
let options = crash_if_err!(EXIT_ERR, Options::from_matches(&matches));
|
||||||
|
@ -667,8 +675,15 @@ impl Options {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
|
{
|
||||||
|
ReflinkMode::Auto
|
||||||
|
}
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||||
|
{
|
||||||
ReflinkMode::Never
|
ReflinkMode::Never
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
backup: backup_mode,
|
backup: backup_mode,
|
||||||
backup_suffix,
|
backup_suffix,
|
||||||
|
@ -1218,28 +1233,39 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
|
||||||
/// Copy the file from `source` to `dest` either using the normal `fs::copy` or a
|
/// Copy the file from `source` to `dest` either using the normal `fs::copy` or a
|
||||||
/// copy-on-write scheme if --reflink is specified and the filesystem supports it.
|
/// copy-on-write scheme if --reflink is specified and the filesystem supports it.
|
||||||
fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
|
fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
|
||||||
if options.reflink_mode != ReflinkMode::Never {
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
|
||||||
return Err("--reflink is only supported on linux and macOS"
|
|
||||||
.to_string()
|
|
||||||
.into());
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
copy_on_write_macos(source, dest, options.reflink_mode)?;
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
copy_on_write_linux(source, dest, options.reflink_mode)?;
|
|
||||||
} else if !options.dereference && fs::symlink_metadata(&source)?.file_type().is_symlink() {
|
|
||||||
copy_link(source, dest)?;
|
|
||||||
} else if source.to_string_lossy() == "/dev/null" {
|
|
||||||
/* workaround a limitation of fs::copy
|
|
||||||
* https://github.com/rust-lang/rust/issues/79390
|
|
||||||
*/
|
|
||||||
File::create(dest)?;
|
|
||||||
} else {
|
|
||||||
if options.parents {
|
if options.parents {
|
||||||
let parent = dest.parent().unwrap_or(dest);
|
let parent = dest.parent().unwrap_or(dest);
|
||||||
fs::create_dir_all(parent)?;
|
fs::create_dir_all(parent)?;
|
||||||
}
|
}
|
||||||
|
let is_symlink = fs::symlink_metadata(&source)?.file_type().is_symlink();
|
||||||
|
if source.to_string_lossy() == "/dev/null" {
|
||||||
|
/* workaround a limitation of fs::copy
|
||||||
|
* https://github.com/rust-lang/rust/issues/79390
|
||||||
|
*/
|
||||||
|
File::create(dest)?;
|
||||||
|
} else if !options.dereference && is_symlink {
|
||||||
|
copy_link(source, dest)?;
|
||||||
|
} else if options.reflink_mode != ReflinkMode::Never {
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||||
|
return Err("--reflink is only supported on linux and macOS"
|
||||||
|
.to_string()
|
||||||
|
.into());
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
|
if is_symlink {
|
||||||
|
assert!(options.dereference);
|
||||||
|
let real_path = std::fs::read_link(source)?;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
copy_on_write_macos(&real_path, dest, options.reflink_mode)?;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
copy_on_write_linux(&real_path, dest, options.reflink_mode)?;
|
||||||
|
} else {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
copy_on_write_macos(source, dest, options.reflink_mode)?;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
copy_on_write_linux(source, dest, options.reflink_mode)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
fs::copy(source, dest).context(&*context_for(source, dest))?;
|
fs::copy(source, dest).context(&*context_for(source, dest))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1254,11 +1280,16 @@ fn copy_link(source: &Path, dest: &Path) -> CopyResult<()> {
|
||||||
Some(name) => dest.join(name).into(),
|
Some(name) => dest.join(name).into(),
|
||||||
None => crash!(
|
None => crash!(
|
||||||
EXIT_ERR,
|
EXIT_ERR,
|
||||||
"cannot stat ‘{}’: No such file or directory",
|
"cannot stat '{}': No such file or directory",
|
||||||
source.display()
|
source.display()
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// we always need to remove the file to be able to create a symlink,
|
||||||
|
// even if it is writeable.
|
||||||
|
if dest.exists() {
|
||||||
|
fs::remove_file(dest)?;
|
||||||
|
}
|
||||||
dest.into()
|
dest.into()
|
||||||
};
|
};
|
||||||
symlink_file(&link, &dest, &*context_for(&link, &dest))
|
symlink_file(&link, &dest, &*context_for(&link, &dest))
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/csplit.rs"
|
path = "src/csplit.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
regex = "1.0.0"
|
regex = "1.0.0"
|
||||||
glob = "0.2.11"
|
glob = "0.2.11"
|
||||||
|
|
|
@ -711,10 +711,37 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.collect_str(InvalidEncodingHandling::Ignore)
|
.collect_str(InvalidEncodingHandling::Ignore)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
|
|
||||||
|
// get the file to split
|
||||||
|
let file_name = matches.value_of(options::FILE).unwrap();
|
||||||
|
|
||||||
|
// get the patterns to split on
|
||||||
|
let patterns: Vec<String> = matches
|
||||||
|
.values_of(options::PATTERN)
|
||||||
|
.unwrap()
|
||||||
|
.map(str::to_string)
|
||||||
|
.collect();
|
||||||
|
let patterns = return_if_err!(1, patterns::get_patterns(&patterns[..]));
|
||||||
|
let options = CsplitOptions::new(&matches);
|
||||||
|
if file_name == "-" {
|
||||||
|
let stdin = io::stdin();
|
||||||
|
crash_if_err!(1, csplit(&options, patterns, stdin.lock()));
|
||||||
|
} else {
|
||||||
|
let file = return_if_err!(1, File::open(file_name));
|
||||||
|
let file_metadata = return_if_err!(1, file.metadata());
|
||||||
|
if !file_metadata.is_file() {
|
||||||
|
crash!(1, "'{}' is not a regular file", file_name);
|
||||||
|
}
|
||||||
|
crash_if_err!(1, csplit(&options, patterns, BufReader::new(file)));
|
||||||
|
};
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about(SUMMARY)
|
.about(SUMMARY)
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::SUFFIX_FORMAT)
|
Arg::with_name(options::SUFFIX_FORMAT)
|
||||||
.short("b")
|
.short("b")
|
||||||
|
@ -768,29 +795,4 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.required(true),
|
.required(true),
|
||||||
)
|
)
|
||||||
.after_help(LONG_HELP)
|
.after_help(LONG_HELP)
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
// get the file to split
|
|
||||||
let file_name = matches.value_of(options::FILE).unwrap();
|
|
||||||
|
|
||||||
// get the patterns to split on
|
|
||||||
let patterns: Vec<String> = matches
|
|
||||||
.values_of(options::PATTERN)
|
|
||||||
.unwrap()
|
|
||||||
.map(str::to_string)
|
|
||||||
.collect();
|
|
||||||
let patterns = return_if_err!(1, patterns::get_patterns(&patterns[..]));
|
|
||||||
let options = CsplitOptions::new(&matches);
|
|
||||||
if file_name == "-" {
|
|
||||||
let stdin = io::stdin();
|
|
||||||
crash_if_err!(1, csplit(&options, patterns, stdin.lock()));
|
|
||||||
} else {
|
|
||||||
let file = return_if_err!(1, File::open(file_name));
|
|
||||||
let file_metadata = return_if_err!(1, file.metadata());
|
|
||||||
if !file_metadata.is_file() {
|
|
||||||
crash!(1, "'{}' is not a regular file", file_name);
|
|
||||||
}
|
|
||||||
crash_if_err!(1, csplit(&options, patterns, BufReader::new(file)));
|
|
||||||
};
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||||
|
#![allow(clippy::nonstandard_macro_braces)]
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/cut.rs"
|
path = "src/cut.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
memchr = "2"
|
memchr = "2"
|
||||||
|
|
|
@ -396,88 +396,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.collect_str(InvalidEncodingHandling::Ignore)
|
.collect_str(InvalidEncodingHandling::Ignore)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().get_matches_from(args);
|
||||||
.name(NAME)
|
|
||||||
.version(crate_version!())
|
|
||||||
.usage(SYNTAX)
|
|
||||||
.about(SUMMARY)
|
|
||||||
.after_help(LONG_HELP)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::BYTES)
|
|
||||||
.short("b")
|
|
||||||
.long(options::BYTES)
|
|
||||||
.takes_value(true)
|
|
||||||
.help("filter byte columns from the input source")
|
|
||||||
.allow_hyphen_values(true)
|
|
||||||
.value_name("LIST")
|
|
||||||
.display_order(1),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::CHARACTERS)
|
|
||||||
.short("c")
|
|
||||||
.long(options::CHARACTERS)
|
|
||||||
.help("alias for character mode")
|
|
||||||
.takes_value(true)
|
|
||||||
.allow_hyphen_values(true)
|
|
||||||
.value_name("LIST")
|
|
||||||
.display_order(2),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::DELIMITER)
|
|
||||||
.short("d")
|
|
||||||
.long(options::DELIMITER)
|
|
||||||
.help("specify the delimiter character that separates fields in the input source. Defaults to Tab.")
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("DELIM")
|
|
||||||
.display_order(3),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::FIELDS)
|
|
||||||
.short("f")
|
|
||||||
.long(options::FIELDS)
|
|
||||||
.help("filter field columns from the input source")
|
|
||||||
.takes_value(true)
|
|
||||||
.allow_hyphen_values(true)
|
|
||||||
.value_name("LIST")
|
|
||||||
.display_order(4),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::COMPLEMENT)
|
|
||||||
.long(options::COMPLEMENT)
|
|
||||||
.help("invert the filter - instead of displaying only the filtered columns, display all but those columns")
|
|
||||||
.takes_value(false)
|
|
||||||
.display_order(5),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::ONLY_DELIMITED)
|
|
||||||
.short("s")
|
|
||||||
.long(options::ONLY_DELIMITED)
|
|
||||||
.help("in field mode, only print lines which contain the delimiter")
|
|
||||||
.takes_value(false)
|
|
||||||
.display_order(6),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::ZERO_TERMINATED)
|
|
||||||
.short("z")
|
|
||||||
.long(options::ZERO_TERMINATED)
|
|
||||||
.help("instead of filtering columns based on line, filter columns based on \\0 (NULL character)")
|
|
||||||
.takes_value(false)
|
|
||||||
.display_order(8),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::OUTPUT_DELIMITER)
|
|
||||||
.long(options::OUTPUT_DELIMITER)
|
|
||||||
.help("in field mode, replace the delimiter in output lines with this option's argument")
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("NEW_DELIM")
|
|
||||||
.display_order(7),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::FILE)
|
|
||||||
.hidden(true)
|
|
||||||
.multiple(true)
|
|
||||||
)
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let complement = matches.is_present(options::COMPLEMENT);
|
let complement = matches.is_present(options::COMPLEMENT);
|
||||||
|
|
||||||
|
@ -627,3 +546,87 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.name(NAME)
|
||||||
|
.version(crate_version!())
|
||||||
|
.usage(SYNTAX)
|
||||||
|
.about(SUMMARY)
|
||||||
|
.after_help(LONG_HELP)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::BYTES)
|
||||||
|
.short("b")
|
||||||
|
.long(options::BYTES)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("filter byte columns from the input source")
|
||||||
|
.allow_hyphen_values(true)
|
||||||
|
.value_name("LIST")
|
||||||
|
.display_order(1),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::CHARACTERS)
|
||||||
|
.short("c")
|
||||||
|
.long(options::CHARACTERS)
|
||||||
|
.help("alias for character mode")
|
||||||
|
.takes_value(true)
|
||||||
|
.allow_hyphen_values(true)
|
||||||
|
.value_name("LIST")
|
||||||
|
.display_order(2),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::DELIMITER)
|
||||||
|
.short("d")
|
||||||
|
.long(options::DELIMITER)
|
||||||
|
.help("specify the delimiter character that separates fields in the input source. Defaults to Tab.")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("DELIM")
|
||||||
|
.display_order(3),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::FIELDS)
|
||||||
|
.short("f")
|
||||||
|
.long(options::FIELDS)
|
||||||
|
.help("filter field columns from the input source")
|
||||||
|
.takes_value(true)
|
||||||
|
.allow_hyphen_values(true)
|
||||||
|
.value_name("LIST")
|
||||||
|
.display_order(4),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::COMPLEMENT)
|
||||||
|
.long(options::COMPLEMENT)
|
||||||
|
.help("invert the filter - instead of displaying only the filtered columns, display all but those columns")
|
||||||
|
.takes_value(false)
|
||||||
|
.display_order(5),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::ONLY_DELIMITED)
|
||||||
|
.short("s")
|
||||||
|
.long(options::ONLY_DELIMITED)
|
||||||
|
.help("in field mode, only print lines which contain the delimiter")
|
||||||
|
.takes_value(false)
|
||||||
|
.display_order(6),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::ZERO_TERMINATED)
|
||||||
|
.short("z")
|
||||||
|
.long(options::ZERO_TERMINATED)
|
||||||
|
.help("instead of filtering columns based on line, filter columns based on \\0 (NULL character)")
|
||||||
|
.takes_value(false)
|
||||||
|
.display_order(8),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OUTPUT_DELIMITER)
|
||||||
|
.long(options::OUTPUT_DELIMITER)
|
||||||
|
.help("in field mode, replace the delimiter in output lines with this option's argument")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("NEW_DELIM")
|
||||||
|
.display_order(7),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::FILE)
|
||||||
|
.hidden(true)
|
||||||
|
.multiple(true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ path = "src/date.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.4"
|
chrono = "0.4.4"
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
|
@ -142,75 +142,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
{0} [OPTION]... [MMDDhhmm[[CC]YY][.ss]]",
|
{0} [OPTION]... [MMDDhhmm[[CC]YY][.ss]]",
|
||||||
NAME
|
NAME
|
||||||
);
|
);
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&syntax[..]).get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&syntax[..])
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_DATE)
|
|
||||||
.short("d")
|
|
||||||
.long(OPT_DATE)
|
|
||||||
.takes_value(true)
|
|
||||||
.help("display time described by STRING, not 'now'"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_FILE)
|
|
||||||
.short("f")
|
|
||||||
.long(OPT_FILE)
|
|
||||||
.takes_value(true)
|
|
||||||
.help("like --date; once for each line of DATEFILE"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_ISO_8601)
|
|
||||||
.short("I")
|
|
||||||
.long(OPT_ISO_8601)
|
|
||||||
.takes_value(true)
|
|
||||||
.help(ISO_8601_HELP_STRING),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_RFC_EMAIL)
|
|
||||||
.short("R")
|
|
||||||
.long(OPT_RFC_EMAIL)
|
|
||||||
.help(RFC_5322_HELP_STRING),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_RFC_3339)
|
|
||||||
.long(OPT_RFC_3339)
|
|
||||||
.takes_value(true)
|
|
||||||
.help(RFC_3339_HELP_STRING),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_DEBUG)
|
|
||||||
.long(OPT_DEBUG)
|
|
||||||
.help("annotate the parsed date, and warn about questionable usage to stderr"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_REFERENCE)
|
|
||||||
.short("r")
|
|
||||||
.long(OPT_REFERENCE)
|
|
||||||
.takes_value(true)
|
|
||||||
.help("display the last modification time of FILE"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_SET)
|
|
||||||
.short("s")
|
|
||||||
.long(OPT_SET)
|
|
||||||
.takes_value(true)
|
|
||||||
.help(OPT_SET_HELP_STRING),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_UNIVERSAL)
|
|
||||||
.short("u")
|
|
||||||
.long(OPT_UNIVERSAL)
|
|
||||||
.alias(OPT_UNIVERSAL_2)
|
|
||||||
.help("print or set Coordinated Universal Time (UTC)"),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(OPT_FORMAT).multiple(false))
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let format = if let Some(form) = matches.value_of(OPT_FORMAT) {
|
let format = if let Some(form) = matches.value_of(OPT_FORMAT) {
|
||||||
if !form.starts_with('+') {
|
if !form.starts_with('+') {
|
||||||
eprintln!("date: invalid date ‘{}’", form);
|
eprintln!("date: invalid date '{}'", form);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
let form = form[1..].to_string();
|
let form = form[1..].to_string();
|
||||||
|
@ -239,7 +175,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let set_to = match matches.value_of(OPT_SET).map(parse_date) {
|
let set_to = match matches.value_of(OPT_SET).map(parse_date) {
|
||||||
None => None,
|
None => None,
|
||||||
Some(Err((input, _err))) => {
|
Some(Err((input, _err))) => {
|
||||||
eprintln!("date: invalid date ‘{}’", input);
|
eprintln!("date: invalid date '{}'", input);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
Some(Ok(date)) => Some(date),
|
Some(Ok(date)) => Some(date),
|
||||||
|
@ -305,7 +241,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
println!("{}", formatted);
|
println!("{}", formatted);
|
||||||
}
|
}
|
||||||
Err((input, _err)) => {
|
Err((input, _err)) => {
|
||||||
println!("date: invalid date ‘{}’", input);
|
println!("date: invalid date '{}'", input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,6 +250,72 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_DATE)
|
||||||
|
.short("d")
|
||||||
|
.long(OPT_DATE)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("display time described by STRING, not 'now'"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_FILE)
|
||||||
|
.short("f")
|
||||||
|
.long(OPT_FILE)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("like --date; once for each line of DATEFILE"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_ISO_8601)
|
||||||
|
.short("I")
|
||||||
|
.long(OPT_ISO_8601)
|
||||||
|
.takes_value(true)
|
||||||
|
.help(ISO_8601_HELP_STRING),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_RFC_EMAIL)
|
||||||
|
.short("R")
|
||||||
|
.long(OPT_RFC_EMAIL)
|
||||||
|
.help(RFC_5322_HELP_STRING),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_RFC_3339)
|
||||||
|
.long(OPT_RFC_3339)
|
||||||
|
.takes_value(true)
|
||||||
|
.help(RFC_3339_HELP_STRING),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_DEBUG)
|
||||||
|
.long(OPT_DEBUG)
|
||||||
|
.help("annotate the parsed date, and warn about questionable usage to stderr"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_REFERENCE)
|
||||||
|
.short("r")
|
||||||
|
.long(OPT_REFERENCE)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("display the last modification time of FILE"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_SET)
|
||||||
|
.short("s")
|
||||||
|
.long(OPT_SET)
|
||||||
|
.takes_value(true)
|
||||||
|
.help(OPT_SET_HELP_STRING),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_UNIVERSAL)
|
||||||
|
.short("u")
|
||||||
|
.long(OPT_UNIVERSAL)
|
||||||
|
.alias(OPT_UNIVERSAL_2)
|
||||||
|
.help("print or set Coordinated Universal Time (UTC)"),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name(OPT_FORMAT).multiple(false))
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the appropriate format string for the given settings.
|
/// Return the appropriate format string for the given settings.
|
||||||
fn make_format_string(settings: &Settings) -> &str {
|
fn make_format_string(settings: &Settings) -> &str {
|
||||||
match settings.format {
|
match settings.format {
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/df.rs"
|
path = "src/df.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
number_prefix = "0.4"
|
number_prefix = "0.4"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["libc", "fsext"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["libc", "fsext"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -258,120 +258,7 @@ fn use_size(free_size: u64, total_size: u64) -> String {
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_ALL)
|
|
||||||
.short("a")
|
|
||||||
.long("all")
|
|
||||||
.help("include dummy file systems"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_BLOCKSIZE)
|
|
||||||
.short("B")
|
|
||||||
.long("block-size")
|
|
||||||
.takes_value(true)
|
|
||||||
.help(
|
|
||||||
"scale sizes by SIZE before printing them; e.g.\
|
|
||||||
'-BM' prints sizes in units of 1,048,576 bytes",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_DIRECT)
|
|
||||||
.long("direct")
|
|
||||||
.help("show statistics for a file instead of mount point"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_TOTAL)
|
|
||||||
.long("total")
|
|
||||||
.help("produce a grand total"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_HUMAN_READABLE)
|
|
||||||
.short("h")
|
|
||||||
.long("human-readable")
|
|
||||||
.conflicts_with(OPT_HUMAN_READABLE_2)
|
|
||||||
.help("print sizes in human readable format (e.g., 1K 234M 2G)"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_HUMAN_READABLE_2)
|
|
||||||
.short("H")
|
|
||||||
.long("si")
|
|
||||||
.conflicts_with(OPT_HUMAN_READABLE)
|
|
||||||
.help("likewise, but use powers of 1000 not 1024"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_INODES)
|
|
||||||
.short("i")
|
|
||||||
.long("inodes")
|
|
||||||
.help("list inode information instead of block usage"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_KILO)
|
|
||||||
.short("k")
|
|
||||||
.help("like --block-size=1K"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_LOCAL)
|
|
||||||
.short("l")
|
|
||||||
.long("local")
|
|
||||||
.help("limit listing to local file systems"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_NO_SYNC)
|
|
||||||
.long("no-sync")
|
|
||||||
.conflicts_with(OPT_SYNC)
|
|
||||||
.help("do not invoke sync before getting usage info (default)"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_OUTPUT)
|
|
||||||
.long("output")
|
|
||||||
.takes_value(true)
|
|
||||||
.use_delimiter(true)
|
|
||||||
.help(
|
|
||||||
"use the output format defined by FIELD_LIST,\
|
|
||||||
or print all fields if FIELD_LIST is omitted.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_PORTABILITY)
|
|
||||||
.short("P")
|
|
||||||
.long("portability")
|
|
||||||
.help("use the POSIX output format"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_SYNC)
|
|
||||||
.long("sync")
|
|
||||||
.conflicts_with(OPT_NO_SYNC)
|
|
||||||
.help("invoke sync before getting usage info"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_TYPE)
|
|
||||||
.short("t")
|
|
||||||
.long("type")
|
|
||||||
.takes_value(true)
|
|
||||||
.use_delimiter(true)
|
|
||||||
.help("limit listing to file systems of type TYPE"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_PRINT_TYPE)
|
|
||||||
.short("T")
|
|
||||||
.long("print-type")
|
|
||||||
.help("print file system type"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_EXCLUDE_TYPE)
|
|
||||||
.short("x")
|
|
||||||
.long("exclude-type")
|
|
||||||
.takes_value(true)
|
|
||||||
.use_delimiter(true)
|
|
||||||
.help("limit listing to file systems not of type TYPE"),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(OPT_PATHS).multiple(true))
|
|
||||||
.help("Filesystem(s) to list")
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let paths: Vec<String> = matches
|
let paths: Vec<String> = matches
|
||||||
.values_of(OPT_PATHS)
|
.values_of(OPT_PATHS)
|
||||||
|
@ -511,3 +398,118 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
EXIT_OK
|
EXIT_OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_ALL)
|
||||||
|
.short("a")
|
||||||
|
.long("all")
|
||||||
|
.help("include dummy file systems"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_BLOCKSIZE)
|
||||||
|
.short("B")
|
||||||
|
.long("block-size")
|
||||||
|
.takes_value(true)
|
||||||
|
.help(
|
||||||
|
"scale sizes by SIZE before printing them; e.g.\
|
||||||
|
'-BM' prints sizes in units of 1,048,576 bytes",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_DIRECT)
|
||||||
|
.long("direct")
|
||||||
|
.help("show statistics for a file instead of mount point"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_TOTAL)
|
||||||
|
.long("total")
|
||||||
|
.help("produce a grand total"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_HUMAN_READABLE)
|
||||||
|
.short("h")
|
||||||
|
.long("human-readable")
|
||||||
|
.conflicts_with(OPT_HUMAN_READABLE_2)
|
||||||
|
.help("print sizes in human readable format (e.g., 1K 234M 2G)"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_HUMAN_READABLE_2)
|
||||||
|
.short("H")
|
||||||
|
.long("si")
|
||||||
|
.conflicts_with(OPT_HUMAN_READABLE)
|
||||||
|
.help("likewise, but use powers of 1000 not 1024"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_INODES)
|
||||||
|
.short("i")
|
||||||
|
.long("inodes")
|
||||||
|
.help("list inode information instead of block usage"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_KILO)
|
||||||
|
.short("k")
|
||||||
|
.help("like --block-size=1K"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_LOCAL)
|
||||||
|
.short("l")
|
||||||
|
.long("local")
|
||||||
|
.help("limit listing to local file systems"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_NO_SYNC)
|
||||||
|
.long("no-sync")
|
||||||
|
.conflicts_with(OPT_SYNC)
|
||||||
|
.help("do not invoke sync before getting usage info (default)"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_OUTPUT)
|
||||||
|
.long("output")
|
||||||
|
.takes_value(true)
|
||||||
|
.use_delimiter(true)
|
||||||
|
.help(
|
||||||
|
"use the output format defined by FIELD_LIST,\
|
||||||
|
or print all fields if FIELD_LIST is omitted.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_PORTABILITY)
|
||||||
|
.short("P")
|
||||||
|
.long("portability")
|
||||||
|
.help("use the POSIX output format"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_SYNC)
|
||||||
|
.long("sync")
|
||||||
|
.conflicts_with(OPT_NO_SYNC)
|
||||||
|
.help("invoke sync before getting usage info"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_TYPE)
|
||||||
|
.short("t")
|
||||||
|
.long("type")
|
||||||
|
.takes_value(true)
|
||||||
|
.use_delimiter(true)
|
||||||
|
.help("limit listing to file systems of type TYPE"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_PRINT_TYPE)
|
||||||
|
.short("T")
|
||||||
|
.long("print-type")
|
||||||
|
.help("print file system type"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_EXCLUDE_TYPE)
|
||||||
|
.short("x")
|
||||||
|
.long("exclude-type")
|
||||||
|
.takes_value(true)
|
||||||
|
.use_delimiter(true)
|
||||||
|
.help("limit listing to file systems not of type TYPE"),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name(OPT_PATHS).multiple(true))
|
||||||
|
.help("Filesystem(s) to list")
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/dircolors.rs"
|
path = "src/dircolors.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -73,36 +73,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(&args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(SUMMARY)
|
|
||||||
.usage(&usage[..])
|
|
||||||
.after_help(LONG_HELP)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::BOURNE_SHELL)
|
|
||||||
.long("sh")
|
|
||||||
.short("b")
|
|
||||||
.visible_alias("bourne-shell")
|
|
||||||
.help("output Bourne shell code to set LS_COLORS")
|
|
||||||
.display_order(1),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::C_SHELL)
|
|
||||||
.long("csh")
|
|
||||||
.short("c")
|
|
||||||
.visible_alias("c-shell")
|
|
||||||
.help("output C shell code to set LS_COLORS")
|
|
||||||
.display_order(2),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::PRINT_DATABASE)
|
|
||||||
.long("print-database")
|
|
||||||
.short("p")
|
|
||||||
.help("print the byte counts")
|
|
||||||
.display_order(3),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
|
|
||||||
.get_matches_from(&args);
|
|
||||||
|
|
||||||
let files = matches
|
let files = matches
|
||||||
.values_of(options::FILE)
|
.values_of(options::FILE)
|
||||||
|
@ -123,7 +94,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
if matches.is_present(options::PRINT_DATABASE) {
|
if matches.is_present(options::PRINT_DATABASE) {
|
||||||
if !files.is_empty() {
|
if !files.is_empty() {
|
||||||
show_usage_error!(
|
show_usage_error!(
|
||||||
"extra operand ‘{}’\nfile operands cannot be combined with \
|
"extra operand '{}'\nfile operands cannot be combined with \
|
||||||
--print-database (-p)",
|
--print-database (-p)",
|
||||||
files[0]
|
files[0]
|
||||||
);
|
);
|
||||||
|
@ -155,7 +126,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
result = parse(INTERNAL_DB.lines(), out_format, "")
|
result = parse(INTERNAL_DB.lines(), out_format, "")
|
||||||
} else {
|
} else {
|
||||||
if files.len() > 1 {
|
if files.len() > 1 {
|
||||||
show_usage_error!("extra operand ‘{}’", files[1]);
|
show_usage_error!("extra operand '{}'", files[1]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
match File::open(files[0]) {
|
match File::open(files[0]) {
|
||||||
|
@ -181,6 +152,37 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(SUMMARY)
|
||||||
|
.after_help(LONG_HELP)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::BOURNE_SHELL)
|
||||||
|
.long("sh")
|
||||||
|
.short("b")
|
||||||
|
.visible_alias("bourne-shell")
|
||||||
|
.help("output Bourne shell code to set LS_COLORS")
|
||||||
|
.display_order(1),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::C_SHELL)
|
||||||
|
.long("csh")
|
||||||
|
.short("c")
|
||||||
|
.visible_alias("c-shell")
|
||||||
|
.help("output C shell code to set LS_COLORS")
|
||||||
|
.display_order(2),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::PRINT_DATABASE)
|
||||||
|
.long("print-database")
|
||||||
|
.short("p")
|
||||||
|
.help("print the byte counts")
|
||||||
|
.display_order(3),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
|
||||||
|
}
|
||||||
|
|
||||||
pub trait StrUtils {
|
pub trait StrUtils {
|
||||||
/// Remove comments and trim whitespace
|
/// Remove comments and trim whitespace
|
||||||
fn purify(&self) -> &Self;
|
fn purify(&self) -> &Self;
|
||||||
|
@ -192,21 +194,25 @@ pub trait StrUtils {
|
||||||
impl StrUtils for str {
|
impl StrUtils for str {
|
||||||
fn purify(&self) -> &Self {
|
fn purify(&self) -> &Self {
|
||||||
let mut line = self;
|
let mut line = self;
|
||||||
for (n, c) in self.chars().enumerate() {
|
for (n, _) in self
|
||||||
if c != '#' {
|
.as_bytes()
|
||||||
continue;
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, c)| **c == b'#')
|
||||||
|
{
|
||||||
|
// Ignore the content after '#'
|
||||||
|
// only if it is preceded by at least one whitespace
|
||||||
|
match self[..n].chars().last() {
|
||||||
|
Some(c) if c.is_whitespace() => {
|
||||||
|
line = &self[..n - c.len_utf8()];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
// Ignore if '#' is at the beginning of line
|
// n == 0
|
||||||
if n == 0 {
|
|
||||||
line = &self[..0];
|
line = &self[..0];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
// Ignore the content after '#'
|
|
||||||
// only if it is preceded by at least one whitespace
|
|
||||||
if self.chars().nth(n - 1).unwrap().is_whitespace() {
|
|
||||||
line = &self[..n];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
line.trim()
|
line.trim()
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/dirname.rs"
|
path = "src/dirname.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -38,18 +38,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
let after_help = get_long_usage();
|
let after_help = get_long_usage();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app()
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
.usage(&usage[..])
|
||||||
.after_help(&after_help[..])
|
.after_help(&after_help[..])
|
||||||
.version(crate_version!())
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::ZERO)
|
|
||||||
.long(options::ZERO)
|
|
||||||
.short("z")
|
|
||||||
.help("separate output with NUL rather than newline"),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(options::DIR).hidden(true).multiple(true))
|
|
||||||
.get_matches_from(args);
|
.get_matches_from(args);
|
||||||
|
|
||||||
let separator = if matches.is_present(options::ZERO) {
|
let separator = if matches.is_present(options::ZERO) {
|
||||||
|
@ -92,3 +83,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.about(ABOUT)
|
||||||
|
.version(crate_version!())
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::ZERO)
|
||||||
|
.long(options::ZERO)
|
||||||
|
.short("z")
|
||||||
|
.help("separate output with NUL rather than newline"),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name(options::DIR).hidden(true).multiple(true))
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/du.rs"
|
path = "src/du.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -10,6 +10,7 @@ extern crate uucore;
|
||||||
|
|
||||||
use chrono::prelude::DateTime;
|
use chrono::prelude::DateTime;
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
use clap::ArgMatches;
|
||||||
use clap::{crate_version, App, Arg};
|
use clap::{crate_version, App, Arg};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -62,6 +63,8 @@ mod options {
|
||||||
pub const TIME: &str = "time";
|
pub const TIME: &str = "time";
|
||||||
pub const TIME_STYLE: &str = "time-style";
|
pub const TIME_STYLE: &str = "time-style";
|
||||||
pub const ONE_FILE_SYSTEM: &str = "one-file-system";
|
pub const ONE_FILE_SYSTEM: &str = "one-file-system";
|
||||||
|
pub const DEREFERENCE: &str = "dereference";
|
||||||
|
pub const INODES: &str = "inodes";
|
||||||
pub const FILE: &str = "FILE";
|
pub const FILE: &str = "FILE";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +90,8 @@ struct Options {
|
||||||
total: bool,
|
total: bool,
|
||||||
separate_dirs: bool,
|
separate_dirs: bool,
|
||||||
one_file_system: bool,
|
one_file_system: bool,
|
||||||
|
dereference: bool,
|
||||||
|
inodes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
|
@ -100,6 +105,7 @@ struct Stat {
|
||||||
is_dir: bool,
|
is_dir: bool,
|
||||||
size: u64,
|
size: u64,
|
||||||
blocks: u64,
|
blocks: u64,
|
||||||
|
inodes: u64,
|
||||||
inode: Option<FileInfo>,
|
inode: Option<FileInfo>,
|
||||||
created: Option<u64>,
|
created: Option<u64>,
|
||||||
accessed: u64,
|
accessed: u64,
|
||||||
|
@ -107,8 +113,12 @@ struct Stat {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stat {
|
impl Stat {
|
||||||
fn new(path: PathBuf) -> Result<Stat> {
|
fn new(path: PathBuf, options: &Options) -> Result<Stat> {
|
||||||
let metadata = fs::symlink_metadata(&path)?;
|
let metadata = if options.dereference {
|
||||||
|
fs::metadata(&path)?
|
||||||
|
} else {
|
||||||
|
fs::symlink_metadata(&path)?
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let file_info = FileInfo {
|
let file_info = FileInfo {
|
||||||
|
@ -121,6 +131,7 @@ impl Stat {
|
||||||
is_dir: metadata.is_dir(),
|
is_dir: metadata.is_dir(),
|
||||||
size: metadata.len(),
|
size: metadata.len(),
|
||||||
blocks: metadata.blocks() as u64,
|
blocks: metadata.blocks() as u64,
|
||||||
|
inodes: 1,
|
||||||
inode: Some(file_info),
|
inode: Some(file_info),
|
||||||
created: birth_u64(&metadata),
|
created: birth_u64(&metadata),
|
||||||
accessed: metadata.atime() as u64,
|
accessed: metadata.atime() as u64,
|
||||||
|
@ -138,6 +149,7 @@ impl Stat {
|
||||||
size: metadata.len(),
|
size: metadata.len(),
|
||||||
blocks: size_on_disk / 1024 * 2,
|
blocks: size_on_disk / 1024 * 2,
|
||||||
inode: file_info,
|
inode: file_info,
|
||||||
|
inodes: 1,
|
||||||
created: windows_creation_time_to_unix_time(metadata.creation_time()),
|
created: windows_creation_time_to_unix_time(metadata.creation_time()),
|
||||||
accessed: windows_time_to_unix_time(metadata.last_access_time()),
|
accessed: windows_time_to_unix_time(metadata.last_access_time()),
|
||||||
modified: windows_time_to_unix_time(metadata.last_write_time()),
|
modified: windows_time_to_unix_time(metadata.last_write_time()),
|
||||||
|
@ -251,6 +263,18 @@ fn read_block_size(s: Option<&str>) -> usize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn choose_size(matches: &ArgMatches, stat: &Stat) -> u64 {
|
||||||
|
if matches.is_present(options::INODES) {
|
||||||
|
stat.inodes
|
||||||
|
} else if matches.is_present(options::APPARENT_SIZE) || matches.is_present(options::BYTES) {
|
||||||
|
stat.size
|
||||||
|
} else {
|
||||||
|
// The st_blocks field indicates the number of blocks allocated to the file, 512-byte units.
|
||||||
|
// See: http://linux.die.net/man/2/stat
|
||||||
|
stat.blocks * 512
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// this takes `my_stat` to avoid having to stat files multiple times.
|
// this takes `my_stat` to avoid having to stat files multiple times.
|
||||||
// XXX: this should use the impl Trait return type when it is stabilized
|
// XXX: this should use the impl Trait return type when it is stabilized
|
||||||
fn du(
|
fn du(
|
||||||
|
@ -268,7 +292,7 @@ fn du(
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
safe_writeln!(
|
safe_writeln!(
|
||||||
stderr(),
|
stderr(),
|
||||||
"{}: cannot read directory ‘{}‘: {}",
|
"{}: cannot read directory '{}': {}",
|
||||||
options.program_name,
|
options.program_name,
|
||||||
my_stat.path.display(),
|
my_stat.path.display(),
|
||||||
e
|
e
|
||||||
|
@ -279,8 +303,14 @@ fn du(
|
||||||
|
|
||||||
for f in read {
|
for f in read {
|
||||||
match f {
|
match f {
|
||||||
Ok(entry) => match Stat::new(entry.path()) {
|
Ok(entry) => match Stat::new(entry.path(), options) {
|
||||||
Ok(this_stat) => {
|
Ok(this_stat) => {
|
||||||
|
if let Some(inode) = this_stat.inode {
|
||||||
|
if inodes.contains(&inode) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
inodes.insert(inode);
|
||||||
|
}
|
||||||
if this_stat.is_dir {
|
if this_stat.is_dir {
|
||||||
if options.one_file_system {
|
if options.one_file_system {
|
||||||
if let (Some(this_inode), Some(my_inode)) =
|
if let (Some(this_inode), Some(my_inode)) =
|
||||||
|
@ -293,14 +323,9 @@ fn du(
|
||||||
}
|
}
|
||||||
futures.push(du(this_stat, options, depth + 1, inodes));
|
futures.push(du(this_stat, options, depth + 1, inodes));
|
||||||
} else {
|
} else {
|
||||||
if let Some(inode) = this_stat.inode {
|
|
||||||
if inodes.contains(&inode) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
inodes.insert(inode);
|
|
||||||
}
|
|
||||||
my_stat.size += this_stat.size;
|
my_stat.size += this_stat.size;
|
||||||
my_stat.blocks += this_stat.blocks;
|
my_stat.blocks += this_stat.blocks;
|
||||||
|
my_stat.inodes += 1;
|
||||||
if options.all {
|
if options.all {
|
||||||
stats.push(this_stat);
|
stats.push(this_stat);
|
||||||
}
|
}
|
||||||
|
@ -308,18 +333,11 @@ fn du(
|
||||||
}
|
}
|
||||||
Err(error) => match error.kind() {
|
Err(error) => match error.kind() {
|
||||||
ErrorKind::PermissionDenied => {
|
ErrorKind::PermissionDenied => {
|
||||||
let description = format!(
|
let description = format!("cannot access '{}'", entry.path().display());
|
||||||
"cannot access '{}'",
|
|
||||||
entry
|
|
||||||
.path()
|
|
||||||
.as_os_str()
|
|
||||||
.to_str()
|
|
||||||
.unwrap_or("<Un-printable path>")
|
|
||||||
);
|
|
||||||
let error_message = "Permission denied";
|
let error_message = "Permission denied";
|
||||||
show_error_custom_description!(description, "{}", error_message)
|
show_error_custom_description!(description, "{}", error_message)
|
||||||
}
|
}
|
||||||
_ => show_error!("{}", error),
|
_ => show_error!("cannot access '{}': {}", entry.path().display(), error),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Err(error) => show_error!("{}", error),
|
Err(error) => show_error!("{}", error),
|
||||||
|
@ -327,10 +345,11 @@ fn du(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.extend(futures.into_iter().flatten().rev().filter(|stat| {
|
stats.extend(futures.into_iter().flatten().filter(|stat| {
|
||||||
if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path {
|
if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path {
|
||||||
my_stat.size += stat.size;
|
my_stat.size += stat.size;
|
||||||
my_stat.blocks += stat.blocks;
|
my_stat.blocks += stat.blocks;
|
||||||
|
my_stat.inodes += stat.inodes;
|
||||||
}
|
}
|
||||||
options
|
options
|
||||||
.max_depth
|
.max_depth
|
||||||
|
@ -388,10 +407,196 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
|
|
||||||
|
let summarize = matches.is_present(options::SUMMARIZE);
|
||||||
|
|
||||||
|
let max_depth_str = matches.value_of(options::MAX_DEPTH);
|
||||||
|
let max_depth = max_depth_str.as_ref().and_then(|s| s.parse::<usize>().ok());
|
||||||
|
match (max_depth_str, max_depth) {
|
||||||
|
(Some(s), _) if summarize => {
|
||||||
|
show_error!("summarizing conflicts with --max-depth={}", s);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
(Some(s), None) => {
|
||||||
|
show_error!("invalid maximum depth '{}'", s);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
(Some(_), Some(_)) | (None, _) => { /* valid */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
let options = Options {
|
||||||
|
all: matches.is_present(options::ALL),
|
||||||
|
program_name: NAME.to_owned(),
|
||||||
|
max_depth,
|
||||||
|
total: matches.is_present(options::TOTAL),
|
||||||
|
separate_dirs: matches.is_present(options::SEPARATE_DIRS),
|
||||||
|
one_file_system: matches.is_present(options::ONE_FILE_SYSTEM),
|
||||||
|
dereference: matches.is_present(options::DEREFERENCE),
|
||||||
|
inodes: matches.is_present(options::INODES),
|
||||||
|
};
|
||||||
|
|
||||||
|
let files = match matches.value_of(options::FILE) {
|
||||||
|
Some(_) => matches.values_of(options::FILE).unwrap().collect(),
|
||||||
|
None => vec!["."],
|
||||||
|
};
|
||||||
|
|
||||||
|
if options.inodes
|
||||||
|
&& (matches.is_present(options::APPARENT_SIZE) || matches.is_present(options::BYTES))
|
||||||
|
{
|
||||||
|
show_warning!("options --apparent-size and -b are ineffective with --inodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
let block_size = u64::try_from(read_block_size(matches.value_of(options::BLOCK_SIZE))).unwrap();
|
||||||
|
|
||||||
|
let threshold = matches.value_of(options::THRESHOLD).map(|s| {
|
||||||
|
Threshold::from_str(s)
|
||||||
|
.unwrap_or_else(|e| crash!(1, "{}", format_error_message(e, s, options::THRESHOLD)))
|
||||||
|
});
|
||||||
|
|
||||||
|
let multiplier: u64 = if matches.is_present(options::SI) {
|
||||||
|
1000
|
||||||
|
} else {
|
||||||
|
1024
|
||||||
|
};
|
||||||
|
let convert_size_fn = {
|
||||||
|
if matches.is_present(options::HUMAN_READABLE) || matches.is_present(options::SI) {
|
||||||
|
convert_size_human
|
||||||
|
} else if matches.is_present(options::BYTES) {
|
||||||
|
convert_size_b
|
||||||
|
} else if matches.is_present(options::BLOCK_SIZE_1K) {
|
||||||
|
convert_size_k
|
||||||
|
} else if matches.is_present(options::BLOCK_SIZE_1M) {
|
||||||
|
convert_size_m
|
||||||
|
} else {
|
||||||
|
convert_size_other
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let convert_size = |size: u64| {
|
||||||
|
if options.inodes {
|
||||||
|
size.to_string()
|
||||||
|
} else {
|
||||||
|
convert_size_fn(size, multiplier, block_size)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let time_format_str = match matches.value_of("time-style") {
|
||||||
|
Some(s) => match s {
|
||||||
|
"full-iso" => "%Y-%m-%d %H:%M:%S.%f %z",
|
||||||
|
"long-iso" => "%Y-%m-%d %H:%M",
|
||||||
|
"iso" => "%Y-%m-%d",
|
||||||
|
_ => {
|
||||||
|
show_error!(
|
||||||
|
"invalid argument '{}' for 'time style'
|
||||||
|
Valid arguments are:
|
||||||
|
- 'full-iso'
|
||||||
|
- 'long-iso'
|
||||||
|
- 'iso'
|
||||||
|
Try '{} --help' for more information.",
|
||||||
|
s,
|
||||||
|
NAME
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => "%Y-%m-%d %H:%M",
|
||||||
|
};
|
||||||
|
|
||||||
|
let line_separator = if matches.is_present(options::NULL) {
|
||||||
|
"\0"
|
||||||
|
} else {
|
||||||
|
"\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut grand_total = 0;
|
||||||
|
for path_string in files {
|
||||||
|
let path = PathBuf::from(&path_string);
|
||||||
|
match Stat::new(path, &options) {
|
||||||
|
Ok(stat) => {
|
||||||
|
let mut inodes: HashSet<FileInfo> = HashSet::new();
|
||||||
|
if let Some(inode) = stat.inode {
|
||||||
|
inodes.insert(inode);
|
||||||
|
}
|
||||||
|
let iter = du(stat, &options, 0, &mut inodes);
|
||||||
|
let (_, len) = iter.size_hint();
|
||||||
|
let len = len.unwrap();
|
||||||
|
for (index, stat) in iter.enumerate() {
|
||||||
|
let size = choose_size(&matches, &stat);
|
||||||
|
|
||||||
|
if threshold.map_or(false, |threshold| threshold.should_exclude(size)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches.is_present(options::TIME) {
|
||||||
|
let tm = {
|
||||||
|
let secs = {
|
||||||
|
match matches.value_of(options::TIME) {
|
||||||
|
Some(s) => match s {
|
||||||
|
"ctime" | "status" => stat.modified,
|
||||||
|
"access" | "atime" | "use" => stat.accessed,
|
||||||
|
"birth" | "creation" => {
|
||||||
|
if let Some(time) = stat.created {
|
||||||
|
time
|
||||||
|
} else {
|
||||||
|
show_error!(
|
||||||
|
"Invalid argument '{}' for --time.
|
||||||
|
'birth' and 'creation' arguments are not supported on this platform.",
|
||||||
|
s
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// below should never happen as clap already restricts the values.
|
||||||
|
_ => unreachable!("Invalid field for --time"),
|
||||||
|
},
|
||||||
|
None => stat.modified,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DateTime::<Local>::from(UNIX_EPOCH + Duration::from_secs(secs))
|
||||||
|
};
|
||||||
|
if !summarize || index == len - 1 {
|
||||||
|
let time_str = tm.format(time_format_str).to_string();
|
||||||
|
print!(
|
||||||
|
"{}\t{}\t{}{}",
|
||||||
|
convert_size(size),
|
||||||
|
time_str,
|
||||||
|
stat.path.display(),
|
||||||
|
line_separator
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if !summarize || index == len - 1 {
|
||||||
|
print!(
|
||||||
|
"{}\t{}{}",
|
||||||
|
convert_size(size),
|
||||||
|
stat.path.display(),
|
||||||
|
line_separator
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if options.total && index == (len - 1) {
|
||||||
|
// The last element will be the total size of the the path under
|
||||||
|
// path_string. We add it to the grand total.
|
||||||
|
grand_total += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
show_error!("{}: {}", path_string, "No such file or directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.total {
|
||||||
|
print!("{}\ttotal", convert_size(grand_total));
|
||||||
|
print!("{}", line_separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about(SUMMARY)
|
.about(SUMMARY)
|
||||||
.usage(&usage[..])
|
|
||||||
.after_help(LONG_HELP)
|
.after_help(LONG_HELP)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::ALL)
|
Arg::with_name(options::ALL)
|
||||||
|
@ -449,8 +654,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.help("print sizes in human readable format (e.g., 1K 234M 2G)")
|
.help("print sizes in human readable format (e.g., 1K 234M 2G)")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("inodes")
|
Arg::with_name(options::INODES)
|
||||||
.long("inodes")
|
.long(options::INODES)
|
||||||
.help(
|
.help(
|
||||||
"list inode usage information instead of block usage like --block-size=1K"
|
"list inode usage information instead of block usage like --block-size=1K"
|
||||||
)
|
)
|
||||||
|
@ -466,12 +671,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.long("count-links")
|
.long("count-links")
|
||||||
.help("count sizes many times if hard linked")
|
.help("count sizes many times if hard linked")
|
||||||
)
|
)
|
||||||
// .arg(
|
.arg(
|
||||||
// Arg::with_name("dereference")
|
Arg::with_name(options::DEREFERENCE)
|
||||||
// .short("L")
|
.short("L")
|
||||||
// .long("dereference")
|
.long(options::DEREFERENCE)
|
||||||
// .help("dereference all symbolic links")
|
.help("dereference all symbolic links")
|
||||||
// )
|
)
|
||||||
// .arg(
|
// .arg(
|
||||||
// Arg::with_name("no-dereference")
|
// Arg::with_name("no-dereference")
|
||||||
// .short("P")
|
// .short("P")
|
||||||
|
@ -563,184 +768,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.hidden(true)
|
.hidden(true)
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
)
|
)
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let summarize = matches.is_present(options::SUMMARIZE);
|
|
||||||
|
|
||||||
let max_depth_str = matches.value_of(options::MAX_DEPTH);
|
|
||||||
let max_depth = max_depth_str.as_ref().and_then(|s| s.parse::<usize>().ok());
|
|
||||||
match (max_depth_str, max_depth) {
|
|
||||||
(Some(s), _) if summarize => {
|
|
||||||
show_error!("summarizing conflicts with --max-depth={}", s);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
(Some(s), None) => {
|
|
||||||
show_error!("invalid maximum depth '{}'", s);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
(Some(_), Some(_)) | (None, _) => { /* valid */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
let options = Options {
|
|
||||||
all: matches.is_present(options::ALL),
|
|
||||||
program_name: NAME.to_owned(),
|
|
||||||
max_depth,
|
|
||||||
total: matches.is_present(options::TOTAL),
|
|
||||||
separate_dirs: matches.is_present(options::SEPARATE_DIRS),
|
|
||||||
one_file_system: matches.is_present(options::ONE_FILE_SYSTEM),
|
|
||||||
};
|
|
||||||
|
|
||||||
let files = match matches.value_of(options::FILE) {
|
|
||||||
Some(_) => matches.values_of(options::FILE).unwrap().collect(),
|
|
||||||
None => {
|
|
||||||
vec!["./"] // TODO: gnu `du` doesn't use trailing "/" here
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let block_size = u64::try_from(read_block_size(matches.value_of(options::BLOCK_SIZE))).unwrap();
|
|
||||||
|
|
||||||
let threshold = matches.value_of(options::THRESHOLD).map(|s| {
|
|
||||||
Threshold::from_str(s)
|
|
||||||
.unwrap_or_else(|e| crash!(1, "{}", format_error_message(e, s, options::THRESHOLD)))
|
|
||||||
});
|
|
||||||
|
|
||||||
let multiplier: u64 = if matches.is_present(options::SI) {
|
|
||||||
1000
|
|
||||||
} else {
|
|
||||||
1024
|
|
||||||
};
|
|
||||||
let convert_size_fn = {
|
|
||||||
if matches.is_present(options::HUMAN_READABLE) || matches.is_present(options::SI) {
|
|
||||||
convert_size_human
|
|
||||||
} else if matches.is_present(options::BYTES) {
|
|
||||||
convert_size_b
|
|
||||||
} else if matches.is_present(options::BLOCK_SIZE_1K) {
|
|
||||||
convert_size_k
|
|
||||||
} else if matches.is_present(options::BLOCK_SIZE_1M) {
|
|
||||||
convert_size_m
|
|
||||||
} else {
|
|
||||||
convert_size_other
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let convert_size = |size| convert_size_fn(size, multiplier, block_size);
|
|
||||||
|
|
||||||
let time_format_str = match matches.value_of("time-style") {
|
|
||||||
Some(s) => match s {
|
|
||||||
"full-iso" => "%Y-%m-%d %H:%M:%S.%f %z",
|
|
||||||
"long-iso" => "%Y-%m-%d %H:%M",
|
|
||||||
"iso" => "%Y-%m-%d",
|
|
||||||
_ => {
|
|
||||||
show_error!(
|
|
||||||
"invalid argument '{}' for 'time style'
|
|
||||||
Valid arguments are:
|
|
||||||
- 'full-iso'
|
|
||||||
- 'long-iso'
|
|
||||||
- 'iso'
|
|
||||||
Try '{} --help' for more information.",
|
|
||||||
s,
|
|
||||||
NAME
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => "%Y-%m-%d %H:%M",
|
|
||||||
};
|
|
||||||
|
|
||||||
let line_separator = if matches.is_present(options::NULL) {
|
|
||||||
"\0"
|
|
||||||
} else {
|
|
||||||
"\n"
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut grand_total = 0;
|
|
||||||
for path_string in files {
|
|
||||||
let path = PathBuf::from(&path_string);
|
|
||||||
match Stat::new(path) {
|
|
||||||
Ok(stat) => {
|
|
||||||
let mut inodes: HashSet<FileInfo> = HashSet::new();
|
|
||||||
|
|
||||||
let iter = du(stat, &options, 0, &mut inodes);
|
|
||||||
let (_, len) = iter.size_hint();
|
|
||||||
let len = len.unwrap();
|
|
||||||
for (index, stat) in iter.enumerate() {
|
|
||||||
let size = if matches.is_present(options::APPARENT_SIZE)
|
|
||||||
|| matches.is_present(options::BYTES)
|
|
||||||
{
|
|
||||||
stat.size
|
|
||||||
} else {
|
|
||||||
// C's stat is such that each block is assume to be 512 bytes
|
|
||||||
// See: http://linux.die.net/man/2/stat
|
|
||||||
stat.blocks * 512
|
|
||||||
};
|
|
||||||
|
|
||||||
if threshold.map_or(false, |threshold| threshold.should_exclude(size)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches.is_present(options::TIME) {
|
|
||||||
let tm = {
|
|
||||||
let secs = {
|
|
||||||
match matches.value_of(options::TIME) {
|
|
||||||
Some(s) => match s {
|
|
||||||
"ctime" | "status" => stat.modified,
|
|
||||||
"access" | "atime" | "use" => stat.accessed,
|
|
||||||
"birth" | "creation" => {
|
|
||||||
if let Some(time) = stat.created {
|
|
||||||
time
|
|
||||||
} else {
|
|
||||||
show_error!(
|
|
||||||
"Invalid argument ‘{}‘ for --time.
|
|
||||||
‘birth‘ and ‘creation‘ arguments are not supported on this platform.",
|
|
||||||
s
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// below should never happen as clap already restricts the values.
|
|
||||||
_ => unreachable!("Invalid field for --time"),
|
|
||||||
},
|
|
||||||
None => stat.modified,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
DateTime::<Local>::from(UNIX_EPOCH + Duration::from_secs(secs))
|
|
||||||
};
|
|
||||||
if !summarize || index == len - 1 {
|
|
||||||
let time_str = tm.format(time_format_str).to_string();
|
|
||||||
print!(
|
|
||||||
"{}\t{}\t{}{}",
|
|
||||||
convert_size(size),
|
|
||||||
time_str,
|
|
||||||
stat.path.display(),
|
|
||||||
line_separator
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if !summarize || index == len - 1 {
|
|
||||||
print!(
|
|
||||||
"{}\t{}{}",
|
|
||||||
convert_size(size),
|
|
||||||
stat.path.display(),
|
|
||||||
line_separator
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if options.total && index == (len - 1) {
|
|
||||||
// The last element will be the total size of the the path under
|
|
||||||
// path_string. We add it to the grand total.
|
|
||||||
grand_total += size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
show_error!("{}: {}", path_string, "No such file or directory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.total {
|
|
||||||
print!("{}\ttotal", convert_size(grand_total));
|
|
||||||
print!("{}", line_separator);
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/echo.rs"
|
path = "src/echo.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,26 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let args = args
|
let args = args
|
||||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().get_matches_from(args);
|
||||||
|
|
||||||
|
let no_newline = matches.is_present(options::NO_NEWLINE);
|
||||||
|
let escaped = matches.is_present(options::ENABLE_BACKSLASH_ESCAPE);
|
||||||
|
let values: Vec<String> = match matches.values_of(options::STRING) {
|
||||||
|
Some(s) => s.map(|s| s.to_string()).collect(),
|
||||||
|
None => vec!["".to_string()],
|
||||||
|
};
|
||||||
|
|
||||||
|
match execute(no_newline, escaped, values) {
|
||||||
|
Ok(_) => 0,
|
||||||
|
Err(f) => {
|
||||||
|
show_error!("{}", f);
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
.name(NAME)
|
.name(NAME)
|
||||||
// TrailingVarArg specifies the final positional argument is a VarArg
|
// TrailingVarArg specifies the final positional argument is a VarArg
|
||||||
// and it doesn't attempts the parse any further args.
|
// and it doesn't attempts the parse any further args.
|
||||||
|
@ -154,22 +173,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
.allow_hyphen_values(true),
|
.allow_hyphen_values(true),
|
||||||
)
|
)
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let no_newline = matches.is_present(options::NO_NEWLINE);
|
|
||||||
let escaped = matches.is_present(options::ENABLE_BACKSLASH_ESCAPE);
|
|
||||||
let values: Vec<String> = match matches.values_of(options::STRING) {
|
|
||||||
Some(s) => s.map(|s| s.to_string()).collect(),
|
|
||||||
None => vec!["".to_string()],
|
|
||||||
};
|
|
||||||
|
|
||||||
match execute(no_newline, escaped, values) {
|
|
||||||
Ok(_) => 0,
|
|
||||||
Err(f) => {
|
|
||||||
show_error!("{}", f);
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(no_newline: bool, escaped: bool, free: Vec<String>) -> io::Result<()> {
|
fn execute(no_newline: bool, escaped: bool, free: Vec<String>) -> io::Result<()> {
|
||||||
|
|
2
src/uu/env/Cargo.toml
vendored
2
src/uu/env/Cargo.toml
vendored
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/env.rs"
|
path = "src/env.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
rust-ini = "0.13.0"
|
rust-ini = "0.13.0"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
|
|
4
src/uu/env/src/env.rs
vendored
4
src/uu/env/src/env.rs
vendored
|
@ -114,7 +114,7 @@ fn build_command<'a, 'b>(args: &'a mut Vec<&'b str>) -> (Cow<'b, str>, &'a [&'b
|
||||||
(progname, &args[..])
|
(progname, &args[..])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_app() -> App<'static, 'static> {
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
App::new(crate_name!())
|
App::new(crate_name!())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.author(crate_authors!())
|
.author(crate_authors!())
|
||||||
|
@ -158,7 +158,7 @@ fn create_app() -> App<'static, 'static> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_env(args: impl uucore::Args) -> Result<(), i32> {
|
fn run_env(args: impl uucore::Args) -> Result<(), i32> {
|
||||||
let app = create_app();
|
let app = uu_app();
|
||||||
let matches = app.get_matches_from(args);
|
let matches = app.get_matches_from(args);
|
||||||
|
|
||||||
let ignore_env = matches.is_present("ignore-environment");
|
let ignore_env = matches.is_present("ignore-environment");
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/expand.rs"
|
path = "src/expand.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
unicode-width = "0.1.5"
|
unicode-width = "0.1.5"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -108,10 +108,16 @@ impl Options {
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
|
|
||||||
|
expand(Options::new(&matches));
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about(ABOUT)
|
.about(ABOUT)
|
||||||
.usage(&usage[..])
|
|
||||||
.after_help(LONG_HELP)
|
.after_help(LONG_HELP)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::INITIAL)
|
Arg::with_name(options::INITIAL)
|
||||||
|
@ -138,10 +144,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.hidden(true)
|
.hidden(true)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
)
|
)
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
expand(Options::new(&matches));
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open(path: String) -> BufReader<Box<dyn Read + 'static>> {
|
fn open(path: String) -> BufReader<Box<dyn Read + 'static>> {
|
||||||
|
|
|
@ -15,6 +15,7 @@ edition = "2018"
|
||||||
path = "src/expr.rs"
|
path = "src/expr.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
num-bigint = "0.4.0"
|
num-bigint = "0.4.0"
|
||||||
num-traits = "0.2.14"
|
num-traits = "0.2.14"
|
||||||
|
|
|
@ -8,13 +8,20 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
|
use clap::{crate_version, App, Arg};
|
||||||
use uucore::InvalidEncodingHandling;
|
use uucore::InvalidEncodingHandling;
|
||||||
|
|
||||||
mod syntax_tree;
|
mod syntax_tree;
|
||||||
mod tokens;
|
mod tokens;
|
||||||
|
|
||||||
static NAME: &str = "expr";
|
const VERSION: &str = "version";
|
||||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
const HELP: &str = "help";
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.arg(Arg::with_name(VERSION).long(VERSION))
|
||||||
|
.arg(Arg::with_name(HELP).long(HELP))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let args = args
|
let args = args
|
||||||
|
@ -133,5 +140,5 @@ Environment variables:
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_version() {
|
fn print_version() {
|
||||||
println!("{} {}", NAME, VERSION);
|
println!("{} {}", executable!(), crate_version!());
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ rand = { version = "0.7", features = ["small_rng"] }
|
||||||
smallvec = { version = "0.6.14, < 1.0" }
|
smallvec = { version = "0.6.14, < 1.0" }
|
||||||
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" }
|
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" }
|
||||||
uucore_procs = { version = ">=0.0.5", package = "uucore_procs", path = "../../uucore_procs" }
|
uucore_procs = { version = ">=0.0.5", package = "uucore_procs", path = "../../uucore_procs" }
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
paste = "0.1.18"
|
paste = "0.1.18"
|
||||||
|
|
|
@ -36,11 +36,7 @@ fn print_factors_str(num_str: &str, w: &mut impl io::Write) -> Result<(), Box<dy
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(SUMMARY)
|
|
||||||
.arg(Arg::with_name(options::NUMBER).multiple(true))
|
|
||||||
.get_matches_from(args);
|
|
||||||
let stdout = stdout();
|
let stdout = stdout();
|
||||||
let mut w = io::BufWriter::new(stdout.lock());
|
let mut w = io::BufWriter::new(stdout.lock());
|
||||||
|
|
||||||
|
@ -68,3 +64,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(SUMMARY)
|
||||||
|
.arg(Arg::with_name(options::NUMBER).multiple(true))
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ edition = "2018"
|
||||||
path = "src/false.rs"
|
path = "src/false.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,14 @@
|
||||||
// * 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.
|
||||||
|
|
||||||
pub fn uumain(_: impl uucore::Args) -> i32 {
|
use clap::App;
|
||||||
|
use uucore::executable;
|
||||||
|
|
||||||
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
uu_app().get_matches_from(args);
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/fmt.rs"
|
path = "src/fmt.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
unicode-width = "0.1.5"
|
unicode-width = "0.1.5"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
|
|
|
@ -77,129 +77,7 @@ pub struct FmtOptions {
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_CROWN_MARGIN)
|
|
||||||
.short("c")
|
|
||||||
.long(OPT_CROWN_MARGIN)
|
|
||||||
.help(
|
|
||||||
"First and second line of paragraph
|
|
||||||
may have different indentations, in which
|
|
||||||
case the first line's indentation is preserved,
|
|
||||||
and each subsequent line's indentation matches the second line.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_TAGGED_PARAGRAPH)
|
|
||||||
.short("t")
|
|
||||||
.long("tagged-paragraph")
|
|
||||||
.help(
|
|
||||||
"Like -c, except that the first and second line of a paragraph *must*
|
|
||||||
have different indentation or they are treated as separate paragraphs.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_PRESERVE_HEADERS)
|
|
||||||
.short("m")
|
|
||||||
.long("preserve-headers")
|
|
||||||
.help(
|
|
||||||
"Attempt to detect and preserve mail headers in the input.
|
|
||||||
Be careful when combining this flag with -p.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_SPLIT_ONLY)
|
|
||||||
.short("s")
|
|
||||||
.long("split-only")
|
|
||||||
.help("Split lines only, do not reflow."),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_UNIFORM_SPACING)
|
|
||||||
.short("u")
|
|
||||||
.long("uniform-spacing")
|
|
||||||
.help(
|
|
||||||
"Insert exactly one
|
|
||||||
space between words, and two between sentences.
|
|
||||||
Sentence breaks in the input are detected as [?!.]
|
|
||||||
followed by two spaces or a newline; other punctuation
|
|
||||||
is not interpreted as a sentence break.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_PREFIX)
|
|
||||||
.short("p")
|
|
||||||
.long("prefix")
|
|
||||||
.help(
|
|
||||||
"Reformat only lines
|
|
||||||
beginning with PREFIX, reattaching PREFIX to reformatted lines.
|
|
||||||
Unless -x is specified, leading whitespace will be ignored
|
|
||||||
when matching PREFIX.",
|
|
||||||
)
|
|
||||||
.value_name("PREFIX"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_SKIP_PREFIX)
|
|
||||||
.short("P")
|
|
||||||
.long("skip-prefix")
|
|
||||||
.help(
|
|
||||||
"Do not reformat lines
|
|
||||||
beginning with PSKIP. Unless -X is specified, leading whitespace
|
|
||||||
will be ignored when matching PSKIP",
|
|
||||||
)
|
|
||||||
.value_name("PSKIP"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_EXACT_PREFIX)
|
|
||||||
.short("x")
|
|
||||||
.long("exact-prefix")
|
|
||||||
.help(
|
|
||||||
"PREFIX must match at the
|
|
||||||
beginning of the line with no preceding whitespace.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_EXACT_SKIP_PREFIX)
|
|
||||||
.short("X")
|
|
||||||
.long("exact-skip-prefix")
|
|
||||||
.help(
|
|
||||||
"PSKIP must match at the
|
|
||||||
beginning of the line with no preceding whitespace.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_WIDTH)
|
|
||||||
.short("w")
|
|
||||||
.long("width")
|
|
||||||
.help("Fill output lines up to a maximum of WIDTH columns, default 79.")
|
|
||||||
.value_name("WIDTH"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_GOAL)
|
|
||||||
.short("g")
|
|
||||||
.long("goal")
|
|
||||||
.help("Goal width, default ~0.94*WIDTH. Must be less than WIDTH.")
|
|
||||||
.value_name("GOAL"),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(OPT_QUICK).short("q").long("quick").help(
|
|
||||||
"Break lines more quickly at the
|
|
||||||
expense of a potentially more ragged appearance.",
|
|
||||||
))
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_TAB_WIDTH)
|
|
||||||
.short("T")
|
|
||||||
.long("tab-width")
|
|
||||||
.help(
|
|
||||||
"Treat tabs as TABWIDTH spaces for
|
|
||||||
determining line length, default 8. Note that this is used only for
|
|
||||||
calculating line lengths; tabs are preserved in the output.",
|
|
||||||
)
|
|
||||||
.value_name("TABWIDTH"),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true))
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let mut files: Vec<String> = matches
|
let mut files: Vec<String> = matches
|
||||||
.values_of(ARG_FILES)
|
.values_of(ARG_FILES)
|
||||||
|
@ -331,3 +209,127 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_CROWN_MARGIN)
|
||||||
|
.short("c")
|
||||||
|
.long(OPT_CROWN_MARGIN)
|
||||||
|
.help(
|
||||||
|
"First and second line of paragraph \
|
||||||
|
may have different indentations, in which \
|
||||||
|
case the first line's indentation is preserved, \
|
||||||
|
and each subsequent line's indentation matches the second line.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_TAGGED_PARAGRAPH)
|
||||||
|
.short("t")
|
||||||
|
.long("tagged-paragraph")
|
||||||
|
.help(
|
||||||
|
"Like -c, except that the first and second line of a paragraph *must* \
|
||||||
|
have different indentation or they are treated as separate paragraphs.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_PRESERVE_HEADERS)
|
||||||
|
.short("m")
|
||||||
|
.long("preserve-headers")
|
||||||
|
.help(
|
||||||
|
"Attempt to detect and preserve mail headers in the input. \
|
||||||
|
Be careful when combining this flag with -p.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_SPLIT_ONLY)
|
||||||
|
.short("s")
|
||||||
|
.long("split-only")
|
||||||
|
.help("Split lines only, do not reflow."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_UNIFORM_SPACING)
|
||||||
|
.short("u")
|
||||||
|
.long("uniform-spacing")
|
||||||
|
.help(
|
||||||
|
"Insert exactly one \
|
||||||
|
space between words, and two between sentences. \
|
||||||
|
Sentence breaks in the input are detected as [?!.] \
|
||||||
|
followed by two spaces or a newline; other punctuation \
|
||||||
|
is not interpreted as a sentence break.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_PREFIX)
|
||||||
|
.short("p")
|
||||||
|
.long("prefix")
|
||||||
|
.help(
|
||||||
|
"Reformat only lines \
|
||||||
|
beginning with PREFIX, reattaching PREFIX to reformatted lines. \
|
||||||
|
Unless -x is specified, leading whitespace will be ignored \
|
||||||
|
when matching PREFIX.",
|
||||||
|
)
|
||||||
|
.value_name("PREFIX"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_SKIP_PREFIX)
|
||||||
|
.short("P")
|
||||||
|
.long("skip-prefix")
|
||||||
|
.help(
|
||||||
|
"Do not reformat lines \
|
||||||
|
beginning with PSKIP. Unless -X is specified, leading whitespace \
|
||||||
|
will be ignored when matching PSKIP",
|
||||||
|
)
|
||||||
|
.value_name("PSKIP"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_EXACT_PREFIX)
|
||||||
|
.short("x")
|
||||||
|
.long("exact-prefix")
|
||||||
|
.help(
|
||||||
|
"PREFIX must match at the \
|
||||||
|
beginning of the line with no preceding whitespace.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_EXACT_SKIP_PREFIX)
|
||||||
|
.short("X")
|
||||||
|
.long("exact-skip-prefix")
|
||||||
|
.help(
|
||||||
|
"PSKIP must match at the \
|
||||||
|
beginning of the line with no preceding whitespace.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_WIDTH)
|
||||||
|
.short("w")
|
||||||
|
.long("width")
|
||||||
|
.help("Fill output lines up to a maximum of WIDTH columns, default 79.")
|
||||||
|
.value_name("WIDTH"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_GOAL)
|
||||||
|
.short("g")
|
||||||
|
.long("goal")
|
||||||
|
.help("Goal width, default ~0.94*WIDTH. Must be less than WIDTH.")
|
||||||
|
.value_name("GOAL"),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name(OPT_QUICK).short("q").long("quick").help(
|
||||||
|
"Break lines more quickly at the \
|
||||||
|
expense of a potentially more ragged appearance.",
|
||||||
|
))
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(OPT_TAB_WIDTH)
|
||||||
|
.short("T")
|
||||||
|
.long("tab-width")
|
||||||
|
.help(
|
||||||
|
"Treat tabs as TABWIDTH spaces for \
|
||||||
|
determining line length, default 8. Note that this is used only for \
|
||||||
|
calculating line lengths; tabs are preserved in the output.",
|
||||||
|
)
|
||||||
|
.value_name("TABWIDTH"),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true))
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/fold.rs"
|
path = "src/fold.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,35 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let (args, obs_width) = handle_obsolete(&args[..]);
|
let (args, obs_width) = handle_obsolete(&args[..]);
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().get_matches_from(args);
|
||||||
|
|
||||||
|
let bytes = matches.is_present(options::BYTES);
|
||||||
|
let spaces = matches.is_present(options::SPACES);
|
||||||
|
let poss_width = match matches.value_of(options::WIDTH) {
|
||||||
|
Some(v) => Some(v.to_owned()),
|
||||||
|
None => obs_width,
|
||||||
|
};
|
||||||
|
|
||||||
|
let width = match poss_width {
|
||||||
|
Some(inp_width) => match inp_width.parse::<usize>() {
|
||||||
|
Ok(width) => width,
|
||||||
|
Err(e) => crash!(1, "illegal width value (\"{}\"): {}", inp_width, e),
|
||||||
|
},
|
||||||
|
None => 80,
|
||||||
|
};
|
||||||
|
|
||||||
|
let files = match matches.values_of(options::FILE) {
|
||||||
|
Some(v) => v.map(|v| v.to_owned()).collect(),
|
||||||
|
None => vec!["-".to_owned()],
|
||||||
|
};
|
||||||
|
|
||||||
|
fold(files, bytes, spaces, width);
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
.name(NAME)
|
.name(NAME)
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.usage(SYNTAX)
|
.usage(SYNTAX)
|
||||||
|
@ -68,31 +96,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
|
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let bytes = matches.is_present(options::BYTES);
|
|
||||||
let spaces = matches.is_present(options::SPACES);
|
|
||||||
let poss_width = match matches.value_of(options::WIDTH) {
|
|
||||||
Some(v) => Some(v.to_owned()),
|
|
||||||
None => obs_width,
|
|
||||||
};
|
|
||||||
|
|
||||||
let width = match poss_width {
|
|
||||||
Some(inp_width) => match inp_width.parse::<usize>() {
|
|
||||||
Ok(width) => width,
|
|
||||||
Err(e) => crash!(1, "illegal width value (\"{}\"): {}", inp_width, e),
|
|
||||||
},
|
|
||||||
None => 80,
|
|
||||||
};
|
|
||||||
|
|
||||||
let files = match matches.values_of(options::FILE) {
|
|
||||||
Some(v) => v.map(|v| v.to_owned()).collect(),
|
|
||||||
None => vec!["-".to_owned()],
|
|
||||||
};
|
|
||||||
|
|
||||||
fold(files, bytes, spaces, width);
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
|
fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ path = "src/groups.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "process"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "process"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "groups"
|
name = "groups"
|
||||||
|
|
|
@ -5,6 +5,13 @@
|
||||||
//
|
//
|
||||||
// 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.
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
// Test suite summary for GNU coreutils 8.32.162-4eda
|
||||||
|
// ============================================================================
|
||||||
|
// PASS: tests/misc/groups-dash.sh
|
||||||
|
// PASS: tests/misc/groups-process-all.sh
|
||||||
|
// PASS: tests/misc/groups-version.sh
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) passwd
|
// spell-checker:ignore (ToDO) passwd
|
||||||
|
|
||||||
|
@ -14,50 +21,77 @@ use uucore::entries::{get_groups_gnu, gid2grp, Locate, Passwd};
|
||||||
|
|
||||||
use clap::{crate_version, App, Arg};
|
use clap::{crate_version, App, Arg};
|
||||||
|
|
||||||
static ABOUT: &str = "display current group names";
|
mod options {
|
||||||
static OPT_USER: &str = "user";
|
pub const USERS: &str = "USERNAME";
|
||||||
|
}
|
||||||
|
static ABOUT: &str = "Print group memberships for each USERNAME or, \
|
||||||
|
if no USERNAME is specified, for\nthe current process \
|
||||||
|
(which may differ if the groups data‐base has changed).";
|
||||||
|
|
||||||
fn get_usage() -> String {
|
fn get_usage() -> String {
|
||||||
format!("{0} [USERNAME]", executable!())
|
format!("{0} [OPTION]... [USERNAME]...", executable!())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(Arg::with_name(OPT_USER))
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
match matches.value_of(OPT_USER) {
|
let users: Vec<String> = matches
|
||||||
None => {
|
.values_of(options::USERS)
|
||||||
|
.map(|v| v.map(ToString::to_string).collect())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let mut exit_code = 0;
|
||||||
|
|
||||||
|
if users.is_empty() {
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
get_groups_gnu(None)
|
get_groups_gnu(None)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&g| gid2grp(g).unwrap())
|
.map(|&gid| gid2grp(gid).unwrap_or_else(|_| {
|
||||||
|
show_error!("cannot find name for group ID {}", gid);
|
||||||
|
exit_code = 1;
|
||||||
|
gid.to_string()
|
||||||
|
}))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" ")
|
.join(" ")
|
||||||
);
|
);
|
||||||
0
|
return exit_code;
|
||||||
}
|
}
|
||||||
Some(user) => {
|
|
||||||
if let Ok(p) = Passwd::locate(user) {
|
for user in users {
|
||||||
|
if let Ok(p) = Passwd::locate(user.as_str()) {
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{} : {}",
|
||||||
|
user,
|
||||||
p.belongs_to()
|
p.belongs_to()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&g| gid2grp(g).unwrap())
|
.map(|&gid| gid2grp(gid).unwrap_or_else(|_| {
|
||||||
|
show_error!("cannot find name for group ID {}", gid);
|
||||||
|
exit_code = 1;
|
||||||
|
gid.to_string()
|
||||||
|
}))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" ")
|
.join(" ")
|
||||||
);
|
);
|
||||||
0
|
|
||||||
} else {
|
} else {
|
||||||
crash!(1, "unknown user {}", user);
|
show_error!("'{}': no such user", user);
|
||||||
|
exit_code = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exit_code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::USERS)
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name(options::USERS),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ path = "src/hashsum.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
digest = "0.6.2"
|
digest = "0.6.2"
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
hex = "0.2.0"
|
hex = "0.2.0"
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
md5 = "0.3.5"
|
md5 = "0.3.5"
|
||||||
|
|
|
@ -285,119 +285,7 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
|
||||||
// Default binary in Windows, text mode otherwise
|
// Default binary in Windows, text mode otherwise
|
||||||
let binary_flag_default = cfg!(windows);
|
let binary_flag_default = cfg!(windows);
|
||||||
|
|
||||||
let binary_help = format!(
|
let app = uu_app(&binary_name);
|
||||||
"read in binary mode{}",
|
|
||||||
if binary_flag_default {
|
|
||||||
" (default)"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let text_help = format!(
|
|
||||||
"read in text mode{}",
|
|
||||||
if binary_flag_default {
|
|
||||||
""
|
|
||||||
} else {
|
|
||||||
" (default)"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut app = App::new(executable!())
|
|
||||||
.version(crate_version!())
|
|
||||||
.about("Compute and check message digests.")
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("binary")
|
|
||||||
.short("b")
|
|
||||||
.long("binary")
|
|
||||||
.help(&binary_help),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("check")
|
|
||||||
.short("c")
|
|
||||||
.long("check")
|
|
||||||
.help("read hashsums from the FILEs and check them"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("tag")
|
|
||||||
.long("tag")
|
|
||||||
.help("create a BSD-style checksum"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("text")
|
|
||||||
.short("t")
|
|
||||||
.long("text")
|
|
||||||
.help(&text_help)
|
|
||||||
.conflicts_with("binary"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("quiet")
|
|
||||||
.short("q")
|
|
||||||
.long("quiet")
|
|
||||||
.help("don't print OK for each successfully verified file"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("status")
|
|
||||||
.short("s")
|
|
||||||
.long("status")
|
|
||||||
.help("don't output anything, status code shows success"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("strict")
|
|
||||||
.long("strict")
|
|
||||||
.help("exit non-zero for improperly formatted checksum lines"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("warn")
|
|
||||||
.short("w")
|
|
||||||
.long("warn")
|
|
||||||
.help("warn about improperly formatted checksum lines"),
|
|
||||||
)
|
|
||||||
// Needed for variable-length output sums (e.g. SHAKE)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("bits")
|
|
||||||
.long("bits")
|
|
||||||
.help("set the size of the output (only for SHAKE)")
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("BITS")
|
|
||||||
// XXX: should we actually use validators? they're not particularly efficient
|
|
||||||
.validator(is_valid_bit_num),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("FILE")
|
|
||||||
.index(1)
|
|
||||||
.multiple(true)
|
|
||||||
.value_name("FILE"),
|
|
||||||
);
|
|
||||||
|
|
||||||
if !is_custom_binary(&binary_name) {
|
|
||||||
let algorithms = &[
|
|
||||||
("md5", "work with MD5"),
|
|
||||||
("sha1", "work with SHA1"),
|
|
||||||
("sha224", "work with SHA224"),
|
|
||||||
("sha256", "work with SHA256"),
|
|
||||||
("sha384", "work with SHA384"),
|
|
||||||
("sha512", "work with SHA512"),
|
|
||||||
("sha3", "work with SHA3"),
|
|
||||||
("sha3-224", "work with SHA3-224"),
|
|
||||||
("sha3-256", "work with SHA3-256"),
|
|
||||||
("sha3-384", "work with SHA3-384"),
|
|
||||||
("sha3-512", "work with SHA3-512"),
|
|
||||||
(
|
|
||||||
"shake128",
|
|
||||||
"work with SHAKE128 using BITS for the output size",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"shake256",
|
|
||||||
"work with SHAKE256 using BITS for the output size",
|
|
||||||
),
|
|
||||||
("b2sum", "work with BLAKE2"),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (name, desc) in algorithms {
|
|
||||||
app = app.arg(Arg::with_name(name).long(name).help(desc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: this should use get_matches_from_safe() and crash!(), but at the moment that just
|
// FIXME: this should use get_matches_from_safe() and crash!(), but at the moment that just
|
||||||
// causes "error: " to be printed twice (once from crash!() and once from clap). With
|
// causes "error: " to be printed twice (once from crash!() and once from clap). With
|
||||||
|
@ -445,6 +333,124 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app_common() -> App<'static, 'static> {
|
||||||
|
#[cfg(windows)]
|
||||||
|
const BINARY_HELP: &str = "read in binary mode (default)";
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
const BINARY_HELP: &str = "read in binary mode";
|
||||||
|
#[cfg(windows)]
|
||||||
|
const TEXT_HELP: &str = "read in text mode";
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
const TEXT_HELP: &str = "read in text mode (default)";
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about("Compute and check message digests.")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("binary")
|
||||||
|
.short("b")
|
||||||
|
.long("binary")
|
||||||
|
.help(BINARY_HELP),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("check")
|
||||||
|
.short("c")
|
||||||
|
.long("check")
|
||||||
|
.help("read hashsums from the FILEs and check them"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("tag")
|
||||||
|
.long("tag")
|
||||||
|
.help("create a BSD-style checksum"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("text")
|
||||||
|
.short("t")
|
||||||
|
.long("text")
|
||||||
|
.help(TEXT_HELP)
|
||||||
|
.conflicts_with("binary"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("quiet")
|
||||||
|
.short("q")
|
||||||
|
.long("quiet")
|
||||||
|
.help("don't print OK for each successfully verified file"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("status")
|
||||||
|
.short("s")
|
||||||
|
.long("status")
|
||||||
|
.help("don't output anything, status code shows success"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("strict")
|
||||||
|
.long("strict")
|
||||||
|
.help("exit non-zero for improperly formatted checksum lines"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("warn")
|
||||||
|
.short("w")
|
||||||
|
.long("warn")
|
||||||
|
.help("warn about improperly formatted checksum lines"),
|
||||||
|
)
|
||||||
|
// Needed for variable-length output sums (e.g. SHAKE)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("bits")
|
||||||
|
.long("bits")
|
||||||
|
.help("set the size of the output (only for SHAKE)")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("BITS")
|
||||||
|
// XXX: should we actually use validators? they're not particularly efficient
|
||||||
|
.validator(is_valid_bit_num),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("FILE")
|
||||||
|
.index(1)
|
||||||
|
.multiple(true)
|
||||||
|
.value_name("FILE"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app_custom() -> App<'static, 'static> {
|
||||||
|
let mut app = uu_app_common();
|
||||||
|
let algorithms = &[
|
||||||
|
("md5", "work with MD5"),
|
||||||
|
("sha1", "work with SHA1"),
|
||||||
|
("sha224", "work with SHA224"),
|
||||||
|
("sha256", "work with SHA256"),
|
||||||
|
("sha384", "work with SHA384"),
|
||||||
|
("sha512", "work with SHA512"),
|
||||||
|
("sha3", "work with SHA3"),
|
||||||
|
("sha3-224", "work with SHA3-224"),
|
||||||
|
("sha3-256", "work with SHA3-256"),
|
||||||
|
("sha3-384", "work with SHA3-384"),
|
||||||
|
("sha3-512", "work with SHA3-512"),
|
||||||
|
(
|
||||||
|
"shake128",
|
||||||
|
"work with SHAKE128 using BITS for the output size",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"shake256",
|
||||||
|
"work with SHAKE256 using BITS for the output size",
|
||||||
|
),
|
||||||
|
("b2sum", "work with BLAKE2"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (name, desc) in algorithms {
|
||||||
|
app = app.arg(Arg::with_name(name).long(name).help(desc));
|
||||||
|
}
|
||||||
|
app
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashsum is handled differently in build.rs, therefore this is not the same
|
||||||
|
// as in other utilities.
|
||||||
|
fn uu_app(binary_name: &str) -> App<'static, 'static> {
|
||||||
|
if !is_custom_binary(binary_name) {
|
||||||
|
uu_app_custom()
|
||||||
|
} else {
|
||||||
|
uu_app_common()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn hashsum<'a, I>(mut options: Options, files: I) -> Result<(), i32>
|
fn hashsum<'a, I>(mut options: Options, files: I) -> Result<(), i32>
|
||||||
where
|
where
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/head.rs"
|
path = "src/head.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["ringbuffer"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["ringbuffer"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ mod take;
|
||||||
use lines::zlines;
|
use lines::zlines;
|
||||||
use take::take_all_but;
|
use take::take_all_but;
|
||||||
|
|
||||||
fn app<'a>() -> App<'a, 'a> {
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
App::new(executable!())
|
App::new(executable!())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about(ABOUT)
|
.about(ABOUT)
|
||||||
|
@ -167,7 +167,7 @@ impl HeadOptions {
|
||||||
|
|
||||||
///Construct options from matches
|
///Construct options from matches
|
||||||
pub fn get_from(args: impl uucore::Args) -> Result<Self, String> {
|
pub fn get_from(args: impl uucore::Args) -> Result<Self, String> {
|
||||||
let matches = app().get_matches_from(arg_iterate(args)?);
|
let matches = uu_app().get_matches_from(arg_iterate(args)?);
|
||||||
|
|
||||||
let mut options = HeadOptions::new();
|
let mut options = HeadOptions::new();
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ edition = "2018"
|
||||||
path = "src/hostid.rs"
|
path = "src/hostid.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -10,12 +10,10 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
|
use clap::{crate_version, App};
|
||||||
use libc::c_long;
|
use libc::c_long;
|
||||||
use uucore::InvalidEncodingHandling;
|
|
||||||
|
|
||||||
static SYNTAX: &str = "[options]";
|
static SYNTAX: &str = "[options]";
|
||||||
static SUMMARY: &str = "";
|
|
||||||
static LONG_HELP: &str = "";
|
|
||||||
|
|
||||||
// currently rust libc interface doesn't include gethostid
|
// currently rust libc interface doesn't include gethostid
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -23,14 +21,17 @@ extern "C" {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
app!(SYNTAX, SUMMARY, LONG_HELP).parse(
|
uu_app().get_matches_from(args);
|
||||||
args.collect_str(InvalidEncodingHandling::ConvertLossy)
|
|
||||||
.accept_any(),
|
|
||||||
);
|
|
||||||
hostid();
|
hostid();
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.usage(SYNTAX)
|
||||||
|
}
|
||||||
|
|
||||||
fn hostid() {
|
fn hostid() {
|
||||||
/*
|
/*
|
||||||
* POSIX says gethostid returns a "32-bit identifier" but is silent
|
* POSIX says gethostid returns a "32-bit identifier" but is silent
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/hostname.rs"
|
path = "src/hostname.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
hostname = { version = "0.3", features = ["set"] }
|
hostname = { version = "0.3", features = ["set"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["wide"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["wide"] }
|
||||||
|
|
|
@ -52,10 +52,25 @@ fn get_usage() -> String {
|
||||||
}
|
}
|
||||||
fn execute(args: impl uucore::Args) -> i32 {
|
fn execute(args: impl uucore::Args) -> i32 {
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
|
|
||||||
|
match matches.value_of(OPT_HOST) {
|
||||||
|
None => display_hostname(&matches),
|
||||||
|
Some(host) => {
|
||||||
|
if let Err(err) = hostname::set(host) {
|
||||||
|
show_error!("{}", err);
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about(ABOUT)
|
.about(ABOUT)
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(OPT_DOMAIN)
|
Arg::with_name(OPT_DOMAIN)
|
||||||
.short("d")
|
.short("d")
|
||||||
|
@ -80,19 +95,6 @@ fn execute(args: impl uucore::Args) -> i32 {
|
||||||
possible",
|
possible",
|
||||||
))
|
))
|
||||||
.arg(Arg::with_name(OPT_HOST))
|
.arg(Arg::with_name(OPT_HOST))
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
match matches.value_of(OPT_HOST) {
|
|
||||||
None => display_hostname(&matches),
|
|
||||||
Some(host) => {
|
|
||||||
if let Err(err) = hostname::set(host) {
|
|
||||||
show_error!("{}", err);
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_hostname(matches: &ArgMatches) -> i32 {
|
fn display_hostname(matches: &ArgMatches) -> i32 {
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/id.rs"
|
path = "src/id.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "process"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "process"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
selinux = "0.1.1"
|
selinux = "0.1.1"
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
// * Help text based on BSD's `id` manpage and GNU's `id` manpage.
|
// * Help text based on BSD's `id` manpage and GNU's `id` manpage.
|
||||||
//
|
//
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) asid auditid auditinfo auid cstr egid emod euid getaudit getlogin gflag nflag pline rflag termid uflag gsflag zflag cflag testsuite
|
// spell-checker:ignore (ToDO) asid auditid auditinfo auid cstr egid emod euid getaudit getlogin gflag nflag pline rflag termid uflag gsflag zflag cflag
|
||||||
|
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
@ -119,109 +119,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
let after_help = get_description();
|
let after_help = get_description();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app()
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
.usage(&usage[..])
|
||||||
.after_help(&after_help[..])
|
.after_help(&after_help[..])
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::OPT_AUDIT)
|
|
||||||
.short("A")
|
|
||||||
.conflicts_with_all(&[
|
|
||||||
options::OPT_GROUP,
|
|
||||||
options::OPT_EFFECTIVE_USER,
|
|
||||||
options::OPT_HUMAN_READABLE,
|
|
||||||
options::OPT_PASSWORD,
|
|
||||||
options::OPT_GROUPS,
|
|
||||||
options::OPT_ZERO,
|
|
||||||
])
|
|
||||||
.help(
|
|
||||||
"Display the process audit user ID and other process audit properties,\n\
|
|
||||||
which requires privilege (not available on Linux).",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::OPT_EFFECTIVE_USER)
|
|
||||||
.short("u")
|
|
||||||
.long(options::OPT_EFFECTIVE_USER)
|
|
||||||
.conflicts_with(options::OPT_GROUP)
|
|
||||||
.help("Display only the effective user ID as a number."),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::OPT_GROUP)
|
|
||||||
.short("g")
|
|
||||||
.long(options::OPT_GROUP)
|
|
||||||
.conflicts_with(options::OPT_EFFECTIVE_USER)
|
|
||||||
.help("Display only the effective group ID as a number"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::OPT_GROUPS)
|
|
||||||
.short("G")
|
|
||||||
.long(options::OPT_GROUPS)
|
|
||||||
.conflicts_with_all(&[
|
|
||||||
options::OPT_GROUP,
|
|
||||||
options::OPT_EFFECTIVE_USER,
|
|
||||||
options::OPT_CONTEXT,
|
|
||||||
options::OPT_HUMAN_READABLE,
|
|
||||||
options::OPT_PASSWORD,
|
|
||||||
options::OPT_AUDIT,
|
|
||||||
])
|
|
||||||
.help(
|
|
||||||
"Display only the different group IDs as white-space separated numbers, \
|
|
||||||
in no particular order.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::OPT_HUMAN_READABLE)
|
|
||||||
.short("p")
|
|
||||||
.help("Make the output human-readable. Each display is on a separate line."),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::OPT_NAME)
|
|
||||||
.short("n")
|
|
||||||
.long(options::OPT_NAME)
|
|
||||||
.help(
|
|
||||||
"Display the name of the user or group ID for the -G, -g and -u options \
|
|
||||||
instead of the number.\nIf any of the ID numbers cannot be mapped into \
|
|
||||||
names, the number will be displayed as usual.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::OPT_PASSWORD)
|
|
||||||
.short("P")
|
|
||||||
.help("Display the id as a password file entry."),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::OPT_REAL_ID)
|
|
||||||
.short("r")
|
|
||||||
.long(options::OPT_REAL_ID)
|
|
||||||
.help(
|
|
||||||
"Display the real ID for the -G, -g and -u options instead of \
|
|
||||||
the effective ID.",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::OPT_ZERO)
|
|
||||||
.short("z")
|
|
||||||
.long(options::OPT_ZERO)
|
|
||||||
.help(
|
|
||||||
"delimit entries with NUL characters, not whitespace;\n\
|
|
||||||
not permitted in default format",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::OPT_CONTEXT)
|
|
||||||
.short("Z")
|
|
||||||
.long(options::OPT_CONTEXT)
|
|
||||||
.conflicts_with_all(&[options::OPT_GROUP, options::OPT_EFFECTIVE_USER])
|
|
||||||
.help("print only the security context of the process"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::ARG_USERS)
|
|
||||||
.multiple(true)
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name(options::ARG_USERS),
|
|
||||||
)
|
|
||||||
.get_matches_from(args);
|
.get_matches_from(args);
|
||||||
|
|
||||||
let users: Vec<String> = matches
|
let users: Vec<String> = matches
|
||||||
|
@ -302,7 +202,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
match Passwd::locate(users[i].as_str()) {
|
match Passwd::locate(users[i].as_str()) {
|
||||||
Ok(p) => Some(p),
|
Ok(p) => Some(p),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
show_error!("‘{}’: no such user", users[i]);
|
show_error!("'{}': no such user", users[i]);
|
||||||
exit_code = 1;
|
exit_code = 1;
|
||||||
if i + 1 >= users.len() {
|
if i + 1 >= users.len() {
|
||||||
break;
|
break;
|
||||||
|
@ -418,6 +318,110 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
exit_code
|
exit_code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OPT_AUDIT)
|
||||||
|
.short("A")
|
||||||
|
.conflicts_with_all(&[
|
||||||
|
options::OPT_GROUP,
|
||||||
|
options::OPT_EFFECTIVE_USER,
|
||||||
|
options::OPT_HUMAN_READABLE,
|
||||||
|
options::OPT_PASSWORD,
|
||||||
|
options::OPT_GROUPS,
|
||||||
|
options::OPT_ZERO,
|
||||||
|
])
|
||||||
|
.help(
|
||||||
|
"Display the process audit user ID and other process audit properties,\n\
|
||||||
|
which requires privilege (not available on Linux).",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OPT_EFFECTIVE_USER)
|
||||||
|
.short("u")
|
||||||
|
.long(options::OPT_EFFECTIVE_USER)
|
||||||
|
.conflicts_with(options::OPT_GROUP)
|
||||||
|
.help("Display only the effective user ID as a number."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OPT_GROUP)
|
||||||
|
.short("g")
|
||||||
|
.long(options::OPT_GROUP)
|
||||||
|
.conflicts_with(options::OPT_EFFECTIVE_USER)
|
||||||
|
.help("Display only the effective group ID as a number"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OPT_GROUPS)
|
||||||
|
.short("G")
|
||||||
|
.long(options::OPT_GROUPS)
|
||||||
|
.conflicts_with_all(&[
|
||||||
|
options::OPT_GROUP,
|
||||||
|
options::OPT_EFFECTIVE_USER,
|
||||||
|
options::OPT_CONTEXT,
|
||||||
|
options::OPT_HUMAN_READABLE,
|
||||||
|
options::OPT_PASSWORD,
|
||||||
|
options::OPT_AUDIT,
|
||||||
|
])
|
||||||
|
.help(
|
||||||
|
"Display only the different group IDs as white-space separated numbers, \
|
||||||
|
in no particular order.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OPT_HUMAN_READABLE)
|
||||||
|
.short("p")
|
||||||
|
.help("Make the output human-readable. Each display is on a separate line."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OPT_NAME)
|
||||||
|
.short("n")
|
||||||
|
.long(options::OPT_NAME)
|
||||||
|
.help(
|
||||||
|
"Display the name of the user or group ID for the -G, -g and -u options \
|
||||||
|
instead of the number.\nIf any of the ID numbers cannot be mapped into \
|
||||||
|
names, the number will be displayed as usual.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OPT_PASSWORD)
|
||||||
|
.short("P")
|
||||||
|
.help("Display the id as a password file entry."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OPT_REAL_ID)
|
||||||
|
.short("r")
|
||||||
|
.long(options::OPT_REAL_ID)
|
||||||
|
.help(
|
||||||
|
"Display the real ID for the -G, -g and -u options instead of \
|
||||||
|
the effective ID.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OPT_ZERO)
|
||||||
|
.short("z")
|
||||||
|
.long(options::OPT_ZERO)
|
||||||
|
.help(
|
||||||
|
"delimit entries with NUL characters, not whitespace;\n\
|
||||||
|
not permitted in default format",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OPT_CONTEXT)
|
||||||
|
.short("Z")
|
||||||
|
.long(options::OPT_CONTEXT)
|
||||||
|
.conflicts_with_all(&[options::OPT_GROUP, options::OPT_EFFECTIVE_USER])
|
||||||
|
.help("print only the security context of the process"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::ARG_USERS)
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name(options::ARG_USERS),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn pretty(possible_pw: Option<Passwd>) {
|
fn pretty(possible_pw: Option<Passwd>) {
|
||||||
if let Some(p) = possible_pw {
|
if let Some(p) = possible_pw {
|
||||||
print!("uid\t{}\ngroups\t", p.name());
|
print!("uid\t{}\ngroups\t", p.name());
|
||||||
|
|
|
@ -18,7 +18,7 @@ edition = "2018"
|
||||||
path = "src/install.rs"
|
path = "src/install.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
file_diff = "1.0.0"
|
file_diff = "1.0.0"
|
||||||
libc = ">= 0.2"
|
libc = ">= 0.2"
|
||||||
|
|
|
@ -15,6 +15,7 @@ extern crate uucore;
|
||||||
use clap::{crate_version, App, Arg, ArgMatches};
|
use clap::{crate_version, App, Arg, ArgMatches};
|
||||||
use file_diff::diff;
|
use file_diff::diff;
|
||||||
use filetime::{set_file_times, FileTime};
|
use filetime::{set_file_times, FileTime};
|
||||||
|
use uucore::backup_control::{self, BackupMode};
|
||||||
use uucore::entries::{grp2gid, usr2uid};
|
use uucore::entries::{grp2gid, usr2uid};
|
||||||
use uucore::perms::{wrap_chgrp, wrap_chown, Verbosity};
|
use uucore::perms::{wrap_chgrp, wrap_chown, Verbosity};
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@ const DEFAULT_STRIP_PROGRAM: &str = "strip";
|
||||||
pub struct Behavior {
|
pub struct Behavior {
|
||||||
main_function: MainFunction,
|
main_function: MainFunction,
|
||||||
specified_mode: Option<u32>,
|
specified_mode: Option<u32>,
|
||||||
|
backup_mode: BackupMode,
|
||||||
suffix: String,
|
suffix: String,
|
||||||
owner: String,
|
owner: String,
|
||||||
group: String,
|
group: String,
|
||||||
|
@ -42,6 +44,7 @@ pub struct Behavior {
|
||||||
strip: bool,
|
strip: bool,
|
||||||
strip_program: String,
|
strip_program: String,
|
||||||
create_leading: bool,
|
create_leading: bool,
|
||||||
|
target_dir: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
|
@ -67,7 +70,7 @@ static ABOUT: &str = "Copy SOURCE to DEST or multiple SOURCE(s) to the existing
|
||||||
|
|
||||||
static OPT_COMPARE: &str = "compare";
|
static OPT_COMPARE: &str = "compare";
|
||||||
static OPT_BACKUP: &str = "backup";
|
static OPT_BACKUP: &str = "backup";
|
||||||
static OPT_BACKUP_2: &str = "backup2";
|
static OPT_BACKUP_NO_ARG: &str = "backup2";
|
||||||
static OPT_DIRECTORY: &str = "directory";
|
static OPT_DIRECTORY: &str = "directory";
|
||||||
static OPT_IGNORED: &str = "ignored";
|
static OPT_IGNORED: &str = "ignored";
|
||||||
static OPT_CREATE_LEADING: &str = "create-leading";
|
static OPT_CREATE_LEADING: &str = "create-leading";
|
||||||
|
@ -97,21 +100,49 @@ fn get_usage() -> String {
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
|
|
||||||
|
let paths: Vec<String> = matches
|
||||||
|
.values_of(ARG_FILES)
|
||||||
|
.map(|v| v.map(ToString::to_string).collect())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if let Err(s) = check_unimplemented(&matches) {
|
||||||
|
show_error!("Unimplemented feature: {}", s);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
let behavior = match behavior(&matches) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(ret) => {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match behavior.main_function {
|
||||||
|
MainFunction::Directory => directory(paths, behavior),
|
||||||
|
MainFunction::Standard => standard(paths, behavior),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about(ABOUT)
|
.about(ABOUT)
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(OPT_BACKUP)
|
Arg::with_name(OPT_BACKUP)
|
||||||
.long(OPT_BACKUP)
|
.long(OPT_BACKUP)
|
||||||
.help("(unimplemented) make a backup of each existing destination file")
|
.help("make a backup of each existing destination file")
|
||||||
|
.takes_value(true)
|
||||||
|
.require_equals(true)
|
||||||
|
.min_values(0)
|
||||||
.value_name("CONTROL")
|
.value_name("CONTROL")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
// TODO implement flag
|
// TODO implement flag
|
||||||
Arg::with_name(OPT_BACKUP_2)
|
Arg::with_name(OPT_BACKUP_NO_ARG)
|
||||||
.short("b")
|
.short("b")
|
||||||
.help("(unimplemented) like --backup but does not accept an argument")
|
.help("like --backup but does not accept an argument")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(OPT_IGNORED)
|
Arg::with_name(OPT_IGNORED)
|
||||||
|
@ -184,7 +215,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
Arg::with_name(OPT_SUFFIX)
|
Arg::with_name(OPT_SUFFIX)
|
||||||
.short("S")
|
.short("S")
|
||||||
.long(OPT_SUFFIX)
|
.long(OPT_SUFFIX)
|
||||||
.help("(unimplemented) override the usual backup suffix")
|
.help("override the usual backup suffix")
|
||||||
.value_name("SUFFIX")
|
.value_name("SUFFIX")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.min_values(1)
|
.min_values(1)
|
||||||
|
@ -194,7 +225,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
Arg::with_name(OPT_TARGET_DIRECTORY)
|
Arg::with_name(OPT_TARGET_DIRECTORY)
|
||||||
.short("t")
|
.short("t")
|
||||||
.long(OPT_TARGET_DIRECTORY)
|
.long(OPT_TARGET_DIRECTORY)
|
||||||
.help("(unimplemented) move all SOURCE arguments into DIRECTORY")
|
.help("move all SOURCE arguments into DIRECTORY")
|
||||||
.value_name("DIRECTORY")
|
.value_name("DIRECTORY")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
@ -227,29 +258,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.value_name("CONTEXT")
|
.value_name("CONTEXT")
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
|
.arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let paths: Vec<String> = matches
|
|
||||||
.values_of(ARG_FILES)
|
|
||||||
.map(|v| v.map(ToString::to_string).collect())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if let Err(s) = check_unimplemented(&matches) {
|
|
||||||
show_error!("Unimplemented feature: {}", s);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
let behavior = match behavior(&matches) {
|
|
||||||
Ok(x) => x,
|
|
||||||
Err(ret) => {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match behavior.main_function {
|
|
||||||
MainFunction::Directory => directory(paths, behavior),
|
|
||||||
MainFunction::Standard => standard(paths, behavior),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check for unimplemented command line arguments.
|
/// Check for unimplemented command line arguments.
|
||||||
|
@ -262,15 +270,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
|
fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
|
||||||
if matches.is_present(OPT_BACKUP) {
|
if matches.is_present(OPT_NO_TARGET_DIRECTORY) {
|
||||||
Err("--backup")
|
|
||||||
} else if matches.is_present(OPT_BACKUP_2) {
|
|
||||||
Err("-b")
|
|
||||||
} else if matches.is_present(OPT_SUFFIX) {
|
|
||||||
Err("--suffix, -S")
|
|
||||||
} else if matches.is_present(OPT_TARGET_DIRECTORY) {
|
|
||||||
Err("--target-directory, -t")
|
|
||||||
} else if matches.is_present(OPT_NO_TARGET_DIRECTORY) {
|
|
||||||
Err("--no-target-directory, -T")
|
Err("--no-target-directory, -T")
|
||||||
} else if matches.is_present(OPT_PRESERVE_CONTEXT) {
|
} else if matches.is_present(OPT_PRESERVE_CONTEXT) {
|
||||||
Err("--preserve-context, -P")
|
Err("--preserve-context, -P")
|
||||||
|
@ -308,16 +308,16 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let backup_suffix = if matches.is_present(OPT_SUFFIX) {
|
let target_dir = matches.value_of(OPT_TARGET_DIRECTORY).map(|d| d.to_owned());
|
||||||
matches.value_of(OPT_SUFFIX).ok_or(1)?
|
|
||||||
} else {
|
|
||||||
"~"
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Behavior {
|
Ok(Behavior {
|
||||||
main_function,
|
main_function,
|
||||||
specified_mode,
|
specified_mode,
|
||||||
suffix: backup_suffix.to_string(),
|
backup_mode: backup_control::determine_backup_mode(
|
||||||
|
matches.is_present(OPT_BACKUP_NO_ARG) || matches.is_present(OPT_BACKUP),
|
||||||
|
matches.value_of(OPT_BACKUP),
|
||||||
|
),
|
||||||
|
suffix: backup_control::determine_backup_suffix(matches.value_of(OPT_SUFFIX)),
|
||||||
owner: matches.value_of(OPT_OWNER).unwrap_or("").to_string(),
|
owner: matches.value_of(OPT_OWNER).unwrap_or("").to_string(),
|
||||||
group: matches.value_of(OPT_GROUP).unwrap_or("").to_string(),
|
group: matches.value_of(OPT_GROUP).unwrap_or("").to_string(),
|
||||||
verbose: matches.is_present(OPT_VERBOSE),
|
verbose: matches.is_present(OPT_VERBOSE),
|
||||||
|
@ -330,6 +330,7 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
||||||
.unwrap_or(DEFAULT_STRIP_PROGRAM),
|
.unwrap_or(DEFAULT_STRIP_PROGRAM),
|
||||||
),
|
),
|
||||||
create_leading: matches.is_present(OPT_CREATE_LEADING),
|
create_leading: matches.is_present(OPT_CREATE_LEADING),
|
||||||
|
target_dir,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,16 +393,17 @@ fn is_new_file_path(path: &Path) -> bool {
|
||||||
///
|
///
|
||||||
/// Returns an integer intended as a program return code.
|
/// Returns an integer intended as a program return code.
|
||||||
///
|
///
|
||||||
fn standard(paths: Vec<String>, b: Behavior) -> i32 {
|
fn standard(mut paths: Vec<String>, b: Behavior) -> i32 {
|
||||||
let sources = &paths[0..paths.len() - 1]
|
let target: PathBuf = b
|
||||||
.iter()
|
.target_dir
|
||||||
.map(PathBuf::from)
|
.clone()
|
||||||
.collect::<Vec<_>>();
|
.unwrap_or_else(|| paths.pop().unwrap())
|
||||||
|
.into();
|
||||||
|
|
||||||
let target = Path::new(paths.last().unwrap());
|
let sources = &paths.iter().map(PathBuf::from).collect::<Vec<_>>();
|
||||||
|
|
||||||
if sources.len() > 1 || (target.exists() && target.is_dir()) {
|
if sources.len() > 1 || (target.exists() && target.is_dir()) {
|
||||||
copy_files_into_dir(sources, &target.to_path_buf(), &b)
|
copy_files_into_dir(sources, &target, &b)
|
||||||
} else {
|
} else {
|
||||||
if let Some(parent) = target.parent() {
|
if let Some(parent) = target.parent() {
|
||||||
if !parent.exists() && b.create_leading {
|
if !parent.exists() && b.create_leading {
|
||||||
|
@ -417,8 +419,8 @@ fn standard(paths: Vec<String>, b: Behavior) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if target.is_file() || is_new_file_path(target) {
|
if target.is_file() || is_new_file_path(&target) {
|
||||||
copy_file_to_file(&sources[0], &target.to_path_buf(), &b)
|
copy_file_to_file(&sources[0], &target, &b)
|
||||||
} else {
|
} else {
|
||||||
show_error!(
|
show_error!(
|
||||||
"invalid target {}: No such file or directory",
|
"invalid target {}: No such file or directory",
|
||||||
|
@ -512,6 +514,28 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> Result<(), ()> {
|
||||||
if b.compare && !need_copy(from, to, b) {
|
if b.compare && !need_copy(from, to, b) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
// Declare the path here as we may need it for the verbose output below.
|
||||||
|
let mut backup_path = None;
|
||||||
|
|
||||||
|
// Perform backup, if any, before overwriting 'to'
|
||||||
|
//
|
||||||
|
// The codes actually making use of the backup process don't seem to agree
|
||||||
|
// on how best to approach the issue. (mv and ln, for example)
|
||||||
|
if to.exists() {
|
||||||
|
backup_path = backup_control::get_backup_path(b.backup_mode, to, &b.suffix);
|
||||||
|
if let Some(ref backup_path) = backup_path {
|
||||||
|
// TODO!!
|
||||||
|
if let Err(err) = fs::rename(to, backup_path) {
|
||||||
|
show_error!(
|
||||||
|
"install: cannot backup file '{}' to '{}': {}",
|
||||||
|
to.display(),
|
||||||
|
backup_path.display(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if from.to_string_lossy() == "/dev/null" {
|
if from.to_string_lossy() == "/dev/null" {
|
||||||
/* workaround a limitation of fs::copy
|
/* workaround a limitation of fs::copy
|
||||||
|
@ -619,7 +643,11 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> Result<(), ()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.verbose {
|
if b.verbose {
|
||||||
show_error!("'{}' -> '{}'", from.display(), to.display());
|
print!("'{}' -> '{}'", from.display(), to.display());
|
||||||
|
match backup_path {
|
||||||
|
Some(path) => println!(" (backup: '{}')", path.display()),
|
||||||
|
None => println!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/join.rs"
|
path = "src/join.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
|
@ -442,7 +442,72 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let matches = App::new(NAME)
|
let matches = uu_app().get_matches_from(args);
|
||||||
|
|
||||||
|
let keys = parse_field_number_option(matches.value_of("j"));
|
||||||
|
let key1 = parse_field_number_option(matches.value_of("1"));
|
||||||
|
let key2 = parse_field_number_option(matches.value_of("2"));
|
||||||
|
|
||||||
|
let mut settings: Settings = Default::default();
|
||||||
|
|
||||||
|
if let Some(value) = matches.value_of("v") {
|
||||||
|
settings.print_unpaired = parse_file_number(value);
|
||||||
|
settings.print_joined = false;
|
||||||
|
} else if let Some(value) = matches.value_of("a") {
|
||||||
|
settings.print_unpaired = parse_file_number(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ignore_case = matches.is_present("i");
|
||||||
|
settings.key1 = get_field_number(keys, key1);
|
||||||
|
settings.key2 = get_field_number(keys, key2);
|
||||||
|
|
||||||
|
if let Some(value) = matches.value_of("t") {
|
||||||
|
settings.separator = match value.len() {
|
||||||
|
0 => Sep::Line,
|
||||||
|
1 => Sep::Char(value.chars().next().unwrap()),
|
||||||
|
_ => crash!(1, "multi-character tab {}", value),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(format) = matches.value_of("o") {
|
||||||
|
if format == "auto" {
|
||||||
|
settings.autoformat = true;
|
||||||
|
} else {
|
||||||
|
settings.format = format
|
||||||
|
.split(|c| c == ' ' || c == ',' || c == '\t')
|
||||||
|
.map(Spec::parse)
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(empty) = matches.value_of("e") {
|
||||||
|
settings.empty = empty.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches.is_present("nocheck-order") {
|
||||||
|
settings.check_order = CheckOrder::Disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches.is_present("check-order") {
|
||||||
|
settings.check_order = CheckOrder::Enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches.is_present("header") {
|
||||||
|
settings.headers = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file1 = matches.value_of("file1").unwrap();
|
||||||
|
let file2 = matches.value_of("file2").unwrap();
|
||||||
|
|
||||||
|
if file1 == "-" && file2 == "-" {
|
||||||
|
crash!(1, "both files cannot be standard input");
|
||||||
|
}
|
||||||
|
|
||||||
|
exec(file1, file2, &settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(NAME)
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about(
|
.about(
|
||||||
"For each pair of input lines with identical join fields, write a line to
|
"For each pair of input lines with identical join fields, write a line to
|
||||||
|
@ -542,68 +607,6 @@ FILENUM is 1 or 2, corresponding to FILE1 or FILE2",
|
||||||
.value_name("FILE2")
|
.value_name("FILE2")
|
||||||
.hidden(true),
|
.hidden(true),
|
||||||
)
|
)
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let keys = parse_field_number_option(matches.value_of("j"));
|
|
||||||
let key1 = parse_field_number_option(matches.value_of("1"));
|
|
||||||
let key2 = parse_field_number_option(matches.value_of("2"));
|
|
||||||
|
|
||||||
let mut settings: Settings = Default::default();
|
|
||||||
|
|
||||||
if let Some(value) = matches.value_of("v") {
|
|
||||||
settings.print_unpaired = parse_file_number(value);
|
|
||||||
settings.print_joined = false;
|
|
||||||
} else if let Some(value) = matches.value_of("a") {
|
|
||||||
settings.print_unpaired = parse_file_number(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ignore_case = matches.is_present("i");
|
|
||||||
settings.key1 = get_field_number(keys, key1);
|
|
||||||
settings.key2 = get_field_number(keys, key2);
|
|
||||||
|
|
||||||
if let Some(value) = matches.value_of("t") {
|
|
||||||
settings.separator = match value.len() {
|
|
||||||
0 => Sep::Line,
|
|
||||||
1 => Sep::Char(value.chars().next().unwrap()),
|
|
||||||
_ => crash!(1, "multi-character tab {}", value),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(format) = matches.value_of("o") {
|
|
||||||
if format == "auto" {
|
|
||||||
settings.autoformat = true;
|
|
||||||
} else {
|
|
||||||
settings.format = format
|
|
||||||
.split(|c| c == ' ' || c == ',' || c == '\t')
|
|
||||||
.map(Spec::parse)
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(empty) = matches.value_of("e") {
|
|
||||||
settings.empty = empty.to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches.is_present("nocheck-order") {
|
|
||||||
settings.check_order = CheckOrder::Disabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches.is_present("check-order") {
|
|
||||||
settings.check_order = CheckOrder::Enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches.is_present("header") {
|
|
||||||
settings.headers = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let file1 = matches.value_of("file1").unwrap();
|
|
||||||
let file2 = matches.value_of("file2").unwrap();
|
|
||||||
|
|
||||||
if file1 == "-" && file2 == "-" {
|
|
||||||
crash!(1, "both files cannot be standard input");
|
|
||||||
}
|
|
||||||
|
|
||||||
exec(file1, file2, &settings)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 {
|
fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 {
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/kill.rs"
|
path = "src/kill.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["signals"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["signals"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -43,38 +43,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let (args, obs_signal) = handle_obsolete(args);
|
let (args, obs_signal) = handle_obsolete(args);
|
||||||
|
|
||||||
let usage = format!("{} [OPTIONS]... PID...", executable!());
|
let usage = format!("{} [OPTIONS]... PID...", executable!());
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::LIST)
|
|
||||||
.short("l")
|
|
||||||
.long(options::LIST)
|
|
||||||
.help("Lists signals")
|
|
||||||
.conflicts_with(options::TABLE)
|
|
||||||
.conflicts_with(options::TABLE_OLD),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::TABLE)
|
|
||||||
.short("t")
|
|
||||||
.long(options::TABLE)
|
|
||||||
.help("Lists table of signals"),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(options::TABLE_OLD).short("L").hidden(true))
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::SIGNAL)
|
|
||||||
.short("s")
|
|
||||||
.long(options::SIGNAL)
|
|
||||||
.help("Sends given signal")
|
|
||||||
.takes_value(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::PIDS_OR_SIGNALS)
|
|
||||||
.hidden(true)
|
|
||||||
.multiple(true),
|
|
||||||
)
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let mode = if matches.is_present(options::TABLE) || matches.is_present(options::TABLE_OLD) {
|
let mode = if matches.is_present(options::TABLE) || matches.is_present(options::TABLE_OLD) {
|
||||||
Mode::Table
|
Mode::Table
|
||||||
|
@ -106,6 +75,39 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
EXIT_OK
|
EXIT_OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::LIST)
|
||||||
|
.short("l")
|
||||||
|
.long(options::LIST)
|
||||||
|
.help("Lists signals")
|
||||||
|
.conflicts_with(options::TABLE)
|
||||||
|
.conflicts_with(options::TABLE_OLD),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::TABLE)
|
||||||
|
.short("t")
|
||||||
|
.long(options::TABLE)
|
||||||
|
.help("Lists table of signals"),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name(options::TABLE_OLD).short("L").hidden(true))
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::SIGNAL)
|
||||||
|
.short("s")
|
||||||
|
.long(options::SIGNAL)
|
||||||
|
.help("Sends given signal")
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::PIDS_OR_SIGNALS)
|
||||||
|
.hidden(true)
|
||||||
|
.multiple(true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_obsolete(mut args: Vec<String>) -> (Vec<String>, Option<String>) {
|
fn handle_obsolete(mut args: Vec<String>) -> (Vec<String>, Option<String>) {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < args.len() {
|
while i < args.len() {
|
||||||
|
|
|
@ -18,7 +18,7 @@ path = "src/link.rs"
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "link"
|
name = "link"
|
||||||
|
|
|
@ -32,19 +32,7 @@ pub fn normalize_error_message(e: Error) -> String {
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::FILES)
|
|
||||||
.hidden(true)
|
|
||||||
.required(true)
|
|
||||||
.min_values(2)
|
|
||||||
.max_values(2)
|
|
||||||
.takes_value(true),
|
|
||||||
)
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let files: Vec<_> = matches
|
let files: Vec<_> = matches
|
||||||
.values_of_os(options::FILES)
|
.values_of_os(options::FILES)
|
||||||
|
@ -61,3 +49,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::FILES)
|
||||||
|
.hidden(true)
|
||||||
|
.required(true)
|
||||||
|
.min_values(2)
|
||||||
|
.max_values(2)
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/ln.rs"
|
path = "src/ln.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -97,11 +97,71 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
let long_usage = get_long_usage();
|
let long_usage = get_long_usage();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app()
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
.usage(&usage[..])
|
||||||
.after_help(&long_usage[..])
|
.after_help(&long_usage[..])
|
||||||
|
.get_matches_from(args);
|
||||||
|
|
||||||
|
/* the list of files */
|
||||||
|
|
||||||
|
let paths: Vec<PathBuf> = matches
|
||||||
|
.values_of(ARG_FILES)
|
||||||
|
.unwrap()
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let overwrite_mode = if matches.is_present(options::FORCE) {
|
||||||
|
OverwriteMode::Force
|
||||||
|
} else if matches.is_present(options::INTERACTIVE) {
|
||||||
|
OverwriteMode::Interactive
|
||||||
|
} else {
|
||||||
|
OverwriteMode::NoClobber
|
||||||
|
};
|
||||||
|
|
||||||
|
let backup_mode = if matches.is_present(options::B) {
|
||||||
|
BackupMode::ExistingBackup
|
||||||
|
} else if matches.is_present(options::BACKUP) {
|
||||||
|
match matches.value_of(options::BACKUP) {
|
||||||
|
None => BackupMode::ExistingBackup,
|
||||||
|
Some(mode) => match mode {
|
||||||
|
"simple" | "never" => BackupMode::SimpleBackup,
|
||||||
|
"numbered" | "t" => BackupMode::NumberedBackup,
|
||||||
|
"existing" | "nil" => BackupMode::ExistingBackup,
|
||||||
|
"none" | "off" => BackupMode::NoBackup,
|
||||||
|
_ => panic!(), // cannot happen as it is managed by clap
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
BackupMode::NoBackup
|
||||||
|
};
|
||||||
|
|
||||||
|
let backup_suffix = if matches.is_present(options::SUFFIX) {
|
||||||
|
matches.value_of(options::SUFFIX).unwrap()
|
||||||
|
} else {
|
||||||
|
"~"
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings = Settings {
|
||||||
|
overwrite: overwrite_mode,
|
||||||
|
backup: backup_mode,
|
||||||
|
suffix: backup_suffix.to_string(),
|
||||||
|
symbolic: matches.is_present(options::SYMBOLIC),
|
||||||
|
relative: matches.is_present(options::RELATIVE),
|
||||||
|
target_dir: matches
|
||||||
|
.value_of(options::TARGET_DIRECTORY)
|
||||||
|
.map(String::from),
|
||||||
|
no_target_dir: matches.is_present(options::NO_TARGET_DIRECTORY),
|
||||||
|
no_dereference: matches.is_present(options::NO_DEREFERENCE),
|
||||||
|
verbose: matches.is_present(options::VERBOSE),
|
||||||
|
};
|
||||||
|
|
||||||
|
exec(&paths[..], &settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(ABOUT)
|
||||||
.arg(Arg::with_name(options::B).short(options::B).help(
|
.arg(Arg::with_name(options::B).short(options::B).help(
|
||||||
"make a backup of each file that would otherwise be overwritten or \
|
"make a backup of each file that would otherwise be overwritten or \
|
||||||
removed",
|
removed",
|
||||||
|
@ -198,62 +258,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.required(true)
|
.required(true)
|
||||||
.min_values(1),
|
.min_values(1),
|
||||||
)
|
)
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
/* the list of files */
|
|
||||||
|
|
||||||
let paths: Vec<PathBuf> = matches
|
|
||||||
.values_of(ARG_FILES)
|
|
||||||
.unwrap()
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let overwrite_mode = if matches.is_present(options::FORCE) {
|
|
||||||
OverwriteMode::Force
|
|
||||||
} else if matches.is_present(options::INTERACTIVE) {
|
|
||||||
OverwriteMode::Interactive
|
|
||||||
} else {
|
|
||||||
OverwriteMode::NoClobber
|
|
||||||
};
|
|
||||||
|
|
||||||
let backup_mode = if matches.is_present(options::B) {
|
|
||||||
BackupMode::ExistingBackup
|
|
||||||
} else if matches.is_present(options::BACKUP) {
|
|
||||||
match matches.value_of(options::BACKUP) {
|
|
||||||
None => BackupMode::ExistingBackup,
|
|
||||||
Some(mode) => match mode {
|
|
||||||
"simple" | "never" => BackupMode::SimpleBackup,
|
|
||||||
"numbered" | "t" => BackupMode::NumberedBackup,
|
|
||||||
"existing" | "nil" => BackupMode::ExistingBackup,
|
|
||||||
"none" | "off" => BackupMode::NoBackup,
|
|
||||||
_ => panic!(), // cannot happen as it is managed by clap
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BackupMode::NoBackup
|
|
||||||
};
|
|
||||||
|
|
||||||
let backup_suffix = if matches.is_present(options::SUFFIX) {
|
|
||||||
matches.value_of(options::SUFFIX).unwrap()
|
|
||||||
} else {
|
|
||||||
"~"
|
|
||||||
};
|
|
||||||
|
|
||||||
let settings = Settings {
|
|
||||||
overwrite: overwrite_mode,
|
|
||||||
backup: backup_mode,
|
|
||||||
suffix: backup_suffix.to_string(),
|
|
||||||
symbolic: matches.is_present(options::SYMBOLIC),
|
|
||||||
relative: matches.is_present(options::RELATIVE),
|
|
||||||
target_dir: matches
|
|
||||||
.value_of(options::TARGET_DIRECTORY)
|
|
||||||
.map(String::from),
|
|
||||||
no_target_dir: matches.is_present(options::NO_TARGET_DIRECTORY),
|
|
||||||
no_dereference: matches.is_present(options::NO_DEREFERENCE),
|
|
||||||
verbose: matches.is_present(options::VERBOSE),
|
|
||||||
};
|
|
||||||
|
|
||||||
exec(&paths[..], &settings)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(files: &[PathBuf], settings: &Settings) -> i32 {
|
fn exec(files: &[PathBuf], settings: &Settings) -> i32 {
|
||||||
|
@ -382,12 +386,15 @@ fn relative_path<'a>(src: &Path, dst: &Path) -> Result<Cow<'a, Path>> {
|
||||||
|
|
||||||
let src_iter = src_abs.components().skip(suffix_pos).map(|x| x.as_os_str());
|
let src_iter = src_abs.components().skip(suffix_pos).map(|x| x.as_os_str());
|
||||||
|
|
||||||
let result: PathBuf = dst_abs
|
let mut result: PathBuf = dst_abs
|
||||||
.components()
|
.components()
|
||||||
.skip(suffix_pos + 1)
|
.skip(suffix_pos + 1)
|
||||||
.map(|_| OsStr::new(".."))
|
.map(|_| OsStr::new(".."))
|
||||||
.chain(src_iter)
|
.chain(src_iter)
|
||||||
.collect();
|
.collect();
|
||||||
|
if result.as_os_str().is_empty() {
|
||||||
|
result.push(".");
|
||||||
|
}
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ path = "src/logname.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
|
@ -45,11 +45,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
let _ = App::new(executable!())
|
let _ = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(SUMMARY)
|
|
||||||
.usage(&usage[..])
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
match get_userlogin() {
|
match get_userlogin() {
|
||||||
Some(userlogin) => println!("{}", userlogin),
|
Some(userlogin) => println!("{}", userlogin),
|
||||||
|
@ -58,3 +54,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(SUMMARY)
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ path = "src/ls.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
locale = "0.2.2"
|
locale = "0.2.2"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
unicode-width = "0.1.8"
|
unicode-width = "0.1.8"
|
||||||
number_prefix = "0.4"
|
number_prefix = "0.4"
|
||||||
term_grid = "0.1.5"
|
term_grid = "0.1.5"
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf
|
// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf
|
||||||
|
|
||||||
|
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||||
|
#![allow(clippy::nonstandard_macro_braces)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
@ -14,7 +17,6 @@ extern crate uucore;
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
mod quoting_style;
|
mod quoting_style;
|
||||||
mod version_cmp;
|
|
||||||
|
|
||||||
use clap::{crate_version, App, Arg};
|
use clap::{crate_version, App, Arg};
|
||||||
use globset::{self, Glob, GlobSet, GlobSetBuilder};
|
use globset::{self, Glob, GlobSet, GlobSetBuilder};
|
||||||
|
@ -26,10 +28,11 @@ use quoting_style::{escape_name, QuotingStyle};
|
||||||
use std::os::windows::fs::MetadataExt;
|
use std::os::windows::fs::MetadataExt;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Reverse,
|
cmp::Reverse,
|
||||||
|
error::Error,
|
||||||
|
fmt::Display,
|
||||||
fs::{self, DirEntry, FileType, Metadata},
|
fs::{self, DirEntry, FileType, Metadata},
|
||||||
io::{stdout, BufWriter, Stdout, Write},
|
io::{stdout, BufWriter, Stdout, Write},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::exit,
|
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
@ -38,26 +41,20 @@ use std::{
|
||||||
os::unix::fs::{FileTypeExt, MetadataExt},
|
os::unix::fs::{FileTypeExt, MetadataExt},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
|
use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
|
||||||
|
use uucore::error::{set_exit_code, FromIo, UCustomError, UResult};
|
||||||
|
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR};
|
use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR};
|
||||||
|
use uucore::{fs::display_permissions, version_cmp::version_cmp};
|
||||||
static ABOUT: &str = "
|
|
||||||
By default, ls will list the files and contents of any directories on
|
|
||||||
the command line, expect that it will ignore files and directories
|
|
||||||
whose names start with '.'
|
|
||||||
";
|
|
||||||
static AFTER_HELP: &str = "The TIME_STYLE argument can be full-iso, long-iso, iso.
|
|
||||||
Also the TIME_STYLE environment variable sets the default style to use.";
|
|
||||||
|
|
||||||
fn get_usage() -> String {
|
fn get_usage() -> String {
|
||||||
format!("{0} [OPTION]... [FILE]...", executable!())
|
format!("{0} [OPTION]... [FILE]...", executable!())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod options {
|
pub mod options {
|
||||||
|
|
||||||
pub mod format {
|
pub mod format {
|
||||||
pub static ONE_LINE: &str = "1";
|
pub static ONE_LINE: &str = "1";
|
||||||
pub static LONG: &str = "long";
|
pub static LONG: &str = "long";
|
||||||
|
@ -68,10 +65,12 @@ pub mod options {
|
||||||
pub static LONG_NO_GROUP: &str = "o";
|
pub static LONG_NO_GROUP: &str = "o";
|
||||||
pub static LONG_NUMERIC_UID_GID: &str = "numeric-uid-gid";
|
pub static LONG_NUMERIC_UID_GID: &str = "numeric-uid-gid";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod files {
|
pub mod files {
|
||||||
pub static ALL: &str = "all";
|
pub static ALL: &str = "all";
|
||||||
pub static ALMOST_ALL: &str = "almost-all";
|
pub static ALMOST_ALL: &str = "almost-all";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod sort {
|
pub mod sort {
|
||||||
pub static SIZE: &str = "S";
|
pub static SIZE: &str = "S";
|
||||||
pub static TIME: &str = "t";
|
pub static TIME: &str = "t";
|
||||||
|
@ -79,30 +78,36 @@ pub mod options {
|
||||||
pub static VERSION: &str = "v";
|
pub static VERSION: &str = "v";
|
||||||
pub static EXTENSION: &str = "X";
|
pub static EXTENSION: &str = "X";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod time {
|
pub mod time {
|
||||||
pub static ACCESS: &str = "u";
|
pub static ACCESS: &str = "u";
|
||||||
pub static CHANGE: &str = "c";
|
pub static CHANGE: &str = "c";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod size {
|
pub mod size {
|
||||||
pub static HUMAN_READABLE: &str = "human-readable";
|
pub static HUMAN_READABLE: &str = "human-readable";
|
||||||
pub static SI: &str = "si";
|
pub static SI: &str = "si";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod quoting {
|
pub mod quoting {
|
||||||
pub static ESCAPE: &str = "escape";
|
pub static ESCAPE: &str = "escape";
|
||||||
pub static LITERAL: &str = "literal";
|
pub static LITERAL: &str = "literal";
|
||||||
pub static C: &str = "quote-name";
|
pub static C: &str = "quote-name";
|
||||||
}
|
}
|
||||||
pub static QUOTING_STYLE: &str = "quoting-style";
|
|
||||||
pub mod indicator_style {
|
pub mod indicator_style {
|
||||||
pub static SLASH: &str = "p";
|
pub static SLASH: &str = "p";
|
||||||
pub static FILE_TYPE: &str = "file-type";
|
pub static FILE_TYPE: &str = "file-type";
|
||||||
pub static CLASSIFY: &str = "classify";
|
pub static CLASSIFY: &str = "classify";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod dereference {
|
pub mod dereference {
|
||||||
pub static ALL: &str = "dereference";
|
pub static ALL: &str = "dereference";
|
||||||
pub static ARGS: &str = "dereference-command-line";
|
pub static ARGS: &str = "dereference-command-line";
|
||||||
pub static DIR_ARGS: &str = "dereference-command-line-symlink-to-dir";
|
pub static DIR_ARGS: &str = "dereference-command-line-symlink-to-dir";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub static QUOTING_STYLE: &str = "quoting-style";
|
||||||
pub static HIDE_CONTROL_CHARS: &str = "hide-control-chars";
|
pub static HIDE_CONTROL_CHARS: &str = "hide-control-chars";
|
||||||
pub static SHOW_CONTROL_CHARS: &str = "show-control-chars";
|
pub static SHOW_CONTROL_CHARS: &str = "show-control-chars";
|
||||||
pub static WIDTH: &str = "width";
|
pub static WIDTH: &str = "width";
|
||||||
|
@ -125,6 +130,32 @@ pub mod options {
|
||||||
pub static IGNORE: &str = "ignore";
|
pub static IGNORE: &str = "ignore";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum LsError {
|
||||||
|
InvalidLineWidth(String),
|
||||||
|
NoMetadata(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UCustomError for LsError {
|
||||||
|
fn code(&self) -> i32 {
|
||||||
|
match self {
|
||||||
|
LsError::InvalidLineWidth(_) => 2,
|
||||||
|
LsError::NoMetadata(_) => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for LsError {}
|
||||||
|
|
||||||
|
impl Display for LsError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
LsError::InvalidLineWidth(s) => write!(f, "invalid line width: '{}'", s),
|
||||||
|
LsError::NoMetadata(p) => write!(f, "could not open file: '{}'", p.display()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
enum Format {
|
enum Format {
|
||||||
Columns,
|
Columns,
|
||||||
|
@ -218,7 +249,7 @@ struct LongFormat {
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn from(options: clap::ArgMatches) -> Config {
|
fn from(options: clap::ArgMatches) -> UResult<Config> {
|
||||||
let (mut format, opt) = if let Some(format_) = options.value_of(options::FORMAT) {
|
let (mut format, opt) = if let Some(format_) = options.value_of(options::FORMAT) {
|
||||||
(
|
(
|
||||||
match format_ {
|
match format_ {
|
||||||
|
@ -369,15 +400,13 @@ impl Config {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let width = options
|
let width = match options.value_of(options::WIDTH) {
|
||||||
.value_of(options::WIDTH)
|
Some(x) => match x.parse::<u16>() {
|
||||||
.map(|x| {
|
Ok(u) => Some(u),
|
||||||
x.parse::<u16>().unwrap_or_else(|_e| {
|
Err(_) => return Err(LsError::InvalidLineWidth(x.into()).into()),
|
||||||
show_error!("invalid line width: ‘{}’", x);
|
},
|
||||||
exit(2);
|
None => termsize::get().map(|s| s.cols),
|
||||||
})
|
};
|
||||||
})
|
|
||||||
.or_else(|| termsize::get().map(|s| s.cols));
|
|
||||||
|
|
||||||
#[allow(clippy::needless_bool)]
|
#[allow(clippy::needless_bool)]
|
||||||
let show_control = if options.is_present(options::HIDE_CONTROL_CHARS) {
|
let show_control = if options.is_present(options::HIDE_CONTROL_CHARS) {
|
||||||
|
@ -528,7 +557,7 @@ impl Config {
|
||||||
Dereference::DirArgs
|
Dereference::DirArgs
|
||||||
};
|
};
|
||||||
|
|
||||||
Config {
|
Ok(Config {
|
||||||
format,
|
format,
|
||||||
files,
|
files,
|
||||||
sort,
|
sort,
|
||||||
|
@ -547,29 +576,54 @@ impl Config {
|
||||||
quoting_style,
|
quoting_style,
|
||||||
indicator_style,
|
indicator_style,
|
||||||
time_style,
|
time_style,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
#[uucore_procs::gen_uumain]
|
||||||
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let args = args
|
let args = args
|
||||||
.collect_str(InvalidEncodingHandling::Ignore)
|
.collect_str(InvalidEncodingHandling::Ignore)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
|
|
||||||
let app = App::new(executable!())
|
let app = uu_app().usage(&usage[..]);
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
|
||||||
|
|
||||||
|
let matches = app.get_matches_from(args);
|
||||||
|
|
||||||
|
let locs = matches
|
||||||
|
.values_of(options::PATHS)
|
||||||
|
.map(|v| v.map(ToString::to_string).collect())
|
||||||
|
.unwrap_or_else(|| vec![String::from(".")]);
|
||||||
|
|
||||||
|
list(locs, Config::from(matches)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.version(crate_version!())
|
||||||
|
.about(
|
||||||
|
"By default, ls will list the files and contents of any directories on \
|
||||||
|
the command line, expect that it will ignore files and directories \
|
||||||
|
whose names start with '.'.",
|
||||||
|
)
|
||||||
// Format arguments
|
// Format arguments
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::FORMAT)
|
Arg::with_name(options::FORMAT)
|
||||||
.long(options::FORMAT)
|
.long(options::FORMAT)
|
||||||
.help("Set the display format.")
|
.help("Set the display format.")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.possible_values(&["long", "verbose", "single-column", "columns", "vertical", "across", "horizontal", "commas"])
|
.possible_values(&[
|
||||||
|
"long",
|
||||||
|
"verbose",
|
||||||
|
"single-column",
|
||||||
|
"columns",
|
||||||
|
"vertical",
|
||||||
|
"across",
|
||||||
|
"horizontal",
|
||||||
|
"commas",
|
||||||
|
])
|
||||||
.hide_possible_values(true)
|
.hide_possible_values(true)
|
||||||
.require_equals(true)
|
.require_equals(true)
|
||||||
.overrides_with_all(&[
|
.overrides_with_all(&[
|
||||||
|
@ -639,41 +693,51 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
Arg::with_name(options::format::ONE_LINE)
|
Arg::with_name(options::format::ONE_LINE)
|
||||||
.short(options::format::ONE_LINE)
|
.short(options::format::ONE_LINE)
|
||||||
.help("List one file per line.")
|
.help("List one file per line.")
|
||||||
.multiple(true)
|
.multiple(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::format::LONG_NO_GROUP)
|
Arg::with_name(options::format::LONG_NO_GROUP)
|
||||||
.short(options::format::LONG_NO_GROUP)
|
.short(options::format::LONG_NO_GROUP)
|
||||||
.help("Long format without group information. Identical to --format=long with --no-group.")
|
.help(
|
||||||
.multiple(true)
|
"Long format without group information. \
|
||||||
|
Identical to --format=long with --no-group.",
|
||||||
|
)
|
||||||
|
.multiple(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::format::LONG_NO_OWNER)
|
Arg::with_name(options::format::LONG_NO_OWNER)
|
||||||
.short(options::format::LONG_NO_OWNER)
|
.short(options::format::LONG_NO_OWNER)
|
||||||
.help("Long format without owner information.")
|
.help("Long format without owner information.")
|
||||||
.multiple(true)
|
.multiple(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::format::LONG_NUMERIC_UID_GID)
|
Arg::with_name(options::format::LONG_NUMERIC_UID_GID)
|
||||||
.short("n")
|
.short("n")
|
||||||
.long(options::format::LONG_NUMERIC_UID_GID)
|
.long(options::format::LONG_NUMERIC_UID_GID)
|
||||||
.help("-l with numeric UIDs and GIDs.")
|
.help("-l with numeric UIDs and GIDs.")
|
||||||
.multiple(true)
|
.multiple(true),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Quoting style
|
// Quoting style
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::QUOTING_STYLE)
|
Arg::with_name(options::QUOTING_STYLE)
|
||||||
.long(options::QUOTING_STYLE)
|
.long(options::QUOTING_STYLE)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("Set quoting style.")
|
.help("Set quoting style.")
|
||||||
.possible_values(&["literal", "shell", "shell-always", "shell-escape", "shell-escape-always", "c", "escape"])
|
.possible_values(&[
|
||||||
|
"literal",
|
||||||
|
"shell",
|
||||||
|
"shell-always",
|
||||||
|
"shell-escape",
|
||||||
|
"shell-escape-always",
|
||||||
|
"c",
|
||||||
|
"escape",
|
||||||
|
])
|
||||||
.overrides_with_all(&[
|
.overrides_with_all(&[
|
||||||
options::QUOTING_STYLE,
|
options::QUOTING_STYLE,
|
||||||
options::quoting::LITERAL,
|
options::quoting::LITERAL,
|
||||||
options::quoting::ESCAPE,
|
options::quoting::ESCAPE,
|
||||||
options::quoting::C,
|
options::quoting::C,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::quoting::LITERAL)
|
Arg::with_name(options::quoting::LITERAL)
|
||||||
|
@ -685,7 +749,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::quoting::LITERAL,
|
options::quoting::LITERAL,
|
||||||
options::quoting::ESCAPE,
|
options::quoting::ESCAPE,
|
||||||
options::quoting::C,
|
options::quoting::C,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::quoting::ESCAPE)
|
Arg::with_name(options::quoting::ESCAPE)
|
||||||
|
@ -697,7 +761,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::quoting::LITERAL,
|
options::quoting::LITERAL,
|
||||||
options::quoting::ESCAPE,
|
options::quoting::ESCAPE,
|
||||||
options::quoting::C,
|
options::quoting::C,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::quoting::C)
|
Arg::with_name(options::quoting::C)
|
||||||
|
@ -709,76 +773,63 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::quoting::LITERAL,
|
options::quoting::LITERAL,
|
||||||
options::quoting::ESCAPE,
|
options::quoting::ESCAPE,
|
||||||
options::quoting::C,
|
options::quoting::C,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Control characters
|
// Control characters
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::HIDE_CONTROL_CHARS)
|
Arg::with_name(options::HIDE_CONTROL_CHARS)
|
||||||
.short("q")
|
.short("q")
|
||||||
.long(options::HIDE_CONTROL_CHARS)
|
.long(options::HIDE_CONTROL_CHARS)
|
||||||
.help("Replace control characters with '?' if they are not escaped.")
|
.help("Replace control characters with '?' if they are not escaped.")
|
||||||
.overrides_with_all(&[
|
.overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]),
|
||||||
options::HIDE_CONTROL_CHARS,
|
|
||||||
options::SHOW_CONTROL_CHARS,
|
|
||||||
])
|
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::SHOW_CONTROL_CHARS)
|
Arg::with_name(options::SHOW_CONTROL_CHARS)
|
||||||
.long(options::SHOW_CONTROL_CHARS)
|
.long(options::SHOW_CONTROL_CHARS)
|
||||||
.help("Show control characters 'as is' if they are not escaped.")
|
.help("Show control characters 'as is' if they are not escaped.")
|
||||||
.overrides_with_all(&[
|
.overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]),
|
||||||
options::HIDE_CONTROL_CHARS,
|
|
||||||
options::SHOW_CONTROL_CHARS,
|
|
||||||
])
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Time arguments
|
// Time arguments
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::TIME)
|
Arg::with_name(options::TIME)
|
||||||
.long(options::TIME)
|
.long(options::TIME)
|
||||||
.help("Show time in <field>:\n\
|
.help(
|
||||||
|
"Show time in <field>:\n\
|
||||||
\taccess time (-u): atime, access, use;\n\
|
\taccess time (-u): atime, access, use;\n\
|
||||||
\tchange time (-t): ctime, status.\n\
|
\tchange time (-t): ctime, status.\n\
|
||||||
\tbirth time: birth, creation;")
|
\tbirth time: birth, creation;",
|
||||||
|
)
|
||||||
.value_name("field")
|
.value_name("field")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.possible_values(&["atime", "access", "use", "ctime", "status", "birth", "creation"])
|
.possible_values(&[
|
||||||
|
"atime", "access", "use", "ctime", "status", "birth", "creation",
|
||||||
|
])
|
||||||
.hide_possible_values(true)
|
.hide_possible_values(true)
|
||||||
.require_equals(true)
|
.require_equals(true)
|
||||||
.overrides_with_all(&[
|
.overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]),
|
||||||
options::TIME,
|
|
||||||
options::time::ACCESS,
|
|
||||||
options::time::CHANGE,
|
|
||||||
])
|
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::time::CHANGE)
|
Arg::with_name(options::time::CHANGE)
|
||||||
.short(options::time::CHANGE)
|
.short(options::time::CHANGE)
|
||||||
.help("If the long listing format (e.g., -l, -o) is being used, print the status \
|
.help(
|
||||||
change time (the ‘ctime’ in the inode) instead of the modification time. When \
|
"If the long listing format (e.g., -l, -o) is being used, print the status \
|
||||||
|
change time (the 'ctime' in the inode) instead of the modification time. When \
|
||||||
explicitly sorting by time (--sort=time or -t) or when not using a long listing \
|
explicitly sorting by time (--sort=time or -t) or when not using a long listing \
|
||||||
format, sort according to the status change time.")
|
format, sort according to the status change time.",
|
||||||
.overrides_with_all(&[
|
)
|
||||||
options::TIME,
|
.overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]),
|
||||||
options::time::ACCESS,
|
|
||||||
options::time::CHANGE,
|
|
||||||
])
|
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::time::ACCESS)
|
Arg::with_name(options::time::ACCESS)
|
||||||
.short(options::time::ACCESS)
|
.short(options::time::ACCESS)
|
||||||
.help("If the long listing format (e.g., -l, -o) is being used, print the status \
|
.help(
|
||||||
|
"If the long listing format (e.g., -l, -o) is being used, print the status \
|
||||||
access time instead of the modification time. When explicitly sorting by time \
|
access time instead of the modification time. When explicitly sorting by time \
|
||||||
(--sort=time or -t) or when not using a long listing format, sort according to the \
|
(--sort=time or -t) or when not using a long listing format, sort according to the \
|
||||||
access time.")
|
access time.",
|
||||||
.overrides_with_all(&[
|
)
|
||||||
options::TIME,
|
.overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]),
|
||||||
options::time::ACCESS,
|
|
||||||
options::time::CHANGE,
|
|
||||||
])
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hide and ignore
|
// Hide and ignore
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::HIDE)
|
Arg::with_name(options::HIDE)
|
||||||
|
@ -786,7 +837,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
.value_name("PATTERN")
|
.value_name("PATTERN")
|
||||||
.help("do not list implied entries matching shell PATTERN (overridden by -a or -A)")
|
.help(
|
||||||
|
"do not list implied entries matching shell PATTERN (overridden by -a or -A)",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::IGNORE)
|
Arg::with_name(options::IGNORE)
|
||||||
|
@ -795,7 +848,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
.value_name("PATTERN")
|
.value_name("PATTERN")
|
||||||
.help("do not list implied entries matching shell PATTERN")
|
.help("do not list implied entries matching shell PATTERN"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::IGNORE_BACKUPS)
|
Arg::with_name(options::IGNORE_BACKUPS)
|
||||||
|
@ -803,7 +856,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.long(options::IGNORE_BACKUPS)
|
.long(options::IGNORE_BACKUPS)
|
||||||
.help("Ignore entries which end with ~."),
|
.help("Ignore entries which end with ~."),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sort arguments
|
// Sort arguments
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::SORT)
|
Arg::with_name(options::SORT)
|
||||||
|
@ -820,7 +872,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::sort::NONE,
|
options::sort::NONE,
|
||||||
options::sort::VERSION,
|
options::sort::VERSION,
|
||||||
options::sort::EXTENSION,
|
options::sort::EXTENSION,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::sort::SIZE)
|
Arg::with_name(options::sort::SIZE)
|
||||||
|
@ -833,7 +885,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::sort::NONE,
|
options::sort::NONE,
|
||||||
options::sort::VERSION,
|
options::sort::VERSION,
|
||||||
options::sort::EXTENSION,
|
options::sort::EXTENSION,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::sort::TIME)
|
Arg::with_name(options::sort::TIME)
|
||||||
|
@ -846,7 +898,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::sort::NONE,
|
options::sort::NONE,
|
||||||
options::sort::VERSION,
|
options::sort::VERSION,
|
||||||
options::sort::EXTENSION,
|
options::sort::EXTENSION,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::sort::VERSION)
|
Arg::with_name(options::sort::VERSION)
|
||||||
|
@ -859,7 +911,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::sort::NONE,
|
options::sort::NONE,
|
||||||
options::sort::VERSION,
|
options::sort::VERSION,
|
||||||
options::sort::EXTENSION,
|
options::sort::EXTENSION,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::sort::EXTENSION)
|
Arg::with_name(options::sort::EXTENSION)
|
||||||
|
@ -872,14 +924,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::sort::NONE,
|
options::sort::NONE,
|
||||||
options::sort::VERSION,
|
options::sort::VERSION,
|
||||||
options::sort::EXTENSION,
|
options::sort::EXTENSION,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::sort::NONE)
|
Arg::with_name(options::sort::NONE)
|
||||||
.short(options::sort::NONE)
|
.short(options::sort::NONE)
|
||||||
.help("Do not sort; list the files in whatever order they are stored in the \
|
.help(
|
||||||
|
"Do not sort; list the files in whatever order they are stored in the \
|
||||||
directory. This is especially useful when listing very large directories, \
|
directory. This is especially useful when listing very large directories, \
|
||||||
since not doing any sorting can be noticeably faster.")
|
since not doing any sorting can be noticeably faster.",
|
||||||
|
)
|
||||||
.overrides_with_all(&[
|
.overrides_with_all(&[
|
||||||
options::SORT,
|
options::SORT,
|
||||||
options::sort::SIZE,
|
options::sort::SIZE,
|
||||||
|
@ -887,9 +941,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::sort::NONE,
|
options::sort::NONE,
|
||||||
options::sort::VERSION,
|
options::sort::VERSION,
|
||||||
options::sort::EXTENSION,
|
options::sort::EXTENSION,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dereferencing
|
// Dereferencing
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::dereference::ALL)
|
Arg::with_name(options::dereference::ALL)
|
||||||
|
@ -903,7 +956,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::dereference::ALL,
|
options::dereference::ALL,
|
||||||
options::dereference::DIR_ARGS,
|
options::dereference::DIR_ARGS,
|
||||||
options::dereference::ARGS,
|
options::dereference::ARGS,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::dereference::DIR_ARGS)
|
Arg::with_name(options::dereference::DIR_ARGS)
|
||||||
|
@ -916,35 +969,30 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::dereference::ALL,
|
options::dereference::ALL,
|
||||||
options::dereference::DIR_ARGS,
|
options::dereference::DIR_ARGS,
|
||||||
options::dereference::ARGS,
|
options::dereference::ARGS,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::dereference::ARGS)
|
Arg::with_name(options::dereference::ARGS)
|
||||||
.short("H")
|
.short("H")
|
||||||
.long(options::dereference::ARGS)
|
.long(options::dereference::ARGS)
|
||||||
.help(
|
.help("Do not dereference symlinks except when given as command line arguments.")
|
||||||
"Do not dereference symlinks except when given as command line arguments.",
|
|
||||||
)
|
|
||||||
.overrides_with_all(&[
|
.overrides_with_all(&[
|
||||||
options::dereference::ALL,
|
options::dereference::ALL,
|
||||||
options::dereference::DIR_ARGS,
|
options::dereference::DIR_ARGS,
|
||||||
options::dereference::ARGS,
|
options::dereference::ARGS,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Long format options
|
// Long format options
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::NO_GROUP)
|
Arg::with_name(options::NO_GROUP)
|
||||||
.long(options::NO_GROUP)
|
.long(options::NO_GROUP)
|
||||||
.short("-G")
|
.short("-G")
|
||||||
.help("Do not show group in long format.")
|
.help("Do not show group in long format."),
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::AUTHOR)
|
|
||||||
.long(options::AUTHOR)
|
|
||||||
.help("Show author in long format. On the supported platforms, the author \
|
|
||||||
always matches the file owner.")
|
|
||||||
)
|
)
|
||||||
|
.arg(Arg::with_name(options::AUTHOR).long(options::AUTHOR).help(
|
||||||
|
"Show author in long format. \
|
||||||
|
On the supported platforms, the author always matches the file owner.",
|
||||||
|
))
|
||||||
// Other Flags
|
// Other Flags
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::files::ALL)
|
Arg::with_name(options::files::ALL)
|
||||||
|
@ -957,8 +1005,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.short("A")
|
.short("A")
|
||||||
.long(options::files::ALMOST_ALL)
|
.long(options::files::ALMOST_ALL)
|
||||||
.help(
|
.help(
|
||||||
"In a directory, do not ignore all file names that start with '.', only ignore \
|
"In a directory, do not ignore all file names that start with '.', \
|
||||||
'.' and '..'.",
|
only ignore '.' and '..'.",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
@ -982,7 +1030,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::size::SI)
|
Arg::with_name(options::size::SI)
|
||||||
.long(options::size::SI)
|
.long(options::size::SI)
|
||||||
.help("Print human readable file sizes using powers of 1000 instead of 1024.")
|
.help("Print human readable file sizes using powers of 1000 instead of 1024."),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::INODE)
|
Arg::with_name(options::INODE)
|
||||||
|
@ -994,9 +1042,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
Arg::with_name(options::REVERSE)
|
Arg::with_name(options::REVERSE)
|
||||||
.short("r")
|
.short("r")
|
||||||
.long(options::REVERSE)
|
.long(options::REVERSE)
|
||||||
.help("Reverse whatever the sorting method is--e.g., list files in reverse \
|
.help(
|
||||||
|
"Reverse whatever the sorting method is e.g., list files in reverse \
|
||||||
alphabetical order, youngest first, smallest first, or whatever.",
|
alphabetical order, youngest first, smallest first, or whatever.",
|
||||||
))
|
),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::RECURSIVE)
|
Arg::with_name(options::RECURSIVE)
|
||||||
.short("R")
|
.short("R")
|
||||||
|
@ -1009,7 +1059,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.short("w")
|
.short("w")
|
||||||
.help("Assume that the terminal is COLS columns wide.")
|
.help("Assume that the terminal is COLS columns wide.")
|
||||||
.value_name("COLS")
|
.value_name("COLS")
|
||||||
.takes_value(true)
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::COLOR)
|
Arg::with_name(options::COLOR)
|
||||||
|
@ -1022,8 +1072,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::INDICATOR_STYLE)
|
Arg::with_name(options::INDICATOR_STYLE)
|
||||||
.long(options::INDICATOR_STYLE)
|
.long(options::INDICATOR_STYLE)
|
||||||
.help(" append indicator with style WORD to entry names: none (default), slash\
|
.help(
|
||||||
(-p), file-type (--file-type), classify (-F)")
|
"Append indicator with style WORD to entry names: \
|
||||||
|
none (default), slash (-p), file-type (--file-type), classify (-F)",
|
||||||
|
)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.possible_values(&["none", "slash", "file-type", "classify"])
|
.possible_values(&["none", "slash", "file-type", "classify"])
|
||||||
.overrides_with_all(&[
|
.overrides_with_all(&[
|
||||||
|
@ -1031,21 +1083,24 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::indicator_style::SLASH,
|
options::indicator_style::SLASH,
|
||||||
options::indicator_style::CLASSIFY,
|
options::indicator_style::CLASSIFY,
|
||||||
options::INDICATOR_STYLE,
|
options::INDICATOR_STYLE,
|
||||||
]))
|
]),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::indicator_style::CLASSIFY)
|
Arg::with_name(options::indicator_style::CLASSIFY)
|
||||||
.short("F")
|
.short("F")
|
||||||
.long(options::indicator_style::CLASSIFY)
|
.long(options::indicator_style::CLASSIFY)
|
||||||
.help("Append a character to each file name indicating the file type. Also, for \
|
.help(
|
||||||
|
"Append a character to each file name indicating the file type. Also, for \
|
||||||
regular files that are executable, append '*'. The file type indicators are \
|
regular files that are executable, append '*'. The file type indicators are \
|
||||||
'/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \
|
'/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \
|
||||||
'>' for doors, and nothing for regular files.")
|
'>' for doors, and nothing for regular files.",
|
||||||
|
)
|
||||||
.overrides_with_all(&[
|
.overrides_with_all(&[
|
||||||
options::indicator_style::FILE_TYPE,
|
options::indicator_style::FILE_TYPE,
|
||||||
options::indicator_style::SLASH,
|
options::indicator_style::SLASH,
|
||||||
options::indicator_style::CLASSIFY,
|
options::indicator_style::CLASSIFY,
|
||||||
options::INDICATOR_STYLE,
|
options::INDICATOR_STYLE,
|
||||||
])
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::indicator_style::FILE_TYPE)
|
Arg::with_name(options::indicator_style::FILE_TYPE)
|
||||||
|
@ -1056,18 +1111,19 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
options::indicator_style::SLASH,
|
options::indicator_style::SLASH,
|
||||||
options::indicator_style::CLASSIFY,
|
options::indicator_style::CLASSIFY,
|
||||||
options::INDICATOR_STYLE,
|
options::INDICATOR_STYLE,
|
||||||
]))
|
]),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::indicator_style::SLASH)
|
Arg::with_name(options::indicator_style::SLASH)
|
||||||
.short(options::indicator_style::SLASH)
|
.short(options::indicator_style::SLASH)
|
||||||
.help("Append / indicator to directories."
|
.help("Append / indicator to directories.")
|
||||||
)
|
|
||||||
.overrides_with_all(&[
|
.overrides_with_all(&[
|
||||||
options::indicator_style::FILE_TYPE,
|
options::indicator_style::FILE_TYPE,
|
||||||
options::indicator_style::SLASH,
|
options::indicator_style::SLASH,
|
||||||
options::indicator_style::CLASSIFY,
|
options::indicator_style::CLASSIFY,
|
||||||
options::INDICATOR_STYLE,
|
options::INDICATOR_STYLE,
|
||||||
]))
|
]),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
//This still needs support for posix-*, +FORMAT
|
//This still needs support for posix-*, +FORMAT
|
||||||
Arg::with_name(options::TIME_STYLE)
|
Arg::with_name(options::TIME_STYLE)
|
||||||
|
@ -1075,36 +1131,25 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.help("time/date format with -l; see TIME_STYLE below")
|
.help("time/date format with -l; see TIME_STYLE below")
|
||||||
.value_name("TIME_STYLE")
|
.value_name("TIME_STYLE")
|
||||||
.env("TIME_STYLE")
|
.env("TIME_STYLE")
|
||||||
.possible_values(&[
|
.possible_values(&["full-iso", "long-iso", "iso", "locale"])
|
||||||
"full-iso",
|
.overrides_with_all(&[options::TIME_STYLE]),
|
||||||
"long-iso",
|
|
||||||
"iso",
|
|
||||||
"locale",
|
|
||||||
])
|
|
||||||
.overrides_with_all(&[
|
|
||||||
options::TIME_STYLE
|
|
||||||
])
|
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::FULL_TIME)
|
Arg::with_name(options::FULL_TIME)
|
||||||
.long(options::FULL_TIME)
|
.long(options::FULL_TIME)
|
||||||
.overrides_with(options::FULL_TIME)
|
.overrides_with(options::FULL_TIME)
|
||||||
.help("like -l --time-style=full-iso")
|
.help("like -l --time-style=full-iso"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Positional arguments
|
// Positional arguments
|
||||||
.arg(Arg::with_name(options::PATHS).multiple(true).takes_value(true))
|
.arg(
|
||||||
|
Arg::with_name(options::PATHS)
|
||||||
.after_help(AFTER_HELP);
|
.multiple(true)
|
||||||
|
.takes_value(true),
|
||||||
let matches = app.get_matches_from(args);
|
)
|
||||||
|
.after_help(
|
||||||
let locs = matches
|
"The TIME_STYLE argument can be full-iso, long-iso, iso. \
|
||||||
.values_of(options::PATHS)
|
Also the TIME_STYLE environment variable sets the default style to use.",
|
||||||
.map(|v| v.map(ToString::to_string).collect())
|
)
|
||||||
.unwrap_or_else(|| vec![String::from(".")]);
|
|
||||||
|
|
||||||
list(locs, Config::from(matches))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a Path along with it's associated data
|
/// Represents a Path along with it's associated data
|
||||||
|
@ -1187,31 +1232,27 @@ impl PathData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list(locs: Vec<String>, config: Config) -> i32 {
|
fn list(locs: Vec<String>, config: Config) -> UResult<()> {
|
||||||
let mut files = Vec::<PathData>::new();
|
let mut files = Vec::<PathData>::new();
|
||||||
let mut dirs = Vec::<PathData>::new();
|
let mut dirs = Vec::<PathData>::new();
|
||||||
let mut has_failed = false;
|
|
||||||
|
|
||||||
let mut out = BufWriter::new(stdout());
|
let mut out = BufWriter::new(stdout());
|
||||||
|
|
||||||
for loc in &locs {
|
for loc in &locs {
|
||||||
let p = PathBuf::from(&loc);
|
let p = PathBuf::from(&loc);
|
||||||
if !p.exists() {
|
let path_data = PathData::new(p, None, None, &config, true);
|
||||||
show_error!("'{}': {}", &loc, "No such file or directory");
|
|
||||||
/*
|
if path_data.md().is_none() {
|
||||||
We found an error, the return code of ls should not be 0
|
show!(std::io::ErrorKind::NotFound
|
||||||
And no need to continue the execution
|
.map_err_context(|| format!("cannot access '{}'", path_data.p_buf.display())));
|
||||||
*/
|
// We found an error, no need to continue the execution
|
||||||
has_failed = true;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path_data = PathData::new(p, None, None, &config, true);
|
|
||||||
|
|
||||||
let show_dir_contents = match path_data.file_type() {
|
let show_dir_contents = match path_data.file_type() {
|
||||||
Some(ft) => !config.directory && ft.is_dir(),
|
Some(ft) => !config.directory && ft.is_dir(),
|
||||||
None => {
|
None => {
|
||||||
has_failed = true;
|
set_exit_code(1);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1232,11 +1273,8 @@ fn list(locs: Vec<String>, config: Config) -> i32 {
|
||||||
}
|
}
|
||||||
enter_directory(&dir, &config, &mut out);
|
enter_directory(&dir, &config, &mut out);
|
||||||
}
|
}
|
||||||
if has_failed {
|
|
||||||
1
|
Ok(())
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_entries(entries: &mut Vec<PathData>, config: &Config) {
|
fn sort_entries(entries: &mut Vec<PathData>, config: &Config) {
|
||||||
|
@ -1253,7 +1291,8 @@ fn sort_entries(entries: &mut Vec<PathData>, config: &Config) {
|
||||||
}
|
}
|
||||||
// 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_by(|a, b| version_cmp::version_cmp(&a.p_buf, &b.p_buf)),
|
Sort::Version => entries
|
||||||
|
.sort_by(|a, b| version_cmp(&a.p_buf.to_string_lossy(), &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()
|
||||||
|
@ -1270,7 +1309,8 @@ fn sort_entries(entries: &mut Vec<PathData>, config: &Config) {
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn is_hidden(file_path: &DirEntry) -> bool {
|
fn is_hidden(file_path: &DirEntry) -> bool {
|
||||||
let metadata = fs::metadata(file_path.path()).unwrap();
|
let path = file_path.path();
|
||||||
|
let metadata = fs::metadata(&path).unwrap_or_else(|_| fs::symlink_metadata(&path).unwrap());
|
||||||
let attr = metadata.file_attributes();
|
let attr = metadata.file_attributes();
|
||||||
(attr & 0x2) > 0
|
(attr & 0x2) > 0
|
||||||
}
|
}
|
||||||
|
@ -1331,7 +1371,7 @@ fn enter_directory(dir: &PathData, config: &Config, out: &mut BufWriter<Stdout>)
|
||||||
|
|
||||||
fn get_metadata(entry: &Path, dereference: bool) -> std::io::Result<Metadata> {
|
fn get_metadata(entry: &Path, dereference: bool) -> std::io::Result<Metadata> {
|
||||||
if dereference {
|
if dereference {
|
||||||
entry.metadata().or_else(|_| entry.symlink_metadata())
|
entry.metadata()
|
||||||
} else {
|
} else {
|
||||||
entry.symlink_metadata()
|
entry.symlink_metadata()
|
||||||
}
|
}
|
||||||
|
@ -1463,8 +1503,6 @@ fn display_grid(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use uucore::fs::display_permissions;
|
|
||||||
|
|
||||||
fn display_item_long(
|
fn display_item_long(
|
||||||
item: &PathData,
|
item: &PathData,
|
||||||
max_links: usize,
|
max_links: usize,
|
||||||
|
@ -1474,7 +1512,7 @@ fn display_item_long(
|
||||||
) {
|
) {
|
||||||
let md = match item.md() {
|
let md = match item.md() {
|
||||||
None => {
|
None => {
|
||||||
show_error!("could not show file: {}", &item.p_buf.display());
|
show!(LsError::NoMetadata(item.p_buf.clone()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Some(md) => md,
|
Some(md) => md,
|
||||||
|
@ -1733,7 +1771,11 @@ fn display_file_name(path: &PathData, config: &Config) -> Option<Cell> {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
if config.format != Format::Long && config.inode {
|
if config.format != Format::Long && config.inode {
|
||||||
name = get_inode(path.md()?) + " " + &name;
|
name = path
|
||||||
|
.md()
|
||||||
|
.map_or_else(|| "?".to_string(), |md| get_inode(md))
|
||||||
|
+ " "
|
||||||
|
+ &name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,306 +0,0 @@
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
/// Compare paths in a way that matches the GNU version sort, meaning that
|
|
||||||
/// numbers get sorted in a natural way.
|
|
||||||
pub(crate) fn version_cmp(a: &Path, b: &Path) -> Ordering {
|
|
||||||
let a_string = a.to_string_lossy();
|
|
||||||
let b_string = b.to_string_lossy();
|
|
||||||
let mut a = a_string.chars().peekable();
|
|
||||||
let mut b = b_string.chars().peekable();
|
|
||||||
|
|
||||||
// The order determined from the number of leading zeroes.
|
|
||||||
// This is used if the filenames are equivalent up to leading zeroes.
|
|
||||||
let mut leading_zeroes = Ordering::Equal;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match (a.next(), b.next()) {
|
|
||||||
// If the characters are both numerical. We collect the rest of the number
|
|
||||||
// and parse them to u64's and compare them.
|
|
||||||
(Some(a_char @ '0'..='9'), Some(b_char @ '0'..='9')) => {
|
|
||||||
let mut a_leading_zeroes = 0;
|
|
||||||
if a_char == '0' {
|
|
||||||
a_leading_zeroes = 1;
|
|
||||||
while let Some('0') = a.peek() {
|
|
||||||
a_leading_zeroes += 1;
|
|
||||||
a.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut b_leading_zeroes = 0;
|
|
||||||
if b_char == '0' {
|
|
||||||
b_leading_zeroes = 1;
|
|
||||||
while let Some('0') = b.peek() {
|
|
||||||
b_leading_zeroes += 1;
|
|
||||||
b.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The first different number of leading zeros determines the order
|
|
||||||
// so if it's already been determined by a previous number, we leave
|
|
||||||
// it as that ordering.
|
|
||||||
// It's b.cmp(&a), because the *largest* number of leading zeros
|
|
||||||
// should go first
|
|
||||||
if leading_zeroes == Ordering::Equal {
|
|
||||||
leading_zeroes = b_leading_zeroes.cmp(&a_leading_zeroes);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut a_str = String::new();
|
|
||||||
let mut b_str = String::new();
|
|
||||||
if a_char != '0' {
|
|
||||||
a_str.push(a_char);
|
|
||||||
}
|
|
||||||
if b_char != '0' {
|
|
||||||
b_str.push(b_char);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unwrapping here is fine because we only call next if peek returns
|
|
||||||
// Some(_), so next should also return Some(_).
|
|
||||||
while let Some('0'..='9') = a.peek() {
|
|
||||||
a_str.push(a.next().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
while let Some('0'..='9') = b.peek() {
|
|
||||||
b_str.push(b.next().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since the leading zeroes are stripped, the length can be
|
|
||||||
// used to compare the numbers.
|
|
||||||
match a_str.len().cmp(&b_str.len()) {
|
|
||||||
Ordering::Equal => {}
|
|
||||||
x => return x,
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, leading zeroes are stripped and the lengths
|
|
||||||
// are equal, meaning that the strings can be compared using
|
|
||||||
// the standard compare function.
|
|
||||||
match a_str.cmp(&b_str) {
|
|
||||||
Ordering::Equal => {}
|
|
||||||
x => return x,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If there are two characters we just compare the characters
|
|
||||||
(Some(a_char), Some(b_char)) => match a_char.cmp(&b_char) {
|
|
||||||
Ordering::Equal => {}
|
|
||||||
x => return x,
|
|
||||||
},
|
|
||||||
// Otherwise, we compare the options (because None < Some(_))
|
|
||||||
(a_opt, b_opt) => match a_opt.cmp(&b_opt) {
|
|
||||||
// If they are completely equal except for leading zeroes, we use the leading zeroes.
|
|
||||||
Ordering::Equal => return leading_zeroes,
|
|
||||||
x => return x,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::version_cmp::version_cmp;
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
#[test]
|
|
||||||
fn test_version_cmp() {
|
|
||||||
// Identical strings
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("hello"), &PathBuf::from("hello")),
|
|
||||||
Ordering::Equal
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("file12"), &PathBuf::from("file12")),
|
|
||||||
Ordering::Equal
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(
|
|
||||||
&PathBuf::from("file12-suffix"),
|
|
||||||
&PathBuf::from("file12-suffix")
|
|
||||||
),
|
|
||||||
Ordering::Equal
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(
|
|
||||||
&PathBuf::from("file12-suffix24"),
|
|
||||||
&PathBuf::from("file12-suffix24")
|
|
||||||
),
|
|
||||||
Ordering::Equal
|
|
||||||
);
|
|
||||||
|
|
||||||
// Shortened names
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("world"), &PathBuf::from("wo")),
|
|
||||||
Ordering::Greater,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("hello10wo"), &PathBuf::from("hello10world")),
|
|
||||||
Ordering::Less,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Simple names
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("world"), &PathBuf::from("hello")),
|
|
||||||
Ordering::Greater,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("hello"), &PathBuf::from("world")),
|
|
||||||
Ordering::Less
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("apple"), &PathBuf::from("ant")),
|
|
||||||
Ordering::Greater
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("ant"), &PathBuf::from("apple")),
|
|
||||||
Ordering::Less
|
|
||||||
);
|
|
||||||
|
|
||||||
// Uppercase letters
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("Beef"), &PathBuf::from("apple")),
|
|
||||||
Ordering::Less,
|
|
||||||
"Uppercase letters are sorted before all lowercase letters"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("Apple"), &PathBuf::from("apple")),
|
|
||||||
Ordering::Less
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("apple"), &PathBuf::from("aPple")),
|
|
||||||
Ordering::Greater
|
|
||||||
);
|
|
||||||
|
|
||||||
// Numbers
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("100"), &PathBuf::from("20")),
|
|
||||||
Ordering::Greater,
|
|
||||||
"Greater numbers are greater even if they start with a smaller digit",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("20"), &PathBuf::from("20")),
|
|
||||||
Ordering::Equal,
|
|
||||||
"Equal numbers are equal"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("15"), &PathBuf::from("200")),
|
|
||||||
Ordering::Less,
|
|
||||||
"Small numbers are smaller"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Comparing numbers with other characters
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("1000"), &PathBuf::from("apple")),
|
|
||||||
Ordering::Less,
|
|
||||||
"Numbers are sorted before other characters"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
// spell-checker:disable-next-line
|
|
||||||
version_cmp(&PathBuf::from("file1000"), &PathBuf::from("fileapple")),
|
|
||||||
Ordering::Less,
|
|
||||||
"Numbers in the middle of the name are sorted before other characters"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Leading zeroes
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("012"), &PathBuf::from("12")),
|
|
||||||
Ordering::Less,
|
|
||||||
"A single leading zero can make a difference"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("000800"), &PathBuf::from("0000800")),
|
|
||||||
Ordering::Greater,
|
|
||||||
"Leading number of zeroes is used even if both non-zero number of zeros"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Numbers and other characters combined
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("ab10"), &PathBuf::from("aa11")),
|
|
||||||
Ordering::Greater
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("aa10"), &PathBuf::from("aa11")),
|
|
||||||
Ordering::Less,
|
|
||||||
"Numbers after other characters are handled correctly."
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("aa2"), &PathBuf::from("aa100")),
|
|
||||||
Ordering::Less,
|
|
||||||
"Numbers after alphabetical characters are handled correctly."
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("aa10bb"), &PathBuf::from("aa11aa")),
|
|
||||||
Ordering::Less,
|
|
||||||
"Number is used even if alphabetical characters after it differ."
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("aa10aa0010"), &PathBuf::from("aa11aa1")),
|
|
||||||
Ordering::Less,
|
|
||||||
"Second number is ignored if the first number differs."
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("aa10aa0010"), &PathBuf::from("aa10aa1")),
|
|
||||||
Ordering::Greater,
|
|
||||||
"Second number is used if the rest is equal."
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("aa10aa0010"), &PathBuf::from("aa00010aa1")),
|
|
||||||
Ordering::Greater,
|
|
||||||
"Second number is used if the rest is equal up to leading zeroes of the first number."
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("aa10aa0022"), &PathBuf::from("aa010aa022")),
|
|
||||||
Ordering::Greater,
|
|
||||||
"The leading zeroes of the first number has priority."
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("aa10aa0022"), &PathBuf::from("aa10aa022")),
|
|
||||||
Ordering::Less,
|
|
||||||
"The leading zeroes of other numbers than the first are used."
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(&PathBuf::from("file-1.4"), &PathBuf::from("file-1.13")),
|
|
||||||
Ordering::Less,
|
|
||||||
"Periods are handled as normal text, not as a decimal point."
|
|
||||||
);
|
|
||||||
|
|
||||||
// Greater than u64::Max
|
|
||||||
// u64 == 18446744073709551615 so this should be plenty:
|
|
||||||
// 20000000000000000000000
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(
|
|
||||||
&PathBuf::from("aa2000000000000000000000bb"),
|
|
||||||
&PathBuf::from("aa002000000000000000000001bb")
|
|
||||||
),
|
|
||||||
Ordering::Less,
|
|
||||||
"Numbers larger than u64::MAX are handled correctly without crashing"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
version_cmp(
|
|
||||||
&PathBuf::from("aa2000000000000000000000bb"),
|
|
||||||
&PathBuf::from("aa002000000000000000000000bb")
|
|
||||||
),
|
|
||||||
Ordering::Greater,
|
|
||||||
"Leading zeroes for numbers larger than u64::MAX are handled correctly without crashing"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/mkdir.rs"
|
path = "src/mkdir.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs", "mode"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs", "mode"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -5,151 +5,126 @@
|
||||||
// * 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.
|
||||||
|
|
||||||
|
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||||
|
#![allow(clippy::nonstandard_macro_braces)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
|
use clap::OsValues;
|
||||||
use clap::{crate_version, App, Arg};
|
use clap::{crate_version, App, Arg};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use uucore::error::{FromIo, UResult, USimpleError};
|
||||||
|
|
||||||
static ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist";
|
static ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist";
|
||||||
static OPT_MODE: &str = "mode";
|
mod options {
|
||||||
static OPT_PARENTS: &str = "parents";
|
pub const MODE: &str = "mode";
|
||||||
static OPT_VERBOSE: &str = "verbose";
|
pub const PARENTS: &str = "parents";
|
||||||
|
pub const VERBOSE: &str = "verbose";
|
||||||
static ARG_DIRS: &str = "dirs";
|
pub const DIRS: &str = "dirs";
|
||||||
|
}
|
||||||
|
|
||||||
fn get_usage() -> String {
|
fn get_usage() -> String {
|
||||||
format!("{0} [OPTION]... [USER]", executable!())
|
format!("{0} [OPTION]... [USER]", executable!())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#[uucore_procs::gen_uumain]
|
||||||
* Handles option parsing
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
*/
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
|
||||||
let usage = get_usage();
|
let usage = get_usage();
|
||||||
|
|
||||||
// Linux-specific options, not implemented
|
// Linux-specific options, not implemented
|
||||||
// opts.optflag("Z", "context", "set SELinux security context" +
|
// opts.optflag("Z", "context", "set SELinux security context" +
|
||||||
// " of each created directory to CTX"),
|
// " of each created directory to CTX"),
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
.version(crate_version!())
|
|
||||||
.about(ABOUT)
|
|
||||||
.usage(&usage[..])
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_MODE)
|
|
||||||
.short("m")
|
|
||||||
.long(OPT_MODE)
|
|
||||||
.help("set file mode")
|
|
||||||
.default_value("755"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_PARENTS)
|
|
||||||
.short("p")
|
|
||||||
.long(OPT_PARENTS)
|
|
||||||
.alias("parent")
|
|
||||||
.help("make parent directories as needed"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_VERBOSE)
|
|
||||||
.short("v")
|
|
||||||
.long(OPT_VERBOSE)
|
|
||||||
.help("print a message for each printed directory"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(ARG_DIRS)
|
|
||||||
.multiple(true)
|
|
||||||
.takes_value(true)
|
|
||||||
.min_values(1),
|
|
||||||
)
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let dirs: Vec<String> = matches
|
let dirs = matches.values_of_os(options::DIRS).unwrap_or_default();
|
||||||
.values_of(ARG_DIRS)
|
let verbose = matches.is_present(options::VERBOSE);
|
||||||
.map(|v| v.map(ToString::to_string).collect())
|
let recursive = matches.is_present(options::PARENTS);
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let verbose = matches.is_present(OPT_VERBOSE);
|
|
||||||
let recursive = matches.is_present(OPT_PARENTS);
|
|
||||||
|
|
||||||
// Translate a ~str in octal form to u16, default to 755
|
// Translate a ~str in octal form to u16, default to 755
|
||||||
// Not tested on Windows
|
// Not tested on Windows
|
||||||
let mode_match = matches.value_of(OPT_MODE);
|
let mode: u16 = match matches.value_of(options::MODE) {
|
||||||
let mode: u16 = match mode_match {
|
Some(m) => u16::from_str_radix(m, 8)
|
||||||
Some(m) => {
|
.map_err(|_| USimpleError::new(1, format!("invalid mode '{}'", m)))?,
|
||||||
let res: Option<u16> = u16::from_str_radix(m, 8).ok();
|
None => 0o755_u16,
|
||||||
match res {
|
|
||||||
Some(r) => r,
|
|
||||||
_ => crash!(1, "no mode given"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => 0o755_u16,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exec(dirs, recursive, mode, verbose)
|
exec(dirs, recursive, mode, verbose)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
* Create the list of new directories
|
App::new(executable!())
|
||||||
*/
|
.version(crate_version!())
|
||||||
fn exec(dirs: Vec<String>, recursive: bool, mode: u16, verbose: bool) -> i32 {
|
.about(ABOUT)
|
||||||
let mut status = 0;
|
.arg(
|
||||||
let empty = Path::new("");
|
Arg::with_name(options::MODE)
|
||||||
for dir in &dirs {
|
.short("m")
|
||||||
let path = Path::new(dir);
|
.long(options::MODE)
|
||||||
if !recursive {
|
.help("set file mode (not implemented on windows)")
|
||||||
if let Some(parent) = path.parent() {
|
.default_value("755"),
|
||||||
if parent != empty && !parent.exists() {
|
)
|
||||||
show_error!(
|
.arg(
|
||||||
"cannot create directory '{}': No such file or directory",
|
Arg::with_name(options::PARENTS)
|
||||||
path.display()
|
.short("p")
|
||||||
);
|
.long(options::PARENTS)
|
||||||
status = 1;
|
.alias("parent")
|
||||||
continue;
|
.help("make parent directories as needed"),
|
||||||
}
|
)
|
||||||
}
|
.arg(
|
||||||
}
|
Arg::with_name(options::VERBOSE)
|
||||||
status |= mkdir(path, recursive, mode, verbose);
|
.short("v")
|
||||||
}
|
.long(options::VERBOSE)
|
||||||
status
|
.help("print a message for each printed directory"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::DIRS)
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.min_values(1),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper to catch errors, return 1 if failed
|
* Create the list of new directories
|
||||||
*/
|
*/
|
||||||
fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> i32 {
|
fn exec(dirs: OsValues, recursive: bool, mode: u16, verbose: bool) -> UResult<()> {
|
||||||
|
for dir in dirs {
|
||||||
|
let path = Path::new(dir);
|
||||||
|
show_if_err!(mkdir(path, recursive, mode, verbose));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> UResult<()> {
|
||||||
let create_dir = if recursive {
|
let create_dir = if recursive {
|
||||||
fs::create_dir_all
|
fs::create_dir_all
|
||||||
} else {
|
} else {
|
||||||
fs::create_dir
|
fs::create_dir
|
||||||
};
|
};
|
||||||
if let Err(e) = create_dir(path) {
|
|
||||||
show_error!("{}: {}", path.display(), e.to_string());
|
create_dir(path).map_err_context(|| format!("cannot create directory '{}'", path.display()))?;
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
println!("{}: created directory '{}'", executable!(), path.display());
|
println!("{}: created directory '{}'", executable!(), path.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chmod(path, mode)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(unix, target_os = "redox"))]
|
#[cfg(any(unix, target_os = "redox"))]
|
||||||
fn chmod(path: &Path, mode: u16) -> i32 {
|
fn chmod(path: &Path, mode: u16) -> UResult<()> {
|
||||||
use std::fs::{set_permissions, Permissions};
|
use std::fs::{set_permissions, Permissions};
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
let mode = Permissions::from_mode(u32::from(mode));
|
let mode = Permissions::from_mode(u32::from(mode));
|
||||||
|
|
||||||
if let Err(err) = set_permissions(path, mode) {
|
set_permissions(path, mode)
|
||||||
show_error!("{}: {}", path.display(), err);
|
.map_err_context(|| format!("cannot set permissions '{}'", path.display()))
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[allow(unused_variables)]
|
fn chmod(_path: &Path, _mode: u16) -> UResult<()> {
|
||||||
fn chmod(path: &Path, mode: u16) -> i32 {
|
|
||||||
// chmod on Windows only sets the readonly flag, which isn't even honored on directories
|
// chmod on Windows only sets the readonly flag, which isn't even honored on directories
|
||||||
0
|
Ok(())
|
||||||
}
|
|
||||||
chmod(path, mode)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
path = "src/mkfifo.rs"
|
path = "src/mkfifo.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
|
@ -29,27 +29,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.collect_str(InvalidEncodingHandling::Ignore)
|
.collect_str(InvalidEncodingHandling::Ignore)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let matches = App::new(executable!())
|
let matches = uu_app().get_matches_from(args);
|
||||||
.name(NAME)
|
|
||||||
.version(crate_version!())
|
|
||||||
.usage(USAGE)
|
|
||||||
.about(SUMMARY)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::MODE)
|
|
||||||
.short("m")
|
|
||||||
.long(options::MODE)
|
|
||||||
.help("file permissions for the fifo")
|
|
||||||
.default_value("0666")
|
|
||||||
.value_name("0666"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::SE_LINUX_SECURITY_CONTEXT)
|
|
||||||
.short(options::SE_LINUX_SECURITY_CONTEXT)
|
|
||||||
.help("set the SELinux security context to default type")
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(options::CONTEXT).long(options::CONTEXT).value_name("CTX").help("like -Z, or if CTX is specified then set the SELinux\nor SMACK security context to CTX"))
|
|
||||||
.arg(Arg::with_name(options::FIFO).hidden(true).multiple(true))
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
if matches.is_present(options::CONTEXT) {
|
if matches.is_present(options::CONTEXT) {
|
||||||
crash!(1, "--context is not implemented");
|
crash!(1, "--context is not implemented");
|
||||||
|
@ -88,3 +68,34 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
|
||||||
exit_code
|
exit_code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
App::new(executable!())
|
||||||
|
.name(NAME)
|
||||||
|
.version(crate_version!())
|
||||||
|
.usage(USAGE)
|
||||||
|
.about(SUMMARY)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::MODE)
|
||||||
|
.short("m")
|
||||||
|
.long(options::MODE)
|
||||||
|
.help("file permissions for the fifo")
|
||||||
|
.default_value("0666")
|
||||||
|
.value_name("0666"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::SE_LINUX_SECURITY_CONTEXT)
|
||||||
|
.short(options::SE_LINUX_SECURITY_CONTEXT)
|
||||||
|
.help("set the SELinux security context to default type"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::CONTEXT)
|
||||||
|
.long(options::CONTEXT)
|
||||||
|
.value_name("CTX")
|
||||||
|
.help(
|
||||||
|
"like -Z, or if CTX is specified then set the SELinux \
|
||||||
|
or SMACK security context to CTX",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name(options::FIFO).hidden(true).multiple(true))
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue