mirror of
https://github.com/uutils/coreutils
synced 2024-12-15 15:52:42 +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 (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:
|
||||
PROJECT_NAME: coreutils
|
||||
PROJECT_DESC: "Core universal (cross-platform) utilities"
|
||||
|
@ -17,6 +19,40 @@ env:
|
|||
on: [push, pull_request]
|
||||
|
||||
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:
|
||||
name: Style/format
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
|
@ -26,13 +62,13 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
## 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
|
||||
# * CARGO_FEATURES_OPTION
|
||||
CARGO_FEATURES_OPTION='' ;
|
||||
|
@ -48,36 +84,19 @@ jobs:
|
|||
- name: "`fmt` testing"
|
||||
shell: bash
|
||||
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>
|
||||
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"
|
||||
if: success() || failure() # run regardless of prior step success/failure
|
||||
shell: bash
|
||||
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>
|
||||
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:
|
||||
name: Style/spelling
|
||||
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
|
||||
code_lint:
|
||||
name: Style/lint
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
@ -87,13 +106,13 @@ jobs:
|
|||
- { os: macos-latest , features: feat_os_macos }
|
||||
- { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
## 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
|
||||
# * CARGO_FEATURES_OPTION
|
||||
CARGO_FEATURES_OPTION='' ;
|
||||
|
@ -106,13 +125,32 @@ jobs:
|
|||
default: true
|
||||
profile: minimal # minimal component installation (ie, no documentation)
|
||||
components: clippy
|
||||
- name: "`clippy` testing"
|
||||
if: success() || failure() # run regardless of prior step success/failure
|
||||
- name: "`clippy` lint testing"
|
||||
shell: bash
|
||||
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>
|
||||
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:
|
||||
name: MinRustV # Minimum supported rust version
|
||||
|
@ -122,7 +160,7 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }})
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
@ -137,33 +175,32 @@ jobs:
|
|||
use-tool-cache: true
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: stable
|
||||
- name: Confirm compatible 'Cargo.lock'
|
||||
- name: Confirm MinSRV compatible 'Cargo.lock'
|
||||
shell: bash
|
||||
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 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
|
||||
shell: bash
|
||||
run: |
|
||||
# Info
|
||||
## environment
|
||||
## Info
|
||||
# environment
|
||||
echo "## environment"
|
||||
echo "CI='${CI}'"
|
||||
## tooling info display
|
||||
# tooling info display
|
||||
echo "## tooling"
|
||||
which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true
|
||||
rustup -V
|
||||
rustup -V 2>/dev/null
|
||||
rustup show active-toolchain
|
||||
cargo -V
|
||||
rustc -V
|
||||
cargo-tree tree -V
|
||||
## dependencies
|
||||
# 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 --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
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
|
@ -172,8 +209,8 @@ jobs:
|
|||
env:
|
||||
RUSTFLAGS: '-Awarnings'
|
||||
|
||||
busybox_test:
|
||||
name: Busybox test suite
|
||||
build_makefile:
|
||||
name: Build/Makefile
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
@ -181,49 +218,26 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest }
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- 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: "prepare busytest"
|
||||
- name: Install/setup prerequisites
|
||||
shell: bash
|
||||
run: |
|
||||
make prepare-busytest
|
||||
- name: "run busybox testsuite"
|
||||
## Install/setup prerequisites
|
||||
sudo apt-get -y update ; sudo apt-get -y install python3-sphinx ;
|
||||
- name: "`make build`"
|
||||
shell: bash
|
||||
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
|
||||
- name: "`make test`"
|
||||
shell: bash
|
||||
run: |
|
||||
make test
|
||||
|
||||
build:
|
||||
name: Build
|
||||
|
@ -235,8 +249,6 @@ jobs:
|
|||
# { 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: 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: 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-msvc , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install/setup prerequisites
|
||||
shell: bash
|
||||
run: |
|
||||
## install/setup prerequisites
|
||||
## Install/setup prerequisites
|
||||
case '${{ matrix.job.target }}' in
|
||||
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 ;;
|
||||
|
@ -267,7 +279,7 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
## 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="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)
|
||||
|
@ -353,7 +365,7 @@ jobs:
|
|||
- name: Create all needed build/work directories
|
||||
shell: bash
|
||||
run: |
|
||||
## create build/work space
|
||||
## Create build/work space
|
||||
mkdir -p '${{ steps.vars.outputs.STAGING }}'
|
||||
mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}'
|
||||
mkdir -p '${{ steps.vars.outputs.STAGING }}/dpkg'
|
||||
|
@ -373,7 +385,7 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
## 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
|
||||
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
|
||||
echo UTILITY_LIST=${UTILITY_LIST}
|
||||
|
@ -390,26 +402,26 @@ jobs:
|
|||
- name: Info
|
||||
shell: bash
|
||||
run: |
|
||||
# Info
|
||||
## commit info
|
||||
## Info
|
||||
# commit info
|
||||
echo "## commit"
|
||||
echo GITHUB_REF=${GITHUB_REF}
|
||||
echo GITHUB_SHA=${GITHUB_SHA}
|
||||
## environment
|
||||
# environment
|
||||
echo "## environment"
|
||||
echo "CI='${CI}'"
|
||||
## tooling info display
|
||||
# tooling info display
|
||||
echo "## tooling"
|
||||
which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true
|
||||
rustup -V
|
||||
rustup -V 2>/dev/null
|
||||
rustup show active-toolchain
|
||||
cargo -V
|
||||
rustc -V
|
||||
cargo-tree tree -V
|
||||
## dependencies
|
||||
# dependencies
|
||||
echo "## dependency list"
|
||||
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
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
|
@ -436,7 +448,7 @@ jobs:
|
|||
- name: Package
|
||||
shell: bash
|
||||
run: |
|
||||
## package artifact(s)
|
||||
## Package artifact(s)
|
||||
# binary
|
||||
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)
|
||||
|
@ -477,6 +489,37 @@ jobs:
|
|||
env:
|
||||
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:
|
||||
name: Code Coverage
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
|
@ -489,11 +532,11 @@ jobs:
|
|||
- { os: macos-latest , features: macos }
|
||||
- { os: windows-latest , features: windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install/setup prerequisites
|
||||
shell: bash
|
||||
run: |
|
||||
## install/setup prerequisites
|
||||
## Install/setup prerequisites
|
||||
case '${{ matrix.job.os }}' in
|
||||
macos-latest) brew install coreutils ;; # needed for testing
|
||||
esac
|
||||
|
@ -504,7 +547,7 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
## 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="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
|
||||
|
@ -539,7 +582,7 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
## 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
|
||||
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
|
||||
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)"
|
||||
|
@ -587,7 +630,7 @@ jobs:
|
|||
id: coverage
|
||||
shell: bash
|
||||
run: |
|
||||
# generate coverage data
|
||||
## Generate coverage data
|
||||
COVERAGE_REPORT_DIR="target/debug"
|
||||
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?)
|
||||
|
|
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]
|
||||
|
||||
|
@ -7,7 +9,6 @@ jobs:
|
|||
name: Run GNU tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Checks out a copy of your repository on the ubuntu-latest machine
|
||||
- name: Checkout code uutil
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
|
@ -18,7 +19,7 @@ jobs:
|
|||
repository: 'coreutils/coreutils'
|
||||
path: 'gnu'
|
||||
ref: v8.32
|
||||
- name: Checkout GNU corelib
|
||||
- name: Checkout GNU coreutils library (gnulib)
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'coreutils/gnulib'
|
||||
|
@ -32,16 +33,18 @@ jobs:
|
|||
default: true
|
||||
profile: minimal # minimal component installation (ie, no documentation)
|
||||
components: rustfmt
|
||||
- name: Install deps
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
## Install dependencies
|
||||
sudo apt-get update
|
||||
sudo apt-get install autoconf autopoint bison texinfo gperf gcc g++ gdb python-pyinotify python3-sphinx jq
|
||||
- name: Build binaries
|
||||
shell: bash
|
||||
run: |
|
||||
cd uutils
|
||||
bash util/build-gnu.sh
|
||||
## Build binaries
|
||||
cd uutils
|
||||
bash util/build-gnu.sh
|
||||
- name: Run GNU tests
|
||||
shell: bash
|
||||
run: |
|
||||
|
@ -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/zero.sh
|
||||
bash uutils/util/run-gnu-test.sh tests/id/gnu-zero-uids.sh
|
||||
- name: Extract tests info
|
||||
- name: Extract testing info
|
||||
shell: bash
|
||||
run: |
|
||||
## Extract testing info
|
||||
LOG_FILE=gnu/tests/test-suite.log
|
||||
if test -f "$LOG_FILE"
|
||||
then
|
||||
|
@ -65,7 +69,13 @@ jobs:
|
|||
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)
|
||||
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 \
|
||||
--arg date "$(date --rfc-email)" \
|
||||
--arg sha "$GITHUB_SHA" \
|
||||
|
@ -79,12 +89,10 @@ jobs:
|
|||
else
|
||||
echo "::error ::Failed to get summary of test results"
|
||||
fi
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: test-report
|
||||
path: gnu/tests/**/*.log
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: gnu-result
|
|
@ -12,6 +12,7 @@ FIFOs
|
|||
FQDN # fully qualified domain name
|
||||
GID # group ID
|
||||
GIDs
|
||||
GNU
|
||||
GNUEABI
|
||||
GNUEABIhf
|
||||
JFS
|
||||
|
@ -45,6 +46,7 @@ Deno
|
|||
EditorConfig
|
||||
FreeBSD
|
||||
Gmail
|
||||
GNU
|
||||
Irix
|
||||
MS-DOS
|
||||
MSDOS
|
||||
|
|
|
@ -78,6 +78,7 @@ symlinks
|
|||
syscall
|
||||
syscalls
|
||||
tokenize
|
||||
toolchain
|
||||
truthy
|
||||
unbuffered
|
||||
unescape
|
||||
|
|
|
@ -58,6 +58,10 @@ Haitao Li
|
|||
Inokentiy Babushkin
|
||||
Inokentiy
|
||||
Babushkin
|
||||
Jan Scheer * jhscheer
|
||||
Jan
|
||||
Scheer
|
||||
jhscheer
|
||||
Jeremiah Peschka
|
||||
Jeremiah
|
||||
Peschka
|
||||
|
|
|
@ -48,17 +48,19 @@ xattr
|
|||
# * rust/rustc
|
||||
RUSTDOCFLAGS
|
||||
RUSTFLAGS
|
||||
clippy
|
||||
rustc
|
||||
rustfmt
|
||||
rustup
|
||||
#
|
||||
bitor # BitOr trait function
|
||||
bitxor # BitXor trait function
|
||||
clippy
|
||||
concat
|
||||
fract
|
||||
powi
|
||||
println
|
||||
repr
|
||||
rfind
|
||||
rustc
|
||||
rustfmt
|
||||
struct
|
||||
structs
|
||||
substr
|
||||
|
|
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -220,6 +220,7 @@ dependencies = [
|
|||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"term_size",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
|
@ -261,6 +262,7 @@ version = "0.0.6"
|
|||
dependencies = [
|
||||
"atty",
|
||||
"chrono",
|
||||
"clap",
|
||||
"conv",
|
||||
"filetime",
|
||||
"glob 0.3.0",
|
||||
|
@ -1566,21 +1568,6 @@ dependencies = [
|
|||
"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]]
|
||||
name = "sha1"
|
||||
version = "0.6.0"
|
||||
|
@ -2107,6 +2094,7 @@ dependencies = [
|
|||
name = "uu_expr"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"libc",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
|
@ -2134,6 +2122,7 @@ dependencies = [
|
|||
name = "uu_false"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
]
|
||||
|
@ -2199,6 +2188,7 @@ dependencies = [
|
|||
name = "uu_hostid"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"libc",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
|
@ -2476,6 +2466,7 @@ name = "uu_pr"
|
|||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"getopts",
|
||||
"itertools 0.10.0",
|
||||
"quick-error 2.0.1",
|
||||
|
@ -2498,6 +2489,7 @@ dependencies = [
|
|||
name = "uu_printf"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"itertools 0.8.2",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
|
@ -2631,7 +2623,6 @@ dependencies = [
|
|||
"ouroboros",
|
||||
"rand 0.7.3",
|
||||
"rayon",
|
||||
"semver",
|
||||
"tempfile",
|
||||
"unicode-width",
|
||||
"uucore",
|
||||
|
@ -2734,6 +2725,7 @@ dependencies = [
|
|||
name = "uu_test"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"libc",
|
||||
"redox_syscall 0.1.57",
|
||||
"uucore",
|
||||
|
@ -2777,6 +2769,7 @@ dependencies = [
|
|||
name = "uu_true"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
]
|
||||
|
|
|
@ -63,6 +63,7 @@ feat_common_core = [
|
|||
"more",
|
||||
"mv",
|
||||
"nl",
|
||||
"numfmt",
|
||||
"od",
|
||||
"paste",
|
||||
"pr",
|
||||
|
@ -160,7 +161,6 @@ feat_require_unix = [
|
|||
"mkfifo",
|
||||
"mknod",
|
||||
"nice",
|
||||
"numfmt",
|
||||
"nohup",
|
||||
"pathchk",
|
||||
"stat",
|
||||
|
@ -225,6 +225,7 @@ test = [ "uu_test" ]
|
|||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
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
|
||||
uucore = { version=">=0.0.8", package="uucore", path="src/uucore" }
|
||||
|
|
|
@ -314,6 +314,11 @@ else
|
|||
endif
|
||||
$(foreach man, $(filter $(INSTALLEES), $(basename $(notdir $(wildcard $(DOCSDIR)/_build/man/*)))), \
|
||||
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:
|
||||
ifeq (${MULTICALL}, y)
|
||||
|
@ -321,6 +326,9 @@ ifeq (${MULTICALL}, y)
|
|||
endif
|
||||
rm -f $(addprefix $(INSTALLDIR_MAN)/,$(PROG_PREFIX)coreutils.1.gz)
|
||||
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)))
|
||||
|
||||
.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 does not install files necessary for shell completion. For shell completion to work,
|
||||
use `GNU Make` or see `Manually install shell completions`.
|
||||
|
||||
### GNU Make
|
||||
|
||||
To install all available utilities:
|
||||
|
@ -179,6 +182,10 @@ Set install parent directory (default value is /usr/local):
|
|||
$ 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
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
### 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 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();
|
||||
|
||||
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\
|
||||
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n\
|
||||
\tlet mut map = UtilityMap::new();\n\
|
||||
|
@ -54,10 +54,33 @@ pub fn main() {
|
|||
|
||||
for krate in crates {
|
||||
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) => {
|
||||
mf.write_all(
|
||||
format!(
|
||||
"\tmap.insert(\"{k}\", {krate}::uumain);\n",
|
||||
"\tmap.insert(\"{k}\", ({krate}::uumain, {krate}::uu_app));\n",
|
||||
k = krate[override_prefix.len()..].to_string(),
|
||||
krate = krate
|
||||
)
|
||||
|
@ -77,7 +100,7 @@ pub fn main() {
|
|||
"false" | "true" => {
|
||||
mf.write_all(
|
||||
format!(
|
||||
"\tmap.insert(\"{krate}\", r#{krate}::uumain);\n",
|
||||
"\tmap.insert(\"{krate}\", (r#{krate}::uumain, r#{krate}::uu_app));\n",
|
||||
krate = krate
|
||||
)
|
||||
.as_bytes(),
|
||||
|
@ -97,20 +120,20 @@ pub fn main() {
|
|||
mf.write_all(
|
||||
format!(
|
||||
"\
|
||||
\tmap.insert(\"{krate}\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"md5sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"sha1sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"sha224sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"sha256sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"sha384sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"sha512sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"sha3sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"sha3-224sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"sha3-256sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"sha3-384sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"sha3-512sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"shake128sum\", {krate}::uumain);\n\
|
||||
\t\tmap.insert(\"shake256sum\", {krate}::uumain);\n\
|
||||
\tmap.insert(\"{krate}\", ({krate}::uumain, {krate}::uu_app_custom));\n\
|
||||
\t\tmap.insert(\"md5sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"sha1sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"sha224sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"sha256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"sha384sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"sha512sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"sha3sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"sha3-224sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"sha3-256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"sha3-384sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"sha3-512sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"shake128sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
\t\tmap.insert(\"shake256sum\", ({krate}::uumain, {krate}::uu_app_common));\n\
|
||||
",
|
||||
krate = krate
|
||||
)
|
||||
|
@ -130,7 +153,7 @@ pub fn main() {
|
|||
_ => {
|
||||
mf.write_all(
|
||||
format!(
|
||||
"\tmap.insert(\"{krate}\", {krate}::uumain);\n",
|
||||
"\tmap.insert(\"{krate}\", ({krate}::uumain, {krate}::uu_app));\n",
|
||||
krate = krate
|
||||
)
|
||||
.as_bytes(),
|
||||
|
|
|
@ -16,7 +16,7 @@ Synopsis
|
|||
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.
|
||||
|
||||
--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
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use clap::App;
|
||||
use clap::Arg;
|
||||
use clap::Shell;
|
||||
use std::cmp;
|
||||
use std::collections::hash_map::HashMap;
|
||||
use std::ffi::OsString;
|
||||
|
@ -52,7 +55,7 @@ fn main() {
|
|||
let binary_as_util = name(&binary);
|
||||
|
||||
// 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)));
|
||||
}
|
||||
|
||||
|
@ -74,8 +77,12 @@ fn main() {
|
|||
if let Some(util_os) = util_name {
|
||||
let util = util_os.as_os_str().to_string_lossy();
|
||||
|
||||
if util == "completion" {
|
||||
gen_completions(args, utils);
|
||||
}
|
||||
|
||||
match utils.get(&util[..]) {
|
||||
Some(&uumain) => {
|
||||
Some(&(uumain, _)) => {
|
||||
process::exit(uumain((vec![util_os].into_iter()).chain(args)));
|
||||
}
|
||||
None => {
|
||||
|
@ -85,7 +92,7 @@ fn main() {
|
|||
let util = util_os.as_os_str().to_string_lossy();
|
||||
|
||||
match utils.get(&util[..]) {
|
||||
Some(&uumain) => {
|
||||
Some(&(uumain, _)) => {
|
||||
let code = uumain(
|
||||
(vec![util_os, OsString::from("--help")].into_iter())
|
||||
.chain(args),
|
||||
|
@ -113,3 +120,50 @@ fn main() {
|
|||
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]
|
||||
platform-info = "0.1"
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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
|
||||
// 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]
|
||||
extern crate uucore;
|
||||
|
||||
use platform_info::*;
|
||||
|
||||
use clap::{crate_version, App};
|
||||
use uucore::error::{FromIo, UResult};
|
||||
|
||||
static ABOUT: &str = "Display machine architecture";
|
||||
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!())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
.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"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features = ["encoding"] }
|
||||
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 clap::App;
|
||||
use uucore::encoding::Format;
|
||||
|
||||
pub mod base_common;
|
||||
|
@ -56,3 +57,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
0
|
||||
}
|
||||
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
base_common::base_app(executable!(), VERSION, ABOUT)
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ impl Config {
|
|||
Some(mut values) => {
|
||||
let name = values.next().unwrap();
|
||||
if values.len() != 0 {
|
||||
return Err(format!("extra operand ‘{}’", name));
|
||||
return Err(format!("extra operand '{}'", name));
|
||||
}
|
||||
|
||||
if name == "-" {
|
||||
|
@ -58,7 +58,7 @@ impl Config {
|
|||
.value_of(options::WRAP)
|
||||
.map(|num| {
|
||||
num.parse::<usize>()
|
||||
.map_err(|e| format!("Invalid wrap size: ‘{}’: {}", num, e))
|
||||
.map_err(|e| format!("Invalid wrap size: '{}': {}", num, e))
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
|
@ -78,10 +78,17 @@ pub fn parse_base_cmd_args(
|
|||
about: &str,
|
||||
usage: &str,
|
||||
) -> 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)
|
||||
.about(about)
|
||||
.usage(usage)
|
||||
// Format arguments.
|
||||
.arg(
|
||||
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
|
||||
// file passed in.
|
||||
.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))
|
||||
.arg(Arg::with_name(options::FILE).index(1).multiple(true))
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features = ["encoding"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
uu_base32 = { version=">=0.0.6", package="uu_base32", path="../base32"}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
extern crate uucore;
|
||||
|
||||
use uu_base32::base_common;
|
||||
pub use uu_base32::uu_app;
|
||||
|
||||
use uucore::encoding::Format;
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/basename.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.2"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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
|
||||
//
|
||||
let matches = App::new(executable!())
|
||||
.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);
|
||||
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||
|
||||
// too few arguments
|
||||
if !matches.is_present(options::NAME) {
|
||||
|
@ -116,6 +92,32 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
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 {
|
||||
// Remove all platform-specific path separators from the end
|
||||
let path = fullname.trim_end_matches(is_separator);
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/cat.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
thiserror = "1.0"
|
||||
atty = "0.2"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
// 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)]
|
||||
extern crate unix_socket;
|
||||
#[macro_use]
|
||||
|
@ -169,7 +172,65 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.collect_str(InvalidEncodingHandling::Ignore)
|
||||
.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)
|
||||
.version(crate_version!())
|
||||
.usage(SYNTAX)
|
||||
|
@ -229,61 +290,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.long(options::SHOW_NONPRINTING)
|
||||
.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>(
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/chgrp.rs"
|
||||
|
||||
[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_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
walkdir = "2.2"
|
||||
|
|
|
@ -73,84 +73,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
let usage = get_usage();
|
||||
|
||||
let mut app = App::new(executable!())
|
||||
.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"),
|
||||
);
|
||||
let mut app = uu_app().usage(&usage[..]);
|
||||
|
||||
// we change the positional args based on whether
|
||||
// --reference was used.
|
||||
|
@ -274,6 +197,86 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
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 {
|
||||
dest_gid: gid_t,
|
||||
bit_flag: u8,
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/chmod.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.3"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs", "mode"] }
|
||||
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 after_help = get_long_usage();
|
||||
|
||||
let matches = App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
let matches = uu_app()
|
||||
.usage(&usage[..])
|
||||
.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::with_name(options::CHANGES)
|
||||
.long(options::CHANGES)
|
||||
|
@ -120,55 +173,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.required_unless(options::MODE)
|
||||
.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
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/chown.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
glob = "0.3.0"
|
||||
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" }
|
||||
|
|
|
@ -73,101 +73,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
let usage = get_usage();
|
||||
|
||||
let matches = App::new(executable!())
|
||||
.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);
|
||||
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||
|
||||
/* First arg is the owner/group */
|
||||
let owner = matches.value_of(ARG_OWNER).unwrap();
|
||||
|
@ -273,6 +179,102 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
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> {
|
||||
let args = spec.split_terminator(':').collect::<Vec<_>>();
|
||||
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 {
|
||||
Some(
|
||||
Passwd::locate(args[0])
|
||||
.map_err(|_| format!("invalid user: ‘{}’", spec))?
|
||||
.map_err(|_| format!("invalid user: '{}'", spec))?
|
||||
.uid(),
|
||||
)
|
||||
} else {
|
||||
|
@ -290,7 +292,7 @@ fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
|
|||
let gid = if grp_only || usr_grp {
|
||||
Some(
|
||||
Group::locate(args[1])
|
||||
.map_err(|_| format!("invalid group: ‘{}’", spec))?
|
||||
.map_err(|_| format!("invalid group: '{}'", spec))?
|
||||
.gid(),
|
||||
)
|
||||
} else {
|
||||
|
|
|
@ -36,54 +36,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||
.accept_any();
|
||||
|
||||
let matches = 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),
|
||||
)
|
||||
.get_matches_from(args);
|
||||
let matches = uu_app().get_matches_from(args);
|
||||
|
||||
let default_shell: &'static str = "/bin/sh";
|
||||
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) {
|
||||
let userspec_str = options.value_of(options::USERSPEC);
|
||||
let user_str = options.value_of(options::USER).unwrap_or_default();
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/cksum.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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)
|
||||
.accept_any();
|
||||
|
||||
let matches = App::new(executable!())
|
||||
.name(NAME)
|
||||
.version(crate_version!())
|
||||
.about(SUMMARY)
|
||||
.usage(SYNTAX)
|
||||
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
|
||||
.get_matches_from(args);
|
||||
let matches = uu_app().get_matches_from(args);
|
||||
|
||||
let files: Vec<String> = match matches.values_of(options::FILE) {
|
||||
Some(v) => v.clone().map(|v| v.to_owned()).collect(),
|
||||
|
@ -217,3 +211,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
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"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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)
|
||||
.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!())
|
||||
.about(ABOUT)
|
||||
.usage(&usage[..])
|
||||
.after_help(LONG_HELP)
|
||||
.arg(
|
||||
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_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"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
filetime = "0.2"
|
||||
libc = "0.2.85"
|
||||
quick-error = "1.2.3"
|
||||
|
|
|
@ -290,13 +290,10 @@ static DEFAULT_ATTRIBUTES: &[Attribute] = &[
|
|||
Attribute::Timestamps,
|
||||
];
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let usage = get_usage();
|
||||
let matches = App::new(executable!())
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
.after_help(&*format!("{}\n{}", LONG_HELP, backup_control::BACKUP_CONTROL_LONG_HELP))
|
||||
.usage(&usage[..])
|
||||
.arg(Arg::with_name(options::TARGET_DIRECTORY)
|
||||
.short("t")
|
||||
.conflicts_with(options::NO_TARGET_DIRECTORY)
|
||||
|
@ -378,7 +375,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.arg(Arg::with_name(options::UPDATE)
|
||||
.short("u")
|
||||
.long(options::UPDATE)
|
||||
.help("copy only when the SOURCE file is newer than the destination file\
|
||||
.help("copy only when the SOURCE file is newer than the destination file \
|
||||
or when the destination file is missing"))
|
||||
.arg(Arg::with_name(options::REFLINK)
|
||||
.long(options::REFLINK)
|
||||
|
@ -401,7 +398,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.conflicts_with_all(&[options::PRESERVE_DEFAULT_ATTRIBUTES, options::NO_PRESERVE])
|
||||
// -d sets this option
|
||||
// --archive sets this option
|
||||
.help("Preserve the specified attributes (default: mode(unix only),ownership,timestamps),\
|
||||
.help("Preserve the specified attributes (default: mode (unix only), ownership, timestamps), \
|
||||
if possible additional attributes: context, links, xattr, all"))
|
||||
.arg(Arg::with_name(options::PRESERVE_DEFAULT_ATTRIBUTES)
|
||||
.short("-p")
|
||||
|
@ -464,6 +461,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
.arg(Arg::with_name(options::PATHS)
|
||||
.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);
|
||||
|
||||
let options = crash_if_err!(EXIT_ERR, Options::from_matches(&matches));
|
||||
|
@ -667,7 +675,14 @@ impl Options {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
ReflinkMode::Never
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
{
|
||||
ReflinkMode::Auto
|
||||
}
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||
{
|
||||
ReflinkMode::Never
|
||||
}
|
||||
}
|
||||
},
|
||||
backup: backup_mode,
|
||||
|
@ -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-on-write scheme if --reflink is specified and the filesystem supports it.
|
||||
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" {
|
||||
if options.parents {
|
||||
let parent = dest.parent().unwrap_or(dest);
|
||||
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.parents {
|
||||
let parent = dest.parent().unwrap_or(dest);
|
||||
fs::create_dir_all(parent)?;
|
||||
} 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))?;
|
||||
}
|
||||
|
||||
|
@ -1254,11 +1280,16 @@ fn copy_link(source: &Path, dest: &Path) -> CopyResult<()> {
|
|||
Some(name) => dest.join(name).into(),
|
||||
None => crash!(
|
||||
EXIT_ERR,
|
||||
"cannot stat ‘{}’: No such file or directory",
|
||||
"cannot stat '{}': No such file or directory",
|
||||
source.display()
|
||||
),
|
||||
}
|
||||
} 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()
|
||||
};
|
||||
symlink_file(&link, &dest, &*context_for(&link, &dest))
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/csplit.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
thiserror = "1.0"
|
||||
regex = "1.0.0"
|
||||
glob = "0.2.11"
|
||||
|
|
|
@ -711,10 +711,37 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.collect_str(InvalidEncodingHandling::Ignore)
|
||||
.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!())
|
||||
.about(SUMMARY)
|
||||
.usage(&usage[..])
|
||||
.arg(
|
||||
Arg::with_name(options::SUFFIX_FORMAT)
|
||||
.short("b")
|
||||
|
@ -768,29 +795,4 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.required(true),
|
||||
)
|
||||
.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 thiserror::Error;
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/cut.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
memchr = "2"
|
||||
|
|
|
@ -396,88 +396,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.collect_str(InvalidEncodingHandling::Ignore)
|
||||
.accept_any();
|
||||
|
||||
let matches = 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)
|
||||
)
|
||||
.get_matches_from(args);
|
||||
let matches = uu_app().get_matches_from(args);
|
||||
|
||||
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]
|
||||
chrono = "0.4.4"
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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]]",
|
||||
NAME
|
||||
);
|
||||
let matches = App::new(executable!())
|
||||
.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 matches = uu_app().usage(&syntax[..]).get_matches_from(args);
|
||||
|
||||
let format = if let Some(form) = matches.value_of(OPT_FORMAT) {
|
||||
if !form.starts_with('+') {
|
||||
eprintln!("date: invalid date ‘{}’", form);
|
||||
eprintln!("date: invalid date '{}'", form);
|
||||
return 1;
|
||||
}
|
||||
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) {
|
||||
None => None,
|
||||
Some(Err((input, _err))) => {
|
||||
eprintln!("date: invalid date ‘{}’", input);
|
||||
eprintln!("date: invalid date '{}'", input);
|
||||
return 1;
|
||||
}
|
||||
Some(Ok(date)) => Some(date),
|
||||
|
@ -305,7 +241,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
println!("{}", formatted);
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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.
|
||||
fn make_format_string(settings: &Settings) -> &str {
|
||||
match settings.format {
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/df.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
number_prefix = "0.4"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["libc", "fsext"] }
|
||||
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 {
|
||||
let usage = get_usage();
|
||||
let matches = App::new(executable!())
|
||||
.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 matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||
|
||||
let paths: Vec<String> = matches
|
||||
.values_of(OPT_PATHS)
|
||||
|
@ -511,3 +398,118 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
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"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
glob = "0.3.0"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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 matches = App::new(executable!())
|
||||
.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 matches = uu_app().usage(&usage[..]).get_matches_from(&args);
|
||||
|
||||
let files = matches
|
||||
.values_of(options::FILE)
|
||||
|
@ -123,7 +94,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
if matches.is_present(options::PRINT_DATABASE) {
|
||||
if !files.is_empty() {
|
||||
show_usage_error!(
|
||||
"extra operand ‘{}’\nfile operands cannot be combined with \
|
||||
"extra operand '{}'\nfile operands cannot be combined with \
|
||||
--print-database (-p)",
|
||||
files[0]
|
||||
);
|
||||
|
@ -155,7 +126,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
result = parse(INTERNAL_DB.lines(), out_format, "")
|
||||
} else {
|
||||
if files.len() > 1 {
|
||||
show_usage_error!("extra operand ‘{}’", files[1]);
|
||||
show_usage_error!("extra operand '{}'", files[1]);
|
||||
return 1;
|
||||
}
|
||||
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 {
|
||||
/// Remove comments and trim whitespace
|
||||
fn purify(&self) -> &Self;
|
||||
|
@ -192,21 +194,25 @@ pub trait StrUtils {
|
|||
impl StrUtils for str {
|
||||
fn purify(&self) -> &Self {
|
||||
let mut line = self;
|
||||
for (n, c) in self.chars().enumerate() {
|
||||
if c != '#' {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore if '#' is at the beginning of line
|
||||
if n == 0 {
|
||||
line = &self[..0];
|
||||
break;
|
||||
}
|
||||
|
||||
for (n, _) in self
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, c)| **c == b'#')
|
||||
{
|
||||
// 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];
|
||||
match self[..n].chars().last() {
|
||||
Some(c) if c.is_whitespace() => {
|
||||
line = &self[..n - c.len_utf8()];
|
||||
break;
|
||||
}
|
||||
None => {
|
||||
// n == 0
|
||||
line = &self[..0];
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
line.trim()
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/dirname.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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 after_help = get_long_usage();
|
||||
|
||||
let matches = App::new(executable!())
|
||||
.about(ABOUT)
|
||||
let matches = uu_app()
|
||||
.usage(&usage[..])
|
||||
.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);
|
||||
|
||||
let separator = if matches.is_present(options::ZERO) {
|
||||
|
@ -92,3 +83,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
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"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
chrono = "0.4"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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::Local;
|
||||
use clap::ArgMatches;
|
||||
use clap::{crate_version, App, Arg};
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryFrom;
|
||||
|
@ -62,6 +63,8 @@ mod options {
|
|||
pub const TIME: &str = "time";
|
||||
pub const TIME_STYLE: &str = "time-style";
|
||||
pub const ONE_FILE_SYSTEM: &str = "one-file-system";
|
||||
pub const DEREFERENCE: &str = "dereference";
|
||||
pub const INODES: &str = "inodes";
|
||||
pub const FILE: &str = "FILE";
|
||||
}
|
||||
|
||||
|
@ -87,6 +90,8 @@ struct Options {
|
|||
total: bool,
|
||||
separate_dirs: bool,
|
||||
one_file_system: bool,
|
||||
dereference: bool,
|
||||
inodes: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||
|
@ -100,6 +105,7 @@ struct Stat {
|
|||
is_dir: bool,
|
||||
size: u64,
|
||||
blocks: u64,
|
||||
inodes: u64,
|
||||
inode: Option<FileInfo>,
|
||||
created: Option<u64>,
|
||||
accessed: u64,
|
||||
|
@ -107,8 +113,12 @@ struct Stat {
|
|||
}
|
||||
|
||||
impl Stat {
|
||||
fn new(path: PathBuf) -> Result<Stat> {
|
||||
let metadata = fs::symlink_metadata(&path)?;
|
||||
fn new(path: PathBuf, options: &Options) -> Result<Stat> {
|
||||
let metadata = if options.dereference {
|
||||
fs::metadata(&path)?
|
||||
} else {
|
||||
fs::symlink_metadata(&path)?
|
||||
};
|
||||
|
||||
#[cfg(not(windows))]
|
||||
let file_info = FileInfo {
|
||||
|
@ -121,6 +131,7 @@ impl Stat {
|
|||
is_dir: metadata.is_dir(),
|
||||
size: metadata.len(),
|
||||
blocks: metadata.blocks() as u64,
|
||||
inodes: 1,
|
||||
inode: Some(file_info),
|
||||
created: birth_u64(&metadata),
|
||||
accessed: metadata.atime() as u64,
|
||||
|
@ -138,6 +149,7 @@ impl Stat {
|
|||
size: metadata.len(),
|
||||
blocks: size_on_disk / 1024 * 2,
|
||||
inode: file_info,
|
||||
inodes: 1,
|
||||
created: windows_creation_time_to_unix_time(metadata.creation_time()),
|
||||
accessed: windows_time_to_unix_time(metadata.last_access_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.
|
||||
// XXX: this should use the impl Trait return type when it is stabilized
|
||||
fn du(
|
||||
|
@ -268,7 +292,7 @@ fn du(
|
|||
Err(e) => {
|
||||
safe_writeln!(
|
||||
stderr(),
|
||||
"{}: cannot read directory ‘{}‘: {}",
|
||||
"{}: cannot read directory '{}': {}",
|
||||
options.program_name,
|
||||
my_stat.path.display(),
|
||||
e
|
||||
|
@ -279,8 +303,14 @@ fn du(
|
|||
|
||||
for f in read {
|
||||
match f {
|
||||
Ok(entry) => match Stat::new(entry.path()) {
|
||||
Ok(entry) => match Stat::new(entry.path(), options) {
|
||||
Ok(this_stat) => {
|
||||
if let Some(inode) = this_stat.inode {
|
||||
if inodes.contains(&inode) {
|
||||
continue;
|
||||
}
|
||||
inodes.insert(inode);
|
||||
}
|
||||
if this_stat.is_dir {
|
||||
if options.one_file_system {
|
||||
if let (Some(this_inode), Some(my_inode)) =
|
||||
|
@ -293,14 +323,9 @@ fn du(
|
|||
}
|
||||
futures.push(du(this_stat, options, depth + 1, inodes));
|
||||
} else {
|
||||
if let Some(inode) = this_stat.inode {
|
||||
if inodes.contains(&inode) {
|
||||
continue;
|
||||
}
|
||||
inodes.insert(inode);
|
||||
}
|
||||
my_stat.size += this_stat.size;
|
||||
my_stat.blocks += this_stat.blocks;
|
||||
my_stat.inodes += 1;
|
||||
if options.all {
|
||||
stats.push(this_stat);
|
||||
}
|
||||
|
@ -308,18 +333,11 @@ fn du(
|
|||
}
|
||||
Err(error) => match error.kind() {
|
||||
ErrorKind::PermissionDenied => {
|
||||
let description = format!(
|
||||
"cannot access '{}'",
|
||||
entry
|
||||
.path()
|
||||
.as_os_str()
|
||||
.to_str()
|
||||
.unwrap_or("<Un-printable path>")
|
||||
);
|
||||
let description = format!("cannot access '{}'", entry.path().display());
|
||||
let error_message = "Permission denied";
|
||||
show_error_custom_description!(description, "{}", error_message)
|
||||
}
|
||||
_ => show_error!("{}", error),
|
||||
_ => show_error!("cannot access '{}': {}", entry.path().display(), 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 {
|
||||
my_stat.size += stat.size;
|
||||
my_stat.blocks += stat.blocks;
|
||||
my_stat.inodes += stat.inodes;
|
||||
}
|
||||
options
|
||||
.max_depth
|
||||
|
@ -388,10 +407,196 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
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!())
|
||||
.about(SUMMARY)
|
||||
.usage(&usage[..])
|
||||
.after_help(LONG_HELP)
|
||||
.arg(
|
||||
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)")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("inodes")
|
||||
.long("inodes")
|
||||
Arg::with_name(options::INODES)
|
||||
.long(options::INODES)
|
||||
.help(
|
||||
"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")
|
||||
.help("count sizes many times if hard linked")
|
||||
)
|
||||
// .arg(
|
||||
// Arg::with_name("dereference")
|
||||
// .short("L")
|
||||
// .long("dereference")
|
||||
// .help("dereference all symbolic links")
|
||||
// )
|
||||
.arg(
|
||||
Arg::with_name(options::DEREFERENCE)
|
||||
.short("L")
|
||||
.long(options::DEREFERENCE)
|
||||
.help("dereference all symbolic links")
|
||||
)
|
||||
// .arg(
|
||||
// Arg::with_name("no-dereference")
|
||||
// .short("P")
|
||||
|
@ -563,184 +768,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.hidden(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)]
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/echo.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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
|
||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||
.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)
|
||||
// TrailingVarArg specifies the final positional argument is a VarArg
|
||||
// and it doesn't attempts the parse any further args.
|
||||
|
@ -154,22 +173,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.multiple(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<()> {
|
||||
|
|
2
src/uu/env/Cargo.toml
vendored
2
src/uu/env/Cargo.toml
vendored
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/env.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
rust-ini = "0.13.0"
|
||||
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[..])
|
||||
}
|
||||
|
||||
fn create_app() -> App<'static, 'static> {
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
App::new(crate_name!())
|
||||
.version(crate_version!())
|
||||
.author(crate_authors!())
|
||||
|
@ -158,7 +158,7 @@ fn create_app() -> App<'static, 'static> {
|
|||
}
|
||||
|
||||
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 ignore_env = matches.is_present("ignore-environment");
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/expand.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
unicode-width = "0.1.5"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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 {
|
||||
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!())
|
||||
.about(ABOUT)
|
||||
.usage(&usage[..])
|
||||
.after_help(LONG_HELP)
|
||||
.arg(
|
||||
Arg::with_name(options::INITIAL)
|
||||
|
@ -138,10 +144,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.hidden(true)
|
||||
.takes_value(true)
|
||||
)
|
||||
.get_matches_from(args);
|
||||
|
||||
expand(Options::new(&matches));
|
||||
0
|
||||
}
|
||||
|
||||
fn open(path: String) -> BufReader<Box<dyn Read + 'static>> {
|
||||
|
|
|
@ -15,6 +15,7 @@ edition = "2018"
|
|||
path = "src/expr.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
num-bigint = "0.4.0"
|
||||
num-traits = "0.2.14"
|
||||
|
|
|
@ -8,13 +8,20 @@
|
|||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use clap::{crate_version, App, Arg};
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
mod syntax_tree;
|
||||
mod tokens;
|
||||
|
||||
static NAME: &str = "expr";
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
const VERSION: &str = "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 {
|
||||
let args = args
|
||||
|
@ -133,5 +140,5 @@ Environment variables:
|
|||
}
|
||||
|
||||
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" }
|
||||
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" }
|
||||
uucore_procs = { version = ">=0.0.5", package = "uucore_procs", path = "../../uucore_procs" }
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
|
||||
[dev-dependencies]
|
||||
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 {
|
||||
let matches = App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(SUMMARY)
|
||||
.arg(Arg::with_name(options::NUMBER).multiple(true))
|
||||
.get_matches_from(args);
|
||||
let matches = uu_app().get_matches_from(args);
|
||||
let stdout = stdout();
|
||||
let mut w = io::BufWriter::new(stdout.lock());
|
||||
|
||||
|
@ -68,3 +64,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
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"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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
|
||||
// * 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
|
||||
}
|
||||
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
App::new(executable!())
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/fmt.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
unicode-width = "0.1.5"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
|
|
|
@ -77,129 +77,7 @@ pub struct FmtOptions {
|
|||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let usage = get_usage();
|
||||
|
||||
let matches = App::new(executable!())
|
||||
.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 matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||
|
||||
let mut files: Vec<String> = matches
|
||||
.values_of(ARG_FILES)
|
||||
|
@ -331,3 +209,127 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
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"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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();
|
||||
|
||||
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)
|
||||
.version(crate_version!())
|
||||
.usage(SYNTAX)
|
||||
|
@ -68,31 +96,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.takes_value(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>) {
|
||||
|
|
|
@ -17,7 +17,7 @@ path = "src/groups.rs"
|
|||
[dependencies]
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "process"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
|
||||
[[bin]]
|
||||
name = "groups"
|
||||
|
|
|
@ -5,6 +5,13 @@
|
|||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// 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
|
||||
|
||||
|
@ -14,50 +21,77 @@ use uucore::entries::{get_groups_gnu, gid2grp, Locate, Passwd};
|
|||
|
||||
use clap::{crate_version, App, Arg};
|
||||
|
||||
static ABOUT: &str = "display current group names";
|
||||
static OPT_USER: &str = "user";
|
||||
mod options {
|
||||
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 {
|
||||
format!("{0} [USERNAME]", executable!())
|
||||
format!("{0} [OPTION]... [USERNAME]...", executable!())
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let usage = get_usage();
|
||||
|
||||
let matches = App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
.usage(&usage[..])
|
||||
.arg(Arg::with_name(OPT_USER))
|
||||
.get_matches_from(args);
|
||||
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||
|
||||
match matches.value_of(OPT_USER) {
|
||||
None => {
|
||||
let users: Vec<String> = matches
|
||||
.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!(
|
||||
"{}",
|
||||
get_groups_gnu(None)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|&gid| gid2grp(gid).unwrap_or_else(|_| {
|
||||
show_error!("cannot find name for group ID {}", gid);
|
||||
exit_code = 1;
|
||||
gid.to_string()
|
||||
}))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
for user in users {
|
||||
if let Ok(p) = Passwd::locate(user.as_str()) {
|
||||
println!(
|
||||
"{}",
|
||||
get_groups_gnu(None)
|
||||
.unwrap()
|
||||
"{} : {}",
|
||||
user,
|
||||
p.belongs_to()
|
||||
.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<_>>()
|
||||
.join(" ")
|
||||
);
|
||||
0
|
||||
}
|
||||
Some(user) => {
|
||||
if let Ok(p) = Passwd::locate(user) {
|
||||
println!(
|
||||
"{}",
|
||||
p.belongs_to()
|
||||
.iter()
|
||||
.map(|&g| gid2grp(g).unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
);
|
||||
0
|
||||
} else {
|
||||
crash!(1, "unknown user {}", user);
|
||||
}
|
||||
} else {
|
||||
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]
|
||||
digest = "0.6.2"
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
hex = "0.2.0"
|
||||
libc = "0.2.42"
|
||||
md5 = "0.3.5"
|
||||
|
|
|
@ -285,119 +285,7 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
|
|||
// Default binary in Windows, text mode otherwise
|
||||
let binary_flag_default = cfg!(windows);
|
||||
|
||||
let binary_help = format!(
|
||||
"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));
|
||||
}
|
||||
}
|
||||
let app = uu_app(&binary_name);
|
||||
|
||||
// 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
|
||||
|
@ -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)]
|
||||
fn hashsum<'a, I>(mut options: Options, files: I) -> Result<(), i32>
|
||||
where
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/head.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["ringbuffer"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ mod take;
|
|||
use lines::zlines;
|
||||
use take::take_all_but;
|
||||
|
||||
fn app<'a>() -> App<'a, 'a> {
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
|
@ -167,7 +167,7 @@ impl HeadOptions {
|
|||
|
||||
///Construct options from matches
|
||||
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();
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ edition = "2018"
|
|||
path = "src/hostid.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
|
|
@ -10,12 +10,10 @@
|
|||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use clap::{crate_version, App};
|
||||
use libc::c_long;
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
static SYNTAX: &str = "[options]";
|
||||
static SUMMARY: &str = "";
|
||||
static LONG_HELP: &str = "";
|
||||
|
||||
// currently rust libc interface doesn't include gethostid
|
||||
extern "C" {
|
||||
|
@ -23,14 +21,17 @@ extern "C" {
|
|||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
app!(SYNTAX, SUMMARY, LONG_HELP).parse(
|
||||
args.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||
.accept_any(),
|
||||
);
|
||||
uu_app().get_matches_from(args);
|
||||
hostid();
|
||||
0
|
||||
}
|
||||
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.usage(SYNTAX)
|
||||
}
|
||||
|
||||
fn hostid() {
|
||||
/*
|
||||
* POSIX says gethostid returns a "32-bit identifier" but is silent
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/hostname.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
hostname = { version = "0.3", features = ["set"] }
|
||||
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 {
|
||||
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!())
|
||||
.about(ABOUT)
|
||||
.usage(&usage[..])
|
||||
.arg(
|
||||
Arg::with_name(OPT_DOMAIN)
|
||||
.short("d")
|
||||
|
@ -80,19 +95,6 @@ fn execute(args: impl uucore::Args) -> i32 {
|
|||
possible",
|
||||
))
|
||||
.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 {
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/id.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "process"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
selinux = "0.1.1"
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
// http://www.opensource.apple.com/source/shell_cmds/shell_cmds-118/id/id.c
|
||||
//
|
||||
// * This was partially rewritten in order for stdout/stderr/exit_code
|
||||
// to be conform with GNU coreutils (8.32) testsuite for `id`.
|
||||
// to be conform with GNU coreutils (8.32) test suite for `id`.
|
||||
//
|
||||
// * This supports multiple users (a feature that was introduced in coreutils 8.31)
|
||||
//
|
||||
// * This passes GNU's coreutils Testsuite (8.32)
|
||||
// * This passes GNU's coreutils Test suite (8.32)
|
||||
// for "tests/id/uid.sh" and "tests/id/zero/sh".
|
||||
//
|
||||
// * Option '--zero' does not exist for BSD's `id`, therefore '--zero' is only
|
||||
|
@ -26,7 +26,7 @@
|
|||
// * 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(dead_code)]
|
||||
|
@ -119,109 +119,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let usage = get_usage();
|
||||
let after_help = get_description();
|
||||
|
||||
let matches = App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
let matches = uu_app()
|
||||
.usage(&usage[..])
|
||||
.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);
|
||||
|
||||
let users: Vec<String> = matches
|
||||
|
@ -255,7 +155,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
crash!(1, "cannot print only names or real IDs in default format");
|
||||
}
|
||||
if state.zflag && default_format && !state.cflag {
|
||||
// NOTE: GNU testsuite "id/zero.sh" needs this stderr output:
|
||||
// NOTE: GNU test suite "id/zero.sh" needs this stderr output:
|
||||
crash!(1, "option --zero not permitted in default format");
|
||||
}
|
||||
if state.user_specified && state.cflag {
|
||||
|
@ -302,7 +202,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
match Passwd::locate(users[i].as_str()) {
|
||||
Ok(p) => Some(p),
|
||||
Err(_) => {
|
||||
show_error!("‘{}’: no such user", users[i]);
|
||||
show_error!("'{}': no such user", users[i]);
|
||||
exit_code = 1;
|
||||
if i + 1 >= users.len() {
|
||||
break;
|
||||
|
@ -418,6 +318,110 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
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>) {
|
||||
if let Some(p) = possible_pw {
|
||||
print!("uid\t{}\ngroups\t", p.name());
|
||||
|
|
|
@ -18,7 +18,7 @@ edition = "2018"
|
|||
path = "src/install.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
filetime = "0.2"
|
||||
file_diff = "1.0.0"
|
||||
libc = ">= 0.2"
|
||||
|
|
|
@ -15,6 +15,7 @@ extern crate uucore;
|
|||
use clap::{crate_version, App, Arg, ArgMatches};
|
||||
use file_diff::diff;
|
||||
use filetime::{set_file_times, FileTime};
|
||||
use uucore::backup_control::{self, BackupMode};
|
||||
use uucore::entries::{grp2gid, usr2uid};
|
||||
use uucore::perms::{wrap_chgrp, wrap_chown, Verbosity};
|
||||
|
||||
|
@ -33,6 +34,7 @@ const DEFAULT_STRIP_PROGRAM: &str = "strip";
|
|||
pub struct Behavior {
|
||||
main_function: MainFunction,
|
||||
specified_mode: Option<u32>,
|
||||
backup_mode: BackupMode,
|
||||
suffix: String,
|
||||
owner: String,
|
||||
group: String,
|
||||
|
@ -42,6 +44,7 @@ pub struct Behavior {
|
|||
strip: bool,
|
||||
strip_program: String,
|
||||
create_leading: bool,
|
||||
target_dir: Option<String>,
|
||||
}
|
||||
|
||||
#[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_BACKUP: &str = "backup";
|
||||
static OPT_BACKUP_2: &str = "backup2";
|
||||
static OPT_BACKUP_NO_ARG: &str = "backup2";
|
||||
static OPT_DIRECTORY: &str = "directory";
|
||||
static OPT_IGNORED: &str = "ignored";
|
||||
static OPT_CREATE_LEADING: &str = "create-leading";
|
||||
|
@ -97,21 +100,49 @@ fn get_usage() -> String {
|
|||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
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!())
|
||||
.about(ABOUT)
|
||||
.usage(&usage[..])
|
||||
.arg(
|
||||
Arg::with_name(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")
|
||||
)
|
||||
.arg(
|
||||
// TODO implement flag
|
||||
Arg::with_name(OPT_BACKUP_2)
|
||||
Arg::with_name(OPT_BACKUP_NO_ARG)
|
||||
.short("b")
|
||||
.help("(unimplemented) like --backup but does not accept an argument")
|
||||
.help("like --backup but does not accept an argument")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_IGNORED)
|
||||
|
@ -184,7 +215,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
Arg::with_name(OPT_SUFFIX)
|
||||
.short("S")
|
||||
.long(OPT_SUFFIX)
|
||||
.help("(unimplemented) override the usual backup suffix")
|
||||
.help("override the usual backup suffix")
|
||||
.value_name("SUFFIX")
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
|
@ -194,7 +225,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
Arg::with_name(OPT_TARGET_DIRECTORY)
|
||||
.short("t")
|
||||
.long(OPT_TARGET_DIRECTORY)
|
||||
.help("(unimplemented) move all SOURCE arguments into DIRECTORY")
|
||||
.help("move all SOURCE arguments into DIRECTORY")
|
||||
.value_name("DIRECTORY")
|
||||
)
|
||||
.arg(
|
||||
|
@ -227,29 +258,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.value_name("CONTEXT")
|
||||
)
|
||||
.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.
|
||||
|
@ -262,15 +270,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
///
|
||||
///
|
||||
fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
|
||||
if matches.is_present(OPT_BACKUP) {
|
||||
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) {
|
||||
if matches.is_present(OPT_NO_TARGET_DIRECTORY) {
|
||||
Err("--no-target-directory, -T")
|
||||
} else if matches.is_present(OPT_PRESERVE_CONTEXT) {
|
||||
Err("--preserve-context, -P")
|
||||
|
@ -308,16 +308,16 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
|||
None
|
||||
};
|
||||
|
||||
let backup_suffix = if matches.is_present(OPT_SUFFIX) {
|
||||
matches.value_of(OPT_SUFFIX).ok_or(1)?
|
||||
} else {
|
||||
"~"
|
||||
};
|
||||
let target_dir = matches.value_of(OPT_TARGET_DIRECTORY).map(|d| d.to_owned());
|
||||
|
||||
Ok(Behavior {
|
||||
main_function,
|
||||
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(),
|
||||
group: matches.value_of(OPT_GROUP).unwrap_or("").to_string(),
|
||||
verbose: matches.is_present(OPT_VERBOSE),
|
||||
|
@ -330,6 +330,7 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
|||
.unwrap_or(DEFAULT_STRIP_PROGRAM),
|
||||
),
|
||||
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.
|
||||
///
|
||||
fn standard(paths: Vec<String>, b: Behavior) -> i32 {
|
||||
let sources = &paths[0..paths.len() - 1]
|
||||
.iter()
|
||||
.map(PathBuf::from)
|
||||
.collect::<Vec<_>>();
|
||||
fn standard(mut paths: Vec<String>, b: Behavior) -> i32 {
|
||||
let target: PathBuf = b
|
||||
.target_dir
|
||||
.clone()
|
||||
.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()) {
|
||||
copy_files_into_dir(sources, &target.to_path_buf(), &b)
|
||||
copy_files_into_dir(sources, &target, &b)
|
||||
} else {
|
||||
if let Some(parent) = target.parent() {
|
||||
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) {
|
||||
copy_file_to_file(&sources[0], &target.to_path_buf(), &b)
|
||||
if target.is_file() || is_new_file_path(&target) {
|
||||
copy_file_to_file(&sources[0], &target, &b)
|
||||
} else {
|
||||
show_error!(
|
||||
"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) {
|
||||
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" {
|
||||
/* workaround a limitation of fs::copy
|
||||
|
@ -619,7 +643,11 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> Result<(), ()> {
|
|||
}
|
||||
|
||||
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(())
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/join.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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 {
|
||||
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!())
|
||||
.about(
|
||||
"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")
|
||||
.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 {
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/kill.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["signals"] }
|
||||
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 usage = format!("{} [OPTIONS]... PID...", executable!());
|
||||
let matches = App::new(executable!())
|
||||
.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 matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||
|
||||
let mode = if matches.is_present(options::TABLE) || matches.is_present(options::TABLE_OLD) {
|
||||
Mode::Table
|
||||
|
@ -106,6 +75,39 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
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>) {
|
||||
let mut i = 0;
|
||||
while i < args.len() {
|
||||
|
|
|
@ -18,7 +18,7 @@ path = "src/link.rs"
|
|||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
|
||||
[[bin]]
|
||||
name = "link"
|
||||
|
|
|
@ -32,19 +32,7 @@ pub fn normalize_error_message(e: Error) -> String {
|
|||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let usage = get_usage();
|
||||
let matches = App::new(executable!())
|
||||
.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 matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||
|
||||
let files: Vec<_> = matches
|
||||
.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"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||
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 long_usage = get_long_usage();
|
||||
|
||||
let matches = App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
let matches = uu_app()
|
||||
.usage(&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(
|
||||
"make a backup of each file that would otherwise be overwritten or \
|
||||
removed",
|
||||
|
@ -198,62 +258,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.required(true)
|
||||
.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 {
|
||||
|
@ -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 result: PathBuf = dst_abs
|
||||
let mut result: PathBuf = dst_abs
|
||||
.components()
|
||||
.skip(suffix_pos + 1)
|
||||
.map(|_| OsStr::new(".."))
|
||||
.chain(src_iter)
|
||||
.collect();
|
||||
if result.as_os_str().is_empty() {
|
||||
result.push(".");
|
||||
}
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ path = "src/logname.rs"
|
|||
|
||||
[dependencies]
|
||||
libc = "0.2.42"
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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();
|
||||
|
||||
let usage = get_usage();
|
||||
let _ = App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(SUMMARY)
|
||||
.usage(&usage[..])
|
||||
.get_matches_from(args);
|
||||
let _ = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||
|
||||
match get_userlogin() {
|
||||
Some(userlogin) => println!("{}", userlogin),
|
||||
|
@ -58,3 +54,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
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]
|
||||
locale = "0.2.2"
|
||||
chrono = "0.4.19"
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
unicode-width = "0.1.8"
|
||||
number_prefix = "0.4"
|
||||
term_grid = "0.1.5"
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
// 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]
|
||||
extern crate uucore;
|
||||
#[cfg(unix)]
|
||||
|
@ -14,7 +17,6 @@ extern crate uucore;
|
|||
extern crate lazy_static;
|
||||
|
||||
mod quoting_style;
|
||||
mod version_cmp;
|
||||
|
||||
use clap::{crate_version, App, Arg};
|
||||
use globset::{self, Glob, GlobSet, GlobSetBuilder};
|
||||
|
@ -26,10 +28,11 @@ use quoting_style::{escape_name, QuotingStyle};
|
|||
use std::os::windows::fs::MetadataExt;
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
error::Error,
|
||||
fmt::Display,
|
||||
fs::{self, DirEntry, FileType, Metadata},
|
||||
io::{stdout, BufWriter, Stdout, Write},
|
||||
path::{Path, PathBuf},
|
||||
process::exit,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
#[cfg(unix)]
|
||||
|
@ -38,26 +41,20 @@ use std::{
|
|||
os::unix::fs::{FileTypeExt, MetadataExt},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
|
||||
use uucore::error::{set_exit_code, FromIo, UCustomError, UResult};
|
||||
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
#[cfg(unix)]
|
||||
use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR};
|
||||
|
||||
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.";
|
||||
use uucore::{fs::display_permissions, version_cmp::version_cmp};
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!("{0} [OPTION]... [FILE]...", executable!())
|
||||
}
|
||||
|
||||
pub mod options {
|
||||
|
||||
pub mod format {
|
||||
pub static ONE_LINE: &str = "1";
|
||||
pub static LONG: &str = "long";
|
||||
|
@ -68,10 +65,12 @@ pub mod options {
|
|||
pub static LONG_NO_GROUP: &str = "o";
|
||||
pub static LONG_NUMERIC_UID_GID: &str = "numeric-uid-gid";
|
||||
}
|
||||
|
||||
pub mod files {
|
||||
pub static ALL: &str = "all";
|
||||
pub static ALMOST_ALL: &str = "almost-all";
|
||||
}
|
||||
|
||||
pub mod sort {
|
||||
pub static SIZE: &str = "S";
|
||||
pub static TIME: &str = "t";
|
||||
|
@ -79,30 +78,36 @@ pub mod options {
|
|||
pub static VERSION: &str = "v";
|
||||
pub static EXTENSION: &str = "X";
|
||||
}
|
||||
|
||||
pub mod time {
|
||||
pub static ACCESS: &str = "u";
|
||||
pub static CHANGE: &str = "c";
|
||||
}
|
||||
|
||||
pub mod size {
|
||||
pub static HUMAN_READABLE: &str = "human-readable";
|
||||
pub static SI: &str = "si";
|
||||
}
|
||||
|
||||
pub mod quoting {
|
||||
pub static ESCAPE: &str = "escape";
|
||||
pub static LITERAL: &str = "literal";
|
||||
pub static C: &str = "quote-name";
|
||||
}
|
||||
pub static QUOTING_STYLE: &str = "quoting-style";
|
||||
|
||||
pub mod indicator_style {
|
||||
pub static SLASH: &str = "p";
|
||||
pub static FILE_TYPE: &str = "file-type";
|
||||
pub static CLASSIFY: &str = "classify";
|
||||
}
|
||||
|
||||
pub mod dereference {
|
||||
pub static ALL: &str = "dereference";
|
||||
pub static ARGS: &str = "dereference-command-line";
|
||||
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 SHOW_CONTROL_CHARS: &str = "show-control-chars";
|
||||
pub static WIDTH: &str = "width";
|
||||
|
@ -125,6 +130,32 @@ pub mod options {
|
|||
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)]
|
||||
enum Format {
|
||||
Columns,
|
||||
|
@ -218,7 +249,7 @@ struct LongFormat {
|
|||
|
||||
impl Config {
|
||||
#[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) {
|
||||
(
|
||||
match format_ {
|
||||
|
@ -369,15 +400,13 @@ impl Config {
|
|||
}
|
||||
};
|
||||
|
||||
let width = options
|
||||
.value_of(options::WIDTH)
|
||||
.map(|x| {
|
||||
x.parse::<u16>().unwrap_or_else(|_e| {
|
||||
show_error!("invalid line width: ‘{}’", x);
|
||||
exit(2);
|
||||
})
|
||||
})
|
||||
.or_else(|| termsize::get().map(|s| s.cols));
|
||||
let width = match options.value_of(options::WIDTH) {
|
||||
Some(x) => match x.parse::<u16>() {
|
||||
Ok(u) => Some(u),
|
||||
Err(_) => return Err(LsError::InvalidLineWidth(x.into()).into()),
|
||||
},
|
||||
None => termsize::get().map(|s| s.cols),
|
||||
};
|
||||
|
||||
#[allow(clippy::needless_bool)]
|
||||
let show_control = if options.is_present(options::HIDE_CONTROL_CHARS) {
|
||||
|
@ -528,7 +557,7 @@ impl Config {
|
|||
Dereference::DirArgs
|
||||
};
|
||||
|
||||
Config {
|
||||
Ok(Config {
|
||||
format,
|
||||
files,
|
||||
sort,
|
||||
|
@ -547,29 +576,54 @@ impl Config {
|
|||
quoting_style,
|
||||
indicator_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
|
||||
.collect_str(InvalidEncodingHandling::Ignore)
|
||||
.accept_any();
|
||||
|
||||
let usage = get_usage();
|
||||
|
||||
let app = App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
.usage(&usage[..])
|
||||
let app = uu_app().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
|
||||
.arg(
|
||||
Arg::with_name(options::FORMAT)
|
||||
.long(options::FORMAT)
|
||||
.help("Set the display format.")
|
||||
.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)
|
||||
.require_equals(true)
|
||||
.overrides_with_all(&[
|
||||
|
@ -639,41 +693,51 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
Arg::with_name(options::format::ONE_LINE)
|
||||
.short(options::format::ONE_LINE)
|
||||
.help("List one file per line.")
|
||||
.multiple(true)
|
||||
.multiple(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::format::LONG_NO_GROUP)
|
||||
.short(options::format::LONG_NO_GROUP)
|
||||
.help("Long format without group information. Identical to --format=long with --no-group.")
|
||||
.multiple(true)
|
||||
.help(
|
||||
"Long format without group information. \
|
||||
Identical to --format=long with --no-group.",
|
||||
)
|
||||
.multiple(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::format::LONG_NO_OWNER)
|
||||
.short(options::format::LONG_NO_OWNER)
|
||||
.help("Long format without owner information.")
|
||||
.multiple(true)
|
||||
.multiple(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::format::LONG_NUMERIC_UID_GID)
|
||||
.short("n")
|
||||
.long(options::format::LONG_NUMERIC_UID_GID)
|
||||
.help("-l with numeric UIDs and GIDs.")
|
||||
.multiple(true)
|
||||
.multiple(true),
|
||||
)
|
||||
|
||||
// Quoting style
|
||||
.arg(
|
||||
Arg::with_name(options::QUOTING_STYLE)
|
||||
.long(options::QUOTING_STYLE)
|
||||
.takes_value(true)
|
||||
.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(&[
|
||||
options::QUOTING_STYLE,
|
||||
options::quoting::LITERAL,
|
||||
options::quoting::ESCAPE,
|
||||
options::quoting::C,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::quoting::LITERAL)
|
||||
|
@ -685,7 +749,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
options::quoting::LITERAL,
|
||||
options::quoting::ESCAPE,
|
||||
options::quoting::C,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::quoting::ESCAPE)
|
||||
|
@ -697,7 +761,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
options::quoting::LITERAL,
|
||||
options::quoting::ESCAPE,
|
||||
options::quoting::C,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::quoting::C)
|
||||
|
@ -709,76 +773,63 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
options::quoting::LITERAL,
|
||||
options::quoting::ESCAPE,
|
||||
options::quoting::C,
|
||||
])
|
||||
]),
|
||||
)
|
||||
|
||||
// Control characters
|
||||
.arg(
|
||||
Arg::with_name(options::HIDE_CONTROL_CHARS)
|
||||
.short("q")
|
||||
.long(options::HIDE_CONTROL_CHARS)
|
||||
.help("Replace control characters with '?' if they are not escaped.")
|
||||
.overrides_with_all(&[
|
||||
options::HIDE_CONTROL_CHARS,
|
||||
options::SHOW_CONTROL_CHARS,
|
||||
])
|
||||
.overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::SHOW_CONTROL_CHARS)
|
||||
.long(options::SHOW_CONTROL_CHARS)
|
||||
.help("Show control characters 'as is' if they are not escaped.")
|
||||
.overrides_with_all(&[
|
||||
options::HIDE_CONTROL_CHARS,
|
||||
options::SHOW_CONTROL_CHARS,
|
||||
])
|
||||
.overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]),
|
||||
)
|
||||
|
||||
// Time arguments
|
||||
.arg(
|
||||
Arg::with_name(options::TIME)
|
||||
.long(options::TIME)
|
||||
.help("Show time in <field>:\n\
|
||||
.help(
|
||||
"Show time in <field>:\n\
|
||||
\taccess time (-u): atime, access, use;\n\
|
||||
\tchange time (-t): ctime, status.\n\
|
||||
\tbirth time: birth, creation;")
|
||||
\tbirth time: birth, creation;",
|
||||
)
|
||||
.value_name("field")
|
||||
.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)
|
||||
.require_equals(true)
|
||||
.overrides_with_all(&[
|
||||
options::TIME,
|
||||
options::time::ACCESS,
|
||||
options::time::CHANGE,
|
||||
])
|
||||
.overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::time::CHANGE)
|
||||
.short(options::time::CHANGE)
|
||||
.help("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 \
|
||||
.help(
|
||||
"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 \
|
||||
format, sort according to the status change time.")
|
||||
.overrides_with_all(&[
|
||||
options::TIME,
|
||||
options::time::ACCESS,
|
||||
options::time::CHANGE,
|
||||
])
|
||||
format, sort according to the status change time.",
|
||||
)
|
||||
.overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(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 \
|
||||
(--sort=time or -t) or when not using a long listing format, sort according to the \
|
||||
access time.")
|
||||
.overrides_with_all(&[
|
||||
options::TIME,
|
||||
options::time::ACCESS,
|
||||
options::time::CHANGE,
|
||||
])
|
||||
access time.",
|
||||
)
|
||||
.overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]),
|
||||
)
|
||||
|
||||
// Hide and ignore
|
||||
.arg(
|
||||
Arg::with_name(options::HIDE)
|
||||
|
@ -786,7 +837,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.takes_value(true)
|
||||
.multiple(true)
|
||||
.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::with_name(options::IGNORE)
|
||||
|
@ -795,7 +848,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.takes_value(true)
|
||||
.multiple(true)
|
||||
.value_name("PATTERN")
|
||||
.help("do not list implied entries matching shell PATTERN")
|
||||
.help("do not list implied entries matching shell PATTERN"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::IGNORE_BACKUPS)
|
||||
|
@ -803,7 +856,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.long(options::IGNORE_BACKUPS)
|
||||
.help("Ignore entries which end with ~."),
|
||||
)
|
||||
|
||||
// Sort arguments
|
||||
.arg(
|
||||
Arg::with_name(options::SORT)
|
||||
|
@ -820,7 +872,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::sort::SIZE)
|
||||
|
@ -833,7 +885,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::sort::TIME)
|
||||
|
@ -846,7 +898,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::sort::VERSION)
|
||||
|
@ -859,7 +911,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::sort::EXTENSION)
|
||||
|
@ -872,14 +924,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::sort::NONE)
|
||||
.short(options::sort::NONE)
|
||||
.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, \
|
||||
since not doing any sorting can be noticeably faster.")
|
||||
.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, \
|
||||
since not doing any sorting can be noticeably faster.",
|
||||
)
|
||||
.overrides_with_all(&[
|
||||
options::SORT,
|
||||
options::sort::SIZE,
|
||||
|
@ -887,9 +941,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
|
||||
// Dereferencing
|
||||
.arg(
|
||||
Arg::with_name(options::dereference::ALL)
|
||||
|
@ -903,48 +956,43 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
options::dereference::ALL,
|
||||
options::dereference::DIR_ARGS,
|
||||
options::dereference::ARGS,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::dereference::DIR_ARGS)
|
||||
.long(options::dereference::DIR_ARGS)
|
||||
.help(
|
||||
"Do not dereference symlinks except when they link to directories and are \
|
||||
given as command line arguments.",
|
||||
given as command line arguments.",
|
||||
)
|
||||
.overrides_with_all(&[
|
||||
options::dereference::ALL,
|
||||
options::dereference::DIR_ARGS,
|
||||
options::dereference::ARGS,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::dereference::ARGS)
|
||||
.short("H")
|
||||
.long(options::dereference::ARGS)
|
||||
.help(
|
||||
"Do not dereference symlinks except when given as command line arguments.",
|
||||
)
|
||||
.help("Do not dereference symlinks except when given as command line arguments.")
|
||||
.overrides_with_all(&[
|
||||
options::dereference::ALL,
|
||||
options::dereference::DIR_ARGS,
|
||||
options::dereference::ARGS,
|
||||
])
|
||||
]),
|
||||
)
|
||||
|
||||
// Long format options
|
||||
.arg(
|
||||
Arg::with_name(options::NO_GROUP)
|
||||
.long(options::NO_GROUP)
|
||||
.short("-G")
|
||||
.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.")
|
||||
.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.",
|
||||
))
|
||||
// Other Flags
|
||||
.arg(
|
||||
Arg::with_name(options::files::ALL)
|
||||
|
@ -957,9 +1005,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.short("A")
|
||||
.long(options::files::ALMOST_ALL)
|
||||
.help(
|
||||
"In a directory, do not ignore all file names that start with '.', only ignore \
|
||||
'.' and '..'.",
|
||||
),
|
||||
"In a directory, do not ignore all file names that start with '.', \
|
||||
only ignore '.' and '..'.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::DIRECTORY)
|
||||
|
@ -982,7 +1030,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.arg(
|
||||
Arg::with_name(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::with_name(options::INODE)
|
||||
|
@ -994,9 +1042,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
Arg::with_name(options::REVERSE)
|
||||
.short("r")
|
||||
.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.",
|
||||
))
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::RECURSIVE)
|
||||
.short("R")
|
||||
|
@ -1009,7 +1059,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.short("w")
|
||||
.help("Assume that the terminal is COLS columns wide.")
|
||||
.value_name("COLS")
|
||||
.takes_value(true)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::COLOR)
|
||||
|
@ -1022,8 +1072,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.arg(
|
||||
Arg::with_name(options::INDICATOR_STYLE)
|
||||
.long(options::INDICATOR_STYLE)
|
||||
.help(" append indicator with style WORD to entry names: none (default), slash\
|
||||
(-p), file-type (--file-type), classify (-F)")
|
||||
.help(
|
||||
"Append indicator with style WORD to entry names: \
|
||||
none (default), slash (-p), file-type (--file-type), classify (-F)",
|
||||
)
|
||||
.takes_value(true)
|
||||
.possible_values(&["none", "slash", "file-type", "classify"])
|
||||
.overrides_with_all(&[
|
||||
|
@ -1031,21 +1083,24 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
options::indicator_style::SLASH,
|
||||
options::indicator_style::CLASSIFY,
|
||||
options::INDICATOR_STYLE,
|
||||
]))
|
||||
.arg(
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::indicator_style::CLASSIFY)
|
||||
.short("F")
|
||||
.long(options::indicator_style::CLASSIFY)
|
||||
.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 \
|
||||
'/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \
|
||||
'>' for doors, and nothing for regular files.")
|
||||
.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 \
|
||||
'/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \
|
||||
'>' for doors, and nothing for regular files.",
|
||||
)
|
||||
.overrides_with_all(&[
|
||||
options::indicator_style::FILE_TYPE,
|
||||
options::indicator_style::SLASH,
|
||||
options::indicator_style::CLASSIFY,
|
||||
options::INDICATOR_STYLE,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
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::CLASSIFY,
|
||||
options::INDICATOR_STYLE,
|
||||
]))
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::indicator_style::SLASH)
|
||||
.short(options::indicator_style::SLASH)
|
||||
.help("Append / indicator to directories."
|
||||
)
|
||||
.help("Append / indicator to directories.")
|
||||
.overrides_with_all(&[
|
||||
options::indicator_style::FILE_TYPE,
|
||||
options::indicator_style::SLASH,
|
||||
options::indicator_style::CLASSIFY,
|
||||
options::INDICATOR_STYLE,
|
||||
]))
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
//This still needs support for posix-*, +FORMAT
|
||||
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")
|
||||
.value_name("TIME_STYLE")
|
||||
.env("TIME_STYLE")
|
||||
.possible_values(&[
|
||||
"full-iso",
|
||||
"long-iso",
|
||||
"iso",
|
||||
"locale",
|
||||
])
|
||||
.overrides_with_all(&[
|
||||
options::TIME_STYLE
|
||||
])
|
||||
.possible_values(&["full-iso", "long-iso", "iso", "locale"])
|
||||
.overrides_with_all(&[options::TIME_STYLE]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::FULL_TIME)
|
||||
.long(options::FULL_TIME)
|
||||
.overrides_with(options::FULL_TIME)
|
||||
.help("like -l --time-style=full-iso")
|
||||
.long(options::FULL_TIME)
|
||||
.overrides_with(options::FULL_TIME)
|
||||
.help("like -l --time-style=full-iso"),
|
||||
)
|
||||
// Positional arguments
|
||||
.arg(
|
||||
Arg::with_name(options::PATHS)
|
||||
.multiple(true)
|
||||
.takes_value(true),
|
||||
)
|
||||
.after_help(
|
||||
"The TIME_STYLE argument can be full-iso, long-iso, iso. \
|
||||
Also the TIME_STYLE environment variable sets the default style to use.",
|
||||
)
|
||||
|
||||
// Positional arguments
|
||||
.arg(Arg::with_name(options::PATHS).multiple(true).takes_value(true))
|
||||
|
||||
.after_help(AFTER_HELP);
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
/// 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 dirs = Vec::<PathData>::new();
|
||||
let mut has_failed = false;
|
||||
|
||||
let mut out = BufWriter::new(stdout());
|
||||
|
||||
for loc in &locs {
|
||||
let p = PathBuf::from(&loc);
|
||||
if !p.exists() {
|
||||
show_error!("'{}': {}", &loc, "No such file or directory");
|
||||
/*
|
||||
We found an error, the return code of ls should not be 0
|
||||
And no need to continue the execution
|
||||
*/
|
||||
has_failed = true;
|
||||
let path_data = PathData::new(p, None, None, &config, true);
|
||||
|
||||
if path_data.md().is_none() {
|
||||
show!(std::io::ErrorKind::NotFound
|
||||
.map_err_context(|| format!("cannot access '{}'", path_data.p_buf.display())));
|
||||
// We found an error, no need to continue the execution
|
||||
continue;
|
||||
}
|
||||
|
||||
let path_data = PathData::new(p, None, None, &config, true);
|
||||
|
||||
let show_dir_contents = match path_data.file_type() {
|
||||
Some(ft) => !config.directory && ft.is_dir(),
|
||||
None => {
|
||||
has_failed = true;
|
||||
set_exit_code(1);
|
||||
false
|
||||
}
|
||||
};
|
||||
|
@ -1232,11 +1273,8 @@ fn list(locs: Vec<String>, config: Config) -> i32 {
|
|||
}
|
||||
enter_directory(&dir, &config, &mut out);
|
||||
}
|
||||
if has_failed {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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
|
||||
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| {
|
||||
a.p_buf
|
||||
.extension()
|
||||
|
@ -1270,7 +1309,8 @@ fn sort_entries(entries: &mut Vec<PathData>, config: &Config) {
|
|||
|
||||
#[cfg(windows)]
|
||||
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();
|
||||
(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> {
|
||||
if dereference {
|
||||
entry.metadata().or_else(|_| entry.symlink_metadata())
|
||||
entry.metadata()
|
||||
} else {
|
||||
entry.symlink_metadata()
|
||||
}
|
||||
|
@ -1463,8 +1503,6 @@ fn display_grid(
|
|||
}
|
||||
}
|
||||
|
||||
use uucore::fs::display_permissions;
|
||||
|
||||
fn display_item_long(
|
||||
item: &PathData,
|
||||
max_links: usize,
|
||||
|
@ -1474,7 +1512,7 @@ fn display_item_long(
|
|||
) {
|
||||
let md = match item.md() {
|
||||
None => {
|
||||
show_error!("could not show file: {}", &item.p_buf.display());
|
||||
show!(LsError::NoMetadata(item.p_buf.clone()));
|
||||
return;
|
||||
}
|
||||
Some(md) => md,
|
||||
|
@ -1733,7 +1771,11 @@ fn display_file_name(path: &PathData, config: &Config) -> Option<Cell> {
|
|||
#[cfg(unix)]
|
||||
{
|
||||
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"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs", "mode"] }
|
||||
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
|
||||
// * 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]
|
||||
extern crate uucore;
|
||||
|
||||
use clap::OsValues;
|
||||
use clap::{crate_version, App, Arg};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use uucore::error::{FromIo, UResult, USimpleError};
|
||||
|
||||
static ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist";
|
||||
static OPT_MODE: &str = "mode";
|
||||
static OPT_PARENTS: &str = "parents";
|
||||
static OPT_VERBOSE: &str = "verbose";
|
||||
|
||||
static ARG_DIRS: &str = "dirs";
|
||||
mod options {
|
||||
pub const MODE: &str = "mode";
|
||||
pub const PARENTS: &str = "parents";
|
||||
pub const VERBOSE: &str = "verbose";
|
||||
pub const DIRS: &str = "dirs";
|
||||
}
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!("{0} [OPTION]... [USER]", executable!())
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles option parsing
|
||||
*/
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
#[uucore_procs::gen_uumain]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let usage = get_usage();
|
||||
|
||||
// Linux-specific options, not implemented
|
||||
// opts.optflag("Z", "context", "set SELinux security context" +
|
||||
// " of each created directory to CTX"),
|
||||
let matches = App::new(executable!())
|
||||
.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 matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||
|
||||
let dirs: Vec<String> = matches
|
||||
.values_of(ARG_DIRS)
|
||||
.map(|v| v.map(ToString::to_string).collect())
|
||||
.unwrap_or_default();
|
||||
|
||||
let verbose = matches.is_present(OPT_VERBOSE);
|
||||
let recursive = matches.is_present(OPT_PARENTS);
|
||||
let dirs = matches.values_of_os(options::DIRS).unwrap_or_default();
|
||||
let verbose = matches.is_present(options::VERBOSE);
|
||||
let recursive = matches.is_present(options::PARENTS);
|
||||
|
||||
// Translate a ~str in octal form to u16, default to 755
|
||||
// Not tested on Windows
|
||||
let mode_match = matches.value_of(OPT_MODE);
|
||||
let mode: u16 = match mode_match {
|
||||
Some(m) => {
|
||||
let res: Option<u16> = u16::from_str_radix(m, 8).ok();
|
||||
match res {
|
||||
Some(r) => r,
|
||||
_ => crash!(1, "no mode given"),
|
||||
}
|
||||
}
|
||||
_ => 0o755_u16,
|
||||
let mode: u16 = match matches.value_of(options::MODE) {
|
||||
Some(m) => u16::from_str_radix(m, 8)
|
||||
.map_err(|_| USimpleError::new(1, format!("invalid mode '{}'", m)))?,
|
||||
None => 0o755_u16,
|
||||
};
|
||||
|
||||
exec(dirs, recursive, mode, verbose)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the list of new directories
|
||||
*/
|
||||
fn exec(dirs: Vec<String>, recursive: bool, mode: u16, verbose: bool) -> i32 {
|
||||
let mut status = 0;
|
||||
let empty = Path::new("");
|
||||
for dir in &dirs {
|
||||
let path = Path::new(dir);
|
||||
if !recursive {
|
||||
if let Some(parent) = path.parent() {
|
||||
if parent != empty && !parent.exists() {
|
||||
show_error!(
|
||||
"cannot create directory '{}': No such file or directory",
|
||||
path.display()
|
||||
);
|
||||
status = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
status |= mkdir(path, recursive, mode, verbose);
|
||||
}
|
||||
status
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
.arg(
|
||||
Arg::with_name(options::MODE)
|
||||
.short("m")
|
||||
.long(options::MODE)
|
||||
.help("set file mode (not implemented on windows)")
|
||||
.default_value("755"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::PARENTS)
|
||||
.short("p")
|
||||
.long(options::PARENTS)
|
||||
.alias("parent")
|
||||
.help("make parent directories as needed"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::VERBOSE)
|
||||
.short("v")
|
||||
.long(options::VERBOSE)
|
||||
.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 {
|
||||
fs::create_dir_all
|
||||
} else {
|
||||
fs::create_dir
|
||||
};
|
||||
if let Err(e) = create_dir(path) {
|
||||
show_error!("{}: {}", path.display(), e.to_string());
|
||||
return 1;
|
||||
}
|
||||
|
||||
create_dir(path).map_err_context(|| format!("cannot create directory '{}'", path.display()))?;
|
||||
|
||||
if verbose {
|
||||
println!("{}: created directory '{}'", executable!(), path.display());
|
||||
}
|
||||
|
||||
#[cfg(any(unix, target_os = "redox"))]
|
||||
fn chmod(path: &Path, mode: u16) -> i32 {
|
||||
use std::fs::{set_permissions, Permissions};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let mode = Permissions::from_mode(u32::from(mode));
|
||||
|
||||
if let Err(err) = set_permissions(path, mode) {
|
||||
show_error!("{}: {}", path.display(), err);
|
||||
return 1;
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(windows)]
|
||||
#[allow(unused_variables)]
|
||||
fn chmod(path: &Path, mode: u16) -> i32 {
|
||||
// chmod on Windows only sets the readonly flag, which isn't even honored on directories
|
||||
0
|
||||
}
|
||||
chmod(path, mode)
|
||||
}
|
||||
|
||||
#[cfg(any(unix, target_os = "redox"))]
|
||||
fn chmod(path: &Path, mode: u16) -> UResult<()> {
|
||||
use std::fs::{set_permissions, Permissions};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let mode = Permissions::from_mode(u32::from(mode));
|
||||
|
||||
set_permissions(path, mode)
|
||||
.map_err_context(|| format!("cannot set permissions '{}'", path.display()))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn chmod(_path: &Path, _mode: u16) -> UResult<()> {
|
||||
// chmod on Windows only sets the readonly flag, which isn't even honored on directories
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/mkfifo.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
clap = { version = "2.33", features = ["wrap_help"] }
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
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)
|
||||
.accept_any();
|
||||
|
||||
let matches = 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\nor SMACK security context to CTX"))
|
||||
.arg(Arg::with_name(options::FIFO).hidden(true).multiple(true))
|
||||
.get_matches_from(args);
|
||||
let matches = uu_app().get_matches_from(args);
|
||||
|
||||
if matches.is_present(options::CONTEXT) {
|
||||
crash!(1, "--context is not implemented");
|
||||
|
@ -88,3 +68,34 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
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